diff --git a/TODO.md b/TODO.md index 47634e7..460d5de 100644 --- a/TODO.md +++ b/TODO.md @@ -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` → retopo/UV assist → atlas → `audit`), and document the exact commands in `docs/asset-tooling.md`. -- [-] Joke Shop building-appearance pass, same evidence model as the masonic facade: generate a - dark stucco wall tile reference-anchored on the public-domain frontage photo (PD allows - derived work without attribution conditions), generate a photo-anchored day concept for the - three-up board, wire the tile behind `VENUE_SPECS`, then recapture and rebuild the Joke Shop - day board. Verify texel density stays inside the 40–400 px/m band and the signature fascia - + red curtain bays still read against the reference. +- [x] Joke Shop building-appearance pass: `dark_stucco` tile (1024 px) promoted via the picks + pipeline and wired into `_build_comedy_store_building`, replacing the grey paver tile so the + hall reads as flat matte charcoal instead of masonry; photo-anchored day concept generated + from the hillside PD frame (the branded frontage photo trips the model's image-recitation + filter — recorded in the concept README); day capture promoted and the three-up board rebuilt. + Test asserts the hall wall carries `dark_stucco`. - [x] Building-appearance realism pass, measured against the reference set: bring the masonic 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 diff --git a/assets/textures/ai/game_ready/v2/picks/dark_stucco.png b/assets/textures/ai/game_ready/v2/picks/dark_stucco.png new file mode 100644 index 0000000..48e7f58 Binary files /dev/null and b/assets/textures/ai/game_ready/v2/picks/dark_stucco.png differ diff --git a/assets/textures/ai/game_ready/v2/picks/dark_stucco_normal.png b/assets/textures/ai/game_ready/v2/picks/dark_stucco_normal.png new file mode 100644 index 0000000..291f510 Binary files /dev/null and b/assets/textures/ai/game_ready/v2/picks/dark_stucco_normal.png differ diff --git a/docs/screens/boards/README.md b/docs/screens/boards/README.md index 40f9cc3..c1c5081 100644 --- a/docs/screens/boards/README.md +++ b/docs/screens/boards/README.md @@ -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 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. | diff --git a/docs/screens/boards/jokeshop-day-board.jpg b/docs/screens/boards/jokeshop-day-board.jpg index b8bc1b4..a99f53a 100644 Binary files a/docs/screens/boards/jokeshop-day-board.jpg and b/docs/screens/boards/jokeshop-day-board.jpg differ diff --git a/docs/screens/boards/jokeshop-day-board.json b/docs/screens/boards/jokeshop-day-board.json index f56b97a..46e13c1 100644 --- a/docs/screens/boards/jokeshop-day-board.json +++ b/docs/screens/boards/jokeshop-day-board.json @@ -1,7 +1,7 @@ { "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", - "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" } diff --git a/docs/screens/concepts/joke_shop/README.md b/docs/screens/concepts/joke_shop/README.md index 759b946..d2167ac 100644 --- a/docs/screens/concepts/joke_shop/README.md +++ b/docs/screens/concepts/joke_shop/README.md @@ -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 [`../../../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 ![The black comedy club under its signature canopy in daylight](joke_shop_entrance_day.png) diff --git a/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.png b/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.png new file mode 100644 index 0000000..1593cac Binary files /dev/null and b/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.png differ diff --git a/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.refgen.json b/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.refgen.json new file mode 100644 index 0000000..d349c45 --- /dev/null +++ b/docs/screens/concepts/joke_shop/joke_shop_entrance_day-v2.refgen.json @@ -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." +} diff --git a/docs/screenshots/12-joke-shop.png b/docs/screenshots/12-joke-shop.png index f83f616..17d2d83 100644 Binary files a/docs/screenshots/12-joke-shop.png and b/docs/screenshots/12-joke-shop.png differ diff --git a/src/game/materials.gd b/src/game/materials.gd index 9acce1d..4a96941 100644 --- a/src/game/materials.gd +++ b/src/game/materials.gd @@ -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). const T_ASHLAR := preload(PICK_DIR + "limestone_ashlar.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 ## 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: 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), wet: bool = false) -> StandardMaterial3D: var texture: Texture2D = T_ASPHALT_WET if wet else T_ASPHALT diff --git a/src/game/world_builder.gd b/src/game/world_builder.gd index c63f953..30f4b91 100644 --- a/src/game/world_builder.gd +++ b/src/game/world_builder.gd @@ -10,6 +10,7 @@ const T_ASPHALT := preload(PICK_DIR + "asphalt.png") const T_AWNING := preload(PICK_DIR + "awning_canvas.png") const T_LIMESTONE := preload(PICK_DIR + "stone_steps.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 ## 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") @@ -374,10 +375,12 @@ func _build_comedy_store_building() -> void: var black_deep := Color(0.045, 0.045, 0.05) var signature := Color(0.58, 0.56, 0.52) var red := Color(0.42, 0.035, 0.045) - # Main hall. The black stucco is the real building's dominant surface; the - # concrete texture keeps it matte instead of turning it into glossy paint. + # Main hall. The black stucco is the real building's dominant surface: a flat + # 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, - 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. _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") diff --git a/src/test/tests/test_world.gd b/src/test/tests/test_world.gd index fddfe47..4adcddc 100644 --- a/src/test/tests/test_world.gd +++ b/src/test/tests/test_world.gd @@ -76,12 +76,21 @@ func test_joke_shop_uses_the_real_sunset_strip_layout() -> bool: "the low entrance step is built") var g := TestUtils.check(root.find_child("AlleyWall", true, false) == null, "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 h := TestUtils.check(not placements.is_empty() and WorldBuilder.placement_problems(placements).is_empty(), "the Joke Shop prop placements stay on the sidewalk and clear the lane") 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: var root := Node3D.new() diff --git a/tools/scene_concepts.py b/tools/scene_concepts.py index f8c22d7..ba9f58c 100644 --- a/tools/scene_concepts.py +++ b/tools/scene_concepts.py @@ -231,6 +231,7 @@ PICKS = { "brick_facade": "brick_facade", "stone_steps": "stone_steps-v2", "limestone_ashlar": "limestone_ashlar", + "dark_stucco": "dark_stucco", "sidewalk_pavers": "sidewalk_pavers", "asphalt": "asphalt", "asphalt_wet": "asphalt_wet",