polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit 6291496d8715522922309ca13746ff212a4452c3
parent 5081718387b3df8914a9624fc109adc5cc9e9aad
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  5 Aug 2021 06:58:55 -0700

terrain loading for wasm

Diffstat:
MMakefile | 8++++----
Mdefault.nix | 2+-
Mmain.c | 3+++
Msrc/dae.c | 2+-
Msrc/delaunay.c | 12++++++------
Msrc/game.c | 10+++++++---
Msrc/geometry.c | 2+-
Msrc/mdl.c | 9+++++----
Msrc/mdl.h | 4++--
Msrc/skybox.c | 1+
Msrc/terrain.c | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/terrain.h | 5+++--
Msrc/terrain_collision.c | 2++
Msrc/update.c | 25++++++++++++++-----------
Mtools/compile-model.c | 2+-
15 files changed, 173 insertions(+), 94 deletions(-)

diff --git a/Makefile b/Makefile @@ -17,13 +17,13 @@ BASE_CFLAGS = $(DEFS) -O2 -g -I src -Wall -Werror -Wextra -std=c99 \ $(shell pkg-config --cflags sdl2 gl) CFLAGS = $(BASE_CFLAGS) -EM_CFLAGS = $(BASE_CFLAGS) - +EM_CFLAGS = $(BASE_CFLAGS) -s STANDALONE_WASM LDFLAGS = $(shell pkg-config --libs sdl2 gl) -lm SRC=src SRCS=$(wildcard $(SRC)/*.c) +HEADERS=$(wildcard $(SRC)/*.h) PLYS=$(wildcard data/models/*.ply) MODELS=$(PLYS:.ply=.mdl) @@ -81,6 +81,6 @@ install: $(BIN) install $(BIN) $(PREFIX)/bin tags: - ctags $(SRCS) + ctags $(SRCS) $(HEADERS) -.PHONY: TAGS +.PHONY: TAGS tags diff --git a/default.nix b/default.nix @@ -13,7 +13,7 @@ stdenv.mkDerivation rec { makeFlags = "PREFIX=$(out)"; - nativeBuildInputs = with pkgs; [ tinycc pkg-config gdb emscripten ]; + nativeBuildInputs = with pkgs; [ tinycc pkg-config gdb emscripten binaryen ]; buildInputs = with pkgs; [ SDL2 mesa libglvnd ] ++ (with xorg; [ libX11 libxcb libXau libXdmcp libXext libXcursor diff --git a/main.c b/main.c @@ -72,6 +72,7 @@ int main(void) while (!game.quit) { + game.input.keystates = SDL_GetKeyboardState(NULL); game.frame++; process_events(&game.input, game.frame); if (game.input.resized_height) { @@ -83,6 +84,8 @@ int main(void) double new_time = hires_time_in_seconds(); double frame_time = new_time - last; game.dt = frame_time; + if (game.frame % 24 == 0) + printf("frame time %f\n", frame_time * 1000); update(&game); diff --git a/src/dae.c b/src/dae.c @@ -351,7 +351,7 @@ void dae_attr(struct xmlparser *x, const char *t, size_t tl, } else if (data->state == PARSING_NODE && streq(a, "name")) { - strncpy(data->current_name, v, sizeof(data->current_name)); + strncpy(data->current_name, v, sizeof(data->current_name)-1); } else if (data->state == PARSING_NODE && streq(a, "type") diff --git a/src/delaunay.c b/src/delaunay.c @@ -513,11 +513,11 @@ static halfedge_t* del_valid_left( halfedge_t* b ) v = b->next->pair->vertex; /* pair(next(next(halfedge)) point */ - if( classify_point_seg(g, d, u) == ON_LEFT ) + if( v!=u && classify_point_seg(g, d, u) == ON_LEFT ) { /* 3 points aren't colinear */ /* as long as the 4 points belong to the same circle, do the cleaning */ - assert( v != u && "1: floating point precision error"); + //assert( v != u && "1: floating point precision error"); while( v != d && v != g && in_circle(g, d, u, v) == INSIDE ) { c = b->next; @@ -528,7 +528,7 @@ static halfedge_t* del_valid_left( halfedge_t* b ) v = b->next->pair->vertex; } - assert( v != u && "2: floating point precision error"); + //assert( v != u && "2: floating point precision error"); if( v != d && v != g && in_circle(g, d, u, v) == ON_CIRCLE ) { du = du->prev; @@ -559,9 +559,9 @@ static halfedge_t* del_valid_right( halfedge_t *b ) v = b->prev->pair->vertex; - if( classify_point_seg(lv, rv, u) == ON_LEFT ) + if( v!=u && classify_point_seg(lv, rv, u) == ON_LEFT ) { - assert( v != u && "1: floating point precision error"); + //assert( v != u && "1: floating point precision error"); while( v != lv && v != rv && in_circle(lv, rv, u, v) == INSIDE ) { c = b->prev; @@ -572,7 +572,7 @@ static halfedge_t* del_valid_right( halfedge_t *b ) v = b->prev->pair->vertex; } - assert( v != u && "1: floating point precision error"); + //assert( v != u && "1: floating point precision error"); if( v != lv && v != rv && in_circle(lv, rv, u, v) == ON_CIRCLE ) { du = du->next; diff --git a/src/game.c b/src/game.c @@ -56,7 +56,7 @@ static void init_user_settings(struct user_settings *settings) { static void init_sdl(SDL_Window **window, int width, int height) { - SDL_Init( SDL_INIT_JOYSTICK ); + SDL_Init( 0); /* SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); */ @@ -95,7 +95,8 @@ void init_misc(struct game *game, int width, int height) { game->quit = 0; game->frame = 0; - const double size = 1000.0; + const double size = 4000.0; + //double scale = 0.03; double scale = 0.03; terrain->settings = (struct perlin_settings){ @@ -110,16 +111,19 @@ void init_misc(struct game *game, int width, int height) { .scale = scale }; + debug("creating ui...\n"); create_ui(&game->ui, width, height, &res->programs[UI_PROGRAM]); check_gl(); init_terrain(terrain, size); /* terrain->samples = load_samples(&seed, &terrain->n_samples); */ terrain->n_samples = 0; - create_terrain(terrain, size, game->seed); + //create_terrain(terrain, size, game->seed); + load_terrain(terrain); /* update_terrain(terrain, terrain->cell_size); */ /* get_entity(&terrain->entity_id)->flags |= ENT_INVISIBLE; */ + debug("creating skybox...\n"); create_skybox(&res->skybox, &res->programs[SKYBOX_PROGRAM]); /* node_translate(&res->skybox.node, V3(-100.0, -100.0, 0.0)); */ /* node_scale(&res->skybox.node, size/4.0); */ diff --git a/src/geometry.c b/src/geometry.c @@ -64,7 +64,7 @@ void render_geometry(struct geometry *geom, gpu_addr *vertex_attrs, GL_UNSIGNED_INT, /* type */ (void*)0 /* element array buffer offset */ ); - /* printf("render_geometry %s\n", program->name); */ + //printf("render_geometry %d %s\n", geom->num_indices, program->name); check_gl(); } else { diff --git a/src/mdl.c b/src/mdl.c @@ -41,7 +41,7 @@ void init_mdl_geometry(struct mdl_geometry *lgeom) vec3_set(V3(0,0,0), lgeom->max); } -void save_mdl_fd(FILE *out, struct model *model, struct mdl_geometry *lgeom) +void save_mdl_fd(FILE *out, struct mdl_geometry *lgeom) { struct make_geometry *mkgeom = &lgeom->mkgeom; assert(mkgeom->vertices); @@ -140,7 +140,7 @@ static inline int read_int(FILE *stream) return val; } -#define MAX_MDL_ARRAY 128000 // only enforced at dev time for sanity +#define MAX_MDL_ARRAY 1280000 // only enforced at dev time for sanity static inline int read_nfloats(FILE *stream, int n, float *floats) { @@ -184,6 +184,7 @@ void load_mdl_fd(FILE *in, struct model *model, struct mdl_geometry *lgeom) switch (tag) { case MDL_POSITION: mkgeom->num_verts = read_floats(in, &mkgeom->vertices) / 3; + printf("mdl verts %d\n", mkgeom->num_verts); break; case MDL_NORMAL: @@ -235,11 +236,11 @@ void load_mdl_fd(FILE *in, struct model *model, struct mdl_geometry *lgeom) } } -void save_mdl(const char *filename, struct model *model, struct mdl_geometry *lgeom) +void save_mdl(const char *filename, struct mdl_geometry *lgeom) { FILE *out = fopen(filename, "wb"); assert(out); - save_mdl_fd(out, model, lgeom); + save_mdl_fd(out, lgeom); fclose(out); } diff --git a/src/mdl.h b/src/mdl.h @@ -27,8 +27,8 @@ struct mdl_geometry { }; -void save_mdl(const char *filename, struct model *model, struct mdl_geometry *geom); -void save_mdl_fd(FILE *out, struct model *model, struct mdl_geometry *geom); +void save_mdl(const char *filename, struct mdl_geometry *geom); +void save_mdl_fd(FILE *out, struct mdl_geometry *geom); void load_mdl(const char *filename, struct model *model, struct mdl_geometry *geom); void load_mdl_fd(FILE *in, struct model *model, struct mdl_geometry *geom); diff --git a/src/skybox.c b/src/skybox.c @@ -82,6 +82,7 @@ void create_skybox(struct skybox *skybox, struct gpu_program *program) { /* }; */ model->texture = create_cubemap(faces); + printf("cubemap texture %d\n", model->texture); make_shader(GL_VERTEX_SHADER, SHADER("skybox.v.glsl"), &vertex); check_gl(); diff --git a/src/terrain.c b/src/terrain.c @@ -6,6 +6,9 @@ #include "vec3.h" #include "perlin.h" #include "poisson.h" +#include "mdl.h" + +static const double pdist = 24.0; static const float plane_verts[] = { -1,-1,0, -1,1,0, 1,1,0, 1,-1,0 @@ -52,6 +55,13 @@ void reset_terrain(struct terrain *terrain, float size) { assert(ent); } +double offset_fn(struct terrain* terrain, double x, double y) { + struct perlin_settings *perlin = &terrain->settings; + double ox = perlin->ox; + double oy = perlin->oy; + return old_noisy_boi(terrain, ox+x, oy+y); +} + void init_terrain(struct terrain *terrain, float size) { init_id(&terrain->entity_id); @@ -72,18 +82,22 @@ void init_terrain(struct terrain *terrain, float size) { /* node->pos[2] = 20.0; */ reset_terrain(terrain, size); -} -double offset_fn(struct terrain* terrain, double x, double y) { - struct perlin_settings *perlin = &terrain->settings; - double ox = perlin->ox; - double oy = perlin->oy; - return old_noisy_boi(terrain, ox+x, oy+y); + terrain->fn = offset_fn; + terrain->cell_size = pdist; + terrain->n_cells = round(size / terrain->cell_size) + 1; + debug("n_cells %d\n", terrain->n_cells); + + struct terrain_cell *grid = + calloc(terrain->n_cells * terrain->n_cells, sizeof(struct terrain_cell)); + + terrain->grid = grid; + } void gen_terrain_samples(struct terrain *terrain, float scale, const double pdist) { - debug("generating terrain samples\n"); + debug("generating terrain samples...\n"); if (terrain->samples) free(terrain->samples); @@ -93,9 +107,12 @@ void gen_terrain_samples(struct terrain *terrain, float scale, const double pdis /* struct point *samples = */ /* uniform_samples(n_samples, game->terrain.size); */ + struct point *samples = poisson_disk_samples(pdist, terrain->size, 30, &n_samples); + debug("done generating terrain samples\n"); + /* remap_samples(samples, n_samples, game->terrain.size); */ /* draw_samples(samples, pdist, n_samples, game->terrain.size); */ @@ -108,7 +125,6 @@ void gen_terrain_samples(struct terrain *terrain, float scale, const double pdis static inline struct terrain_cell *index_terrain_cell(struct terrain *terrain, int x, int y) { if (x < 0 || y < 0 || x >= terrain->n_cells || y >= terrain->n_cells) { - assert(!"terrain oob"); return NULL; } @@ -119,10 +135,11 @@ static inline struct terrain_cell *query_terrain_cell(struct terrain *terrain, float x, float y, int *grid_x, int *grid_y) { - assert(x >= 0); - assert(y >= 0); - assert(x < terrain->size); - assert(y < terrain->size); + if (x < 0) return NULL; + if(y < 0) return NULL; + if(x >= terrain->size) return NULL; + if(y >= terrain->size) return NULL; + *grid_x = grid_index(terrain, x); *grid_y = grid_index(terrain, y); @@ -132,7 +149,7 @@ static inline struct terrain_cell *query_terrain_cell(struct terrain *terrain, void query_terrain_grid(struct terrain *terrain, float x, float y, struct terrain_cell *cells[9]) { - int grid_x, grid_y; + int grid_x = 0, grid_y = 0; // middle cells[4] = query_terrain_cell(terrain, x, y, &grid_x, &grid_y); @@ -203,45 +220,94 @@ static double distance_to_closest_edge(double size, double x, double y) { return min(top, min(left, min(right, bottom))); } +static void compute_bounding(float *vertices, int num_verts, vec3 *min, vec3 *max) +{ + int i; + for (i = 0; i < num_verts; i++) { + } +} + +void create_terrain_collision(struct terrain *terrain) +{ + double x, y; + int n; + u32 i; + + for (i = 0; i < (u32)terrain->n_verts; i++) { + n = i*3; + /* double dx, dy; */ + + x = terrain->verts[n+0]; + y = terrain->verts[n+1]; + + int grid_x = x / terrain->cell_size; + int grid_y = y / terrain->cell_size; + + // clamp height at edge + printf("%f %f %f %d grid_x %d grid_y %f cell_size\n", + x, y, terrain->verts[n+2], + grid_x, grid_y, terrain->cell_size); + + struct terrain_cell *cell = + index_terrain_cell(terrain, grid_x, grid_y); + + insert_grid_vertex(cell, n); + } +} + + +void load_terrain(struct terrain *terrain) +{ + int seed; + + assert(terrain->size > 0); + struct mdl_geometry terrain_mdl; + init_mdl_geometry(&terrain_mdl); + load_mdl("terrain.mdl", NULL, &terrain_mdl); + + struct entity *ent = get_entity(&terrain->entity_id); assert(ent); + struct model *model = get_model(&ent->model_id); assert(model); + struct geometry *geom = get_geometry(&model->geom_id); assert(geom); + + assert(terrain_mdl.mkgeom.joint_weights == 0); + + make_buffer_geometry(&terrain_mdl.mkgeom, geom); + + terrain->verts = terrain_mdl.mkgeom.vertices; + terrain->n_verts = terrain_mdl.mkgeom.num_verts; + + //struct vert_tris *vert_tris = calloc(terrain->n_verts, sizeof(struct vert_tris)); + //terrain->vtris = vert_tris; + + //create_terrain_collision(terrain); +} + void create_terrain(struct terrain *terrain, float scale, int seed) { u32 i; + int n; + double x, y, z; const double size = terrain->size; - static const double pdist = 24.0; terrain->settings.seed = seed; float tmp1[3], tmp2[3]; if (!terrain->n_samples) { gen_terrain_samples(terrain, scale, pdist); - /* save_samples(terrain->samples, seed, terrain->n_samples); */ + //save_samples(terrain->samples, seed, terrain->n_samples); } assert(terrain->n_samples > 0); del_point2d_t *points = calloc(terrain->n_samples, sizeof(*points)); float *verts = calloc(terrain->n_samples * 3, sizeof(*verts)); - terrain->cell_size = pdist; - terrain->n_cells = round(size / terrain->cell_size) + 1; - debug("n_cells %d\n", terrain->n_cells); - - struct terrain_cell *grid = - calloc(terrain->n_cells * terrain->n_cells, sizeof(struct terrain_cell)); - - terrain->grid = grid; - /* float *normals = calloc(terrain->n_samples * 3, sizeof(*verts)); */ - - terrain->fn = offset_fn; - - // n random samples from our noise function for (i = 0; i < (u32)terrain->n_samples; i++) { - int n = i*3; - double x, y; - /* double dx, dy; */ - + n = i*3; x = terrain->samples[i].x; y = terrain->samples[i].y; double z = terrain->fn(terrain, x, y); + double d = distance_to_closest_edge(size, x, y); + z *= (d / (size/2.0)) * 2.0; points[i].x = x; points[i].y = y; @@ -249,28 +315,17 @@ void create_terrain(struct terrain *terrain, float scale, int seed) { verts[n] = (float)x; verts[n+1] = (float)y; - int grid_x = verts[n] / terrain->cell_size; - int grid_y = verts[n+1] / terrain->cell_size; - - // clamp height at edge - - double d = distance_to_closest_edge(size, x, y); - z *= (d / (size/2.0)) * 2.0; - - struct terrain_cell *cell = - index_terrain_cell(terrain, grid_x, grid_y); - - assert(cell); - - insert_grid_vertex(cell, n); - static const double limit = 1.4; if (x < limit || x > size-limit || y < limit || y > size-limit) - verts[n+2] = 0; + verts[n+2] = 0; else - verts[n+2] = (float)z; + verts[n+2] = (float)z; } + /* float *normals = calloc(terrain->n_samples * 3, sizeof(*verts)); */ + + // n random samples from our noise function + delaunay2d_t *del = delaunay2d_from(points, terrain->n_samples); tri_delaunay2d_t *tri = tri_delaunay2d_from(del); @@ -335,20 +390,27 @@ void create_terrain(struct terrain *terrain, float scale, int seed) { struct entity *ent = get_entity(&terrain->entity_id); assert(ent); - struct make_geometry mkgeom; - init_make_geometry(&mkgeom); + struct mdl_geometry terrain_mdl; + init_mdl_geometry(&terrain_mdl); + + struct make_geometry *mkgeom = &terrain_mdl.mkgeom; - mkgeom.num_verts = num_verts; - mkgeom.vertices = (float*)del_verts; - mkgeom.normals = (float*)del_norms; - mkgeom.indices = (u32*)del_indices; - mkgeom.num_indices = num_verts; + mkgeom->num_verts = num_verts; + mkgeom->vertices = (float*)del_verts; + mkgeom->normals = (float*)del_norms; + mkgeom->indices = (u32*)del_indices; + mkgeom->num_indices = num_verts; struct model *model = get_model(&ent->model_id); assert(model); struct geometry *geom = get_geometry(&model->geom_id); assert(geom); - assert(mkgeom.joint_weights == 0); - make_buffer_geometry(&mkgeom, geom); + assert(mkgeom->joint_weights == 0); + + make_buffer_geometry(mkgeom, geom); + + compute_bounding(mkgeom->vertices, mkgeom->num_verts, terrain_mdl.min, + terrain_mdl.max); + save_mdl("terrain.mdl", &terrain_mdl); delaunay2d_release(del); tri_delaunay2d_release(tri); @@ -363,6 +425,8 @@ void create_terrain(struct terrain *terrain, float scale, int seed) { terrain->verts = verts; terrain->n_verts = num_verts; + create_terrain_collision(terrain); + // we might need norms in memory eventually as well ? free(del_norms); free(del_indices); diff --git a/src/terrain.h b/src/terrain.h @@ -7,8 +7,8 @@ #include "model.h" #include "debug.h" -#define MAX_CELL_VERTS 4 -#define MAX_VERT_TRIS 20 +#define MAX_CELL_VERTS 16 +#define MAX_VERT_TRIS 32 struct point; @@ -68,6 +68,7 @@ void gen_terrain_samples(struct terrain *terrain, float scale, const double pdis void init_terrain(struct terrain *terrain, float size); void reset_terrain(struct terrain *terrain, float size); void create_terrain(struct terrain *terrain, float scale, int seed); +void load_terrain(struct terrain *terrain); void destroy_terrain(struct terrain *terrain); void query_terrain_grid(struct terrain *terrain, float x, float y, struct terrain_cell *cells[9]); diff --git a/src/terrain_collision.c b/src/terrain_collision.c @@ -23,6 +23,8 @@ static void get_closest_verts(struct terrain *terrain, { for (int i = 0; i < 9; i++) { struct terrain_cell *cell = cells[i]; + if (!cell) + continue; for (int j = 0; j < cell->vert_count; j++) { vec3 *vpos = &terrain->verts[cell->verts_index[j]]; float d = vec3_distsq(pos, vpos); diff --git a/src/update.c b/src/update.c @@ -331,10 +331,13 @@ void orbit_update_from_mouse(struct orbit *camera, struct input *input, camera->coords.azimuth += mx; camera->coords.inclination += my; - /* printf("coords azimuth %f inclination %f radius %f\n", */ - /* camera->coords.azimuth, */ - /* camera->coords.inclination, */ - /* camera->coords.radius); */ + + /* + printf("coords azimuth %f inclination %f radius %f\n", + camera->coords.azimuth, + camera->coords.inclination, + camera->coords.radius); + */ spherical_look_at(&camera->coords, target, cam_node->mat); @@ -346,7 +349,7 @@ static void camera_keep_above_ground(struct terrain *terrain, return; float move[3]; float *camworld = node_world(camera); - float pen; + float pen = 0.0; static const float penlim = 1.0; struct tri *tri = collide_terrain(terrain, camworld, move, &pen); @@ -386,7 +389,7 @@ static void player_update(struct game *game, struct entity *player) orbit_update_from_mouse(camera, &game->input, game->user_settings.mouse_sens, player, game->dt); - camera_keep_above_ground(&game->terrain, cam_node); + //camera_keep_above_ground(&game->terrain, cam_node); // move player camera toward camera orientation if (input_is_dragging(&game->input, SDL_BUTTON_RIGHT)) { @@ -400,11 +403,11 @@ static void player_update(struct game *game, struct entity *player) float move[3]; float pos[3]; - float pen; + float pen = 0.0; vec3_copy(node_world(node), pos); /* debug("node_world(player) %f %f %f\n", pos[0], pos[1], pos[2]); */ - struct tri *tri = collide_terrain(terrain, pos, move, &pen); - //struct tri *tri = NULL; + //struct tri *tri = collide_terrain(terrain, pos, move, &pen); + struct tri *tri = NULL; /* node_translate(node, move); */ if (tri) { @@ -414,7 +417,7 @@ static void player_update(struct game *game, struct entity *player) else if (pen < 0) { node_translate(node, move); /* vec3_all(player->velocity, 0); */ - vec3_scale(player->velocity, 0.1, player->velocity); + //vec3_scale(player->velocity, 0.1, player->velocity); } else { player->flags &= ~ENT_ON_GROUND; @@ -461,7 +464,7 @@ void update (struct game *game) { float *time = &res->time; float *light = res->light_dir; - gravity(game); + //gravity(game); if (needs_terrain_update) { /* update_terrain(terrain); */ diff --git a/tools/compile-model.c b/tools/compile-model.c @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) int ok = parse_ply_with_mkgeom(filename, &mdl_geom); assert(ok); - save_mdl(outfile, &model, &mdl_geom); + save_mdl(outfile, &mdl_geom); return 0; }