← Back to file list Raw

src/CModelCompiler.cpp

#include "CModelCompiler.h"
#include "CBinaryFile.h"
#include "CModelLoadInfo.h"
#include "CScopeExit.h"
#include "U_Log.h"
#include "U_Files.h"
#include <fstream>
/*
class CModelLoadInfo
{
public:
class CBone
{
public:
int ParentIndex = -1;
std::string Name;
CTransform BindTransform;
};
class CVertex
{
public:
class CBoneLink
{
public:
float Weight = 1.0f;
int BoneIndex = -1;
};
//TODO vertex color
glm::vec3 Position;
std::uint8_t BoneCount = 0;
std::vector<CBoneLink> Bones;
};
class CTriangle
{
public:
glm::u64vec3 VertexIndex;
glm::u64vec3 UvIndex;
glm::u64vec3 NormalIndex;
int MaterialIndex = -1;
};
std::vector<CBone> Bones;
std::vector<CVertex> Vertices;
std::vector<glm::vec2> UvCoords;
std::vector<glm::vec3> Normals;
std::vector<CTriangle> Triangles;
std::vector<std::string> MaterialsList;
void Reset();
bool Load(const std::filesystem::path& path);
int CheckBoneValidity(CBone& bone);
bool CheckIndices(const glm::u64vec3& vec, size_t limit);
};
*/
bool CModelCompiler::CompileModel(const std::filesystem::path& srcpath)
{
CModelLoadInfo model;
std::ifstream objfile(srcpath);
if (!objfile.is_open()) { Log::ErrInstance() << "Can't open source file\n"; return false; }
CScopeExit streamExiter([&objfile]() { objfile.close(); });
std::filesystem::path outpath = FileUtils::get_executable_path() / "resources" / "models" / (srcpath.stem().string() + ".emdl");
CBinaryFile outfile(outpath, false); //closed on destructor
if(!outfile.IsOpen()) { Log::ErrInstance() << "Can't open output file\n"; return false; }
glm::vec3 Scale{ 1.0f, 1.0f, 1.0f };
std::vector<CModelLoadInfo::CVertex::CBoneLink> CurrentBoneLinking;
int CurrentMaterial = -1;
std::string ReadLine;
while (!objfile.eof())
{
std::getline(objfile, ReadLine, '\n');
if (ReadLine.substr(0, 5) == "scale")
{
std::vector<std::string> splitted;
StringUtils::split_str(ReadLine, ' ', splitted);
float scl = atof(splitted[1].c_str());
Scale = { scl, scl, scl };
Log::Instance() << "Scaled to " << scl << "\n";
}
else if (ReadLine.substr(0, 6) == "scale3")
{
std::vector<std::string> splitted;
StringUtils::split_str(ReadLine, ' ', splitted);
float scl[3];
for(int i = 0; i < 3; i++) { scl[i] = atof(splitted[i + 1].c_str()); }
Scale = { scl[0], scl[1], scl[2] };
Log::Instance() << "Scaled to { " << StringUtils::ToStr(Scale) << " }\n";
}
else if (ReadLine[0] == 'v')
{
if (ReadLine[1] == 't')
{
float X, Y;
sscanf(ReadLine.c_str(), "vt %f %f", &X, &Y);
model.UvCoords.push_back({ X, Y });
}
else if (ReadLine[1] == 'n')
{
float X, Y, Z;
sscanf(ReadLine.c_str(), "vn %f %f %f", &X, &Y, &Z);
model.Normals.push_back({ X, Y, Z });
}
else
{
float X, Y, Z;
sscanf(ReadLine.c_str(), "v %f %f %f", &X, &Y, &Z);
X *= Scale.x;
Y *= Scale.y;
Z *= Scale.z;
CModelLoadInfo::CVertex vertex;
vertex.BoneCount = CurrentBoneLinking.size();
vertex.Bones = CurrentBoneLinking;
vertex.Position = { X, Y, Z };
model.Vertices.push_back(vertex);
}
}
else if (ReadLine[0] == 'l')
{
ReadLine.erase(0, 2);
bool found = std::find(model.MaterialsList.begin(), model.MaterialsList.end(), ReadLine) != model.MaterialsList.end();
if (!found)
{
model.MaterialsList.push_back(ReadLine);
Log::Instance() << "Added material " << ReadLine << "\n";
}
CurrentMaterial = std::distance(model.MaterialsList.begin(), std::find(model.MaterialsList.begin(), model.MaterialsList.end(), ReadLine));
}
else if (ReadLine[0] == 'm')
{
ReadLine.erase(0, 2);
bool found = std::find(model.MaterialsList.begin(), model.MaterialsList.end(), ReadLine) != model.MaterialsList.end();
if (!found)
{
model.MaterialsList.push_back(ReadLine);
Log::Instance() << "Forced material " << ReadLine << "\n";
}
CurrentMaterial = std::distance(model.MaterialsList.begin(), std::find(model.MaterialsList.begin(), model.MaterialsList.end(), ReadLine));
}
else if (ReadLine[0] == 'f')
{
int vertex[3];
int texture[3];
int normal[3];
sscanf(ReadLine.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &vertex[0], &texture[0], &normal[0], &vertex[1], &texture[1], &normal[1], &vertex[2], &texture[2], &normal[2]);
CModelLoadInfo::CTriangle triangle;
for(int i = 0; i < 3; i++) { triangle.VertexIndex[i] = vertex[i] - 1; }
for(int i = 0; i < 3; i++) { triangle.UvIndex[i] = texture[i] - 1; }
for(int i = 0; i < 3; i++) { triangle.NormalIndex[i] = normal[i] - 1; }
triangle.MaterialIndex = CurrentMaterial;
model.Triangles.push_back(triangle);
}
else if (ReadLine[0] == 'j')
{
if (ReadLine[1] == 'c')
{
ReadLine.erase(0, 3);
std::vector<std::string> splitted;
StringUtils::split_str(ReadLine, ' ', splitted);
std::string boneName = splitted.front();
splitted.erase(splitted.begin());
std::string boneParentName;
bool found = std::find_if(model.Bones.begin(), model.Bones.end(), [&boneName](auto& bone) -> bool
{
return bone.Name == boneName;
}) != model.Bones.end();
if (found)
{
continue;
}
CModelLoadInfo::CBone bone;
bone.Name = boneName;
size_t transformIndex = 1;
if(!StringUtils::isNumber(splitted.front()))
{
boneParentName = splitted.front();
splitted.erase(splitted.begin());
auto parentIt = std::find_if(model.Bones.begin(), model.Bones.end(), [&boneParentName](auto& bone) -> bool
{
return bone.Name == boneParentName;
});
if(parentIt == model.Bones.end())
{
Log::ErrInstance() << "Can't find parent bone \"" << boneParentName << "\" for \"" << bone.Name << "\"\n";
return false;
}
bone.ParentIndex = std::distance(model.Bones.begin(), parentIt);
transformIndex = 2;
}
//glm::vec3 Position, Scale;
//glm::quat Rotation;
glm::mat4 bindMatrix;
for(size_t y = 0; y < glm::mat4::length(); y++)
{
for(size_t x = 0; x < glm::mat4::length(); x++)
{
bindMatrix[x][y] = StringUtils::FromStr<float>(splitted.front());
splitted.erase(splitted.begin());
}
}
//for(size_t i = 0; i < Position.length(); i++) { Position[i] = StringUtils::FromStr<float>(splitted.front()); splitted.erase(splitted.begin()); }
//for(size_t i = 0; i < Rotation.length(); i++) { Rotation[i] = StringUtils::FromStr<float>(splitted.front()); splitted.erase(splitted.begin()); }
//for(size_t i = 0; i < Scale.length(); i++) { Scale[i] = StringUtils::FromStr<float>(splitted.front()); splitted.erase(splitted.begin()); }
//bone.BindTransform.SetFromMatrix()
//bone.BindTransform.SetPRS(Position, Rotation, Scale);
bone.BindTransform = bindMatrix;
model.Bones.push_back(bone);
auto thisBoneIt = std::find_if(model.Bones.begin(), model.Bones.end(), [&boneName](auto& bone) -> bool
{
return bone.Name == boneName;
});
CurrentBoneLinking.clear();
CModelLoadInfo::CVertex::CBoneLink newLink;
newLink.BoneIndex = std::distance(model.Bones.begin(), thisBoneIt);
newLink.Weight = 1.0f;
CurrentBoneLinking.push_back(newLink);
}
else
{
CurrentBoneLinking.clear();
ReadLine.erase(0, 2);
std::vector<std::string> splitted;
StringUtils::split_str(ReadLine, ' ', splitted);
size_t boneCount = 0;
std::string boneName;
float boneWeight = 1.0f;
for(size_t i = 0; i < splitted.size(); i++)
{
if(i % 2 == 0)
{
boneName = splitted[i];
}
else
{
boneWeight = StringUtils::FromStr<float>(splitted[i]);
auto boneIt = std::find_if(model.Bones.begin(), model.Bones.end(), [&boneName](auto& bone) -> bool
{
return bone.Name == boneName;
});
if(boneIt == model.Bones.end())
{
Log::ErrInstance() << "Can't find bone \"" << boneName << "\"\n";
return false;
}
CModelLoadInfo::CVertex::CBoneLink newLink;
newLink.BoneIndex = std::distance(model.Bones.begin(), boneIt);
newLink.Weight = boneWeight;
CurrentBoneLinking.push_back(newLink);
}
}
}
}
}
size_t _dbg_MaterialIndex = 0, _dbg_BoneIndex = 0;
outfile.Write<std::uint16_t>(model.MaterialsList.size());
Log::Instance() << "Materials count: " << model.MaterialsList.size() << "\n";
for(size_t i = 0; i < model.MaterialsList.size(); i++)
{
outfile.WriteString(model.MaterialsList.at(i));
Log::Instance() << "Material #" << _dbg_MaterialIndex << " is " << model.MaterialsList.at(i) << "\n"; _dbg_MaterialIndex++;
}
outfile.Write<std::uint16_t>(model.Bones.size());
Log::Instance() << "Bones count: " << model.Bones.size() << "\n";
for(size_t i = 0; i < model.Bones.size(); i++)
{
outfile.Write(model.Bones.at(i).ParentIndex);
outfile.WriteString(model.Bones.at(i).Name);
outfile.Write(model.Bones.at(i).BindTransform);
//outfile.Write(model.Bones.at(i).BindTransform.GetPosition());
//outfile.Write(model.Bones.at(i).BindTransform.GetRotation());
//outfile.Write(model.Bones.at(i).BindTransform.GetScale());
Log::Instance() << "Bone #" << _dbg_BoneIndex << " is " << model.Bones.at(i).Name << " with parent index of " << model.Bones.at(i).ParentIndex << "\n"; _dbg_BoneIndex++;
}
outfile.Write<std::uint64_t>(model.UvCoords.size());
Log::Instance() << "Uv coords count: " << model.UvCoords.size() << "\n";
for(size_t i = 0; i < model.UvCoords.size(); i++)
{
outfile.Write(model.UvCoords.at(i));
}
outfile.Write<std::uint64_t>(model.Normals.size());
Log::Instance() << "Normals count: " << model.Normals.size() << "\n";
for(size_t i = 0; i < model.Normals.size(); i++)
{
outfile.Write(model.Normals.at(i));
}
outfile.Write<std::uint64_t>(model.Vertices.size());
Log::Instance() << "Vertices count: " << model.Vertices.size() << "\n";
for(size_t i = 0; i < model.Vertices.size(); i++)
{
outfile.Write(model.Vertices.at(i).Position);
outfile.Write(model.Vertices.at(i).BoneCount);
size_t _dbg_BoneCycles = 0;
for(std::uint8_t j = 0; j < model.Vertices.at(i).BoneCount; j++)
{
outfile.Write(model.Vertices.at(i).Bones.at(j).BoneIndex);
outfile.Write(model.Vertices.at(i).Bones.at(j).Weight);
_dbg_BoneCycles++;
}
if(_dbg_BoneCycles != model.Vertices.at(i).BoneCount)
{
Log::Errln("Warning! There's mismatch in actual written bones count and BoneCount var!");
}
}
outfile.Write<std::uint64_t>(model.MaterialsList.size());
for(size_t i = 0; i < model.MaterialsList.size(); i++)
{
outfile.Write<int>(i);
std::uint64_t count = 0;
for(auto& tri : model.Triangles)
{
if(tri.MaterialIndex == i)
{
count++;
}
}
Log::Instance() << "Writing " << count << " polygons for material #" << i << "\n";
outfile.Write<std::uint64_t>(count);
for(auto& tri : model.Triangles)
{
if(tri.MaterialIndex == i)
{
outfile.Write(tri.VertexIndex);
outfile.Write(tri.UvIndex);
outfile.Write(tri.NormalIndex);
}
}
}
Log::Instance() << "Model info loaded!\n";
return true;
}