Give the Joke Shop a dark stucco wall and a photo-anchored concept
Full build / Import + headless tests (push) Successful in 48s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-03 16:22:20 +01:00
1 parent 36583921cf
commit 634b1f75a3
14 files changed
+56 -13

No files matched your search

+6 -6
View File
@@ -393,12 +393,12 @@ facade evidence is still the 2009 CC BY frame. The 8 downloaded candidates are i
- [ ] Run the assisted Blender pipeline end to end on the first block (OSM import → `inspect` → - [ ] Run the assisted Blender pipeline end to end on the first block (OSM import → `inspect` →
retopo/UV assist → atlas → `audit`), and document the exact commands in retopo/UV assist → atlas → `audit`), and document the exact commands in
`docs/asset-tooling.md`. `docs/asset-tooling.md`.
- [-] Joke Shop building-appearance pass, same evidence model as the masonic facade: generate a - [x] Joke Shop building-appearance pass: `dark_stucco` tile (1024 px) promoted via the picks
dark stucco wall tile reference-anchored on the public-domain frontage photo (PD allows pipeline and wired into `_build_comedy_store_building`, replacing the grey paver tile so the
derived work without attribution conditions), generate a photo-anchored day concept for the hall reads as flat matte charcoal instead of masonry; photo-anchored day concept generated
three-up board, wire the tile behind `VENUE_SPECS`, then recapture and rebuild the Joke Shop from the hillside PD frame (the branded frontage photo trips the model's image-recitation
day board. Verify texel density stays inside the 40–400 px/m band and the signature fascia filter — recorded in the concept README); day capture promoted and the three-up board rebuilt.
+ red curtain bays still read against the reference. Test asserts the hall wall carries `dark_stucco`.
- [x] Building-appearance realism pass, measured against the reference set: bring the masonic - [x] Building-appearance realism pass, measured against the reference set: bring the masonic
facade's material response (limestone value/roughness, joint lines, weathering streaks), facade's material response (limestone value/roughness, joint lines, weathering streaks),
the banner/canopy and marquee materials, and the night emissive balance to the level of the the banner/canopy and marquee materials, and the night emissive balance to the level of the
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

