PIXELPUNK JOURNAL / CHAPTER 01 Editorial evidence captured 23 September 2026. These are historical project notes. Status and next-step instructions in these excerpts belong to their original development session. Performance and test results quoted here are from those sessions, not tests performed by the journal build. ======================================================================== PRODUCT.md | original lines 1–28 ======================================================================== # PixelPunk product context ## Register product ## Users and purpose Desktop players build, edit, and photograph cozy 3D dioramas. All parts are available immediately. The primary workflow is painting land, placing architecture, reshaping it, and decorating it. Source: README.md. ## Brand personality Cozy, playful, architectural. Support Day, Golden Hour, and Night (user confirmed 15 September 2026), retaining Night as the default. Preserve warm windows, the orthographic scene, and compact desktop editor controls. The Tiny Glade interaction and procedural architecture reference is documented in docs/tiny-glade-merging-plan.md. ## Anti-references The project deliberately excludes economy, progression, traffic, and walkable interiors (README.md). The editor should keep scene creation central and avoid adding unrelated management screens. ## Design principles - Keep editing reversible through the existing undo history. - Show geometry changes directly in the scene. - Reuse the inspector's standard sliders, options, keyboard handling, and focus theme. - Preserve the existing visual identity when extending controls. ## Accessibility and inclusion The existing editor provides keyboard shortcuts, focus styling, labelled controls, tooltips, sound toggles, and photo mode. Preserve these affordances. No additional accessibility target is specified in the project documentation. ======================================================================== HANDOFF.md | original lines 431–459 ======================================================================== ## Design decisions (fixed, do not re-litigate) ### Phase 0 1. `MeshBatch` groups become packed arrays and non-indexed triangle lists: `{"vertices": PackedVector3Array, "normals": PackedVector3Array, "colors": PackedColorArray, "uv": PackedVector2Array}`; there is no `indices` key any more. Primitive meshes are expanded once into cached triangle-order arrays; `append_mesh` does `actual * PackedVector3Array` for vertices and `Transform3D(normal_basis, ZERO) * normals` (no per-vertex GDScript loop). The scene shader normalises interpolated normals, so scaled normals are acceptable. `append_quad`, `triangle`, `loft` append vertices directly. Tests that read `indices` or call `.any()` on vertex arrays must be rewritten (Packed arrays have no `any`). 2. Save `VERSION = 2`. `DioramaState.migrate(data)` upgrades version 1: for building/tower, `height = floors * 2.6`, `floors` removed, `w`/`d` become floats. `validate_document` accepts 1 or 2 (migrates a copy first, then validates as 2); `restore` migrates version 1 input; version 99 stays rejected. 3. Clusters: `DioramaState.clusters()` returns connected components of buildings/towers whose footprint polygons touch or overlap (bounds grown 0.05 intersect, then polygon test). `WorldView` renders one `MeshInstance3D` per cluster (mesh built by generating each member with `batch.transform = transform_of(member)`), keyed by the sorted member ids, with the old neighbour-aware signature. Connections stay one node per object. `object_nodes` becomes `cluster_nodes`. 4. Preview level and queue: `Architecture.building(batch, o, state, detail)` where detail is "full" or "preview"; preview draws only massing (box or cylinder, lifted pillars, simple roof). `WorldView.sync_scene(state, force)` stays immediate and full. New `WorldView.sync_live(state)` is used during drags: rebuilds dirty clusters at preview level within a 6 ms budget per call and continues the remainder in `_process`; a preview-level cluster is marked so the next full sync rebuilds it. ### Phase 1 5. Building params: `w`, `d` floats (1.0 to 12.0, 0.25 steps), `height` float (1.0 to 15.6, 0.1 steps), `base` float lift (0 to 4.0), `ridge` optional float (0.3 to 8.0; roof height above the eave for pitched roofs; when absent the old per-style constant applies), `roof` still "flat" or "pitched", free `rot`. `dimensions()` returns (w, height, d). `local_bounds` starts at y = base. Towers still use `kind == "tower"` (no `shape` param). 6. Generators draw rows of `FLOOR_H` with a shortened last row (windows only when the row is at least 2.0 m). A lifted building gets simple stone corner pillars from the ground to its base (full support logic is phase 4). 7. `roof_ridge(o)` is the single source of the roof height; `roof_rise`, `local_bounds`, `ray_roof`, the canal roof and `styled_roof` all derive from it. 8. Footprints: `footprint(o)` returns world XZ polygon points (rect rotated, tower as 24-gon). Overlap = `Geometry2D.intersect_polygons` on polygons shrunk by 0.04 with `offset_polygon`. `footprint_supported` and `level_foundation` use the cells the polygon touches. Stacking = child polygon inside parent polygon (`clip_polygons` empty). Facade occlusion uses each neighbour's local box (rotation-correct) instead of its world AABB. 9. Gizmos: face handles (x±, z±, continuous 0.25 m), top knob (height, 0.1 m), bottom knob (base lift, 0.1 m), corner rotate handle (5 degree steps), roof apex handle (ridge, 0.1 m; dragging below 0.3 turns the roof flat, dragging a flat roof's apex above 0.3 makes it pitched). Wall parts on the dragged face move with it; other children keep world position; stacked children follow height and base changes. 10. Snapping in `object_candidate`: 0.25 m grid, then edge-to-edge flush snap within 0.4 m to parallel neighbour edges, centre-to-centre for towers over towers, and centre-to-ridge for pitched neighbours; holding Ctrl (or Cmd) disables snapping. The candidate preview draws the rotated footprint prism as a wireframe cage. 11. Inspector: Width, Depth, Height, Lift sliders (decimal display), Roof, Roof rise (pitched only), Windows, Stone. `main.params` defaults `height 5.2`, `base 0`. ## Progress log - [x] 0.1 MeshBatch packed arrays (`scripts/mesh_batch.gd` rewritten; courtyard buildings, 233k vertices, generate in about 18 ms) - [x] 0.2 Save version 2 and migration (`DioramaState.migrate`, `validate_document` accepts 1 and 2, `restore` migrates, `SceneStorage.read_scene` opens version 1 files) - [x] 0.3 Clusters in WorldView (`DioramaState.clusters`, `WorldView._sync_clusters`, `_build_cluster`, `_sync_connections`; `cluster_nodes` keyed by sorted ids) - [x] 0.4 Preview level and live queue (`Architecture.building(..., detail)`, `Architecture.massing`, `WorldView.sync_live`, `_process` drains `pending_clusters` within `LIVE_BUDGET_USEC`) - [x] 1.1 Float dimensions, base, ridge, footprints, validation (`dimensions`, `base_of`, `building_height`, `floor_rows`, `roof_ridge`, `roof_rise`, `local_bounds` are static; `footprint`, `polygons_overlap`, `polygons_touch`, `polygon_inside`, `cells_under`, `footprint_supported(o)`, `level_foundation(target, y)`) - [x] 1.2 Generators for partial rows, lift pillars, ridge (`canal_building`, `styled_building`, `lift_pillars`, `occluders` with local boxes, `styled_roof(..., rise)`) - [x] 1.3 Gizmos and resize (`WorldView.show_handles` with base, rot, ridge knobs; `main.start_resize`, `_handle_angle`, `drag_resize`, `_apply_resize`, `carry_attachments`) - [x] 1.4 Snapping and cage preview (`main.snap_candidate`, `outline_edges`, `snapping_active`; `WorldView.show_cage`, `show_bounds` with a frame) ======================================================================== README.md | original lines 71–81 ======================================================================== ```bash tools/setup.sh --export-templates .tools/godot-macos/Godot.app/Contents/MacOS/Godot --editor --path . tools/test.sh tools/build.sh ``` Setup downloads the official portable Godot for the current platform into `.tools` and extracts only that platform's export templates from the shared 1.2 GB template archive. The runtime, imports, builds, and verification artifacts are ignored by Git. ### Switching between PC and Mac