#include #include #include #include #include #include //dynamically link tomlcpp if it becomes common in repositories #include #include #include #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; }