a flying and rotating quadcopter in three.js
Published: 2022-01-19 14:49:51

IT IS ALIVE and rolls and spins 

{% youtube fhNs-hd2HN4 %}

###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.