diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/X/XFileParser.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/X/XFileParser.cpp | 1360 |
1 files changed, 0 insertions, 1360 deletions
diff --git a/libs/assimp/code/AssetLib/X/XFileParser.cpp b/libs/assimp/code/AssetLib/X/XFileParser.cpp deleted file mode 100644 index 558b979..0000000 --- a/libs/assimp/code/AssetLib/X/XFileParser.cpp +++ /dev/null @@ -1,1360 +0,0 @@ -/* ---------------------------------------------------------------------------- -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 Implementation of the XFile parser helper class */ - -#ifndef ASSIMP_BUILD_NO_X_IMPORTER - -#include "XFileParser.h" -#include "XFileHelper.h" -#include <assimp/ByteSwapper.h> -#include <assimp/Exceptional.h> -#include <assimp/StringUtils.h> -#include <assimp/TinyFormatter.h> -#include <assimp/fast_atof.h> -#include <assimp/DefaultLogger.hpp> - -using namespace Assimp; -using namespace Assimp::XFile; -using namespace Assimp::Formatter; - -#ifndef ASSIMP_BUILD_NO_COMPRESSED_X - -#include "Common/Compression.h" - -// Magic identifier for MSZIP compressed data -constexpr unsigned int MSZIP_MAGIC = 0x4B43; -constexpr size_t MSZIP_BLOCK = 32786l; - -#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X - -// ------------------------------------------------------------------------------------------------ -// Throws an exception with a line number and the given text. -template<typename... T> -AI_WONT_RETURN void XFileParser::ThrowException(T&&... args) { - if (mIsBinaryFormat) { - throw DeadlyImportError(args...); - } else { - throw DeadlyImportError("Line ", mLineNumber, ": ", args...); - } -} - -// ------------------------------------------------------------------------------------------------ -// Constructor. Creates a data structure out of the XFile given in the memory block. -XFileParser::XFileParser(const std::vector<char> &pBuffer) : - mMajorVersion(0), mMinorVersion(0), mIsBinaryFormat(false), mBinaryNumCount(0), mP(nullptr), mEnd(nullptr), mLineNumber(0), mScene(nullptr) { - // vector to store uncompressed file for INFLATE'd X files - std::vector<char> uncompressed; - - // set up memory pointers - mP = &pBuffer.front(); - mEnd = mP + pBuffer.size() - 1; - - // check header - if (0 != strncmp(mP, "xof ", 4)) { - throw DeadlyImportError("Header mismatch, file is not an XFile."); - } - - // read version. It comes in a four byte format such as "0302" - mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48); - mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48); - - bool compressed = false; - - // txt - pure ASCII text format - if (strncmp(mP + 8, "txt ", 4) == 0) - mIsBinaryFormat = false; - - // bin - Binary format - else if (strncmp(mP + 8, "bin ", 4) == 0) - mIsBinaryFormat = true; - - // tzip - Inflate compressed text format - else if (strncmp(mP + 8, "tzip", 4) == 0) { - mIsBinaryFormat = false; - compressed = true; - } - // bzip - Inflate compressed binary format - else if (strncmp(mP + 8, "bzip", 4) == 0) { - mIsBinaryFormat = true; - compressed = true; - } else - ThrowException("Unsupported x-file format '", mP[8], mP[9], mP[10], mP[11], "'"); - - // float size - mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48); - - if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64) - ThrowException("Unknown float size ", mBinaryFloatSize, " specified in x-file header."); - - // The x format specifies size in bits, but we work in bytes - mBinaryFloatSize /= 8; - - mP += 16; - - // If this is a compressed X file, apply the inflate algorithm to it - if (compressed) { -#ifdef ASSIMP_BUILD_NO_COMPRESSED_X - throw DeadlyImportError("Assimp was built without compressed X support"); -#else - /* /////////////////////////////////////////////////////////////////////// - * COMPRESSED X FILE FORMAT - * /////////////////////////////////////////////////////////////////////// - * [xhead] - * 2 major - * 2 minor - * 4 type // bzip,tzip - * [mszip_master_head] - * 4 unkn // checksum? - * 2 unkn // flags? (seems to be constant) - * [mszip_head] - * 2 ofs // offset to next section - * 2 magic // 'CK' - * ... ofs bytes of data - * ... next mszip_head - * - * http://www.kdedevelopers.org/node/3181 has been very helpful. - * /////////////////////////////////////////////////////////////////////// - */ - - // skip unknown data (checksum, flags?) - mP += 6; - - // First find out how much storage we'll need. Count sections. - const char *P1 = mP; - unsigned int est_out = 0; - - while (P1 + 3 < mEnd) { - // read next offset - uint16_t ofs = *((uint16_t *)P1); - AI_SWAP2(ofs); - P1 += 2; - - if (ofs >= MSZIP_BLOCK) - throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block"); - - // check magic word - uint16_t magic = *((uint16_t *)P1); - AI_SWAP2(magic); - P1 += 2; - - if (magic != MSZIP_MAGIC) - throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header"); - - // and advance to the next offset - P1 += ofs; - est_out += MSZIP_BLOCK; // one decompressed block is 327861 in size - } - - // Allocate storage and terminating zero and do the actual uncompressing - Compression compression; - uncompressed.resize(est_out + 1); - char *out = &uncompressed.front(); - if (compression.open(mIsBinaryFormat ? Compression::Format::Binary : Compression::Format::ASCII, - Compression::FlushMode::SyncFlush, -Compression::MaxWBits)) { - while (mP + 3 < mEnd) { - uint16_t ofs = *((uint16_t *)mP); - AI_SWAP2(ofs); - mP += 4; - - if (mP + ofs > mEnd + 2) { - throw DeadlyImportError("X: Unexpected EOF in compressed chunk"); - } - out += compression.decompressBlock(mP, ofs, out, MSZIP_BLOCK); - mP += ofs; - } - compression.close(); - } - - // ok, update pointers to point to the uncompressed file data - mP = &uncompressed[0]; - mEnd = out; - - // FIXME: we don't need the compressed data anymore, could release - // it already for better memory usage. Consider breaking const-co. - ASSIMP_LOG_INFO("Successfully decompressed MSZIP-compressed file"); -#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X - } else { - // start reading here - ReadUntilEndOfLine(); - } - - mScene = new Scene; - ParseFile(); - - // filter the imported hierarchy for some degenerated cases - if (mScene->mRootNode) { - FilterHierarchy(mScene->mRootNode); - } -} - -// ------------------------------------------------------------------------------------------------ -// Destructor. Destroys all imported data along with it -XFileParser::~XFileParser() { - // kill everything we created - delete mScene; -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseFile() { - bool running = true; - while (running) { - // read name of next object - std::string objectName = GetNextToken(); - if (objectName.length() == 0) { - break; - } - - // parse specific object - if (objectName == "template") { - ParseDataObjectTemplate(); - } else if (objectName == "Frame") { - ParseDataObjectFrame(nullptr); - } else if (objectName == "Mesh") { - // some meshes have no frames at all - Mesh *mesh = new Mesh; - ParseDataObjectMesh(mesh); - mScene->mGlobalMeshes.push_back(mesh); - } else if (objectName == "AnimTicksPerSecond") - ParseDataObjectAnimTicksPerSecond(); - else if (objectName == "AnimationSet") - ParseDataObjectAnimationSet(); - else if (objectName == "Material") { - // Material outside of a mesh or node - Material material; - ParseDataObjectMaterial(&material); - mScene->mGlobalMaterials.push_back(material); - } else if (objectName == "}") { - // whatever? - ASSIMP_LOG_WARN("} found in dataObject"); - } else { - // unknown format - ASSIMP_LOG_WARN("Unknown data object in animation of .x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectTemplate() { - // parse a template data object. Currently not stored. - std::string name; - readHeadOfDataObject(&name); - - // read GUID - std::string guid = GetNextToken(); - - // read and ignore data members - bool running = true; - while (running) { - std::string s = GetNextToken(); - - if (s == "}") { - break; - } - - if (s.length() == 0) { - ThrowException("Unexpected end of file reached while parsing template definition"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectFrame(Node *pParent) { - // A coordinate frame, or "frame of reference." The Frame template - // is open and can contain any object. The Direct3D extensions (D3DX) - // mesh-loading functions recognize Mesh, FrameTransformMatrix, and - // Frame template instances as child objects when loading a Frame - // instance. - std::string name; - readHeadOfDataObject(&name); - - // create a named node and place it at its parent, if given - Node *node = new Node(pParent); - node->mName = name; - if (pParent) { - pParent->mChildren.push_back(node); - } else { - // there might be multiple root nodes - if (mScene->mRootNode != nullptr) { - // place a dummy root if not there - if (mScene->mRootNode->mName != "$dummy_root") { - Node *exroot = mScene->mRootNode; - mScene->mRootNode = new Node(nullptr); - mScene->mRootNode->mName = "$dummy_root"; - mScene->mRootNode->mChildren.push_back(exroot); - exroot->mParent = mScene->mRootNode; - } - // put the new node as its child instead - mScene->mRootNode->mChildren.push_back(node); - node->mParent = mScene->mRootNode; - } else { - // it's the first node imported. place it as root - mScene->mRootNode = node; - } - } - - // Now inside a frame. - // read tokens until closing brace is reached. - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - if (objectName.size() == 0) - ThrowException("Unexpected end of file reached while parsing frame"); - - if (objectName == "}") - break; // frame finished - else if (objectName == "Frame") - ParseDataObjectFrame(node); // child frame - else if (objectName == "FrameTransformMatrix") - ParseDataObjectTransformationMatrix(node->mTrafoMatrix); - else if (objectName == "Mesh") { - Mesh *mesh = new Mesh(name); - node->mMeshes.push_back(mesh); - ParseDataObjectMesh(mesh); - } else { - ASSIMP_LOG_WARN("Unknown data object in frame in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectTransformationMatrix(aiMatrix4x4 &pMatrix) { - // read header, we're not interested if it has a name - readHeadOfDataObject(); - - // read its components - pMatrix.a1 = ReadFloat(); - pMatrix.b1 = ReadFloat(); - pMatrix.c1 = ReadFloat(); - pMatrix.d1 = ReadFloat(); - pMatrix.a2 = ReadFloat(); - pMatrix.b2 = ReadFloat(); - pMatrix.c2 = ReadFloat(); - pMatrix.d2 = ReadFloat(); - pMatrix.a3 = ReadFloat(); - pMatrix.b3 = ReadFloat(); - pMatrix.c3 = ReadFloat(); - pMatrix.d3 = ReadFloat(); - pMatrix.a4 = ReadFloat(); - pMatrix.b4 = ReadFloat(); - pMatrix.c4 = ReadFloat(); - pMatrix.d4 = ReadFloat(); - - // trailing symbols - CheckForSemicolon(); - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMesh(Mesh *pMesh) { - std::string name; - readHeadOfDataObject(&name); - - // read vertex count - unsigned int numVertices = ReadInt(); - pMesh->mPositions.resize(numVertices); - - // read vertices - for (unsigned int a = 0; a < numVertices; a++) - pMesh->mPositions[a] = ReadVector3(); - - // read position faces - unsigned int numPosFaces = ReadInt(); - pMesh->mPosFaces.resize(numPosFaces); - for (unsigned int a = 0; a < numPosFaces; ++a) { - // read indices - unsigned int numIndices = ReadInt(); - Face &face = pMesh->mPosFaces[a]; - for (unsigned int b = 0; b < numIndices; ++b) { - const int idx(ReadInt()); - if (static_cast<unsigned int>(idx) <= numVertices) { - face.mIndices.push_back(idx); - } - } - TestForSeparator(); - } - - // here, other data objects may follow - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - - if (objectName.empty()) - ThrowException("Unexpected end of file while parsing mesh structure"); - else if (objectName == "}") - break; // mesh finished - else if (objectName == "MeshNormals") - ParseDataObjectMeshNormals(pMesh); - else if (objectName == "MeshTextureCoords") - ParseDataObjectMeshTextureCoords(pMesh); - else if (objectName == "MeshVertexColors") - ParseDataObjectMeshVertexColors(pMesh); - else if (objectName == "MeshMaterialList") - ParseDataObjectMeshMaterialList(pMesh); - else if (objectName == "VertexDuplicationIndices") - ParseUnknownDataObject(); // we'll ignore vertex duplication indices - else if (objectName == "XSkinMeshHeader") - ParseDataObjectSkinMeshHeader(pMesh); - else if (objectName == "SkinWeights") - ParseDataObjectSkinWeights(pMesh); - else { - ASSIMP_LOG_WARN("Unknown data object in mesh in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) { - if (nullptr == pMesh) { - return; - } - readHeadOfDataObject(); - - std::string transformNodeName; - GetNextTokenAsString(transformNodeName); - - pMesh->mBones.push_back(Bone()); - Bone &bone = pMesh->mBones.back(); - bone.mName = transformNodeName; - - // read vertex weights - unsigned int numWeights = ReadInt(); - bone.mWeights.reserve(numWeights); - - for (unsigned int a = 0; a < numWeights; a++) { - BoneWeight weight = {}; - weight.mVertex = ReadInt(); - bone.mWeights.push_back(weight); - } - - // read vertex weights - for (unsigned int a = 0; a < numWeights; a++) - bone.mWeights[a].mWeight = ReadFloat(); - - // read matrix offset - bone.mOffsetMatrix.a1 = ReadFloat(); - bone.mOffsetMatrix.b1 = ReadFloat(); - bone.mOffsetMatrix.c1 = ReadFloat(); - bone.mOffsetMatrix.d1 = ReadFloat(); - bone.mOffsetMatrix.a2 = ReadFloat(); - bone.mOffsetMatrix.b2 = ReadFloat(); - bone.mOffsetMatrix.c2 = ReadFloat(); - bone.mOffsetMatrix.d2 = ReadFloat(); - bone.mOffsetMatrix.a3 = ReadFloat(); - bone.mOffsetMatrix.b3 = ReadFloat(); - bone.mOffsetMatrix.c3 = ReadFloat(); - bone.mOffsetMatrix.d3 = ReadFloat(); - bone.mOffsetMatrix.a4 = ReadFloat(); - bone.mOffsetMatrix.b4 = ReadFloat(); - bone.mOffsetMatrix.c4 = ReadFloat(); - bone.mOffsetMatrix.d4 = ReadFloat(); - - CheckForSemicolon(); - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectSkinMeshHeader(Mesh * /*pMesh*/) { - readHeadOfDataObject(); - - /*unsigned int maxSkinWeightsPerVertex =*/ReadInt(); - /*unsigned int maxSkinWeightsPerFace =*/ReadInt(); - /*unsigned int numBonesInMesh = */ ReadInt(); - - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMeshNormals(Mesh *pMesh) { - readHeadOfDataObject(); - - // read count - unsigned int numNormals = ReadInt(); - pMesh->mNormals.resize(numNormals); - - // read normal vectors - for (unsigned int a = 0; a < numNormals; ++a) { - pMesh->mNormals[a] = ReadVector3(); - } - - // read normal indices - unsigned int numFaces = ReadInt(); - if (numFaces != pMesh->mPosFaces.size()) { - ThrowException("Normal face count does not match vertex face count."); - } - - // do not crah when no face definitions are there - if (numFaces > 0) { - // normal face creation - pMesh->mNormFaces.resize(numFaces); - for (unsigned int a = 0; a < numFaces; ++a) { - unsigned int numIndices = ReadInt(); - pMesh->mNormFaces[a] = Face(); - Face &face = pMesh->mNormFaces[a]; - for (unsigned int b = 0; b < numIndices; ++b) { - face.mIndices.push_back(ReadInt()); - } - - TestForSeparator(); - } - } - - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMeshTextureCoords(Mesh *pMesh) { - readHeadOfDataObject(); - if (pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS) - ThrowException("Too many sets of texture coordinates"); - - std::vector<aiVector2D> &coords = pMesh->mTexCoords[pMesh->mNumTextures++]; - - unsigned int numCoords = ReadInt(); - if (numCoords != pMesh->mPositions.size()) - ThrowException("Texture coord count does not match vertex count"); - - coords.resize(numCoords); - for (unsigned int a = 0; a < numCoords; a++) - coords[a] = ReadVector2(); - - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMeshVertexColors(Mesh *pMesh) { - readHeadOfDataObject(); - if (pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS) - ThrowException("Too many colorsets"); - std::vector<aiColor4D> &colors = pMesh->mColors[pMesh->mNumColorSets++]; - - unsigned int numColors = ReadInt(); - if (numColors != pMesh->mPositions.size()) - ThrowException("Vertex color count does not match vertex count"); - - colors.resize(numColors, aiColor4D(0, 0, 0, 1)); - for (unsigned int a = 0; a < numColors; a++) { - unsigned int index = ReadInt(); - if (index >= pMesh->mPositions.size()) - ThrowException("Vertex color index out of bounds"); - - colors[index] = ReadRGBA(); - // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma. - // Ignore gracefully. - if (!mIsBinaryFormat) { - FindNextNoneWhiteSpace(); - if (*mP == ';' || *mP == ',') - mP++; - } - } - - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMeshMaterialList(Mesh *pMesh) { - readHeadOfDataObject(); - - // read material count - /*unsigned int numMaterials =*/ReadInt(); - // read non triangulated face material index count - unsigned int numMatIndices = ReadInt(); - - // some models have a material index count of 1... to be able to read them we - // replicate this single material index on every face - if (numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1) - ThrowException("Per-Face material index count does not match face count."); - - // read per-face material indices - for (unsigned int a = 0; a < numMatIndices; a++) - pMesh->mFaceMaterials.push_back(ReadInt()); - - // in version 03.02, the face indices end with two semicolons. - // commented out version check, as version 03.03 exported from blender also has 2 semicolons - if (!mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) - { - if (mP < mEnd && *mP == ';') - ++mP; - } - - // if there was only a single material index, replicate it on all faces - while (pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size()) - pMesh->mFaceMaterials.push_back(pMesh->mFaceMaterials.front()); - - // read following data objects - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - if (objectName.size() == 0) - ThrowException("Unexpected end of file while parsing mesh material list."); - else if (objectName == "}") - break; // material list finished - else if (objectName == "{") { - // template materials - std::string matName = GetNextToken(); - Material material; - material.mIsReference = true; - material.mName = matName; - pMesh->mMaterials.push_back(material); - - CheckForClosingBrace(); // skip } - } else if (objectName == "Material") { - pMesh->mMaterials.push_back(Material()); - ParseDataObjectMaterial(&pMesh->mMaterials.back()); - } else if (objectName == ";") { - // ignore - } else { - ASSIMP_LOG_WARN("Unknown data object in material list in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectMaterial(Material *pMaterial) { - std::string matName; - readHeadOfDataObject(&matName); - if (matName.empty()) - matName = std::string("material") + ai_to_string(mLineNumber); - pMaterial->mName = matName; - pMaterial->mIsReference = false; - - // read material values - pMaterial->mDiffuse = ReadRGBA(); - pMaterial->mSpecularExponent = ReadFloat(); - pMaterial->mSpecular = ReadRGB(); - pMaterial->mEmissive = ReadRGB(); - - // read other data objects - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - if (objectName.size() == 0) - ThrowException("Unexpected end of file while parsing mesh material"); - else if (objectName == "}") - break; // material finished - else if (objectName == "TextureFilename" || objectName == "TextureFileName") { - // some exporters write "TextureFileName" instead. - std::string texname; - ParseDataObjectTextureFilename(texname); - pMaterial->mTextures.push_back(TexEntry(texname)); - } else if (objectName == "NormalmapFilename" || objectName == "NormalmapFileName") { - // one exporter writes out the normal map in a separate filename tag - std::string texname; - ParseDataObjectTextureFilename(texname); - pMaterial->mTextures.push_back(TexEntry(texname, true)); - } else { - ASSIMP_LOG_WARN("Unknown data object in material in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectAnimTicksPerSecond() { - readHeadOfDataObject(); - mScene->mAnimTicksPerSecond = ReadInt(); - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectAnimationSet() { - std::string animName; - readHeadOfDataObject(&animName); - - Animation *anim = new Animation; - mScene->mAnims.push_back(anim); - anim->mName = animName; - - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - if (objectName.length() == 0) - ThrowException("Unexpected end of file while parsing animation set."); - else if (objectName == "}") - break; // animation set finished - else if (objectName == "Animation") - ParseDataObjectAnimation(anim); - else { - ASSIMP_LOG_WARN("Unknown data object in animation set in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectAnimation(Animation *pAnim) { - readHeadOfDataObject(); - AnimBone *banim = new AnimBone; - pAnim->mAnims.push_back(banim); - - bool running = true; - while (running) { - std::string objectName = GetNextToken(); - - if (objectName.length() == 0) - ThrowException("Unexpected end of file while parsing animation."); - else if (objectName == "}") - break; // animation finished - else if (objectName == "AnimationKey") - ParseDataObjectAnimationKey(banim); - else if (objectName == "AnimationOptions") - ParseUnknownDataObject(); // not interested - else if (objectName == "{") { - // read frame name - banim->mBoneName = GetNextToken(); - CheckForClosingBrace(); - } else { - ASSIMP_LOG_WARN("Unknown data object in animation in x file"); - ParseUnknownDataObject(); - } - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) { - readHeadOfDataObject(); - - // read key type - unsigned int keyType = ReadInt(); - - // read number of keys - unsigned int numKeys = ReadInt(); - - for (unsigned int a = 0; a < numKeys; a++) { - // read time - unsigned int time = ReadInt(); - - // read keys - switch (keyType) { - case 0: // rotation quaternion - { - // read count - if (ReadInt() != 4) - ThrowException("Invalid number of arguments for quaternion key in animation"); - - aiQuatKey key; - key.mTime = double(time); - key.mValue.w = ReadFloat(); - key.mValue.x = ReadFloat(); - key.mValue.y = ReadFloat(); - key.mValue.z = ReadFloat(); - pAnimBone->mRotKeys.push_back(key); - - CheckForSemicolon(); - break; - } - - case 1: // scale vector - case 2: // position vector - { - // read count - if (ReadInt() != 3) - ThrowException("Invalid number of arguments for vector key in animation"); - - aiVectorKey key; - key.mTime = double(time); - key.mValue = ReadVector3(); - - if (keyType == 2) - pAnimBone->mPosKeys.push_back(key); - else - pAnimBone->mScaleKeys.push_back(key); - - break; - } - - case 3: // combined transformation matrix - case 4: // denoted both as 3 or as 4 - { - // read count - if (ReadInt() != 16) - ThrowException("Invalid number of arguments for matrix key in animation"); - - // read matrix - MatrixKey key; - key.mTime = double(time); - key.mMatrix.a1 = ReadFloat(); - key.mMatrix.b1 = ReadFloat(); - key.mMatrix.c1 = ReadFloat(); - key.mMatrix.d1 = ReadFloat(); - key.mMatrix.a2 = ReadFloat(); - key.mMatrix.b2 = ReadFloat(); - key.mMatrix.c2 = ReadFloat(); - key.mMatrix.d2 = ReadFloat(); - key.mMatrix.a3 = ReadFloat(); - key.mMatrix.b3 = ReadFloat(); - key.mMatrix.c3 = ReadFloat(); - key.mMatrix.d3 = ReadFloat(); - key.mMatrix.a4 = ReadFloat(); - key.mMatrix.b4 = ReadFloat(); - key.mMatrix.c4 = ReadFloat(); - key.mMatrix.d4 = ReadFloat(); - pAnimBone->mTrafoKeys.push_back(key); - - CheckForSemicolon(); - break; - } - - default: - ThrowException("Unknown key type ", keyType, " in animation."); - break; - } // end switch - - // key separator - CheckForSeparator(); - } - - CheckForClosingBrace(); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectTextureFilename(std::string &pName) { - readHeadOfDataObject(); - GetNextTokenAsString(pName); - CheckForClosingBrace(); - - // FIX: some files (e.g. AnimationTest.x) have "" as texture file name - if (!pName.length()) { - ASSIMP_LOG_WARN("Length of texture file name is zero. Skipping this texture."); - } - - // some exporters write double backslash paths out. We simply replace them if we find them - while (pName.find("\\\\") != std::string::npos) - pName.replace(pName.find("\\\\"), 2, "\\"); -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ParseUnknownDataObject() { - // find opening delimiter - bool running = true; - while (running) { - std::string t = GetNextToken(); - if (t.length() == 0) - ThrowException("Unexpected end of file while parsing unknown segment."); - - if (t == "{") - break; - } - - unsigned int counter = 1; - - // parse until closing delimiter - while (counter > 0) { - std::string t = GetNextToken(); - - if (t.length() == 0) - ThrowException("Unexpected end of file while parsing unknown segment."); - - if (t == "{") - ++counter; - else if (t == "}") - --counter; - } -} - -// ------------------------------------------------------------------------------------------------ -//! checks for closing curly brace -void XFileParser::CheckForClosingBrace() { - if (GetNextToken() != "}") - ThrowException("Closing brace expected."); -} - -// ------------------------------------------------------------------------------------------------ -//! checks for one following semicolon -void XFileParser::CheckForSemicolon() { - if (mIsBinaryFormat) - return; - - if (GetNextToken() != ";") - ThrowException("Semicolon expected."); -} - -// ------------------------------------------------------------------------------------------------ -//! checks for a separator char, either a ',' or a ';' -void XFileParser::CheckForSeparator() { - if (mIsBinaryFormat) - return; - - std::string token = GetNextToken(); - if (token != "," && token != ";") - ThrowException("Separator character (';' or ',') expected."); -} - -// ------------------------------------------------------------------------------------------------ -// tests and possibly consumes a separator char, but does nothing if there was no separator -void XFileParser::TestForSeparator() { - if (mIsBinaryFormat) - return; - - FindNextNoneWhiteSpace(); - if (mP >= mEnd) - return; - - // test and skip - if (*mP == ';' || *mP == ',') - mP++; -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::readHeadOfDataObject(std::string *poName) { - std::string nameOrBrace = GetNextToken(); - if (nameOrBrace != "{") { - if (poName) - *poName = nameOrBrace; - - if (GetNextToken() != "{") { - delete mScene; - ThrowException("Opening brace expected."); - } - } -} - -// ------------------------------------------------------------------------------------------------ -std::string XFileParser::GetNextToken() { - std::string s; - - // process binary-formatted file - if (mIsBinaryFormat) { - // in binary mode it will only return NAME and STRING token - // and (correctly) skip over other tokens. - if (mEnd - mP < 2) { - return s; - } - unsigned int tok = ReadBinWord(); - unsigned int len; - - // standalone tokens - switch (tok) { - case 1: { - // name token - if (mEnd - mP < 4) { - return s; - } - len = ReadBinDWord(); - const int bounds = int(mEnd - mP); - const int iLen = int(len); - if (iLen < 0) { - return s; - } - if (bounds < iLen) { - return s; - } - s = std::string(mP, len); - mP += len; - } - return s; - - case 2: - // string token - if (mEnd - mP < 4) return s; - len = ReadBinDWord(); - if (mEnd - mP < int(len)) return s; - s = std::string(mP, len); - mP += (len + 2); - return s; - case 3: - // integer token - mP += 4; - return "<integer>"; - case 5: - // GUID token - mP += 16; - return "<guid>"; - case 6: - if (mEnd - mP < 4) return s; - len = ReadBinDWord(); - mP += (len * 4); - return "<int_list>"; - case 7: - if (mEnd - mP < 4) return s; - len = ReadBinDWord(); - mP += (len * mBinaryFloatSize); - return "<flt_list>"; - case 0x0a: - return "{"; - case 0x0b: - return "}"; - case 0x0c: - return "("; - case 0x0d: - return ")"; - case 0x0e: - return "["; - case 0x0f: - return "]"; - case 0x10: - return "<"; - case 0x11: - return ">"; - case 0x12: - return "."; - case 0x13: - return ","; - case 0x14: - return ";"; - case 0x1f: - return "template"; - case 0x28: - return "WORD"; - case 0x29: - return "DWORD"; - case 0x2a: - return "FLOAT"; - case 0x2b: - return "DOUBLE"; - case 0x2c: - return "CHAR"; - case 0x2d: - return "UCHAR"; - case 0x2e: - return "SWORD"; - case 0x2f: - return "SDWORD"; - case 0x30: - return "void"; - case 0x31: - return "string"; - case 0x32: - return "unicode"; - case 0x33: - return "cstring"; - case 0x34: - return "array"; - } - } - // process text-formatted file - else { - FindNextNoneWhiteSpace(); - if (mP >= mEnd) - return s; - - while ((mP < mEnd) && !isspace((unsigned char)*mP)) { - // either keep token delimiters when already holding a token, or return if first valid char - if (*mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') { - if (!s.size()) - s.append(mP++, 1); - break; // stop for delimiter - } - s.append(mP++, 1); - } - } - return s; -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::FindNextNoneWhiteSpace() { - if (mIsBinaryFormat) - return; - - bool running = true; - while (running) { - while (mP < mEnd && isspace((unsigned char)*mP)) { - if (*mP == '\n') - mLineNumber++; - ++mP; - } - - if (mP >= mEnd) - return; - - // check if this is a comment - if ((mP[0] == '/' && mP[1] == '/') || mP[0] == '#') - ReadUntilEndOfLine(); - else - break; - } -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::GetNextTokenAsString(std::string &poString) { - if (mIsBinaryFormat) { - poString = GetNextToken(); - return; - } - - FindNextNoneWhiteSpace(); - if (mP >= mEnd) { - delete mScene; - ThrowException("Unexpected end of file while parsing string"); - } - - if (*mP != '"') { - delete mScene; - ThrowException("Expected quotation mark."); - } - ++mP; - - while (mP < mEnd && *mP != '"') - poString.append(mP++, 1); - - if (mP >= mEnd - 1) { - delete mScene; - ThrowException("Unexpected end of file while parsing string"); - } - - if (mP[1] != ';' || mP[0] != '"') { - delete mScene; - ThrowException("Expected quotation mark and semicolon at the end of a string."); - } - mP += 2; -} - -// ------------------------------------------------------------------------------------------------ -void XFileParser::ReadUntilEndOfLine() { - if (mIsBinaryFormat) - return; - - while (mP < mEnd) { - if (*mP == '\n' || *mP == '\r') { - ++mP; - mLineNumber++; - return; - } - - ++mP; - } -} - -// ------------------------------------------------------------------------------------------------ -unsigned short XFileParser::ReadBinWord() { - ai_assert(mEnd - mP >= 2); - const unsigned char *q = (const unsigned char *)mP; - unsigned short tmp = q[0] | (q[1] << 8); - mP += 2; - return tmp; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int XFileParser::ReadBinDWord() { - ai_assert(mEnd - mP >= 4); - - const unsigned char *q = (const unsigned char *)mP; - unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); - mP += 4; - return tmp; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int XFileParser::ReadInt() { - if (mIsBinaryFormat) { - if (mBinaryNumCount == 0 && mEnd - mP >= 2) { - unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 - if (tmp == 0x06 && mEnd - mP >= 4) // array of ints follows - mBinaryNumCount = ReadBinDWord(); - else // single int follows - mBinaryNumCount = 1; - } - - --mBinaryNumCount; - const size_t len(mEnd - mP); - if (len >= 4) { - return ReadBinDWord(); - } else { - mP = mEnd; - return 0; - } - } else { - FindNextNoneWhiteSpace(); - - // TODO: consider using strtol10 instead??? - - // check preceding minus sign - bool isNegative = false; - if (*mP == '-') { - isNegative = true; - mP++; - } - - // at least one digit expected - if (!isdigit((unsigned char)*mP)) - ThrowException("Number expected."); - - // read digits - unsigned int number = 0; - while (mP < mEnd) { - if (!isdigit((unsigned char)*mP)) - break; - number = number * 10 + (*mP - 48); - mP++; - } - - CheckForSeparator(); - - return isNegative ? ((unsigned int)-int(number)) : number; - } -} - -// ------------------------------------------------------------------------------------------------ -ai_real XFileParser::ReadFloat() { - if (mIsBinaryFormat) { - if (mBinaryNumCount == 0 && mEnd - mP >= 2) { - unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 - if (tmp == 0x07 && mEnd - mP >= 4) // array of floats following - mBinaryNumCount = ReadBinDWord(); - else // single float following - mBinaryNumCount = 1; - } - - --mBinaryNumCount; - if (mBinaryFloatSize == 8) { - if (mEnd - mP >= 8) { - double res; - ::memcpy(&res, mP, 8); - mP += 8; - const ai_real result(static_cast<ai_real>(res)); - return result; - } else { - mP = mEnd; - return 0; - } - } else { - if (mEnd - mP >= 4) { - ai_real result; - ::memcpy(&result, mP, 4); - mP += 4; - return result; - } else { - mP = mEnd; - return 0; - } - } - } - - // text version - FindNextNoneWhiteSpace(); - // check for various special strings to allow reading files from faulty exporters - // I mean you, Blender! - // Reading is safe because of the terminating zero - if (strncmp(mP, "-1.#IND00", 9) == 0 || strncmp(mP, "1.#IND00", 8) == 0) { - mP += 9; - CheckForSeparator(); - return 0.0; - } else if (strncmp(mP, "1.#QNAN0", 8) == 0) { - mP += 8; - CheckForSeparator(); - return 0.0; - } - - ai_real result = 0.0; - mP = fast_atoreal_move<ai_real>(mP, result); - - CheckForSeparator(); - - return result; -} - -// ------------------------------------------------------------------------------------------------ -aiVector2D XFileParser::ReadVector2() { - aiVector2D vector; - vector.x = ReadFloat(); - vector.y = ReadFloat(); - TestForSeparator(); - - return vector; -} - -// ------------------------------------------------------------------------------------------------ -aiVector3D XFileParser::ReadVector3() { - aiVector3D vector; - vector.x = ReadFloat(); - vector.y = ReadFloat(); - vector.z = ReadFloat(); - TestForSeparator(); - - return vector; -} - -// ------------------------------------------------------------------------------------------------ -aiColor4D XFileParser::ReadRGBA() { - aiColor4D color; - color.r = ReadFloat(); - color.g = ReadFloat(); - color.b = ReadFloat(); - color.a = ReadFloat(); - TestForSeparator(); - - return color; -} - -// ------------------------------------------------------------------------------------------------ -aiColor3D XFileParser::ReadRGB() { - aiColor3D color; - color.r = ReadFloat(); - color.g = ReadFloat(); - color.b = ReadFloat(); - TestForSeparator(); - - return color; -} - -// ------------------------------------------------------------------------------------------------ -// Filters the imported hierarchy for some degenerated cases that some exporters produce. -void XFileParser::FilterHierarchy(XFile::Node *pNode) { - // if the node has just a single unnamed child containing a mesh, remove - // the anonymous node between. The 3DSMax kwXport plugin seems to produce this - // mess in some cases - if (pNode->mChildren.size() == 1 && pNode->mMeshes.empty()) { - XFile::Node *child = pNode->mChildren.front(); - if (child->mName.length() == 0 && child->mMeshes.size() > 0) { - // transfer its meshes to us - for (unsigned int a = 0; a < child->mMeshes.size(); a++) - pNode->mMeshes.push_back(child->mMeshes[a]); - child->mMeshes.clear(); - - // transfer the transform as well - pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix; - - // then kill it - delete child; - pNode->mChildren.clear(); - } - } - - // recurse - for (unsigned int a = 0; a < pNode->mChildren.size(); a++) - FilterHierarchy(pNode->mChildren[a]); -} - -#endif // !! ASSIMP_BUILD_NO_X_IMPORTER |