+1 -1
View File
@@ -15,4 +15,4 @@ attribution lives in the caption and the [refs sidecars](../refs/README.md)).
| --- | --- | | --- | --- |
| ![three-up board: elcapitan day](elcapitan-day-board.jpg) | El Capitan facade, day. Photo: [CC BY 2.0, woo, 2009](https://commons.wikimedia.org/wiki/File:El_Capitan_Theater_frontal_fachade-The_Masonic_Temple.jpg) via `refs/hollywood`. Concept: photo-anchored generation. Capture: current build with the ashlar limestone field (`limestone_ashlar`, anchored on the same photo). | | ![three-up board: elcapitan day](elcapitan-day-board.jpg) | El Capitan facade, day. Photo: [CC BY 2.0, woo, 2009](https://commons.wikimedia.org/wiki/File:El_Capitan_Theater_frontal_fachade-The_Masonic_Temple.jpg) via `refs/hollywood`. Concept: photo-anchored generation. Capture: current build with the ashlar limestone field (`limestone_ashlar`, anchored on the same photo). |
| ![three-up board: elcapitan night](elcapitan-night-board.jpg) | El Capitan block, night. Photo: CC BY-SA 3.0 (reviewed; redistribution only, no derived assets) via `refs/hollywood`. Concept: photo-anchored night restage. Capture: current build. | | ![three-up board: elcapitan night](elcapitan-night-board.jpg) | El Capitan block, night. Photo: CC BY-SA 3.0 (reviewed; redistribution only, no derived assets) via `refs/hollywood`. Concept: photo-anchored night restage. Capture: current build. |
| ![three-up board: jokeshop day](jokeshop-day-board.jpg) | Joke Shop frontage, day. Photo: public domain (2006) via `refs/joke-shop`. Concept: generated. Capture: current build. | | ![three-up board: jokeshop day](jokeshop-day-board.jpg) | Joke Shop frontage, day. Photo: public domain (2006) via `refs/joke-shop`. Concept: photo-anchored (hillside frame; the branded frontage trips the model's recitation filter). Capture: current build with the `dark_stucco` wall tile. |
Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 112 KiB

+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"board": "jokeshop-day-board.jpg", "board": "jokeshop-day-board.jpg",
"caption": "Joke Shop, day: licensed photo | generated concept | current capture", "caption": "Joke Shop frontage, day. Photo: public domain 2006. Concept: photo-anchored (hillside frame). Capture: dark stucco wall tile.",
"photo": "docs/screens/refs/joke-shop/comedy-store-frontage-palms-day-pd-2006-12-29.jpg", "photo": "docs/screens/refs/joke-shop/comedy-store-frontage-palms-day-pd-2006-12-29.jpg",
"concept": "docs/screens/concepts/joke_shop/joke_shop_entrance_day.png", "concept": "docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.png",
"capture": "docs/screenshots/12-joke-shop.png" "capture": "docs/screenshots/12-joke-shop.png"
} }
+11
View File
@@ -6,6 +6,17 @@ real Comedy Store block at 8433 Sunset Boulevard. They are generated with
game textures. The geometry mapping and source links are in game textures. The geometry mapping and source links are in
[`../../../joke-shop-map.md`](../../../joke-shop-map.md). [`../../../joke-shop-map.md`](../../../joke-shop-map.md).
## Entrance — day, photo-anchored
![The black comedy club under its signature canopy, anchored on the hillside block photo](joke_shop_entrance_day-v2.png)
Generated with `generate --reference`, anchored on the licence-cleared
[`comedy-store-hillside-canopy-day-pd-2006-12-29.jpg`](../../refs/joke-shop/comedy-store-hillside-canopy-day-pd-2006-12-29.jpg)
board photo (public domain); provenance in
[`joke_shop_entrance_day-v2.refgen.json`](joke_shop_entrance_day-v2.refgen.json).
The frontage photo itself triggers the model's image-recitation filter, so the
hillside block frame anchors the facade; shape follows the frontage photo.
## Entrance — day ## Entrance — day
![The black comedy club under its signature canopy in daylight](joke_shop_entrance_day.png) ![The black comedy club under its signature canopy in daylight](joke_shop_entrance_day.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

@@ -0,0 +1,12 @@
{
"concept": "joke_shop_entrance_day",
"generated": "joke_shop_entrance_day-v2.png",
"model": "google/gemini-2.5-flash-image",
"reference": {
"file": "docs/screens/refs/joke-shop/comedy-store-hillside-canopy-day-pd-2006-12-29.jpg",
"licence": "Public domain",
"page": "https://commons.wikimedia.org/wiki/File:ComedyStore_01.jpg",
"licence_checked": true
},
"suffix": "Use the attached reference photograph as the fixed source of geometry and composition: keep the building's proportions, column rhythm, band heights, signage placement and camera angle exactly as photographed. Only restage lighting, weather, people and surface condition where the prompt asks for it. Do not invent extra storeys, wings or ornaments that the photo does not show."
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 KiB

After

Width:  |  Height:  |  Size: 535 KiB

+7
View File
@@ -36,6 +36,10 @@ const T_LIMESTONE_NORMAL := preload(PICK_DIR + "stone_steps_normal.png")
## `stone_steps` tile reads as small pavers, not wall ashlar). ## `stone_steps` tile reads as small pavers, not wall ashlar).
const T_ASHLAR := preload(PICK_DIR + "limestone_ashlar.png") const T_ASHLAR := preload(PICK_DIR + "limestone_ashlar.png")
const T_ASHLAR_NORMAL := preload(PICK_DIR + "limestone_ashlar_normal.png") const T_ASHLAR_NORMAL := preload(PICK_DIR + "limestone_ashlar_normal.png")
## Dark stucco for the Comedy Store hall wall: flat matte charcoal (the concrete
## paver tile read as grey masonry, not the building's flat black surface).
const T_STUCCO := preload(PICK_DIR + "dark_stucco.png")
const T_STUCCO_NORMAL := preload(PICK_DIR + "dark_stucco_normal.png")
## Roughness / metallic pairs per surface role. Exposed so tests and tooling can ## Roughness / metallic pairs per surface role. Exposed so tests and tooling can
## assert the profiles instead of hard-coding numbers twice. ## assert the profiles instead of hard-coding numbers twice.
@@ -118,6 +122,9 @@ static func limestone(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(
static func ashlar(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(6, 3)) -> StandardMaterial3D: static func ashlar(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(6, 3)) -> StandardMaterial3D:
return textured(T_ASHLAR, tint, uv_scale, "stone", T_ASHLAR_NORMAL) return textured(T_ASHLAR, tint, uv_scale, "stone", T_ASHLAR_NORMAL)
static func stucco(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(6, 3)) -> StandardMaterial3D:
return textured(T_STUCCO, tint, uv_scale, "concrete", T_STUCCO_NORMAL)
static func asphalt(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(30, 7), static func asphalt(tint: Color = Color(1, 1, 1), uv_scale: Vector2 = Vector2(30, 7),
wet: bool = false) -> StandardMaterial3D: wet: bool = false) -> StandardMaterial3D:
var texture: Texture2D = T_ASPHALT_WET if wet else T_ASPHALT var texture: Texture2D = T_ASPHALT_WET if wet else T_ASPHALT
+6 -3
View File
@@ -10,6 +10,7 @@ const T_ASPHALT := preload(PICK_DIR + "asphalt.png")
const T_AWNING := preload(PICK_DIR + "awning_canvas.png") const T_AWNING := preload(PICK_DIR + "awning_canvas.png")
const T_LIMESTONE := preload(PICK_DIR + "stone_steps.png") const T_LIMESTONE := preload(PICK_DIR + "stone_steps.png")
const T_ASHLAR := preload(PICK_DIR + "limestone_ashlar.png") const T_ASHLAR := preload(PICK_DIR + "limestone_ashlar.png")
const T_STUCCO := preload(PICK_DIR + "dark_stucco.png")
## One skyline panorama per shift phase (realism pass): hazy daylight, warm dusk ## One skyline panorama per shift phase (realism pass): hazy daylight, warm dusk
## with lit windows, and a night city. The old single strip was a black silhouette. ## with lit windows, and a night city. The old single strip was a black silhouette.
const T_SKYLINE_DAY := preload(PICK_DIR + "skyline_day.png") const T_SKYLINE_DAY := preload(PICK_DIR + "skyline_day.png")
@@ -374,10 +375,12 @@ func _build_comedy_store_building() -> void:
var black_deep := Color(0.045, 0.045, 0.05) var black_deep := Color(0.045, 0.045, 0.05)
var signature := Color(0.58, 0.56, 0.52) var signature := Color(0.58, 0.56, 0.52)
var red := Color(0.42, 0.035, 0.045) var red := Color(0.42, 0.035, 0.045)
# Main hall. The black stucco is the real building's dominant surface; the # Main hall. The black stucco is the real building's dominant surface: a flat
# concrete texture keeps it matte instead of turning it into glossy paint. # matte charcoal tile now replaces the grey paver texture so the wall reads as
# dark plaster, not masonry. The 0.105 tint keeps the effective value near the
# reference's flat black while the fine grain stops it looking like glossy paint.
_add_box(building, Vector3(w, h, d), Vector3(0, h * 0.5, cz), black, _add_box(building, Vector3(w, h, d), Vector3(0, h * 0.5, cz), black,
false, T_CONCRETE, Vector2(w * 0.42, h * 0.42), "concrete") false, T_STUCCO, Vector2(w * 0.42, h * 0.42), "concrete")
# Low plinth and a thin parapet cap so the roofline reads against the hill. # Low plinth and a thin parapet cap so the roofline reads against the hill.
_add_box(building, Vector3(w, 0.30, 0.34), Vector3(0, 0.15, front + 0.05), _add_box(building, Vector3(w, 0.30, 0.34), Vector3(0, 0.15, front + 0.05),
Color(0.07, 0.07, 0.075), true, null, Vector2.ONE, "stone") Color(0.07, 0.07, 0.075), true, null, Vector2.ONE, "stone")
+10 -1
View File
@@ -76,12 +76,21 @@ func test_joke_shop_uses_the_real_sunset_strip_layout() -> bool:
"the low entrance step is built") "the low entrance step is built")
var g := TestUtils.check(root.find_child("AlleyWall", true, false) == null, var g := TestUtils.check(root.find_child("AlleyWall", true, false) == null,
"the old alley walls are gone") "the old alley walls are gone")
# The hall wall uses the dark stucco pick, not the grey sidewalk-paver tile,
# so it reads as the real club's flat black plaster.
var wall := root.find_child("Building", true, false) as Node3D
var wall_mesh := wall.get_child(0) as MeshInstance3D if wall != null and wall.get_child_count() > 0 else null
var wall_mat: Material = wall_mesh.mesh.surface_get_material(0) if wall_mesh != null and wall_mesh.mesh != null else null
var g2 := TestUtils.check(wall_mat is StandardMaterial3D
and (wall_mat as StandardMaterial3D).albedo_texture != null
and (wall_mat as StandardMaterial3D).albedo_texture.resource_path.contains("dark_stucco"),
"the hall wall carries the dark stucco texture")
var placements: Array = builder.spec.get("prop_placements", []) var placements: Array = builder.spec.get("prop_placements", [])
var h := TestUtils.check(not placements.is_empty() var h := TestUtils.check(not placements.is_empty()
and WorldBuilder.placement_problems(placements).is_empty(), and WorldBuilder.placement_problems(placements).is_empty(),
"the Joke Shop prop placements stay on the sidewalk and clear the lane") "the Joke Shop prop placements stay on the sidewalk and clear the lane")
root.free() root.free()
return a and b and c and d and e and f and g and h return a and b and c and d and e and f and g and g2 and h
func test_joke_shop_geometry_stays_inside_the_map_budget() -> bool: func test_joke_shop_geometry_stays_inside_the_map_budget() -> bool:
var root := Node3D.new() var root := Node3D.new()
+1
View File
@@ -231,6 +231,7 @@ PICKS = {
"brick_facade": "brick_facade", "brick_facade": "brick_facade",
"stone_steps": "stone_steps-v2", "stone_steps": "stone_steps-v2",
"limestone_ashlar": "limestone_ashlar", "limestone_ashlar": "limestone_ashlar",
"dark_stucco": "dark_stucco",
"sidewalk_pavers": "sidewalk_pavers", "sidewalk_pavers": "sidewalk_pavers",
"asphalt": "asphalt", "asphalt": "asphalt",
"asphalt_wet": "asphalt_wet", "asphalt_wet": "asphalt_wet",