From 13465911d7740259528cda7e9fda76bccaa48c7a Mon Sep 17 00:00:00 2001 From: Shariq Shah Date: Wed, 6 Jan 2016 13:14:35 +0500 Subject: [PATCH] Added bounding volume computation and frustum culling --- src/bounding_volumes.c | 72 ++++++++++++++++++++++++++++++++++++++++++ src/bounding_volumes.h | 41 ++++++++++++++++++++++++ src/camera.c | 47 ++++++++++++++++++++++++++- src/camera.h | 1 + src/game.c | 26 ++++++++++++--- src/geometry.c | 45 ++++++++++++++++++++++++-- src/geometry.h | 10 +++++- src/linmath.c | 7 ++++ src/linmath.h | 1 + src/model.c | 2 +- src/renderer.c | 2 ++ 11 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 src/bounding_volumes.c create mode 100644 src/bounding_volumes.h diff --git a/src/bounding_volumes.c b/src/bounding_volumes.c new file mode 100644 index 0000000..319e350 --- /dev/null +++ b/src/bounding_volumes.c @@ -0,0 +1,72 @@ +#include "bounding_volumes.h" +#include "transform.h" + +#include + +int bv_intersect_frustum_box(vec4* frustum, struct Bounding_Box* box, struct Transform* transform) +{ + vec3 min, max, size, center, half_ext, half_size; + vec3_add(&min, &box->min, &transform->position); + vec3_mul(&min, &min, &transform->scale); + vec3_add(&max, &box->max, &transform->position); + vec3_mul(&min, &min, &transform->scale); + vec3_sub(&size, &max, &min); + vec3_add(¢er, &max, &min); + vec3_scale(¢er, ¢er, (1.f/ 2.f)); + vec3_assign(&half_ext, &size); + vec3_scale(&half_size, &size, (1.f / 2.f)); + for(int i = 0; i < 6; i++) + { + vec3 normal = {frustum[i].x, frustum[i].y, frustum[i].z}; + float distance = frustum[i].w; + float d = vec3_dot(&normal, ¢er); + vec3 abs_normal = {fabsf(normal.x), fabsf(normal.y), fabsf(normal.z)}; + float r = vec3_dot(&half_ext, &abs_normal); + if(d + r < -distance) + { + return IT_OUTSIDE; + } + } + return IT_INSIDE; +} + +int bv_intersect_frustum_sphere(vec4* frustum, struct Bounding_Sphere* sphere, struct Transform* transform) +{ + int intersect_type = IT_INSIDE; + vec3 center; + vec3_add(¢er, &sphere->center, &transform->position); + vec3_mul(¢er, ¢er, &transform->scale); + + for(int i = 0; i < 6; i++) + { + vec3 plane_normal = {frustum[i].x, frustum[i].y, frustum[i].z}; + float distance = frustum[i].w; + float dot = vec3_dot(&plane_normal, ¢er) + distance; + if(dot < -sphere->radius) + { + intersect_type = IT_OUTSIDE; + return intersect_type; + } + + if(fabsf(dot) < sphere->radius) + { + intersect_type = IT_INTERSECT; + return intersect_type; + } + } + return intersect_type; +} + +int bv_intersect_frustum_point(vec4* frustum, const vec3* point) +{ + int success = 1; + for(int i = 0; i < 6; i++) + { + if((frustum[i].x * point->x + + frustum[i].y * point->y + + frustum[i].z * point->z + + frustum[i].w) < 0 ) + success = 0; + } + return success; +} diff --git a/src/bounding_volumes.h b/src/bounding_volumes.h new file mode 100644 index 0000000..ad8e219 --- /dev/null +++ b/src/bounding_volumes.h @@ -0,0 +1,41 @@ +#ifndef bounding_volumes_H +#define bounding_volumes_H + +#include "linmath.h" + +struct Transform; + +struct Bounding_Box +{ + vec3 min; + vec3 max; +}; + +struct Bounding_Sphere +{ + vec3 center; + float radius; +}; + +enum Intersection_Type +{ + IT_OUTSIDE = 0, + IT_INTERSECT, + IT_INSIDE, +}; + +enum Frustum_Planes +{ + FP_LEFT = 0, + FP_RIGHT, + FP_BOTTOM, + FP_TOP, + FP_NEAR, + FP_FAR +}; + +int bv_intersect_frustum_box(vec4* frustum, struct Bounding_Box* box, struct Transform* transform); +int bv_intersect_frustum_sphere(vec4* frustum, struct Bounding_Sphere* sphere, struct Transform* transform); +int bv_intersect_frustum_point(vec4* frustum, const vec3* point); + +#endif diff --git a/src/camera.c b/src/camera.c index 9fc26fa..703fdac 100644 --- a/src/camera.c +++ b/src/camera.c @@ -5,6 +5,7 @@ #include "array.h" #include "framebuffer.h" #include "texture.h" +#include "bounding_volumes.h" #include "utils.h" #include "log.h" @@ -16,6 +17,7 @@ static struct Camera* camera_list; static int* empty_indices; static int primary_camera_index; +static void update_frustum(struct Camera* camera); struct Camera* camera_get(int index) { @@ -91,6 +93,7 @@ int camera_create(int node, int width, int height) void camera_update_view_proj(struct Camera* camera) { mat4_mul(&camera->view_proj_mat, &camera->proj_mat, &camera->view_mat); + update_frustum(camera); } void camera_update_view(struct Camera* camera) @@ -195,7 +198,6 @@ void camera_set_primary_viewer(struct Camera* camera) if(camera_list[i].node == camera->node) { primary_camera_index = i; - log_message("Camera at index %d set as primary viewer", primary_camera_index); break; } @@ -210,3 +212,46 @@ struct Camera* camera_get_primary(void) primary_camera = &camera_list[primary_camera_index]; return primary_camera; } + +static void update_frustum(struct Camera* camera) +{ + assert(camera); + float* mvp = &camera->view_proj_mat.mat[0]; + + camera->frustum[FP_LEFT].x = mvp[3] + mvp[0]; + camera->frustum[FP_LEFT].y = mvp[7] + mvp[4]; + camera->frustum[FP_LEFT].z = mvp[11] + mvp[2]; + camera->frustum[FP_LEFT].w = mvp[15] + mvp[12]; + + camera->frustum[FP_RIGHT].x = mvp[3] - mvp[0]; + camera->frustum[FP_RIGHT].y = mvp[7] - mvp[4]; + camera->frustum[FP_RIGHT].z = mvp[11] - mvp[8]; + camera->frustum[FP_RIGHT].w = mvp[15] - mvp[12]; + + camera->frustum[FP_BOTTOM].x = mvp[3] + mvp[1]; + camera->frustum[FP_BOTTOM].y = mvp[11] + mvp[5]; + camera->frustum[FP_BOTTOM].z = mvp[11] + mvp[9]; + camera->frustum[FP_BOTTOM].w = mvp[15] + mvp[13]; + + camera->frustum[FP_TOP].x = mvp[3] - mvp[1]; + camera->frustum[FP_TOP].y = mvp[7] - mvp[5]; + camera->frustum[FP_TOP].z = mvp[11] - mvp[9]; + camera->frustum[FP_TOP].w = mvp[15] - mvp[13]; + + camera->frustum[FP_NEAR].x = mvp[3] + mvp[2]; + camera->frustum[FP_NEAR].y = mvp[7] + mvp[6]; + camera->frustum[FP_NEAR].z = mvp[11] + mvp[10]; + camera->frustum[FP_NEAR].w = mvp[15] + mvp[14]; + + camera->frustum[FP_FAR].x = mvp[3] - mvp[2]; + camera->frustum[FP_FAR].y = mvp[7] - mvp[6]; + camera->frustum[FP_FAR].z = mvp[11] - mvp[10]; + camera->frustum[FP_FAR].w = mvp[15] - mvp[14]; + + for(int i = 0; i < 6; i++) + { + vec3 plane_xyz = {camera->frustum[i].x, camera->frustum[i].y, camera->frustum[i].z}; + float length = vec3_len(&plane_xyz); + vec4_scale(&camera->frustum[i], &camera->frustum[i], (1.f / length)); + } +} diff --git a/src/camera.h b/src/camera.h index bb56026..23d80fa 100644 --- a/src/camera.h +++ b/src/camera.h @@ -18,6 +18,7 @@ struct Camera int render_tex; int depth_tex; vec4 clear_color; + vec4 frustum[6]; }; struct Camera* camera_get(int index); diff --git a/src/game.c b/src/game.c index f57b8c9..efdfdbd 100644 --- a/src/game.c +++ b/src/game.c @@ -83,6 +83,10 @@ void scene_setup(void) player_node = player->node; vec3 viewer_pos = {0, 0, 10}; struct Transform* viewer_tran = entity_component_get(player, C_TRANSFORM); + struct Model* player_model = entity_component_add(player, C_MODEL, "sphere.pamesh"); + vec4 color = {0.f, 1.f, 1.f, 1.f }; + model_set_material_param(player_model, "diffuse_color", &color); + vec4_fill(&color, 1.f, 1.f, 1.f, 1.f); transform_set_position(viewer_tran, &viewer_pos); int render_width, render_height; render_width = 800; @@ -95,7 +99,6 @@ void scene_setup(void) struct Entity* new_ent = scene_add_new("Model_Entity", NULL); struct Transform* tran = entity_component_get(new_ent, C_TRANSFORM); vec3 position = {0, 0, -5}; - vec4 color = {1.f, 1.f, 1.f, 1.f }; transform_translate(tran, &position, TS_WORLD); struct Model* box_model = entity_component_add(new_ent, C_MODEL, "default.pamesh"); model_set_material_param(box_model, "diffuse_color", &color); @@ -124,8 +127,7 @@ void scene_setup(void) screen_model->geometry_index = geom_find("Quad"); struct Entity* screen_camera = scene_add_as_child("Screen_Camera", NULL, screen->node); struct Transform* screen_camera_tran = entity_component_get(screen_camera, C_TRANSFORM); - vec3 screen_cam_tran = {0, -5, 5}; - transform_translate(screen_camera_tran, &screen_camera_tran, TS_PARENT); + transform_rotate(screen_camera_tran, &UNIT_Y, 180.f, TS_WORLD); struct Camera* cam = entity_component_add(screen_camera, C_CAMERA, 1024, 1024); camera_attach_fbo(cam, 1024, 1024, 1, 1); model_set_material_param(screen_model, "diffuse_color", &color); @@ -134,7 +136,10 @@ void scene_setup(void) void debug(float dt) { - struct Entity* entity = entity_get(player_node); + //struct Entity* entity = entity_get(player_node); + struct Entity* entity = !input_key_state_get('C', GLFW_PRESS) ? entity_get(player_node) : scene_find("Screen_Camera"); + struct Camera* cam = entity_component_get(entity, C_CAMERA); + camera_set_primary_viewer(cam); struct Transform* transform = entity_component_get(entity, C_TRANSFORM); float move_speed = 5.f, turn_speed = 50.f; vec3 offset = {0, 0, 0}; @@ -248,6 +253,19 @@ void debug(float dt) vec3 amount = {0, 0, 5 * dt}; transform_translate(mod_tran, &amount, TS_LOCAL); } + + /* if(input_key_state_get(GLFW_KEY_C, GLFW_PRESS)) */ + /* { */ + /* struct Entity* cam_ent = scene_find("Screen_Camera"); */ + /* struct Camera* cam = entity_component_get(cam_ent, C_CAMERA); */ + /* camera_set_primary_viewer(cam); */ + /* } */ + + /* if(input_key_state_get(GLFW_KEY_V, GLFW_PRESS)) */ + /* { */ + /* struct Camera* cam = entity_component_get(entity, C_CAMERA); */ + /* camera_set_primary_viewer(cam); */ + /* } */ } void run(void) diff --git a/src/geometry.c b/src/geometry.c index ee52b4d..0d0ea32 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -4,6 +4,8 @@ #include "file_io.h" #include "log.h" #include "renderer.h" +#include "bounding_volumes.h" +#include "transform.h" #include "GL/glew.h" #include "GLFW/glfw3.h" @@ -12,6 +14,7 @@ #include #include #include +#include struct Geometry { @@ -29,8 +32,8 @@ struct Geometry vec3* normals; vec2* uvs; uint* indices; - /* BoundingBox boundingBox; */ - /* BoundingSphere boundingSphere; */ + struct Bounding_Box bounding_box; + struct Bounding_Sphere bounding_sphere; }; @@ -41,6 +44,7 @@ static int* empty_indices; static int load_from_file(struct Geometry* geometry, const char* filename); static void create_vao(struct Geometry* geometry); static struct Geometry* generate_new_index(int* out_new_index); +static void generate_bounding_volume(int geomtry_index); void geom_init(void) { @@ -63,6 +67,29 @@ int geom_find(const char* filename) return index; } +static void generate_bounding_volume(int geometry_index) +{ + struct Geometry* geometry = &geometry_list[geometry_index]; + struct Bounding_Box* box = &geometry->bounding_box; + struct Bounding_Sphere* sphere = &geometry->bounding_sphere; + for(int i = 0; i < array_len(geometry->vertices); i++) + { + vec3* vertex = &geometry->vertices[i]; + if(vertex->x > box->max.x) box->max.x = vertex->x; + if(vertex->y > box->max.y) box->max.y = vertex->y; + if(vertex->z > box->max.z) box->max.z = vertex->z; + + if(vertex->x < box->min.x) box->min.x = vertex->x; + if(vertex->y < box->min.y) box->min.y = vertex->y; + if(vertex->z < box->min.z) box->min.z = vertex->z; + } + vec3_add(&sphere->center, &box->max, &box->min); + vec3_scale(&sphere->center, &sphere->center, (1.f / 2.f)); + vec3 len_vec; + vec3_sub(&len_vec, &box->max, &sphere->center); + sphere->radius = fabs(vec3_len(&len_vec)); +} + static struct Geometry* generate_new_index(int* out_new_index) { assert(out_new_index); @@ -97,7 +124,7 @@ int geom_create_from_file(const char* name) if(load_from_file(new_geo, name)) { create_vao(new_geo); - //generateBoundingBox(index); + generate_bounding_volume(index); } else { @@ -320,3 +347,15 @@ void geom_render(int index) glBindVertexArray(0); } + +void geom_render_in_frustum(int index, vec4* frustum, struct Transform* transform) +{ + struct Geometry* geometry = &geometry_list[index]; + int intersection = bv_intersect_frustum_sphere(frustum, &geometry->bounding_sphere, transform); + if(intersection == IT_INTERSECT || intersection == IT_INSIDE) + { + intersection = bv_intersect_frustum_box(frustum, &geometry->bounding_box, transform); + if(intersection == IT_INTERSECT || intersection == IT_INSIDE) + geom_render(index); + } +} diff --git a/src/geometry.h b/src/geometry.h index 204c62d..cfb867c 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -4,12 +4,20 @@ #include "num_types.h" #include "linmath.h" +struct Transform; + void geom_init(void); int geom_create_from_file(const char* name); -int geom_create(const char* name, vec3* vertices, vec2* uvs, vec3* normals, uint* indices, vec3* vertex_colors); int geom_find(const char* filename); void geom_remove(int index); void geom_cleanup(void); void geom_render(int index); +void geom_render_in_frustum(int index, vec4* frustum, struct Transform* transform); +int geom_create(const char* name, + vec3* vertices, + vec2* uvs, + vec3* normals, + uint* indices, + vec3* vertex_colors); #endif diff --git a/src/linmath.c b/src/linmath.c index f33d029..50017cd 100644 --- a/src/linmath.c +++ b/src/linmath.c @@ -181,6 +181,13 @@ void vec3_transform_norm(vec3* res, const vec3* val, const mat4* mat) res->z = v.z; } +float vec3_dot(vec3* v1, vec3* v2) +{ + return (v1->x * v2->x + + v1->y * v2->y + + v1->z * v2->z); +} + void vec4_fill(vec4* res, float x, float y, float z, float w) { diff --git a/src/linmath.h b/src/linmath.h index 64e8b74..bfcadca 100644 --- a/src/linmath.h +++ b/src/linmath.h @@ -79,6 +79,7 @@ void vec3_scale(vec3* res, const vec3* val, float s); void vec3_transform_norm(vec3* res, const vec3* val, const mat4* mat); int vec3_equals(vec3* v1, vec3* v2); float vec3_len(vec3* val); +float vec3_dot(vec3* v1, vec3* v2); /* vec4 */ int vec4_equals(vec4* v1, vec4* v2); diff --git a/src/model.c b/src/model.c index 2e4e4a6..aee1159 100644 --- a/src/model.c +++ b/src/model.c @@ -146,7 +146,7 @@ void model_render_all(struct Camera* camera) } } /* Render the geometry */ - geom_render(model->geometry_index); + geom_render_in_frustum(model->geometry_index, &camera->frustum, transform); for(int k = 0; k < array_len(model->material_params); k++) { diff --git a/src/renderer.c b/src/renderer.c index bc634c8..056f5fb 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -25,6 +25,8 @@ void renderer_init(GLFWwindow* window) glClearColor(0.3f, 0.6f, 0.9f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); glfwSetFramebufferSizeCallback(window, on_framebuffer_size_change); /* Quad geometry for final render */