Compare commits

..

6 Commits

  1. 3
      .gitmodules
  2. 1
      README.org
  3. 49
      assets/glsl/batch-3d.frag
  4. 21
      assets/glsl/batch-3d.vert
  5. 3
      assets/glsl/batch-font.vert
  6. 3
      src/inferno/application.cpp
  7. 1
      src/inferno/asset/asset-manager.h
  8. 110
      src/inferno/asset/model.cpp
  9. 53
      src/inferno/asset/model.h
  10. 30
      src/inferno/asset/texture.cpp
  11. 3
      src/inferno/asset/texture.h
  12. 26
      src/inferno/component/model-component.cpp
  13. 25
      src/inferno/component/model-component.h
  14. 10
      src/inferno/render/buffer.cpp
  15. 8
      src/inferno/render/buffer.h
  16. 6
      src/inferno/render/render-command.cpp
  17. 6
      src/inferno/render/render-command.h
  18. 191
      src/inferno/render/renderer.cpp
  19. 60
      src/inferno/render/renderer.h
  20. 5
      src/inferno/scene/scene.cpp
  21. 11
      src/inferno/system/rendersystem.cpp
  22. 13
      vendor/CMakeLists.txt
  23. 1
      vendor/assimp

3
.gitmodules vendored

@ -19,3 +19,6 @@
[submodule "inferno/vendor/ruc"] [submodule "inferno/vendor/ruc"]
path = vendor/ruc path = vendor/ruc
url = https://github.com/Riyyi/ruc url = https://github.com/Riyyi/ruc
[submodule "inferno/vendor/assimp"]
path = vendor/assimp
url = https://github.com/assimp/assimp

1
README.org

