Improves renderer and adds shader support

Adds shader class to core and modifies the renderer to use it,
while also adding profiling macros and fixing resize issues.
The changes ensure the render target is resized only when the
requested size is different from the current size and also clears
the screen to a dark gray color.
This commit is contained in:
OusmBlueNinja 2025-05-21 22:05:10 -05:00
parent 4194764d7a
commit f7e0882597
6 changed files with 149 additions and 114 deletions

View File

@ -99,6 +99,7 @@ add_library(Core STATIC
src/core/systems/Asset.h src/core/systems/Asset.h
src/core/systems/assets/Texture2D.cpp src/core/systems/assets/Texture2D.cpp
src/core/systems/assets/Texture2D.h src/core/systems/assets/Texture2D.h
src/core/systems/Shader.cpp
) )
target_include_directories(Core PUBLIC src/core) target_include_directories(Core PUBLIC src/core)

View File

@ -3,41 +3,59 @@
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <iostream> #include <iostream>
namespace OX { namespace OX
{
// ——— Camera2D implementation (unchanged) ———
Camera2D::Camera2D(float left, float right, float bottom, float top)
: _left(left), _right(right), _bottom(bottom), _top(top)
{
Recalculate();
}
// ——— Camera2D implementation (unchanged) ——— void Camera2D::SetPosition(const glm::vec2 &pos)
Camera2D::Camera2D(float left, float right, float bottom, float top) {
: _left(left), _right(right), _bottom(bottom), _top(top) _position = pos;
{ Recalculate(); } Recalculate();
}
void Camera2D::SetPosition(const glm::vec2& pos) { _position = pos; Recalculate(); } void Camera2D::SetZoom(float z)
void Camera2D::SetZoom(float z) { _zoom = z; Recalculate(); } {
_zoom = z;
Recalculate();
}
void Camera2D::Recalculate() { void Camera2D::Recalculate()
float halfW = (_right - _left) * 0.5f / _zoom; {
float halfH = (_top - _bottom) * 0.5f / _zoom; float halfW = (_right - _left) * 0.5f / _zoom;
glm::mat4 proj = glm::ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f); float halfH = (_top - _bottom) * 0.5f / _zoom;
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(-_position, 0.0f)); glm::mat4 proj = glm::ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f);
_viewProj = proj * view; glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(-_position, 0.0f));
} _viewProj = proj * view;
}
const glm::mat4& Camera2D::GetViewProjection() const { return _viewProj; } const glm::mat4 &Camera2D::GetViewProjection() const { return _viewProj; }
// ——— Renderer ——— // ——— Renderer ———
Renderer::~Renderer() { Renderer::~Renderer()
if (m_quadVAO) glDeleteVertexArrays(1, &m_quadVAO); {
if (m_quadVBO) glDeleteBuffers(1, &m_quadVBO); if (m_quadVAO)
if (m_fbo) glDeleteFramebuffers(1, &m_fbo); glDeleteVertexArrays(1, &m_quadVAO);
if (m_colorTex) glDeleteTextures(1, &m_colorTex); if (m_quadVBO)
if (m_depthRBO) glDeleteRenderbuffers(1, &m_depthRBO); glDeleteBuffers(1, &m_quadVBO);
} if (m_fbo)
glDeleteFramebuffers(1, &m_fbo);
if (m_colorTex) glDeleteTextures(1, &m_colorTex);
if (m_depthRBO)
glDeleteRenderbuffers(1, &m_depthRBO);
}
void Renderer::Init(int targetWidth, int targetHeight) { void Renderer::Init(int targetWidth, int targetHeight)
// create offscreen FBO & attachments {
CreateFramebuffer(targetWidth, targetHeight); // create offscreen FBO & attachments
CreateFramebuffer(targetWidth, targetHeight);
// compile our sprite shader // compile our sprite shader
static const char* vs = R"GLSL( static const char *vs = R"GLSL(
#version 330 core #version 330 core
layout(location = 0) in vec2 aPos; layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV; layout(location = 1) in vec2 aUV;
@ -51,7 +69,7 @@ void Renderer::Init(int targetWidth, int targetHeight) {
gl_Position = u_ViewProj * vec4(pos,0,1); gl_Position = u_ViewProj * vec4(pos,0,1);
} }
)GLSL"; )GLSL";
static const char* fs = R"GLSL( static const char *fs = R"GLSL(
#version 330 core #version 330 core
in vec2 vUV; in vec2 vUV;
out vec4 FragColor; out vec4 FragColor;
@ -61,102 +79,116 @@ void Renderer::Init(int targetWidth, int targetHeight) {
} }
)GLSL"; )GLSL";
m_shader.LoadFromSource(vs, fs); m_shader.LoadFromSource(vs, fs);
CreateQuad(); CreateQuad();
} }
void Renderer::CreateFramebuffer(int width, int height) { void Renderer::CreateFramebuffer(int width, int height)
// cleanup old {
if (m_fbo) glDeleteFramebuffers(1, &m_fbo); size = {width, height};
if (m_colorTex) glDeleteTextures(1, &m_colorTex);
if (m_depthRBO) glDeleteRenderbuffers(1, &m_depthRBO);
// color texture // cleanup old
glGenTextures(1, &m_colorTex); if (m_fbo)
glBindTexture(GL_TEXTURE_2D, m_colorTex); glDeleteFramebuffers(1, &m_fbo);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, if (m_colorTex) glDeleteTextures(1, &m_colorTex);
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); if (m_depthRBO)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glDeleteRenderbuffers(1, &m_depthRBO);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// depth renderbuffer // color texture
glGenRenderbuffers(1, &m_depthRBO); glGenTextures(1, &m_colorTex);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRBO); glBindTexture(GL_TEXTURE_2D, m_colorTex);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// framebuffer // depth renderbuffer
glGenFramebuffers(1, &m_fbo); glGenRenderbuffers(1, &m_depthRBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glBindRenderbuffer(GL_RENDERBUFFER, m_depthRBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
GL_TEXTURE_2D, m_colorTex, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_depthRBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) // framebuffer
std::cerr << "[Renderer] Framebuffer not complete!\n"; glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, m_colorTex, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_depthRBO);
// unbind if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
glBindFramebuffer(GL_FRAMEBUFFER, 0); std::cerr << "[Renderer] Framebuffer not complete!\n";
}
void Renderer::ResizeTarget(int width, int height) { // unbind
CreateFramebuffer(width, height); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Renderer::BeginScene(const Camera2D& camera) { void Renderer::ResizeTarget(int width, int height)
// bind offscreen {
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); OX_PROFILE_FUNCTION();
// clear if (size == Vec2i{width, height}) {
glViewport(0, 0, /*width*/0, /*height*/0); // you may want to track size return;
glClearColor(0,0,0,1); }
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); CreateFramebuffer(width, height);
}
// setup shader & camera void Renderer::BeginScene(const Camera2D &camera)
m_viewProj = camera.GetViewProjection(); {
m_shader.Use(); OX_PROFILE_FUNCTION();
m_shader.SetMat4("u_ViewProj", m_viewProj); // bind offscreen
m_shader.SetInt("u_Texture", 0); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
} // clear
glViewport(0, 0, size.x, size.y);
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
void Renderer::DrawSprite(const Sprite& s) { // setup shader & camera
glActiveTexture(GL_TEXTURE0); m_viewProj = camera.GetViewProjection();
glBindTexture(GL_TEXTURE_2D, s.textureID); m_shader.Use();
m_shader.SetMat4("u_ViewProj", m_viewProj);
m_shader.SetInt("u_Texture", 0);
}
m_shader.SetVec2("u_Offset", s.position); void Renderer::DrawSprite(const Sprite &s)
m_shader.SetVec2("u_Scale", s.size); {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, s.textureID);
glBindVertexArray(m_quadVAO); m_shader.SetVec2("u_Offset", s.position);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); m_shader.SetVec2("u_Scale", s.size);
}
void Renderer::EndScene() { glBindVertexArray(m_quadVAO);
// unbind FBO → subsequent draws go to default framebuffer glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glBindFramebuffer(GL_FRAMEBUFFER, 0); }
}
void Renderer::CreateQuad() { void Renderer::EndScene()
float verts[] = { {
-0.5f, -0.5f, 0,0, // unbind FBO → subsequent draws go to default framebuffer
0.5f, -0.5f, 1,0, glBindFramebuffer(GL_FRAMEBUFFER, 0);
0.5f, 0.5f, 1,1, }
-0.5f, 0.5f, 0,1
};
unsigned int idx[] = {0,1,2, 2,3,0};
glGenVertexArrays(1, &m_quadVAO); void Renderer::CreateQuad()
glGenBuffers(1, &m_quadVBO); {
float verts[] = {
-0.5f, -0.5f, 0, 0,
0.5f, -0.5f, 1, 0,
0.5f, 0.5f, 1, 1,
-0.5f, 0.5f, 0, 1
};
unsigned int idx[] = {0, 1, 2, 2, 3, 0};
glBindVertexArray(m_quadVAO); glGenVertexArrays(1, &m_quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, m_quadVBO); glGenBuffers(1, &m_quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); glBindVertexArray(m_quadVAO);
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)0); glBindBuffer(GL_ARRAY_BUFFER, m_quadVBO);
glEnableVertexAttribArray(1); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)(2*sizeof(float)));
glBindVertexArray(0); glEnableVertexAttribArray(0);
} glVertexAttribPointer(0, 2,GL_FLOAT,GL_FALSE, 4 * sizeof(float), (void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2,GL_FLOAT,GL_FALSE, 4 * sizeof(float), (void *) (2 * sizeof(float)));
glBindVertexArray(0);
}
} // namespace OX } // namespace OX

