Started On Renderer

This commit is contained in:
OusmBlueNinja 2025-05-21 21:55:38 -05:00
parent 375af6829c
commit 4194764d7a
6 changed files with 484 additions and 27 deletions

View File

@ -6,6 +6,7 @@
#include "systems/MACROS.h"
#include "systems/AssetManager.h"
#include "systems/assets/Texture2D.h"
namespace OX
{
@ -32,9 +33,7 @@ namespace OX
}
for (const auto &layer : m_layers) {
for (const auto &layer: m_layers) {
Logger::LogDebug("Initializing Layer: '%s'", layer->GetName().c_str());
layer->Init(*this);
}
@ -42,6 +41,7 @@ namespace OX
std::string fullTitle = layerTitle + " - " + m_name;
window.SetWindowTitle(fullTitle);
renderer.Init(800, 600);
Logger::LogOk("Core Initialization Complete.");
@ -49,7 +49,6 @@ namespace OX
}
void Core::Run()
{
m_running = true;
@ -80,6 +79,18 @@ namespace OX
{
OX_PROFILE_FUNCTION();
Camera2D cam(0, 800, 0, 600);
renderer.BeginScene(cam);
if (auto asset = AssetManager::Get("res://Assets/tile_001.png"); asset && asset->GetTypeName() == "texture2D") {
const auto tex = std::static_pointer_cast<Texture2D>(asset);
renderer.DrawSprite({tex->GetID(), {100, 100}, {64, 64}});
}
renderer.EndScene();
for (auto &layer: m_layers) {
layer->Draw(*this);

View File

@ -1,9 +1,162 @@
//
// Created by spenc on 5/21/2025.
//
// File: src/Renderer.cpp
#include "Renderer.h"
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
namespace OX {
} // OX
// ——— Camera2D implementation (unchanged) ———
Camera2D::Camera2D(float left, float right, float bottom, float top)
: _left(left), _right(right), _bottom(bottom), _top(top)
{ Recalculate(); }
void Camera2D::SetPosition(const glm::vec2& pos) { _position = pos; Recalculate(); }
void Camera2D::SetZoom(float z) { _zoom = z; Recalculate(); }
void Camera2D::Recalculate() {
float halfW = (_right - _left) * 0.5f / _zoom;
float halfH = (_top - _bottom) * 0.5f / _zoom;
glm::mat4 proj = glm::ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f);
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; }
// ——— Renderer ———
Renderer::~Renderer() {
if (m_quadVAO) glDeleteVertexArrays(1, &m_quadVAO);
if (m_quadVBO) 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) {
// create offscreen FBO & attachments
CreateFramebuffer(targetWidth, targetHeight);
// compile our sprite shader
static const char* vs = R"GLSL(
#version 330 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
uniform mat4 u_ViewProj;
uniform vec2 u_Offset;
uniform vec2 u_Scale;
out vec2 vUV;
void main(){
vUV = aUV;
vec2 pos = aPos * u_Scale + u_Offset;
gl_Position = u_ViewProj * vec4(pos,0,1);
}
)GLSL";
static const char* fs = R"GLSL(
#version 330 core
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D u_Texture;
void main(){
FragColor = texture(u_Texture, vUV);
}
)GLSL";
m_shader.LoadFromSource(vs, fs);
CreateQuad();
}
void Renderer::CreateFramebuffer(int width, int height) {
// cleanup old
if (m_fbo) glDeleteFramebuffers(1, &m_fbo);
if (m_colorTex) glDeleteTextures(1, &m_colorTex);
if (m_depthRBO) glDeleteRenderbuffers(1, &m_depthRBO);
// color texture
glGenTextures(1, &m_colorTex);
glBindTexture(GL_TEXTURE_2D, m_colorTex);
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);
// depth renderbuffer
glGenRenderbuffers(1, &m_depthRBO);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
// framebuffer
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);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cerr << "[Renderer] Framebuffer not complete!\n";
// unbind
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer::ResizeTarget(int width, int height) {
CreateFramebuffer(width, height);
}
void Renderer::BeginScene(const Camera2D& camera) {
// bind offscreen
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// clear
glViewport(0, 0, /*width*/0, /*height*/0); // you may want to track size
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// setup shader & camera
m_viewProj = camera.GetViewProjection();
m_shader.Use();
m_shader.SetMat4("u_ViewProj", m_viewProj);
m_shader.SetInt("u_Texture", 0);
}
void Renderer::DrawSprite(const Sprite& s) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, s.textureID);
m_shader.SetVec2("u_Offset", s.position);
m_shader.SetVec2("u_Scale", s.size);
glBindVertexArray(m_quadVAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
}
void Renderer::EndScene() {
// unbind FBO → subsequent draws go to default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer::CreateQuad() {
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};
glGenVertexArrays(1, &m_quadVAO);
glGenBuffers(1, &m_quadVBO);
glBindVertexArray(m_quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, m_quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
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

View File

@ -1,26 +1,73 @@
//
// Created by spenc on 5/21/2025.
//
// File: src/Renderer.h
#pragma once
#ifndef RENDERER_H
#define RENDERER_H
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "systems/Shader.h"
#include <gl/glew.h>
namespace OX {
class Renderer {
public:
Renderer() = default;
~Renderer() = default;
struct Sprite {
GLuint textureID;
glm::vec2 position;
glm::vec2 size;
};
[[nodiscard]] GLuint GetRenderTarget() const {return m_renderTarget; }
class Camera2D {
public:
Camera2D(float left, float right, float bottom, float top);
void SetPosition(const glm::vec2& pos);
void SetZoom(float zoom);
const glm::mat4& GetViewProjection() const;
private:
GLuint m_renderTarget;
};
private:
void Recalculate();
glm::vec2 _position{0.0f};
float _zoom{1.0f};
float _left, _right, _bottom, _top;
glm::mat4 _viewProj{1.0f};
};
} // OX
class Renderer {
public:
Renderer() = default;
~Renderer();
#endif //RENDERER_H
/// Must call once after OpenGL context + GLEW init.
/// Provide the desired offscreen target size here.
void Init(int targetWidth, int targetHeight);
/// Call at start of each scene (binds FBO and sets camera).
void BeginScene(const Camera2D& camera);
/// Draw one sprite into the current scene.
void DrawSprite(const Sprite& sprite);
/// Finish rendering, unbinds FBO.
void EndScene();
/// Access the colorattachment texture ID that holds the rendered scene.
GLuint GetRenderTexture() const { return m_colorTex; }
/// If window resized, call this to resize the offscreen target.
void ResizeTarget(int width, int height);
private:
// offscreen
GLuint m_fbo = 0;
GLuint m_colorTex = 0;
GLuint m_depthRBO = 0;
// quad geometry
GLuint m_quadVAO = 0, m_quadVBO = 0;
// shader
Shader m_shader;
glm::mat4 m_viewProj{1.0f};
// helpers
void CreateQuad();
void CreateFramebuffer(int width, int height);
};
} // namespace OX

