draft custom door
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { useRegistry, type DoorNode } from '@pascal-app/core'
|
||||
import { useRef } from 'react'
|
||||
import type { Mesh } from 'three'
|
||||
import { useNodeEvents } from '../../../hooks/use-node-events'
|
||||
|
||||
export const DoorRenderer = ({ node }: { node: DoorNode }) => {
|
||||
const ref = useRef<Mesh>(null!)
|
||||
|
||||
useRegistry(node.id, 'door', ref)
|
||||
const handlers = useNodeEvents(node, 'door')
|
||||
|
||||
return (
|
||||
<mesh
|
||||
ref={ref}
|
||||
castShadow
|
||||
receiveShadow
|
||||
visible={node.visible}
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
{...handlers}
|
||||
>
|
||||
{/* DoorSystem replaces this geometry each time the node is dirty */}
|
||||
<boxGeometry args={[0, 0, 0]} />
|
||||
<meshStandardMaterial color="#d1d5db" />
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { type AnyNode, useScene } from '@pascal-app/core'
|
||||
import { BuildingRenderer } from './building/building-renderer'
|
||||
import { CeilingRenderer } from './ceiling/ceiling-renderer'
|
||||
import { DoorRenderer } from './door/door-renderer'
|
||||
import { GuideRenderer } from './guide/guide-renderer'
|
||||
import { ItemRenderer } from './item/item-renderer'
|
||||
import { LevelRenderer } from './level/level-renderer'
|
||||
@@ -28,6 +29,7 @@ export const NodeRenderer = ({ nodeId }: { nodeId: AnyNode['id'] }) => {
|
||||
{node.type === 'item' && <ItemRenderer node={node} />}
|
||||
{node.type === 'slab' && <SlabRenderer node={node} />}
|
||||
{node.type === 'wall' && <WallRenderer node={node} />}
|
||||
{node.type === 'door' && <DoorRenderer node={node} />}
|
||||
{node.type === 'window' && <WindowRenderer node={node} />}
|
||||
{node.type === 'zone' && <ZoneRenderer node={node} />}
|
||||
{node.type === 'roof' && <RoofRenderer node={node} />}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { CeilingSystem, ItemSystem, RoofSystem, SlabSystem, WallSystem, WindowSystem } from '@pascal-app/core'
|
||||
import { CeilingSystem, DoorSystem, ItemSystem, RoofSystem, SlabSystem, WallSystem, WindowSystem } from '@pascal-app/core'
|
||||
import { Bvh } from '@react-three/drei'
|
||||
import { Canvas, extend, type ThreeToJSXElements } from '@react-three/fiber'
|
||||
import * as THREE from 'three/webgpu'
|
||||
@@ -61,6 +61,7 @@ const Viewer: React.FC<ViewerProps> = ({ children, selectionManager = 'default'
|
||||
<WallCutout />
|
||||
{/* Core systems */}
|
||||
<CeilingSystem />
|
||||
<DoorSystem />
|
||||
<ItemSystem />
|
||||
<RoofSystem />
|
||||
<SlabSystem />
|
||||
|
||||
@@ -29,6 +29,7 @@ type SelectableNodeType =
|
||||
| 'zone'
|
||||
| 'wall'
|
||||
| 'window'
|
||||
| 'door'
|
||||
| 'item'
|
||||
| 'slab'
|
||||
| 'ceiling'
|
||||
@@ -86,8 +87,8 @@ const isNodeOnLevel = (node: AnyNode, levelId: string): boolean => {
|
||||
// Direct child of level
|
||||
if (node.parentId === levelId) return true
|
||||
|
||||
// Wall-attached items (windows/doors): check if parent wall is on the level
|
||||
if (node.type === 'item' && node.parentId) {
|
||||
// Wall-attached nodes (window/door/item): check if parent wall is on the level
|
||||
if ((node.type === 'item' || node.type === 'window' || node.type === 'door') && node.parentId) {
|
||||
const parentNode = nodes[node.parentId as keyof typeof nodes]
|
||||
if (parentNode?.type === 'wall' && parentNode.parentId === levelId) {
|
||||
return true
|
||||
@@ -200,9 +201,9 @@ const getStrategy = (): SelectionStrategy | null => {
|
||||
}
|
||||
}
|
||||
|
||||
// Zone selected -> can select/hover contents (walls, items, slabs, ceilings, roofs, windows)
|
||||
// Zone selected -> can select/hover contents (walls, items, slabs, ceilings, roofs, windows, doors)
|
||||
return {
|
||||
types: ['wall', 'item', 'slab', 'ceiling', 'roof', 'window'],
|
||||
types: ['wall', 'item', 'slab', 'ceiling', 'roof', 'window', 'door'],
|
||||
handleClick: (node) => {
|
||||
const { selectedIds } = useViewer.getState().selection
|
||||
// Toggle selection - if already selected, deselect; otherwise select
|
||||
@@ -224,7 +225,7 @@ const getStrategy = (): SelectionStrategy | null => {
|
||||
}
|
||||
},
|
||||
isValid: (node) => {
|
||||
const validTypes = ['wall', 'item', 'slab', 'ceiling', 'roof', 'window']
|
||||
const validTypes = ['wall', 'item', 'slab', 'ceiling', 'roof', 'window', 'door']
|
||||
if (!validTypes.includes(node.type)) return false
|
||||
return isNodeInZone(node, levelId, zoneId)
|
||||
},
|
||||
@@ -277,6 +278,7 @@ export const SelectionManager = () => {
|
||||
'ceiling',
|
||||
'roof',
|
||||
'window',
|
||||
'door',
|
||||
]
|
||||
for (const type of allTypes) {
|
||||
emitter.on(`${type}:enter`, onEnter)
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
type BuildingNode,
|
||||
type CeilingEvent,
|
||||
type CeilingNode,
|
||||
type DoorEvent,
|
||||
type DoorNode,
|
||||
type EventSuffix,
|
||||
emitter,
|
||||
type ItemEvent,
|
||||
@@ -36,6 +38,7 @@ type NodeConfig = {
|
||||
ceiling: { node: CeilingNode; event: CeilingEvent }
|
||||
roof: { node: RoofNode; event: RoofEvent }
|
||||
window: { node: WindowNode; event: WindowEvent }
|
||||
door: { node: DoorNode; event: DoorEvent }
|
||||
}
|
||||
|
||||
type NodeType = keyof NodeConfig
|
||||
|
||||
Reference in New Issue
Block a user