From 2614d259aada4ea31ecc5755c55cec6cf8f64e39 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 09:10:49 +0100 Subject: [PATCH] =?UTF-8?q?feat(facades):=200.1.6=20retail=20frontage=20fi?= =?UTF-8?q?rst=20pass=20=E2=80=94=20Lee=20Drug=20and=206806-6820?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Models the two retail buildings beside the hero from their own photograph rather than the generic kit: bronze cornice, mural row, lettering band, stepped Deco parapet and rounded corner tower on 6800 Lee Drug; burgundy fascia, pilasters, white-framed windows and banded parapet on 6806-6820. Reference analysis written before any geometry (Art/blender/retail/ref-analysis.md), every unmeasured proportion carried into the part report as an assumption. 6822 Budget is deferred, not guessed: no photograph of it exists and the 6.0 x 5.5 m sliver cannot be read out of the neighbour shot. Pipeline fixes found on the way: - render_screenshots.sh called ./Engine/... for the capture step while every commandlet used the absolute path; set -e killed the script at 127 *after* the maps were rebuilt, so screenshots silently stayed a day old. - the facade kit applies to every Frontage_*/North_* actor, so it pasted its own language over the new modelled facades; it now skips them, and the trim stages as RetailTrim_*. - pure bay arithmetic moved to Tools/_common.py so it is unit-testable without Blender (4 new tests, 82 total pass). Known gaps recorded in Screenshots/README.md, not hidden: the parapet reads as a blank band, murals and lettering do not resolve at review distance, the tower is a plain cylinder, and the review camera crops the east end. --- Art/blender/retail/ref-analysis.md | 76 ++++ Game/GigLine/Content/Hollywood/DemoMap.umap | 4 +- .../Hollywood/Frontage/M_Retail_Bronze.uasset | 3 + .../Frontage/M_Retail_Burgundy.uasset | 3 + .../Hollywood/Frontage/M_Retail_Glass.uasset | 3 + .../Hollywood/Frontage/M_Retail_Mural.uasset | 3 + .../Hollywood/Frontage/M_Retail_Stucco.uasset | 3 + .../Hollywood/Frontage/M_Retail_White.uasset | 3 + .../Hollywood/Frontage/SM_Retail_6806.uasset | 3 + .../Frontage/SM_Retail_LeeDrug.uasset | 3 + .../Content/Hollywood/Site/M_Asphalt.uasset | 4 +- .../Content/Hollywood/Site/M_Concrete.uasset | 4 +- .../Hollywood/Site/M_FacadeBanner.uasset | 4 +- .../Hollywood/Site/M_FacadeGlass.uasset | 4 +- .../Hollywood/Site/M_FacadeStone.uasset | 4 +- .../Hollywood/Site/M_FacadeStoneShadow.uasset | 4 +- .../Content/Hollywood/Site/M_Ground.uasset | 4 +- .../Hollywood/Site/M_KerbConcrete.uasset | 4 +- .../Content/Hollywood/Site/M_RoadPaint.uasset | 4 +- .../Content/Hollywood/Site/M_Stucco.uasset | 4 +- .../Content/Hollywood/Site/Terrain_DEM.uasset | 2 +- .../Hollywood/Textures/T_Asphalt_BC.uasset | 2 +- .../Hollywood/Textures/T_Asphalt_R.uasset | 2 +- .../Hollywood/Textures/T_Concrete_BC.uasset | 2 +- .../Hollywood/Textures/T_Concrete_R.uasset | 2 +- .../Textures/T_StoneAshlar_BC.uasset | 2 +- .../Hollywood/Textures/T_StoneAshlar_R.uasset | 2 +- .../Hollywood/Textures/T_Stucco_BC.uasset | 2 +- .../Hollywood/Textures/T_Stucco_R.uasset | 2 +- .../Theatre/SM_Trim_BladeSign.uasset | 2 +- .../Theatre/SM_Trim_BladeSignBracket.uasset | 2 +- .../Theatre/SM_Trim_BladeSignFrame.uasset | 2 +- .../Theatre/SM_Trim_MarqueeCanopy.uasset | 2 +- .../Theatre/SM_Trim_MarqueeFascia.uasset | 2 +- .../Theatre/SM_Trim_MarqueeSoffit.uasset | 2 +- Game/GigLine/Content/Test/ReviewMap.umap | 2 +- MEMORY.md | 8 + Screenshots/README.md | 18 + Screenshots/demo/review-demo-crossing.png | 4 +- .../demo/review-demo-north-buildings.png | 4 +- .../demo/review-demo-retail-frontage.png | 3 + Screenshots/demo/review-demo-spawn-view.png | 4 +- Screenshots/demo/review-demo-street-level.png | 4 +- TODO.md | 5 +- Tools/README.md | 3 + Tools/_common.py | 27 ++ Tools/render_screenshots.sh | 5 +- Tools/retail_frontage_build.py | 329 ++++++++++++++++++ Tools/tests/test_retail_frontage.py | 32 ++ Tools/ue_apply_facade_kit.py | 12 +- Tools/ue_build_demo_map.py | 19 + Tools/ue_build_review_map.py | 6 + Tools/ue_import_retail_detail.py | 94 +++++ Tools/visual_targets.json | 12 + 54 files changed, 715 insertions(+), 50 deletions(-) create mode 100644 Art/blender/retail/ref-analysis.md create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_Bronze.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_Burgundy.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_Glass.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_Mural.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_Stucco.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/M_Retail_White.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/SM_Retail_6806.uasset create mode 100644 Game/GigLine/Content/Hollywood/Frontage/SM_Retail_LeeDrug.uasset create mode 100644 Screenshots/demo/review-demo-retail-frontage.png create mode 100644 Tools/_common.py create mode 100644 Tools/retail_frontage_build.py create mode 100644 Tools/tests/test_retail_frontage.py create mode 100644 Tools/ue_import_retail_detail.py diff --git a/Art/blender/retail/ref-analysis.md b/Art/blender/retail/ref-analysis.md new file mode 100644 index 0000000..fb253a5 --- /dev/null +++ b/Art/blender/retail/ref-analysis.md @@ -0,0 +1,76 @@ +# Reference analysis — retail frontage, 0.1.6 + +Phase 0 of the `references` pack (reference-image-match). Written before any geometry, +because the pass that produced the hero façade skipped it and cost two rebuilds. + +## Source + +| | | +| --- | --- | +| Image | `Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg` (1920×953) | +| Author / licence | Gb321, own work, **CC BY-SA 4.0** — attribution required | +| Captured | 2024-07-19 11:11 local | +| Description | "Lee Drug and 6806 Hollywood Buildings in Hollywood" | +| Coverage | Both target buildings in one frame, mid-morning sun from the south-east | + +**One photograph is the whole evidence base for 6800 and 6806–6820.** It is a street-level +oblique, not an elevation: verticals converge, the ground floor of 6806–6820 is partly behind +a parked car, and the Lee Drug shopfront is behind a bus. Everything below that the photo does +not resolve is marked *assumed* and is carried into the part report. + +**6822 Budget has no photograph.** It is a 6.0 m × 5.5 m single-storey sliver west of +6806–6820, and the TODO item already records that Commons has nothing usable. It stays a +blockout extrusion and the item is deferred rather than guessed. + +## Measured (not from the photo) + +From `Geo/raw/osm-api-map-2026-11-06.osm` and the blockout, in the shared frame +(metres, X east, Y north, Z from the project datum): + +| Building | OSM way | X | Front face Y | Height | Depth | +| --- | --- | --- | --- | --- | --- | +| 6800 Lee Drug | 424024149 | −33.20 … −14.37 | −14.63 | 12.10 m | 33.5 m | +| 6806–6820 | 424024150 | −66.08 … −33.07 | −12.52 | 12.10 m | 30.8 m | +| 6822 Budget | 424024144 | −78.54 … −72.52 | −12.92 | 5.50 m | 15.4 m | + +Both target buildings are 12.10 m tall, which makes the *relative* proportions in the photo +directly usable: the parapet line, the cornice and the shopfront line can be read as fractions +of the frame height. + +## 6800 Lee Drug — reading + +| Category | Observation | +| --- | --- | +| Subject | Corner retail block at Hollywood × Highland; the street face and the corner return are both visible | +| Silhouette | Long low box, broken by a **rounded corner tower** that rises above the frontage parapet; the parapet is **stepped**, not flat | +| Storeys | Two zones, not storeys: a tall shopfront zone and a single tall display wall above it | +| Proportion (vertical) | Shopfront ≈ 0 → 0.36 H; bronze cornice ≈ 0.36 → 0.43 H; mural + lettering wall ≈ 0.43 → 0.82 H; stepped parapet ≈ 0.82 → 1.0 H (*assumed split; read off the frame, ±0.03 H*) | +| Proportion (horizontal) | Corner tower occupies ≈ 4 m of the east end; eight mural panels at roughly 2.2 m centres along the frontage | +| Colour | Cream stucco wall `#EDE6D8`, bronze/brown cornice `#6B4A2F`, near-black mural plates `#1C1A18`, dark glazing `#20262A` (*sampled from the frame*) | +| Unique features | Stepped Deco parapet with 4 horizontal reveal lines; projecting bronze cornice; row of black celebrity-portrait panels; large dark "SOUVENIRS OF …" lettering on the upper wall; shopfront glazing with slim mullions | +| Mood | 1930s Deco retail, freshly painted, bright midday | + +## 6806–6820 — reading + +| Category | Observation | +| --- | --- | +| Silhouette | Flat-fronted two-storey block; parapet steps up in fins at the west end | +| Proportion (vertical) | Shopfront ≈ 0 → 0.32 H; burgundy fascia band ≈ 0.32 → 0.38 H; window storey ≈ 0.38 → 0.62 H; blank wall + parapet ≈ 0.62 → 1.0 H (*assumed*) | +| Horizontal | Brown pilaster strips divide the frontage into roughly 3 m bays; seven upper windows across 33 m | +| Colour | Same cream stucco; brown pilasters `#6B4A2F`; **burgundy-green** fascia `#5B3A3A`; white window frames | +| Unique features | Banded parapet with 3 reveal lines; white-framed upper windows with security bars; shopfront glazing behind a burgundy fascia | + +## Camera match (for the review shot) + +The photo is a street-level oblique from the **north** side looking south-east, subject filling +~80 % of the frame width. The demo review shot must reproduce that: camera at 1.7 m on the +north sidewalk, yaw toward the south frontage, 35–42° lens, both buildings in frame. A wide +block view will not compare usefully against this crop. + +## Assumptions to carry into the model report + +1. Vertical proportions, as above — the photo's convergence makes a pixel-perfect read impossible. +2. Mural panel size and spacing; the lettering is modelled as a recessed plate, not as text. +3. Shopfront bay spacing on both buildings; the bus hides most of Lee Drug's ground floor. +4. Parapet reveal line count (4 on Lee Drug, 3 on 6806–6820). +5. Corner tower radius; only the outer curve is visible. diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index 9b1cfcc..e734bfc 100644 --- a/Game/GigLine/Content/Hollywood/DemoMap.umap +++ b/Game/GigLine/Content/Hollywood/DemoMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:979c81662cbb7cce37da5401a873c85b56a8da9f13a1e27790bd10f8098c54a4 -size 920287 +oid sha256:0d6d69622d2135200c91e09decfe462e93b416bcbad430cb7e8ce5df4368b83c +size 781795 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Bronze.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Bronze.uasset new file mode 100644 index 0000000..3017713 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Bronze.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4b1ebcefdb8b02bf6f3dfc61f25ceed6bde1e5ecf40067437fd4d70f7a93c45 +size 56705 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Burgundy.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Burgundy.uasset new file mode 100644 index 0000000..3b32fd8 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Burgundy.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:682a4a2e3d365daa848fb3c8f8c902a1ccd181fc68120fe72452449337c95dc6 +size 56702 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Glass.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Glass.uasset new file mode 100644 index 0000000..2311b01 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Glass.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aedc0b3c2b40ead88ddda2e1bfdd8fe7047fc044255c57a4be1a736c329332bd +size 56688 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Mural.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Mural.uasset new file mode 100644 index 0000000..377e376 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Mural.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b2688239130bb6210829d2d1a39b9ef4da74eb06f4fdfacae08faf2dc7e7e6b +size 56685 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Stucco.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Stucco.uasset new file mode 100644 index 0000000..2c9b5fd --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_Stucco.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf6cbf3830fba67080af03dca25da41054ef6d7ef22fc88da9e67b898c9ff124 +size 56681 diff --git a/Game/GigLine/Content/Hollywood/Frontage/M_Retail_White.uasset b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_White.uasset new file mode 100644 index 0000000..24a99b5 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/M_Retail_White.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd9698b0577e35d145117f049777fd3218d602cff0f78c8ca7fa7965d08ad8ad +size 56694 diff --git a/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_6806.uasset b/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_6806.uasset new file mode 100644 index 0000000..9095828 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_6806.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d436f4ed1e6d1b922ab93b61d801296453e755133039cb68a24a768cf80fd9eb +size 93500 diff --git a/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_LeeDrug.uasset b/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_LeeDrug.uasset new file mode 100644 index 0000000..43664c0 --- /dev/null +++ b/Game/GigLine/Content/Hollywood/Frontage/SM_Retail_LeeDrug.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48acd6aa22718c98df68b913b0e9a1d258b33ccf9a59ba76439d5cbeaf7c3137 +size 96341 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset index a8aa8b3..fe6cb0f 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42651b3f341042ff5b59565061b40e10f742cb040ad32159cc2497baaa53241f -size 9818 +oid sha256:3fb76d8de906114912d8d5fe6e5cccd5f661eaf7bd644f9d32a5a4a122bedc4d +size 10019 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset index 4990334..25741c9 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac79d443e78c66dfdd0e6dcc826acb516157bb3b099643ca46a48a11ade12fd1 -size 9823 +oid sha256:83c4ce0706040145c130664a8098614a8022e9deb814b4a281098cc81b340b4c +size 10024 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset index 96db4a4..64c183f 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed6a9b1306a3f54006dd09d33edea45635af4445f88f9b7b021a6be96226d0f8 -size 11054 +oid sha256:1926a4806a096dca5ac226f0104df86adfe5861639ca31159192e392619f7e0a +size 11419 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset index 674550a..2eee7c5 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8c25c5cf5e02e8ae7eddb42d6ab3ab5eb1868021ea6373a4e01676223daf7fe -size 11049 +oid sha256:1371c411f28b429836bea1000d8bd45317385e89fadb65229c6d5e30ad8deafa +size 11414 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset index b52bd8f..87483be 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee7e3321ede0f26ff779baa0dbfb1cba79682c00c3c604a57a6ff511df1e1a96 -size 16924 +oid sha256:3bce906bf91b38622e821768ec39b18e628e6362eb8f32c591c7896692662df6 +size 17168 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset index beb0987..947e285 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a63d50ddbe2b8674fa37ec42936e02ae20f604503966bb0eb9acd072c2061508 -size 16954 +oid sha256:90a31499a481fbb8efeb5d5f30aa5c1beb6ca3afa91c1a154e689e2a55c49345 +size 17198 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset index 79b8458..adc5f84 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f24e2319667b421615b371015edf38ae1645049a29a4ed07e79f8f780161b4ec -size 9813 +oid sha256:fe37d1ab7c4d2f3621f7993c1dfdb0f062398b589ee8a5c3aace444a9417f66e +size 10014 diff --git a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset index cc43899..0d2e7a9 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27dad21ae40278bc8f85b71f816b26f63cebf3032cf6f026dc827770ae70c112 -size 9843 +oid sha256:234d60830b619bc25f6ea9ddbcc192e462409629c5e17a121879569ee5cc01bb +size 10044 diff --git a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset index a9892bc..8363427 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd1f661a78dc559fe32a7eba5b8de1c3c11eb02db915c6af75698233064f3bfd -size 9828 +oid sha256:90ebf4aa1f81ee4935afa7b683d574ea07fffb6860cc2b3a8cedbed826888c4f +size 10029 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset index 9620116..40bde8e 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79afcede88edfbdbca48cc80d414f90824c1b3c29f4e0d6fa20bcfd83956add4 -size 11024 +oid sha256:288b925a6c4207632777e5c13190345ba5e028ad2e675e34c32c99815f68cecb +size 11389 diff --git a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset index 98a9441..ca32959 100644 --- a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60e5397f227314e5d59fd3783e9c31713db9ebcbdc29a34aa44cddb305d76ff9 +oid sha256:1d7eac1b72eb0165f70af81739f992156409a0be1d5f0423f9e66c1d0f2ed381 size 357901 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset index eba8aec..552ac30 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5d7a3acd47b38355b6903b962d6b938ff1f5ddc602923f8e944c29a4674dee2 +oid sha256:a071c19c111adaebfc466dd12bb94cb79510846a670720bd8e3df7b12b8ca051 size 70879 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset index 3bd6c08..f2c4f7a 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2454b2c9f7d9950d68af0402b8553acc3aa64aab1faee95fe71fe2b2efe4710e +oid sha256:53353dae2b3d37e609a7c79ce8c7c0ee1239386da5aa0ac16b5e4c1b177ad616 size 29011 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset index d463d4e..7a4d7d3 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:782166930730273c3bc82ac7be18f084436d65da79bcff316a4f171e77e6ef93 +oid sha256:65a2605304ecc6c11c8a7039411bb02d315e6dadbac8b8c76a4fe48c102b31a1 size 58568 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset index 27ff573..80e6b8e 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:805d8f4ad82682c0d2409cf46e8a62a65438a38073b4a307ff498b3de912a696 +oid sha256:41d659ccf84e1d3a7bfae39164958cbff7540660ae7066aafd061299e0bd05a0 size 17517 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset index ebb8dce..0eb1f34 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a0d33fee2e6475e7c0392c838a9d7b132c6c95db66f44f39247f1e6ea3ce456 +oid sha256:2c021ab82a3e28c055cc67b948646a168c60799388d6c4e8899fa844a9ae5fa9 size 40610 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset index c55cf87..253e4e0 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3ba7d4166902628d5ca37b7d6f164d8e4bf8a58990777a49fe3871640645c6c +oid sha256:1ae2847b7459749bf312fe1ca4876660ea14eaa8abfdf35f531948c96fb6733a size 18770 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset index 71ce6ce..b807bb7 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f435587872a1f21006d6cbc466b01728110b8574ed8384287f414bf332b62cd6 +oid sha256:d08632fa729287eec03db544758715677ed22dbb86ae31129d8f00579b895bc1 size 62746 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset index 7711f4e..d8db282 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92a72806044679f1ddccf944775d8c2c1a131006030c17480788c70404734d13 +oid sha256:543dc4b621d0bb3b5067d33aa503ddb00498465cceec1fa0c0082ee6c9ec8b92 size 17541 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset index d557461..474cbdb 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5152da3143f8b6b00f3e595d5adf07f65b260a2f32032140450a34538767f14e +oid sha256:5ab8c2326cb8d12c02a0d74896994dfd3d19974cb59da6487e41484399d5394d size 72330 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset index d18ceec..44a4f0b 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3a7c47afac32e8601e9ac4649c2e16663dfda8d409241527bd1d2c7bbd7c32e +oid sha256:43288f8565f631cfa95e372aa0914ad87e3e3b84bc03384882b778f53308f1a8 size 72466 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset index e0fdde9..ca163ef 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f135922b7e89057917176a516fd283f1b739e9c6960892b8838daabd67eecfe0 +oid sha256:2f2de2082fd77b5e67d4f5f3b17c488b1b66dcaf4de7817862044f3b7e128370 size 72406 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset index b09cfb1..f56ea37 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3aaf3835733ebdaed780d33e8ee6c5ef2047e7f53de03c2984aafcde75830aa +oid sha256:35245987ecb3264697558b7a0711326c2e7f7eb8376ebd047600c9091a5f99c3 size 72370 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset index bbf4643..ae74f6f 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e42c24ba5e098c001a4b7ef3a79ab7ca4077a6c29647a7f85037e8a7e97a07a +oid sha256:25fd1abced705fc871b794b7709c7b0cd183b6e18c014f9463902af9e3105927 size 72438 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset index f352bf4..e352273 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffdf61d494359fea1c003dda276fc8c25b943bca7b9dd209c41381705a1ca13f +oid sha256:5cd83749385ed136b18544597898dd364028126c1f5cb098580f0adf40086a66 size 72413 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 22da558..6edf4aa 100644 --- a/Game/GigLine/Content/Test/ReviewMap.umap +++ b/Game/GigLine/Content/Test/ReviewMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24297b54b7f130738e1b29acf2c806522a191d185fce1164e52da1d744d81bdd +oid sha256:53c99032f45546d5b87630d2f916ed038b632b068455aced623e4fae1f57ed99 size 28811 diff --git a/MEMORY.md b/MEMORY.md index de67cf7..9396193 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -523,3 +523,11 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO. - Precedent applied again: tag when the stated scope is done, carry blocked scope into the next patch rather than letting a release wait indefinitely on missing reference photography. + +## 2026-11-09 — Retail frontage and two pipeline traps + +- **`Tools/render_screenshots.sh` called `./Engine/...` for the capture step** while every commandlet above went through `run_cmdlet`'s `cd "$ENGINE"`. With `set -e` the script died at exit 127 *after* rebuilding the maps, so the PNGs silently stayed a day old and `--verify` never ran. Fixed to `"$ENGINE/Engine/Binaries/..."`. Symptom to remember: maps rebuild, screenshots do not move. +- **The generic façade kit is applied to every actor labelled `Frontage_*` or `North_*`**, so staging a hand-modelled façade under those labels gets the kit pasted over it — two contradicting façade languages on one wall. `ue_apply_facade_kit.py` now skips `MODELLED` labels, and retail trim stages as `RetailTrim_*`. +- **A Blender builder cannot be imported by a unit test** (`import bpy` at module scope). Pure layout arithmetic lives in `Tools/_common.py`; `Tools/tests/test_retail_frontage.py` tests it there, and the builder delegates. Same trick `fbx_export_preset.py` uses for its preset dicts. +- **Joining 85 parts into one mesh per building** before export cut the Unreal import from 85 assets to 2, and the staging step to two spawns. Record per-part dimensions *before* the join: after it only the joined bounds exist. + diff --git a/Screenshots/README.md b/Screenshots/README.md index f22ad24..093d18d 100644 --- a/Screenshots/README.md +++ b/Screenshots/README.md @@ -22,6 +22,7 @@ Each shot has an **expectation** in [`Tools/visual_targets.json`](../Tools/visua | 2 | `demo/review-demo-street-level.png` | eye height, north side | Street level: the hero colonnade with the adjacent frontage buildings either side, shopfronts at pavement level | ☑ 2026-11-09 | ☑ 2026-11-09 | | 3 | `demo/review-demo-crossing.png` | along the crosswalk | The designated mid-block crossing (s=130 m, UE X −14956): zebra bars on the asphalt, south frontage and colonnade behind | ☑ 2026-11-09 | ☑ 2026-11-09 | | 4 | `demo/review-demo-north-buildings.png` | across the Boulevard | The two north-side buildings (TCL Chinese Theatre, Madame Tussaud's) from the south side, full height in frame | ☑ 2026-11-09 | ☑ 2026-11-09 | +| 5 | `demo/review-demo-retail-frontage.png` | north sidewalk, level horizon | The retail run beside the hero: 6800 Lee Drug's bronze cornice, mural row and parapet next to 6806–6820's burgundy fascia, pilasters and window storey | ☑ 2026-11-09 | ☐ pending | Approval is per shot, in the table above (owner review of the `0.1.4` set, recorded in [`Docs/reviews/0.1.4-review.md`](../Docs/reviews/0.1.4-review.md)). Add a row when a shot is added; remove the file in the same change when a shot is retired. @@ -49,3 +50,20 @@ Note the comparison is only meaningful when the shot and the reference frame the - Banners sit on the column line rather than flush to the wall (they hang on the columns in the photographs, so this is acceptable). - Between kerb and building line the walkable surface is the terrain mesh, not a modelled pavement. - The El Capitan Theatre has no marquee or blade sign yet — the feature that makes it read as itself. + +## 0.1.6 retail frontage — what to look at + +Shot 5 is the review shot for this pass. Read it against +`Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg` (Gb321, CC BY-SA 4.0). + +Known gaps in this first pass, recorded rather than hidden: + +- **The parapet reads as a blank white band.** It is modelled as one 2.18 m wall with four + 0.10 m reveal strips, and at review distance the strips disappear into the wash. +- **The mural row and lettering band barely read.** They are shallow plates (0.06–0.08 m) on + the same plane as the wall; they need either depth or a decal before they read as the + photographs' black-framed panels. +- **The corner tower is a plain cylinder with two cap rings** — the stepped Deco profile in the + photograph is richer than that. +- **The camera crops the east end** of Lee Drug at 68° from the north sidewalk. +- **6822 Budget is untouched** and still a bare blockout volume: no photograph of it exists. diff --git a/Screenshots/demo/review-demo-crossing.png b/Screenshots/demo/review-demo-crossing.png index b49e26e..61b729a 100644 --- a/Screenshots/demo/review-demo-crossing.png +++ b/Screenshots/demo/review-demo-crossing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7729a7ef0b10c5827f3c5b4167e12ba03ca6aa81a8a1da0c55f9fb122897694 -size 2992299 +oid sha256:671fa6bdd2f90291619dde25e019b9ed2901b9195541e981f815ac7a0d12d0b6 +size 3090405 diff --git a/Screenshots/demo/review-demo-north-buildings.png b/Screenshots/demo/review-demo-north-buildings.png index c655726..6b60f6f 100644 --- a/Screenshots/demo/review-demo-north-buildings.png +++ b/Screenshots/demo/review-demo-north-buildings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a854cc82d6a1ad84173664076a076f649c8dfbf38bb7d887f7d6444a7dc4e57c -size 2417601 +oid sha256:63cfbd2cf6b1cceabfa712136e06b2009700f20d2e34664baa6ddc255e95a380 +size 2618666 diff --git a/Screenshots/demo/review-demo-retail-frontage.png b/Screenshots/demo/review-demo-retail-frontage.png new file mode 100644 index 0000000..0899709 --- /dev/null +++ b/Screenshots/demo/review-demo-retail-frontage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45d882e702e3e868e2870e34b78e3fe89be0a50c3d78a13eb09c74bf60648206 +size 2048484 diff --git a/Screenshots/demo/review-demo-spawn-view.png b/Screenshots/demo/review-demo-spawn-view.png index f868673..6f7b0dd 100644 --- a/Screenshots/demo/review-demo-spawn-view.png +++ b/Screenshots/demo/review-demo-spawn-view.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e06925a4ca759e762c3ad01375cc098e22fdab065878a85269a63d62b9ba7256 -size 2505879 +oid sha256:2015b86cb791f8e038004cd9d7510839475f3464518c121016eedf04a1850dad +size 2955383 diff --git a/Screenshots/demo/review-demo-street-level.png b/Screenshots/demo/review-demo-street-level.png index 21ad29f..2bbfae9 100644 --- a/Screenshots/demo/review-demo-street-level.png +++ b/Screenshots/demo/review-demo-street-level.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0412f034f6ac55928521c14278043f5626ce32ecaaca7e178ec3efd011efc22 -size 2705233 +oid sha256:5b20e4c0d58d9314a0d9577a5a86329e3d3a288f3ba876970c9a786ac82546a5 +size 2873893 diff --git a/TODO.md b/TODO.md index 12e367b..d1a83e0 100644 --- a/TODO.md +++ b/TODO.md @@ -15,7 +15,10 @@ Suggested blender skills: **references** (reference-image-match), **archviz**, * ### Patch tasks - active - session-5598ce7e-27e3-4c64-bb49-c04a4a31ee58 (branch `facade-retail-0.1.6`) -- [A] **Model 6822 Budget, 6806–6820 and 6800 Lee Drug from their own references.** `Art/reference/retail-neighbours/` holds 6800 and its neighbour (Gb321, CC BY-SA 4.0); Commons has nothing usable for 6822 or 6806–6820, so those two need photography before they can be modelled honestly. Do not substitute the El Capitan Theatre's 18-frame set — it would misattribute the façades. +- [A] **Model 6800 Lee Drug and 6806–6820 from their own reference.** First pass 2026-11-09 on branch `facade-retail-0.1.6`: `Tools/retail_frontage_build.py` builds both façades from the one photograph (`Art/reference/retail-neighbours/`, Gb321 CC BY-SA 4.0) with the analysis in `Art/blender/retail/ref-analysis.md`; + `Tools/ue_import_retail_detail.py` imports them and the demo builder stages them as `RetailTrim_*`. The generic façade kit is now skipped on these two, so their walls carry one façade language instead of two. + **Open on this item:** the parapet reads as a blank band, the mural row and lettering band do not resolve at review distance, the corner tower is a plain cylinder, and the review camera crops the east end — all listed in `Screenshots/README.md`. +- [D] **Model 6822 Budget.** Deferred: no photograph exists for it (Commons has nothing usable) and the single-storey 6.0 × 5.5 m volume cannot be read from the neighbour shot. Needs photography before it can be modelled honestly; do not substitute the El Capitan Theatre's set or the 6806–6820 shot. - [ ] Review every façade change with `Tools/render_screenshots.sh --verify` before tagging. - [ ] Tag `v0.1.6` and write `CHANGELOG.md` entry; then remove this section. diff --git a/Tools/README.md b/Tools/README.md index 0a54062..83018ef 100644 --- a/Tools/README.md +++ b/Tools/README.md @@ -17,7 +17,10 @@ Geographic tools live in [`Geo/tools/`](../Geo/README.md) and the Blender-side b | --- | --- | | `check-lfs.sh` | Fails if the `lfs` filter is missing or an LFS-tracked file was committed as a plain blob | | `facade_kit_build.py` | Builds the reusable façade **module kit** at the measured 4.96 m bay: pilaster, sill, lintel, window bay (surround + glazing), shopfront bay (piers, glass, fascia), door bay (jambs, door, threshold), cornice and parapet + coping. Exports each piece with the shared preset plus a JSON part report (dims + evidence per piece). | +| `ue_import_retail_detail.py` | Imports the two retail trim meshes into `/Game/Hollywood/Frontage` with complex-as-simple collision — the trim decorates a solid the player already collides with, so it must not add snag points. Staged by the demo builder as `RetailTrim_*`. | | `ue_import_facade_kit.py` | Imports the kit into `/Game/Hollywood/Kit/*` as static meshes with complex-as-simple collision; writes `/tmp/gigline/kit-import.txt`. | +| `retail_frontage_build.py` | Retail frontage trim for **6800 Lee Drug** and **6806–6820** (0.1.6): bronze cornice, mural row, lettering band, stepped Deco parapet and rounded corner tower on Lee Drug; burgundy fascia, pilasters, white-framed windows and banded parapet on 6806–6820. Built in world coordinates from the blockout bounds, one joined mesh per building, JSON part report with the evidence behind every piece. `6822 Budget` is **not** built — no photograph exists. | +| `retail_frontage_build.py` | Retail frontage trim for **6800 Lee Drug** and **6806–6820** (0.1.6): bronze cornice, mural row, lettering band, stepped Deco parapet and rounded corner tower on Lee Drug; burgundy fascia, pilasters, white-framed windows and banded parapet on 6806–6820. Built in world coordinates from the blockout bounds, one joined mesh per building, JSON part report with the evidence behind every piece. `6822 Budget` is **not** built — no photograph exists. | | `theatre_trim_build.py` | El Capitan Theatre trim: projecting marquee canopy, illuminated fascia, recessed soffit, vertical blade sign with frame and bracket. Built **in world coordinates** (span and pavement height baked in) with a JSON part report, so staging is a single spawn at the origin. | | `ue_stage_theatre_trim.py` | Imports the trim to `/Game/Hollywood/Theatre` and stages one actor per piece in the DemoMap with site materials; clears a previous run first. | | `ue_apply_facade_kit.py` | Lays the kit over a building's street face from its own bounding box: bay count = round(width / 4.96), storeys from the height, shopfronts on the ground floor, windows above, pilasters on bay boundaries, cornice and parapet at the top. Clears a previous application first; unique labels per building. | diff --git a/Tools/_common.py b/Tools/_common.py new file mode 100644 index 0000000..d1e4ac5 --- /dev/null +++ b/Tools/_common.py @@ -0,0 +1,27 @@ +"""Shared helpers that must stay importable outside Blender and Unreal. + +Blender-side builders import `bpy` at module scope, so nothing in them can be unit +tested. Pure layout arithmetic lives here instead: the builders import it, and +`Tools/tests/` imports it without a 3D application. + +Standard library only. +""" + +from __future__ import annotations + + +def bay_centres(x0, x1, margin=0.9, bay=2.2): + """Centres of equal bays across a frontage, inset by `margin` at each end. + + Used by the façade kit and the retail frontage so both use one rhythm. The count + is the span divided by the bay module and rounded, so a 33 m frontage at a 2.2 m + module gets 14 bays rather than one bay per 2.2 m of wall with a stub at the end. + + Returns [] when the margins consume the whole frontage. + """ + span = (x1 - x0) - 2.0 * margin + if span <= 0: + return [] + count = max(1, int(round(span / bay))) + step = span / count + return [x0 + margin + step * (i + 0.5) for i in range(count)] diff --git a/Tools/render_screenshots.sh b/Tools/render_screenshots.sh index 1d34493..cec9181 100755 --- a/Tools/render_screenshots.sh +++ b/Tools/render_screenshots.sh @@ -39,7 +39,10 @@ run_cmdlet ue_build_review_map.py "5/6 stage the demo shot cameras" tail -3 /tmp/ue-review-map-result.txt 2>/dev/null || true echo "== 6/6 capture the demo map ==" -./Engine/Binaries/Linux/UnrealEditor-Cmd "$PROJ" /Game/Hollywood/DemoMap \ +# The engine path is absolute: this step runs from the repo, not from $ENGINE, and a +# bare ./Engine/... failed with "No such file or directory" while every commandlet +# above succeeded through run_cmdlet's cd. +"$ENGINE/Engine/Binaries/Linux/UnrealEditor-Cmd" "$PROJ" /Game/Hollywood/DemoMap \ -game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -stdout echo "== collect the demo screenshots ==" diff --git a/Tools/retail_frontage_build.py b/Tools/retail_frontage_build.py new file mode 100644 index 0000000..64a740d --- /dev/null +++ b/Tools/retail_frontage_build.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +"""Retail frontage trim for 6800 Lee Drug and 6806–6820 (0.1.6). + +Both buildings are bare OSM extrusions with a flat stucco material; what makes them +read as *the* retail run beside the hero is the façade layer: the bronze cornice and +stepped Deco parapet on Lee Drug, and the pilaster-and-fascia rhythm on 6806–6820. + +The trim is built **in world coordinates** in the shared frame (metres, X east, +Y north, Z from the project datum), so the export carries its position and the +Unreal staging step is a single spawn at the origin — the same convention as +`Tools/theatre_trim_build.py` and `Tools/export_block_frontage.py`. + + blender -b --factory-startup --python Tools/retail_frontage_build.py -- \ + --out /tmp/gigline/retail --report /tmp/gigline/retail-report.json + +Bounds are **measured**, not assumed: they are the blockout objects' world bounds +(`Lee Drug Building`, `element.019`), which sit at Z 0 with the volume top at 12.10 m. +Vertical proportions come from the single reference photograph and are recorded with +the assumption in `Art/blender/retail/ref-analysis.md`. + +6822 Budget is deliberately **not** built: there is no photograph of it, and the TODO +item says so. Guessing its façade would be the misattribution the item warns against. +""" + +from __future__ import annotations + +import argparse +import json +import math +import os +import sys + +import bpy + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import _common # noqa: E402 +import fbx_export_preset as fbx_preset # noqa: E402 + +# --- measured bounds, from the blockout (world metres) ------------------------ +# Lee Drug 6800, OSM way 424024149. Front face is the north edge (max Y). +LD_X0, LD_X1 = -33.20, -14.37 +LD_FACE_Y = -14.63 +LD_H = 12.10 + +# 6806-6820, OSM way 424024150. +B6_X0, B6_X1 = -66.08, -33.07 +B6_FACE_Y = -12.52 +B6_H = 12.10 + +# --- proportions, read off the reference photograph --------------------------- +# Fractions of the 12.10 m height; see Art/blender/retail/ref-analysis.md for how +# they were read and why they are assumptions at ±0.03 H. +LD_SHOP_TOP = 0.36 * LD_H # 4.36 m: shopfront glazing / cornice soffit +LD_CORNICE_TOP = 0.43 * LD_H # 5.20 m: top of the bronze band +LD_MURAL_Z = (0.49 * LD_H, 0.61 * LD_H) +LD_LETTER_Z = (0.65 * LD_H, 0.76 * LD_H) +LD_PARAPET_Z = 0.82 * LD_H # 9.92 m: bottom of the stepped parapet + +B6_SHOP_TOP = 0.32 * B6_H # 3.87 m +B6_FASCIA_TOP = 0.38 * B6_H # 4.60 m +B6_WINDOW_Z = (0.45 * B6_H, 0.60 * B6_H) +B6_PARAPET_Z = 0.62 * B6_H # 7.50 m + +BAY = 2.20 # mural / bay rhythm on Lee Drug, assumed +PROJECT = 0.55 # bronze cornice projection, assumed +TOWER_R = 2.10 # Lee Drug corner tower radius, assumed + +PARTS: list[dict] = [] + + +def make_material(name, rgb, rough=0.6, metallic=0.0): + """Principled material with an explicit colour; the export preset carries it as a slot.""" + mat = bpy.data.materials.new(name) + mat.use_nodes = True + bsdf = mat.node_tree.nodes.get("Principled BSDF") + bsdf.inputs["Base Color"].default_value = (rgb[0], rgb[1], rgb[2], 1.0) + bsdf.inputs["Roughness"].default_value = rough + bsdf.inputs["Metallic"].default_value = metallic + return mat + + +def clear_scene(): + bpy.ops.object.select_all(action="SELECT") + bpy.ops.object.delete(use_global=False) + for mesh in list(bpy.data.meshes): + if mesh.users == 0: + bpy.data.meshes.remove(mesh) + + +def cube(name, x, y, z, dx, dy, dz, material): + bpy.ops.mesh.primitive_cube_add(size=1.0, location=(x, y, z)) + obj = bpy.context.active_object + obj.name = name + obj.scale = (dx, dy, dz) + bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + obj.data.materials.append(material) + return obj + + +def cylinder(name, x, y, z, radius, height, material, vertices=28): + bpy.ops.mesh.primitive_cylinder_add(vertices=vertices, radius=radius, depth=height, + location=(x, y, z)) + obj = bpy.context.active_object + obj.name = name + bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + obj.data.materials.append(material) + return obj + + +def bay_centres(x0, x1, margin=0.9, bay=BAY): + """Bay centres; the arithmetic lives in `_common` so it is testable without Blender.""" + return _common.bay_centres(x0, x1, margin=margin, bay=bay) + + +def note(obj, evidence): + PARTS.append({ + "object": obj.name, + "asset": "SM_" + obj.name, + "dims_m": [round(d, 3) for d in obj.dimensions], + "location_m": [round(c, 3) for c in obj.location], + "evidence": evidence, + }) + print(f"[retail] {obj.name:26} {obj.dimensions.x:6.2f} x {obj.dimensions.y:5.2f} " + f"x {obj.dimensions.z:5.2f} m ({evidence})") + + +def build_lee_drug(mats): + """6800 Lee Drug: bronze cornice, mural row, lettering band, stepped Deco parapet, + rounded corner tower.""" + stucco, bronze, mural, glass, white = (mats["stucco"], mats["bronze"], mats["mural"], + mats["glass"], mats["white"]) + parts = [] + cx = (LD_X0 + LD_X1) / 2.0 + width = LD_X1 - LD_X0 + + # Bronze cornice: the strongest horizontal line on the building. + cornice = cube("LD_Cornice", cx, LD_FACE_Y + PROJECT / 2.0, + (LD_SHOP_TOP + LD_CORNICE_TOP) / 2.0, + width + 0.4, PROJECT, LD_CORNICE_TOP - LD_SHOP_TOP, bronze) + parts.append((cornice, "photographs: projecting bronze cornice over the shopfront; " + f"{PROJECT:.2f} m projection assumed")) + + # Shopfront glazing, recessed behind the face, one panel per bay. + for i, bx in enumerate(bay_centres(LD_X0, LD_X1, margin=1.2), start=1): + panel = cube(f"LD_ShopGlass_{i}", bx, LD_FACE_Y + 0.12, LD_SHOP_TOP / 2.0, + BAY - 0.30, 0.10, LD_SHOP_TOP - 0.55, glass) + parts.append((panel, "photographs: full-height shopfront glazing; bay width and " + "the 0.55 m kickplate are assumed (a bus hides this zone)")) + mullion = cube(f"LD_Mullion_{i}", bx - (BAY - 0.30) / 2.0, LD_FACE_Y - 0.02, + LD_SHOP_TOP / 2.0, 0.10, 0.16, LD_SHOP_TOP - 0.55, bronze) + parts.append((mullion, "assumption: slim mullion at each bay boundary")) + + # Celebrity-portrait panels, one per bay, in the band above the cornice. + z0, z1 = LD_MURAL_Z + for i, bx in enumerate(bay_centres(LD_X0, LD_X1, margin=1.2), start=1): + panel = cube(f"LD_Mural_{i}", bx, LD_FACE_Y - 0.04, (z0 + z1) / 2.0, + BAY - 0.75, 0.08, z1 - z0, mural) + parts.append((panel, "photographs: row of black-framed portrait panels; count and " + "size assumed from the visible row")) + + # Lettering band: a recessed plate, not text. Reproducing the lettering needs a + # decal, which is a materials-pass job. + lz0, lz1 = LD_LETTER_Z + letter = cube("LD_LetterBand", cx, LD_FACE_Y - 0.03, (lz0 + lz1) / 2.0, + width - 3.0, 0.06, lz1 - lz0, mural) + parts.append((letter, "photographs: large dark lettering across the upper wall; " + "modelled as a recessed plate, the text itself is a decal job")) + + # Stepped Deco parapet: wall band plus four horizontal reveals. + para = cube("LD_Parapet", cx, LD_FACE_Y + 0.10, (LD_PARAPET_Z + LD_H) / 2.0, + width + 0.2, 0.30, LD_H - LD_PARAPET_Z, stucco) + parts.append((para, "photographs: parapet steps above the frontage wall; " + "thickness assumed")) + for i in range(4): + z = LD_PARAPET_Z + (LD_H - LD_PARAPET_Z) * (i + 1) / 5.0 + groove = cube(f"LD_ParapetReveal_{i + 1}", cx, LD_FACE_Y - 0.03, z, + width + 0.2, 0.07, 0.10, mural) + parts.append((groove, "photographs: four horizontal reveal lines in the parapet; " + "count read off the frame")) + + # Rounded corner tower at the east end, stepped twice at the top. The centre + # sits INSIDE the footprint so the tower projects 0.35 m from the face instead of + # bulging 4 m into the pavement, which the first run did. + tower_y = LD_FACE_Y - TOWER_R + 0.35 + tower = cylinder("LD_CornerTower", LD_X1 - TOWER_R, tower_y, + (LD_H - 0.9) / 2.0, TOWER_R, LD_H - 0.9, stucco) + parts.append((tower, "photographs: rounded Deco corner tower rising above the " + f"frontage; {TOWER_R:.2f} m radius assumed")) + # Cap rings stack exactly to the measured 12.10 m top: 11.20 -> 11.75 -> 12.10. + cap_z = LD_H - 0.9 + for i, (dh, radius) in enumerate(((0.55, TOWER_R - 0.35), (0.35, TOWER_R - 0.70)), start=1): + cap = cylinder(f"LD_TowerCap_{i}", LD_X1 - TOWER_R, tower_y, + cap_z + dh / 2.0, radius, dh, white) + parts.append((cap, "photographs: two stepped cap rings on the tower")) + cap_z += dh + return parts + + +def build_6806(mats): + """6806–6820: burgundy fascia, brown pilasters, white-framed windows, banded parapet.""" + stucco, bronze, glass, white, burgundy = (mats["stucco"], mats["bronze"], mats["glass"], + mats["white"], mats["burgundy"]) + parts = [] + cx = (B6_X0 + B6_X1) / 2.0 + width = B6_X1 - B6_X0 + + fascia = cube("B6_Fascia", cx, B6_FACE_Y - 0.02, + (B6_SHOP_TOP + B6_FASCIA_TOP) / 2.0, width, 0.22, + B6_FASCIA_TOP - B6_SHOP_TOP, burgundy) + parts.append((fascia, "photographs: dark fascia band over the shopfront; colour " + "sampled, height assumed")) + + centres = bay_centres(B6_X0, B6_X1, margin=1.0) + for i, bx in enumerate(centres, start=1): + panel = cube(f"B6_ShopGlass_{i}", bx, B6_FACE_Y + 0.12, B6_SHOP_TOP / 2.0, + BAY - 0.25, 0.10, B6_SHOP_TOP - 0.45, glass) + parts.append((panel, "photographs: framed shopfront glazing behind the fascia")) + # Brown pilaster on every second bay boundary, as the photo shows. + if i % 2 == 1: + pil = cube(f"B6_Pilaster_{i}", bx, B6_FACE_Y - 0.10, + (B6_FASCIA_TOP + B6_PARAPET_Z) / 2.0, 0.40, 0.22, + B6_PARAPET_Z - B6_FASCIA_TOP, bronze) + parts.append((pil, "photographs: brown pilaster strips dividing the frontage")) + + wz0, wz1 = B6_WINDOW_Z + for i, bx in enumerate(centres, start=1): + frame = cube(f"B6_WindowFrame_{i}", bx, B6_FACE_Y - 0.06, (wz0 + wz1) / 2.0, + 1.70, 0.14, wz1 - wz0, white) + parts.append((frame, "photographs: white-framed upper windows with security bars; " + "count and size assumed")) + pane = cube(f"B6_WindowGlass_{i}", bx, B6_FACE_Y + 0.02, (wz0 + wz1) / 2.0, + 1.46, 0.08, wz1 - wz0 - 0.24, glass) + parts.append((pane, "photographs: dark glazing inside each frame")) + + para = cube("B6_Parapet", cx, B6_FACE_Y + 0.08, (B6_PARAPET_Z + B6_H) / 2.0, + width + 0.2, 0.26, B6_H - B6_PARAPET_Z, stucco) + parts.append((para, "photographs: parapet band above the window storey; thickness assumed")) + for i in range(3): + z = B6_PARAPET_Z + (B6_H - B6_PARAPET_Z) * (i + 1) / 4.0 + groove = cube(f"B6_ParapetReveal_{i + 1}", cx, B6_FACE_Y - 0.03, z, + width + 0.2, 0.07, 0.09, bronze) + parts.append((groove, "photographs: three reveal lines in the parapet; count assumed")) + return parts + + +def join_parts(parts, name): + """Join one building's parts into a single mesh so Unreal imports one asset. + + The per-part dimensions and evidence are recorded before this runs, so joining + loses nothing from the report. Materials survive as separate slots. + """ + bpy.ops.object.select_all(action="DESELECT") + for obj, _text in parts: + obj.select_set(True) + bpy.context.view_layer.objects.active = parts[0][0] + bpy.ops.object.join() + joined = bpy.context.active_object + joined.name = name + bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + return joined + + +def export_objects(objects, out_dir): + count = 0 + for obj in objects: + bpy.ops.object.select_all(action="DESELECT") + obj.select_set(True) + bpy.context.view_layer.objects.active = obj + fbx_preset.export_static(os.path.join(out_dir, "SM_" + obj.name + ".fbx"), + selection=True) + count += 1 + return count + + +def main(argv=None) -> int: + argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--out", default="/tmp/gigline/retail") + ap.add_argument("--report", default=None) + args = ap.parse_args(argv) + os.makedirs(args.out, exist_ok=True) + + clear_scene() + mats = { + "stucco": make_material("M_Retail_Stucco", (0.86, 0.83, 0.76), rough=0.72), + "bronze": make_material("M_Retail_Bronze", (0.20, 0.11, 0.06), rough=0.45, metallic=0.35), + "mural": make_material("M_Retail_Mural", (0.035, 0.032, 0.030), rough=0.55), + "glass": make_material("M_Retail_Glass", (0.035, 0.045, 0.055), rough=0.15), + "white": make_material("M_Retail_White", (0.90, 0.88, 0.84), rough=0.60), + "burgundy": make_material("M_Retail_Burgundy", (0.20, 0.09, 0.09), rough=0.55), + } + + ld_parts = build_lee_drug(mats) + b6_parts = build_6806(mats) + for obj, text in ld_parts + b6_parts: + note(obj, text) + + # One mesh per building: 85 separate actors over the frontage would cost more + # draw calls than the trim is worth, and the staging step stays two spawns. + joined = [join_parts(ld_parts, "Retail_LeeDrug"), join_parts(b6_parts, "Retail_6806")] + for obj in joined: + print(f"[retail] joined {obj.name}: {len(obj.data.vertices)} verts, " + f"{len(obj.data.materials)} material slot(s), " + f"X {min(v.co.x for v in obj.data.vertices):.2f}.." + f"{max(v.co.x for v in obj.data.vertices):.2f}") + exported = export_objects(joined, args.out) + print(f"[retail] exported {exported} object(s) to {args.out}") + + if args.report: + with open(args.report, "w", encoding="utf-8") as handle: + json.dump({ + "scope": ["6800 Lee Drug (way 424024149)", "6806-6820 (way 424024150)"], + "deferred": ["6822 Budget (way 424024144): no photograph, not modelled"], + "bounds_m": { + "lee_drug": [LD_X0, LD_X1, LD_FACE_Y, LD_H], + "b6806": [B6_X0, B6_X1, B6_FACE_Y, B6_H], + }, + "reference": "Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg " + "(Gb321, CC BY-SA 4.0)", + "analysis": "Art/blender/retail/ref-analysis.md", + "parts": PARTS, + }, handle, indent=2) + print(f"[retail] report -> {args.report}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Tools/tests/test_retail_frontage.py b/Tools/tests/test_retail_frontage.py new file mode 100644 index 0000000..412ec1b --- /dev/null +++ b/Tools/tests/test_retail_frontage.py @@ -0,0 +1,32 @@ +"""Tests for the pure layout helper in Tools/retail_frontage_build.py. + +The builder itself needs Blender; `bay_centres` does not, so the bay rhythm the +façade depends on is checkable without starting Blender or Unreal. +""" + +import unittest + +from Tools._common import bay_centres + + +class BayCentreTests(unittest.TestCase): + def test_centres_stay_inside_the_margins(self): + centres = bay_centres(-33.20, -14.37, margin=1.2) + self.assertGreaterEqual(min(centres), -33.20 + 1.2 - 1e-9) + self.assertLessEqual(max(centres), -14.37 - 1.2 + 1e-9) + + def test_bays_are_evenly_spaced(self): + centres = bay_centres(-66.08, -33.07, margin=1.0) + steps = {round(b - a, 6) for a, b in zip(centres, centres[1:])} + self.assertEqual(len(steps), 1, f"uneven bay spacing: {steps}") + + def test_bay_count_is_the_round_of_span_over_bay(self): + # 33.01 m frontage, 1.0 m margin each end -> 31.01 m -> 14 bays at 2.2 m. + self.assertEqual(len(bay_centres(-66.08, -33.07, margin=1.0)), 14) + + def test_degenerate_frontage_returns_nothing(self): + self.assertEqual(bay_centres(0.0, 1.0, margin=1.0), []) + + +if __name__ == "__main__": + unittest.main() diff --git a/Tools/ue_apply_facade_kit.py b/Tools/ue_apply_facade_kit.py index 42c5091..0cf9ac7 100644 --- a/Tools/ue_apply_facade_kit.py +++ b/Tools/ue_apply_facade_kit.py @@ -235,9 +235,17 @@ def main() -> None: removed += 1 log(f"cleared {removed} previous kit actors") + # The kit is the fallback for buildings that have nothing but a blockout. These + # two are modelled from their own photograph in 0.1.6 + # (Tools/retail_frontage_build.py); pasting the generic kit over them put a + # second, contradicting facade language on the same wall. + MODELLED = ("Frontage_SM_LeeDrug_6800", "Frontage_SM_Hollywood_6806") targets = [a for a in unreal.EditorLevelLibrary.get_all_level_actors() - if a.get_actor_label().startswith(("Frontage_", "North_"))] - log(f"frontage actors: {len(targets)}") + if a.get_actor_label().startswith(("Frontage_", "North_")) + and a.get_actor_label() not in MODELLED] + skipped = [a.get_actor_label() for a in unreal.EditorLevelLibrary.get_all_level_actors() + if a.get_actor_label() in MODELLED] + log(f"frontage actors: {len(targets)} (kit skipped on modelled facades: {skipped})") total = 0 for actor in targets: total += apply_to_actor(actor, kit, materials) diff --git a/Tools/ue_build_demo_map.py b/Tools/ue_build_demo_map.py index 4a57c66..b325ddc 100644 --- a/Tools/ue_build_demo_map.py +++ b/Tools/ue_build_demo_map.py @@ -597,6 +597,25 @@ def build(): staged_frontage += 1 log(f"staged {staged_frontage} frontage buildings") + # --- retail frontage trim (0.1.6) ------------------------------------- + # The two retail buildings beside the hero are modelled from their own + # photograph (Art/reference/retail-neighbours/); the trim is laid over the + # blockout volume, so it keeps the trim's own sampled materials rather than a + # look-dev override. 6822 Budget has no photograph and stays a bare volume. + for asset_name, label in (("SM_Retail_LeeDrug", "6800 Lee Drug trim"), + ("SM_Retail_6806", "6806-6820 trim")): + mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Frontage/{asset_name}") + if mesh is None: + log(f"MISSING trim {asset_name} — run Tools/ue_import_retail_detail.py") + continue + # RetailTrim_ rather than Frontage_ so the kit assembler leaves it alone. + actor = spawn(unreal.StaticMeshActor, unreal.Vector(0, 0, 0), f"RetailTrim_{asset_name}") + if actor: + comp = actor.static_mesh_component + comp.set_static_mesh(mesh) + comp.set_mobility(unreal.ComponentMobility.MOVABLE) + log(f"staged: {label}") + # --- player start: north sidewalk, facing the hero frontage ------------- # Signs were measured from the imported meshes rather than assumed: the FBX # preset flips north, so in Unreal the road spans Y -1382..+1047, the NORTH diff --git a/Tools/ue_build_review_map.py b/Tools/ue_build_review_map.py index cf570bc..375f0d8 100644 --- a/Tools/ue_build_review_map.py +++ b/Tools/ue_build_review_map.py @@ -300,6 +300,12 @@ try: # on the north sidewalk looking along the crosswalk itself, which is # the pedestrian's view and shows the zebra bars as bars. ("demo-crossing", unreal.Vector(-15250, -2500, 400), unreal.Vector(-14956, 400, 150)), + # 0.1.6 retail frontage: 6800 Lee Drug (X -3340..-1417, face Y +1467) + # and 6806-6820 (X -6618..-3297, face Y +1252). Stand on the north + # sidewalk at 2 m, look south with a level horizon so the parapets and + # the cornice line read as horizontals. 68 deg covers both frontages at + # this distance; the ends crop rather than the elevation leaning. + ("demo-retail-frontage", unreal.Vector(-4700, -2000, 200), unreal.Vector(-4700, 1400, 560), 68.0), # the two north-side background buildings (TCL, Tussaud's) seen from # the south across Hollywood Blvd. Probed world-space (2026-11-08): # TCL bbox X -22889..-18442, Y -9431..-1802; Tussaud's X -27071.. diff --git a/Tools/ue_import_retail_detail.py b/Tools/ue_import_retail_detail.py new file mode 100644 index 0000000..313646e --- /dev/null +++ b/Tools/ue_import_retail_detail.py @@ -0,0 +1,94 @@ +# Import the retail frontage trim (6800 Lee Drug, 6806–6820) into Unreal. +# +# blender -b --factory-startup --python Tools/retail_frontage_build.py -- \ +# --out /tmp/gigline/retail --report /tmp/gigline/retail-report.json +# /Engine/Binaries/Linux/UnrealEditor-Cmd -run=pythonscript \ +# -script=/Tools/ue_import_retail_detail.py -nullrhi -unattended -nopause -stdout +# +# Two meshes arrive, one per building, each already at its world position in the +# project frame (the FBX carries the offset), so staging spawns at the origin. +# Destination: /Game/Hollywood/Frontage/ — same folder as the block frontage +# so the demo builder stages them together. +# +# Result file: /tmp/gigline/retail-import.txt + +import glob +import os + +import unreal + +SRC = "/tmp/gigline/retail" +DEST = "/Game/Hollywood/Frontage" +RESULT = "/tmp/gigline/retail-import.txt" +lines = [] + + +def log(msg): + lines.append(str(msg)) + unreal.log(str(msg)) + + +def import_fbx(path, name): + task = unreal.AssetImportTask() + task.filename = path + task.destination_path = DEST + task.destination_name = name + task.automated = True + task.save = True + task.replace_existing = True + + ui = unreal.FbxImportUI() + ui.import_mesh = True + ui.import_materials = True + ui.import_textures = False + ui.import_as_skeletal = False + smd = ui.static_mesh_import_data + smd.import_uniform_scale = 1.0 + smd.combine_meshes = True + smd.convert_scene_unit = False + smd.force_front_x_axis = False + smd.normal_import_method = unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS + task.options = ui + unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) + + mesh = unreal.EditorAssetLibrary.load_asset(f"{DEST}/{name}") + if mesh is None: + log(f"MISSING after import: {DEST}/{name}") + return None + + # Trim is decoration on a solid the player already collides with: no collision + # of its own, otherwise the cornice and mullions become snag points. + try: + body = mesh.get_editor_property("body_setup") + body.set_editor_property("collision_trace_flag", + unreal.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE) + mesh.set_editor_property("body_setup", body) + except Exception as exc: # noqa: BLE001 + log(f"{name}: collision flag not set ({exc!r})") + + box = mesh.get_bounding_box() + mn, mx = box.min, box.max + log(f"{name:22} X {mn.x:8.0f}..{mx.x:8.0f} Y {mn.y:7.0f}..{mx.y:7.0f} " + f"Z {mn.z:5.0f}..{mx.z:5.0f} slots {len(mesh.get_editor_property('static_materials'))}") + return mesh + + +def main(): + files = sorted(glob.glob(os.path.join(SRC, "*.fbx"))) + if not files: + log(f"no FBX in {SRC}; run Tools/retail_frontage_build.py first") + return + for path in files: + import_fbx(path, os.path.splitext(os.path.basename(path))[0]) + unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True) + + +try: + main() +except Exception as exc: # noqa: BLE001 + import traceback + log(f"FAILED: {type(exc).__name__}: {exc}") + log(traceback.format_exc()) + +with open(RESULT, "w") as f: + f.write("\n".join(lines) + "\n") diff --git a/Tools/visual_targets.json b/Tools/visual_targets.json index c3e0665..3a9b9f2 100644 --- a/Tools/visual_targets.json +++ b/Tools/visual_targets.json @@ -48,6 +48,18 @@ "Art/reference/north-chinese/01-grauman-s-chinese-theatre-by-carol-highsmith.jpg" ], "expect": "The two north-side buildings (TCL Chinese Theatre, Madame Tussaud's) seen from across Hollywood Boulevard, full height in frame, daylight" + }, + { + "name": "demo-retail-frontage", + "shot": "Screenshots/demo/review-demo-retail-frontage.png", + "size": [ + 1920, + 1080 + ], + "references": [ + "Art/reference/retail-neighbours/01-lee-drug-and-neighbor.jpg" + ], + "expect": "The retail run beside the hero from across the street: 6800 Lee Drug's bronze cornice, mural row and stepped parapet next to 6806-6820's pilasters and window storey, daylight" } ] } \ No newline at end of file