Psi_2_seminar-Zhening — how it was made

← back to reconstruction

① One-shot authoring

model claude-fable-5 · 121 tool calls · 50m19s · cost $20.197310000000005
STEP #1
I'll start by reading `Room.md` and `Room.py` in full, then study the stitched references.
📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /home/zelda/zh340/.claude/projects/-scra🔧 ToolSearch select:mcp__cap__render,mcp__cap__fetch_📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen🎨 fetch_material blue office carpet tiles low pile🎨 fetch_material white painted brick wall🎨 fetch_material office suspended ceiling tile
✏️EDIT #2
The carpet match was flagged weak — let me inspect the recoloured diffuse before wiring it.
📄 Read /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash🖼 render room📄 Read /home/zelda/zh340/.claude/projects/-scra⌘ Bash⌘ Bash⌘ Bash
STEP #3
The room rebuilt cleanly with the harness layer. Now the per-wall verification — reading each compare (ortho render | rectified stitch).
📄 Read /tmp/psi_ref/Wall0_compare.png📄 Read /tmp/psi_ref/Wall1_compare.png
✏️EDIT #4
The comparisons are too small to judge silhouettes — let me crop and enlarge each half.
⌘ Bash⌘ Bash📄 Read /tmp/psi_ref/big/Wall0_render.png📄 Read /tmp/psi_ref/big/Wall0_real.png📄 Read /tmp/psi_ref/big/Wall1_render.png📄 Read /tmp/psi_ref/big/Wall1_real.png📄 Read /tmp/psi_ref/big/Wall2_render.png📄 Read /tmp/psi_ref/big/Wall2_real.png📄 Read /tmp/psi_ref/big/Wall3_render.png📄 Read /tmp/psi_ref/big/Wall3_real.png📄 Read /tmp/psi_ref/big/Ceiling0_render.png📄 Read /tmp/psi_ref/big/Ceiling0_real.png📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /tmp/psi_ref/Floor0_compare.png🎨 fetch_material white painted brick wall✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen
✏️EDIT #5
Now the ceiling T-bar grid (the room is an exact parallelogram, so an affine a/b grid covers it):
✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash⌘ Bash📄 Read /tmp/psi_ref2/big/Wall2_render.png📄 Read /tmp/psi_ref2/big/Wall3_render.png📄 Read /tmp/psi_ref2/big/Wall1_render.png📄 Read /tmp/psi_ref2/big/Ceiling0_render.png
✏️EDIT #6
All four walls now match. Let me spot-check perspective frames for floating/clipping artifacts, then run the critic.
📄 Read /tmp/psi_ref2/Wall1_frame_00004.png📄 Read /tmp/psi_ref2/Wall3_frame_00016.png📄 Read /tmp/psi_ref2/Wall0_frame_00087.png⚖️ critic✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash⌘ Bash📄 Read /tmp/psi_ref3/big/Wall1_render.png📄 Read /tmp/psi_ref3/big/Wall0_render.png⚖️ critic
✏️EDIT #7
Checking frame 27 before adjusting blinds — I need to see which windows the critic says should be open.
📄 Read /tmp/psi_ref3/Wall1_frame_00027.png📄 Read /tmp/psi_ref3/Wall1_frame_00004.png🎨 fetch_material white painted brick wall✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref4/big/Wall1_render.png📄 Read /tmp/psi_ref4/big/Wall0_render.png📄 Read /tmp/psi_ref4/big/Wall3_render.png⌘ Bash📄 Read /tmp/psi_ref4/big/W5_zoom.png✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref5/big/Wall1_render.png📄 Read /tmp/psi_ref5/Wall1_frame_00027.png✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref6/W5_zoom.png⚖️ critic📄 Read /tmp/psi_ref5/Wall0_frame_00082.png📄 Read /tmp/psi_ref5/Wall3_frame_00014.png✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref7/Wall0_frame_00082.png📄 Read /tmp/psi_ref7/Wall3_frame_00014.png📄 Read /tmp/psi_ref7/Wall0_frame_00087.png✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref8/Wall0_frame_00082.png📄 Read /tmp/psi_ref8/big/Wall0_render.png
✏️EDIT #8
Much better — the partition reads as clear glass with scattered notices. Final critic gate.
⚖️ critic✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref9/big/Wall0_render.png📄 Read /tmp/psi_ref9/Wall0_frame_00082.png📄 Read /tmp/psi_ref9/big/Wall1_render.png📄 Read /tmp/psi_ref9/big/Wall2_render.png
✏️EDIT #9
All fixes landed. Final critic check.
⚖️ critic✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /tmp/psi_ref10/big/Wall0_render.png📄 Read /tmp/psi_ref10/big/Wall3_render.png📄 Read /tmp/psi_ref10/Wall0_frame_00082.png📄 Read /tmp/psi_ref10/Wall3_frame_00016.png✏️ Write /home/zelda/zh340/.claude/projects/-scra⌘ Bash
STEP #10
Done — `Room.py` now carries the full harness layer (structure fixes, materials, ceiling, and ~30 grouped multi-part fixtures), compiles, and rebuilds

Authoring — model summary