@ -33,6 +33,7 @@ $ cmake .. && make
* Libraries * Libraries
- [[https://github.com/assimp/assimp][assimp]]
# - [[https://github.com/bulletphysics/bullet3][Bullet]] # - [[https://github.com/bulletphysics/bullet3][Bullet]]
- [[https://github.com/skypjack/entt][EnTT]] - [[https://github.com/skypjack/entt][EnTT]]
- [[https://github.com/Dav1dde/glad][glad]] - [[https://github.com/Dav1dde/glad][glad]]

49
assets/glsl/batch-3d.frag

@ -0,0 +1,49 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec3 v_normal;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = vec4(1.0f);
switch(int(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;
case 3: textureColor *= texture(u_textures[3], v_textureCoordinates); break;
case 4: textureColor *= texture(u_textures[4], v_textureCoordinates); break;
case 5: textureColor *= texture(u_textures[5], v_textureCoordinates); break;
case 6: textureColor *= texture(u_textures[6], v_textureCoordinates); break;
case 7: textureColor *= texture(u_textures[7], v_textureCoordinates); break;
case 8: textureColor *= texture(u_textures[8], v_textureCoordinates); break;
case 9: textureColor *= texture(u_textures[9], v_textureCoordinates); break;
case 10: textureColor *= texture(u_textures[10], v_textureCoordinates); break;
case 11: textureColor *= texture(u_textures[11], v_textureCoordinates); break;
case 12: textureColor *= texture(u_textures[12], v_textureCoordinates); break;
case 13: textureColor *= texture(u_textures[13], v_textureCoordinates); break;
case 14: textureColor *= texture(u_textures[14], v_textureCoordinates); break;
case 15: textureColor *= texture(u_textures[15], v_textureCoordinates); break;
case 16: textureColor *= texture(u_textures[16], v_textureCoordinates); break;
case 17: textureColor *= texture(u_textures[17], v_textureCoordinates); break;
case 18: textureColor *= texture(u_textures[18], v_textureCoordinates); break;
case 19: textureColor *= texture(u_textures[19], v_textureCoordinates); break;
case 20: textureColor *= texture(u_textures[20], v_textureCoordinates); break;
case 21: textureColor *= texture(u_textures[21], v_textureCoordinates); break;
case 22: textureColor *= texture(u_textures[22], v_textureCoordinates); break;
case 23: textureColor *= texture(u_textures[23], v_textureCoordinates); break;
case 24: textureColor *= texture(u_textures[24], v_textureCoordinates); break;
case 25: textureColor *= texture(u_textures[25], v_textureCoordinates); break;
case 26: textureColor *= texture(u_textures[26], v_textureCoordinates); break;
case 27: textureColor *= texture(u_textures[27], v_textureCoordinates); break;
case 28: textureColor *= texture(u_textures[28], v_textureCoordinates); break;
case 29: textureColor *= texture(u_textures[29], v_textureCoordinates); break;
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
color = textureColor;
}

21
assets/glsl/batch-3d.vert

@ -0,0 +1,21 @@
#version 450 core
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;
out vec3 v_normal;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
uniform mat4 u_projectionView;
void main()
{
v_normal = a_normal;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
}

3
assets/glsl/batch-font.vert

@ -33,7 +33,4 @@ void main()
v_borderColor = a_borderColor; v_borderColor = a_borderColor;
v_offset = a_offset; v_offset = a_offset;
gl_Position = vec4(a_position, 1.0f); gl_Position = vec4(a_position, 1.0f);
// Vclip = Camera projection * Camera view * Model transform * Vlocal
// gl_Position = u_projectionView * vec4(a_position, 1.0f);
} }

3
src/inferno/application.cpp

@ -80,6 +80,7 @@ Application::~Application()
RendererFont::destroy(); RendererFont::destroy();
Renderer2D::destroy(); Renderer2D::destroy();
Renderer3D::destroy();
RendererCubemap::destroy(); RendererCubemap::destroy();
RenderCommand::destroy(); RenderCommand::destroy();
AssetManager::destroy(); AssetManager::destroy();
@ -168,6 +169,7 @@ int Application::run()
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView(); std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment 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 Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
@ -175,6 +177,7 @@ int Application::run()
// RendererCharacter::the().drawCharacter(character, f->texture()); // RendererCharacter::the().drawCharacter(character, f->texture());
RendererCubemap::the().endScene(); RendererCubemap::the().endScene();
Renderer3D::the().endScene();
Renderer2D::the().endScene(); Renderer2D::the().endScene();
RendererFont::the().endScene(); RendererFont::the().endScene();

1
src/inferno/asset/asset-manager.h

@ -31,6 +31,7 @@ public:
bool fastIs() const = delete; bool fastIs() const = delete;
virtual bool isFont() const { return false; } virtual bool isFont() const { return false; }
virtual bool isModel() const { return false; }
virtual bool isShader() const { return false; } virtual bool isShader() const { return false; }
virtual bool isTexture() const { return false; } virtual bool isTexture() const { return false; }
virtual bool isTexture2D() const { return false; } virtual bool isTexture2D() const { return false; }

110
src/inferno/asset/model.cpp

@ -0,0 +1,110 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include "assimp/Importer.hpp"
#include "assimp/postprocess.h"
#include "assimp/scene.h"
#include "assimp/texture.h"
#include "ruc/format/log.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
namespace Inferno {
std::shared_ptr<Model> Model::create(std::string_view path)
{
auto result = std::shared_ptr<Model>(new Model(path));
Assimp::Importer importer; // importer destructor uses RAII cleanup
const aiScene* scene = importer.ReadFile(
path.data(),
aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
VERIFY(scene != nullptr && (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) == 0 && scene->mRootNode != nullptr,
"assimp loading file failed: {}", importer.GetErrorString());
processScene(result, scene);
processNode(result, scene->mRootNode, scene);
return result;
}
void Model::processScene(std::shared_ptr<Model> model, const aiScene* scene)
{
VERIFY(scene->HasMeshes(), "malformed model");
VERIFY(scene->mNumTextures < 2, "unsupported model type");
if (scene->mNumTextures == 1) {
aiTexture* texture = scene->mTextures[0];
model->m_texture = Texture2D::create(texture);
}
}
void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene)
{
for (uint32_t i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(model, mesh, scene);
}
for (uint32_t i = 0; i < node->mNumChildren; ++i) {
processNode(model, node->mChildren[i], scene);
}
}
void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene)
{
VERIFY(mesh->HasPositions(), "malformed model");
VERIFY(mesh->HasNormals(), "malformed model");
// Pre-allocate memory
model->m_vertices = std::vector<Vertex>(mesh->mNumVertices);
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D v = mesh->mVertices[i];
model->m_vertices[i].position = { v.x, v.y, v.z };
}
// Size of vertices == size of normals
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D normal = mesh->mNormals[i];
model->m_vertices[i].normal = { normal.x, normal.y, normal.x };
}
if (mesh->HasTextureCoords(0)) {
// Size of vertices == size of texture coordinates
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D tc = mesh->mTextureCoords[0][i];
model->m_vertices[i].textureCoordinates = { tc.x, tc.y };
}
}
// TODO: position in the texture atlas
if (mesh->HasFaces()) {
for (uint32_t i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
for (uint32_t j = 0; j < face.mNumIndices; ++j) {
model->m_elements.push_back(face.mIndices[j]);
}
}
}
ruc::debug("mesh: {:p}", mesh->mTextureCoordsNames);
// for (size_t i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
// ruc::debug("mesh: {}", mesh->mTextureCoordsNames[i]);
// }
ruc::debug("has texture: {}", scene->HasTextures());
ruc::error("asset::model vert {}", model->m_vertices.size());
ruc::error("asset::model elem {}", model->m_elements.size());
}
} // namespace Inferno

53
src/inferno/asset/model.h

@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint32_t
#include <memory>
#include <span>
#include <vector>
#include "assimp/scene.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/render/renderer.h"
namespace Inferno {
class Texture2D;
class Model final : public Asset {
public:
virtual ~Model() {}
// Factory function
static std::shared_ptr<Model> create(std::string_view path);
std::span<const Vertex> vertices() const { return m_vertices; }
std::span<const uint32_t> elements() const { return m_elements; }
std::shared_ptr<Texture2D> texture() const { return m_texture; }
private:
Model(std::string_view path)
: Asset(path)
{
}
static void processScene(std::shared_ptr<Model> model, const aiScene* scene);
static void processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene);
static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene);
virtual bool isModel() const override { return true; }
private:
std::vector<Vertex> m_vertices;
std::vector<uint32_t> m_elements;
// Some file formats embed their texture
std::shared_ptr<Texture2D> m_texture;
};
} // namespace Inferno

