163 lines
6.0 KiB
C
163 lines
6.0 KiB
C
#include "twn_game_api.h"
|
|
#include "state.h"
|
|
#include "twn_vec.h"
|
|
|
|
#include <SDL.h>
|
|
|
|
/* planned features: */
|
|
/* grid-based, bounded space (65536 / POINTS_PER_METER meters), which allows for efficient storage */
|
|
/* 65534 point limit, 16 object limit, 2048 faces per object, 32 textures per object */
|
|
/* triangles and quads only */
|
|
/* texture painting */
|
|
/* bones with mesh animations, snapping to grid, with no weights */
|
|
/* billboard render to specified angles */
|
|
/* 1 point light primitive lighting */
|
|
|
|
static State state;
|
|
static bool init;
|
|
|
|
|
|
static uint8_t new_object(const char *name) {
|
|
if (state.objects_sz >= OBJECT_LIMIT)
|
|
return INVALID_OBJECT;
|
|
state.objects_sz++;
|
|
state.objects = SDL_realloc(state.objects, state.objects_sz * sizeof (*state.objects));
|
|
SDL_memset(&state.objects[state.objects_sz-1], 0, sizeof (Object));
|
|
state.objects[state.objects_sz-1].name = SDL_strdup(name);
|
|
return state.objects_sz-1;
|
|
}
|
|
|
|
|
|
static uint16_t new_point(int16_t x, int16_t y, int16_t z) {
|
|
if (state.points_sz >= POINT_LIMIT)
|
|
return INVALID_POINT;
|
|
state.points_sz++;
|
|
state.points[state.points_sz-1] = (Point){x, y, z};
|
|
return state.points_sz-1;
|
|
}
|
|
|
|
|
|
static uint16_t push_face(uint8_t object, uint16_t p0, uint16_t p1, uint16_t p2, uint16_t p3, uint8_t texture) {
|
|
Object *o = &state.objects[object];
|
|
o->faces_sz++;
|
|
o->faces[o->faces_sz-1] = (Face) {p0, p1, p2, p3, texture};
|
|
return o->faces_sz-1;
|
|
}
|
|
|
|
|
|
static uint8_t push_texture(uint8_t object, char *texture) {
|
|
/* TODO: search and combine if it already exists */
|
|
Object *o = &state.objects[object];
|
|
o->textures_sz++;
|
|
o->textures[o->textures_sz-1] = SDL_strdup(texture);
|
|
return o->textures_sz-1;
|
|
}
|
|
|
|
|
|
static void render_object(uint8_t object) {
|
|
Object *o = &state.objects[object];
|
|
|
|
if (o->is_invisible)
|
|
return;
|
|
|
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
|
Face *f = &o->faces[fi];
|
|
/* quads */
|
|
if (f->p3 != INVALID_POINT) {
|
|
Vec3 p0 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p0].x),
|
|
(float)POINTS_PER_METER / (o->position.y + state.points[f->p0].y),
|
|
(float)POINTS_PER_METER / (o->position.z + state.points[f->p0].z) };
|
|
Vec3 p1 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p1].x),
|
|
(float)POINTS_PER_METER / (o->position.y + state.points[f->p1].y),
|
|
(float)POINTS_PER_METER / (o->position.z + state.points[f->p1].z) };
|
|
Vec3 p2 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p2].x),
|
|
(float)POINTS_PER_METER / (o->position.y + state.points[f->p2].y),
|
|
(float)POINTS_PER_METER / (o->position.z + state.points[f->p2].z) };
|
|
Vec3 p3 = (Vec3){(float)POINTS_PER_METER / (o->position.x + state.points[f->p3].x),
|
|
(float)POINTS_PER_METER / (o->position.y + state.points[f->p3].y),
|
|
(float)POINTS_PER_METER / (o->position.z + state.points[f->p3].z) };
|
|
|
|
if (state.solid_display_mode)
|
|
draw_quad(o->textures[f->texture],
|
|
p0, p1, p2, p3,
|
|
(Rect){0},
|
|
(Color){255,255,255,255} );
|
|
else {
|
|
draw_line_3d(p0, p1, 1, (Color){255,255,255,255});
|
|
draw_line_3d(p1, p2, 1, (Color){255,255,255,255});
|
|
draw_line_3d(p2, p3, 1, (Color){255,255,255,255});
|
|
draw_line_3d(p3, p0, 1, (Color){255,255,255,255});
|
|
}
|
|
} else {
|
|
/* triangles */
|
|
/* TODO: */
|
|
SDL_assert_always(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static uint8_t new_cube(Point pos, Point size) {
|
|
uint8_t object = new_object("cube");
|
|
|
|
uint16_t p0 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
|
uint16_t p1 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
|
uint16_t p2 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
|
uint16_t p3 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
|
uint16_t p4 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
|
uint16_t p5 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
|
uint16_t p6 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
|
uint16_t p7 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
|
|
|
uint8_t txt = push_texture(object, "/data/default.png");
|
|
push_face(object, p0, p1, p2, p3, txt);
|
|
push_face(object, p7, p6, p5, p4, txt);
|
|
push_face(object, p4, p5, p1, p0, txt);
|
|
push_face(object, p6, p7, p3, p2, txt);
|
|
push_face(object, p2, p1, p5, p6, txt);
|
|
push_face(object, p0, p3, p7, p4, txt);
|
|
|
|
return object;
|
|
}
|
|
|
|
|
|
void game_tick(void) {
|
|
if (!init) {
|
|
/* default state */
|
|
new_cube((Point){0,0,0}, (Point){POINTS_PER_METER,POINTS_PER_METER,POINTS_PER_METER});
|
|
state.camera_position = (Vec3){10,5,10};
|
|
state.camera_direction = vec3_norm(((Vec3){-10,-5,-10}));
|
|
init = true;
|
|
}
|
|
|
|
input_action("toggle_display_mode", "C");
|
|
|
|
if (input_action_just_pressed("toggle_display_mode"))
|
|
state.solid_display_mode = !state.solid_display_mode;
|
|
|
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
|
|
render_object(obj);
|
|
|
|
draw_text("twndel\x03", (Vec2){0, 0}, 32, (Color){255,255,255,255}, NULL);
|
|
draw_camera(
|
|
state.camera_position,
|
|
state.camera_direction,
|
|
(Vec3){0, 1, 0},
|
|
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
|
state.camera_zoom,
|
|
100
|
|
);
|
|
}
|
|
|
|
|
|
void game_end(void) {
|
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
|
Object *o = &state.objects[obj];
|
|
for (uint8_t t = 0; t < o->textures_sz; ++t)
|
|
SDL_free(o->textures[t]);
|
|
SDL_free(o->name);
|
|
}
|
|
if (state.objects_sz != 0)
|
|
SDL_free(state.objects);
|
|
}
|