From 2f3e72413f25897c1903e883a94fa5cf4293e26b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 10 Apr 2025 15:30:12 +0200 Subject: [PATCH] Bugfix/ensure collada parsing works issue 1488 (#6087) * Add text to ensure that float parsing in colladata works right --- code/AssetLib/Collada/ColladaLoader.cpp | 94 ++-- code/AssetLib/Collada/ColladaLoader.h | 19 +- code/AssetLib/Collada/ColladaParser.cpp | 713 ++++++++++++------------ code/AssetLib/Collada/ColladaParser.h | 193 +++---- test/CMakeLists.txt | 1 + test/unit/Common/utParsingUtils.cpp | 66 +++ test/unit/utFastAtof.cpp | 3 +- test/unit/utTypes.cpp | 3 - 8 files changed, 555 insertions(+), 537 deletions(-) create mode 100644 test/unit/Common/utParsingUtils.cpp diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index d50efddf6..e0c0648ad 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -89,6 +89,14 @@ inline void AddNodeMetaData(aiNode *node, const std::string &key, const T &value node->mMetaData->Add(key, value); } +// ------------------------------------------------------------------------------------------------ +// Reads a float value from an accessor and its data array. +static ai_real ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) { + size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset; + ai_assert(pos < pData.mValues.size()); + return pData.mValues[pos]; +} + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ColladaLoader::ColladaLoader() : @@ -152,7 +160,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO throw DeadlyImportError("Collada: File came out empty. Something is wrong here."); } - // reserve some storage to avoid unnecessary reallocs + // reserve some storage to avoid unnecessary reallocates newMats.reserve(parser.mMaterialLibrary.size() * 2u); mMeshes.reserve(parser.mMeshLibrary.size() * 2u); @@ -224,7 +232,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO // Recursively constructs a scene node for the given parser node and returns it. aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode) { // create a node for it - aiNode *node = new aiNode(); + auto *node = new aiNode(); // find a name for the new node. It's more complicated than you might think node->mName.Set(FindNameForNode(pNode)); @@ -272,24 +280,24 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad // ------------------------------------------------------------------------------------------------ // Resolve node instances void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode, - std::vector &resolved) { + std::vector &resolved) const { // reserve enough storage resolved.reserve(pNode->mNodeInstances.size()); // ... and iterate through all nodes to be instanced as children of pNode - for (const auto &nodeInst : pNode->mNodeInstances) { + for (const auto &[mNode] : pNode->mNodeInstances) { // find the corresponding node in the library - const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode); + const auto itt = pParser.mNodeLibrary.find(mNode); const Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second; // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632 // need to check for both name and ID to catch all. To avoid breaking valid files, // the workaround is only enabled when the first attempt to resolve the node has failed. if (nullptr == nd) { - nd = FindNode(pParser.mRootNode, nodeInst.mNode); + nd = FindNode(pParser.mRootNode, mNode); } if (nullptr == nd) { - ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode); + ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", mNode); } else { // attach this node to the list of children resolved.push_back(nd); @@ -299,8 +307,8 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Nod // ------------------------------------------------------------------------------------------------ // Resolve UV channels -void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) { - SemanticMappingTable::InputSemanticMap::const_iterator it = table.mMap.find(sampler.mUVChannel); +static void ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) { + const auto it = table.mMap.find(sampler.mUVChannel); if (it == table.mMap.end()) { return; } @@ -317,7 +325,7 @@ void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const S void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) { for (const LightInstance &lid : pNode->mLights) { // find the referred light - ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight); + auto srcLightIt = pParser.mLightLibrary.find(lid.mLight); if (srcLightIt == pParser.mLightLibrary.end()) { ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping."); continue; @@ -325,7 +333,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node const Collada::Light *srcLight = &srcLightIt->second; // now fill our ai data structure - aiLight *out = new aiLight(); + auto out = new aiLight(); out->mName = pTarget->mName; out->mType = (aiLightSourceType)srcLight->mType; @@ -382,7 +390,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) { for (const CameraInstance &cid : pNode->mCameras) { // find the referred light - ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera); + auto srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera); if (srcCameraIt == pParser.mCameraLibrary.end()) { ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping."); continue; @@ -395,7 +403,7 @@ void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node } // now fill our ai data structure - aiCamera *out = new aiCamera(); + auto *out = new aiCamera(); out->mName = pTarget->mName; // collada cameras point in -Z by default, rest is specified in node transform @@ -445,10 +453,10 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node const Controller *srcController = nullptr; // find the referred mesh - ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController); + auto srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController); if (srcMeshIt == pParser.mMeshLibrary.end()) { // if not found in the mesh-library, it might also be a controller referring to a mesh - ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController); + auto srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController); if (srcContrIt != pParser.mControllerLibrary.end()) { srcController = &srcContrIt->second; srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId); @@ -462,7 +470,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node continue; } } else { - // ID found in the mesh library -> direct reference to an unskinned mesh + // ID found in the mesh library -> direct reference to a not skinned mesh srcMesh = srcMeshIt->second; } @@ -476,7 +484,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node // find material assigned to this submesh std::string meshMaterial; - std::map::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial); + auto meshMatIt = mid.mMaterials.find(submesh.mMaterial); const Collada::SemanticMappingTable *table = nullptr; if (meshMatIt != mid.mMaterials.end()) { @@ -492,7 +500,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table // given. The only mapping stuff which we do actually support is the UV channel. - std::map::const_iterator matIt = mMaterialIndexByName.find(meshMaterial); + auto matIt = mMaterialIndexByName.find(meshMaterial); unsigned int matIdx = 0; if (matIt != mMaterialIndexByName.end()) { matIdx = static_cast(matIt->second); @@ -515,7 +523,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node ColladaMeshIndex index(mid.mMeshOrController, sm, meshMaterial); // if we already have the mesh at the library, just add its index to the node's array - std::map::const_iterator dstMeshIt = mMeshIndexByID.find(index); + auto dstMeshIt = mMeshIndexByID.find(index); if (dstMeshIt != mMeshIndexByID.end()) { newMeshRefs.push_back(dstMeshIt->second); } else { @@ -530,7 +538,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node faceStart += submesh.mNumFaces; // assign the material index - std::map::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial); + auto subMatIt = mMaterialIndexByName.find(submesh.mMaterial); if (subMatIt != mMaterialIndexByName.end()) { dstMesh->mMaterialIndex = static_cast(subMatIt->second); } else { @@ -618,7 +626,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents); } - // bitangents, if given. + // bi-tangents, if given. if (pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) { dstMesh->mBitangents = new aiVector3D[numVertices]; std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents); @@ -664,7 +672,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc std::vector targetWeights; Collada::MorphMethod method = Normalized; - for (std::map::const_iterator it = pParser.mControllerLibrary.begin(); + for (auto it = pParser.mControllerLibrary.begin(); it != pParser.mControllerLibrary.end(); ++it) { const Controller &c = it->second; const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId); @@ -754,7 +762,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc std::vector weightStartPerVertex; weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end()); - IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); + auto pit = pSrcController->mWeights.begin(); for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) { weightStartPerVertex[a] = pit; pit += pSrcController->mWeightCounts[a]; @@ -766,7 +774,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc // the controller assigns the vertex weights size_t orgIndex = pSrcMesh->mFacePosIndices[a]; // find the vertex weights for this vertex - IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; + auto iit = weightStartPerVertex[orgIndex]; size_t pairCount = pSrcController->mWeightCounts[orgIndex]; for (size_t b = 0; b < pairCount; ++b, ++iit) { @@ -807,7 +815,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc } // create bone with its weights - aiBone *bone = new aiBone; + auto bone = new aiBone; bone->mName = ReadString(jointNamesAcc, jointNames, a); bone->mOffsetMatrix.a1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 0); bone->mOffsetMatrix.a2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 1); @@ -973,7 +981,7 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse // if there are other animations which fit the template anim, combine all channels into a single anim if (!collectedAnimIndices.empty()) { - aiAnimation *combinedAnim = new aiAnimation(); + auto *combinedAnim = new aiAnimation(); combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a)); combinedAnim->mDuration = templateAnim->mDuration; combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond; @@ -1040,7 +1048,7 @@ struct MorphTimeValues { }; void insertMorphTimeValue(std::vector &values, float time, float weight, unsigned int value) { - MorphTimeValues::key k; + MorphTimeValues::key k{}; k.mValue = value; k.mWeight = weight; if (values.empty() || time < values[0].mTime) { @@ -1106,7 +1114,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse // now check all channels if they affect the current node std::string targetID, subElement; - for (std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); + for (auto cit = pSrcAnim->mChannels.begin(); cit != pSrcAnim->mChannels.end(); ++cit) { const AnimationChannel &srcChannel = *cit; ChannelEntry entry; @@ -1349,7 +1357,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse // build an animation channel for the given node out of these trafo keys if (!resultTrafos.empty()) { - aiNodeAnim *dstAnim = new aiNodeAnim; + auto *dstAnim = new aiNodeAnim; dstAnim->mNodeName = nodeName; dstAnim->mNumPositionKeys = static_cast(resultTrafos.size()); dstAnim->mNumRotationKeys = static_cast(resultTrafos.size()); @@ -1391,7 +1399,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse // or 2) one channel with morph target count arrays // assume first - aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim; + auto *morphAnim = new aiMeshMorphAnim; morphAnim->mName.Set(nodeName); std::vector morphTimeValues; @@ -1434,7 +1442,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse } if (!anims.empty() || !morphAnims.empty()) { - aiAnimation *anim = new aiAnimation; + auto anim = new aiAnimation; anim->mName.Set(pName); anim->mNumChannels = static_cast(anims.size()); if (anim->mNumChannels > 0) { @@ -1514,7 +1522,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { + for (auto it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { if (IsNumeric(*it)) { map = strtoul10(&(*it)); break; @@ -1532,7 +1540,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat, // Fills materials from the collada material definitions void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pScene*/) { for (auto &elem : newMats) { - aiMaterial &mat = (aiMaterial &)*elem.second; + auto &mat = (aiMaterial &)*elem.second; Collada::Effect &effect = *elem.first; // resolve shading mode @@ -1642,17 +1650,17 @@ void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pSce void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) { newMats.reserve(pParser.mMaterialLibrary.size()); - for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); + for (auto matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt) { const Material &material = matIt->second; // a material is only a reference to an effect - ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); + auto effIt = pParser.mEffectLibrary.find(material.mEffect); if (effIt == pParser.mEffectLibrary.end()) continue; Effect &effect = effIt->second; // create material - aiMaterial *mat = new aiMaterial; + auto *mat = new aiMaterial; aiString name(material.mName.empty() ? matIt->first : material.mName); mat->AddProperty(&name, AI_MATKEY_NAME); @@ -1675,7 +1683,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse std::string name = pName; while (true) { // the given string is a param entry. Find it - Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name); + auto it = pEffect.mParams.find(name); // if not found, we're at the end of the recursion. The resulting string should be the image ID if (it == pEffect.mParams.end()) break; @@ -1685,7 +1693,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse } // find the image referred by this name in the image library of the scene - ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find(name); + auto imIt = pParser.mImageLibrary.find(name); if (imIt == pParser.mImageLibrary.end()) { ASSIMP_LOG_WARN("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\"."); @@ -1697,7 +1705,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse // if this is an embedded texture image setup an aiTexture for it if (!imIt->second.mImageData.empty()) { - aiTexture *tex = new aiTexture(); + auto *tex = new aiTexture(); // Store embedded texture name reference tex->mFilename.Set(imIt->second.mFileName.c_str()); @@ -1729,14 +1737,6 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse return result; } -// ------------------------------------------------------------------------------------------------ -// Reads a float value from an accessor and its data array. -ai_real ColladaLoader::ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) const { - size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset; - ai_assert(pos < pData.mValues.size()); - return pData.mValues[pos]; -} - // ------------------------------------------------------------------------------------------------ // Reads a string value from an accessor and its data array. const std::string &ColladaLoader::ReadString(const Accessor &pAccessor, const Data &pData, size_t pIndex) const { diff --git a/code/AssetLib/Collada/ColladaLoader.h b/code/AssetLib/Collada/ColladaLoader.h index 7f21629b5..efeb4f1fa 100644 --- a/code/AssetLib/Collada/ColladaLoader.h +++ b/code/AssetLib/Collada/ColladaLoader.h @@ -109,7 +109,7 @@ protected: /// Resolve node instances void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode, - std::vector &resolved); + std::vector &resolved) const; /// Builds meshes for the given node and references them void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode, @@ -166,10 +166,6 @@ protected: /** Fill materials from the collada material definitions */ void FillMaterials(const ColladaParser &pParser, aiScene *pScene); - /** Resolve UV channel mappings*/ - void ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler, - const Collada::SemanticMappingTable &table); - /** Add a texture and all of its sampling properties to a material*/ void AddTexture(aiMaterial &mat, const ColladaParser &pParser, const Collada::Effect &effect, @@ -180,22 +176,13 @@ protected: aiString FindFilenameForEffectTexture(const ColladaParser &pParser, const Collada::Effect &pEffect, const std::string &pName); - /** Reads a float value from an accessor and its data array. - * @param pAccessor The accessor to use for reading - * @param pData The data array to read from - * @param pIndex The index of the element to retrieve - * @param pOffset Offset into the element, for multipart elements such as vectors or matrices - * @return the specified value - */ - ai_real ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const; - /** Reads a string value from an accessor and its data array. * @param pAccessor The accessor to use for reading * @param pData The data array to read from * @param pIndex The index of the element to retrieve * @return the specified value */ - const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const; + [[nodiscard]] const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const; /** Recursively collects all nodes into the given array */ void CollectNodes(const aiNode *pNode, std::vector &poNodes) const; @@ -208,7 +195,7 @@ protected: /** Finds a proper name for a node derived from the collada-node's properties */ std::string FindNameForNode(const Collada::Node *pNode); -protected: +private: /** Filename, for a verbose error message */ std::string mFileName; diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 1c74dd43a..5f8dcd5bd 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -61,6 +61,7 @@ using namespace Assimp; using namespace Assimp::Collada; using namespace Assimp::Formatter; +// ------------------------------------------------------------------------------------------------ static void ReportWarning(const char *msg, ...) { ai_assert(nullptr != msg); @@ -75,6 +76,7 @@ static void ReportWarning(const char *msg, ...) { ASSIMP_LOG_WARN("Validation warning: ", std::string(szBuffer, iLen)); } +// ------------------------------------------------------------------------------------------------ static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVector &key_renaming, size_t &found_index) { for (size_t i = 0; i < key_renaming.size(); ++i) { if (key_renaming[i].first == collada_key) { @@ -87,6 +89,7 @@ static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVecto return false; } +// ------------------------------------------------------------------------------------------------ static void readUrlAttribute(XmlNode &node, std::string &url) { url.clear(); if (!XmlParser::getStdStrAttribute(node, "url", url)) { @@ -98,23 +101,319 @@ static void readUrlAttribute(XmlNode &node, std::string &url) { url = url.c_str() + 1; } +// ------------------------------------------------------------------------------------------------ +// Reads a node transformation entry of the given type and adds it to the given node's transformation list. +static void ReadNodeTransformation(XmlNode &node, Node *pNode, TransformType pType) { + if (node.empty()) { + return; + } + + std::string tagName = node.name(); + + Transform tf; + tf.mType = pType; + + // read SID + if (XmlParser::hasAttribute(node, "sid")) { + XmlParser::getStdStrAttribute(node, "sid", tf.mID); + } + + // how many parameters to read per transformation type + static constexpr unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; + std::string value; + XmlParser::getValueAsString(node, value); + const char *content = value.c_str(); + const char *end = value.c_str() + value.size(); + // read as many parameters and store in the transformation + for (unsigned int a = 0; a < sNumParameters[pType]; a++) { + // skip whitespace before the number + SkipSpacesAndLineEnd(&content, end); + // read a number + content = fast_atoreal_move(content, tf.f[a]); + } + + // place the transformation at the queue of the node + pNode->mTransforms.push_back(tf); +} + +// ------------------------------------------------------------------------------------------------ +// Reads a single string metadata item +static void ReadMetaDataItem(XmlNode &node, ColladaParser::StringMetaData &metadata) { + const MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase(); + const std::string name = node.name(); + if (name.empty()) { + return; + } + + std::string v; + if (!XmlParser::getValueAsString(node, v)) { + return; + } + + v = ai_trim(v); + aiString aistr; + aistr.Set(v); + + std::string camel_key_str(name); + ToCamelCase(camel_key_str); + + size_t found_index; + if (FindCommonKey(camel_key_str, key_renaming, found_index)) { + metadata.emplace(key_renaming[found_index].second, aistr); + } else { + metadata.emplace(camel_key_str, aistr); + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads an animation sampler into the given anim channel +static void ReadAnimationSampler(const XmlNode &node, AnimationChannel &pChannel) { + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "input") { + if (XmlParser::hasAttribute(currentNode, "semantic")) { + std::string semantic, sourceAttr; + XmlParser::getStdStrAttribute(currentNode, "semantic", semantic); + if (XmlParser::hasAttribute(currentNode, "source")) { + XmlParser::getStdStrAttribute(currentNode, "source", sourceAttr); + const char *source = sourceAttr.c_str(); + if (source[0] != '#') { + throw DeadlyImportError("Unsupported URL format"); + } + source++; + + if (semantic == "INPUT") { + pChannel.mSourceTimes = source; + } else if (semantic == "OUTPUT") { + pChannel.mSourceValues = source; + } else if (semantic == "IN_TANGENT") { + pChannel.mInTanValues = source; + } else if (semantic == "OUT_TANGENT") { + pChannel.mOutTanValues = source; + } else if (semantic == "INTERPOLATION") { + pChannel.mInterpolationValues = source; + } + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the joint definitions for the given controller +static void ReadControllerJoints(const XmlNode &node, Controller &pController) { + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "input") { + const char *attrSemantic = currentNode.attribute("semantic").as_string(); + const char *attrSource = currentNode.attribute("source").as_string(); + if (attrSource[0] != '#') { + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + } + ++attrSource; + // parse source URL to corresponding source + if (strcmp(attrSemantic, "JOINT") == 0) { + pController.mJointNameSource = attrSource; + } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { + pController.mJointOffsetMatrixSource = attrSource; + } else { + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +static void ReadControllerWeightsInput(const XmlNode ¤tNode, Controller &pController) { + InputChannel channel; + + const char *attrSemantic = currentNode.attribute("semantic").as_string(); + const char *attrSource = currentNode.attribute("source").as_string(); + channel.mOffset = currentNode.attribute("offset").as_int(); + + // local URLS always start with a '#'. We don't support global URLs + if (attrSource[0] != '#') { + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + } + channel.mAccessor = attrSource + 1; + + // parse source URL to corresponding source + if (strcmp(attrSemantic, "JOINT") == 0) { + pController.mWeightInputJoints = channel; + } else if (strcmp(attrSemantic, "WEIGHT") == 0) { + pController.mWeightInputWeights = channel; + } else { + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); + } +} + +// ------------------------------------------------------------------------------------------------ +static void ReadControllerWeightsVCount(const XmlNode ¤tNode, Controller &pController) { + const std::string stdText = currentNode.text().as_string(); + const char *text = stdText.c_str(); + const char *end = text + stdText.size(); + size_t numWeights = 0; + for (auto it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) { + if (*text == 0) { + throw DeadlyImportError("Out of data while reading "); + } + + *it = strtoul10(text, &text); + numWeights += *it; + SkipSpacesAndLineEnd(&text, end); + } + // reserve weight count + pController.mWeights.resize(numWeights); +} + +// ------------------------------------------------------------------------------------------------ +static void ReadControllerWeightsJoint2verts(XmlNode ¤tNode, Controller &pController) { + // read JointIndex - WeightIndex pairs + std::string stdText; + XmlParser::getValueAsString(currentNode, stdText); + const char *text = stdText.c_str(); + const char *end = text + stdText.size(); + for (auto it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) { + if (text == nullptr) { + throw DeadlyImportError("Out of data while reading "); + } + SkipSpacesAndLineEnd(&text, end); + it->first = strtoul10(text, &text); + SkipSpacesAndLineEnd(&text, end); + if (*text == 0) { + throw DeadlyImportError("Out of data while reading "); + } + it->second = strtoul10(text, &text); + SkipSpacesAndLineEnd(&text, end); + } + +} + +// ------------------------------------------------------------------------------------------------ +// Reads the joint weights for the given controller +static void ReadControllerWeights(XmlNode &node, Controller &pController) { + // Read vertex count from attributes and resize the array accordingly + int vertexCount = 0; + XmlParser::getIntAttribute(node, "count", vertexCount); + pController.mWeightCounts.resize(vertexCount); + + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "input") { + ReadControllerWeightsInput(currentNode, pController); + } else if (currentName == "vcount" && vertexCount > 0) { + ReadControllerWeightsVCount(currentNode, pController); + } else if (currentName == "v" && vertexCount > 0) { + ReadControllerWeightsJoint2verts(currentNode, pController); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a material entry into the given material +static void ReadMaterial(const XmlNode &node, Material &pMaterial) { + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "instance_effect") { + std::string url; + readUrlAttribute(currentNode, url); + pMaterial.mEffect = url; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a light entry into the given light +static void ReadLight(XmlNode &node, Light &pLight) { + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); + XmlNode currentNode; + // TODO: Check the current technique and skip over unsupported extra techniques + + while (xmlIt.getNext(currentNode)) { + const std::string ¤tName = currentNode.name(); + if (currentName == "spot") { + pLight.mType = aiLightSource_SPOT; + } else if (currentName == "ambient") { + pLight.mType = aiLightSource_AMBIENT; + } else if (currentName == "directional") { + pLight.mType = aiLightSource_DIRECTIONAL; + } else if (currentName == "point") { + pLight.mType = aiLightSource_POINT; + } else if (currentName == "color") { + // text content contains 3 floats + std::string v; + XmlParser::getValueAsString(currentNode, v); + const char *content = v.c_str(); + const char *end = content + v.size(); + + content = fast_atoreal_move(content, (ai_real &)pLight.mColor.r); + SkipSpacesAndLineEnd(&content, end); + + content = fast_atoreal_move(content, (ai_real &)pLight.mColor.g); + SkipSpacesAndLineEnd(&content, end); + + content = fast_atoreal_move(content, (ai_real &)pLight.mColor.b); + SkipSpacesAndLineEnd(&content, end); + } else if (currentName == "constant_attenuation") { + XmlParser::getValueAsReal(currentNode, pLight.mAttConstant); + } else if (currentName == "linear_attenuation") { + XmlParser::getValueAsReal(currentNode, pLight.mAttLinear); + } else if (currentName == "quadratic_attenuation") { + XmlParser::getValueAsReal(currentNode, pLight.mAttQuadratic); + } else if (currentName == "falloff_angle") { + XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); + } else if (currentName == "falloff_exponent") { + XmlParser::getValueAsReal(currentNode, pLight.mFalloffExponent); + } + // FCOLLADA extensions + // ------------------------------------------------------- + else if (currentName == "outer_cone") { + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); + } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone + XmlParser::getValueAsReal(currentNode, pLight.mPenumbraAngle); + } else if (currentName == "intensity") { + XmlParser::getValueAsReal(currentNode, pLight.mIntensity); + } else if (currentName == "falloff") { + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); + } else if (currentName == "hotspot_beam") { + XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); + } + // OpenCOLLADA extensions + // ------------------------------------------------------- + else if (currentName == "decay_falloff") { + XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a camera entry into the given light +static void ReadCamera(XmlNode &node, Camera &camera) { + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); + XmlNode currentNode; + while (xmlIt.getNext(currentNode)) { + const std::string ¤tName = currentNode.name(); + if (currentName == "orthographic") { + camera.mOrtho = true; + } else if (currentName == "xfov" || currentName == "xmag") { + XmlParser::getValueAsReal(currentNode, camera.mHorFov); + } else if (currentName == "yfov" || currentName == "ymag") { + XmlParser::getValueAsReal(currentNode, camera.mVerFov); + } else if (currentName == "aspect_ratio") { + XmlParser::getValueAsReal(currentNode, camera.mAspect); + } else if (currentName == "znear") { + XmlParser::getValueAsReal(currentNode, camera.mZNear); + } else if (currentName == "zfar") { + XmlParser::getValueAsReal(currentNode, camera.mZFar); + } + } +} + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : mFileName(pFile), - mXmlParser(), - mDataLibrary(), - mAccessorLibrary(), - mMeshLibrary(), - mNodeLibrary(), - mImageLibrary(), - mEffectLibrary(), - mMaterialLibrary(), - mLightLibrary(), - mCameraLibrary(), - mControllerLibrary(), mRootNode(nullptr), - mAnims(), mUnitSize(1.0f), mUpDirection(UP_Y), mFormat(FV_1_5_n) { @@ -122,13 +421,13 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : throw DeadlyImportError("IOSystem is nullptr."); } - std::unique_ptr daefile; + std::unique_ptr daeFile; std::unique_ptr zip_archive; // Determine type - std::string extension = BaseImporter::GetExtension(pFile); + const std::string extension = BaseImporter::GetExtension(pFile); if (extension != "dae") { - zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile)); + zip_archive = std::make_unique(pIOHandler, pFile); } if (zip_archive && zip_archive->isOpen()) { @@ -138,24 +437,24 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : throw DeadlyImportError("Invalid ZAE"); } - daefile.reset(zip_archive->Open(dae_filename.c_str())); - if (daefile == nullptr) { + daeFile.reset(zip_archive->Open(dae_filename.c_str())); + if (daeFile == nullptr) { throw DeadlyImportError("Invalid ZAE manifest: '", dae_filename, "' is missing"); } } else { // attempt to open the file directly - daefile.reset(pIOHandler->Open(pFile)); - if (daefile == nullptr) { + daeFile.reset(pIOHandler->Open(pFile)); + if (daeFile == nullptr) { throw DeadlyImportError("Failed to open file '", pFile, "'."); } } // generate a XML reader for it - if (!mXmlParser.parse(daefile.get())) { + if (!mXmlParser.parse(daeFile.get())) { throw DeadlyImportError("Unable to read file, malformed XML"); } // start reading - XmlNode node = mXmlParser.getRootNode(); + const XmlNode node = mXmlParser.getRootNode(); XmlNode colladaNode = node.child("COLLADA"); if (colladaNode.empty()) { return; @@ -190,14 +489,14 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { zip_archive.getFileListExtension(file_list, "dae"); if (file_list.empty()) { - return std::string(); + return {}; } return file_list.front(); } XmlParser manifestParser; if (!manifestParser.parse(manifestfile.get())) { - return std::string(); + return {}; } XmlNode root = manifestParser.getRootNode(); @@ -205,7 +504,7 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { if (name != "dae_root") { root = *manifestParser.findNode("dae_root"); if (nullptr == root) { - return std::string(); + return {}; } std::string v; XmlParser::getValueAsString(root, v); @@ -214,7 +513,7 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { return std::string(ai_str.C_Str()); } - return std::string(); + return {}; } // ------------------------------------------------------------------------------------------------ @@ -246,7 +545,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { char mychar[3] = { it[1], it[2], 0 }; size_t nbr = strtoul16(mychar); it += 3; - *out++ = (char)(nbr & 0xFF); + *out++ = static_cast(nbr & 0xFF); } else { *out++ = *it++; } @@ -261,12 +560,11 @@ void ColladaParser::UriDecodePath(aiString &ss) { // ------------------------------------------------------------------------------------------------ // Reads the contents of the file void ColladaParser::ReadContents(XmlNode &node) { - const std::string name = node.name(); - if (name == "COLLADA") { + if (const std::string name = node.name(); name == "COLLADA") { std::string version; if (XmlParser::getStdStrAttribute(node, "version", version)) { aiString v; - v.Set(version.c_str()); + v.Set(version); mAssetMetaData.emplace(AI_METADATA_SOURCE_FORMAT_VERSION, v); if (!::strncmp(version.c_str(), "1.5", 3)) { mFormat = FV_1_5_n; @@ -287,8 +585,7 @@ void ColladaParser::ReadContents(XmlNode &node) { // Reads the structure of the file void ColladaParser::ReadStructure(XmlNode &node) { for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "asset") { + if (const std::string ¤tName = currentNode.name(); currentName == "asset") { ReadAssetInfo(currentNode); } else if (currentName == "library_animations") { ReadAnimationLibrary(currentNode); @@ -329,8 +626,7 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) { } for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "unit") { + if (const std::string ¤tName = currentNode.name(); currentName == "unit") { mUnitSize = 1.f; std::string tUnitSizeString; if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) { @@ -364,35 +660,6 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) { } } -// ------------------------------------------------------------------------------------------------ -// Reads a single string metadata item -void ColladaParser::ReadMetaDataItem(XmlNode &node, StringMetaData &metadata) { - const Collada::MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase(); - const std::string name = node.name(); - if (name.empty()) { - return; - } - - std::string v; - if (!XmlParser::getValueAsString(node, v)) { - return; - } - - v = ai_trim(v); - aiString aistr; - aistr.Set(v); - - std::string camel_key_str(name); - ToCamelCase(camel_key_str); - - size_t found_index; - if (FindCommonKey(camel_key_str, key_renaming, found_index)) { - metadata.emplace(key_renaming[found_index].second, aistr); - } else { - metadata.emplace(camel_key_str, aistr); - } -} - // ------------------------------------------------------------------------------------------------ // Reads the animation clips void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { @@ -424,15 +691,16 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { } } +// ------------------------------------------------------------------------------------------------ +// The controller post processing step void ColladaParser::PostProcessControllers() { - std::string meshId; for (auto &it : mControllerLibrary) { - meshId = it.second.mMeshId; + std::string meshId = it.second.mMeshId; if (meshId.empty()) { continue; } - ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId); + auto findItr = mControllerLibrary.find(meshId); while (findItr != mControllerLibrary.end()) { meshId = findItr->second.mMeshId; findItr = mControllerLibrary.find(meshId); @@ -454,13 +722,13 @@ void ColladaParser::PostProcessRootAnimations() { for (auto &it : mAnimationClipLibrary) { std::string clipName = it.first; - Animation *clip = new Animation(); + auto *clip = new Animation(); clip->mName = clipName; temp.mSubAnims.push_back(clip); for (const std::string &animationID : it.second) { - AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); + auto animation = mAnimationLibrary.find(animationID); if (animation != mAnimationLibrary.end()) { Animation *pSourceAnimation = animation->second; @@ -533,7 +801,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { std::string id; if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { // have it read into a channel - ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; + auto newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ReadAnimationSampler(currentNode, newChannel->second); } } else if (currentName == "channel") { @@ -543,7 +811,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { if (source_name[0] == '#') { source_name = source_name.substr(1, source_name.size() - 1); } - ChannelMap::iterator cit = channels.find(source_name); + auto cit = channels.find(source_name); if (cit != channels.end()) { cit->second.mTarget = target; } @@ -568,40 +836,6 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { } } -// ------------------------------------------------------------------------------------------------ -// Reads an animation sampler into the given anim channel -void ColladaParser::ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "input") { - if (XmlParser::hasAttribute(currentNode, "semantic")) { - std::string semantic, sourceAttr; - XmlParser::getStdStrAttribute(currentNode, "semantic", semantic); - if (XmlParser::hasAttribute(currentNode, "source")) { - XmlParser::getStdStrAttribute(currentNode, "source", sourceAttr); - const char *source = sourceAttr.c_str(); - if (source[0] != '#') { - throw DeadlyImportError("Unsupported URL format"); - } - source++; - - if (semantic == "INPUT") { - pChannel.mSourceTimes = source; - } else if (semantic == "OUTPUT") { - pChannel.mSourceValues = source; - } else if (semantic == "IN_TANGENT") { - pChannel.mInTanValues = source; - } else if (semantic == "OUT_TANGENT") { - pChannel.mOutTanValues = source; - } else if (semantic == "INTERPOLATION") { - pChannel.mInterpolationValues = source; - } - } - } - } - } -} - // ------------------------------------------------------------------------------------------------ // Reads the skeleton controller library void ColladaParser::ReadControllerLibrary(XmlNode &node) { @@ -614,8 +848,7 @@ void ColladaParser::ReadControllerLibrary(XmlNode &node) { if (currentName != "controller") { continue; } - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { + if (std::string id; XmlParser::getStdStrAttribute(currentNode, "id", id)) { mControllerLibrary[id] = Controller(); ReadController(currentNode, mControllerLibrary[id]); } @@ -632,13 +865,11 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "morph") { + if (const std::string ¤tName = currentNode.name(); currentName == "morph") { controller.mType = Morph; std::string id = currentNode.attribute("source").as_string(); controller.mMeshId = id.substr(1, id.size() - 1); - int methodIndex = currentNode.attribute("method").as_int(); - if (methodIndex > 0) { + if (const int methodIndex = currentNode.attribute("method").as_int(); methodIndex > 0) { std::string method; XmlParser::getValueAsString(currentNode, method); @@ -647,8 +878,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } } } else if (currentName == "skin") { - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { + if (std::string id; XmlParser::getStdStrAttribute(currentNode, "source", id)) { controller.mMeshId = id.substr(1, id.size() - 1); } } else if (currentName == "bind_shape_matrix") { @@ -656,10 +886,10 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlParser::getValueAsString(currentNode, v); const char *content = v.c_str(); const char *end = content + v.size(); - for (unsigned int a = 0; a < 16; a++) { + for (float & a : controller.mBindShapeMatrix) { SkipSpacesAndLineEnd(&content, end); // read a number - content = fast_atoreal_move(content, controller.mBindShapeMatrix[a]); + content = fast_atoreal_move(content, a); // skip whitespace after it SkipSpacesAndLineEnd(&content, end); } @@ -686,108 +916,13 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } } -// ------------------------------------------------------------------------------------------------ -// Reads the joint definitions for the given controller -void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pController) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "input") { - const char *attrSemantic = currentNode.attribute("semantic").as_string(); - const char *attrSource = currentNode.attribute("source").as_string(); - if (attrSource[0] != '#') { - throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); - } - ++attrSource; - // parse source URL to corresponding source - if (strcmp(attrSemantic, "JOINT") == 0) { - pController.mJointNameSource = attrSource; - } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { - pController.mJointOffsetMatrixSource = attrSource; - } else { - throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the joint weights for the given controller -void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) { - // Read vertex count from attributes and resize the array accordingly - int vertexCount = 0; - XmlParser::getIntAttribute(node, "count", vertexCount); - pController.mWeightCounts.resize(vertexCount); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "input") { - InputChannel channel; - - const char *attrSemantic = currentNode.attribute("semantic").as_string(); - const char *attrSource = currentNode.attribute("source").as_string(); - channel.mOffset = currentNode.attribute("offset").as_int(); - - // local URLS always start with a '#'. We don't support global URLs - if (attrSource[0] != '#') { - throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); - } - channel.mAccessor = attrSource + 1; - - // parse source URL to corresponding source - if (strcmp(attrSemantic, "JOINT") == 0) { - pController.mWeightInputJoints = channel; - } else if (strcmp(attrSemantic, "WEIGHT") == 0) { - pController.mWeightInputWeights = channel; - } else { - throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); - } - } else if (currentName == "vcount" && vertexCount > 0) { - const std::string stdText = currentNode.text().as_string(); - const char *text = stdText.c_str(); - const char *end = text + stdText.size(); - size_t numWeights = 0; - for (std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) { - if (*text == 0) { - throw DeadlyImportError("Out of data while reading "); - } - - *it = strtoul10(text, &text); - numWeights += *it; - SkipSpacesAndLineEnd(&text, end); - } - // reserve weight count - pController.mWeights.resize(numWeights); - } else if (currentName == "v" && vertexCount > 0) { - // read JointIndex - WeightIndex pairs - std::string stdText; - XmlParser::getValueAsString(currentNode, stdText); - const char *text = stdText.c_str(); - const char *end = text + stdText.size(); - for (std::vector>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) { - if (text == nullptr) { - throw DeadlyImportError("Out of data while reading "); - } - SkipSpacesAndLineEnd(&text, end); - it->first = strtoul10(text, &text); - SkipSpacesAndLineEnd(&text, end); - if (*text == 0) { - throw DeadlyImportError("Out of data while reading "); - } - it->second = strtoul10(text, &text); - SkipSpacesAndLineEnd(&text, end); - } - } - } -} - // ------------------------------------------------------------------------------------------------ // Reads the image library contents -void ColladaParser::ReadImageLibrary(XmlNode &node) { +void ColladaParser::ReadImageLibrary(const XmlNode &node) { for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "image") { - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { + if (std::basic_string id; XmlParser::getStdStrAttribute(currentNode, "id", id)) { mImageLibrary[id] = Image(); // read on from there ReadImage(currentNode, mImageLibrary[id]); @@ -798,7 +933,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) { // ------------------------------------------------------------------------------------------------ // Reads an image entry into the given image -void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { +void ColladaParser::ReadImage(const XmlNode &node, Collada::Image &pImage) const { for (XmlNode ¤tNode : node.children()) { const std::string currentName = currentNode.name(); if (currentName == "image") { @@ -861,13 +996,13 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { // Reads the material library void ColladaParser::ReadMaterialLibrary(XmlNode &node) { std::map names; - for (XmlNode ¤tNode : node.children()) { + for (const XmlNode ¤tNode : node.children()) { std::string id = currentNode.attribute("id").as_string(); std::string name = currentNode.attribute("name").as_string(); mMaterialLibrary[id] = Material(); if (!name.empty()) { - std::map::iterator it = names.find(name); + auto it = names.find(name); if (it != names.end()) { std::ostringstream strStream; strStream << ++it->second; @@ -922,107 +1057,6 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) { } } -// ------------------------------------------------------------------------------------------------ -// Reads a material entry into the given material -void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "instance_effect") { - std::string url; - readUrlAttribute(currentNode, url); - pMaterial.mEffect = url; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a light entry into the given light -void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - // TODO: Check the current technique and skip over unsupported extra techniques - - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "spot") { - pLight.mType = aiLightSource_SPOT; - } else if (currentName == "ambient") { - pLight.mType = aiLightSource_AMBIENT; - } else if (currentName == "directional") { - pLight.mType = aiLightSource_DIRECTIONAL; - } else if (currentName == "point") { - pLight.mType = aiLightSource_POINT; - } else if (currentName == "color") { - // text content contains 3 floats - std::string v; - XmlParser::getValueAsString(currentNode, v); - const char *content = v.c_str(); - const char *end = content + v.size(); - - content = fast_atoreal_move(content, (ai_real &)pLight.mColor.r); - SkipSpacesAndLineEnd(&content, end); - - content = fast_atoreal_move(content, (ai_real &)pLight.mColor.g); - SkipSpacesAndLineEnd(&content, end); - - content = fast_atoreal_move(content, (ai_real &)pLight.mColor.b); - SkipSpacesAndLineEnd(&content, end); - } else if (currentName == "constant_attenuation") { - XmlParser::getValueAsReal(currentNode, pLight.mAttConstant); - } else if (currentName == "linear_attenuation") { - XmlParser::getValueAsReal(currentNode, pLight.mAttLinear); - } else if (currentName == "quadratic_attenuation") { - XmlParser::getValueAsReal(currentNode, pLight.mAttQuadratic); - } else if (currentName == "falloff_angle") { - XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); - } else if (currentName == "falloff_exponent") { - XmlParser::getValueAsReal(currentNode, pLight.mFalloffExponent); - } - // FCOLLADA extensions - // ------------------------------------------------------- - else if (currentName == "outer_cone") { - XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); - } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone - XmlParser::getValueAsReal(currentNode, pLight.mPenumbraAngle); - } else if (currentName == "intensity") { - XmlParser::getValueAsReal(currentNode, pLight.mIntensity); - } - else if (currentName == "falloff") { - XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); - } else if (currentName == "hotspot_beam") { - XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle); - } - // OpenCOLLADA extensions - // ------------------------------------------------------- - else if (currentName == "decay_falloff") { - XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a camera entry into the given light -void ColladaParser::ReadCamera(XmlNode &node, Collada::Camera &camera) { - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "orthographic") { - camera.mOrtho = true; - } else if (currentName == "xfov" || currentName == "xmag") { - XmlParser::getValueAsReal(currentNode, camera.mHorFov); - } else if (currentName == "yfov" || currentName == "ymag") { - XmlParser::getValueAsReal(currentNode, camera.mVerFov); - } else if (currentName == "aspect_ratio") { - XmlParser::getValueAsReal(currentNode, camera.mAspect); - } else if (currentName == "znear") { - XmlParser::getValueAsReal(currentNode, camera.mZNear); - } else if (currentName == "zfar") { - XmlParser::getValueAsReal(currentNode, camera.mZFar); - } - } -} - // ------------------------------------------------------------------------------------------------ // Reads the effect library void ColladaParser::ReadEffectLibrary(XmlNode &node) { @@ -1733,7 +1767,6 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { + for (auto it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { InputChannel &input = *it; if (input.mResolved) { continue; @@ -1794,7 +1827,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { + for (auto it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { InputChannel &input = *it; if (input.mResolved) { continue; @@ -1902,11 +1935,11 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n ai_assert((baseOffset + numOffsets - 1) < indices.size()); // extract per-vertex channels using the global per-vertex offset - for (std::vector::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { + for (auto it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh); } // and extract per-index channels using there specified offset - for (std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { + for (auto it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh); } @@ -2173,40 +2206,6 @@ void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) { } } -// ------------------------------------------------------------------------------------------------ -// Reads a node transformation entry of the given type and adds it to the given node's transformation list. -void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, TransformType pType) { - if (node.empty()) { - return; - } - - std::string tagName = node.name(); - - Transform tf; - tf.mType = pType; - - // read SID - if (XmlParser::hasAttribute(node, "sid")) { - XmlParser::getStdStrAttribute(node, "sid", tf.mID); - } - - // how many parameters to read per transformation type - static constexpr unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; - std::string value; - XmlParser::getValueAsString(node, value); - const char *content = value.c_str(); - const char *end = value.c_str() + value.size(); - // read as many parameters and store in the transformation - for (unsigned int a = 0; a < sNumParameters[pType]; a++) { - // skip whitespace before the number - SkipSpacesAndLineEnd(&content, end); - // read a number - content = fast_atoreal_move(content, tf.f[a]); - } - - // place the transformation at the queue of the node - pNode->mTransforms.push_back(tf); -} // ------------------------------------------------------------------------------------------------ // Processes bind_vertex_input and bind elements @@ -2244,9 +2243,7 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary for (auto &it : mImageLibrary) { - Collada::Image &image = it.second; - - if (image.mImageData.empty()) { + if (Image &image = it.second; image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); if (image_file) { image.mImageData.resize(image_file->FileSize()); @@ -2329,7 +2326,7 @@ void ColladaParser::ReadScene(XmlNode &node) { } // find the referred scene, skip the leading # - NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1); + auto sit = mNodeLibrary.find(url.c_str() + 1); if (sit == mNodeLibrary.end()) { throw DeadlyImportError("Unable to resolve visual_scene reference \"", std::string(std::move(url)), "\" in element."); } @@ -2401,7 +2398,7 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector // ------------------------------------------------------------------------------------------------ // Determines the input data type for the given semantic string -Collada::InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) { +InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) { if (semantic.empty()) { ASSIMP_LOG_WARN("Vertex input type is empty."); return IT_Invalid; diff --git a/code/AssetLib/Collada/ColladaParser.h b/code/AssetLib/Collada/ColladaParser.h index 90619a838..e2bc895df 100644 --- a/code/AssetLib/Collada/ColladaParser.h +++ b/code/AssetLib/Collada/ColladaParser.h @@ -48,7 +48,6 @@ #define AI_COLLADAPARSER_H_INC #include "ColladaHelper.h" -#include #include #include @@ -67,268 +66,240 @@ class ZipArchiveIOSystem; class ColladaParser { friend class ColladaLoader; - /** Converts a path read from a collada file to the usual representation */ - static void UriDecodePath(aiString &ss); +public: + /// Map for generic metadata as aiString. + using StringMetaData = std::map; -protected: - /** Map for generic metadata as aiString */ - typedef std::map StringMetaData; - - /** Constructor from XML file */ + /// Constructor from XML file. ColladaParser(IOSystem *pIOHandler, const std::string &pFile); - /** Destructor */ + /// Destructor ~ColladaParser(); - /** Attempts to read the ZAE manifest and returns the DAE to open */ + /// Attempts to read the ZAE manifest and returns the DAE to open static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive); - /** Reads the contents of the file */ + /// Reads the contents of the file void ReadContents(XmlNode &node); - /** Reads the structure of the file */ + /// Reads the structure of the file void ReadStructure(XmlNode &node); - /** Reads asset information such as coordinate system information and legal blah */ + /// Reads asset information such as coordinate system information and legal blah void ReadAssetInfo(XmlNode &node); - /** Reads contributor information such as author and legal blah */ + /// Reads contributor information such as author and legal blah void ReadContributorInfo(XmlNode &node); - /** Reads generic metadata into provided map and renames keys for Assimp */ - void ReadMetaDataItem(XmlNode &node, StringMetaData &metadata); - - /** Reads the animation library */ + /// Reads the animation library void ReadAnimationLibrary(XmlNode &node); - /** Reads the animation clip library */ + /// Reads the animation clip library void ReadAnimationClipLibrary(XmlNode &node); - /** Unwrap controllers dependency hierarchy */ + /// Unwrap controllers dependency hierarchy void PostProcessControllers(); - /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */ + /// Re-build animations from animation clip library, if present, otherwise combine single-channel animations void PostProcessRootAnimations(); - /** Reads an animation into the given parent structure */ + /// Reads an animation into the given parent structure void ReadAnimation(XmlNode &node, Collada::Animation *pParent); - /** Reads an animation sampler into the given anim channel */ - void ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel); - - /** Reads the skeleton controller library */ + /// Reads the skeleton controller library void ReadControllerLibrary(XmlNode &node); - /** Reads a controller into the given mesh structure */ + /// Reads a controller into the given mesh structure void ReadController(XmlNode &node, Collada::Controller &pController); - /** Reads the joint definitions for the given controller */ - void ReadControllerJoints(XmlNode &node, Collada::Controller &pController); + /// Reads the image library contents + void ReadImageLibrary(const XmlNode &node); - /** Reads the joint weights for the given controller */ - void ReadControllerWeights(XmlNode &node, Collada::Controller &pController); + /// Reads an image entry into the given image + void ReadImage(const XmlNode &node, Collada::Image &pImage) const; - /** Reads the image library contents */ - void ReadImageLibrary(XmlNode &node); - - /** Reads an image entry into the given image */ - void ReadImage(XmlNode &node, Collada::Image &pImage); - - /** Reads the material library */ + /// Reads the material library void ReadMaterialLibrary(XmlNode &node); - /** Reads a material entry into the given material */ - void ReadMaterial(XmlNode &node, Collada::Material &pMaterial); - - /** Reads the camera library */ + /// Reads the camera library void ReadCameraLibrary(XmlNode &node); - /** Reads a camera entry into the given camera */ - void ReadCamera(XmlNode &node, Collada::Camera &pCamera); - - /** Reads the light library */ + /// Reads the light library void ReadLightLibrary(XmlNode &node); - /** Reads a light entry into the given light */ - void ReadLight(XmlNode &node, Collada::Light &pLight); - - /** Reads the effect library */ + /// Reads the effect library void ReadEffectLibrary(XmlNode &node); - /** Reads an effect entry into the given effect*/ + /// Reads an effect entry into the given effect void ReadEffect(XmlNode &node, Collada::Effect &pEffect); - /** Reads an COMMON effect profile */ + /// Reads an COMMON effect profile void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect); - /** Read sampler properties */ + /// Read sampler properties void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler); - /** Reads an effect entry containing a color or a texture defining that color */ + /// Reads an effect entry containing a color or a texture defining that color void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler); - /** Reads an effect entry containing a float */ + /// Reads an effect entry containing a float void ReadEffectFloat(XmlNode &node, ai_real &pFloat); - /** Reads an effect parameter specification of any kind */ + /// Reads an effect parameter specification of any kind void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam); - /** Reads the geometry library contents */ + /// Reads the geometry library contents void ReadGeometryLibrary(XmlNode &node); - /** Reads a geometry from the geometry library. */ + /// Reads a geometry from the geometry library. void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh); - /** Reads a mesh from the geometry library */ + /// Reads a mesh from the geometry library void ReadMesh(XmlNode &node, Collada::Mesh &pMesh); - /** Reads a source element - a combination of raw data and an accessor defining - * things that should not be redefinable. Yes, that's another rant. - */ + /// Reads a source element - a combination of raw data and an accessor defining + ///things that should not be definable. Yes, that's another rant. void ReadSource(XmlNode &node); - /** Reads a data array holding a number of elements, and stores it in the global library. - * Currently supported are array of floats and arrays of strings. - */ + /// Reads a data array holding a number of elements, and stores it in the global library. + /// Currently supported are array of floats and arrays of strings. void ReadDataArray(XmlNode &node); - /** Reads an accessor and stores it in the global library under the given ID - - * accessors use the ID of the parent element - */ + /// Reads an accessor and stores it in the global library under the given ID - + /// accessors use the ID of the parent element void ReadAccessor(XmlNode &node, const std::string &pID); - /** Reads input declarations of per-vertex mesh data into the given mesh */ + /// Reads input declarations of per-vertex mesh data into the given mesh void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh); - /** Reads input declarations of per-index mesh data into the given mesh */ + /// Reads input declarations of per-index mesh data into the given mesh void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh); - /** Reads a single input channel element and stores it in the given array, if valid */ + /// Reads a single input channel element and stores it in the given array, if valid void ReadInputChannel(XmlNode &node, std::vector &poChannels); - /** Reads a

primitive index list and assembles the mesh data into the given mesh */ + /// Reads a

primitive index list and assembles the mesh data into the given mesh size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector &pPerIndexChannels, size_t pNumPrimitives, const std::vector &pVCount, Collada::PrimitiveType pPrimType); - /** Copies the data for a single primitive into the mesh, based on the InputChannels */ + /// Copies the data for a single primitive into the mesh, based on the InputChannels void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Collada::Mesh &pMesh, std::vector &pPerIndexChannels, size_t currentPrimitive, const std::vector &indices); - /** Reads one triangle of a tristrip into the mesh */ + /// Reads one triangle of a tristrip into the mesh void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh, std::vector &pPerIndexChannels, size_t currentPrimitive, const std::vector &indices); - /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */ + /// Extracts a single object from an input channel and stores it in the appropriate mesh data array void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh); - /** Reads the library of node hierarchies and scene parts */ + /// Reads the library of node hierarchies and scene parts void ReadSceneLibrary(XmlNode &node); - /** Reads a scene node's contents including children and stores it in the given node */ + /// Reads a scene node's contents including children and stores it in the given node void ReadSceneNode(XmlNode &node, Collada::Node *pNode); - - /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */ - void ReadNodeTransformation(XmlNode &node, Collada::Node *pNode, Collada::TransformType pType); - - /** Reads a mesh reference in a node and adds it to the node's mesh list */ + + /// Reads a mesh reference in a node and adds it to the node's mesh list void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode); - /** Reads the collada scene */ + /// Reads the collada scene void ReadScene(XmlNode &node); - // Processes bind_vertex_input and bind elements + /// Processes bind_vertex_input and bind elements void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl); - /** Reads embedded textures from a ZAE archive*/ + /// Reads embedded textures from a ZAE archive void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive); protected: - /** Calculates the resulting transformation from all the given transform steps */ + /// Converts a path read from a collada file to the usual representation + static void UriDecodePath(aiString &ss); + + /// Calculates the resulting transformation from all the given transform steps aiMatrix4x4 CalculateResultTransform(const std::vector &pTransforms) const; - /** Determines the input data type for the given semantic string */ + /// Determines the input data type for the given semantic string Collada::InputType GetTypeForSemantic(const std::string &pSemantic); - /** Finds the item in the given library by its reference, throws if not found */ + /// Finds the item in the given library by its reference, throws if not found template const Type &ResolveLibraryReference(const std::map &pLibrary, const std::string &pURL) const; -protected: - // Filename, for a verbose error message +private: + /// Filename, for a verbose error message std::string mFileName; - // XML reader, member for everyday use + /// XML reader, member for everyday use XmlParser mXmlParser; - /** All data arrays found in the file by ID. Might be referred to by actually - everyone. Collada, you are a steaming pile of indirection. */ + /// All data arrays found in the file by ID. Might be referred to by actually + /// everyone. Collada, you are a steaming pile of indirection. using DataLibrary = std::map ; DataLibrary mDataLibrary; - /** Same for accessors which define how the data in a data array is accessed. */ + /// Same for accessors which define how the data in a data array is accessed. using AccessorLibrary = std::map ; AccessorLibrary mAccessorLibrary; - /** Mesh library: mesh by ID */ + /// Mesh library: mesh by ID using MeshLibrary = std::map; MeshLibrary mMeshLibrary; - /** node library: root node of the hierarchy part by ID */ + /// node library: root node of the hierarchy part by ID using NodeLibrary = std::map; NodeLibrary mNodeLibrary; - /** Image library: stores texture properties by ID */ + /// Image library: stores texture properties by ID using ImageLibrary = std::map ; ImageLibrary mImageLibrary; - /** Effect library: surface attributes by ID */ + /// Effect library: surface attributes by ID using EffectLibrary = std::map ; EffectLibrary mEffectLibrary; - /** Material library: surface material by ID */ + /// Material library: surface material by ID using MaterialLibrary = std::map ; MaterialLibrary mMaterialLibrary; - /** Light library: surface light by ID */ + /// Light library: surface light by ID using LightLibrary = std::map ; LightLibrary mLightLibrary; - /** Camera library: surface material by ID */ + /// Camera library: surface material by ID using CameraLibrary = std::map ; CameraLibrary mCameraLibrary; - /** Controller library: joint controllers by ID */ + /// Controller library: joint controllers by ID using ControllerLibrary = std::map ; ControllerLibrary mControllerLibrary; - /** Animation library: animation references by ID */ + /// Animation library: animation references by ID using AnimationLibrary = std::map ; AnimationLibrary mAnimationLibrary; - /** Animation clip library: clip animation references by ID */ + /// Animation clip library: clip animation references by ID using AnimationClipLibrary = std::vector>> ; AnimationClipLibrary mAnimationClipLibrary; - /** Pointer to the root node. Don't delete, it just points to one of - the nodes in the node library. */ + /// Pointer to the root node. Don't delete, it just points to one of the nodes in the node library. Collada::Node *mRootNode; - /** Root animation container */ + /// Root animation container Collada::Animation mAnims; - /** Size unit: how large compared to a meter */ + /// Size unit: how large compared to a meter ai_real mUnitSize; - /** Which is the up vector */ + /// Which is the up vector enum { UP_X, UP_Y, UP_Z } mUpDirection; - /** Asset metadata (global for scene) */ + /// Asset metadata (global for scene) StringMetaData mAssetMetaData; - /** Collada file format version */ + /// Collada file format version Collada::FormatVersion mFormat; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91ce9c902..392951b29 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,7 @@ SET( COMMON unit/TestModelFactory.h unit/utTypes.cpp unit/utVersion.cpp + unit/Common/utParsingUtils.cpp unit/utProfiler.cpp unit/utSharedPPData.cpp unit/utStringUtils.cpp diff --git a/test/unit/Common/utParsingUtils.cpp b/test/unit/Common/utParsingUtils.cpp new file mode 100644 index 000000000..09ff0fcac --- /dev/null +++ b/test/unit/Common/utParsingUtils.cpp @@ -0,0 +1,66 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2025, 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. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" +#include +#include +#include + +using namespace Assimp; + +class utParsingUtils : public ::testing::Test {}; + +TEST_F(utParsingUtils, parseFloatsStringTest) { + const std::array floatArray = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 7.54979e-8f, -1.0f, 0.0f, + 0.0f, 1.0f, 7.54979e-8f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + const std::string floatArrayAsStr = "1 0 0 0 0 7.54979e-8 -1 0 0 1 7.54979e-8 0 0 0 0 1"; + const char *content = floatArrayAsStr.c_str(); + const char *end = content + floatArrayAsStr.size(); + for (float i : floatArray) { + float value = 0.0f; + SkipSpacesAndLineEnd(&content, end); + content = fast_atoreal_move(content, value); + EXPECT_FLOAT_EQ(value, i); + } +} diff --git a/test/unit/utFastAtof.cpp b/test/unit/utFastAtof.cpp index 3d77a6d02..3c34565fb 100644 --- a/test/unit/utFastAtof.cpp +++ b/test/unit/utFastAtof.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2025, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -122,6 +120,7 @@ protected: TEST_CASE(0e-19); TEST_CASE(400012); TEST_CASE(5.9e-76); + TEST_CASE(7.54979e-8); TEST_CASE_INF(inf); TEST_CASE_INF(inf); TEST_CASE_INF(infinity); diff --git a/test/unit/utTypes.cpp b/test/unit/utTypes.cpp index df94124b4..b5c91c2e9 100644 --- a/test/unit/utTypes.cpp +++ b/test/unit/utTypes.cpp @@ -1,12 +1,9 @@ /* --------------------------------------------------------------------------- Open Asset Import Library (assimp) ---------------------------------------------------------------------------- Copyright (c) 2006-2025, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms,