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}
+ />
)