Tesseract-Engine/src/Windows/RenderWindow.cpp

731 lines
26 KiB
C++
Raw Normal View History

#include "RenderWindow.h"
2025-01-05 04:04:21 +00:00
2025-01-05 04:04:21 +00:00
#include <vector>
2025-01-06 18:43:18 +00:00
#include <GL/glew.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
2025-01-05 04:04:21 +00:00
#include "Components/GameObject.h"
#include "Components/mesh.h"
#include "Components/transform.h"
2025-01-06 18:43:18 +00:00
#include "Components/CameraComponent.h"
2025-01-05 04:04:21 +00:00
#include "Engine/AssetManager.h"
#include "Engine/Settings.h"
2025-01-06 18:43:18 +00:00
#include "Engine/InputManager.h"
#include "Engine/KeyCode.h"
2025-01-05 04:04:21 +00:00
#include "Rendering/Shader.h"
2025-01-05 04:04:21 +00:00
#include "ImGuizmo.h"
#include "gcml.h"
2025-01-05 04:04:21 +00:00
#include "Icons.h"
2025-01-06 18:43:18 +00:00
#include "imgui.h"
#define CAM_FOV 45.0f
#define CAM_NEAR_PLAIN 0.1f
2024-12-30 06:51:58 +00:00
#define CAM_FAR_PLAIN 2048.0f
2025-01-06 18:43:18 +00:00
float editorYaw = -90.0f; // Horizontal angle, initialized to face along negative Z-axis
float editorPitch = 0.0f; // Vertical angle
float editorDistance = 5.0f; // Distance from the target
glm::vec3 editorTarget(0.0f, 0.0f, 0.0f); // The point the camera orbits around
// Configuration Parameters
const float rotationSpeed = 0.1f; // Sensitivity for mouse rotation
const float zoomSpeed = 2.0f; // Sensitivity for zooming
const float movementSpeed = 5.0f; // Speed for panning
const float minZoom = 2.0f; // Minimum zoom distance
const float maxZoom = 20.0f; // Maximum zoom distance
2025-01-05 04:04:21 +00:00
// Managers
extern AssetManager g_AssetManager;
extern Settings g_SettingsManager;
2025-01-06 18:43:18 +00:00
extern InputManager g_InputManager;
2025-01-05 04:04:21 +00:00
// Settings
extern bool DrawBBBO;
// GameObjects
extern std::vector<std::shared_ptr<GameObject>> g_GameObjects;
extern std::shared_ptr<CameraComponent> g_RuntimeCameraObject;
2025-01-05 04:04:21 +00:00
extern GameObject *g_SelectedObject;
2025-01-05 04:04:21 +00:00
// Profilers
extern int g_GPU_Triangles_drawn_to_screen;
2025-01-03 23:14:25 +00:00
extern int g_GPU_Draw_Calls;
// Simple container for six planes
struct FrustumPlanes
{
glm::vec4 planes[6]; // Each plane is (A,B,C,D)
};
// Extract 6 planes from the combined view-projection matrix
inline FrustumPlanes ExtractFrustumPlanes(const glm::mat4 &vp)
{
FrustumPlanes frustum;
// Left
frustum.planes[0] = glm::vec4(
vp[0][3] + vp[0][0],
vp[1][3] + vp[1][0],
vp[2][3] + vp[2][0],
vp[3][3] + vp[3][0]);
// Right
frustum.planes[1] = glm::vec4(
vp[0][3] - vp[0][0],
vp[1][3] - vp[1][0],
vp[2][3] - vp[2][0],
vp[3][3] - vp[3][0]);
// Bottom
frustum.planes[2] = glm::vec4(
vp[0][3] + vp[0][1],
vp[1][3] + vp[1][1],
vp[2][3] + vp[2][1],
vp[3][3] + vp[3][1]);
// Top
frustum.planes[3] = glm::vec4(
vp[0][3] - vp[0][1],
vp[1][3] - vp[1][1],
vp[2][3] - vp[2][1],
vp[3][3] - vp[3][1]);
// Near
frustum.planes[4] = glm::vec4(
vp[0][3] + vp[0][2],
vp[1][3] + vp[1][2],
vp[2][3] + vp[2][2],
vp[3][3] + vp[3][2]);
// Far
frustum.planes[5] = glm::vec4(
vp[0][3] - vp[0][2],
vp[1][3] - vp[1][2],
vp[2][3] - vp[2][2],
vp[3][3] - vp[3][2]);
// Normalize planes (optional but recommended)
for (int i = 0; i < 6; i++)
{
float length = glm::length(glm::vec3(
frustum.planes[i].x,
frustum.planes[i].y,
frustum.planes[i].z));
if (length > 0.0f)
{
frustum.planes[i] /= length;
}
}
return frustum;
}
inline bool IsBoxInFrustum(const glm::vec3 &bmin,
const glm::vec3 &bmax,
const FrustumPlanes &frustum)
{
// Build the 8 corners in world space
// (We assume bmin/bmax are already in world space.)
std::array<glm::vec3, 8> corners = {
glm::vec3(bmin.x, bmin.y, bmin.z),
glm::vec3(bmin.x, bmin.y, bmax.z),
glm::vec3(bmin.x, bmax.y, bmin.z),
glm::vec3(bmin.x, bmax.y, bmax.z),
glm::vec3(bmax.x, bmin.y, bmin.z),
glm::vec3(bmax.x, bmin.y, bmax.z),
glm::vec3(bmax.x, bmax.y, bmin.z),
glm::vec3(bmax.x, bmax.y, bmax.z)};
// For each plane, check if all corners are behind it.
// If yes => box is completely outside.
for (int p = 0; p < 6; p++)
{
const glm::vec4 &plane = frustum.planes[p];
int outCount = 0;
for (const auto &corner : corners)
{
// Plane eq: A*x + B*y + C*z + D
float distance = plane.x * corner.x +
plane.y * corner.y +
plane.z * corner.z +
plane.w;
if (distance < 0.0f)
outCount++;
}
// If all corners are 'behind' the plane, box is outside
if (outCount == 8)
return false;
}
// Otherwise, it's at least partially inside
return true;
}
2024-12-31 22:35:06 +00:00
bool PlayPauseButton(const char *label, bool *isPlaying, ImVec2 Size)
{
// Define button size
// Begin the button
2024-12-31 22:35:06 +00:00
if (ImGui::Button(label, Size))
{
// Toggle the state
*isPlaying = !(*isPlaying);
return true; // Indicate that the state was toggled
}
// Add tooltip
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip(*isPlaying ? "Pause (Space)" : "Play (Space)");
}
// Get the current window's draw list
2024-12-31 22:35:06 +00:00
ImDrawList *draw_list = ImGui::GetWindowDrawList();
// Get the position of the button
ImVec2 button_pos = ImGui::GetItemRectMin();
ImVec2 button_size = ImGui::GetItemRectSize();
ImVec2 center = ImVec2(button_pos.x + button_size.x * 0.5f, button_pos.y + button_size.y * 0.5f);
// Define icon size
2024-12-31 22:35:06 +00:00
float icon_size = 0.4f * Size.x;
float half_icon_size = icon_size / 2.0f;
// Define colors
ImU32 icon_color = ImGui::GetColorU32(ImGuiCol_Text);
if (*isPlaying)
{
// Draw Pause Icon (two vertical bars)
float bar_width = 4.0f;
2024-12-31 22:35:06 +00:00
float spacing = 0.1f * Size.x;
// Left bar
ImVec2 left_bar_p1 = ImVec2(center.x - spacing - bar_width, center.y - half_icon_size);
ImVec2 left_bar_p2 = ImVec2(center.x - spacing, center.y + half_icon_size);
draw_list->AddRectFilled(left_bar_p1, left_bar_p2, icon_color, 2.0f);
// Right bar
ImVec2 right_bar_p1 = ImVec2(center.x + spacing, center.y - half_icon_size);
ImVec2 right_bar_p2 = ImVec2(center.x + spacing + bar_width, center.y + half_icon_size);
draw_list->AddRectFilled(right_bar_p1, right_bar_p2, icon_color, 2.0f);
}
else
{
// Draw Play Icon (triangle)
ImVec2 p1 = ImVec2(center.x - half_icon_size, center.y - half_icon_size);
ImVec2 p2 = ImVec2(center.x - half_icon_size, center.y + half_icon_size);
ImVec2 p3 = ImVec2(center.x + half_icon_size, center.y);
draw_list->AddTriangleFilled(p1, p2, p3, icon_color);
}
return false; // No toggle occurred
}
2025-01-05 04:04:21 +00:00
// Enum for gizmo operations
enum GizmoOperation
{
GIZMO_TRANSLATE,
GIZMO_ROTATE,
GIZMO_SCALE
};
// Initialize with a default operation
GizmoOperation currentOperation = GIZMO_TRANSLATE;
2025-01-06 18:43:18 +00:00
void RenderWindow::Show(bool *GameRunning, double deltaTime)
{
2025-01-05 04:04:21 +00:00
// Begin the ImGui window with an icon and label
2025-01-01 19:39:45 +00:00
ImGui::Begin(ICON_FA_GAMEPAD " Editor##EditorWindow");
2025-01-05 04:04:21 +00:00
ImGuizmo::BeginFrame();
ImGuizmo::SetOrthographic(false);
ImGuizmo::SetDrawlist();
// Initialize OpenGL resources if not already done
if (!m_Initialized)
{
InitGLResources();
m_Initialized = true;
}
2025-01-05 04:04:21 +00:00
// Get the available size for rendering within the window
2024-12-31 22:35:06 +00:00
ImVec2 size = ImGui::GetContentRegionAvail();
int w = static_cast<int>(size.x);
int h = static_cast<int>(size.y);
2025-01-05 04:04:21 +00:00
// Check if there's space to render
if (w > 0 && h > 0)
{
2025-01-05 04:04:21 +00:00
// Resize the FBO if the window size has changed
if (w != m_LastWidth || h != m_LastHeight)
{
m_FBO.Create(w, h);
m_LastWidth = w;
m_LastHeight = h;
}
2025-01-05 04:04:21 +00:00
// Render the 3D scene to the FBO
RenderSceneToFBO(GameRunning);
2025-01-05 04:04:21 +00:00
// Display the rendered scene as an image in ImGui
// Correctly cast the texture ID for OpenGL
ImGui::Image(m_FBO.GetTextureID(), size, ImVec2(0, 0), ImVec2(1, 1));
2024-12-31 22:35:06 +00:00
// Calculate button position to place it slightly right and down from the top-left of the image
ImVec2 imagePos = ImGui::GetItemRectMin();
// Add an offset to position the button
2025-01-05 04:04:21 +00:00
ImVec2 buttonOffset(10.0f, 10.0f); // 10 pixels right and 10 pixels down
2024-12-31 22:35:06 +00:00
ImVec2 buttonPos = ImVec2(imagePos.x + buttonOffset.x, imagePos.y + buttonOffset.y);
// Set cursor position for the button
ImGui::SetCursorScreenPos(buttonPos);
// Dynamically calculate button size based on window size
2025-01-05 04:04:21 +00:00
float buttonWidth = size.x * 0.05f; // 5% of the window width
2024-12-31 22:35:06 +00:00
ImVec2 buttonSize = ImVec2(buttonWidth, buttonWidth);
// Render the Play/Pause button with the calculated size
PlayPauseButton("##PlayPauseButton", GameRunning, buttonSize);
2025-01-05 04:04:21 +00:00
// ---------------------------------------------------
// *** Gizmo Operation Selection UI ***
// ---------------------------------------------------
// ---------------------------------------------------
// *** Integrate ImGuizmo for Manipulating Objects ***
// ---------------------------------------------------
// Ensure a GameObject is selected
if (g_SelectedObject)
{
// Retrieve the TransformComponent from the selected object
auto transform = g_SelectedObject->GetComponent<TransformComponent>();
if (transform)
{
// Get the current transformation matrix
glm::mat4 modelMatrix = transform->GetTransformMatrix();
// Obtain view and projection matrices from the active camera
glm::mat4 viewMatrix;
glm::mat4 projectionMatrix;
2025-01-06 18:43:18 +00:00
// Camera Selection and Setup Logic (e.g., within your rendering or update function)
2025-01-05 04:04:21 +00:00
2025-01-06 18:43:18 +00:00
if (m_ActiveCamera)
2025-01-05 04:04:21 +00:00
{
2025-01-06 18:43:18 +00:00
// Use the existing active camera
2025-01-05 04:04:21 +00:00
viewMatrix = m_ActiveCamera->GetViewMatrix();
projectionMatrix = m_ActiveCamera->GetProjectionMatrix();
}
else
{
2025-01-06 18:43:18 +00:00
m_ActiveCamera = m_EditorCamera.GetComponent<CameraComponent>();
2025-01-05 04:04:21 +00:00
2025-01-06 18:43:18 +00:00
// 1. Mouse Input for Rotation
if (g_InputManager.IsMouseButtonPressed(MouseButton::RIGHT))
{
float deltaX = g_InputManager.GetMouseDeltaX();
float deltaY = g_InputManager.GetMouseDeltaY();
2025-01-05 04:04:21 +00:00
2025-01-06 18:43:18 +00:00
editorYaw += deltaX * rotationSpeed;
editorPitch += deltaY * rotationSpeed;
2025-01-05 04:04:21 +00:00
2025-01-06 18:43:18 +00:00
// Clamp the pitch to prevent flipping
if (editorPitch > 89.0f)
editorPitch = 89.0f;
if (editorPitch < -89.0f)
editorPitch = -89.0f;
2025-01-05 04:04:21 +00:00
}
2025-01-06 18:43:18 +00:00
// 2. Scroll Input for Zooming
float scrollDelta = g_InputManager.GetScrollDelta();
editorDistance -= scrollDelta * zoomSpeed;
editorDistance = glm::clamp(editorDistance, minZoom, maxZoom);
// 3. Keyboard Input for Panning (WASD)
glm::vec3 forward = glm::normalize(editorTarget - m_EditorCamera.GetComponent<TransformComponent>()->GetPosition());
glm::vec3 right = glm::normalize(glm::cross(forward, glm::vec3(0.0f, 1.0f, 0.0f)));
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
deltaTime = static_cast<float>(deltaTime);
if (g_InputManager.IsKeyPressed(KeyCode::W))
editorTarget += up * movementSpeed * deltaTime;
if (g_InputManager.IsKeyPressed(KeyCode::S))
editorTarget -= up * movementSpeed * deltaTime;
if (g_InputManager.IsKeyPressed(KeyCode::A))
editorTarget -= right * movementSpeed * deltaTime;
if (g_InputManager.IsKeyPressed(KeyCode::D))
editorTarget += right * movementSpeed * deltaTime;
// 4. Calculate the New Camera Position
glm::vec3 direction;
direction.x = cos(glm::radians(editorYaw)) * cos(glm::radians(editorPitch));
direction.y = sin(glm::radians(editorPitch));
direction.z = sin(glm::radians(editorYaw)) * cos(glm::radians(editorPitch));
direction = glm::normalize(direction);
glm::vec3 newPosition = editorTarget - direction * editorDistance;
// 5. Update the Editor Camera's Transform
auto editorTransform = m_EditorCamera.GetComponent<TransformComponent>();
editorTransform->SetPosition(newPosition);
editorTransform->LookAt(editorTarget, up);
// 6. Retrieve Updated Matrices
viewMatrix = m_ActiveCamera->GetViewMatrix();
projectionMatrix = m_ActiveCamera->GetProjectionMatrix();
static ImGuizmo::OPERATION currentOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsWindowFocused() && ImGui::IsWindowHovered())
2025-01-05 04:04:21 +00:00
{
2025-01-06 18:43:18 +00:00
if (ImGui::IsKeyPressed(ImGuiKey_T))
{
currentOperation = ImGuizmo::TRANSLATE;
}
if (ImGui::IsKeyPressed(ImGuiKey_R))
{
currentOperation = ImGuizmo::ROTATE;
}
if (ImGui::IsKeyPressed(ImGuiKey_S))
{
currentOperation = ImGuizmo::SCALE;
}
2025-01-05 04:04:21 +00:00
}
2025-01-06 18:43:18 +00:00
// Define snap settings
bool snap = false; // Enable snapping if needed
float snapValue = 0.1f; // Snap increment
// Set the ImGuizmo rectangle to the window's position and size
ImVec2 windowPos = ImGui::GetWindowPos();
ImVec2 windowSize = ImGui::GetWindowSize();
ImGuizmo::SetRect(windowPos.x, windowPos.y, windowSize.x, windowSize.y);
// Render the gizmo and handle user interaction
projectionMatrix[1][1] *= -1.0f; // Flip Image Internaly
ImGuizmo::Manipulate(
glm::value_ptr(viewMatrix),
glm::value_ptr(projectionMatrix),
currentOperation,
ImGuizmo::LOCAL,
glm::value_ptr(modelMatrix),
nullptr, // Optional delta matrix
snap ? &snapValue : nullptr // Optional snap values
);
// Check if the gizmo is being used (i.e., if the user is interacting with it)
if (ImGuizmo::IsUsing())
2025-01-05 04:04:21 +00:00
{
2025-01-06 18:43:18 +00:00
// Update the TransformComponent with the modified matrix
transform->SetTransformMatrix(modelMatrix);
2025-01-05 04:04:21 +00:00
}
}
}
}
}
else
{
2025-01-05 04:04:21 +00:00
// Display message if there's insufficient space to render the scene
ImGui::Text("No space to render.");
}
2025-01-05 04:04:21 +00:00
// End the ImGui window
ImGui::End();
}
void RenderWindow::InitGLResources()
{
// ----------------------------------------------------
// 1) Load SHADER from the asset manager
// ----------------------------------------------------
2025-01-05 04:04:21 +00:00
// throw this in here cus we dont have a constructor
m_ActiveCamera = nullptr;
2025-01-06 18:43:18 +00:00
// Setup the editor camera
m_EditorCamera.AddComponent(std::make_shared<CameraComponent>());
m_EditorCamera.AddComponent(std::make_shared<TransformComponent>());
{
std::shared_ptr<Shader> shaderAsset = g_AssetManager.loadAsset<Shader>(AssetType::SHADER, "assets/shaders/UnlitMaterial");
if (!shaderAsset)
{
fprintf(stderr, "[RenderWindow] Failed to load shader via AssetManager.\n");
return;
}
// Cast back to your Shader class
m_ShaderPtr = shaderAsset.get();
}
{
std::shared_ptr<Shader> shaderAsset = g_AssetManager.loadAsset<Shader>(AssetType::SHADER, "assets/shaders/Line");
if (!shaderAsset)
{
fprintf(stderr, "[RenderWindow] Failed to load shader via AssetManager.\n");
return;
}
// Cast back to your Shader class
m_LineShaderPtr = shaderAsset.get();
}
// ----------------------------------------------------
// 3) Load TEXTURE from the asset manager
// ----------------------------------------------------
{
std::shared_ptr<GLuint> texAsset = g_AssetManager.loadAsset<GLuint>(AssetType::TEXTURE, "assets/textures/wood.png");
if (!texAsset)
{
fprintf(stderr, "[RenderWindow] Failed to load texture.\n");
}
else
{
// Cast from void* to GLuint
m_TextureID = *texAsset; // Assign the GLuint value
}
}
// ----------------------------------------------------
// 4) Initialize GameObjects
// ----------------------------------------------------
}
void CheckOpenGLError(const std::string &location)
{
GLenum err;
bool hasError = false;
while ((err = glGetError()) != GL_NO_ERROR)
{
std::cerr << "[OpenGL Error] (" << err << ") at " << location << std::endl;
hasError = true;
}
if (hasError)
{
// Optionally, you can throw an exception or handle the error as needed
}
}
#include <glm/gtc/type_ptr.hpp> // For glm::value_ptr
#include <algorithm> // Ensure <algorithm> is included
void RenderWindow::RenderSceneToFBO(bool *GameRunning)
{
m_RotationAngle += 0.001f; // Spin per frame
// 1) Bind the FBO and set up viewport
m_FBO.Bind();
glViewport(0, 0, m_LastWidth, m_LastHeight);
2025-01-01 19:39:45 +00:00
#if TRANSPERANCY
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
glEnable(GL_DEPTH_TEST);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 2) Check our main rendering shader
if (!m_ShaderPtr)
{
2024-12-30 06:51:58 +00:00
DEBUG_PRINT("[RenderWindow] Shader pointer is null. Cannot render.");
m_FBO.Unbind();
return;
}
m_ShaderPtr->Use();
// 3) Obtain view/projection from the active camera (or fallback)
std::shared_ptr<CameraComponent> activeCamera = nullptr;
glm::mat4 view, proj;
if (*GameRunning && g_RuntimeCameraObject)
{
activeCamera = g_RuntimeCameraObject;
}
if (activeCamera)
{
2025-01-05 04:04:21 +00:00
// TODO: Add camera Movement in editor
view = activeCamera->GetViewMatrix();
proj = activeCamera->GetProjectionMatrix();
2025-01-05 04:04:21 +00:00
m_ActiveCamera = activeCamera;
}
else
{
// Fallback if no camera
view = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, -5.f));
float aspect = (m_LastHeight != 0) ? (float)m_LastWidth / (float)m_LastHeight : 1.0f;
proj = glm::perspective(glm::radians(CAM_FOV), aspect, CAM_NEAR_PLAIN, CAM_FAR_PLAIN);
2025-01-05 04:04:21 +00:00
m_ActiveCamera = nullptr;
}
// 4) Extract frustum planes for culling
glm::mat4 vp = proj * view;
FrustumPlanes frustum = ExtractFrustumPlanes(vp);
// 5) Iterate over each GameObject and render
glm::vec4 LineColor = g_SettingsManager.S_LineColor;
for (auto &obj : g_GameObjects)
{
glm::mat4 model = glm::mat4(1.f);
auto transform = obj->GetComponent<TransformComponent>();
auto mesh = obj->GetComponent<MeshComponent>();
if (transform && mesh)
{
// 5A) Build the Model matrix
model = glm::translate(model, transform->position);
model = glm::rotate(model, glm::radians(transform->rotation.x), glm::vec3(1.f, 0.f, 0.f));
model = glm::rotate(model, glm::radians(transform->rotation.y), glm::vec3(0.f, 1.f, 0.f));
model = glm::rotate(model, glm::radians(transform->rotation.z), glm::vec3(0.f, 0.f, 1.f));
model = glm::scale(model, transform->scale);
// 5B) For each submesh
for (auto &submesh : mesh->submeshes)
{
// Check if VAO is valid
if (submesh.vao == 0)
{
DEBUG_PRINT("[RenderWindow] Warning: Submesh VAO is not initialized.");
continue;
}
// -------------------------
// *** FRUSTUM CULLING ***
// Transform submesh bounding box from local -> world space
glm::vec3 worldMin(FLT_MAX), worldMax(-FLT_MAX);
std::array<glm::vec3, 8> corners = {
glm::vec3(submesh.minExtents.x, submesh.minExtents.y, submesh.minExtents.z),
glm::vec3(submesh.minExtents.x, submesh.minExtents.y, submesh.maxExtents.z),
glm::vec3(submesh.minExtents.x, submesh.maxExtents.y, submesh.minExtents.z),
glm::vec3(submesh.minExtents.x, submesh.maxExtents.y, submesh.maxExtents.z),
glm::vec3(submesh.maxExtents.x, submesh.minExtents.y, submesh.minExtents.z),
glm::vec3(submesh.maxExtents.x, submesh.minExtents.y, submesh.maxExtents.z),
glm::vec3(submesh.maxExtents.x, submesh.maxExtents.y, submesh.minExtents.z),
glm::vec3(submesh.maxExtents.x, submesh.maxExtents.y, submesh.maxExtents.z)};
for (auto &c : corners)
{
// Transform corner by the model matrix
glm::vec4 worldPos = model * glm::vec4(c, 1.0f);
// Expand worldMin / worldMax
worldMin.x = std::min(worldMin.x, worldPos.x);
worldMin.y = std::min(worldMin.y, worldPos.y);
worldMin.z = std::min(worldMin.z, worldPos.z);
worldMax.x = std::max(worldMax.x, worldPos.x);
worldMax.y = std::max(worldMax.y, worldPos.y);
worldMax.z = std::max(worldMax.z, worldPos.z);
}
// Now test that box against frustum
if (!IsBoxInFrustum(worldMin, worldMax, frustum))
{
// The submesh is completely outside the camera's view => skip
continue;
}
// -------------------------
// 5C) Compute MVP and pass to shader
glm::mat4 mvp = vp * model;
m_ShaderPtr->SetMat4("uMVP", mvp);
m_ShaderPtr->SetMat4("uModel", model);
// 5D) Update tri count
g_GPU_Triangles_drawn_to_screen += static_cast<int>(submesh.indices.size() / 3);
// 5E) Bind textures
const int MAX_DIFFUSE = 32;
int textureUnit = 0;
for (auto &texture : submesh.textures)
{
if (texture.type == "texture_diffuse")
{
if (textureUnit >= MAX_DIFFUSE)
{
DEBUG_PRINT("[RenderWindow] Warning: Exceeded maximum diffuse textures.");
break;
}
glActiveTexture(GL_TEXTURE0 + textureUnit);
glBindTexture(GL_TEXTURE_2D, texture.id);
std::string uniformName = "uTextures.texture_diffuse[" + std::to_string(textureUnit) + "]";
m_ShaderPtr->SetInt(uniformName, textureUnit);
textureUnit++;
}
}
// Assign default texture to unused slots
2024-12-31 22:35:06 +00:00
for (int i = textureUnit; i < MAX_DIFFUSE; ++i)
{
std::string uniformName = "uTextures.texture_diffuse[" + std::to_string(i) + "]";
m_ShaderPtr->SetInt(uniformName, 0);
}
m_ShaderPtr->SetInt("uNumDiffuseTextures", textureUnit);
// 5F) Draw submesh (filled)
glBindVertexArray(submesh.vao);
glDrawElements(GL_TRIANGLES,
static_cast<GLsizei>(submesh.indices.size()),
GL_UNSIGNED_INT,
nullptr);
g_GPU_Draw_Calls++;
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
// ----------------------------------------
// 5G) OPTIONAL: Draw a bounding box outline
// ----------------------------------------
if (DrawBBBO && m_LineShaderPtr && submesh.bboxVAO != 0)
{
// Use a simpler line shader that doesn't rely on textures
m_LineShaderPtr->Use();
// Recompute the MVP for the bounding box (same model, same vp)
glm::mat4 bboxMVP = vp * model;
m_LineShaderPtr->SetMat4("uMVP", bboxMVP);
m_LineShaderPtr->SetVec4("uColor", LineColor);
glLineWidth(2.0f);
// If your line shader has a uniform color:
// m_LineShaderPtr->SetVec4("uColor", glm::vec4(1,1,0,1)); // e.g., yellow
// Draw the bounding box in wireframe lines
glBindVertexArray(submesh.bboxVAO);
// We assume submesh.bboxVertexCount is the number of line-vertices
glDrawArrays(GL_LINES, 0, submesh.bboxVertexCount);
glBindVertexArray(0);
glUseProgram(0);
}
}
}
}
2025-01-01 19:39:45 +00:00
#if TRANSPERANCY
glDisable(GL_BLEND);
#endif
glUseProgram(0);
// 6) Unbind the FBO
m_FBO.Unbind();
}