feat(facades): 0.1.6 retail frontage first pass — Lee Drug and 6806-6820
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.
This commit is contained in:
1 parent
4e703fcfd9
commit
2614d259aa
54 files changed
+715
-50
No files matched your search
@@ -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.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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.
|
||||
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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)]
|
||||
@@ -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 =="
|
||||
|
||||
@@ -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())
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..
|
||||
|
||||
@@ -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>/Engine/Binaries/Linux/UnrealEditor-Cmd <uproject> -run=pythonscript \
|
||||
# -script=<repo>/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/<name> — 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")
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user