Files
2026-08-04 17:39:07 +01:00

298 lines
12 KiB
C++

#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<HydraGLTFActor>();
// 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<uint32_t> texture_lookup;
std::vector<uint32_t> material_lookup;
assert(scene != nullptr && "Failed to load file");
HydraECS *ecs = GetEngine()->ECS();
HydraPositionComponent position_component = {};
ecs->AddComponent<HydraPositionComponent>(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<uint32_t> material_lookup)
{
HydraActorManager *actor_manager = GetEngine()->ActorManager();
HydraID parent_actor_id = actor_manager->CreateActor<HydraGLTFActor>();
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<HydraPositionComponent>(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<uint32_t> material_lookup)
{
HydraActorManager *actor_manager = GetEngine()->ActorManager();
HydraID mesh_actor_id = actor_manager->CreateActor<HydraGLTFActor>();
aiMaterial *mat = scene->mMaterials[mesh->mMaterialIndex];
std::vector<StandardVertex> vertexes;
std::vector<uint32_t> indexes;
HydraECS *ecs = GetEngine()->ECS();
HydraPositionComponent position_component = {};
HydraMeshManager *mesh_manager = GetEngine()->MeshManager();
HydraID mesh_id = mesh_manager->CreateMesh();
ecs->AddComponent<HydraPositionComponent>(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<HydraMaterialComponent>(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<HydraMeshComponent>(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<HydraRenderableComponent>(mesh_actor_id, render_comp);
actor_manager->SetRelationship(parent_actor_id, mesh_actor_id);
}
void HydraGLTFLoader::_ProcessGLTFMaterials(aiScene const *scene,
std::vector<uint32_t> &texture_lookup,
std::vector<uint32_t> &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<uint32_t> &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<stbi_uc*>(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,
GL_RGBA,
GL_UNSIGNED_BYTE,
5,
data);
texture_manager->BindTexture(texture_id, GL_TEXTURE_2D);
// 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;
}
}