facecam2d/src/model.cpp

177 lines
5.1 KiB
C++
Raw Normal View History

#include <iostream>
#include <vector>
#include <zip.h>
#include <tomlcpp.hpp> //dynamically link tomlcpp if it becomes common in repositories
#include <model.hpp>
2021-02-07 14:01:31 +00:00
#include <config.hpp>
#define BUFFER_SIZE_MODEL_DESC 8192 // 8 KiB
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
2021-02-07 14:01:31 +00:00
#define SUPPORTED_MODEL_MAJOR 0
#define SUPPORTED_MODEL_MINOR 2
2021-02-07 14:01:31 +00:00
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 {
std::cerr << path << " does not exist in archive!" << std::endl;
}
}
Model::Model(const char* path) {
int zipError;
zip_t* archive = zip_open(path, ZIP_RDONLY, &zipError);
if (!archive) {
std::cerr << "Model file " << path << " does not exist!" << std::endl;
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) {
std::cerr << "cannot parse model.toml! " << std::endl << modelDesc.errmsg << std::endl;
}
2021-02-07 14:01:31 +00:00
// get format table
auto format = modelDesc.table->getTable("format");
if (!format) {
std::cerr << "Model does not have a format table!" << std::endl;
} 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 ) {
std::cerr << "Model format version " <<
formatMajResult.second << "." << formatMinResult.second <<
" is unsupported! This version of " << PROJECT_NAME << " supports model file version " <<
SUPPORTED_MODEL_MAJOR << ".0 to version " <<
SUPPORTED_MODEL_MAJOR << "." << SUPPORTED_MODEL_MINOR << std::endl;
}
} else {
std::cerr << "Model does not define a format version!" << std::endl;
}
}
// get model info table
2021-02-07 11:03:27 +00:00
auto modelInfo = modelDesc.table->getTable("model_info");
2021-02-07 14:01:31 +00:00
2021-02-07 11:03:27 +00:00
if (!modelInfo) {
std::cerr << "Model does not have a model_info table!" << std::endl;
} else {
2021-02-07 14:01:31 +00:00
// get name
2021-02-07 11:03:27 +00:00
auto nameResult = modelInfo->getString("name");
if (!nameResult.first) {
std::cerr << "Model does not have a name!" << std::endl;
} else {
name = nameResult.second;
}
}
2021-02-07 14:01:31 +00:00
// 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) {
std::cerr << "Part " << i << " does not define a bind!" << std::endl;
} else {
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.setFollowFactor((float)factorResult.second);
} else {
newPart.setFollowFactor(1.0f);
}
}
// rotation and scale factor
auto rotFacResult = partsVec[i].getDouble("rot_factor");
auto scaleFacResult = partsVec[i].getDouble("scale_factor");
if (rotFacResult.first) {
newPart.setRotationFactor((float)rotFacResult.second);
}
if (scaleFacResult.first) {
newPart.setScaleFactor((float)scaleFacResult.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) {
std::cerr << "Part " << i << " does not define any textures!" << std::endl;
} 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);
}
}
2021-02-07 11:03:27 +00:00
std::string Model::getName() {
return name;
}