Asset+Component+Render: Fix assimp modeling loading + batching
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+21
-21
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2024 Riyyi
|
* Copyright (C) 2022 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2024 Riyyi
|
* Copyright (C) 2022 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user