Compare commits

...

10 Commits

  1. 4
      assets/glsl/batch-2d.frag
  2. 19
      assets/glsl/batch-2d.vert
  3. 18
      assets/glsl/batch-3d.frag
  4. 17
      assets/glsl/batch-3d.vert
  5. 4
      assets/glsl/batch-cubemap.frag
  6. 8
      assets/glsl/batch-cubemap.vert
  7. 4
      assets/glsl/batch-font.frag
  8. 4
      assets/glsl/batch-font.vert
  9. 14
      assets/glsl/lightsource.frag
  10. 15
      assets/glsl/lightsource.vert
  11. 78
      assets/glsl/post-process.frag
  12. 25
      assets/glsl/post-process.vert
  13. BIN
      assets/model/quad.blend
  14. 15
      assets/model/quad.obj
  15. 15
      assets/scene/scene1.json
  16. 2
      assets/settings.json
  17. 76
      src/inferno/application.cpp
  18. 2
      src/inferno/application.h
  19. 2
      src/inferno/asset/model.cpp
  20. 45
      src/inferno/asset/shader.cpp
  21. 18
      src/inferno/asset/shader.h
  22. 51
      src/inferno/asset/texture.cpp
  23. 3
      src/inferno/asset/texture.h
  24. 3
      src/inferno/component/cubemap-component.cpp
  25. 1
      src/inferno/component/cubemap-component.h
  26. 4
      src/inferno/component/model-component.cpp
  27. 1
      src/inferno/component/model-component.h
  28. 31
      src/inferno/event/keyevent.h
  29. 19
      src/inferno/keycodes.cpp
  30. 4
      src/inferno/keycodes.h
  31. 104
      src/inferno/render/buffer.cpp
  32. 30
      src/inferno/render/buffer.h
  33. 47
      src/inferno/render/framebuffer.cpp
  34. 12
      src/inferno/render/framebuffer.h
  35. 26
      src/inferno/render/render-command.cpp
  36. 4
      src/inferno/render/render-command.h
  37. 106
      src/inferno/render/renderer.cpp
  38. 95
      src/inferno/render/renderer.h
  39. 240
      src/inferno/render/uniformbuffer.cpp
  40. 130
      src/inferno/render/uniformbuffer.h
  41. 5
      src/inferno/scene/scene.cpp
  42. 13
      src/inferno/system/camerasystem.cpp
  43. 1
      src/inferno/system/camerasystem.h
  44. 178
      src/inferno/system/rendersystem.cpp
  45. 22
      src/inferno/system/rendersystem.h
  46. 6
      src/inferno/window.cpp

4
assets/glsl/batch-quad.frag → assets/glsl/batch-2d.frag

@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;

19
assets/glsl/batch-2d.vert

@ -0,0 +1,19 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}

18
assets/glsl/batch-3d.frag

@ -1,17 +1,21 @@
#version 450 core
layout(location = 0) out vec4 color;
layout(location = 0) out vec4 albedoSpec; // RGB diffuse color
layout(location = 1) out vec4 position;
layout(location = 2) out vec4 normal;
in vec3 v_position;
in vec3 v_normal;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = vec4(1.0f);
switch(int(v_textureIndex)) {
vec4 textureColor = v_color;
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
@ -45,5 +49,9 @@ void main()
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
color = textureColor;
albedoSpec.rgb = textureColor.rgb;
albedoSpec.a = 1.0; // TODO: read specular from model material
position = vec4(v_position, 1.0f);
normal = vec4(normalize(v_normal), 1.0f);
}

17
assets/glsl/batch-3d.vert

@ -2,18 +2,27 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 2) in vec4 a_color;
layout(location = 3) in vec2 a_textureCoordinates;
layout(location = 4) in uint a_textureIndex;
out vec3 v_position;
out vec3 v_normal;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_position = a_position;
v_normal = a_normal;
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal

4
assets/glsl/batch-cubemap.frag

@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;

8
assets/glsl/batch-cubemap.vert

@ -2,13 +2,13 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in float a_textureIndex;
layout(location = 2) in uint a_textureIndex;
out vec4 v_color;
out vec3 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
uniform mat4 u_projectionView2;
void main()
{
@ -16,5 +16,5 @@ void main()
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
gl_Position = u_projectionView2 * vec4(a_position, 1.0f);
}

4
assets/glsl/batch-font.frag

@ -4,7 +4,7 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
in float v_width;
in float v_edge;
in float v_borderWidth;
@ -25,7 +25,7 @@ float alpha(float textureAlpha)
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
// case 1: textureColor.a = 1; break;
case 1: textureColor.a = alpha(texture(u_textures[1], v_textureCoordinates).a); break;

4
assets/glsl/batch-font.vert

@ -3,7 +3,7 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 3) in uint a_textureIndex;
layout(location = 4) in float a_width;
layout(location = 5) in float a_edge;
layout(location = 6) in float a_borderWidth;
@ -13,7 +13,7 @@ layout(location = 9) in float a_offset;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
out float v_width;
out float v_edge;
out float v_borderWidth;

14
assets/glsl/lightsource.frag

@ -0,0 +1,14 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
color = v_color;
}

15
assets/glsl/batch-quad.vert → assets/glsl/lightsource.vert

@ -2,19 +2,22 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 2) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out vec3 v_textureCoordinates;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);

78
assets/glsl/post-process.frag

@ -0,0 +1,78 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
// -----------------------------------------
layout(std140, binding = 0) uniform Camera {
mat4 u_projectionView;
vec3 u_position;
};
// -----------------------------------------
struct DirectionalLight {
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
const int MAX_DIRECTIONAL_LIGHTS = 32;
layout(std140, binding = 1) uniform DirectionalLights {
DirectionalLight u_directionalLight[MAX_DIRECTIONAL_LIGHTS];
};
// -----------------------------------------
void main()
{
float isObject = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).a;
if (isObject == 0.0f) {
color = vec4(0,0,0,0);
return;
}
vec3 albedo = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).rgb;
float specular = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).a;
vec3 position = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).rgb;
vec3 normal = texture(u_textures[v_textureIndex + 2], v_textureCoordinates).rgb;
vec3 lighting = vec3(0.0f, 0.0f, 0.0f);//albedo * v_color.xyz;
vec3 viewDirection = normalize(u_position - position);
// Loop through all directional lights
for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; ++i) {
// Diffuse
vec3 lightDirection = normalize(-u_directionalLight[i].direction);
float diffuse = max(dot(normal, lightDirection), 0.0f);
// Specular
vec3 reflectionDirection = reflect(-lightDirection, normal);
float specular = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 32);
lighting +=
(albedo * u_directionalLight[i].ambient) +
(albedo * diffuse * u_directionalLight[i].diffuse) +
(specular * u_directionalLight[i].specular);
}
// Loop through all point lights
// TODO
// vec3 lightDirection = normalize(lightPosition - position);
// float diffuse = max(dot(normal, lightDirection), 0.0f);
// lighting += diffuse * albedo * u_lightColor;
// Loop through all spot lights
// TODO
color = vec4(lighting, 1.0f);
}

25
assets/glsl/post-process.vert

@ -0,0 +1,25 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}

BIN
assets/model/quad.blend

Binary file not shown.

15
assets/model/quad.obj