30
src/inferno/asset/texture.cpp

@ -8,6 +8,7 @@
#include <cstdint> // uint8_t, uint32_t #include <cstdint> // uint8_t, uint32_t
#include <memory> // std::shared_ptr #include <memory> // std::shared_ptr
#include "assimp/texture.h"
#include "glad/glad.h" #include "glad/glad.h"
#include "ruc/meta/assert.h" #include "ruc/meta/assert.h"
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
@ -56,6 +57,32 @@ std::shared_ptr<Texture2D> Texture2D::create(std::string_view path)
return result; return result;
} }
std::shared_ptr<Texture2D> Texture2D::create(aiTexture* texture)
{
auto result = std::shared_ptr<Texture2D>(new Texture2D(texture->mFilename.C_Str()));
int width = static_cast<int>(texture->mWidth);
int height = static_cast<int>(texture->mHeight);
int channels = 0;
unsigned char* data = reinterpret_cast<unsigned char*>(texture->pcData);
bool isCompressed = height == 0; // height 0 is compression, byte length stored in width variable
if (isCompressed) {
stbi_set_flip_vertically_on_load(0);
data = stbi_load_from_memory(data, width, &width, &height, &channels, STBI_default);
}
else {
channels = 4; // ARGB888
// TODO: imprement format hints? `archFormatHint'
VERIFY_NOT_REACHED();
}
result->init(width, height, channels);
result->create(data);
return result;
}
void Texture2D::bind(uint32_t unit) const void Texture2D::bind(uint32_t unit) const
{ {
// Set active unit // Set active unit
@ -198,6 +225,3 @@ void TextureCubemap::create()
} }
} // namespace Inferno } // namespace Inferno
// FIXME
// auto texture = (type == Texture::TwoDimensional) ? Texture2D::create(path) : TextureCubemap::create(path);

