- Hero cornice finished: projecting corona over a 52-dentil course (0.18 x 0.30 x
0.22 m at 0.45 m centres) so the top of the order has a shadow line instead of
one flat box. Hero 3044 tris / 148 parts, still 14.50 m; rebuilt, re-imported,
DemoMap rebuilt, packaged -qaselftest all PASS, screenshots re-rendered.
- CHANGELOG: full 0.1.2 entry (added: concept generator, FBX preset, facade probe,
Ionic detail, tool tests; changed: 3.1 m storey height, 53.1% colonnade, smoothing
groups; fixed: floating windows, flat medallions, missing facade collision, the
three non-building heights).
- TODO: 0.1.2 section removed (goal stays in ROADMAP, whose status line is updated).
- MEMORY: release record incl. the recorded ornament assumptions.
The bpy.ops.export_scene.fbx argument block was duplicated in six scripts (hero,
block frontage, north buildings, site, scale test, MPFB character) and all six
carried mesh_smooth_type="OFF", which drops smoothing groups and made every
Unreal import warn and every round part faceted.
Tools/fbx_export_preset.py now owns the presets:
- preset_kwargs(preset, selection, **overrides) is bpy-free and unit-tested;
- export_static()/export_character() are thin wrappers that import bpy lazily;
- ue_metric_v1 and ue_character_v1 keep the recorded scale contract
(FBX_SCALE_NONE, apply_unit_scale, -Y forward / Z up) and use FACE smoothing.
Callers switched: hero_facade_build, export_block_frontage, export_north_buildings,
scale_test_export, mpfb_character_export, Art/blender/site/export_site_fbx.
Verified by re-exporting every asset through the preset and re-importing all of
them: 0 smoothing warnings on site, frontage and north buildings; packaging and
-qaselftest all PASS (walk, transect 0 holes, wall probe 0/35, facade probe 0/9).
49 tool tests pass (9 new for the preset contract).
UE export hygiene (unreal-export skill checklist) plus a gameplay defect the new
probe caught:
- FBX export now uses mesh_smooth_type="FACE" (was OFF, which is why every import
logged "No smoothing group information was found"), and the round parts are
shaded with shade_smooth_by_angle(35 deg) before export, so columns and
medallions read round instead of faceted.
- SM_HeroFacade had complex-only collision, so capsule sweeps and line traces
passed through the wall: the player could walk through the hero facade.
auto_generate_collision was tried and rejected (coarse hulls bulged into the
street; the QA wall probe went 0/35 -> 2/35). It now uses UseComplexAsSimple,
saved on the asset, like the terrain.
- New -qaselftest facade occlusion probe: 40 m traces sideways from the road
centre at walking height, required only where a building is staged (explicit X
spans mirroring ue_build_demo_map). Result 0/9 -> PASS, alongside transect
0 holes and wall probe 0/35.
Docs: release-checks records the probe and both collision attempts; Tools/README
lists the new phase; MEMORY records the smoothing/collision lessons.
Detail pass on the hero, all verified by measuring the built mesh:
- Column base moulding: a 1.36 m wide, 0.22 m tall cylinder at each shaft foot
(2.00-2.22 m). The shaft previously started straight off the square pedestal,
so the base never read in silhouette.
- Abacus: a 1.30 m square, 0.14 m thick slab over each capital. It takes the top
of the capital zone, so the colonnade still ends at 9.70 m and the 53.1 %
proportion is unchanged.
- Medallions fixed: they were built with the Z-axis cylinder helper, so the two
'discs' were pancakes lying flat on the parapet coping (1.1 x 1.1 x 0.02,
z 14.02-14.04). New disc_on_wall() helper rotates the cylinder 90 deg about X;
they now stand 1.1 m across on the parapet face (y -1.32..-1.28,
z 13.05..14.15).
Hero rebuilt (95 parts, 2420 tris, still 14.50 m), re-imported, DemoMap rebuilt,
screenshot set re-rendered. Part report records the new dims and their assumption
notes. Screenshots README notes the detail pass.
Adds Tools/facade_detail_concepts.py: generates architectural detail plates from a
local reference photograph through OpenRouter's image models. Spec-driven (eight
studies: door recess, entrance, column order, entablature/cornice, parapet/motto,
window rhythm, street wall, banner), never overwrites (writes -vN), writes a
.refgen.json sidecar (model, prompt, source photo, SHA-256) per image, and keeps
all output under the gitignored Art/reference/facade-details/ - local only, never
shipped. Stdlib only; http 429/5xx back-off; refuses to write a sidecar containing
the token. 11 tests cover the study table, prompt assembly, next-free naming,
data-URL decoding (both response shapes plus a refusal) and the local-only rule.
Measurement pass on the plates: the hero window panes were at Y -1.98, i.e. 1.44 m
in FRONT of the wall (Y -0.50) and ahead of the column line, so the glazing floated
in the colonnade instead of reading as openings behind it. Moved to Y -0.56; hero
rebuilt, re-imported, DemoMap rebuilt, full screenshot set re-rendered.
Docs: Tools/README row, Screenshots README known-issues entry now records the fix.
Measured from the built mesh, the colonnade (columns + capitals) was 6.90 m of the
14.50 m elevation = 47.6 %: the entablature (3.40 m) and parapet (2.20 m) were
using 5.60 m of the height. Thinned both to 3.00 m and 1.80 m in
hero_facade_build.py, which lifts the colonnade to 7.70 m = 53.1 % - inside the
50-60 % band - with the envelope, motto band and medallions unchanged relative to
the parapet.
Hero rebuilt, re-imported (bounds 2604 x 1257 x 1450 cm), DemoMap rebuilt and the
screenshot set re-rendered for review. Hero part report updated (columns 7.45 ->
8.25 m, entablature 3.40 -> 3.00 m).
TODO: colonnade item closed. MEMORY records the FBX-vs-blend --out gotcha.
- Converted 13 individual .adoc section files to .md via
asciidoctor→docbook5→pandoc(gfm) + post-processing, placed alongside
the .adoc sources in Docs/Architecture/
- Rebuilt architecture.md from the individual section .md files
- Ran prettier --write on all .md in the repo (23 files changed):
normalised table column alignment, trailing whitespace, line breaks
- Added .agents/ to .prettierignore (vendored third-party content)
- All .md now passes prettier --check
Completes "Fixed daytime lighting and basic ambience" for 0.1.0. All 14
screenshots approved by the owner.
Lighting rig (Tools/ue_build_demo_map.py):
- Sun pitch -50 / yaw 120, intensity 4, warm (255/245/230), atmosphere sun light
- Sky light 2.6 with real-time capture, sky atmosphere, height fog (density 0.008)
- Unbound post-process volume with fixed exposure (min == max brightness) so a
shot cannot wash out or crush to black depending on how much sky it frames
- Dynamic-only: r.AllowStaticLighting=False plus MOVABLE lights and mesh
components, so the editor's "LIGHTING NEEDS TO BE REBUILT" warning no longer
fires and nothing needs baking
Bugs found and fixed on the way:
- Blender diffuse colours do not survive the FBX -> Unreal import: every site
material arrived white and the terrain arrived with WorldGridMaterial, which
is what actually made the scene look blown out. The demo builder now creates
and assigns M_Asphalt, M_Concrete, M_Ground and M_RoadPaint.
- Only one of the three crossings reached the game: each zebra band was its own
object, so Blender renamed the duplicates .001/.002 and exact-name site
assembly skipped them. All bands are now one Crossing_Markings mesh.
- Demo cameras: street-level sat inside the facade body (rendering its unlit
back face) and the crossing camera aimed at the s=36.6 crossing instead of
the designated s=130.1 (UE X -14956).
- The terrain had no material and imported as a bright default grid.
- Cleared a committed Android File Server SecurityToken (plugin disabled and
shipping-excluded; the value must stay empty).
Docs:
- Screenshots/README.md: one consolidated table for all 14 shots with map,
camera view and description; all approved.
- MEMORY.md: records the material-import, exposure, light_color struct,
band-naming, camera-bounds and lighting-build traps.
- TODO.md: lighting item marked done.
Squash of branch gameplay-lighting-0.1.0 (PR #3).
- street_mesh.py: continental zebra markings (0.5 m bars, 0.5 m gaps, 3 m
band) at all three signalised crossings, triangles not quads (FBX SDK
refuses to triangulate the crown-twisted quads), stripes +6 cm above the
DEM so they stay above the road surface; winding flipped so every street
surface faces up (road/sidewalks/markings were 100% down-facing and
invisible in engine once the terrain stopped covering them).
- terrain_from_dem.py: cut a 10.7 m corridor around the Hollywood Blvd
centreline so road, kerbs and sidewalks are not buried under the terrain;
sidewalks themselves cover the transition band, walk-check stays at 0 holes.
- Blender builders save with a writability guard + fatal failure: *.blend
is LFS-lockable and checks out read-only; a failed save previously left
downstream steps building from stale files.
- ue_import_site.py: Crossing_Markings imports with no collision (paint on
the road); ue_build_demo_map.py stages it and chmods the lockable .umap
before saving (save_map silently returned False on read-only maps).
- ue_build_review_map.py: demo-crossing camera moved to the real crossing
(s=130.1 m -> UE -24291,-1340) and fixed the ensure_writable path (was
missing the .umap suffix, so the chmod never fired).
0.5 m white bars with 0.5 m gaps across a 3 m band per crossing, laid
1 cm above the road surface. Makes the mid-block crossing (OSM way
385648319, s=130.1 m) a designated crossing in game. Metadata audit
passes (1 sidecar, 0 problems).
- mpfb_character_export.py: new --pose idle folds the A/T-bind arms down to
a relaxed standing rest pose (baked via pose.armature_apply before export);
new make_diffuse synthesizes a warm skin-tone diffuse with a darker
shorts-suggestive band, replacing the Cycles v2-skin bake that came out
black and was slow at 1024^2.
- write_png now encodes from img.pixels via numpy (fast) because img.save()
writes black for an in-memory image.
- Re-exported idle character, re-imported to /Game/Test/Character (138 bones,
warm M_Character_Export/T_Character_Diffuse); removed stale T_Skin_Diffuse.
- Untracked the review PNGs again: the Screenshots/ gitignore rule had been
dropped and the parallel session had committed the PNGs, against the
AGENTS.md 'never commit the PNGs' rule. Restored the ignore rule; only
Screenshots/README.md stays tracked.
- README/roadmap/memory updated; review-01-character re-pended for approval.
Tools/ue_build_demo_map.py builds /Game/Hollywood/DemoMap: sun, sky, the hero
facade on the building line, six site meshes imported with collision, and a
player start on the north sidewalk. Tools/ue_import_site.py imports the site
FBXs; terrain, road and sidewalks use complex-as-simple so a simple hull cannot
ramp over the kerbs. Art/blender/site/export_site_fbx.py exports one FBX per
object so each can carry its own collision.
Reset (R) and pause (Escape) join the character and the input context: reset
restores the spawn transform and clears momentum, pause is a real game pause that
releases the cursor. DefaultInput.ini captures the mouse on launch.
Three placement defects found by measuring the imported actors rather than
trusting the plan: the spawn was on the wrong sidewalk because the FBX flip
inverts north, a mirror I added to the exporter double-corrected an already
correct sidewalk, and the street meshes only carry part of the north walk - the
rest is terrain.
The portal detail shot failed for a modelling reason, not a camera reason: the
doors were 0.1 m panels on a solid plinth, so they read as shading. The plinth is
now four piers plus a lintel band, with a 1.2 m reveal, stone jambs and lintels
standing 0.10 m proud and three mullions per opening. review-06 frames them from
8 m at eye height.
Two defects fixed while doing it: the jamb boxes hung 0.15 m below the pavement,
so the facade imported with min Z -15 cm; and a rebuild run against a read-only
.blend silently skipped the save, which is why screenshots kept showing the old
geometry.
Geo/tools/ is now every geographic script, with Geo/README.md documenting each
one; the Blender-side builders moved beside their outputs in Art/blender/*/ and
import their helpers from Geo/tools/osm_common.py.
That module gains to_unreal_local() and to_blender_local(): the only place in the
repository that multiplies metres by 100, so a second silent scale cannot be
introduced. block_height_report.py adds height provenance for the block — 3 of
15 buildings have no height data and render at a placeholder 6.4 m.
asset_metadata.py writes and audits <asset>.geo.json sidecars; four now exist
(hero-facade, terrain, street, blockout) and the audit recomputes each centre from
the origin, failing anything that disagrees.
Tools/sibling_asset_audit.py reads the sibling security-game project's prop
library straight from the GLB headers: 20 street props, 11.8k triangles, every
one with a Blender source. The 50 .blend sources are copied to
Art/props/blender_src/ with a manifest; nothing is imported into Unreal yet. Its
building meshes are deliberately not taken - they are Godot GLBs built around a
29.6 m facade that this project measured at 25.75 m.
The facade proportions were wrong against the photograph: 5.7 m of blank wall
above the capitals. Entablature is now 3.4 m, parapet 2.2 m, with architrave and
cornice bands and square pedestals, so the elevation reads as layered stone.
Tools/hero_facade_build.py builds the north elevation parametrically and writes
a JSON report naming the evidence behind every part: the 25.75 m frontage and
14.5 m height are measured, the 4.96 m column bay comes from the sibling notes
and matches that frontage, the three portal flights follow the photographs (five
risers, overturning the inherited eight-step, 12 m wide claim), and the rest is
labelled assumption.
Tools/ue_hero_facade_import.py imports it with the ue_metric_v1 preset
(2604 x 1257 x 1450 cm, 1892 tris, 4 materials) and places it on the measured
building line of /Game/Hollywood/DemoMap.
Two new review shots at review-04 and review-05, both pending approval. Camera
placement cost four renders: the first two sat inside the facade body, and the
frontage faces Unreal -Y, so a camera has to stand at Y < -1450 cm, not north of
the building line in project terms.
94 MIT-licensed skill packs plus their shared references, copied unedited from
the local checkout of github.com/arjun988/blender-skills at commit 8f778d2
(1.2 MB, 269 Markdown files). They are agent instructions, not add-ons; the
pipeline still drives Blender headlessly through bpy, so the optional Blender MCP
server is not required. Provenance recorded in THIRD_PARTY_NOTICES.md and the
version, source, and update procedure in Docs/toolchain.md.
site-2026-11-06.blend gathers terrain, street and blockout into categorised
collections: 281 objects, ~58k triangles. A downward raycast on a 1 m grid along
both sidewalks and the designated crossing finds no holes, so nothing drops the
player on either side. Two steps are flagged: 15.8 cm at the sidewalk's outer
terrain seam and 10-13 cm ramps at the crossing flush transitions, steeper than
a real 8% ramp.
Sampling is projected through PROJ: placing points with the OSM spherical
approximation put them metres off the UTM meshes and reported 91 phantom holes.
Tools/street_mesh.py builds the corridor in the project frame from the measured
profile: carriageway -10.5..+10.5 m, kerbs 0.15 m (0.13 m as built), sidewalks
4.0 m south and 5.3 m north, all elevations sampled from the DEM. Kerbs flush at
the three signalised crossings. Crown at mid-block reproduces the cross-sections
to the centimetre.
Tools/terrain_from_dem.py builds a 191x106 grid at 2 m over the block, Z being
NAVD88 minus the origin ground. Building footprints from the OSM extract are cut
out so rooftops never become terrain (72 footprints, 6310 cells). Spot checks
reproduce the measured profile: origin 0.00, Orange Dr +3.39, south gutter
+0.93, crown +1.38, north sidewalk +1.43.
Blosm projects with a sphere, not an ellipsoid, so its frame was ~0.4% oversized
and rotated by meridian convergence: about 2 m at the theatre. The blockout now
fits a six-parameter affine into EPSG:26911 on five control points (0.0 cm
residual) and transforms root objects only, since children inherit and double
transforming them read as a 2 m placement error.
Tools/blosm_blockout.py imports the committed extract in 3Dsimple mode and
reports per-building dimensions so a units failure is visible in the log. The
result (87 meshes) is committed as the blockout baseline: hero 25.68 x 54.88 x
14.50 m against 25.75 x 54.88 m of OSM geometry, so the import is 1:1 metres.
Two headless defects needed working around: the asset-directory check that
fires before mode is honoured, and an empty default-level table sending the
add-on down a broken relative import. Member fetching stays off so the import
does not depend on Overpass.
Reference images are development-only material: licensing is not a gate for
local private use, so they are simply never committed. Art/reference/ is now
gitignored; the licensed status of anything shipped is still recorded in
THIRD_PARTY_NOTICES.md.
Adds Tools/commons_fetch.py, which fetches Wikimedia Commons imagery by search
or exact title and writes a provenance sidecar, plus Docs/reference-notes-hollywood.md
recording what each local set is evidence for.
Create Game/, Art/, Geo/, Tools/, Docs/ with LFS and ignore rules, record the
workstation audit, toolchain pins, setup checklist, release checks, and
licensing policy. ROADMAP 0.1.0 environment tasks marked to match verified state.