@ -0,0 +1,15 @@
# Blender 4.2.0
# www.blender.org
usemtl (null)
o Plane
v -1.000000 -1.000000 -0.000000
v 1.000000 -1.000000 -0.000000
v -1.000000 1.000000 0.000000
v 1.000000 1.000000 0.000000
vn -0.0000 -0.0000 1.0000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1

15
assets/scene/scene1.json

@ -35,8 +35,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
@ -48,8 +49,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 0.5, 0.6, 0.8, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
@ -61,8 +63,9 @@
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
@ -74,8 +77,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
@ -87,8 +91,9 @@
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
}
}

2
assets/settings.json

@ -3,7 +3,7 @@
"fullscreen": "windowed",
"height": 720,
"title": "Inferno",
"vsync": true,
"vsync": false,
"width": 1280
}
}

76
src/inferno/application.cpp

@ -23,6 +23,8 @@
#include "inferno/render/buffer.h"
#include "inferno/render/context.h"
#include "inferno/render/framebuffer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/system/rendersystem.h"
// #include "inferno/render/gltf.h"
#include "inferno/asset/shader.h"
#include "inferno/asset/texture.h"
@ -52,20 +54,7 @@ Application::Application()
Input::initialize();
RenderCommand::initialize();
m_framebuffer = Framebuffer::create({
.attachments = { Framebuffer::Type::Color, Framebuffer::Type::Depth },
.width = m_window->getWidth(),
.height = m_window->getHeight(),
.clearColor = { 0.2f, 0.3f, 0.3f, 1.0f },
.clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
});
m_screenFramebuffer = Framebuffer::create({
.renderToScreen = true,
.clearColor = { 1.0f, 1.0f, 1.0f, 1.0f },
.clearBit = GL_COLOR_BUFFER_BIT,
});
RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight());
m_scene = std::make_shared<Scene>();
m_scene->initialize();
@ -90,11 +79,14 @@ Application::Application()
Application::~Application()
{
m_scene->destroy();
Uniformbuffer::destroy();
RendererFont::destroy();
Renderer2D::destroy();
Renderer3D::destroy();
RendererCubemap::destroy();
RendererPostProcess::destroy();
RendererLightCube::destroy();
RenderCommand::destroy();
AssetManager::destroy();
// Input::destroy();
@ -158,11 +150,9 @@ int Application::run()
// offset
#endif
constexpr glm::vec4 vectorOne { 1.0f, 1.0f, 1.0f, 1.0f };
constexpr glm::mat4 matIdentity { 1.0f };
constexpr TransformComponent transformIdentity;
double gametime = 0;
uint64_t frames = 0;
// m_window->setVSync(false);
while (!m_window->shouldClose()) {
float time = Time::time();
@ -170,6 +160,9 @@ int Application::run()
m_lastFrameTime = time;
// ruc::debug("Frametime {}ms", deltaTime * 1000);
gametime += deltaTime;
frames++;
// ---------------------------------
// Update
@ -182,50 +175,15 @@ int Application::run()
// ---------------------------------
// Render
m_framebuffer->bind();
RenderCommand::clearColor(m_framebuffer->clearColor());
RenderCommand::clearBit(m_framebuffer->clearBit());
render();
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer3D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
m_scene->render();
// RendererCharacter::the().drawCharacter(character, f->texture());
RendererCubemap::the().endScene();
Renderer3D::the().endScene();
Renderer2D::the().endScene();
RendererFont::the().endScene();
m_framebuffer->unbind();
// ---------------------------------
// Framebuffer
m_screenFramebuffer->bind();
RenderCommand::clearColor(m_screenFramebuffer->clearColor());
RenderCommand::clearBit(m_screenFramebuffer->clearBit());
Renderer2D::the().setEnableDepthBuffer(false);
Renderer2D::the().beginScene(matIdentity, matIdentity);
Renderer2D::the().drawQuad(transformIdentity, vectorOne, m_framebuffer->texture(0));
Renderer2D::the().endScene();
Renderer2D::the().setEnableDepthBuffer(true);
m_screenFramebuffer->unbind();
m_window->render();
}
ruc::debug("Application shutdown");
ruc::debug("Average frametime: {:.2f}ms", (gametime / frames) * 1000);
return m_status;
}
@ -254,8 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e)
{
ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight());
RenderCommand::setViewport(0, 0, e.getWidth(), e.getHeight());
m_framebuffer->resize(e.getWidth(), e.getHeight());
RenderSystem::the().resize(e.getWidth(), e.getHeight());
return true;
}
@ -269,6 +226,11 @@ bool Application::onKeyPress(KeyPressEvent& e)
m_window->setShouldClose(true);
}
if (e.getKey() == keyCode("GLFW_KEY_F12")) {
ruc::info("Taking screenshot..");
Texture::saveScreenshotPNG("screenshot.png", m_window->getWidth(), m_window->getHeight());
}
return true;
}

2
src/inferno/application.h

@ -51,8 +51,6 @@ private:
float m_lastFrameTime { 0.0f };
std::unique_ptr<Window> m_window;
std::shared_ptr<Framebuffer> m_framebuffer;
std::shared_ptr<Framebuffer> m_screenFramebuffer;
std::shared_ptr<Scene> m_scene;
//

2
src/inferno/asset/model.cpp

