feat(facades): 0.1.6 retail second pass — relief, material mapping, framing
What the first pass got wrong, and what changed: - The mural plates were placed 0.20 m INSIDE the solid blockout volume, so they never rendered. Nothing can be recessed into a solid we do not cut: the inset is now faked by a proud frame with a near-flush plate, and the row reads. - Detail sat 0.05 m off the wall, under a pixel of depth at 40 m. Trim planes now stand 0.10-0.20 m proud, and the parapet became two tiers with deeper reveals. - 6806's parapet started at 0.62 H and left a 4.6 m blank band; the window storey and parapet moved up to follow the photograph. - Security bars on the upper windows, twelve fluted fins on the corner tower. - The trim's stucco/white/glass slots are mapped onto the demo's look materials, so the walls sit in the same noise/texture family as the rest of the block; the colour slots sampled from the photograph stay as authored. - Review camera pulled back and widened to 66 deg so the east end is in frame. - The scene audit caught duplicate RetailTrim_ actors: the demo builder's clear list did not include the new label, so every rebuild stacked another copy. Still open and not fixable by geometry: wall lettering. A plate at 0.10 m does not read at 40 m, so it moves to the 0.9.0 decal route (TODO.md, Screenshots/README.md).
This commit is contained in:
1 parent
4132f42ca1
commit
3942e9a215
36 files changed
+136
-75
No files matched your search
@@ -58,8 +58,8 @@ 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
|
||||
B6_WINDOW_Z = (0.47 * B6_H, 0.64 * B6_H)
|
||||
B6_PARAPET_Z = 0.74 * B6_H # 8.95 m (first pass used 0.62 and left a 4.6 m blank band)
|
||||
|
||||
BAY = 2.20 # mural / bay rhythm on Lee Drug, assumed
|
||||
PROJECT = 0.55 # bronze cornice projection, assumed
|
||||
@@ -129,6 +129,7 @@ def build_lee_drug(mats):
|
||||
rounded corner tower."""
|
||||
stucco, bronze, mural, glass, white = (mats["stucco"], mats["bronze"], mats["mural"],
|
||||
mats["glass"], mats["white"])
|
||||
lettering, reveal = mats["lettering"], mats["reveal"]
|
||||
parts = []
|
||||
cx = (LD_X0 + LD_X1) / 2.0
|
||||
width = LD_X1 - LD_X0
|
||||
@@ -151,31 +152,49 @@ def build_lee_drug(mats):
|
||||
parts.append((mullion, "assumption: slim mullion at each bay boundary"))
|
||||
|
||||
# Celebrity-portrait panels, one per bay, in the band above the cornice.
|
||||
#
|
||||
# First pass modelled these as a shallow plate on the wall plane and they vanished
|
||||
# at review distance: no shadow, no edge. Now each panel is a near-black plate set
|
||||
# 0.14 m back inside a stucco frame that stands 0.10 m proud, so the sun casts a
|
||||
# shadow line down one side and the row reads as a row of pictures.
|
||||
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)
|
||||
frame = cube(f"LD_MuralFrame_{i}", bx, LD_FACE_Y + 0.20, (z0 + z1) / 2.0,
|
||||
BAY - 0.55, 0.12, (z1 - z0) + 0.30, white)
|
||||
parts.append((frame, "photographs: stucco frame around each portrait panel; "
|
||||
"0.30 m border assumed"))
|
||||
panel = cube(f"LD_Mural_{i}", bx, LD_FACE_Y + 0.12, (z0 + z1) / 2.0,
|
||||
BAY - 0.85, 0.06, (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.
|
||||
# Lettering band: the photographs show dark letters painted straight on the stucco,
|
||||
# so this is a slightly proud plate in a mid-dark tone rather than a black recess.
|
||||
# The words themselves remain a decal job; the plate only reserves the band.
|
||||
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)
|
||||
letter = cube("LD_LetterBand", cx, LD_FACE_Y + 0.10, (lz0 + lz1) / 2.0,
|
||||
width - 3.6, 0.10, lz1 - lz0, lettering)
|
||||
parts.append((letter, "photographs: large dark lettering across the upper wall; "
|
||||
"modelled as a recessed plate, the text itself is a decal job"))
|
||||
"modelled as a painted band, 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; "
|
||||
# Stepped Deco parapet. A single 2.18 m wall with 0.10 m reveals read as one blank
|
||||
# white band, so it is now two tiers with a 0.10 m step between them, and the
|
||||
# reveals are 0.22 m tall and cut 0.16 m deep - deep enough to hold a shadow.
|
||||
tier_h = (LD_H - LD_PARAPET_Z) / 2.0
|
||||
lower = cube("LD_ParapetLower", cx, LD_FACE_Y + 0.14, LD_PARAPET_Z + tier_h / 2.0,
|
||||
width + 0.2, 0.34, tier_h, stucco)
|
||||
parts.append((lower, "photographs: parapet lower tier above the frontage wall; "
|
||||
"thickness assumed"))
|
||||
upper = cube("LD_ParapetUpper", cx, LD_FACE_Y + 0.24, LD_PARAPET_Z + tier_h * 1.5,
|
||||
width - 3.2, 0.30, tier_h, stucco)
|
||||
parts.append((upper, "photographs: parapet upper tier, stepped back from the lower "
|
||||
"one; setback and length assumed"))
|
||||
for i, (z, span) in enumerate(((LD_PARAPET_Z + tier_h * 0.45, width + 0.2),
|
||||
(LD_PARAPET_Z + tier_h * 1.30, width + 0.2),
|
||||
(LD_H - 0.14, width - 3.2)), start=1):
|
||||
groove = cube(f"LD_ParapetReveal_{i}", cx, LD_FACE_Y - 0.02, z,
|
||||
span, 0.16, 0.22, reveal)
|
||||
parts.append((groove, "photographs: 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
|
||||
@@ -186,6 +205,16 @@ def build_lee_drug(mats):
|
||||
(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"))
|
||||
# Vertical fins around the curve: the photograph's tower is fluted, not a smooth
|
||||
# drum. Twelve ribs at this radius are legible from the street.
|
||||
for i in range(12):
|
||||
ang = math.pi * (i + 0.5) / 12.0
|
||||
fx = (LD_X1 - TOWER_R) + math.cos(ang) * (TOWER_R + 0.06)
|
||||
fy = tower_y + math.sin(ang) * (TOWER_R + 0.06)
|
||||
fin = cube(f"LD_TowerFin_{i + 1}", fx, fy, (LD_H - 0.9) / 2.0,
|
||||
0.16, 0.16, LD_H - 0.9, white)
|
||||
parts.append((fin, "photographs: fluted fins on the corner tower; count 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):
|
||||
@@ -200,6 +229,7 @@ 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"])
|
||||
reveal = mats["reveal"]
|
||||
parts = []
|
||||
cx = (B6_X0 + B6_X1) / 2.0
|
||||
width = B6_X1 - B6_X0
|
||||
@@ -224,22 +254,36 @@ def build_6806(mats):
|
||||
|
||||
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)
|
||||
frame = cube(f"B6_WindowFrame_{i}", bx, B6_FACE_Y - 0.08, (wz0 + wz1) / 2.0,
|
||||
1.70, 0.18, 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,
|
||||
pane = cube(f"B6_WindowGlass_{i}", bx, B6_FACE_Y + 0.04, (wz0 + wz1) / 2.0,
|
||||
1.46, 0.08, wz1 - wz0 - 0.24, glass)
|
||||
parts.append((pane, "photographs: dark glazing inside each frame"))
|
||||
# Security bars: three thin verticals per window, the detail that stops the
|
||||
# upper storey reading as a row of black rectangles.
|
||||
for b in (-1, 0, 1):
|
||||
bar = cube(f"B6_WindowBar_{i}_{b + 2}", bx + b * 0.40, B6_FACE_Y - 0.15,
|
||||
(wz0 + wz1) / 2.0, 0.06, 0.06, wz1 - wz0 - 0.30, white)
|
||||
parts.append((bar, "photographs: vertical security bars on the upper windows"))
|
||||
|
||||
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"))
|
||||
# Two-tier parapet with deep reveals, as on Lee Drug: one flat band with hairlines
|
||||
# disappeared at review distance.
|
||||
tier_h = (B6_H - B6_PARAPET_Z) / 2.0
|
||||
para = cube("B6_Parapet", cx, B6_FACE_Y + 0.12, B6_PARAPET_Z + tier_h / 2.0,
|
||||
width + 0.2, 0.30, tier_h, stucco)
|
||||
parts.append((para, "photographs: parapet lower tier above the window storey; "
|
||||
"thickness assumed"))
|
||||
upper = cube("B6_ParapetUpper", cx, B6_FACE_Y + 0.20, B6_PARAPET_Z + tier_h * 1.5,
|
||||
width - 2.4, 0.26, tier_h, stucco)
|
||||
parts.append((upper, "photographs: parapet upper tier stepped back; length assumed"))
|
||||
for i, (z, span) in enumerate(((B6_PARAPET_Z + tier_h * 0.45, width + 0.2),
|
||||
(B6_PARAPET_Z + tier_h * 1.30, width + 0.2),
|
||||
(B6_H - 0.12, width - 2.4)), start=1):
|
||||
groove = cube(f"B6_ParapetReveal_{i}", cx, B6_FACE_Y - 0.02, z,
|
||||
span, 0.14, 0.18, reveal)
|
||||
parts.append((groove, "photographs: reveal lines in the parapet; count assumed"))
|
||||
return parts
|
||||
|
||||
|
||||
@@ -289,6 +333,10 @@ def main(argv=None) -> int:
|
||||
"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),
|
||||
# Painted lettering and shadow reveals: same value family as the wall so they
|
||||
# read as paint and shadow, not as black holes.
|
||||
"lettering": make_material("M_Retail_Lettering", (0.30, 0.27, 0.24), rough=0.65),
|
||||
"reveal": make_material("M_Retail_Reveal", (0.42, 0.38, 0.34), rough=0.70),
|
||||
}
|
||||
|
||||
ld_parts = build_lee_drug(mats)
|
||||
|
||||
@@ -305,7 +305,8 @@ def build():
|
||||
log(f"could not create {MAP}")
|
||||
return
|
||||
|
||||
removed = clear(("Demo_", "PlayerStart", "Hero_Facade", "Frontage_", "ShotSequencer"))
|
||||
removed = clear(("Demo_", "PlayerStart", "Hero_Facade", "Frontage_", "RetailTrim_",
|
||||
"ShotSequencer"))
|
||||
log(f"cleared {removed} previous demo actors")
|
||||
|
||||
# --- lighting: fixed daytime ------------------------------------------
|
||||
@@ -614,7 +615,19 @@ def build():
|
||||
comp = actor.static_mesh_component
|
||||
comp.set_static_mesh(mesh)
|
||||
comp.set_mobility(unreal.ComponentMobility.MOVABLE)
|
||||
log(f"staged: {label}")
|
||||
# Map the trim's own slots onto the look-dev materials where the surface
|
||||
# type matches, so the walls sit in the same noise/texture family as the
|
||||
# rest of the block. The trim's colour slots (bronze, burgundy, mural,
|
||||
# lettering) stay as authored: those tones came from the photograph.
|
||||
TRIM_LOOK = {"M_Retail_Stucco": "M_Stucco", "M_Retail_White": "M_Stucco",
|
||||
"M_Retail_Glass": "M_FacadeGlass"}
|
||||
mapped = 0
|
||||
for index, slot in enumerate(mesh.get_editor_property("static_materials")):
|
||||
target = TRIM_LOOK.get(str(slot.get_editor_property("material_slot_name")))
|
||||
if target and target in built:
|
||||
comp.set_material(index, built[target])
|
||||
mapped += 1
|
||||
log(f"staged: {label} ({mapped} slot(s) onto look materials)")
|
||||
|
||||
# --- player start: north sidewalk, facing the hero frontage -------------
|
||||
# Signs were measured from the imported meshes rather than assumed: the FBX
|
||||
|
||||
@@ -305,7 +305,7 @@ try:
|
||||
# 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),
|
||||
("demo-retail-frontage", unreal.Vector(-4700, -2600, 210), unreal.Vector(-4700, 1400, 620), 66.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..
|
||||
|
||||
Reference in new issue
Block a user