View File

@ -4,7 +4,7 @@
#include "glm/glm.hpp" #include "glm/glm.hpp"
#include "systems/Shader.h" #include "systems/Shader.h"
#include <gl/glew.h> #include <gl/glew.h>
#include "types/vec2.h"
namespace OX { namespace OX {
struct Sprite { struct Sprite {
@ -54,6 +54,7 @@ namespace OX {
private: private:
// offscreen // offscreen
Vec2i size;
GLuint m_fbo = 0; GLuint m_fbo = 0;
GLuint m_colorTex = 0; GLuint m_colorTex = 0;
GLuint m_depthRBO = 0; GLuint m_depthRBO = 0;

View File

@ -24,7 +24,7 @@ namespace OX
void Use() const; void Use() const;
void CheckHotReload(); // 🔥 void CheckHotReload();
GLuint GetID() const { return m_programID; } GLuint GetID() const { return m_programID; }

View File

@ -146,7 +146,7 @@ namespace OX
// --- Render ImGui onto FBO 0 --- // --- Render ImGui onto FBO 0 ---
{ {
OX_PROFILE_LABEL("VSYNC Wait");
ImGui::EndFrame(); ImGui::EndFrame();
ImGui::Render(); ImGui::Render();

View File

@ -1,6 +1,7 @@
// File: src/FileBrowser.cpp // File: src/FileBrowser.cpp
#include "FileBrowser.h" #include "FileBrowser.h"
#include <filesystem> #include <filesystem>
#include "systems/Profiler.h"
namespace OX namespace OX
{ {
@ -13,6 +14,7 @@ namespace OX
void FileBrowser::Draw(const char *title) void FileBrowser::Draw(const char *title)
{ {
OX_PROFILE_FUNCTION();
ImGui::Begin(title); ImGui::Begin(title);
// --- toolbar now contains back button, inline path, filter & view toggle all on one row --- // --- toolbar now contains back button, inline path, filter & view toggle all on one row ---
@ -86,6 +88,7 @@ namespace OX
// ——— Polished grid view ——— // ——— Polished grid view ———
void FileBrowser::DrawGridView(const std::shared_ptr<ResourceTreeNode> &node) void FileBrowser::DrawGridView(const std::shared_ptr<ResourceTreeNode> &node)
{ {
OX_PROFILE_FUNCTION();
const float cellW = _cfg.thumbnailSize + _cfg.padding * 2; const float cellW = _cfg.thumbnailSize + _cfg.padding * 2;
ImVec2 avail = ImGui::GetContentRegionAvail(); ImVec2 avail = ImGui::GetContentRegionAvail();
int cols = std::max(1, int(avail.x / cellW)); int cols = std::max(1, int(avail.x / cellW));
@ -93,13 +96,11 @@ namespace OX
ImGui::BeginChild("GridRegion", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysUseWindowPadding); ImGui::BeginChild("GridRegion", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysUseWindowPadding);
ImGui::Columns(cols, nullptr, false); ImGui::Columns(cols, nullptr, false);
std::vector<std::shared_ptr<ResourceTreeNode> > children; std::vector<std::shared_ptr<ResourceTreeNode> > children; {
{
std::lock_guard<std::mutex> lock(AssetManager::s_TreeMutex); // assume you add this mutex std::lock_guard<std::mutex> lock(AssetManager::s_TreeMutex); // assume you add this mutex
children = node->children; children = node->children;
} }
for (auto& c : children) { for (auto &c: children) {
if (!c) return; if (!c) return;
if (!_filter.empty() && !PassesFilter(c->name)) continue; if (!_filter.empty() && !PassesFilter(c->name)) continue;