Asset+Component: Add model component and asset
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user