2024-12-25 23:35:38 +00:00
|
|
|
// RenderWindow.cpp
|
|
|
|
|
2024-12-25 21:44:33 +00:00
|
|
|
#include "RenderWindow.h"
|
2024-12-26 00:15:18 +00:00
|
|
|
#include <vector> // Add this line
|
|
|
|
|
2024-12-25 21:44:33 +00:00
|
|
|
#include <GL/glew.h>
|
2024-12-25 23:01:05 +00:00
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
2024-12-25 21:44:33 +00:00
|
|
|
#include "imgui.h"
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
#include "Engine/Settings.h"
|
|
|
|
|
2024-12-27 01:34:34 +00:00
|
|
|
#include "gcml.h"
|
2024-12-26 00:15:18 +00:00
|
|
|
|
2024-12-27 01:34:34 +00:00
|
|
|
#include "Componenets/GameObject.h"
|
|
|
|
#include "Componenets/mesh.h"
|
|
|
|
#include "Componenets/transform.h"
|
2024-12-26 00:15:18 +00:00
|
|
|
|
2024-12-27 01:34:34 +00:00
|
|
|
extern std::vector<std::shared_ptr<GameObject>> g_GameObjects;
|
2024-12-26 00:53:17 +00:00
|
|
|
|
2024-12-27 01:34:34 +00:00
|
|
|
#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
|
2024-12-26 00:15:18 +00:00
|
|
|
|
2024-12-25 23:35:38 +00:00
|
|
|
// Include your AssetManager & Shader headers
|
|
|
|
#include "Engine/AssetManager.h"
|
|
|
|
#include "Rendering/Shader.h"
|
2024-12-25 21:44:33 +00:00
|
|
|
|
2025-01-01 04:31:58 +00:00
|
|
|
#include "Icons.h"
|
|
|
|
|
2024-12-25 23:35:38 +00:00
|
|
|
// Extern reference to our global (or extern) asset manager
|
2024-12-25 23:01:05 +00:00
|
|
|
extern AssetManager g_AssetManager;
|
2025-01-04 00:32:39 +00:00
|
|
|
extern Settings g_SettingsManager;
|
2024-12-25 21:44:33 +00:00
|
|
|
|
2024-12-30 04:25:16 +00:00
|
|
|
extern std::shared_ptr<CameraComponent> g_RuntimeCameraObject;
|
|
|
|
|
2024-12-29 02:50:39 +00:00
|
|
|
extern int g_GPU_Triangles_drawn_to_screen;
|
2025-01-03 23:14:25 +00:00
|
|
|
extern int g_GPU_Draw_Calls;
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
extern bool DrawBBBO;
|
|
|
|
|
|
|
|
// 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-27 01:34:34 +00:00
|
|
|
|
2024-12-31 22:35:06 +00:00
|
|
|
bool PlayPauseButton(const char *label, bool *isPlaying, ImVec2 Size)
|
2024-12-25 21:44:33 +00:00
|
|
|
{
|
2024-12-29 02:50:39 +00:00
|
|
|
// Define button size
|
|
|
|
|
|
|
|
// Begin the button
|
2024-12-31 22:35:06 +00:00
|
|
|
if (ImGui::Button(label, Size))
|
2024-12-29 02:50:39 +00:00
|
|
|
{
|
|
|
|
// 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();
|
2024-12-29 02:50:39 +00:00
|
|
|
|
|
|
|
// 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;
|
2024-12-29 02:50:39 +00:00
|
|
|
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;
|
2024-12-29 02:50:39 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderWindow::Show(bool *GameRunning)
|
|
|
|
{
|
2025-01-01 19:39:45 +00:00
|
|
|
ImGui::Begin(ICON_FA_GAMEPAD " Editor##EditorWindow");
|
2024-12-25 21:44:33 +00:00
|
|
|
|
|
|
|
if (!m_Initialized)
|
|
|
|
{
|
|
|
|
InitGLResources();
|
|
|
|
m_Initialized = true;
|
|
|
|
}
|
|
|
|
|
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);
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2024-12-25 23:35:38 +00:00
|
|
|
// If there's space, render to the FBO, then show it as an ImGui image
|
2024-12-25 21:44:33 +00:00
|
|
|
if (w > 0 && h > 0)
|
|
|
|
{
|
|
|
|
if (w != m_LastWidth || h != m_LastHeight)
|
|
|
|
{
|
|
|
|
m_FBO.Create(w, h);
|
2024-12-27 01:34:34 +00:00
|
|
|
m_LastWidth = w;
|
2024-12-25 21:44:33 +00:00
|
|
|
m_LastHeight = h;
|
|
|
|
}
|
|
|
|
|
2024-12-30 04:25:16 +00:00
|
|
|
RenderSceneToFBO(GameRunning);
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2024-12-31 22:35:06 +00:00
|
|
|
// Render the image first
|
2024-12-27 01:34:34 +00:00
|
|
|
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
|
|
|
|
ImVec2 buttonOffset(10.0f, 10.0f); // Adjust these values as needed for the desired offset
|
|
|
|
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
|
|
|
|
float buttonWidth = size.x * 0.03f; // 5% of the window width
|
|
|
|
ImVec2 buttonSize = ImVec2(buttonWidth, buttonWidth);
|
|
|
|
|
|
|
|
// Render the Play/Pause button with the calculated size
|
|
|
|
PlayPauseButton("##PlayPauseButton", GameRunning, buttonSize);
|
2024-12-25 21:44:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ImGui::Text("No space to render.");
|
|
|
|
}
|
|
|
|
|
2024-12-29 02:50:39 +00:00
|
|
|
ImGui::End();
|
2024-12-25 21:44:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderWindow::InitGLResources()
|
|
|
|
{
|
2024-12-25 23:35:38 +00:00
|
|
|
// ----------------------------------------------------
|
|
|
|
// 1) Load SHADER from the asset manager
|
|
|
|
// ----------------------------------------------------
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2024-12-25 21:44:33 +00:00
|
|
|
{
|
2024-12-31 08:40:23 +00:00
|
|
|
std::shared_ptr<Shader> shaderAsset = g_AssetManager.loadAsset<Shader>(AssetType::SHADER, "assets/shaders/UnlitMaterial");
|
2024-12-25 23:35:38 +00:00
|
|
|
if (!shaderAsset)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "[RenderWindow] Failed to load shader via AssetManager.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Cast back to your Shader class
|
2024-12-31 08:40:23 +00:00
|
|
|
m_ShaderPtr = shaderAsset.get();
|
2024-12-25 21:44:33 +00:00
|
|
|
}
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-12-25 23:35:38 +00:00
|
|
|
// ----------------------------------------------------
|
|
|
|
// 3) Load TEXTURE from the asset manager
|
|
|
|
// ----------------------------------------------------
|
2024-12-25 23:01:05 +00:00
|
|
|
{
|
2024-12-31 08:40:23 +00:00
|
|
|
std::shared_ptr<GLuint> texAsset = g_AssetManager.loadAsset<GLuint>(AssetType::TEXTURE, "assets/textures/wood.png");
|
2024-12-25 23:35:38 +00:00
|
|
|
if (!texAsset)
|
2024-12-25 23:01:05 +00:00
|
|
|
{
|
2024-12-25 23:35:38 +00:00
|
|
|
fprintf(stderr, "[RenderWindow] Failed to load texture.\n");
|
2024-12-25 23:01:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-12-25 23:35:38 +00:00
|
|
|
// Cast from void* to GLuint
|
2024-12-31 08:40:23 +00:00
|
|
|
m_TextureID = *texAsset; // Assign the GLuint value
|
2024-12-25 23:01:05 +00:00
|
|
|
}
|
|
|
|
}
|
2024-12-26 00:15:18 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------
|
|
|
|
// 4) Initialize GameObjects
|
|
|
|
// ----------------------------------------------------
|
2024-12-27 01:34:34 +00:00
|
|
|
}
|
2024-12-26 00:15:18 +00:00
|
|
|
|
2024-12-30 04:25:16 +00:00
|
|
|
void CheckOpenGLError(const std::string &location)
|
2024-12-25 21:44:33 +00:00
|
|
|
{
|
2024-12-30 04:25:16 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-31 08:40:23 +00:00
|
|
|
#include <glm/gtc/type_ptr.hpp> // For glm::value_ptr
|
|
|
|
#include <algorithm> // Ensure <algorithm> is included
|
2024-12-30 04:25:16 +00:00
|
|
|
void RenderWindow::RenderSceneToFBO(bool *GameRunning)
|
|
|
|
{
|
|
|
|
m_RotationAngle += 0.001f; // Spin per frame
|
2024-12-25 21:44:33 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 1) Bind the FBO and set up viewport
|
2024-12-25 21:44:33 +00:00
|
|
|
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
|
|
|
|
|
2024-12-25 21:44:33 +00:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
2024-12-25 23:01:05 +00:00
|
|
|
|
2024-12-27 01:34:34 +00:00
|
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
2024-12-25 21:44:33 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 2) Check our main rendering shader
|
2024-12-26 00:15:18 +00:00
|
|
|
if (!m_ShaderPtr)
|
2024-12-30 04:25:16 +00:00
|
|
|
{
|
2024-12-30 06:51:58 +00:00
|
|
|
DEBUG_PRINT("[RenderWindow] Shader pointer is null. Cannot render.");
|
2024-12-30 04:25:16 +00:00
|
|
|
m_FBO.Unbind();
|
2025-01-04 00:32:39 +00:00
|
|
|
return;
|
2024-12-30 04:25:16 +00:00
|
|
|
}
|
2024-12-26 00:15:18 +00:00
|
|
|
m_ShaderPtr->Use();
|
2024-12-25 21:44:33 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 3) Obtain view/projection from the active camera (or fallback)
|
2024-12-30 04:25:16 +00:00
|
|
|
std::shared_ptr<CameraComponent> activeCamera = nullptr;
|
2025-01-04 00:32:39 +00:00
|
|
|
glm::mat4 view, proj;
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2024-12-30 04:25:16 +00:00
|
|
|
if (*GameRunning && g_RuntimeCameraObject)
|
|
|
|
{
|
|
|
|
activeCamera = g_RuntimeCameraObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (activeCamera)
|
2024-12-29 02:50:39 +00:00
|
|
|
{
|
2024-12-30 04:25:16 +00:00
|
|
|
view = activeCamera->GetViewMatrix();
|
|
|
|
proj = activeCamera->GetProjectionMatrix();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-01-04 00:32:39 +00:00
|
|
|
// Fallback if no camera
|
2024-12-30 04:25:16 +00:00
|
|
|
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);
|
|
|
|
}
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 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;
|
|
|
|
|
2024-12-30 04:25:16 +00:00
|
|
|
for (auto &obj : g_GameObjects)
|
|
|
|
{
|
2024-12-26 00:15:18 +00:00
|
|
|
glm::mat4 model = glm::mat4(1.f);
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
auto transform = obj->GetComponent<TransformComponent>();
|
|
|
|
auto mesh = obj->GetComponent<MeshComponent>();
|
2024-12-26 00:15:18 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
if (transform && mesh)
|
2024-12-27 01:34:34 +00:00
|
|
|
{
|
2025-01-04 00:32:39 +00:00
|
|
|
// 5A) Build the Model matrix
|
2024-12-27 01:34:34 +00:00
|
|
|
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);
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 5B) For each submesh
|
|
|
|
for (auto &submesh : mesh->submeshes)
|
2024-12-30 04:25:16 +00:00
|
|
|
{
|
2025-01-04 00:32:39 +00:00
|
|
|
// Check if VAO is valid
|
2024-12-31 08:40:23 +00:00
|
|
|
if (submesh.vao == 0)
|
|
|
|
{
|
|
|
|
DEBUG_PRINT("[RenderWindow] Warning: Submesh VAO is not initialized.");
|
|
|
|
continue;
|
|
|
|
}
|
2024-12-27 01:34:34 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// -------------------------
|
|
|
|
// *** 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
|
2024-12-31 08:40:23 +00:00
|
|
|
g_GPU_Triangles_drawn_to_screen += static_cast<int>(submesh.indices.size() / 3);
|
2024-12-30 04:25:16 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 5E) Bind textures
|
|
|
|
const int MAX_DIFFUSE = 32;
|
2024-12-31 08:40:23 +00:00
|
|
|
int textureUnit = 0;
|
2025-01-04 00:32:39 +00:00
|
|
|
for (auto &texture : submesh.textures)
|
2024-12-30 04:25:16 +00:00
|
|
|
{
|
2024-12-31 08:40:23 +00:00
|
|
|
if (texture.type == "texture_diffuse")
|
2024-12-30 04:25:16 +00:00
|
|
|
{
|
2024-12-31 08:40:23 +00:00
|
|
|
if (textureUnit >= MAX_DIFFUSE)
|
|
|
|
{
|
2025-01-04 00:32:39 +00:00
|
|
|
DEBUG_PRINT("[RenderWindow] Warning: Exceeded maximum diffuse textures.");
|
|
|
|
break;
|
2024-12-31 08:40:23 +00:00
|
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture.id);
|
2024-12-30 04:25:16 +00:00
|
|
|
|
2024-12-31 08:40:23 +00:00
|
|
|
std::string uniformName = "uTextures.texture_diffuse[" + std::to_string(textureUnit) + "]";
|
|
|
|
m_ShaderPtr->SetInt(uniformName, textureUnit);
|
2024-12-30 04:25:16 +00:00
|
|
|
|
2024-12-31 08:40:23 +00:00
|
|
|
textureUnit++;
|
2024-12-30 04:25:16 +00:00
|
|
|
}
|
|
|
|
}
|
2025-01-04 00:32:39 +00:00
|
|
|
// Assign default texture to unused slots
|
2024-12-31 22:35:06 +00:00
|
|
|
for (int i = textureUnit; i < MAX_DIFFUSE; ++i)
|
2024-12-30 04:25:16 +00:00
|
|
|
{
|
2024-12-31 08:40:23 +00:00
|
|
|
std::string uniformName = "uTextures.texture_diffuse[" + std::to_string(i) + "]";
|
2025-01-04 00:32:39 +00:00
|
|
|
m_ShaderPtr->SetInt(uniformName, 0);
|
2024-12-30 04:25:16 +00:00
|
|
|
}
|
2024-12-31 08:40:23 +00:00
|
|
|
m_ShaderPtr->SetInt("uNumDiffuseTextures", textureUnit);
|
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 5F) Draw submesh (filled)
|
2024-12-31 08:40:23 +00:00
|
|
|
glBindVertexArray(submesh.vao);
|
2025-01-04 00:32:39 +00:00
|
|
|
glDrawElements(GL_TRIANGLES,
|
|
|
|
static_cast<GLsizei>(submesh.indices.size()),
|
|
|
|
GL_UNSIGNED_INT,
|
|
|
|
nullptr);
|
|
|
|
g_GPU_Draw_Calls++;
|
2024-12-31 08:40:23 +00:00
|
|
|
glBindVertexArray(0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2025-01-04 00:32:39 +00:00
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// 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);
|
|
|
|
}
|
2024-12-31 08:40:23 +00:00
|
|
|
}
|
2024-12-27 01:34:34 +00:00
|
|
|
}
|
2024-12-26 00:15:18 +00:00
|
|
|
}
|
2024-12-25 21:44:33 +00:00
|
|
|
|
2025-01-01 19:39:45 +00:00
|
|
|
#if TRANSPERANCY
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
#endif
|
2024-12-26 00:15:18 +00:00
|
|
|
glUseProgram(0);
|
2024-12-30 04:25:16 +00:00
|
|
|
|
2025-01-04 00:32:39 +00:00
|
|
|
// 6) Unbind the FBO
|
2024-12-25 23:35:38 +00:00
|
|
|
m_FBO.Unbind();
|
2024-12-25 21:44:33 +00:00
|
|
|
}
|