mirror of
https://github.com/troisjs/trois.git
synced 2024-11-24 04:12:02 +08:00
simple cannonjs component
This commit is contained in:
parent
f6ed7bac92
commit
c373adc7df
63
src/components/physics/CannonWorld.js
Normal file
63
src/components/physics/CannonWorld.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import useCannon from './useCannon.js';
|
||||||
|
// import { bindProp } from '../../tools';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
inject: ['three', 'scene', 'rendererComponent'],
|
||||||
|
props: {
|
||||||
|
gravity: { type: Object, default: () => ({ x: 0, y: 0, z: -9.82 }) },
|
||||||
|
broadphase: { type: String },
|
||||||
|
onBeforeStep: Function,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this._parent = this.getParent();
|
||||||
|
if (!this._parent) console.error('Missing parent (Scene, Group...)');
|
||||||
|
|
||||||
|
this.cannon = useCannon({ gravity: this.gravity, broadphase: this.broadphase });
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.rendererComponent.onBeforeRender(this.step);
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.rendererComponent.offBeforeRender(this.step);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
step() {
|
||||||
|
this.onBeforeStep?.(this.cannon);
|
||||||
|
this.cannon.step();
|
||||||
|
},
|
||||||
|
add(o) {
|
||||||
|
this.addToParent(o);
|
||||||
|
this.cannon.addMesh(o);
|
||||||
|
},
|
||||||
|
remove(o) {
|
||||||
|
this.removeFromParent(o);
|
||||||
|
this.cannon.removeMesh(o);
|
||||||
|
},
|
||||||
|
getParent() {
|
||||||
|
let parent = this.$parent;
|
||||||
|
while (parent) {
|
||||||
|
if (parent.add) return parent;
|
||||||
|
parent = parent.$parent;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
addToParent(o) {
|
||||||
|
if (this._parent) {
|
||||||
|
this._parent.add(o);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
removeFromParent(o) {
|
||||||
|
if (this._parent) {
|
||||||
|
this._parent.remove(o);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return this.$slots.default ? this.$slots.default() : [];
|
||||||
|
},
|
||||||
|
});
|
194
src/components/physics/useCannon.js
Normal file
194
src/components/physics/useCannon.js
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import {
|
||||||
|
Box, Cylinder, Plane, Sphere,
|
||||||
|
Body, World,
|
||||||
|
SAPBroadphase,
|
||||||
|
Quaternion, Vec3,
|
||||||
|
} from 'cannon';
|
||||||
|
|
||||||
|
export default function useCannon(options) {
|
||||||
|
const {
|
||||||
|
broadphase = null,
|
||||||
|
gravity = new Vec3(0, 0, -9.82),
|
||||||
|
// solverIterations = 10,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const world = new World();
|
||||||
|
world.gravity.set(gravity.x, gravity.y, gravity.z);
|
||||||
|
|
||||||
|
if (broadphase === 'sap') {
|
||||||
|
world.broadphase = new SAPBroadphase(world);
|
||||||
|
}
|
||||||
|
// world.solver.iterations = solverIterations;
|
||||||
|
|
||||||
|
const meshes = [];
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
world,
|
||||||
|
addMesh,
|
||||||
|
removeMesh,
|
||||||
|
step,
|
||||||
|
};
|
||||||
|
|
||||||
|
function addMesh(mesh) {
|
||||||
|
const shape = getShape(mesh.geometry);
|
||||||
|
if (shape) {
|
||||||
|
if (mesh.isInstancedMesh) {
|
||||||
|
handleInstancedMesh(mesh, shape);
|
||||||
|
} else if (mesh.isMesh) {
|
||||||
|
handleMesh(mesh, shape);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Unhandled Mesh geometry ${mesh.geometry.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMesh(mesh) {
|
||||||
|
const index = meshes.indexOf(mesh);
|
||||||
|
if (index !== -1) {
|
||||||
|
meshes.splice(index, 1);
|
||||||
|
}
|
||||||
|
if (mesh.userData.bodies) {
|
||||||
|
mesh.userData.bodies.forEach(body => {
|
||||||
|
world.removeBody(body);
|
||||||
|
});
|
||||||
|
mesh.userData.bodies = [];
|
||||||
|
}
|
||||||
|
if (mesh.userData.body) {
|
||||||
|
world.removeBody(mesh.userData.body);
|
||||||
|
delete mesh.userData.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function step() {
|
||||||
|
world.step(1 / 60);
|
||||||
|
for (let i = 0, l = meshes.length; i < l; i++) {
|
||||||
|
const mesh = meshes[i];
|
||||||
|
if (mesh.isInstancedMesh) {
|
||||||
|
const iMatrix = mesh.instanceMatrix.array;
|
||||||
|
const bodies = mesh.userData.bodies;
|
||||||
|
for (let j = 0; j < bodies.length; j++) {
|
||||||
|
const body = bodies[j];
|
||||||
|
compose(body.position, body.quaternion, mesh.userData.scales[j], iMatrix, j * 16);
|
||||||
|
}
|
||||||
|
mesh.instanceMatrix.needsUpdate = true;
|
||||||
|
} else if (mesh.isMesh) {
|
||||||
|
mesh.position.copy(mesh.userData.body.position);
|
||||||
|
mesh.quaternion.copy(mesh.userData.body.quaternion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getShape(geometry) {
|
||||||
|
const parameters = geometry.parameters;
|
||||||
|
switch (geometry.type) {
|
||||||
|
case 'BoxGeometry':
|
||||||
|
return new Box(new Vec3(
|
||||||
|
parameters.width / 2,
|
||||||
|
parameters.height / 2,
|
||||||
|
parameters.depth / 2
|
||||||
|
));
|
||||||
|
|
||||||
|
case 'PlaneGeometry':
|
||||||
|
return new Plane();
|
||||||
|
|
||||||
|
case 'SphereGeometry':
|
||||||
|
return new Sphere(parameters.radius);
|
||||||
|
|
||||||
|
case 'CylinderGeometry':
|
||||||
|
return new Cylinder(parameters.radiusTop, parameters.radiusBottom, parameters.height, parameters.radialSegments);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleMesh(mesh, shape) {
|
||||||
|
const position = new Vec3();
|
||||||
|
position.copy(mesh.position);
|
||||||
|
|
||||||
|
const quaternion = new Quaternion();
|
||||||
|
quaternion.copy(mesh.quaternion);
|
||||||
|
|
||||||
|
const mass = mesh.userData.mass ? mesh.userData.mass : 0;
|
||||||
|
const damping = mesh.userData.damping ? mesh.userData.damping : 0.01;
|
||||||
|
|
||||||
|
const body = new Body({ shape, position, quaternion, mass, linearDamping: damping, angularDamping: damping });
|
||||||
|
world.addBody(body);
|
||||||
|
|
||||||
|
mesh.userData.body = body;
|
||||||
|
if (mesh.userData.mass > 0) {
|
||||||
|
meshes.push(mesh);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleInstancedMesh(mesh, shape) {
|
||||||
|
const iMatrix = mesh.instanceMatrix.array;
|
||||||
|
const bodies = [];
|
||||||
|
for (let i = 0; i < mesh.count; i++) {
|
||||||
|
const index = i * 16;
|
||||||
|
|
||||||
|
const position = new Vec3();
|
||||||
|
position.set(iMatrix[index + 12], iMatrix[index + 13], iMatrix[index + 14]);
|
||||||
|
|
||||||
|
// handle instance scale
|
||||||
|
let scale = 1;
|
||||||
|
if (mesh.userData.scales?.[i]) scale = mesh.userData.scales?.[i];
|
||||||
|
const geoParams = mesh.geometry.parameters;
|
||||||
|
if (mesh.geometry.type === 'SphereGeometry') {
|
||||||
|
shape = new Sphere(scale * geoParams.radius);
|
||||||
|
} else if (mesh.geometry.type === 'BoxGeometry') {
|
||||||
|
shape = new Box(new Vec3(
|
||||||
|
scale * geoParams.width / 2,
|
||||||
|
scale * geoParams.height / 2,
|
||||||
|
scale * geoParams.depth / 2
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
console.warn(`Unhandled InstancedMesh geometry ${mesh.geometry.type}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mass = 0;
|
||||||
|
if (mesh.userData.masses?.[i]) mass = mesh.userData.masses[i];
|
||||||
|
else if (mesh.userData.mass) mass = mesh.userData.mass;
|
||||||
|
|
||||||
|
let damping = 0.01;
|
||||||
|
if (mesh.userData.dampings?.[i]) damping = mesh.userData.dampings?.[i];
|
||||||
|
else if (mesh.userData.damping) damping = mesh.userData.damping;
|
||||||
|
|
||||||
|
const body = new Body({ shape, position, mass, linearDamping: damping, angularDamping: damping });
|
||||||
|
world.addBody(body);
|
||||||
|
bodies.push(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.userData.bodies = bodies;
|
||||||
|
meshes.push(mesh);
|
||||||
|
};
|
||||||
|
|
||||||
|
function compose(position, quaternion, scale, iMatrix, index) {
|
||||||
|
const x = quaternion.x, y = quaternion.y, z = quaternion.z, w = quaternion.w;
|
||||||
|
const x2 = x + x, y2 = y + y, z2 = z + z;
|
||||||
|
const xx = x * x2, xy = x * y2, xz = x * z2;
|
||||||
|
const yy = y * y2, yz = y * z2, zz = z * z2;
|
||||||
|
const wx = w * x2, wy = w * y2, wz = w * z2;
|
||||||
|
|
||||||
|
iMatrix[index + 0] = (1 - (yy + zz)) * scale;
|
||||||
|
iMatrix[index + 1] = (xy + wz) * scale;
|
||||||
|
iMatrix[index + 2] = (xz - wy) * scale;
|
||||||
|
iMatrix[index + 3] = 0;
|
||||||
|
|
||||||
|
iMatrix[index + 4] = (xy - wz) * scale;
|
||||||
|
iMatrix[index + 5] = (1 - (xx + zz)) * scale;
|
||||||
|
iMatrix[index + 6] = (yz + wx) * scale;
|
||||||
|
iMatrix[index + 7] = 0;
|
||||||
|
|
||||||
|
iMatrix[index + 8] = (xz + wy) * scale;
|
||||||
|
iMatrix[index + 9] = (yz - wx) * scale;
|
||||||
|
iMatrix[index + 10] = (1 - (xx + yy)) * scale;
|
||||||
|
iMatrix[index + 11] = 0;
|
||||||
|
|
||||||
|
iMatrix[index + 12] = position.x;
|
||||||
|
iMatrix[index + 13] = position.y;
|
||||||
|
iMatrix[index + 14] = position.z;
|
||||||
|
iMatrix[index + 15] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user