summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/MD5
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/MD5
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/MD5')
-rw-r--r--libs/assimp/code/AssetLib/MD5/MD5Loader.cpp735
-rw-r--r--libs/assimp/code/AssetLib/MD5/MD5Loader.h181
-rw-r--r--libs/assimp/code/AssetLib/MD5/MD5Parser.cpp472
-rw-r--r--libs/assimp/code/AssetLib/MD5/MD5Parser.h467
4 files changed, 1855 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/MD5/MD5Loader.cpp b/libs/assimp/code/AssetLib/MD5/MD5Loader.cpp
new file mode 100644
index 0000000..2d5da4d
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MD5/MD5Loader.cpp
@@ -0,0 +1,735 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MD5Loader.cpp
+ * @brief Implementation of the MD5 importer class
+ */
+
+#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
+
+// internal headers
+#include "MD5Loader.h"
+#include <assimp/MathFunctions.h>
+#include <assimp/RemoveComments.h>
+#include <assimp/SkeletonMeshBuilder.h>
+#include <assimp/StringComparison.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Importer.hpp>
+#include <memory>
+
+using namespace Assimp;
+
+// Minimum weight value. Weights inside [-n ... n] are ignored
+#define AI_MD5_WEIGHT_EPSILON Math::getEpsilon<float>()
+
+static const aiImporterDesc desc = {
+ "Doom 3 / MD5 Mesh Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportBinaryFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "md5mesh md5camera md5anim"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD5Importer::MD5Importer() :
+ mIOHandler(nullptr),
+ mBuffer(),
+ mFileSize(),
+ mLineNumber(),
+ mScene(),
+ mHadMD5Mesh(),
+ mHadMD5Anim(),
+ mHadMD5Camera(),
+ mCconfigNoAutoLoad(false) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD5Importer::~MD5Importer() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ static const char *tokens[] = { "MD5Version" };
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all supported extensions
+const aiImporterDesc *MD5Importer::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void MD5Importer::SetupProperties(const Importer *pImp) {
+ // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
+ mCconfigNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD, 0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD5Importer::InternReadFile(const std::string &pFile, aiScene *_pScene, IOSystem *pIOHandler) {
+ mIOHandler = pIOHandler;
+ mScene = _pScene;
+ mHadMD5Mesh = mHadMD5Anim = mHadMD5Camera = false;
+
+ // remove the file extension
+ const std::string::size_type pos = pFile.find_last_of('.');
+ mFile = (std::string::npos == pos ? pFile : pFile.substr(0, pos + 1));
+
+ const std::string extension = GetExtension(pFile);
+ try {
+ if (extension == "md5camera") {
+ LoadMD5CameraFile();
+ } else if (mCconfigNoAutoLoad || extension == "md5anim") {
+ // determine file extension and process just *one* file
+ if (extension.length() == 0) {
+ throw DeadlyImportError("Failure, need file extension to determine MD5 part type");
+ }
+ if (extension == "md5anim") {
+ LoadMD5AnimFile();
+ } else if (extension == "md5mesh") {
+ LoadMD5MeshFile();
+ }
+ } else {
+ LoadMD5MeshFile();
+ LoadMD5AnimFile();
+ }
+ } catch (...) { // std::exception, Assimp::DeadlyImportError
+ UnloadFileFromMemory();
+ throw;
+ }
+
+ // make sure we have at least one file
+ if (!mHadMD5Mesh && !mHadMD5Anim && !mHadMD5Camera) {
+ throw DeadlyImportError("Failed to read valid contents out of this MD5* file");
+ }
+
+ // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system
+ mScene->mRootNode->mTransformation = aiMatrix4x4(1.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
+
+ // the output scene wouldn't pass the validation without this flag
+ if (!mHadMD5Mesh) {
+ mScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ // clean the instance -- the BaseImporter instance may be reused later.
+ UnloadFileFromMemory();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a file into a memory buffer
+void MD5Importer::LoadFileIntoMemory(IOStream *file) {
+ // unload the previous buffer, if any
+ UnloadFileFromMemory();
+
+ ai_assert(nullptr != file);
+ mFileSize = (unsigned int)file->FileSize();
+ ai_assert(mFileSize);
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ mBuffer = new char[mFileSize + 1];
+ file->Read((void *)mBuffer, 1, mFileSize);
+ mLineNumber = 1;
+
+ // append a terminal 0
+ mBuffer[mFileSize] = '\0';
+
+ // now remove all line comments from the file
+ CommentRemover::RemoveLineComments("//", mBuffer, ' ');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unload the current memory buffer
+void MD5Importer::UnloadFileFromMemory() {
+ // delete the file buffer
+ delete[] mBuffer;
+ mBuffer = nullptr;
+ mFileSize = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build unique vertices
+void MD5Importer::MakeDataUnique(MD5::MeshDesc &meshSrc) {
+ std::vector<bool> abHad(meshSrc.mVertices.size(), false);
+
+ // allocate enough storage to keep the output structures
+ const unsigned int iNewNum = static_cast<unsigned int>(meshSrc.mFaces.size() * 3);
+ unsigned int iNewIndex = static_cast<unsigned int>(meshSrc.mVertices.size());
+ meshSrc.mVertices.resize(iNewNum);
+
+ // try to guess how much storage we'll need for new weights
+ const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
+ const unsigned int guess = (unsigned int)(fWeightsPerVert * iNewNum);
+ meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
+
+ for (FaceList::const_iterator iter = meshSrc.mFaces.begin(), iterEnd = meshSrc.mFaces.end(); iter != iterEnd; ++iter) {
+ const aiFace &face = *iter;
+ for (unsigned int i = 0; i < 3; ++i) {
+ if (face.mIndices[0] >= meshSrc.mVertices.size()) {
+ throw DeadlyImportError("MD5MESH: Invalid vertex index");
+ }
+
+ if (abHad[face.mIndices[i]]) {
+ // generate a new vertex
+ meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
+ face.mIndices[i] = iNewIndex++;
+ } else
+ abHad[face.mIndices[i]] = true;
+ }
+ // swap face order
+ std::swap(face.mIndices[0], face.mIndices[2]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5MESH
+void MD5Importer::AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneList &bones) {
+ ai_assert(nullptr != piParent);
+ ai_assert(!piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size(); ++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode *[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size(); ++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ aiNode *pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the transformation matrix from rotation and translational components
+ aiQuaternion quat;
+ MD5::ConvertQuaternion(bones[i].mRotationQuat, quat);
+
+ bones[i].mTransform = aiMatrix4x4(quat.GetMatrix());
+ bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
+ bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
+ bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
+
+ // store it for later use
+ pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
+ bones[i].mInvTransform.Inverse();
+
+ // the transformations for each bone are absolute, so we need to multiply them
+ // with the inverse of the absolute matrix of the parent joint
+ if (-1 != iParentID) {
+ pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
+ }
+
+ // add children to this node, too
+ AttachChilds_Mesh(i, pc, bones);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5ANIM
+void MD5Importer::AttachChilds_Anim(int iParentID, aiNode *piParent, AnimBoneList &bones, const aiNodeAnim **node_anims) {
+ ai_assert(nullptr != piParent);
+ ai_assert(!piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size(); ++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode *[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size(); ++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ aiNode *pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the corresponding animation channel and its first frame
+ const aiNodeAnim **cur = node_anims;
+ while ((**cur).mNodeName != pc->mName)
+ ++cur;
+
+ aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue, pc->mTransformation);
+ pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix());
+
+ // add children to this node, too
+ AttachChilds_Anim(i, pc, bones, node_anims);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a MD5MESH file
+void MD5Importer::LoadMD5MeshFile() {
+ std::string filename = mFile + "md5mesh";
+ std::unique_ptr<IOStream> file(mIOHandler->Open(filename, "rb"));
+
+ // Check whether we can read from the file
+ if (file.get() == nullptr || !file->FileSize()) {
+ ASSIMP_LOG_WARN("Failed to access MD5MESH file: ", filename);
+ return;
+ }
+ mHadMD5Mesh = true;
+ LoadFileIntoMemory(file.get());
+
+ // now construct a parser and parse the file
+ MD5::MD5Parser parser(mBuffer, mFileSize);
+
+ // load the mesh information from it
+ MD5::MD5MeshParser meshParser(parser.mSections);
+
+ // create the bone hierarchy - first the root node and dummy nodes for all meshes
+ mScene->mRootNode = new aiNode("<MD5_Root>");
+ mScene->mRootNode->mNumChildren = 2;
+ mScene->mRootNode->mChildren = new aiNode *[2];
+
+ // build the hierarchy from the MD5MESH file
+ aiNode *pcNode = mScene->mRootNode->mChildren[1] = new aiNode();
+ pcNode->mName.Set("<MD5_Hierarchy>");
+ pcNode->mParent = mScene->mRootNode;
+ AttachChilds_Mesh(-1, pcNode, meshParser.mJoints);
+
+ pcNode = mScene->mRootNode->mChildren[0] = new aiNode();
+ pcNode->mName.Set("<MD5_Mesh>");
+ pcNode->mParent = mScene->mRootNode;
+
+#if 0
+ if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
+ SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
+#else
+
+ // FIX: MD5 files exported from Blender can have empty meshes
+ for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(), end = meshParser.mMeshes.end(); it != end; ++it) {
+ if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) {
+ ++mScene->mNumMaterials;
+ }
+ }
+
+ // generate all meshes
+ mScene->mNumMeshes = mScene->mNumMaterials;
+ mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
+ mScene->mMaterials = new aiMaterial *[mScene->mNumMeshes];
+
+ // storage for node mesh indices
+ pcNode->mNumMeshes = mScene->mNumMeshes;
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ for (unsigned int m = 0; m < pcNode->mNumMeshes; ++m) {
+ pcNode->mMeshes[m] = m;
+ }
+
+ unsigned int n = 0;
+ for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(), end = meshParser.mMeshes.end(); it != end; ++it) {
+ MD5::MeshDesc &meshSrc = *it;
+ if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) {
+ continue;
+ }
+
+ aiMesh *mesh = mScene->mMeshes[n] = new aiMesh();
+ mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // generate unique vertices in our internal verbose format
+ MakeDataUnique(meshSrc);
+
+ std::string name(meshSrc.mShader.C_Str());
+ name += ".msh";
+ mesh->mName = name;
+ mesh->mNumVertices = (unsigned int)meshSrc.mVertices.size();
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+
+ // copy texture coordinates
+ aiVector3D *pv = mesh->mTextureCoords[0];
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+ pv->x = (*iter).mUV.x;
+ pv->y = 1.0f - (*iter).mUV.y; // D3D to OpenGL
+ pv->z = 0.0f;
+ }
+
+ // sort all bone weights - per bone
+ unsigned int *piCount = new unsigned int[meshParser.mJoints.size()];
+ ::memset(piCount, 0, sizeof(unsigned int) * meshParser.mJoints.size());
+
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) {
+ MD5::WeightDesc &weightDesc = meshSrc.mWeights[w];
+ /* FIX for some invalid exporters */
+ if (!(weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON)) {
+ ++piCount[weightDesc.mBone];
+ }
+ }
+ }
+
+ // check how many we will need
+ for (unsigned int p = 0; p < meshParser.mJoints.size(); ++p) {
+ if (piCount[p]) mesh->mNumBones++;
+ }
+
+ // just for safety
+ if (mesh->mNumBones) {
+ mesh->mBones = new aiBone *[mesh->mNumBones];
+ for (unsigned int q = 0, h = 0; q < meshParser.mJoints.size(); ++q) {
+ if (!piCount[q]) continue;
+ aiBone *p = mesh->mBones[h] = new aiBone();
+ p->mNumWeights = piCount[q];
+ p->mWeights = new aiVertexWeight[p->mNumWeights];
+ p->mName = aiString(meshParser.mJoints[q].mName);
+ p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
+
+ // store the index for later use
+ MD5::BoneDesc &boneSrc = meshParser.mJoints[q];
+ boneSrc.mMap = h++;
+
+ // compute w-component of quaternion
+ MD5::ConvertQuaternion(boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted);
+ }
+
+ pv = mesh->mVertices;
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+ // compute the final vertex position from all single weights
+ *pv = aiVector3D();
+
+ // there are models which have weights which don't sum to 1 ...
+ ai_real fSum = 0.0;
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) {
+ fSum += meshSrc.mWeights[w].mWeight;
+ }
+ if (!fSum) {
+ ASSIMP_LOG_ERROR("MD5MESH: The sum of all vertex bone weights is 0");
+ continue;
+ }
+
+ // process bone weights
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) {
+ if (w >= meshSrc.mWeights.size()) {
+ throw DeadlyImportError("MD5MESH: Invalid weight index");
+ }
+
+ MD5::WeightDesc &weightDesc = meshSrc.mWeights[w];
+ if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
+ continue;
+ }
+
+ const ai_real fNewWeight = weightDesc.mWeight / fSum;
+
+ // transform the local position into worldspace
+ MD5::BoneDesc &boneSrc = meshParser.mJoints[weightDesc.mBone];
+ const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate(weightDesc.vOffsetPosition);
+
+ // use the original weight to compute the vertex position
+ // (some MD5s seem to depend on the invalid weight values ...)
+ *pv += ((boneSrc.mPositionXYZ + v) * (ai_real)weightDesc.mWeight);
+
+ aiBone *bone = mesh->mBones[boneSrc.mMap];
+ *bone->mWeights++ = aiVertexWeight((unsigned int)(pv - mesh->mVertices), fNewWeight);
+ }
+ }
+
+ // undo our nice offset tricks ...
+ for (unsigned int p = 0; p < mesh->mNumBones; ++p) {
+ mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
+ }
+ }
+
+ delete[] piCount;
+
+ // now setup all faces - we can directly copy the list
+ // (however, take care that the aiFace destructor doesn't delete the mIndices array)
+ mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for (unsigned int c = 0; c < mesh->mNumFaces; ++c) {
+ mesh->mFaces[c].mNumIndices = 3;
+ mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
+ meshSrc.mFaces[c].mIndices = nullptr;
+ }
+
+ // generate a material for the mesh
+ aiMaterial *mat = new aiMaterial();
+ mScene->mMaterials[n] = mat;
+
+ // insert the typical doom3 textures:
+ // nnn_local.tga - normal map
+ // nnn_h.tga - height map
+ // nnn_s.tga - specular map
+ // nnn_d.tga - diffuse map
+ if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data, '.')) {
+
+ aiString temp(meshSrc.mShader);
+ temp.Append("_local.tga");
+ mat->AddProperty(&temp, AI_MATKEY_TEXTURE_NORMALS(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_s.tga");
+ mat->AddProperty(&temp, AI_MATKEY_TEXTURE_SPECULAR(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_d.tga");
+ mat->AddProperty(&temp, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_h.tga");
+ mat->AddProperty(&temp, AI_MATKEY_TEXTURE_HEIGHT(0));
+
+ // set this also as material name
+ mat->AddProperty(&meshSrc.mShader, AI_MATKEY_NAME);
+ } else {
+ mat->AddProperty(&meshSrc.mShader, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ mesh->mMaterialIndex = n++;
+ }
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5ANIM file
+void MD5Importer::LoadMD5AnimFile() {
+ std::string pFile = mFile + "md5anim";
+ std::unique_ptr<IOStream> file(mIOHandler->Open(pFile, "rb"));
+
+ // Check whether we can read from the file
+ if (!file.get() || !file->FileSize()) {
+ ASSIMP_LOG_WARN("Failed to read MD5ANIM file: ", pFile);
+ return;
+ }
+
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer, mFileSize);
+
+ // load the animation information from the parse tree
+ MD5::MD5AnimParser animParser(parser.mSections);
+
+ // generate and fill the output animation
+ if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() ||
+ animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) {
+ ASSIMP_LOG_ERROR("MD5ANIM: No frames or animated bones loaded");
+ } else {
+ mHadMD5Anim = true;
+
+ mScene->mAnimations = new aiAnimation *[mScene->mNumAnimations = 1];
+ aiAnimation *anim = mScene->mAnimations[0] = new aiAnimation();
+ anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
+ anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
+ for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
+ aiNodeAnim *node = anim->mChannels[i] = new aiNodeAnim();
+ node->mNodeName = aiString(animParser.mAnimatedBones[i].mName);
+
+ // allocate storage for the keyframes
+ node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
+ node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
+ }
+
+ // 1 tick == 1 frame
+ anim->mTicksPerSecond = animParser.fFrameRate;
+
+ for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end(); iter != iterEnd; ++iter) {
+ double dTime = (double)(*iter).iIndex;
+ aiNodeAnim **pcAnimNode = anim->mChannels;
+ if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
+ {
+ // now process all values in there ... read all joints
+ MD5::BaseFrameDesc *pcBaseFrame = &animParser.mBaseFrames[0];
+ for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end(); ++iter2,
+ ++pcAnimNode, ++pcBaseFrame) {
+ if ((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
+
+ // Allow for empty frames
+ if ((*iter2).iFlags != 0) {
+ throw DeadlyImportError("MD5: Keyframe index is out of range");
+ }
+ continue;
+ }
+ const float *fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
+ aiNodeAnim *pcCurAnimBone = *pcAnimNode;
+
+ aiVectorKey *vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
+ aiQuatKey *qKey = &pcCurAnimBone->mRotationKeys[pcCurAnimBone->mNumRotationKeys++];
+ aiVector3D vTemp;
+
+ // translational component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (1u << i)) {
+ vKey->mValue[i] = *fpCur++;
+ } else
+ vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
+ }
+
+ // orientation component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (8u << i)) {
+ vTemp[i] = *fpCur++;
+ } else
+ vTemp[i] = pcBaseFrame->vRotationQuat[i];
+ }
+
+ MD5::ConvertQuaternion(vTemp, qKey->mValue);
+ qKey->mTime = vKey->mTime = dTime;
+ }
+ }
+
+ // compute the duration of the animation
+ anim->mDuration = std::max(dTime, anim->mDuration);
+ }
+
+ // If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
+ // construct it now from the data given in the MD5ANIM.
+ if (!mScene->mRootNode) {
+ mScene->mRootNode = new aiNode();
+ mScene->mRootNode->mName.Set("<MD5_Hierarchy>");
+
+ AttachChilds_Anim(-1, mScene->mRootNode, animParser.mAnimatedBones, (const aiNodeAnim **)anim->mChannels);
+
+ // Call SkeletonMeshBuilder to construct a mesh to represent the shape
+ if (mScene->mRootNode->mNumChildren) {
+ SkeletonMeshBuilder skeleton_maker(mScene, mScene->mRootNode->mChildren[0]);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5CAMERA file
+void MD5Importer::LoadMD5CameraFile() {
+ std::string pFile = mFile + "md5camera";
+ std::unique_ptr<IOStream> file(mIOHandler->Open(pFile, "rb"));
+
+ // Check whether we can read from the file
+ if (!file.get() || !file->FileSize()) {
+ throw DeadlyImportError("Failed to read MD5CAMERA file: ", pFile);
+ }
+ mHadMD5Camera = true;
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer, mFileSize);
+
+ // load the camera animation data from the parse tree
+ MD5::MD5CameraParser cameraParser(parser.mSections);
+
+ if (cameraParser.frames.empty()) {
+ throw DeadlyImportError("MD5CAMERA: No frames parsed");
+ }
+
+ std::vector<unsigned int> &cuts = cameraParser.cuts;
+ std::vector<MD5::CameraAnimFrameDesc> &frames = cameraParser.frames;
+
+ // Construct output graph - a simple root with a dummy child.
+ // The root node performs the coordinate system conversion
+ aiNode *root = mScene->mRootNode = new aiNode("<MD5CameraRoot>");
+ root->mChildren = new aiNode *[root->mNumChildren = 1];
+ root->mChildren[0] = new aiNode("<MD5Camera>");
+ root->mChildren[0]->mParent = root;
+
+ // ... but with one camera assigned to it
+ mScene->mCameras = new aiCamera *[mScene->mNumCameras = 1];
+ aiCamera *cam = mScene->mCameras[0] = new aiCamera();
+ cam->mName = "<MD5Camera>";
+
+ // FIXME: Fov is currently set to the first frame's value
+ cam->mHorizontalFOV = AI_DEG_TO_RAD(frames.front().fFOV);
+
+ // every cut is written to a separate aiAnimation
+ if (!cuts.size()) {
+ cuts.push_back(0);
+ cuts.push_back(static_cast<unsigned int>(frames.size() - 1));
+ } else {
+ cuts.insert(cuts.begin(), 0);
+
+ if (cuts.back() < frames.size() - 1)
+ cuts.push_back(static_cast<unsigned int>(frames.size() - 1));
+ }
+
+ mScene->mNumAnimations = static_cast<unsigned int>(cuts.size() - 1);
+ aiAnimation **tmp = mScene->mAnimations = new aiAnimation *[mScene->mNumAnimations];
+ for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end() - 1; ++it) {
+
+ aiAnimation *anim = *tmp++ = new aiAnimation();
+ anim->mName.length = ::ai_snprintf(anim->mName.data, MAXLEN, "anim%u_from_%u_to_%u", (unsigned int)(it - cuts.begin()), (*it), *(it + 1));
+
+ anim->mTicksPerSecond = cameraParser.fFrameRate;
+ anim->mChannels = new aiNodeAnim *[anim->mNumChannels = 1];
+ aiNodeAnim *nd = anim->mChannels[0] = new aiNodeAnim();
+ nd->mNodeName.Set("<MD5Camera>");
+
+ nd->mNumPositionKeys = nd->mNumRotationKeys = *(it + 1) - (*it);
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+ nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
+ for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
+
+ nd->mPositionKeys[i].mValue = frames[*it + i].vPositionXYZ;
+ MD5::ConvertQuaternion(frames[*it + i].vRotationQuat, nd->mRotationKeys[i].mValue);
+ nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it + i;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER
diff --git a/libs/assimp/code/AssetLib/MD5/MD5Loader.h b/libs/assimp/code/AssetLib/MD5/MD5Loader.h
new file mode 100644
index 0000000..63fb1c3
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MD5/MD5Loader.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file MD5Loader.h
+ * @brief Definition of the .MD5 importer class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+*/
+#pragma once
+#ifndef AI_MD5LOADER_H_INCLUDED
+#define AI_MD5LOADER_H_INCLUDED
+
+#include "MD5Parser.h"
+#include <assimp/BaseImporter.h>
+#include <assimp/types.h>
+
+struct aiNode;
+struct aiNodeAnim;
+
+namespace Assimp {
+
+class IOStream;
+using namespace Assimp::MD5;
+
+// ---------------------------------------------------------------------------
+/** Importer class for the MD5 file format
+*/
+class MD5Importer : public BaseImporter {
+public:
+ MD5Importer();
+ ~MD5Importer() override;
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+ bool checkSig) const override;
+
+protected:
+ // -------------------------------------------------------------------
+ /** Return importer meta information.
+ * See #BaseImporter::GetInfo for the details
+ */
+ const aiImporterDesc *GetInfo() const override;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer *pImp) override;
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile(const std::string &pFile, aiScene *pScene,
+ IOSystem *pIOHandler) override;
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5MESH file.
+ */
+ void LoadMD5MeshFile();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5ANIM file.
+ */
+ void LoadMD5AnimFile();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5CAMERA file.
+ */
+ void LoadMD5CameraFile();
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5ANIM
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ * @param node_anims Generated node animations
+ */
+ void AttachChilds_Anim(int iParentID, aiNode *piParent,
+ AnimBoneList &bones, const aiNodeAnim **node_anims);
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5MESH
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ */
+ void AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneList &bones);
+
+ // -------------------------------------------------------------------
+ /** Build unique vertex buffers from a given MD5ANIM
+ * @param meshSrc Input data
+ */
+ void MakeDataUnique(MD5::MeshDesc &meshSrc);
+
+ // -------------------------------------------------------------------
+ /** Load the contents of a specific file into memory and
+ * allocates a buffer to keep it.
+ *
+ * mBuffer is modified to point to this buffer.
+ * @param pFile File stream to be read
+ */
+ void LoadFileIntoMemory(IOStream *pFile);
+ void UnloadFileFromMemory();
+
+ /** IOSystem to be used to access files */
+ IOSystem *mIOHandler;
+
+ /** Path to the file, excluding the file extension but
+ with the dot */
+ std::string mFile;
+
+ /** Buffer to hold the loaded file */
+ char *mBuffer;
+
+ /** Size of the file */
+ unsigned int mFileSize;
+
+ /** Current line number. For debugging purposes */
+ unsigned int mLineNumber;
+
+ /** Scene to be filled */
+ aiScene *mScene;
+
+ /** true if a MD5MESH file has already been parsed */
+ bool mHadMD5Mesh;
+
+ /** true if a MD5ANIM file has already been parsed */
+ bool mHadMD5Anim;
+
+ /** true if a MD5CAMERA file has already been parsed */
+ bool mHadMD5Camera;
+
+ /** configuration option: prevent anim autoload */
+ bool mCconfigNoAutoLoad;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/libs/assimp/code/AssetLib/MD5/MD5Parser.cpp b/libs/assimp/code/AssetLib/MD5/MD5Parser.cpp
new file mode 100644
index 0000000..7ed23cb
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MD5/MD5Parser.cpp
@@ -0,0 +1,472 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file MD5Parser.cpp
+ * @brief Implementation of the MD5 parser class
+ */
+
+// internal headers
+#include "AssetLib/MD5/MD5Loader.h"
+#include "Material/MaterialSystem.h"
+
+#include <assimp/ParsingUtils.h>
+#include <assimp/StringComparison.h>
+#include <assimp/fast_atof.h>
+#include <assimp/mesh.h>
+#include <assimp/DefaultLogger.hpp>
+
+using namespace Assimp;
+using namespace Assimp::MD5;
+
+// ------------------------------------------------------------------------------------------------
+// Parse the segment structure for an MD5 file
+MD5Parser::MD5Parser(char *_buffer, unsigned int _fileSize) {
+ ai_assert(nullptr != _buffer);
+ ai_assert(0 != _fileSize);
+
+ buffer = _buffer;
+ fileSize = _fileSize;
+ lineNumber = 0;
+
+ ASSIMP_LOG_DEBUG("MD5Parser begin");
+
+ // parse the file header
+ ParseHeader();
+
+ // and read all sections until we're finished
+ bool running = true;
+ while (running) {
+ mSections.push_back(Section());
+ Section &sec = mSections.back();
+ if (!ParseSection(sec)) {
+ break;
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger()) {
+ char szBuffer[128]; // should be sufficiently large
+ ::ai_snprintf(szBuffer, 128, "MD5Parser end. Parsed %i sections", (int)mSections.size());
+ ASSIMP_LOG_DEBUG(szBuffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report error to the log stream
+/*static*/ AI_WONT_RETURN void MD5Parser::ReportError(const char *error, unsigned int line) {
+ char szBuffer[1024];
+ ::ai_snprintf(szBuffer, 1024, "[MD5] Line %u: %s", line, error);
+ throw DeadlyImportError(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report warning to the log stream
+/*static*/ void MD5Parser::ReportWarning(const char *warn, unsigned int line) {
+ char szBuffer[1024];
+ ::sprintf(szBuffer, "[MD5] Line %u: %s", line, warn);
+ ASSIMP_LOG_WARN(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse and validate the MD5 header
+void MD5Parser::ParseHeader() {
+ // parse and validate the file version
+ SkipSpaces();
+ if (!TokenMatch(buffer, "MD5Version", 10)) {
+ ReportError("Invalid MD5 file: MD5Version tag has not been found");
+ }
+ SkipSpaces();
+ unsigned int iVer = ::strtoul10(buffer, (const char **)&buffer);
+ if (10 != iVer) {
+ ReportError("MD5 version tag is unknown (10 is expected)");
+ }
+ SkipLine();
+
+ // print the command line options to the console
+ // FIX: can break the log length limit, so we need to be careful
+ char *sz = buffer;
+ while (!IsLineEnd(*buffer++))
+ ;
+ ASSIMP_LOG_INFO(std::string(sz, std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer - sz))));
+ SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive MD5 parsing function
+bool MD5Parser::ParseSection(Section &out) {
+ // store the current line number for use in error messages
+ out.iLineNumber = lineNumber;
+
+ // first parse the name of the section
+ char *sz = buffer;
+ while (!IsSpaceOrNewLine(*buffer))
+ buffer++;
+ out.mName = std::string(sz, (uintptr_t)(buffer - sz));
+ SkipSpaces();
+
+ bool running = true;
+ while (running) {
+ if ('{' == *buffer) {
+ // it is a normal section so read all lines
+ buffer++;
+ bool run = true;
+ while (run) {
+ if (!SkipSpacesAndLineEnd()) {
+ return false; // seems this was the last section
+ }
+ if ('}' == *buffer) {
+ buffer++;
+ break;
+ }
+
+ out.mElements.push_back(Element());
+ Element &elem = out.mElements.back();
+
+ elem.iLineNumber = lineNumber;
+ elem.szStart = buffer;
+
+ // terminate the line with zero
+ while (!IsLineEnd(*buffer))
+ buffer++;
+ if (*buffer) {
+ ++lineNumber;
+ *buffer++ = '\0';
+ }
+ }
+ break;
+ } else if (!IsSpaceOrNewLine(*buffer)) {
+ // it is an element at global scope. Parse its value and go on
+ sz = buffer;
+ while (!IsSpaceOrNewLine(*buffer++))
+ ;
+ out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
+ continue;
+ }
+ break;
+ }
+ return SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Some dirty macros just because they're so funny and easy to debug
+
+// skip all spaces ... handle EOL correctly
+#define AI_MD5_SKIP_SPACES() \
+ if (!SkipSpaces(&sz)) \
+ MD5Parser::ReportWarning("Unexpected end of line", elem.iLineNumber);
+
+// read a triple float in brackets: (1.0 1.0 1.0)
+#define AI_MD5_READ_TRIPLE(vec) \
+ AI_MD5_SKIP_SPACES(); \
+ if ('(' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atoreal_move<float>(sz, (float &)vec.x); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atoreal_move<float>(sz, (float &)vec.y); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atoreal_move<float>(sz, (float &)vec.z); \
+ AI_MD5_SKIP_SPACES(); \
+ if (')' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber);
+
+// parse a string, enclosed in quotation marks or not
+#define AI_MD5_PARSE_STRING(out) \
+ bool bQuota = (*sz == '\"'); \
+ const char *szStart = sz; \
+ while (!IsSpaceOrNewLine(*sz)) \
+ ++sz; \
+ const char *szEnd = sz; \
+ if (bQuota) { \
+ szStart++; \
+ if ('\"' != *(szEnd -= 1)) { \
+ MD5Parser::ReportWarning("Expected closing quotation marks in string", \
+ elem.iLineNumber); \
+ continue; \
+ } \
+ } \
+ out.length = (size_t)(szEnd - szStart); \
+ ::memcpy(out.data, szStart, out.length); \
+ out.data[out.length] = '\0';
+
+// parse a string, enclosed in quotation marks
+#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \
+ while ('\"' != *sz) \
+ ++sz; \
+ const char *szStart = ++sz; \
+ while ('\"' != *sz) \
+ ++sz; \
+ const char *szEnd = (sz++); \
+ out.length = (ai_uint32)(szEnd - szStart); \
+ ::memcpy(out.data, szStart, out.length); \
+ out.data[out.length] = '\0';
+// ------------------------------------------------------------------------------------------------
+// .MD5MESH parsing function
+MD5MeshParser::MD5MeshParser(SectionList &mSections) {
+ ASSIMP_LOG_DEBUG("MD5MeshParser begin");
+
+ // now parse all sections
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+ if ((*iter).mName == "numMeshes") {
+ mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "numJoints") {
+ mJoints.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "joints") {
+ // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
+ for (const auto &elem : (*iter).mElements) {
+ mJoints.push_back(BoneDesc());
+ BoneDesc &desc = mJoints.back();
+
+ const char *sz = elem.szStart;
+ AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // negative values, at least -1, is allowed here
+ desc.mParentIndex = (int)strtol10(sz, &sz);
+
+ AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
+ }
+ } else if ((*iter).mName == "mesh") {
+ mMeshes.push_back(MeshDesc());
+ MeshDesc &desc = mMeshes.back();
+
+ for (const auto &elem : (*iter).mElements) {
+ const char *sz = elem.szStart;
+
+ // shader attribute
+ if (TokenMatch(sz, "shader", 6)) {
+ AI_MD5_SKIP_SPACES();
+ AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mShader);
+ }
+ // numverts attribute
+ else if (TokenMatch(sz, "numverts", 8)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mVertices.resize(strtoul10(sz));
+ }
+ // numtris attribute
+ else if (TokenMatch(sz, "numtris", 7)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mFaces.resize(strtoul10(sz));
+ }
+ // numweights attribute
+ else if (TokenMatch(sz, "numweights", 10)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mWeights.resize(strtoul10(sz));
+ }
+ // vert attribute
+ // "vert 0 ( 0.394531 0.513672 ) 0 1"
+ else if (TokenMatch(sz, "vert", 4)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = ::strtoul10(sz, &sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mVertices.size())
+ desc.mVertices.resize(idx + 1);
+
+ VertexDesc &vert = desc.mVertices[idx];
+ if ('(' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atoreal_move<float>(sz, (float &)vert.mUV.x);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atoreal_move<float>(sz, (float &)vert.mUV.y);
+ AI_MD5_SKIP_SPACES();
+ if (')' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ vert.mFirstWeight = ::strtoul10(sz, &sz);
+ AI_MD5_SKIP_SPACES();
+ vert.mNumWeights = ::strtoul10(sz, &sz);
+ }
+ // tri attribute
+ // "tri 0 15 13 12"
+ else if (TokenMatch(sz, "tri", 3)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtoul10(sz, &sz);
+ if (idx >= desc.mFaces.size())
+ desc.mFaces.resize(idx + 1);
+
+ aiFace &face = desc.mFaces[idx];
+ face.mIndices = new unsigned int[face.mNumIndices = 3];
+ for (unsigned int i = 0; i < 3; ++i) {
+ AI_MD5_SKIP_SPACES();
+ face.mIndices[i] = strtoul10(sz, &sz);
+ }
+ }
+ // weight attribute
+ // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
+ else if (TokenMatch(sz, "weight", 6)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtoul10(sz, &sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mWeights.size())
+ desc.mWeights.resize(idx + 1);
+
+ WeightDesc &weight = desc.mWeights[idx];
+ weight.mBone = strtoul10(sz, &sz);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atoreal_move<float>(sz, weight.mWeight);
+ AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
+ }
+ }
+ }
+ }
+ ASSIMP_LOG_DEBUG("MD5MeshParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5ANIM parsing function
+MD5AnimParser::MD5AnimParser(SectionList &mSections) {
+ ASSIMP_LOG_DEBUG("MD5AnimParser begin");
+
+ fFrameRate = 24.0f;
+ mNumAnimatedComponents = UINT_MAX;
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+ if ((*iter).mName == "hierarchy") {
+ // "sheath" 0 63 6
+ for (const auto &elem : (*iter).mElements) {
+ mAnimatedBones.push_back(AnimBoneDesc());
+ AnimBoneDesc &desc = mAnimatedBones.back();
+
+ const char *sz = elem.szStart;
+ AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // parent index - negative values are allowed (at least -1)
+ desc.mParentIndex = ::strtol10(sz, &sz);
+
+ // flags (highest is 2^6-1)
+ AI_MD5_SKIP_SPACES();
+ if (63 < (desc.iFlags = ::strtoul10(sz, &sz))) {
+ MD5Parser::ReportWarning("Invalid flag combination in hierarchy section", elem.iLineNumber);
+ }
+ AI_MD5_SKIP_SPACES();
+
+ // index of the first animation keyframe component for this joint
+ desc.iFirstKeyIndex = ::strtoul10(sz, &sz);
+ }
+ } else if ((*iter).mName == "baseframe") {
+ // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
+ for (const auto &elem : (*iter).mElements) {
+ const char *sz = elem.szStart;
+
+ mBaseFrames.push_back(BaseFrameDesc());
+ BaseFrameDesc &desc = mBaseFrames.back();
+
+ AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.vRotationQuat);
+ }
+ } else if ((*iter).mName == "frame") {
+ if (!(*iter).mGlobalValue.length()) {
+ MD5Parser::ReportWarning("A frame section must have a frame index", (*iter).iLineNumber);
+ continue;
+ }
+
+ mFrames.push_back(FrameDesc());
+ FrameDesc &desc = mFrames.back();
+ desc.iIndex = strtoul10((*iter).mGlobalValue.c_str());
+
+ // we do already know how much storage we will presumably need
+ if (UINT_MAX != mNumAnimatedComponents) {
+ desc.mValues.reserve(mNumAnimatedComponents);
+ }
+
+ // now read all elements (continuous list of floats)
+ for (const auto &elem : (*iter).mElements) {
+ const char *sz = elem.szStart;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ float f;
+ sz = fast_atoreal_move<float>(sz, f);
+ desc.mValues.push_back(f);
+ }
+ }
+ } else if ((*iter).mName == "numFrames") {
+ mFrames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "numJoints") {
+ const unsigned int num = strtoul10((*iter).mGlobalValue.c_str());
+ mAnimatedBones.reserve(num);
+
+ // try to guess the number of animated components if that element is not given
+ if (UINT_MAX == mNumAnimatedComponents) {
+ mNumAnimatedComponents = num * 6;
+ }
+ } else if ((*iter).mName == "numAnimatedComponents") {
+ mAnimatedBones.reserve(strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "frameRate") {
+ fast_atoreal_move<float>((*iter).mGlobalValue.c_str(), fFrameRate);
+ }
+ }
+ ASSIMP_LOG_DEBUG("MD5AnimParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5CAMERA parsing function
+MD5CameraParser::MD5CameraParser(SectionList &mSections) {
+ ASSIMP_LOG_DEBUG("MD5CameraParser begin");
+ fFrameRate = 24.0f;
+
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+ if ((*iter).mName == "numFrames") {
+ frames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "frameRate") {
+ fFrameRate = fast_atof((*iter).mGlobalValue.c_str());
+ } else if ((*iter).mName == "numCuts") {
+ cuts.reserve(strtoul10((*iter).mGlobalValue.c_str()));
+ } else if ((*iter).mName == "cuts") {
+ for (const auto &elem : (*iter).mElements) {
+ cuts.push_back(strtoul10(elem.szStart) + 1);
+ }
+ } else if ((*iter).mName == "camera") {
+ for (const auto &elem : (*iter).mElements) {
+ const char *sz = elem.szStart;
+
+ frames.push_back(CameraAnimFrameDesc());
+ CameraAnimFrameDesc &cur = frames.back();
+ AI_MD5_READ_TRIPLE(cur.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(cur.vRotationQuat);
+ AI_MD5_SKIP_SPACES();
+ cur.fFOV = fast_atof(sz);
+ }
+ }
+ }
+ ASSIMP_LOG_DEBUG("MD5CameraParser end");
+}
diff --git a/libs/assimp/code/AssetLib/MD5/MD5Parser.h b/libs/assimp/code/AssetLib/MD5/MD5Parser.h
new file mode 100644
index 0000000..3108655
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MD5/MD5Parser.h
@@ -0,0 +1,467 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file MD5Parser.h
+ * @brief Definition of the .MD5 parser class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+ */
+#ifndef AI_MD5PARSER_H_INCLUDED
+#define AI_MD5PARSER_H_INCLUDED
+
+#include <assimp/types.h>
+#include <assimp/ParsingUtils.h>
+#include <vector>
+#include <stdint.h>
+
+struct aiFace;
+
+namespace Assimp {
+namespace MD5 {
+
+// ---------------------------------------------------------------------------
+/** Represents a single element in a MD5 file
+ *
+ * Elements are always contained in sections.
+*/
+struct Element
+{
+ //! Points to the starting point of the element
+ //! Whitespace at the beginning and at the end have been removed,
+ //! Elements are terminated with \0
+ char* szStart;
+
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+};
+
+typedef std::vector< Element > ElementList;
+
+// ---------------------------------------------------------------------------
+/** Represents a section of a MD5 file (such as the mesh or the joints section)
+ *
+ * A section is always enclosed in { and } brackets.
+*/
+struct Section
+{
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+
+ //! List of all elements which have been parsed in this section.
+ ElementList mElements;
+
+ //! Name of the section
+ std::string mName;
+
+ //! For global elements: the value of the element as string
+ //! Iif !length() the section is not a global element
+ std::string mGlobalValue;
+};
+
+typedef std::vector< Section> SectionList;
+
+// ---------------------------------------------------------------------------
+/** Basic information about a joint
+*/
+struct BaseJointDescription
+{
+ //! Name of the bone
+ aiString mName;
+
+ //! Parent index of the bone
+ int mParentIndex;
+};
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Mesh file
+*/
+struct BoneDesc : BaseJointDescription
+{
+ //! Absolute position of the bone
+ aiVector3D mPositionXYZ;
+
+ //! Absolute rotation of the bone
+ aiVector3D mRotationQuat;
+ aiQuaternion mRotationQuatConverted;
+
+ //! Absolute transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mTransform;
+
+ //! Inverse transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mInvTransform;
+
+ //! Internal
+ unsigned int mMap;
+};
+
+typedef std::vector< BoneDesc > BoneList;
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Anim file
+*/
+struct AnimBoneDesc : BaseJointDescription
+{
+ //! Flags (AI_MD5_ANIMATION_FLAG_xxx)
+ unsigned int iFlags;
+
+ //! Index of the first key that corresponds to this anim bone
+ unsigned int iFirstKeyIndex;
+};
+
+typedef std::vector< AnimBoneDesc > AnimBoneList;
+
+
+// ---------------------------------------------------------------------------
+/** Represents a base frame descriptor in a MD5Anim file
+*/
+struct BaseFrameDesc
+{
+ aiVector3D vPositionXYZ;
+ aiVector3D vRotationQuat;
+};
+
+typedef std::vector< BaseFrameDesc > BaseFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a camera animation frame in a MDCamera file
+*/
+struct CameraAnimFrameDesc : BaseFrameDesc
+{
+ float fFOV;
+};
+
+typedef std::vector< CameraAnimFrameDesc > CameraFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a frame descriptor in a MD5Anim file
+*/
+struct FrameDesc
+{
+ //! Index of the frame
+ unsigned int iIndex;
+
+ //! Animation keyframes - a large blob of data at first
+ std::vector< float > mValues;
+};
+
+typedef std::vector< FrameDesc > FrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex descriptor in a MD5 file
+*/
+struct VertexDesc {
+ VertexDesc() AI_NO_EXCEPT
+ : mFirstWeight(0)
+ , mNumWeights(0) {
+ // empty
+ }
+
+ //! UV coordinate of the vertex
+ aiVector2D mUV;
+
+ //! Index of the first weight of the vertex in
+ //! the vertex weight list
+ unsigned int mFirstWeight;
+
+ //! Number of weights assigned to this vertex
+ unsigned int mNumWeights;
+};
+
+typedef std::vector< VertexDesc > VertexList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex weight descriptor in a MD5 file
+*/
+struct WeightDesc
+{
+ //! Index of the bone to which this weight refers
+ unsigned int mBone;
+
+ //! The weight value
+ float mWeight;
+
+ //! The offset position of this weight
+ // ! (in the coordinate system defined by the parent bone)
+ aiVector3D vOffsetPosition;
+};
+
+typedef std::vector< WeightDesc > WeightList;
+typedef std::vector< aiFace > FaceList;
+
+// ---------------------------------------------------------------------------
+/** Represents a mesh in a MD5 file
+*/
+struct MeshDesc
+{
+ //! Weights of the mesh
+ WeightList mWeights;
+
+ //! Vertices of the mesh
+ VertexList mVertices;
+
+ //! Faces of the mesh
+ FaceList mFaces;
+
+ //! Name of the shader (=texture) to be assigned to the mesh
+ aiString mShader;
+};
+
+typedef std::vector< MeshDesc > MeshList;
+
+// ---------------------------------------------------------------------------
+// Convert a quaternion to its usual representation
+inline void ConvertQuaternion (const aiVector3D& in, aiQuaternion& out) {
+
+ out.x = in.x;
+ out.y = in.y;
+ out.z = in.z;
+
+ const float t = 1.0f - (in.x*in.x) - (in.y*in.y) - (in.z*in.z);
+
+ if (t < 0.0f)
+ out.w = 0.0f;
+ else out.w = std::sqrt (t);
+
+ // Assimp convention.
+ out.w *= -1.f;
+}
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 mesh file
+*/
+class MD5MeshParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5MeshParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ explicit MD5MeshParser(SectionList& mSections);
+
+ //! List of all meshes
+ MeshList mMeshes;
+
+ //! List of all joints
+ BoneList mJoints;
+};
+
+// remove this flag if you need to the bounding box data
+#define AI_MD5_PARSE_NO_BOUNDS
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 animation file
+*/
+class MD5AnimParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5AnimParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ explicit MD5AnimParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of animation bones
+ AnimBoneList mAnimatedBones;
+
+ //! List of base frames
+ BaseFrameList mBaseFrames;
+
+ //! List of animation frames
+ FrameList mFrames;
+
+ //! Number of animated components
+ unsigned int mNumAnimatedComponents;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 camera animation file
+*/
+class MD5CameraParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5CameraParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ explicit MD5CameraParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of cuts
+ std::vector<unsigned int> cuts;
+
+ //! Frames
+ CameraFrameList frames;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
+ * further processing)
+*/
+class MD5Parser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5Parser instance from an existing buffer.
+ *
+ * @param buffer File buffer
+ * @param fileSize Length of the file in bytes (excluding a terminal 0)
+ */
+ MD5Parser(char* buffer, unsigned int fileSize);
+
+
+ // -------------------------------------------------------------------
+ /** Report a specific error message and throw an exception
+ * @param error Error message to be reported
+ * @param line Index of the line where the error occurred
+ */
+ AI_WONT_RETURN static void ReportError (const char* error, unsigned int line) AI_WONT_RETURN_SUFFIX;
+
+ // -------------------------------------------------------------------
+ /** Report a specific warning
+ * @param warn Warn message to be reported
+ * @param line Index of the line where the error occurred
+ */
+ static void ReportWarning (const char* warn, unsigned int line);
+
+
+ void ReportError (const char* error) {
+ return ReportError(error, lineNumber);
+ }
+
+ void ReportWarning (const char* warn) {
+ return ReportWarning(warn, lineNumber);
+ }
+
+public:
+
+ //! List of all sections which have been read
+ SectionList mSections;
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Parses a file section. The current file pointer must be outside
+ * of a section.
+ * @param out Receives the section data
+ * @return true if the end of the file has been reached
+ * @throws ImportErrorException if an error occurs
+ */
+ bool ParseSection(Section& out);
+
+ // -------------------------------------------------------------------
+ /** Parses the file header
+ * @throws ImportErrorException if an error occurs
+ */
+ void ParseHeader();
+
+
+ // override these functions to make sure the line counter gets incremented
+ // -------------------------------------------------------------------
+ bool SkipLine( const char* in, const char** out)
+ {
+ ++lineNumber;
+ return Assimp::SkipLine(in,out);
+ }
+ // -------------------------------------------------------------------
+ bool SkipLine( )
+ {
+ return SkipLine(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( const char* in, const char** out)
+ {
+ bool bHad = false;
+ bool running = true;
+ while (running) {
+ if( *in == '\r' || *in == '\n') {
+ // we open files in binary mode, so there could be \r\n sequences ...
+ if (!bHad) {
+ bHad = true;
+ ++lineNumber;
+ }
+ }
+ else if (*in == '\t' || *in == ' ')bHad = false;
+ else break;
+ in++;
+ }
+ *out = in;
+ return *in != '\0';
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( )
+ {
+ return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpaces( )
+ {
+ return Assimp::SkipSpaces((const char**)&buffer);
+ }
+
+ char* buffer;
+ unsigned int fileSize;
+ unsigned int lineNumber;
+};
+}}
+
+#endif // AI_MD5PARSER_H_INCLUDED