diff --git a/packages/nodes/src/slab/__tests__/geometry.test.ts b/packages/nodes/src/slab/__tests__/geometry.test.ts new file mode 100644 index 00000000..411d051f --- /dev/null +++ b/packages/nodes/src/slab/__tests__/geometry.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from 'bun:test' +import { SlabNode } from '@pascal-app/core' +import { Mesh } from 'three' +import { buildSlabGeometry } from '../geometry' + +describe('buildSlabGeometry', () => { + test('copies the primary UVs into uv2 for every slab mesh', () => { + const slab = SlabNode.parse({ + polygon: [ + [0, 0], + [2, 0], + [2, 2], + [0, 2], + ], + }) + + const group = buildSlabGeometry(slab, undefined, 'solid', false) + const meshes = group.children.filter((child): child is Mesh => child instanceof Mesh) + + expect(meshes).toHaveLength(2) + for (const mesh of meshes) { + const uv = mesh.geometry.getAttribute('uv') + const uv2 = mesh.geometry.getAttribute('uv2') + + expect(uv2).toBeDefined() + expect(uv2.itemSize).toBe(2) + expect(uv2.count).toBe(uv.count) + expect(Array.from(uv2.array)).toEqual(Array.from(uv.array)) + } + }) +}) diff --git a/packages/nodes/src/slab/geometry.ts b/packages/nodes/src/slab/geometry.ts index 38f248c6..69d8e1fa 100644 --- a/packages/nodes/src/slab/geometry.ts +++ b/packages/nodes/src/slab/geometry.ts @@ -127,7 +127,10 @@ function splitSlabFacesByFacing(geometry: BufferGeometry): { const build = (data: { pos: number[]; uv: number[] }) => { const geo = new BufferGeometry() geo.setAttribute('position', new Float32BufferAttribute(data.pos, 3)) - if (data.uv.length > 0) geo.setAttribute('uv', new Float32BufferAttribute(data.uv, 2)) + if (data.uv.length > 0) { + geo.setAttribute('uv', new Float32BufferAttribute(data.uv, 2)) + geo.setAttribute('uv2', new Float32BufferAttribute(data.uv.slice(), 2)) + } geo.computeVertexNormals() return geo } diff --git a/packages/viewer/src/components/viewer/post-processing.tsx b/packages/viewer/src/components/viewer/post-processing.tsx index f313cd63..b4e83eaf 100644 --- a/packages/viewer/src/components/viewer/post-processing.tsx +++ b/packages/viewer/src/components/viewer/post-processing.tsx @@ -755,6 +755,8 @@ const PostProcessingPasses = ({ } } catch (error) { hasPipelineErrorRef.current = true + // A failed MRT pass may leave its target bound; clear it before the fallback render. + ;(renderer as any).setRenderTarget?.(null) console.error('[viewer/post-processing] Render pass failed.', { retryCount: retryCountRef.current, rendererCtor: (renderer as any).constructor?.name,