Program Listing for File ViewFrustumCulling.cpp

Program Listing for File ViewFrustumCulling.cpp#

Return to documentation for file (Src/GraphicsEngineOpenGL/scene/ViewFrustumCulling.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);
}