Browse Source

Asset+Component+Render: Fix assimp modeling loading + batching

master
Riyyi 4 months ago
parent
commit
5f6a5f48dd
  1. 7
      src/inferno/application.cpp
  2. 2
      src/inferno/application.h
  3. 42
      src/inferno/asset/model.cpp
  4. 9
      src/inferno/asset/model.h
  5. 2
      src/inferno/component/model-component.cpp
  6. 2
      src/inferno/component/model-component.h
  7. 347
      src/inferno/render/gltf.cpp
  8. 176
      src/inferno/render/gltf.h
  9. 2
      src/inferno/render/render-command.cpp
  10. 46
      src/inferno/render/renderer.cpp
  11. 2
      src/inferno/render/renderer.h

7
src/inferno/application.cpp

@ -16,8 +16,6 @@
#include "inferno/event/event.h" #include "inferno/event/event.h"
#include "inferno/event/keyevent.h" #include "inferno/event/keyevent.h"
#include "inferno/event/mouseevent.h" #include "inferno/event/mouseevent.h"
// #include "inferno/io/gltffile.h"
#include "inferno/asset/font.h"
#include "inferno/io/input.h" #include "inferno/io/input.h"
#include "inferno/keycodes.h" #include "inferno/keycodes.h"
#include "inferno/render/buffer.h" #include "inferno/render/buffer.h"
@ -55,8 +53,6 @@ Application::Application()
m_scene = std::make_shared<Scene>(); m_scene = std::make_shared<Scene>();
m_scene->initialize(); m_scene->initialize();
// Load assets
// m_font = FontManager::the().load("assets/fnt/dejavu-sans"); // m_font = FontManager::the().load("assets/fnt/dejavu-sans");
// auto bla = GlTFFile::read("assets/gltf/box.glb"); // auto bla = GlTFFile::read("assets/gltf/box.glb");
@ -145,12 +141,13 @@ int Application::run()
// offset // offset
#endif #endif
// m_window->setVSync(false);
while (!m_window->shouldClose()) { while (!m_window->shouldClose()) {
float time = Time::time(); float time = Time::time();
float deltaTime = time - m_lastFrameTime; float deltaTime = time - m_lastFrameTime;
m_lastFrameTime = time; m_lastFrameTime = time;
// ruc::debug("Frametime " << deltaTime * 1000 << "ms"); // ruc::debug("Frametime {}ms", deltaTime * 1000);
// Update // Update

2
src/inferno/application.h

@ -8,8 +8,6 @@
#include <memory> // std::unique_ptr, std::shared_ptr #include <memory> // std::unique_ptr, std::shared_ptr
#include "ruc/singleton.h"
namespace Inferno { namespace Inferno {
class Event; class Event;

42
src/inferno/asset/model.cpp

@ -8,10 +8,10 @@
#include <memory> // std::shared_ptr #include <memory> // std::shared_ptr
#include "assimp/Importer.hpp" #include "assimp/Importer.hpp"
#include "assimp/mesh.h"
#include "assimp/postprocess.h" #include "assimp/postprocess.h"
#include "assimp/scene.h" #include "assimp/scene.h"
#include "assimp/texture.h" #include "assimp/texture.h"
#include "ruc/format/log.h"
#include "inferno/asset/model.h" #include "inferno/asset/model.h"
#include "inferno/asset/texture.h" #include "inferno/asset/texture.h"
@ -23,6 +23,7 @@ std::shared_ptr<Model> Model::create(std::string_view path)
auto result = std::shared_ptr<Model>(new Model(path)); auto result = std::shared_ptr<Model>(new Model(path));
Assimp::Importer importer; // importer destructor uses RAII cleanup Assimp::Importer importer; // importer destructor uses RAII cleanup
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
const aiScene* scene = importer.ReadFile( const aiScene* scene = importer.ReadFile(
path.data(), path.data(),
aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
@ -51,7 +52,7 @@ void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScen
{ {
for (uint32_t i = 0; i < node->mNumMeshes; ++i) { for (uint32_t i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(model, mesh, scene); processMesh(model, mesh, scene, node->mTransformation);
} }
for (uint32_t i = 0; i < node->mNumChildren; ++i) { for (uint32_t i = 0; i < node->mNumChildren; ++i) {
@ -59,52 +60,51 @@ void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScen
} }
} }
void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene) void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform)
{ {
VERIFY(mesh->HasPositions(), "malformed model"); VERIFY(mesh->HasPositions(), "malformed model");
VERIFY(mesh->HasNormals(), "malformed model"); VERIFY(mesh->HasNormals(), "malformed model");
// Pre-allocate memory // Pre-allocate memory
model->m_vertices = std::vector<Vertex>(mesh->mNumVertices); size_t startIndex = model->m_vertices.size();
model->m_vertices.resize(startIndex + mesh->mNumVertices);
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) { for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D v = mesh->mVertices[i]; aiVector3D v = parentTransform * mesh->mVertices[i];
model->m_vertices[i].position = { v.x, v.y, v.z }; model->m_vertices[startIndex + i].position = { v.x, v.y, v.z };
} }
// Size of vertices == size of normals // Size of vertices == size of normals
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) { for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D normal = mesh->mNormals[i]; aiVector3D normal = mesh->mNormals[startIndex + i];
model->m_vertices[i].normal = { normal.x, normal.y, normal.x }; model->m_vertices[startIndex + i].normal = { normal.x, normal.y, normal.z };
} }
if (mesh->HasTextureCoords(0)) { if (mesh->HasTextureCoords(0)) {
// Size of vertices == size of texture coordinates // Size of vertices == size of texture coordinates
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) { for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D tc = mesh->mTextureCoords[0][i]; aiVector3D tc = mesh->mTextureCoords[0][i];
model->m_vertices[i].textureCoordinates = { tc.x, tc.y }; model->m_vertices[startIndex + i].textureCoordinates = { tc.x, tc.y };
} }
} }
// TODO: position in the texture atlas // TODO: position in the texture atlas
if (mesh->HasFaces()) { if (mesh->HasFaces()) {
// Pre-allocate memory
size_t startIndex2 = model->m_elements.size();
model->m_elements.resize(startIndex2 + (mesh->mNumFaces * 3)); // assuming triangles
size_t offset = 0;
for (uint32_t i = 0; i < mesh->mNumFaces; ++i) { for (uint32_t i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i]; aiFace face = mesh->mFaces[i];
for (uint32_t j = 0; j < face.mNumIndices; ++j) { VERIFY(face.mNumIndices == 3, "unsupported face type: {}", face.mNumIndices);
model->m_elements.push_back(face.mIndices[j]); for (uint32_t j = 0; j < face.mNumIndices; ++j, ++offset) {
// Indices are referenced relative to vertices[0], if there are multiple meshes,
// then the indices need to be offset by the total amount of vertices
model->m_elements[startIndex2 + (i * 3) + j] = startIndex + 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 } // namespace Inferno

9
src/inferno/asset/model.h

@ -39,7 +39,7 @@ private:
static void processScene(std::shared_ptr<Model> model, const aiScene* scene); 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 processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene);
static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene); static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform = aiMatrix4x4());
virtual bool isModel() const override { return true; } virtual bool isModel() const override { return true; }
@ -50,4 +50,11 @@ private:
std::shared_ptr<Texture2D> m_texture; std::shared_ptr<Texture2D> m_texture;
}; };
// clang-format off
template<>
inline bool Asset::fastIs<Model>() const { return isModel(); }
// clang-format on
} // namespace Inferno } // namespace Inferno
// TODO: figure out this weird thing: https://github.com/assimp/assimp/blob/master/BUILDBINARIES_EXAMPLE.bat#L6-L10

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

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 Riyyi * Copyright (C) 2022 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

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

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 Riyyi * Copyright (C) 2022 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

