matrix-engine-wgpu
Version:
obj sequence anim +HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.
361 lines (319 loc) • 13.1 kB
JavaScript
import {LOG_FUNNY, degToRad, quaternion_rotation_matrix, radToDeg, scriptManager} from "../engine/utils";
export default class MatrixAmmo {
constructor() {
// THIS PATH IS PATH FROM PUBLIC FINAL FOLDER
scriptManager.LOAD(
"https://maximumroulette.com/apps/megpu/ammo.js",
"ammojs",
undefined,
undefined,
this.init,
);
this.lastRoll = '';
this.presentScore = '';
this.speedUpSimulation = 1;
}
init = () => {
Ammo().then(Ammo => {
// Physics variables
this.dynamicsWorld = null;
this.rigidBodies = [];
this.Ammo = Ammo;
this.lastUpdate = 0
console.log("%c Ammo core loaded.", LOG_FUNNY);
this.initPhysics();
// simulate async
setTimeout(() => dispatchEvent(new CustomEvent('AmmoReady', {})), 100);
});
};
initPhysics() {
let Ammo = this.Ammo;
// Physics configuration
var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(),
dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration),
overlappingPairCache = new Ammo.btDbvtBroadphase(),
solver = new Ammo.btSequentialImpulseConstraintSolver();
this.dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
this.dynamicsWorld.setGravity(new Ammo.btVector3(0, -10, 0));
var groundShape = new Ammo.btBoxShape(new Ammo.btVector3(70, 1, 70)),
groundTransform = new Ammo.btTransform();
groundTransform.setIdentity();
groundTransform.setOrigin(new Ammo.btVector3(0, -4.45, 0));
var mass = 0,
isDynamic = (mass !== 0),
localInertia = new Ammo.btVector3(0, 0, 0);
if(isDynamic) groundShape.calculateLocalInertia(mass, localInertia);
var myMotionState = new Ammo.btDefaultMotionState(groundTransform),
rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, groundShape, localInertia),
body = new Ammo.btRigidBody(rbInfo);
body.name = 'ground';
this.ground = body;
this.dynamicsWorld.addRigidBody(body);
this.detectCollision()
}
addPhysics(MEObject, pOptions) {
if(pOptions.geometry == "Sphere") {
this.addPhysicsSphere(MEObject, pOptions)
} else if(pOptions.geometry == "Cube") {
this.addPhysicsBox(MEObject, pOptions)
}
}
addPhysicsSphere(MEObject, pOptions) {
const FLAGS = {
TEST_NIDZA: 3,
CF_KINEMATIC_OBJECT: 2
}
let Ammo = this.Ammo;
console.log(pOptions.radius + "<<pOptions.radius")
var colShape = new Ammo.btSphereShape(Array.isArray(pOptions.radius) ? pOptions.radius[0] : pOptions.radius),
startTransform = new Ammo.btTransform();
startTransform.setIdentity();
var mass = 1;
var localInertia = new Ammo.btVector3(0, 0, 0);
colShape.calculateLocalInertia(mass, localInertia);
startTransform.setOrigin(new Ammo.btVector3(pOptions.position.x, pOptions.position.y, pOptions.position.z));
var myMotionState = new Ammo.btDefaultMotionState(startTransform),
rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia),
body = new Ammo.btRigidBody(rbInfo);
if(pOptions.mass == 0 && typeof pOptions.state == 'undefined' && typeof pOptions.collide == 'undefined') {
body.setActivationState(2)
body.setCollisionFlags(FLAGS.CF_KINEMATIC_OBJECT);
// console.log('what is pOptions.mass and state is 2 ....', pOptions.mass)
} else if(typeof pOptions.collide != 'undefined' && pOptions.collide == false) {
// idea not work for now - eliminate collide effect
body.setActivationState(4)
body.setCollisionFlags(FLAGS.TEST_NIDZA);
} else {
body.setActivationState(4)
}
body.name = pOptions.name;
MEObject.itIsPhysicsBody = true;
body.MEObject = MEObject;
this.dynamicsWorld.addRigidBody(body);
this.rigidBodies.push(body);
return body;
}
addPhysicsBox(MEObject, pOptions) {
const FLAGS = {
TEST_NIDZA: 3,
CF_KINEMATIC_OBJECT: 2
}
let Ammo = this.Ammo;
// improve this - scale by comp
var colShape = new Ammo.btBoxShape(new Ammo.btVector3(pOptions.scale[0], pOptions.scale[1], pOptions.scale[2])),
startTransform = new Ammo.btTransform();
startTransform.setIdentity();
var mass = pOptions.mass;
var localInertia = new Ammo.btVector3(0, 0, 0);
colShape.calculateLocalInertia(mass, localInertia);
startTransform.setOrigin(new Ammo.btVector3(pOptions.position.x, pOptions.position.y, pOptions.position.z));
// console.log('startTransform.setRotation', startTransform.setRotation)
var t = startTransform.getRotation()
t.setX(degToRad(pOptions.rotation.x))
t.setY(degToRad(pOptions.rotation.y))
t.setZ(degToRad(pOptions.rotation.z))
startTransform.setRotation(t)
var myMotionState = new Ammo.btDefaultMotionState(startTransform),
rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia),
body = new Ammo.btRigidBody(rbInfo);
if(pOptions.mass == 0 && typeof pOptions.state == 'undefined' && typeof pOptions.collide == 'undefined') {
body.setActivationState(2)
body.setCollisionFlags(FLAGS.CF_KINEMATIC_OBJECT);
// console.log('what is pOptions.mass and state is 2 ....', pOptions.mass)
} else if(typeof pOptions.collide != 'undefined' && pOptions.collide == false) {
// idea not work for now - eliminate collide effect
body.setActivationState(4)
body.setCollisionFlags(FLAGS.TEST_NIDZA);
} else {
body.setActivationState(4)
}
body.name = pOptions.name;
MEObject.itIsPhysicsBody = true;
body.MEObject = MEObject;
this.dynamicsWorld.addRigidBody(body);
this.rigidBodies.push(body);
return body;
}
setBodyVelocity(body, x, y, z) {
var tbv30 = new Ammo.btVector3();
tbv30.setValue(x, y, z);
body.setLinearVelocity(tbv30);
}
setKinematicTransform(body, x, y, z, rx, ry, rz) {
if(typeof rx == 'undefined') {var rx = 0;}
if(typeof ry == 'undefined') {var ry = 0;}
if(typeof rz == 'undefined') {var rz = 0;}
let pos = new Ammo.btVector3();
// let quat = new Ammo.btQuaternion();
pos = body.getWorldTransform().getOrigin();
let localRot = body.getWorldTransform().getRotation();
// console.log('pre pos x:', pos.x(), " y : ", pos.y(), " z:", pos.z())
pos.setX(pos.x() + x)
pos.setY(pos.y() + y)
pos.setZ(pos.z() + z)
localRot.setX(rx)
localRot.setY(ry)
localRot.setZ(rz)
let physicsBody = body;
let ms = physicsBody.getMotionState();
if(ms) {
var tmpTrans = new Ammo.btTransform();
tmpTrans.setIdentity();
tmpTrans.setOrigin(pos);
tmpTrans.setRotation(localRot);
ms.setWorldTransform(tmpTrans);
}
}
getBodyByName(name) {
var b = null;
this.rigidBodies.forEach((item, index, array) => {
if(item.name == name) {
b = array[index];
}
});
return b;
}
getNameByBody(body) {
var b = null;
this.rigidBodies.forEach((item, index, array) => {
if(item.kB == body.kB) {
b = array[index].name;
}
});
return b;
}
deactivatePhysics(body) {
const CF_KINEMATIC_OBJECT = 2;
const DISABLE_DEACTIVATION = 4;
// 1. Remove from world
this.dynamicsWorld.removeRigidBody(body);
// 2. Set body to kinematic
const flags = body.getCollisionFlags();
body.setCollisionFlags(flags | CF_KINEMATIC_OBJECT);
body.setActivationState(DISABLE_DEACTIVATION); // no auto-wakeup
// 3. Clear motion
const zero = new Ammo.btVector3(0, 0, 0);
body.setLinearVelocity(zero);
body.setAngularVelocity(zero);
// 4. Reset transform to current position (optional — preserves pose)
const currentTransform = body.getWorldTransform();
body.setWorldTransform(currentTransform);
body.getMotionState().setWorldTransform(currentTransform);
// 5. Add back to physics world
this.matrixAmmo.dynamicsWorld.addRigidBody(body);
// 6. Mark it manually (logic flag)
body.isKinematic = true;
}
detectCollision() {
// console.log('override this')
return;
this.lastRoll = '';
this.presentScore = '';
let dispatcher = this.dynamicsWorld.getDispatcher();
let numManifolds = dispatcher.getNumManifolds();
for(let i = 0;i < numManifolds;i++) {
let contactManifold = dispatcher.getManifoldByIndexInternal(i);
// let numContacts = contactManifold.getNumContacts();
// this.rigidBodies.forEach((item) => {
// if(item.kB == contactManifold.getBody0().kB) {
// // console.log('Detected body0 =', item.name)
// }
// if(item.kB == contactManifold.getBody1().kB) {
// // console.log('Detected body1 =', item.name)
// }
// })
if(this.ground.kB == contactManifold.getBody0().kB &&
this.getNameByBody(contactManifold.getBody1()) == 'CubePhysics1') {
// console.log(this.ground ,'GROUND IS IN CONTACT WHO IS BODY1 ', contactManifold.getBody1())
// console.log('GROUND IS IN CONTACT WHO IS BODY1 getNameByBody ', this.getNameByBody(contactManifold.getBody1()))
// CHECK ROTATION
var testR = contactManifold.getBody1().getWorldTransform().getRotation();
if(Math.abs(testR.y()) < 0.00001) {
this.lastRoll += " 4 +";
this.presentScore += 4;
dispatchEvent(new CustomEvent('dice-1', {}));
}
if(Math.abs(testR.x()) < 0.00001) {
this.lastRoll += " 3 +";
this.presentScore += 3;
dispatchEvent(new CustomEvent('dice-4', {}));
}
if(testR.x().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
this.lastRoll += " 2 +";
this.presentScore += 2;
dispatchEvent(new CustomEvent('dice-6', {}));
}
if(testR.x().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
this.lastRoll += " 1 +";
this.presentScore += 1;
dispatchEvent(new CustomEvent('dice-2', {}));
}
if(testR.z().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
this.lastRoll += " 6 +";
this.presentScore += 6;
dispatchEvent(new CustomEvent('dice-5', {}));
}
if(testR.z().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
this.lastRoll += " 5 +";
this.presentScore += 5;
dispatchEvent(new CustomEvent('dice-3', {}));
}
console.log('this.lastRoll = ', this.lastRoll, ' presentScore = ', this.presentScore)
}
}
}
updatePhysics() {
if(typeof Ammo === 'undefined') return;
const trans = new Ammo.btTransform();
const transform = new Ammo.btTransform();
this.rigidBodies.forEach(function(body) {
if(body.isKinematic) {
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(
body.MEObject.position.x,
body.MEObject.position.y,
body.MEObject.position.z
));
const quat = new Ammo.btQuaternion();
quat.setRotation(
new Ammo.btVector3(
body.MEObject.rotation.axis.x,
body.MEObject.rotation.axis.y,
body.MEObject.rotation.axis.z
),
degToRad(body.MEObject.rotation.angle)
);
transform.setRotation(quat);
body.setWorldTransform(transform);
const ms = body.getMotionState();
if(ms) ms.setWorldTransform(transform);
}
});
Ammo.destroy(transform);
// Step simulation AFTER setting kinematic transforms
const timeStep = 1 / 60;
const maxSubSteps = 10;
for(let i = 0;i < this.speedUpSimulation;i++) {
this.dynamicsWorld.stepSimulation(timeStep, maxSubSteps);
}
this.rigidBodies.forEach(function(body) {
if(!body.isKinematic && body.getMotionState()) {
body.getMotionState().getWorldTransform(trans);
const _x = +trans.getOrigin().x().toFixed(2);
const _y = +trans.getOrigin().y().toFixed(2);
const _z = +trans.getOrigin().z().toFixed(2);
body.MEObject.position.setPosition(_x, _y, _z);
const rot = trans.getRotation();
const rotAxis = rot.getAxis();
rot.normalize();
body.MEObject.rotation.axis.x = rotAxis.x();
body.MEObject.rotation.axis.y = rotAxis.y();
body.MEObject.rotation.axis.z = rotAxis.z();
body.MEObject.rotation.matrixRotation = quaternion_rotation_matrix(rot);
body.MEObject.rotation.angle = radToDeg(parseFloat(rot.getAngle().toFixed(2)));
}
});
Ammo.destroy(trans);
this.detectCollision();
}
}