3
src/inferno/asset/texture.h

@ -12,6 +12,8 @@
#include "inferno/asset/asset-manager.h" #include "inferno/asset/asset-manager.h"
struct aiTexture;
namespace Inferno { namespace Inferno {
class Texture2D; class Texture2D;
@ -63,6 +65,7 @@ public:
// Factory function // Factory function
static std::shared_ptr<Texture2D> create(std::string_view path); static std::shared_ptr<Texture2D> create(std::string_view path);
static std::shared_ptr<Texture2D> create(aiTexture* texture);
virtual void bind(uint32_t unit = 0) const override; virtual void bind(uint32_t unit = 0) const override;
virtual void unbind() const override; virtual void unbind() const override;

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

@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "inferno/component/model-component.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
namespace Inferno {
void fromJson(const ruc::Json& json, ModelComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("model") && json.at("model").type() == ruc::Json::Type::String) {
value.model = AssetManager::the().load<Model>(json.at("model").asString());
}
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = AssetManager::the().load<Texture2D>(json.at("texture").asString());
}
}
} // namespace Inferno

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

@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> // std::shared_ptr
#include "ruc/json/json.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
namespace Inferno {
struct ModelComponent {
std::shared_ptr<Model> model;
std::shared_ptr<Texture2D> texture;
};
void fromJson(const ruc::Json& json, ModelComponent& value);
} // namespace Inferno

10
src/inferno/render/buffer.cpp

@ -321,6 +321,16 @@ void IndexBuffer::unbind() const
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
} }
void IndexBuffer::uploadData(const void* data, uint32_t size)
{
bind();
// Upload data to the GPU
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data);
unbind();
}
// ----------------------------------------- // -----------------------------------------
VertexArray::VertexArray() VertexArray::VertexArray()

8
src/inferno/render/buffer.h