@ -40,7 +40,7 @@ std::shared_ptr<Model> Model::create(std::string_view path)
void Model::processScene(std::shared_ptr<Model> model, const aiScene* scene)
{
VERIFY(scene->HasMeshes(), "malformed model");
VERIFY(scene->mNumTextures < 2, "unsupported model type");
VERIFY(scene->mNumTextures < 2, "unsupported model type: {}/1", scene->mNumTextures);
if (scene->mNumTextures == 1) {
aiTexture* texture = scene->mTextures[0];

45
src/inferno/asset/shader.cpp

@ -50,65 +50,74 @@ Shader::~Shader()
}
}
int32_t Shader::findUniform(std::string_view name) const
// -----------------------------------------
uint32_t Shader::findUniformLocation(std::string_view name)
{
// Cache uniform locations, prevent going to the GPU every call
if (m_uniformLocations.find(name) != m_uniformLocations.end()) {
return m_uniformLocations[name];
}
int32_t location = glGetUniformLocation(m_id, name.data());
VERIFY(location != -1, "Shader could not find uniform '{}'", name);
m_uniformLocations[name] = static_cast<uint32_t>(location);
return location;
}
void Shader::setInt(std::string_view name, int value)
{
// Set uniform int
glUniform1i(findUniform(name), value);
glUniform1i(findUniformLocation(name), value);
}
void Shader::setInt(std::string_view name, int* values, uint32_t count)
{
// Set uniform int array
glUniform1iv(findUniform(name), count, values);
glUniform1iv(findUniformLocation(name), count, values);
}
void Shader::setFloat(std::string_view name, float value) const
void Shader::setFloat(std::string_view name, float value)
{
// Set uniform float
glUniform1f(findUniform(name), value);
glUniform1f(findUniformLocation(name), value);
}
void Shader::setFloat(std::string_view name, float v1, float v2, float v3, float v4) const
void Shader::setFloat(std::string_view name, float v1, float v2, float v3, float v4)
{
// Set uniform vec4 data
glUniform4f(findUniform(name), v1, v2, v3, v4);
glUniform4f(findUniformLocation(name), v1, v2, v3, v4);
}
void Shader::setFloat(std::string_view name, glm::vec2 value) const
void Shader::setFloat(std::string_view name, glm::vec2 value)
{
// Set uniform vec2 data
glUniform2f(findUniform(name), value.x, value.y);
glUniform2f(findUniformLocation(name), value.x, value.y);
}
void Shader::setFloat(std::string_view name, glm::vec3 value) const
void Shader::setFloat(std::string_view name, glm::vec3 value)
{
// Set uniform vec3 data
glUniform3f(findUniform(name), value.x, value.y, value.z);
glUniform3f(findUniformLocation(name), value.x, value.y, value.z);
}
void Shader::setFloat(std::string_view name, glm::vec4 value) const
void Shader::setFloat(std::string_view name, glm::vec4 value)
{
// Set uniform vec4 data
glUniform4f(findUniform(name), value.x, value.y, value.z, value.w);
glUniform4f(findUniformLocation(name), value.x, value.y, value.z, value.w);
}
void Shader::setFloat(std::string_view name, glm::mat3 matrix) const
void Shader::setFloat(std::string_view name, glm::mat3 matrix)
{
// Set uniform mat3 data
glUniformMatrix3fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
glUniformMatrix3fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::setFloat(std::string_view name, glm::mat4 matrix) const
void Shader::setFloat(std::string_view name, glm::mat4 matrix)
{
// Set uniform mat4 data
glUniformMatrix4fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
glUniformMatrix4fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::bind() const
@ -121,6 +130,8 @@ void Shader::unbind() const
glUseProgram(0);
}
// -----------------------------------------
uint32_t Shader::compileShader(int32_t type, const char* source) const
{
// Create new shader

18
src/inferno/asset/shader.h

@ -6,6 +6,7 @@
#include <cstdint> // int32_t, uint32_t
#include <string_view>
#include <unordered_map>
#include "glm/fwd.hpp" // glm::mat3, glm::mat4, glm::vec2, glm::vec3
@ -20,17 +21,17 @@ public:
// Factory function
static std::shared_ptr<Shader> create(std::string_view path);
int32_t findUniform(std::string_view name) const;
uint32_t findUniformLocation(std::string_view name);
void setInt(std::string_view name, int value);
void setInt(std::string_view name, int* values, uint32_t count);
void setFloat(std::string_view name, float value) const;
void setFloat(std::string_view name, float v1, float v2, float v3, float v4) const;
void setFloat(std::string_view name, glm::vec2 value) const;
void setFloat(std::string_view name, glm::vec3 value) const;
void setFloat(std::string_view name, glm::vec4 value) const;
void setFloat(std::string_view name, glm::mat3 matrix) const;
void setFloat(std::string_view name, glm::mat4 matrix) const;
void setFloat(std::string_view name, float value);
void setFloat(std::string_view name, float v1, float v2, float v3, float v4);
void setFloat(std::string_view name, glm::vec2 value);
void setFloat(std::string_view name, glm::vec3 value);
void setFloat(std::string_view name, glm::vec4 value);
void setFloat(std::string_view name, glm::mat3 matrix);
void setFloat(std::string_view name, glm::mat4 matrix);
void bind() const;
void unbind() const;
@ -52,6 +53,7 @@ private:
private:
uint32_t m_id { 0 };
std::unordered_map<std::string_view, uint32_t> m_uniformLocations;
};
// -----------------------------------------

51
src/inferno/asset/texture.cpp

@ -13,6 +13,8 @@
#include "ruc/meta/assert.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image_write.h"
#include "inferno/asset/texture.h"
@ -23,6 +25,51 @@ Texture::~Texture()
glDeleteTextures(1, &m_id);
}
// -----------------------------------------
void Texture::savePNG(std::string_view path, std::shared_ptr<Texture> texture)
{
texture->bind();
// Allocate memory
uint32_t dataFormat = texture->m_dataFormat == GL_RGBA ? 4 : 3;
size_t dataSize = texture->m_width * texture->m_height * dataFormat;
std::vector<unsigned char> data(dataSize);
// Read texture data from the GPU
glGetTexImage(GL_TEXTURE_2D, 0, texture->m_dataFormat, texture->m_dataType, data.data());
// Write image to a file
stbi_flip_vertically_on_write(1);
stbi_write_png(
path.data(),
texture->m_width,
texture->m_height,
dataFormat,
data.data(),
texture->m_width * dataFormat);
texture->unbind();
}
void Texture::saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height)
{
// Allocate memory
size_t dataSize = width * height * 4;
std::vector<unsigned char> data(dataSize);
// Set the pack alignment to 1 (to avoid row alignment issues)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Read the pixels from the default framebuffer
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
stbi_flip_vertically_on_write(1);
stbi_write_png(path.data(), width, height, 4, data.data(), width * 4);
}
// -----------------------------------------
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
{
init(width, height,
@ -283,8 +330,8 @@ void TextureFramebuffer::createImpl()
m_dataType, // Texture source datatype
NULL); // Image data
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);

3
src/inferno/asset/texture.h

@ -22,6 +22,9 @@ class Texture : public Asset {
public:
virtual ~Texture();
static void savePNG(std::string_view path, std::shared_ptr<Texture> texture);
static void saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height);
void init(uint32_t width, uint32_t height, uint8_t channels);
void init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType);

3
src/inferno/component/cubemap-component.cpp

@ -22,6 +22,9 @@ void fromJson(const ruc::Json& json, CubemapComponent& value)
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = AssetManager::the().load<TextureCubemap>(json.at("texture").asString());
}
if (json.exists("isLight")) {
json.at("isLight").getTo(value.isLight);
}
}
} // namespace Inferno

1
src/inferno/component/cubemap-component.h

@ -18,6 +18,7 @@ namespace Inferno {
struct CubemapComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Texture> texture;
bool isLight { false };
};
void fromJson(const ruc::Json& json, CubemapComponent& value);

4
src/inferno/component/model-component.cpp

