diff --git a/code/Collada/ColladaExporter.cpp b/code/Collada/ColladaExporter.cpp index 6fd31ed3b..a93dfa59a 100644 --- a/code/Collada/ColladaExporter.cpp +++ b/code/Collada/ColladaExporter.cpp @@ -92,6 +92,36 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p } // end of namespace Assimp +// ------------------------------------------------------------------------------------------------ +// Encodes a string into a valid XML ID using the xsd:ID schema qualifications. +static const std::string XMLIDEncode(const std::string& name) { + const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-."; + const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char); + + if (name.length() == 0) { + return name; + } + + std::stringstream idEncoded; + + // xsd:ID must start with letter or underscore + if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) { + idEncoded << '_'; + } + + for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) { + // xsd:ID can only contain letters, digits, underscores, hyphens and periods + if (strchr(XML_ID_CHARS, *it) != nullptr) { + idEncoded << *it; + } else { + // Select placeholder character based on invalid character to prevent name collisions + idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT]; + } + } + + return idEncoded.str(); +} + // ------------------------------------------------------------------------------------------------ // Constructor for a specific scene to export ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) @@ -146,7 +176,7 @@ void ColladaExporter::WriteFile() { // useless Collada fu at the end, just in case we haven't had enough indirections, yet. mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mRootNode->mName.C_Str()) + "\" />" << endstr; + mOutput << startstr << "mRootNode->mName.C_Str()) + "\" />" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); @@ -356,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() { void ColladaExporter::WriteCamera(size_t pIndex){ const aiCamera *cam = mScene->mCameras[pIndex]; - const std::string idstrEscaped = XMLEscape(cam->mName.C_Str()); + const std::string cameraName = XMLEscape(cam->mName.C_Str()); + const std::string cameraId = XMLIDEncode(cam->mName.C_Str()); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); @@ -412,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() { void ColladaExporter::WriteLight(size_t pIndex){ const aiLight *light = mScene->mLights[pIndex]; - const std::string idstrEscaped = XMLEscape(light->mName.C_Str()); + const std::string lightName = XMLEscape(light->mName.C_Str()); + const std::string lightId = XMLIDEncode(light->mName.C_Str()); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); @@ -586,7 +618,7 @@ static bool isalnum_C(char c) { void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) { if( !pSurface.texture.empty() ) { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << ""; @@ -619,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std } else { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } PopTag(); mOutput << startstr << "" << endstr; @@ -633,21 +665,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture if( !pSurface.texture.empty() ) { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << XMLEscape(pMatName) << "-" << pTypeName << "-image" << endstr; + mOutput << startstr << "" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << XMLEscape(pMatName) << "-" << pTypeName << "-surface" << endstr; + mOutput << startstr << "" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); @@ -699,11 +731,6 @@ void ColladaExporter::WriteMaterials() materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName); } } - for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) { - if( !isalnum_C( *it ) ) { - *it = '_'; - } - } aiShadingMode shading = aiShadingMode_Flat; materials[a].shading_model = "phong"; @@ -768,7 +795,7 @@ void ColladaExporter::WriteMaterials() { const Material& mat = *it; // this is so ridiculous it must be right - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); @@ -819,9 +846,9 @@ void ColladaExporter::WriteMaterials() for( std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it ) { const Material& mat = *it; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; } @@ -850,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary() void ColladaExporter::WriteController( size_t pIndex) { const aiMesh* mesh = mScene->mMeshes[pIndex]; - const std::string idstr = GetMeshId( pIndex); - const std::string idstrEscaped = XMLEscape(idstr); + const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str(); + const std::string idstrEscaped = XMLIDEncode(idstr); if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) return; @@ -886,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex) mOutput << startstr << "mNumBones << "\">"; for( size_t i = 0; i < mesh->mNumBones; ++i ) - mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " "; + mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " "; mOutput << "" << endstr; @@ -1021,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary() void ColladaExporter::WriteGeometry( size_t pIndex) { const aiMesh* mesh = mScene->mMeshes[pIndex]; - const std::string idstr = GetMeshId( pIndex); - const std::string idstrEscaped = XMLEscape(idstr); + const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str(); + const std::string geometryName = XMLEscape(idstr); + const std::string geometryId = XMLIDEncode(idstr); if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) return; // opening tag - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; @@ -1059,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex) // assemble vertex structure // Only write input for POSITION since we will write other as shared inputs in polygon definition - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; @@ -1079,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex) { mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; if( mesh->HasNormals() ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { if( mesh->HasTextureCoords(static_cast(a)) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) { if( mesh->HasVertexColors(static_cast(a) ) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } mOutput << startstr << "

