Compare commits

...

4 commits

2 changed files with 416 additions and 396 deletions

View file

@ -4,7 +4,7 @@
NAME := gbafix NAME := gbafix
CC := gcc # CC := gcc
RM := rm -rf RM := rm -rf
# `make V=` builds the binary in verbose build mode # `make V=` builds the binary in verbose build mode

View file

@ -3,541 +3,561 @@
// SPDX-FileContributor: Antonio Niño Díaz, 2022 // SPDX-FileContributor: Antonio Niño Díaz, 2022
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#define GBA_SCREEN_W 240 #define GBA_SCREEN_W 240
#define GBA_SCREEN_H 160 #define GBA_SCREEN_H 160
#define REG_DISPCNT *((volatile uint16_t *)0x04000000) #define REG_DISPCNT *((volatile uint16_t *)0x04000000)
#define REG_KEYINPUT *((volatile uint16_t *)0x04000130) #define REG_KEYINPUT *((volatile uint16_t *)0x04000130)
#define DEFAULT_REG_KEYINPUT *((volatile uint16_t *)0x04000130) #define DEFAULT_REG_KEYINPUT *((volatile uint16_t *)0x04000130)
#define KEY_DOWN_NOW(key) (~(REG_KEYINPUT) & key) #define KEY_DOWN_NOW(key) (~(REG_KEYINPUT) & key)
#define KEY_A 1 #define KEY_A 1
#define KEY_B 2 #define KEY_B 2
#define KEY_SELECT 3 #define KEY_SELECT 3
#define KEY_START 4 #define KEY_START 4
#define KEY_DPAD_RIGHT 5 #define KEY_DPAD_RIGHT 5
#define KEY_DPAD_LEFT 6 #define KEY_DPAD_LEFT 6
#define KEY_DPAD_UP 7 #define KEY_DPAD_UP 7
#define KEY_DPAD_DOWN 8 #define KEY_DPAD_DOWN 8
#define KEY_TRIGGER_LEFT 9 #define KEY_TRIGGER_LEFT 9
#define KEY_TRIGGER_RIGHT 10 #define KEY_TRIGGER_RIGHT 10
#define DISPCNT_BG_MODE_MASK (0x7) #define DISPCNT_BG_MODE_MASK (0x7)
#define DISPCNT_BG_MODE(n) ((n) & DISPCNT_BG_MODE_MASK) // 0 to 5 #define DISPCNT_BG_MODE(n) ((n) & DISPCNT_BG_MODE_MASK) // 0 to 5
#define DISPCNT_BG2_ENABLE (1 << 10) #define DISPCNT_BG2_ENABLE (1 << 10)
#define MEM_VRAM_MODE3_FB ((uint16_t *)0x06000000) #define MEM_VRAM_MODE4 ((uint8_t *)buffer)
#define SHOW_BACK 0x10;
#define FRONT_BUFFER (0x6000000)
#define BACK_BUFFER (0x600A000)
#define PALETTE ((uint16_t *)0x5000000)
static volatile uint8_t lastPaletteIndex = 0;
#define FIXED_POINT int32_t #define FIXED_POINT int32_t
#define fp 12 #define FP 12
#define SHIFT_THRESHOLD 0.05 #define SHIFT_THRESHOLD 0.05
#define SHIFT_THRESHOLD_FP ((1 << fp) * SHIFT_THRESHOLD) #define SHIFT_THRESHOLD_FP ((1 << FP) * SHIFT_THRESHOLD)
#define FLOAT2FIXED(value) (int)((value) * (1 << fp)) #define FLOAT2FIXED(value) (int)((value) * (1 << FP))
#define FIXED2FLOAT(value) ((value) / (float)(1 << fp)) #define FIXED2FLOAT(value) ((value) / (float)(1 << FP))
static uint8_t *buffer = (uint8_t *)FRONT_BUFFER;
static inline uint16_t static inline uint16_t
RGB15(uint16_t r, uint16_t g, uint16_t b) RGB15(uint16_t r, uint16_t g, uint16_t b)
{ {
return (r & 0x1F) | ((g & 0x1F) << 5) | ((b & 0x1F) << 10); return (r & 0x1F) | ((g & 0x1F) << 5) | ((b & 0x1F) << 10);
}
void
flipBuffers()
{
if(buffer == (uint8_t *)FRONT_BUFFER) {
REG_DISPCNT &= ~SHOW_BACK;
buffer = (uint8_t *)BACK_BUFFER;
} else {
REG_DISPCNT |= SHOW_BACK;
buffer = (uint8_t *)FRONT_BUFFER;
}
} }
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
#include <stdio.h>
#include <math.h> #include <math.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <float.h> #include <float.h>
#define VWIDTH 50
#define VHEIGHT 50
#define CUBE_WIDTH 10 #define CUBE_WIDTH 10
#define CUBE_WIDTH_FP ((1 << fp) * CUBE_WIDTH) #define CUBE_WIDTH_FP ((1 << FP) * CUBE_WIDTH)
enum faces { enum faces {
FACE_FRONT = 0, FACE_FRONT = 0,
FACE_LEFT, FACE_LEFT,
FACE_RIGHT, FACE_RIGHT,
FACE_BOTTOM, FACE_BOTTOM,
FACE_TOP, FACE_TOP,
FACE_BACK, FACE_BACK,
NUM_FACES, NUM_FACES,
}; };
#define STEP 5 #define STEP 20
#define STEP_FP ((1 << fp) * STEP) #define STEP_FP ((1 << FP) * STEP)
#define ACTION_STEP 0.1 #define ACTION_STEP 0.1
#define ACTION_STEP_FP ((1 << fp) * ACTION_STEP) #define ACTION_STEP_FP ((1 << FP) * ACTION_STEP)
#define PITCH_STEP 0.05 #define SCALE 5 // how much is our initial render scaled
#define ROLL_STEP 0.05
#define YAW_STEP 0.05
volatile FIXED_POINT K1 = 60; #define K1 (20)
volatile FIXED_POINT K2 = (2 * CUBE_WIDTH) + 20; #define K2 (2 * CUBE_WIDTH + 10)
#define MULT_FP(a, b) ((a * b) >> FP)
#define MULT_FP(a,b) ((a * b) >> fp)
#define SQ(n) (n * n) #define SQ(n) (n * n)
#define SQ_FP(n) (MULT_FP(n, n)) #define SQ_FP(n) (MULT_FP(n, n))
#define COORD2INDEX(x, y) (y * VWIDTH + x) #define GET_ROTATE_X_Q(a) ({ float _a = (a) ; \
#define COUPLE2INDEX(x) (COORD2INDEX(x[0], x[1])) struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
q.x = FLOAT2FIXED(sin(_a * .5)); q; })
#define GET_ROTATE_X_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \ #define GET_ROTATE_Y_Q(a) ({ float _a = (a) ; \
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \ struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
q.x = FLOAT2FIXED(sin(_a * .5)); q; }) q.y = FLOAT2FIXED(sin(_a * .5)); q; })
#define GET_ROTATE_Y_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \ #define GET_ROTATE_Z_Q(a) ({ float _a = (a) ; \
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \ struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
q.y = FLOAT2FIXED(sin(_a * .5)); q; }) q.z = FLOAT2FIXED(sin(_a * .5)); q; })
#define GET_ROTATE_Z_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
q.z = FLOAT2FIXED(sin(_a * .5)); q; })
//TODO Idle animations //TODO Idle animations
#define IS_IDLE (Idle.x || Idle.y || Idle.z) #define IS_IDLE (Idle.x || Idle.y || Idle.z)
#define RESET_IDLE {Idle.x = 0; Idle.y = 0; Idle.z = 0;} #define RESET_IDLE {Idle.x = 0; Idle.y = 0; Idle.z = 0;}
void
init_colors()
{
const uint16_t color_order[NUM_FACES + 1] = {
RGB15(0, 0, 0),
RGB15(31, 0, 0),
RGB15(0, 31, 0),
RGB15(0, 0, 31),
RGB15(0, 31, 31),
RGB15(31, 31, 0),
RGB15(31, 0, 31),
};
for ( ; lastPaletteIndex <= NUM_FACES ; ++lastPaletteIndex)
PALETTE[lastPaletteIndex] = color_order[lastPaletteIndex];
}
uint8_t
chooseColor(uint8_t c)
{
if (c >= 0 && c < NUM_FACES)
return c + 1; // palette 0 is bg
else
return 0;
}
void
putPx(uint16_t x, uint16_t y, uint8_t c)
{
for (uint8_t i = 0 ; i < SCALE ; ++i)
for (uint8_t j = 0 ; j < SCALE ; ++j) {
uint16_t pos = GBA_SCREEN_W * (y * SCALE + j) + x * SCALE + i;
buffer[pos] = chooseColor(c);
}
}
struct { struct {
char x; uint8_t x;
char y; uint8_t y;
char z; uint8_t z;
} Idle; } Idle;
struct Quaternions { struct Quaternions {
FIXED_POINT w; FIXED_POINT w;
FIXED_POINT x; FIXED_POINT x;
FIXED_POINT y; FIXED_POINT y;
FIXED_POINT z; FIXED_POINT z;
} Target, Current; } Target, Current;
FIXED_POINT interpolationStep = 0; static FIXED_POINT interpolationStep = 0;
FIXED_POINT zBuffer[VHEIGHT * VWIDTH]; static volatile uint16_t vertices[NUM_FACES * 2 * 4];
char output[VHEIGHT * VWIDTH]; static volatile FIXED_POINT zBuffer[NUM_FACES * 2];
static volatile char shouldBreak = 1; static volatile uint8_t shouldBreak = 1;
static volatile char currentlyMoving = 0; static volatile uint8_t currentlyMoving = 0;
static volatile char currentCountR = 0; static volatile uint8_t currentCountR = 0;
static volatile char frontFacingFace = FACE_FRONT; static volatile uint8_t frontFacingFace = FACE_FRONT;
void void
normalize(struct Quaternions *q) normalize(struct Quaternions *q)
{ {
float n = sqrt(FIXED2FLOAT(SQ_FP(q->w) + SQ_FP(q->x) + float n = sqrt(FIXED2FLOAT(SQ_FP(q->w) + SQ_FP(q->x) +
SQ_FP(q->y) + SQ_FP(q->z))); SQ_FP(q->y) + SQ_FP(q->z)));
if (n == 0) if (n == 0.|| n == 1.)
return; return;
q->w = FLOAT2FIXED(FIXED2FLOAT(q->w) / n);
q->x = FLOAT2FIXED(FIXED2FLOAT(q->x) / n); q->w = FLOAT2FIXED(FIXED2FLOAT(q->w) / n);
q->y = FLOAT2FIXED(FIXED2FLOAT(q->y) / n); q->x = FLOAT2FIXED(FIXED2FLOAT(q->x) / n);
q->z = FLOAT2FIXED(FIXED2FLOAT(q->z) / n); q->y = FLOAT2FIXED(FIXED2FLOAT(q->y) / n);
q->z = FLOAT2FIXED(FIXED2FLOAT(q->z) / n);
} }
struct Quaternions struct Quaternions
mult(struct Quaternions q, FIXED_POINT x, FIXED_POINT y, FIXED_POINT z) mult(struct Quaternions *q, FIXED_POINT x, FIXED_POINT y, FIXED_POINT z)
{ {
//p = q * p * qbar //p = q * p * qbar
struct Quaternions res; struct Quaternions res;
res.w = 0; // << 1 <=> * 2
res.x = MULT_FP(x, (SQ_FP(q.w) + SQ_FP(q.x) - SQ_FP(q.y) - SQ_FP(q.z))) res.w = 0;
+ (MULT_FP(y, (MULT_FP(q.x, q.y) - MULT_FP(q.w, q.z))) * 2) res.x = MULT_FP(x, (SQ_FP(q->w) + SQ_FP(q->x) - SQ_FP(q->y) - SQ_FP(q->z)))
+ (MULT_FP(z, (MULT_FP(q.x, q.z) + MULT_FP(q.w, q.y))) * 2); + (MULT_FP(y, (MULT_FP(q->x, q->y) - MULT_FP(q->w, q->z))) << 1)
+ (MULT_FP(z, (MULT_FP(q->x, q->z) + MULT_FP(q->w, q->y))) << 1);
res.y = (MULT_FP(x, (MULT_FP(q.x, q.y) + MULT_FP(q.w,q.z))) * 2) res.y = (MULT_FP(x, (MULT_FP(q->x, q->y) + MULT_FP(q->w,q->z))) << 1)
+ (MULT_FP(y, (SQ_FP(q.w) - SQ_FP(q.x) + SQ_FP(q.y) - SQ_FP(q.z)))) + (MULT_FP(y, (SQ_FP(q->w) - SQ_FP(q->x) + SQ_FP(q->y) - SQ_FP(q->z))))
+ (MULT_FP(z, (MULT_FP(q.y, q.z) - MULT_FP(q.w, q.x))) << 2); + (MULT_FP(z, (MULT_FP(q->y, q->z) - MULT_FP(q->w, q->x))) << 1);
res.z = (MULT_FP(x, (MULT_FP(q.x, q.z) - MULT_FP(q.w, q.y)))* 2) res.z = (MULT_FP(x, (MULT_FP(q->x, q->z) - MULT_FP(q->w, q->y))) << 1)
+ (MULT_FP(y, (MULT_FP(q.y, q.z) + MULT_FP(q.w, q.x))) * 2) + (MULT_FP(y, (MULT_FP(q->y, q->z) + MULT_FP(q->w, q->x))) << 1)
+ MULT_FP(z, (SQ_FP(q.w) - SQ_FP(q.x) - SQ_FP(q.y) + SQ_FP(q.z))); + MULT_FP(z, (SQ_FP(q->w) - SQ_FP(q->x) - SQ_FP(q->y) + SQ_FP(q->z)));
return res; return res;
} }
struct Quaternions // res in quat p
multQ(struct Quaternions p, struct Quaternions q) void
multQ(struct Quaternions *p, struct Quaternions *q)
{ {
if (p.x <= SHIFT_THRESHOLD_FP && p.x >= -SHIFT_THRESHOLD_FP if (p->x <= SHIFT_THRESHOLD_FP && p->x >= -SHIFT_THRESHOLD_FP
&& p.y <= SHIFT_THRESHOLD_FP && p.y >= -SHIFT_THRESHOLD_FP && p->y <= SHIFT_THRESHOLD_FP && p->y >= -SHIFT_THRESHOLD_FP
&& p.z <= SHIFT_THRESHOLD_FP && p.z >= -SHIFT_THRESHOLD_FP) && p->z <= SHIFT_THRESHOLD_FP && p->z >= -SHIFT_THRESHOLD_FP) {
return q; p = q;
return;
}
if (q.x <= SHIFT_THRESHOLD_FP && q.x >= -SHIFT_THRESHOLD_FP if (q->x <= SHIFT_THRESHOLD_FP && q->x >= -SHIFT_THRESHOLD_FP
&& q.y <= SHIFT_THRESHOLD_FP && q.y >= -SHIFT_THRESHOLD_FP && q->y <= SHIFT_THRESHOLD_FP && q->y >= -SHIFT_THRESHOLD_FP
&& q.z <= SHIFT_THRESHOLD_FP && q.z >= -SHIFT_THRESHOLD_FP) && q->z <= SHIFT_THRESHOLD_FP && q->z >= -SHIFT_THRESHOLD_FP)
return p; return;
struct Quaternions res = { FIXED_POINT w = MULT_FP(p->w, q->w) - MULT_FP(p->x, q->x) -
.w = MULT_FP(p.w, q.w) - MULT_FP(p.x, q.x) - MULT_FP(p->y, q->y) - MULT_FP(p->z, q->z);
MULT_FP(p.y, q.y) - MULT_FP(p.z, q.z), FIXED_POINT x = MULT_FP(p->w, q->x) + MULT_FP(p->x, q->w) +
.x = MULT_FP(p.w, q.x) + MULT_FP(p.x, q.w) + MULT_FP(p->y, q->z) - MULT_FP(p->z, q->y);
MULT_FP(p.y, q.z) - MULT_FP(p.z, q.y), FIXED_POINT y = MULT_FP(p->w, q->y) - MULT_FP(p->x, q->z) +
.y = MULT_FP(p.w, q.y) - MULT_FP(p.x, q.z) + MULT_FP(p->y, q->w) + MULT_FP(p->z, q->x);
MULT_FP(p.y, q.w) + MULT_FP(p.z, q.x), FIXED_POINT z = MULT_FP(p->w, q->z) + MULT_FP(p->x, q->y) -
.z = MULT_FP(p.w, q.z) + MULT_FP(p.x, q.y) - MULT_FP(p->y, q->x) + MULT_FP(p->z, q->w);
MULT_FP(p.y, q.x) + MULT_FP(p.z, q.w), p->w = w;
}; p->x = x;
p->y = y;
return res; p->z = z;
} }
uint16_t
chooseColor(char c)
{
switch (c) {
case FACE_FRONT:
return RGB15(31, 0, 0);
case FACE_BACK:
return RGB15(31, 15, 31);
case FACE_BOTTOM:
return RGB15(31, 0, 31);
case FACE_LEFT:
return RGB15(0, 0, 31);
case FACE_RIGHT:
return RGB15(0, 31, 31);
case FACE_TOP:
return RGB15(0, 31, 0);
default:
// BG
return RGB15(31, 31, 31);
}
}
char uint8_t
chooseMainFace() chooseMainFace()
{ {
int total = 0; // TODO
int faces[NUM_FACES] = {0}; return 0;
for (int k = 0; k < VWIDTH * VHEIGHT; ++k)
if (output[k] >= 0 && output[k] < NUM_FACES) {
faces[output[k]]++;
++total;
}
int max = 0, idx = 0;
for (int k = 0; k < NUM_FACES; ++k)
if (faces[k] > max) {
max = faces[k];
idx = k;
}
frontFacingFace = max > total * 0.9 ? idx : -1;
return frontFacingFace;
} }
char uint8_t
isInQuad(char curr[2], char top[2], char left[2], isInQuad(const uint16_t x, const uint16_t y, uint8_t current_face)
char right[2], char bot[2])
{ {
char *points[4] = {top, left, bot, right}; uint16_t *points = (uint16_t *)&vertices[current_face * 8];
char pos = 0, neg = 0; uint8_t pos = 0, neg = 0;
char x = curr[0]; int32_t d;
char y = curr[1];;
int d;
for (char i = 0; i < 4; ++i) { for (uint8_t i = 0; i < 4; ++i) {
if (points[i][0] == curr[0] && points[i][1] == curr[1]) if (points[2 * i] == x && points[2 * i + 1] == y)
return 1; return 1;
//Form a segment between the i'th point //Form a segment between the i'th point
char x1 = points[i][0]; uint16_t x1 = points[2 * i];
char y1 = points[i][1]; uint16_t y1 = points[2 * i + 1];
//And the i+1'th, or if i is the last, with the first point //And the i+1'th, or if i is the last, with the first point
char i2 = (i + 1) % 4; uint8_t i2 = (i + 1) % 4;
char x2 = points[i2][0]; uint16_t x2 = points[2 * i2];
char y2 = points[i2][1]; uint16_t y2 = points[2 * i2 + 1];
//Compute the cross product
d = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);
//Compute the cross product if (d > 0) ++pos;
d = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); if (d < 0) ++neg;
if (d > 0) ++pos; //If the sign changes, then point is outside
if (d < 0) ++neg; if (pos > 0 && neg > 0)
return 0;
}
//If the sign changes, then point is outside return 1;
if (pos > 0 && neg > 0) }
return 0;
} // the 4 vertices rendered do not make a convex quad
// we have to switch 2 vertices for that
// only works because we render 4 points left to right
void
makeConvex(uint16_t *points)
{
// little hack
uint16_t tmpX = points[6], tmpY = points[7];
points[6] = points[4];
points[7] = points[5];
points[4] = tmpX;
points[5] = tmpY;
return 1;
} }
void void
fill_quads(char current_face, char top[2], char left[2], fillQuads(uint8_t current_face)
char right[2], char bot[2])
{ {
if (current_face != 0) return; makeConvex((uint16_t *)&vertices[current_face * 8]);
output[COUPLE2INDEX(top)] = RGB15(0, 0, 15);
output[COUPLE2INDEX(left)] = RGB15(0, 0, 15);
output[COUPLE2INDEX(right)] = RGB15(0, 0, 15);
for (int y = top[1] ; y < bot[1] ; ++y) { uint16_t top = UINT16_MAX, bot = 0, left = UINT16_MAX, right = 0;
for (int x = left[0] ; x < right[0] ; ++x) { for (uint8_t k = 0 ; k < 8 ; ++k) {
char curr[2] = {x, y}; const uint16_t item = vertices[current_face * 8 + k];
if (isInQuad(curr, top, left, right, bot))
//zbuffer issue if (k & 1) {
{} if (item > bot)
//output[COORD2INDEX(x, y)] = current_face; bot = item;
} if (item < top)
} top = item;
} else {
if (item > right)
right = item;
if (item < left)
left = item;
}
}
for (uint16_t y = top ; y <= bot; ++y) {
for (uint16_t x = left ; x <= right ; ++x) {
if (isInQuad(x, y, current_face))
putPx(x, y, current_face);
}
}
}
uint8_t
detect(uint8_t current_face)
{
for (uint8_t k = current_face * 8; k < current_face * 8 + 8; ++k)
if (vertices[k] == UINT16_MAX)
return 0;
return 1;
}
int
comp(const void *p1, const void *p2) {
FIXED_POINT left = *(const FIXED_POINT *)p1;
FIXED_POINT right = *(const FIXED_POINT *)p2;
return ((left > right) - (left < right));
} }
void void
detect_and_fill_quads() detectAndFillQuads()
{ {
for (int current_face = 0 ; current_face < NUM_FACES ; ++current_face) { qsort((FIXED_POINT *)zBuffer, NUM_FACES, 2 * sizeof(FIXED_POINT), comp);
char last_top [2] = {VWIDTH, VHEIGHT}; for (uint8_t idx = 0 ; idx < NUM_FACES ; ++idx) {
char last_left[2] = {VWIDTH, 0}; char ch = zBuffer[2 * idx + 1];
char last_right [2] = {0, 0}; if (detect(ch))
char last_bot[2] = {0, 0}; fillQuads(ch);
char top [2] = {VWIDTH, VHEIGHT}; }
char left[2] = {VWIDTH, 0};
char right [2] = {0, 0};
char bot[2] = {0, 0};
for (char y = 0; y < VHEIGHT; ++y) {
for (char x = 0; x < VWIDTH; ++x) {
if (output[COORD2INDEX(x, y)] != current_face)
continue;
if (x <= left[0]) {
left[0] = x;
left[1] = y;
}
if (y <= top[1]) {
top[0] = x;
top[1] = y;
}
if (x >= right[0]) {
right[0] = x;
right[1] = y;
}
if (y >= bot[1]) {
bot[0] = x;
bot[1] = y;
}
}
}
fill_quads(current_face, top, left, right, bot);
}
} }
void void
printAscii() printAscii()
{ {
// TODO scale up detectAndFillQuads();
MEM_VRAM_MODE3_FB[120 + 80 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0); flipBuffers();
MEM_VRAM_MODE3_FB[136 + 80 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
MEM_VRAM_MODE3_FB[120 + 96 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
currentCountR = currentCountR == 31 ? 0 : 31;
detect_and_fill_quads();
for (int i = 0; i < VHEIGHT; ++i) {
for (int j = 0; j < VWIDTH; ++j) {
char prevc = 0;
char *c = output + (i * VWIDTH + j);
MEM_VRAM_MODE3_FB[(i + 50) * GBA_SCREEN_W + j + 50] = chooseColor(*c);
}
}
} }
void void
rotateCube(FIXED_POINT cubeX, FIXED_POINT cubeY, FIXED_POINT cubeZ, char ch) rotateCube(FIXED_POINT cubeX, FIXED_POINT cubeY, FIXED_POINT cubeZ, uint8_t ch)
{ {
struct Quaternions q = mult(Current, cubeX, cubeY, cubeZ); struct Quaternions q = mult(&Current, cubeX, cubeY, cubeZ);
int x = q.x >> fp; uint32_t x = q.x >> FP;
int y = q.y >> fp; uint32_t y = q.y >> FP;
// not fixed point yet!! // not fixed point yet!!
float invZ = (1 << fp) / (float)(q.z + K2 * (1 << fp)); float invZ = (1 << FP) / (float)(q.z + K2 * (1 << FP));
int screenX = (int)(VWIDTH * 0.5) + (int)((x) * K1) * invZ; int32_t screenX = CUBE_WIDTH * 2 + (int32_t)((x) * K1) * invZ;
int screenY = (int)(VHEIGHT * 0.5) + (int)((y) * K1) * invZ; int32_t screenY = CUBE_WIDTH * 2 + (int32_t)((y) * K1) * invZ;
//TODO luminescence //TODO luminescence
if (screenX > VWIDTH || screenX < 0) return; if (screenX > GBA_SCREEN_W || screenX < 0
|| screenY > GBA_SCREEN_H || screenY < 0) return;
int idx = screenY * VWIDTH + screenX; FIXED_POINT invZFixed = FLOAT2FIXED(invZ);
if (idx >= 0 && idx < VWIDTH * VHEIGHT) { uint8_t firstEmptyVertex = ch * 8;
invZ = FLOAT2FIXED(invZ); for ( ; vertices[firstEmptyVertex] != UINT16_MAX
if (zBuffer[idx] < invZ) { && firstEmptyVertex - ch * 8 < 8 ; firstEmptyVertex+=2);
zBuffer[idx] = invZ;
output[idx] = ch; vertices[firstEmptyVertex] = screenX;
} vertices[firstEmptyVertex + 1] = screenY;
}
if (zBuffer[2 * ch] < invZFixed) {
zBuffer[2 * ch] = invZFixed;
zBuffer[2 * ch + 1] = ch;
}
} }
struct Quaternions void
interpolate(struct Quaternions qa, struct Quaternions qb) interpolate(struct Quaternions *qa, struct Quaternions *qb)
{ {
frontFacingFace = -1; frontFacingFace = -1;
struct Quaternions res; float cosHalfTheta =
float cosHalfTheta = FIXED2FLOAT(MULT_FP(qa->w, qb->w) +
FIXED2FLOAT(MULT_FP(qa.w, qb.w) + MULT_FP(qa->x, qb->x) +
MULT_FP(qa.x, qb.x) + MULT_FP(qa->y, qb->y) +
MULT_FP(qa.y, qb.y) + MULT_FP(qa->z, qb->z));
MULT_FP(qa.z, qb.z)); //if qa = qb or qa = -qb then theta = 0 and we can return qa
//if qa = qb or qa = -qb then theta = 0 and we can return qa if (cosHalfTheta >= 1.0 || cosHalfTheta <= -1.0) {
if (cosHalfTheta >= 1.0 || cosHalfTheta <= -1.0) { goto exit;
res.w = qa.w; }
res.x = qa.x; if (cosHalfTheta < 0) {
res.y = qa.y; qb->w = -qb->w;
res.z = qa.z; qb->x = -qb->x;
goto exit; qb->y = -qb->y;
} qb->z = qb->z;
if (cosHalfTheta < 0) { cosHalfTheta = -cosHalfTheta;
qb.w = -qb.w; }
qb.x = -qb.x;
qb.y = -qb.y;
qb.z = qb.z;
cosHalfTheta = -cosHalfTheta;
}
float halfTheta = acos(cosHalfTheta); float halfTheta = acos(cosHalfTheta);
float sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta); float sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta);
//if theta = 180 degrees then result is not fully defined //if theta = 180 degrees then result is not fully defined
// we could rotate around any axis normal to qa or qb // we could rotate around any axis normal to qa or qb
if (sinHalfTheta < 0.001 && sinHalfTheta > -0.001) { if (sinHalfTheta < 0.01 && sinHalfTheta > -0.01) {
res.w = ((qa.w >> 1) + (qb.w >> 1)); qa->w = ((qa->w >> 1) + (qb->w >> 1));
res.x = ((qa.x >> 1) + (qb.x >> 1)); qa->x = ((qa->x >> 1) + (qb->x >> 1));
res.y = ((qa.y >> 1) + (qb.y >> 1)); qa->y = ((qa->y >> 1) + (qb->y >> 1));
res.z = ((qa.z >> 1) + (qb.z >> 1)); qa->z = ((qa->z >> 1) + (qb->z >> 1));
goto exit; goto exit;
} }
FIXED_POINT ratioA = FLOAT2FIXED(sin((1 - FIXED2FLOAT(interpolationStep)) * halfTheta) / sinHalfTheta); FIXED_POINT ratioA = FLOAT2FIXED(sin((1 - FIXED2FLOAT(interpolationStep)) * halfTheta) / sinHalfTheta);
FIXED_POINT ratioB = FLOAT2FIXED(sin(FIXED2FLOAT(interpolationStep) * halfTheta) / sinHalfTheta); FIXED_POINT ratioB = FLOAT2FIXED(sin(FIXED2FLOAT(interpolationStep) * halfTheta) / sinHalfTheta);
res.w = (MULT_FP(qa.w, ratioA) + MULT_FP(qb.w, ratioB)); qa->w = (MULT_FP(qa->w, ratioA) + MULT_FP(qb->w, ratioB));
res.x = (MULT_FP(qa.x, ratioA) + MULT_FP(qb.x, ratioB)); qa->x = (MULT_FP(qa->x, ratioA) + MULT_FP(qb->x, ratioB));
res.y = (MULT_FP(qa.y, ratioA) + MULT_FP(qb.y, ratioB)); qa->y = (MULT_FP(qa->y, ratioA) + MULT_FP(qb->y, ratioB));
res.z = (MULT_FP(qa.z, ratioA) + MULT_FP(qb.z, ratioB)); qa->z = (MULT_FP(qa->z, ratioA) + MULT_FP(qb->z, ratioB));
exit: exit:
interpolationStep += ACTION_STEP_FP; interpolationStep += ACTION_STEP_FP;
return res;
} }
void void
handleAngle(char input) handleAngle(uint8_t input)
{ {
// TODO // TODO
if (currentlyMoving == 0) { if (currentlyMoving == 0) {
currentlyMoving = input; currentlyMoving = input;
switch (input) { struct Quaternions tmp;
case 'w': switch (input) {
case 'W': case 'w':
Target = multQ(GET_ROTATE_X_Q(FLOAT2FIXED(M_PI_2)), Current); case 'W':
break; tmp = GET_ROTATE_X_Q(M_PI_2);
case 'a': break;
case 'A': case 'a':
Target = multQ(GET_ROTATE_Y_Q(-FLOAT2FIXED(M_PI_2)), Current); case 'A':
break; tmp = GET_ROTATE_Y_Q(-M_PI_2);
case 's': break;
case 'S': case 's':
Target = multQ(GET_ROTATE_X_Q(-FLOAT2FIXED(M_PI_2)), Current); case 'S':
break; tmp = GET_ROTATE_X_Q(-M_PI_2);
case 'd': break;
case 'D': case 'd':
Target = multQ(GET_ROTATE_Y_Q(FLOAT2FIXED(M_PI_2)), Current); case 'D':
break; tmp = GET_ROTATE_Y_Q(M_PI_2);
case 'q': break;
case 'Q': case 'q':
Target = multQ(GET_ROTATE_Z_Q(-FLOAT2FIXED(M_PI_2)), Current); case 'Q':
break; tmp = GET_ROTATE_Z_Q(-M_PI_2);
case 'e': break;
case 'E': case 'e':
Target = multQ(GET_ROTATE_Z_Q(FLOAT2FIXED(M_PI_2)), Current); case 'E':
break; tmp = GET_ROTATE_Z_Q(M_PI_2);
default: break;
currentlyMoving = 0; default:
//TODO idle movement currentlyMoving = 0;
} return;
normalize(&Target); //TODO idle movement
} else { }
if (interpolationStep < (1 << fp) - ACTION_STEP_FP * 2) { multQ(&tmp, &Target);
Current = interpolate(Current, Target); Target = tmp;
normalize(&Current); normalize(&Target);
} } else {
else { if (interpolationStep < (1 << FP) ) {
Current = Target; interpolate(&Current, &Target);
interpolationStep = 0; normalize(&Current);
currentlyMoving = 0; }
} else {
} Current = Target;
interpolationStep = 0;
currentlyMoving = 0;
}
}
} }
char uint8_t
getInput() getInput()
{ {
// TODO // TODO
char c = 'd'; uint8_t c = 's';
handleAngle(c); handleAngle(c);
return c; return c;
} }
int int
main() main()
{ {
REG_DISPCNT = DISPCNT_BG_MODE(3) | DISPCNT_BG2_ENABLE; REG_DISPCNT = DISPCNT_BG_MODE(4) | DISPCNT_BG2_ENABLE;
Current = GET_ROTATE_Z_Q(0);
init_colors();
Current = GET_ROTATE_Z_Q(0); while (1) {
memset(MEM_VRAM_MODE4, 0, GBA_SCREEN_H * GBA_SCREEN_W);
memset((uint16_t *)vertices, UINT16_MAX, sizeof(uint16_t) * NUM_FACES * 4 * 2);
//zBuff is not correct data struct, need a map or smth
memset((FIXED_POINT *)zBuffer, 0, NUM_FACES * 2 * sizeof(FIXED_POINT));
while (1) { for (FIXED_POINT cubeX = -CUBE_WIDTH_FP + 1 * (1 << FP);
memset(output, NUM_FACES, VWIDTH * VHEIGHT); cubeX <= CUBE_WIDTH_FP - 1 * (1 << FP); cubeX += STEP_FP - 2 * (1 << FP)) {
memset(zBuffer, 0xffffffff, VWIDTH * VHEIGHT * sizeof(FIXED_POINT)); for (FIXED_POINT cubeY = -CUBE_WIDTH_FP + 1 * (1 << FP);
cubeY <= CUBE_WIDTH_FP - 1 * (1 << FP); cubeY += STEP_FP - 2 * (1 << FP)) {
switch (frontFacingFace) {
case FACE_FRONT:
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
break;
case FACE_LEFT:
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
break;
case FACE_RIGHT:
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
break;
case FACE_BOTTOM:
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
break;
case FACE_TOP:
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_TOP);
break;
case FACE_BACK:
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
break;
default: // idk render all
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_TOP);
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
}
}
}
for (FIXED_POINT cubeX = -CUBE_WIDTH_FP + STEP_FP ; printAscii();
cubeX <= CUBE_WIDTH_FP - STEP_FP; cubeX += STEP_FP) { getInput();
for (FIXED_POINT cubeY = -CUBE_WIDTH_FP + STEP_FP; }
cubeY <= CUBE_WIDTH_FP - STEP_FP; cubeY += STEP_FP) {
switch (FACE_FRONT) {
case FACE_FRONT:
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
break;
case FACE_LEFT:
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
break;
case FACE_RIGHT:
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
break;
case FACE_BOTTOM:
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
break;
case FACE_TOP:
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_TOP);
break;
case FACE_BACK:
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
break;
default: // idk render all
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_TOP);
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
}
}
}
printAscii();
getInput();
}
} }