@ -8,6 +8,7 @@
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
#include "inferno/component/spritecomponent.h" // TODO: Move glm::x toJson/fromJson to separate file
namespace Inferno {
@ -15,6 +16,9 @@ void fromJson(const ruc::Json& json, ModelComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("color")) {
json.at("color").getTo(value.color);
}
if (json.exists("model") && json.at("model").type() == ruc::Json::Type::String) {
value.model = AssetManager::the().load<Model>(json.at("model").asString());
}

1
src/inferno/component/model-component.h

@ -16,6 +16,7 @@
namespace Inferno {
struct ModelComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Model> model;
std::shared_ptr<Texture2D> texture;
};

31
src/inferno/event/keyevent.h

@ -14,24 +14,27 @@ namespace Inferno {
class KeyEvent : public Event {
public:
inline int getKey() const { return m_key; }
int getKey() const { return m_key; }
int getMods() const { return m_mods; }
EVENT_CLASS_CATEGORY(InputEventCategory | KeyEventCategory)
protected:
KeyEvent(int key)
KeyEvent(int key, int mods)
: m_key(key)
, m_mods(mods)
{
}
private:
int m_key;
int m_key { 0 };
int m_mods { 0 };
};
class KeyPressEvent : public KeyEvent {
class KeyPressEvent final : public KeyEvent {
public:
KeyPressEvent(int key)
: KeyEvent(key)
KeyPressEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@ -45,10 +48,10 @@ public:
EVENT_CLASS_TYPE(KeyPress)
};
class KeyReleaseEvent : public KeyEvent {
class KeyReleaseEvent final : public KeyEvent {
public:
KeyReleaseEvent(int key)
: KeyEvent(key)
KeyReleaseEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@ -59,13 +62,13 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRelease)
};
class KeyRepeatEvent : public KeyEvent {
class KeyRepeatEvent final : public KeyEvent {
public:
KeyRepeatEvent(int key)
: KeyEvent(key)
KeyRepeatEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@ -76,7 +79,7 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRepeat)
};
} // namespace Inferno

19
src/inferno/keycodes.cpp

@ -139,14 +139,33 @@ static std::unordered_map<std::string_view, int> keys({
{ MAP_KEY(GLFW_KEY_MENU) },
});
static std::unordered_map<std::string_view, int> modifiers({
{ MAP_KEY(GLFW_MOD_SHIFT) },
{ MAP_KEY(GLFW_MOD_CONTROL) },
{ MAP_KEY(GLFW_MOD_ALT) },
{ MAP_KEY(GLFW_MOD_SUPER) },
{ MAP_KEY(GLFW_MOD_CAPS_LOCK) }, // State, not really a modifier
{ MAP_KEY(GLFW_MOD_NUM_LOCK) }, // State, not really a modifier
});
// -----------------------------------------
// Example usage:
// event.getKey() == keyCode("GLFW_KEY_ESCAPE")
int keyCode(std::string_view name)
{
VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name);
return keys.at(name);
}
// Example usage:
// event.getMods() & keyMod("GLFW_MOD_SHIFT")
int keyMod(std::string_view name)
{
VERIFY(modifiers.find(name) != modifiers.end(), "could not find key modifier: {}", name);
return modifiers.at(name);
}
std::string_view keyName(int key)
{
auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) {

4
src/inferno/keycodes.h

@ -13,6 +13,8 @@
namespace Inferno {
int keyCode(std::string_view name);
std::string_view keyName(int);
int keyMod(std::string_view name);
std::string_view keyName(int key);
} // namespace Inferno

104
src/inferno/render/buffer.cpp

@ -4,6 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string>
#include <utility> // std::pair
#include "glad/glad.h"
#include "ruc/meta/assert.h"
@ -61,10 +67,10 @@ uint32_t BufferElement::getTypeSize(const BufferElementType type)
case BufferElementType::Vec3:
case BufferElementType::Vec4:
return sizeof(float) * getTypeCount(type);
case BufferElementType::VecDouble:
case BufferElementType::VecDouble2:
case BufferElementType::VecDouble3:
case BufferElementType::VecDouble4:
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return sizeof(double) * getTypeCount(type);
case BufferElementType::Mat2:
case BufferElementType::Mat3:
@ -89,25 +95,25 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
case BufferElementType::Int:
case BufferElementType::Uint:
case BufferElementType::Float:
case BufferElementType::VecDouble:
case BufferElementType::Double:
return 1;
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2:
case BufferElementType::VecDouble2:
case BufferElementType::Vec2Double:
return 2;
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3:
case BufferElementType::VecDouble3:
case BufferElementType::Vec3Double:
return 3;
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4:
case BufferElementType::VecDouble4:
case BufferElementType::Vec4Double:
return 4;
case BufferElementType::Mat2:
return 2 * 2;
@ -152,10 +158,10 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
case BufferElementType::Vec3:
case BufferElementType::Vec4:
return GL_FLOAT;
case BufferElementType::VecDouble:
case BufferElementType::VecDouble2:
case BufferElementType::VecDouble3:
case BufferElementType::VecDouble4:
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return GL_DOUBLE;
case BufferElementType::Mat2:
case BufferElementType::Mat3:
@ -184,7 +190,7 @@ void BufferLayout::calculateOffsetsAndStride()
m_stride = 0;
for (auto& element : m_elements) {
element.setOffset(m_stride);
m_stride += element.getSize();
m_stride += element.size();
}
}
@ -202,7 +208,7 @@ VertexBuffer::VertexBuffer(size_t size, float* vertices)
bind();
// Upload data to the GPU
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
unbind();
}
@ -242,7 +248,7 @@ IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
bind();
// Upload data to the GPU
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_DYNAMIC_DRAW);
unbind();
}
@ -297,8 +303,8 @@ void VertexArray::unbind() const
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
{
const auto& layout = vertexBuffer->getLayout();
VERIFY(layout.getElements().size(), "VertexBuffer has no layout");
const auto& layout = vertexBuffer->layout();
VERIFY(layout.elements().size(), "VertexBuffer has no layout");
bind();
vertexBuffer->bind();
@ -306,13 +312,63 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
uint32_t index = 0;
for (const auto& element : layout) {
glEnableVertexAttribArray(index);
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.getNormalized() ? GL_TRUE : GL_FALSE,
layout.getStride(),
reinterpret_cast<const void*>(element.getOffset()));
switch (element.type()) {
case BufferElementType::None:
break;
case BufferElementType::Int:
case BufferElementType::Int2:
case BufferElementType::Int3:
case BufferElementType::Int4:
case BufferElementType::Uint:
case BufferElementType::Uint2:
case BufferElementType::Uint3:
case BufferElementType::Uint4: {
glVertexAttribIPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Bool:
case BufferElementType::Bool2:
case BufferElementType::Bool3:
case BufferElementType::Bool4:
case BufferElementType::Float:
case BufferElementType::Vec2:
case BufferElementType::Vec3:
case BufferElementType::Vec4:
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.normalized() ? GL_TRUE : GL_FALSE,
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4: {
glVertexAttribLPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
default:
VERIFY_NOT_REACHED();
};
index++;
}

30
src/inferno/render/buffer.h

@ -7,10 +7,10 @@
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <vector> // std::vector
#include <string>
#include <vector>
namespace Inferno {
@ -22,7 +22,7 @@ enum class BufferElementType {
Int, Int2, Int3, Int4, // ivec
Uint, Uint2, Uint3, Uint4, // uvec
Float, Vec2, Vec3, Vec4, // vec
VecDouble, VecDouble2, VecDouble3, VecDouble4, // dvec
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
Mat2, Mat3, Mat4, // mat
MatDouble2, MatDouble3, MatDouble4, // dmat
};
@ -43,11 +43,11 @@ public:
static uint32_t getTypeCount(const BufferElementType type);
static uint32_t getTypeGL(const BufferElementType type);
BufferElementType getType() const { return m_type; }
std::string getName() const { return m_name; }
uint32_t getSize() const { return m_size; }
uint32_t getOffset() const { return m_offset; }
bool getNormalized() const { return m_normalized; }
BufferElementType type() const { return m_type; }
std::string name() const { return m_name; }
uint32_t size() const { return m_size; }
uint32_t offset() const { return m_offset; }
bool normalized() const { return m_normalized; }
void setType(const BufferElementType& type) { m_type = type; }
void setName(const std::string& name) { m_name = name; }
@ -72,8 +72,8 @@ public:
BufferLayout(const std::initializer_list<BufferElement>& elements);
~BufferLayout() = default;
const std::vector<BufferElement>& getElements() const { return m_elements; }
uint32_t getStride() const { return m_stride; }
const std::vector<BufferElement>& elements() const { return m_elements; }
uint32_t stride() const { return m_stride; }
// Iterators
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
@ -103,9 +103,9 @@ public:
void uploadData(const void* data, uint32_t size);
const BufferLayout& getLayout() const { return m_layout; }
const BufferLayout& layout() const { return m_layout; }
inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
void setLayout(const BufferLayout& layout) { m_layout = layout; }
private:
uint32_t m_id { 0 };
@ -125,7 +125,7 @@ public:
void uploadData(const void* data, uint32_t size);
uint32_t getCount() const { return m_count; }
uint32_t count() const { return m_count; }
private:
uint32_t m_id { 0 };
@ -147,7 +147,7 @@ public:
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
std::shared_ptr<VertexBuffer> at(size_t i) const { return m_vertexBuffers.at(i); }
std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; }
std::shared_ptr<IndexBuffer> indexBuffer() const { return m_indexBuffer; }
private:
uint32_t m_id { 0 };

47
src/inferno/render/framebuffer.cpp

@ -36,6 +36,14 @@ Framebuffer::~Framebuffer()
glDeleteFramebuffers(1, &m_id);
}
void Framebuffer::copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, from->m_id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer
glBlitFramebuffer(0, 0, from->m_width, from->m_height, 0, 0, to->m_width, to->m_height, bits, filter);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// -----------------------------------------
void Framebuffer::bind() const
@ -86,22 +94,47 @@ void Framebuffer::createTextures()
bind();
auto it = m_attachments.begin();
uint8_t color_attachment = 0;
m_colorAttachmentCount = 0;
size_t size = m_attachments.size();
m_textures.resize(size);
for (size_t i = 0; i < size; ++i) {
TypeProperties type = *(it + i);
if (type.type == Type::Color) {
if (type.type == Type::RGB8) {
// Set color attachment 0 out of 32
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_RGB, GL_RGB));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + color_attachment, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGB8, GL_RGB);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA8) { // Color
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA8, GL_RGBA);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA16F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA16F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA32F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA32F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
// This combined texture is required for older GPUs
if (type.type == Type::Depth24Stencil8) {
if (type.type == Type::Depth24Stencil8) { // Depth
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0);
@ -117,7 +150,7 @@ void Framebuffer::createTextures()
}
}
VERIFY(color_attachment <= 32, "maximum color attachments was exceeded: {}/32", color_attachment);
VERIFY(m_colorAttachmentCount <= 32, "maximum color attachments was exceeded: {}/32", m_colorAttachmentCount);
check();
unbind();