@ -90,7 +90,7 @@ private:
// ----------------------------------------- // -----------------------------------------
// GPU memory which holds raw vertex data // GPU memory which holds raw vertex data
class VertexBuffer { class VertexBuffer { // Vertex Buffer Object, VBO
public: public:
VertexBuffer(size_t size); VertexBuffer(size_t size);
VertexBuffer(float* vertices, size_t size); VertexBuffer(float* vertices, size_t size);
@ -113,7 +113,7 @@ private:
// ----------------------------------------- // -----------------------------------------
// Vertices order of rendering // Vertices order of rendering
class IndexBuffer { class IndexBuffer { // Element Buffer Object, EBO
public: public:
IndexBuffer(uint32_t* indices, size_t size); IndexBuffer(uint32_t* indices, size_t size);
~IndexBuffer(); ~IndexBuffer();
@ -121,6 +121,8 @@ public:
void bind() const; void bind() const;
void unbind() const; void unbind() const;
void uploadData(const void* data, uint32_t size);
uint32_t getCount() const { return m_count; } uint32_t getCount() const { return m_count; }
private: private:
@ -131,7 +133,7 @@ private:
// ----------------------------------------- // -----------------------------------------
// Array that holds the vertex attributes configuration // Array that holds the vertex attributes configuration
class VertexArray { class VertexArray { // Vertex Array Object, VAO
public: public:
VertexArray(); VertexArray();
~VertexArray(); ~VertexArray();

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

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #include <memory> // std::shadred_ptr
#include "glad/glad.h" #include "glad/glad.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
@ -39,9 +39,9 @@ void RenderCommand::clearColor(const glm::vec4& color)
glClearColor(color.r, color.g, color.b, color.a); glClearColor(color.r, color.g, color.b, color.a);
} }
void RenderCommand::drawIndexed(const VertexArray& vertexArray, uint32_t indexCount) 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->getIndexBuffer()->getCount();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
} }

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

@ -6,7 +6,9 @@
#pragma once #pragma once
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include <memory> // std::shadred_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
namespace Inferno { namespace Inferno {
@ -19,7 +21,7 @@ public:
static void clear(); static void clear();
static void clearColor(const glm::vec4& color); static void clearColor(const glm::vec4& color);
static void drawIndexed(const VertexArray& vertexArray, uint32_t indexCount = 0); static void drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount = 0);
static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height);
static void setDepthTest(bool enabled); static void setDepthTest(bool enabled);

191
src/inferno/render/renderer.cpp

@ -4,8 +4,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <algorithm> // std::min #include <algorithm> // std::copy, std::min
#include <utility> // std::move #include <span>
#include "glad/glad.h" #include "glad/glad.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
@ -61,32 +61,7 @@ void Renderer<T>::initialize()
// Create vertex array // Create vertex array
m_vertexArray = std::make_shared<VertexArray>(); m_vertexArray = std::make_shared<VertexArray>();
// CPU createElementBuffer();
// ---------------------------------
// Generate indices
uint32_t* indices = new uint32_t[maxIndices];
uint32_t offset = 0;
for (uint32_t i = 0; i < maxIndices; i += indexPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// GPU
// ---------------------------------
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * maxIndices);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
} }
template<typename T> template<typename T>
@ -146,24 +121,56 @@ void Renderer<T>::unbind()
m_shader->unbind(); m_shader->unbind();
} }
template<typename T>
void Renderer<T>::createElementBuffer()
{
// CPU
// ---------------------------------
// Generate indices
uint32_t* indices = new uint32_t[maxElements];
uint32_t offset = 0;
for (uint32_t i = 0; i < maxElements; i += elementPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// GPU
// ---------------------------------
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
}
template<typename T> template<typename T>
void Renderer<T>::flush() void Renderer<T>::flush()
{ {
if (m_quadIndex == 0) { if (m_vertexIndex == 0 || m_elementIndex == 0) {
return; return;
} }
// Upload index data to GPU
uploadElementBuffer();
// Upload vertex data to GPU // Upload vertex data to GPU
m_vertexArray->at(0)->uploadData( m_vertexArray->at(0)->uploadData(m_vertexBufferBase, m_vertexIndex * sizeof(T));
m_vertexBufferBase,
m_quadIndex * vertexPerQuad * sizeof(T));
bind(); bind();
// Render // Render
bool depthTest = RenderCommand::depthTest(); bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(m_enableDepthBuffer); RenderCommand::setDepthTest(m_enableDepthBuffer);
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad); RenderCommand::drawIndexed(m_vertexArray, m_elementIndex);
RenderCommand::setDepthTest(depthTest); RenderCommand::setDepthTest(depthTest);
unbind(); unbind();
@ -172,7 +179,8 @@ void Renderer<T>::flush()
template<typename T> template<typename T>
void Renderer<T>::startBatch() void Renderer<T>::startBatch()
{ {
m_quadIndex = 0; m_vertexIndex = 0;
m_elementIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase; m_vertexBufferPtr = m_vertexBufferBase;
m_textureSlotIndex = 1; m_textureSlotIndex = 1;
@ -245,7 +253,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color,
void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture) void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture)
{ {
// Create a new batch if the quad limit has been reached // Create a new batch if the quad limit has been reached
if (m_quadIndex >= maxQuads) { if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) {
nextBatch(); nextBatch();
} }
@ -267,7 +275,8 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
m_quadIndex++; m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
} }
void Renderer2D::loadShader() void Renderer2D::loadShader()
@ -366,7 +375,8 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture) void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture)
{ {
// Create a new batch if the quad limit has been reached // Create a new batch if the quad limit has been reached
if (m_quadIndex >= maxQuads) { if (m_vertexIndex + (vertexPerQuad * quadPerCube) > maxVertices
|| m_elementIndex + (elementPerQuad * quadPerCube) > maxElements) {
nextBatch(); nextBatch();
} }
@ -380,7 +390,8 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
m_quadIndex += quadPerCube; m_vertexIndex += vertexPerQuad * quadPerCube;
m_elementIndex += elementPerQuad * quadPerCube;
} }
void RendererCubemap::loadShader() void RendererCubemap::loadShader()
@ -428,7 +439,7 @@ RendererFont::RendererFont(s)
void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture) void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture)
{ {
// Create a new batch if the quad limit has been reached // Create a new batch if the quad limit has been reached
if (m_quadIndex >= maxQuads) { if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) {
nextBatch(); nextBatch();
} }
@ -451,7 +462,8 @@ void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQua
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
m_quadIndex++; m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
} }
void RendererFont::loadShader() void RendererFont::loadShader()
@ -459,4 +471,103 @@ void RendererFont::loadShader()
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-font"); m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-font");
} }
// -----------------------------------------
Renderer3D::Renderer3D(s)
{
Renderer::initialize();
// CPU
// ---------------------------------
// Create array for storing quads vertices
m_vertexBufferBase = new Vertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase;
// GPU
// ---------------------------------
m_enableDepthBuffer = true;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(Vertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec3, "a_normal" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer3D initialized");
}
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> texture)
{
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer");
VERIFY(indices.size() <= maxElements, "model elements too big for buffer");
// Create a new batch if the quad limit has been reached
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + indices.size() > maxElements) {
nextBatch();
}
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the vertices
for (auto vertex : vertices) {
m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f);
m_vertexBufferPtr->normal = vertex.normal;
m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates;
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex;
m_vertexBufferPtr++;
}
std::copy(indices.begin(), indices.end(), m_elementBufferPtr);
m_elementBufferPtr += indices.size();
m_vertexIndex += vertices.size();
m_elementIndex += indices.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()
{
// CPU
// ---------------------------------
// Create array for storing quads vertices
m_elementBufferBase = new uint32_t[maxElements];
m_elementBufferPtr = m_elementBufferBase;
// GPU
// ---------------------------------
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(m_elementBufferBase, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
}
void Renderer3D::uploadElementBuffer()
{
m_vertexArray->getIndexBuffer()->uploadData(m_elementBufferBase, m_elementIndex * sizeof(uint32_t));
}
void Renderer3D::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-3d");
}
void Renderer3D::startBatch()
{
Renderer<Vertex>::startBatch();
m_elementBufferPtr = m_elementBufferBase;
}
} // namespace Inferno } // namespace Inferno

