@pascal-app/editor 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +15 -13
- package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
- package/src/components/editor/bake-exporter.tsx +47 -0
- package/src/components/editor/custom-camera-controls.tsx +795 -28
- package/src/components/editor/editor-layout-mobile.tsx +8 -3
- package/src/components/editor/editor-layout-v2.tsx +76 -51
- package/src/components/editor/export-manager.tsx +69 -32
- package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
- package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
- package/src/components/editor/first-person/build-collider-world.ts +228 -83
- package/src/components/editor/first-person-controls.tsx +943 -27
- package/src/components/editor/floating-action-menu.tsx +676 -208
- package/src/components/editor/floating-building-action-menu.tsx +2 -2
- package/src/components/editor/floorplan-background-selection.test.ts +58 -0
- package/src/components/editor/floorplan-background-selection.ts +3 -2
- package/src/components/editor/floorplan-panel.tsx +6916 -12137
- package/src/components/editor/grid.tsx +197 -36
- package/src/components/editor/group-actions.ts +457 -0
- package/src/components/editor/group-floating-action-menu.tsx +130 -0
- package/src/components/editor/group-move-3d.ts +397 -0
- package/src/components/editor/group-rotate-handle.tsx +420 -0
- package/src/components/editor/group-selection-box-3d.tsx +173 -0
- package/src/components/editor/group-transform-shared.test.ts +396 -0
- package/src/components/editor/group-transform-shared.ts +489 -0
- package/src/components/editor/handles/handle-arrow.tsx +581 -0
- package/src/components/editor/handles/use-handle-drag.ts +246 -0
- package/src/components/editor/index.tsx +412 -162
- package/src/components/editor/measurement-pill.tsx +115 -0
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +1646 -0
- package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
- package/src/components/editor/quick-measurement-card.tsx +70 -0
- package/src/components/editor/quick-measurement-hud.tsx +28 -0
- package/src/components/editor/riser-diagram-panel.tsx +137 -0
- package/src/components/editor/selection-manager.tsx +1106 -483
- package/src/components/editor/site-edge-labels.tsx +45 -21
- package/src/components/editor/slab-hole-highlights.tsx +474 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
- package/src/components/editor/three-context-bridge.ts +22 -0
- package/src/components/editor/thumbnail-generator.tsx +140 -41
- package/src/components/editor/use-floorplan-background-placement.ts +190 -35
- package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
- package/src/components/editor/use-floorplan-scene-data.ts +19 -7
- package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
- package/src/components/editor/wall-measurement-label.tsx +14 -19
- package/src/components/editor/wall-move-side-handles.tsx +870 -0
- package/src/components/editor/wall-opening-highlights.tsx +181 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
- package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
- package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
- package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
- package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
- package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
- package/src/components/editor-2d/svg-paths.ts +10 -2
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
- package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
- package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
- package/src/components/systems/selection-affordance-manager.tsx +38 -0
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
- package/src/components/systems/zone/zone-system.tsx +42 -13
- package/src/components/tools/elevator/elevator-defaults.ts +8 -0
- package/src/components/tools/elevator/elevator-tool.tsx +375 -0
- package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
- package/src/components/tools/fence/fence-drafting.ts +141 -27
- package/src/components/tools/item/move-tool.tsx +45 -105
- package/src/components/tools/item/placement-math.test.ts +22 -0
- package/src/components/tools/item/placement-math.ts +59 -7
- package/src/components/tools/item/placement-strategies.ts +432 -16
- package/src/components/tools/item/placement-types.ts +22 -1
- package/src/components/tools/item/use-draft-node.ts +51 -2
- package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
- package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
- package/src/components/tools/roof/roof-tool.tsx +350 -28
- package/src/components/tools/select/box-select-state.ts +88 -0
- package/src/components/tools/select/box-select-tool.tsx +442 -520
- package/src/components/tools/select/marquee-geometry.test.ts +161 -0
- package/src/components/tools/select/marquee-geometry.ts +155 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
- package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
- package/src/components/tools/select/select-candidates.test.ts +109 -0
- package/src/components/tools/select/select-candidates.ts +85 -0
- package/src/components/tools/shared/affordance-dispatch.ts +30 -0
- package/src/components/tools/shared/cursor-sphere.tsx +62 -26
- package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
- package/src/components/tools/shared/facing-indicator.tsx +87 -0
- package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
- package/src/components/tools/shared/floor-stack-preview.ts +25 -0
- package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
- package/src/components/tools/shared/placement-box-geometry.ts +123 -0
- package/src/components/tools/shared/placement-box.tsx +305 -0
- package/src/components/tools/shared/polygon-editor.tsx +866 -175
- package/src/components/tools/shared/segment-angle.ts +56 -0
- package/src/components/tools/site/site-boundary-editor.tsx +520 -16
- package/src/components/tools/site/site-flag-model.ts +18 -0
- package/src/components/tools/stair/stair-defaults.ts +2 -1
- package/src/components/tools/stair/stair-tool.tsx +342 -37
- package/src/components/tools/tool-manager.tsx +239 -62
- package/src/components/tools/wall/wall-drafting.test.ts +330 -0
- package/src/components/tools/wall/wall-drafting.ts +248 -171
- package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
- package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
- package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
- package/src/components/tools/zone/zone-tool.tsx +86 -82
- package/src/components/ui/action-menu/action-button.tsx +21 -1
- package/src/components/ui/action-menu/camera-actions.tsx +24 -17
- package/src/components/ui/action-menu/control-modes.tsx +40 -170
- package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
- package/src/components/ui/action-menu/index.tsx +3 -117
- package/src/components/ui/action-menu/measurement-control.tsx +188 -0
- package/src/components/ui/action-menu/structure-tools.tsx +27 -93
- package/src/components/ui/action-menu/view-toggles.tsx +279 -90
- package/src/components/ui/command-palette/editor-commands.tsx +20 -15
- package/src/components/ui/command-palette/index.tsx +5 -3
- package/src/components/ui/controls/material-paint-panel.tsx +152 -0
- package/src/components/ui/controls/material-picker.tsx +87 -177
- package/src/components/ui/controls/material-properties-editor.tsx +108 -0
- package/src/components/ui/controls/metric-control.tsx +142 -46
- package/src/components/ui/controls/scene-material-list.tsx +247 -0
- package/src/components/ui/controls/slider-control.tsx +88 -38
- package/src/components/ui/floating-level-selector.tsx +47 -10
- package/src/components/ui/helpers/building-helper.tsx +10 -23
- package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
- package/src/components/ui/helpers/helper-manager.tsx +235 -23
- package/src/components/ui/helpers/item-helper.tsx +28 -32
- package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
- package/src/components/ui/helpers/roof-helper.tsx +10 -12
- package/src/components/ui/icon-ref.tsx +47 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
- package/src/components/ui/level-duplicate-dialog.tsx +2 -1
- package/src/components/ui/panels/node-display.ts +17 -16
- package/src/components/ui/panels/panel-manager.tsx +55 -58
- package/src/components/ui/panels/panel-wrapper.tsx +238 -12
- package/src/components/ui/panels/parametric-inspector.tsx +482 -0
- package/src/components/ui/panels/reference-panel.tsx +54 -19
- package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
- package/src/components/ui/primitives/shortcut-token.tsx +39 -3
- package/src/components/ui/primitives/sidebar.tsx +1 -0
- package/src/components/ui/scene-loader.tsx +1 -3
- package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
- package/src/components/ui/sidebar/icon-rail.tsx +50 -31
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
- package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
- package/src/components/ui/sidebar/tab-bar.tsx +75 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
- package/src/components/ui/snap-target-badge.test.tsx +40 -0
- package/src/components/ui/snap-target-badge.tsx +88 -0
- package/src/components/viewer-overlay.tsx +253 -61
- package/src/components/viewer-zone-system.tsx +7 -2
- package/src/hooks/use-auto-save.test.ts +14 -0
- package/src/hooks/use-auto-save.ts +64 -10
- package/src/hooks/use-ceiling-events.ts +176 -0
- package/src/hooks/use-drag-action.ts +127 -0
- package/src/hooks/use-keyboard.ts +493 -95
- package/src/hooks/use-selection.ts +64 -0
- package/src/index.tsx +451 -6
- package/src/lib/active-placement-surface.ts +35 -0
- package/src/lib/ceiling-plan-snap.ts +24 -0
- package/src/lib/constants.ts +6 -2
- package/src/lib/contextual-help.test.ts +112 -0
- package/src/lib/contextual-help.ts +144 -0
- package/src/lib/continuation.ts +64 -0
- package/src/lib/direct-manipulation.test.ts +192 -0
- package/src/lib/direct-manipulation.ts +109 -0
- package/src/lib/editor-api.ts +50 -0
- package/src/lib/elevator-support.ts +96 -0
- package/src/lib/floorplan/apply-alignment.test.ts +25 -0
- package/src/lib/floorplan/apply-alignment.ts +117 -0
- package/src/lib/floorplan/floorplan-export.tsx +364 -0
- package/src/lib/floorplan/geometry.ts +53 -0
- package/src/lib/floorplan/index.ts +10 -0
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/plan-coords.ts +21 -0
- package/src/lib/floorplan/selection-tool.ts +40 -0
- package/src/lib/floorplan/stairs.ts +12 -1
- package/src/lib/fresh-planar-placement.test.ts +339 -0
- package/src/lib/fresh-planar-placement.ts +130 -0
- package/src/lib/glb-export.test.ts +441 -0
- package/src/lib/glb-export.ts +874 -0
- package/src/lib/history.ts +11 -1
- package/src/lib/interaction/hot-set.test.ts +133 -0
- package/src/lib/interaction/hot-set.ts +67 -0
- package/src/lib/interaction/overlay-policy.test.ts +51 -0
- package/src/lib/interaction/overlay-policy.ts +59 -0
- package/src/lib/interaction/scope.ts +211 -0
- package/src/lib/level-duplication.ts +54 -42
- package/src/lib/level-selection.ts +2 -2
- package/src/lib/material-paint.ts +227 -48
- package/src/lib/measurement-kind.test.ts +30 -0
- package/src/lib/measurement-kind.ts +19 -0
- package/src/lib/measurement-label.test.ts +47 -0
- package/src/lib/measurement-label.ts +68 -0
- package/src/lib/measurement-parser.ts +116 -0
- package/src/lib/measurements.test.ts +183 -0
- package/src/lib/measurements.ts +199 -0
- package/src/lib/paint-scope.test.ts +454 -0
- package/src/lib/paint-scope.ts +461 -0
- package/src/lib/placement-drag-release.ts +23 -0
- package/src/lib/placement-metadata.ts +29 -0
- package/src/lib/planar-cursor-placement.test.ts +100 -0
- package/src/lib/planar-cursor-placement.ts +42 -0
- package/src/lib/plugin-panels.test.ts +19 -0
- package/src/lib/plugin-panels.ts +91 -0
- package/src/lib/quick-measurement.test.ts +77 -0
- package/src/lib/quick-measurement.ts +109 -0
- package/src/lib/roof-duplication.ts +7 -7
- package/src/lib/roof-hover-outline-proxy.ts +22 -0
- package/src/lib/roof-wall-hit.ts +164 -0
- package/src/lib/scene-clipboard.test.ts +196 -0
- package/src/lib/scene-clipboard.ts +332 -0
- package/src/lib/scene.ts +40 -8
- package/src/lib/selection-routing.test.ts +298 -0
- package/src/lib/selection-routing.ts +175 -0
- package/src/lib/sfx/index.ts +1 -0
- package/src/lib/sfx/movement-tick.test.ts +65 -0
- package/src/lib/sfx/movement-tick.ts +18 -0
- package/src/lib/sfx-bus.ts +69 -10
- package/src/lib/sfx-player.test.ts +124 -0
- package/src/lib/sfx-player.ts +148 -55
- package/src/lib/site-boundary.ts +1 -0
- package/src/lib/slab-plan-snap.test.ts +93 -0
- package/src/lib/slab-plan-snap.ts +108 -0
- package/src/lib/snapping-mode.test.ts +138 -0
- package/src/lib/snapping-mode.ts +179 -0
- package/src/lib/stair-duplication.ts +4 -4
- package/src/lib/stair-levels.test.ts +146 -0
- package/src/lib/stair-levels.ts +177 -0
- package/src/lib/surface-plan-snap.test.ts +71 -0
- package/src/lib/surface-plan-snap.ts +256 -0
- package/src/lib/use-linear-display.ts +40 -0
- package/src/lib/world-grid-snap.ts +231 -0
- package/src/lib/zone-content.ts +130 -0
- package/src/store/use-alignment-guides.ts +21 -0
- package/src/store/use-direct-manipulation-feedback.ts +20 -0
- package/src/store/use-editor.tsx +767 -114
- package/src/store/use-facing-pose.ts +44 -0
- package/src/store/use-fence-curve-draft.ts +21 -0
- package/src/store/use-floorplan-draft-preview.ts +101 -0
- package/src/store/use-floorplan-marquee.ts +50 -0
- package/src/store/use-interaction-scope.test.ts +186 -0
- package/src/store/use-interaction-scope.ts +132 -0
- package/src/store/use-measurement-draft.test.ts +530 -0
- package/src/store/use-measurement-draft.ts +543 -0
- package/src/store/use-opening-guides.ts +41 -0
- package/src/store/use-placement-preview.ts +39 -0
- package/src/store/use-quick-measurement-hud.test.ts +53 -0
- package/src/store/use-quick-measurement-hud.ts +77 -0
- package/src/store/use-segment-draft-chain.ts +28 -0
- package/src/store/use-stair-build-preview.ts +43 -0
- package/src/store/use-wall-move-ghosts.ts +36 -0
- package/src/store/use-wall-snap-indicator.ts +34 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
- package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
- package/src/components/tools/building/move-building-tool.tsx +0 -157
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
- package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
- package/src/components/tools/column/column-tool.tsx +0 -97
- package/src/components/tools/column/move-column-tool.tsx +0 -105
- package/src/components/tools/door/door-math.ts +0 -110
- package/src/components/tools/door/door-tool.tsx +0 -324
- package/src/components/tools/door/move-door-tool.tsx +0 -412
- package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
- package/src/components/tools/fence/fence-tool.tsx +0 -346
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
- package/src/components/tools/fence/move-fence-tool.tsx +0 -302
- package/src/components/tools/item/item-tool.tsx +0 -26
- package/src/components/tools/roof/move-roof-tool.tsx +0 -364
- package/src/components/tools/slab/move-slab-tool.tsx +0 -182
- package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
- package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
- package/src/components/tools/slab/slab-tool.tsx +0 -322
- package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
- package/src/components/tools/spawn/spawn-tool.tsx +0 -130
- package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
- package/src/components/tools/wall/move-wall-tool.tsx +0 -358
- package/src/components/tools/wall/wall-tool.tsx +0 -332
- package/src/components/tools/window/move-window-tool.tsx +0 -447
- package/src/components/tools/window/window-math.ts +0 -117
- package/src/components/tools/window/window-tool.tsx +0 -332
- package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
- package/src/components/ui/helpers/slab-helper.tsx +0 -20
- package/src/components/ui/helpers/wall-helper.tsx +0 -20
- package/src/components/ui/panels/ceiling-panel.tsx +0 -249
- package/src/components/ui/panels/column-panel.tsx +0 -759
- package/src/components/ui/panels/door-panel.tsx +0 -1299
- package/src/components/ui/panels/fence-panel.tsx +0 -222
- package/src/components/ui/panels/item-panel.tsx +0 -306
- package/src/components/ui/panels/paint-panel.tsx +0 -163
- package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
- package/src/components/ui/panels/roof-panel.tsx +0 -280
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
- package/src/components/ui/panels/slab-panel.tsx +0 -250
- package/src/components/ui/panels/spawn-panel.tsx +0 -161
- package/src/components/ui/panels/stair-panel.tsx +0 -577
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
- package/src/components/ui/panels/wall-panel.tsx +0 -273
- package/src/components/ui/panels/window-panel.tsx +0 -968
- package/src/contexts/presets-context.tsx +0 -121
- package/src/hooks/use-contextual-tools.ts +0 -61
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { emitter, sceneRegistry } from '@pascal-app/core'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
backdropGradient,
|
|
6
|
+
deepSkyColor,
|
|
7
|
+
GRID_LAYER,
|
|
8
|
+
getSceneTheme,
|
|
9
|
+
horizonHazeColor,
|
|
10
|
+
packNormalToRGB,
|
|
11
|
+
SSGI_PARAMS,
|
|
12
|
+
snapLevelsToTruePositions,
|
|
13
|
+
unpackRGBToNormal,
|
|
14
|
+
useViewer,
|
|
15
|
+
} from '@pascal-app/viewer'
|
|
5
16
|
import type { CameraControls } from '@react-three/drei'
|
|
6
17
|
import { useThree } from '@react-three/fiber'
|
|
7
18
|
import { useCallback, useEffect, useRef } from 'react'
|
|
@@ -11,16 +22,18 @@ import { ssgi } from 'three/addons/tsl/display/SSGINode.js'
|
|
|
11
22
|
import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js'
|
|
12
23
|
import { fxaa } from 'three/examples/jsm/tsl/display/FXAANode.js'
|
|
13
24
|
import {
|
|
14
|
-
colorToDirection,
|
|
15
25
|
convertToTexture,
|
|
16
26
|
diffuseColor,
|
|
17
|
-
directionToColor,
|
|
18
27
|
float,
|
|
28
|
+
mix,
|
|
19
29
|
mrt,
|
|
20
30
|
normalView,
|
|
21
31
|
output,
|
|
22
32
|
pass,
|
|
23
33
|
sample,
|
|
34
|
+
screenUV,
|
|
35
|
+
smoothstep,
|
|
36
|
+
uniform,
|
|
24
37
|
vec4,
|
|
25
38
|
} from 'three/tsl'
|
|
26
39
|
import { RenderPipeline, RenderTarget, type WebGPURenderer } from 'three/webgpu'
|
|
@@ -54,6 +67,18 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
54
67
|
const pipelineRef = useRef<RenderPipeline | null>(null)
|
|
55
68
|
const renderTargetRef = useRef<RenderTarget | null>(null)
|
|
56
69
|
|
|
70
|
+
// Backdrop compositing for scene snapshots (studio renders, project
|
|
71
|
+
// thumbnails): theme background + sky gradient, same world-ray math as the
|
|
72
|
+
// viewport backdrop in viewer's post-processing. Uniform-driven so the one
|
|
73
|
+
// cached pipeline serves both opaque and transparent (preset/item) captures.
|
|
74
|
+
const bgColorUniform = useRef(uniform(new THREE.Color('#ffffff')))
|
|
75
|
+
const bgSkyUniform = useRef(uniform(new THREE.Color('#ffffff')))
|
|
76
|
+
const bgSkyDeepUniform = useRef(uniform(new THREE.Color('#ffffff')))
|
|
77
|
+
const bgHazeUniform = useRef(uniform(new THREE.Color('#ffffff')))
|
|
78
|
+
const bgProjInvUniform = useRef(uniform(new THREE.Matrix4()))
|
|
79
|
+
const bgCamWorldUniform = useRef(uniform(new THREE.Matrix4()))
|
|
80
|
+
const bgMixUniform = useRef(uniform(1))
|
|
81
|
+
|
|
57
82
|
useEffect(() => {
|
|
58
83
|
onThumbnailCaptureRef.current = onThumbnailCapture
|
|
59
84
|
}, [onThumbnailCapture])
|
|
@@ -62,6 +87,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
62
87
|
useEffect(() => {
|
|
63
88
|
const cam = new THREE.PerspectiveCamera(60, THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT, 0.1, 1000)
|
|
64
89
|
cam.layers.disable(EDITOR_LAYER)
|
|
90
|
+
cam.layers.disable(GRID_LAYER)
|
|
65
91
|
thumbnailCameraRef.current = cam
|
|
66
92
|
|
|
67
93
|
let mounted = true
|
|
@@ -73,13 +99,13 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
73
99
|
|
|
74
100
|
// pass() handles MRT internally for all material types, including custom
|
|
75
101
|
// shaders — unlike renderer.setMRT() which crashes on non-NodeMaterials.
|
|
76
|
-
// pass() also respects camera.layers, so EDITOR_LAYER objects are filtered.
|
|
102
|
+
// pass() also respects camera.layers, so EDITOR_LAYER + GRID_LAYER objects are filtered.
|
|
77
103
|
const scenePass = pass(scene, cam)
|
|
78
104
|
scenePass.setMRT(
|
|
79
105
|
mrt({
|
|
80
106
|
output,
|
|
81
107
|
diffuseColor,
|
|
82
|
-
normal:
|
|
108
|
+
normal: packNormalToRGB(normalView),
|
|
83
109
|
}),
|
|
84
110
|
)
|
|
85
111
|
|
|
@@ -90,7 +116,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
90
116
|
scenePass.getTexture('diffuseColor').type = UnsignedByteType
|
|
91
117
|
scenePass.getTexture('normal').type = UnsignedByteType
|
|
92
118
|
|
|
93
|
-
const sceneNormal = sample((uv) =>
|
|
119
|
+
const sceneNormal = sample((uv) => unpackRGBToNormal(scenePassNormal.sample(uv)))
|
|
94
120
|
|
|
95
121
|
const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, cam as any)
|
|
96
122
|
giPass.sliceCount.value = SSGI_PARAMS.sliceCount
|
|
@@ -105,14 +131,49 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
105
131
|
giPass.useScreenSpaceSampling.value = SSGI_PARAMS.useScreenSpaceSampling
|
|
106
132
|
giPass.useTemporalFiltering = SSGI_PARAMS.useTemporalFiltering
|
|
107
133
|
|
|
108
|
-
|
|
109
|
-
|
|
134
|
+
// r185: SSGI's AO lives in its own single-channel texture (getAONode)
|
|
135
|
+
// rather than the alpha of one packed rgba texture.
|
|
136
|
+
const aoTexture = (giPass as any).getAONode()
|
|
137
|
+
const aoAsRgb = vec4(aoTexture.r, aoTexture.r, aoTexture.r, float(1))
|
|
110
138
|
const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, cam)
|
|
111
139
|
denoisePass.index.value = 0
|
|
112
140
|
denoisePass.radius.value = 4
|
|
113
141
|
|
|
114
|
-
|
|
115
|
-
|
|
142
|
+
// Same far-field AO fade as the viewport pipeline — without it the
|
|
143
|
+
// horizon picks up a visible AO line in captures.
|
|
144
|
+
const aoFarFade = smoothstep(
|
|
145
|
+
float(0.9994),
|
|
146
|
+
float(0.9998),
|
|
147
|
+
scenePassDepth.sample(screenUV).r,
|
|
148
|
+
)
|
|
149
|
+
const ao = mix((denoisePass as any).r, float(1), aoFarFade)
|
|
150
|
+
const sceneRgb = scenePassColor.rgb.mul(ao)
|
|
151
|
+
|
|
152
|
+
// Per-pixel world ray from the capture camera → sky gradient above the
|
|
153
|
+
// horizon (dir.y = 0), flat background below — mirrors the viewport
|
|
154
|
+
// backdrop. bgMix 0 bypasses it and keeps the capture transparent.
|
|
155
|
+
const ndc = vec4(
|
|
156
|
+
screenUV.x.mul(2).sub(1),
|
|
157
|
+
float(1).sub(screenUV.y).mul(2).sub(1),
|
|
158
|
+
1,
|
|
159
|
+
1,
|
|
160
|
+
) as any
|
|
161
|
+
const viewRay = (bgProjInvUniform.current as any).mul(ndc)
|
|
162
|
+
const worldDir = (bgCamWorldUniform.current as any)
|
|
163
|
+
.mul(vec4(viewRay.xyz, 0))
|
|
164
|
+
.xyz.normalize()
|
|
165
|
+
const bgGradient = backdropGradient({
|
|
166
|
+
dirY: worldDir.y,
|
|
167
|
+
background: bgColorUniform.current,
|
|
168
|
+
haze: bgHazeUniform.current,
|
|
169
|
+
sky: bgSkyUniform.current,
|
|
170
|
+
skyDeep: bgSkyDeepUniform.current,
|
|
171
|
+
})
|
|
172
|
+
const alpha = scenePassColor.a
|
|
173
|
+
const finalOutput = vec4(
|
|
174
|
+
mix(sceneRgb, mix(bgGradient, sceneRgb, alpha), bgMixUniform.current),
|
|
175
|
+
mix(alpha, float(1), bgMixUniform.current),
|
|
176
|
+
)
|
|
116
177
|
|
|
117
178
|
// FXAA requires a texture node as input; convertToTexture renders finalOutput
|
|
118
179
|
// into an intermediate RT so FXAA can sample it with neighbour UV offsets.
|
|
@@ -150,7 +211,11 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
150
211
|
snapLevels: boolean,
|
|
151
212
|
captureMode?: 'standard' | 'viewport' | 'area',
|
|
152
213
|
cropRegion?: { x: number; y: number; width: number; height: number },
|
|
214
|
+
standardSize?: { w: number; h: number },
|
|
215
|
+
transparent = false,
|
|
153
216
|
) => {
|
|
217
|
+
const standardW = standardSize?.w ?? THUMBNAIL_WIDTH
|
|
218
|
+
const standardH = standardSize?.h ?? THUMBNAIL_HEIGHT
|
|
154
219
|
if (isGenerating.current) return
|
|
155
220
|
if (!onThumbnailCaptureRef.current) return
|
|
156
221
|
|
|
@@ -172,6 +237,21 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
172
237
|
const { width, height } = gl.domElement
|
|
173
238
|
thumbnailCamera.aspect = width / height
|
|
174
239
|
thumbnailCamera.updateProjectionMatrix()
|
|
240
|
+
// The capture camera never joins the scene graph, so its matrixWorld
|
|
241
|
+
// is only refreshed by the render itself — too late for the backdrop
|
|
242
|
+
// uniforms below.
|
|
243
|
+
thumbnailCamera.updateMatrixWorld()
|
|
244
|
+
|
|
245
|
+
const theme = getSceneTheme(useViewer.getState().sceneTheme)
|
|
246
|
+
bgColorUniform.current.value.set(theme.background)
|
|
247
|
+
bgSkyUniform.current.value.set(theme.backgroundSky ?? theme.background)
|
|
248
|
+
bgSkyDeepUniform.current.value.set(deepSkyColor(theme.backgroundSky ?? theme.background))
|
|
249
|
+
bgHazeUniform.current.value.set(
|
|
250
|
+
horizonHazeColor(theme.backgroundSky ?? theme.background, theme.appearance),
|
|
251
|
+
)
|
|
252
|
+
bgProjInvUniform.current.value.copy(thumbnailCamera.projectionMatrixInverse)
|
|
253
|
+
bgCamWorldUniform.current.value.copy(thumbnailCamera.matrixWorld)
|
|
254
|
+
bgMixUniform.current.value = transparent ? 0 : 1
|
|
175
255
|
|
|
176
256
|
// Capture camera data for snapshot storage
|
|
177
257
|
const pos = mainCamera.position
|
|
@@ -201,13 +281,15 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
201
281
|
restoreLevels = snapLevelsToTruePositions()
|
|
202
282
|
}
|
|
203
283
|
|
|
204
|
-
// Hide scan and
|
|
205
|
-
// thumbnail regardless of whether ScanSystem/GuideSystem listeners
|
|
206
|
-
// registered.
|
|
284
|
+
// Hide scan, guide, and spawn nodes directly so they are excluded from
|
|
285
|
+
// the thumbnail regardless of whether ScanSystem/GuideSystem listeners
|
|
286
|
+
// are registered. Spawn renders on SCENE_LAYER for occlusion, so the
|
|
287
|
+
// thumbnail camera's layer mask can't filter it either. Returns a
|
|
288
|
+
// function that restores the original visibility.
|
|
207
289
|
const restoreNodeVisibility = (() => {
|
|
208
290
|
const saved = new Map<THREE.Object3D, boolean>()
|
|
209
|
-
for (const type of ['scan', 'guide'] as const) {
|
|
210
|
-
const ids = sceneRegistry.byType[type]
|
|
291
|
+
for (const type of ['scan', 'guide', 'spawn'] as const) {
|
|
292
|
+
const ids = sceneRegistry.byType[type]!
|
|
211
293
|
ids.forEach((id) => {
|
|
212
294
|
const node = sceneRegistry.nodes.get(id)
|
|
213
295
|
if (node) {
|
|
@@ -237,18 +319,21 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
237
319
|
|
|
238
320
|
// Notify other systems (wall cutouts, selection manager) to restore
|
|
239
321
|
// their overrides before capture and re-apply them after.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
322
|
+
try {
|
|
323
|
+
emitter.emit('thumbnail:before-capture', undefined)
|
|
324
|
+
;(renderer as any).setClearAlpha(0)
|
|
325
|
+
renderer.setRenderTarget(rt)
|
|
326
|
+
pipelineRef.current.render()
|
|
327
|
+
} finally {
|
|
328
|
+
// Restore level positions, levelMode, and node visibility immediately
|
|
329
|
+
// after the render — before the async GPU readback. Runs in `finally`
|
|
330
|
+
// so a render failure can't leave helpers permanently hidden.
|
|
331
|
+
renderer.setRenderTarget(null)
|
|
332
|
+
emitter.emit('thumbnail:after-capture', undefined)
|
|
333
|
+
restoreLevels()
|
|
334
|
+
restoreLevelMode?.()
|
|
335
|
+
restoreNodeVisibility()
|
|
336
|
+
}
|
|
252
337
|
|
|
253
338
|
// Read pixels from the RT asynchronously.
|
|
254
339
|
// WebGPU copyTextureToBuffer aligns each row to 256 bytes, so we must
|
|
@@ -336,9 +421,9 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
336
421
|
offscreen.getContext('2d')!.drawImage(srcCanvas, sx, sy, outW, outH, 0, 0, outW, outH)
|
|
337
422
|
blob = await offscreen.convertToBlob({ type: 'image/png' })
|
|
338
423
|
} else {
|
|
339
|
-
// Standard: center-crop to 1920×1080
|
|
424
|
+
// Standard: center-crop to the requested aspect (default 1920×1080)
|
|
340
425
|
const srcAspect = width / height
|
|
341
|
-
const dstAspect =
|
|
426
|
+
const dstAspect = standardW / standardH
|
|
342
427
|
let sx = 0,
|
|
343
428
|
sy = 0,
|
|
344
429
|
sWidth = width,
|
|
@@ -350,8 +435,8 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
350
435
|
sHeight = Math.round(width / dstAspect)
|
|
351
436
|
sy = Math.round((height - sHeight) / 2)
|
|
352
437
|
}
|
|
353
|
-
outW =
|
|
354
|
-
outH =
|
|
438
|
+
outW = standardW
|
|
439
|
+
outH = standardH
|
|
355
440
|
const offscreen = new OffscreenCanvas(outW, outH)
|
|
356
441
|
offscreen
|
|
357
442
|
.getContext('2d')!
|
|
@@ -363,12 +448,15 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
363
448
|
cameraData.resolution = { w: outW, h: outH }
|
|
364
449
|
} else {
|
|
365
450
|
// Fallback: plain render directly to the canvas
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
451
|
+
try {
|
|
452
|
+
emitter.emit('thumbnail:before-capture', undefined)
|
|
453
|
+
gl.render(scene, thumbnailCamera)
|
|
454
|
+
} finally {
|
|
455
|
+
emitter.emit('thumbnail:after-capture', undefined)
|
|
456
|
+
restoreLevels()
|
|
457
|
+
restoreLevelMode?.()
|
|
458
|
+
restoreNodeVisibility()
|
|
459
|
+
}
|
|
372
460
|
|
|
373
461
|
let outW: number
|
|
374
462
|
let outH: number
|
|
@@ -405,7 +493,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
405
493
|
)
|
|
406
494
|
} else {
|
|
407
495
|
const srcAspect = width / height
|
|
408
|
-
const dstAspect =
|
|
496
|
+
const dstAspect = standardW / standardH
|
|
409
497
|
let sx = 0,
|
|
410
498
|
sy = 0,
|
|
411
499
|
sWidth = width,
|
|
@@ -417,8 +505,8 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
417
505
|
sHeight = Math.round(width / dstAspect)
|
|
418
506
|
sy = Math.round((height - sHeight) / 2)
|
|
419
507
|
}
|
|
420
|
-
outW =
|
|
421
|
-
outH =
|
|
508
|
+
outW = standardW
|
|
509
|
+
outH = standardH
|
|
422
510
|
const offscreen = document.createElement('canvas')
|
|
423
511
|
offscreen.width = outW
|
|
424
512
|
offscreen.height = outH
|
|
@@ -459,9 +547,20 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
459
547
|
const handleGenerateThumbnail = async (event: {
|
|
460
548
|
captureMode?: 'standard' | 'viewport' | 'area'
|
|
461
549
|
cropRegion?: { x: number; y: number; width: number; height: number }
|
|
550
|
+
standardSize?: { w: number; h: number }
|
|
462
551
|
snapLevels?: boolean
|
|
552
|
+
// Preset/item captures keep the alpha channel (their thumbnails compose
|
|
553
|
+
// onto arbitrary palette backgrounds); scene snapshots — studio renders
|
|
554
|
+
// and project thumbnails — composite the theme backdrop + sky.
|
|
555
|
+
transparent?: boolean
|
|
463
556
|
}) => {
|
|
464
|
-
await generate(
|
|
557
|
+
await generate(
|
|
558
|
+
event.snapLevels === true,
|
|
559
|
+
event.captureMode,
|
|
560
|
+
event.cropRegion,
|
|
561
|
+
event.standardSize,
|
|
562
|
+
event.transparent === true,
|
|
563
|
+
)
|
|
465
564
|
}
|
|
466
565
|
|
|
467
566
|
emitter.on('camera-controls:generate-thumbnail', handleGenerateThumbnail)
|