12
src/inferno/render/framebuffer.h

@ -24,11 +24,14 @@ public:
None = 0,
// Color
RGBA8 = 1,
RGB8 = 1,
RGBA8 = 2,
RGBA16F = 3,
RGBA32F = 4,
// Depth/stencil
Depth32F = 2,
Depth24Stencil8 = 3,
Depth24Stencil8 = 5,
Depth32F = 6,
// Defaults
Color = RGBA8,
@ -59,12 +62,14 @@ public:
// Factory function
static std::shared_ptr<Framebuffer> create(const Properties& properties);
static void copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter);
void bind() const;
void unbind() const;
bool check() const;
void resize(uint32_t width, uint32_t height);
uint8_t colorAttachmentCount() const { return m_colorAttachmentCount; }
uint32_t id() const { return m_id; }
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
@ -87,6 +92,7 @@ private:
private:
bool m_renderToScreen { false };
uint8_t m_colorAttachmentCount { 1 };
uint32_t m_id { 0 };
uint32_t m_width { 0 };
uint32_t m_height { 0 };

26
src/inferno/render/render-command.cpp

@ -4,10 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::shared_ptr
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
#include "inferno/render/render-command.h"
@ -42,7 +44,7 @@ void RenderCommand::clearColor(const glm::vec4& color)
void RenderCommand::drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount)
{
uint32_t count = indexCount ? indexCount : vertexArray->getIndexBuffer()->getCount();
uint32_t count = indexCount ? indexCount : vertexArray->indexBuffer()->count();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
@ -57,6 +59,26 @@ void RenderCommand::setDepthTest(bool enabled)
enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
void RenderCommand::setColorAttachmentCount(uint32_t count)
{
static constexpr uint32_t colorAttachments[] = {
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5,
GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, GL_COLOR_ATTACHMENT8,
GL_COLOR_ATTACHMENT9, GL_COLOR_ATTACHMENT10, GL_COLOR_ATTACHMENT11,
GL_COLOR_ATTACHMENT12, GL_COLOR_ATTACHMENT13, GL_COLOR_ATTACHMENT14,
GL_COLOR_ATTACHMENT15, GL_COLOR_ATTACHMENT16, GL_COLOR_ATTACHMENT17,
GL_COLOR_ATTACHMENT18, GL_COLOR_ATTACHMENT19, GL_COLOR_ATTACHMENT20,
GL_COLOR_ATTACHMENT21, GL_COLOR_ATTACHMENT22, GL_COLOR_ATTACHMENT23,
GL_COLOR_ATTACHMENT24, GL_COLOR_ATTACHMENT25, GL_COLOR_ATTACHMENT26,
GL_COLOR_ATTACHMENT27, GL_COLOR_ATTACHMENT28, GL_COLOR_ATTACHMENT29,
GL_COLOR_ATTACHMENT30, GL_COLOR_ATTACHMENT31
};
static constexpr uint32_t maxCount = sizeof(colorAttachments) / sizeof(colorAttachments[0]);
VERIFY(count > 0 && count <= maxCount, "incorrect colorbuffer count: {}/{}", count, maxCount);
glDrawBuffers(static_cast<int32_t>(count), colorAttachments); // Multiple Render Targets (MRT)
}
bool RenderCommand::depthTest()
{
unsigned char depthTest = GL_FALSE;

4
src/inferno/render/render-command.h

@ -6,7 +6,8 @@
#pragma once
#include <memory> // std::shadred_ptr
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shadred_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
@ -25,6 +26,7 @@ public:
static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height);
static void setDepthTest(bool enabled);
static void setColorAttachmentCount(uint32_t count);
static bool depthTest();
static int32_t textureUnitAmount();

106
src/inferno/render/renderer.cpp

@ -8,6 +8,7 @@
#include <span>
#include "glad/glad.h"
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/format/log.h"
#include "inferno/asset/asset-manager.h"
@ -170,6 +171,7 @@ void Renderer<T>::flush()
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(m_enableDepthBuffer);
RenderCommand::setColorAttachmentCount(m_colorAttachmentCount);
RenderCommand::drawIndexed(m_vertexArray, m_elementIndex);
RenderCommand::setDepthTest(depthTest);
@ -196,6 +198,11 @@ void Renderer<T>::nextBatch()
// -----------------------------------------
Renderer2D::Renderer2D(s)
{
Renderer2D::initialize();
}
void Renderer2D::initialize()
{
Renderer::initialize();
@ -215,26 +222,21 @@ Renderer2D::Renderer2D(s)
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer2D initialized");
}
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color)
{
drawQuad(transform, color, nullptr);
@ -271,7 +273,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@ -281,12 +283,17 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
void Renderer2D::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-quad");
m_shader = AssetManager::the().load<Shader>("assets/glsl/post-process");
}
// -----------------------------------------
RendererCubemap::RendererCubemap(s)
{
RendererCubemap::initialize();
}
void RendererCubemap::initialize()
{
Renderer::initialize();
@ -345,7 +352,7 @@ RendererCubemap::RendererCubemap(s)
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
@ -360,10 +367,10 @@ void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraVie
// x x x 0
// x x x 0
// 0 0 0 1
cameraView = glm::mat4(glm::mat3(cameraView));
// cameraView = glm::mat4(glm::mat3(cameraView));
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->setFloat("u_projectionView2", cameraProjection * cameraView);
m_shader->unbind();
}
@ -386,7 +393,7 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4];
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@ -423,7 +430,7 @@ RendererFont::RendererFont(s)
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
{ BufferElementType::Float, "a_width" },
{ BufferElementType::Float, "a_edge" },
{ BufferElementType::Float, "a_borderWidth" },
@ -450,7 +457,7 @@ void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQua
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->quad.textureIndex = textureUnitIndex;
m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = symbolQuad[i].edge;
@ -488,26 +495,28 @@ Renderer3D::Renderer3D(s)
// GPU
m_enableDepthBuffer = true;
m_colorAttachmentCount = 3;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(Vertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec3, "a_normal" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer3D initialized");
}
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, std::shared_ptr<Texture> texture)
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{
// ruc::error("drawModel");
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer");
VERIFY(elements.size() <= maxElements, "model elements too big for buffer");
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer, {}/{}", vertices.size(), maxVertices);
VERIFY(elements.size() <= maxElements, "model elements too big for buffer, {}/{}", elements.size(), maxElements);
// Create a new batch if the quad limit has been reached
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + elements.size() > maxElements) {
@ -517,11 +526,13 @@ void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uin
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the vertices
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(transform.transform)));
for (const auto& vertex : vertices) {
m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f);
m_vertexBufferPtr->normal = vertex.normal;
m_vertexBufferPtr->normal = normalMatrix * vertex.normal; // take non-uniform scaling into consideration
m_vertexBufferPtr->color = color;
m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates;
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@ -536,13 +547,6 @@ void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uin
m_elementIndex += elements.size();
}
void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void Renderer3D::createElementBuffer()
{
// ---------------------------------
@ -562,7 +566,7 @@ void Renderer3D::createElementBuffer()
void Renderer3D::uploadElementBuffer()
{
m_vertexArray->getIndexBuffer()->uploadData(m_elementBufferBase, m_elementIndex * sizeof(uint32_t));
m_vertexArray->indexBuffer()->uploadData(m_elementBufferBase, m_elementIndex * sizeof(uint32_t));
}
void Renderer3D::loadShader()
@ -576,4 +580,46 @@ void Renderer3D::startBatch()
m_elementBufferPtr = m_elementBufferBase;
}
// -----------------------------------------
void RendererPostProcess::drawQuad(const TransformComponent& transform, std::shared_ptr<Texture> albedo, std::shared_ptr<Texture> position, std::shared_ptr<Texture> normal)
{
nextBatch();
constexpr glm::vec2 textureCoordinates[] = {
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 1.0f, 1.0f },
{ 0.0f, 1.0f }
};
uint32_t textureUnitIndex = addTextureUnit(albedo);
addTextureUnit(position);
addTextureUnit(normal);
// Add the quads 4 vertices
for (uint32_t i = 0; i < vertexPerQuad; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
}
void RendererPostProcess::loadShader()
{
ruc::error("POSTPROCESSING!");
m_shader = AssetManager::the().load<Shader>("assets/glsl/post-process");
}
// -----------------------------------------
void RendererLightCube::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/lightsource");
}
} // namespace Inferno

