
.. _program_listing_file_Src_GraphicsEngineOpenGL_scene_ViewFrustumCulling.cpp:

Program Listing for File ViewFrustumCulling.cpp
===============================================

|exhale_lsh| :ref:`Return to documentation for file <file_Src_GraphicsEngineOpenGL_scene_ViewFrustumCulling.cpp>` (``Src/GraphicsEngineOpenGL/scene/ViewFrustumCulling.cpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   module;
   
   #include <glad/glad.h>
   #include <glm/ext/vector_float3.hpp>
   #include <glm/ext/matrix_float4x4.hpp>
   #include <glm/geometric.hpp>
   #include <glm/trigonometric.hpp>
   #include <memory>
   #include <vector>
   #include <utility>
   
   #include "hostDevice/GlobalValues.hpp"
   
   module kataglyphis.opengl.view_frustum_culling;
   
   import kataglyphis.opengl.camera;
   import kataglyphis.opengl.aabb;
   
   ViewFrustumCulling::ViewFrustumCulling()
   {
       VBO = 0;
       VAO = 0;
       EBO = 0;
       m_drawCount = 0;
   
       near_plane = 0.F;
       far_plane = 0.F;
       fov = 0.F;
       ratio = 0.F;
   
       tan = 0.F;
       near_height = 0.F;
       near_width = 0.F;
       far_height = 0.F;
       far_width = 0.F;
   
       dir = glm::vec3(0.F);
       near_center = glm::vec3(0.F);
       far_center = glm::vec3(0.F);
   
       near_top_left = glm::vec3(0.F);
       near_top_right = glm::vec3(0.F);
       near_bottom_left = glm::vec3(0.F);
       near_bottom_right = glm::vec3(0.F);
   
       far_top_left = glm::vec3(0.F);
       far_top_right = glm::vec3(0.F);
       far_bottom_left = glm::vec3(0.F);
       far_bottom_right = glm::vec3(0.0f, 0.0f, 0.0f);
   }
   
   auto ViewFrustumCulling::is_inside(GLfloat cam_ratio,
     const std::shared_ptr<Camera> &cam,
     const std::shared_ptr<AABB> &bounding_box,
     glm::mat4 model) -> bool
   {
       GLfloat const cam_near_plane = cam->get_near_plane();
       GLfloat const cam_far_plane = cam->get_far_plane();
       GLfloat const cam_fov = cam->get_fov();
   
       update_frustum_param(cam_near_plane, cam_far_plane, cam_fov, cam_ratio, cam);
   
       std::vector<glm::vec3> const aabb_corners = bounding_box->get_corners(model);
   
       // layout:                      [0]: near plane, [1] far plane, [2] up    ,
       // [3] bottom , [4]: left , [5]: right outcodes (binary) :  100000 , 010000 ,
       // 000100 , 001000     , 000010, 000001 outcodes (dezi)     :  32 , 16 , 4 , 8
       // , 2           , 1
       bool result = true;
   
       GLint const outcode_near_plane = 32;
       GLint const outcode_far_plane = 16;
       GLint const outcode_up = 4;
       GLint const outcode_bottom = 8;
       GLint const outcode_left = 2;
       GLint const outcode_right = 1;
       // GLint outcode;
   
       GLint const outcodes_pattern[NUM_FRUSTUM_PLANES] = {
           outcode_near_plane, outcode_far_plane, outcode_up, outcode_bottom, outcode_left, outcode_right
       };
   
       for (size_t i = 0; i < NUM_FRUSTUM_PLANES; i++) {
           frustum_plane const plane = frustum_planes[i];
   
           if (corners_outside_plane(aabb_corners, plane, static_cast<GLuint>(outcodes_pattern[i]))) {
               result = false;
               break;
           }
       }
   
       return result;
   }
   
   void ViewFrustumCulling::render_view_frustum() const
   {
       // seeing as we only have a single VAO there's no need to bind it every time,
       // but we'll do so to keep things a bit more organized
       glBindVertexArray(VAO);
       // glDrawArrays(GL_TRIANGLES, 0, 6);
       glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, nullptr);
   
       // unbind all again
       glBindVertexArray(0);
   }
   
   auto ViewFrustumCulling::corners_outside_plane(std::vector<glm::vec3> aabb_corners,
     frustum_plane plane,
     GLuint outcode_pattern) -> bool
   {
       GLuint outcode = outcode_pattern;
   
       for (size_t i = 0; std::cmp_less(i, aabb_corners.size()); i++) {
           if (plane_point_distance(plane, aabb_corners[i]) < 0.0F) {
               if (i == 0) {
                   outcode = outcode_pattern;
               } else {
                   outcode = outcode & outcode_pattern;
               }
           } else {
               if (i == 0) {
                   outcode = 0;
               } else {
                   outcode = outcode & 0;
               }
           }
       }
   
       return outcode != 0;
   }
   
   auto ViewFrustumCulling::plane_point_distance(frustum_plane plane, glm::vec3 corner) -> GLfloat
   {
       GLfloat result = 0.0F;
   
       glm::vec3 const plane_normal = plane.normal;
       glm::vec3 const plane_position = plane.position;
   
       GLfloat const d = glm::dot(plane_normal, plane_position);
   
       result = (glm::dot(plane_normal, corner) - d) / glm::length(plane_normal);
   
       return result;
   }
   
   void ViewFrustumCulling::update_frustum_param(GLfloat np,
     GLfloat fp,
     GLfloat f,
     GLfloat r,
     const std::shared_ptr<Camera> &cam)
   {
       this->near_plane = np;
       this->far_plane = fp;
       this->fov = f;
       this->ratio = r;
   
       tan = glm::tan(glm::radians(f) * 0.5F);
       near_height = np * tan;
       near_width = near_height * r;
       far_height = fp * tan;
       far_width = far_height * r;
   
       this->main_camera = cam;
   
       near_center = cam->get_camera_position() + cam->get_camera_direction() * np;
       far_center = cam->get_camera_position() + cam->get_camera_direction() * fp;
   
       glm::vec3 aux_position;
       glm::vec3 aux;
       glm::vec3 aux_normal;
   
       // layout:  [0]: near plane
       frustum_planes[0].normal = cam->get_camera_direction();
       frustum_planes[0].position = near_center;
   
       // [1] far plane
       frustum_planes[1].normal = -cam->get_camera_direction();
       frustum_planes[1].position = far_center;
   
       aux_position = near_center + cam->get_up_axis() * near_height;
       aux = aux_position - cam->get_camera_position();
       aux = glm::normalize(aux);
       aux_normal = glm::cross(aux, cam->get_right_axis());
   
       // [2] top
       frustum_planes[2].normal = normalize(aux_normal);
       frustum_planes[2].position = aux_position;
   
       aux_position = near_center - cam->get_up_axis() * near_height;
       aux = aux_position - cam->get_camera_position();
       aux = glm::normalize(aux);
       aux_normal = glm::cross(cam->get_right_axis(), aux);
   
       // [3] bottom
       frustum_planes[3].normal = normalize(aux_normal);
       frustum_planes[3].position = aux_position;
   
       aux_position = near_center - cam->get_right_axis() * near_width;
       aux = aux_position - cam->get_camera_position();
       aux = glm::normalize(aux);
       aux_normal = glm::cross(aux, cam->get_up_axis());
   
       // [4]: left
       frustum_planes[4].normal = normalize(aux_normal);
       frustum_planes[4].position = aux_position;
   
       aux_position = near_center + cam->get_right_axis() * near_width;
       aux = aux_position - cam->get_camera_position();
       aux = glm::normalize(aux);
       aux_normal = glm::cross(cam->get_up_axis(), aux);
   
       // [5]: right
       frustum_planes[5].normal = normalize(aux_normal);
       frustum_planes[5].position = aux_position;
   
       // init(frustum_corners);
   }
   
   void ViewFrustumCulling::init(std::vector<glm::vec3> frustum_corner)
   {
       // unsigned int num_corners = 8;
       m_drawCount = 36;// num_corners;
   
       float vertices[] = {
   
           frustum_corner[0].x,
           frustum_corner[0].y,
           frustum_corner[0].z,// left bottom front
           frustum_corner[1].x,
           frustum_corner[1].y,
           frustum_corner[1].z,// left bottom back
           frustum_corner[2].x,
           frustum_corner[2].y,
           frustum_corner[2].z,// left top front
           frustum_corner[3].x,
           frustum_corner[3].y,
           frustum_corner[3].z,// left top back
           frustum_corner[4].x,
           frustum_corner[4].y,
           frustum_corner[4].z,// right bottom front
           frustum_corner[5].x,
           frustum_corner[5].y,
           frustum_corner[5].z,// right bottom back
           frustum_corner[6].x,
           frustum_corner[6].y,
           frustum_corner[6].z,// right top front
           frustum_corner[7].x,
           frustum_corner[7].y,
           frustum_corner[7].z// right top back
   
       };
   
       unsigned int indices[] = {
           // note that we start from 0!
           // left
           0,
           1,
           3,
           0,
           2,
           3,
           // right
           4,
           5,
           7,
           4,
           6,
           7,
           // top
           3,
           2,
           7,
           3,
           2,
           8,
           // bottom
           0,
           1,
           4,
           0,
           1,
           5,
           // back
           1,
           3,
           5,
           1,
           3,
           7,
           // front
           0,
           2,
           4,
           0,
           2,
           6,
       };
   
       glGenVertexArrays(1, &VAO);
       glGenBuffers(1, &VBO);
       glGenBuffers(1, &EBO);
       // bind the Vertex Array Object first, then bind and set vertex buffer(s), and
       // then configure vertex attributes(s).
       glBindVertexArray(VAO);
   
       glBindBuffer(GL_ARRAY_BUFFER, VBO);
       glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
   
       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
       glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
   
       glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
       glEnableVertexAttribArray(0);
   
       // note that this is allowed, the call to glVertexAttribPointer registered VBO
       // as the vertex attribute's bound vertex buffer object so afterwards we can
       // safely unbind
       glBindBuffer(GL_ARRAY_BUFFER, 0);
   
       // remember: do NOT unbind the EBO while a VAO is active as the bound element
       // buffer object IS stored in the VAO; keep the EBO bound.
       // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
   
       // You can unbind the VAO afterwards so other VAO calls won't accidentally
       // modify this VAO, but this rarely happens. Modifying other VAOs requires a
       // call to glBindVertexArray anyways so we generally don't unbind VAOs (nor
       // VBOs) when it's not directly necessary.
       glBindVertexArray(0);
   }
   
   ViewFrustumCulling::~ViewFrustumCulling()
   {
       glDeleteVertexArrays(1, &VAO);
       glDeleteBuffers(1, &VBO);
       glDeleteBuffers(1, &EBO);
   }
