townengine/apps/tools/twndel/tool.c
2025-03-07 07:10:44 +03:00

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);
}