Torque3D/Engine/lib/assimp/code/AssetLib/X/XFileParser.cpp

1361 lines
44 KiB
C++
Raw Normal View History

2019-02-08 22:25:43 +00:00
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
Copyright (c) 2006-2022, assimp team
2019-02-08 22:25:43 +00:00
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"
2019-03-05 20:39:38 +00:00
#include <assimp/ByteSwapper.h>
2022-04-26 16:56:24 +00:00
#include <assimp/Exceptional.h>
2019-03-05 20:39:38 +00:00
#include <assimp/StringUtils.h>
2022-04-26 16:56:24 +00:00
#include <assimp/TinyFormatter.h>
#include <assimp/fast_atof.h>
2019-02-08 22:25:43 +00:00
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
using namespace Assimp::XFile;
using namespace Assimp::Formatter;
#ifndef ASSIMP_BUILD_NO_COMPRESSED_X
2022-04-26 16:56:24 +00:00
#include "Common/Compression.h"
2019-02-08 22:25:43 +00:00
// Magic identifier for MSZIP compressed data
2022-04-26 16:56:24 +00:00
constexpr unsigned int MSZIP_MAGIC = 0x4B43;
constexpr size_t MSZIP_BLOCK = 32786l;
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
// ------------------------------------------------------------------------------------------------
// 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...);
}
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
// Constructor. Creates a data structure out of the XFile given in the memory block.
2022-04-26 16:56:24 +00:00
XFileParser::XFileParser(const std::vector<char> &pBuffer) :
mMajorVersion(0), mMinorVersion(0), mIsBinaryFormat(false), mBinaryNumCount(0), mP(nullptr), mEnd(nullptr), mLineNumber(0), mScene(nullptr) {
2019-02-08 22:25:43 +00:00
// vector to store uncompressed file for INFLATE'd X files
std::vector<char> uncompressed;
// set up memory pointers
2019-03-05 20:39:38 +00:00
mP = &pBuffer.front();
mEnd = mP + pBuffer.size() - 1;
2019-02-08 22:25:43 +00:00
// check header
2022-04-26 16:56:24 +00:00
if (0 != strncmp(mP, "xof ", 4)) {
throw DeadlyImportError("Header mismatch, file is not an XFile.");
2019-03-05 20:39:38 +00:00
}
2019-02-08 22:25:43 +00:00
// read version. It comes in a four byte format such as "0302"
2019-03-05 20:39:38 +00:00
mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48);
mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48);
2019-02-08 22:25:43 +00:00
bool compressed = false;
// txt - pure ASCII text format
2022-04-26 16:56:24 +00:00
if (strncmp(mP + 8, "txt ", 4) == 0)
2019-02-08 22:25:43 +00:00
mIsBinaryFormat = false;
// bin - Binary format
2022-04-26 16:56:24 +00:00
else if (strncmp(mP + 8, "bin ", 4) == 0)
2019-02-08 22:25:43 +00:00
mIsBinaryFormat = true;
// tzip - Inflate compressed text format
2022-04-26 16:56:24 +00:00
else if (strncmp(mP + 8, "tzip", 4) == 0) {
2019-02-08 22:25:43 +00:00
mIsBinaryFormat = false;
compressed = true;
}
// bzip - Inflate compressed binary format
2022-04-26 16:56:24 +00:00
else if (strncmp(mP + 8, "bzip", 4) == 0) {
2019-02-08 22:25:43 +00:00
mIsBinaryFormat = true;
compressed = true;
2022-04-26 16:56:24 +00:00
} else
ThrowException("Unsupported x-file format '", mP[8], mP[9], mP[10], mP[11], "'");
2019-02-08 22:25:43 +00:00
// float size
2022-04-26 16:56:24 +00:00
mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
ThrowException("Unknown float size ", mBinaryFloatSize, " specified in x-file header.");
2019-02-08 22:25:43 +00:00
// The x format specifies size in bits, but we work in bytes
mBinaryFloatSize /= 8;
2019-03-05 20:39:38 +00:00
mP += 16;
2019-02-08 22:25:43 +00:00
// If this is a compressed X file, apply the inflate algorithm to it
2022-04-26 16:56:24 +00:00
if (compressed) {
2019-02-08 22:25:43 +00:00
#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?)
2019-03-05 20:39:38 +00:00
mP += 6;
2019-02-08 22:25:43 +00:00
// First find out how much storage we'll need. Count sections.
2022-04-26 16:56:24 +00:00
const char *P1 = mP;
2019-02-08 22:25:43 +00:00
unsigned int est_out = 0;
2022-04-26 16:56:24 +00:00
while (P1 + 3 < mEnd) {
2019-02-08 22:25:43 +00:00
// read next offset
2022-04-26 16:56:24 +00:00
uint16_t ofs = *((uint16_t *)P1);
AI_SWAP2(ofs);
P1 += 2;
2019-02-08 22:25:43 +00:00
if (ofs >= MSZIP_BLOCK)
throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block");
// check magic word
2022-04-26 16:56:24 +00:00
uint16_t magic = *((uint16_t *)P1);
AI_SWAP2(magic);
P1 += 2;
2019-02-08 22:25:43 +00:00
if (magic != MSZIP_MAGIC)
throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header");
// and advance to the next offset
P1 += ofs;
2022-04-26 16:56:24 +00:00
est_out += MSZIP_BLOCK; // one decompressed block is 327861 in size
2019-02-08 22:25:43 +00:00
}
2022-04-26 16:56:24 +00:00
2019-02-08 22:25:43 +00:00
// Allocate storage and terminating zero and do the actual uncompressing
2022-04-26 16:56:24 +00:00
Compression compression;
2019-02-08 22:25:43 +00:00
uncompressed.resize(est_out + 1);
2022-04-26 16:56:24 +00:00
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;
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (mP + ofs > mEnd + 2) {
throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
}
out += compression.decompressBlock(mP, ofs, out, MSZIP_BLOCK);
mP += ofs;
2019-02-08 22:25:43 +00:00
}
2022-04-26 16:56:24 +00:00
compression.close();
2019-02-08 22:25:43 +00:00
}
// ok, update pointers to point to the uncompressed file data
2019-03-05 20:39:38 +00:00
mP = &uncompressed[0];
mEnd = out;
2019-02-08 22:25:43 +00:00
// FIXME: we don't need the compressed data anymore, could release
// it already for better memory usage. Consider breaking const-co.
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_INFO("Successfully decompressed MSZIP-compressed file");
2019-02-08 22:25:43 +00:00
#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
2022-04-26 16:56:24 +00:00
} else {
2019-02-08 22:25:43 +00:00
// start reading here
ReadUntilEndOfLine();
}
mScene = new Scene;
ParseFile();
// filter the imported hierarchy for some degenerated cases
2022-04-26 16:56:24 +00:00
if (mScene->mRootNode) {
FilterHierarchy(mScene->mRootNode);
2019-02-08 22:25:43 +00:00
}
}
// ------------------------------------------------------------------------------------------------
// Destructor. Destroys all imported data along with it
2022-04-26 16:56:24 +00:00
XFileParser::~XFileParser() {
2019-02-08 22:25:43 +00:00
// kill everything we created
delete mScene;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseFile() {
2019-02-08 22:25:43 +00:00
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
// read name of next object
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.length() == 0) {
2019-02-08 22:25:43 +00:00
break;
2022-04-26 16:56:24 +00:00
}
2019-02-08 22:25:43 +00:00
// parse specific object
2022-04-26 16:56:24 +00:00
if (objectName == "template") {
2019-02-08 22:25:43 +00:00
ParseDataObjectTemplate();
2022-04-26 16:56:24 +00:00
} else if (objectName == "Frame") {
ParseDataObjectFrame(nullptr);
} else if (objectName == "Mesh") {
2019-02-08 22:25:43 +00:00
// some meshes have no frames at all
2022-04-26 16:56:24 +00:00
Mesh *mesh = new Mesh;
ParseDataObjectMesh(mesh);
mScene->mGlobalMeshes.push_back(mesh);
} else if (objectName == "AnimTicksPerSecond")
2019-02-08 22:25:43 +00:00
ParseDataObjectAnimTicksPerSecond();
2022-04-26 16:56:24 +00:00
else if (objectName == "AnimationSet")
2019-02-08 22:25:43 +00:00
ParseDataObjectAnimationSet();
2022-04-26 16:56:24 +00:00
else if (objectName == "Material") {
2019-02-08 22:25:43 +00:00
// Material outside of a mesh or node
Material material;
2022-04-26 16:56:24 +00:00
ParseDataObjectMaterial(&material);
mScene->mGlobalMaterials.push_back(material);
} else if (objectName == "}") {
2019-02-08 22:25:43 +00:00
// whatever?
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("} found in dataObject");
2022-04-26 16:56:24 +00:00
} else {
2019-02-08 22:25:43 +00:00
// unknown format
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in animation of .x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectTemplate() {
2019-02-08 22:25:43 +00:00
// parse a template data object. Currently not stored.
std::string name;
2022-04-26 16:56:24 +00:00
readHeadOfDataObject(&name);
2019-02-08 22:25:43 +00:00
// read GUID
std::string guid = GetNextToken();
// read and ignore data members
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string s = GetNextToken();
2022-04-26 16:56:24 +00:00
if (s == "}") {
2019-02-08 22:25:43 +00:00
break;
2022-04-26 16:56:24 +00:00
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (s.length() == 0) {
ThrowException("Unexpected end of file reached while parsing template definition");
}
2019-02-08 22:25:43 +00:00
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectFrame(Node *pParent) {
2019-02-08 22:25:43 +00:00
// 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
2022-04-26 16:56:24 +00:00
Node *node = new Node(pParent);
2019-02-08 22:25:43 +00:00
node->mName = name;
2022-04-26 16:56:24 +00:00
if (pParent) {
pParent->mChildren.push_back(node);
} else {
2019-02-08 22:25:43 +00:00
// there might be multiple root nodes
2022-04-26 16:56:24 +00:00
if (mScene->mRootNode != nullptr) {
2019-02-08 22:25:43 +00:00
// place a dummy root if not there
2022-04-26 16:56:24 +00:00
if (mScene->mRootNode->mName != "$dummy_root") {
Node *exroot = mScene->mRootNode;
mScene->mRootNode = new Node(nullptr);
2019-02-08 22:25:43 +00:00
mScene->mRootNode->mName = "$dummy_root";
2022-04-26 16:56:24 +00:00
mScene->mRootNode->mChildren.push_back(exroot);
2019-02-08 22:25:43 +00:00
exroot->mParent = mScene->mRootNode;
}
// put the new node as its child instead
2022-04-26 16:56:24 +00:00
mScene->mRootNode->mChildren.push_back(node);
2019-02-08 22:25:43 +00:00
node->mParent = mScene->mRootNode;
2022-04-26 16:56:24 +00:00
} else {
2019-02-08 22:25:43 +00:00
// 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;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
if (objectName.size() == 0)
2022-04-26 16:56:24 +00:00
ThrowException("Unexpected end of file reached while parsing frame");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // frame finished
2022-04-26 16:56:24 +00:00
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 {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in frame in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectTransformationMatrix(aiMatrix4x4 &pMatrix) {
2019-02-08 22:25:43 +00:00
// read header, we're not interested if it has a name
readHeadOfDataObject();
// read its components
2022-04-26 16:56:24 +00:00
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();
2019-02-08 22:25:43 +00:00
// trailing symbols
CheckForSemicolon();
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMesh(Mesh *pMesh) {
2019-02-08 22:25:43 +00:00
std::string name;
2022-04-26 16:56:24 +00:00
readHeadOfDataObject(&name);
2019-02-08 22:25:43 +00:00
// read vertex count
unsigned int numVertices = ReadInt();
2022-04-26 16:56:24 +00:00
pMesh->mPositions.resize(numVertices);
2019-02-08 22:25:43 +00:00
// read vertices
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numVertices; a++)
2019-02-08 22:25:43 +00:00
pMesh->mPositions[a] = ReadVector3();
// read position faces
unsigned int numPosFaces = ReadInt();
2022-04-26 16:56:24 +00:00
pMesh->mPosFaces.resize(numPosFaces);
for (unsigned int a = 0; a < numPosFaces; ++a) {
2019-02-08 22:25:43 +00:00
// read indices
unsigned int numIndices = ReadInt();
2022-04-26 16:56:24 +00:00
Face &face = pMesh->mPosFaces[a];
2019-03-05 20:39:38 +00:00
for (unsigned int b = 0; b < numIndices; ++b) {
2022-04-26 16:56:24 +00:00
const int idx(ReadInt());
if (static_cast<unsigned int>(idx) <= numVertices) {
face.mIndices.push_back(idx);
2019-03-05 20:39:38 +00:00
}
2019-02-08 22:25:43 +00:00
}
TestForSeparator();
}
// here, other data objects may follow
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.empty())
ThrowException("Unexpected end of file while parsing mesh structure");
else if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // mesh finished
2022-04-26 16:56:24 +00:00
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")
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject(); // we'll ignore vertex duplication indices
2022-04-26 16:56:24 +00:00
else if (objectName == "XSkinMeshHeader")
ParseDataObjectSkinMeshHeader(pMesh);
else if (objectName == "SkinWeights")
ParseDataObjectSkinWeights(pMesh);
else {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in mesh in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) {
if (nullptr == pMesh) {
2019-03-05 20:39:38 +00:00
return;
}
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
std::string transformNodeName;
2022-04-26 16:56:24 +00:00
GetNextTokenAsString(transformNodeName);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
pMesh->mBones.push_back(Bone());
Bone &bone = pMesh->mBones.back();
2019-02-08 22:25:43 +00:00
bone.mName = transformNodeName;
// read vertex weights
unsigned int numWeights = ReadInt();
2022-04-26 16:56:24 +00:00
bone.mWeights.reserve(numWeights);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numWeights; a++) {
BoneWeight weight = {};
2019-02-08 22:25:43 +00:00
weight.mVertex = ReadInt();
2022-04-26 16:56:24 +00:00
bone.mWeights.push_back(weight);
2019-02-08 22:25:43 +00:00
}
// read vertex weights
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numWeights; a++)
2019-02-08 22:25:43 +00:00
bone.mWeights[a].mWeight = ReadFloat();
// read matrix offset
2022-04-26 16:56:24 +00:00
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();
2019-02-08 22:25:43 +00:00
CheckForSemicolon();
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectSkinMeshHeader(Mesh * /*pMesh*/) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
2022-04-26 16:56:24 +00:00
/*unsigned int maxSkinWeightsPerVertex =*/ReadInt();
/*unsigned int maxSkinWeightsPerFace =*/ReadInt();
/*unsigned int numBonesInMesh = */ ReadInt();
2019-02-08 22:25:43 +00:00
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMeshNormals(Mesh *pMesh) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
// read count
unsigned int numNormals = ReadInt();
2022-04-26 16:56:24 +00:00
pMesh->mNormals.resize(numNormals);
2019-02-08 22:25:43 +00:00
// read normal vectors
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numNormals; ++a) {
2019-02-08 22:25:43 +00:00
pMesh->mNormals[a] = ReadVector3();
2019-11-10 14:40:50 +00:00
}
2019-02-08 22:25:43 +00:00
// read normal indices
unsigned int numFaces = ReadInt();
2022-04-26 16:56:24 +00:00
if (numFaces != pMesh->mPosFaces.size()) {
ThrowException("Normal face count does not match vertex face count.");
2019-11-10 14:40:50 +00:00
}
2019-02-08 22:25:43 +00:00
2019-11-10 14:40:50 +00:00
// do not crah when no face definitions are there
if (numFaces > 0) {
// normal face creation
2022-04-26 16:56:24 +00:00
pMesh->mNormFaces.resize(numFaces);
for (unsigned int a = 0; a < numFaces; ++a) {
2019-11-10 14:40:50 +00:00
unsigned int numIndices = ReadInt();
pMesh->mNormFaces[a] = Face();
2022-04-26 16:56:24 +00:00
Face &face = pMesh->mNormFaces[a];
for (unsigned int b = 0; b < numIndices; ++b) {
face.mIndices.push_back(ReadInt());
2019-11-10 14:40:50 +00:00
}
2019-02-08 22:25:43 +00:00
2019-11-10 14:40:50 +00:00
TestForSeparator();
}
2019-02-08 22:25:43 +00:00
}
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMeshTextureCoords(Mesh *pMesh) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
2022-04-26 16:56:24 +00:00
if (pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS)
ThrowException("Too many sets of texture coordinates");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
std::vector<aiVector2D> &coords = pMesh->mTexCoords[pMesh->mNumTextures++];
2019-02-08 22:25:43 +00:00
unsigned int numCoords = ReadInt();
2022-04-26 16:56:24 +00:00
if (numCoords != pMesh->mPositions.size())
ThrowException("Texture coord count does not match vertex count");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
coords.resize(numCoords);
for (unsigned int a = 0; a < numCoords; a++)
2019-02-08 22:25:43 +00:00
coords[a] = ReadVector2();
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMeshVertexColors(Mesh *pMesh) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
2022-04-26 16:56:24 +00:00
if (pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS)
ThrowException("Too many colorsets");
std::vector<aiColor4D> &colors = pMesh->mColors[pMesh->mNumColorSets++];
2019-02-08 22:25:43 +00:00
unsigned int numColors = ReadInt();
2022-04-26 16:56:24 +00:00
if (numColors != pMesh->mPositions.size())
ThrowException("Vertex color count does not match vertex count");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
colors.resize(numColors, aiColor4D(0, 0, 0, 1));
for (unsigned int a = 0; a < numColors; a++) {
2019-02-08 22:25:43 +00:00
unsigned int index = ReadInt();
2022-04-26 16:56:24 +00:00
if (index >= pMesh->mPositions.size())
ThrowException("Vertex color index out of bounds");
2019-02-08 22:25:43 +00:00
colors[index] = ReadRGBA();
// HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma.
// Ignore gracefully.
2022-04-26 16:56:24 +00:00
if (!mIsBinaryFormat) {
2019-02-08 22:25:43 +00:00
FindNextNoneWhiteSpace();
2022-04-26 16:56:24 +00:00
if (*mP == ';' || *mP == ',')
2019-03-05 20:39:38 +00:00
mP++;
2019-02-08 22:25:43 +00:00
}
}
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMeshMaterialList(Mesh *pMesh) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
// read material count
2022-04-26 16:56:24 +00:00
/*unsigned int numMaterials =*/ReadInt();
2019-02-08 22:25:43 +00:00
// 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
2022-04-26 16:56:24 +00:00
if (numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
ThrowException("Per-Face material index count does not match face count.");
2019-02-08 22:25:43 +00:00
// read per-face material indices
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numMatIndices; a++)
pMesh->mFaceMaterials.push_back(ReadInt());
2019-02-08 22:25:43 +00:00
// 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
2022-04-26 16:56:24 +00:00
if (!mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
2019-02-08 22:25:43 +00:00
{
2022-04-26 16:56:24 +00:00
if (mP < mEnd && *mP == ';')
2019-03-05 20:39:38 +00:00
++mP;
2019-02-08 22:25:43 +00:00
}
// if there was only a single material index, replicate it on all faces
2022-04-26 16:56:24 +00:00
while (pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
pMesh->mFaceMaterials.push_back(pMesh->mFaceMaterials.front());
2019-02-08 22:25:43 +00:00
// read following data objects
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.size() == 0)
ThrowException("Unexpected end of file while parsing mesh material list.");
else if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // material list finished
2022-04-26 16:56:24 +00:00
else if (objectName == "{") {
2019-02-08 22:25:43 +00:00
// template materials
std::string matName = GetNextToken();
Material material;
material.mIsReference = true;
material.mName = matName;
2022-04-26 16:56:24 +00:00
pMesh->mMaterials.push_back(material);
2019-02-08 22:25:43 +00:00
CheckForClosingBrace(); // skip }
2022-04-26 16:56:24 +00:00
} else if (objectName == "Material") {
pMesh->mMaterials.push_back(Material());
ParseDataObjectMaterial(&pMesh->mMaterials.back());
} else if (objectName == ";") {
2019-02-08 22:25:43 +00:00
// ignore
2022-04-26 16:56:24 +00:00
} else {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in material list in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectMaterial(Material *pMaterial) {
2019-02-08 22:25:43 +00:00
std::string matName;
2022-04-26 16:56:24 +00:00
readHeadOfDataObject(&matName);
if (matName.empty())
matName = std::string("material") + ai_to_string(mLineNumber);
2019-02-08 22:25:43 +00:00
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;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.size() == 0)
ThrowException("Unexpected end of file while parsing mesh material");
else if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // material finished
2022-04-26 16:56:24 +00:00
else if (objectName == "TextureFilename" || objectName == "TextureFileName") {
2019-02-08 22:25:43 +00:00
// some exporters write "TextureFileName" instead.
std::string texname;
2022-04-26 16:56:24 +00:00
ParseDataObjectTextureFilename(texname);
pMaterial->mTextures.push_back(TexEntry(texname));
} else if (objectName == "NormalmapFilename" || objectName == "NormalmapFileName") {
2019-02-08 22:25:43 +00:00
// one exporter writes out the normal map in a separate filename tag
std::string texname;
2022-04-26 16:56:24 +00:00
ParseDataObjectTextureFilename(texname);
pMaterial->mTextures.push_back(TexEntry(texname, true));
} else {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in material in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectAnimTicksPerSecond() {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
mScene->mAnimTicksPerSecond = ReadInt();
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectAnimationSet() {
2019-02-08 22:25:43 +00:00
std::string animName;
2022-04-26 16:56:24 +00:00
readHeadOfDataObject(&animName);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
Animation *anim = new Animation;
mScene->mAnims.push_back(anim);
2019-02-08 22:25:43 +00:00
anim->mName = animName;
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.length() == 0)
ThrowException("Unexpected end of file while parsing animation set.");
else if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // animation set finished
2022-04-26 16:56:24 +00:00
else if (objectName == "Animation")
ParseDataObjectAnimation(anim);
else {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in animation set in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectAnimation(Animation *pAnim) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
2022-04-26 16:56:24 +00:00
AnimBone *banim = new AnimBone;
pAnim->mAnims.push_back(banim);
2019-02-08 22:25:43 +00:00
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string objectName = GetNextToken();
2022-04-26 16:56:24 +00:00
if (objectName.length() == 0)
ThrowException("Unexpected end of file while parsing animation.");
else if (objectName == "}")
2019-02-08 22:25:43 +00:00
break; // animation finished
2022-04-26 16:56:24 +00:00
else if (objectName == "AnimationKey")
ParseDataObjectAnimationKey(banim);
else if (objectName == "AnimationOptions")
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject(); // not interested
2022-04-26 16:56:24 +00:00
else if (objectName == "{") {
2019-02-08 22:25:43 +00:00
// read frame name
banim->mBoneName = GetNextToken();
CheckForClosingBrace();
2022-04-26 16:56:24 +00:00
} else {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Unknown data object in animation in x file");
2019-02-08 22:25:43 +00:00
ParseUnknownDataObject();
}
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
// read key type
unsigned int keyType = ReadInt();
// read number of keys
unsigned int numKeys = ReadInt();
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < numKeys; a++) {
2019-02-08 22:25:43 +00:00
// read time
unsigned int time = ReadInt();
// read keys
2022-04-26 16:56:24 +00:00
switch (keyType) {
case 0: // rotation quaternion
2019-02-08 22:25:43 +00:00
{
2022-04-26 16:56:24 +00:00
// 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;
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
case 1: // scale vector
case 2: // position vector
{
// read count
if (ReadInt() != 3)
ThrowException("Invalid number of arguments for vector key in animation");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
aiVectorKey key;
key.mTime = double(time);
key.mValue = ReadVector3();
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (keyType == 2)
pAnimBone->mPosKeys.push_back(key);
else
pAnimBone->mScaleKeys.push_back(key);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
break;
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
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;
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
default:
ThrowException("Unknown key type ", keyType, " in animation.");
break;
2019-02-08 22:25:43 +00:00
} // end switch
// key separator
CheckForSeparator();
}
CheckForClosingBrace();
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseDataObjectTextureFilename(std::string &pName) {
2019-02-08 22:25:43 +00:00
readHeadOfDataObject();
2022-04-26 16:56:24 +00:00
GetNextTokenAsString(pName);
2019-02-08 22:25:43 +00:00
CheckForClosingBrace();
// FIX: some files (e.g. AnimationTest.x) have "" as texture file name
2022-04-26 16:56:24 +00:00
if (!pName.length()) {
2019-03-05 20:39:38 +00:00
ASSIMP_LOG_WARN("Length of texture file name is zero. Skipping this texture.");
2019-02-08 22:25:43 +00:00
}
// some exporters write double backslash paths out. We simply replace them if we find them
2022-04-26 16:56:24 +00:00
while (pName.find("\\\\") != std::string::npos)
pName.replace(pName.find("\\\\"), 2, "\\");
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ParseUnknownDataObject() {
2019-02-08 22:25:43 +00:00
// find opening delimiter
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
2019-02-08 22:25:43 +00:00
std::string t = GetNextToken();
2022-04-26 16:56:24 +00:00
if (t.length() == 0)
ThrowException("Unexpected end of file while parsing unknown segment.");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (t == "{")
2019-02-08 22:25:43 +00:00
break;
}
unsigned int counter = 1;
// parse until closing delimiter
2022-04-26 16:56:24 +00:00
while (counter > 0) {
2019-02-08 22:25:43 +00:00
std::string t = GetNextToken();
2022-04-26 16:56:24 +00:00
if (t.length() == 0)
ThrowException("Unexpected end of file while parsing unknown segment.");
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (t == "{")
2019-02-08 22:25:43 +00:00
++counter;
2022-04-26 16:56:24 +00:00
else if (t == "}")
2019-02-08 22:25:43 +00:00
--counter;
}
}
// ------------------------------------------------------------------------------------------------
//! checks for closing curly brace
2022-04-26 16:56:24 +00:00
void XFileParser::CheckForClosingBrace() {
if (GetNextToken() != "}")
ThrowException("Closing brace expected.");
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
//! checks for one following semicolon
2022-04-26 16:56:24 +00:00
void XFileParser::CheckForSemicolon() {
if (mIsBinaryFormat)
2019-02-08 22:25:43 +00:00
return;
2022-04-26 16:56:24 +00:00
if (GetNextToken() != ";")
ThrowException("Semicolon expected.");
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
//! checks for a separator char, either a ',' or a ';'
2022-04-26 16:56:24 +00:00
void XFileParser::CheckForSeparator() {
if (mIsBinaryFormat)
2019-02-08 22:25:43 +00:00
return;
std::string token = GetNextToken();
2022-04-26 16:56:24 +00:00
if (token != "," && token != ";")
ThrowException("Separator character (';' or ',') expected.");
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
// tests and possibly consumes a separator char, but does nothing if there was no separator
2022-04-26 16:56:24 +00:00
void XFileParser::TestForSeparator() {
if (mIsBinaryFormat)
return;
FindNextNoneWhiteSpace();
if (mP >= mEnd)
return;
// test and skip
if (*mP == ';' || *mP == ',')
mP++;
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::readHeadOfDataObject(std::string *poName) {
2019-02-08 22:25:43 +00:00
std::string nameOrBrace = GetNextToken();
2022-04-26 16:56:24 +00:00
if (nameOrBrace != "{") {
if (poName)
2019-02-08 22:25:43 +00:00
*poName = nameOrBrace;
2022-04-26 16:56:24 +00:00
if (GetNextToken() != "{") {
2019-03-05 20:39:38 +00:00
delete mScene;
2022-04-26 16:56:24 +00:00
ThrowException("Opening brace expected.");
2019-03-05 20:39:38 +00:00
}
2019-02-08 22:25:43 +00:00
}
}
// ------------------------------------------------------------------------------------------------
2019-03-05 20:39:38 +00:00
std::string XFileParser::GetNextToken() {
2019-02-08 22:25:43 +00:00
std::string s;
// process binary-formatted file
2022-04-26 16:56:24 +00:00
if (mIsBinaryFormat) {
2019-02-08 22:25:43 +00:00
// in binary mode it will only return NAME and STRING token
// and (correctly) skip over other tokens.
2022-04-26 16:56:24 +00:00
if (mEnd - mP < 2) {
2019-03-05 20:39:38 +00:00
return s;
}
2019-02-08 22:25:43 +00:00
unsigned int tok = ReadBinWord();
unsigned int len;
// standalone tokens
2022-04-26 16:56:24 +00:00
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;
2019-03-05 20:39:38 +00:00
}
2022-04-26 16:56:24 +00:00
s = std::string(mP, len);
mP += len;
}
2019-03-05 20:39:38 +00:00
return s;
2022-04-26 16:56:24 +00:00
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";
2019-02-08 22:25:43 +00:00
}
}
// process text-formatted file
2022-04-26 16:56:24 +00:00
else {
2019-02-08 22:25:43 +00:00
FindNextNoneWhiteSpace();
2022-04-26 16:56:24 +00:00
if (mP >= mEnd)
2019-02-08 22:25:43 +00:00
return s;
2022-04-26 16:56:24 +00:00
while ((mP < mEnd) && !isspace((unsigned char)*mP)) {
2019-02-08 22:25:43 +00:00
// either keep token delimiters when already holding a token, or return if first valid char
2022-04-26 16:56:24 +00:00
if (*mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') {
if (!s.size())
s.append(mP++, 1);
2019-02-08 22:25:43 +00:00
break; // stop for delimiter
}
2022-04-26 16:56:24 +00:00
s.append(mP++, 1);
2019-02-08 22:25:43 +00:00
}
}
return s;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::FindNextNoneWhiteSpace() {
if (mIsBinaryFormat)
2019-02-08 22:25:43 +00:00
return;
bool running = true;
2022-04-26 16:56:24 +00:00
while (running) {
while (mP < mEnd && isspace((unsigned char)*mP)) {
if (*mP == '\n')
2019-02-08 22:25:43 +00:00
mLineNumber++;
2019-03-05 20:39:38 +00:00
++mP;
2019-02-08 22:25:43 +00:00
}
2022-04-26 16:56:24 +00:00
if (mP >= mEnd)
2019-02-08 22:25:43 +00:00
return;
// check if this is a comment
2022-04-26 16:56:24 +00:00
if ((mP[0] == '/' && mP[1] == '/') || mP[0] == '#')
2019-02-08 22:25:43 +00:00
ReadUntilEndOfLine();
else
break;
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::GetNextTokenAsString(std::string &poString) {
if (mIsBinaryFormat) {
2019-02-08 22:25:43 +00:00
poString = GetNextToken();
return;
}
FindNextNoneWhiteSpace();
2022-04-26 16:56:24 +00:00
if (mP >= mEnd) {
2019-03-05 20:39:38 +00:00
delete mScene;
2022-04-26 16:56:24 +00:00
ThrowException("Unexpected end of file while parsing string");
2019-03-05 20:39:38 +00:00
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (*mP != '"') {
2019-03-05 20:39:38 +00:00
delete mScene;
2022-04-26 16:56:24 +00:00
ThrowException("Expected quotation mark.");
2019-03-05 20:39:38 +00:00
}
++mP;
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
while (mP < mEnd && *mP != '"')
poString.append(mP++, 1);
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (mP >= mEnd - 1) {
2019-03-05 20:39:38 +00:00
delete mScene;
2022-04-26 16:56:24 +00:00
ThrowException("Unexpected end of file while parsing string");
2019-03-05 20:39:38 +00:00
}
2019-02-08 22:25:43 +00:00
2022-04-26 16:56:24 +00:00
if (mP[1] != ';' || mP[0] != '"') {
2019-03-05 20:39:38 +00:00
delete mScene;
2022-04-26 16:56:24 +00:00
ThrowException("Expected quotation mark and semicolon at the end of a string.");
2019-03-05 20:39:38 +00:00
}
2022-04-26 16:56:24 +00:00
mP += 2;
2019-02-08 22:25:43 +00:00
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
void XFileParser::ReadUntilEndOfLine() {
if (mIsBinaryFormat)
2019-02-08 22:25:43 +00:00
return;
2022-04-26 16:56:24 +00:00
while (mP < mEnd) {
if (*mP == '\n' || *mP == '\r') {
++mP;
mLineNumber++;
2019-02-08 22:25:43 +00:00
return;
}
2019-03-05 20:39:38 +00:00
++mP;
2019-02-08 22:25:43 +00:00
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
unsigned short XFileParser::ReadBinWord() {
2019-03-05 20:39:38 +00:00
ai_assert(mEnd - mP >= 2);
2022-04-26 16:56:24 +00:00
const unsigned char *q = (const unsigned char *)mP;
2019-02-08 22:25:43 +00:00
unsigned short tmp = q[0] | (q[1] << 8);
2019-03-05 20:39:38 +00:00
mP += 2;
2019-02-08 22:25:43 +00:00
return tmp;
}
// ------------------------------------------------------------------------------------------------
2019-03-05 20:39:38 +00:00
unsigned int XFileParser::ReadBinDWord() {
ai_assert(mEnd - mP >= 4);
2022-04-26 16:56:24 +00:00
const unsigned char *q = (const unsigned char *)mP;
2019-02-08 22:25:43 +00:00
unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
2019-03-05 20:39:38 +00:00
mP += 4;
2019-02-08 22:25:43 +00:00
return tmp;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
unsigned int XFileParser::ReadInt() {
if (mIsBinaryFormat) {
if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
2019-02-08 22:25:43 +00:00
unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
2022-04-26 16:56:24 +00:00
if (tmp == 0x06 && mEnd - mP >= 4) // array of ints follows
2019-02-08 22:25:43 +00:00
mBinaryNumCount = ReadBinDWord();
else // single int follows
mBinaryNumCount = 1;
}
--mBinaryNumCount;
2022-04-26 16:56:24 +00:00
const size_t len(mEnd - mP);
if (len >= 4) {
2019-02-08 22:25:43 +00:00
return ReadBinDWord();
} else {
2019-03-05 20:39:38 +00:00
mP = mEnd;
2019-02-08 22:25:43 +00:00
return 0;
}
2022-04-26 16:56:24 +00:00
} else {
2019-02-08 22:25:43 +00:00
FindNextNoneWhiteSpace();
// TODO: consider using strtol10 instead???
// check preceding minus sign
bool isNegative = false;
2022-04-26 16:56:24 +00:00
if (*mP == '-') {
2019-02-08 22:25:43 +00:00
isNegative = true;
2019-03-05 20:39:38 +00:00
mP++;
2019-02-08 22:25:43 +00:00
}
// at least one digit expected
2022-04-26 16:56:24 +00:00
if (!isdigit((unsigned char)*mP))
ThrowException("Number expected.");
2019-02-08 22:25:43 +00:00
// read digits
unsigned int number = 0;
2022-04-26 16:56:24 +00:00
while (mP < mEnd) {
if (!isdigit((unsigned char)*mP))
2019-02-08 22:25:43 +00:00
break;
2019-03-05 20:39:38 +00:00
number = number * 10 + (*mP - 48);
mP++;
2019-02-08 22:25:43 +00:00
}
CheckForSeparator();
2019-03-05 20:39:38 +00:00
2022-04-26 16:56:24 +00:00
return isNegative ? ((unsigned int)-int(number)) : number;
2019-02-08 22:25:43 +00:00
}
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
ai_real XFileParser::ReadFloat() {
if (mIsBinaryFormat) {
if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
2019-02-08 22:25:43 +00:00
unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
2022-04-26 16:56:24 +00:00
if (tmp == 0x07 && mEnd - mP >= 4) // array of floats following
2019-02-08 22:25:43 +00:00
mBinaryNumCount = ReadBinDWord();
else // single float following
mBinaryNumCount = 1;
}
--mBinaryNumCount;
2022-04-26 16:56:24 +00:00
if (mBinaryFloatSize == 8) {
if (mEnd - mP >= 8) {
2019-03-05 20:39:38 +00:00
double res;
2022-04-26 16:56:24 +00:00
::memcpy(&res, mP, 8);
2019-03-05 20:39:38 +00:00
mP += 8;
2022-04-26 16:56:24 +00:00
const ai_real result(static_cast<ai_real>(res));
2019-02-08 22:25:43 +00:00
return result;
} else {
2019-03-05 20:39:38 +00:00
mP = mEnd;
2019-02-08 22:25:43 +00:00
return 0;
}
2019-03-05 20:39:38 +00:00
} else {
2022-04-26 16:56:24 +00:00
if (mEnd - mP >= 4) {
2019-03-05 20:39:38 +00:00
ai_real result;
2022-04-26 16:56:24 +00:00
::memcpy(&result, mP, 4);
2019-03-05 20:39:38 +00:00
mP += 4;
2019-02-08 22:25:43 +00:00
return result;
} else {
2019-03-05 20:39:38 +00:00
mP = mEnd;
2019-02-08 22:25:43 +00:00
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
2022-04-26 16:56:24 +00:00
if (strncmp(mP, "-1.#IND00", 9) == 0 || strncmp(mP, "1.#IND00", 8) == 0) {
2019-03-05 20:39:38 +00:00
mP += 9;
2019-02-08 22:25:43 +00:00
CheckForSeparator();
return 0.0;
2022-04-26 16:56:24 +00:00
} else if (strncmp(mP, "1.#QNAN0", 8) == 0) {
2019-03-05 20:39:38 +00:00
mP += 8;
2019-02-08 22:25:43 +00:00
CheckForSeparator();
return 0.0;
}
ai_real result = 0.0;
2022-04-26 16:56:24 +00:00
mP = fast_atoreal_move<ai_real>(mP, result);
2019-02-08 22:25:43 +00:00
CheckForSeparator();
return result;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
aiVector2D XFileParser::ReadVector2() {
2019-02-08 22:25:43 +00:00
aiVector2D vector;
vector.x = ReadFloat();
vector.y = ReadFloat();
TestForSeparator();
return vector;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
aiVector3D XFileParser::ReadVector3() {
2019-02-08 22:25:43 +00:00
aiVector3D vector;
vector.x = ReadFloat();
vector.y = ReadFloat();
vector.z = ReadFloat();
TestForSeparator();
return vector;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
aiColor4D XFileParser::ReadRGBA() {
2019-02-08 22:25:43 +00:00
aiColor4D color;
color.r = ReadFloat();
color.g = ReadFloat();
color.b = ReadFloat();
color.a = ReadFloat();
TestForSeparator();
return color;
}
// ------------------------------------------------------------------------------------------------
2022-04-26 16:56:24 +00:00
aiColor3D XFileParser::ReadRGB() {
2019-02-08 22:25:43 +00:00
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.
2022-04-26 16:56:24 +00:00
void XFileParser::FilterHierarchy(XFile::Node *pNode) {
2019-02-08 22:25:43 +00:00
// 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
2022-04-26 16:56:24 +00:00
if (pNode->mChildren.size() == 1 && pNode->mMeshes.empty()) {
XFile::Node *child = pNode->mChildren.front();
if (child->mName.length() == 0 && child->mMeshes.size() > 0) {
2019-02-08 22:25:43 +00:00
// transfer its meshes to us
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < child->mMeshes.size(); a++)
pNode->mMeshes.push_back(child->mMeshes[a]);
2019-02-08 22:25:43 +00:00
child->mMeshes.clear();
// transfer the transform as well
pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix;
// then kill it
delete child;
pNode->mChildren.clear();
}
}
// recurse
2022-04-26 16:56:24 +00:00
for (unsigned int a = 0; a < pNode->mChildren.size(); a++)
FilterHierarchy(pNode->mChildren[a]);
2019-02-08 22:25:43 +00:00
}
#endif // !! ASSIMP_BUILD_NO_X_IMPORTER