Browse Source

Bugfix/ensure collada parsing works issue 1488 (#6087)

* Add text to ensure that float parsing in colladata works right
Kim Kulling 3 months ago
parent
commit
2f3e72413f

+ 47 - 47
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<const Node*> &resolved) {
+        std::vector<const Node*> &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<std::string, SemanticMappingTable>::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<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find(meshMaterial);
+            auto matIt = mMaterialIndexByName.find(meshMaterial);
             unsigned int matIdx = 0;
             if (matIt != mMaterialIndexByName.end()) {
                 matIdx = static_cast<unsigned int>(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<ColladaMeshIndex, size_t>::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<std::string, size_t>::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
+                auto subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
                 if (subMatIt != mMaterialIndexByName.end()) {
                     dstMesh->mMaterialIndex = static_cast<unsigned int>(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<float> targetWeights;
     Collada::MorphMethod method = Normalized;
 
-    for (std::map<std::string, Controller>::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<IndexPairVector::const_iterator> 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<MorphTimeValues> &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<AnimationChannel>::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<unsigned int>(resultTrafos.size());
             dstAnim->mNumRotationKeys = static_cast<unsigned int>(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> 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<unsigned int>(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 {

+ 3 - 16
code/AssetLib/Collada/ColladaLoader.h

@@ -109,7 +109,7 @@ protected:
 
     /// Resolve node instances
     void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
-            std::vector<const Collada::Node *> &resolved);
+            std::vector<const Collada::Node *> &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<const aiNode *> &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;
 

File diff suppressed because it is too large
+ 354 - 357
code/AssetLib/Collada/ColladaParser.cpp


+ 82 - 111
code/AssetLib/Collada/ColladaParser.h

@@ -48,7 +48,6 @@
 #define AI_COLLADAPARSER_H_INC
 
 #include "ColladaHelper.h"
-#include <assimp/TinyFormatter.h>
 #include <assimp/ai_assert.h>
 #include <assimp/XmlParser.h>
 
@@ -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);
-
-protected:
-    /** Map for generic metadata as aiString */
-    typedef std::map<std::string, aiString> StringMetaData;
+public:
+    /// Map for generic metadata as aiString.
+    using StringMetaData = std::map<std::string, aiString>;
 
-    /** 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 joint weights for the given controller */
-    void ReadControllerWeights(XmlNode &node, Collada::Controller &pController);
+    /// Reads the image library contents
+    void ReadImageLibrary(const XmlNode &node);
 
-    /** Reads the image library contents */
-    void ReadImageLibrary(XmlNode &node);
+    /// Reads an image entry into the given image
+    void ReadImage(const XmlNode &node, Collada::Image &pImage) const;
 
-    /** 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 <source> element
-         */
+    /// Reads an accessor and stores it in the global library under the given ID -
+    /// accessors use the ID of the parent <source> 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<Collada::InputChannel> &poChannels);
 
-    /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+    /// Reads a <p> primitive index list and assembles the mesh data into the given mesh
     size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
             size_t pNumPrimitives, const std::vector<size_t> &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<Collada::InputChannel> &pPerIndexChannels,
             size_t currentPrimitive, const std::vector<size_t> &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<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &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<Collada::Transform> &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 <typename Type>
     const Type &ResolveLibraryReference(const std::map<std::string, Type> &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<std::string, Collada::Data> ;
     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<std::string, Collada::Accessor> ;
     AccessorLibrary mAccessorLibrary;
 
-    /** Mesh library: mesh by ID */
+    /// Mesh library: mesh by ID
     using MeshLibrary = std::map<std::string, Collada::Mesh *>;
     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<std::string, Collada::Node *>;
     NodeLibrary mNodeLibrary;
 
-    /** Image library: stores texture properties by ID */
+    /// Image library: stores texture properties by ID
     using ImageLibrary = std::map<std::string, Collada::Image> ;
     ImageLibrary mImageLibrary;
 
-    /** Effect library: surface attributes by ID */
+    /// Effect library: surface attributes by ID
     using EffectLibrary = std::map<std::string, Collada::Effect> ;
     EffectLibrary mEffectLibrary;
 
-    /** Material library: surface material by ID */
+    /// Material library: surface material by ID
     using MaterialLibrary = std::map<std::string, Collada::Material> ;
     MaterialLibrary mMaterialLibrary;
 
-    /** Light library: surface light by ID */
+    /// Light library: surface light by ID
     using LightLibrary = std::map<std::string, Collada::Light> ;
     LightLibrary mLightLibrary;
 
-    /** Camera library: surface material by ID */
+    /// Camera library: surface material by ID
     using CameraLibrary = std::map<std::string, Collada::Camera> ;
     CameraLibrary mCameraLibrary;
 
-    /** Controller library: joint controllers by ID */
+    /// Controller library: joint controllers by ID
     using ControllerLibrary = std::map<std::string, Collada::Controller> ;
     ControllerLibrary mControllerLibrary;
 
-    /** Animation library: animation references by ID */
+    /// Animation library: animation references by ID
     using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
     AnimationLibrary mAnimationLibrary;
 
-    /** Animation clip library: clip animation references by ID */
+    /// Animation clip library: clip animation references by ID
     using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
     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;
 };
 

+ 1 - 0
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

+ 66 - 0
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 <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <array>
+
+using namespace Assimp;
+
+class utParsingUtils : public ::testing::Test {};
+
+TEST_F(utParsingUtils, parseFloatsStringTest) {
+    const std::array<float, 16> 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<ai_real>(content, value);
+        EXPECT_FLOAT_EQ(value, i);
+    }
+}

+ 1 - 2
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);

+ 0 - 3
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,

Some files were not shown because too many files changed in this diff