76 lines
3.0 KiB
JavaScript
76 lines
3.0 KiB
JavaScript
import { BufferAttribute, BufferGeometry, Mesh } from "three";
|
|
|
|
/**
|
|
* @recast-navigation/three currently assumes position/index attributes are
|
|
* tightly-packed Float32/Uint arrays. glTFLoader may return interleaved or
|
|
* normalized BufferAttributes, so copy decoded accessor values into a plain
|
|
* geometry before handing meshes to its generator helpers.
|
|
*/
|
|
export function unpackMeshesForRecast(meshes) {
|
|
let interleavedPositionAttributeCount = 0;
|
|
let normalizedPositionAttributeCount = 0;
|
|
let nonFloatPositionAttributeCount = 0;
|
|
let unpackedPositionAccessorCount = 0;
|
|
let decodedIndexAccessorCount = 0;
|
|
|
|
const preparedMeshes = meshes.map((sourceMesh) => {
|
|
const sourceGeometry = sourceMesh.geometry;
|
|
const sourcePositions = sourceGeometry?.getAttribute("position");
|
|
if (!sourcePositions || sourcePositions.itemSize !== 3) {
|
|
throw new Error(`${sourceMesh.name || "collision mesh"}: POSITION must be a VEC3 attribute.`);
|
|
}
|
|
|
|
const positions = new Float32Array(sourcePositions.count * 3);
|
|
for (let index = 0; index < sourcePositions.count; index += 1) {
|
|
const offset = index * 3;
|
|
positions[offset] = sourcePositions.getX(index);
|
|
positions[offset + 1] = sourcePositions.getY(index);
|
|
positions[offset + 2] = sourcePositions.getZ(index);
|
|
}
|
|
|
|
const geometry = new BufferGeometry();
|
|
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
if (sourcePositions.isInterleavedBufferAttribute) interleavedPositionAttributeCount += 1;
|
|
if (sourcePositions.normalized) normalizedPositionAttributeCount += 1;
|
|
if (!(sourcePositions.array instanceof Float32Array)) nonFloatPositionAttributeCount += 1;
|
|
unpackedPositionAccessorCount += 1;
|
|
|
|
const sourceIndices = sourceGeometry.getIndex();
|
|
if (sourceIndices) {
|
|
const indices = new Uint32Array(sourceIndices.count);
|
|
for (let index = 0; index < sourceIndices.count; index += 1) {
|
|
const value = sourceIndices.getX(index);
|
|
if (!Number.isInteger(value) || value < 0 || value >= sourcePositions.count) {
|
|
throw new Error(
|
|
`${sourceMesh.name || "collision mesh"}: index ${index} references invalid position ${value}.`,
|
|
);
|
|
}
|
|
indices[index] = value;
|
|
}
|
|
geometry.setIndex(new BufferAttribute(indices, 1));
|
|
decodedIndexAccessorCount += 1;
|
|
}
|
|
|
|
sourceMesh.updateWorldMatrix(true, false);
|
|
const preparedMesh = new Mesh(geometry);
|
|
preparedMesh.name = sourceMesh.name;
|
|
preparedMesh.matrixAutoUpdate = false;
|
|
preparedMesh.matrix.copy(sourceMesh.matrixWorld);
|
|
preparedMesh.matrixWorld.copy(sourceMesh.matrixWorld);
|
|
return preparedMesh;
|
|
});
|
|
|
|
return {
|
|
meshes: preparedMeshes,
|
|
report: {
|
|
adapter: "decoded-three-buffer-attributes-v1",
|
|
sourceMeshCount: meshes.length,
|
|
unpackedPositionAccessorCount,
|
|
interleavedPositionAttributeCount,
|
|
normalizedPositionAttributeCount,
|
|
nonFloatPositionAttributeCount,
|
|
decodedIndexAccessorCount,
|
|
},
|
|
};
|
|
}
|