#include "CModelLoadInfo.h" #include "CBinaryFile.h" #include "CScopeExit.h" #include "U_String.h" #include "U_Numbers.h" #include "U_Log.h" #include "U_Files.h" CModelLoadInfo::CBone& CModelLoadInfo::CBone::GetParent(std::vector& bones) { auto it = std::find_if(bones.begin(), bones.end(), [this](const CBone& bone) -> bool { return bone.Name == ParentName; }); return (*it); } //BONEMATRIX calculating bind pose here void CModelLoadInfo::CBone::CalculateBind(std::vector& bones) { GlobalBind = glm::mat4x4(); auto LocalBind = BindTransform; if(ParentIndex > -1) { GlobalBind = GetParent(bones).GlobalBind * LocalBind; } else { GlobalBind = LocalBind; } /*Log::Instance() << "Bone " << Name << " local bind is:\n"; for(size_t y = 0; y < glm::mat4::length(); y++) { for(size_t x = 0; x < glm::mat4::length(); x++) { Log::Instance() << LocalBind[x][y] << " "; } } Log::Instance() << Log::Endl << Log::Endl; Log::Instance() << "Bone " << Name << " global bind is:\n"; for(size_t y = 0; y < glm::mat4::length(); y++) { for(size_t x = 0; x < glm::mat4::length(); x++) { Log::Instance() << GlobalBind[x][y] << " "; } } Log::Instance() << Log::Endl << Log::Endl;*/ } void CModelLoadInfo::Reset() { Triangles.clear(); Normals.clear(); UvCoords.clear(); Vertices.clear(); Bones.clear(); MaterialsList.clear(); } bool CModelLoadInfo::CheckIndices(const glm::u64vec3& vec, size_t limit) { return (vec.x < limit || vec.y < limit || vec.z < limit); } int CModelLoadInfo::CheckBoneValidity(CBone& bone) { if(StringUtils::IsEmpty(bone.Name)) { return 1; } if(bone.ParentIndex < -1 || bone.ParentIndex >= static_cast(Bones.size())) { return 2; } if (&bone - Bones.data() == bone.ParentIndex) { return 3; } int visitedCount = 0; int parentIndex = bone.ParentIndex; while (parentIndex != -1) { if (visitedCount++ > Bones.size()) { return 4; } if (parentIndex == (&bone - Bones.data())) { return 5; } parentIndex = Bones[parentIndex].ParentIndex; } if (bone.ParentIndex >= 0 && bone.ParentIndex >= static_cast(&bone - Bones.data())) { return 6; } return 0; } bool CModelLoadInfo::Load(const std::filesystem::path& path) { CScopeExit guard([this]() { Reset(); }); auto Error = [](const std::string& err) -> bool { Log::Errln(StringUtils::StrToWstr(err)); return false; }; auto found_path = FileUtils::find_first_by_name(FileUtils::get_executable_path() / "resources" / "models", path.string()); if(!found_path.has_value()) { return Error("Couldn't find file " + path.string()); } CBinaryFile bin; bin.OpenRead(found_path.value()); //file closes itself on ~CBinaryFile if(!bin.IsOpen()) { return Error("Couldn't open file " + found_path.value().string()); } std::uint16_t MaterialsCount, BonesCount; std::uint64_t VerticesCount, UvCoordsCount, NormalsCount, MaterialLinksCount; bin.Read(MaterialsCount); MaterialsList.reserve(MaterialsCount); //Log::Instance() << "Materials count: " << MaterialsCount << "\n"; LOGLOG for(std::uint16_t i = 0; i < MaterialsCount; i++) { std::string matname = bin.ReadString(); //Log::Instance() << "Got material name: \"" << matname << "\"\n"; LOGLOG if(!StringUtils::IsEmpty(matname)) { MaterialsList.push_back(matname); } } bin.Read(BonesCount); Bones.reserve(BonesCount); //Log::Instance() << "Bones count: " << BonesCount << "\n"; LOGLOG for(std::uint16_t i = 0; i < BonesCount; i++) { CBone Bone; bin.Read(Bone.ParentIndex); bin.ReadString(Bone.Name); //Log::Instance() << "Got bone named \"" << Bone.Name << "\" with parent index of " << Bone.ParentIndex << "\n"; LOGLOG //CTransform::CMeasurePack transform_pack; //bin.Read(transform_pack.Position); //bin.Read(transform_pack.Rotation); //bin.Read(transform_pack.Scale); bin.Read(Bone.BindTransform); //Bone.BindTransform.SetPRS(transform_pack); Bones.push_back(Bone); } for(auto& bone : Bones) { int status = CheckBoneValidity(bone); if(status != 0) { return Error("Invalid bone with the status of " + std::to_string(status) + " was detected at \"" + bone.Name + "\""); } else { if(bone.ParentIndex > -1) { bone.ParentName = Bones.at(bone.ParentIndex).Name; } bone.CalculateBind(Bones); } } bin.Read(UvCoordsCount); UvCoords.reserve(UvCoordsCount); //Log::Instance() << "Uv coords count: " << UvCoordsCount << "\n"; LOGLOG for(std::uint64_t i = 0; i < UvCoordsCount; i++) { UvCoords.push_back(bin.Read()); } bin.Read(NormalsCount); Normals.reserve(NormalsCount); //Log::Instance() << "Normals count: " << NormalsCount << "\n"; LOGLOG for(std::uint64_t i = 0; i < NormalsCount; i++) { Normals.push_back(bin.Read()); } bin.Read(VerticesCount); Vertices.reserve(VerticesCount); //Log::Instance() << "Vertices count: " << VerticesCount << "\n"; LOGLOG for(std::uint64_t i = 0; i < VerticesCount; i++) { CVertex vertex; bin.Read(vertex.Position); bin.Read(vertex.BoneCount); float TotalWeight = vertex.BoneCount == 0 ? 1.0f : 0.0f; for(std::uint8_t j = 0; j < vertex.BoneCount; j++) { CVertex::CBoneLink link; bin.Read(link.BoneIndex); bin.Read(link.Weight); if(link.BoneIndex < 0) { return Error("Vertex " + std::to_string(i) + " has invalid bone link"); } TotalWeight += link.Weight; vertex.Bones.push_back(link); } if(!almost_equal(TotalWeight, 1.0f, 0.1f)) { return Error("Bad total weight of " + std::to_string(TotalWeight) + " at vertex " + std::to_string(i)); } Vertices.push_back(vertex); } bin.Read(MaterialLinksCount); //Log::Instance() << "Material links count: " << MaterialLinksCount << "\n"; LOGLOG for(std::uint64_t i = 0; i < MaterialLinksCount; i++) { int materialIndex = -1; bin.Read(materialIndex); if(materialIndex < -1 || materialIndex >= static_cast(MaterialsList.size())) { return Error("Invalid material index (" + std::to_string(materialIndex) + ") with available range of [-1; " + std::to_string(static_cast(MaterialsList.size())) + "]"); } std::uint64_t TrianglesCount; bin.Read(TrianglesCount); for(std::uint64_t i = 0; i < TrianglesCount; i++) { CTriangle triangle; bin.Read(triangle.VertexIndex); bin.Read(triangle.UvIndex); bin.Read(triangle.NormalIndex); if(!CheckIndices(triangle.VertexIndex, Vertices.size())) { return Error("Invalid vertex index (" + std::to_string(triangle.VertexIndex.x) + ", " + std::to_string(triangle.VertexIndex.y) + ", " + std::to_string(triangle.VertexIndex.z) + ") with available range of [0; " + std::to_string(static_cast(Vertices.size())) + "]"); //return Error("Invalid vertex index"); } if(!CheckIndices(triangle.UvIndex, UvCoords.size())) { return Error("Invalid UV coord index (" + std::to_string(triangle.UvIndex.x) + ", " + std::to_string(triangle.UvIndex.y) + ", " + std::to_string(triangle.UvIndex.z) + ") with available range of [0; " + std::to_string(static_cast(UvCoords.size())) + "]"); //return Error("Invalid UV coord index"); } if(!CheckIndices(triangle.NormalIndex, Normals.size())) { return Error("Invalid normal vector index (" + std::to_string(triangle.NormalIndex.x) + ", " + std::to_string(triangle.NormalIndex.y) + ", " + std::to_string(triangle.NormalIndex.z) + ") with available range of [0; " + std::to_string(static_cast(Normals.size())) + "]"); //return Error("Invalid normal vector index"); } triangle.MaterialIndex = materialIndex; Triangles.push_back(triangle); } } guard.Dismiss(); return true; }