From 8ce26154d9b57466de254ff2a8c3f08d37ae6bb1 Mon Sep 17 00:00:00 2001 From: Aymeric Rabot Date: Tue, 9 Jun 2026 13:17:04 -0400 Subject: [PATCH] fix: respect scene depth for site and ceiling handles --- .../ceiling-selection-affordance-system.tsx | 16 +++------ .../tools/shared/polygon-editor.tsx | 33 +++++++++++++------ .../tools/site/site-boundary-editor.tsx | 21 +++++++++--- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx b/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx index c61b8665..31134980 100644 --- a/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +++ b/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx @@ -18,9 +18,9 @@ const BRACKET_THICKNESS = 0.04 const BRACKET_HEIGHT = 0.04 const BRACKET_Y_OFFSET = 0.035 const HIT_BOX_SIZE: [number, number, number] = [0.28, 0.08, 0.28] -// Draw the corner handles after everything else and with depth testing -// off (see materials below) so they stay visible — and clickable — even -// when a wall, roof, or the ceiling itself would otherwise occlude them. +// Draw the corner handles after the ceiling surface so they read cleanly +// when unobstructed, while material depth testing still lets other scene +// geometry hide them. const CORNER_RENDER_ORDER = 1000 type CornerBracketData = { @@ -189,7 +189,7 @@ const CornerBracket = ({ - + ) } diff --git a/packages/editor/src/components/tools/shared/polygon-editor.tsx b/packages/editor/src/components/tools/shared/polygon-editor.tsx index 2257bcf9..8f75d69f 100644 --- a/packages/editor/src/components/tools/shared/polygon-editor.tsx +++ b/packages/editor/src/components/tools/shared/polygon-editor.tsx @@ -6,6 +6,7 @@ import { BufferGeometry, Color, CylinderGeometry, + DoubleSide, ExtrudeGeometry, Float32BufferAttribute, type Line, @@ -20,7 +21,6 @@ import { ARROW_COLOR as EDGE_ARROW_COLOR, ARROW_HOVER_COLOR as EDGE_ARROW_HOVER_COLOR, ARROW_SCALE as EDGE_ARROW_SCALE, - useArrowMaterial, useInvisibleHitAreaMaterial, } from '../../editor/node-arrow-handles' import { snapToHalf } from '../item/placement-math' @@ -181,7 +181,7 @@ function usePolygonNodeMaterial(color: string, opacity = 1): MeshBasicNodeMateri () => new MeshBasicNodeMaterial({ color: new Color('#ffffff'), - depthTest: false, + depthTest: true, depthWrite: true, opacity: 1, transparent: true, @@ -198,11 +198,24 @@ function usePolygonNodeMaterial(color: string, opacity = 1): MeshBasicNodeMateri 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 -// post-processing ink-edge pass outlines it, and carries the pointer handlers -// directly so it stays grabbable — matching the registry arrow gizmos in -// node-arrow-handles.tsx. No paired hit mesh is needed; the R3F event -// raycaster picks SCENE_LAYER meshes too. +// post-processing ink-edge pass outlines it. The visual material still +// depth-tests, so walls/items in front can occlude it. function OutlinedCylinderHandle({ radius, height, @@ -290,7 +303,7 @@ function OutlinedEdgeArrowHandle({ rotationY: number scale: number } & PolygonHandleHandlers) { - const material = useArrowMaterial() + const material = usePolygonArrowMaterial() useEffect(() => { material.color.set(color) }, [color, material]) @@ -741,9 +754,9 @@ export const PolygonEditor: React.FC = ({ const handleHeight = Math.max(MIN_HANDLE_HEIGHT, surfaceHeight + 0.02) const edgeHandleY = editY + handleHeight - EDGE_HANDLE_HEIGHT / 2 - // Interactive handles are single SCENE_LAYER node-material meshes (like the - // registry arrow gizmos) so the ink-edge pass outlines them while they stay - // grabbable. The edge BAR and border line stay on EDITOR_LAYER, visual-only + // Interactive handles are SCENE_LAYER node-material meshes so the ink-edge + // pass outlines them while normal scene depth can hide them. The edge BAR and + // border line stay on EDITOR_LAYER, visual-only // (raycast disabled) so they never steal clicks from the vertex/midpoint // handles overlapping them — edge dragging starts from the chevron arrow // outside the polygon edge. diff --git a/packages/editor/src/components/tools/site/site-boundary-editor.tsx b/packages/editor/src/components/tools/site/site-boundary-editor.tsx index 5a77f886..41da633e 100644 --- a/packages/editor/src/components/tools/site/site-boundary-editor.tsx +++ b/packages/editor/src/components/tools/site/site-boundary-editor.tsx @@ -38,6 +38,7 @@ const SITE_FLAG_HALO_COLOR = '#6366f1' type TintableMaterial = { color?: Color + depthTest: boolean depthWrite: boolean opacity: number needsUpdate: boolean @@ -69,10 +70,9 @@ function SiteFlagModel({ mesh.frustumCulled = false mesh.raycast = NO_RAYCAST mesh.receiveShadow = false - mesh.renderOrder = 1010 mesh.material = new MeshBasicNodeMaterial({ color: new Color(ARROW_COLOR), - depthTest: false, + depthTest: true, depthWrite: opacity >= 0.999, opacity, transparent: opacity < 0.999, @@ -93,6 +93,7 @@ function SiteFlagModel({ for (const material of materials as Array) { material.color?.copy(color) + material.depthTest = true material.opacity = opacity material.transparent = opacity < 0.999 material.depthWrite = opacity >= 0.999 @@ -217,11 +218,23 @@ function SiteFlagFallback({ - + = 0.999} + opacity={opacity} + transparent={opacity < 0.999} + /> - + = 0.999} + opacity={opacity} + transparent={opacity < 0.999} + /> )