180
src/core/systems/Shader.cpp Normal file
View File

@ -0,0 +1,180 @@
#include "Shader.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include "systems/Logger.h"
namespace OX
{
Shader::Shader(const std::string &vertexPath, const std::string &fragmentPath)
{
LoadFromFiles(vertexPath, fragmentPath);
}
bool Shader::LoadFromFiles(const std::string &vertexPath, const std::string &fragmentPath)
{
m_vertexPath = vertexPath;
m_fragmentPath = fragmentPath;
std::string vertexSrc = ReadFile(vertexPath);
std::string fragmentSrc = ReadFile(fragmentPath);
try {
m_vertTimestamp = std::filesystem::last_write_time(vertexPath);
m_fragTimestamp = std::filesystem::last_write_time(fragmentPath);
} catch (...) {
std::cerr << "[Shader] Warning: Could not get file timestamps.\n";
}
return LoadFromSource(vertexSrc, fragmentSrc);
}
bool Shader::LoadFromSource(const std::string &vertexSrc, const std::string &fragmentSrc)
{
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexSrc);
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentSrc);
if (!vertexShader || !fragmentShader) return false;
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char info[512];
glGetProgramInfoLog(program, 512, nullptr, info);
std::cerr << "[Shader] Linking failed:\n" << info << '\n';
glDeleteProgram(program);
return false;
}
if (m_programID)
glDeleteProgram(m_programID);
m_programID = program;
m_uniformCache.clear();
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
Logger::LogOk("Shader Loaded (%s,%s)", m_vertexPath.c_str(), m_fragmentPath.c_str());
return true;
}
void Shader::Reload()
{
OX_PROFILE_FUNCTION();
LoadFromFiles(m_vertexPath, m_fragmentPath);
}
void Shader::Use() const
{
glUseProgram(m_programID);
}
GLuint Shader::CompileShader(GLenum type, const std::string &source)
{
OX_PROFILE_FUNCTION();
GLuint shader = glCreateShader(type);
const char *src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char info[512];
glGetShaderInfoLog(shader, 512, nullptr, info);
Logger::LogError("Failed to Compile Shader '%s', '%s'", m_fragmentPath.c_str(), info);
return 0;
}
return shader;
}
void Shader::CheckHotReload()
{
OX_PROFILE_FUNCTION();
if (m_vertexPath.empty() || m_fragmentPath.empty()) {
return;
}
try {
OX_PROFILE_LABEL("Get Write Time");
auto vertTime = std::filesystem::last_write_time(m_vertexPath);
auto fragTime = std::filesystem::last_write_time(m_fragmentPath);
if (vertTime != m_vertTimestamp || fragTime != m_fragTimestamp) {
std::cout << "[Shader] Reloading shader: " << m_vertexPath << " / " << m_fragmentPath << "\n";
Reload();
}
} catch (const std::exception &e) {
Logger::LogError("Failed to Reload Shader '%s', %s", m_vertexPath.c_str(), e.what());
}
}
std::string Shader::ReadFile(const std::string &path)
{
if (!std::filesystem::exists(path)) {
std::ofstream file(path, std::ios::app);
}
std::ifstream file(path);
std::stringstream ss;
ss << file.rdbuf();
return ss.str();
}
GLint Shader::GetUniformLocation(const std::string &name)
{
if (m_uniformCache.contains(name))
return m_uniformCache[name];
GLint location = glGetUniformLocation(m_programID, name.c_str());
m_uniformCache[name] = location;
return location;
}
void Shader::SetInt(const std::string &name, int value)
{
glUniform1i(GetUniformLocation(name), value);
}
void Shader::SetFloat(const std::string &name, float value)
{
glUniform1f(GetUniformLocation(name), value);
}
void Shader::SetVec2(const std::string &name, const glm::vec2 &value)
{
glUniform2fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetVec3(const std::string &name, const glm::vec3 &value)
{
glUniform3fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetVec4(const std::string &name, const glm::vec4 &value)
{
glUniform4fv(GetUniformLocation(name), 1, &value[0]);
}
void Shader::SetMat4(const std::string &name, const glm::mat4 &value)
{
glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, &value[0][0]);
}
void Shader::SetBool(const std::string &name, bool value)
{
glUniform1i(GetUniformLocation(name), (int) value);
}
}

