I’m introducing myself to Three.js and Ammo.js and I’m having some issues writing functions.
So, I’m sure I’m doing something novice here, but I’m at a bit of a loss.
When I’m calling Ammo constructors within functions, I’m having to pass Ammo as an arguament to the function - is this expected behaviour?
If I don’t include it, I get an error stating that “X is not a valid constructor”
Any pointers are greatly appreciated!
I’m only working in the main.js file as below:
// Style import
import './style.css'
// THREE import
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// AMMO import
import Ammo from './js/ammo'
// Globals
var physicsWorld = undefined;
var clock = new THREE.Clock();
var scene = undefined;
var camera = undefined;
var renderer = undefined;
var rigidBodyList = new Array();
var tmpTransformation = undefined;
// ====================== FUNCTIONS =======================
function QuaternionFromAxis(Ammo, x, y, z, a) {
// Calculate sin(theta/2)
let factor = Math.sin(a/2.0);
// Calculate x, y and z of quaternion
let Qx = x*factor;
let Qy = y*factor;
let Qz = z*factor;
let Qw = Math.cos(a/2.0);
let quaternion = new Ammo.btQuaternion(Qx, Qy, Qz, Qw);
return quaternion
}
function randomColour() {
let colour = Math.floor(Math.random()*(255 + 1)) * 0xffffff;
return colour
}
// ======================= THREE.JS =======================
function setupGraphicsWorld() {
// Clock setup
clock = new THREE.Clock();
// Scene setup
scene = new THREE.Scene();
// Camera setup
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
let CameraPosition = 30;
camera.position.set(CameraPosition, CameraPosition/2, CameraPosition);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Renderer setup
renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#bg'),
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Set background colour
renderer.setClearColor( 0xFEE8FF, 1);
// Set tone mapping (HDR)
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
// Orbit controls setup
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// Grid Helper setup
const gridHelper = new THREE.GridHelper(20, 50);
scene.add(gridHelper);
// Ambient Light setup
const light = new THREE.AmbientLight(0x404040, 0.2); // Soft white light
scene.add(light);
// Directional light setup
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(1, 0.9, 0.4);
scene.add(directionalLight);
// Point Light
const spotlight = new THREE.PointLight(0x404040, 1); // Declare spotlight
spotlight.position.set(10, 10, -10); // Set spotlight position
const spotlightHelper = new THREE.PointLightHelper(spotlight); // Declare spotlight helper
scene.add(spotlight, spotlightHelper); // Add spotlight and helper to scene
}
// ======================= AMMO.JS ========================
function setupPhysicsWorld(Ammo, g) {
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
let overlappingPairCache = new Ammo.btDbvtBroadphase();
let solver = new Ammo.btSequentialImpulseConstraintSolver();
physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
physicsWorld.setGravity(new Ammo.btVector3(0, g, 0));
}
// ======================== OBJECTS ========================
function createCube(Ammo, scale, position, mass, rot_quaternion, colour) {
let quaternion = undefined;
if(rot_quaternion == null) {quaternion = {x:0, y:0, z:0, w:1}}
else {quaternion = rot_quaternion}
// ------ Graphics World - Three.JS ------
let newcube =
new THREE.Mesh(new THREE.BoxBufferGeometry(scale.x, scale.y, scale.z),
new THREE.MeshPhongMaterial({color: colour}));
newcube.position.set(position.x, position.y, position.z);
scene.add(newcube);
// ------ Physics World - Ammo.js ------
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
transform.setRotation(new Ammo.btQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w));
let defaultMotionState = new Ammo.btDefaultMotionState(transform);
let structColShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x*0.5, scale.y*0.5, scale.z*0.5));
structColShape.setMargin(0.05);
let localInertia = new Ammo.btVector3(0, 0, 0);
structColShape.calculateLocalInertia(mass, localInertia);
let RBodyInfo = new Ammo.btRigidBodyConstructionInfo(mass, defaultMotionState, structColShape, localInertia);
let RBody = new Ammo.btRigidBody(RBodyInfo);
physicsWorld.addRigidBody(RBody);
newcube.userData.physicsBody = RBody;
rigidBodyList.push(newcube);
}
function createSphere(Ammo, radius, position, mass, rot_quaternion, colour) {
let quaternion = undefined;
if(rot_quaternion == null) {quaternion = {x:0, y:0, z:0, w:1}}
else {quaternion = rot_quaternion}
// ------ Graphics World - Three.JS ------
let newsphere =
new THREE.Mesh(new THREE.SphereBufferGeometry(radius, 30, 30),
new THREE.MeshPhongMaterial({color: colour}));
newsphere.position.set(position.x, position.y, position.z);
scene.add(newsphere);
// ------ Physics World - Ammo.js ------
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
transform.setRotation(new Ammo.btQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w));
let defaultMotionState = new Ammo.btDefaultMotionState(transform);
let structColShape = new Ammo.btSphereShape(radius);
structColShape.setMargin(0.05);
let localInertia = new Ammo.btVector3(0, 0, 0);
structColShape.calculateLocalInertia(mass, localInertia);
let RBodyInfo = new Ammo.btRigidBodyConstructionInfo(mass, defaultMotionState, structColShape, localInertia);
let RBody = new Ammo.btRigidBody(RBodyInfo);
physicsWorld.addRigidBody(RBody);
newsphere.userData.physicsBody = RBody;
rigidBodyList.push(newsphere);
}
function launchSphere(Ammo, radius, position, mass, rot_quaternion, velocity, colour) {
let quaternion = undefined;
if(rot_quaternion == null) {quaternion = {x:0, y:0, z:0, w:1}}
else {quaternion = rot_quaternion}
// ------ Graphics World - Three.JS ------
let newsphere =
new THREE.Mesh(new THREE.SphereBufferGeometry(radius, 30, 30),
new THREE.MeshPhongMaterial({color: colour}));
newsphere.position.set(position.x, position.y, position.z);
scene.add(newsphere);
// ------ Physics World - Ammo.js ------
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
transform.setRotation(new Ammo.btQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w));
let defaultMotionState = new Ammo.btDefaultMotionState(transform);
let structColShape = new Ammo.btSphereShape(radius);
structColShape.setMargin(0.05);
let localInertia = new Ammo.btVector3(0, 0, 0);
structColShape.calculateLocalInertia(mass, localInertia);
let RBodyInfo = new Ammo.btRigidBodyConstructionInfo(mass, defaultMotionState, structColShape, localInertia);
let RBody = new Ammo.btRigidBody(RBodyInfo);
RBody.setLinearVelocity(velocity);
physicsWorld.addRigidBody(RBody);
newsphere.userData.physicsBody = RBody;
rigidBodyList.push(newsphere);
}
// ======================= ANIMATION =======================
// Animation loop
function animate() {
let deltaTime = clock.getDelta();
updatephysicsWorld(deltaTime);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
// Physics loop
function updatephysicsWorld(deltaTime) {
physicsWorld.stepSimulation(deltaTime, 10);
for (let i = 0; i < rigidBodyList.length; i++) {
let Graphics_Obj = rigidBodyList[i];
let Physics_Obj = Graphics_Obj.userData.physicsBody;
let motionState = Physics_Obj.getMotionState();
if (motionState) {
motionState.getWorldTransform(tmpTransformation);
let new_pos = tmpTransformation.getOrigin();
let new_qua = tmpTransformation.getRotation();
Graphics_Obj.position.set(new_pos.x(), new_pos.y(), new_pos.z());
Graphics_Obj.quaternion.set(new_qua.x(), new_qua.y(), new_qua.z(), new_qua.w());
}
}
}
// ========================= DEMOS =========================
function DemoJenga(Ammo) {
// Jenga Block
let offset = 0;
for (let i = 1; i < 20; i++) {
if (i%2 == 0) { // Even
createCube(Ammo, new THREE.Vector3(6, 1, 2), new THREE.Vector3(0, i + offset, -2), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(6, 1, 2), new THREE.Vector3(0, i + offset, 0), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(6, 1, 2), new THREE.Vector3(0, i + offset, 2), 1, null, Math.random() * 0xffffff);
}
else { // Odd
createCube(Ammo, new THREE.Vector3(2, 1, 6), new THREE.Vector3(-2, i + offset, 0), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(2, 1, 6), new THREE.Vector3(0, i + offset, 0), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(2, 1, 6), new THREE.Vector3(2, i + offset, 0), 1, null, Math.random() * 0xffffff);
}
}
// Launch sphere - Ammo, radius, position, mass, rot_quaternion, velocity, colour
launchSphere(Ammo, 2, new THREE.Vector3(30, 10, -30), 10, null, new Ammo.btVector3(-40, 0, 40), Math.random() * 0xffffff);
}
function DemoSpheresAndCubes(Ammo) {
// Falling cubes - Ammo, scale, position, mass, rot_quaternion, colour
createCube(Ammo, new THREE.Vector3(0.4, 0.4, 0.4), new THREE.Vector3(0, 1, 0), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.2, 0.2, 0.2), new THREE.Vector3(1, 3, 0), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.4, 0.4, 0.4), new THREE.Vector3(1, 2, 1), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.6, 0.6, 0.6), new THREE.Vector3(0.5, 4, 2), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.8, 0.8, 0.8), new THREE.Vector3(2.5, 10, 0.5), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.8, 0.8, 0.8), new THREE.Vector3(2, 6, 2.5), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.4, 0.4, 0.4), new THREE.Vector3(2, 10, 2.5), 1, null, Math.random() * 0xffffff);
createCube(Ammo, new THREE.Vector3(0.2, 0.2, 0.2), new THREE.Vector3(2, 20, 2.5), 1, null, Math.random() * 0xffffff);
// Falling spheres - Ammo, radius, position, mass, rot_quaternion, colour
for (let i = 0; i < 100; i++) {
createSphere(Ammo, Math.random(), new THREE.Vector3((Math.random()-0.5)*10, Math.random()*10, (Math.random()-0.5)*10), 1, null, Math.random() * 0xffffff);
}
}
function DemoDoor(Ammo) {
// Frame
// Three.js
let loader = new GLTFLoader();
loader.load('recources/PuertaDeLaLuna.glb',
function (gltf) {
gltf.scene.translateY(3.1);
scene.add(gltf.scene);
},
undefined,
function (error) {
console.error(error);
}
);
}
// ==================== Initialisation =====================
function StartApp(Ammo) {
// Set up physics world
console.log("%cStarting Ammo.JS", "color: lightgreen");
tmpTransformation = new Ammo.btTransform();
setupPhysicsWorld(Ammo, -10);
console.log("%cGravity = " + physicsWorld.getGravity().y(), "color: lightgreen");
// Set up graphics world
console.log("%cStarting Three.JS", "color: lightskyblue");
setupGraphicsWorld();
console.log("%cThree.JS initialised", "color: lightskyblue");
// Declare objects
// Base - Ammo, scale, position, mass, rot_quaternion, colour
createCube(Ammo, new THREE.Vector3(50, 1, 50), new THREE.Vector3(0, 0, 0), 0, null, 0xffffff);
// Demos:
//DemoSpheresAndCubes(Ammo);
//DemoJenga(Ammo);
//DemoDoor(Ammo);
// Start animation loop
animate();
}
// ==================== Initialise App ====================
Ammo().then(StartApp);