From fc9e818c7972d5e0e75b57b7651a0a6e90753650 Mon Sep 17 00:00:00 2001 From: KNSO Date: Mon, 14 Sep 2026 13:41:46 +0200 Subject: [PATCH] =?UTF-8?q?test(world):=20entquantisierte=20Positionen=20a?= =?UTF-8?q?uch=20f=C3=BCr=20interleaved=20Attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GLTFLoader liefert quantisierte Positionen in echt als InterleavedBufferAttribute (gemeinsamer Puffer mit Normale/UV), nicht als eigene BufferAttribute wie im bisherigen Test. dequantizePosition braucht dafür keine Änderung: es liest nur über das gemeinsame Attribut-Interface (normalized, getX/getY/getZ, count), das three.js für beide Klassen implementiert. Der Test läuft grün und schreibt das fest. Co-Authored-By: Claude Sonnet 5 --- tests/places.test.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tests/places.test.js b/tests/places.test.js index f35e429..a891f7f 100644 --- a/tests/places.test.js +++ b/tests/places.test.js @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { BoxGeometry, BufferAttribute, BufferGeometry, Group, Mesh, MeshStandardMaterial, Scene, - Vector3 } from "three"; +import { BoxGeometry, BufferAttribute, BufferGeometry, Group, InterleavedBuffer, + InterleavedBufferAttribute, Mesh, MeshStandardMaterial, Scene, Vector3 } from "three"; import { PLACES, PlaceLayer, SOLID_MATERIALS, isSolidMaterial, placeTransform, prepareModel, wantedPlaces } from "../src/world/places.js"; @@ -122,6 +122,36 @@ describe("prepareModel", () => { expect(worldGeometry.boundingBox.max.z).toBeCloseTo(-1050.5); }); + // Bruch: GLTFLoader liefert quantisierte Positionen in echt als InterleavedBufferAttribute + // (gemeinsamer Puffer mit weiteren Attributen wie Normale und UV), nicht als eigene + // BufferAttribute — eine Entquantisierung, die nur den einfachen Fall abdeckt, ließe die echten + // Modelldaten quantisiert und der Hof kollidierte wie oben nahe dem Weltursprung. + it("entquantisiert feste Positionen auch als interleaved Attribut", () => { + const buffer = new InterleavedBuffer( + new Int16Array([-32767, 0, -32767, 0, 0, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 0, 0]), 8); + const geometry = new BufferGeometry(); + geometry.setAttribute("position", new InterleavedBufferAttribute(buffer, 3, 0, true)); + const mesh = new Mesh(geometry, new MeshStandardMaterial({ name: "cont_a_side" })); + mesh.scale.setScalar(17); + const root = new Group(); + root.add(mesh); + + prepareModel(root, { at: [1489, 1067.5], rotation: 0, lift: -0.28 }); + + expect(mesh.geometry.attributes.position.array).toBeInstanceOf(Float32Array); + expect(mesh.geometry.attributes.position.normalized).toBe(false); + + // Wie colliderBuild.ts: Geometrie klonen und die Weltmatrix hineinrechnen. + const worldGeometry = mesh.geometry.clone(); + worldGeometry.applyMatrix4(mesh.matrixWorld); + worldGeometry.computeBoundingBox(); + + expect(worldGeometry.boundingBox.min.x).toBeCloseTo(1472); + expect(worldGeometry.boundingBox.max.x).toBeCloseTo(1506); + expect(worldGeometry.boundingBox.min.z).toBeCloseTo(-1084.5); + expect(worldGeometry.boundingBox.max.z).toBeCloseTo(-1050.5); + }); + it("lässt eine durchlässige Position quantisiert", () => { const geometry = new BufferGeometry(); const position = new BufferAttribute(new Int16Array([0, 0, 0, 32767, 32767, 32767]), 3, true);