IT IS ALIVE and rolls and spins
In the previous write-up
I've started an ambitious project with drones flying anywhere i want, as long as "anywhere i want" is written in THREE.js.
I got SOMEWHERE using oimo.js rigid body physics simulation, applying lift force in update() loop, using angular velocity on rigid body to perform yaw spins.
The thing i couldn't get right was rotation around X and Z axes.
See, rigid body physics engines all provide multiple body types, two that you need to know of for getting this article are:
- DYNAMIC (moved with impulses and FORCE VECTORS, collide other bodies as if in "REAL LIFE")
- KINEMATIC (things just go through each other, you set their position and rotation yourself, but collision events are still fired somewhere, just saying HEY you're flying through CUBE-A, hehe)
What kind of body was my drone in part1? - If you said dynamic, you were right. And then before each render i ran:
export function updateMeshPositions(bodies) {
let i = bodies.length;
while (i--) {
const body = bodies[i];
const mesh = meshes[i];
if (!body.sleeping && mesh) {
mesh.position.copy(body.getPosition());
mesh.quaternion.copy(body.getQuaternion());
}
}
}
keeping physics and 3d meshes in sync.
Angular force for rotation + lift as linear velocity were not all that precious of "REAL SIMULATIONS" anyways, so i have given up and switched to kinematic body type.
Forces to codes
Two things that WORKED were gravity, lift, that i got calling
const {x,y,z} = {x: 0, y: MAX_LIFT * controller.throttle, z: 0};
const upforce = new Vector3(x,y,z);
// to make Y force a RELATIVE y based on body position, not WORLD Y
upforce.applyQuaternion(body.getQuaternion());
body.applyImpulse(body.getPosition(), upforce);
and rotation via
body.angularVelocity += MAX_ANGLE_PER_FRAME * controller.yaw
I've left our LERPs for the simplicity here. So what would that be for a kinematic object?
Knowing that F = ma
, and gravity is mg
is enough.
// called every frame
update(timeSinceStart = 0) {
const timeMs = timeSinceStart - this.timeSinceStart;
const timeSeconds = timeMs / 1000;
const position = this.mesh.position.clone();
const sumOfForces = new Vector3(0, 0, 0);
const throttleAcceleration = (controller.throttle + 1) / 2 * MAX_THROTTLE;
const gravityVector = new Vector3(0, KINEMATIC_GRAVITY * timeSeconds, 0);
const upforceVector = new Vector3(0, throttleAcceleration * timeSeconds, 0);
upforceVector.applyQuaternion(this.mesh.quaternion);
sumOfForces.add(gravityVector);
if (!flags.isPlayerOnTheGround) {
this.rotate(timeSeconds)
this.moveForward(timeSeconds);
this.moveSideways(timeSeconds);
}
if (controller.armed) {
sumOfForces.add(upforceVector);
}
position.add(sumOfForces);
Thats your "moving up and down", covered.
then pitch, yaw and roll become just transforms on mesh
rotate() {
this.nextFrameRotation = { ...this.nextFrameRotation, y: MAX_YAW_ANGLE * controller.yaw };
}
moveForward() {
// rotate obj
this.nextFrameRotation = ({ ... this.nextFrameRotation, x: - MAX_ROTATION_ANGLE * controller.roll });
}
moveSideways() {
this.nextFrameRotation = { ...this.nextFrameRotation, z: MAX_ROTATION_ANGLE * -controller.pitch }
}
and once the next angles are there - i'm spinning the body
const { x, y, z } = this.nextFrameRotation;
let forward = new Vector3(0, 0, 1);
let right = new Vector3(1, 0, 0);
forward.normalize();
right.normalize();
this.mesh.rotateOnAxis(forward, z); //sideways
this.mesh.rotateOnAxis(right, x); //forward
this.mesh.rotateOnAxis(this.mesh.up, y); //yaw
aaand that's it mostly. For collisions i'd use a raycast in each direction. Now with no "physics" my chances of wrapping it all into a "camera controls" plugin for THREE are way higher. Sweet stuff.
Guess all that i have for you today, babies. Wait for part three, perhaps it's gonna be an exciting one.
Top comments (0)