Move render systems out of core
This commit is contained in:
@@ -56,8 +56,6 @@
|
|||||||
"idb-keyval": "^6.2.2",
|
"idb-keyval": "^6.2.2",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"three-bvh-csg": "^0.0.18",
|
|
||||||
"three-mesh-bvh": "^0.9.8",
|
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"zundo": "^2.3.0",
|
"zundo": "^2.3.0",
|
||||||
"zustand": "^5",
|
"zustand": "^5",
|
||||||
@@ -196,6 +194,8 @@
|
|||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"polygon-clipping": "^0.15.7",
|
"polygon-clipping": "^0.15.7",
|
||||||
|
"three-bvh-csg": "^0.0.18",
|
||||||
|
"three-mesh-bvh": "^0.9.8",
|
||||||
"zustand": "^5",
|
"zustand": "^5",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -67,8 +67,6 @@
|
|||||||
"idb-keyval": "^6.2.2",
|
"idb-keyval": "^6.2.2",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"three-bvh-csg": "^0.0.18",
|
|
||||||
"three-mesh-bvh": "^0.9.8",
|
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"zundo": "^2.3.0",
|
"zundo": "^2.3.0",
|
||||||
"zustand": "^5"
|
"zustand": "^5"
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ export {
|
|||||||
type MaterialCategory,
|
type MaterialCategory,
|
||||||
toLibraryMaterialRef,
|
toLibraryMaterialRef,
|
||||||
} from './material-library'
|
} from './material-library'
|
||||||
export { baseMaterial, glassMaterial } from './materials'
|
|
||||||
export * from './schema'
|
export * from './schema'
|
||||||
export {
|
export {
|
||||||
getSceneHistoryPauseDepth,
|
getSceneHistoryPauseDepth,
|
||||||
@@ -67,13 +66,7 @@ export {
|
|||||||
} from './store/use-interactive'
|
} from './store/use-interactive'
|
||||||
export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms'
|
export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms'
|
||||||
export { clearSceneHistory, default as useScene } from './store/use-scene'
|
export { clearSceneHistory, default as useScene } from './store/use-scene'
|
||||||
export { CeilingSystem } from './systems/ceiling/ceiling-system'
|
export { syncAutoStairOpenings } from './systems/stair/stair-opening-sync'
|
||||||
export { DoorSystem } from './systems/door/door-system'
|
|
||||||
export { FenceSystem } from './systems/fence/fence-system'
|
|
||||||
export { ItemSystem } from './systems/item/item-system'
|
|
||||||
export { RoofSystem } from './systems/roof/roof-system'
|
|
||||||
export { SlabSystem } from './systems/slab/slab-system'
|
|
||||||
export { StairSystem } from './systems/stair/stair-system'
|
|
||||||
export {
|
export {
|
||||||
getClampedWallCurveOffset,
|
getClampedWallCurveOffset,
|
||||||
getMaxWallCurveOffset,
|
getMaxWallCurveOffset,
|
||||||
@@ -95,14 +88,13 @@ export {
|
|||||||
} from './systems/wall/wall-footprint'
|
} from './systems/wall/wall-footprint'
|
||||||
export {
|
export {
|
||||||
calculateLevelMiters,
|
calculateLevelMiters,
|
||||||
|
getAdjacentWallIds,
|
||||||
getWallMiterBoundaryPoints,
|
getWallMiterBoundaryPoints,
|
||||||
type Point2D,
|
type Point2D,
|
||||||
pointToKey,
|
pointToKey,
|
||||||
type WallMiterBoundaryPoints,
|
type WallMiterBoundaryPoints,
|
||||||
type WallMiterData,
|
type WallMiterData,
|
||||||
} from './systems/wall/wall-mitering'
|
} from './systems/wall/wall-mitering'
|
||||||
export { WallSystem } from './systems/wall/wall-system'
|
|
||||||
export { WindowSystem } from './systems/window/window-system'
|
|
||||||
export type { SceneGraph } from './utils/clone-scene-graph'
|
export type { SceneGraph } from './utils/clone-scene-graph'
|
||||||
export { cloneLevelSubtree, cloneSceneGraph, forkSceneGraph } from './utils/clone-scene-graph'
|
export { cloneLevelSubtree, cloneSceneGraph, forkSceneGraph } from './utils/clone-scene-graph'
|
||||||
export { isObject } from './utils/types'
|
export { isObject } from './utils/types'
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared base material for structural elements: walls, frames, slabs, roof.
|
|
||||||
*/
|
|
||||||
export const baseMaterial = new MeshStandardNodeMaterial({
|
|
||||||
color: '#f2f0ed',
|
|
||||||
roughness: 0.5,
|
|
||||||
metalness: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared glass material for windows, glazed door panels, and glass items.
|
|
||||||
*/
|
|
||||||
export const glassMaterial = new MeshStandardNodeMaterial({
|
|
||||||
name: 'glass',
|
|
||||||
color: 'lightblue',
|
|
||||||
roughness: 0.05,
|
|
||||||
metalness: 0.1,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.35,
|
|
||||||
side: DoubleSide,
|
|
||||||
depthWrite: false,
|
|
||||||
})
|
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"polygon-clipping": "^0.15.7",
|
"polygon-clipping": "^0.15.7",
|
||||||
|
"three-bvh-csg": "^0.0.18",
|
||||||
|
"three-mesh-bvh": "^0.9.8",
|
||||||
"zustand": "^5"
|
"zustand": "^5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
type AnimationEffect,
|
type AnimationEffect,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
baseMaterial,
|
|
||||||
glassMaterial,
|
|
||||||
type Interactive,
|
type Interactive,
|
||||||
type ItemNode,
|
type ItemNode,
|
||||||
type LightEffect,
|
type LightEffect,
|
||||||
@@ -21,6 +19,7 @@ import { positionLocal, smoothstep, time } from 'three/tsl'
|
|||||||
import { MeshStandardNodeMaterial } from 'three/webgpu'
|
import { MeshStandardNodeMaterial } from 'three/webgpu'
|
||||||
import { useNodeEvents } from '../../../hooks/use-node-events'
|
import { useNodeEvents } from '../../../hooks/use-node-events'
|
||||||
import { resolveCdnUrl } from '../../../lib/asset-url'
|
import { resolveCdnUrl } from '../../../lib/asset-url'
|
||||||
|
import { baseMaterial, glassMaterial } from '../../../lib/materials'
|
||||||
import { useItemLightPool } from '../../../store/use-item-light-pool'
|
import { useItemLightPool } from '../../../store/use-item-light-pool'
|
||||||
import {
|
import {
|
||||||
requestItemMeshMetadataSync,
|
requestItemMeshMetadataSync,
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import {
|
|
||||||
CeilingSystem,
|
|
||||||
DoorSystem,
|
|
||||||
FenceSystem,
|
|
||||||
ItemSystem,
|
|
||||||
RoofSystem,
|
|
||||||
SlabSystem,
|
|
||||||
StairSystem,
|
|
||||||
WallSystem,
|
|
||||||
WindowSystem,
|
|
||||||
} from '@pascal-app/core'
|
|
||||||
import { Bvh } from '@react-three/drei'
|
import { Bvh } from '@react-three/drei'
|
||||||
import { Canvas, extend, type ThreeToJSXElements, useFrame, useThree } from '@react-three/fiber'
|
import { Canvas, extend, type ThreeToJSXElements, useFrame, useThree } from '@react-three/fiber'
|
||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useMemo, useRef } from 'react'
|
||||||
import * as THREE from 'three/webgpu'
|
import * as THREE from 'three/webgpu'
|
||||||
import useViewer from '../../store/use-viewer'
|
import useViewer from '../../store/use-viewer'
|
||||||
|
import { CeilingSystem } from '../../systems/ceiling/ceiling-system'
|
||||||
|
import { DoorSystem } from '../../systems/door/door-system'
|
||||||
|
import { FenceSystem } from '../../systems/fence/fence-system'
|
||||||
import { GuideSystem } from '../../systems/guide/guide-system'
|
import { GuideSystem } from '../../systems/guide/guide-system'
|
||||||
|
import { ItemSystem } from '../../systems/item/item-system'
|
||||||
import { ItemLightSystem } from '../../systems/item-light/item-light-system'
|
import { ItemLightSystem } from '../../systems/item-light/item-light-system'
|
||||||
import { ItemMeshMetadataSystem } from '../../systems/item-mesh-metadata/item-mesh-metadata-system'
|
import { ItemMeshMetadataSystem } from '../../systems/item-mesh-metadata/item-mesh-metadata-system'
|
||||||
import { LevelSystem } from '../../systems/level/level-system'
|
import { LevelSystem } from '../../systems/level/level-system'
|
||||||
|
import { RoofSystem } from '../../systems/roof/roof-system'
|
||||||
import { ScanSystem } from '../../systems/scan/scan-system'
|
import { ScanSystem } from '../../systems/scan/scan-system'
|
||||||
|
import { SlabSystem } from '../../systems/slab/slab-system'
|
||||||
|
import { StairSystem } from '../../systems/stair/stair-system'
|
||||||
import { WallCutout } from '../../systems/wall/wall-cutout'
|
import { WallCutout } from '../../systems/wall/wall-cutout'
|
||||||
|
import { WallSystem } from '../../systems/wall/wall-system'
|
||||||
|
import { WindowSystem } from '../../systems/window/window-system'
|
||||||
import { ZoneSystem } from '../../systems/zone/zone-system'
|
import { ZoneSystem } from '../../systems/zone/zone-system'
|
||||||
import { ErrorBoundary } from '../error-boundary'
|
import { ErrorBoundary } from '../error-boundary'
|
||||||
import { SceneRenderer } from '../renderers/scene-renderer'
|
import { SceneRenderer } from '../renderers/scene-renderer'
|
||||||
|
|||||||
@@ -7,6 +7,22 @@ import {
|
|||||||
resolveMaterial,
|
resolveMaterial,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
|
import { MeshStandardNodeMaterial } from 'three/webgpu'
|
||||||
|
|
||||||
|
export const baseMaterial = new MeshStandardNodeMaterial({
|
||||||
|
color: '#f2f0ed',
|
||||||
|
roughness: 0.5,
|
||||||
|
metalness: 0.0,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const glassMaterial = new MeshStandardNodeMaterial({
|
||||||
|
color: '#e0f2fe',
|
||||||
|
roughness: 0.05,
|
||||||
|
metalness: 0.0,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.35,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
})
|
||||||
|
|
||||||
const sideMap: Record<MaterialProperties['side'], THREE.Side> = {
|
const sideMap: Record<MaterialProperties['side'], THREE.Side> = {
|
||||||
front: THREE.FrontSide,
|
front: THREE.FrontSide,
|
||||||
|
|||||||
+1
-3
@@ -1,8 +1,6 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import { type AnyNodeId, type CeilingNode, sceneRegistry, useScene } from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import type { AnyNodeId, CeilingNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
function ensureUv2Attribute(geometry: THREE.BufferGeometry) {
|
function ensureUv2Attribute(geometry: THREE.BufferGeometry) {
|
||||||
const uv = geometry.getAttribute('uv')
|
const uv = geometry.getAttribute('uv')
|
||||||
+7
-4
@@ -1,9 +1,12 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNodeId,
|
||||||
|
type DoorNode,
|
||||||
|
sceneRegistry,
|
||||||
|
useScene,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
import { baseMaterial, glassMaterial } from '../../lib/materials'
|
||||||
import { baseMaterial, glassMaterial } from '../../materials'
|
|
||||||
import type { AnyNodeId, DoorNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
// Invisible material for root mesh — used as selection hitbox only
|
// Invisible material for root mesh — used as selection hitbox only
|
||||||
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
||||||
+8
-4
@@ -1,10 +1,14 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNodeId,
|
||||||
|
type FenceNode,
|
||||||
|
getWallCurveFrameAt,
|
||||||
|
getWallCurveLength,
|
||||||
|
sceneRegistry,
|
||||||
|
useScene,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import type { AnyNodeId, FenceNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
import { getWallCurveFrameAt, getWallCurveLength } from '../wall/wall-curve'
|
|
||||||
|
|
||||||
type FencePart = {
|
type FencePart = {
|
||||||
position: [number, number, number]
|
position: [number, number, number]
|
||||||
+10
-5
@@ -1,10 +1,15 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNodeId,
|
||||||
|
getScaledDimensions,
|
||||||
|
type ItemNode,
|
||||||
|
resolveLevelId,
|
||||||
|
sceneRegistry,
|
||||||
|
spatialGridManager,
|
||||||
|
useScene,
|
||||||
|
type WallNode,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import type * as THREE from 'three'
|
import type * as THREE from 'three'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager'
|
|
||||||
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync'
|
|
||||||
import { type AnyNodeId, getScaledDimensions, type ItemNode, type WallNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// ITEM SYSTEM
|
// ITEM SYSTEM
|
||||||
+18
-12
@@ -1,21 +1,30 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNode,
|
||||||
|
type AnyNodeId,
|
||||||
|
type RoofNode,
|
||||||
|
type RoofSegmentNode,
|
||||||
|
type RoofType,
|
||||||
|
sceneRegistry,
|
||||||
|
useScene,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
import { ADDITION, Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg'
|
import { ADDITION, Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg'
|
||||||
import { computeBoundsTree } from 'three-mesh-bvh'
|
import { computeBoundsTree } from 'three-mesh-bvh'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import type { AnyNode, AnyNodeId, RoofNode, RoofSegmentNode } from '../../schema'
|
|
||||||
import type { RoofType } from '../../schema/nodes/roof-segment'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
const csgEvaluator = new Evaluator()
|
const csgEvaluator = new Evaluator()
|
||||||
csgEvaluator.useGroups = true
|
csgEvaluator.useGroups = true
|
||||||
;(csgEvaluator as any).consolidateGroups = false // shared dummyMats across brushes causes consolidation to misalign groupIndices vs groupOrder indices → crash
|
;(csgEvaluator as any).consolidateGroups = false // shared dummyMats across brushes causes consolidation to misalign groupIndices vs groupOrder indices → crash
|
||||||
csgEvaluator.attributes = ['position', 'normal', 'uv']
|
csgEvaluator.attributes = ['position', 'normal', 'uv']
|
||||||
|
|
||||||
|
function computeGeometryBoundsTree(geometry: THREE.BufferGeometry) {
|
||||||
|
;(geometry as any).computeBoundsTree = computeBoundsTree
|
||||||
|
;(geometry as any).computeBoundsTree({ maxLeafSize: 10 })
|
||||||
|
}
|
||||||
|
|
||||||
function prepareBrushForCSG(brush: Brush) {
|
function prepareBrushForCSG(brush: Brush) {
|
||||||
brush.geometry.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(brush.geometry)
|
||||||
brush.geometry.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
brush.updateMatrixWorld()
|
brush.updateMatrixWorld()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +87,7 @@ export const RoofSystem = () => {
|
|||||||
mesh.geometry.dispose()
|
mesh.geometry.dispose()
|
||||||
const placeholder = new THREE.BufferGeometry()
|
const placeholder = new THREE.BufferGeometry()
|
||||||
placeholder.setAttribute('position', new THREE.Float32BufferAttribute([], 3))
|
placeholder.setAttribute('position', new THREE.Float32BufferAttribute([], 3))
|
||||||
placeholder.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(placeholder)
|
||||||
placeholder.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
mesh.geometry = placeholder
|
mesh.geometry = placeholder
|
||||||
}
|
}
|
||||||
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
||||||
@@ -134,8 +142,7 @@ function updateRoofSegmentGeometry(node: RoofSegmentNode, mesh: THREE.Mesh) {
|
|||||||
|
|
||||||
mesh.geometry.dispose()
|
mesh.geometry.dispose()
|
||||||
mesh.geometry = newGeo
|
mesh.geometry = newGeo
|
||||||
newGeo.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(newGeo)
|
||||||
newGeo.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
|
|
||||||
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
||||||
mesh.rotation.y = node.rotation
|
mesh.rotation.y = node.rotation
|
||||||
@@ -524,8 +531,7 @@ export function getRoofSegmentBrushes(
|
|||||||
// when a group exists but covers no triangles (can happen after mergeVertices)
|
// when a group exists but covers no triangles (can happen after mergeVertices)
|
||||||
geo.groups = geo.groups.filter((g) => g.count > 0)
|
geo.groups = geo.groups.filter((g) => g.count > 0)
|
||||||
if (geo.groups.length === 0) return null
|
if (geo.groups.length === 0) return null
|
||||||
geo.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(geo)
|
||||||
geo.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
const brush = new Brush(geo, dummyMats)
|
const brush = new Brush(geo, dummyMats)
|
||||||
brush.updateMatrixWorld()
|
brush.updateMatrixWorld()
|
||||||
return brush
|
return brush
|
||||||
+7
-4
@@ -1,9 +1,12 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNodeId,
|
||||||
|
getRenderableSlabPolygon,
|
||||||
|
sceneRegistry,
|
||||||
|
type SlabNode,
|
||||||
|
useScene,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import { getRenderableSlabPolygon } from '../../lib/slab-polygon'
|
|
||||||
import type { AnyNodeId, SlabNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
function ensureUv2Attribute(geometry: THREE.BufferGeometry) {
|
function ensureUv2Attribute(geometry: THREE.BufferGeometry) {
|
||||||
const uv = geometry.getAttribute('uv')
|
const uv = geometry.getAttribute('uv')
|
||||||
+11
-6
@@ -1,13 +1,18 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNode,
|
||||||
|
type AnyNodeId,
|
||||||
|
resolveLevelId,
|
||||||
|
sceneRegistry,
|
||||||
|
spatialGridManager,
|
||||||
|
type StairNode,
|
||||||
|
type StairSegmentNode,
|
||||||
|
syncAutoStairOpenings,
|
||||||
|
useScene,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager'
|
|
||||||
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync'
|
|
||||||
import type { AnyNode, AnyNodeId, StairNode, StairSegmentNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
import { syncAutoStairOpenings } from './stair-opening-sync'
|
|
||||||
|
|
||||||
const pendingStairUpdates = new Set<AnyNodeId>()
|
const pendingStairUpdates = new Set<AnyNodeId>()
|
||||||
const MAX_STAIRS_PER_FRAME = 2
|
const MAX_STAIRS_PER_FRAME = 2
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
baseMaterial,
|
|
||||||
getEffectiveWallSurfaceMaterial,
|
getEffectiveWallSurfaceMaterial,
|
||||||
getMaterialPresetByRef,
|
getMaterialPresetByRef,
|
||||||
getWallSurfaceMaterialSignature,
|
getWallSurfaceMaterialSignature,
|
||||||
@@ -10,7 +9,7 @@ import {
|
|||||||
import { Color, type Material } from 'three'
|
import { Color, type Material } from 'three'
|
||||||
import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, vec2 } from 'three/tsl'
|
import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, vec2 } from 'three/tsl'
|
||||||
import { MeshStandardNodeMaterial } from 'three/webgpu'
|
import { MeshStandardNodeMaterial } from 'three/webgpu'
|
||||||
import { createMaterial, createMaterialFromPresetRef } from '../../lib/materials'
|
import { baseMaterial, createMaterial, createMaterialFromPresetRef } from '../../lib/materials'
|
||||||
|
|
||||||
const DEFAULT_WALL_COLOR = '#f2f0ed'
|
const DEFAULT_WALL_COLOR = '#f2f0ed'
|
||||||
|
|
||||||
|
|||||||
+24
-14
@@ -2,21 +2,29 @@ import { useFrame } from '@react-three/fiber'
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg'
|
import { Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg'
|
||||||
import { computeBoundsTree } from 'three-mesh-bvh'
|
import { computeBoundsTree } from 'three-mesh-bvh'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
|
||||||
import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager'
|
|
||||||
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync'
|
|
||||||
import type { AnyNode, AnyNodeId, DoorNode, WallNode, WindowNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
import { getWallCurveFrameAt, getWallSurfacePolygon, isCurvedWall } from './wall-curve'
|
|
||||||
import { DEFAULT_WALL_HEIGHT, getWallPlanFootprint, getWallThickness } from './wall-footprint'
|
|
||||||
import {
|
import {
|
||||||
calculateLevelMiters,
|
calculateLevelMiters,
|
||||||
|
type AnyNode,
|
||||||
|
type AnyNodeId,
|
||||||
|
type DoorNode,
|
||||||
getAdjacentWallIds,
|
getAdjacentWallIds,
|
||||||
|
DEFAULT_WALL_HEIGHT,
|
||||||
|
getWallCurveFrameAt,
|
||||||
getWallMiterBoundaryPoints,
|
getWallMiterBoundaryPoints,
|
||||||
|
getWallPlanFootprint,
|
||||||
|
getWallSurfacePolygon,
|
||||||
|
getWallThickness,
|
||||||
|
isCurvedWall,
|
||||||
type Point2D,
|
type Point2D,
|
||||||
pointToKey,
|
pointToKey,
|
||||||
|
resolveLevelId,
|
||||||
|
sceneRegistry,
|
||||||
|
spatialGridManager,
|
||||||
|
useScene,
|
||||||
|
type WallNode,
|
||||||
type WallMiterData,
|
type WallMiterData,
|
||||||
} from './wall-mitering'
|
type WindowNode,
|
||||||
|
} from '@pascal-app/core'
|
||||||
|
|
||||||
// Reusable CSG evaluator for better performance
|
// Reusable CSG evaluator for better performance
|
||||||
const csgEvaluator = new Evaluator()
|
const csgEvaluator = new Evaluator()
|
||||||
@@ -24,6 +32,11 @@ const CURVED_WALL_3D_ENDPOINT_INSET = 0.0015
|
|||||||
const WALL_FACE_NORMAL_Y_EPSILON = 0.6
|
const WALL_FACE_NORMAL_Y_EPSILON = 0.6
|
||||||
const WALL_FACE_EDGE_DISTANCE_EPSILON = 0.003
|
const WALL_FACE_EDGE_DISTANCE_EPSILON = 0.003
|
||||||
|
|
||||||
|
function computeGeometryBoundsTree(geometry: THREE.BufferGeometry) {
|
||||||
|
;(geometry as any).computeBoundsTree = computeBoundsTree
|
||||||
|
;(geometry as any).computeBoundsTree({ maxLeafSize: 10 })
|
||||||
|
}
|
||||||
|
|
||||||
type WallBoundaryEdgeTag = 'front' | 'back' | 'base'
|
type WallBoundaryEdgeTag = 'front' | 'back' | 'base'
|
||||||
|
|
||||||
type TaggedWallBoundaryEdge = {
|
type TaggedWallBoundaryEdge = {
|
||||||
@@ -495,8 +508,7 @@ export function generateExtrudedWall(
|
|||||||
|
|
||||||
// Create wall brush from geometry
|
// Create wall brush from geometry
|
||||||
// Pre-compute BVH with new API to avoid deprecation warning
|
// Pre-compute BVH with new API to avoid deprecation warning
|
||||||
geometry.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(geometry)
|
||||||
geometry.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
|
|
||||||
const wallBrush = new Brush(geometry)
|
const wallBrush = new Brush(geometry)
|
||||||
wallBrush.updateMatrixWorld()
|
wallBrush.updateMatrixWorld()
|
||||||
@@ -599,8 +611,7 @@ function collectCutoutBrushes(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Pre-compute BVH with new API to avoid deprecation warning
|
// Pre-compute BVH with new API to avoid deprecation warning
|
||||||
boxGeo.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(boxGeo)
|
||||||
boxGeo.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
|
|
||||||
const brush = new Brush(boxGeo)
|
const brush = new Brush(boxGeo)
|
||||||
brushes.push(brush)
|
brushes.push(brush)
|
||||||
@@ -638,8 +649,7 @@ function createShapedOpeningCutoutBrush(opening: ShapedOpeningNode, wallThicknes
|
|||||||
})
|
})
|
||||||
|
|
||||||
geometry.translate(0, 0, -depth / 2)
|
geometry.translate(0, 0, -depth / 2)
|
||||||
geometry.computeBoundsTree = computeBoundsTree
|
computeGeometryBoundsTree(geometry)
|
||||||
geometry.computeBoundsTree({ maxLeafSize: 10 })
|
|
||||||
|
|
||||||
return new Brush(geometry)
|
return new Brush(geometry)
|
||||||
}
|
}
|
||||||
+7
-4
@@ -1,9 +1,12 @@
|
|||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
|
import {
|
||||||
|
type AnyNodeId,
|
||||||
|
sceneRegistry,
|
||||||
|
useScene,
|
||||||
|
type WindowNode,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
import { baseMaterial, glassMaterial } from '../../lib/materials'
|
||||||
import { baseMaterial, glassMaterial } from '../../materials'
|
|
||||||
import type { AnyNodeId, WindowNode } from '../../schema'
|
|
||||||
import useScene from '../../store/use-scene'
|
|
||||||
|
|
||||||
// Invisible material for root mesh — used as selection hitbox only
|
// Invisible material for root mesh — used as selection hitbox only
|
||||||
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
||||||
Reference in New Issue
Block a user