61
src/core/systems/Shader.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <string>
#include <unordered_map>
#include <glm/glm.hpp>
#include <GL/glew.h>
#include <filesystem>
#include "systems/Profiler.h"
namespace OX
{
class Shader
{
public:
Shader() = default;
Shader(const std::string &vertexPath, const std::string &fragmentPath);
bool LoadFromFiles(const std::string &vertexPath, const std::string &fragmentPath);
bool LoadFromSource(const std::string &vertexSrc, const std::string &fragmentSrc);
void Reload();
void Use() const;
void CheckHotReload(); // 🔥
GLuint GetID() const { return m_programID; }
void SetInt(const std::string &name, int value);
void SetFloat(const std::string &name, float value);
void SetVec2(const std::string &name, const glm::vec2 &value);
void SetVec3(const std::string &name, const glm::vec3 &value);
void SetVec4(const std::string &name, const glm::vec4 &value);
void SetMat4(const std::string &name, const glm::mat4 &value);
void SetBool(const std::string &name, const bool value);
private:
GLuint m_programID = 0;
std::string m_vertexPath;
std::string m_fragmentPath;
std::unordered_map<std::string, GLint> m_uniformCache;
std::filesystem::file_time_type m_vertTimestamp;
std::filesystem::file_time_type m_fragTimestamp;
GLuint CompileShader(GLenum type, const std::string &source);
std::string ReadFile(const std::string &path);
GLint GetUniformLocation(const std::string &name);
};
}

View File

@ -13,6 +13,8 @@ namespace OX
{
// Construct title
std::string title = m_name + " (" + std::to_string(m_id) + ")";
GLuint textureID = core.GetRenderer().GetRenderTexture(); // You must define this method
// Remove window padding
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
@ -24,14 +26,17 @@ namespace OX
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
// === Fullscreen image ===
GLuint textureID = core.GetRenderer().GetRenderTarget(); // You must define this method
if (textureID != 0) {
ImGui::Image(textureID, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
} else {
ImGui::Text("No Render Target.");
}
ImGui::End();
ImGui::PopStyleVar(); // Restore padding
core.GetRenderer().ResizeTarget(viewportSize.x, viewportSize.y);
}
};