97 lines
3.8 KiB
C
97 lines
3.8 KiB
C
// Camera state and world-to-camera transform.
|
|
//
|
|
// Coordinate conventions (right-handed, looks down +Z):
|
|
// +X right (in camera frame)
|
|
// +Y up
|
|
// +Z forward (out of the cockpit)
|
|
//
|
|
// Position uses the same Q16.16 world-unit convention as `AircraftT`
|
|
// (low 16 bits fractional). Forward speed is Q8.8 world-units / frame.
|
|
// Orientation is stored as byte angles (256 == full turn). The
|
|
// rotation matrix is Q1.15 (matches `math6502Sin/Cos` output), so a
|
|
// world->camera transform is a 3x3 dot product of int16 against
|
|
// Q16.16 deltas, normalised by `>> 15`.
|
|
|
|
#ifndef CAMERA_H
|
|
#define CAMERA_H
|
|
|
|
#include <stdint.h>
|
|
#include "types.h"
|
|
|
|
#define CAM_POS_FRACT_BITS 16
|
|
#define CAM_POS_FRACT_ONE (1 << CAM_POS_FRACT_BITS)
|
|
#define CAM_RATE_FRACT_BITS 8
|
|
#define CAM_RATE_FRACT_ONE (1 << CAM_RATE_FRACT_BITS)
|
|
#define CAM_ROT_FRACT_BITS 15
|
|
#define CAM_ROT_ONE (1 << CAM_ROT_FRACT_BITS)
|
|
|
|
|
|
static inline int32_t metresFromQ1616(int32_t v_q1616) {
|
|
return v_q1616 >> CAM_POS_FRACT_BITS;
|
|
}
|
|
|
|
|
|
static inline int32_t q1616FromMetres(int32_t m) {
|
|
return m * CAM_POS_FRACT_ONE;
|
|
}
|
|
|
|
|
|
// Convert FS2 byte angle (0..255 == 0..359 degrees) to degrees.
|
|
static inline int16_t byteAngleToDegrees(uint8_t angle) {
|
|
return (int16_t)(((int32_t)angle * 360) / 256);
|
|
}
|
|
|
|
typedef struct CameraT {
|
|
int32_t worldX; // Q16.16 world units
|
|
int32_t worldY;
|
|
int32_t worldZ;
|
|
uint8_t pitch; // X-axis rotation (nose up/down)
|
|
uint8_t bank; // Z-axis rotation (roll)
|
|
uint8_t yaw; // Y-axis rotation (heading)
|
|
// Sub-byte angle precision matching chunk5's 16-bit
|
|
// representation at $6C/$6E/$70. The combined 16-bit angle
|
|
// is `((pitch << 8) | pitchFine)` etc. -- chunk5SetupView-
|
|
// Projection uses these full 16-bit values to derive the
|
|
// matrix. MAME's Meigs boot has $6C/$6D=-109 (= -0.6 deg),
|
|
// which is finer than the 1/256-of-a-circle 8-bit pitch
|
|
// can express on its own.
|
|
uint8_t pitchFine;
|
|
uint8_t bankFine;
|
|
uint8_t yawFine;
|
|
// chunk5 ViewDirection ($0A70). Multiplied by 16 inside
|
|
// SetupViewProjection's L6155 to bias the matrix's yaw.
|
|
// MAME's Meigs boot has $0A70 = $0F.
|
|
uint8_t viewDirection;
|
|
int16_t forwardSpeed; // Q8.8 world units per frame
|
|
int16_t rot[3][3]; // Q1.15 world -> camera rotation matrix (R^T)
|
|
// chunk5 SetupViewProjection lays $78..$89 out as R (camera-
|
|
// to-world), NOT R^T. sceneryAttachCamera mirrors this matrix
|
|
// into writableRam so downstream chunk5 paths (notably L631D
|
|
// section base) see the same shape they would on the
|
|
// original. Same data as `rot` but transposed.
|
|
int16_t rotChunk5[3][3];
|
|
} CameraT;
|
|
|
|
void cameraInit(CameraT *cam);
|
|
|
|
// Recompute the rotation matrix from pitch/bank/yaw. Call once after
|
|
// any orientation change.
|
|
void cameraUpdate(CameraT *cam);
|
|
|
|
// Transform a world-space point into camera space. Caller supplies
|
|
// a fresh `CameraT` (already updated this frame). All coords are
|
|
// Q16.16 world units.
|
|
void cameraTransform(const CameraT *cam, int32_t wx_q1616, int32_t wy_q1616, int32_t wz_q1616, int32_t *cx_q1616, int32_t *cy_q1616, int32_t *cz_q1616);
|
|
|
|
// Move the camera forward by `forwardSpeed` along its current heading.
|
|
void cameraStep(CameraT *cam);
|
|
|
|
// Decompose the camera's 3x3 rotation matrix into the 2x3 form chunk5
|
|
// uses (XZ in the world plane -> 3D camera-space). Each matrix entry
|
|
// is scaled to int8_t with $7F == 1.0 so MultiplyXY's int8 inputs see
|
|
// the right magnitude. Y (altitude) is handled per-section by the
|
|
// scenery $0D Header opcode and so is excluded from this matrix; the
|
|
// world driver handles altitude through the section-base instead.
|
|
void cameraGet2x3Matrix(const CameraT *cam, int8_t outRowX[3], int8_t outRowZ[3]);
|
|
|
|
#endif
|