Done — `Room.py` now carries the full harness layer (structure fixes, materials, ceiling, and ~30 grouped multi-part fixtures), compiles, and rebuilds cleanly; every wall was render-verified against the stitches and capture frames over 10 iterations, with fixes applied after each look. **Shell structure corrections (evidence-based, in `SHELL`):** both internal windows on the door wall were badly under-sized by the scan — the real room has wide glazed corridor partitions on either side of the double door. `Window0` → offset 1.25 / width 1.55 / sill 1.0 / height 1.4, `Window1` → offset 4.95 / width 1.9 / sill 1.0 / height 1.4 (confirmed by frames 82/14/87). No furniture boxes were touched. **Materials:** blue carpet tiles (Poly Haven, recoloured; matches the real checkered blue carpet), faint whitewashed brick on Wall1 (pattern flattened to 0.22 — the wall is painted brick), cream paint on Walls 0/2/3, and a real ceiling: tile-textured prism + suspended T-bar grid + `CeilingLights0` (6 troffers, frame + emissive lens). A `_fix_dark_glass()` pass lightens the recon windows' near-black glass so they read as daylit. **Fixtures per wall** (each a named hide-as-a-unit group via `group_fixture`, every part wall-prefixed): - **Wall0 (door wall)** — `Trunking0` (channel+lid, broken at the door), `Socket0`/`Socket1` (faceplate + 2 raised outlets each), `WindowSill0` (board+lip under the partition), `ExitSign0` (2 drop rods + green emissive box above the door), `NoticeBoard0` (cork patch behind glass + 13 A4 sheets taped on the room side — behind-glass papers were invisible through the frosted GLB glass), `Skirting0` (board+lip, breaks at door). Verified frames 82/87 + ortho. - **Wall1 (brick window wall)** — `Trunking1`, `Socket2-4`, `WindowSill1-4`, `VBlind0-3` (headrail + slat loops: W4 closed @yaw 12°, W3 open stack, W2 ~35% drawn @45°, W5 open stack at end), `Radiator0` (back panel + 14-fin loop + top grille + end caps + 2 valves with pipe stubs), `Pipe0` (full-height boxed pipe + 3 clips), `PipeRun0` (twin flow/return pipes + brackets), `Skirting1`. Verified frames 4/27 + ortho. Note: Window5's GLB has a closed blind baked into the asset — matches the raw stitch, left as-is. - **Wall2** — `Whiteboard0` (back + gloss face + 4 aluminium bars + tray+lip; re-seated to u 3.42 after the rectified compare showed the raw stitch was mirrored), `Thermostat0` (plate + LCD + button at u 0.5), `Trunking2`, `Socket5-7`, `Skirting2`. Verified frames 7/39 + ortho. - **Wall3 ✓ [3/6] one-shot authoring · 50m20s ⊘ [4/6] per-object refinement — skipped (SKIP_REFINE=1) ⊘ [5/6] per-wall fixture refinement — skipped (REFINE_WALLS=0) ▶ [6/6] export three.js viewer (0 stages left) === RoomScene.build (blank) === shell <- SHELL(compact): 4 walls, 7 openings, 18 objects(无 usdz) thickened 4 walls outward to ~0.1 m cut 7/7 openings {'Door0': 'Wall0', 'Window0': 'Wall0', 'Window1': 'Wall0', 'Window2': 'Wall1', 'Window3': 'Wall1', 'Window4': 'Wall1', 'Window5': 'Wall1'} 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.002 14:17:51 | INFO: Blender create Mesh node Cube.007 14:17:51 | INFO: Blender create Mesh node Cube.012 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.007 14:17:51 | INFO: Blender create Mesh node Cube.013 14:17:51 | INFO: Blender create Mesh node Cube.006 14:17:51 | INFO: glTF import finished in 0.00s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cylinder 14:17:51 | INFO: Blender create Mesh node Cylinder.001 14:17:51 | INFO: Blender create Mesh node Cube.005 14:17:51 | INFO: Blender create Mesh node Cube.009 14:17:51 | INFO: Blender create Mesh node Cube.012 14:17:51 | INFO: Blender create Mesh node Cylinder.002 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.004 14:17:51 | INFO: glTF import finished in 0.00s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.017 14:17:51 | INFO: Blender create Mesh node Cube.005 14:17:51 | INFO: Blender create Mesh node Cube.013 14:17:51 | INFO: Blender create Mesh node Cube.014 14:17:51 | INFO: Blender create Mesh node Cube.015 14:17:51 | INFO: Blender create Mesh node Cube.016 14:17:51 | INFO: Blender create Mesh node Cube.004 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.008 14:17:51 | INFO: Blender create Mesh node Cube.009 14:17:51 | INFO: Blender create Mesh node Cube.017 14:17:51 | INFO: Blender create Mesh node Cube.007 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cylinder 14:17:51 | INFO: Blender create Mesh node Cube.006 14:17:51 | INFO: Blender create Mesh node Cube.007 14:17:51 | INFO: Blender create Mesh node Cube.005 14:17:51 | INFO: Blender create Mesh node Cube.012 14:17:51 | INFO: Blender create Mesh node Cube.004 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node Cube 14:17:51 | INFO: Blender create Mesh node Cube.011 14:17:51 | INFO: Blender create Mesh node Cube.012 14:17:51 | INFO: Blender create Mesh node Cube.026 14:17:51 | INFO: Blender create Mesh node Cube.010 14:17:51 | INFO: Blender create Mesh node Cube.005 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s 14:17:51 | INFO: Data are loaded, start creating Blender stuff 14:17:51 | INFO: Blender create Mesh node geometry_0 14:17:51 | INFO: glTF import finished in 0.01s placed 25 asset instances built 0 capture cameras indexed 30 objects, 0 cameras floor-grounded 0 visible piece(s) harness layer: materials + ceiling + fixtures built === ROOM SCENE (blank) === blind : VBlind0, VBlind1, VBlind2, VBlind3 ceiling : Ceiling0 ceiling_grid: CeilingGrid0 chair : Chair0, Chair1, Chair10, Chair11, Chair12, Chair13, Chair14, Chair15, Chair2, Chair3, Chair4, Chair5, Chair6, Chair7, Chair8, Chair9 door : Door0 floor : Floor0 light : CeilingLights0 noticeboard: NoticeBoard0 pipe : Pipe0, PipeRun0 radiator : Radiator0 rail : Rail0 sign : ExitSign0 sill : WindowSill0, WindowSill1, WindowSill2, WindowSill3, WindowSill4 skirting : Skirting0, Skirting1, Skirting2, Skirting3 socket : Socket0, Socket1, Socket2, Socket3, Socket4, Socket5, Socket6, Socket7, Socket8, Socket9 table : Table0, Table1 television: TV0 thermostat: Thermostat0 trunking : Trunking0, Trunking1, Trunking2, Trunking3 wall : Wall0, Wall1, Wall2, Wall3 whiteboard: Whiteboard0, Whiteboard1 window : Window0, Window1, Window2, Window3, Window4, Window5 placeable surfaces (top z): Table0 z=-0.631 center=[2.3025, -0.3011, -1.0016] Table1 z=-0.632 center=[-0.906, 1.2525, -1.002] cameras: none INFO Draco mesh compression is available, use library at /scratch2/zh340/blender-4.5.1-linux-x64/4.5/scripts/addons_core/io_scene_gltf2/libextern_draco.so 14:17:51 | INFO: Starting glTF 2.0 export 14:17:51 | INFO: Extracting primitive: Floor0 14:17:51 | INFO: Primitives created: 1 14:17:51 | INFO: Extracting primitive: Wall0 14:17:51 | INFO: Primitives created: 1 14:17:51 | INFO: Extracting primitive: Wall1 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: Wall2 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: Wall3 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: Ceiling0 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: Cube 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 3 14:17:52 | INFO: Extracting primitive: Cube.001 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 2 14:17:52 | INFO: Extracting primitive: Cube.002 14:17:52 | INFO: Primitives created: 3 14:17:52 | INFO: Extracting primitive: Cube.007 14:17:52 | INFO: Primitives created: 3 14:17:52 | INFO: Extracting primitive: Cube.012 14:17:52 | INFO: Primitives created: 3 14:17:52 | INFO: Extracting primitive: geometry_0 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: geometry_0.001 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: geometry_0.002 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: geometry_0.003 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: geometry_0.004 14:17:52 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:52 | INFO: Primitives created: 1 14:17:52 | INFO: Extracting primitive: geometry_0.005 14:17:53 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:53 | INFO: Primitives created: 1 14:17:53 | INFO: Extracting primitive: geometry_0.006 14:17:53 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:53 | INFO: Primitives created: 1 14:17:53 | INFO: Extracting primitive: geometry_0.007 14:17:53 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:53 | INFO: Primitives created: 1 14:17:53 | INFO: Extracting primitive: geometry_0.008 14:17:53 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:53 | INFO: Primitives created: 1 14:17:53 | INFO: Extracting primitive: geometry_0.009 14:17:53 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:53 | INFO: Primitives created: 1 14:17:53 | INFO: Extracting primitive: geometry_0.010 14:17:54 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:54 | INFO: Primitives created: 1 14:17:54 | INFO: Extracting primitive: geometry_0.011 14:17:54 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:54 | INFO: Primitives created: 1 14:17:54 | INFO: Extracting primitive: geometry_0.012 14:17:54 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:54 | INFO: Primitives created: 1 14:17:54 | INFO: Extracting primitive: geometry_0.013 14:17:54 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:54 | INFO: Primitives created: 1 14:17:54 | INFO: Extracting primitive: geometry_0.014 14:17:54 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:54 | INFO: Primitives created: 1 14:17:54 | INFO: Extracting primitive: geometry_0.015 14:17:55 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.003 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.004 14:17:55 | INFO: Primitives created: 3 14:17:55 | INFO: Extracting primitive: Cube.013 14:17:55 | INFO: Primitives created: 3 14:17:55 | INFO: Extracting primitive: Cube.006 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Plane 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.005 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cylinder 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cylinder.001 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.008 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.009 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.010 14:17:55 | INFO: Primitives created: 3 14:17:55 | INFO: Extracting primitive: Cylinder.002 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Plane.001 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.011 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.014 14:17:55 | INFO: Primitives created: 3 14:17:55 | INFO: Extracting primitive: Plane.002 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.015 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.017 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.016 14:17:55 | INFO: Primitives created: 2 14:17:55 | INFO: Extracting primitive: Cube.018 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.019 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.020 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.021 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.022 14:17:55 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Plane.003 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.023 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.024 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.025 14:17:55 | INFO: Primitives created: 2 14:17:55 | INFO: Extracting primitive: Cube.026 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.027 14:17:55 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Plane.004 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.028 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cylinder.003 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.029 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.030 14:17:55 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.031 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.032 14:17:55 | INFO: Primitives created: 3 14:17:55 | INFO: Extracting primitive: Cube.033 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Plane.005 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.034 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.035 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.036 14:17:55 | WARNING: More than one shader node tex image used for a texture. The resulting glTF sampler will behave like the first shader node tex image. 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.037 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.038 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Cube.039 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA6 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridA7 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB6 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB7 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB8 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_gridB9 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Trunking0_ch0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Trunking0_ch1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Trunking0_lid0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Trunking0_lid1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out30 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_out31 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket0_pl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out30 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_out31 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Socket1_pl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Sill0_board 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Sill0_lip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Exit0_box 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Exit0_rod0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Exit0_rod1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_cork 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg12 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg6 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg7 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg8 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Notice0_pg9 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Skirting0_bd0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Skirting0_bd1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Skirting0_lip0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall0_Skirting0_lip1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Trunking1_ch0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Trunking1_lid0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket2_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket3_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out30 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_out31 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Socket4_pl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill1_board 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill1_lip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill2_board 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill2_lip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill3_board 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill3_lip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill4_board 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_WindowSill4_lip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_rail 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl12 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl13 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl14 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl6 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl7 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl8 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind0_sl9 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_rail 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind1_sl5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_rail 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_sl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_sl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_sl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_sl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind2_sl4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_rail 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_VBlind3_sl5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_cap0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_cap1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin12 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin13 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin4 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin5 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin6 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin7 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin8 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_fin9 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_panel 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_pipe0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_pipe1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_top 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_valve0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Rad0_valve1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Pipe0_clip0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Pipe0_clip1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Pipe0_clip2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Pipe0_run 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_br0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_br1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_br2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_p00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_p01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_p10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_PipeRun0_p11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Skirting1_bd0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall1_Skirting1_lip0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_back 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_face 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_fB 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_fL 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_fR 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_fT 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_tray 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Whiteboard0_traylip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Thermo_btn 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Thermo_lcd 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Thermo_plate 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Trunking2_ch0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Trunking2_lid0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket5_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out30 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_out31 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket6_pl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Socket7_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Skirting2_bd0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall2_Skirting2_lip0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_back 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_face 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_fB 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_fL 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_fR 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_fT 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_tray 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Whiteboard1_traylip 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_base 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_bzB 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_bzL 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_bzR 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_bzT 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_cable 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_pole 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_TV0_screen 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Rail0_bar 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Rail0_br0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Rail0_br1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Rail0_br2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Rail0_br3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Trunking3_ch0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Trunking3_lid0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out30 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_out31 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket8_pl3 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out00 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out01 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out10 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out11 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out20 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_out21 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_pl0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_pl1 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Socket9_pl2 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Skirting3_bd0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Wall3_Skirting3_lip0 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt00_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt00_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt01_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt01_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt10_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt10_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt11_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt11_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt20_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt20_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt21_fr 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Extracting primitive: Ceil_Lt21_lens 14:17:55 | INFO: Primitives created: 1 14:17:55 | INFO: Finished glTF 2.0 export in 3.7441282272338867 s EXPORTED -> /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room_preview/Room.glb (20868 KB) LAYOUT -> /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room_preview/room_layout.json (70 objects, 0 cameras) Info: Saved as "Room.blend" BLEND -> /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room_preview/Room.blend (69844 KB) COPIED -> /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room_preview/build_room.py DONE Blender 4.5.1 LTS (hash b0a72b245dcf built 2025-07-29 06:24:35) Blender quit === 物化 11 个物体 === tex Table0: 重建 4 张贴图 regen Table0 (object.py) tex Table1: 重建 3 张贴图 regen Table1 (object.py) regen Wall0_Door_0 (object.py) regen Wall0_Window_0 (object.py) regen Wall0_Window_1 (object.py) tex Wall1_Window_2: 重建 4 张贴图 regen Wall1_Window_2 (object.py) tex Wall1_Window_3: 重建 4 张贴图 regen Wall1_Window_3 (object.py) tex Wall1_Window_4: 重建 1 张贴图 regen Wall1_Window_4 (object.py) tex Wall1_Window_5: 重建 1 张贴图 regen Wall1_Window_5 (object.py) wrap ChairCluster0 (object.py) wrap ChairCluster1 (object.py) room materials: 重建 9 张贴图 -> /scratch2/code/LiteReality_to_share/agentic_litereality/final_results/Psi_2_seminar-Zhening/scene_stage/_oneshot/room/materials === 装配(无 frames(仅几何),外壳来自 Room.py 的 SHELL)-> Room.glb + Room.blend === 物化 11/11 个物体 Room_preview -> /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room_preview viewer → /scratch2/code/LiteReality_to_share/agentic_litereality/final_results/Psi_2_seminar-Zhening/Psi_2_seminar-Zhening.html (12.9 MB, self-contained) final_results → /scratch2/code/LiteReality_to_share/agentic_litereality/final_results/Psi_2_seminar-Zhening ✓ [6/6] export three.js viewer · 17s ╭────────── Psi_2_seminar-Zhening ──────────╮ ✓ init (skipped) 0s ✓ stitches (skipped) 0s ✓ one-shot authoring 50m20s ✓ obj refine (skipped) 0s ✓ wall refine (skipped) 0s ✓ export three.js viewer 17s total 50m37s viewer /scratch2/code/LiteReality_to_share/agentic_litereality/final_results/Psi_2_seminar-Zhening/Psi_2_seminar-Zhening.html room /scratch2/code/LiteReality_to_share/agentic_litereality/output/Psi_2_seminar-Zhening/scene_stage/_oneshot/room/Room.py ╰────────────────────────────────────────────────╯

