Compare commits

..

5 Commits

Author SHA1 Message Date
063b228b97 feat: support uniform blocks 2025-10-23 22:43:03 +02:00
1440fd847c test: try uniform buffers 2025-10-23 22:42:54 +02:00
337da2b3b7 feat: opengl buffer & nested uniform buffer class 2025-10-23 22:42:41 +02:00
3574634c4c chore: formatting 2025-10-23 21:34:13 +02:00
32873d14ae chore: rename transform to Transform 2025-10-23 20:32:12 +02:00
10 changed files with 164 additions and 29 deletions

View File

@ -7,6 +7,8 @@ set(SOURCES
src/input/input.cpp src/input/input.cpp
src/opengl/buffers.cpp
src/scene/scene.cpp src/scene/scene.cpp
src/window/window.cpp src/window/window.cpp

View File

@ -9,7 +9,7 @@ namespace Core {
struct ENGINE_API batch { struct ENGINE_API batch {
friend class Renderer; friend class Renderer;
public: public:
// requires transform component // requires Transform component
struct item { struct item {
unsigned int batchId; unsigned int batchId;
}; };

View File

@ -5,11 +5,13 @@
#include "engine/export.h" #include "engine/export.h"
namespace Core { namespace Core {
struct ENGINE_API transform {
struct ENGINE_API Transform {
glm::vec3 position; glm::vec3 position;
glm::vec3 rotation; glm::vec3 rotation;
glm::vec3 scale; glm::vec3 scale;
}; };
} }
#endif // COMPONENTS_TRANSFORM_H_ #endif // COMPONENTS_TRANSFORM_H_

View File

@ -0,0 +1,43 @@
#ifndef OPENGL_BUFFERS_H_
#define OPENGL_BUFFERS_H_
#include <GL/glew.h>
#include "engine/export.h"
namespace Core {
namespace OpenGL {
using BufferTarget = GLenum;
using BufferUsage = GLenum;
using BufferID = unsigned int;
class ENGINE_API Buffer {
public:
Buffer(BufferTarget target, BufferUsage usage);
void Data(void* data, size_t size);
void SubData(void *data, size_t size, size_t offset);
void BindBuffer(unsigned int index);
void BindBufferRanged(unsigned int index, size_t offset, size_t size);
protected:
void Bind();
void Unbind();
private:
BufferID m_buffer;
BufferTarget m_target;
BufferUsage m_usage;
};
class ENGINE_API UniformBuffer : public Buffer {
public:
UniformBuffer(size_t size, unsigned int index);
public:
};
} // namespace OpenGL
} // namespace Core
#endif // OPENGL_BUFFERS_H_

View File

@ -8,6 +8,8 @@
#include "engine/export.h" #include "engine/export.h"
#include "engine/components/light.h" #include "engine/components/light.h"
#include "engine/opengl/buffers.h"
namespace Core { namespace Core {
// TODO: make static or singleton // TODO: make static or singleton
@ -26,6 +28,8 @@ private:
void GenerateShadowMaps(); void GenerateShadowMaps();
void EnsureShadowResources(light& l); void EnsureShadowResources(light& l);
private: private:
OpenGL::UniformBuffer m_uniform_matrices;
Shader m_shader; Shader m_shader;
Shader m_depthShader; Shader m_depthShader;

View File

@ -0,0 +1,58 @@
#include "engine/opengl/buffers.h"
namespace Core {
namespace OpenGL {
Buffer::Buffer(BufferTarget target, BufferUsage usage)
: m_target(target), m_usage(usage)
{
glGenBuffers(1, &m_buffer);
Bind();
Data(nullptr, 0);
Unbind();
}
void Buffer::Bind() {
glBindBuffer(m_target, m_buffer);
}
void Buffer::Unbind() {
glBindBuffer(m_target, 0);
}
void Buffer::Data(void *data, size_t size) {
Bind();
glBufferData(m_target, size, data, m_usage);
Unbind();
}
void Buffer::SubData(void *data, size_t size, size_t offset) {
Bind();
glBufferSubData(m_target, offset, size, data);
Unbind();
}
void Buffer::BindBuffer(unsigned int index) {
Bind();
glBindBufferBase(m_target, index, m_buffer);
Unbind();
}
void Buffer::BindBufferRanged(unsigned int index, size_t offset, size_t size) {
Bind();
glBindBufferRange(m_target, index, m_buffer, offset, size);
Unbind();
}
UniformBuffer::UniformBuffer(size_t size, unsigned int index)
: Buffer(GL_UNIFORM_BUFFER, GL_STATIC_DRAW)
{
Data(nullptr, size);
BindBuffer(index);
}
} // namespace OpenGL
} // namespace Core

View File

@ -9,6 +9,7 @@
#include <glm/gtx/euler_angles.hpp> #include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/string_cast.hpp> #include <glm/gtx/string_cast.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "engine/renderer/renderer.h" #include "engine/renderer/renderer.h"
#include "engine/window/window.h" #include "engine/window/window.h"
@ -22,15 +23,9 @@
namespace Core { namespace Core {
Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene) Renderer::Renderer(std::shared_ptr<Scene> scene)
: m_scene(scene), m_uniform_matrices(2 * sizeof(glm::mat4), 1)
{ {
m_proj = glm::perspective(
static_cast<float>(M_PI_2),
static_cast<float>(Window::GetWidth()) / static_cast<float>(Window::GetHeight()),
0.01f,
100.0f
);
m_shader.init( m_shader.init(
FileManager::read("./engine/src/shaders/main.vs"), FileManager::read("./engine/src/shaders/main.vs"),
FileManager::read("./engine/src/shaders/pbr.fs") FileManager::read("./engine/src/shaders/pbr.fs")
@ -41,6 +36,18 @@ Renderer::Renderer(std::shared_ptr<Scene> scene) : m_scene(scene)
FileManager::read("./engine/src/shaders/depth.fs") FileManager::read("./engine/src/shaders/depth.fs")
); );
glUniformBlockBinding(m_shader.m_id, glGetUniformBlockIndex(m_shader.m_id, "Matrices"), 1);
// glUniformBlockBinding(m_depthShader.m_id, glGetUniformBlockIndex(m_depthShader.m_id, "Matrices"), 1);
m_proj = glm::perspective(
static_cast<float>(M_PI_2),
static_cast<float>(Window::GetWidth()) / static_cast<float>(Window::GetHeight()),
0.01f,
100.0f
);
m_uniform_matrices.SubData(glm::value_ptr(m_proj), sizeof(glm::mat4), 0);
m_model = glm::mat4(1.f); m_model = glm::mat4(1.f);
m_shader.use(); m_shader.use();
@ -58,6 +65,7 @@ void Renderer::OnWindowResized(int w, int h) {
0.01f, 0.01f,
100.0f 100.0f
); );
m_uniform_matrices.SubData(glm::value_ptr(m_proj), sizeof(glm::mat4), 0);
} }
void Renderer::ApplyLights(Shader &shader) { void Renderer::ApplyLights(Shader &shader) {
@ -67,7 +75,7 @@ void Renderer::ApplyLights(Shader &shader) {
size_t lightIndex = 0; size_t lightIndex = 0;
for (auto entity : lights) { for (auto entity : lights) {
auto &l = m_scene->m_registry.get<light>(entity); auto &l = m_scene->m_registry.get<light>(entity);
auto &transf = m_scene->m_registry.get<transform>(entity); auto &transf = m_scene->m_registry.get<Transform>(entity);
shader.setInt("lights[" + std::to_string(lightIndex) + "].type", static_cast<int>(l.type)); shader.setInt("lights[" + std::to_string(lightIndex) + "].type", static_cast<int>(l.type));
shader.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position); shader.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position);
@ -111,8 +119,8 @@ void Renderer::EnsureShadowResources(light& l) {
void Renderer::UpdateView() { void Renderer::UpdateView() {
auto camView = m_scene->m_registry.view<camera>(); auto camView = m_scene->m_registry.view<camera>();
auto camTransform = camView.size() > 0 ? auto camTransform = camView.size() > 0 ?
m_scene->m_registry.get<transform>(camView.back()) : m_scene->m_registry.get<Transform>(camView.back()) :
transform {glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)}; Transform {glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)};
m_view = glm::lookAt( m_view = glm::lookAt(
camTransform.position, camTransform.position,
@ -120,6 +128,8 @@ void Renderer::UpdateView() {
glm::vec3(0.f, 1.f, 0.f) glm::vec3(0.f, 1.f, 0.f)
); );
m_uniform_matrices.SubData(glm::value_ptr(m_view), sizeof(glm::mat4), sizeof(glm::mat4));
m_shader.setVec3("viewPos", camTransform.position); m_shader.setVec3("viewPos", camTransform.position);
m_shader.setMat4("u_view", m_view); m_shader.setMat4("u_view", m_view);
@ -149,7 +159,7 @@ void Renderer::RenderScene(Shader &shader) {
models.reserve(batchItems.size()); models.reserve(batchItems.size());
for (auto item : batchItems) { for (auto item : batchItems) {
auto &t = m_scene->m_registry.get<transform>(item); auto &t = m_scene->m_registry.get<Transform>(item);
glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z); glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z);
auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation; auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation;
models.push_back(itemModel); models.push_back(itemModel);
@ -165,7 +175,7 @@ void Renderer::RenderScene(Shader &shader) {
} }
shader.setBool("u_isInstanced", false); shader.setBool("u_isInstanced", false);
for (auto [entity, transf, mesh] : m_scene->m_registry.view<transform, mesh>(entt::exclude<batch, batch::item>).each()) { for (auto [entity, transf, mesh] : m_scene->m_registry.view<Transform, mesh>(entt::exclude<batch, batch::item>).each()) {
if (mesh.object == nullptr) { if (mesh.object == nullptr) {
std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl; std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl;
return; return;
@ -206,7 +216,7 @@ void Renderer::Render() {
glCullFace(GL_FRONT); glCullFace(GL_FRONT);
const auto lights = m_scene->m_registry.view<light, transform>(); const auto lights = m_scene->m_registry.view<light, Transform>();
for (auto [_, l, t] : lights.each()) { for (auto [_, l, t] : lights.each()) {
// TODO: support other light types when ready // TODO: support other light types when ready

View File

@ -1,13 +1,23 @@
#version 410 core #version 460 core
// Input vertex attributes // Input vertex attributes
layout (location = 0) in vec3 position; // Vertex position in local space (model space) layout (location = 0) in vec3 position; // Vertex position in local space (model space)
layout (location = 3) in mat4 instanceModel; // Vertex texture uv
// layout (std140, binding = 1) uniform Matrices
// {
// mat4 projection;
// mat4 view;
// };
// Uniforms for transformation matrices // Uniforms for transformation matrices
uniform mat4 u_model; // Model matrix: transforms from local space to world space uniform mat4 u_model; // Model matrix: transforms from local space to world space
uniform mat4 u_lightSpace; uniform mat4 u_lightSpace;
uniform bool u_isInstanced;
void main() void main()
{ {
gl_Position = u_lightSpace * u_model * vec4(position, 1.0); mat4 model = u_isInstanced ? instanceModel : u_model;
gl_Position = u_lightSpace * model * vec4(position, 1.0);
} }

View File

@ -6,6 +6,12 @@ layout (location = 1) in vec3 normal; // vertex normal
layout (location = 2) in vec2 texCoord; // Vertex texture uv layout (location = 2) in vec2 texCoord; // Vertex texture uv
layout (location = 3) in mat4 instanceModel; // Vertex texture uv layout (location = 3) in mat4 instanceModel; // Vertex texture uv
layout (std140, binding = 1) uniform Matrices
{
mat4 projection;
mat4 view;
};
// Output to fragment shader // Output to fragment shader
out vec3 vertexPos; out vec3 vertexPos;
out vec3 vertexNormal; out vec3 vertexNormal;
@ -14,8 +20,8 @@ out vec4 fragPosLightSpace;
// Uniforms for transformation matrices // Uniforms for transformation matrices
uniform mat4 u_model; // Model matrix: transforms from local space to world space uniform mat4 u_model; // Model matrix: transforms from local space to world space
uniform mat4 u_view; // View matrix: transforms from world space to camera space (view space) // uniform mat4 u_view;
uniform mat4 u_projection; // Projection matrix: transforms from camera space to clip space // uniform mat4 u_projection;
uniform bool u_isInstanced; uniform bool u_isInstanced;
void main() void main()
@ -32,5 +38,5 @@ void main()
// fragPosLightSpace = u_lightSpace * vec4(vertexPos, 1.0); // fragPosLightSpace = u_lightSpace * vec4(vertexPos, 1.0);
gl_Position = u_projection * u_view * vec4(vertexPos, 1.0); gl_Position = projection * view * vec4(vertexPos, 1.0);
} }

View File

@ -36,20 +36,20 @@ public:
Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj"); Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj");
lightEntity = scene->CreateEntity(); lightEntity = scene->CreateEntity();
lightEntity.AddComponent<transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f)); lightEntity.AddComponent<Transform>(glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f));
lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f); lightEntity.AddComponent<light>(light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f);
lightEntity.AddComponent<mesh>(std::shared_ptr<Object>(lightObj)); lightEntity.AddComponent<mesh>(std::shared_ptr<Object>(lightObj));
assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!"); assert(lightEntity.HasComponent<mesh>() && "light doesn't have any mesh!");
cameraEntity = scene->CreateEntity(); cameraEntity = scene->CreateEntity();
cameraEntity.AddComponent<camera>(); cameraEntity.AddComponent<camera>();
cameraEntity.AddComponent<transform>(glm::vec3(0.f, 2.f, 2.f)); cameraEntity.AddComponent<Transform>(glm::vec3(0.f, 2.f, 2.f));
assert(cameraEntity.HasComponent<camera>() && "Camera doesn't have required 'camera' component"); assert(cameraEntity.HasComponent<camera>() && "Camera doesn't have required 'camera' component");
assert(cameraEntity.HasComponent<transform>() && "Camera doesn't have 'transform' component"); assert(cameraEntity.HasComponent<Transform>() && "Camera doesn't have 'transform' component");
Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj"); Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj");
modelEntity = scene->CreateEntity(); modelEntity = scene->CreateEntity();
modelEntity.AddComponent<transform>(glm::vec3(0.f, 0.0f, 0.f)); modelEntity.AddComponent<Transform>(glm::vec3(0.f, 0.0f, 0.f));
modelEntity.AddComponent<mesh>(std::shared_ptr<Object>(targetObj)); modelEntity.AddComponent<mesh>(std::shared_ptr<Object>(targetObj));
modelEntity.AddComponent<rotate>(); modelEntity.AddComponent<rotate>();
assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!"); assert(modelEntity.HasComponent<mesh>() && "model doesn't have any mesh!");
@ -70,14 +70,14 @@ public:
float y = static_cast<float>(rand()) / RAND_MAX * 10.f; // range [0, 10] float y = static_cast<float>(rand()) / RAND_MAX * 10.f; // range [0, 10]
float z = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100] float z = static_cast<float>(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100]
cubeEntity.AddComponent<transform>(glm::vec3(x, y, z)); cubeEntity.AddComponent<Transform>(glm::vec3(x, y, z));
cubeEntity.AddComponent<rotate>(); cubeEntity.AddComponent<rotate>();
cubeEntity.AddComponent<batch::item>(cubeBatch.id()); cubeEntity.AddComponent<batch::item>(cubeBatch.id());
} }
Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj"); Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj");
auto floorEntt = scene->CreateEntity(); auto floorEntt = scene->CreateEntity();
floorEntt.AddComponent<transform>(glm::vec3(0.f)); floorEntt.AddComponent<Transform>(glm::vec3(0.f));
floorEntt.AddComponent<mesh>(std::shared_ptr<Object>(floorObj)); floorEntt.AddComponent<mesh>(std::shared_ptr<Object>(floorObj));
assert(floorEntt.HasComponent<mesh>() && "floor doesn't have any mesh component!"); assert(floorEntt.HasComponent<mesh>() && "floor doesn't have any mesh component!");
@ -120,7 +120,7 @@ public:
if (Input::IsKeyPressed(SDL_SCANCODE_SPACE)) velocity.y += 1.f; if (Input::IsKeyPressed(SDL_SCANCODE_SPACE)) velocity.y += 1.f;
if (Input::IsKeyPressed(SDL_SCANCODE_LSHIFT)) velocity.y -= 1.f; if (Input::IsKeyPressed(SDL_SCANCODE_LSHIFT)) velocity.y -= 1.f;
auto& camTransform = cameraEntity.GetComponent<transform>(); auto& camTransform = cameraEntity.GetComponent<Transform>();
camTransform.position += velocity * (float)dt * 2.5f; // speed is e.g. 2.5f camTransform.position += velocity * (float)dt * 2.5f; // speed is e.g. 2.5f
camTransform.rotation = cameraViewDirection; camTransform.rotation = cameraViewDirection;
@ -154,7 +154,7 @@ public:
// Update the directional light in the registry // Update the directional light in the registry
auto& l = lightEntity.GetComponent<light>(); auto& l = lightEntity.GetComponent<light>();
auto& t = lightEntity.GetComponent<transform>(); auto& t = lightEntity.GetComponent<Transform>();
if (l.type == light::LightType::DIRECTIONAL) { if (l.type == light::LightType::DIRECTIONAL) {
// "position" for directional light often stores direction vector // "position" for directional light often stores direction vector
// If your system instead uses transform.rotation, adjust accordingly // If your system instead uses transform.rotation, adjust accordingly