fix: respect scene depth for site and ceiling handles
This commit is contained in:
+5
-11
@@ -18,9 +18,9 @@ const BRACKET_THICKNESS = 0.04
|
|||||||
const BRACKET_HEIGHT = 0.04
|
const BRACKET_HEIGHT = 0.04
|
||||||
const BRACKET_Y_OFFSET = 0.035
|
const BRACKET_Y_OFFSET = 0.035
|
||||||
const HIT_BOX_SIZE: [number, number, number] = [0.28, 0.08, 0.28]
|
const HIT_BOX_SIZE: [number, number, number] = [0.28, 0.08, 0.28]
|
||||||
// Draw the corner handles after everything else and with depth testing
|
// Draw the corner handles after the ceiling surface so they read cleanly
|
||||||
// off (see materials below) so they stay visible — and clickable — even
|
// when unobstructed, while material depth testing still lets other scene
|
||||||
// when a wall, roof, or the ceiling itself would otherwise occlude them.
|
// geometry hide them.
|
||||||
const CORNER_RENDER_ORDER = 1000
|
const CORNER_RENDER_ORDER = 1000
|
||||||
|
|
||||||
type CornerBracketData = {
|
type CornerBracketData = {
|
||||||
@@ -189,7 +189,7 @@ const CornerBracket = ({
|
|||||||
<boxGeometry args={HIT_BOX_SIZE} />
|
<boxGeometry args={HIT_BOX_SIZE} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial
|
||||||
color={cubeColor}
|
color={cubeColor}
|
||||||
depthTest={false}
|
depthTest
|
||||||
depthWrite={false}
|
depthWrite={false}
|
||||||
opacity={cubeOpacity}
|
opacity={cubeOpacity}
|
||||||
transparent
|
transparent
|
||||||
@@ -227,13 +227,7 @@ const BracketLeg = ({
|
|||||||
rotation={[0, angle, 0]}
|
rotation={[0, angle, 0]}
|
||||||
>
|
>
|
||||||
<boxGeometry args={[length, BRACKET_HEIGHT, BRACKET_THICKNESS]} />
|
<boxGeometry args={[length, BRACKET_HEIGHT, BRACKET_THICKNESS]} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial color={color} depthTest depthWrite={false} opacity={opacity} transparent />
|
||||||
color={color}
|
|
||||||
depthTest={false}
|
|
||||||
depthWrite={false}
|
|
||||||
opacity={opacity}
|
|
||||||
transparent
|
|
||||||
/>
|
|
||||||
</mesh>
|
</mesh>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
BufferGeometry,
|
BufferGeometry,
|
||||||
Color,
|
Color,
|
||||||
CylinderGeometry,
|
CylinderGeometry,
|
||||||
|
DoubleSide,
|
||||||
ExtrudeGeometry,
|
ExtrudeGeometry,
|
||||||
Float32BufferAttribute,
|
Float32BufferAttribute,
|
||||||
type Line,
|
type Line,
|
||||||
@@ -20,7 +21,6 @@ import {
|
|||||||
ARROW_COLOR as EDGE_ARROW_COLOR,
|
ARROW_COLOR as EDGE_ARROW_COLOR,
|
||||||
ARROW_HOVER_COLOR as EDGE_ARROW_HOVER_COLOR,
|
ARROW_HOVER_COLOR as EDGE_ARROW_HOVER_COLOR,
|
||||||
ARROW_SCALE as EDGE_ARROW_SCALE,
|
ARROW_SCALE as EDGE_ARROW_SCALE,
|
||||||
useArrowMaterial,
|
|
||||||
useInvisibleHitAreaMaterial,
|
useInvisibleHitAreaMaterial,
|
||||||
} from '../../editor/node-arrow-handles'
|
} from '../../editor/node-arrow-handles'
|
||||||
import { snapToHalf } from '../item/placement-math'
|
import { snapToHalf } from '../item/placement-math'
|
||||||
@@ -181,7 +181,7 @@ function usePolygonNodeMaterial(color: string, opacity = 1): MeshBasicNodeMateri
|
|||||||
() =>
|
() =>
|
||||||
new MeshBasicNodeMaterial({
|
new MeshBasicNodeMaterial({
|
||||||
color: new Color('#ffffff'),
|
color: new Color('#ffffff'),
|
||||||
depthTest: false,
|
depthTest: true,
|
||||||
depthWrite: true,
|
depthWrite: true,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
@@ -198,11 +198,24 @@ function usePolygonNodeMaterial(color: string, opacity = 1): MeshBasicNodeMateri
|
|||||||
return material
|
return material
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function usePolygonArrowMaterial(): MeshBasicNodeMaterial {
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
new MeshBasicNodeMaterial({
|
||||||
|
color: new Color(EDGE_ARROW_COLOR),
|
||||||
|
depthTest: true,
|
||||||
|
depthWrite: true,
|
||||||
|
opacity: 1,
|
||||||
|
side: DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// One mesh per handle: lives on SCENE_LAYER with a node material so the
|
// One mesh per handle: lives on SCENE_LAYER with a node material so the
|
||||||
// post-processing ink-edge pass outlines it, and carries the pointer handlers
|
// post-processing ink-edge pass outlines it. The visual material still
|
||||||
// directly so it stays grabbable — matching the registry arrow gizmos in
|
// depth-tests, so walls/items in front can occlude it.
|
||||||
// node-arrow-handles.tsx. No paired hit mesh is needed; the R3F event
|
|
||||||
// raycaster picks SCENE_LAYER meshes too.
|
|
||||||
function OutlinedCylinderHandle({
|
function OutlinedCylinderHandle({
|
||||||
radius,
|
radius,
|
||||||
height,
|
height,
|
||||||
@@ -290,7 +303,7 @@ function OutlinedEdgeArrowHandle({
|
|||||||
rotationY: number
|
rotationY: number
|
||||||
scale: number
|
scale: number
|
||||||
} & PolygonHandleHandlers) {
|
} & PolygonHandleHandlers) {
|
||||||
const material = useArrowMaterial()
|
const material = usePolygonArrowMaterial()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
material.color.set(color)
|
material.color.set(color)
|
||||||
}, [color, material])
|
}, [color, material])
|
||||||
@@ -741,9 +754,9 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
|
|||||||
const handleHeight = Math.max(MIN_HANDLE_HEIGHT, surfaceHeight + 0.02)
|
const handleHeight = Math.max(MIN_HANDLE_HEIGHT, surfaceHeight + 0.02)
|
||||||
const edgeHandleY = editY + handleHeight - EDGE_HANDLE_HEIGHT / 2
|
const edgeHandleY = editY + handleHeight - EDGE_HANDLE_HEIGHT / 2
|
||||||
|
|
||||||
// Interactive handles are single SCENE_LAYER node-material meshes (like the
|
// Interactive handles are SCENE_LAYER node-material meshes so the ink-edge
|
||||||
// registry arrow gizmos) so the ink-edge pass outlines them while they stay
|
// pass outlines them while normal scene depth can hide them. The edge BAR and
|
||||||
// grabbable. The edge BAR and border line stay on EDITOR_LAYER, visual-only
|
// border line stay on EDITOR_LAYER, visual-only
|
||||||
// (raycast disabled) so they never steal clicks from the vertex/midpoint
|
// (raycast disabled) so they never steal clicks from the vertex/midpoint
|
||||||
// handles overlapping them — edge dragging starts from the chevron arrow
|
// handles overlapping them — edge dragging starts from the chevron arrow
|
||||||
// outside the polygon edge.
|
// outside the polygon edge.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const SITE_FLAG_HALO_COLOR = '#6366f1'
|
|||||||
|
|
||||||
type TintableMaterial = {
|
type TintableMaterial = {
|
||||||
color?: Color
|
color?: Color
|
||||||
|
depthTest: boolean
|
||||||
depthWrite: boolean
|
depthWrite: boolean
|
||||||
opacity: number
|
opacity: number
|
||||||
needsUpdate: boolean
|
needsUpdate: boolean
|
||||||
@@ -69,10 +70,9 @@ function SiteFlagModel({
|
|||||||
mesh.frustumCulled = false
|
mesh.frustumCulled = false
|
||||||
mesh.raycast = NO_RAYCAST
|
mesh.raycast = NO_RAYCAST
|
||||||
mesh.receiveShadow = false
|
mesh.receiveShadow = false
|
||||||
mesh.renderOrder = 1010
|
|
||||||
mesh.material = new MeshBasicNodeMaterial({
|
mesh.material = new MeshBasicNodeMaterial({
|
||||||
color: new Color(ARROW_COLOR),
|
color: new Color(ARROW_COLOR),
|
||||||
depthTest: false,
|
depthTest: true,
|
||||||
depthWrite: opacity >= 0.999,
|
depthWrite: opacity >= 0.999,
|
||||||
opacity,
|
opacity,
|
||||||
transparent: opacity < 0.999,
|
transparent: opacity < 0.999,
|
||||||
@@ -93,6 +93,7 @@ function SiteFlagModel({
|
|||||||
|
|
||||||
for (const material of materials as Array<MeshBasicNodeMaterial & TintableMaterial>) {
|
for (const material of materials as Array<MeshBasicNodeMaterial & TintableMaterial>) {
|
||||||
material.color?.copy(color)
|
material.color?.copy(color)
|
||||||
|
material.depthTest = true
|
||||||
material.opacity = opacity
|
material.opacity = opacity
|
||||||
material.transparent = opacity < 0.999
|
material.transparent = opacity < 0.999
|
||||||
material.depthWrite = opacity >= 0.999
|
material.depthWrite = opacity >= 0.999
|
||||||
@@ -217,11 +218,23 @@ function SiteFlagFallback({
|
|||||||
<group position={[0, SITE_FLAG_BASE_Y + (active ? SITE_FLAG_ACTIVE_LIFT : 0), 0]}>
|
<group position={[0, SITE_FLAG_BASE_Y + (active ? SITE_FLAG_ACTIVE_LIFT : 0), 0]}>
|
||||||
<mesh layers={SCENE_LAYER} position={[0, 0.16, 0]} raycast={NO_RAYCAST}>
|
<mesh layers={SCENE_LAYER} position={[0, 0.16, 0]} raycast={NO_RAYCAST}>
|
||||||
<cylinderGeometry args={[0.11, 0.16, 0.32, 24]} />
|
<cylinderGeometry args={[0.11, 0.16, 0.32, 24]} />
|
||||||
<meshBasicMaterial color={color} depthTest={false} opacity={opacity} transparent />
|
<meshBasicMaterial
|
||||||
|
color={color}
|
||||||
|
depthTest
|
||||||
|
depthWrite={opacity >= 0.999}
|
||||||
|
opacity={opacity}
|
||||||
|
transparent={opacity < 0.999}
|
||||||
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
<mesh layers={SCENE_LAYER} position={[0, 0.34, 0]} raycast={NO_RAYCAST}>
|
<mesh layers={SCENE_LAYER} position={[0, 0.34, 0]} raycast={NO_RAYCAST}>
|
||||||
<cylinderGeometry args={[0.04, 0.11, 0.14, 24]} />
|
<cylinderGeometry args={[0.04, 0.11, 0.14, 24]} />
|
||||||
<meshBasicMaterial color={color} depthTest={false} opacity={opacity} transparent />
|
<meshBasicMaterial
|
||||||
|
color={color}
|
||||||
|
depthTest
|
||||||
|
depthWrite={opacity >= 0.999}
|
||||||
|
opacity={opacity}
|
||||||
|
transparent={opacity < 0.999}
|
||||||
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user