60
src/inferno/render/renderer.h

@ -8,6 +8,7 @@
#include <cstdint> // int32_t, uint32_t #include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique #include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique
#include <span>
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float2.hpp" // glm::vec2 #include "glm/ext/vector_float2.hpp" // glm::vec2
@ -49,21 +50,27 @@ struct SymbolVertex {
float offset = 0.0f; 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;
};
// ------------------------------------- // -------------------------------------
template<typename T> template<typename T>
class Renderer { class Renderer {
public: public:
static constexpr const uint32_t vertexPerQuad = 4; static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t indexPerQuad = 6; static constexpr const uint32_t elementPerQuad = 6;
static constexpr const uint32_t quadPerCube = 6;
// When to start a new batch // When to start a new batch
static constexpr const uint32_t maxQuads = 20000; static constexpr const uint32_t maxVertices = 60000;
static constexpr const uint32_t maxVertices = maxQuads * vertexPerQuad; static constexpr const uint32_t maxElements = 60000;
static constexpr const uint32_t maxIndices = maxQuads * indexPerQuad;
static constexpr const uint32_t maxTextureSlots = 32; static constexpr const uint32_t maxTextureSlots = 32;
public:
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView); virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView);
virtual void endScene(); virtual void endScene();
@ -79,6 +86,8 @@ protected:
void bind(); void bind();
void unbind(); void unbind();
virtual void createElementBuffer();
virtual void uploadElementBuffer() {}
virtual void loadShader() = 0; virtual void loadShader() = 0;
virtual void flush(); virtual void flush();
virtual void startBatch(); virtual void startBatch();
@ -86,7 +95,8 @@ protected:
protected: protected:
// CPU quad vertices // CPU quad vertices
uint32_t m_quadIndex { 0 }; uint32_t m_vertexIndex { 0 };
uint32_t m_elementIndex { 0 };
T* m_vertexBufferBase { nullptr }; T* m_vertexBufferBase { nullptr };
T* m_vertexBufferPtr { nullptr }; T* m_vertexBufferPtr { nullptr };
@ -102,7 +112,7 @@ protected:
}; };
// TOOD: // TOOD:
// - Deduplicate flush() // v Deduplicate flush()
// v Add bool for disabling depth buffer // v Add bool for disabling depth buffer
// - Add Size for uploadData (this is prob not needed, we got T already) // - Add Size for uploadData (this is prob not needed, we got T already)
// - Decide if its worth to remove template<T> from Renderer, just cast vertexBufferPtr before usage // - Decide if its worth to remove template<T> from Renderer, just cast vertexBufferPtr before usage
@ -114,7 +124,7 @@ class Renderer2D final
, public ruc::Singleton<Renderer2D> { , public ruc::Singleton<Renderer2D> {
public: public:
Renderer2D(s); Renderer2D(s);
virtual ~Renderer2D() {}; virtual ~Renderer2D() = default;
using Singleton<Renderer2D>::destroy; using Singleton<Renderer2D>::destroy;
@ -136,9 +146,12 @@ private:
class RendererCubemap final class RendererCubemap final
: public Renderer<CubemapVertex> : public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> { , public ruc::Singleton<RendererCubemap> {
public:
static constexpr const uint32_t quadPerCube = 6;
public: public:
RendererCubemap(s); RendererCubemap(s);
virtual ~RendererCubemap() {}; virtual ~RendererCubemap() = default;
using Singleton<RendererCubemap>::destroy; using Singleton<RendererCubemap>::destroy;
@ -161,7 +174,7 @@ class RendererFont final
, public ruc::Singleton<RendererFont> { , public ruc::Singleton<RendererFont> {
public: public:
RendererFont(s); RendererFont(s);
virtual ~RendererFont() {}; virtual ~RendererFont() = default;
using Singleton<RendererFont>::destroy; using Singleton<RendererFont>::destroy;
@ -171,4 +184,31 @@ private:
void loadShader() override; void loadShader() override;
}; };
// -------------------------------------
class Renderer3D final
: public Renderer<Vertex>
, public ruc::Singleton<Renderer3D> {
public:
Renderer3D(s);
virtual ~Renderer3D() = default;
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);
private:
void createElementBuffer() override;
void uploadElementBuffer() override;
void loadShader() override;
void startBatch() override;
private:
// CPU element vertices
uint32_t* m_elementBufferBase { nullptr };
uint32_t* m_elementBufferPtr { nullptr };
};
} // namespace Inferno } // namespace Inferno

5
src/inferno/scene/scene.cpp

@ -19,6 +19,7 @@
#include "inferno/component/cubemap-component.h" #include "inferno/component/cubemap-component.h"
#include "inferno/component/id-component.h" #include "inferno/component/id-component.h"
#include "inferno/component/luascriptcomponent.h" #include "inferno/component/luascriptcomponent.h"
#include "inferno/component/model-component.h"
#include "inferno/component/nativescriptcomponent.h" #include "inferno/component/nativescriptcomponent.h"
#include "inferno/component/spritecomponent.h" #include "inferno/component/spritecomponent.h"
#include "inferno/component/tagcomponent.h" #include "inferno/component/tagcomponent.h"
@ -166,6 +167,10 @@ uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
auto& text = addComponent<TextAreaComponent>(entity); auto& text = addComponent<TextAreaComponent>(entity);
components.at("text").getTo(text); components.at("text").getTo(text);
} }
if (components.exists("model")) {
auto& text = addComponent<ModelComponent>(entity);
components.at("model").getTo(text);
}
if (components.exists("children")) { if (components.exists("children")) {
VERIFY(components.at("children").type() == ruc::Json::Type::Array); VERIFY(components.at("children").type() == ruc::Json::Type::Array);
const auto& children = components.at("children").asArray(); const auto& children = components.at("children").asArray();

11
src/inferno/system/rendersystem.cpp

@ -5,9 +5,11 @@
*/ */
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians #include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "inferno/component/model-component.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
#include "inferno/component/cubemap-component.h" #include "inferno/component/cubemap-component.h"
#include "inferno/component/model-component.h"
#include "inferno/component/spritecomponent.h" #include "inferno/component/spritecomponent.h"
#include "inferno/component/transformcomponent.h" #include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
@ -37,6 +39,15 @@ void RenderSystem::render()
for (auto [entity, transform, cubemap] : cubemapView.each()) { for (auto [entity, transform, cubemap] : cubemapView.each()) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture); RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
} }
auto modelView = m_registry->view<TransformComponent, ModelComponent>();
for (auto [entity, transform, model] : modelView.each()) {
Renderer3D::the().drawModel(model.model->vertices(),
model.model->elements(),
transform,
model.model->texture() ? model.model->texture() : model.texture);
}
} }
} // namespace Inferno } // namespace Inferno