95
src/inferno/render/renderer.h

@ -24,16 +24,16 @@ class TransformComponent;
class VertexArray;
struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
struct CubemapVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
uint32_t textureIndex { 0 };
};
struct SymbolVertex {
@ -45,16 +45,17 @@ struct SymbolVertex {
// Outline
float borderWidth = 0.7f;
float borderEdge = 0.1f;
glm::vec4 borderColor { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 borderColor { 1.0f };
// Dropshadow
float offset = 0.0f;
};
struct Vertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec3 normal { 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec3 normal { 1.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
// -------------------------------------
@ -111,6 +112,7 @@ protected:
// GPU objects
bool m_enableDepthBuffer { true };
uint32_t m_colorAttachmentCount { 1 };
std::shared_ptr<Shader> m_shader;
std::shared_ptr<VertexArray> m_vertexArray;
};
@ -123,7 +125,7 @@ protected:
// -------------------------------------
class Renderer2D final
class Renderer2D
: public Renderer<QuadVertex>
, public ruc::Singleton<Renderer2D> {
public:
@ -132,22 +134,25 @@ public:
using Singleton<Renderer2D>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawQuad(const TransformComponent& transform, glm::vec4 color);
void drawQuad(const TransformComponent& transform, glm::mat4 color);
void drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
private:
void loadShader() override;
protected:
Renderer2D() { Renderer2D::initialize(); } // Needed for derived classes
void initialize();
// Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad];
private:
virtual void loadShader() override;
};
// -------------------------------------
class RendererCubemap final
class RendererCubemap
: public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> {
public:
@ -164,8 +169,13 @@ public:
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
protected:
RendererCubemap() { RendererCubemap::initialize(); } // Needed for derived classes
void initialize();
private:
void loadShader() override;
virtual void loadShader() override;
// Default cubemap vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
@ -199,9 +209,7 @@ public:
using Singleton<Renderer3D>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> texture);
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
private:
void createElementBuffer() override;
@ -215,4 +223,47 @@ private:
uint32_t* m_elementBufferPtr { nullptr };
};
// -----------------------------------------
class RendererPostProcess final
: public Renderer2D
, public ruc::Singleton<RendererPostProcess> {
public:
RendererPostProcess(ruc::Singleton<RendererPostProcess>::s)
: Renderer2D()
{
}
virtual ~RendererPostProcess() = default;
using Singleton<RendererPostProcess>::the;
using Singleton<RendererPostProcess>::destroy;
void drawQuad(const TransformComponent& transform, std::shared_ptr<Texture> albedo, std::shared_ptr<Texture> position, std::shared_ptr<Texture> normal);
private:
virtual void loadShader() override;
};
// -----------------------------------------
class RendererLightCube final
: public RendererCubemap
, public ruc::Singleton<RendererLightCube> {
public:
RendererLightCube(ruc::Singleton<RendererLightCube>::s)
: RendererCubemap()
{
m_enableDepthBuffer = true;
}
virtual ~RendererLightCube() = default;
using Singleton<RendererLightCube>::the;
using Singleton<RendererLightCube>::destroy;
void beginScene(glm::mat4, glm::mat4) override {}
private:
virtual void loadShader() override;
};
} // namespace Inferno

240
src/inferno/render/uniformbuffer.cpp

@ -0,0 +1,240 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t, uint8_t
#include <string_view>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
#include "inferno/render/uniformbuffer.h"
namespace Inferno {
Uniformbuffer::Uniformbuffer(s)
{
// Get maximum uniformbuffer bindings the GPU supports
int32_t maxBindingPoints = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxBindingPoints);
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
}
Uniformbuffer::~Uniformbuffer()
{
for (const auto& [_, block] : m_blocks) {
glDeleteBuffers(1, &block.id);
}
m_blocks.clear();
}
// -----------------------------------------
void Uniformbuffer::setLayout(std::string_view blockName, const UniformbufferBlock& block)
{
VERIFY(block.size && block.bindingPoint && block.uniformLocations.size(),
"invalid uniformbuffer block definition: {}", blockName);
VERIFY(block.bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", block.bindingPoint, m_maxBindingPoints);
m_blocks[blockName] = block;
}
void Uniformbuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout)
{
VERIFY(bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints);
if (!exists(blockName)) {
m_blocks[blockName] = {};
}
UniformbufferBlock& block = m_blocks[blockName];
block.bindingPoint = bindingPoint;
// Example block layout:
// - mat3
// - float
// - vec2
// - vec2
// - float
// Chunks, 4 slots, 4 bytes per slot
// [x][x][x][ ] #1
// [x][x][x][ ] #2
// [x][x][x][ ] #3
// [x][ ][x][x] #4
// [x][x][x][ ] #5
size_t chunk = 0;
uint8_t offset = 0;
for (auto it = layout.begin(); it != layout.end(); ++it) {
BufferElementType type = it->type();
const std::string& name = it->name();
// Calculate offset
switch (type) {
// Scalar 1
case BufferElementType::Bool:
case BufferElementType::Int:
case BufferElementType::Uint:
case BufferElementType::Float: {
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 1;
break;
}
// Scalar 2
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2: {
// Add padding
if (offset == 1) {
offset++;
}
if (offset == 3) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 2;
break;
}
// Scalar 3
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 3;
break;
}
// Scalar 4
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 4;
break;
}
// Array types
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Additional rows
if (type == BufferElementType::Mat2) {
chunk += 1;
}
else if (type == BufferElementType::Mat3) {
chunk += 2;
}
else {
chunk += 3;
}
// Jump
offset += 4;
break;
}
// TODO: Implement these types
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
VERIFY_NOT_REACHED();
case BufferElementType::None:
VERIFY_NOT_REACHED();
};
// Overflow slots to next chunk
if (offset > 3) {
offset = 0;
chunk++;
}
}
// Pad the end of the buffer
if (offset != 0) {
offset = 0;
chunk++;
}
block.size = chunk * 16;
}
void Uniformbuffer::create(std::string_view blockName)
{
VERIFY(exists(blockName), "uniformbuffer block doesnt exist");
UniformbufferBlock& block = m_blocks[blockName];
if (block.id != 0) {
glDeleteBuffers(1, &block.id);
}
// Allocate buffer
block.id = UINT_MAX;
glGenBuffers(1, &block.id);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferData(GL_UNIFORM_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// Bind buffer to binding point
glBindBufferBase(GL_UNIFORM_BUFFER, block.bindingPoint, block.id);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, bool value)
{
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t));
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3);
}
} // namespace Inferno

