fix tools visibility

This commit is contained in:
wass08
2026-02-24 16:09:03 +09:00
parent 81ede241d5
commit 14708c041a
7 changed files with 170 additions and 170 deletions
@@ -2,8 +2,9 @@ import { CeilingNode, emitter, type GridEvent, type LevelNode, useScene } from '
import { useViewer } from '@pascal-app/viewer' import { useViewer } from '@pascal-app/viewer'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from 'three' import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from 'three'
import useEditor from '@/store/use-editor'
import { sfxEmitter } from '@/lib/sfx-bus' import { sfxEmitter } from '@/lib/sfx-bus'
import useEditor from '@/store/use-editor'
import { CursorSphere } from '../shared/cursor-sphere'
const CEILING_HEIGHT = 2.52 const CEILING_HEIGHT = 2.52
const GRID_OFFSET = 0.02 const GRID_OFFSET = 0.02
@@ -97,12 +98,19 @@ export const CeilingTool: React.FC = () => {
// Calculate snapped display position (bypass snap when Shift is held) // Calculate snapped display position (bypass snap when Shift is held)
const lastPoint = points[points.length - 1] const lastPoint = points[points.length - 1]
const displayPoint = (shiftPressed.current || !lastPoint) ? gridPosition : calculateSnapPoint(lastPoint, gridPosition) const displayPoint =
shiftPressed.current || !lastPoint
? gridPosition
: calculateSnapPoint(lastPoint, gridPosition)
setSnappedCursorPosition(displayPoint) setSnappedCursorPosition(displayPoint)
// Play snap sound when the snapped position actually changes (only when drawing) // Play snap sound when the snapped position actually changes (only when drawing)
if (points.length > 0 && previousSnappedPointRef.current && if (
(displayPoint[0] !== previousSnappedPointRef.current[0] || displayPoint[1] !== previousSnappedPointRef.current[1])) { points.length > 0 &&
previousSnappedPointRef.current &&
(displayPoint[0] !== previousSnappedPointRef.current[0] ||
displayPoint[1] !== previousSnappedPointRef.current[1])
) {
sfxEmitter.emit('sfx:grid-snap') sfxEmitter.emit('sfx:grid-snap')
} }
@@ -150,8 +158,12 @@ export const CeilingTool: React.FC = () => {
setPoints([]) setPoints([])
} }
const onKeyDown = (e: KeyboardEvent) => { if (e.key === 'Shift') shiftPressed.current = true } const onKeyDown = (e: KeyboardEvent) => {
const onKeyUp = (e: KeyboardEvent) => { if (e.key === 'Shift') shiftPressed.current = false } if (e.key === 'Shift') shiftPressed.current = true
}
const onKeyUp = (e: KeyboardEvent) => {
if (e.key === 'Shift') shiftPressed.current = false
}
document.addEventListener('keydown', onKeyDown) document.addEventListener('keydown', onKeyDown)
document.addEventListener('keyup', onKeyUp) document.addEventListener('keyup', onKeyUp)
@@ -242,15 +254,12 @@ export const CeilingTool: React.FC = () => {
return ( return (
<group> <group>
{/* Cursor at ceiling height */} {/* Cursor at ceiling height */}
<mesh ref={cursorRef}> <CursorSphere ref={cursorRef} />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial color="#d4d4d4" depthTest={false} depthWrite={false} />
</mesh>
{/* Grid-level cursor indicator */} {/* Grid-level cursor indicator */}
<mesh ref={gridCursorRef} rotation={[-Math.PI / 2, 0, 0]}> <mesh ref={gridCursorRef} rotation={[-Math.PI / 2, 0, 0]} renderOrder={2}>
<ringGeometry args={[0.15, 0.2, 32]} /> <ringGeometry args={[0.15, 0.2, 32]} />
<meshBasicMaterial color="#a3a3a3" side={DoubleSide} depthTest={false} depthWrite={false} /> <meshBasicMaterial color="#a3a3a3" side={DoubleSide} depthTest={false} depthWrite={true} />
</mesh> </mesh>
{/* Preview fill */} {/* Preview fill */}
@@ -294,14 +303,11 @@ export const CeilingTool: React.FC = () => {
{/* Point markers */} {/* Point markers */}
{points.map(([x, z], index) => ( {points.map(([x, z], index) => (
<mesh key={index} position={[x, levelY + CEILING_HEIGHT + 0.01, z]}> <CursorSphere
<sphereGeometry args={[0.1, 16, 16]} /> key={index}
<meshBasicMaterial position={[x, levelY + CEILING_HEIGHT + 0.01, z]}
color={index === 0 ? '#22c55e' : '#d4d4d4'} color={index === 0 ? '#22c55e' : undefined}
depthTest={false} />
depthWrite={false}
/>
</mesh>
))} ))}
</group> </group>
) )
+114 -114
View File
@@ -1,37 +1,44 @@
import { emitter, type GridEvent, useScene, RoofNode, type LevelNode, type AnyNode } from "@pascal-app/core"; import {
import { useViewer } from "@pascal-app/viewer"; type AnyNode,
import { useEffect, useMemo, useRef, useState } from "react"; emitter,
import { BufferGeometry, DoubleSide, type Line, Vector3 } from "three"; type GridEvent,
import useEditor from "@/store/use-editor"; type LevelNode,
import { sfxEmitter } from '@/lib/sfx-bus'; RoofNode,
useScene,
} from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { useEffect, useMemo, useRef, useState } from 'react'
import { BufferGeometry, DoubleSide, type Line, Vector3 } from 'three'
import { sfxEmitter } from '@/lib/sfx-bus'
import useEditor from '@/store/use-editor'
// Default roof dimensions // Default roof dimensions
const DEFAULT_HEIGHT = 1.5; const DEFAULT_HEIGHT = 1.5
const PREVIEW_LINE_HEIGHT = 0.03; // Very thin preview const PREVIEW_LINE_HEIGHT = 0.03 // Very thin preview
/** /**
* Creates a roof with the given corners * Creates a roof with the given corners
*/ */
const commitRoofPlacement = ( const commitRoofPlacement = (
levelId: LevelNode["id"], levelId: LevelNode['id'],
corner1: [number, number, number], corner1: [number, number, number],
corner2: [number, number, number] corner2: [number, number, number],
): RoofNode["id"] => { ): RoofNode['id'] => {
const { createNode, nodes } = useScene.getState(); const { createNode, nodes } = useScene.getState()
// Calculate center position and dimensions from corners // Calculate center position and dimensions from corners
const centerX = (corner1[0] + corner2[0]) / 2; const centerX = (corner1[0] + corner2[0]) / 2
const centerZ = (corner1[2] + corner2[2]) / 2; const centerZ = (corner1[2] + corner2[2]) / 2
const length = Math.abs(corner2[0] - corner1[0]); const length = Math.abs(corner2[0] - corner1[0])
const width = Math.abs(corner2[2] - corner1[2]); const width = Math.abs(corner2[2] - corner1[2])
// Split width evenly between left and right slopes // Split width evenly between left and right slopes
const slopeWidth = Math.max(width / 2, 0.5); const slopeWidth = Math.max(width / 2, 0.5)
// Count existing roofs for naming // Count existing roofs for naming
const roofCount = Object.values(nodes).filter((n) => n.type === "roof").length; const roofCount = Object.values(nodes).filter((n) => n.type === 'roof').length
const name = `Roof ${roofCount + 1}`; const name = `Roof ${roofCount + 1}`
const roof = RoofNode.parse({ const roof = RoofNode.parse({
name, name,
@@ -40,151 +47,153 @@ const commitRoofPlacement = (
height: DEFAULT_HEIGHT, height: DEFAULT_HEIGHT,
leftWidth: slopeWidth, leftWidth: slopeWidth,
rightWidth: slopeWidth, rightWidth: slopeWidth,
}); })
createNode(roof, levelId); createNode(roof, levelId)
sfxEmitter.emit('sfx:structure-build'); sfxEmitter.emit('sfx:structure-build')
return roof.id; return roof.id
}; }
type PreviewState = { type PreviewState = {
corner1: [number, number, number] | null; corner1: [number, number, number] | null
cursorPosition: [number, number, number]; cursorPosition: [number, number, number]
levelY: number; levelY: number
}; }
export const RoofTool: React.FC = () => { export const RoofTool: React.FC = () => {
const outlineRef = useRef<Line>(null!); const outlineRef = useRef<Line>(null!)
const currentLevelId = useViewer((state) => state.selection.levelId); const currentLevelId = useViewer((state) => state.selection.levelId)
const setSelection = useViewer((state) => state.setSelection); const setSelection = useViewer((state) => state.setSelection)
const setTool = useEditor((state) => state.setTool); const setTool = useEditor((state) => state.setTool)
const setMode = useEditor((state) => state.setMode); const setMode = useEditor((state) => state.setMode)
const corner1Ref = useRef<[number, number, number] | null>(null); const corner1Ref = useRef<[number, number, number] | null>(null)
const previousGridPosRef = useRef<[number, number] | null>(null); const previousGridPosRef = useRef<[number, number] | null>(null)
const [preview, setPreview] = useState<PreviewState>({ const [preview, setPreview] = useState<PreviewState>({
corner1: null, corner1: null,
cursorPosition: [0, 0, 0], cursorPosition: [0, 0, 0],
levelY: 0, levelY: 0,
}); })
useEffect(() => { useEffect(() => {
if (!currentLevelId) return; if (!currentLevelId) return
// Initialize outline geometry // Initialize outline geometry
outlineRef.current.geometry = new BufferGeometry(); outlineRef.current.geometry = new BufferGeometry()
const updateOutline = (corner1: [number, number, number], corner2: [number, number, number]) => { const updateOutline = (
const y = corner1[1] + PREVIEW_LINE_HEIGHT; corner1: [number, number, number],
corner2: [number, number, number],
) => {
const y = corner1[1] + PREVIEW_LINE_HEIGHT
const points = [ const points = [
new Vector3(corner1[0], y, corner1[2]), new Vector3(corner1[0], y, corner1[2]),
new Vector3(corner2[0], y, corner1[2]), new Vector3(corner2[0], y, corner1[2]),
new Vector3(corner2[0], y, corner2[2]), new Vector3(corner2[0], y, corner2[2]),
new Vector3(corner1[0], y, corner2[2]), new Vector3(corner1[0], y, corner2[2]),
new Vector3(corner1[0], y, corner1[2]), // Close the loop new Vector3(corner1[0], y, corner1[2]), // Close the loop
]; ]
outlineRef.current.geometry.dispose(); outlineRef.current.geometry.dispose()
outlineRef.current.geometry = new BufferGeometry().setFromPoints(points); outlineRef.current.geometry = new BufferGeometry().setFromPoints(points)
outlineRef.current.visible = true; outlineRef.current.visible = true
}; }
const onGridMove = (event: GridEvent) => { const onGridMove = (event: GridEvent) => {
// Snap to 0.5 grid // Snap to 0.5 grid
const gridX = Math.round(event.position[0] * 2) / 2; const gridX = Math.round(event.position[0] * 2) / 2
const gridZ = Math.round(event.position[2] * 2) / 2; const gridZ = Math.round(event.position[2] * 2) / 2
const y = event.position[1]; const y = event.position[1]
const cursorPosition: [number, number, number] = [gridX, y, gridZ]; const cursorPosition: [number, number, number] = [gridX, y, gridZ]
// Play snap sound when grid position changes (only when placing) // Play snap sound when grid position changes (only when placing)
if (corner1Ref.current && previousGridPosRef.current && if (
(gridX !== previousGridPosRef.current[0] || gridZ !== previousGridPosRef.current[1])) { corner1Ref.current &&
sfxEmitter.emit('sfx:grid-snap'); previousGridPosRef.current &&
(gridX !== previousGridPosRef.current[0] || gridZ !== previousGridPosRef.current[1])
) {
sfxEmitter.emit('sfx:grid-snap')
} }
previousGridPosRef.current = [gridX, gridZ]; previousGridPosRef.current = [gridX, gridZ]
setPreview({ setPreview({
corner1: corner1Ref.current, corner1: corner1Ref.current,
cursorPosition, cursorPosition,
levelY: y, levelY: y,
}); })
// Update outline if we have first corner // Update outline if we have first corner
if (corner1Ref.current) { if (corner1Ref.current) {
updateOutline(corner1Ref.current, cursorPosition); updateOutline(corner1Ref.current, cursorPosition)
} }
}; }
const onGridClick = (event: GridEvent) => { const onGridClick = (event: GridEvent) => {
if (!currentLevelId) return; if (!currentLevelId) return
const gridX = Math.round(event.position[0] * 2) / 2; const gridX = Math.round(event.position[0] * 2) / 2
const gridZ = Math.round(event.position[2] * 2) / 2; const gridZ = Math.round(event.position[2] * 2) / 2
const y = event.position[1]; const y = event.position[1]
if (!corner1Ref.current) { if (!corner1Ref.current) {
// First click - set corner 1 // First click - set corner 1
corner1Ref.current = [gridX, y, gridZ]; corner1Ref.current = [gridX, y, gridZ]
setPreview((prev) => ({ setPreview((prev) => ({
...prev, ...prev,
corner1: corner1Ref.current, corner1: corner1Ref.current,
})); }))
} else { } else {
// Second click - create the roof // Second click - create the roof
const roofId = commitRoofPlacement( const roofId = commitRoofPlacement(currentLevelId, corner1Ref.current, [gridX, y, gridZ])
currentLevelId,
corner1Ref.current,
[gridX, y, gridZ]
);
// Auto-select the newly created roof // Auto-select the newly created roof
setSelection({ selectedIds: [roofId as AnyNode["id"]] }); setSelection({ selectedIds: [roofId as AnyNode['id']] })
// Reset state // Reset state
corner1Ref.current = null; corner1Ref.current = null
outlineRef.current.visible = false; outlineRef.current.visible = false
// Switch to select mode and deactivate tool // Switch to select mode and deactivate tool
setMode('select'); setMode('select')
setTool(null); setTool(null)
} }
}; }
const onCancel = () => { const onCancel = () => {
if (corner1Ref.current) { if (corner1Ref.current) {
corner1Ref.current = null; corner1Ref.current = null
outlineRef.current.visible = false; outlineRef.current.visible = false
setPreview((prev) => ({ ...prev, corner1: null })); setPreview((prev) => ({ ...prev, corner1: null }))
} }
}; }
// Subscribe to events // Subscribe to events
emitter.on("grid:move", onGridMove); emitter.on('grid:move', onGridMove)
emitter.on("grid:click", onGridClick); emitter.on('grid:click', onGridClick)
emitter.on("tool:cancel", onCancel); emitter.on('tool:cancel', onCancel)
return () => { return () => {
emitter.off("grid:move", onGridMove); emitter.off('grid:move', onGridMove)
emitter.off("grid:click", onGridClick); emitter.off('grid:click', onGridClick)
emitter.off("tool:cancel", onCancel); emitter.off('tool:cancel', onCancel)
// Reset state on unmount // Reset state on unmount
corner1Ref.current = null; corner1Ref.current = null
}; }
}, [currentLevelId, setTool, setSelection, setMode]); }, [currentLevelId, setTool, setSelection, setMode])
const { corner1, cursorPosition, levelY } = preview; const { corner1, cursorPosition, levelY } = preview
// Calculate preview dimensions for display // Calculate preview dimensions for display
const previewDimensions = useMemo(() => { const previewDimensions = useMemo(() => {
if (!corner1) return null; if (!corner1) return null
const length = Math.abs(cursorPosition[0] - corner1[0]); const length = Math.abs(cursorPosition[0] - corner1[0])
const width = Math.abs(cursorPosition[2] - corner1[2]); const width = Math.abs(cursorPosition[2] - corner1[2])
const centerX = (corner1[0] + cursorPosition[0]) / 2; const centerX = (corner1[0] + cursorPosition[0]) / 2
const centerZ = (corner1[2] + cursorPosition[2]) / 2; const centerZ = (corner1[2] + cursorPosition[2]) / 2
return { length, width, centerX, centerZ }; return { length, width, centerX, centerZ }
}, [corner1, cursorPosition]); }, [corner1, cursorPosition])
return ( return (
<group> <group>
@@ -192,34 +201,25 @@ export const RoofTool: React.FC = () => {
{/* @ts-ignore */} {/* @ts-ignore */}
<line ref={outlineRef} frustumCulled={false} renderOrder={1} visible={false}> <line ref={outlineRef} frustumCulled={false} renderOrder={1} visible={false}>
<bufferGeometry /> <bufferGeometry />
<lineBasicNodeMaterial <lineBasicNodeMaterial color="#8b4513" linewidth={2} depthTest={false} depthWrite={false} />
color="#8b4513"
linewidth={2}
depthTest={false}
depthWrite={false}
/>
</line> </line>
{/* First corner marker */} {/* First corner marker */}
{corner1 && ( {corner1 && (
<mesh position={[corner1[0], levelY + 0.02, corner1[2]]} rotation={[-Math.PI / 2, 0, 0]}> <mesh position={[corner1[0], levelY + 0.02, corner1[2]]} rotation={[-Math.PI / 2, 0, 0]} renderOrder={2}>
<ringGeometry args={[0.1, 0.15, 32]} /> <ringGeometry args={[0.1, 0.15, 32]} />
<meshBasicMaterial <meshBasicMaterial color="#22c55e" depthTest={false} depthWrite={true} />
color="#22c55e"
depthTest={false}
depthWrite={false}
/>
</mesh> </mesh>
)} )}
{/* Cursor marker on ground */} {/* Cursor marker on ground */}
<mesh position={[cursorPosition[0], cursorPosition[1] + 0.02, cursorPosition[2]]} rotation={[-Math.PI / 2, 0, 0]}> <mesh
position={[cursorPosition[0], cursorPosition[1] + 0.02, cursorPosition[2]]}
rotation={[-Math.PI / 2, 0, 0]}
renderOrder={2}
>
<ringGeometry args={[0.1, 0.15, 32]} /> <ringGeometry args={[0.1, 0.15, 32]} />
<meshBasicMaterial <meshBasicMaterial color="#8b4513" depthTest={false} depthWrite={true} />
color="#8b4513"
depthTest={false}
depthWrite={false}
/>
</mesh> </mesh>
{/* Thin preview fill when drawing */} {/* Thin preview fill when drawing */}
@@ -240,5 +240,5 @@ export const RoofTool: React.FC = () => {
</mesh> </mesh>
)} )}
</group> </group>
); )
}; }
@@ -0,0 +1,19 @@
import { forwardRef } from 'react'
import type { Mesh } from 'three'
interface CursorSphereProps extends Omit<JSX.IntrinsicElements['mesh'], 'ref'> {
color?: string
depthWrite?: boolean
}
export const CursorSphere = forwardRef<Mesh, CursorSphereProps>(function CursorSphere(
{ color = '#f1c066', ...props },
ref,
) {
return (
<mesh ref={ref} {...props} renderOrder={2}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial color={color} depthTest={false} depthWrite={true} />
</mesh>
)
})
@@ -3,6 +3,7 @@ import { useViewer } from '@pascal-app/viewer'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from 'three' import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from 'three'
import { sfxEmitter } from '@/lib/sfx-bus' import { sfxEmitter } from '@/lib/sfx-bus'
import { CursorSphere } from '../shared/cursor-sphere'
const Y_OFFSET = 0.02 const Y_OFFSET = 0.02
@@ -236,10 +237,7 @@ export const SlabTool: React.FC = () => {
return ( return (
<group> <group>
{/* Cursor */} {/* Cursor */}
<mesh ref={cursorRef}> <CursorSphere ref={cursorRef} />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial color="#a3a3a3" depthTest={false} depthWrite={false} />
</mesh>
{/* Preview fill */} {/* Preview fill */}
{previewShape && ( {previewShape && (
@@ -282,14 +280,7 @@ export const SlabTool: React.FC = () => {
{/* Point markers */} {/* Point markers */}
{points.map(([x, z], index) => ( {points.map(([x, z], index) => (
<mesh key={index} position={[x, levelY + Y_OFFSET + 0.01, z]}> <CursorSphere key={index} position={[x, levelY + Y_OFFSET + 0.01, z]} color={index === 0 ? '#22c55e' : undefined} />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial
color={index === 0 ? '#22c55e' : '#a3a3a3'}
depthTest={false}
depthWrite={false}
/>
</mesh>
))} ))}
</group> </group>
) )
@@ -3,6 +3,7 @@ import { useViewer } from '@pascal-app/viewer'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { DoubleSide, type Mesh, Shape, ShapeGeometry, Vector3 } from 'three' import { DoubleSide, type Mesh, Shape, ShapeGeometry, Vector3 } from 'three'
import { sfxEmitter } from '@/lib/sfx-bus' import { sfxEmitter } from '@/lib/sfx-bus'
import { CursorSphere } from '../shared/cursor-sphere'
const WALL_HEIGHT = 2.5 const WALL_HEIGHT = 2.5
const WALL_THICKNESS = 0.15 const WALL_THICKNESS = 0.15
@@ -183,10 +184,7 @@ export const WallTool: React.FC = () => {
return ( return (
<group> <group>
{/* Cursor indicator */} {/* Cursor indicator */}
<mesh ref={cursorRef}> <CursorSphere ref={cursorRef} />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial color="#a3a3a3" depthTest={false} depthWrite={false} />
</mesh>
{/* Wall preview */} {/* Wall preview */}
<mesh ref={wallPreviewRef} visible={false} renderOrder={1}> <mesh ref={wallPreviewRef} visible={false} renderOrder={1}>
@@ -3,6 +3,7 @@ import { useViewer } from "@pascal-app/viewer";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three"; import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three";
import useEditor from "@/store/use-editor"; import useEditor from "@/store/use-editor";
import { CursorSphere } from "../shared/cursor-sphere";
// Zone colors for cycling through // Zone colors for cycling through
const ZONE_COLORS = [ const ZONE_COLORS = [
@@ -317,14 +318,7 @@ export const ZoneTool: React.FC = () => {
return ( return (
<group> <group>
{/* Cursor */} {/* Cursor */}
<mesh ref={cursorRef}> <CursorSphere ref={cursorRef} color="#3b82f6" />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial
color="#3b82f6"
depthTest={false}
depthWrite={false}
/>
</mesh>
{/* Preview fill */} {/* Preview fill */}
{previewShape && ( {previewShape && (
@@ -373,14 +367,7 @@ export const ZoneTool: React.FC = () => {
{/* Point markers */} {/* Point markers */}
{points.map(([x, z], index) => {points.map(([x, z], index) =>
isValidPoint([x, z]) ? ( isValidPoint([x, z]) ? (
<mesh key={index} position={[x, levelY + Y_OFFSET + 0.01, z]}> <CursorSphere key={index} position={[x, levelY + Y_OFFSET + 0.01, z]} color={index === 0 ? "#22c55e" : "#3b82f6"} />
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial
color={index === 0 ? "#22c55e" : "#3b82f6"}
depthTest={false}
depthWrite={false}
/>
</mesh>
) : null ) : null
)} )}
</group> </group>
@@ -1,7 +1,7 @@
import { type CeilingNode, useRegistry } from '@pascal-app/core' import { type CeilingNode, useRegistry } from '@pascal-app/core'
import { useRef } from 'react' import { useRef } from 'react'
import { faceDirection, float, mix, positionWorld, smoothstep, step } from 'three/tsl' import { faceDirection, float, mix, positionWorld, smoothstep, step } from 'three/tsl'
import { DoubleSide, type Mesh, MeshBasicNodeMaterial } from 'three/webgpu' import { type Mesh, MeshBasicNodeMaterial } from 'three/webgpu'
import { useNodeEvents } from '../../../hooks/use-node-events' import { useNodeEvents } from '../../../hooks/use-node-events'
import { NodeRenderer } from '../node-renderer' import { NodeRenderer } from '../node-renderer'
@@ -10,7 +10,6 @@ import { NodeRenderer } from '../node-renderer'
// - Front face (looking down at ceiling from above): 30% opacity // - Front face (looking down at ceiling from above): 30% opacity
const ceilingMaterial = new MeshBasicNodeMaterial({ const ceilingMaterial = new MeshBasicNodeMaterial({
color: 0x999999, color: 0x999999,
side: DoubleSide,
transparent: true, transparent: true,
depthWrite: false, depthWrite: false,
}) })
@@ -30,8 +29,8 @@ const lineY = smoothstep(lineWidth, 0, gridY).add(smoothstep(1.0 - lineWidth, 1.
// Combine: if either X or Y is a line, show the line // Combine: if either X or Y is a line, show the line
const gridPattern = lineX.max(lineY) const gridPattern = lineX.max(lineY)
// Grid lines at 0.8 opacity, spaces at 0.1 opacity // Grid lines at 0.5 opacity, spaces at 0 opacity
const gridOpacity = mix(float(0.1), float(0.8), gridPattern) const gridOpacity = mix(float(0.0), float(0.5), gridPattern)
// faceDirection is 1.0 for front face, -1.0 for back face // faceDirection is 1.0 for front face, -1.0 for back face
// Front face (top, looking down): grid pattern, Back face (bottom, looking up): solid // Front face (top, looking down): grid pattern, Back face (bottom, looking up): solid