diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/MD5/MD5Parser.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/MD5/MD5Parser.cpp | 472 |
1 files changed, 472 insertions, 0 deletions
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"); +} |