THREE.JS - ROTATIONS

Elsewhere, I discussed how to use the Napier equations to rotate the aircraft object (in a display) and the flight path (in a flight simulation). Because of the way three.js handles 3D objects, you can use linked 3D objects to correctly rotate your aircraft and perform these calculations. This is especially useful when attempting to compute yaw.

The Program

You can start by assigning variables:

// Inputs:
let ACBDif = 0; // Bank
let PPPDif = 0; // Pitch (along Pitch plane)
let YawDif = 0; // Yaw (along Yaw plane)
// Aircraft Rotation (Relative to Surface)
let ACBank = 0; // Bank
let ACPtch = 0; // Pitch
let ACHead = 0; // Heading
// Useful Constants
const PieVal = Math.PI; // PI
const DegRad = PieVal / 180; // Convert Degrees to Radians
const RadDeg = 180 / PieVal; // Convert Radians to Degrees

You can then create the linked parent/child objects. The parent object (AirAxe) contains the current position of the aircraft. The child object (AirPBY) is linked to AirAxe so that AirPBY rotation is determined by the rotation of the AirAxe object. This provides the correct framework for making subsequent pitch, bank and yaw changes. The quaternion is used to temporarily store the combined rotation of AirAxe and AirPBY.

const AirAxe = new THREE.Object3D();
    AirAxe.rotation.order = "YXZ";
    scene.add(AirAxe);
const AirPBY = new THREE.Object3D();
    AirPBY.rotation.order = "YXZ";
    AirAxe.add(AirPBY);
var quaternion = new THREE.Quaternion();
    // Enter Starting Values in AirAxe
    AirAxe.rotation.z = Mod360(360-ACBank) * DegRad;
    AirAxe.rotation.x = Mod360(ACPtch) * DegRad;
    AirAxe.rotation.y = Mod360(-ACHead) * DegRad;

Here is the subroutine for making the changes:

function roteAirObj() {
    // Make Bank, Pitch and Yaw Changes to AirPBY
    AirPBY.rotation.z = -ACBDif*DegRad;
    AirPBY.rotation.x = PPPDif*DegRad;
    AirPBY.rotation.y = -YawDif*DegRad;
    // Transfer Combined Rotation to AirAxe
    AirPBY.getWorldQuaternion(quaternion);
    AirAxe.setRotationFromQuaternion(quaternion);
    // Zero Out AirObj Rotations (so changes not doubled)
    AirPBY.rotation.z = 0;
    AirPBY.rotation.x = 0;
    AirPBY.rotation.y = 0;
    // Load Resulting Values into Variables (for display)
    ACBank = Mod360(-AirAxe.rotation.z*RadDeg);
    ACPtch = AirAxe.rotation.x*RadDeg;
    // Update AirAxe Heading for Bank (updated later)
    ACHead = Mod360(-AirAxe.rotation.y*RadDeg);
    ACHead = Mod360(ACHead+ACHSpd);
    AirAxe.rotation.y = -ACHead*DegRad;
}

Changes that affect only AirAxe are made after the update. This is because changes to the parent object will not be made until the next cycle. In the static display version (shown above), this includes the estimated change in Heading due to ACBank. In the flight simulation version (ACflytEZ), this includes the subsequent adjustment to ACPtch due to gravity which is used to compute the change in Heading.

Demonstration
You can see a simple demonstration here:


In Full View, use the Arrow Keys to move Ailerons and Elevator and Z/X Keys to move Rudder.