2021-02-04 14:58:03 +00:00
|
|
|
#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>
|
2021-02-04 14:58:03 +00:00
|
|
|
|
|
|
|
#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
|
2021-02-10 09:02:38 +00:00
|
|
|
#define SUPPORTED_MODEL_MINOR 2
|
2021-02-07 14:01:31 +00:00
|
|
|
|
2021-02-04 14:58:03 +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
|
2021-02-04 14:58:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-10 09:02:38 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2021-02-04 14:58:03 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|