slab renderer
This commit is contained in:
@@ -26,6 +26,7 @@ export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query'
|
||||
export * from './schema'
|
||||
export { default as useScene } from './store/use-scene'
|
||||
// Systems
|
||||
export { SlabSystem } from './systems/slab/slab-system'
|
||||
export { WallSystem } from './systems/wall/wall-system'
|
||||
|
||||
export { isObject } from './utils/types'
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import * as THREE from 'three'
|
||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
||||
import type { AnyNodeId, SlabNode } from '../../schema'
|
||||
import useScene from '../../store/use-scene'
|
||||
|
||||
// ============================================================================
|
||||
// SLAB SYSTEM
|
||||
// ============================================================================
|
||||
|
||||
export const SlabSystem = () => {
|
||||
const { nodes, dirtyNodes, clearDirty } = useScene()
|
||||
|
||||
useFrame(() => {
|
||||
if (dirtyNodes.size === 0) return
|
||||
|
||||
// Process dirty slabs
|
||||
dirtyNodes.forEach((id) => {
|
||||
const node = nodes[id]
|
||||
if (!node || node.type !== 'slab') return
|
||||
|
||||
const mesh = sceneRegistry.nodes.get(id) as THREE.Mesh
|
||||
if (mesh) {
|
||||
updateSlabGeometry(node as SlabNode, mesh)
|
||||
}
|
||||
clearDirty(id as AnyNodeId)
|
||||
})
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the geometry for a single slab
|
||||
*/
|
||||
function updateSlabGeometry(node: SlabNode, mesh: THREE.Mesh) {
|
||||
const newGeo = generateSlabGeometry(node)
|
||||
|
||||
mesh.geometry.dispose()
|
||||
mesh.geometry = newGeo
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates extruded slab geometry from polygon
|
||||
*/
|
||||
export function generateSlabGeometry(slabNode: SlabNode): THREE.BufferGeometry {
|
||||
const polygon = slabNode.polygon
|
||||
const elevation = slabNode.elevation ?? 0.05
|
||||
|
||||
if (polygon.length < 3) {
|
||||
return new THREE.BufferGeometry()
|
||||
}
|
||||
|
||||
// Create shape from polygon
|
||||
// Shape is in X-Y plane, we'll rotate to X-Z plane after extrusion
|
||||
const shape = new THREE.Shape()
|
||||
const firstPt = polygon[0]!
|
||||
|
||||
// Negate Y (which becomes Z) to get correct orientation after rotation
|
||||
shape.moveTo(firstPt[0], -firstPt[1])
|
||||
|
||||
for (let i = 1; i < polygon.length; i++) {
|
||||
const pt = polygon[i]!
|
||||
shape.lineTo(pt[0], -pt[1])
|
||||
}
|
||||
shape.closePath()
|
||||
|
||||
// Extrude the shape by elevation
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, {
|
||||
depth: elevation,
|
||||
bevelEnabled: false,
|
||||
})
|
||||
|
||||
// Rotate so extrusion direction (Z) becomes height direction (Y)
|
||||
geometry.rotateX(-Math.PI / 2)
|
||||
geometry.computeVertexNormals()
|
||||
|
||||
return geometry
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { type AnyNode, useScene } from '@pascal-app/core'
|
||||
import { BuildingRenderer } from './building/building-renderer'
|
||||
import { ItemRenderer } from './item/item-renderer'
|
||||
import { LevelRenderer } from './level/level-renderer'
|
||||
import { SlabRenderer } from './slab/slab-renderer'
|
||||
import { WallRenderer } from './wall/wall-renderer'
|
||||
import { ZoneRenderer } from './zone/zone-renderer'
|
||||
|
||||
@@ -17,6 +18,7 @@ export const NodeRenderer = ({ nodeId }: { nodeId: AnyNode['id'] }) => {
|
||||
{node.type === 'building' && <BuildingRenderer node={node} />}
|
||||
{node.type === 'level' && <LevelRenderer node={node} />}
|
||||
{node.type === 'item' && <ItemRenderer node={node} />}
|
||||
{node.type === 'slab' && <SlabRenderer node={node} />}
|
||||
{node.type === 'wall' && <WallRenderer node={node} />}
|
||||
{node.type === 'zone' && <ZoneRenderer node={node} />}
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useRegistry, type SlabNode } from '@pascal-app/core'
|
||||
import { useRef } from 'react'
|
||||
import type { Mesh } from 'three'
|
||||
|
||||
export const SlabRenderer = ({ node }: { node: SlabNode }) => {
|
||||
const ref = useRef<Mesh>(null!)
|
||||
|
||||
useRegistry(node.id, 'slab', ref)
|
||||
|
||||
return (
|
||||
<mesh ref={ref} castShadow receiveShadow>
|
||||
{/* SlabSystem will replace this geometry in the next frame */}
|
||||
<boxGeometry args={[0, 0, 0]} />
|
||||
<meshStandardMaterial color="#e5e5e5" />
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { WallSystem } from '@pascal-app/core'
|
||||
import { SlabSystem, WallSystem } from '@pascal-app/core'
|
||||
import { Bvh, Environment } from '@react-three/drei'
|
||||
import { Canvas, extend, type ThreeToJSXElements } from '@react-three/fiber'
|
||||
import * as THREE from 'three/webgpu'
|
||||
@@ -40,6 +40,7 @@ const Viewer: React.FC<ViewerProps> = ({ children }) => {
|
||||
|
||||
{/* Default Systems */}
|
||||
<LevelSystem />
|
||||
<SlabSystem />
|
||||
<WallSystem />
|
||||
<PostProcessing />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user