From d3cbd939c74dbbab6d89e518288743dc3f7d8fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Sat, 24 Aug 2024 13:33:05 +0200 Subject: [PATCH] frustum culling --- engine/joint/v4k.h | 100 +++++++++++++++++++++++++++++++++++++- engine/split/v4k_render.c | 86 +++++++++++++++++++++++++++++++- engine/split/v4k_scene.c | 14 ++++++ engine/v4k.c | 100 +++++++++++++++++++++++++++++++++++++- 4 files changed, 294 insertions(+), 6 deletions(-) diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index f3e0e9b..e09fc15 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -385267,11 +385267,66 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) { int invalid = texture_checker().id; q->textures[i] = invalid; struct iqmmesh *mesh = &q->meshes[i]; -#if 1 +#if 0 GLfloat *pos = verts[q->meshes[i].first_vertex].position; m->meshcenters[i] = vec3(pos[0], pos[1], pos[2]); #else - // for (int j = 0; j < ) + int first_triangle = mesh->first_triangle; + int num_triangles = mesh->num_triangles; + int vertex_count = 0; + vec3 center = {0}; + aabb box = { + .min = {FLT_MAX, FLT_MAX, FLT_MAX}, + .max = {-FLT_MAX, -FLT_MAX, -FLT_MAX} + }; + float max_distance_squared = 0.0f; + for (int j = first_triangle; j < num_triangles+first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + // calculate mesh center + for (int k = 0; k < 3; ++k) { + iqm_vertex *v = &verts[tri->vertex[k]]; + GLfloat *pos = v->position; + center.x += pos[0]; + center.y += pos[1]; + center.z += pos[2]; + vertex_count++; + + // Update AABB + box.min.x = fminf(box.min.x, pos[0]); + box.min.y = fminf(box.min.y, pos[1]); + box.min.z = fminf(box.min.z, pos[2]); + box.max.x = fmaxf(box.max.x, pos[0]); + box.max.y = fmaxf(box.max.y, pos[1]); + box.max.z = fmaxf(box.max.z, pos[2]); + } + } + + if (vertex_count) { + center.x /= vertex_count; + center.y /= vertex_count; + center.z /= vertex_count; + } + + // Compute bounding sphere radius + for (int j = first_triangle; j < num_triangles + first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + for (int k = 0; k < 3; ++k) { + int vertex_index = tri->vertex[k]; + GLfloat *pos = verts[vertex_index].position; + + float dx = pos[0] - center.x; + float dy = pos[1] - center.y; + float dz = pos[2] - center.z; + float distance_squared = dx*dx + dy*dy + dz*dz; + max_distance_squared = fmaxf(max_distance_squared, distance_squared); + } + } + + m->meshcenters[i] = center; + m->meshbounds[i] = box; + m->meshradii[i] = sqrtf(max_distance_squared); #endif } @@ -385939,6 +385994,28 @@ void model_clear_frustum(model_t *m) { m->frustum_enabled = 0; } +static inline +bool model_is_visible(model_t m, int mesh, mat44 model_mat) { + if(!m.iqm) return false; + if(!m.frustum_enabled) return true; + + sphere s; s.c = transform344(model_mat, m.meshcenters[mesh]); s.r = m.meshradii[mesh]; + + if (!frustum_test_sphere(m.frustum_state, s)) { + return false; + } + + aabb box = m.meshbounds[mesh]; + box.min = transform344(model_mat, box.min); + box.max = transform344(model_mat, box.max); + + if (!frustum_test_aabb(m.frustum_state, box)) { + return false; + } + + return true; +} + static void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_mat) { if(!m.iqm) return; @@ -385971,11 +386048,14 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if (rs_idx > RENDER_PASS_OVERRIDES_BEGIN) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; array_push(drawcalls, (drawcall_t){i, -1}); } } else { if(pass == -1 || pass == RENDER_PASS_OPAQUE) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect opaque drawcalls if (required_rs[i] == RENDER_PASS_OPAQUE) { drawcall_t call; @@ -385991,6 +386071,8 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if(pass == -1 || pass == RENDER_PASS_TRANSPARENT) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect transparent drawcalls if (required_rs[i] == RENDER_PASS_TRANSPARENT) { drawcall_t call; @@ -388087,8 +388169,22 @@ int scene_obj_distance_compare(const void *a, const void *b) { void scene_render(int flags) { camera_t *cam = camera_get_active(); + +#if 1 mat44 projview; multiply44x2(projview, cam->proj, cam->view); frustum frustum_state = frustum_build(projview); +#else + static frustum frustum_state; + do_once { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } + + if (input_down(KEY_T)) { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } +#endif if(flags & SCENE_BACKGROUND) { if(last_scene->skybox.program) { diff --git a/engine/split/v4k_render.c b/engine/split/v4k_render.c index e705ac4..3c33412 100644 --- a/engine/split/v4k_render.c +++ b/engine/split/v4k_render.c @@ -3611,11 +3611,66 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) { int invalid = texture_checker().id; q->textures[i] = invalid; struct iqmmesh *mesh = &q->meshes[i]; -#if 1 +#if 0 GLfloat *pos = verts[q->meshes[i].first_vertex].position; m->meshcenters[i] = vec3(pos[0], pos[1], pos[2]); #else - // for (int j = 0; j < ) + int first_triangle = mesh->first_triangle; + int num_triangles = mesh->num_triangles; + int vertex_count = 0; + vec3 center = {0}; + aabb box = { + .min = {FLT_MAX, FLT_MAX, FLT_MAX}, + .max = {-FLT_MAX, -FLT_MAX, -FLT_MAX} + }; + float max_distance_squared = 0.0f; + for (int j = first_triangle; j < num_triangles+first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + // calculate mesh center + for (int k = 0; k < 3; ++k) { + iqm_vertex *v = &verts[tri->vertex[k]]; + GLfloat *pos = v->position; + center.x += pos[0]; + center.y += pos[1]; + center.z += pos[2]; + vertex_count++; + + // Update AABB + box.min.x = fminf(box.min.x, pos[0]); + box.min.y = fminf(box.min.y, pos[1]); + box.min.z = fminf(box.min.z, pos[2]); + box.max.x = fmaxf(box.max.x, pos[0]); + box.max.y = fmaxf(box.max.y, pos[1]); + box.max.z = fmaxf(box.max.z, pos[2]); + } + } + + if (vertex_count) { + center.x /= vertex_count; + center.y /= vertex_count; + center.z /= vertex_count; + } + + // Compute bounding sphere radius + for (int j = first_triangle; j < num_triangles + first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + for (int k = 0; k < 3; ++k) { + int vertex_index = tri->vertex[k]; + GLfloat *pos = verts[vertex_index].position; + + float dx = pos[0] - center.x; + float dy = pos[1] - center.y; + float dz = pos[2] - center.z; + float distance_squared = dx*dx + dy*dy + dz*dz; + max_distance_squared = fmaxf(max_distance_squared, distance_squared); + } + } + + m->meshcenters[i] = center; + m->meshbounds[i] = box; + m->meshradii[i] = sqrtf(max_distance_squared); #endif } @@ -4283,6 +4338,28 @@ void model_clear_frustum(model_t *m) { m->frustum_enabled = 0; } +static inline +bool model_is_visible(model_t m, int mesh, mat44 model_mat) { + if(!m.iqm) return false; + if(!m.frustum_enabled) return true; + + sphere s; s.c = transform344(model_mat, m.meshcenters[mesh]); s.r = m.meshradii[mesh]; + + if (!frustum_test_sphere(m.frustum_state, s)) { + return false; + } + + aabb box = m.meshbounds[mesh]; + box.min = transform344(model_mat, box.min); + box.max = transform344(model_mat, box.max); + + if (!frustum_test_aabb(m.frustum_state, box)) { + return false; + } + + return true; +} + static void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_mat) { if(!m.iqm) return; @@ -4315,11 +4392,14 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if (rs_idx > RENDER_PASS_OVERRIDES_BEGIN) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; array_push(drawcalls, (drawcall_t){i, -1}); } } else { if(pass == -1 || pass == RENDER_PASS_OPAQUE) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect opaque drawcalls if (required_rs[i] == RENDER_PASS_OPAQUE) { drawcall_t call; @@ -4335,6 +4415,8 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if(pass == -1 || pass == RENDER_PASS_TRANSPARENT) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect transparent drawcalls if (required_rs[i] == RENDER_PASS_TRANSPARENT) { drawcall_t call; diff --git a/engine/split/v4k_scene.c b/engine/split/v4k_scene.c index f2d6158..91e01f4 100644 --- a/engine/split/v4k_scene.c +++ b/engine/split/v4k_scene.c @@ -513,8 +513,22 @@ int scene_obj_distance_compare(const void *a, const void *b) { void scene_render(int flags) { camera_t *cam = camera_get_active(); + +#if 1 mat44 projview; multiply44x2(projview, cam->proj, cam->view); frustum frustum_state = frustum_build(projview); +#else + static frustum frustum_state; + do_once { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } + + if (input_down(KEY_T)) { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } +#endif if(flags & SCENE_BACKGROUND) { if(last_scene->skybox.program) { diff --git a/engine/v4k.c b/engine/v4k.c index 28215fd..9a52fa4 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -20410,11 +20410,66 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) { int invalid = texture_checker().id; q->textures[i] = invalid; struct iqmmesh *mesh = &q->meshes[i]; -#if 1 +#if 0 GLfloat *pos = verts[q->meshes[i].first_vertex].position; m->meshcenters[i] = vec3(pos[0], pos[1], pos[2]); #else - // for (int j = 0; j < ) + int first_triangle = mesh->first_triangle; + int num_triangles = mesh->num_triangles; + int vertex_count = 0; + vec3 center = {0}; + aabb box = { + .min = {FLT_MAX, FLT_MAX, FLT_MAX}, + .max = {-FLT_MAX, -FLT_MAX, -FLT_MAX} + }; + float max_distance_squared = 0.0f; + for (int j = first_triangle; j < num_triangles+first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + // calculate mesh center + for (int k = 0; k < 3; ++k) { + iqm_vertex *v = &verts[tri->vertex[k]]; + GLfloat *pos = v->position; + center.x += pos[0]; + center.y += pos[1]; + center.z += pos[2]; + vertex_count++; + + // Update AABB + box.min.x = fminf(box.min.x, pos[0]); + box.min.y = fminf(box.min.y, pos[1]); + box.min.z = fminf(box.min.z, pos[2]); + box.max.x = fmaxf(box.max.x, pos[0]); + box.max.y = fmaxf(box.max.y, pos[1]); + box.max.z = fmaxf(box.max.z, pos[2]); + } + } + + if (vertex_count) { + center.x /= vertex_count; + center.y /= vertex_count; + center.z /= vertex_count; + } + + // Compute bounding sphere radius + for (int j = first_triangle; j < num_triangles + first_triangle; ++j) { + struct iqmtriangle *tri = &tris[j]; + + for (int k = 0; k < 3; ++k) { + int vertex_index = tri->vertex[k]; + GLfloat *pos = verts[vertex_index].position; + + float dx = pos[0] - center.x; + float dy = pos[1] - center.y; + float dz = pos[2] - center.z; + float distance_squared = dx*dx + dy*dy + dz*dz; + max_distance_squared = fmaxf(max_distance_squared, distance_squared); + } + } + + m->meshcenters[i] = center; + m->meshbounds[i] = box; + m->meshradii[i] = sqrtf(max_distance_squared); #endif } @@ -21082,6 +21137,28 @@ void model_clear_frustum(model_t *m) { m->frustum_enabled = 0; } +static inline +bool model_is_visible(model_t m, int mesh, mat44 model_mat) { + if(!m.iqm) return false; + if(!m.frustum_enabled) return true; + + sphere s; s.c = transform344(model_mat, m.meshcenters[mesh]); s.r = m.meshradii[mesh]; + + if (!frustum_test_sphere(m.frustum_state, s)) { + return false; + } + + aabb box = m.meshbounds[mesh]; + box.min = transform344(model_mat, box.min); + box.max = transform344(model_mat, box.max); + + if (!frustum_test_aabb(m.frustum_state, box)) { + return false; + } + + return true; +} + static void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_mat) { if(!m.iqm) return; @@ -21114,11 +21191,14 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if (rs_idx > RENDER_PASS_OVERRIDES_BEGIN) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; array_push(drawcalls, (drawcall_t){i, -1}); } } else { if(pass == -1 || pass == RENDER_PASS_OPAQUE) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect opaque drawcalls if (required_rs[i] == RENDER_PASS_OPAQUE) { drawcall_t call; @@ -21134,6 +21214,8 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_ if(pass == -1 || pass == RENDER_PASS_TRANSPARENT) { for(int i = 0; i < q->nummeshes; i++) { + if (!model_is_visible(m, i, model_mat)) continue; + // collect transparent drawcalls if (required_rs[i] == RENDER_PASS_TRANSPARENT) { drawcall_t call; @@ -23230,8 +23312,22 @@ int scene_obj_distance_compare(const void *a, const void *b) { void scene_render(int flags) { camera_t *cam = camera_get_active(); + +#if 1 mat44 projview; multiply44x2(projview, cam->proj, cam->view); frustum frustum_state = frustum_build(projview); +#else + static frustum frustum_state; + do_once { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } + + if (input_down(KEY_T)) { + mat44 projview; multiply44x2(projview, cam->proj, cam->view); + frustum_state = frustum_build(projview); + } +#endif if(flags & SCENE_BACKGROUND) { if(last_scene->skybox.program) {