wall cutout

This commit is contained in:
wass08
2026-02-03 13:45:56 +09:00
parent a15952055a
commit b27f44d685
3 changed files with 67 additions and 1 deletions
@@ -15,7 +15,6 @@ export const WallRenderer = ({ node }: { node: WallNode }) => {
<mesh ref={ref} castShadow receiveShadow visible={node.visible}>
{/* WallSystem will replace this geometry in the next frame */}
<boxGeometry args={[0, 0, 0]} />
<meshStandardMaterial color="white" />
<mesh name="collision-mesh" {...handlers} visible={false}>
<boxGeometry args={[0, 0, 0]} />
</mesh>
@@ -7,6 +7,7 @@ import * as THREE from 'three/webgpu'
import { GuideSystem } from '../../systems/guide/guide-system'
import { LevelSystem } from '../../systems/level/level-system'
import { ScanSystem } from '../../systems/scan/scan-system'
import { WallCutout } from '../../systems/wall/wall-cutout'
import { SceneRenderer } from '../renderers/scene-renderer'
import { Lights } from './lights'
import PostProcessing from './post-processing'
@@ -55,6 +56,7 @@ const Viewer: React.FC<ViewerProps> = ({ children, selectionManager = 'default'
<LevelSystem />
<GuideSystem />
<ScanSystem />
<WallCutout />
{/* Core systems */}
<CeilingSystem />
<ItemSystem />
@@ -0,0 +1,65 @@
import { sceneRegistry, useScene, type WallNode } from '@pascal-app/core'
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
import { float, mix, positionLocal } from 'three/tsl'
import { type Mesh, MeshStandardNodeMaterial, Vector3 } from 'three/webgpu'
const tmpVec = new Vector3()
const u = new Vector3()
const v = new Vector3()
const invsibleWallMaterial = new MeshStandardNodeMaterial({
// opacity: 0.1,
transparent: true,
opacityNode: mix(float(1), float(0.1), positionLocal.y.add(0.1)),
})
const wallMaterial = new MeshStandardNodeMaterial({
color: 'white',
})
export const WallCutout = () => {
const lastCameraPosition = useRef(new Vector3())
const lastCameraTarget = useRef(new Vector3())
useFrame(({ camera }) => {
const currentCameraPosition = camera.position
camera.getWorldDirection(tmpVec)
tmpVec.add(currentCameraPosition)
if (
!currentCameraPosition.equals(lastCameraPosition.current) ||
!tmpVec.equals(lastCameraTarget.current)
) {
// Camera has moved, update cutout logic here
// Update last known positions
lastCameraPosition.current.copy(currentCameraPosition)
lastCameraTarget.current.copy(tmpVec)
camera.getWorldDirection(u)
// TODO: Debounce
const walls = sceneRegistry.byType.wall
walls.forEach((wallId) => {
const wallMesh = sceneRegistry.nodes.get(wallId)
if (!wallMesh) return
const wallNode = useScene.getState().nodes[wallId as WallNode['id']]
if (!wallNode || wallNode.type !== 'wall') return
wallMesh.getWorldDirection(v)
let hideWall = wallNode.frontSide === 'interior' && wallNode.backSide === 'interior'
if (v.dot(u) < 0) {
// Front side
if (wallNode.frontSide === 'exterior') {
hideWall = true
}
} else {
// Back side
if (wallNode.backSide === 'exterior') {
hideWall = true
}
}
;(wallMesh as Mesh).material = hideWall ? invsibleWallMaterial : wallMaterial
})
}
})
return null
}