130
src/inferno/render/uniformbuffer.h

@ -0,0 +1,130 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include <string>
#include <string_view>
#include <unordered_map>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/singleton.h"
#include "inferno/render/buffer.h"
#define CHECK_SET_CALL(blockName, member) \
VERIFY(exists(blockName), "uniformbuffer block doesnt exist"); \
const UniformbufferBlock& block = m_blocks[blockName]; \
VERIFY(block.uniformLocations.find(member.data()) != block.uniformLocations.end(), \
"uniformbuffer block member doesnt exist");
namespace Inferno {
// Uniform block layouts, using std140 memory layout rules
#define MAX_DIRECTIONAL_LIGHTS 32
struct UniformDirectionalLight {
glm::vec3 direction { 0 };
float __padding0 { 0 };
glm::vec3 ambient { 0 };
float __padding1 { 0 };
glm::vec3 diffuse { 0 };
float __padding2 { 0 };
glm::vec3 specular { 0 };
float __padding3 { 0 };
};
// -----------------------------------------
struct UniformbufferBlock {
uint32_t id { 0 };
uint32_t size { 0 };
uint8_t bindingPoint { 0 };
std::unordered_map<std::string, uint32_t> uniformLocations {};
};
class Uniformbuffer final : public ruc::Singleton<Uniformbuffer> { // Uniform Buffer Object, UBO
public:
Uniformbuffer(s);
~Uniformbuffer();
void setLayout(std::string_view blockName, const UniformbufferBlock& block);
void setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout);
void create(std::string_view blockName);
template<typename T> // Capture value by reference, instead of decaying to pointer
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0)
{
CHECK_SET_CALL(blockName, member);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferSubData(GL_UNIFORM_BUFFER, block.uniformLocations.at(member.data()), (size) ? size : sizeof(T), &value);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// Exceptions:
void setValue(std::string_view blockName, std::string_view member, bool value);
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value);
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value);
bool exists(std::string_view blockName) const { return m_blocks.find(blockName) != m_blocks.end(); }
private:
uint8_t m_maxBindingPoints { 0 };
std::unordered_map<std::string_view, UniformbufferBlock> m_blocks;
};
} // namespace Inferno
#if 0
// -----------------------------------------
// Example usage:
Uniformbuffer::the().setLayout(
"ExampleBlock", 0,
{
{ BufferElementType::Mat3, "a" },
{ BufferElementType::Float, "b" },
{ BufferElementType::Vec2, "c" },
{ BufferElementType::Vec2, "d" },
{ BufferElementType::Float, "e" },
});
Uniformbuffer::the().create("ExampleBlock");
#endif
// -----------------------------------------
// Memory alignment of uniform blocks using std140
//
// Main points:
// - Memory is organized into chunks.
// - A block is at least the size of 1 chunk.
// - One chunk has 4 slots, 4 bytes per slot.
// - Can't fit? Move to next chunk.
//
// The rules:
// 1. Scalar (bool, int, uint, float) takes up 1 slot, can appear after anything
// 2. Vec2 takes up 2 slots, in first or last half of a chunk
// 3. Vec3 takes up 3 slots, only at the start of a chunk
// 4. Everything else:
// - Take up maxumum room
// - As often as needed
// - Add padding as needed
// 5. Mat3 (or any matrix) are treated like arrays
// 6. Each member of *any* array gets its own chunk
// TODO: How do double types work?
// -----------------------------------------
// References:
// - https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
// - https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
// - https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html (The std140 Layout Rules)
// - https://www.youtube.com/watch?v=JPvbRko9lBg (WebGL 2: Uniform Buffer Objects)

5
src/inferno/scene/scene.cpp

@ -9,7 +9,7 @@
#include <limits> // std::numeric_limits
#include <utility> // std::pair
#include "entt/entity/entity.hpp" // ent::entity
#include "entt/entity/fwd.hpp" // ent::entity
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/json/json.h"
@ -25,6 +25,8 @@
#include "inferno/component/tagcomponent.h"
#include "inferno/component/textareacomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/scene/scene.h"
#include "inferno/script/nativescript.h"
#include "inferno/system/camerasystem.h"
@ -84,7 +86,6 @@ void Scene::update(float deltaTime)
void Scene::render()
{
RenderSystem::the().render();
TextAreaSystem::the().render();
}
void Scene::destroy()

13
src/inferno/system/camerasystem.cpp

@ -53,10 +53,23 @@ std::pair<glm::mat4, glm::mat4> CameraSystem::projectionView()
}
VERIFY_NOT_REACHED();
return {};
}
glm::vec3 CameraSystem::translate()
{
auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) {
return transform.translate;
}
VERIFY_NOT_REACHED();
return {};
}
// -----------------------------------------
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera)
{
// Update camera matrix

1
src/inferno/system/camerasystem.h

@ -32,6 +32,7 @@ public:
* @brief Return a pair from the camera component: { projection, view }
*/
std::pair<glm::mat4, glm::mat4> projectionView();
glm::vec3 translate();
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };

