facecam2d/src/model.cpp
Epicalert 678393ccb1
Clean up model format version
Commit 09c60db8 should have incrememnted the model format version but
didn't. Also added "since version x.x" in the model format docs.
2021-11-12 20:12:06 +08:00

215 lines
6.2 KiB
C++

#include <iostream>
#include <vector>
#include <zip.h>
#include <fmt/core.h>
#include <glm/vec2.hpp>
#include <tomlcpp.hpp> //dynamically link tomlcpp if it becomes common in repositories
#include <model.hpp>
#include <config.hpp>
#include <error.hpp>
#define BUFFER_SIZE_MODEL_DESC 8192 // 8 KiB
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
#define SUPPORTED_MODEL_MAJOR 0
#define SUPPORTED_MODEL_MINOR 4
void textureFromArchive(zip_t* archive, const char* path, ModelPart* part, size_t slot, std::string triggerName) {
zip_file_t* textureFile = zip_fopen(archive, path, 0);
if (textureFile != NULL) {
unsigned char* textureBuffer = new unsigned char[BUFFER_SIZE_TEXTURE];
size_t textureLength = zip_fread(textureFile, textureBuffer, BUFFER_SIZE_TEXTURE-1);
part->addTexture(textureBuffer, textureLength, slot, triggerName);
delete [] textureBuffer;
} else {
showError(fmt::format("Texture file \"{}\" does not exist in archive!", path), "Could not open model");
}
}
Model::Model(const char* path) {
int zipError;
zip_t* archive = zip_open(path, ZIP_RDONLY, &zipError);
if (!archive) {
showError(fmt::format("Model file {} does not exist or is corrupt!", path), "Could not open model");
return;
}
// get model description file (model.toml)
zip_file_t* modelDescFile = zip_fopen(archive, "model.toml", 0);
char modelDescBuffer[BUFFER_SIZE_MODEL_DESC];
size_t descLength = zip_fread(modelDescFile, modelDescBuffer, BUFFER_SIZE_MODEL_DESC-1);
modelDescBuffer[descLength] = 0; //null-terminate
// parse model.toml
auto modelDesc = toml::parse(std::string(modelDescBuffer));
if (!modelDesc.table) {
showError("Cannot parse model.toml:\n" + modelDesc.errmsg, "Could not open model");
}
// get format table
auto format = modelDesc.table->getTable("format");
if (!format) {
showError("Model does not have a format table!", "Could not open model");
} else {
// get format version
auto formatMajResult = format->getInt("version_major");
auto formatMinResult = format->getInt("version_minor");
if (formatMajResult.first && formatMinResult.first) {
// check format version
if (formatMajResult.second != SUPPORTED_MODEL_MAJOR ||
formatMinResult.second > SUPPORTED_MODEL_MINOR ) {
showError(fmt::format("Model format version {0}.{1} is unsupported! This version of {2} supports model file version"
" {3}.0 to version {3}.{4}.",
formatMajResult.second, formatMinResult.second,
PROJECT_NAME,
SUPPORTED_MODEL_MAJOR, SUPPORTED_MODEL_MINOR), "Could not open model");
}
} else {
showError("Model does not define a format version!", "Could not open model");
}
}
// get model info table
auto modelInfo = modelDesc.table->getTable("model_info");
if (!modelInfo) {
showError("Model does not have a model_info table!", "Could not open model");
} else {
// get name
auto nameResult = modelInfo->getString("name");
if (!nameResult.first) {
showWarning("Model does not have a name!", "Model warning");
} else {
name = nameResult.second;
}
}
// parse parts
auto partsDescArray = modelDesc.table->getArray("part");
auto partsVec = *partsDescArray->getTableVector();
for (int i = 0; i < partsVec.size(); i++) {
ModelPart newPart;
// position
auto bindResult = partsVec[i].getString("bind");
if (bindResult.first) {
newPart.setBind(bindResult.second);
}
auto parentResult = partsVec[i].getString("follow");
auto factorResult = partsVec[i].getDouble("factor");
if (parentResult.first) {
newPart.setFollowTarget(parentResult.second);
if (factorResult.first) {
newPart.factor = (float)factorResult.second;
} else {
newPart.factor = 1.0f;
}
}
// rotation and scale factor
auto rotFacResult = partsVec[i].getDouble("rot_factor");
auto scaleFacResult = partsVec[i].getDouble("scale_factor");
auto offsetFacResult = partsVec[i].getDouble("offset_factor");
if (rotFacResult.first) {
newPart.rotFactor = (float)rotFacResult.second;
}
if (scaleFacResult.first) {
newPart.scaleFactor = (float)scaleFacResult.second;
}
if (offsetFacResult.first) {
newPart.offsetFactor = (float)offsetFacResult.second;
}
// origin
auto originArray = partsVec[i].getArray("origin");
if (originArray) {
auto originVec = *originArray->getDoubleVector();
newPart.origin = glm::vec2((float)originVec[0], (float)originVec[1]);
}
// offsets
auto posOffsetArray = partsVec[i].getArray("pos_offset");
if (posOffsetArray) {
auto offsetVec = *posOffsetArray->getDoubleVector();
newPart.posOffset = glm::vec2((float)offsetVec[0], (float)offsetVec[1]);
}
auto scaleOffsetArray = partsVec[i].getArray("scale_offset");
if (scaleOffsetArray) {
auto offsetVec = *scaleOffsetArray->getDoubleVector();
newPart.scaleOffset = glm::vec2((float)offsetVec[0], (float)offsetVec[1]);
}
// offset bind
auto offsetBindResult = partsVec[i].getString("offset_bind");
if (offsetBindResult.first) {
newPart.setOffsetBind(offsetBindResult.second);
}
// texture
auto textureSingle = partsVec[i].getString("texture");
if (textureSingle.first) {
// only a single texture was defined
textureFromArchive(archive, textureSingle.second.c_str(), &newPart, 0, "null");
} else {
auto textureArray = partsVec[i].getArray("textures");
auto textureVec = *textureArray->getTableVector().get();
if (textureVec.size() < 1) {
showWarning(fmt::format("Part {} does not define any textures! Parts with no textures defined will"
" show a default \"missing texture\" pattern.", i), "Model warning");
} else {
// a list of textures with triggers were defined
for (int j = 0; j < textureVec.size(); j++) {
auto fileResult = textureVec[j].getString("file");
auto triggerResult = textureVec[j].getString("trigger");
if (fileResult.first) {
std::string trigger = triggerResult.first ? triggerResult.second : "null";
textureFromArchive(archive, fileResult.second.c_str(), &newPart, j, trigger);
}
}
}
}
parts.push_back(newPart);
}
}
void Model::draw() {
for (size_t i = 0; i < parts.size(); i++) {
parts[i].bindAndDraw();
}
}
void Model::updateTransforms(struct FaceData faceData) {
for (size_t i = 0; i < parts.size(); i++) {
parts[i].processFaceData(faceData);
}
}
std::string Model::getName() {
return name;
}