722 lines
18 KiB
C
722 lines
18 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 */
|
|
#define TYPES_IMPL
|
|
#include "types.h"
|
|
|
|
#define W 800
|
|
#define H 600
|
|
#define PI 3.14159265358979323844
|
|
|
|
int wireframe = 1;
|
|
|
|
void
|
|
die(char *msg)
|
|
{
|
|
fprintf(stderr, "%s\n", msg);
|
|
exit(1);
|
|
}
|
|
|
|
const rgb WHITE = {255, 255, 255, 255};
|
|
const rgb BLACK = {0, 0, 0, 255};
|
|
const rgb RED = {255, 60, 40, 255};
|
|
const rgb GREEN = {40, 255, 70, 255};
|
|
const rgb BLUE = {50, 70, 255, 255};
|
|
|
|
const vec3 UP = {0, 1, 0};
|
|
|
|
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;
|
|
|
|
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 znear, 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 = {
|
|
.znear = znear,
|
|
.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;
|
|
}
|
|
|
|
mesh
|
|
load_mesh(char *path)
|
|
{
|
|
mesh ret;
|
|
|
|
FILE *f = fopen(path, "r");
|
|
if (f == NULL)
|
|
die("load_mesh: couldn't open model");
|
|
|
|
/* Vertices */
|
|
fread(&ret.vertex_num, 4, 1, f);
|
|
ret.vertices = malloc(sizeof(vec3) * ret.vertex_num);
|
|
for (uint32_t i = 0; i < ret.vertex_num; i++) {
|
|
fread(&ret.vertices[i].x, 8, 1, f);
|
|
fread(&ret.vertices[i].y, 8, 1, f);
|
|
fread(&ret.vertices[i].z, 8, 1, f);
|
|
}
|
|
|
|
/* Indices */
|
|
fread(&ret.index_num, 4, 1, f);
|
|
|
|
if (ret.index_num % 3 != 0) {
|
|
free(ret.vertices);
|
|
die("load_mesh: invalid number of indices, should be a multiple of 3");
|
|
}
|
|
|
|
ret.tri_num = ret.index_num / 3;
|
|
|
|
ret.indices = malloc(sizeof(uint32_t) * ret.index_num);
|
|
for (uint32_t i = 0; i < ret.index_num; i++)
|
|
fread(&ret.indices[i], 4, 1, f);
|
|
|
|
return ret;
|
|
}
|
|
|
|
mesh
|
|
copy_mesh(mesh orig)
|
|
{
|
|
return new_mesh(orig.vertex_num,
|
|
orig.tri_num,
|
|
orig.index_num,
|
|
orig.vertices,
|
|
orig.indices);
|
|
}
|
|
|
|
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 s = y1 > y0 ? 1 : -1;
|
|
int eps = 0;
|
|
|
|
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 += s;
|
|
eps -= dx;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (y0 >= y1) {
|
|
swap(y0, y1);
|
|
swap(x0, x1);
|
|
}
|
|
int x = x0;
|
|
for (int y = y0; y <= y1; y++) {
|
|
plot_rgb(canvas, x, y, c);
|
|
eps += dx;
|
|
if ((eps * 2) >= dy) {
|
|
x += s;
|
|
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-1; 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)
|
|
{
|
|
vec3 u = vec3_sub(v, c.pos);
|
|
// TODO rotation
|
|
double r = c.znear / u.z;
|
|
return (vec2){W / 2 + r * u.x, H / 2 + r * u.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++) {
|
|
vec3 v0 = mesh.vertices[mesh.indices[3*i ]],
|
|
v1 = mesh.vertices[mesh.indices[3*i+1]],
|
|
v2 = mesh.vertices[mesh.indices[3*i+2]];
|
|
|
|
if (v0.z > c.znear && v1.z > c.znear && v2.z > c.znear) continue;
|
|
|
|
vec3 norm = vec3_normalize(vec3_cross(vec3_sub(v1, v0), vec3_sub(v2, v0)));
|
|
double x = vec3_dot(vec3_sub(v0, c.pos), norm);
|
|
|
|
if (wireframe) {
|
|
if (x < 0)
|
|
draw_triangle(canvas, col, project_triangle(c, (triangle3) {
|
|
v0, v1, v2
|
|
}));
|
|
/* else */
|
|
/* draw_triangle(canvas, */
|
|
/* (rgb) { */
|
|
/* .r = col.r * 0.2, */
|
|
/* .g = col.g * 0.2, */
|
|
/* .b = col.b * 0.2, */
|
|
/* .a = col.a */
|
|
/* }, */
|
|
/* project_triangle(c, (triangle3) { */
|
|
/* v0, v1, v2 */
|
|
/* })); */
|
|
}
|
|
else {
|
|
if (x < 0) {
|
|
double col_mod = clamp(vec3_dot(norm, vec3_normalize(c.pos)), 0.0, 1.0);
|
|
fill_triangle(canvas,
|
|
(rgb) {
|
|
.r = col.r * col_mod,
|
|
.g = col.g * col_mod,
|
|
.b = col.b * col_mod
|
|
},
|
|
project_triangle(c, (triangle3) {
|
|
v0, v1, v2
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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, 200}, (vec3){0, 0, 0}, UP);
|
|
|
|
/* print_camera(cam); */
|
|
|
|
SDL_Event event;
|
|
|
|
const mesh obj = load_mesh("iso.mod");
|
|
mesh obj_copy = copy_mesh(obj);
|
|
|
|
uint64_t t = 0;
|
|
|
|
double rot_speed = 0.6;
|
|
for (;;) {
|
|
double elapsed, start = SDL_GetPerformanceCounter();
|
|
|
|
matrix3x3 rotation = matrix3x3_multiply(
|
|
matrix3x3_multiply(
|
|
z_rotation(1.2 * t * rot_speed),
|
|
y_rotation(1.1 * t * rot_speed)),
|
|
x_rotation(1.0 * t * rot_speed));
|
|
|
|
int x, y;
|
|
uint32_t buttons = SDL_GetMouseState(&x, &y);
|
|
|
|
clear_canvas(state.canvas, (rgb){7, 7, 7, 255});
|
|
|
|
for (size_t i = 0; i < obj.vertex_num; i++) {
|
|
obj_copy.vertices[i] = vec3_matrix3x3_multiply(obj.vertices[i], rotation);
|
|
}
|
|
|
|
draw_mesh(state.canvas, WHITE, cam, obj_copy);
|
|
|
|
render(state);
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
case SDL_KEYDOWN: {
|
|
switch (event.key.keysym.scancode) {
|
|
case SDL_SCANCODE_F3: {
|
|
if (!event.key.repeat) wireframe = !wireframe;
|
|
break;
|
|
}
|
|
case SDL_SCANCODE_F6: {
|
|
if (!event.key.repeat) screenshot(state.canvas);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SDL_QUIT:
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0;
|
|
SDL_Delay(clamp(16.666f - elapsed, 0, 1000));
|
|
|
|
t++;
|
|
}
|
|
|
|
quit:
|
|
free_mesh(obj);
|
|
free_mesh(obj_copy);
|
|
free_sdl(state);
|
|
}
|