685 lines
16 KiB
C
685 lines
16 KiB
C
#define DEBUG
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
/* cursed struct definitions, thanks bx */
|
|
#include "types.h"
|
|
|
|
#define W 800
|
|
#define H 600
|
|
#define PI 3.14159265358979323844
|
|
|
|
int wireframe = 0;
|
|
|
|
void
|
|
die(char *msg)
|
|
{
|
|
fprintf(stderr, "%s\n", msg);
|
|
exit(1);
|
|
}
|
|
|
|
typedef struct {
|
|
unsigned char r, g, b, a;
|
|
} rgb;
|
|
const rgb WHITE = {255, 255, 255, 255};
|
|
const rgb BLACK = {0, 0, 0, 255};
|
|
const rgb RED = {255, 0, 0, 255};
|
|
const rgb GREEN = {0, 255, 0, 255};
|
|
const rgb BLUE = {0, 0, 255, 255};
|
|
|
|
typedef struct {
|
|
SDL_Renderer *renderer;
|
|
SDL_Window *window;
|
|
SDL_Texture *tex;
|
|
rgb *canvas;
|
|
} sdl_state;
|
|
|
|
typedef struct {
|
|
vec2 a, b, c;
|
|
} triangle2;
|
|
|
|
typedef struct {
|
|
vec3 a, b, c;
|
|
} triangle3;
|
|
|
|
typedef struct {
|
|
uint32_t vertex_num;
|
|
uint32_t tri_num; /* for loops */
|
|
uint32_t index_num;
|
|
vec3 *vertices;
|
|
uint32_t *indices;
|
|
} mesh;
|
|
|
|
typedef struct {
|
|
double nums[3][3];
|
|
} matrix3x3;
|
|
|
|
vec3
|
|
vec3_matrix3x3_multiply(vec3 v, matrix3x3 r)
|
|
{
|
|
return (vec3) {
|
|
.x = (v.x * r.nums[0][0]) + (v.y * r.nums[0][1]) + (v.z * r.nums[0][2]),
|
|
.y = (v.x * r.nums[1][0]) + (v.y * r.nums[1][1]) + (v.z * r.nums[1][2]),
|
|
.z = (v.x * r.nums[2][0]) + (v.y * r.nums[2][1]) + (v.z * r.nums[2][2])
|
|
};
|
|
}
|
|
|
|
camera
|
|
new_camera(double range, vec3 pos, vec3 target, vec3 up)
|
|
{
|
|
if (pos.x == target.x &&
|
|
pos.y == target.y &&
|
|
pos.z == target.z)
|
|
die("cannot create camera with same target and position");
|
|
|
|
camera ret = {
|
|
.range = range,
|
|
.pos = pos,
|
|
.target = target,
|
|
.up = up,
|
|
};
|
|
ret.dir = vec3_normalize(vec3_sub(pos, target));
|
|
ret.right = vec3_normalize(vec3_cross(up, ret.dir));
|
|
|
|
return ret;
|
|
}
|
|
|
|
mesh
|
|
new_mesh(uint32_t vertex_num, uint32_t tri_num, uint32_t index_num, vec3 vertices[], uint32_t indices[])
|
|
{
|
|
mesh ret;
|
|
|
|
ret.vertex_num = vertex_num;
|
|
ret.tri_num = tri_num;
|
|
ret.index_num = index_num;
|
|
ret.vertices = malloc(sizeof(vec3) * vertex_num);
|
|
for (uint32_t i = 0; i < vertex_num; i++)
|
|
ret.vertices[i] = vertices[i];
|
|
ret.indices = malloc(sizeof(uint32_t) * index_num);
|
|
for (uint32_t i = 0; i < index_num; i++)
|
|
ret.indices[i] = indices[i];
|
|
|
|
return ret;
|
|
}
|
|
|
|
void free_mesh(mesh mesh) { free(mesh.vertices); free(mesh.indices); }
|
|
|
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
#define swap(x, y) do { \
|
|
(x) ^= (y); \
|
|
(y) ^= (x); \
|
|
(x) ^= (y); \
|
|
} while (0)
|
|
#define clamp(x, min, max) (((x) >= (min)) ? ((x) <= (max)) ? (x) : (max) : (min))
|
|
|
|
void
|
|
swapd(double *a, double *b)
|
|
{
|
|
double tmp = *a;
|
|
*a = *b;
|
|
*b = tmp;
|
|
}
|
|
|
|
void clear_canvas(rgb canvas[], rgb colour);
|
|
sdl_state init_sdl(void);
|
|
void free_sdl(sdl_state state);
|
|
void render(sdl_state state);
|
|
void plot_rgb(rgb canvas[], int32_t x, int32_t y, rgb colour);
|
|
void draw_line(rgb canvas[], rgb c, vec2 p0, vec2 p1);
|
|
vec2 project(camera c, vec3 v);
|
|
|
|
sdl_state
|
|
init_sdl(void)
|
|
{
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
|
|
sdl_state ret;
|
|
|
|
ret.window = SDL_CreateWindow("Game",
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
W,
|
|
H,
|
|
// TODO: when we have a proper event loop, we can
|
|
// add resizing windows.
|
|
/* SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN); */
|
|
SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_SHOWN);
|
|
ret.renderer = SDL_CreateRenderer(ret.window, -1, SDL_RENDERER_SOFTWARE);
|
|
SDL_SetRenderDrawColor(ret.renderer, 0, 0, 0, 255);
|
|
SDL_RenderClear(ret.renderer);
|
|
SDL_RenderPresent(ret.renderer);
|
|
|
|
ret.tex = SDL_CreateTexture(ret.renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STATIC,
|
|
W, H);
|
|
|
|
ret.canvas = malloc(sizeof(rgb) * W * H);
|
|
clear_canvas(ret.canvas, (rgb){0, 0, 0, 255});
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
free_sdl(sdl_state state)
|
|
{
|
|
SDL_DestroyRenderer(state.renderer);
|
|
SDL_DestroyWindow(state.window);
|
|
SDL_DestroyTexture(state.tex);
|
|
free(state.canvas);
|
|
SDL_Quit();
|
|
}
|
|
|
|
uint32_t
|
|
rgb_to_int(rgb c)
|
|
{
|
|
return (((uint32_t)c.a << 24)
|
|
| ((uint32_t)c.r << 16)
|
|
| ((uint32_t)c.g << 8)
|
|
| ((uint32_t)c.b));
|
|
}
|
|
|
|
/* display the contents of the canvas */
|
|
void
|
|
render(sdl_state state)
|
|
{
|
|
uint32_t pixels[H*W];
|
|
for (size_t y = 0; y < H; y++) {
|
|
for (size_t x = 0; x < W; x++) {
|
|
pixels[x + y * W] = rgb_to_int(state.canvas[x + y * W]);
|
|
}
|
|
}
|
|
SDL_UpdateTexture(state.tex, NULL, pixels, W * sizeof(uint32_t));
|
|
|
|
/* present the texture */
|
|
SDL_RenderCopy(state.renderer, state.tex, NULL, NULL);
|
|
SDL_RenderPresent(state.renderer);
|
|
}
|
|
|
|
|
|
void
|
|
plot_rgb(rgb canvas[], int32_t x, int32_t y, rgb colour)
|
|
{
|
|
if (x < W && y < H && x >= 0 && y >= 0)
|
|
canvas[x + y * W] = colour;
|
|
}
|
|
|
|
void
|
|
clear_canvas_trailing(rgb canvas[], rgb colour)
|
|
{
|
|
for (size_t i = 0; i < W * H; i++)
|
|
if ((rand()&31)==0) canvas[i] = colour;
|
|
}
|
|
|
|
void
|
|
clear_canvas(rgb canvas[], rgb colour)
|
|
{
|
|
for (size_t i = 0; i < W * H; i += 2) {
|
|
canvas[i] = colour;
|
|
canvas[i+1] = colour;
|
|
}
|
|
}
|
|
|
|
void
|
|
draw_line(rgb canvas[], rgb c, vec2 p0, vec2 p1)
|
|
{
|
|
int32_t x0 = (int32_t)p0.x,
|
|
x1 = (int32_t)p1.x,
|
|
y0 = (int32_t)p0.y,
|
|
y1 = (int32_t)p1.y;
|
|
|
|
if (x0 > x1) {
|
|
swap(x0, x1);
|
|
swap(y0, y1);
|
|
}
|
|
|
|
int dx = abs(x1 - x0);
|
|
int dy = abs(y1 - y0);
|
|
int eps = 0;
|
|
|
|
if (y1 > y0) {
|
|
if (dy < dx) {
|
|
int y = y0;
|
|
for (int x = x0; x <= x1; x++) {
|
|
plot_rgb(canvas, x, y, c);
|
|
eps += dy;
|
|
if ((eps * 2) >= dx) {
|
|
y++;
|
|
eps -= dx;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int x = x0;
|
|
for (int y = y0; y <= y1; y++) {
|
|
plot_rgb(canvas, x, y, c);
|
|
eps += dx;
|
|
if ((eps * 2) >= dy) {
|
|
x++;
|
|
eps -= dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (dy < dx) {
|
|
int y = y0;
|
|
for (int x = x0; x <= x1; x++) {
|
|
plot_rgb(canvas, x, y, c);
|
|
eps += dy;
|
|
if ((eps * 2) >= dx) {
|
|
y--;
|
|
eps -= dx;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
swap(x0, x1);
|
|
swap(y0, y1);
|
|
int x = x0;
|
|
for (int y = y0; y <= y1; y++) {
|
|
plot_rgb(canvas, x, y, c);
|
|
eps += dx;
|
|
if ((eps * 2) >= dy) {
|
|
x--;
|
|
eps -= dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
draw_horizontal_line(rgb canvas[], rgb c, int32_t x0, int32_t x1, int32_t y)
|
|
{
|
|
if (y < 0) return;
|
|
if (x0 < 0) x0 = 0;
|
|
if (x1 >= W) x1 = W - 1;
|
|
if (y >= H) return;
|
|
|
|
for (int32_t x = x0; x <= x1; x++)
|
|
canvas[x + y * W] = c;
|
|
}
|
|
|
|
void
|
|
draw_triangle(rgb canvas[], rgb col, triangle2 tri)
|
|
{
|
|
draw_line(canvas, col, tri.a, tri.b);
|
|
draw_line(canvas, col, tri.b, tri.c);
|
|
draw_line(canvas, col, tri.c, tri.a);
|
|
}
|
|
|
|
void
|
|
fill_bottom_flat_triangle(rgb canvas[], rgb c, triangle2 tri)
|
|
{
|
|
if (tri.b.x > tri.c.x)
|
|
swapd(&tri.b.x, &tri.c.x);
|
|
|
|
/* tri.a x
|
|
* / \
|
|
* / \
|
|
* tri.b x_____x tri.c
|
|
*/
|
|
|
|
int32_t ax = (int32_t)tri.a.x,
|
|
bx = (int32_t)tri.b.x,
|
|
cx = (int32_t)tri.c.x,
|
|
ay = (int32_t)tri.a.y,
|
|
by = (int32_t)tri.b.y,
|
|
cy = (int32_t)tri.c.y;
|
|
|
|
assert(ay <= cy);
|
|
assert(ay <= by);
|
|
|
|
assert(bx <= cx);
|
|
|
|
int32_t dx_right = abs(cx - ax);
|
|
int32_t dy_right = abs(cy - ay);
|
|
int32_t dx_left = abs(bx - ax);
|
|
int32_t dy_left = abs(by - ay);
|
|
|
|
int32_t eps_r = 0;
|
|
int32_t eps_l = 0;
|
|
int32_t xr = ax;
|
|
int32_t xl = ax;
|
|
|
|
int32_t xr_change = ax < cx ? 1 : -1;
|
|
int32_t xl_change = ax < bx ? 1 : -1;
|
|
|
|
for (int32_t y = ay; y < by; y++) {
|
|
eps_r += dx_right;
|
|
eps_l += dx_left;
|
|
while (eps_r > 0) {
|
|
eps_r -= dy_right;
|
|
xr += xr_change;
|
|
}
|
|
while (eps_l > 0) {
|
|
eps_l -= dy_left;
|
|
xl += xl_change;
|
|
}
|
|
draw_horizontal_line(canvas, c, xl, xr, y);
|
|
}
|
|
}
|
|
|
|
void
|
|
fill_top_flat_triangle(rgb canvas[], rgb c, triangle2 tri)
|
|
{
|
|
if (tri.a.x > tri.b.x)
|
|
swapd(&tri.a.x, &tri.b.x);
|
|
|
|
/* tri.a x_____x tri.b
|
|
* \ /
|
|
* \ /
|
|
* tri.c x
|
|
*/
|
|
|
|
int32_t ax = (int32_t)tri.a.x,
|
|
bx = (int32_t)tri.b.x,
|
|
cx = (int32_t)tri.c.x,
|
|
ay = (int32_t)tri.a.y,
|
|
by = (int32_t)tri.b.y,
|
|
cy = (int32_t)tri.c.y;
|
|
|
|
assert(cy >= ay);
|
|
assert(cy >= by);
|
|
|
|
assert(ax <= bx);
|
|
|
|
int32_t dx_right = abs(cx - bx);
|
|
int32_t dy_right = abs(cy - by);
|
|
int32_t dx_left = abs(cx - ax);
|
|
int32_t dy_left = abs(cy - ay);
|
|
|
|
int32_t eps_r = 0;
|
|
int32_t eps_l = 0;
|
|
int32_t xr = cx;
|
|
int32_t xl = cx;
|
|
|
|
int32_t xr_change = cx < bx ? 1 : -1;
|
|
int32_t xl_change = cx < ax ? 1 : -1;
|
|
|
|
int32_t y;
|
|
for (y = cy; y > by; y--) {
|
|
eps_r += dx_right;
|
|
eps_l += dx_left;
|
|
while (eps_r > 0) {
|
|
eps_r -= dy_right;
|
|
xr += xr_change;
|
|
}
|
|
while (eps_l > 0) {
|
|
eps_l -= dy_left;
|
|
xl += xl_change;
|
|
}
|
|
draw_horizontal_line(canvas, c, xl, xr, y);
|
|
}
|
|
|
|
/* We're missing one line, but setting the loop conditional to be y >= by
|
|
* messes up the drawing.
|
|
*/
|
|
draw_horizontal_line(canvas, c, xl, xr, y);
|
|
}
|
|
|
|
/* http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html */
|
|
void
|
|
fill_triangle(rgb canvas[], rgb c, triangle2 tri)
|
|
{
|
|
/* tri.a <= tri.b <= tri.c */
|
|
if (tri.b.y < tri.a.y) {
|
|
swapd(&tri.b.y, &tri.a.y);
|
|
swapd(&tri.b.x, &tri.a.x);
|
|
}
|
|
if (tri.c.y < tri.a.y) {
|
|
swapd(&tri.c.y, &tri.a.y);
|
|
swapd(&tri.c.x, &tri.a.x);
|
|
}
|
|
if (tri.c.y < tri.b.y) {
|
|
swapd(&tri.c.y, &tri.b.y);
|
|
swapd(&tri.c.x, &tri.b.x);
|
|
}
|
|
|
|
/* simple solutions */
|
|
if ((int32_t)tri.b.y == (int32_t)tri.c.y) {
|
|
fill_bottom_flat_triangle(canvas, c, tri);
|
|
}
|
|
else if ((int32_t)tri.a.y == (int32_t)tri.b.y) {
|
|
fill_top_flat_triangle(canvas, c, tri);
|
|
}
|
|
else {
|
|
vec2 trid = {
|
|
(tri.a.x + (tri.b.y - tri.a.y) / (tri.c.y - tri.a.y) * (tri.c.x - tri.a.x)),
|
|
tri.b.y};
|
|
|
|
if (tri.a.y > tri.b.y) {
|
|
swapd(&tri.a.y, &tri.b.y);
|
|
swapd(&tri.a.x, &tri.b.x);
|
|
}
|
|
|
|
fill_top_flat_triangle(canvas, c, (triangle2) {
|
|
tri.b, trid, tri.c
|
|
});
|
|
fill_bottom_flat_triangle(canvas, c, (triangle2) {
|
|
tri.a, tri.b, trid
|
|
});
|
|
}
|
|
}
|
|
|
|
vec2
|
|
midpoint(vec2 a, vec2 b)
|
|
{
|
|
return (vec2) {
|
|
(a.x + b.x) / 2,
|
|
(a.y + b.y) / 2
|
|
};
|
|
}
|
|
|
|
vec2
|
|
project(camera c, vec3 v)
|
|
{
|
|
double r = 200 / (v.z + c.range);
|
|
return (vec2){W / 2 + r * v.x, H / 2 + r * v.y};
|
|
}
|
|
|
|
triangle2
|
|
project_triangle(camera c, triangle3 tri)
|
|
{
|
|
return (triangle2) {
|
|
.a = project(c, tri.a),
|
|
.b = project(c, tri.b),
|
|
.c = project(c, tri.c),
|
|
};
|
|
}
|
|
|
|
/* saves canvas as a P6 image */
|
|
void
|
|
screenshot(rgb canvas[])
|
|
{
|
|
time_t t = time(NULL);
|
|
char output[64];
|
|
strftime(output, sizeof(output), "screenshot-%Y%m%d-%H%M%S.ppm", localtime(&t));
|
|
FILE *out = fopen(output, "w");
|
|
fprintf(out, "P6\n%d %d\n255\n", W, H);
|
|
for (int y = 0; y < H; y++) {
|
|
for (int x = 0; x < W; x++) {
|
|
rgb cur = canvas[x + y * W];
|
|
fprintf(out, "%c%c%c", cur.r, cur.g, cur.b);
|
|
}
|
|
}
|
|
fclose(out);
|
|
fprintf(stderr, "Saved %s\n", output);
|
|
}
|
|
|
|
void
|
|
draw_mesh(rgb canvas[], rgb col, camera c, mesh mesh)
|
|
{
|
|
for (uint32_t i = 0; i < mesh.tri_num; i++) {
|
|
if (wireframe)
|
|
draw_triangle(canvas, col, project_triangle(c, (triangle3) {
|
|
mesh.vertices[mesh.indices[3*i ]],
|
|
mesh.vertices[mesh.indices[3*i+1]],
|
|
mesh.vertices[mesh.indices[3*i+2]],
|
|
}));
|
|
else
|
|
fill_triangle(canvas, col, project_triangle(c, (triangle3) {
|
|
mesh.vertices[mesh.indices[3*i ]],
|
|
mesh.vertices[mesh.indices[3*i+1]],
|
|
mesh.vertices[mesh.indices[3*i+2]],
|
|
}));
|
|
}
|
|
}
|
|
|
|
matrix3x3
|
|
matrix3x3_multiply(matrix3x3 r, matrix3x3 s)
|
|
{
|
|
#define a r.nums
|
|
#define b s.nums
|
|
return (matrix3x3) {
|
|
.nums =
|
|
{{a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],
|
|
a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],
|
|
a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2]},
|
|
{a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],
|
|
a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],
|
|
a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2]},
|
|
{a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],
|
|
a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],
|
|
a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2]}}
|
|
};
|
|
#undef a
|
|
#undef b
|
|
}
|
|
|
|
double
|
|
to_deg(double deg)
|
|
{
|
|
return deg * 2 * PI / 360.0;
|
|
}
|
|
|
|
matrix3x3
|
|
x_rotation(double deg)
|
|
{
|
|
return (matrix3x3) {
|
|
.nums =
|
|
{{1, 0, 0},
|
|
{0, cos(to_deg(deg)), -sin(to_deg(deg))},
|
|
{0, sin(to_deg(deg)), cos(to_deg(deg))}}
|
|
};
|
|
}
|
|
|
|
matrix3x3
|
|
y_rotation(double deg)
|
|
{
|
|
return (matrix3x3) {
|
|
.nums =
|
|
{{ cos(to_deg(deg)), 0, sin(to_deg(deg))},
|
|
{ 0, 1, 0},
|
|
{-sin(to_deg(deg)), 0, cos(to_deg(deg))}}
|
|
};
|
|
}
|
|
|
|
matrix3x3
|
|
z_rotation(double deg)
|
|
{
|
|
return (matrix3x3) {
|
|
.nums =
|
|
{{cos(to_deg(deg)), -sin(to_deg(deg)), 0},
|
|
{sin(to_deg(deg)), cos(to_deg(deg)), 0},
|
|
{0, 0, 1}}
|
|
};
|
|
}
|
|
|
|
#include "other.h"
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
sdl_state state = init_sdl();
|
|
camera cam = new_camera(180, (vec3){0, 0, 3}, (vec3){0, 0, 0}, UP);
|
|
print_camera(&cam);
|
|
|
|
SDL_Event event;
|
|
|
|
vec3 vertices[8] = {
|
|
/* front */
|
|
{0, 0, 0},
|
|
{100, 0, 0},
|
|
{100, 100, 0},
|
|
{0, 100, 0},
|
|
/* back */
|
|
{0, 0, 100},
|
|
{100, 0, 100},
|
|
{100, 100, 100},
|
|
{0, 100, 100},
|
|
};
|
|
uint32_t indices[3 * 12] = {
|
|
/* front */
|
|
0, 1, 2,
|
|
2, 3, 0,
|
|
/* back */
|
|
4, 5, 6,
|
|
6, 7, 4,
|
|
/* left */
|
|
0, 4, 7,
|
|
7, 3, 0,
|
|
/* right */
|
|
1, 5, 6,
|
|
6, 2, 1,
|
|
/* top */
|
|
0, 1, 5,
|
|
5, 4, 0,
|
|
/* bottom */
|
|
3, 2, 6,
|
|
6, 7, 3,
|
|
};
|
|
const mesh box = new_mesh(8, 12, 3 * 12, vertices, indices);
|
|
mesh box_copy = new_mesh(8, 12, 3 * 12, vertices, indices);
|
|
|
|
uint64_t t = 0;
|
|
|
|
for (;;) {
|
|
double elapsed, start = SDL_GetPerformanceCounter();
|
|
|
|
matrix3x3 m = matrix3x3_multiply(
|
|
matrix3x3_multiply(
|
|
z_rotation(1.2 * t),
|
|
y_rotation(1.1 * t)),
|
|
x_rotation(1.0 * t));
|
|
|
|
int x, y;
|
|
uint32_t buttons = SDL_GetMouseState(&x, &y);
|
|
|
|
if (buttons & SDL_BUTTON(2))
|
|
screenshot(state.canvas);
|
|
else if (buttons & (SDL_BUTTON(1)))
|
|
wireframe = !wireframe;
|
|
|
|
clear_canvas(state.canvas, BLACK);
|
|
|
|
for (size_t i = 0; i < box.vertex_num; i++) {
|
|
box_copy.vertices[i] = vec3_matrix3x3_multiply(box.vertices[i], m);
|
|
}
|
|
|
|
draw_mesh(state.canvas, WHITE, cam, box_copy);
|
|
|
|
render(state);
|
|
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
|
|
break;
|
|
|
|
elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0;
|
|
SDL_Delay(clamp(16.666f - elapsed, 0, 1000));
|
|
|
|
t++;
|
|
}
|
|
|
|
free_mesh(box);
|
|
free_mesh(box_copy);
|
|
free_sdl(state);
|
|
}
|