13
vendor/CMakeLists.txt vendored

@ -9,6 +9,16 @@ add_subdirectory(glfw)
# Add RUC target to project # Add RUC target to project
add_subdirectory(ruc) add_subdirectory(ruc)
# assimp options
option(ASSIMP_BUILD_ASSIMP_VIEW OFF)
option(ASSIMP_BUILD_TESTS OFF)
option(ASSIMP_INSTALL OFF)
option(ASSIMP_INSTALL_PDB OFF)
option(ASSIMP_WARNINGS_AS_ERRORS OFF)
# Add assimp target to project
add_subdirectory(assimp)
# Define engine source files # Define engine source files
file(GLOB_RECURSE GLAD "glad/*.c") file(GLOB_RECURSE GLAD "glad/*.c")
file(GLOB LUA "lua/lua/*.c") file(GLOB LUA "lua/lua/*.c")
@ -19,6 +29,7 @@ set(ENGINE_SOURCES ${GLAD} ${LUA})
add_library(${ENGINE}-dependencies ${ENGINE_SOURCES}) add_library(${ENGINE}-dependencies ${ENGINE_SOURCES})
target_include_directories(${ENGINE}-dependencies PUBLIC target_include_directories(${ENGINE}-dependencies PUBLIC
"assimp/include"
"glad/include" "glad/include"
"lua") "lua")
target_link_libraries(${ENGINE}-dependencies glfw ruc ruc-test) target_link_libraries(${ENGINE}-dependencies glfw ruc ruc-test assimp)

1
vendor/assimp vendored

@ -0,0 +1 @@
Subproject commit 762ad8e9b7d8749c17dedd3e0b9a32b933949e8f
Loading…
Cancel
Save