Compare commits
77
Commits
f4c1fed99b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1a6645fb3 | ||
|
|
2d7322253a | ||
|
|
d84549e686 | ||
|
|
8c34eaece0 | ||
|
|
bc9fed8e11 | ||
|
|
33f8139cb2 | ||
|
|
1d57ed477d | ||
|
|
2dac1d0921 | ||
|
|
ff9eb0a4ad | ||
|
|
0189ff00fe | ||
|
|
6f896fb701 | ||
|
|
9a444418e6 | ||
|
|
dbb42322ff | ||
|
|
24f1e682fe | ||
|
|
4d27109668 | ||
|
|
f681d83736 | ||
|
|
70a7e19841 | ||
|
|
65e8f01586 | ||
|
|
3872ffb742 | ||
|
|
abde390045 | ||
|
|
bf7dfe1052 | ||
|
|
266fc1bb0c | ||
|
|
19ed4fefd4 | ||
|
|
61dd28bc89 | ||
|
|
670c692c43 | ||
|
|
03ef4dc945 | ||
|
|
7b9a7a3867 | ||
|
|
01e49ff889 | ||
|
|
e19cc361ad | ||
|
|
fc1e832664 | ||
|
|
fe219b038c | ||
|
|
a1771cb55f | ||
|
|
410d924403 | ||
|
|
387a4530de | ||
|
|
331e2016be | ||
|
|
358673bcfc | ||
|
|
d1636f6986 | ||
|
|
348ed1ac69 | ||
|
|
0f059a5c93 | ||
|
|
6d025491ef | ||
|
|
14ed01a4e6 | ||
|
|
2ecd029cc0 | ||
|
|
a372fdf389 | ||
|
|
de83f990ac | ||
|
|
f29d9d7e49 | ||
|
|
be86ee6d73 | ||
|
|
4498274328 | ||
|
|
f65c6e8d45 | ||
|
|
4db33489e1 | ||
|
|
fb5488c1df | ||
|
|
1b4b46e957 | ||
|
|
4132dc77e3 | ||
|
|
4f9e92d975 | ||
|
|
b9919b8d1e | ||
|
|
cbdb8e6e3a | ||
|
|
b84d47ec18 | ||
|
|
b551396882 | ||
|
|
16eaf5a07d | ||
|
|
d0d87e8fda | ||
|
|
e50f620aa3 | ||
|
|
1916321761 | ||
|
|
1ee2a35d53 | ||
|
|
579ae2ba41 | ||
|
|
76859b64cf | ||
|
|
ca7e410663 | ||
|
|
f8b9f070d9 | ||
|
|
72d5bab427 | ||
|
|
a983dfdcdf | ||
|
|
c94ec7bbb9 | ||
|
|
1bb4bb97ed | ||
|
|
ca73563060 | ||
|
|
f1d6283384 | ||
|
|
dde451fd69 | ||
|
|
d17db7d050 | ||
|
|
fd2ad4b391 | ||
|
|
58095b6a33 | ||
|
|
1da209a902 |
No files matched your search
@@ -69,3 +69,5 @@ Thumbs.db
|
|||||||
|
|
||||||
# Unreal per-project build artefacts (FileOpenOrder logs and the like)
|
# Unreal per-project build artefacts (FileOpenOrder logs and the like)
|
||||||
Build/
|
Build/
|
||||||
|
dsh-session-*
|
||||||
|
tmp/
|
||||||
@@ -126,7 +126,7 @@ Rules that keep tools reusable and reviewable:
|
|||||||
- **Result files for Unreal.** Unreal commandlets swallow `print()`; write the outcome to `/tmp/gigline/<tool>.txt` and read that instead of trusting the exit code alone.
|
- **Result files for Unreal.** Unreal commandlets swallow `print()`; write the outcome to `/tmp/gigline/<tool>.txt` and read that instead of trusting the exit code alone.
|
||||||
- **Absolute paths into sandboxes.** Blender Flatpak (`LD_LIBRARY_PATH="$B/blender/lib"`) and Unreal do not share the repo's working directory; pass absolute paths.
|
- **Absolute paths into sandboxes.** Blender Flatpak (`LD_LIBRARY_PATH="$B/blender/lib"`) and Unreal do not share the repo's working directory; pass absolute paths.
|
||||||
- **Clean worktree for generators.** Tools that rewrite committed assets (site/street/hero builders, importers, map builders) run from a clean worktree; inspect the LFS diff before committing. Note that `.blend` and `.umap` check out read-only — `chmod u+w` first or the save silently fails.
|
- **Clean worktree for generators.** Tools that rewrite committed assets (site/street/hero builders, importers, map builders) run from a clean worktree; inspect the LFS diff before committing. Note that `.blend` and `.umap` check out read-only — `chmod u+w` first or the save silently fails.
|
||||||
- **Network and paid-API tools are human-triggered.** `Tools/commons_fetch.py`, `Tools/page_image_fetch.py`, `Tools/judge_screenshot.py` (OpenRouter) and anything similar never sit in a gate or CI. Handle HTTP 429 with back-off rather than retrying immediately.
|
- **Network and paid-API tools are human-triggered.** `Tools/gen/commons_fetch.py`, `Tools/gen/page_image_fetch.py`, `Tools/review/judge_screenshot.py` (OpenRouter) and anything similar never sit in a gate or CI. Handle HTTP 429 with back-off rather than retrying immediately.
|
||||||
- **Measure, don't assert.** Verification tools print the sampled numbers — counts, holes, Z ranges, worst step and its location, frame-time percentiles — so a claim in `TODO.md`, `CHANGELOG.md` or `Docs/` can be re-checked by re-running the command.
|
- **Measure, don't assert.** Verification tools print the sampled numbers — counts, holes, Z ranges, worst step and its location, frame-time percentiles — so a claim in `TODO.md`, `CHANGELOG.md` or `Docs/` can be re-checked by re-running the command.
|
||||||
|
|
||||||
**Document every tool.** A new or changed tool gets a row in the inventory table of the README beside it: [`Tools/README.md`](Tools/README.md), [`Geo/README.md`](Geo/README.md), or [`Art/blender/README.md`](Art/blender/README.md). What it does, its arguments, and a typical command. A tool without a README row is unfinished.
|
**Document every tool.** A new or changed tool gets a row in the inventory table of the README beside it: [`Tools/README.md`](Tools/README.md), [`Geo/README.md`](Geo/README.md), or [`Art/blender/README.md`](Art/blender/README.md). What it does, its arguments, and a typical command. A tool without a README row is unfinished.
|
||||||
@@ -179,7 +179,11 @@ Always use a consistent scale test, named export presets, and repeatable import
|
|||||||
|
|
||||||
## Blender skills
|
## Blender skills
|
||||||
|
|
||||||
`.agents/skills/blender-skills/` holds 94 Blender skill packs (MIT, blender-skills 1.3.0). They are agent instructions, not add-ons, and nothing in the pipeline depends on them.
|
The 93 upstream Blender skill packs (MIT, blender-skills 1.3.0) live once, in the **common** agent folder `~/.pi/agent/skills/`, together with the shared `references/` notes and the upstream `LICENSE`. They are agent instructions, not add-ons, and nothing in the pipeline depends on them.
|
||||||
|
|
||||||
|
Only the project-specific pack stays in the repository: `.agents/skills/blender-skills/blender-director/` — the pipeline orchestrator, which names this project's tools and stages. It refers to the shared notes by absolute path (`~/.pi/agent/skills/references/…`).
|
||||||
|
|
||||||
|
Do not copy the common packs into the repository: a second copy under a different root is a name collision, and Pi keeps the first one it finds and warns on every start. Add a pack to `~/.pi/agent/skills/<pack>/SKILL.md` (one level down, flat) and it is picked up everywhere; `references/` sits beside them at `~/.pi/agent/skills/references/`.
|
||||||
|
|
||||||
**All agents must consult the relevant skill packs before starting a task in that discipline.** Each patch section in `TODO.md` lists its suggested skills; the general mapping is:
|
**All agents must consult the relevant skill packs before starting a task in that discipline.** Each patch section in `TODO.md` lists its suggested skills; the general mapping is:
|
||||||
|
|
||||||
@@ -208,10 +212,10 @@ Always use a consistent scale test, named export presets, and repeatable import
|
|||||||
|
|
||||||
Any change that affects the game's appearance — meshes, materials, lighting, levels, character, camera, post-processing — must be rendered for human review:
|
Any change that affects the game's appearance — meshes, materials, lighting, levels, character, camera, post-processing — must be rendered for human review:
|
||||||
|
|
||||||
1. Run `Tools/render_screenshots.sh` (rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it, captures the fixed camera list, copies the PNGs into `Screenshots/`).
|
1. Run `Tools/review/render_screenshots.sh` (rebuilds `Game/GigLine/Content/Test/ReviewMap.umap` from current content, runs the game on it, captures the fixed camera list, copies the PNGs into `Screenshots/`).
|
||||||
2. Optional pre-check with a vision model: `Tools/judge_screenshot.py --image <shot.png> --expect "<what it should show>"` (or `--shots` to run every shot in the README) flags likely-missing content before a human looks. It reads `OPENROUTER_API_KEY` from `.env` (ignored). Small helper only — it never replaces the human verdict.
|
2. Optional pre-check with a vision model: `Tools/review/judge_screenshot.py --image <shot.png> --expect "<what it should show>"` (or `--shots` to run every shot in the README) flags likely-missing content before a human looks. It reads `OPENROUTER_API_KEY` from `.env` (ignored). Small helper only — it never replaces the human verdict.
|
||||||
3. Update `Screenshots/README.md` **in the same change as any visual work** — every change that affects appearance, not only new shots: the image is **embedded inline** (``) with its view, expected content, verification and approval state, plus one row in the status table at the top. The review happens in the file; a path alone is not a review, and a shot whose README section is stale is a defect.
|
3. Update `Screenshots/README.md` **in the same change as any visual work** — every change that affects appearance, not only new shots: the image is **embedded inline** (``) with its view, expected content, verification and approval state, plus one row in the status table at the top. The review happens in the file; a path alone is not a review, and a shot whose README section is stale is a defect.
|
||||||
4. The PNGs **are committed** so reviewers see exactly what the build rendered (`.gitignore` does not exclude them). Keep them in sync with this README: drop retired shots in the same change.
|
4. The PNGs **are committed** so reviewers see exactly what the build rendered (`.gitignore` does not exclude them). Keep them in sync with this README: drop retired shots in the same change.
|
||||||
5. Wait for the user's approval before treating a visual change as done; record approval by ticking both the shot's section and its row in the status table in `Screenshots/README.md`.
|
5. Wait for the user's approval before treating a visual change as done; record approval by ticking both the shot's section and its row in the status table in `Screenshots/README.md`.
|
||||||
|
|
||||||
New content that needs a different camera angle gets a new entry in the shot list in `Tools/ue_build_review_map.py`.
|
New content that needs a different camera angle gets a new entry in the shot list in `Tools/unreal/ue_build_review_map.py`.
|
||||||
@@ -153,7 +153,7 @@ The extract is complete for the block but not for every relation inside its bbox
|
|||||||
|
|
||||||
## Scale test and the Unreal frame (2026-11-07)
|
## Scale test and the Unreal frame (2026-11-07)
|
||||||
|
|
||||||
`Art/blender/scaletest/scale-test-2026-11-07.blend` — three 1 m cubes at origin, +10 m east, +10 m north, plus one red test material. Regenerate with `Tools/scale_test_export.py`; import with `Tools/ue_scale_test_import.py`. Both are the named-preset templates for production assets.
|
`Art/blender/scaletest/scale-test-2026-11-07.blend` — three 1 m cubes at origin, +10 m east, +10 m north, plus one red test material. Regenerate with `Tools/blender/scale_test_export.py`; import with `Tools/unreal/ue_scale_test_import.py`. Both are the named-preset templates for production assets.
|
||||||
|
|
||||||
Export preset **`ue_metric_v1`** (Blender FBX): `apply_unit_scale`, `apply_scale_options='FBX_SCALE_NONE'`, `axis_forward='-Y'`, `axis_up='Z'`, `use_space_transform`, no bake-space-transform. Import preset **`ue_metric_v1`** (UE `FbxImportUI`): static mesh, import materials, uniform scale 1.0, no unit conversion, no force-front-X.
|
Export preset **`ue_metric_v1`** (Blender FBX): `apply_unit_scale`, `apply_scale_options='FBX_SCALE_NONE'`, `axis_forward='-Y'`, `axis_up='Z'`, `use_space_transform`, no bake-space-transform. Import preset **`ue_metric_v1`** (UE `FbxImportUI`): static mesh, import materials, uniform scale 1.0, no unit conversion, no force-front-X.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -10,12 +10,12 @@
|
|||||||
"max": [
|
"max": [
|
||||||
13.08,
|
13.08,
|
||||||
0.13,
|
0.13,
|
||||||
14.5
|
14.7
|
||||||
],
|
],
|
||||||
"size": [
|
"size": [
|
||||||
26.16,
|
26.16,
|
||||||
12.63,
|
12.63,
|
||||||
14.5
|
14.7
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"parts": [
|
"parts": [
|
||||||
@@ -122,10 +122,10 @@
|
|||||||
"object": "Hero_Medallion_x2",
|
"object": "Hero_Medallion_x2",
|
||||||
"dims_m": [
|
"dims_m": [
|
||||||
1.1,
|
1.1,
|
||||||
0.04,
|
0.2,
|
||||||
1.1
|
1.1
|
||||||
],
|
],
|
||||||
"evidence": "photographs: two stone discs beside the motto, mounted on the parapet face"
|
"evidence": "photographs: two round reliefs beside the motto - dark rim, raised light field, dark boss"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"object": "Hero_Stylobate",
|
"object": "Hero_Stylobate",
|
||||||
@@ -166,9 +166,9 @@
|
|||||||
{
|
{
|
||||||
"object": "Hero_Banner_x3",
|
"object": "Hero_Banner_x3",
|
||||||
"dims_m": [
|
"dims_m": [
|
||||||
1.55,
|
1.35,
|
||||||
0.06,
|
0.06,
|
||||||
5.4
|
6.2
|
||||||
],
|
],
|
||||||
"evidence": "photographs: JIMMY KIMMEL LIVE banners on the column line"
|
"evidence": "photographs: JIMMY KIMMEL LIVE banners on the column line"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,85 @@ import fbx_export_preset as fbx_preset # noqa: E402
|
|||||||
MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North",
|
MESHES = ("Terrain_DEM", "Road_Hollywood", "Sidewalk_South", "Sidewalk_North",
|
||||||
"Kerb_South", "Kerb_North", "Crossing_Markings")
|
"Kerb_South", "Kerb_North", "Crossing_Markings")
|
||||||
|
|
||||||
|
# Ground surfaces are exported in slices. One 279 m slab cannot report an honest height:
|
||||||
|
# its bounding box is the highest point anywhere along the street, which is 2.2 m above the
|
||||||
|
# surface at the player's position, and that misled every placement decision in the demo.
|
||||||
|
# A 20 m slice has a bounding box that means something where it stands.
|
||||||
|
# The road is sliced for the same reason as the sidewalks: a 279 m strip imports with a single
|
||||||
|
# convex hull whose top is the strip's MAXIMUM height, so a downward trace anywhere on it lands on
|
||||||
|
# that hull - a flat lid 2 m above the visible surface. A 20 m slice has a hull that follows the
|
||||||
|
# surface it belongs to.
|
||||||
|
SEGMENTED = ("Sidewalk_North", "Sidewalk_South", "Kerb_North", "Kerb_South",
|
||||||
|
"Road_Hollywood")
|
||||||
|
SEGMENT_M = 20.0
|
||||||
|
|
||||||
|
|
||||||
|
def _ucx_box(name, x0, x1, y0, y1, z0, z1):
|
||||||
|
"""A thin convex box for a ground slice, named for Unreal's UCX convention."""
|
||||||
|
thickness = max(0.10, min(0.30, (z1 - z0) * 0.25))
|
||||||
|
cx, cy = (x0 + x1) * 0.5, (y0 + y1) * 0.5
|
||||||
|
cz = z1 - thickness * 0.5
|
||||||
|
bpy.ops.mesh.primitive_cube_add(size=1.0, location=(cx, cy, cz))
|
||||||
|
hull = bpy.context.active_object
|
||||||
|
hull.name = f"UCX_{name}_01"
|
||||||
|
hull.scale = ((x1 - x0) * 0.99, (y1 - y0) * 0.99, thickness)
|
||||||
|
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||||
|
return hull
|
||||||
|
|
||||||
|
|
||||||
|
def _segment(obj, name, out_dir, length_m):
|
||||||
|
"""Export `obj` as slices of `length_m` along X, each with its own honest bbox."""
|
||||||
|
xs = [(obj.matrix_world @ v.co).x for v in obj.data.vertices]
|
||||||
|
x0, x1 = min(xs), max(xs)
|
||||||
|
count = max(1, int(round((x1 - x0) / length_m)))
|
||||||
|
step = (x1 - x0) / count
|
||||||
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
for index in range(count):
|
||||||
|
a = x0 + index * step
|
||||||
|
b = a + step
|
||||||
|
bpy.ops.object.select_all(action="DESELECT")
|
||||||
|
obj.select_set(True)
|
||||||
|
bpy.ops.object.duplicate()
|
||||||
|
piece = bpy.context.active_object
|
||||||
|
bpy.context.view_layer.objects.active = piece
|
||||||
|
bpy.ops.object.mode_set(mode="EDIT")
|
||||||
|
# Two cuts, each with the normal pointing at the half to KEEP and clear_inner=True:
|
||||||
|
# normal +X at a -> keeps x > a
|
||||||
|
# normal -X at b -> keeps x < b
|
||||||
|
# The first version used clear_outer for the far cut and produced nested slices that
|
||||||
|
# each still spanned to the end of the street, which made every slice's bbox top out
|
||||||
|
# at the same far-away maximum.
|
||||||
|
bpy.ops.mesh.select_all(action="SELECT")
|
||||||
|
bpy.ops.mesh.bisect(plane_co=(a, 0.0, 0.0), plane_no=(1.0, 0.0, 0.0),
|
||||||
|
clear_inner=True)
|
||||||
|
bpy.ops.mesh.select_all(action="SELECT")
|
||||||
|
bpy.ops.mesh.bisect(plane_co=(b, 0.0, 0.0), plane_no=(-1.0, 0.0, 0.0),
|
||||||
|
clear_inner=True)
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
piece.name = f"{name}_{index:02d}"
|
||||||
|
zs = [(piece.matrix_world @ v.co).z for v in piece.data.vertices]
|
||||||
|
# Explicit UCX box per slice. Neither the triangulated complex collision nor
|
||||||
|
# complex-as-simple makes these meshes block in Unreal, so the collision is authored
|
||||||
|
# here: one convex box per 20 m slice, thin, with its top at the slice's surface.
|
||||||
|
xs_w = [(piece.matrix_world @ v.co).x for v in piece.data.vertices]
|
||||||
|
ys_w = [(piece.matrix_world @ v.co).y for v in piece.data.vertices]
|
||||||
|
hull = _ucx_box(piece.name, min(xs_w), max(xs_w), min(ys_w), max(ys_w),
|
||||||
|
min(zs), max(zs))
|
||||||
|
path = os.path.join(out_dir, f"{piece.name}.fbx")
|
||||||
|
bpy.ops.object.select_all(action="DESELECT")
|
||||||
|
piece.select_set(True)
|
||||||
|
hull.select_set(True)
|
||||||
|
bpy.context.view_layer.objects.active = piece
|
||||||
|
fbx_preset.export_static(path, selection=True)
|
||||||
|
bpy.data.objects.remove(hull, do_unlink=True)
|
||||||
|
print(f"[site] {piece.name:22} x {a:+.1f}..{b:+.1f} m "
|
||||||
|
f"z {min(zs):+.2f}..{max(zs):+.2f} m -> {path}")
|
||||||
|
bpy.data.objects.remove(piece, do_unlink=True)
|
||||||
|
# The context loses its active object when the duplicate goes; restore the source so
|
||||||
|
# the next iteration's operators have something to act on.
|
||||||
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
obj.select_set(True)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
|
argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
|
||||||
@@ -62,6 +141,9 @@ def main():
|
|||||||
me.update()
|
me.update()
|
||||||
_ = bpy.context.view_layer
|
_ = bpy.context.view_layer
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
if name in SEGMENTED:
|
||||||
|
_segment(obj, name, args.out, SEGMENT_M)
|
||||||
|
continue
|
||||||
path = os.path.join(args.out, name + ".fbx")
|
path = os.path.join(args.out, name + ".fbx")
|
||||||
fbx_preset.export_static(path, selection=True)
|
fbx_preset.export_static(path, selection=True)
|
||||||
if mirrored:
|
if mirrored:
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"prompt": "top-down orthographic game texture, flat even lighting, no shadows, no AO, no vignette, PBR albedo, material scan, neutral colours, smooth pale limestone marble cladding, large thin-jointed ashlar blocks, very low contrast, desaturated off-white concrete tone, fine even grain, flat even lighting, no shadows, no vignette, bright clean PBR albedo",
|
||||||
|
"profile": "albedobase",
|
||||||
|
"size": [
|
||||||
|
1024,
|
||||||
|
1024
|
||||||
|
],
|
||||||
|
"seed": 55,
|
||||||
|
"settings": {
|
||||||
|
"steps": 30,
|
||||||
|
"cfg": 6.0,
|
||||||
|
"sampler": "dpmpp_2m",
|
||||||
|
"scheduler": "karras"
|
||||||
|
},
|
||||||
|
"licence": "AlbedoBase XL v3.1 (SDXL) - game albedo, commercial use with credit",
|
||||||
|
"tool": "Tools/gen/comfyui_generate.py",
|
||||||
|
"generated_utc": "2026-11-10T13:15:21Z",
|
||||||
|
"workflow": {
|
||||||
|
"1": {
|
||||||
|
"class_type": "CheckpointLoaderSimple",
|
||||||
|
"inputs": {
|
||||||
|
"ckpt_name": "albedobaseXL_v31Large.safetensors"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"class_type": "CLIPTextEncode",
|
||||||
|
"inputs": {
|
||||||
|
"text": "top-down orthographic game texture, flat even lighting, no shadows, no AO, no vignette, PBR albedo, material scan, neutral colours, smooth pale limestone marble cladding, large thin-jointed ashlar blocks, very low contrast, desaturated off-white concrete tone, fine even grain, flat even lighting, no shadows, no vignette, bright clean PBR albedo",
|
||||||
|
"clip": [
|
||||||
|
"1",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"class_type": "CLIPTextEncode",
|
||||||
|
"inputs": {
|
||||||
|
"text": "shadows, perspective, vignette, dramatic lighting, blurry, low quality, text, watermark",
|
||||||
|
"clip": [
|
||||||
|
"1",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"class_type": "EmptyLatentImage",
|
||||||
|
"inputs": {
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
"batch_size": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"7": {
|
||||||
|
"class_type": "KSampler",
|
||||||
|
"inputs": {
|
||||||
|
"seed": 55,
|
||||||
|
"steps": 30,
|
||||||
|
"cfg": 6.0,
|
||||||
|
"sampler_name": "dpmpp_2m",
|
||||||
|
"scheduler": "karras",
|
||||||
|
"denoise": 1.0,
|
||||||
|
"model": [
|
||||||
|
"1",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"positive": [
|
||||||
|
"4",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"5",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"latent_image": [
|
||||||
|
"6",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"class_type": "VAEDecode",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"7",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"1",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"9": {
|
||||||
|
"class_type": "SaveImage",
|
||||||
|
"inputs": {
|
||||||
|
"images": [
|
||||||
|
"8",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"filename_prefix": "texture_albedobase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comfyui_prompt_id": "aa517bfa-9ad0-4adb-b01a-39e50b1f8f23",
|
||||||
|
"note": "generated locally; record the model licence in THIRD_PARTY_NOTICES.md before shipping"
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"prompt": "top-down orthographic game texture, flat even lighting, no shadows, no AO, no vignette, PBR albedo, material scan, neutral colours, smooth pale limestone marble cladding, large thin-jointed ashlar blocks, very low contrast, desaturated off-white concrete tone, fine even grain, flat even lighting, no shadows, no vignette, bright clean PBR albedo",
|
||||||
|
"profile": "krea2",
|
||||||
|
"size": [
|
||||||
|
1024,
|
||||||
|
1024
|
||||||
|
],
|
||||||
|
"seed": 55,
|
||||||
|
"settings": {
|
||||||
|
"steps": 8,
|
||||||
|
"cfg": 1.0,
|
||||||
|
"sampler": "euler",
|
||||||
|
"scheduler": "simple"
|
||||||
|
},
|
||||||
|
"licence": "Krea-2 Turbo NVFP4 - fast stochastic detail (stone, bark, foliage)",
|
||||||
|
"tool": "Tools/gen/comfyui_generate.py",
|
||||||
|
"generated_utc": "2026-11-10T13:15:33Z",
|
||||||
|
"workflow": {
|
||||||
|
"1": {
|
||||||
|
"class_type": "UNETLoader",
|
||||||
|
"inputs": {
|
||||||
|
"unet_name": "krea2_turbo_nvfp4.safetensors",
|
||||||
|
"weight_dtype": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"class_type": "CLIPLoader",
|
||||||
|
"inputs": {
|
||||||
|
"clip_name": "qwen3vl_4b_int8_convrot.safetensors",
|
||||||
|
"type": "krea2",
|
||||||
|
"device": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"class_type": "VAELoader",
|
||||||
|
"inputs": {
|
||||||
|
"vae_name": "texture_fix_vae_for_qwen_image_1_and_krea_2_bf16.safetensors"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"class_type": "CLIPTextEncode",
|
||||||
|
"inputs": {
|
||||||
|
"text": "top-down orthographic game texture, flat even lighting, no shadows, no AO, no vignette, PBR albedo, material scan, neutral colours, smooth pale limestone marble cladding, large thin-jointed ashlar blocks, very low contrast, desaturated off-white concrete tone, fine even grain, flat even lighting, no shadows, no vignette, bright clean PBR albedo",
|
||||||
|
"clip": [
|
||||||
|
"2",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"class_type": "ConditioningZeroOut",
|
||||||
|
"inputs": {
|
||||||
|
"conditioning": [
|
||||||
|
"4",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"class_type": "EmptyLatentImage",
|
||||||
|
"inputs": {
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
"batch_size": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"7": {
|
||||||
|
"class_type": "KSampler",
|
||||||
|
"inputs": {
|
||||||
|
"seed": 55,
|
||||||
|
"steps": 8,
|
||||||
|
"cfg": 1.0,
|
||||||
|
"sampler_name": "euler",
|
||||||
|
"scheduler": "simple",
|
||||||
|
"denoise": 1.0,
|
||||||
|
"model": [
|
||||||
|
"1",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"positive": [
|
||||||
|
"4",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"5",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"latent_image": [
|
||||||
|
"6",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"class_type": "VAEDecode",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"7",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"3",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"9": {
|
||||||
|
"class_type": "SaveImage",
|
||||||
|
"inputs": {
|
||||||
|
"images": [
|
||||||
|
"8",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"filename_prefix": "texture_krea2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comfyui_prompt_id": "946b5d1a-7198-43ac-8f71-afa800416803",
|
||||||
|
"note": "generated locally; record the model licence in THIRD_PARTY_NOTICES.md before shipping"
|
||||||
|
}
|
||||||
Binary file not shown.
+21
-21
@@ -38,12 +38,12 @@ The character becomes a character: clothed, haired, and measured against a gener
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Clothing** — navy tee, dark jeans and off-white shoes as three separate skinned meshes (`Tools/character_clothing_build.py`), built from the body's own faces per the `character-artist` workflow: duplicate faces, solidify for cloth thickness, one mesh per piece. Each imports with its own single material slot and is attached as a leader-pose component.
|
- **Clothing** — navy tee, dark jeans and off-white shoes as three separate skinned meshes (`Tools/blender/character_clothing_build.py`), built from the body's own faces per the `character-artist` workflow: duplicate faces, solidify for cloth thickness, one mesh per piece. Each imports with its own single material slot and is attached as a leader-pose component.
|
||||||
- **Hair cards** (`Tools/character_hair_build.py`) — a scalp cap plus 26 cards in three overlapping rows, quads only, every vertex weighted to `head`. Cards are oriented from the scalp normal at their anchor, so the cut follows the skull instead of standing out as spikes.
|
- **Hair cards** (`Tools/blender/character_hair_build.py`) — a scalp cap plus 26 cards in three overlapping rows, quads only, every vertex weighted to `head`. Cards are oriented from the scalp normal at their anchor, so the cut follows the skull instead of standing out as spikes.
|
||||||
- **Character blueprints** (`Tools/comfyui_character_blueprint.py`) — front, side, back and head views generated by the local ComfyUI instance from one description, with fixed seeds and a JSON record of prompts and prompt ids.
|
- **Character blueprints** (`Tools/blender/comfyui_character_blueprint.py`) — front, side, back and head views generated by the local ComfyUI instance from one description, with fixed seeds and a JSON record of prompts and prompt ids.
|
||||||
- **Blueprint comparison** (`Tools/character_match.py`) — figure aspect, fill and coverage deltas, mean CIE76 and dominant-palette distance, plus a side-by-side montage. The deltas stay separate on purpose: one similarity number between a photoreal sheet and a low-detail body would mean nothing.
|
- **Blueprint comparison** (`Tools/blender/character_match.py`) — figure aspect, fill and coverage deltas, mean CIE76 and dominant-palette distance, plus a side-by-side montage. The deltas stay separate on purpose: one similarity number between a photoreal sheet and a low-detail body would mean nothing.
|
||||||
- **ComfyUI driver** (`Tools/comfyui_generate.py`) and **procedural textures** (`Tools/generate_character_textures.py`), each writing a provenance sidecar.
|
- **ComfyUI driver** (`Tools/gen/comfyui_generate.py`) and **procedural textures** (`Tools/gen/generate_character_textures.py`), each writing a provenance sidecar.
|
||||||
- **Material builder** (`Tools/ue_character_materials.py`) — `M_Character_Skin` and `M_Character_Hair_Mat` built from scratch, because `unreal.MaterialEditingLibrary` segfaults in a headless commandlet when it rebuilds an existing graph.
|
- **Material builder** (`Tools/unreal/ue_character_materials.py`) — `M_Character_Skin` and `M_Character_Hair_Mat` built from scratch, because `unreal.MaterialEditingLibrary` segfaults in a headless commandlet when it rebuilds an existing graph.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ Third-person body. The pawn gains a visible, animated body and a third-person ca
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **`Tools/character_anim_build.py`**: `GigLine_Idle` (60 frames) and `GigLine_Walk` (30 frames) at 30 fps, authored as parametric cycles so the loop closes by construction. Limb axes were **measured** on the rig — a probe rotated `upperleg01.L` by 0.5 rad on each axis and read `foot.L`, which showed X swings the limb fore/aft while Y twists and Z abducts.
|
- **`Tools/blender/character_anim_build.py`**: `GigLine_Idle` (60 frames) and `GigLine_Walk` (30 frames) at 30 fps, authored as parametric cycles so the loop closes by construction. Limb axes were **measured** on the rig — a probe rotated `upperleg01.L` by 0.5 rad on each axis and read `foot.L`, which showed X swings the limb fore/aft while Y twists and Z abducts.
|
||||||
- **Third-person rig** (`ADR-9`): a visible skeletal body, a `USpringArmComponent` with collision testing, and a **V** toggle. One pawn carries both rigs; first person stays the shipped default.
|
- **Third-person rig** (`ADR-9`): a visible skeletal body, a `USpringArmComponent` with collision testing, and a **V** toggle. One pawn carries both rigs; first person stays the shipped default.
|
||||||
- **Walk capture harness**: a per-shot `ShotFollow` mode that re-places the review camera every frame in the pawn's frame, plus `SetReviewWalk` / `ReturnToStartForReview`, so a stride is captured deterministically and the fixed-camera shots still find the pawn at spawn.
|
- **Walk capture harness**: a per-shot `ShotFollow` mode that re-places the review camera every frame in the pawn's frame, plus `SetReviewWalk` / `ReturnToStartForReview`, so a stride is captured deterministically and the fixed-camera shots still find the pawn at spawn.
|
||||||
- **Review shots 6, 7 and 8** — third-person back, front and mid-stride — embedded in `Screenshots/README.md`.
|
- **Review shots 6, 7 and 8** — third-person back, front and mid-stride — embedded in `Screenshots/README.md`.
|
||||||
@@ -130,8 +130,8 @@ Retail frontage. The two retail buildings beside the hero are modelled from thei
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **`Tools/retail_frontage_build.py`**: 6800 Lee Drug and 6806–6820 built from the one usable reference (`Art/reference/retail-neighbours/`, Gb321, CC BY-SA 4.0). Lee Drug gets a bronze cornice, storefront glazing with mullions, framed portrait panels, a painted lettering band, two-tier parapet with reveals and a fluted corner tower; 6806–6820 gets a burgundy fascia, brown pilasters, white-framed windows with security bars and its own banded parapet. Built in world coordinates from the blockout bounds (Lee Drug X −33.20…−14.37, face Y −14.63, H 12.10 m; 6806–6820 X −66.08…−33.07), joined to one mesh per building with a JSON part report.
|
- **`Tools/blender/retail_frontage_build.py`**: 6800 Lee Drug and 6806–6820 built from the one usable reference (`Art/reference/retail-neighbours/`, Gb321, CC BY-SA 4.0). Lee Drug gets a bronze cornice, storefront glazing with mullions, framed portrait panels, a painted lettering band, two-tier parapet with reveals and a fluted corner tower; 6806–6820 gets a burgundy fascia, brown pilasters, white-framed windows with security bars and its own banded parapet. Built in world coordinates from the blockout bounds (Lee Drug X −33.20…−14.37, face Y −14.63, H 12.10 m; 6806–6820 X −66.08…−33.07), joined to one mesh per building with a JSON part report.
|
||||||
- **`Tools/ue_import_retail_detail.py`**: imports the two meshes with complex-as-simple collision — trim decorates a solid the player already collides with and must not add snag points.
|
- **`Tools/unreal/ue_import_retail_detail.py`**: imports the two meshes with complex-as-simple collision — trim decorates a solid the player already collides with and must not add snag points.
|
||||||
- **`Art/blender/retail/ref-analysis.md`**: the reference analysis the `references` skill pack requires, written before modelling.
|
- **`Art/blender/retail/ref-analysis.md`**: the reference analysis the `references` skill pack requires, written before modelling.
|
||||||
- **`Tools/_common.py`** and `Tools/tests/test_retail_frontage.py`: the bay-layout arithmetic, extracted so it is testable without Blender (4 tests).
|
- **`Tools/_common.py`** and `Tools/tests/test_retail_frontage.py`: the bay-layout arithmetic, extracted so it is testable without Blender (4 tests).
|
||||||
- **Review shot 5**, `demo/review-demo-retail-frontage`, embedded in `Screenshots/README.md`.
|
- **Review shot 5**, `demo/review-demo-retail-frontage`, embedded in `Screenshots/README.md`.
|
||||||
@@ -144,7 +144,7 @@ Retail frontage. The two retail buildings beside the hero are modelled from thei
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **`Tools/render_screenshots.sh` never captured anything.** The capture step called `./Engine/...` while every commandlet above it went through `run_cmdlet`'s `cd "$ENGINE"`. Under `set -e` the script died at exit 127 _after_ rebuilding the maps, so the PNGs silently stayed a day old and `--verify` never ran.
|
- **`Tools/review/render_screenshots.sh` never captured anything.** The capture step called `./Engine/...` while every commandlet above it went through `run_cmdlet`'s `cd "$ENGINE"`. Under `set -e` the script died at exit 127 _after_ rebuilding the maps, so the PNGs silently stayed a day old and `--verify` never ran.
|
||||||
- **Duplicate `RetailTrim_*` actors on every rebuild**: the demo builder's clear list did not include the new label, so each run stacked another copy. Caught by the scene audit, not by eye.
|
- **Duplicate `RetailTrim_*` actors on every rebuild**: the demo builder's clear list did not include the new label, so each run stacked another copy. Caught by the scene audit, not by eye.
|
||||||
- **Mural plates were placed 0.20 m inside the solid blockout volume** and never rendered; the inset is now faked with a proud frame and a near-flush plate.
|
- **Mural plates were placed 0.20 m inside the solid blockout volume** and never rendered; the inset is now faked with a proud frame and a near-flush plate.
|
||||||
- **Trim detail sat 0.05 m off the wall**, under a pixel of depth at 40 m, so the wall won the depth test. Trim planes now stand 0.10–0.20 m proud.
|
- **Trim detail sat 0.05 m off the wall**, under a pixel of depth at 40 m, so the wall won the depth test. Trim planes now stand 0.10–0.20 m proud.
|
||||||
@@ -170,8 +170,8 @@ Façade finishing. The kit gets the hero detail it was missing, and a class of s
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Scene audit** (`Tools/ue_audit_scene.py`): fails when any staged mesh has no material, when a frontage building is more than ±1 m off its verified footprint, or when two actors share a label. Wired into `Tools/render_screenshots.sh`, which now runs the whole staging chain in the correct order and stops on an audit failure.
|
- **Scene audit** (`Tools/unreal/ue_audit_scene.py`): fails when any staged mesh has no material, when a frontage building is more than ±1 m off its verified footprint, or when two actors share a label. Wired into `Tools/review/render_screenshots.sh`, which now runs the whole staging chain in the correct order and stops on an audit failure.
|
||||||
- **`Tools/ue_placement.py`**: one source of truth for staged positions and the verified frontage spans, with `span_error` / `describe_span_failure` validators.
|
- **`Tools/unreal/ue_placement.py`**: one source of truth for staged positions and the verified frontage spans, with `span_error` / `describe_span_failure` validators.
|
||||||
- **Hero fluting**: 20 grooves per column as shallow shadow-tone cylinders (120 pieces) rather than boolean cuts.
|
- **Hero fluting**: 20 grooves per column as shallow shadow-tone cylinders (120 pieces) rather than boolean cuts.
|
||||||
- **Presence of references for the retail run**: `Art/reference/retail-neighbours/` (6800 Lee Drug and its neighbour). Local only, not committed.
|
- **Presence of references for the retail run**: `Art/reference/retail-neighbours/` (6800 Lee Drug and its neighbour). Local only, not committed.
|
||||||
|
|
||||||
@@ -210,9 +210,9 @@ Building façades. The block goes from flat extruded boxes with flat colours to
|
|||||||
- **Generated surface textures** — deterministic stdlib PNGs for ashlar stone, stucco, asphalt and concrete (base colour + roughness), sampled world-aligned with per-material tiling; byte-identical on re-run.
|
- **Generated surface textures** — deterministic stdlib PNGs for ashlar stone, stucco, asphalt and concrete (base colour + roughness), sampled world-aligned with per-material tiling; byte-identical on re-run.
|
||||||
- **Materials**: masonry coursing and roughness variation in the material graphs; `M_FacadeStoneShadow`, `M_FacadeGlass` (specular 0.62 ≈ IOR 1.5) and `M_FacadeBanner`. Material assignment is now **per slot**, keyed on the imported slot material, instead of slot 0 only.
|
- **Materials**: masonry coursing and roughness variation in the material graphs; `M_FacadeStoneShadow`, `M_FacadeGlass` (specular 0.62 ≈ IOR 1.5) and `M_FacadeBanner`. Material assignment is now **per slot**, keyed on the imported slot material, instead of slot 0 only.
|
||||||
- **El Capitan Theatre trim**: projecting marquee (12.0 × 2.60 m canopy at a 4.60 m soffit), illuminated fascia, recessed soffit, and a vertical blade sign (1.30 × 5.20 m) with frame and wall bracket.
|
- **El Capitan Theatre trim**: projecting marquee (12.0 × 2.60 m canopy at a 4.60 m soffit), illuminated fascia, recessed soffit, and a vertical blade sign (1.30 × 5.20 m) with frame and wall bracket.
|
||||||
- **Visual review loop** (`Tools/visual_loop.py` + `visual_targets.json`): verifies every capture against an expectation (missing, blank, tone-less or wrong-size shots fail) and measures each render against its reference photograph, emitting feedback that names the discipline to consult. Iterations are written under `Art/reference/iterations/`, which is gitignored until a shot is confirmed.
|
- **Visual review loop** (`Tools/review/visual_loop.py` + `visual_targets.json`): verifies every capture against an expectation (missing, blank, tone-less or wrong-size shots fail) and measures each render against its reference photograph, emitting feedback that names the discipline to consult. Iterations are written under `Art/reference/iterations/`, which is gitignored until a shot is confirmed.
|
||||||
- **`Tools/fbx_export_preset.py`** — one export preset replacing six copies, and it exports smoothing groups (`FACE`), so round geometry is no longer faceted.
|
- **`Tools/blender/fbx_export_preset.py`** — one export preset replacing six copies, and it exports smoothing groups (`FACE`), so round geometry is no longer faceted.
|
||||||
- **`Tools/ue_common.py`** — clears the read-only bit an LFS `.umap` checks out with and makes a refused save loud.
|
- **`Tools/unreal/ue_common.py`** — clears the read-only bit an LFS `.umap` checks out with and makes a refused save loud.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ Packaging and setup fixes. No gameplay or visual scope; this patch makes the pip
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **Map imports could report success while saving nothing.** `.umap` files are LFS-tracked and check out read-only, so `save_map()` returned `False` with no error and the staged actor vanished on the next load. New `Tools/ue_common.py` (`make_writable`, `require_saved`) clears the read-only bit and makes the failure loud; `ue_hero_facade_import.py` and `ue_import_north_buildings.py` now use it. Verified from a forced `0o444` map: chmod logged, `save_map: True`, exit 0.
|
- **Map imports could report success while saving nothing.** `.umap` files are LFS-tracked and check out read-only, so `save_map()` returned `False` with no error and the staged actor vanished on the next load. New `Tools/unreal/ue_common.py` (`make_writable`, `require_saved`) clears the read-only bit and makes the failure loud; `ue_hero_facade_import.py` and `ue_import_north_buildings.py` now use it. Verified from a forced `0o444` map: chmod logged, `save_map: True`, exit 0.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -268,8 +268,8 @@ Scale and façade correction patch. Buildings and the hero elevation now match t
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **`Tools/facade_detail_concepts.py`** — generates frontal building plates (whole elevation, entrance in its façade, building with neighbours) from a local reference photograph through OpenRouter image models. Spec-driven, never overwrites, writes a provenance sidecar per image, output stays in the gitignored `Art/reference/` tree. Local working material only.
|
- **`Tools/gen/facade_detail_concepts.py`** — generates frontal building plates (whole elevation, entrance in its façade, building with neighbours) from a local reference photograph through OpenRouter image models. Spec-driven, never overwrites, writes a provenance sidecar per image, output stays in the gitignored `Art/reference/` tree. Local working material only.
|
||||||
- **`Tools/fbx_export_preset.py`** — the single FBX export preset (`ue_metric_v1` / `ue_character_v1`). Replaces the argument block that had been copied into six exporters.
|
- **`Tools/blender/fbx_export_preset.py`** — the single FBX export preset (`ue_metric_v1` / `ue_character_v1`). Replaces the argument block that had been copied into six exporters.
|
||||||
- **Façade occlusion probe** in `-qaselftest`: 40 m traces sideways from the road centre at walking height, required wherever a building is staged.
|
- **Façade occlusion probe** in `-qaselftest`: 40 m traces sideways from the road centre at walking height, required wherever a building is staged.
|
||||||
- **Ionic detail on the hero**: base moulding at every shaft foot, a square abacus over every capital, and a 52-dentil cornice course.
|
- **Ionic detail on the hero**: base moulding at every shaft foot, a square abacus over every capital, and a 52-dentil cornice course.
|
||||||
- **Tool tests**: `Tools/tests/` now covers the input driver, height classification, the Mapbox cross-check, the concept generator and the FBX preset (49 tests).
|
- **Tool tests**: `Tools/tests/` now covers the input driver, height classification, the Mapbox cross-check, the concept generator and the FBX preset (49 tests).
|
||||||
@@ -335,16 +335,16 @@ The first playable release: a walkable exterior demo of the **6840 Hollywood Blv
|
|||||||
|
|
||||||
- **Unreal project** (`Game/GigLine`) — C++ game module, Enhanced Input, packaged Linux build, and a `DemoMap` that a plain launch opens directly.
|
- **Unreal project** (`Game/GigLine`) — C++ game module, Enhanced Input, packaged Linux build, and a `DemoMap` that a plain launch opens directly.
|
||||||
- **Walkable demo level** — terrain from the 3DEP DEM, Hollywood Blvd carriageway, both sidewalks and kerbs with collision, the designated mid-block crossing, and fixed daytime lighting (sun, sky light, sky atmosphere, height fog, pinned exposure).
|
- **Walkable demo level** — terrain from the 3DEP DEM, Hollywood Blvd carriageway, both sidewalks and kerbs with collision, the designated mid-block crossing, and fixed daytime lighting (sun, sky light, sky atmosphere, height fog, pinned exposure).
|
||||||
- **Hero façade** for 6840 Hollywood Blvd — six-column neoclassical elevation with three genuine portal recesses, entablature, parapet and motto band (`Tools/hero_facade_build.py`, `Tools/ue_hero_facade_import.py`).
|
- **Hero façade** for 6840 Hollywood Blvd — six-column neoclassical elevation with three genuine portal recesses, entablature, parapet and motto band (`Tools/blender/hero_facade_build.py`, `Tools/unreal/ue_hero_facade_import.py`).
|
||||||
- **Block frontage** — the five verified south-side neighbours (6922, 6902, 6838 El Capitan Theatre, 6822 Budget, 6806–6822, 6800 Lee Drug) and the two north-side buildings (TCL Chinese Theatre, Madame Tussaud's) placed at their OSM positions.
|
- **Block frontage** — the five verified south-side neighbours (6922, 6902, 6838 El Capitan Theatre, 6822 Budget, 6806–6822, 6800 Lee Drug) and the two north-side buildings (TCL Chinese Theatre, Madame Tussaud's) placed at their OSM positions.
|
||||||
- **First-person player** — `AGigLineCharacter`: 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look, jump, reset (`R`) and pause (`Escape`); controls documented in `Docs/controls.md` and shown in-game.
|
- **First-person player** — `AGigLineCharacter`: 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look, jump, reset (`R`) and pause (`Escape`); controls documented in `Docs/controls.md` and shown in-game.
|
||||||
- **In-game ODbL attribution** — `AGigLineHUD` draws the OpenStreetMap credit and licence line every frame.
|
- **In-game ODbL attribution** — `AGigLineHUD` draws the OpenStreetMap credit and licence line every frame.
|
||||||
- **QA harness** — `-qaselftest` (walk, reset, pause, seam transect, invisible-wall probe, frame-time capture) and `Tools/input_test.py` / `Tools/input_emulation.py` for OS-level virtual keyboard/mouse input.
|
- **QA harness** — `-qaselftest` (walk, reset, pause, seam transect, invisible-wall probe, frame-time capture) and `Tools/input/input_test.py` / `Tools/input/input_emulation.py` for OS-level virtual keyboard/mouse input.
|
||||||
- **Documentation** — setup, build, toolchain, licensing, ODbL review, release checks, and arc42 architecture docs.
|
- **Documentation** — setup, build, toolchain, licensing, ODbL review, release checks, and arc42 architecture docs.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **All controls were dead in packaged and PIE builds.** The mapping-context generator wrote key bindings to the deprecated `mappings` array; UE 5.8 reads `DefaultKeyMappings.Mappings`, so the cooked game had zero runtime mappings and every key resolved to nothing. `Tools/ue_create_input_assets.py` now populates the current field.
|
- **All controls were dead in packaged and PIE builds.** The mapping-context generator wrote key bindings to the deprecated `mappings` array; UE 5.8 reads `DefaultKeyMappings.Mappings`, so the cooked game had zero runtime mappings and every key resolved to nothing. `Tools/unreal/ue_create_input_assets.py` now populates the current field.
|
||||||
- **"Fall-through" was an 8 m invisible box.** `Terrain_DEM` shipped only a bounding-box collision proxy; forcing complex-as-simple collision (`UGigLineAssetTools`) put the player on the actual street.
|
- **"Fall-through" was an 8 m invisible box.** `Terrain_DEM` shipped only a bounding-box collision proxy; forcing complex-as-simple collision (`UGigLineAssetTools`) put the player on the actual street.
|
||||||
- **Reset returned the player to mid-air** — the start position is now captured on the first grounded frame.
|
- **Reset returned the player to mid-air** — the start position is now captured on the first grounded frame.
|
||||||
- **Plain launch opened the engine template** — the default map now lives in `DefaultEngine.ini` and points at `DemoMap`.
|
- **Plain launch opened the engine template** — the default map now lives in `DefaultEngine.ini` and points at `DemoMap`.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ All modelling and geographic processing live outside the game project in `Art/`,
|
|||||||
|
|
||||||
## Quality achievement
|
## Quality achievement
|
||||||
|
|
||||||
- **Realism first**: reference photography and measured data drive every façade decision; renders are compared against the photographs with `Tools/visual_loop.py`, and a façade is built from the shared 4.96 m module kit so the street wall reads consistently. Materials are authored in Unreal with world-aligned generated textures, and lighting is fixed daytime so a shot is comparable between runs.
|
- **Realism first**: reference photography and measured data drive every façade decision; renders are compared against the photographs with `Tools/review/visual_loop.py`, and a façade is built from the shared 4.96 m module kit so the street wall reads consistently. Materials are authored in Unreal with world-aligned generated textures, and lighting is fixed daytime so a shot is comparable between runs.
|
||||||
- **Performance as a budget, not a driver**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw for street-level geometry, and a 60 FPS at 1440p floor that every release must meet. It closes the quality list because it constrains how much realism fits, not what the block should look like.
|
- **Performance as a budget, not a driver**: dynamic-only lighting (no build step, no static shadow maps), world-space-noise materials (cheap procedural variation), minimal overdraw for street-level geometry, and a 60 FPS at 1440p floor that every release must meet. It closes the quality list because it constrains how much realism fits, not what the block should look like.
|
||||||
|
|
||||||
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
- **Walkability**: complex-as-simple collision on terrain (C++ helper forces the flag on the asset), block-all collision on every site mesh, swept transect QA verifies zero raycast holes.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Static meshes and materials in `/Game/Hollywood/Site/`:
|
|||||||
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
| `SM_HeroFacade` | El Capitan Entertainment Centre detailed façade |
|
||||||
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
| `SM_ElCapitanTheatre`, `SM_Budget_6822`, `SM_Hollywood_6806`, `SM_Hollywood_6902`, `SM_Hollywood_6922`, `SM_LeeDrug_6800` | South block frontage (6 buildings) |
|
||||||
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
| `SM_Madame_Tussauds`, `SM_TCL_Chinese_Theatre` | North-side buildings (within 50 m) |
|
||||||
| `SM_Retail_LeeDrug`, `SM_Retail_6806` | Retail trim over 6800 and 6806–6820, modelled from their own photograph (`Tools/retail_frontage_build.py`, staged as `RetailTrim_*`). `6822 Budget` stays a bare volume: no photograph exists. |
|
| `SM_Retail_LeeDrug`, `SM_Retail_6806` | Retail trim over 6800 and 6806–6820, modelled from their own photograph (`Tools/blender/retail_frontage_build.py`, staged as `RetailTrim_*`). `6822 Budget` stays a bare volume: no photograph exists. |
|
||||||
|
|
||||||
### Art Pipeline
|
### Art Pipeline
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
## Screenshot capture
|
## Screenshot capture
|
||||||
|
|
||||||
1. `Tools/render_screenshots.sh` compiles editor, builds `ReviewMap` from `Tools/ue_build_review_map.py`, launches game in `-game` mode.
|
1. `Tools/review/render_screenshots.sh` compiles editor, builds `ReviewMap` from `Tools/unreal/ue_build_review_map.py`, launches game in `-game` mode.
|
||||||
|
|
||||||
2. `AGigLineShotActor` iterates the camera list, sets view, calls `HighResShot 1280x720`, waits for capture.
|
2. `AGigLineShotActor` iterates the camera list, sets view, calls `HighResShot 1280x720`, waits for capture.
|
||||||
|
|
||||||
@@ -48,6 +48,6 @@
|
|||||||
|
|
||||||
1. Blender headless (`Tools/export_*.py`) reads `.blend` files, applies metre→centimetre conversion, exports FBX with `ue_metric_v1` preset.
|
1. Blender headless (`Tools/export_*.py`) reads `.blend` files, applies metre→centimetre conversion, exports FBX with `ue_metric_v1` preset.
|
||||||
|
|
||||||
2. Unreal headless (`Tools/ue_import_*.py`) imports FBX via Interchange, places meshes at geographic coordinates, sets collision profiles.
|
2. Unreal headless (`Tools/unreal/ue_import_*.py`) imports FBX via Interchange, places meshes at geographic coordinates, sets collision profiles.
|
||||||
|
|
||||||
3. `Tools/ue_build_demo_map.py` assembles `DemoMap`: creates/repositions actors, sets lighting, saves map and assets.
|
3. `Tools/unreal/ue_build_demo_map.py` assembles `DemoMap`: creates/repositions actors, sets lighting, saves map and assets.
|
||||||
@@ -22,7 +22,7 @@ Ordered by priority: realism first, performance last. The order decides design t
|
|||||||
|
|
||||||
| ID | Source/Stimulus | Response | Metric |
|
| ID | Source/Stimulus | Response | Metric |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| QS-1 | Screenshot is compared against its reference photograph | The render reads as the same building | Similarity score and visual match confirmed by the owner; `Tools/visual_loop.py` reports the deltas |
|
| QS-1 | Screenshot is compared against its reference photograph | The render reads as the same building | Similarity score and visual match confirmed by the owner; `Tools/review/visual_loop.py` reports the deltas |
|
||||||
| QS-2 | A façade is placed from measured data | It sits on its verified footprint | World span probed against `Geo/verification/` within ~1 m |
|
| QS-2 | A façade is placed from measured data | It sits on its verified footprint | World span probed against `Geo/verification/` within ~1 m |
|
||||||
| QS-3 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
| QS-3 | Player walks both sides of the demo | No fall-through, character stays at ground Z | 0 raycast holes in 84-sample transect |
|
||||||
| QS-4 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
| QS-4 | Player holds W for 2 s on flat road | Character moves forward | ≥ 200 cm displacement, Z constant within ±5 cm |
|
||||||
@@ -41,5 +41,5 @@ Ordered by priority: realism first, performance last. The order decides design t
|
|||||||
| BR-1 | A building with a reference photograph has a review shot compared against it | Silhouette and proportion deltas recorded per building in `Screenshots/README.md`; owner approval per shot |
|
| BR-1 | A building with a reference photograph has a review shot compared against it | Silhouette and proportion deltas recorded per building in `Screenshots/README.md`; owner approval per shot |
|
||||||
| BR-2 | Every building is either photo-matched or explicitly recorded as an unfounded blockout | List in `TODO.md`; no building in an in-between state |
|
| BR-2 | Every building is either photo-matched or explicitly recorded as an unfounded blockout | List in `TODO.md`; no building in an in-between state |
|
||||||
| BR-3 | Every building has a collision proxy and at least one LOD | Count of buildings missing either = 0 |
|
| BR-3 | Every building has a collision proxy and at least one LOD | Count of buildings missing either = 0 |
|
||||||
| BR-4 | No surface carries a placeholder material | Material audit in `Tools/ue_audit_scene.py` reports 0 placeholder slots |
|
| BR-4 | No surface carries a placeholder material | Material audit in `Tools/unreal/ue_audit_scene.py` reports 0 placeholder slots |
|
||||||
| BR-5 | The realism pass does not break the floor | QS-9 still holds at 1440p, with the measured cost of the pass recorded |
|
| BR-5 | The realism pass does not break the floor | QS-9 still holds at 1440p, with the measured cost of the pass recorded |
|
||||||
+19
-19
@@ -56,9 +56,9 @@ Both content-acceptance tests run headless and are the template for all asset im
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Static-mesh scale test (Blender export -> Unreal import -> measure)
|
# Static-mesh scale test (Blender export -> Unreal import -> measure)
|
||||||
# Blender: Tools/scale_test_export.py, Unreal: Tools/ue_scale_test_import.py
|
# Blender: Tools/blender/scale_test_export.py, Unreal: Tools/unreal/ue_scale_test_import.py
|
||||||
# MPFB character (generate -> rig -> bake -> export -> skeletal import)
|
# MPFB character (generate -> rig -> bake -> export -> skeletal import)
|
||||||
# Blender: Tools/mpfb_character_export.py, Unreal: Tools/ue_character_import.py
|
# Blender: Tools/blender/mpfb_character_export.py, Unreal: Tools/unreal/ue_character_import.py
|
||||||
|
|
||||||
# Unreal side of any test:
|
# Unreal side of any test:
|
||||||
./Engine/Binaries/Linux/UnrealEditor-Cmd \
|
./Engine/Binaries/Linux/UnrealEditor-Cmd \
|
||||||
@@ -78,22 +78,22 @@ The hero block and the demo level are built by a fixed sequence. Each step write
|
|||||||
| Street | `Art/blender/street/street_mesh.py` | `Art/blender/street/street-*.blend` |
|
| Street | `Art/blender/street/street_mesh.py` | `Art/blender/street/street-*.blend` |
|
||||||
| Site assembly | `Art/blender/site/site_assembly.py` | `Art/blender/site/site-*.blend` |
|
| Site assembly | `Art/blender/site/site_assembly.py` | `Art/blender/site/site-*.blend` |
|
||||||
| Site export | `Art/blender/site/export_site_fbx.py` | seven FBXs, one per mesh (terrain, road, both sidewalks, both kerbs, crossing) |
|
| Site export | `Art/blender/site/export_site_fbx.py` | seven FBXs, one per mesh (terrain, road, both sidewalks, both kerbs, crossing) |
|
||||||
| Hero façade | `Tools/hero_facade_build.py` | `Art/blender/hero/hero-facade-*.blend` + JSON part report |
|
| Hero façade | `Tools/blender/hero_facade_build.py` | `Art/blender/hero/hero-facade-*.blend` + JSON part report |
|
||||||
| Block frontage | `Tools/export_block_frontage.py` | six frontage FBXs from the blockout |
|
| Block frontage | `Tools/blender/export_block_frontage.py` | six frontage FBXs from the blockout |
|
||||||
| North buildings | `Tools/export_north_buildings.py` | TCL and Tussaud's FBXs from the blockout |
|
| North buildings | `Tools/blender/export_north_buildings.py` | TCL and Tussaud's FBXs from the blockout |
|
||||||
| Site import | `Tools/ue_import_site.py` | `/Game/Hollywood/Site/*` |
|
| Site import | `Tools/unreal/ue_import_site.py` | `/Game/Hollywood/Site/*` |
|
||||||
| Frontage import | `Tools/ue_import_block_frontage.py` | `/Game/Hollywood/Frontage/*` |
|
| Frontage import | `Tools/unreal/ue_import_block_frontage.py` | `/Game/Hollywood/Frontage/*` |
|
||||||
| North import | `Tools/ue_import_north_buildings.py` | `/Game/Hollywood/North/*` |
|
| North import | `Tools/unreal/ue_import_north_buildings.py` | `/Game/Hollywood/North/*` |
|
||||||
| Hero import | `Tools/ue_hero_facade_import.py` | `/Game/Hollywood/ElCapitan/SM_HeroFacade` |
|
| Hero import | `Tools/unreal/ue_hero_facade_import.py` | `/Game/Hollywood/ElCapitan/SM_HeroFacade` |
|
||||||
| Demo map | `Tools/ue_build_demo_map.py` | `/Game/Hollywood/DemoMap` |
|
| Demo map | `Tools/unreal/ue_build_demo_map.py` | `/Game/Hollywood/DemoMap` |
|
||||||
| Façade kit assembly | `Tools/ue_apply_facade_kit.py` | kit actors in `DemoMap` |
|
| Façade kit assembly | `Tools/unreal/ue_apply_facade_kit.py` | kit actors in `DemoMap` |
|
||||||
| Theatre trim | `Tools/ue_stage_theatre_trim.py` | marquee + blade sign actors |
|
| Theatre trim | `Tools/unreal/ue_stage_theatre_trim.py` | marquee + blade sign actors |
|
||||||
| Scene audit | `Tools/ue_audit_scene.py` | materials, placement spans, labels (gate) |
|
| Scene audit | `Tools/unreal/ue_audit_scene.py` | materials, placement spans, labels (gate) |
|
||||||
| Review shots | `Tools/ue_build_review_map.py` + `Tools/render_screenshots.sh` | `Screenshots/` |
|
| Review shots | `Tools/unreal/ue_build_review_map.py` + `Tools/review/render_screenshots.sh` | `Screenshots/` |
|
||||||
|
|
||||||
**Order matters.** `ue_build_demo_map.py` rebuilds the site materials, so any actor staged by another tool must be (re)staged _after_ it, or it keeps a reference to a material that no longer exists — that is how 604 actors once ended up untethered from their materials while the map only looked slightly flat. `Tools/render_screenshots.sh` runs the full chain in the right order and gates on `ue_audit_scene.py`.
|
**Order matters.** `ue_build_demo_map.py` rebuilds the site materials, so any actor staged by another tool must be (re)staged _after_ it, or it keeps a reference to a material that no longer exists — that is how 604 actors once ended up untethered from their materials while the map only looked slightly flat. `Tools/review/render_screenshots.sh` runs the full chain in the right order and gates on `ue_audit_scene.py`.
|
||||||
|
|
||||||
Every Blender exporter calls [`Tools/fbx_export_preset.py`](../Tools/fbx_export_preset.py) (`ue_metric_v1` / `ue_character_v1`) instead of repeating the FBX argument block — one place for the scale conversion, the axis mapping and `mesh_smooth_type="FACE"` (without which every Unreal import warns about missing smoothing groups and round geometry arrives faceted).
|
Every Blender exporter calls [`Tools/blender/fbx_export_preset.py`](../Tools/blender/fbx_export_preset.py) (`ue_metric_v1` / `ue_character_v1`) instead of repeating the FBX argument block — one place for the scale conversion, the axis mapping and `mesh_smooth_type="FACE"` (without which every Unreal import warns about missing smoothing groups and round geometry arrives faceted).
|
||||||
|
|
||||||
Two rules that the pipeline enforces rather than documents:
|
Two rules that the pipeline enforces rather than documents:
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ Two rules that the pipeline enforces rather than documents:
|
|||||||
## Known engine quirks (2026-11-07)
|
## Known engine quirks (2026-11-07)
|
||||||
|
|
||||||
- One handled `PSOPrecache` ensure fires at packaged-build exit only; not blocking.
|
- One handled `PSOPrecache` ensure fires at packaged-build exit only; not blocking.
|
||||||
- Interchange 5.8 does not create material/texture assets for skeletal FBX imports — `Tools/ue_character_import.py` finishes that chain explicitly.
|
- Interchange 5.8 does not create material/texture assets for skeletal FBX imports — `Tools/unreal/ue_character_import.py` finishes that chain explicitly.
|
||||||
- Android Turnkey status errors and datarouter DNS warnings in logs are benign.
|
- Android Turnkey status errors and datarouter DNS warnings in logs are benign.
|
||||||
|
|
||||||
## Packaged Linux build
|
## Packaged Linux build
|
||||||
@@ -129,13 +129,13 @@ cd /run/media/system/DATA/Builds/GigLineQA/Linux
|
|||||||
| `Add-on not loaded: "bl_ext.blender_org.mpfb"` | `mpfb_character_export.py` needs the MPFB add-on visible to Blender | export `BLENDER_USER_SCRIPTS` (see the invocation in [../Art/blender/README.md](../Art/blender/README.md)) |
|
| `Add-on not loaded: "bl_ext.blender_org.mpfb"` | `mpfb_character_export.py` needs the MPFB add-on visible to Blender | export `BLENDER_USER_SCRIPTS` (see the invocation in [../Art/blender/README.md](../Art/blender/README.md)) |
|
||||||
| The packaged run exits after a couple of seconds during QA | `GigLineShotActor` runs its screenshot list and quits (`bQuitWhenDone`) | it skips its rounds when `-qaselftest` is on the command line — keep that guard |
|
| The packaged run exits after a couple of seconds during QA | `GigLineShotActor` runs its screenshot list and quits (`bQuitWhenDone`) | it skips its rounds when `-qaselftest` is on the command line — keep that guard |
|
||||||
| Headless `-nullrhi` traces report no hits for everything | the null RHI has no physics scene | verify traces in the **packaged** run (`-qaselftest`), never in a commandlet |
|
| Headless `-nullrhi` traces report no hits for everything | the null RHI has no physics scene | verify traces in the **packaged** run (`-qaselftest`), never in a commandlet |
|
||||||
| Import logs "No smoothing group information" for every mesh | `mesh_smooth_type="OFF"` in the FBX export | use `Tools/fbx_export_preset.py` (`FACE`) |
|
| Import logs "No smoothing group information" for every mesh | `mesh_smooth_type="OFF"` in the FBX export | use `Tools/blender/fbx_export_preset.py` (`FACE`) |
|
||||||
|
|
||||||
## PIE input test (compare editor vs packaged input intake)
|
## PIE input test (compare editor vs packaged input intake)
|
||||||
|
|
||||||
Use to isolate whether the "no controls respond" bug is the packaged standalone's input intake or the Enhanced Input wiring generally.
|
Use to isolate whether the "no controls respond" bug is the packaged standalone's input intake or the Enhanced Input wiring generally.
|
||||||
|
|
||||||
- `Tools/run_pie.sh` opens the editor on `/Game/Hollywood/DemoMap`. Press the green **Play** (PIE) button and walk / mouse-look.
|
- `Tools/input/run_pie.sh` opens the editor on `/Game/Hollywood/DemoMap`. Press the green **Play** (PIE) button and walk / mouse-look.
|
||||||
- Interpretation:
|
- Interpretation:
|
||||||
- PIE **works** -> the Enhanced Input wiring is fine and the standalone packaged build's SDL input intake is the environment-dependent bug.
|
- PIE **works** -> the Enhanced Input wiring is fine and the standalone packaged build's SDL input intake is the environment-dependent bug.
|
||||||
- PIE **fails** too -> the Enhanced Input setup itself is broken; rebuild the input config / character from scratch.
|
- PIE **fails** too -> the Enhanced Input setup itself is broken; rebuild the input config / character from scratch.
|
||||||
|
|||||||
+3
-3
@@ -19,7 +19,7 @@ Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (`
|
|||||||
|
|
||||||
- Movement is relative to where you are looking (controller yaw), so the capsule stays upright on the sloped street.
|
- Movement is relative to where you are looking (controller yaw), so the capsule stays upright on the sloped street.
|
||||||
- The mouse is captured on launch, so looking works without clicking first. `Escape` toggles the capture as part of pause.
|
- The mouse is captured on launch, so looking works without clicking first. `Escape` toggles the capture as part of pause.
|
||||||
- Bindings are Enhanced Input actions in `/Game/Input` built by `Tools/ue_create_input_assets.py`; the mapping context is `IMC_Default`.
|
- Bindings are Enhanced Input actions in `/Game/Input` built by `Tools/unreal/ue_create_input_assets.py`; the mapping context is `IMC_Default`.
|
||||||
|
|
||||||
## Changes since `0.1.0`
|
## Changes since `0.1.0`
|
||||||
|
|
||||||
@@ -48,10 +48,10 @@ The walking shot is driven per shot rather than by the switch: the shot actor en
|
|||||||
|
|
||||||
## Testing input without a physical keyboard
|
## Testing input without a physical keyboard
|
||||||
|
|
||||||
`Tools/input_test.py` injects OS-level keyboard/mouse events through a virtual `/dev/uinput` device (see `Tools/input_emulation.py`), e.g.:
|
`Tools/input/input_test.py` injects OS-level keyboard/mouse events through a virtual `/dev/uinput` device (see `Tools/input/input_emulation.py`), e.g.:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 Tools/input_test.py --sequence move:2,look:3
|
python3 Tools/input/input_test.py --sequence move:2,look:3
|
||||||
```
|
```
|
||||||
|
|
||||||
It drives the focused game window exactly like real hardware, which is how the controls were regression-tested on the packaged build.
|
It drives the focused game window exactly like real hardware, which is how the controls were regression-tested on the packaged build.
|
||||||
@@ -50,7 +50,7 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB
|
|||||||
|
|
||||||
## `0.1.0` — Placement vs references — record (2026-11-08)
|
## `0.1.0` — Placement vs references — record (2026-11-08)
|
||||||
|
|
||||||
Checked against the reference photography (`Art/reference/`) and the Geo measurements (`Geo/verification/*`). Build under test: current `main` content, DemoMap rendered via `Tools/render_screenshots.sh` (2026-11-08, Vulkan SM5, 1920×1080); engine positions probed from the DemoMap (commandlet, 2026-11-08).
|
Checked against the reference photography (`Art/reference/`) and the Geo measurements (`Geo/verification/*`). Build under test: current `main` content, DemoMap rendered via `Tools/review/render_screenshots.sh` (2026-11-08, Vulkan SM5, 1920×1080); engine positions probed from the DemoMap (commandlet, 2026-11-08).
|
||||||
|
|
||||||
### Placement and scale (in-engine ↔ reference)
|
### Placement and scale (in-engine ↔ reference)
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ Checked against the reference photography (`Art/reference/`) and the Geo measure
|
|||||||
- **Crossing ramps are kerb flushes, not graded ramps** — the `0.1.1` Crossing ramp gradient item.
|
- **Crossing ramps are kerb flushes, not graded ramps** — the `0.1.1` Crossing ramp gradient item.
|
||||||
- Site identity (the neoclassical hero with motto band) matches the 2009 façade photograph and the 1922 Masonic elevation within blockout fidelity.
|
- Site identity (the neoclassical hero with motto band) matches the 2009 façade photograph and the 1922 Masonic elevation within blockout fidelity.
|
||||||
|
|
||||||
Pre-check method: `Tools/judge_screenshot.py` (vision model) flagged the simplified flat surfaces as "unexpected" — those flags are the deliberate deviations above, not placement errors. Final verdict is by human review of the shots in `Screenshots/demo/`.
|
Pre-check method: `Tools/review/judge_screenshot.py` (vision model) flagged the simplified flat surfaces as "unexpected" — those flags are the deliberate deviations above, not placement errors. Final verdict is by human review of the shots in `Screenshots/demo/`.
|
||||||
|
|
||||||
## `0.1.0` — Collision and packaging — audit note (2026-11-08)
|
## `0.1.0` — Collision and packaging — audit note (2026-11-08)
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ Run on the packaged build (`GigLineQA`, Development, Vulkan SM5) with `-qaselfte
|
|||||||
|
|
||||||
- **Transect (holes/gaps):** 1456 samples over 16 rows — lane centres **plus the kerb/road/sidewalk seam Ys** (−1382, −1053, +718, +1047 and neighbours) and X −20000..−2000. Result **0 holes**. (The old 84-sample check covered only 3 lane centres; the seam rows are what this check adds.)
|
- **Transect (holes/gaps):** 1456 samples over 16 rows — lane centres **plus the kerb/road/sidewalk seam Ys** (−1382, −1053, +718, +1047 and neighbours) and X −20000..−2000. Result **0 holes**. (The old 84-sample check covered only 3 lane centres; the seam rows are what this check adds.)
|
||||||
- **Invisible-wall probe:** horizontal traces 30 m long, grounded at 160 cm above the local ground, along the road and both seam rows. Result **0/35 blocking hits inside 21 m** — no invisible walls or misplaced blockers in the street envelope.
|
- **Invisible-wall probe:** horizontal traces 30 m long, grounded at 160 cm above the local ground, along the road and both seam rows. Result **0/35 blocking hits inside 21 m** — no invisible walls or misplaced blockers in the street envelope.
|
||||||
- **Facade occlusion probe:** from the road centre, 40 m traces sideways at walking height toward each frontage, required only where the map stages a building (the spans are listed in `AGigLineCharacter` and mirror `Tools/ue_build_demo_map.py`). Result **0/9 directions without a blocking wall** — the player cannot walk through the hero or the frontage. This probe found the hero façade had **no simple collision** at all (complex-only, so traces and capsule sweeps passed through it); it is now `UseComplexAsSimple`. Auto-generated convex hulls were tried first and rejected: on a 95-part, 26 m façade they bulged into the street (the wall probe went 0/35 → 2/35). (First pass used a fixed Z 160 start; the street is at Z ~366, so the probe started inside the earth and reported every row as a dist-0 blocker. Grounding the probe fixed that measurement bug.)
|
- **Facade occlusion probe:** from the road centre, 40 m traces sideways at walking height toward each frontage, required only where the map stages a building (the spans are listed in `AGigLineCharacter` and mirror `Tools/unreal/ue_build_demo_map.py`). Result **0/9 directions without a blocking wall** — the player cannot walk through the hero or the frontage. This probe found the hero façade had **no simple collision** at all (complex-only, so traces and capsule sweeps passed through it); it is now `UseComplexAsSimple`. Auto-generated convex hulls were tried first and rejected: on a 95-part, 26 m façade they bulged into the street (the wall probe went 0/35 → 2/35). (First pass used a fixed Z 160 start; the street is at Z ~366, so the probe started inside the earth and reported every row as a dist-0 blocker. Grounding the probe fixed that measurement bug.)
|
||||||
- **Walk / reset / pause:** 761 cm in 2 s (Z steady 456), reset 0 cm, pause `IsGamePaused()=1`, unpause 0 — all PASS.
|
- **Walk / reset / pause:** 761 cm in 2 s (Z steady 456), reset 0 cm, pause `IsGamePaused()=1`, unpause 0 — all PASS.
|
||||||
- Packaging: the packaged build launches, reaches gameplay, and the QA run quits cleanly. The `GigLineShotActor` screenshot rounds are suppressed under `-qaselftest` so they cannot quit the run before the audit phases.
|
- Packaging: the packaged build launches, reaches gameplay, and the QA run quits cleanly. The `GigLineShotActor` screenshot rounds are suppressed under `-qaselftest` so they cannot quit the run before the audit phases.
|
||||||
- Caveat recorded: ground Z extremes (−12 m / 3000 cm) are trace-start/terrain-edge artifacts of the widened extent, not walkable holes — the walkable corridor is continuous (walk + transect both PASS).
|
- Caveat recorded: ground Z extremes (−12 m / 3000 cm) are trace-start/terrain-edge artifacts of the widened extent, not walkable holes — the walkable corridor is continuous (walk + transect both PASS).
|
||||||
|
|||||||
+1
-1
@@ -91,7 +91,7 @@ Two tools talk to paid services and read their keys from the gitignored `.env`:
|
|||||||
|
|
||||||
| Variable | Used by | Needed for |
|
| Variable | Used by | Needed for |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `OPENROUTER_API_KEY` | `Tools/judge_screenshot.py`, `Tools/facade_detail_concepts.py` | vision pre-check and AI concept plates |
|
| `OPENROUTER_API_KEY` | `Tools/review/judge_screenshot.py`, `Tools/gen/facade_detail_concepts.py` | vision pre-check and AI concept plates |
|
||||||
| `MAPBOX_ACCESS_TOKEN` | `Geo/tools/mapbox_buildings.py` | the second building-height source |
|
| `MAPBOX_ACCESS_TOKEN` | `Geo/tools/mapbox_buildings.py` | the second building-height source |
|
||||||
|
|
||||||
Nothing in the build, the tests or the release path needs either key. Never commit `.env`; `git check-ignore .env` should succeed.
|
Nothing in the build, the tests or the release path needs either key. Never commit `.env`; `git check-ignore .env` should succeed.
|
||||||
|
|||||||
+3
-2
@@ -33,8 +33,9 @@ Unreal Engine 5.9 is in preview and Unreal Engine 6 has been announced as of Nov
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Package | blender-skills `1.3.0`, upstream commit `8f778d2`, MIT |
|
| Package | blender-skills `1.3.0`, upstream commit `8f778d2`, MIT |
|
||||||
| Source | <https://github.com/arjun988/blender-skills> |
|
| Source | <https://github.com/arjun988/blender-skills> |
|
||||||
| Location | Local only: `.agents/skills/blender-skills/` — not distributed with the repository |
|
| Location | Local only, common root: `~/.pi/agent/skills/` (93 packs, `references/`, `LICENSE`) — not distributed with the repository |
|
||||||
| Installed | 2026-11-06, copied from a local checkout of `.claude/skills/` without edits |
|
| Project-specific | `.agents/skills/blender-skills/blender-director/` in this repository |
|
||||||
|
| Installed | 2026-11-06, copied from a local checkout of `.claude/skills/` without edits; flattened to the common root on 2026-11-10 |
|
||||||
|
|
||||||
These are instructions and reference notes for agents, not code that runs in a build. Four of them assume a **Blender MCP server**; this project drives Blender headlessly through `bpy` (`Tools/*.py`) instead, so MCP is optional and nothing depends on it.
|
These are instructions and reference notes for agents, not code that runs in a build. Four of them assume a **Blender MCP server**; this project drives Blender headlessly through `bpy` (`Tools/*.py`) instead, so MCP is optional and nothing depends on it.
|
||||||
|
|
||||||
|
|||||||
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.
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.
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.
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.
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.
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.
Loaded 100 of 261 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user