347
src/inferno/render/gltf.cpp

@ -1,347 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#if 0
#include <algorithm> // std::copy
#include <utility> // std::move
#include "nlohmann/json.hpp"
#include "ruc/meta/assert.h"
#include "inferno/io/file.h"
#include "inferno/io/gltffile.h"
#include "inferno/io/log.h"
#include "inferno/render/gltf.h"
#include "inferno/util/integer.h"
namespace Inferno {
Gltf::Gltf(const std::string& path)
: m_path(std::move(path))
{
auto gltf = GltfFile::read(path);
VERIFY(gltf.first, "Gltf model invalid JSON content '{}'", path);
json json = json::parse(gltf.first.get());
// Add binary data from .glb files
if (gltf.second) {
m_model.data.emplace(0, std::move(gltf.second));
}
// Properties
// Asset
// ---------------------------------
VERIFY(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'");
auto asset = json["asset"];
VERIFY(asset.is_object(), "Gltf model invalid property type 'asset'");
parseAsset(&m_model.asset, asset);
// Scene
// ---------------------------------
if (Json::hasProperty(json, "scenes")) {
auto scenes = json["scenes"];
VERIFY(scenes.is_array(), "Gltf model invalid property type 'scenes'");
for (auto& [key, object] : scenes.items()) {
glTF::Scene scene;
parseScene(&scene, key, object);
m_model.scenes.emplace_back(std::move(scene));
}
}
// Node
// ---------------------------------
if (Json::hasProperty(json, "nodes")) {
auto nodes = json["nodes"];
VERIFY(nodes.is_array(), "Gltf model invalid property type 'nodes'");
for (auto& [key, object] : nodes.items()) {
glTF::Node node;
parseNode(&node, key, object);
m_model.nodes.emplace_back(std::move(node));
}
}
// Mesh
// ---------------------------------
if (Json::hasProperty(json, "meshes")) {
auto meshes = json["meshes"];
VERIFY(meshes.is_array(), "Gltf model invalid property type 'meshes'");
for (auto& [key, object] : meshes.items()) {
glTF::Mesh mesh;
parseMesh(&mesh, key, object);
m_model.meshes.emplace_back(std::move(mesh));
}
}
// Accessor
// ---------------------------------
if (Json::hasProperty(json, "accessors")) {
auto accessors = json["accessors"];
VERIFY(accessors.is_array(), "Gltf model invalid property type 'accessors'");
for (auto& [key, object] : accessors.items()) {
glTF::Accessor accessor;
parseAccessor(&accessor, key, object);
m_model.accessors.emplace_back(std::move(accessor));
}
}
// Bufferview
// ---------------------------------
if (Json::hasProperty(json, "bufferViews")) {
auto bufferViews = json["bufferViews"];
VERIFY(bufferViews.is_array(), "Gltf model invalid property type 'bufferViews'");
for (auto& [key, object] : bufferViews.items()) {
glTF::BufferView bufferView;
parseBufferView(&bufferView, key, object);
m_model.bufferViews.emplace_back(std::move(bufferView));
}
}
// Buffer
// ---------------------------------
if (Json::hasProperty(json, "buffers")) {
auto buffers = json["buffers"];
VERIFY(buffers.is_array(), "Gltf model invalid property type 'buffers'");
for (auto& [key, object] : buffers.items()) {
glTF::Buffer buffer;
parseBuffer(&buffer, key, object, &m_model.data);
m_model.buffers.emplace_back(std::move(buffer));
}
}
}
Gltf::~Gltf()
{
}
void Gltf::parseAsset(glTF::Asset* asset, const json& object)
{
auto copyright = Json::parseStringProperty(object, "copyright", false);
auto generator = Json::parseStringProperty(object, "generator", false);
auto version = Json::parseStringProperty(object, "version", true);
VERIFY(version, "GlTF model missing required property 'version'");
VERIFY(version.value().compare("2.0") == 0, "GlTF version unsupported '{}'", version.value());
auto minVersion = Json::parseStringProperty(object, "minVersion", false);
if (copyright) asset->copyright = copyright.value();
if (generator) asset->generator = generator.value();
if (version) asset->version = version.value();
if (minVersion) asset->minVersion = minVersion.value();
}
void Gltf::parseScene(glTF::Scene* scene, const std::string& key, const json& object)
{
auto nodes = Json::parseFloatArrayProperty(object, "nodes", false);
VERIFY(!nodes || nodes.value().size() > 0, "Gltf scene '{}' empty 'nodes' property", key);
auto name = Json::parseStringProperty(object, "name", false);
scene->name = name ? name.value() : key;
}
void Gltf::parseNode(glTF::Node* node, const std::string& key, const json& object)
{
auto camera = Json::parseUnsignedProperty(object, "camera", false);
auto children = Json::parseUnsignedArrayProperty(object, "children", false);
VERIFY(!children || children.value().size() > 0, "Gltf node '{}' empty property 'children'", key);
auto skin = Json::parseUnsignedProperty(object, "skin", false);
auto matrix = Json::parseFloatArrayProperty(object, "matrix", false);
VERIFY(!matrix || matrix.value().size() == 16, "Gltf node '{}' property 'matrix' invalid size", key);
auto mesh = Json::parseUnsignedProperty(object, "mesh", false);
auto rotation = Json::parseFloatArrayProperty(object, "rotation", false);
VERIFY(!rotation || rotation.value().size() == 4, "Gltf node '{}' property 'rotation' invalid size", key);
auto scale = Json::parseFloatArrayProperty(object, "scale", false);
VERIFY(!scale || scale.value().size() == 3, "Gltf node '{}' property 'scale' invalid size", key);
auto translation = Json::parseFloatArrayProperty(object, "translation", false);
VERIFY(!translation || translation.value().size() == 3, "Gltf node '{}' property 'translation' invalid size", key);
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf node '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (camera) node->camera = camera.value();
if (children) node->children = children.value();
if (skin) node->skin = skin.value();
if (matrix) std::copy(matrix.value().begin(), matrix.value().end(), node->matrix.begin());
if (mesh) node->mesh = mesh.value();
if (rotation) std::copy(rotation.value().begin(), rotation.value().end(), node->rotation.begin());
if (scale) std::copy(scale.value().begin(), scale.value().end(), node->scale.begin());
if (translation) std::copy(translation.value().begin(), translation.value().end(), node->translation.begin());
if (weights) node->weights = weights.value();
node->name = name ? name.value() : key;
}
void Gltf::parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object)
{
auto attributes = Json::parseUnsignedObjectProperty(object, "attributes", true);
VERIFY(attributes && attributes.value().size() > 0, "Gltf primitive '{}' empty property 'attributes'", key);
auto indices = Json::parseUnsignedProperty(object, "indices", false);
auto material = Json::parseUnsignedProperty(object, "material", false);
auto mode = Json::parseUnsignedProperty(object, "mode", false);
if (Json::hasProperty(object, "targets")) {
auto targets = object["targets"];
VERIFY(targets.is_array(), "Gltf primitive '{}' property 'targets' invalid type", key);
for (auto& targetObject : targets) {
std::map<std::string, uint32_t> target;
for (auto& [key, propertyValue] : targetObject.items()) {
auto value = Json::getPropertyValue<uint32_t>(propertyValue, json::value_t::number_unsigned);
if (value) target.emplace(std::move(key), value.value());
}
VERIFY(target.size() > 0, "Gltf primitive '{}' empty 'target' object", key);
primitive->targets.emplace_back(std::move(target));
}
}
if (attributes) primitive->attributes = attributes.value();
if (indices) primitive->indices = indices.value();
if (material) primitive->material = material.value();
if (mode) primitive->mode = static_cast<unsigned char>(mode.value());
}
void Gltf::parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object)
{
VERIFY(Json::hasProperty(object, "primitives"), "Gltf mesh '{}' missing required property 'primitives'", key);
auto primitives = object["primitives"];
VERIFY(primitives.is_array(), "Gltf mesh '{}' property 'primitives' invalid type", key);
for (auto& primitiveObject : primitives) {
glTF::Primitive primitive;
parsePrimitive(&primitive, key, primitiveObject);
mesh->primitives.emplace_back(std::move(primitive));
}
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf mesh '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (weights) mesh->weights = weights.value();
mesh->name = name ? name.value() : key;
}
void Gltf::parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object)
{
auto bufferView = Json::parseUnsignedProperty(object, "bufferView", false);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto componentType = Json::parseUnsignedProperty(object, "componentType", true);
VERIFY(componentType, "Gltf accessor '{}' missing required property 'componentType'", key);
auto normalized = Json::parseBoolProperty(object, "normalized", false);
auto count = Json::parseUnsignedProperty(object, "count", true);
VERIFY(count, "Gltf accessor '{}' missing required property 'count'", key);
auto type = Json::parseStringProperty(object, "type", true);
VERIFY(type, "Gltf accessor '{}' missing required property 'type'", key);
auto max = Json::parseFloatArrayProperty(object, "max", false);
VERIFY(!max || max.value().size() > 0, "Gltf accessor '{}' empty property 'max'", key);
auto min = Json::parseFloatArrayProperty(object, "min", false);
VERIFY(!min || min.value().size() > 0, "Gltf accessor '{}' empty property 'min'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (bufferView) accessor->bufferView = bufferView.value();
if (byteOffset) accessor->byteOffset = byteOffset.value();
if (normalized) accessor->normalized = normalized.value();
if (count) accessor->count = count.value();
if (type) accessor->type = type.value();
if (max) accessor->max = max.value();
if (min) accessor->min = min.value();
accessor->name = name ? name.value() : key;
}
void Gltf::parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object)
{
auto buffer = Json::parseUnsignedProperty(object, "buffer", false);
VERIFY(buffer, "Gltf bufferView '{}' missing required property 'buffer'", key);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf bufferView '{}' missing required property 'byteLength'", key);
auto byteStride = Json::parseUnsignedProperty(object, "byteStride", false);
auto target = Json::parseUnsignedProperty(object, "target", false);
auto name = Json::parseStringProperty(object, "name", false);
if (buffer) bufferView->buffer = buffer.value();
if (byteOffset) bufferView->byteOffset = byteOffset.value();
if (byteLength) bufferView->byteLength = byteLength.value();
if (byteStride) bufferView->byteStride = byteStride.value();
if (target) bufferView->target = target.value();
bufferView->name = name ? name.value() : key;
}
void Gltf::parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data)
{
auto uri = Json::parseStringProperty(object, "uri", false);
// Load external binary data
if (uri) {
VERIFY(uri.value().find("data:", 0) == std::string::npos, "GltfFile embedded binary data is unsupported");
data->emplace(std::stou(key), File::raw("assets/gltf/" + uri.value()));
}
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf buffer '{}' missing required property 'byteLength'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (uri) buffer->uri = uri.value();
if (byteLength) buffer->byteLength = byteLength.value();
buffer->name = name ? name.value() : key;
}
} // namespace Inferno
#endif

