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