cannon-es-debugger
Version:
Wireframe debugger for use with cannon-es https://github.com/pmndrs/cannon-es
303 lines (244 loc) • 8.17 kB
JavaScript
'use strict';
var cannonEs = require('cannon-es');
var three = require('three');
function CannonDebugger(scene, world, _temp) {
let {
color = 0x00ff00,
scale = 1,
onInit,
onUpdate
} = _temp === void 0 ? {} : _temp;
const _meshes = [];
const _material = new three.MeshBasicMaterial({
color: color != null ? color : 0x00ff00,
wireframe: true
});
const _tempVec0 = new cannonEs.Vec3();
const _tempVec1 = new cannonEs.Vec3();
const _tempVec2 = new cannonEs.Vec3();
const _tempQuat0 = new cannonEs.Quaternion();
const _sphereGeometry = new three.SphereGeometry(1);
const _boxGeometry = new three.BoxGeometry(1, 1, 1);
const _planeGeometry = new three.PlaneGeometry(10, 10, 10, 10); // Move the planeGeometry forward a little bit to prevent z-fighting
_planeGeometry.translate(0, 0, 0.0001);
function createConvexPolyhedronGeometry(shape) {
const geometry = new three.BufferGeometry(); // Add vertices
const positions = [];
for (let i = 0; i < shape.vertices.length; i++) {
const vertex = shape.vertices[i];
positions.push(vertex.x, vertex.y, vertex.z);
}
geometry.setAttribute('position', new three.Float32BufferAttribute(positions, 3)); // Add faces
const indices = [];
for (let i = 0; i < shape.faces.length; i++) {
const face = shape.faces[i];
const a = face[0];
for (let j = 1; j < face.length - 1; j++) {
const b = face[j];
const c = face[j + 1];
indices.push(a, b, c);
}
}
geometry.setIndex(indices);
geometry.computeBoundingSphere();
geometry.computeVertexNormals();
return geometry;
}
function createTrimeshGeometry(shape) {
const geometry = new three.BufferGeometry();
const positions = [];
const v0 = _tempVec0;
const v1 = _tempVec1;
const v2 = _tempVec2;
for (let i = 0; i < shape.indices.length / 3; i++) {
shape.getTriangleVertices(i, v0, v1, v2);
positions.push(v0.x, v0.y, v0.z);
positions.push(v1.x, v1.y, v1.z);
positions.push(v2.x, v2.y, v2.z);
}
geometry.setAttribute('position', new three.Float32BufferAttribute(positions, 3));
geometry.computeBoundingSphere();
geometry.computeVertexNormals();
return geometry;
}
function createHeightfieldGeometry(shape) {
const geometry = new three.BufferGeometry();
const s = shape.elementSize || 1; // assumes square heightfield, else i*x, j*y
const positions = shape.data.flatMap((row, i) => row.flatMap((z, j) => [i * s, j * s, z]));
const indices = [];
for (let xi = 0; xi < shape.data.length - 1; xi++) {
for (let yi = 0; yi < shape.data[xi].length - 1; yi++) {
const stride = shape.data[xi].length;
const index = xi * stride + yi;
indices.push(index + 1, index + stride, index + stride + 1);
indices.push(index + stride, index + 1, index);
}
}
geometry.setIndex(indices);
geometry.setAttribute('position', new three.Float32BufferAttribute(positions, 3));
geometry.computeBoundingSphere();
geometry.computeVertexNormals();
return geometry;
}
function createMesh(shape) {
let mesh = new three.Mesh();
const {
SPHERE,
BOX,
PLANE,
CYLINDER,
CONVEXPOLYHEDRON,
TRIMESH,
HEIGHTFIELD
} = cannonEs.Shape.types;
switch (shape.type) {
case SPHERE:
{
mesh = new three.Mesh(_sphereGeometry, _material);
break;
}
case BOX:
{
mesh = new three.Mesh(_boxGeometry, _material);
break;
}
case PLANE:
{
mesh = new three.Mesh(_planeGeometry, _material);
break;
}
case CYLINDER:
{
const geometry = new three.CylinderGeometry(shape.radiusTop, shape.radiusBottom, shape.height, shape.numSegments);
mesh = new three.Mesh(geometry, _material);
shape.geometryId = geometry.id;
break;
}
case CONVEXPOLYHEDRON:
{
const geometry = createConvexPolyhedronGeometry(shape);
mesh = new three.Mesh(geometry, _material);
shape.geometryId = geometry.id;
break;
}
case TRIMESH:
{
const geometry = createTrimeshGeometry(shape);
mesh = new three.Mesh(geometry, _material);
shape.geometryId = geometry.id;
break;
}
case HEIGHTFIELD:
{
const geometry = createHeightfieldGeometry(shape);
mesh = new three.Mesh(geometry, _material);
shape.geometryId = geometry.id;
break;
}
}
scene.add(mesh);
return mesh;
}
function scaleMesh(mesh, shape) {
const {
SPHERE,
BOX,
PLANE,
CYLINDER,
CONVEXPOLYHEDRON,
TRIMESH,
HEIGHTFIELD
} = cannonEs.Shape.types;
switch (shape.type) {
case SPHERE:
{
const {
radius
} = shape;
mesh.scale.set(radius * scale, radius * scale, radius * scale);
break;
}
case BOX:
{
mesh.scale.copy(shape.halfExtents);
mesh.scale.multiplyScalar(2 * scale);
break;
}
case PLANE:
{
break;
}
case CYLINDER:
{
mesh.scale.set(1 * scale, 1 * scale, 1 * scale);
break;
}
case CONVEXPOLYHEDRON:
{
mesh.scale.set(1 * scale, 1 * scale, 1 * scale);
break;
}
case TRIMESH:
{
mesh.scale.copy(shape.scale).multiplyScalar(scale);
break;
}
case HEIGHTFIELD:
{
mesh.scale.set(1 * scale, 1 * scale, 1 * scale);
break;
}
}
}
function typeMatch(mesh, shape) {
if (!mesh) return false;
const {
geometry
} = mesh;
return geometry instanceof three.SphereGeometry && shape.type === cannonEs.Shape.types.SPHERE || geometry instanceof three.BoxGeometry && shape.type === cannonEs.Shape.types.BOX || geometry instanceof three.PlaneGeometry && shape.type === cannonEs.Shape.types.PLANE || geometry.id === shape.geometryId && shape.type === cannonEs.Shape.types.CYLINDER || geometry.id === shape.geometryId && shape.type === cannonEs.Shape.types.CONVEXPOLYHEDRON || geometry.id === shape.geometryId && shape.type === cannonEs.Shape.types.TRIMESH || geometry.id === shape.geometryId && shape.type === cannonEs.Shape.types.HEIGHTFIELD;
}
function updateMesh(index, shape) {
let mesh = _meshes[index];
let didCreateNewMesh = false;
if (!typeMatch(mesh, shape)) {
if (mesh) scene.remove(mesh);
_meshes[index] = mesh = createMesh(shape);
didCreateNewMesh = true;
}
scaleMesh(mesh, shape);
return didCreateNewMesh;
}
function update() {
const meshes = _meshes;
const shapeWorldPosition = _tempVec0;
const shapeWorldQuaternion = _tempQuat0;
let meshIndex = 0;
for (const body of world.bodies) {
for (let i = 0; i !== body.shapes.length; i++) {
const shape = body.shapes[i];
const didCreateNewMesh = updateMesh(meshIndex, shape);
const mesh = meshes[meshIndex];
if (mesh) {
// Get world position
body.quaternion.vmult(body.shapeOffsets[i], shapeWorldPosition);
body.position.vadd(shapeWorldPosition, shapeWorldPosition); // Get world quaternion
body.quaternion.mult(body.shapeOrientations[i], shapeWorldQuaternion); // Copy to meshes
mesh.position.copy(shapeWorldPosition);
mesh.quaternion.copy(shapeWorldQuaternion);
if (didCreateNewMesh && onInit instanceof Function) onInit(body, mesh, shape);
if (!didCreateNewMesh && onUpdate instanceof Function) onUpdate(body, mesh, shape);
}
meshIndex++;
}
}
for (let i = meshIndex; i < meshes.length; i++) {
const mesh = meshes[i];
if (mesh) scene.remove(mesh);
}
meshes.length = meshIndex;
}
return {
update
};
}
module.exports = CannonDebugger;