Riyyi
6 months ago
5 changed files with 215 additions and 0 deletions
@ -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
|
Loading…
Reference in new issue