fix: bug pass — legacy window crash, plant exports, zone snapping/visibility, HDR (#473)

* fix(core): apply window schema defaults on scene load

Windows saved before a schema field existed (columnRatios/rowRatios/
frameThickness/…) loaded with those fields missing; the window mesh
builder reads them unconditionally and threw every frame, crashing the
viewer on legacy scenes. Zod-parse windows on load like doors so the
schema defaults land.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(editor): mount plant geometry during client GLB/STL/OBJ export

The client export never set isExporting, so instanced kinds (trees/
flowers/grass) kept their colorWrite:false raycast collider mounted and
exported it as an opaque white box, while the real geometry (which only
mounts while exporting) was never captured. Reuse BakeExporter's
flag + frame-wait dance in ExportManager, and harden isRenderableMesh
to drop colorWrite:false materials from exports entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(editor): route zone drawing through the shared surface snap

The zone tool quantized later vertices by distance along a free ray
instead of snapping to the grid, and never joined the magnetic
wall-corner/midpoint/crossing + alignment-guide pipeline that walls,
slabs and ceilings use. Give zone the slab treatment in the 3D tool and
both 2D floorplan branches (move + click + double-click commit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(viewer): self-host the scene environment HDR

drei's preset="sunset" fetches venice_sunset_1k.hdr from
raw.githack.com, which intermittently fails ("Could not load
venice_sunset_1k.hdr: Failed to fetch"). Point Environment at
/hdri/venice_sunset_1k.hdr and ship the file in the app's public/ —
same mirroring convention as /audios/sfx; consuming apps must carry
the file too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(editor): unmount zones entirely outside the zones layer

Zone labels showed in every editing mode (visual noise), and each zone
kept a drei <Html> mounted at opacity 0 — an <Html> costs per-frame
matrix work and live DOM even when invisible. A new viewer presentation
flag (showZones, default true) lets the editor unmount zone meshes and
labels whenever the structure layer isn't 'zones' (and during snapshot
capture); the registered group stays so zones keep their scene identity
for selection and GLB export. Preview / first-person / viewer surfaces
are untouched. Raycast-disable moved from a one-shot group flag to
per-frame on the meshes, which now remount on layer toggles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-07-08 10:19:27 -04:00
committed by GitHub
co-authored by Claude Fable 5
parent 51fddc2d9c
commit 717c2c5c0a
14 changed files with 378 additions and 226 deletions
@@ -12,11 +12,16 @@ import { Suspense } from 'react'
* do alone. Intensity is dialled below the preset default so it complements
* the scene lights rather than washing them out. Only visible in `rendered`
* shading.
*
* The HDR is self-hosted (drei's `preset="sunset"` resolves to the same
* `venice_sunset_1k.hdr` on raw.githack.com, which intermittently fails).
* Every app that mounts this — like `/audios/sfx` — must ship the file in its
* own `public/hdri/`.
*/
export function SceneEnvironment() {
return (
<Suspense fallback={null}>
<Environment preset="sunset" environmentIntensity={0.6} />
<Environment environmentIntensity={0.6} files="/hdri/venice_sunset_1k.hdr" />
</Suspense>
)
}
+11
View File
@@ -87,6 +87,14 @@ type ViewerState = {
showGrid: boolean
setShowGrid: (show: boolean) => void
// Presentation flag for parametric zones. When false the zone renderer
// unmounts its meshes AND its drei <Html> label (an <Html> costs per-frame
// matrix work + live DOM even at opacity 0, so hiding is not enough). The
// editor drives this from its structure layer; viewer surfaces keep the
// default. Not persisted — derived state, not a user preference.
showZones: boolean
setShowZones: (show: boolean) => void
transparentBackground: boolean
setTransparentBackground: (transparent: boolean) => void
@@ -308,6 +316,9 @@ const useViewer = create<ViewerState>()(
return { showGrid: show, projectPreferences }
}),
showZones: true,
setShowZones: (show) => set({ showZones: show }),
transparentBackground: false,
setTransparentBackground: (transparent) => set({ transparentBackground: transparent }),