feat: curved wall and fixes (#236)

* feat: add polygon movement support to editor and implement dedicated move tools for walls, slabs, and ceilings

* feat: add uv2 attribute to ceiling and slab geometries for lightmap support

* Fix curved wall miters and align roof position sliders

* Fix wall measurement label and extension anchors

* Fix WebGPU renderer initialization order

* fix(editor): scope wall movement to the selected level

---------

Co-authored-by: Pascal <open@pascal.app>
This commit is contained in:
Sudhir Yadav
2026-04-15 12:41:16 -04:00
committed by GitHub
co-authored by Pascal
parent 57df224948
commit b1709de44a
37 changed files with 2307 additions and 188 deletions
@@ -1,5 +1,6 @@
import { type SlabNode, useRegistry } from '@pascal-app/core'
import { useMemo, useRef } from 'react'
import { useEffect, useMemo, useRef } from 'react'
import * as THREE from 'three'
import type { Mesh } from 'three'
import { useNodeEvents } from '../../../hooks/use-node-events'
import {
@@ -17,10 +18,20 @@ export const SlabRenderer = ({ node }: { node: SlabNode }) => {
const material = useMemo(() => {
const presetMaterial = createMaterialFromPresetRef(node.materialPreset)
if (presetMaterial) return presetMaterial
const mat = node.material
if (!mat) return DEFAULT_SLAB_MATERIAL
return createMaterial(mat)
const sourceMaterial = presetMaterial ?? (node.material ? createMaterial(node.material) : DEFAULT_SLAB_MATERIAL)
const slabMaterial = sourceMaterial.clone()
// Slabs participate in the WebGPU MRT scene pass. Keeping them opaque avoids
// pipeline variants that can fail when geometry is regenerated while a
// transparent/custom material is attached.
slabMaterial.transparent = false
slabMaterial.opacity = 1
slabMaterial.alphaMap = null
slabMaterial.side = THREE.DoubleSide
slabMaterial.depthWrite = true
slabMaterial.needsUpdate = true
return slabMaterial
}, [
node.material,
node.material?.preset,
@@ -29,6 +40,12 @@ export const SlabRenderer = ({ node }: { node: SlabNode }) => {
node.materialPreset,
])
useEffect(() => {
return () => {
material.dispose()
}
}, [material])
return (
<mesh
castShadow