fix(paint-slots): make window/door paint hits land on the tagged children
The window/door root is an invisible hitbox the system gives full-depth BoxGeometry; its front face intercepted every paint/hover ray, so the hit resolved to the hitbox (no slotId) → role null → paint silently disabled. Disable the hitbox's own raycast in the visual path so R3F's recursive intersect returns the tagged frame/glass (panel/glass) children instead; selection still works because those child hits bubble to the root's event handlers. Restored to the default raycast each build, and kept for 'opening' windows/doors (no visuals to paint, still need a selectable hitbox). The 'cutout' child is visible=false so the raycaster already skips it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9f1627e923
commit
cebb29460d
@@ -27,6 +27,8 @@ import useViewer from '../../store/use-viewer'
|
||||
|
||||
// Invisible material for root mesh — used as selection hitbox only
|
||||
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
||||
// Disables a mesh's own raycast so its children become the hit targets.
|
||||
const noopHitboxRaycast: THREE.Mesh['raycast'] = () => {}
|
||||
const defaultRevealMaterial = new THREE.MeshBasicMaterial({ color: '#7f766c' })
|
||||
let baseMaterial = getBaseMaterial()
|
||||
let revealMaterial: THREE.Material = defaultRevealMaterial
|
||||
@@ -2022,6 +2024,10 @@ function updateDoorMesh(rawNode: DoorNode, mesh: THREE.Mesh) {
|
||||
mesh.geometry.dispose()
|
||||
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth)
|
||||
mesh.material = hitboxMaterial
|
||||
// Default (selectable) hitbox raycast — restored each build; the visual path
|
||||
// below disables it so the tagged panel/glass children are the hit targets
|
||||
// (otherwise the full-depth invisible box intercepts every paint/hover ray).
|
||||
mesh.raycast = THREE.Mesh.prototype.raycast
|
||||
|
||||
// Sync transform from node (React may lag behind the system by a frame during drag)
|
||||
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
||||
@@ -2077,6 +2083,9 @@ function updateDoorMesh(rawNode: DoorNode, mesh: THREE.Mesh) {
|
||||
return
|
||||
}
|
||||
|
||||
// Visuals exist: let the tagged children receive paint/hover/selection rays.
|
||||
mesh.raycast = noopHitboxRaycast
|
||||
|
||||
const insideWidth = width - 2 * frameThickness
|
||||
const leafH = height - frameThickness // only top frame
|
||||
const leafDepth = 0.04
|
||||
|
||||
@@ -24,6 +24,8 @@ import useViewer from '../../store/use-viewer'
|
||||
|
||||
// Invisible material for root mesh — used as selection hitbox only
|
||||
const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false })
|
||||
// Disables a mesh's own raycast so its children become the hit targets.
|
||||
const noopHitboxRaycast: THREE.Mesh['raycast'] = () => {}
|
||||
let baseMaterial = getBaseMaterial()
|
||||
let glassMaterial: THREE.Material = defaultGlassMaterial
|
||||
// Per-frame viewer state, captured so the per-node mesh builder (which runs
|
||||
@@ -3228,6 +3230,10 @@ function updateWindowMesh(node: WindowNode, mesh: THREE.Mesh) {
|
||||
mesh.geometry.dispose()
|
||||
mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth)
|
||||
mesh.material = hitboxMaterial
|
||||
// Default (selectable) hitbox raycast — restored each build; the visual path
|
||||
// below disables it so the tagged frame/glass children are the hit targets
|
||||
// (otherwise the full-depth invisible box intercepts every paint/hover ray).
|
||||
mesh.raycast = THREE.Mesh.prototype.raycast
|
||||
|
||||
// Sync transform from node (React may lag behind the system by a frame during drag)
|
||||
mesh.position.set(node.position[0], node.position[1], node.position[2])
|
||||
@@ -3268,6 +3274,9 @@ function updateWindowMesh(node: WindowNode, mesh: THREE.Mesh) {
|
||||
return
|
||||
}
|
||||
|
||||
// Visuals exist: let the tagged children receive paint/hover/selection rays.
|
||||
mesh.raycast = noopHitboxRaycast
|
||||
|
||||
if (windowType === 'sliding') {
|
||||
addSlidingWindowVisuals(node, mesh)
|
||||
syncWindowCutout(node, mesh)
|
||||
|
||||
Reference in New Issue
Block a user