fix(editor): harden editor interactions and WebGPU rendering

Fix editor bug sweep regressions, WebGPU CSG/material crashes, Shift snap bypass behavior, arrow handle drag projection, and the wall preview null guard covered by the Sentry follow-up PRs.
This commit is contained in:
Aymeric Rabot
2026-06-11 13:03:34 -04:00
committed by GitHub
parent 2d2dba5dba
commit aab48e053f
111 changed files with 2211 additions and 955 deletions
@@ -67,7 +67,10 @@ export default function MoveTurbineVentTool({ node }: { node: TurbineVentNode })
const sx = Math.round(target.localX * 20) / 20
const sz = Math.round(target.localZ * 20) / 20
if (!lastSnap || lastSnap[0] !== sx || lastSnap[1] !== sz) {
if (
event.nativeEvent?.shiftKey !== true &&
(!lastSnap || lastSnap[0] !== sx || lastSnap[1] !== sz)
) {
triggerSFX('sfx:grid-snap')
lastSnap = [sx, sz]
}
+7 -5
View File
@@ -14,7 +14,7 @@ import { useViewer } from '@pascal-app/viewer'
import { useEffect, useMemo, useRef, useState } from 'react'
import * as THREE from 'three'
import { resolveRoofSegmentHit } from '../shared/roof-segment-hit'
import { getAnalyticalNormal, surfaceQuatFromNormal } from '../shared/roof-surface'
import { getAnalyticalNormal, getDownSlopeYaw, surfaceQuatFromNormal } from '../shared/roof-surface'
import { turbineVentDefinition } from './definition'
import TurbineVentPreview from './preview'
@@ -33,6 +33,7 @@ const TurbineVentTool = () => {
const [previewPos, setPreviewPos] = useState<[number, number, number] | null>(null)
const [previewSurfaceQuat, setPreviewSurfaceQuat] = useState<THREE.Quaternion | null>(null)
const [previewYaw, setPreviewYaw] = useState(0)
const [previewRotation, setPreviewRotation] = useState(0)
const lastSnapRef = useRef<[number, number] | null>(null)
const previewNode = useMemo(
@@ -41,9 +42,9 @@ const TurbineVentTool = () => {
...turbineVentDefinition.defaults(),
name: 'Turbine Vent',
position: [0, 0, 0],
rotation: 0,
rotation: previewRotation,
}),
[],
[previewRotation],
)
useEffect(() => {
@@ -65,7 +66,7 @@ const TurbineVentTool = () => {
const sx = Math.round(wx * 20) / 20
const sz = Math.round(wz * 20) / 20
const prev = lastSnapRef.current
if (!prev || prev[0] !== sx || prev[1] !== sz) {
if (event.nativeEvent?.shiftKey !== true && (!prev || prev[0] !== sx || prev[1] !== sz)) {
triggerSFX('sfx:grid-snap')
lastSnapRef.current = [sx, sz]
}
@@ -76,6 +77,7 @@ const TurbineVentTool = () => {
const normal = getAnalyticalNormal(hit.localX, hit.localZ, hit.segment)
setPreviewSurfaceQuat(surfaceQuatFromNormal(normal, new THREE.Quaternion()))
setPreviewYaw((event.node.rotation ?? 0) + (hit.segment.rotation ?? 0))
setPreviewRotation(getDownSlopeYaw(hit.localX, hit.localZ, hit.segment))
setPreviewPos(worldToBuildingLocal(wx, wy, wz))
event.stopPropagation()
}
@@ -95,7 +97,7 @@ const TurbineVentTool = () => {
name: 'Turbine Vent',
roofSegmentId: hit.segment.id,
position: [hit.localX, hit.localY, hit.localZ],
rotation: 0,
rotation: getDownSlopeYaw(hit.localX, hit.localZ, hit.segment),
})
state.createNode(vent, hit.segment.id as AnyNodeId)
state.dirtyNodes.add(hit.segment.id as AnyNodeId)