I'm using Ammo.js, a direct JavaScript port of C++ Bullet Physics. The unfortunate result being that the documentation is C++, not great reading if your languages are Python and JavaScript.
I have the documentation for Ammo.btCompoundShape
here but can't make sense of it.
I have a working code here where the Bone
instance just falls through the floor, as you'll see. Don't worry about the naming of "Bone", at this stage in development it's just meant to test a compound shape of two blocks.
class RenderEngine {
constructor(gameEngine) {
this.gameEngine = gameEngine
this.gameEngine.clock = new THREE.Clock();
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xbfd1e5);
this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 5000);
this.camera.position.set(0, 30, 70);
this.camera.lookAt(new THREE.Vector3(0, 0, 0));
const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.1);
hemiLight.color.setHSL(0.6, 0.6, 0.6);
hemiLight.groundColor.setHSL(0.1, 1, 0.4);
hemiLight.position.set(0, 50, 0);
this.scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.color.setHSL(0.1, 1, 0.95);
dirLight.position.set(-1, 1.75, 1);
dirLight.position.multiplyScalar(100);
this.scene.add(dirLight);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
const d = 50;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.camera.far = 13500;
this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setClearColor(0xbfd1e5);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(this.renderer.domElement);
this.renderer.shadowMap.enabled = true;
}
renderFrame() {
this.renderer.render(this.scene, this.camera)
}
}
class PhysicsEngine {
constructor(gameEngine, physicsEngine) {
this.gameEngine = gameEngine
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(),
dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration),
overlappingPairCache = new Ammo.btDbvtBroadphase(),
solver = new Ammo.btSequentialImpulseConstraintSolver();
this.tmpTrans = new Ammo.btTransform();
this.physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
this.physicsWorld.setGravity(new Ammo.btVector3(0, -10, 0));
}
updateFrame() {
this.physicsWorld.stepSimulation(this.gameEngine.clock.getDelta(), 10);
this.gameEngine.objects.forEach(object => {
const ms = object.ammo.getMotionState()
if (ms) {
ms.getWorldTransform(this.tmpTrans)
const p = this.tmpTrans.getOrigin()
const q = this.tmpTrans.getRotation()
object.mesh.position.set(p.x(), p.y(), p.z())
object.mesh.quaternion.set(q.x(), q.y(), q.z(), q.w())
}
})
}
}
class GameEngine {
constructor(renderEngine, physicsEngine) {
this.objects = []
this.renderEngine = new RenderEngine(this, renderEngine)
this.physicsEngine = new PhysicsEngine(this, physicsEngine)
}
run() {
this.physicsEngine.updateFrame()
this.renderEngine.renderFrame()
requestAnimationFrame(() => {
this.run()
});
}
add(object) {
this.objects.push(object)
return this.objects.length - 1
}
remove(objectIndex) {
this.objects[objectIndex] = false
}
}
class Box {
constructor(gameEngine, properties) {
this.gameEngine = gameEngine
this._initPhysics_(properties)
this._initRendering_(properties)
this.id = gameEngine.add(this)
}
_initPhysics_(properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const mass = properties.mass
const group = properties.group
const interactionGroup = properties.interactionGroup
const physicsWorld = this.gameEngine.physicsEngine.physicsWorld
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z))
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w))
const motionState = new Ammo.btDefaultMotionState(transform)
const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5))
colShape.setMargin(0.05)
const localInertia = new Ammo.btVector3(0, 0, 0)
colShape.calculateLocalInertia(mass, localInertia)
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia)
const body = new Ammo.btRigidBody(rbInfo)
physicsWorld.addRigidBody(body, group, interactionGroup)
this.ammo = body
}
_initRendering_(properties) {
const pos = properties.pos
const scale = properties.scale
const color = properties.color
this.mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(), new THREE.MeshPhongMaterial({
color
}))
this.mesh.position.set(pos.x, pos.y, pos.z)
this.mesh.scale.set(scale.x, scale.y, scale.z)
this.mesh.castShadow = true
this.mesh.receiveShadow = true
this.gameEngine.renderEngine.scene.add(this.mesh)
}
}
class Bone {
constructor(gameEngine, properties) {
this.gameEngine = gameEngine
this._initPhysics_(properties)
this._initRendering_(properties)
this.id = gameEngine.add(this)
}
_initPhysics_(properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const mass = properties.mass
const group = properties.group
const interactionGroup = properties.interactionGroup
const physicsWorld = this.gameEngine.physicsEngine.physicsWorld
const compoundShape = new Ammo.btCompoundShape()
this._addSection_(compoundShape, {
pos,
quat,
scale,
offset: {
x: 0,
y: 0,
z: 0
},
rotation: {
x: 0,
y: 0,
z: 0,
w: 0
}
})
this._addSection_(compoundShape, {
pos,
quat,
scale,
offset: {
x: 0,
y: 0,
z: 0
},
rotation: {
x: 0,
y: 0,
z: 0,
w: 0
}
})
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z))
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w))
const motionState = new Ammo.btDefaultMotionState(transform)
compoundShape.setMargin(0.05)
const localInertia = new Ammo.btVector3(0, 0, 0)
compoundShape.calculateLocalInertia(mass, localInertia)
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, compoundShape, localInertia)
const body = new Ammo.btRigidBody(rbInfo)
physicsWorld.addRigidBody(body, group, interactionGroup)
this.ammo = body
}
_initRendering_(properties) {
const pos = properties.pos
const scale = properties.scale
const color = properties.color
this.mesh = new THREE.Mesh(new THREE.BoxBufferGeometry(), new THREE.MeshPhongMaterial({
color
}))
this.mesh.position.set(pos.x, pos.y, pos.z)
this.mesh.scale.set(scale.x, scale.y, scale.z)
this.mesh.castShadow = true
this.mesh.receiveShadow = true
this.gameEngine.renderEngine.scene.add(this.mesh)
}
_addSection_(compoundShape, properties) {
const pos = properties.pos
const quat = properties.quat
const scale = properties.scale
const offset = properties.offset
const rotation = properties.rotation
const transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(pos.x + offset.x, pos.y + offset.y, pos.z + offset.z))
transform.setRotation(new Ammo.btQuaternion(quat.x + rotation.x, quat.y + rotation.y, quat.z + rotation.z, quat.w + rotation.w))
const motionState = new Ammo.btDefaultMotionState(transform)
const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5))
compoundShape.addChildShape(transform, colShape)
}
}
Ammo().then((Ammo) => {
const gameEngine = new GameEngine(THREE, Ammo)
const plane = new Box(gameEngine, {
pos: {
x: 0,
y: 0,
z: 0
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 50,
y: 2,
z: 50
},
mass: 0,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const box1 = new Box(gameEngine, {
pos: {
x: 0,
y: 5,
z: 0
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const box2 = new Box(gameEngine, {
pos: {
x: 0.75,
y: 8,
z: 0.75
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
const bone1 = new Bone(gameEngine, {
pos: {
x: -0.75,
y: 10,
z: -0.75
},
quat: {
x: 0,
y: 0,
z: 0,
w: 1
},
scale: {
x: 2,
y: 2,
z: 2
},
mass: 1,
group: 1,
interactionGroup: 1,
color: 0xa0afa4
})
console.log("gameEngine", gameEngine)
gameEngine.run()
})
canvas, body, html {
margin: 0px;
padding: 0px;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
<script src="https://cdn.babylonjs.com/ammo.js"></script>
The two Box
instances land on the floor (plane
), bone1
falls through. I assume I did something wrong with the Ammo.btCompoundShape. There are no errors. What's the correct way?
from How do I use Ammo.btCompoundShape (JavaScript port of Bullet Physics)?
No comments:
Post a Comment