3dcam/3dcam-tri-quads.c

2890 lines
93 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 3dcam
// With huge help from :
// @NicolasNoble : https://discord.com/channels/642647820683444236/646765703143227394/796876392670429204
// @Lameguy64
// @Impiaa
// @paul
/* PSX screen coordinate system
*
* Z+
* /
* /
* +------X+
* /|
* / |
* / Y+
* eye */
// Blender debug mode
// bpy. app. debug = True
#include <sys/types.h>
#include <libgte.h>
#include <libgpu.h>
#include <libetc.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
// Precalculated arctan values
#include "atan.c"
// Sample level
#include "coridor2.c"
//
#define VMODE 0
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define FOV CENTERX // With a FOV of 1/2, camera focal length is ~= 16 mm / 90°
// Lower values mean wider angle
// pixel > cm : used in physics calculations
#define SCALE 4
#define FNT_POS_X 960
#define FNT_POS_Y 256
#define OT2LEN 8
#define OTLEN 256 // Maximum number of OT entries
#define PRIMBUFFLEN 4096 * sizeof(POLY_GT4) // Maximum number of POLY_GT3 primitives
// MACROS
// swap(x, y, buffer)
#define SWAP(a,b,c) {(c)=(a); (a)=(b); (b)=(c);}
// dotproduct of two vectors
#define dotProduct(v0, v1) \
(v0).vx * (v1).vx + \
(v0).vy * (v1).vy + \
(v0).vz * (v1).vz
// min value
#define min(a,b) \
(a)-(b)>0?(b):(a)
// max
#define max(a,b) \
(a)-(b)>0?(a):(b)
// substract vector
#define subVector(v0, v1) \
(v0).vx - (v1).vx, \
(v0).vy - (v1).vy, \
(v0).vz - (v1).vz
#define normalizeVector(v) \
((v)->vx << 12) >> 8, \
((v)->vy << 12) >> 8, \
((v)->vz << 12) >> 8
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
// OT for BG/FG discrimination
u_long otdisc[2][OT2LEN] = {0};
// Main OT
u_long ot[2][OTLEN] = {0}; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN] = {0}; // Primitive list // That's our prim buffer
//~ int primcnt=0; // Primitive counter
char * nextpri = primbuff[0]; // Primitive counter
char db = 0; // Current buffer counter
CVECTOR BGc = {50, 50, 75, 0}; // Far color
VECTOR BKc = {128, 128, 128, 0}; // Back color
// Local color matrix
//~ static MATRIX cmat = {
//~ /* light source #0, #1, #2, */
//~ ONE, 0, 0, /* R */
//~ 0, ONE, 0, /* G */
//~ 0, 0, ONE, /* B */
//~ };
//~ // local light matrix : Direction and reach of each light source.
//~ // Each light is aligned with the axis, hence direction is in the same coordinate system as the PSX (Y-axis down)
//~ // One == 4096 is reach/intensity of light source
//~ static MATRIX lgtmat = {
//~ // X Y Z
//~ ONE, 0, 0, // Light 0
//~ 0,0,0, // Light 1
//~ 0,0,0 // Light 2
//~ };
// Light
//~ MATRIX rottrans;
MATRIX rotlgt;
SVECTOR lgtang = {0, 0, 0};
MATRIX light;
static int m_cosTable[512]; // precalc costable
static const unsigned int DC_2PI = 2048; // this is from here : https://github.com/grumpycoders/Balau/blob/master/tests/test-Handles.cc#L20-L102
static const unsigned int DC_PI = 1024;
static const unsigned int DC_PI2 = 512;
short vs;
typedef struct{
int x, xv; // x: current value += xv : new value
int y, yv; // x,y,z, vx, vy, vz are in PSX units (ONE == 4096)
int z, zv;
int pan, panv;
int tilt, tiltv;
int rol;
VECTOR pos;
SVECTOR rot;
SVECTOR dvs;
MATRIX mat;
} CAMERA;
CAMERA camera = {
0,0,
0,0,
0,0,
0,0,
0,0,
0,
{0,0,0},
{0,0,0},
{0,0,0}
};
// physics
long time = 0;
const int gravity = 10;
//Pad
int pressed = 0;
u_short timer = 0;
// Cam stuff
int camMode = 0;
long timeB = 0;
int lerping = 0;
short curCamAngle = 0;
// Inverted Cam coordinates for Forward Vector calc
VECTOR InvCamPos = {0,0,0,0};
VECTOR fVecActor = {0,0,0,0};
u_long triCount = 0;
// Prototypes
// Stolen from grumpycoder
// Sin/Cos Table
void generateTable(void);
int ncos(u_int t);
int nsin(u_int t);
// Atan table
long long patan(long x, long y);
// Sqrt
u_int psqrt(u_int n);
// fixed point math
static inline int32_t dMul(int32_t a, int32_t b);
static inline uint32_t lerpU(uint32_t start, uint32_t dest, unsigned pos);
static inline int32_t lerpS(int32_t start, int32_t dest, unsigned pos);
static inline int32_t lerpD(int32_t start, int32_t dest, int32_t pos);
static inline long long lerpL(long long start, long long dest, long long pos);
// PSX setup
void init(void);
void display(void);
// Utils
void LoadTexture(u_long * tim, TIM_IMAGE * tparam);
int cliptest3(short * v1);
int lerp(int start, int end, int factor); // FIXME : not working as it should
SVECTOR SVlerp(SVECTOR start, SVECTOR end, int factor); // FIXME
VECTOR getVectorTo(VECTOR actor, VECTOR target);
//~ int alignAxisToVect(VECTOR target, short axis, int factor);
void worldToScreen( VECTOR * worldPos, VECTOR * screenPos );
void screenToWorld( VECTOR * screenPos, VECTOR * worldPos );
short checkLineW( VECTOR * pointA, VECTOR * pointB, MESH * mesh );
short checkLineS( VECTOR * pointA, VECTOR * pointB, MESH * mesh );
// Drawing
void transformMesh(MESH * meshes);
void drawBG(void);
void drawPoly(MESH * meshes, long * Flag, int atime);
// Camera
void getCameraXZ(int * x, int * z, int actorX, int actorZ, int angle, int distance);
void applyCamera(CAMERA * cam);
void setCameraPos(VECTOR pos, SVECTOR rot);
// Physics
VECTOR getIntCollision(BODY one, BODY two);
VECTOR getExtCollision(BODY one, BODY two);
void ResolveCollision( BODY * one, BODY * two );
VECTOR angularMom(BODY body); // Not this kind of mom ;)
void applyAcceleration(BODY * actor);
// Pad
void callback();
int main() {
VECTOR sp = {CENTERX,CENTERY,0};
VECTOR wp = {0,0,0};
// FIXME : Poly subdiv
//~ DIVPOLYGON4 div4 = { 0 };
//~ div4.pih = SCREENXRES;
//~ div4.piv = SCREENYRES;
//~ div4.ndiv = 2;
//~ long OTc = 0;
//~ DIVPOLYGON3 div3 = { 0 };
//~ div3.pih = SCREENXRES;
//~ div3.piv = SCREENYRES;
//~ div3.ndiv = 1;
init();
generateTable();
VSyncCallback(callback);
// Load textures
for (int k = 0; k < sizeof(meshes)/sizeof(TMESH *); k++){
LoadTexture(meshes[k]->tim_data, meshes[k]->tim);
}
// Load current BG
if (camPtr->tim_data){
LoadTexture(camPtr->tim_data, camPtr->BGtim);
}
// Physics
short physics = 1;
long dt;
VECTOR col_lvl, col_sphere, col_sphere_act = {0};
// Cam stuff
VECTOR posToActor = {0, 0, 0, 0}; // position of camera relative to actor
VECTOR camAngleToAct = {0, 0, 0, 0}; // rotation angles for the camera to point at actor
// Sprite system
VECTOR posToCam = {0, 0, 0, 0};
VECTOR objAngleToCam = {0, 0, 0, 0};
int angle = 0; //PSX units = 4096 == 360° = 2Pi
int dist = 0; //PSX units
short timediv = 1;
int atime = 0;
// Polycount
for (int k = 0; k < sizeof(meshes)/sizeof(meshes[0]); k++){
triCount += meshes[k]->tmesh->len;
}
// Set camera starting pos
setCameraPos(camPtr->campos->pos, camPtr->campos->rot);
// Find curCamAngle if using pre-calculated BGs
if (camMode == 2) {
if (camPtr->tim_data){
curCamAngle = 1;
}
}
// Main loop
//~ while (1) {
while ( VSync(1) ) {
// Clear the main OT
ClearOTagR(otdisc[db], OT2LEN);
// Clear Secondary OT
ClearOTagR(ot[db], OTLEN);
// timeB = time;
time ++;
// atime is used for animations timing
timediv = 1;
if (time % timediv == 0){
atime ++;
}
// Angle between camera and actor
// using atantable (faster)
camAngleToAct.vy = (patan(-posToActor.vx, -posToActor.vz) / 16) - 3076 ;
camAngleToAct.vx = patan(dist, posToActor.vy) >> 4;
// Sprite system WIP
objAngleToCam.vy = patan( posToCam.vx,posToCam.vz );
objAngleToCam.vx = patan( posToCam.vx,posToCam.vy );
//~ objAngleToCam.vz = patan( posToCam.vz,posToCam.vy );
//~ objAngleToCam.vx = patan( psqrt(posToCam.vx * posToCam.vx + posToCam.vy * posToCam.vy), posToCam.vy );
//~ meshPlan.rot->vx = -( (objAngleToCam.vx >> 4) - 3076 ) ;
//~ meshPlan.rot->vx = (( (objAngleToCam.vx >> 4) - 3076 ) * ( (objAngleToCam.vz >> 4) - 3076 ) >> 12) * (nsin(posToCam.vz) >> 10 < 0 ? -1 : 1);
//~ meshPlan.rot->vx = ( (objAngleToCam.vx >> 4) - 3076 ) * ( (objAngleToCam.vz >> 4) - 3076 ) >> 12 ;
meshPlan.rot->vy = -( (objAngleToCam.vy >> 4) + 1024 ) ;
//~ posToCam = getVectorTo(*meshPlan.pos, camera.pos);
//~ posToCam = getVectorTo(camera.pos, *meshPlan.pos);
posToCam.vx = -camera.pos.vx - modelPlan_pos.vx ;
posToCam.vz = -camera.pos.vz - modelPlan_pos.vz ;
posToCam.vy = -camera.pos.vy - modelPlan_pos.vy ;
//~ psqrt(posToCam.vx * posToCam.vx + posToCam.vy * posToCam.vy);
// Actor Forward vector for 3d relative orientation
fVecActor = *actorPtr->pos;
fVecActor.vx = actorPtr->pos->vx + (nsin(actorPtr->rot->vy/2));
fVecActor.vz = actorPtr->pos->vz - (ncos(actorPtr->rot->vy/2));
// Camera modes
if(camMode != 2) {
camera.rot.vy = camAngleToAct.vy;
// using csin/ccos, no need for theta
//~ camera.rot.vy = angle;
camera.rot.vx = camAngleToAct.vx;
}
if(camMode < 4 ) {
lerping = 0;
}
// Camera follows actor with lerp for rotations
if(camMode == 0) {
dist = 150;
camera.pos.vx = -(camera.x/ONE);
//~ camera.pos.vy = -(camera.y/ONE);
camera.pos.vz = -(camera.z/ONE);
//~ InvCamPos.vx = camera.x/ONE;
//~ InvCamPos.vz = camera.z/ONE;
//~ applyVector(&InvCamPos, -1,-1,-1, *=);
angle = -actorPtr->rot->vy / 2;
//~ angle = actorPtr->rot->vy;
getCameraXZ(&camera.x, &camera.z, actorPtr->pos->vx, actorPtr->pos->vz, angle, dist);
// FIXME! camera lerping to pos
//~ angle += lerp(camera.rot.vy, -actorPtr->rot->vy, 128);
//~ angle = lerpD(camera.rot.vy << 12, actorPtr->rot->vy << 12, 1024 << 12) >> 12;
}
// Camera rotates continuously around actor
if (camMode == 1) {
dist = 150;
camera.pos.vx = -(camera.x/ONE);
//~ camera.pos.vy = -(camera.y/ONE);
camera.pos.vz = -(camera.z/ONE);
//~ fVecActor = *actorPtr->pos;
//~ fVecActor.vx = actorPtr->pos->vx + (nsin(actorPtr->rot->vy));
//~ fVecActor.vz = actorPtr->pos->vz - (ncos(actorPtr->rot->vy));
getCameraXZ(&camera.x, &camera.z, actorPtr->pos->vx, actorPtr->pos->vz, angle, dist);
angle += 10;
}
// Fixed Camera with actor tracking
if (camMode == 3) {
// Using precalc sqrt
dist = psqrt( (posToActor.vx * posToActor.vx ) + (posToActor.vz * posToActor.vz) );
camera.pos.vx = 190;
camera.pos.vz = 100;
camera.pos.vy = 180;
}
// Fixed Camera angle
if (camMode == 2) {
// If BG images exist
if (camPtr->tim_data){
checkLineW( &camAngles[ curCamAngle ]->fw.v3, &camAngles[ curCamAngle ]->fw.v2, actorPtr);
if ( camAngles[ curCamAngle ]->fw.v0.vx ) {
//~ FntPrint("BL x : %d, y : %d\n", camAngles[ curCamAngle ]->fw.v3.vx, camAngles[ curCamAngle ]->fw.v3.vy);
//~ FntPrint("BR x : %d, y : %d\n", camAngles[ curCamAngle ]->fw.v2.vx, camAngles[ curCamAngle ]->fw.v2.vy);
//~ FntPrint("Pos : %d\n", checkLineW( &camAngles[ curCamAngle ]->fw.v3, &camAngles[ curCamAngle ]->fw.v2, actorPtr) );
//~ FntPrint("Pos : %d\n", checkLineW( &camAngles[ curCamAngle ]->bw.v2, &camAngles[ curCamAngle ]->bw.v3, actorPtr) );
// If actor in camAngle->fw area of screen
if ( checkLineW( &camAngles[ curCamAngle ]->fw.v3, &camAngles[ curCamAngle ]->fw.v2, actorPtr) == -1 &&
( checkLineW( &camAngles[ curCamAngle ]->bw.v2, &camAngles[ curCamAngle ]->bw.v3, actorPtr) >= 0
)
) {
if (curCamAngle < 5) {
curCamAngle++;
camPtr = camAngles[ curCamAngle ];
LoadTexture(camPtr->tim_data, camPtr->BGtim);
}
}
}
if ( camAngles[ curCamAngle ]->bw.v0.vx ) {
//~ FntPrint("BL x : %d, y : %d\n", camAngles[ curCamAngle ]->bw.v3.vx, camAngles[ curCamAngle ]->bw.v3.vy);
//~ FntPrint("BR x : %d, y : %d\n", camAngles[ curCamAngle ]->bw.v2.vx, camAngles[ curCamAngle ]->bw.v2.vy);
//~ // FntPrint("Pos : %d\n", checkLineW( &camAngles[ curCamAngle ]->bw.v2, &camAngles[ curCamAngle ]->bw.v3, actorPtr) );
// If actor in camAngle->bw area of screen
if ( checkLineW( &camAngles[ curCamAngle ]->fw.v3, &camAngles[ curCamAngle ]->fw.v2, actorPtr) >= 0 &&
checkLineW( &camAngles[ curCamAngle ]->bw.v2, &camAngles[ curCamAngle ]->bw.v3, actorPtr) == -1
) {
if (curCamAngle > 0) {
curCamAngle--;
camPtr = camAngles[ curCamAngle ];
LoadTexture(camPtr->tim_data, camPtr->BGtim);
}
}
}
}
setCameraPos(camPtr->campos->pos, camPtr->campos->rot);
}
// Flyby mode with LERP from camStart to camEnd
if (camMode == 4) {
// If key pos exist for camera
if (camPath.len) {
// Lerping sequence has not begun
if (!lerping){
// Set cam start position ( first key pos )
camera.pos.vx = camPath.points[camPath.cursor].vx;
camera.pos.vy = camPath.points[camPath.cursor].vy;
camera.pos.vz = camPath.points[camPath.cursor].vz;
// Lerping sequence is starting
lerping = 1;
// Set cam pos index to 0
camPath.pos = 0;
}
// Pre calculated sqrt ( see psqrt() )
dist = psqrt( (posToActor.vx * posToActor.vx ) + (posToActor.vz * posToActor.vz));
// Fixed point precision 2^12 == 4096
int precision = 12;
camera.pos.vx = lerpD(camPath.points[camPath.cursor].vx << precision, camPath.points[camPath.cursor+1].vx << precision, camPath.pos << precision) >> precision;
camera.pos.vy = lerpD(camPath.points[camPath.cursor].vy << precision, camPath.points[camPath.cursor+1].vy << precision, camPath.pos << precision) >> precision;
camera.pos.vz = lerpD(camPath.points[camPath.cursor].vz << precision, camPath.points[camPath.cursor+1].vz << precision, camPath.pos << precision) >> precision;
//~ FntPrint("Cam %d, %d\n", (int32_t)camPath.points[camPath.cursor].vx, camPath.points[camPath.cursor+1].vx);
//~ FntPrint("Cam %d, %d, %d\n", camera.pos.vx, camera.pos.vy, camera.pos.vz);
//~ FntPrint("Theta y: %d x: %d\n", theta.vy, theta.vx);
//~ FntPrint("Pos: %d Cur: %d\nTheta y: %d x: %d\n", camPath.pos, camPath.cursor, theta.vy, theta.vx);
// Linearly increment the lerp factor
camPath.pos += 20;
// If camera has reached next key pos, reset pos index, move cursor to next key pos
if (camPath.pos > (1 << precision) ){
camPath.pos = 0;
camPath.cursor ++;
}
// Last key pos is reached, reset cursor to first key pos, lerping sequence is over
if ( camPath.cursor == camPath.len - 1 ){
lerping = 0;
camPath.cursor = 0;
}
} else {
// if no key pos exists, switch to next camMode
camMode ++; }
}
// Camera "on a rail" - cam is tracking actor, and moving with constraints on all axis
if (camMode == 5) {
// track actor. If theta (actor/cam rotation angle) is above or below an arbitrary angle,
// move cam so that the angle doesn't increase/decrease anymore.
short cameraSpeed = 40;
if (camPath.len) {
// Lerping sequence has not begun
if (!lerping){
// Set cam start position ( first key pos )
camera.pos.vx = camPath.points[camPath.cursor].vx;
camera.pos.vy = camPath.points[camPath.cursor].vy;
camera.pos.vz = camPath.points[camPath.cursor].vz;
// Lerping sequence is starting
lerping = 1;
// Set cam pos index to 0
camPath.pos = 0;
}
// Pre calculated sqrt ( see psqrt() )
dist = psqrt( (posToActor.vx * posToActor.vx ) + (posToActor.vz * posToActor.vz));
// Fixed point precision 2^12 == 4096
short precision = 12;
camera.pos.vx = lerpD(camPath.points[camPath.cursor].vx << precision, camPath.points[camPath.cursor + 1].vx << precision, camPath.pos << precision) >> precision;
camera.pos.vy = lerpD(camPath.points[camPath.cursor].vy << precision, camPath.points[camPath.cursor + 1].vy << precision, camPath.pos << precision) >> precision;
camera.pos.vz = lerpD(camPath.points[camPath.cursor].vz << precision, camPath.points[camPath.cursor + 1].vz << precision, camPath.pos << precision) >> precision;
//~ FntPrint("Cam %d, %d\n", (int32_t)camPath.points[camPath.cursor].vx, camPath.points[camPath.cursor+1].vx);
//~ FntPrint("Cam %d, %d, %d\n", camera.pos.vx, camera.pos.vy, camera.pos.vz);
//~ FntPrint("Pos: %d Cur: %d\nTheta y: %d x: %d\n", camPath.pos, camPath.cursor, theta.vy, theta.vx);
FntPrint("%d %d %d %d\n", camAngleToAct.vy, camera.pos.vx, camera.rot.vy, dist);
// Ony move cam if position is between first camPath.vx and last camPath.vx
if ( camAngleToAct.vy < -50 && camera.pos.vx > camPath.points[camPath.len - 1].vx ) {
// Clamp camPath position to cameraSpeed
camPath.pos += dist < cameraSpeed ? 0 : cameraSpeed ;
}
if ( camAngleToAct.vy > 50 && camera.pos.vx > camPath.points[camPath.cursor].vx ) {
camPath.pos -= dist < cameraSpeed ? 0 : cameraSpeed;
}
// If camera has reached next key pos, reset pos index, move cursor to next key pos
if (camPath.pos > (1 << precision) ){
camPath.pos = 0;
camPath.cursor ++;
//~ camPath.dir = 1;
}
if (camPath.pos < -100 ){
camPath.pos = 1 << precision;
camPath.cursor --;
//~ camPath.dir *= -1;
}
// Last key pos is reached, reset cursor to first key pos, lerping sequence is over
if ( camPath.cursor == camPath.len - 1 || camPath.cursor < 0 ){
lerping = 0;
camPath.cursor = 0;
}
} else {
// if no key pos exists, switch to next camMode
camMode ++;
}
}
//~ dt = time/180+1 - time/180;
// Spatial partitioning
for ( int msh = 0; msh < curNode->siblings->index; msh ++ ) {
// Actor
if ( !getIntCollision( *actorPtr->body , *curNode->siblings->list[msh]->plane->body).vx &&
!getIntCollision( *actorPtr->body , *curNode->siblings->list[msh]->plane->body).vz )
{
if ( curNode != curNode->siblings->list[msh] ) {
curNode = curNode->siblings->list[msh];
levelPtr = curNode->plane;
}
}
// FIXME !
// Moveable prop
//~ if ( !getIntCollision( *propPtr->body , *curNode->siblings->list[msh]->plane->body).vx &&
//~ !getIntCollision( *propPtr->body , *curNode->siblings->list[msh]->plane->body).vz ) {
//~ if ( propPtr->node != curNode->siblings->list[ msh ]){
//~ propPtr->node = curNode->siblings->list[ msh ];
//~ }
//~ }
if ( !getIntCollision( *propPtr->body , *curNode->plane->body).vx &&
!getIntCollision( *propPtr->body , *curNode->plane->body).vz ) {
propPtr->node = curNode;
}
}
// Physics
if ( physics ) {
// if(time%1 == 0){
for ( int k = 0; k < sizeof(meshes)/sizeof(meshes[0]);k ++ ) {
//~ for ( int k = 0; k < curNode->objects->index ; k ++){
if ( ( *meshes[k]->isRigidBody == 1 ) ) {
//~ if ( ( *curNode->rigidbodies->list[k]->isRigidBody == 1 ) ) {
//~ applyAcceleration(curNode->rigidbodies->list[k]->body);
applyAcceleration(meshes[k]->body);
// Get col with level ( modelgnd_body )
col_lvl = getIntCollision( *meshes[k]->body , *levelPtr->body );
col_sphere = getIntCollision( *propPtr->body, *propPtr->node->plane->body );
// col_sphere = getIntCollision( *propPtr->body, *levelPtr->body );
col_sphere_act = getExtCollision( *actorPtr->body, *propPtr->body );
// If no col with ground, fall off
if ( col_lvl.vy ) {
if ( !col_lvl.vx && !col_lvl.vz ) {
actorPtr->body->position.vy = actorPtr->body->min.vy;
}
}
if (col_sphere.vy){
if ( !col_sphere.vx && !col_sphere.vz ) {
propPtr->body->position.vy = propPtr->body->min.vy;
}
}
if (col_sphere_act.vx && col_sphere_act.vz ) {
propPtr->body->velocity.vx += actorPtr->body->velocity.vx;
propPtr->body->velocity.vz += actorPtr->body->velocity.vz;
if ( propPtr->body->velocity.vx ) {
VECTOR L = angularMom(*propPtr->body);
propPtr->rot->vz -= L.vx;
}
if ( propPtr->body->velocity.vz ) {
VECTOR L = angularMom( *propPtr->body );
propPtr->rot->vx -= L.vz;
}
}
meshes[k]->pos->vx = meshes[k]->body->position.vx;
meshes[k]->pos->vy = meshes[k]->body->position.vy ;
meshes[k]->pos->vz = meshes[k]->body->position.vz;
}
meshes[k]->body->velocity.vy = 0;
meshes[k]->body->velocity.vx = 0;
meshes[k]->body->velocity.vz = 0;
}
// }
}
if ( (camMode == 2) && (camPtr->tim_data ) ) {
worldToScreen(actorPtr->pos, &actorPtr->pos2D);
}
// Camera setup
// position of cam relative to actor
posToActor.vx = actorPtr->pos->vx + camera.pos.vx;
posToActor.vz = actorPtr->pos->vz + camera.pos.vz;
posToActor.vy = actorPtr->pos->vy + camera.pos.vy;
// Polygon drawing
static long Flag;
if ( (camMode == 2) && (camPtr->tim_data ) ) {
//~ if (camPtr->tim_data){
drawBG();
// Loop on camAngles
for ( int mesh = 0 ; mesh < camAngles[ curCamAngle ]->index; mesh ++ ) {
transformMesh(camAngles[curCamAngle]->objects[mesh]);
drawPoly(camAngles[curCamAngle]->objects[mesh], &Flag, atime);
}
// Get screen coordinates of actor
//~ }
}
else {
//~ long t = 0;
// Draw current node's plane
drawPoly( curNode->plane, &Flag, atime);
// Draw surrounding planes
for ( int sibling = 0; sibling < curNode->siblings->index; sibling++ ) {
drawPoly( curNode->siblings->list[ sibling ]->plane, &Flag, atime);
}
// Draw adjacent planes's children
for ( int sibling = 0; sibling < curNode->siblings->index; sibling++ ) {
for ( int object = 0; object < curNode->siblings->list[ sibling ]->objects->index; object++ ) {
long t = 0;
transformMesh(curNode->siblings->list[ sibling ]->objects->list[ object ]);
drawPoly( curNode->siblings->list[ sibling ]->objects->list[ object ], &Flag, atime);
}
}
// Draw current plane children
for ( int object = 0; object < curNode->objects->index; object++ ) {
transformMesh(curNode->objects->list[ object ]);
drawPoly( curNode->objects->list[ object ], &Flag, atime);
}
// Draw rigidbodies
for ( int object = 0; object < curNode->rigidbodies->index; object++ ) {
transformMesh(curNode->rigidbodies->list[ object ]);
drawPoly( curNode->rigidbodies->list[ object ], &Flag, atime);
}
}
// Find and apply light rotation matrix
RotMatrix(&lgtang, &rotlgt);
MulMatrix0(&lgtmat, &rotlgt, &light);
SetLightMatrix(&light);
// Set camera
applyCamera(&camera);
// Add secondary OT to main OT
AddPrims(otdisc[db], ot[db] + OTLEN - 1, ot[db]);
//~ FntPrint("CurNode : %x\nIndex: %d", curNode, curNode->siblings->index);
FntPrint("Time : %d dt :%d\n", VSync(-1) / 60, dt);
FntPrint("%d\n", curCamAngle );
//~ FntPrint("Actor : %d %d\n", actorPtr->pos->vx, actorPtr->pos->vy);
//~ FntPrint("%d %d\n", actorPtr->pos->vx, actorPtr->pos->vz);
//~ FntPrint("%d %d\n", actorPtr->pos2D.vx + CENTERX, actorPtr->pos2D.vy + CENTERY);
//~ FntPrint(" %d %d %d\n", wp.vx, wp.vy, wp.vz);
FntFlush(-1);
display();
//~ frame = VSync(-1);
}
return 0;
}
void init() {
ResetCallback();
// Reset the GPU before doing anything and the controller
ResetGraph(0);
PadInit(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset( CENTERX, CENTERY ); // x, y offset
SetGeomScreen( FOV ); // Distance between eye and screen - Camera FOV
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
// If PAL
if ( VMODE ) {
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
// Set Draw area color
setRGB0(&draw[0], BGc.r, BGc.g, BGc.b);
setRGB0(&draw[1], BGc.r, BGc.g, BGc.b);
// Set Draw area clear flag
draw[0].isbg = 1;
draw[1].isbg = 1;
// Set the disp and draw env
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(FNT_POS_X, FNT_POS_Y);
FntOpen(16, 90, 240, 180, 0, 512);
// Lighting setup
SetColorMatrix(&cmat);
SetBackColor(BKc.vx,BKc.vy,BKc.vz);
SetFarColor(BGc.r, BGc.g, BGc.b);
SetFogNearFar(1200, 1600, SCREENXRES);
};
void display(void){
//~ DrawSync(0);
vs = VSync(2); // Using VSync 2 insures constant framerate. 0 makes the fr polycount dependant.
ResetGraph(1);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
SetDispMask(1);
// Main OT
DrawOTag(otdisc[db] + OT2LEN - 1);
db = !db;
nextpri = primbuff[db];
};
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
};
void transformMesh(MESH * mesh){
MATRIX mat;
// Apply rotation matrix
RotMatrix_gte(mesh->rot, &mat);
// Apply translation matrix
TransMatrix(&mat, mesh->pos);
// Compose matrix with cam
CompMatrix(&camera.mat, &mat, &mat);
// Set default rotation and translation matrices
SetRotMatrix(&mat);
SetTransMatrix(&mat);
//~ }
};
// Drawing
void drawPoly(MESH * mesh, long * Flag, int atime){
long nclip, t = 0;
// mesh is POLY_GT3 ( triangle )
if (mesh->index[t].code == 4) {
POLY_GT3 * poly;
// len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
for ( int i = 0; i < (mesh->tmesh->len * 3); i += 3 ) {
// If mesh is not part of precalculated background, draw them, else, discard
if ( !( *mesh->isBG ) || camMode != 2) {
poly = (POLY_GT3 *)nextpri;
// If Vertex Anim flag is set, use it
if (*mesh->isAnim){
// If interpolation flag is set, use it
if(mesh->anim->interpolate){
// Ping pong
//~ //if (mesh->anim->cursor > 4096 || mesh->anim->cursor < 0){
//~ // mesh->anim->dir *= -1;
//~ //}
// Fixed point math precision
short precision = 12;
// Find next keyframe
if (mesh->anim->cursor > (1 << precision)) {
// There are still keyframes to interpolate between
if ( mesh->anim->lerpCursor < mesh->anim->nframes - 1 ) {
mesh->anim->lerpCursor ++;
mesh->anim->cursor = 0;
}
// We've reached last frame, go back to first frame
if ( mesh->anim->lerpCursor == mesh->anim->nframes - 1 ) {
mesh->anim->lerpCursor = 0;
mesh->anim->cursor = 0;
}
}
// Let's lerp between keyframes
// TODO : Finish lerped animation implementation
// Vertex 1
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vx = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vx].vx << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vx].vx << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vz = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vx].vz << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vx].vz << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vy = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vx].vy << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vx].vy << precision, mesh->anim->cursor << precision) >> precision;
// Vertex 2
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vx = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vz].vx << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vz].vx << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vz = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vz].vz << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vz].vz << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vy = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vz].vy << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vz].vy << precision, mesh->anim->cursor << precision) >> precision;
// Vertex 3
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vx = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vy].vx << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vy].vx << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vz = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vy].vz << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vy].vz << precision, mesh->anim->cursor << precision) >> precision;
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vy = lerpD( mesh->anim->data[mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[t].order.vy].vy << precision , mesh->anim->data[(mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[t].order.vy].vy << precision, mesh->anim->cursor << precision) >> precision;
mesh->anim->cursor += 24 * mesh->anim->dir;
// Coord transformation from world space to screen space
nclip = RotAverageNclip3(
&mesh->tmesh->v[ mesh->index[t].order.vx ],
&mesh->tmesh->v[ mesh->index[t].order.vz ],
&mesh->tmesh->v[ mesh->index[t].order.vy ],
( long* ) &poly->x0, ( long* ) &poly->x1, ( long* ) &poly->x2,
mesh->p,
mesh->OTz,
Flag
);
} else {
// No interpolation
// Use the pre-calculated vertices coordinates from the animation data
nclip = RotAverageNclip3(
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vx ],
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vz ],
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vy ],
( long* ) &poly->x0, ( long* ) &poly->x1, ( long* ) &poly->x2,
mesh->p,
mesh->OTz,
Flag
);
}
} else {
// No animation
// Use model's regular vertex coordinates
nclip = RotAverageNclip3(
&mesh->tmesh->v[ mesh->index[t].order.vx ],
&mesh->tmesh->v[ mesh->index[t].order.vz ],
&mesh->tmesh->v[ mesh->index[t].order.vy ],
( long * ) &poly->x0, ( long * ) &poly->x1, ( long * ) &poly->x2,
mesh->p,
mesh->OTz,
Flag
);
}
// Do not draw invisible meshes
if ( nclip > 0 && *mesh->OTz > 0 && (*mesh->p < 4096) ) {
SetPolyGT3( poly );
// If isPrism flag is set, use it
// FIXME : Doesn't work with pre-rendered BGs
if ( *mesh->isPrism ) {
// Transparency effect :
// Use current DRAWENV clip as TPAGE instead of regular textures
( (POLY_GT3 *) poly )->tpage = getTPage( mesh->tim->mode&0x3, 0,
draw[db].clip.x,
draw[db].clip.y
);
// Use projected coordinates (results from RotAverage...) as UV coords and clamp them to 0-255,0-224 Why 224 though ?
setUV3(poly, (poly->x0 < 0 ? 0 : poly->x0 > 255 ? 255 : poly->x0),
(poly->y0 < 0 ? 0 : poly->y0 > 240 ? 240 : poly->y0),
(poly->x1 < 0 ? 0 : poly->x1 > 255 ? 255 : poly->x1),
(poly->y1 < 0 ? 0 : poly->y1 > 240 ? 240 : poly->y1),
(poly->x2 < 0 ? 0 : poly->x2 > 255 ? 255 : poly->x2),
(poly->y2 < 0 ? 0 : poly->y2 > 240 ? 240 : poly->y2)
);
} else {
// No transparency effect
// Use regular TPAGE
( (POLY_GT3 *) poly )->tpage = getTPage(mesh->tim->mode&0x3, 0,
mesh->tim->prect->x,
mesh->tim->prect->y
);
setUV3(poly, mesh->tmesh->u[i].vx , mesh->tmesh->u[i].vy + mesh->tim->prect->y,
mesh->tmesh->u[i+2].vx, mesh->tmesh->u[i+2].vy + mesh->tim->prect->y,
mesh->tmesh->u[i+1].vx, mesh->tmesh->u[i+1].vy + mesh->tim->prect->y);
}
// CLUT setup
// If tim mode == 0 | 1 (4bits/8bits image), set CLUT coordinates
if ( (mesh->tim->mode & 0x3 ) < 2){
setClut(poly,
mesh->tim->crect->x,
mesh->tim->crect->y);
}
if (*mesh->isSprite){
SetShadeTex( poly, 1 );
}
// Defaults depth color to neutral grey
CVECTOR outCol = { 128,128,128,0 };
CVECTOR outCol1 = { 128,128,128,0 };
CVECTOR outCol2 = { 128,128,128,0 };
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vx ], &mesh->tmesh->c[ mesh->index[t].order.vx ], *mesh->p, &outCol);
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vz ], &mesh->tmesh->c[ mesh->index[t].order.vz ], *mesh->p, &outCol1);
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vy ], &mesh->tmesh->c[ mesh->index[t].order.vy ], *mesh->p, &outCol2);
// If transparent effect is in use, inhibate shadows
if (*mesh->isPrism){
// Use un-interpolated (i.e: no light, no fog) colors
setRGB0(poly, mesh->tmesh->c[i].r, mesh->tmesh->c[i].g, mesh->tmesh->c[i].b);
setRGB1(poly, mesh->tmesh->c[i+1].r, mesh->tmesh->c[i+1].g, mesh->tmesh->c[i+1].b);
setRGB2(poly, mesh->tmesh->c[i+2].r, mesh->tmesh->c[i+2].g, mesh->tmesh->c[i+2].b);
} else {
setRGB0(poly, outCol.r, outCol.g , outCol.b);
setRGB1(poly, outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly, outCol2.r, outCol2.g, outCol2.b);
}
if ( (*mesh->OTz > 0) && (*mesh->OTz < OTLEN) && (*mesh->p < 4096) ) {
AddPrim(&ot[db][*mesh->OTz-2], poly);
}
//~ mesh->pos2D.vx = *(&poly->x0);
//~ mesh->pos2D.vy = *(&poly->x0 + 1);
// mesh->pos2D.vy = poly->x0;
// FntPrint("%d %d\n", *(&poly->x0), *(&poly->x0 + 1));
nextpri += sizeof(POLY_GT3);
}
t+=1;
}
}
}
// If mesh is quad
if (mesh->index[t].code == 8) {
POLY_GT4 * poly4;
for (int i = 0; i < (mesh->tmesh->len * 4); i += 4) {
// if mesh is not part of BG, draw them, else, discard
if ( !(*mesh->isBG) || camMode != 2 ) {
poly4 = (POLY_GT4 *)nextpri;
// Vertex Anim
if (*mesh->isAnim){
// with interpolation
if ( mesh->anim->interpolate ){
// ping pong
//~ if (mesh->anim->cursor > 4096 || mesh->anim->cursor < 0){
//~ mesh->anim->dir *= -1;
//~ }
short precision = 12;
if ( mesh->anim->cursor > 1<<precision ) {
if ( mesh->anim->lerpCursor < mesh->anim->nframes - 1 ) {
mesh->anim->lerpCursor ++;
mesh->anim->cursor = 0;
}
if ( mesh->anim->lerpCursor == mesh->anim->nframes - 1 ) {
mesh->anim->lerpCursor = 0;
mesh->anim->cursor = 0;
}
}
// Vertex 1
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vx = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vx ].vx << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vx ].vx << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vz = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vx ].vz << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vx ].vz << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vx ].vy = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vx ].vy << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vx ].vy << 12, mesh->anim->cursor << 12) >> 12;
// Vertex 2
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vx = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vz ].vx << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vz ].vx << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vz = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vz ].vz << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vz ].vz << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vz ].vy = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vz ].vy << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vz ].vy << 12, mesh->anim->cursor << 12) >> 12;
// Vertex 3
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vx = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vy ].vx << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vy ].vx << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vz = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vy ].vz << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vy ].vz << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.vy ].vy = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.vy ].vy << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.vy ].vy << 12, mesh->anim->cursor << 12) >> 12;
// Vertex 4
mesh->tmesh->v[ mesh->index[ t ].order.pad ].vx = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.pad ].vx << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.pad ].vx << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.pad ].vz = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.pad ].vz << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.pad ].vz << 12, mesh->anim->cursor << 12) >> 12;
mesh->tmesh->v[ mesh->index[ t ].order.pad ].vy = lerpD( mesh->anim->data[ mesh->anim->lerpCursor * mesh->anim->nvert + mesh->index[ t ].order.pad ].vy << 12 , mesh->anim->data[ (mesh->anim->lerpCursor + 1) * mesh->anim->nvert + mesh->index[ t ].order.pad ].vy << 12, mesh->anim->cursor << 12) >> 12;
mesh->anim->cursor += 2 * mesh->anim->dir;
// Coord transformations
nclip = RotAverageNclip4(
&mesh->tmesh->v[ mesh->index[t].order.pad ],
&mesh->tmesh->v[ mesh->index[t].order.vz],
&mesh->tmesh->v[ mesh->index[t].order.vx ],
&mesh->tmesh->v[ mesh->index[t].order.vy ],
( long* )&poly4->x0, ( long* )&poly4->x1, ( long* )&poly4->x2, ( long* )&poly4->x3,
mesh->p,
mesh->OTz,
Flag
);
} else {
// No interpolation, use all vertices coordinates in anim data
nclip = RotAverageNclip4(
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.pad ],
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vz ],
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vx ],
&mesh->anim->data[ atime % mesh->anim->nframes * mesh->anim->nvert + mesh->index[t].order.vy ],
( long* )&poly4->x0, ( long* )&poly4->x1, ( long* )&poly4->x2, ( long* )&poly4->x3,
mesh->p,
mesh->OTz,
Flag
);
}
} else {
// No animation
// Use regulare vertex coords
nclip = RotAverageNclip4(
&mesh->tmesh->v[ mesh->index[t].order.pad ],
&mesh->tmesh->v[ mesh->index[t].order.vz],
&mesh->tmesh->v[ mesh->index[t].order.vx ],
&mesh->tmesh->v[ mesh->index[t].order.vy ],
(long*)&poly4->x0, (long*)&poly4->x1, (long*)&poly4->x2, (long*)&poly4->x3,
mesh->p,
mesh->OTz,
Flag
);
}
if (nclip > 0 && *mesh->OTz > 0 && (*mesh->p < 4096)) {
SetPolyGT4(poly4);
// FIXME : Polygon subdiv - is it working ?
//~ OTc = *mesh->OTz >> 4;
//~ FntPrint("OTC:%d", OTc);
//~ if (OTc < 4) {
//~ if (OTc > 1) div4.ndiv = 1; else div4.ndiv = 2;
//~ DivideGT4(
//~ // Vertex coord
//~ &mesh->tmesh->v[ mesh->index[t].order.pad ],
//~ &mesh->tmesh->v[ mesh->index[t].order.vz ],
//~ &mesh->tmesh->v[ mesh->index[t].order.vx ],
//~ &mesh->tmesh->v[ mesh->index[t].order.vy ],
//~ // UV coord
//~ mesh->tmesh->u[i+3],
//~ mesh->tmesh->u[i+2],
//~ mesh->tmesh->u[i+0],
//~ mesh->tmesh->u[i+1],
//~ // Color
//~ mesh->tmesh->c[i],
//~ mesh->tmesh->c[i+1],
//~ mesh->tmesh->c[i+2],
//~ mesh->tmesh->c[i+3],
//~ // Gpu packet
//~ poly4,
//~ &ot[db][*mesh->OTz],
//~ &div4);
//~ // Increment primitive list pointer
//~ nextpri += ( (sizeof(POLY_GT4) + 3) / 4 ) * (( 1 << ( div4.ndiv )) << ( div4.ndiv ));
//~ triCount = ((1<<(div4.ndiv))<<(div4.ndiv));
//~ } else if (OTc < 48) {
// Transparency effect
if (*mesh->isPrism){
// Use current DRAWENV clip as TPAGE
( (POLY_GT4 *) poly4)->tpage = getTPage(mesh->tim->mode&0x3, 0,
draw[db].clip.x,
draw[db].clip.y
);
// Use projected coordinates
setUV4( poly4,
(poly4->x0 < 0? 0 : poly4->x0 > 255? 255 : poly4->x0),
(poly4->y0 < 0? 0 : poly4->y0 > 224? 224 : poly4->y0),
(poly4->x1 < 0? 0 : poly4->x1 > 255? 255 : poly4->x1),
(poly4->y1 < 0? 0 : poly4->y1 > 224? 224 : poly4->y1),
(poly4->x2 < 0? 0 : poly4->x2 > 255? 255 : poly4->x2),
(poly4->y2 < 0? 0 : poly4->y2 > 224? 224 : poly4->y2),
(poly4->x3 < 0? 0 : poly4->x3 > 255? 255 : poly4->x3),
(poly4->y3 < 0? 0 : poly4->y3 > 224? 224 : poly4->y3)
);
} else {
// Use regular TPAGE
( (POLY_GT4 *) poly4)->tpage = getTPage(
mesh->tim->mode&0x3, 0,
mesh->tim->prect->x,
mesh->tim->prect->y
);
// Use model UV coordinates
setUV4( poly4,
mesh->tmesh->u[i+3].vx, mesh->tmesh->u[i+3].vy + mesh->tim->prect->y,
mesh->tmesh->u[i+2].vx, mesh->tmesh->u[i+2].vy + mesh->tim->prect->y,
mesh->tmesh->u[i+0].vx, mesh->tmesh->u[i+0].vy + mesh->tim->prect->y,
mesh->tmesh->u[i+1].vx, mesh->tmesh->u[i+1].vy + mesh->tim->prect->y
);
}
if (*mesh->isSprite){
SetShadeTex( poly4, 1 );
}
// If tim mode == 0 | 1, set CLUT coordinates
if ( (mesh->tim->mode & 0x3) < 2 ) {
setClut(poly4,
mesh->tim->crect->x,
mesh->tim->crect->y
);
}
CVECTOR outCol = {128,128,128,0};
CVECTOR outCol1 = {128,128,128,0};
CVECTOR outCol2 = {128,128,128,0};
CVECTOR outCol3 = {128,128,128,0};
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.pad ] , &mesh->tmesh->c[ mesh->index[t].order.pad ], *mesh->p, &outCol);
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vz ], &mesh->tmesh->c[ mesh->index[t].order.vz ], *mesh->p, &outCol1);
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vx ], &mesh->tmesh->c[ mesh->index[t].order.vx ], *mesh->p, &outCol2);
NormalColorDpq(&mesh->tmesh->n[ mesh->index[t].order.vy ], &mesh->tmesh->c[ mesh->index[t].order.vy ], *mesh->p, &outCol3);
if (*mesh->isPrism){
setRGB0(poly4, mesh->tmesh->c[i].r, mesh->tmesh->c[i].g, mesh->tmesh->c[i].b);
setRGB1(poly4, mesh->tmesh->c[i+1].r, mesh->tmesh->c[i+1].g, mesh->tmesh->c[i+1].b);
setRGB2(poly4, mesh->tmesh->c[i+2].r, mesh->tmesh->c[i+2].g, mesh->tmesh->c[i+2].b);
setRGB3(poly4, mesh->tmesh->c[i+3].r, mesh->tmesh->c[i+3].g, mesh->tmesh->c[i+3].b);
} else {
setRGB0(poly4, outCol.r, outCol.g , outCol.b);
setRGB1(poly4, outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly4, outCol2.r, outCol2.g, outCol2.b);
setRGB3(poly4, outCol3.r, outCol3.g, outCol3.b);
}
if ( (*mesh->OTz > 0) && (*mesh->OTz < OTLEN) && (*mesh->p < 4096) ) {
AddPrim( &ot[ db ][ *mesh->OTz-3 ], poly4 );
}
nextpri += sizeof( POLY_GT4 );
}
t += 1;
}
}
}
};
void drawBG(void){
// Draw BG image in two SPRT since max width == 256 px
SPRT * sprt;
DR_TPAGE * tpage;
// Left part
sprt = ( SPRT * ) nextpri;
setSprt( sprt );
setRGB0( sprt, 128, 128, 128 );
setXY0( sprt, 0, 0 );
setWH( sprt, 256, SCREENYRES );
setUV0( sprt, 0, 0 );
setClut( sprt,
camPtr->BGtim->crect->x,
camPtr->BGtim->crect->y
);
addPrim( &otdisc[ db ][ OT2LEN-1 ], sprt );
nextpri += sizeof( SPRT );
// Change TPAGE
tpage = (DR_TPAGE *) nextpri;
setDrawTPage(
tpage, 0, 1,
getTPage(
camPtr->BGtim->mode & 0x3, 0,
camPtr->BGtim->prect->x,
camPtr->BGtim->prect->y
)
);
addPrim(&otdisc[db][OT2LEN-1], tpage);
nextpri += sizeof(DR_TPAGE);
// Right part
sprt = ( SPRT * ) nextpri;
setSprt( sprt );
setRGB0( sprt, 128, 128, 128 );
setXY0( sprt, SCREENXRES - ( SCREENXRES - 256 ), 0 );
setWH( sprt, SCREENXRES - 256, SCREENYRES );
setUV0( sprt, 0, 0 );
setClut( sprt,
camPtr->BGtim->crect->x,
camPtr->BGtim->crect->y
);
addPrim( &otdisc[ db ][ OT2LEN-1 ], sprt );
nextpri += sizeof( SPRT );
tpage = ( DR_TPAGE * ) nextpri;
// Change TPAGE
setDrawTPage(
tpage, 0, 1,
getTPage(
camPtr->BGtim->mode & 0x3, 0,
// X offset width depends on TIM's mode
camPtr->BGtim->prect->x + ( 64 << ( camPtr->BGtim->mode & 0x3 ) ),
camPtr->BGtim->prect->y
)
);
addPrim( &otdisc[ db ][ OT2LEN-1 ], tpage );
nextpri += sizeof( DR_TPAGE );
};
// Maths
void getCameraXZ(int * x, int * z, int actorX, int actorZ, int angle, int distance) {
// Using Nic's Costable : https://github.com/grumpycoders/Balau/blob/master/tests/test-Handles.cc#L20-L102
// https://godbolt.org/z/q6cMcj
*x = (actorX << 12) + (distance * nsin(angle));
*z = (actorZ << 12) - (distance * ncos(angle));
};
// @Will : you might want to use sin/cos to move the camera in a circle but you could do that by moving it along its tangent and then clamping the distance
void applyCamera( CAMERA * cam ) {
VECTOR vec; // Vector that holds the output values of the following instructions
RotMatrix_gte(&cam->rot, &cam->mat); // Convert rotation angle in psx units (360° == 4096) to rotation matrix)
ApplyMatrixLV(&cam->mat, &cam->pos, &vec); // Multiply matrix by vector pos and output to vec
TransMatrix(&cam->mat, &vec); // Apply transform vector
SetRotMatrix(&cam->mat); // Set Rotation matrix
SetTransMatrix(&cam->mat); // Set Transform matrix
};
void setCameraPos( VECTOR pos, SVECTOR rot ) {
camera.pos = pos;
camera.rot = rot;
};
VECTOR getVectorTo( VECTOR actor, VECTOR target ) {
VECTOR direction = { subVector(target, actor) };
VECTOR Ndirection = {0,0,0,0};
u_int distSq = (direction.vx * direction.vx) + (direction.vz * direction.vz);
direction.pad = psqrt(distSq);
VectorNormal(&direction, &Ndirection);
return Ndirection ;
};
// From 'psyq/addons/graphics/ZIMEN/CLIP.C'
void worldToScreen( VECTOR * worldPos, VECTOR * screenPos ) {
int distToScreen; // corresponds to FOV
MATRIX curRot; // current rotation matrix
// Get current matrix and projection */
distToScreen = ReadGeomScreen();
ReadRotMatrix(&curRot);
// Get Rotation, Translation coordinates, apply perspective correction
// Muliply world coordinates vector by current rotation matrix, store in screenPos
ApplyMatrixLV(&curRot, worldPos, screenPos);
// Get world translation vectors from rot and add to screenPos vx, vy, vz
applyVector(screenPos, curRot.t[0], curRot.t[1], curRot.t[2], +=);
// Correct perspective
screenPos -> vx = screenPos -> vx * distToScreen / ( screenPos -> vz + 1 ) ; // Add 1 to avoid division by 0
screenPos -> vy = screenPos -> vy * distToScreen / ( screenPos -> vz + 1 ) ;
screenPos -> vz = distToScreen ;
};
void screenToWorld( VECTOR * screenPos, VECTOR * worldPos ) {
int distToScreen; // corresponds to FOV
MATRIX curRot, invRot; // current rotation matrix, transpose matrix
VECTOR Trans; // working translation vector
// Get current matrix and projection
distToScreen = ReadGeomScreen();
ReadRotMatrix( &curRot );
PushMatrix(); // Store matrix on the stack (slow!)
//// worldTrans = invRot * (screenPos - Rot.t)
// Get world translation
Trans.vx = screenPos->vx - curRot.t[0]; // Substract world translation from screenpos
Trans.vy = screenPos->vy - curRot.t[1];
Trans.vz = screenPos->vz - curRot.t[2];
// We want the inverse of the current rotation matrix.
//
// Inverse matrix : M^-1 = 1 / detM * T(M)
// We know that the determinant of a rotation matrix is 1, thus:
// M^-1 = T(M)
//
// Get transpose of current rotation matrix
// > The transpose of a matrix is a new matrix whose rows are the columns of the original.
// https://www.quora.com/What-is-the-geometric-interpretation-of-the-transpose-of-a-matrix
TransposeMatrix( &curRot, &invRot );
// Multiply the transpose of current rotation matrix by the current translation vector
ApplyMatrixLV( &invRot, &Trans, worldPos );
// Get original rotation matrix back
PopMatrix();
};
short checkLineW( VECTOR * pointA, VECTOR * pointB, MESH * mesh ) {
long val1 = ( ( mesh->body->position.vx + mesh->body->min.vx ) - pointA->vx ) * ( pointB->vy - pointA->vy ) - ( ( mesh->body->position.vz + mesh->body->min.vy ) - pointA->vy ) * ( pointB->vx - pointA->vx ) ;
long val2 = ( ( mesh->body->position.vx + mesh->body->max.vx ) - pointA->vx ) * ( pointB->vy - pointA->vy ) - ( ( mesh->body->position.vz + mesh->body->max.vy ) - pointA->vy ) * ( pointB->vx - pointA->vx ) ;
if ( val1 > 0 && val2 > 0 ) {
// right
return 1;
}
else if ( val1 < 0 && val2 < 0 ) {
// left
return -1;
}
else if ( val1 == 0 && val2 == 0 ) {
// identical
return 0;
}
else if (
( val1 > 0 && val2 == 0 ) ||
( val1 == 0 && val2 > 0 )
) {
// right
return 1;
}
else if (
( val1 < 0 && val2 == 0 ) ||
( val1 == 0 && val2 < 0 )
) {
// left
return -1;
}
else if (
( val1 < 0 && val2 > 0 ) ||
( val1 > 0 && val2 < 0 )
) {
// intersect
return 3;
}
};
// Screen space variant
short checkLineS( VECTOR * pointA, VECTOR * pointB, MESH * mesh ) {
// FIXME : mesh->body->min.vx is not in screen space
int val1 = ( ( mesh->pos2D.vx + mesh->body->min.vx ) - pointA->vx ) * ( pointB->vy - pointA->vy ) - ( ( mesh->pos2D.vy + mesh->body->min.vy ) - pointA->vy ) * ( pointB->vx - pointA->vx ) ;
int val2 = ( ( mesh->pos2D.vx + mesh->body->max.vx ) - pointA->vx ) * ( pointB->vy - pointA->vy ) - ( ( mesh->pos2D.vy + mesh->body->max.vy ) - pointA->vy ) * ( pointB->vx - pointA->vx ) ;
if ( val1 > 0 && val2 > 0 ) {
// right
return 1;
}
else if ( val1 < 0 && val2 < 0 ) {
// left
return -1;
}
else if ( val1 == 0 && val2 == 0 ) {
// identical
return 2;
}
else if (
( val1 > 0 && val2 == 0 ) ||
( val1 == 0 && val2 > 0 )
) {
// right
return 1;
}
else if (
( val1 < 0 && val2 == 0 ) ||
( val1 == 0 && val2 < 0 )
) {
// left
return -1;
}
else if (
( val1 < 0 && val2 > 0 ) ||
( val1 > 0 && val2 < 0 )
) {
// intersect
return 3;
}
};
//~ int alignAxisToVect(VECTOR target, short axis, int factor){
//~ }
// Lerp
int lerp(int start, int end, int factor){
// lerp interpolated cam movement
// InBetween = Value 1 + ( ( Value2 - Value1 ) * lerpValue ) ;
// lerpValue should be a float between 0 and 1.
return ( start ) + (( end - start ) * factor ) >> 12;
};
long long easeIn(long long i, int div){
return ((i << 7) * (i << 7) * (i << 7) / div ) >> 19;
};
int easeOut(int i){
return (4096 >> 7) - ((4096 - (i << 7)) * (4096 - (i << 7))) >> 12;
};
int easeInOut(int i, int div){
return lerp(easeIn(i, div), easeOut(i) , i);
};
SVECTOR SVlerp(SVECTOR start, SVECTOR end, int factor){
SVECTOR output = {0,0,0,0};
output.vx = lerp(start.vx, end.vx, factor);
output.vy = lerp(start.vy, end.vy, factor);
output.vz = lerp(start.vz, end.vz, factor);
return output;
};
// Physics
VECTOR getIntCollision(BODY one, BODY two){
VECTOR d1, d2, col;
short correction = 50;
d1.vx = (one.position.vx + one.max.vx) - (two.position.vx + two.min.vx);
d1.vy = (one.position.vy + one.max.vy) - (two.position.vy + two.min.vy);
d1.vz = (one.position.vz + one.max.vz) - (two.position.vz + two.min.vz);
d2.vx = (two.position.v