770 lines
20 KiB
C
770 lines
20 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.
|
|
*/
|
|
|
|
/*
|
|
* Memory Map:
|
|
*
|
|
* Far Heap (upper 448k):
|
|
*
|
|
* 0x7ffff - Top of far heap / End of bitmap page 1
|
|
* - Bitmap page is 0x14000 (81920 bytes)
|
|
* 0x6c000 - Start of bitmap page 1
|
|
* 0x6bfff - End of bitmap page 2
|
|
* - Bitmap page is 0x14000 (81920 bytes)
|
|
* 0x58000 - Start of bitmap page 2
|
|
* 0x57fff - End of A2-3D2 and data to be used in slots 3 and 2/4 below
|
|
* - ...
|
|
* 0x54000 - Start of A2-3D2 and data to be used in slots 3 and 2/4 below
|
|
* - 57344 bytes free
|
|
* 0x45fff - End of overlay code
|
|
* - ...
|
|
* 0x10000 - Bottom of far heap / Start of overlay code
|
|
*
|
|
*
|
|
* Near Heap (lower 64k):
|
|
*
|
|
* 0xffff - Top of near heap
|
|
* - ...
|
|
* 0xe000 - Microkernel
|
|
* - ...
|
|
* 0xc000 - I/O and microkernel
|
|
* - ...
|
|
* 0xa000 - Overlay code
|
|
* 0x9fff - Top of virtual stack
|
|
* - ...
|
|
* 0x8000 - Program code / 3D/2D data
|
|
* - ...
|
|
* 0x6000 - Program code / 3D library
|
|
* - ...
|
|
* 0x0300 - Start of program code
|
|
* 0x02ff - End of program arguments / data storage
|
|
* - ... (We're going to overwrite this!)
|
|
* 0x0200 - Start of program arguments / data storage
|
|
* 0x01ff - Top of CPU stack space
|
|
* - ...
|
|
* 0x0100 - Bottom of CPU stack space
|
|
* 0x00ff - End of microkernel use
|
|
* - ...
|
|
* 0x00f0 - Start of microkernel use
|
|
* - 46 bytes free
|
|
* 0x00c2 - End of A2-3D2 use
|
|
* - ...
|
|
* 0x0060 - Start of A2-3D2 use
|
|
* - 49 bytes free
|
|
* 0x002f - llvm virtual register 31
|
|
* - ...
|
|
* 0x0010 - llvm virtual register 0
|
|
* 0x000f - MMU_MEM_BANK_7
|
|
* - ...
|
|
* 0x0008 - MMU_MEM_BANK_0
|
|
* - 7 bytes free
|
|
* 0x0001 - MMU_IO_CTRL
|
|
* 0x0000 - MMU_MEM_CTRL
|
|
*
|
|
*
|
|
* Calling the 3D Renderer:
|
|
*
|
|
* The MMU splits the near heap into 8k "slots" that 8k "blocks" of far heap
|
|
* can be mapped into. f256lib, by default, sets the near heap up as
|
|
* follows:
|
|
*
|
|
* 0xe000 - Slot 7 - Microkernel
|
|
* 0xc000 - Slot 6 - I/O and Microkernel
|
|
* 0xa000 - Slot 5 - Code Overlay Region
|
|
* 0x8000 - Slot 4 - Program Code
|
|
* 0x6000 - Slot 3 - Program Code
|
|
* 0x4000 - Slot 2 - Program Code
|
|
* 0x2000 - Slot 1 - Program Code
|
|
* 0x0000 - Slot 0 - Zero Page & Program Code
|
|
*
|
|
* Our 3D library, subLOGIC's A2-3D2, was designed to be run on an Apple ][.
|
|
* The Apple ][ has two high-resolution video pages in the middle of the
|
|
* memory map and A2-3D2 loads at 0x6000 immediately after the second video
|
|
* page. This makes sense on the Apple. On the Foenix, it's in the way at
|
|
* slot 3. The library also needs a 3D scene database and outputs an array
|
|
* of 2D drawing instructions. Given the size of A2-3D2 this pushes our
|
|
* memory usage up into slot 4. This leaves slots 0 to 2 for the simulator.
|
|
* That's less than 24k. Not good.
|
|
*
|
|
* We're going to place the 3D library and related data in the far heap and
|
|
* manually swap them in using the MMU when we need to render the scene. To
|
|
* do this, we need to ensure we're not currently executing code from
|
|
* somewhere in the slot 2 to 4 range. To accomplish this, all the functions
|
|
* that deal with A2-3D2 are placed in an overlay. This ensures whatever
|
|
* code calls into slots 2 to 4 has a program counter between 0xa000 and
|
|
* 0xbfff - well out of the way of the 3D stuff.
|
|
*
|
|
* When enabling compiler optimizations, both the compiler and A2-3D2 want
|
|
* use of the remaining zero page. We maintain copies of the area that
|
|
* gets clobbered by each and make sure the right one is in place at the
|
|
* right time.
|
|
*
|
|
* The last thing to worry about is data needed by both the simulator and
|
|
* A2-3D2. This is basically only the camera position and rotation. These
|
|
* 10 bytes will be stored directly into RAM from 0x200 to 0x20a overwriting
|
|
* any command line arguments that may have been in memory.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef __F256__
|
|
#define WITHOUT_FILE
|
|
#define WITHOUT_SPRITE
|
|
#define WITHOUT_TILE
|
|
#define F256LIB_IMPLEMENTATION
|
|
#include "f256lib.h"
|
|
/*
|
|
* embedded.bin is loaded at 0x54000:
|
|
*
|
|
* 8k a2-3d2.bin @ 0x54000
|
|
* 8k scene.3d @ 0x56000
|
|
*
|
|
*/
|
|
EMBED(embedded, "embedded.bin", 0x54000);
|
|
#endif
|
|
|
|
#include "a23d2.h"
|
|
|
|
asm (
|
|
".text\n"
|
|
// " .cpu mosw65c02\n"
|
|
// "mos-insns-w65c02\n"
|
|
// "EF_MOS_ARCH_W65C02 \n"
|
|
".global dmaClear\n"
|
|
"dmaClear:\n"
|
|
"pha\n"
|
|
// "phx\n"
|
|
".byte $DA\n"
|
|
"php \n" // preserver interrupt state + c
|
|
"sei \n" // disable interrupts
|
|
"ldy 1 \n" // y = io_ctrl
|
|
//"phy \n" // save io_ctrl on stack
|
|
".byte $5A\n"
|
|
//"stz 1 \n" // map in the dma registers, where we can get them
|
|
".byte $64,$01 \n"
|
|
//"stz $DF00 \n" // clear dma control
|
|
".byte $9c,$00,$DF\n"
|
|
"ldy #$05 \n" // DMA_CTRL_ENABLE+DMA_CTRL_FILL
|
|
"sty $DF00 \n"
|
|
"ldy __rc8 \n" // Fill Color
|
|
"sty $DF01 \n" // Fill value
|
|
"sta $DF08 \n" // Addr Low
|
|
"stx $DF09 \n" // Addr Hi
|
|
"lda __rc2 \n" // Addr Bank
|
|
"sta $DF0A \n"
|
|
"lda __rc4 \n" // length low
|
|
"sta $DF0C \n"
|
|
"lda __rc5 \n" // length middle
|
|
"sta $DF0D \n"
|
|
"lda __rc6 \n" // length high
|
|
"sta $DF0E \n"
|
|
"lda #$80 \n" // DMA_CTRL_START
|
|
//"tsb $DF00 \n" // DMA_CTRL
|
|
".byte $0C,$00,$DF\n"
|
|
//"wait_start: lda $DF01 \n" // DMA_STATUS
|
|
// "bpl wait_start \n" // Wait for DMA
|
|
"wait_end: lda $DF01 \n" // DMA_STATUS
|
|
"bmi wait_end \n" // Wait for DMA
|
|
//"stz $DF00 \n" // DMA Off
|
|
".byte $9c,$00,$DF\n"
|
|
"nop \n"
|
|
"nop \n"
|
|
"nop \n" // wait
|
|
"nop \n"
|
|
"nop \n"
|
|
"nop \n"
|
|
"pla \n" // restore the io_ctrl
|
|
"sta 1 \n"
|
|
"plp \n"
|
|
// "plx\n"
|
|
".byte $FA\n"
|
|
"pla\n"
|
|
"rts \n"
|
|
);
|
|
|
|
void dmaClear(uint32_t address, uint32_t length, byte color);
|
|
|
|
|
|
#if 0
|
|
typedef struct airplaneS {
|
|
int8_t aileron; // -15 to 15
|
|
int8_t elevator; // -15 to 15
|
|
int8_t rudder; // -15 to 15
|
|
int8_t throttle; // -15 to 15
|
|
bool ignition;
|
|
bool engine;
|
|
int16_t rpm;
|
|
float hSpeed;
|
|
float vSpeed;
|
|
float deltaZ;
|
|
float efAOF;
|
|
float climbRate;
|
|
bool airborne;
|
|
bool stall;
|
|
bool brake;
|
|
int16_t x;
|
|
int16_t y;
|
|
int16_t z;
|
|
double pitch; // 0 to 255
|
|
double yaw; // 0 to 255
|
|
double roll; // 0 to 255
|
|
} airplaneT;
|
|
|
|
|
|
airplaneT _plane;
|
|
byte _gamepad;
|
|
#endif
|
|
|
|
|
|
#if 0
|
|
//#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 atan(double 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 sin(float d) {
|
|
_tdata = d;
|
|
a23d2Sin();
|
|
return _trig;
|
|
}
|
|
|
|
|
|
float cos(float d) {
|
|
_tdata = d;
|
|
a23d2Cos();
|
|
return _trig;
|
|
}
|
|
|
|
|
|
//#define SEGMENT_INPUT
|
|
|
|
|
|
void getInput(void) {
|
|
static byte oneJoy = 0;
|
|
static byte twoJoy = 0;
|
|
static byte keyJoy = 0;
|
|
|
|
do {
|
|
kernelCall(NextEvent);
|
|
|
|
// Read real joysticks.
|
|
if (kernelEventData.type == kernelEvent(GAME)) {
|
|
oneJoy = kernelEventData.game.game0;
|
|
twoJoy = kernelEventData.game.game1;
|
|
}
|
|
|
|
//***TODO*** Add SNES/NES once I get some.
|
|
|
|
// Use keyboard as virtual joystick.
|
|
if (kernelEventData.type == kernelEvent(key.PRESSED)) {
|
|
switch (kernelEventData.key.ascii) {
|
|
case 'w':
|
|
case 'W':
|
|
keyJoy |= JOY_UP;
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
keyJoy |= JOY_LEFT;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
keyJoy |= JOY_DOWN;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
keyJoy |= JOY_RIGHT;
|
|
break;
|
|
case 'j':
|
|
case 'J':
|
|
keyJoy |= JOY_BUTTON_1;
|
|
break;
|
|
case 'k':
|
|
case 'K':
|
|
keyJoy |= JOY_BUTTON_2;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
keyJoy |= JOY_BUTTON_3;
|
|
break;
|
|
}
|
|
}
|
|
if (kernelEventData.type == kernelEvent(key.RELEASED)) {
|
|
switch (kernelEventData.key.ascii) {
|
|
case 'w':
|
|
case 'W':
|
|
keyJoy &= ~JOY_UP;
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
keyJoy &= ~JOY_LEFT;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
keyJoy &= ~JOY_DOWN;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
keyJoy &= ~JOY_RIGHT;
|
|
break;
|
|
case 'j':
|
|
case 'J':
|
|
keyJoy &= ~JOY_BUTTON_1;
|
|
break;
|
|
case 'k':
|
|
case 'K':
|
|
keyJoy &= ~JOY_BUTTON_2;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
keyJoy &= ~JOY_BUTTON_3;
|
|
break;
|
|
}
|
|
}
|
|
} while (kernelGetPending() > 0);
|
|
|
|
// Merge inputs. Yes, this allows dumb things like LEFT and RIGHT at the same time.
|
|
_gamepad = oneJoy | twoJoy | keyJoy;
|
|
}
|
|
|
|
|
|
//#define SEGMENT_FLIGHT_MODEL_1
|
|
|
|
|
|
// These didn't use to be global. Ugly.
|
|
float tmpX;
|
|
float tmpY;
|
|
float tmpZ;
|
|
float newX;
|
|
float newY;
|
|
float newZ;
|
|
float iSpeed;
|
|
float lSpeed;
|
|
float hAccel;
|
|
float lVeloc;
|
|
float gVeloc;
|
|
float deltaZ;
|
|
float AOA;
|
|
float torque;
|
|
float torque2;
|
|
|
|
uint16_t loopTime = 40;
|
|
|
|
// Used to be static. Need preserved.
|
|
double dPitch = 0;
|
|
double dYaw = 0;
|
|
double dRoll = 0;
|
|
float collectX = 0;
|
|
float collectY = 0;
|
|
float collectZ = 0;
|
|
|
|
|
|
|
|
|
|
// 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 += loopTime * 0.5; //***TODO*** T
|
|
if (_plane.rpm > (375 + (_plane.throttle * 117))) _plane.rpm -= loopTime * 0.5;
|
|
} else {
|
|
// Stop engine.
|
|
if (_plane.engine) _plane.engine = false;
|
|
// Run down engine.
|
|
if (_plane.rpm > 0) _plane.rpm -= (int16_t)(loopTime / 2); //***TODO*** This will never work.
|
|
if (_plane.rpm < 0) _plane.rpm = 0;
|
|
}
|
|
|
|
// Flight model. Flight Dynamics.
|
|
// Calculate speed from RPM.
|
|
iSpeed = _plane.rpm / 17.5;
|
|
// Modify speed by pitch.
|
|
iSpeed += (_plane.pitch * 1.5);
|
|
// Horizontal acceleration - thrust.
|
|
hAccel = ((_plane.rpm * (iSpeed - _plane.hSpeed)) / 10000);
|
|
hAccel /= 1000;
|
|
hAccel *= loopTime;
|
|
if (_plane.brake && !_plane.airborne) {
|
|
// Handle brakes.
|
|
if (_plane.hSpeed > 0) {
|
|
_plane.hSpeed -= 1;
|
|
} else {
|
|
_plane.hSpeed = 0;
|
|
}
|
|
} else {
|
|
// Accelerate normally.
|
|
_plane.hSpeed += hAccel;
|
|
}
|
|
// Force speed to range -1..1.
|
|
lSpeed = (_plane.hSpeed / 65) - 1;
|
|
if (lSpeed > 1) lSpeed = 1;
|
|
// Lift curve.
|
|
lVeloc = Degs(atan(lSpeed));
|
|
// Force lift to range 0..90.
|
|
lVeloc += 45;
|
|
// Shift range to 0..-17.
|
|
lVeloc /= 5.29;
|
|
// Multiply by pitch modifier.
|
|
lVeloc *= (-(_plane.pitch * .157) + 1);
|
|
// Time slice.
|
|
lVeloc /= 1000;
|
|
lVeloc *= loopTime;
|
|
// Gravity.
|
|
gVeloc = loopTime * (-16.0 / 10000); // -16.0 is ft/sec for gravity.
|
|
// Sum vertical velocity.
|
|
_plane.vSpeed = gVeloc + 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 / loopTime;
|
|
_plane.climbRate *= 60000L;
|
|
// Expand to ft/hr.
|
|
deltaZ = _plane.hSpeed * 5280;
|
|
// Get ft/ms.
|
|
deltaZ /= 3600000L;
|
|
deltaZ *= loopTime;
|
|
// Find effective angle of flight.
|
|
if (deltaZ) {
|
|
_plane.efAOF = -(atan(_plane.vSpeed / deltaZ));
|
|
} else {
|
|
_plane.efAOF = -(atan(_plane.vSpeed));
|
|
}
|
|
// Convert to degrees.
|
|
AOA = Degs(_plane.efAOF);
|
|
// Handle stalling.
|
|
if (((_plane.pitch < AOA) && (AOA < 0)) && (_plane.hSpeed < 40)) {
|
|
if ((_plane.pitch - 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 (dPitch) {
|
|
dPitch -= dPitch / 10;
|
|
if (((dPitch > 0) && (dPitch < 0.01)) || ((dPitch < 0) && (dPitch > -0.01))) dPitch = 0;
|
|
}
|
|
if (dYaw) {
|
|
dYaw -= dYaw / 10;
|
|
if (((dYaw > 0) && (dYaw < 0.01)) || ((dYaw < 0) && (dYaw > -0.01))) dYaw = 0;
|
|
}
|
|
if (dRoll) {
|
|
dRoll -= dRoll / 10;
|
|
if (((dRoll > 0) && (dRoll < 0.01)) || ((dRoll < 0) && (dRoll > -0.01))) dRoll = 0;
|
|
}
|
|
|
|
// Flight model. Rate of Change.
|
|
if (_plane.airborne) {
|
|
if (_plane.aileron != 0) {
|
|
torque = ((_plane.hSpeed * _plane.aileron) / 10000);
|
|
if (dRoll != (torque * loopTime)) dRoll += torque * 6;
|
|
}
|
|
}
|
|
if (_plane.elevator != 0) {
|
|
torque = ((_plane.hSpeed * _plane.elevator) / 10000);
|
|
if ((!_plane.airborne) && (torque > 0)) torque = 0; //***FIX*** This is dumb.
|
|
if (dPitch != (torque * loopTime)) dPitch += torque * 1.5;
|
|
}
|
|
if (_plane.hSpeed) {
|
|
torque = 0.0;
|
|
if (_plane.rudder != 0) torque -= ((_plane.hSpeed * _plane.rudder) / 10000);
|
|
torque2 = 0.0;
|
|
if ((_plane.roll > 0) && (_plane.roll <= 90)) { //***FIX*** This is dumb.
|
|
torque2 = _plane.roll * 0.00050;
|
|
} else {
|
|
if ((_plane.roll < 0) && (_plane.roll >= -90)) {
|
|
torque2 = _plane.roll * 0.00050;
|
|
}
|
|
}
|
|
torque += torque2;
|
|
if (dYaw != (torque * loopTime)) dYaw += torque * 1.5;
|
|
}
|
|
|
|
// Flight model. Apply Rotations.
|
|
// Transform pitch into components of yaw and pitch based on roll.
|
|
_plane.roll += dRoll;
|
|
_plane.yaw += dYaw;
|
|
_plane.pitch += (dPitch * cos(_plane.roll));
|
|
_plane.yaw += -(dPitch * sin(_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.
|
|
tmpX = 0;
|
|
tmpY = 0;
|
|
tmpZ = deltaZ;
|
|
|
|
// Order of these points is significant.
|
|
// Rotate in Z.
|
|
newX = (tmpX * cos(_plane.roll)) - (tmpY * sin(_plane.roll));
|
|
newY = (tmpX * sin(_plane.roll)) + (tmpY * cos(_plane.roll));
|
|
tmpX = newX;
|
|
tmpY = newY;
|
|
// Rotate in X;
|
|
_plane.efAOF = Degs(_plane.efAOF);
|
|
newY = (tmpY * cos(_plane.efAOF)) - (tmpZ * sin(_plane.efAOF));
|
|
newZ = (tmpY * sin(_plane.efAOF)) + (tmpZ * cos(_plane.efAOF));
|
|
tmpY = newY;
|
|
tmpZ = newZ;
|
|
// Rotate in X;
|
|
newX = (tmpZ * sin(_plane.yaw)) + (tmpX * cos(_plane.yaw));
|
|
newZ = (tmpZ * cos(_plane.yaw)) - (tmpX * sin(_plane.yaw));
|
|
tmpX = newX;
|
|
tmpZ = newZ;
|
|
|
|
// Translate rotated point back to where it should be
|
|
// relative to it's last position.
|
|
collectX += newX;
|
|
if ((collectX > 1) || (collectX < -1)) {
|
|
_plane.x -= collectX;
|
|
collectX = 0;
|
|
}
|
|
collectY += newY;
|
|
if ((collectY > 1) || (collectY < -1)) {
|
|
_plane.y -= collectY;
|
|
collectY = 0;
|
|
}
|
|
collectZ += newZ;
|
|
if ((collectY > 1) || (collectY < -1)) {
|
|
_plane.z += collectZ;
|
|
collectZ = 0;
|
|
}
|
|
|
|
// Are we flying?
|
|
if ((!_plane.airborne) && (-_plane.y)) _plane.airborne = true;
|
|
}
|
|
#endif
|
|
|
|
|
|
#define SEGMENT_MAIN
|
|
|
|
|
|
void runSimulation(void) {
|
|
bool pageZero;
|
|
|
|
// Initialize airplane.
|
|
// memset(&_plane, 0, sizeof(airplaneT));
|
|
|
|
// Which page is visible?
|
|
pageZero = true;
|
|
|
|
// Draw it!
|
|
while (true) {
|
|
|
|
// Flip pages.
|
|
if (pageZero) {
|
|
// Looking at bitmap 0.
|
|
pageZero = false;
|
|
bitmapSetActive(1);
|
|
bitmapSetVisible(0, true);
|
|
bitmapSetVisible(1, false);
|
|
_drawlist = DRAWLIST_P1;
|
|
} else {
|
|
// Looking at bitmap 1.
|
|
pageZero = true;
|
|
bitmapSetActive(0);
|
|
bitmapSetVisible(1, true);
|
|
bitmapSetVisible(0, false);
|
|
_drawlist = DRAWLIST_P0;
|
|
}
|
|
|
|
#if 0
|
|
// Erase old scene.
|
|
bitmapSetColor(0);
|
|
_useColor = false;
|
|
//textPrint("a23d2Draw()\n");
|
|
a23d2Draw();
|
|
#else
|
|
|
|
graphicsWaitVerticalBlank();
|
|
dmaClear(pageZero ? 0x6c000 : 0x58000, 0x12c00, 0);
|
|
|
|
#endif
|
|
|
|
// Draw new scene.
|
|
//textPrint("a23d2Render()\n");
|
|
a23d2Render();
|
|
_useColor = true;
|
|
//textPrint("a23d2Draw()\n");
|
|
a23d2Draw();
|
|
|
|
#if 0
|
|
// Update player input.
|
|
getInput();
|
|
|
|
// Update aircraft control state.
|
|
if ((_gamepad & JOY_BUTTON_1) || (_gamepad & JOY_BUTTON_2) || (_gamepad & JOY_BUTTON_3)) {
|
|
// Modified input with button down.
|
|
if ((_gamepad & JOY_UP) && (_plane.throttle > -15)) _plane.throttle++;
|
|
if ((_gamepad & JOY_DOWN) && (_plane.throttle < 15)) _plane.throttle--;
|
|
if (_gamepad & JOY_RIGHT) _plane.brake = !_plane.brake;
|
|
} else {
|
|
// No button pressed.
|
|
if ((_gamepad & JOY_UP) && (_plane.elevator > -15)) _plane.elevator--;
|
|
if ((_gamepad & JOY_DOWN) && (_plane.elevator < 15)) _plane.elevator++;
|
|
if ((_gamepad & JOY_LEFT) && (_plane.aileron > -15)) _plane.aileron--;
|
|
if ((_gamepad & JOY_RIGHT) && (_plane.aileron < 15)) _plane.aileron++;
|
|
}
|
|
// "Coordinated" flight.
|
|
_plane.rudder = _plane.aileron;
|
|
|
|
// Do the actual flying!
|
|
lightPlane();
|
|
lightPlane2();
|
|
moveAircraft();
|
|
#endif
|
|
|
|
// Move camera.
|
|
//_camera->p += 1; // Change pitch angle.
|
|
//_camera->b += 2; // Change bank angle.
|
|
_camera->h += 1; // Change heading angle.
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
byte c;
|
|
byte ega[16][3] = {
|
|
{ 0x00, 0x00, 0x00 },
|
|
{ 0x00, 0x00, 0xaa },
|
|
{ 0x00, 0xaa, 0x00 },
|
|
{ 0x00, 0xaa, 0xaa },
|
|
{ 0xaa, 0x00, 0x00 },
|
|
{ 0xaa, 0x00, 0xaa },
|
|
{ 0xaa, 0x55, 0x00 },
|
|
{ 0xaa, 0xaa, 0xaa },
|
|
{ 0x55, 0x55, 0x55 },
|
|
{ 0x55, 0x55, 0xff },
|
|
{ 0x55, 0xff, 0x55 },
|
|
{ 0x55, 0xff, 0xff },
|
|
{ 0xff, 0x55, 0x55 },
|
|
{ 0xff, 0x55, 0xff },
|
|
{ 0xff, 0xff, 0x55 },
|
|
{ 0xff, 0xff, 0xff }
|
|
};
|
|
|
|
// Load EGA palette into CLUT0.
|
|
for (c=0; c<16; c++) graphicsDefineColor(0, c, ega[c][0], ega[c][1], ega[c][2]);
|
|
|
|
//textPrint("a23d2Init()\n");
|
|
a23d2Init();
|
|
|
|
_camera->x = 5000;
|
|
_camera->y = 1000;
|
|
_camera->z = 5000;
|
|
_camera->p = 25;
|
|
|
|
// Set up bitmap planes.
|
|
for (c=0; c<2; c++) {
|
|
// Clear screen.
|
|
bitmapSetActive(c);
|
|
bitmapSetColor(0);
|
|
bitmapClear();
|
|
// Control Panel.
|
|
bitmapSetColor(8);
|
|
bitmapLine(0, 194, 319, 194); // Leaves 45 pixels at the bottom below this line.
|
|
}
|
|
bitmapSetVisible(0, true);
|
|
bitmapSetVisible(1, false);
|
|
|
|
//textPrint("runSimulation()\n");
|
|
runSimulation();
|
|
|
|
return 0;
|
|
}
|