@enable3d/ammo-physics
Version:
Physics Plugin for three.js
715 lines • 31.6 kB
JavaScript
/* eslint-disable prefer-const */
/* eslint-disable prefer-template */
/* eslint-disable no-prototype-builtins */
/**
* @author Yannick Deubel (https://github.com/yandeu)
* @copyright Copyright (c) 2020 Yannick Deubel; Project Url: https://github.com/enable3d/enable3d
* @description This is a modified version of the original code from Kevin Lee
* (Includes latest three-to-ammo commit from August 6, 2020)
*/
/**
* @author Kevin Lee (https://github.com/InfiniteLee)
* @copyright Copyright (c) 2020 Kevin Lee; Project Url: https://github.com/InfiniteLee/three-to-ammo
* @license {@link https://github.com/InfiniteLee/three-to-ammo/blob/master/LICENSE|MPL-2.0}
*/
import { Vector3, Matrix4, Box3, REVISION } from 'three';
export const TYPE = {
BOX: 'box',
CYLINDER: 'cylinder',
SPHERE: 'sphere',
CAPSULE: 'capsule',
CONE: 'cone',
HULL: 'hull',
HACD: 'hacd', //Hierarchical Approximate Convex Decomposition
VHACD: 'vhacd', //Volumetric Hierarchical Approximate Convex Decomposition
MESH: 'mesh',
HEIGHTFIELD: 'heightfield'
};
export const FIT = {
ALL: 'all', //A single shape is automatically sized to bound all meshes within the entity.
MANUAL: 'manual' //A single shape is sized manually. Requires halfExtents or sphereRadius.
};
export const HEIGHTFIELD_DATA_TYPE = {
short: 'short',
float: 'float'
};
export const createCollisionShapes = function (vertices, matrices, indexes, matrixWorld, options = {}) {
switch (options.type) {
case TYPE.BOX:
return [createBoxShape(vertices, matrices, matrixWorld, options)];
case TYPE.CYLINDER:
return [createCylinderShape(vertices, matrices, matrixWorld, options)];
case TYPE.CAPSULE:
return [createCapsuleShape(vertices, matrices, matrixWorld, options)];
case TYPE.CONE:
return [createConeShape(vertices, matrices, matrixWorld, options)];
case TYPE.SPHERE:
return [createSphereShape(vertices, matrices, matrixWorld, options)];
case TYPE.HULL:
return [createHullShape(vertices, matrices, matrixWorld, options)];
case TYPE.HACD:
return createHACDShapes(vertices, matrices, indexes, matrixWorld, options);
case TYPE.VHACD:
return createVHACDShapes(vertices, matrices, indexes, matrixWorld, options);
case TYPE.MESH:
return [createTriMeshShape(vertices, matrices, indexes, matrixWorld, options)];
case TYPE.HEIGHTFIELD:
return [createHeightfieldTerrainShape(options)];
default:
console.warn(options.type + ' is not currently supported');
return [];
}
};
//TODO: support gimpact (dynamic trimesh) and heightmap
export const createBoxShape = function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.BOX;
_setOptions(options);
if (options.fit === FIT.ALL) {
options.halfExtents = _computeHalfExtents(_computeBounds(vertices, matrices), options.minHalfExtent, options.maxHalfExtent);
}
const btHalfExtents = new Ammo.btVector3(options.halfExtents.x, options.halfExtents.y, options.halfExtents.z);
const collisionShape = new Ammo.btBoxShape(btHalfExtents);
Ammo.destroy(btHalfExtents);
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
export const createCylinderShape = function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.CYLINDER;
_setOptions(options);
if (options.fit === FIT.ALL) {
options.halfExtents = _computeHalfExtents(_computeBounds(vertices, matrices), options.minHalfExtent, options.maxHalfExtent);
}
const btHalfExtents = new Ammo.btVector3(options.halfExtents.x, options.halfExtents.y, options.halfExtents.z);
const collisionShape = (() => {
switch (options.cylinderAxis) {
case 'y':
return new Ammo.btCylinderShape(btHalfExtents);
case 'x':
return new Ammo.btCylinderShapeX(btHalfExtents);
case 'z':
return new Ammo.btCylinderShapeZ(btHalfExtents);
}
return null;
})();
Ammo.destroy(btHalfExtents);
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
export const createCapsuleShape = function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.CAPSULE;
_setOptions(options);
if (options.fit === FIT.ALL) {
options.halfExtents = _computeHalfExtents(_computeBounds(vertices, matrices), options.minHalfExtent, options.maxHalfExtent);
}
const { x, y, z } = options.halfExtents;
const collisionShape = (() => {
switch (options.cylinderAxis) {
case 'y':
return new Ammo.btCapsuleShape(Math.max(x, z), y * 2);
case 'x':
return new Ammo.btCapsuleShapeX(Math.max(y, z), x * 2);
case 'z':
return new Ammo.btCapsuleShapeZ(Math.max(x, y), z * 2);
}
return null;
})();
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
export const createConeShape = function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.CONE;
_setOptions(options);
if (options.fit === FIT.ALL) {
options.halfExtents = _computeHalfExtents(_computeBounds(vertices, matrices), options.minHalfExtent, options.maxHalfExtent);
}
const { x, y, z } = options.halfExtents;
const collisionShape = (() => {
switch (options.cylinderAxis) {
case 'y':
return new Ammo.btConeShape(Math.max(x, z), y * 2);
case 'x':
return new Ammo.btConeShapeX(Math.max(y, z), x * 2);
case 'z':
return new Ammo.btConeShapeZ(Math.max(x, y), z * 2);
}
return null;
})();
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
export const createSphereShape = function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.SPHERE;
_setOptions(options);
let radius;
if (options.fit === FIT.MANUAL && !isNaN(options.sphereRadius)) {
radius = options.sphereRadius;
}
else {
radius = _computeRadius(vertices, matrices, _computeBounds(vertices, matrices));
}
const collisionShape = new Ammo.btSphereShape(radius);
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
export const createHullShape = (function () {
const vertex = new Vector3();
const center = new Vector3();
const matrix = new Matrix4();
return function (vertices, matrices, matrixWorld, options = {}) {
options.type = TYPE.HULL;
_setOptions(options);
if (options.fit === FIT.MANUAL) {
console.warn('cannot use fit: manual with type: hull');
return null;
}
const bounds = _computeBounds(vertices, matrices);
const btVertex = new Ammo.btVector3();
const originalHull = new Ammo.btConvexHullShape();
originalHull.setMargin(options.margin);
center.addVectors(bounds.max, bounds.min).multiplyScalar(0.5);
let vertexCount = 0;
for (let i = 0; i < vertices.length; i++) {
vertexCount += vertices[i].length / 3;
}
const maxVertices = options.hullMaxVertices || 100000;
// todo: might want to implement this in a deterministic way that doesn't do O(verts) calls to Math.random
if (vertexCount > maxVertices) {
console.warn(`too many vertices for hull shape; sampling ~${maxVertices} from ~${vertexCount} vertices`);
}
const p = Math.min(1, maxVertices / vertexCount);
for (let i = 0; i < vertices.length; i++) {
const components = vertices[i];
matrix.fromArray(matrices[i]);
for (let j = 0; j < components.length; j += 3) {
const isLastVertex = i === vertices.length - 1 && j === components.length - 3;
if (Math.random() <= p || isLastVertex) {
// always include the last vertex
vertex
.set(components[j], components[j + 1], components[j + 2])
.applyMatrix4(matrix)
.sub(center);
btVertex.setValue(vertex.x, vertex.y, vertex.z);
originalHull.addPoint(btVertex, isLastVertex); // recalc AABB only on last geometry
}
}
}
let collisionShape = originalHull;
if (originalHull.getNumVertices() >= 100) {
//Bullet documentation says don't use convexHulls with 100 verts or more
const shapeHull = new Ammo.btShapeHull(originalHull);
shapeHull.buildHull(options.margin);
Ammo.destroy(originalHull);
collisionShape = new Ammo.btConvexHullShape(Ammo.getPointer(shapeHull.getVertexPointer()), shapeHull.numVertices());
Ammo.destroy(shapeHull); // btConvexHullShape makes a copy
}
Ammo.destroy(btVertex);
_finishCollisionShape(collisionShape, options, _computeScale(matrixWorld, options));
return collisionShape;
};
})();
export const createHACDShapes = (function () {
const vector = new Vector3();
const center = new Vector3();
const matrix = new Matrix4();
return function (vertices, matrices, indexes, matrixWorld, options = {}) {
options.type = TYPE.HACD;
_setOptions(options);
if (options.fit === FIT.MANUAL) {
console.warn('cannot use fit: manual with type: hacd');
return [];
}
if (!Ammo.hasOwnProperty('HACD')) {
console.warn('HACD unavailable in included build of Ammo.js. Visit https://github.com/mozillareality/ammo.js for the latest version.');
return [];
}
const bounds = _computeBounds(vertices, matrices);
const scale = _computeScale(matrixWorld, options);
let vertexCount = 0;
let triCount = 0;
center.addVectors(bounds.max, bounds.min).multiplyScalar(0.5);
for (let i = 0; i < vertices.length; i++) {
vertexCount += vertices[i].length / 3;
if (indexes && indexes[i]) {
triCount += indexes[i].length / 3;
}
else {
triCount += vertices[i].length / 9;
}
}
const hacd = new Ammo.HACD();
if (options.hasOwnProperty('compacityWeight'))
hacd.SetCompacityWeight(options.compacityWeight);
if (options.hasOwnProperty('volumeWeight'))
hacd.SetVolumeWeight(options.volumeWeight);
if (options.hasOwnProperty('nClusters'))
hacd.SetNClusters(options.nClusters);
if (options.hasOwnProperty('nVerticesPerCH'))
hacd.SetNVerticesPerCH(options.nVerticesPerCH);
if (options.hasOwnProperty('concavity'))
hacd.SetConcavity(options.concavity);
const points = Ammo._malloc(vertexCount * 3 * 8);
const triangles = Ammo._malloc(triCount * 3 * 4);
hacd.SetPoints(points);
hacd.SetTriangles(triangles);
hacd.SetNPoints(vertexCount);
hacd.SetNTriangles(triCount);
let pptr = points / 8, tptr = triangles / 4;
for (let i = 0; i < vertices.length; i++) {
const components = vertices[i];
matrix.fromArray(matrices[i]);
for (let j = 0; j < components.length; j += 3) {
vector
.set(components[j + 0], components[j + 1], components[j + 2])
.applyMatrix4(matrix)
.sub(center);
Ammo.HEAPF64[pptr + 0] = vector.x;
Ammo.HEAPF64[pptr + 1] = vector.y;
Ammo.HEAPF64[pptr + 2] = vector.z;
pptr += 3;
}
if (indexes[i]) {
const indices = indexes[i];
for (let j = 0; j < indices.length; j++) {
Ammo.HEAP32[tptr] = indices[j];
tptr++;
}
}
else {
for (let j = 0; j < components.length / 3; j++) {
Ammo.HEAP32[tptr] = j;
tptr++;
}
}
}
hacd.Compute();
Ammo._free(points);
Ammo._free(triangles);
const nClusters = hacd.GetNClusters();
const shapes = [];
for (let i = 0; i < nClusters; i++) {
const hull = new Ammo.btConvexHullShape();
hull.setMargin(options.margin);
const nPoints = hacd.GetNPointsCH(i);
const nTriangles = hacd.GetNTrianglesCH(i);
const hullPoints = Ammo._malloc(nPoints * 3 * 8);
const hullTriangles = Ammo._malloc(nTriangles * 3 * 4);
hacd.GetCH(i, hullPoints, hullTriangles);
const pptr = hullPoints / 8;
for (let pi = 0; pi < nPoints; pi++) {
const btVertex = new Ammo.btVector3();
const px = Ammo.HEAPF64[pptr + pi * 3 + 0];
const py = Ammo.HEAPF64[pptr + pi * 3 + 1];
const pz = Ammo.HEAPF64[pptr + pi * 3 + 2];
btVertex.setValue(px, py, pz);
hull.addPoint(btVertex, pi === nPoints - 1);
Ammo.destroy(btVertex);
}
_finishCollisionShape(hull, options, scale);
shapes.push(hull);
}
return shapes;
};
})();
export const createVHACDShapes = (function () {
const vector = new Vector3();
const center = new Vector3();
const matrix = new Matrix4();
return function (vertices, matrices, indexes, matrixWorld, options = {}) {
options.type = TYPE.VHACD;
_setOptions(options);
if (options.fit === FIT.MANUAL) {
console.warn('cannot use fit: manual with type: vhacd');
return [];
}
if (!Ammo.hasOwnProperty('VHACD')) {
console.warn('VHACD unavailable in included build of Ammo.js. Visit https://github.com/mozillareality/ammo.js for the latest version.');
return [];
}
const bounds = _computeBounds(vertices, matrices);
const scale = _computeScale(matrixWorld, options);
let vertexCount = 0;
let triCount = 0;
center.addVectors(bounds.max, bounds.min).multiplyScalar(0.5);
for (let i = 0; i < vertices.length; i++) {
vertexCount += vertices[i].length / 3;
if (indexes && indexes[i]) {
triCount += indexes[i].length / 3;
}
else {
triCount += vertices[i].length / 9;
}
}
const vhacd = new Ammo.VHACD();
const params = new Ammo.Parameters();
//https://kmamou.blogspot.com/2014/12/v-hacd-20-parameters-description.html
if (options.hasOwnProperty('resolution'))
params.set_m_resolution(options.resolution);
if (options.hasOwnProperty('depth'))
params.set_m_depth(options.depth);
if (options.hasOwnProperty('concavity'))
params.set_m_concavity(options.concavity);
if (options.hasOwnProperty('planeDownsampling'))
params.set_m_planeDownsampling(options.planeDownsampling);
if (options.hasOwnProperty('convexhullDownsampling'))
params.set_m_convexhullDownsampling(options.convexhullDownsampling);
if (options.hasOwnProperty('alpha'))
params.set_m_alpha(options.alpha);
if (options.hasOwnProperty('beta'))
params.set_m_beta(options.beta);
if (options.hasOwnProperty('gamma'))
params.set_m_gamma(options.gamma);
if (options.hasOwnProperty('pca'))
params.set_m_pca(options.pca);
if (options.hasOwnProperty('mode'))
params.set_m_mode(options.mode);
if (options.hasOwnProperty('maxNumVerticesPerCH'))
params.set_m_maxNumVerticesPerCH(options.maxNumVerticesPerCH);
if (options.hasOwnProperty('minVolumePerCH'))
params.set_m_minVolumePerCH(options.minVolumePerCH);
if (options.hasOwnProperty('convexhullApproximation'))
params.set_m_convexhullApproximation(options.convexhullApproximation);
if (options.hasOwnProperty('oclAcceleration'))
params.set_m_oclAcceleration(options.oclAcceleration);
const points = Ammo._malloc(vertexCount * 3 * 8 + 3);
const triangles = Ammo._malloc(triCount * 3 * 4);
let pptr = points / 8, tptr = triangles / 4;
for (let i = 0; i < vertices.length; i++) {
const components = vertices[i];
matrix.fromArray(matrices[i]);
for (let j = 0; j < components.length; j += 3) {
vector
.set(components[j + 0], components[j + 1], components[j + 2])
.applyMatrix4(matrix)
.sub(center);
Ammo.HEAPF64[pptr + 0] = vector.x;
Ammo.HEAPF64[pptr + 1] = vector.y;
Ammo.HEAPF64[pptr + 2] = vector.z;
pptr += 3;
}
if (indexes[i]) {
const indices = indexes[i];
for (let j = 0; j < indices.length; j++) {
Ammo.HEAP32[tptr] = indices[j];
tptr++;
}
}
else {
for (let j = 0; j < components.length / 3; j++) {
Ammo.HEAP32[tptr] = j;
tptr++;
}
}
}
vhacd.Compute(points, 3, vertexCount, triangles, 3, triCount, params);
Ammo._free(points);
Ammo._free(triangles);
const nHulls = vhacd.GetNConvexHulls();
const shapes = [];
const ch = new Ammo.ConvexHull();
for (let i = 0; i < nHulls; i++) {
vhacd.GetConvexHull(i, ch);
const nPoints = ch.get_m_nPoints();
const hullPoints = ch.get_m_points();
const hull = new Ammo.btConvexHullShape();
hull.setMargin(options.margin);
for (let pi = 0; pi < nPoints; pi++) {
const btVertex = new Ammo.btVector3();
const px = ch.get_m_points(pi * 3 + 0);
const py = ch.get_m_points(pi * 3 + 1);
const pz = ch.get_m_points(pi * 3 + 2);
btVertex.setValue(px, py, pz);
hull.addPoint(btVertex, pi === nPoints - 1);
Ammo.destroy(btVertex);
}
_finishCollisionShape(hull, options, scale);
shapes.push(hull);
}
Ammo.destroy(ch);
Ammo.destroy(vhacd);
return shapes;
};
})();
export const createTriMeshShape = (function () {
const va = new Vector3();
const vb = new Vector3();
const vc = new Vector3();
const matrix = new Matrix4();
return function (vertices, matrices, indexes, matrixWorld, options = {}) {
options.type = TYPE.MESH;
_setOptions(options);
if (options.fit === FIT.MANUAL) {
console.warn('cannot use fit: manual with type: mesh');
return null;
}
const scale = _computeScale(matrixWorld, options);
const bta = new Ammo.btVector3();
const btb = new Ammo.btVector3();
const btc = new Ammo.btVector3();
const triMesh = new Ammo.btTriangleMesh(true, false);
for (let i = 0; i < vertices.length; i++) {
const components = vertices[i];
const index = indexes[i] ? indexes[i] : null;
matrix.fromArray(matrices[i]);
if (index) {
for (let j = 0; j < index.length; j += 3) {
const ai = index[j] * 3;
const bi = index[j + 1] * 3;
const ci = index[j + 2] * 3;
va.set(components[ai], components[ai + 1], components[ai + 2]).applyMatrix4(matrix);
vb.set(components[bi], components[bi + 1], components[bi + 2]).applyMatrix4(matrix);
vc.set(components[ci], components[ci + 1], components[ci + 2]).applyMatrix4(matrix);
bta.setValue(va.x, va.y, va.z);
btb.setValue(vb.x, vb.y, vb.z);
btc.setValue(vc.x, vc.y, vc.z);
triMesh.addTriangle(bta, btb, btc, false);
}
}
else {
for (let j = 0; j < components.length; j += 9) {
va.set(components[j + 0], components[j + 1], components[j + 2]).applyMatrix4(matrix);
vb.set(components[j + 3], components[j + 4], components[j + 5]).applyMatrix4(matrix);
vc.set(components[j + 6], components[j + 7], components[j + 8]).applyMatrix4(matrix);
bta.setValue(va.x, va.y, va.z);
btb.setValue(vb.x, vb.y, vb.z);
btc.setValue(vc.x, vc.y, vc.z);
triMesh.addTriangle(bta, btb, btc, false);
}
}
}
const localScale = new Ammo.btVector3(scale.x, scale.y, scale.z);
triMesh.setScaling(localScale);
Ammo.destroy(localScale);
// MOD (yandeu): Use btConvexTriangleMeshShape for concave shapes
let collisionShape;
if (options.concave)
collisionShape = new Ammo.btBvhTriangleMeshShape(triMesh, true, true);
else
collisionShape = new Ammo.btConvexTriangleMeshShape(triMesh, true);
// const collisionShape = new Ammo.btBvhTriangleMeshShape(triMesh, true, true)
collisionShape.resources = [triMesh];
Ammo.destroy(bta);
Ammo.destroy(btb);
Ammo.destroy(btc);
_finishCollisionShape(collisionShape, options);
return collisionShape;
};
})();
export const createHeightfieldTerrainShape = function (options = {}) {
_setOptions(options);
if (options.fit === FIT.ALL) {
console.warn('cannot use fit: all with type: heightfield');
return null;
}
const heightfieldDistance = options.heightfieldDistance || 1;
const heightfieldData = options.heightfieldData || [];
const heightScale = options.heightScale || 0;
const upAxis = options.hasOwnProperty('upAxis') ? options.upAxis : 1; // x = 0; y = 1; z = 2
const hdt = (() => {
switch (options.heightDataType) {
case 'short':
return Ammo.PHY_SHORT;
case 'float':
return Ammo.PHY_FLOAT;
default:
return Ammo.PHY_FLOAT;
}
})();
const flipQuadEdges = options.hasOwnProperty('flipQuadEdges') ? options.flipQuadEdges : true;
const heightStickLength = heightfieldData.length;
const heightStickWidth = heightStickLength > 0 ? heightfieldData[0].length : 0;
const data = Ammo._malloc(heightStickLength * heightStickWidth * 4);
const ptr = data / 4;
let minHeight = Number.POSITIVE_INFINITY;
let maxHeight = Number.NEGATIVE_INFINITY;
let index = 0;
for (let l = 0; l < heightStickLength; l++) {
for (let w = 0; w < heightStickWidth; w++) {
const height = heightfieldData[l][w];
Ammo.HEAPF32[ptr + index] = height;
index++;
minHeight = Math.min(minHeight, height);
maxHeight = Math.max(maxHeight, height);
}
}
const collisionShape = new Ammo.btHeightfieldTerrainShape(heightStickWidth, heightStickLength, data, heightScale, minHeight, maxHeight, upAxis, hdt, flipQuadEdges);
const scale = new Ammo.btVector3(heightfieldDistance, 1, heightfieldDistance);
collisionShape.setLocalScaling(scale);
Ammo.destroy(scale);
collisionShape.heightfieldData = data;
_finishCollisionShape(collisionShape, options);
return collisionShape;
};
function _setOptions(options) {
// MOD (yandeu): All of this will be done in physics.ts
// we only keep type and margin
options.type = options.type || TYPE.HULL;
options.margin = options.hasOwnProperty('margin') ? options.margin : 0.01;
return;
// options.fit = options.hasOwnProperty('fit') ? options.fit : FIT.ALL
// options.type = options.type || TYPE.HULL
// options.minHalfExtent = options.hasOwnProperty('minHalfExtent') ? options.minHalfExtent : 0
// options.maxHalfExtent = options.hasOwnProperty('maxHalfExtent') ? options.maxHalfExtent : Number.POSITIVE_INFINITY
// options.cylinderAxis = options.cylinderAxis || 'y'
// options.margin = options.hasOwnProperty('margin') ? options.margin : 0.01
// options.includeInvisible = options.hasOwnProperty('includeInvisible') ? options.includeInvisible : false
// if (!options.offset) {
// options.offset = new Vector3()
// }
// if (!options.orientation) {
// options.orientation = new Quaternion()
// }
}
const _finishCollisionShape = function (collisionShape, options, scale) {
// MOD (yandeu): All of this will be done in physics.ts
return;
// collisionShape.type = options.type
// collisionShape.setMargin(options.margin)
// collisionShape.destroy = () => {
// for (let res of collisionShape.resources || []) {
// Ammo.destroy(res)
// }
// if (collisionShape.heightfieldData) {
// Ammo._free(collisionShape.heightfieldData)
// }
// Ammo.destroy(collisionShape)
// }
// const localTransform = new Ammo.btTransform()
// const rotation = new Ammo.btQuaternion()
// localTransform.setIdentity()
// localTransform.getOrigin().setValue(options.offset.x, options.offset.y, options.offset.z)
// rotation.setValue(options.orientation.x, options.orientation.y, options.orientation.z, options.orientation.w)
// localTransform.setRotation(rotation)
// Ammo.destroy(rotation)
// if (scale) {
// const localScale = new Ammo.btVector3(scale.x, scale.y, scale.z)
// collisionShape.setLocalScaling(localScale)
// Ammo.destroy(localScale)
// }
// collisionShape.localTransform = localTransform
};
export const iterateGeometries = (function () {
const inverse = new Matrix4();
return function (root, options, cb) {
// MOD (yandeu): Update to three.js r123
// compatibility fix for three.js >= r123 (Dezember 2020)
if (parseInt(REVISION) >= 123)
inverse.copy(root.matrixWorld).invert();
// @ts-expect-error getInverse has been deprecated
else
inverse.getInverse(root.matrixWorld);
const scale = new Vector3();
scale.setFromMatrixScale(root.matrixWorld);
root.traverse((mesh) => {
const transform = new Matrix4();
if (mesh.isMesh &&
// MOD (yandeu): No need to check if name is 'Sky'
// mesh.name !== 'Sky' &&
(options.includeInvisible || (mesh.el && mesh.el.object3D.visible) || mesh.visible)) {
if (mesh === root) {
transform.identity();
}
else {
mesh.updateWorldMatrix(true);
transform.multiplyMatrices(inverse, mesh.matrixWorld);
}
// todo: might want to return null xform if this is the root so that callers can avoid multiplying
// things by the identity matrix
let positions;
if (mesh.geometry.isBufferGeometry) {
const bufferPositions = mesh.geometry.attributes.position;
if (bufferPositions.isInterleavedBufferAttribute) {
positions = new Float32Array(bufferPositions.count * bufferPositions.itemSize);
let interleavedInd = bufferPositions.offset;
let positionInd = 0;
while (positionInd < positions.length) {
for (let elemInd = 0; elemInd < bufferPositions.itemSize; elemInd++) {
positions[positionInd++] = bufferPositions.array[interleavedInd + elemInd];
}
interleavedInd += bufferPositions.data.stride;
}
}
else {
positions = bufferPositions.array;
}
}
cb(mesh.geometry.isBufferGeometry ? positions : mesh.geometry.vertices, transform.elements, mesh.geometry.index ? mesh.geometry.index.array : null);
}
});
};
})();
const _computeScale = (function () {
const matrix = new Matrix4();
return function (matrixWorld, options = {}) {
const scale = new Vector3(1, 1, 1);
if (options.fit === FIT.ALL) {
matrix.fromArray(matrixWorld);
scale.setFromMatrixScale(matrix);
}
return scale;
};
})();
const _computeRadius = (function () {
const center = new Vector3();
return function (vertices, matrices, bounds) {
let maxRadiusSq = 0;
let { x: cx, y: cy, z: cz } = bounds.getCenter(center);
_iterateVertices(vertices, matrices, (v) => {
const dx = cx - v.x;
const dy = cy - v.y;
const dz = cz - v.z;
maxRadiusSq = Math.max(maxRadiusSq, dx * dx + dy * dy + dz * dz);
});
return Math.sqrt(maxRadiusSq);
};
})();
const _computeHalfExtents = function (bounds, minHalfExtent, maxHalfExtent) {
const halfExtents = new Vector3();
return halfExtents.subVectors(bounds.max, bounds.min).multiplyScalar(0.5).clampScalar(minHalfExtent, maxHalfExtent);
};
const _computeLocalOffset = function (matrix, bounds, target) {
target.addVectors(bounds.max, bounds.min).multiplyScalar(0.5).applyMatrix4(matrix);
return target;
};
// returns the bounding box for the geometries underneath `root`.
const _computeBounds = function (vertices, matrices) {
const bounds = new Box3();
let minX = +Infinity;
let minY = +Infinity;
let minZ = +Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
let maxZ = -Infinity;
bounds.min.set(0, 0, 0);
bounds.max.set(0, 0, 0);
_iterateVertices(vertices, matrices, (v) => {
if (v.x < minX)
minX = v.x;
if (v.y < minY)
minY = v.y;
if (v.z < minZ)
minZ = v.z;
if (v.x > maxX)
maxX = v.x;
if (v.y > maxY)
maxY = v.y;
if (v.z > maxZ)
maxZ = v.z;
});
bounds.min.set(minX, minY, minZ);
bounds.max.set(maxX, maxY, maxZ);
return bounds;
};
const _iterateVertices = (function () {
const vertex = new Vector3();
const matrix = new Matrix4();
return function (vertices, matrices, cb) {
for (let i = 0; i < vertices.length; i++) {
matrix.fromArray(matrices[i]);
for (let j = 0; j < vertices[i].length; j += 3) {
vertex.set(vertices[i][j], vertices[i][j + 1], vertices[i][j + 2]).applyMatrix4(matrix);
cb(vertex);
}
}
};
})();
//# sourceMappingURL=three-to-ammo.js.map