Compare commits
2
Commits
v0.2.0
...
45a206e45f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45a206e45f | ||
|
|
75694d2577 |
No files matched your search
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.
@@ -50,10 +50,14 @@ All ☑ approved 2026-11-07.
|
||||
Z 2 m; both are exactly 1 m — the scale reference.
|
||||
- **Demo map** (`/Game/Hollywood/DemoMap`): fixed daytime — sun at pitch −50 /
|
||||
yaw 120 (intensity 4, warm), sky light 2.6, sky atmosphere, height fog, fixed
|
||||
exposure. Surface colours are assigned explicitly in
|
||||
`Tools/ue_build_demo_map.py` (`M_Asphalt`, `M_Concrete`, `M_Ground`,
|
||||
`M_RoadPaint`) because Blender's flat diffuse colours do not survive the
|
||||
FBX → Unreal import.
|
||||
exposure.
|
||||
- **Materials**: every exported surface carries a look-developed material built
|
||||
in `Tools/ue_build_demo_map.py` (`ensure_material`) because Blender's flat
|
||||
diffuse colours do not survive the FBX → Unreal import. Each has an explicit
|
||||
roughness and a subtle world-space noise blend between two shades:
|
||||
`M_Asphalt` (0.92), `M_Concrete` (0.88), `M_KerbConcrete` (0.85),
|
||||
`M_Ground` (0.98), `M_RoadPaint` (0.62), `M_FacadeStone` (0.72) and
|
||||
`M_Stucco` (0.86) for the north-side buildings.
|
||||
- **Lighting is dynamic-only** (`r.AllowStaticLighting=False`, all lights
|
||||
Movable), so no lighting build is ever required.
|
||||
- **Attribution overlay**: every shot carries the required ODbL credit in the
|
||||
|
||||
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.
@@ -78,7 +78,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep
|
||||
- [x] Keep the character as a reusable NPC prefab: a visible body, a collision capsule, and a head-height check that the camera does not clip at 1.60 m — for when NPCs/pedestrians are wired in (`0.2.0+`), not the `0.1.0` player. Done 2026-11-07: `AGigLineNPCPerson` (C++), ACharacter with capsule half-height 83 cm / total 1.66 m (head at ~1.66 m, so a 1.60 m camera does not clip), the MPFB human on the built-in mesh as the visible body, `AutoPossessAI` for pedestrians; confirmed spawned feet-on-Z-0 and rendered in `Screenshots/review-npc-prefab` (☑ approved 2026-11-07).
|
||||
- [x] Screenshots use stable camera names (no index prefix), so inserting a shot no longer renumbers/ orphans files; `AGigLineShotActor` + `Screenshots/README.md` updated.
|
||||
|
||||
### Asset production
|
||||
### Asset production - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `materials-lookdev-0.1.0`)
|
||||
|
||||
- [x] Establish a consistent scale test, named export presets, and repeatable import settings. Scale test = the three-cube calibration scene (`Art/blender/scaletest/`, regenerable via `Tools/scale_test_export.py`); named export preset `ue_metric_v1` (FBX settings in that script); named import preset `ue_metric_v1` (`FbxImportUI` settings in `Tools/ue_scale_test_import.py`). Both scripts are the templates for production asset export/import.
|
||||
- [A] Collect reference photography for manual façade work. Local only, never committed: `Art/reference/` is gitignored. **72 images in eleven folders**: hero façades to 4k, the El Capitan Theatre exterior series, premiere-night staging, Ovation Hollywood, the north side, and the Lee Drug neighbour. Gaps largely closed by the Flickr/web-search pass: the hero entrance is now documented (three portals with individual short flights — the inherited "eight steps, 12 m wide" is overturned), and the 6806–6822 retail run appears in an oblique 2013 frame. Still missing: a clean isolated frame of the door recess and a night frame of 6840 itself.
|
||||
@@ -87,7 +87,7 @@ The MPFB character asset is planned as the NPC template (see `ROADMAP.md`). Keep
|
||||
- [ ] Model surrounding block frontage.
|
||||
- [x] Model simplified background buildings directly across Hollywood Boulevard. Done 2026-11-07, the two in-scope north-side buildings — TCL Chinese Theatre (SM_TCL_Chinese_Theatre) and Madame Tussaud's (SM_Madame_Tussauds) — taken from the verified OSM blockout volumes and exported with `ue_metric_v1`, with a Y-mirror so they land north of Hollywood Blvd across the street from the hero (TCL spans Unreal Y 1802..9431, Tussaud's 1888..7586; road centreline ≈ 0, hero façade at Y −1450). Staged in the DemoMap as `North_TCL_Chinese` / `North_Madame_Tussauds`; demo shot `review-03-demo-north-buildings` captured. Simple flat facade materials (surfaces are the `0.1.4` patch).
|
||||
- [A] Build sidewalks, roads, and collision meshes. Terrain, road, sidewalks and kerbs exist (`Art/blender/terrain/`, `street/`) and are assembled in `site/site-2026-11-06.blend`; collision and details (crossing markings, kerb radii, aprons) are not built. Ground must follow the DEM: 3.94 m fall along the block, ~2 % crown between the gutters at ±10–11 m from the centreline, north sidewalk higher than south. Kerb height is an unresolved assumption — the DEM does not resolve it (`Geo/verification/2026-11-06-cross-sections.md`).
|
||||
- [ ] Materials and look development for all exported assets.
|
||||
- [x] Materials and look development for all exported assets. Done 2026-11-07: every exported surface now carries a look-developed material built procedurally in `Tools/ue_build_demo_map.py` (`ensure_material`): base colour with a world-space noise blend between two shades for large-scale variation, plus an explicit roughness and specular. Set is `M_Asphalt` 0.92, `M_Concrete` 0.88, `M_KerbConcrete` 0.85, `M_Ground` 0.98, `M_RoadPaint` 0.62, `M_FacadeStone` 0.72 (hero façade) and `M_Stucco` 0.86 (TCL, Tussaud's), assigned by mesh and by actor label. Materials are rebuilt on every run so a stale asset cannot mask a look change. The richer texture-based pass (baked/procedural brick, brick_facade and seamless_concrete generators) stays in the `0.1.4` façade patch.
|
||||
|
||||
### Gameplay - active - 01a1b26b-3c94-707f-9665-2f6b4a79b564 (branch `gameplay-attribution-0.1.0`)
|
||||
|
||||
|
||||
+115
-25
@@ -50,26 +50,78 @@ def spawn(cls, loc, label, rot=None):
|
||||
return actor
|
||||
|
||||
|
||||
def ensure_material(asset_name, rgb):
|
||||
"""Create/reuse a flat-colour material under /Game/Hollywood/Site.
|
||||
def ensure_material(mat_name, spec):
|
||||
"""Create/reuse a look-developed material under /Game/Hollywood/Site.
|
||||
|
||||
Used where the FBX material round-trip does not survive import: the terrain
|
||||
arrives with WorldGridMaterial (UE's bright default grid), which reads as a
|
||||
blown-out white field under the demo sun.
|
||||
Blender's flat diffuse colours do not survive the FBX -> Unreal import (the
|
||||
terrain even lands with WorldGridMaterial), so the demo has always assigned
|
||||
its own material per surface. Those were flat colour only, which read as
|
||||
plastic. Each material here gets:
|
||||
|
||||
* base colour with a subtle world-space noise blend between two shades,
|
||||
so asphalt and concrete have large-scale variation instead of a fill,
|
||||
* an explicit roughness (and specular) so the surface type is readable,
|
||||
wet-looking stone vs matte concrete vs chalky road paint.
|
||||
|
||||
spec keys: dark, bright (RGB tuples), roughness, specular, noise_scale
|
||||
(world units per noise cycle; smaller = larger features).
|
||||
"""
|
||||
path = f"/Game/Hollywood/Site/{asset_name}"
|
||||
path = f"/Game/Hollywood/Site/{mat_name}"
|
||||
if unreal.EditorAssetLibrary.does_asset_exist(path):
|
||||
return unreal.EditorAssetLibrary.load_asset(path)
|
||||
# Rebuilt every run so a look change cannot be masked by a stale asset.
|
||||
unreal.EditorAssetLibrary.delete_asset(path)
|
||||
|
||||
mat = unreal.AssetToolsHelpers.get_asset_tools().create_asset(
|
||||
asset_name, "/Game/Hollywood/Site", unreal.Material, unreal.MaterialFactoryNew())
|
||||
node = unreal.MaterialEditingLibrary.create_material_expression(
|
||||
mat, unreal.MaterialExpressionConstant3Vector)
|
||||
node.set_editor_property("constant", unreal.LinearColor(rgb[0], rgb[1], rgb[2], 1.0))
|
||||
unreal.MaterialEditingLibrary.connect_material_property(
|
||||
node, "", unreal.MaterialProperty.MP_BASE_COLOR)
|
||||
unreal.MaterialEditingLibrary.recompile_material(mat)
|
||||
mat_name, "/Game/Hollywood/Site", unreal.Material, unreal.MaterialFactoryNew())
|
||||
lib = unreal.MaterialEditingLibrary
|
||||
|
||||
def node(cls):
|
||||
return lib.create_material_expression(mat, cls)
|
||||
|
||||
def c3(rgb):
|
||||
n = node(unreal.MaterialExpressionConstant3Vector)
|
||||
n.set_editor_property("constant", unreal.LinearColor(rgb[0], rgb[1], rgb[2], 1.0))
|
||||
return n
|
||||
|
||||
def scalar(value):
|
||||
n = node(unreal.MaterialExpressionConstant)
|
||||
n.set_editor_property("r", float(value))
|
||||
return n
|
||||
|
||||
# world position -> noise -> lerp(dark, bright) -> base colour
|
||||
wp = node(unreal.MaterialExpressionWorldPosition)
|
||||
scale = scalar(spec.get("noise_scale", 0.004))
|
||||
mul = node(unreal.MaterialExpressionMultiply)
|
||||
lib.connect_material_expressions(wp, "", mul, "A")
|
||||
lib.connect_material_expressions(scale, "", mul, "B")
|
||||
|
||||
noise = node(unreal.MaterialExpressionNoise)
|
||||
for prop, value in (("scale", 1.0), ("quality", 1), ("levels", 2),
|
||||
("output_min", 0.0), ("output_max", 1.0)):
|
||||
try:
|
||||
noise.set_editor_property(prop, value)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
lib.connect_material_expressions(mul, "", noise, "Position")
|
||||
|
||||
lerp = node(unreal.MaterialExpressionLinearInterpolate)
|
||||
lib.connect_material_expressions(c3(spec["dark"]), "", lerp, "A")
|
||||
lib.connect_material_expressions(c3(spec["bright"]), "", lerp, "B")
|
||||
lib.connect_material_expressions(noise, "", lerp, "Alpha")
|
||||
lib.connect_material_property(lerp, "", unreal.MaterialProperty.MP_BASE_COLOR)
|
||||
|
||||
lib.connect_material_property(
|
||||
scalar(spec.get("roughness", 0.85)), "", unreal.MaterialProperty.MP_ROUGHNESS)
|
||||
try:
|
||||
lib.connect_material_property(
|
||||
scalar(spec.get("specular", 0.3)), "", unreal.MaterialProperty.MP_SPECULAR)
|
||||
except Exception: # noqa: BLE001 - specular input is optional in UE5
|
||||
pass
|
||||
|
||||
lib.recompile_material(mat)
|
||||
unreal.EditorAssetLibrary.save_loaded_asset(mat)
|
||||
log(f"created material {asset_name} rgb={rgb}")
|
||||
log(f"material {mat_name}: rough={spec.get('roughness')} "
|
||||
f"dark={spec['dark']} bright={spec['bright']}")
|
||||
return mat
|
||||
|
||||
|
||||
@@ -209,17 +261,38 @@ def build():
|
||||
# builds a material with a default white base colour, and the terrain even
|
||||
# lands with WorldGridMaterial. Every site surface therefore rendered white,
|
||||
# which is what washed out the demo. Assign explicit colours per mesh here.
|
||||
SURFACE_MATERIALS = {
|
||||
"Terrain_DEM": ("M_Ground", (0.30, 0.28, 0.25)),
|
||||
"Road_Hollywood": ("M_Asphalt", (0.055, 0.055, 0.06)),
|
||||
"Sidewalk_South": ("M_Concrete", (0.52, 0.50, 0.47)),
|
||||
"Sidewalk_North": ("M_Concrete", (0.52, 0.50, 0.47)),
|
||||
"Kerb_South": ("M_Concrete", (0.52, 0.50, 0.47)),
|
||||
"Kerb_North": ("M_Concrete", (0.52, 0.50, 0.47)),
|
||||
"Crossing_Markings": ("M_RoadPaint", (0.80, 0.80, 0.78)),
|
||||
# Look-developed surface materials (see ensure_material for the graph).
|
||||
MATERIAL_SPECS = {
|
||||
"M_Asphalt": dict(dark=(0.040, 0.040, 0.044), bright=(0.072, 0.072, 0.078),
|
||||
roughness=0.92, specular=0.22, noise_scale=0.006),
|
||||
"M_Concrete": dict(dark=(0.44, 0.42, 0.39), bright=(0.56, 0.54, 0.50),
|
||||
roughness=0.88, specular=0.28, noise_scale=0.004),
|
||||
"M_KerbConcrete": dict(dark=(0.47, 0.45, 0.42), bright=(0.60, 0.58, 0.54),
|
||||
roughness=0.85, specular=0.30, noise_scale=0.008),
|
||||
"M_Ground": dict(dark=(0.22, 0.20, 0.17), bright=(0.33, 0.30, 0.26),
|
||||
roughness=0.98, specular=0.15, noise_scale=0.003),
|
||||
"M_RoadPaint": dict(dark=(0.74, 0.74, 0.72), bright=(0.86, 0.86, 0.84),
|
||||
roughness=0.62, specular=0.35, noise_scale=0.02),
|
||||
"M_FacadeStone": dict(dark=(0.62, 0.58, 0.50), bright=(0.74, 0.70, 0.62),
|
||||
roughness=0.72, specular=0.32, noise_scale=0.005),
|
||||
"M_Stucco": dict(dark=(0.52, 0.48, 0.42), bright=(0.66, 0.62, 0.55),
|
||||
roughness=0.86, specular=0.25, noise_scale=0.004),
|
||||
}
|
||||
SURFACE_MATERIALS = {
|
||||
"Terrain_DEM": "M_Ground",
|
||||
"Road_Hollywood": "M_Asphalt",
|
||||
"Sidewalk_South": "M_Concrete",
|
||||
"Sidewalk_North": "M_Concrete",
|
||||
"Kerb_South": "M_KerbConcrete",
|
||||
"Kerb_North": "M_KerbConcrete",
|
||||
"Crossing_Markings": "M_RoadPaint",
|
||||
}
|
||||
built = {}
|
||||
for mat_name, spec in MATERIAL_SPECS.items():
|
||||
built[mat_name] = ensure_material(mat_name, spec)
|
||||
|
||||
imported = 0
|
||||
for name, (mat_name, rgb) in SURFACE_MATERIALS.items():
|
||||
for name, mat_name in SURFACE_MATERIALS.items():
|
||||
mesh = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}")
|
||||
if mesh is None:
|
||||
continue
|
||||
@@ -227,12 +300,29 @@ def build():
|
||||
if actor:
|
||||
actor.static_mesh_component.set_static_mesh(mesh)
|
||||
actor.static_mesh_component.set_mobility(unreal.ComponentMobility.MOVABLE)
|
||||
mat = ensure_material(mat_name, rgb)
|
||||
mat = built.get(mat_name)
|
||||
if mat:
|
||||
actor.static_mesh_component.set_material(0, mat)
|
||||
imported += 1
|
||||
log(f"staged {imported} site meshes with explicit surface materials")
|
||||
|
||||
# The hero facade and the two north-side buildings are staged by other
|
||||
# scripts (or reused between rebuilds), so assign their look materials by
|
||||
# actor label after everything is in the level.
|
||||
LABEL_MATERIALS = {"Hero_Facade": "M_FacadeStone",
|
||||
"North_TCL_Chinese": "M_Stucco",
|
||||
"North_Madame_Tussauds": "M_Stucco"}
|
||||
assigned = 0
|
||||
for actor in unreal.EditorLevelLibrary.get_all_level_actors():
|
||||
mat_name = LABEL_MATERIALS.get(actor.get_actor_label())
|
||||
if not mat_name or mat_name not in built:
|
||||
continue
|
||||
comp = actor.get_component_by_class(unreal.StaticMeshComponent)
|
||||
if comp and comp.static_mesh:
|
||||
comp.set_material(0, built[mat_name])
|
||||
assigned += 1
|
||||
log(f"assigned look materials to {assigned} facade/background actors")
|
||||
|
||||
# --- 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
|
||||
|
||||
Reference in new issue
Block a user