"; @@ -1113,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex) { mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; if( mesh->HasNormals() ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { if( mesh->HasTextureCoords(static_cast(a)) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) { if( mesh->HasVertexColors(static_cast(a) ) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } mOutput << startstr << ""; @@ -1173,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy return; } - std::string arrayId = pIdString + "-array"; + std::string arrayId = XMLIDEncode(pIdString) + "-array"; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); // source array - mOutput << startstr << " "; + mOutput << startstr << " "; PushTag(); if( pType == FloatType_TexCoord2 ) @@ -1265,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy // Writes the scene library void ColladaExporter::WriteSceneLibrary() { - const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); + const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str()); + const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str()); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); // start recursive write at the root node @@ -1300,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) idstr = idstr + ending; } - const std::string idstrEscaped = XMLEscape(idstr); + const std::string idstrEscaped = XMLIDEncode(idstr); mOutput << startstr << "" << endstr; PushTag(); @@ -1372,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) } const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); - std::string arrayId = node_idstr + "-array"; + std::string arrayId = XMLIDEncode(node_idstr) + "-array"; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); // source array - mOutput << startstr << " "; + mOutput << startstr << " "; for( size_t a = 0; a < names.size(); ++a ) { mOutput << names[a] << " "; } @@ -1387,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; @@ -1409,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) { // samplers const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; - mOutput << startstr << "mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; - mOutput << startstr << "mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; PopTag(); mOutput << startstr << "" << endstr; @@ -1426,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) { // channels - mOutput << startstr << "mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; } } @@ -1437,8 +1466,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) // ------------------------------------------------------------------------------------------------ void ColladaExporter::WriteAnimationsLibrary() { - const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); - if ( mScene->mNumAnimations > 0 ) { mOutput << startstr << "" << endstr; PushTag(); @@ -1546,16 +1573,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) } } - const std::string node_name_escaped = XMLEscape(pNode->mName.data); + const std::string node_id = XMLIDEncode(pNode->mName.data); + const std::string node_name = XMLEscape(pNode->mName.data); mOutput << startstr << "" << endstr; PushTag(); @@ -1594,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) //check if it is a camera node for(size_t i=0; imNumCameras; i++){ if(mScene->mCameras[i]->mName == pNode->mName){ - mOutput << startstr <<"" << endstr; + mOutput << startstr <<"" << endstr; break; } } //check if it is a light node for(size_t i=0; imNumLights; i++){ if(mScene->mLights[i]->mName == pNode->mName){ - mOutput << startstr <<"" << endstr; + mOutput << startstr <<"" << endstr; break; } } @@ -1615,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) continue; + const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str(); + if( mesh->mNumBones == 0 ) { - mOutput << startstr << "mMeshes[a])) << "\">" << endstr; + mOutput << startstr << "" << endstr; PushTag(); } else { mOutput << startstr - << "mMeshes[a])) << "-skin\">" + << "" << endstr; PushTag(); @@ -1631,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) // use the first bone to find skeleton root const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh ); if ( skeletonRootBoneNode ) { - mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() ); + mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() ); } mOutput << startstr << "#" << mFoundSkeletonRootNodeID << "" << endstr; } @@ -1639,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mMaterialIndex].name) << "\">" << endstr; + mOutput << startstr << "mMaterialIndex].name) << "\">" << endstr; PushTag(); for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) {