176
src/inferno/render/gltf.h

@ -1,176 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#if 0
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
#include "ruc/singleton.h"
#include "inferno/util/json.h"
#define GLTF_TYPE_SCALAR 1
#define GLTF_TYPE_VEC2 2
#define GLTF_TYPE_VEC3 3
#define GLTF_TYPE_VEC4 4
#define GLTF_TYPE_MAT2 8
#define GLTF_TYPE_MAT3 12
#define GLTF_TYPE_MAT4 16
#define GLTF_COMPONENT_TYPE_BYTE 5120
#define GLTF_COMPONENT_TYPE_UNSIGNED_BYTE 5121
#define GLTF_COMPONENT_TYPE_SHORT 5122
#define GLTF_COMPONENT_TYPE_UNSIGNED_SHORT 5123
#define GLTF_COMPONENT_TYPE_INT 5124
#define GLTF_COMPONENT_TYPE_UNSIGNED_INT 5125
#define GLTF_COMPONENT_TYPE_FLOAT 5126
#define GLTF_TARGET_ARRAY_BUFFER 34962
#define GLTF_TARGET_ELEMENT_ARRAY_BUFFER 34963
namespace Inferno {
namespace glTF {
// Type specifications
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#objects
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#asset
struct Asset {
std::string copyright;
std::string generator;
std::string version; // Required
std::string minVersion;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
struct Scene {
std::vector<uint32_t> nodes;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
struct Node {
uint32_t camera;
std::vector<uint32_t> children;
uint32_t skin;
std::array<float, 16> matrix { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // Identity matrix
uint32_t mesh;
std::array<float, 4> rotation { 0, 0, 0, 1 };
std::array<float, 3> scale { 1, 1, 1 };
std::array<float, 3> translation { 0, 0, 0 };
std::vector<float> weights;
std::string name;
};
struct Primitive {
std::map<std::string, uint32_t> attributes; // Required
uint32_t indices;
uint32_t material;
unsigned char mode { 4 };
std::vector<std::map<std::string, uint32_t>> targets;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes
struct Mesh {
std::vector<Primitive> primitives; // Required
std::vector<float> weights;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
struct Accessor {
uint32_t bufferView;
uint32_t byteOffset { 0 };
uint32_t componentType; // Required
bool normalized { false };
uint32_t count; // Required
std::string type; // Required
std::vector<float> max;
std::vector<float> min;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views
struct BufferView {
uint32_t buffer; // Required
uint32_t byteOffset { 0 };
uint32_t byteLength; // Required
uint32_t byteStride;
uint32_t target;
std::string name;
};
struct Buffer {
std::string uri;
uint32_t byteLength; // Required
std::string name;
};
struct Model {
Asset asset;
std::vector<Scene> scenes;
std::vector<Node> nodes;
std::vector<Mesh> meshes;
std::vector<Accessor> accessors;
std::vector<BufferView> bufferViews;
std::vector<Buffer> buffers;
std::map<uint32_t, std::shared_ptr<char[]>> data;
};
} // namespace glTF
// -----------------------------------------
class Gltf {
public:
Gltf(const std::string& path);
virtual ~Gltf();
inline const glTF::Model& model() const { return m_model; }
private:
static void parseAsset(glTF::Asset* asset, const json& object);
static void parseScene(glTF::Scene* scene, const std::string& key, const json& object);
static void parseNode(glTF::Node* node, const std::string& key, const json& object);
static void parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object);
static void parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object);
static void parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object);
static void parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object);
static void parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data);
std::string m_path;
glTF::Model m_model;
};
// -----------------------------------------
class GltfManager final : ruc::Singleton<GltfManager> {
public:
GltfManager(s) {}
virtual ~GltfManager() {}
void add(const std::string& path, std::shared_ptr<Gltf> gltf);
std::shared_ptr<Gltf> load(const std::string& path);
std::shared_ptr<Gltf> get(const std::string& path);
bool exists(const std::string& path);
void remove(const std::string& path);
void remove(std::shared_ptr<Gltf> gltf);
private:
std::unordered_map<std::string, std::shared_ptr<Gltf>> m_gltfList;
};
} // namespace Inferno
#endif

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

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <memory> // std::shadred_ptr #include <memory> // std::shared_ptr
#include "glad/glad.h" #include "glad/glad.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"

46
src/inferno/render/renderer.cpp

@ -129,16 +129,16 @@ void Renderer<T>::createElementBuffer()
// Generate indices // Generate indices
uint32_t* indices = new uint32_t[maxElements]; uint32_t* elements = new uint32_t[maxElements];
uint32_t offset = 0; uint32_t offset = 0;
for (uint32_t i = 0; i < maxElements; i += elementPerQuad) { for (uint32_t i = 0; i < maxElements; i += elementPerQuad) {
indices[i + 0] = offset + 0; elements[i + 0] = offset + 0;
indices[i + 1] = offset + 1; elements[i + 1] = offset + 1;
indices[i + 2] = offset + 2; elements[i + 2] = offset + 2;
indices[i + 3] = offset + 2; elements[i + 3] = offset + 2;
indices[i + 4] = offset + 3; elements[i + 4] = offset + 3;
indices[i + 5] = offset + 0; elements[i + 5] = offset + 0;
offset += vertexPerQuad; offset += vertexPerQuad;
} }
@ -147,9 +147,9 @@ void Renderer<T>::createElementBuffer()
// --------------------------------- // ---------------------------------
// Create index buffer // Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * maxElements); auto indexBuffer = std::make_shared<IndexBuffer>(elements, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer); m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices; delete[] elements;
} }
template<typename T> template<typename T>
@ -271,7 +271,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i]; m_vertexBufferPtr->color = color[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i]; m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
@ -386,7 +386,7 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) { for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4]; m_vertexBufferPtr->color = color[i % 4];
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
@ -450,7 +450,7 @@ void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQua
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position; m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color; m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates; m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex; m_vertexBufferPtr->quad.textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->width = symbolQuad[i].width; m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = symbolQuad[i].edge; m_vertexBufferPtr->edge = symbolQuad[i].edge;
@ -502,32 +502,38 @@ Renderer3D::Renderer3D(s)
ruc::info("Renderer3D initialized"); 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) void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, std::shared_ptr<Texture> texture)
{ {
// ruc::error("drawModel");
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer"); VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer");
VERIFY(indices.size() <= maxElements, "model elements too big for buffer"); VERIFY(elements.size() <= maxElements, "model elements too big for buffer");
// Create a new batch if the quad limit has been reached // Create a new batch if the quad limit has been reached
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + indices.size() > maxElements) { if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + elements.size() > maxElements) {
nextBatch(); nextBatch();
} }
uint32_t textureUnitIndex = addTextureUnit(texture); uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the vertices // Add the vertices
for (auto vertex : vertices) { for (const auto& vertex : vertices) {
m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f); m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f);
m_vertexBufferPtr->normal = vertex.normal; m_vertexBufferPtr->normal = vertex.normal;
m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates; m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates;
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
std::copy(indices.begin(), indices.end(), m_elementBufferPtr); // Copy element indices to the element buffer
m_elementBufferPtr += indices.size(); for (const auto& element : elements) {
// Indices are referenced relative to vertices[0], if there are multiple models in a batch,
// then the indices need to be offset by the total amount of vertices
*m_elementBufferPtr++ = element + m_vertexIndex;
}
m_vertexIndex += vertices.size(); m_vertexIndex += vertices.size();
m_elementIndex += indices.size(); m_elementIndex += elements.size();
} }
void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)

2
src/inferno/render/renderer.h

@ -62,6 +62,8 @@ struct Vertex {
template<typename T> template<typename T>
class Renderer { class Renderer {
public: public:
static constexpr const uint32_t vertexPerFace = 3;
static constexpr const uint32_t elementPerFace = 3;
static constexpr const uint32_t vertexPerQuad = 4; static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t elementPerQuad = 6; static constexpr const uint32_t elementPerQuad = 6;

Loading…
Cancel
Save