178
src/inferno/system/rendersystem.cpp

@ -4,41 +4,148 @@
* SPDX-License-Identifier: MIT
*/
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "inferno/component/model-component.h"
#include <cstdint> // int32_t
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/model-component.h"
#include "inferno/component/spritecomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/framebuffer.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/system/camerasystem.h"
#include "inferno/system/rendersystem.h"
#include "inferno/system/textareasystem.h"
namespace Inferno {
RenderSystem::RenderSystem(s)
{
ruc::info("RenderSystem initialized");
}
RenderSystem::~RenderSystem()
{
}
// -----------------------------------------
void RenderSystem::initialize(uint32_t width, uint32_t height)
{
m_framebuffer = Framebuffer::create({
.attachments = { Framebuffer::Type::Color, Framebuffer::Type::RGBA16F, Framebuffer::Type::RGBA16F, Framebuffer::Type::Depth },
.width = width,
.height = height,
.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f },
.clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
});
m_screenFramebuffer = Framebuffer::create({
.renderToScreen = true,
.clearColor = { 1.0f, 1.0f, 1.0f, 1.0f },
.clearBit = GL_COLOR_BUFFER_BIT,
});
Uniformbuffer::the().setLayout(
"Camera", 0,
{
{ BufferElementType::Mat4, "u_projectionView" },
{ BufferElementType::Vec3, "u_position" },
});
Uniformbuffer::the().create("Camera");
Uniformbuffer::the().setLayout(
"DirectionalLights",
{
.size = sizeof(UniformDirectionalLight) * MAX_DIRECTIONAL_LIGHTS,
.bindingPoint = 1,
.uniformLocations = {
{ "u_directionalLight", 0 },
},
});
Uniformbuffer::the().create("DirectionalLights");
ruc::info("RenderSystem initialized");
}
void RenderSystem::render()
{
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
static constexpr TransformComponent transformIdentity;
for (auto [entity, transform, sprite] : quadView.each()) {
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
}
// ---------------------------------
// Deferred rendering to a framebuffer
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
framebufferSetup(m_framebuffer);
for (auto [entity, transform, cubemap] : cubemapView.each()) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
}
renderGeometry();
framebufferTeardown(m_framebuffer);
// ---------------------------------
// Forward rendering to the screen
framebufferSetup(m_screenFramebuffer);
renderSkybox();
// Render 3D geometry post-processing
RendererPostProcess::the().drawQuad(transformIdentity, m_framebuffer->texture(0), m_framebuffer->texture(1), m_framebuffer->texture(2));
RendererPostProcess::the().endScene();
// Visual representation of light sources
Framebuffer::copyBuffer(m_framebuffer, m_screenFramebuffer, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
renderLightCubes();
// Render 2D, UI
renderOverlay();
framebufferTeardown(m_screenFramebuffer);
}
void RenderSystem::resize(int32_t width, int32_t height)
{
RenderCommand::setViewport(0, 0, width, height);
m_framebuffer->resize(width, height);
m_screenFramebuffer->resize(width, height);
}
// -----------------------------------------
void RenderSystem::framebufferSetup(std::shared_ptr<Framebuffer> framebuffer)
{
framebuffer->bind();
RenderCommand::setColorAttachmentCount(framebuffer->colorAttachmentCount());
RenderCommand::clearColor(framebuffer->clearColor());
RenderCommand::clearBit(framebuffer->clearBit());
}
void RenderSystem::framebufferTeardown(std::shared_ptr<Framebuffer> framebuffer)
{
framebuffer->unbind();
RenderCommand::setColorAttachmentCount(1);
}
void RenderSystem::renderGeometry()
{
auto [projection, view] = CameraSystem::the().projectionView();
auto translate = CameraSystem::the().translate();
Uniformbuffer::the().setValue("Camera", "u_projectionView", projection * view);
Uniformbuffer::the().setValue("Camera", "u_position", translate);
static UniformDirectionalLight directionalLights[1] = {
{
.direction = { -8.0f, -8.0f, -8.0f },
.ambient = { 0.1f, 0.1f, 0.1f },
.diffuse = { 1.0f, 1.0f, 1.0f },
.specular = { 1.0f, 1.0f, 1.0f },
},
};
Uniformbuffer::the().setValue("DirectionalLights", "u_directionalLight", directionalLights);
auto modelView = m_registry->view<TransformComponent, ModelComponent>();
@ -46,8 +153,57 @@ void RenderSystem::render()
Renderer3D::the().drawModel(model.model->vertices(),
model.model->elements(),
transform,
model.color,
model.model->texture() ? model.model->texture() : model.texture);
}
Renderer3D::the().endScene();
}
void RenderSystem::renderSkybox()
{
auto [projection, view] = CameraSystem::the().projectionView();
RendererCubemap::the().beginScene(projection, view); // camera, lights, environment
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
if (!cubemap.isLight) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
}
}
RendererCubemap::the().endScene();
}
void RenderSystem::renderLightCubes()
{
auto [projection, view] = CameraSystem::the().projectionView();
RendererLightCube::the().beginScene(projection, view); // camera, lights, environment
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
if (cubemap.isLight) {
RendererLightCube::the().drawCubemap(transform, cubemap.color, nullptr);
}
}
RendererLightCube::the().endScene();
}
void RenderSystem::renderOverlay()
{
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
for (auto [entity, transform, sprite] : quadView.each()) {
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
}
Renderer2D::the().endScene();
TextAreaSystem::the().render();
RendererFont::the().endScene();
}
} // namespace Inferno

22
src/inferno/system/rendersystem.h

@ -6,25 +6,39 @@
#pragma once
#include <memory> //std::shared_ptr
#include <cstdint> // int32_t, uint32_t
#include <memory> //std::shared_ptr
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "ruc/singleton.h"
#include "entt/entity/fwd.hpp" // entt::registry
#include "inferno/render/renderer.h"
#include "ruc/singleton.h"
namespace Inferno {
class Framebuffer;
class RenderSystem final : public ruc::Singleton<RenderSystem> {
public:
RenderSystem(s);
virtual ~RenderSystem();
void initialize(uint32_t width, uint32_t height);
void render();
void resize(int32_t width, int32_t height);
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
private:
void framebufferSetup(std::shared_ptr<Framebuffer> framebuffer);
void framebufferTeardown(std::shared_ptr<Framebuffer> framebuffer);
void renderGeometry();
void renderSkybox();
void renderLightCubes();
void renderOverlay();
std::shared_ptr<Framebuffer> m_framebuffer;
std::shared_ptr<Framebuffer> m_screenFramebuffer;
std::shared_ptr<entt::registry> m_registry;
};

6
src/inferno/window.cpp

@ -122,17 +122,17 @@ void Window::initialize()
switch (action) {
case GLFW_PRESS: {
KeyPressEvent event(key);
KeyPressEvent event(key, mods);
w.m_eventCallback(event);
break;
}
case GLFW_RELEASE: {
KeyReleaseEvent event(key);
KeyReleaseEvent event(key, mods);
w.m_eventCallback(event);
break;
}
case GLFW_REPEAT: {
KeyRepeatEvent event(key);
KeyRepeatEvent event(key, mods);
w.m_eventCallback(event);
break;
}

Loading…
Cancel
Save