From a266a27f88071d7f361eb7ac58ff8244e13d7437 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Mon, 13 May 2024 14:01:44 +0200 Subject: [PATCH] Asset+Component: Add model component and asset --- src/inferno/asset/asset-manager.h | 1 + src/inferno/asset/model.cpp | 110 ++++++++++++++++++++++ src/inferno/asset/model.h | 53 +++++++++++ src/inferno/component/model-component.cpp | 26 +++++ src/inferno/component/model-component.h | 25 +++++ 5 files changed, 215 insertions(+) create mode 100644 src/inferno/asset/model.cpp create mode 100644 src/inferno/asset/model.h create mode 100644 src/inferno/component/model-component.cpp create mode 100644 src/inferno/component/model-component.h diff --git a/src/inferno/asset/asset-manager.h b/src/inferno/asset/asset-manager.h index 6f8a839..1931ace 100644 --- a/src/inferno/asset/asset-manager.h +++ b/src/inferno/asset/asset-manager.h @@ -31,6 +31,7 @@ public: bool fastIs() const = delete; virtual bool isFont() const { return false; } + virtual bool isModel() const { return false; } virtual bool isShader() const { return false; } virtual bool isTexture() const { return false; } virtual bool isTexture2D() const { return false; } diff --git a/src/inferno/asset/model.cpp b/src/inferno/asset/model.cpp new file mode 100644 index 0000000..f71abf1 --- /dev/null +++ b/src/inferno/asset/model.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // uint32_t +#include // 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::create(std::string_view path) +{ + auto result = std::shared_ptr(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, 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, 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, aiMesh* mesh, const aiScene* scene) +{ + VERIFY(mesh->HasPositions(), "malformed model"); + VERIFY(mesh->HasNormals(), "malformed model"); + + // Pre-allocate memory + model->m_vertices = std::vector(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 diff --git a/src/inferno/asset/model.h b/src/inferno/asset/model.h new file mode 100644 index 0000000..46cfd9b --- /dev/null +++ b/src/inferno/asset/model.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include // uint32_t +#include +#include +#include + +#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 create(std::string_view path); + + std::span vertices() const { return m_vertices; } + std::span elements() const { return m_elements; } + std::shared_ptr texture() const { return m_texture; } + +private: + Model(std::string_view path) + : Asset(path) + { + } + + static void processScene(std::shared_ptr model, const aiScene* scene); + static void processNode(std::shared_ptr model, aiNode* node, const aiScene* scene); + static void processMesh(std::shared_ptr model, aiMesh* mesh, const aiScene* scene); + + virtual bool isModel() const override { return true; } + +private: + std::vector m_vertices; + std::vector m_elements; + // Some file formats embed their texture + std::shared_ptr m_texture; +}; + +} // namespace Inferno diff --git a/src/inferno/component/model-component.cpp b/src/inferno/component/model-component.cpp new file mode 100644 index 0000000..870c3c0 --- /dev/null +++ b/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(json.at("model").asString()); + } + if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) { + value.texture = AssetManager::the().load(json.at("texture").asString()); + } +} + +} // namespace Inferno diff --git a/src/inferno/component/model-component.h b/src/inferno/component/model-component.h new file mode 100644 index 0000000..ea4cf78 --- /dev/null +++ b/src/inferno/component/model-component.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include // 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; + std::shared_ptr texture; +}; + +void fromJson(const ruc::Json& json, ModelComponent& value); + +} // namespace Inferno