② Code changes

Room.py diffseed (blank shell) → authored
+514 −8 lines · seed → authored Room.py
@@ -1159,8 +1159,514 @@
# ============================================================================
+# HARNESS LAYER — Psi_2_seminar: ceiling + materials + multi-part wall fixtures
+# Authored against the head-on stitches (surface_ref/*_stitched.jpg). Every
+# fixture is a named, hide-as-a-unit group (empty handle + own collection).
+# ============================================================================
+MAT_DIR = os.path.join(HERE, "materials")
+
+
+def _flat(name, srgb, rough=0.6, metal=0.0):
+ mat = bpy.data.materials.get(name)
+ if mat is not None:
+ return mat
+ mat = bpy.data.materials.new(name)
+ mat.use_nodes = True
+ nt = mat.node_tree
+ nt.nodes.clear()
+ out = nt.nodes.new("ShaderNodeOutputMaterial")
+ b = nt.nodes.new("ShaderNodeBsdfPrincipled")
+ nt.links.new(b.outputs["BSDF"], out.inputs["Surface"])
+ b.inputs["Base Color"].default_value = (*srgb_to_linear(srgb), 1.0)
+ b.inputs["Roughness"].default_value = rough
+ if "Metallic" in b.inputs:
+ b.inputs["Metallic"].default_value = metal
+ return mat
+
+
+def _emis(name, srgb, strength):
+ mat = bpy.data.materials.get(name)
+ if mat is not None:
+ return mat
+ mat = bpy.data.materials.new(name)
+ mat.use_nodes = True
+ nt = mat.node_tree
+ nt.nodes.clear()
+ e = nt.nodes.new("ShaderNodeEmission")
+ e.inputs["Color"].default_value = (*srgb_to_linear(srgb), 1.0)
+ e.inputs["Strength"].default_value = strength
+ out = nt.nodes.new("ShaderNodeOutputMaterial")
+ nt.links.new(e.outputs["Emission"], out.inputs["Surface"])
+ return mat
+
+
+def _pbr(name, slug, scale=1.0, force_rough=None, nstr=0.6):
+ """Poly Haven set materials/<slug>_{diff,rough,normal}.jpg, BOX-projected on
+ Object coords (shell + part meshes carry metric coords -> scale = repeats/m)."""
+ mat = bpy.data.materials.get(name)
+ if mat is not None:
+ return mat
+ mat = bpy.data.materials.new(name)
+ mat.use_nodes = True
+ nt = mat.node_tree
+ nt.nodes.clear()
+ out = nt.nodes.new("ShaderNodeOutputMaterial")
+ b = nt.nodes.new("ShaderNodeBsdfPrincipled")
+ nt.links.new(b.outputs["BSDF"], out.inputs["Surface"])
+ co = nt.nodes.new("ShaderNodeTexCoord")
+ mp = nt.nodes.new("ShaderNodeMapping")
+ mp.inputs["Scale"].default_value = (scale, scale, scale)
+ nt.links.new(co.outputs["Object"], mp.inputs["Vector"])
+
+ def img(suffix, non_color):
+ p = os.path.join(MAT_DIR, f"{slug}_{suffix}.jpg")
+ if not os.path.exists(p):
+ return None
+ t = nt.nodes.new("ShaderNodeTexImage")
+ t.image = bpy.data.images.load(p, check_existing=True)
+ t.projection = "BOX"
+ t.projection_blend = 0.2
+ if non_color:
+ t.image.colorspace_settings.name = "Non-Color"
+ nt.links.new(mp.outputs["Vector"], t.inputs["Vector"])
+ return t
+
+ d = img("diff", False)
+ if d:
+ nt.links.new(d.outputs["Color"], b.inputs["Base Color"])
+ if force_rough is not None:
+ b.inputs["Roughness"].default_value = force_rough
+ else:
+ r = img("rough", True)
+ if r:
+ nt.links.new(r.outputs["Color"], b.inputs["Roughness"])
+ nrm = img("normal", True)
+ if nrm:
+ nm = nt.nodes.new("ShaderNodeNormalMap")
+ nm.inputs["Strength"].default_value = nstr
+ nt.links.new(nrm.outputs["Color"], nm.inputs["Color"])
+ nt.links.new(nm.outputs["Normal"], b.inputs["Normal"])
+ return mat
+
+
+def _mats():
+ return {
+ "paint": _flat("Paint_Cream", (0.96, 0.94, 0.89), rough=0.85),
+ "pvc": _flat("White_PVC", (0.94, 0.94, 0.92), rough=0.35),
+ "metal_white": _flat("Metal_White", (0.92, 0.92, 0.91), rough=0.5),
+ "alum": _flat("Aluminium", (0.72, 0.73, 0.75), rough=0.35, metal=0.9),
+ "wb_face": _flat("Whiteboard_Face", (0.97, 0.97, 0.96), rough=0.12),
+ "blind": _flat("Blind_Fabric", (0.27, 0.29, 0.34), rough=0.95),
+ "tv_screen": _flat("TV_Screen", (0.02, 0.02, 0.025), rough=0.2),
+ "tv_dark": _flat("TV_Dark", (0.09, 0.09, 0.10), rough=0.6, metal=0.2),
+ "sign_green": _emis("Sign_Green", (0.10, 0.75, 0.30), 1.2),
+ "lens": _emis("Light_Lens", (1.0, 1.0, 1.0), 5.0),
+ "lcd": _flat("LCD_Grey", (0.62, 0.68, 0.62), rough=0.3),
+ "carpet": _pbr("Floor_Carpet", "floor_carpet", scale=0.33, force_rough=0.92),
+ "brick": _pbr("Wall1_Brick", "wall1_brick", scale=0.5, nstr=0.35),
+ "ceil": _pbr("Ceiling_Tile", "ceiling_tile", scale=0.42),
+ }
+
+
+def _wf(wall):
+ """(start, along-dir, inward-normal, length) of a SHELL wall on the floor plane."""
+ ws = SHELL["walls"]
+ c = Vector((0.0, 0.0, 0.0))
+ for w in ws.values():
+ c += Vector((w["start"][0], w["start"][1], 0.0))
+ c /= max(len(ws), 1)
+ w = ws[wall]
+ s = Vector((w["start"][0], w["start"][1], 0.0))
+ e = Vector((w["end"][0], w["end"][1], 0.0))
+ d = e - s
+ L = d.length
+ d.normalize()
+ n = Vector((-d.y, d.x, 0.0))
+ if n.dot(c - (s + e) / 2) < 0:
+ n = -n
+ return s, d, n, L
+
+
+def _frame_M(center, xdir, ydir):
+ x = Vector((xdir.x, xdir.y, 0.0)).normalized()
+ y = Vector((ydir.x, ydir.y, 0.0)).normalized()
+ if (x.x * y.y - x.y * y.x) < 0: # keep right-handed (boxes are xy-symmetric)
+ x = -x
+ return Matrix(
+ (
+ (x.x, y.x, 0.0, center[0]),
+ (x.y, y.y, 0.0, center[1]),
+ (0.0, 0.0, 1.0, center[2]),
+ (0.0, 0.0, 0.0, 1.0),
+ )
+ )
+
+
+def _mkbox(name, dims, mat, M):
+ """Metric-vertex box (local = metres, so BOX projection maps 1:1), placed by a
+ rotation+translation matrix."""
+ me = _unit_cube_mesh(name)
+ for v in me.vertices:
+ v.co.x *= dims[0]
+ v.co.y *= dims[1]
+ v.co.z *= dims[2]
+ me.update()
+ ob = bpy.data.objects.new(name, me)
+ ob.matrix_world = M
+ if mat is not None:
+ ob.data.materials.append(mat)
+ bpy.context.scene.collection.objects.link(ob)
+ return ob
+
+
+def _wall_box(wall, name, mat, u, z, ln, h, dp, out=0.0, yaw=0.0):
+ """A box flush on a wall's interior face. u = centre along wall from its start,
+ z = centre above floor, ln/h = size along wall / vertical, dp = depth into the
+ room, out = gap between wall face and box back, yaw = spin about vertical
+ (for blind slats). Part names carry the wall prefix so the per-wall ortho
+ montage keeps corner fixtures with their own wall."""
+ s, d, n, _L = _wf(wall)
+ c = s + d * u + n * (out + dp / 2.0)
+ r = math.radians(yaw)
+ dr = d * math.cos(r) + n * math.sin(r)
+ nr = Vector((-dr.y, dr.x, 0.0))
+ return _mkbox(
+ f"{wall}_{name}", (ln, dp, h), mat,
+ _frame_M((c.x, c.y, SHELL["floor_z"] + z), dr, nr),
+ )
+
+
+def group_fixture(name, category, parts):
+ """Bundle a fixture's parts into ONE named, hide-as-a-unit group: an empty
+ handle with room_id/category props, every part parented to it, all moved
+ into a collection named after the fixture (selects/hides like Table0)."""
+ parts = [p for p in parts if p is not None]
+ handle = bpy.data.objects.new(name, None)
+ handle.empty_display_size = 0.1
+ handle["room_id"] = name
+ handle["category"] = category
+ coll = get_or_make_collection(name)
+ coll.objects.link(handle)
+ c = Vector((0.0, 0.0, 0.0))
+ for p in parts:
+ c += p.matrix_world.translation
+ c /= max(len(parts), 1)
+ handle.matrix_world = Matrix.Translation(c)
+ inv = Matrix.Translation(c).inverted()
+ for p in parts:
+ p.parent = handle
+ p.matrix_parent_inverse = inv
+ move_to_collection(p, coll)
+ return handle
+
+
+# ---------------------------------------------------------------- shared builders
+def _trunk(wall, name, spans, z, M):
+ """Dado cable trunking: channel base + proud lid per free span."""
+ parts = []
+ for i, (a, b) in enumerate(spans):
+ ln, u = b - a, (a + b) / 2.0
+ parts.append(_wall_box(wall, f"{name}_ch{i}", M["pvc"], u, z, ln, 0.09, 0.038))
+ parts.append(_wall_box(wall, f"{name}_lid{i}", M["pvc"], u, z, ln, 0.055, 0.018, out=0.038))
+ return parts
+
+
+def _sockets(wall, name, us, z, M, out=0.056):
+ """A bank of double sockets on the trunking: faceplate + two raised outlets."""
+ parts = []
+ for i, u in enumerate(us):
+ parts.append(_wall_box(wall, f"{name}_pl{i}", M["pvc"], u, z, 0.146, 0.086, 0.010, out=out))
+ for j, du in enumerate((-0.034, 0.034)):
+ parts.append(
+ _wall_box(wall, f"{name}_out{i}{j}", M["pvc"], u + du, z, 0.048, 0.040, 0.008,
+ out=out + 0.010)
+ )
+ return parts
+
+
+def _skirt(wall, name, spans, M):
+ """Skirting board + slightly proud top lip, broken at door openings."""
+ parts = []
+ for i, (a, b) in enumerate(spans):
+ ln, u = b - a, (a + b) / 2.0
+ parts.append(_wall_box(wall, f"{name}_bd{i}", M["pvc"], u, 0.0475, ln, 0.095, 0.012))
+ parts.append(_wall_box(wall, f"{name}_lip{i}", M["pvc"], u, 0.099, ln, 0.018, 0.018))
+ return parts
+
+
+def _whiteboard(M, wall, name, u, w, z0, z1):
+ """Backing board + recessed gloss face + 4 aluminium frame bars + pen tray."""
+ zc, h = (z0 + z1) / 2.0, z1 - z0
+ tw = min(w, 0.6 * w + 0.4)
+ parts = [
+ _wall_box(wall, name + "_back", M["pvc"], u, zc, w, h, 0.014),
+ _wall_box(wall, name + "_face", M["wb_face"], u, zc, w - 0.07, h - 0.07, 0.004, out=0.014),
+ _wall_box(wall, name + "_fT", M["alum"], u, z1 - 0.016, w, 0.032, 0.020, out=0.006),
+ _wall_box(wall, name + "_fB", M["alum"], u, z0 + 0.016, w, 0.032, 0.020, out=0.006),
+ _wall_box(wall, name + "_fL", M["alum"], u - w / 2 + 0.016, zc, 0.032, h, 0.020, out=0.006),
+ _wall_box(wall, name + "_fR", M["alum"], u + w / 2 - 0.016, zc, 0.032, h, 0.020, out=0.006),
+ _wall_box(wall, name + "_tray", M["alum"], u, z0 - 0.030, tw, 0.014, 0.070),
+ _wall_box(wall, name + "_traylip", M["alum"], u, z0 - 0.012, tw, 0.030, 0.010, out=0.060),
+ ]
+ group_fixture(name, "whiteboard", parts)
+
+
+def _vblind(M, name, u, w, mode):
+ """Vertical blind over a Wall1 window: headrail + a loop of yawed slats.
+ mode: closed = full run of slats, half = end-side half covered, open = slats
+ stacked edge-on at the start-side edge."""
+ wb = w + 0.12
+ parts = [_wall_box("Wall1", name + "_rail", M["pvc"], u, 2.79, wb, 0.05, 0.06, out=0.05)]
+ top, bot = 2.755, 1.12
+ zc, h = (top + bot) / 2.0, top - bot
+ if mode == "closed":
+ nsl = max(6, int(wb / 0.082))
+ for i in range(nsl):
+ ui = u - wb / 2 + 0.05 + i * (wb - 0.10) / max(nsl - 1, 1)
+ parts.append(_wall_box("Wall1", f"{name}_sl{i}", M["blind"], ui, zc, 0.089, h, 0.012,
+ out=0.068, yaw=12))
+ elif mode == "half": # ~35% drawn from the end side, vanes well spaced
+ nsl = max(4, int(wb * 0.35 / 0.09))
+ for i in range(nsl):
+ ui = u + wb / 2 - 0.06 - i * 0.095
+ parts.append(_wall_box("Wall1", f"{name}_sl{i}", M["blind"], ui, zc, 0.089, h, 0.012,
+ out=0.068, yaw=45))
+ else: # open / open_end — slats gathered edge-on at one side of the window
+ sgn = 1.0 if mode == "open_end" else -1.0
+ for i in range(6):
+ ui = u + sgn * (wb / 2 - 0.05 - i * 0.020)
+ parts.append(_wall_box("Wall1", f"{name}_sl{i}", M["blind"], ui, zc, 0.089, h, 0.012,
+ out=0.068, yaw=72))
+ group_fixture(name, "blind", parts)
+
+
+# ---------------------------------------------------------------- per-wall fixtures
+def _wall0_fixtures(M):
+ """Door wall: trunking broken at the double door, socket banks, sill shelf under
+ the notice-board window (Window1), hanging exit box + wall exit sign, skirting."""
+ z = 0.87 # trunking centre — top stays below Window0's 0.9365 sill
+ group_fixture("Trunking0", "trunking",
+ _trunk("Wall0", "Trunking0", [(0.05, 2.17), (3.96, 6.28)], z, M))
+ group_fixture("Socket0", "socket", _sockets("Wall0", "Socket0", (4.15, 4.35, 4.55, 4.75), z, M))
+ group_fixture("Socket1", "socket", _sockets("Wall0", "Socket1", (0.50, 0.67, 0.84, 1.01), z, M))
+ group_fixture("WindowSill0", "sill", [
+ _wall_box("Wall0", "Sill0_board", M["pvc"], 4.95, 0.975, 2.00, 0.03, 0.16),
+ _wall_box("Wall0", "Sill0_lip", M["pvc"], 4.95, 0.940, 2.00, 0.04, 0.015, out=0.13),
+ ])
+ parts = [_wall_box("Wall0", f"Exit0_rod{i}", M["pvc"], 3.05 + du, 2.87, 0.02, 0.30, 0.02, out=0.11)
+ for i, du in enumerate((-0.14, 0.14))]
+ parts.append(_wall_box("Wall0", "Exit0_box", M["sign_green"], 3.05, 2.63, 0.38, 0.18, 0.04, out=0.10))
+ group_fixture("ExitSign0", "sign", parts)
+ # a few notices seen THROUGH Window1's glazed partition (corridor side):
+ # small cork patch + scattered A4 sheets just behind the wall plane —
+ # most of the glass stays clear
+ parts = [
+ _wall_box("Wall0", "Notice0_cork", _flat("Cork", (0.76, 0.62, 0.44), rough=0.9),
+ 4.35, 1.55, 0.55, 0.65, 0.012, out=-0.050),
+ ]
+ for i, (du, dz) in enumerate(
+ ((-0.55, 0.45), (-0.30, 0.10), (-0.08, 0.42), (-0.05, -0.02), (0.18, 0.18),
+ (0.42, 0.48), (0.55, 0.02), (0.80, 0.35), (0.95, -0.12), (1.20, 0.22),
+ (-0.35, -0.35), (0.30, -0.40), (0.90, -0.45))
+ ):
+ parts.append(_wall_box("Wall0", f"Notice0_pg{i}", M["wb_face"], 4.35 + du, 1.55 + dz,
+ 0.20, 0.27, 0.004, out=0.09)) # taped on the room-side glass
+ group_fixture("NoticeBoard0", "noticeboard", parts)
+ group_fixture("Skirting0", "skirting",
+ _skirt("Wall0", "Skirting0", [(0.03, 2.17), (3.96, 6.30)], M))
+
+
+def _wall1_fixtures(M):
+ """Window wall (painted brick): trunking + 3 socket banks, sills, vertical
+ blinds, finned radiator under Window2, vertical pipe, pipe run, skirting."""
+ group_fixture("Trunking1", "trunking", _trunk("Wall1", "Trunking1", [(0.05, 6.28)], 1.00, M))
+ group_fixture("Socket2", "socket", _sockets("Wall1", "Socket2", (0.80, 1.00, 1.20), 1.00, M))
+ group_fixture("Socket3", "socket", _sockets("Wall1", "Socket3", (2.25, 2.45, 2.65), 1.00, M))
+ group_fixture("Socket4", "socket", _sockets("Wall1", "Socket4", (3.62, 3.80, 3.98, 4.16), 1.00, M))
+ for nm, u, w, sill in (
+ ("WindowSill1", 0.7915, 1.159, 1.1911),
+ ("WindowSill2", 2.2755, 1.0176, 1.1345),
+ ("WindowSill3", 3.9999, 1.2438, 1.1345),
+ ("WindowSill4", 5.4415, 1.1872, 1.177),
+ ):
+ group_fixture(nm, "sill", [
+ _wall_box("Wall1", nm + "_board", M["pvc"], u, sill - 0.015, w + 0.10, 0.03, 0.12),
+ _wall_box("Wall1", nm + "_lip", M["pvc"], u, sill - 0.05, w + 0.10, 0.04, 0.014, out=0.09),
+ ])
+ _vblind(M, "VBlind0", 0.7915, 1.159, "closed")
+ _vblind(M, "VBlind1", 2.2755, 1.0176, "open")
+ _vblind(M, "VBlind2", 3.9999, 1.2438, "half")
+ _vblind(M, "VBlind3", 5.4415, 1.1872, "open_end")
+ # radiator under Window2: back panel + fin loop + top grille + caps + valves
+ parts = [_wall_box("Wall1", "Rad0_panel", M["metal_white"], 4.05, 0.41, 1.25, 0.58, 0.022, out=0.025)]
+ for i in range(14):
+ ui = 4.05 - 0.58 + i * 1.16 / 13.0
+ parts.append(_wall_box("Wall1", f"Rad0_fin{i}", M["metal_white"], ui, 0.40, 0.046, 0.50, 0.052,
+ out=0.047))
+ parts.append(_wall_box("Wall1", "Rad0_top", M["metal_white"], 4.05, 0.705, 1.25, 0.015, 0.10, out=0.02))
+ for j, ue in enumerate((3.445, 4.655)):
+ parts.append(_wall_box("Wall1", f"Rad0_cap{j}", M["metal_white"], ue, 0.41, 0.04, 0.60, 0.10, out=0.02))
+ for j, uv in enumerate((3.39, 4.71)):
+ parts.append(_wall_box("Wall1", f"Rad0_valve{j}", M["metal_white"], uv, 0.115, 0.05, 0.07, 0.05, out=0.03))
+ parts.append(_wall_box("Wall1", f"Rad0_pipe{j}", M["metal_white"], uv, 0.04, 0.028, 0.08, 0.028, out=0.04))
+ group_fixture("Radiator0", "radiator", parts)
+ # full-height boxed pipe between Window3 and Window2
+ parts = [_wall_box("Wall1", "Pipe0_run", M["pvc"], 3.08, 1.515, 0.10, 3.03, 0.085)]
+ for i, zb in enumerate((0.35, 1.55, 2.75)):
+ parts.append(_wall_box("Wall1", f"Pipe0_clip{i}", M["pvc"], 3.08, zb, 0.13, 0.035, 0.10))
+ group_fixture("Pipe0", "pipe", parts)
+ # horizontal flow/return pipes: vertical pipe -> radiator -> right corner
+ parts = []
+ for i, (a, b) in enumerate(((3.14, 3.32), (4.68, 6.28))):
+ for j, zp in enumerate((0.44, 0.53)):
+ parts.append(_wall_box("Wall1", f"PipeRun0_p{i}{j}", M["metal_white"], (a + b) / 2, zp,
+ b - a, 0.035, 0.035, out=0.015))
+ for i, ub in enumerate((4.9, 5.5, 6.1)):
+ parts.append(_wall_box("Wall1", f"PipeRun0_br{i}", M["metal_white"], ub, 0.485, 0.03, 0.15, 0.06))
+ group_fixture("PipeRun0", "pipe", parts)
+ group_fixture("Skirting1", "skirting", _skirt("Wall1", "Skirting1", [(0.03, 6.30)], M))
+
+
+def _wall2_fixtures(M):
+ """Small whiteboard, thermostat, trunking + 3 socket banks, skirting."""
+ _whiteboard(M, "Wall2", "Whiteboard0", 3.42, 1.50, 1.08, 2.44)
+ group_fixture("Thermostat0", "thermostat", [
+ _wall_box("Wall2", "Thermo_plate", M["pvc"], 0.50, 1.60, 0.095, 0.13, 0.022),
+ _wall_box("Wall2", "Thermo_lcd", M["lcd"], 0.50, 1.625, 0.06, 0.035, 0.004, out=0.022),
+ _wall_box("Wall2", "Thermo_btn", M["pvc"], 0.50, 1.555, 0.04, 0.018, 0.005, out=0.022),
+ ])
+ group_fixture("Trunking2", "trunking", _trunk("Wall2", "Trunking2", [(0.05, 5.26)], 1.00, M))
+ group_fixture("Socket5", "socket", _sockets("Wall2", "Socket5", (0.70, 1.00, 1.30), 1.00, M))
+ group_fixture("Socket6", "socket", _sockets("Wall2", "Socket6", (1.80, 2.10, 2.40, 2.70), 1.00, M))
+ group_fixture("Socket7", "socket", _sockets("Wall2", "Socket7", (3.60, 3.80, 4.00), 1.00, M))
+ group_fixture("Skirting2", "skirting", _skirt("Wall2", "Skirting2", [(0.03, 5.28)], M))
+
+
+def _wall3_fixtures(M):
+ """Large whiteboard, TV on floor-stand trolley in the window corner, top rail,
+ trunking + 2 socket banks, skirting."""
+ _whiteboard(M, "Wall3", "Whiteboard1", 3.00, 3.40, 1.10, 2.50)
+ u, zc, w, h = 0.85, 1.68, 1.45, 0.85
+ group_fixture("TV0", "television", [
+ _wall_box("Wall3", "TV0_screen", M["tv_screen"], u, zc, w - 0.05, h - 0.05, 0.02, out=0.16),
+ _wall_box("Wall3", "TV0_bzT", M["tv_dark"], u, zc + h / 2 - 0.017, w, 0.035, 0.030, out=0.155),
+ _wall_box("Wall3", "TV0_bzB", M["tv_dark"], u, zc - h / 2 + 0.017, w, 0.035, 0.030, out=0.155),
+ _wall_box("Wall3", "TV0_bzL", M["tv_dark"], u - w / 2 + 0.017, zc, 0.035, h, 0.030, out=0.155),
+ _wall_box("Wall3", "TV0_bzR", M["tv_dark"], u + w / 2 - 0.017, zc, 0.035, h, 0.030, out=0.155),
+ _wall_box("Wall3", "TV0_pole", M["tv_dark"], u, 0.80, 0.075, 1.60, 0.055, out=0.10),
+ _wall_box("Wall3", "TV0_base", M["tv_dark"], u, 0.03, 0.65, 0.06, 0.48),
+ _wall_box("Wall3", "TV0_cable", M["tv_dark"], u + 0.12, 0.60, 0.020, 1.20, 0.015, out=0.13),
+ ])
+ parts = [_wall_box("Wall3", "Rail0_bar", M["pvc"], 3.27, 2.58, 3.95, 0.040, 0.022, out=0.008)]
+ for i, ub in enumerate((1.5, 2.5, 3.5, 4.5)):
+ parts.append(_wall_box("Wall3", f"Rail0_br{i}", M["pvc"], ub, 2.58, 0.03, 0.055, 0.030))
+ group_fixture("Rail0", "rail", parts)
+ group_fixture("Trunking3", "trunking", _trunk("Wall3", "Trunking3", [(0.05, 5.26)], 1.00, M))
+ group_fixture("Socket8", "socket", _sockets("Wall3", "Socket8", (1.95, 2.15, 2.35, 2.55), 1.00, M))
+ group_fixture("Socket9", "socket", _sockets("Wall3", "Socket9", (3.55, 3.75, 3.95), 1.00, M))
+ group_fixture("Skirting3", "skirting", _skirt("Wall3", "Skirting3", [(0.03, 5.28)], M))
+
+
+def _build_ceiling(M):
+ """Suspended-tile ceiling: a thin prism over the wall-corner loop (both faces
+ shaded), tile texture BOX-projected."""
+ cz = SHELL["ceiling_z"]
+ loop = [SHELL["walls"][w]["start"] for w in ("Wall3", "Wall1", "Wall2", "Wall0")] # CCW
+ verts = [(x, y, cz) for x, y in loop] + [(x, y, cz + 0.03) for x, y in loop]
+ faces = [(3, 2, 1, 0), (4, 5, 6, 7), (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7)]
+ me = bpy.data.meshes.new("Ceiling0")
+ me.from_pydata(verts, [], faces)
+ me.update()
+ ob = bpy.data.objects.new("Ceiling0", me)
+ bpy.context.scene.collection.objects.link(ob)
+ ob.data.materials.append(M["ceil"])
+ move_to_collection(ob, get_or_make_collection(COLL_SHELL))
+ # suspended T-bar grid (0.6 m): the room is an exact parallelogram, so an
+ # affine (a,b) grid in the Wall1 frame covers it edge to edge
+ s1, d1, _n, L1 = _wf("Wall1")
+ w2 = SHELL["walls"]["Wall2"]
+ e2 = Vector((w2["start"][0], w2["start"][1], 0.0)) - s1
+ L2 = e2.length
+ e2.normalize()
+ parts = []
+ zg = cz - 0.008
+ for i in range(1, int(L2 / 0.6)): # bars running along Wall1
+ c = s1 + d1 * (L1 / 2) + e2 * (i * 0.6)
+ parts.append(_mkbox(f"Ceil_gridA{i}", (L1, 0.024, 0.016), M["pvc"],
+ _frame_M((c.x, c.y, zg), d1, e2)))
+ for i in range(1, int(L1 / 0.6)): # bars running along Wall2
+ c = s1 + d1 * (i * 0.6) + e2 * (L2 / 2)
+ parts.append(_mkbox(f"Ceil_gridB{i}", (L2, 0.024, 0.016), M["pvc"],
+ _frame_M((c.x, c.y, zg), e2, d1)))
+ group_fixture("CeilingGrid0", "ceiling_grid", parts)
+ return ob
+
+
+def _ceiling_lights(M):
+ """Six recessed troffers (frame + emissive lens) on the ceiling grid."""
+ s, d, n, _L = _wf("Wall1")
+ cz = SHELL["ceiling_z"]
+ parts = []
+ for i, a in enumerate((1.10, 3.17, 5.23)):
+ for j, b in enumerate((1.40, 3.60)):
+ c = s + d * a + n * b
+ parts.append(_mkbox(f"Ceil_Lt{i}{j}_fr", (0.64, 0.64, 0.03), M["pvc"],
+ _frame_M((c.x, c.y, cz - 0.015), d, n)))
+ parts.append(_mkbox(f"Ceil_Lt{i}{j}_lens", (0.55, 0.55, 0.012), M["lens"],
+ _frame_M((c.x, c.y, cz - 0.033), d, n)))
+ group_fixture("CeilingLights0", "light", parts)
+
+
+def _fix_dark_glass():
+ """The recon window GLBs ship near-black tinted glass that reads as a closed
+ blind. Lighten any near-black principled material on the window assets so
+ the daylight card behind shows through as a lit pane."""
+ for i in range(6):
+ h = bpy.data.objects.get(f"Window{i}")
+ if h is None:
+ continue
+ for o in h.children_recursive:
+ if o.type != "MESH":
+ continue
+ for slot in o.material_slots:
+ m = slot.material
+ if not m or not m.use_nodes:
+ continue
+ for node in m.node_tree.nodes:
+ if node.type == "BSDF_PRINCIPLED":
+ c = node.inputs["Base Color"].default_value
+ if (c[0] + c[1] + c[2]) / 3 < 0.35:
+ node.inputs["Base Color"].default_value = (0.72, 0.78, 0.85, 1.0)
+ node.inputs["Roughness"].default_value = 0.15
+ if "Transmission Weight" in node.inputs:
+ node.inputs["Transmission Weight"].default_value = 0.0
+
+
+def harness_stage(scene):
+ """Materials on every surface + ceiling + all grouped wall fixtures."""
+ M = _mats()
+ _fix_dark_glass()
+ for wn, key in (("Wall0", "paint"), ("Wall1", "brick"), ("Wall2", "paint"),
+ ("Wall3", "paint"), ("Floor0", "carpet")):
+ ob = bpy.data.objects.get(wn)
+ if ob is not None:
+ ob.data.materials.clear()
+ ob.data.materials.append(M[key])
+ _build_ceiling(M)
+ _wall0_fixtures(M)
+ _wall1_fixtures(M)
+ _wall2_fixtures(M)
+ _wall3_fixtures(M)
+ _ceiling_lights(M)
+ bpy.context.view_layer.update()
+ print(" harness layer: materials + ceiling + fixtures built")
+
+
+# ============================================================================
# main
# ============================================================================
def main():
scene = RoomScene().build()
+ harness_stage(scene)
+ scene.index()
scene.summary()
scene.export_glb()
@@ -1237,16 +1743,16 @@
"wall": "Wall0",
"type": "window",
- "offset": 1.6254,
- "width": 0.848,
- "height": 1.1742,
- "sill": 0.9365
+ "offset": 1.25,
+ "width": 1.55,
+ "height": 1.4,
+ "sill": 1.0
},
"Window1": {
"wall": "Wall0",
"type": "window",
- "offset": 4.5794,
- "width": 1.0459,
- "height": 1.0469,
- "sill": 1.0638
+ "offset": 4.95,
+ "width": 1.9,
+ "height": 1.4,
+ "sill": 1.0
},
"Window2": {

③ QC pass(es)

qc · qc_20260720_075525.log

👁INSPECT #1
I'll start by reading the room files and the ground-truth stitches.
⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen⌘ Bash🔧 ToolSearch select:mcp__cap__render,mcp__cap__fetch_⌘ Bash⌘ Bash⌘ Bash⌘ Bash⌘ Bash⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen🖼 render room
STEP #2
You're out of usage credits. Run /usage-credits to keep using Fable 5 or /model to switch models.

directed · qc_directed_20260720_115314.log

STEP #1
I'll start by reading the room definition and understanding the scene structure.
⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen⌘ Bash📄 Read /home/zelda/zh340/.claude/projects/-scra📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #2
Now let me look at the window objects and their references. Let me check for window reference images and read the current window builds.
⌘ Bash
STEP #3
Only tables have refs. Windows must be judged from the wall stitches. Let me read the window object.py files and the SHELL in Room.py.
📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #4
This is a major QC task. Let me set up tracking and gather ground truth. Let me look at the raw frames and the SHELL/window placement in Room.py.
⌘ Bash· Grep📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #5
The conf_ files are depth/confidence maps. Let me find the RGB frames.
⌘ Bash⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #6
Frame 27 shows the real windows well. Let me look at a few more window-facing frames to nail the design.
📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #7
Let me compare the window object.py files to see how much they differ, and check how windows are fit to openings.
⌘ Bash
STEP #8
The 4 Wall1 windows are authored as 4 *different* designs — exactly the inconsistency flagged. I need one unified real design. Let me get a clearer lo
⌘ Bash📄 Read /tmp/f27_up.png📄 Read /tmp/w1_right.png
STEP #9
Now I can see the real window: a cream-framed grid, **2 columns × 3 rows** with a central mullion and two transoms, vertical louvre blinds on the room
📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #10
Now I understand the architecture: vertical blinds are separate room fixtures (`_vblind` → VBlind0-3) placed over the windows — so the window GLBs sho
📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #11
Confirmed: GLBs are scaled to fit each opening box, so one canonical design works for all four. Vertical blinds are room fixtures (not part of window
⌘ Bash
👁INSPECT #12
Now I have the full picture. Let me load the render tool and render the current Wall0 and Wall1 to establish a baseline before editing.
🔧 ToolSearch select:mcp__cap__render🖼 render Wall1
STEP #13
The MCP render needs an explicit scan. Per my notes I should use the engine CLI with `scan=`. Let me locate the render engine.
⌘ Bash⌘ Bash⌘ Bash
STEP #14
`render_wall_reference` gives head-on wall renders paired with the reference. Let me render Wall0 and Wall1 to compare with stitches.
⌘ Bash📄 Read /tmp/qc_render/Wall1_compare.png⌘ Bash📄 Read /tmp/Wall1_compare_R.png
STEP #15
Window3 (2nd window) is already the correct real design — 2-column × 3-row cream grid, transparent glass, operable sash, no baked blinds. The other th
📄 Read /tmp/Wall0_compare_R.png
STEP #16
The render shows NoticeBoard0 (scattered sheets) behind the **right** panel (Window1), but the stitch shows notices on the left. I need to resolve the
📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #17
The stitch is mirrored (per my project notes), and the render is built from true geometry — so notices genuinely belong on Window1 (right). The user's
📄 Read /scratch2/code/LiteReality_to_share/agen
✏️EDIT #18
Window3 is the correct canonical design — only its dark sill needs to be paler (real sills are painted pale). Let me fix W3's sill, then copy it to W2
✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen
STEP #19
Now copy the corrected Window3 design to Window2, 4, 5 (unifying all four), fixing the filename references in each.
⌘ Bash
✏️EDIT #20
All four Wall1 windows are now unified to the correct 2×3-grid design. Now remove the unreal rolled-up roller blind from Wall0 Window0 (not present in
✏️ Edit /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen
STEP #21
Now the main NoticeBoard0 rebuild. Let me check `_wall_box` and `_add_daylight_card` signatures first.
⌘ Bash
✏️EDIT #22
The daylight card sits 0.06 m behind the wall, so a board on the room side will read clearly and helpfully occludes the fake-daylight glow. Let me reb
✏️ Edit /scratch2/code/LiteReality_to_share/agen
STEP #23
Let me check the vertical-blind fixture material color (the render showed near-black blinds vs pale-grey real ones), and the `M` material dict.
⌘ Bash
STEP #24
The blinds are `(0.27,0.29,0.34)` — dark navy-grey vs the pale-grey real blinds; I'll judge that after rebuild. First, let me check whether the `final
⌘ Bash
STEP #25
Room.py is hardlinked between copies. Let me verify the object.py files are too, then rebuild with object regeneration.
⌘ Bash
STEP #26
Both hardlinked. Let me rebuild with `--regenerate` so the changed object.py files materialize into new GLBs.
⌘ Bash
STEP #27
Build succeeded (11/11). Now render Wall1 and Wall0 to verify the fixes.
⌘ Bash📄 Read /tmp/Wall1_compare2_R.png
STEP #28
The Wall1 windows are now unified (mostly hidden behind the VBlind fixtures, which is why the render looks similar — only Window3 shows fully and it w
📄 Read /tmp/Wall0_compare2_R.png
STEP #29
The notice board now fills Window1's panel properly (vs scattered sheets). Let me zoom in to confirm the paper grid reads.
⌘ Bash📄 Read /tmp/nb_zoom.png
STEP #30
The ortho compare render is hazy/low-contrast. Let me render an actual perspective frame of Wall0 to judge the notice board properly.
⌘ Bash📄 Read /tmp/qc_f/wall_frame_00082.png
✏️EDIT #31
The board reads as a featureless grey panel — the white papers aren't contrasting against the light backing. Let me darken the backing and make the pa
✏️ Edit /scratch2/code/LiteReality_to_share/agen
✏️EDIT #32
Now lighten the near-black vertical-blind material to the pale-grey of the real blinds (a match-at-a-glance fix; the render showed them far darker tha
✏️ Edit /scratch2/code/LiteReality_to_share/agen
STEP #33
Rebuild (Room.py-only changes, so no `--regenerate` needed — cached object GLBs are reused), then re-render Wall0 and Wall1.
⌘ Bash⌘ Bash📄 Read /tmp/qc_f2/wall_frame_00082.png📄 Read /tmp/Wall1_3R.png
STEP #34
Both fixes verified:
⌘ Bash📄 Read /tmp/win3_zoom.png
✏️EDIT #35
Window3 now shows the correct real design: cream frame, central mullion (2 columns), two transoms (3 rows), transparent glazing, operable sash with ha
📄 Read /home/zelda/zh340/.claude/projects/-scra✏️ Edit /home/zelda/zh340/.claude/projects/-scra
STEP #36
All fixes are complete and render-verified. Here's the per-surface summary of what I changed:
All fixes are complete and render-verified. Here's the per-surface summary of what I changed: ## Summary of QC changes **Wall1 — the four exterior windows (MANDATORY FIX #1)** The four window objects (`Wall1_Window_2/3/4/5`) had **four different hallucinated designs** (asymmetric casement with baked-in blinds, a clean grid, a fabric-blind variant, a mullion-only variant). The real windows — read off the de-rotated raw frame 27 and the Wall1 stitch — are all **one identical type**: a cream-framed **2-column × 3-row grid** (central mullion + two transoms), transparent glazing, one operable side-hung sash. - `Wall1_Window_3` was already this correct design, so I made it canonical (paled its dark wood sill to a painted off-white) and **copied it to Window2/4/5**, unifying all four. - Removed the windows' **baked-in blinds** — the vertical louvre blinds are separate room fixtures (`_vblind`→VBlind0-3) that sit over each window, so a correct window GLB must carry none (the baked blinds were the visible bug on W2/W4). - Glazing is transparent (transmission 0.94) and the sash articulates (revolute), unchanged. **Wall0 — NoticeBoard0 (MANDATORY FIX #2)** It was a cork patch plus ~13 **scattered A4 sheets floating on the glass**, off-centre toward the door. Rebuilt as **one proper notice board** filling Window1's glazed panel: a dark-felt backing (1.52×1.12, centred in the panel) with an aluminium surround, papered with a tidy 6×4 grid of bright A4 sheets (a few tinted). The dark backing was needed so the white papers read (a light board rendered as a featureless grey panel). Kept on Window1 — that's where the notices sit in true geometry; the Wall0 surface stitch is mirrored. **Wall0 — Window0** Removed the unreal rolled-up roller blind (the real borrowed-light corridor partition has none). **Wall1 — blind material** Lightened `M["blind"]` from near-black `(0.27,0.29,0.34)` to a mid-grey `(0.50,0.51,0.54)` to match the real pale-grey vertical blinds (they were rendering almost black). I left everything else (furniture, ceiling, other fixtures, door, materials) untouched. Rebuilt with `build_from_room --regenerate` and render-verified Wall1 (Window3 fully visible = correct grid + transparent glass) and Wall0 (frame 82: board now reads as a papered notice board).

opus · qc_opus_20260720_083725.log

STEP #1
I'll start by reading the Room.md and Room.py in full to understand what's already authored.
⌘ Bash⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #2
Let me read the existing memory for this scene and the full Room.py.
📄 Read /home/zelda/zh340/.claude/projects/-scra📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #3
Let me read the rest of the file.
📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #4
I've read Room.md and Room.py in full. Now let me look at the ground-truth images and set up the render tool to check current state.
🔧 ToolSearch select:mcp__cap__render,mcp__cap__fetch_📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
👁INSPECT #5
Both tables are beech-top flip tables on silver T-legs with casters — Table1 is a wider double. Now let me render the current room and view the wall s
🖼 render room📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #6
The MCP render can't infer the scan from this `_qc` dir. Let me check the render-engine recipe memories.
📄 Read /home/zelda/zh340/.claude/projects/-scra📄 Read /home/zelda/zh340/.claude/projects/-scra
STEP #7
Now I understand the render recipe. Let me check the render engine CLI and the Objects directory structure.
⌘ Bash⌘ Bash
STEP #8
Let me examine the window and door object.py (the GLB source) to understand the glass and articulation.
📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #9
The window GLB already uses transparent glass (transmission 0.92) and an articulated casement sash — good. Let me render to see actual state. I'll use
⌘ Bash⌘ Bash📄 Read /tmp/qc_ref/Wall1_compare.png📄 Read /tmp/qc_ref/Wall0_compare.png
STEP #10
Renders are low-res. Let me read the articulation-recipe memory and inspect the perspective frames + door at higher detail.
📄 Read /home/zelda/zh340/.claude/projects/-scra📄 Read /tmp/qc_ref/Wall1_frame_00004.png
STEP #11
Let me read the articulation recipe context and check the remaining walls, the door object, and a scene frame.
📄 Read /tmp/qc_ref/Wall2_compare.png📄 Read /tmp/qc_ref/Wall3_compare.png📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #12
The door is well-built (two articulated glazed leaves, frosted translucent glass 0.62, metal handle/hinges/kick). Walls 2/3 look good. Let me zoom int
⌘ Bash📄 Read /tmp/qc_ref/Wall1_render_zoom.png
STEP #13
Windows read correctly — open windows show transparent lit glass, closed/half ones show blinds, matching the real scene. Door is translucent frosted,
⌘ Bash📄 Read /tmp/qc_t0/Table0_frame_00016.png
✏️EDIT #14
Table0 matches the reference well (beech flip-table, TV stand, whiteboard all align). Table1 (the wide double beech table) is visible and correct in t
📄 Read /scratch2/code/LiteReality_to_share/agen✏️ Edit /scratch2/code/LiteReality_to_share/agen
✏️EDIT #15
Now add `frame_set(0)` at the end of `harness_stage` so the exported rest pose is the authored state:
✏️ Edit /scratch2/code/LiteReality_to_share/agen
STEP #16
Now let me re-render Wall1 to confirm it still compiles and the static rest pose is unchanged:
⌘ Bash
STEP #17
It compiles. Let me confirm the static blind pose is unchanged (frame 1 = authored rest):
⌘ Bash📄 Read /tmp/qc_ref2/Wall1_render_zoom.png
STEP #18
The static rest pose is byte-for-byte identical to before my edit — same blind states per window (closed / open-lit / half / closed), confirming `fram
⌘ Bash
STEP #19
The `room_preview/Room.glb` is likely stale (built before my edit). Let me check timestamps and rebuild it properly:
⌘ Bash
STEP #20
Stale glb (08:17 < my edit 08:45). Let me rebuild the preview and re-check:
⌘ Bash⌘ Bash⌘ Bash⌘ Bash
✏️EDIT #21
The rebuild now exports **39 animations: 32 vane-tilt clips (blinds) + 7 door/window swing clips** — the blind articulation landed correctly, and the
✏️ Edit /home/zelda/zh340/.claude/projects/-scra
STEP #22
QC pass complete. Here's what I found and changed, per surface:
QC pass complete. Here's what I found and changed, per surface: ## Summary I read `Room.md` + `Room.py` in full and verified every checklist item against the head-on stitches and object references via ortho + perspective renders. The scene was already in good shape — **only one genuine defect** needed fixing. **Wall1 (window/brick wall) — the one fix.** The four vertical blinds (`_vblind`) were static geometry. Blinds must articulate. I added `_anim_vane_tilt(vanes)`: it keyframes each vane's rotation about its own vertical centreline (a real vertical-blind vane tilt), open→closed→open. Frame 1 is the authored rest pose and `harness_stage` now ends with `frame_set(0)`, so the **static look is byte-for-byte identical** (confirmed by before/after render) — the change is purely additive motion. `_vblind` now animates the vanes *after* `group_fixture` so parenting is settled. Rebuild confirms the GLB now carries **32 vane-tilt clips**. **Wall1 windows themselves — already correct, no edit.** GLBs use transparent glass (Principled Transmission 0.92) with an articulated casement sash (revolute); daylight cards behind read as lit panes. Open windows show transparent lit glass, closed/half show blinds — matching the real scene. `_fix_dark_glass` doesn't fire (glass is bright, not near-black). The 4 windows are genuinely different widths (1.02–1.24 m), each matching its own reference, so I did **not** force-unify them. **Wall0 (door wall) — already correct, no edit.** Door GLB has two glazed leaves with frosted translucent glass (0.62, not opaque), brushed-metal handle/hinges/kick, and both leaves articulate (revolute). Openings are true cuts. Trunking/sockets/skirting break at the door and sit below the window sills — no fixture over any opening. I did not add a hydraulic closer (not clearly present in the Wall0 stitch). **Wall2 & Wall3 — already correct, no edit.** Whiteboards, thermostat, TV-on-stand, rails, trunking/sockets all match the stitches; glass N/A. **Ceiling — already materialed** (PBR tile + T-bar grid + 6 emissive troffers; troffers are the only added emitters and are real). **Objects — match references.** Table0 (beech flip-table on silver T-legs) aligns tightly with the real frame; Table1 (wide double beech table) matches too. No changes (and furniture was off-limits anyway). Edits were confined to `Room.py` (`_vblind` + new `_anim_vane_tilt` + one `frame_set(0)` line); the file re-executes in Blender cleanly and the preview rebuilds 11/11 objects.

opus · qc_opus_20260720_113531.log

STEP #1
I'll start by reading the Room.md and Room.py files in full to understand what I'm working with.
⌘ Bash⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #2
Now let me read the full Room.py.
📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen
STEP #3
Let me examine the object structure and look at the reference images and stitches.
⌘ Bash📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /scratch2/code/LiteReality_to_share/agen📄 Read /home/zelda/zh340/.claude/projects/-scra🔧 ToolSearch select:mcp__cap__render
STEP #4
This is very helpful — a prior QC pass was already completed in this exact directory today. Let me verify the current state with render checks rather
⌘ Bash⌘ Bash⌘ Bash⌘ Bash
👁INSPECT #5
Build succeeded. Now let me render the room and the key walls to verify the current state.
🖼 render room
STEP #6
The MCP render needs a scan hint it can't infer. Let me use the render engine directly with explicit scan.
⌘ Bash⌘ Bash⌘ Bash
STEP #7
`render_surface` gives head-on wall views with refs — ideal for verification. Let me render all walls.
⌘ Bash
STEP #8
Let me view the wall comparison renders (render on left/right vs ref).
📄 Read /tmp/qc_surf/Wall1_compare.png📄 Read /tmp/qc_surf/Wall0_compare.png
STEP #9
Let me view larger single-frame perspective renders to check door glass, window glass, and blinds.
📄 Read /tmp/qc_surf/Wall0_frame_00082.png📄 Read /tmp/qc_surf/Wall1_frame_00018.png
STEP #10
Both walls match the references well. Let me verify the actual glass materials programmatically (the "do first" correctness item) to confirm door/wind
⌘ Bash⌘ Bash⌘ Bash⌘ Bash
STEP #11
Glass confirmed transmissive (windows 0.92–0.94 transmission, low roughness; door frosted 0.62), and `_fix_dark_glass` won't fire since all bases are
📄 Read /tmp/qc_surf/Wall2_compare.png📄 Read /tmp/qc_surf/Wall3_compare.png📄 Read /tmp/qc_surf/Ceiling0_compare.png
STEP #12
All surfaces verified. The scene in this directory already reflects a completed QC pass (done earlier today per project memory), and my independent re
All surfaces verified. The scene in this directory already reflects a completed QC pass (done earlier today per project memory), and my independent re-verification confirms every "Done when" criterion is already satisfied. I made **no edits** — the existing authoring is correct, and the prompt explicitly warns against overwriting good work. Here's what I checked and found per surface: **Openings & glass (verified from the actual GLB materials, not just renders):** - **Windows (Window0–5):** `Glass` material has `transmission 0.92–0.94`, roughness 0.03–0.05, bright base — genuine transparent glass, frames/sashes opaque. ✓ - **Door (Wall0_Door_0):** vision panels are `Frosted_Glass` transmission 0.62 (translucent privacy glazing), opaque `Frame_White`/`Brushed_Metal` handle. Both leaves carry revolute clips (2 animations). ✓ - **`_fix_dark_glass()` is a confirmed no-op** — it only fires on near-black glass (<0.35 avg); every window base is 0.90+, so the transmissive glass is never overwritten to opaque. ✓ - Openings are true boolean cuts with the leaf/pane sitting in the hole; horizontal runs (trunking/skirting) break at openings via `_wall_free_spans`. ✓ **Wall0 (door wall):** double door + glazed corridor partitions with notices taped room-side; exit sign; render matches the mirrored stitch. ✓ **Wall1 (window wall):** 4 windows with vertical vane blinds (closed / open / half / open-stack), radiator, sills — matches stitch. Blinds **articulate**: `_anim_vane_tilt` keyframes each vane's revolute tilt (frame 1 = authored rest, so the static look is unchanged). 5/6 window GLBs carry sash clips; Window5 has a closed blind baked into the asset (matches the raw stitch) with the articulated VBlind3 fixture over it — accepted by the prior pass. ✓ **Wall2:** centre-right whiteboard + thermostat + sockets/skirting — matches. ✓ **Wall3:** near-wall-spanning whiteboard + floor-stand TV in the window corner — matches. ✓ **Ceiling0:** materialed with tile PBR + T-bar grid + 6 recessed troffers — matches. ✓ **Objects:** Table0 (single beech folding table) and Table1 (long triple-leg beech table) match their `reference_1024.png` at a glance. ✓ I rebuilt `room_preview/Room.glb` from the current `Room.py` to make sure renders reflected the live source (that's the only artifact touched). No `Room.py` or `object.py` changes were needed or made.

④ Reference images used

Object references (object refs)real crop → reference · 2
captured crop
reference used
captured crop
reference used
Object references (opening refs)real crop → reference · 7
captured crop
reference used
captured crop
reference used
captured crop
reference used
captured crop
reference used
captured crop
reference used
captured crop
reference used
captured crop
reference used
Surface references (per-wall stitches)authoring ground truth · 6

Final — render | photo

photo
render
photo
render
photo
render
photo
render