#include "HydraGLTFLoader.h" #include "../actor/HydraActorManager.h" #include "../actor/HydraActorManager.h" #include "../actor/HydraGLTFActor.h" #include "../engine/HydraEngine.h" #include "../ecs/HydraECS.h" #include "../ecs/components/HydraPositionComponent.h" #include "../ecs/components/HydraMeshComponent.h" #include "../ecs/components/HydraRenderableComponent.h" #include "../ecs/components/HydraMaterialComponent.h" #include "../mesh/HydraMesh.h" #include "../mesh/HydraMeshManager.h" #include "../mesh/StandardVertex.h" #include "../buffer/HydraTextureBufferManager.h" #include "../buffer/HydraMaterialBufferManager.h" #include "../buffer/HydraMaterialBuffer.h" HydraID HydraGLTFLoader::LoadGLTFFile(std::string filename) { HydraID actor_id = INVALID_HYDRA_ID; HydraActorManager *actor_manager = GetEngine()->ActorManager(); actor_id = actor_manager->CreateActor(); // Create an instance of the Importer class Assimp::Importer importer; // And have it read the given file with some example postprocessing // Usually - if speed is not the most important aspect for you - you'll // probably to request more postprocessing than we do in this example. const aiScene *scene = importer.ReadFile(filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_FlipUVs); std::vector texture_lookup; std::vector material_lookup; assert(scene != nullptr && "Failed to load file"); HydraECS *ecs = GetEngine()->ECS(); HydraPositionComponent position_component = {}; ecs->AddComponent(actor_id, position_component); _ProcessGLTFTextures(scene, texture_lookup, actor_id); _ProcessGLTFMaterials(scene, texture_lookup, material_lookup, filename); HydraID root_id = _ProcessGLTFNode(scene, scene->mRootNode, material_lookup); actor_manager->SetRelationship(actor_id, root_id); return root_id; } HydraID HydraGLTFLoader::_ProcessGLTFNode(aiScene const *scene, aiNode *parent_node, std::vector material_lookup) { HydraActorManager *actor_manager = GetEngine()->ActorManager(); HydraID parent_actor_id = actor_manager->CreateActor(); HydraECS *ecs = GetEngine()->ECS(); HydraPositionComponent position_component; aiVector3D tmp_scaling = {}; aiQuaternion tmp_rotation = {}; aiVector3D tmp_position = {}; parent_node->mTransformation.Decompose(tmp_scaling, tmp_rotation, tmp_position); glm::dvec3 scaling = glm::dvec3(tmp_scaling.x, tmp_scaling.y, tmp_scaling.z); glm::quat rotation = glm::quat(tmp_rotation.w, tmp_rotation.x, tmp_rotation.y, tmp_rotation.z); glm::dvec3 position = glm::dvec3(tmp_position.x, tmp_position.y, tmp_position.z); glm::vec3 euler_angle = glm::eulerAngles(rotation); ecs->AddComponent(parent_actor_id, position_component); actor_manager->SetScale(parent_actor_id, scaling); actor_manager->SetPosition(parent_actor_id, position); actor_manager->SetOrientation(parent_actor_id, euler_angle.x, euler_angle.y, euler_angle.z); for (uint32_t i_mesh = 0; i_mesh < parent_node->mNumMeshes; i_mesh++) { aiMesh *mesh = scene->mMeshes[parent_node->mMeshes[i_mesh]]; _ProcessGLTFMesh(scene, parent_actor_id, mesh, material_lookup); } for (uint32_t i_node = 0; i_node < parent_node->mNumChildren; i_node++) { HydraID child_actor_id = _ProcessGLTFNode(scene, parent_node->mChildren[i_node], material_lookup); actor_manager->SetRelationship(parent_actor_id, child_actor_id); } return parent_actor_id; } void HydraGLTFLoader::_ProcessGLTFMesh(aiScene const *scene, HydraID parent_actor_id, aiMesh *mesh, std::vector material_lookup) { HydraActorManager *actor_manager = GetEngine()->ActorManager(); HydraID mesh_actor_id = actor_manager->CreateActor(); aiMaterial *mat = scene->mMaterials[mesh->mMaterialIndex]; std::vector vertexes; std::vector indexes; HydraECS *ecs = GetEngine()->ECS(); HydraPositionComponent position_component = {}; HydraMeshManager *mesh_manager = GetEngine()->MeshManager(); HydraID mesh_id = mesh_manager->CreateMesh(); ecs->AddComponent(mesh_actor_id, position_component); HydraID material_id = material_lookup[mesh->mMaterialIndex]; if(material_id != INVALID_HYDRA_ID) { HydraMaterialComponent mat_comp; mat_comp.material_id = material_id; ecs->AddComponent(mesh_actor_id, mat_comp); } HydraMesh *hydra_mesh = mesh_manager->GetMesh(mesh_id); for (uint32_t i_face = 0; i_face < mesh->mNumFaces; i_face++) { aiFace face = mesh->mFaces[i_face]; for (uint32_t i_index = 0; i_index < face.mNumIndices; i_index++) { hydra_mesh->indexes.push_back(face.mIndices[i_index]); } } for (uint32_t i_verts = 0; i_verts < mesh->mNumVertices; i_verts++) { StandardVertex vert = {}; if (mesh->HasPositions()) { vert.position[0] = mesh->mVertices[i_verts].x; vert.position[1] = mesh->mVertices[i_verts].y; vert.position[2] = mesh->mVertices[i_verts].z; } if (mesh->HasNormals()) { vert.normal[0] = mesh->mNormals[i_verts].x; vert.normal[1] = mesh->mNormals[i_verts].y; vert.normal[2] = mesh->mNormals[i_verts].z; } if(mesh->HasTangentsAndBitangents()) { vert.bitanget[0] = mesh->mBitangents[i_verts].x; vert.bitanget[1] = mesh->mBitangents[i_verts].y; vert.bitanget[2] = mesh->mBitangents[i_verts].z; vert.tangent[0] = mesh->mTangents[i_verts].x; vert.tangent[1] = mesh->mTangents[i_verts].y; vert.tangent[2] = mesh->mTangents[i_verts].z; } if (mesh->HasTextureCoords(0)) { vert.text_coord0[0] = mesh->mTextureCoords[0][i_verts].x; vert.text_coord0[1] = mesh->mTextureCoords[0][i_verts].y; } vert.chunk_id[0] = mesh_actor_id; vert.chunk_id[1] = material_id; hydra_mesh->vertexes.push_back(vert); } HydraMeshComponent mesh_comp; mesh_comp.mesh_id = mesh_id; mesh_comp.mesh_state = HYDRA_STATE::Waiting; ecs->AddComponent(mesh_actor_id, mesh_comp); aiString mat_name; mat->Get(AI_MATKEY_NAME, mat_name); // Add renderable component HydraRenderableComponent render_comp; render_comp.renderable_types.set((uint32_t)HYDRA_RENDERABLE_TYPE::HYDRA_COLOUR, true); ecs->AddComponent(mesh_actor_id, render_comp); actor_manager->SetRelationship(parent_actor_id, mesh_actor_id); } void HydraGLTFLoader::_ProcessGLTFMaterials(aiScene const *scene, std::vector &texture_lookup, std::vector &material_lookup, std::string filename) { material_lookup.resize(scene->mNumMaterials); HydraTextureBufferManager *texture_manager = GetEngine()->TextureBufferManager(); for (uint32_t i_mat = 0; i_mat < scene->mNumMaterials; i_mat++) { HydraMaterialBuffer material; aiMaterial *mat = scene->mMaterials[i_mat]; aiColor3D color(0.f, 0.f, 0.f); aiString name; mat->Get(AI_MATKEY_COLOR_DIFFUSE, color); std::string mat_name = filename + std::to_string(i_mat); aiTexture diffuse_tex; uint32_t diffuse_tex_count = mat->GetTextureCount(aiTextureType::aiTextureType_DIFFUSE); uint32_t metal_tex_count = mat->GetTextureCount(aiTextureType::aiTextureType_METALNESS); uint32_t metalrough_tex_count = mat->GetTextureCount(aiTextureType::aiTextureType_GLTF_METALLIC_ROUGHNESS); uint32_t normal_tex_count = mat->GetTextureCount(aiTextureType::aiTextureType_NORMALS); uint32_t rough_tex_count = mat->GetTextureCount(aiTextureType::aiTextureType_DIFFUSE_ROUGHNESS); // have to do something here for multiple diffuse textures at some point. aiString texture_path; aiTextureMapping texture_mapping; uint32_t uv_index; if (diffuse_tex_count > 0) { mat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texture_path, &texture_mapping, &uv_index); std::string path = std::string(texture_path.C_Str()); uint32_t texture_index = atoi(path.substr(1).c_str()); HydraID texture_id = texture_lookup[texture_index]; material.diffuse_texture_id = texture_id; } if(metalrough_tex_count > 0) { mat->GetTexture(aiTextureType::aiTextureType_GLTF_METALLIC_ROUGHNESS, 0, &texture_path, &texture_mapping, &uv_index); std::string path = std::string(texture_path.C_Str()); uint32_t texture_index = atoi(path.substr(1).c_str()); HydraID texture_id = texture_lookup[texture_index]; material.metallic_rough_texture_id = texture_id; } if(normal_tex_count > 0) { mat->GetTexture(aiTextureType::aiTextureType_NORMALS, 0, &texture_path, &texture_mapping, &uv_index); std::string path = std::string(texture_path.C_Str()); uint32_t texture_index = atoi(path.substr(1).c_str()); HydraID texture_id = texture_lookup[texture_index]; material.normal_texture_id = texture_id; } material.diffuse_colour[0] = color.r; material.diffuse_colour[1] = color.g; material.diffuse_colour[2] = color.b; material.diffuse_colour[3] = 1.0f; HydraID material_id = GetEngine()->MaterialBufferManager()->CreateMaterial(material); material_lookup[i_mat] = material_id; if (material_id == INVALID_HYDRA_ID) { throw std::runtime_error("Could not create material"); } } } /// @brief Load all of the scene textures /// @param scene containing scene /// @param texture_lookup index of texture in the scene to the Hydra texture ID void HydraGLTFLoader::_ProcessGLTFTextures(aiScene const *scene, std::vector &texture_lookup, HydraID actor_id) { HydraTextureBufferManager *texture_manager = GetEngine()->TextureBufferManager(); // Make space for the texture lookups texture_lookup.resize(scene->mNumTextures); for (uint32_t i_tex = 0; i_tex < scene->mNumTextures; i_tex++) { std::string texture_name = std::to_string(actor_id) + "_" + std::to_string(i_tex); aiTexture *tex = scene->mTextures[i_tex]; // Create a holder for the texture int32_t width = 0; int32_t height = 0; int32_t channels = 0; //stbi_set_flip_vertically_on_load(1); stbi_uc* data = stbi_load_from_memory(reinterpret_cast(tex->pcData), tex->mWidth, &width, &height, &channels, 4); HydraID texture_id = texture_manager->CreateTexture(texture_name, width, height, GL_RGBA8, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 1, GL_RGBA, GL_UNSIGNED_BYTE, 5, data); texture_manager->BindTexture(texture_id); // store the texture id against the scene index for the texture. Materials will have *n where n is the index texture_lookup[i_tex] = texture_id; } }