sierrahotel/pc/src/flight.c
2024-04-20 19:38:20 -05:00

326 lines
9.4 KiB
C

/*
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "flight.h"
airplaneT _plane;
#define SEGMENT_MATH
#define PI 3.1415926
#define Rads(d) (((d) < 0 ? (d) + 360 : (d)) * (PI / 180))
#define Degs(r) ((r) * (180 / PI))
// https://www.reddit.com/r/programming/comments/3e7ghi/discrete_arctan_in_6502/
#define ATAN_SPLINE_C0 (double)(-0.14380550980765507115) /* 3pi/4 - 5/2 */
#define ATAN_SPLINE_C1 (double)(-0.07079632679489661923) /* 3/2 - pi/2 */
double ourAtan(double x){
return atan(x);
/*
if (x >= 0) {
if ( x<= 1) {
return x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x;
} else {
x = 1 / x;
return PI / 2 - (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x);
}
} else {
if (x >= -1) {
return x - (ATAN_SPLINE_C0 - ATAN_SPLINE_C1 * x) * x * x;
} else {
x = -1 / x;
return (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x) - PI / 2;
}
}
*/
}
float cosD(float d) {
return cos(Rads(d));
}
float sinD(float d) {
return sin(Rads(d));
}
#define SEGMENT_FLIGHT_MODEL_1
// This flight model is basically the one from "Build Your Own Flight Sim
// in C++" by Michael Radtke and Chris Lampton.
void lightPlane(void) {
// Flight model. Power Dynamics.
if (_plane.ignition) {
// Start engine.
if (!_plane.engine) _plane.engine = true;
// Adjust RPM.
if (_plane.rpm < (375 + (_plane.throttle * 117))) _plane.rpm += _plane.loopTime * 0.5;
if (_plane.rpm > (375 + (_plane.throttle * 117))) _plane.rpm -= _plane.loopTime * 0.5;
} else {
// Stop engine.
if (_plane.engine) _plane.engine = false;
// Run down engine.
if (_plane.rpm > 0) _plane.rpm -= (int16_t)(_plane.loopTime / 2);
if (_plane.rpm < 0) _plane.rpm = 0;
}
// Flight model. Flight Dynamics.
// Calculate speed from RPM.
_plane.iSpeed = _plane.rpm / 17.5;
// Modify speed by pitch.
_plane.iSpeed += (_plane.pitch * 1.5);
// Horizontal acceleration - thrust.
_plane.hAccel = ((_plane.rpm * (_plane.iSpeed - _plane.hSpeed)) / 10000);
_plane.hAccel /= 1000;
_plane.hAccel *= _plane.loopTime;
if (_plane.brake && !_plane.airborne) {
// Handle brakes.
if (_plane.hSpeed > 0) {
_plane.hSpeed -= 1;
} else {
_plane.hSpeed = 0;
}
} else {
// Accelerate normally.
_plane.hSpeed += _plane.hAccel;
}
// Force speed to range -1..1.
_plane.lSpeed = (_plane.hSpeed / 65) - 1;
if (_plane.lSpeed > 1) _plane.lSpeed = 1;
// Lift curve.
_plane.lVeloc = Degs(ourAtan(_plane.lSpeed));
// Force lift to range 0..90.
_plane.lVeloc += 45;
// Shift range to 0..-17.
_plane.lVeloc /= 5.29;
// Multiply by pitch modifier.
_plane.lVeloc *= (-(_plane.pitch * 0.157) + 1);
// Time slice.
_plane.lVeloc /= 1000;
_plane.lVeloc *= _plane.loopTime;
// Gravity.
_plane.gVeloc = _plane.loopTime * (-16.0 / 1000); // -16.0 is ft/sec for gravity.
// Sum vertical velocity.
_plane.vSpeed = _plane.gVeloc + _plane.lVeloc;
// No vertical speed if we're on the ground.
if ((!_plane.airborne) && (_plane.vSpeed < 0)) _plane.vSpeed = 0;
// Save climb rate in ft/min.
_plane.climbRate = _plane.vSpeed / _plane.loopTime;
_plane.climbRate *= 60000L;
// Expand to ft/hr.
_plane.deltaZ = _plane.hSpeed * 5280;
// Get ft/ms.
_plane.deltaZ /= 3600000L;
_plane.deltaZ *= _plane.loopTime;
// Find effective angle of flight.
if (_plane.deltaZ) {
_plane.efAOF = -(ourAtan(_plane.vSpeed / _plane.deltaZ));
} else {
_plane.efAOF = -(ourAtan(_plane.vSpeed));
}
// Convert to degrees.
_plane.AOA = Degs(_plane.efAOF);
// Handle stalling.
if (((_plane.pitch < _plane.AOA) && (_plane.AOA < 0)) && (_plane.hSpeed < 40)) {
if ((_plane.pitch - _plane.AOA) < -20) _plane.stall = true;
}
if (_plane.stall) {
if (_plane.pitch > 30) {
_plane.stall = false;
} else {
_plane.pitch++;
}
}
}
#define SEGMENT_FLIGHT_MODEL_2
void lightPlane2(void) {
// Flight model. Inertial Damping.
if (_plane.dPitch) {
_plane.dPitch -= _plane.dPitch / 10;
if (((_plane.dPitch > 0) && (_plane.dPitch < 0.01)) || ((_plane.dPitch < 0) && (_plane.dPitch > -0.01))) _plane.dPitch = 0;
}
if (_plane.dYaw) {
_plane.dYaw -= _plane.dYaw / 10;
if (((_plane.dYaw > 0) && (_plane.dYaw < 0.01)) || ((_plane.dYaw < 0) && (_plane.dYaw > -0.01))) _plane.dYaw = 0;
}
if (_plane.dRoll) {
_plane.dRoll -= _plane.dRoll / 10;
if (((_plane.dRoll > 0) && (_plane.dRoll < 0.01)) || ((_plane.dRoll < 0) && (_plane.dRoll > -0.01))) _plane.dRoll = 0;
}
// Flight model. Rate of Change.
if (_plane.airborne) {
if (_plane.aileron != 0) {
_plane.torque = ((_plane.hSpeed * _plane.aileron) / 10000);
if (_plane.dRoll != (_plane.torque * _plane.loopTime)) _plane.dRoll += _plane.torque * 6;
}
}
if (_plane.elevator != 0) {
_plane.torque = ((_plane.hSpeed * _plane.elevator) / 10000);
if ((!_plane.airborne) && (_plane.torque > 0)) _plane.torque = 0; //***FIX*** This is dumb.
if (_plane.dPitch != (_plane.torque * _plane.loopTime)) _plane.dPitch += _plane.torque * 1.5;
}
if (_plane.hSpeed) {
_plane.torque = 0.0;
if (_plane.rudder != 0) _plane.torque = -((_plane.hSpeed * _plane.rudder) / 10000);
_plane.torque2 = 0.0;
if ((_plane.roll > 0) && (_plane.roll <= 90)) { //***FIX*** This is dumb.
_plane.torque2 = _plane.roll * 0.00050;
} else {
if ((_plane.roll < 0) && (_plane.roll >= -90)) {
_plane.torque2 = _plane.roll * 0.00050;
}
}
_plane.torque += _plane.torque2;
if (_plane.dYaw != (_plane.torque * _plane.loopTime)) _plane.dYaw += _plane.torque * 1.5;
}
// Flight model. Apply Rotations.
// Transform pitch into components of yaw and pitch based on roll.
_plane.roll += _plane.dRoll;
_plane.yaw += _plane.dYaw;
_plane.pitch += (_plane.dPitch * cosD(_plane.roll));
_plane.yaw += -(_plane.dPitch * sinD(_plane.roll));
if (_plane.roll > 180) {
_plane.roll = -180 + (_plane.roll - 180);
} else {
if (_plane.roll < -180) _plane.roll = 180 + (_plane.roll + 180);
}
if (_plane.yaw > 180) {
_plane.yaw = -180 + (_plane.yaw - 180);
} else {
if (_plane.yaw < -180) _plane.yaw = 180 + (_plane.yaw + 180);
}
// Handle special case when aircraft pitch passes vertical.
if ((_plane.pitch > 90) || (_plane.pitch < -90)) {
if (_plane.roll >= 0) {
_plane.roll -= 180;
} else {
if (_plane.roll < 0) _plane.roll += 180;
}
if (_plane.yaw >= 0) {
_plane.yaw -= 180;
} else {
if (_plane.yaw < 0) _plane.yaw += 180;
}
if (_plane.pitch > 0) {
_plane.pitch = (180 - _plane.pitch);
} else {
if (_plane.pitch < 0) _plane.pitch = (-180 - _plane.pitch);
}
}
// Dampen everything out to 0 if they get close enough.
if ((_plane.pitch > -0.5) && (_plane.pitch < 0.5)) _plane.pitch = 0;
if ((_plane.roll > -0.5) && (_plane.roll < 0.5)) _plane.roll = 0;
if ((_plane.yaw > -0.5) && (_plane.yaw < 0.5)) _plane.yaw = 0;
}
#define SEGMENT_FLIGHT_MODEL_3
void moveAircraft(void) {
// Calculate new aircraft position. Each coordinate is 1 foot in 3D space.
_plane.tmpX = 0;
_plane.tmpY = 0;
_plane.tmpZ = _plane.deltaZ;
// Order of these points is significant.
// Rotate in X.
_plane.newY = -(_plane.tmpZ * sinD(_plane.AOA));
_plane.newZ = (_plane.tmpZ * cosD(_plane.AOA));
_plane.tmpY = _plane.newY;
_plane.tmpZ = _plane.newZ;
// Rotate in Y.
_plane.newX = (_plane.tmpZ * sinD(_plane.yaw));
_plane.newZ = (_plane.tmpZ * cosD(_plane.yaw));
// Translate rotated point back to where it should be
// relative to it's last position.
_plane.collectX += _plane.newX;
if ((_plane.collectX > 1) || (_plane.collectX < -1)) {
_plane.x -= _plane.collectX;
_plane.collectX = 0;
}
_plane.collectY += _plane.newY;
if ((_plane.collectY > 1) || (_plane.collectY < -1)) {
_plane.y -= _plane.collectY;
_plane.collectY = 0;
}
_plane.collectZ += _plane.newZ;
if ((_plane.collectY > 1) || (_plane.collectY < -1)) {
_plane.z += _plane.collectZ;
_plane.collectZ = 0;
}
// Are we flying?
if ((!_plane.airborne) && (_plane.y > PLANE_HEIGHT)) _plane.airborne = true;
}
void resetAircraft(void) {
// Initialize airplane.
memset(&_plane, 0, sizeof(airplaneT));
_plane.brake = true;
/*
_plane.rpm = 0;
_plane.hSpeed = 0;
_plane.vSpeed = 0;
_plane.deltaZ = 0;
_plane.ignition = false;
_plane.engine = false;
_plane.efAOF = 0;
_plane.x = 0;
_plane.y = 0;
_plane.z = 0;
_plane.pitch = 0;
_plane.roll = 0;
_plane.yaw = 0;
_plane.airborne = false;
*/
_plane.loopTime = 40;
}
#define SEGMENT_MAIN
void updateAircraft(void) {
// Do the actual flying!
lightPlane();
lightPlane2();
moveAircraft();
}