#include "twn_game_api.h" #include "state.h" #include "twn_vec.h" #include /* 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); }