diff options
author | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
commit | f1fe73d1909a2448a004a88362a1a532d0d4f7c3 (patch) | |
tree | ab37ae3837e2f858de2932bcee9f26e69fab3db1 /libs/assimp/code/AssetLib/Collada | |
parent | f567ea1e2798fd3156a416e61f083ea3e6b95719 (diff) |
switch to tinyobj and nanovg from assimp and cairo
Diffstat (limited to 'libs/assimp/code/AssetLib/Collada')
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaExporter.cpp | 1748 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaExporter.h | 257 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaHelper.cpp | 99 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaHelper.h | 679 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaLoader.cpp | 1828 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaLoader.h | 249 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaParser.cpp | 2402 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Collada/ColladaParser.h | 348 |
8 files changed, 0 insertions, 7610 deletions
diff --git a/libs/assimp/code/AssetLib/Collada/ColladaExporter.cpp b/libs/assimp/code/AssetLib/Collada/ColladaExporter.cpp deleted file mode 100644 index 5c91daa..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaExporter.cpp +++ /dev/null @@ -1,1748 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER - -#include "ColladaExporter.h" - -#include <assimp/Bitmap.h> -#include <assimp/ColladaMetaData.h> -#include <assimp/DefaultIOSystem.h> -#include <assimp/Exceptional.h> -#include <assimp/MathFunctions.h> -#include <assimp/SceneCombiner.h> -#include <assimp/StringUtils.h> -#include <assimp/XMLTools.h> -#include <assimp/commonMetaData.h> -#include <assimp/fast_atof.h> -#include <assimp/scene.h> -#include <assimp/Exporter.hpp> -#include <assimp/IOSystem.hpp> - -#include <ctime> -#include <memory> - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp -void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) { - std::string path = DefaultIOSystem::absolutePath(std::string(pFile)); - std::string file = DefaultIOSystem::completeBaseName(std::string(pFile)); - - // invoke the exporter - ColladaExporter iDoTheExportThing(pScene, pIOSystem, path, file); - - if (iDoTheExportThing.mOutput.fail()) { - throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); - } - - // we're still here - export successfully completed. Write result to the given IOSYstem - std::unique_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wt")); - if (outfile == nullptr) { - throw DeadlyExportError("could not open output .dae file: " + std::string(pFile)); - } - - // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy. - outfile->Write(iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()), 1); -} - -// ------------------------------------------------------------------------------------------------ -// 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 reduce ID collisions - idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT]; - } - } - - return idEncoded.str(); -} - -// ------------------------------------------------------------------------------------------------ -// Helper functions to create unique ids -inline bool IsUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idStr) { - return (idSet.find(idStr) == idSet.end()); -} - -inline std::string MakeUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idPrefix, const std::string &postfix) { - std::string result(idPrefix + postfix); - if (!IsUniqueId(idSet, result)) { - // Select a number to append - size_t idnum = 1; - do { - result = idPrefix + '_' + ai_to_string(idnum) + postfix; - ++idnum; - } while (!IsUniqueId(idSet, result)); - } - return result; -} - -// ------------------------------------------------------------------------------------------------ -// Constructor for a specific scene to export -ColladaExporter::ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file) : - mIOSystem(pIOSystem), - mPath(path), - mFile(file), - mScene(pScene), - endstr("\n") { - // make sure that all formatting happens using the standard, C locale and not the user's current locale - mOutput.imbue(std::locale("C")); - mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION); - - // start writing the file - WriteFile(); -} - -// ------------------------------------------------------------------------------------------------ -// Destructor -ColladaExporter::~ColladaExporter() { -} - -// ------------------------------------------------------------------------------------------------ -// Starts writing the contents -void ColladaExporter::WriteFile() { - // write the DTD - mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr; - // COLLADA element start - mOutput << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">" << endstr; - PushTag(); - - WriteTextures(); - WriteHeader(); - - // Add node names to the unique id database first so they are most likely to use their names as unique ids - CreateNodeIds(mScene->mRootNode); - - WriteCamerasLibrary(); - WriteLightsLibrary(); - WriteMaterials(); - WriteGeometryLibrary(); - WriteControllerLibrary(); - - WriteSceneLibrary(); - - // customized, Writes the animation library - WriteAnimationsLibrary(); - - // instantiate the scene(s) - // For Assimp there will only ever be one - mOutput << startstr << "<scene>" << endstr; - PushTag(); - mOutput << startstr << "<instance_visual_scene url=\"#" + mSceneId + "\" />" << endstr; - PopTag(); - mOutput << startstr << "</scene>" << endstr; - PopTag(); - mOutput << "</COLLADA>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the asset header -void ColladaExporter::WriteHeader() { - static const ai_real epsilon = Math::getEpsilon<ai_real>(); - static const aiQuaternion x_rot(aiMatrix3x3( - 0, -1, 0, - 1, 0, 0, - 0, 0, 1)); - static const aiQuaternion y_rot(aiMatrix3x3( - 1, 0, 0, - 0, 1, 0, - 0, 0, 1)); - static const aiQuaternion z_rot(aiMatrix3x3( - 1, 0, 0, - 0, 0, 1, - 0, -1, 0)); - - static const unsigned int date_nb_chars = 20; - char date_str[date_nb_chars]; - std::time_t date = std::time(nullptr); - std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date)); - - aiVector3D scaling; - aiQuaternion rotation; - aiVector3D position; - mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position); - rotation.Normalize(); - - mAdd_root_node = false; - - ai_real scale = 1.0; - if (std::abs(scaling.x - scaling.y) <= epsilon && std::abs(scaling.x - scaling.z) <= epsilon && std::abs(scaling.y - scaling.z) <= epsilon) { - scale = (ai_real)((((double)scaling.x) + ((double)scaling.y) + ((double)scaling.z)) / 3.0); - } else { - mAdd_root_node = true; - } - - std::string up_axis = "Y_UP"; - if (rotation.Equal(x_rot, epsilon)) { - up_axis = "X_UP"; - } else if (rotation.Equal(y_rot, epsilon)) { - up_axis = "Y_UP"; - } else if (rotation.Equal(z_rot, epsilon)) { - up_axis = "Z_UP"; - } else { - mAdd_root_node = true; - } - - if (!position.Equal(aiVector3D(0, 0, 0))) { - mAdd_root_node = true; - } - - // Assimp root nodes can have meshes, Collada Scenes cannot - if (mScene->mRootNode->mNumChildren == 0 || mScene->mRootNode->mMeshes != 0) { - mAdd_root_node = true; - } - - if (mAdd_root_node) { - up_axis = "Y_UP"; - scale = 1.0; - } - - mOutput << startstr << "<asset>" << endstr; - PushTag(); - mOutput << startstr << "<contributor>" << endstr; - PushTag(); - - // If no Scene metadata, use root node metadata - aiMetadata *meta = mScene->mMetaData; - if (nullptr == meta) { - meta = mScene->mRootNode->mMetaData; - } - - aiString value; - if (!meta || !meta->Get("Author", value)) { - mOutput << startstr << "<author>" - << "Assimp" - << "</author>" << endstr; - } else { - mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr; - } - - if (nullptr == meta || !meta->Get(AI_METADATA_SOURCE_GENERATOR, value)) { - mOutput << startstr << "<authoring_tool>" - << "Assimp Exporter" - << "</authoring_tool>" << endstr; - } else { - mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr; - } - - if (meta) { - if (meta->Get("Comments", value)) { - mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr; - } - if (meta->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) { - mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr; - } - if (meta->Get("SourceData", value)) { - mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr; - } - } - - PopTag(); - mOutput << startstr << "</contributor>" << endstr; - - if (nullptr == meta || !meta->Get("Created", value)) { - mOutput << startstr << "<created>" << date_str << "</created>" << endstr; - } else { - mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr; - } - - // Modified date is always the date saved - mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr; - - if (meta) { - if (meta->Get("Keywords", value)) { - mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr; - } - if (meta->Get("Revision", value)) { - mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr; - } - if (meta->Get("Subject", value)) { - mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr; - } - if (meta->Get("Title", value)) { - mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr; - } - } - - mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr; - mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr; - PopTag(); - mOutput << startstr << "</asset>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteTextures() { - static const unsigned int buffer_size = 1024; - char str[buffer_size]; - - if (mScene->HasTextures()) { - for (unsigned int i = 0; i < mScene->mNumTextures; i++) { - // It would be great to be able to create a directory in portable standard C++, but it's not the case, - // so we just write the textures in the current directory. - - aiTexture *texture = mScene->mTextures[i]; - if (nullptr == texture) { - continue; - } - - ASSIMP_itoa10(str, buffer_size, i + 1); - - std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint); - - std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb")); - if (outfile == nullptr) { - throw DeadlyExportError("could not open output texture file: " + mPath + name); - } - - if (texture->mHeight == 0) { - outfile->Write((void *)texture->pcData, texture->mWidth, 1); - } else { - Bitmap::Save(texture, outfile.get()); - } - - outfile->Flush(); - - textures.insert(std::make_pair(i, name)); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteCamerasLibrary() { - if (mScene->HasCameras()) { - - mOutput << startstr << "<library_cameras>" << endstr; - PushTag(); - - for (size_t a = 0; a < mScene->mNumCameras; ++a) - WriteCamera(a); - - PopTag(); - mOutput << startstr << "</library_cameras>" << endstr; - } -} - -void ColladaExporter::WriteCamera(size_t pIndex) { - - const aiCamera *cam = mScene->mCameras[pIndex]; - const std::string cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex); - const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex); - - mOutput << startstr << "<camera id=\"" << cameraId << "\" name=\"" << cameraName << "\" >" << endstr; - PushTag(); - mOutput << startstr << "<optics>" << endstr; - PushTag(); - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - //assimp doesn't support the import of orthographic cameras! se we write - //always perspective - mOutput << startstr << "<perspective>" << endstr; - PushTag(); - mOutput << startstr << "<xfov sid=\"xfov\">" << AI_RAD_TO_DEG(cam->mHorizontalFOV) - << "</xfov>" << endstr; - mOutput << startstr << "<aspect_ratio>" - << cam->mAspect - << "</aspect_ratio>" << endstr; - mOutput << startstr << "<znear sid=\"znear\">" - << cam->mClipPlaneNear - << "</znear>" << endstr; - mOutput << startstr << "<zfar sid=\"zfar\">" - << cam->mClipPlaneFar - << "</zfar>" << endstr; - PopTag(); - mOutput << startstr << "</perspective>" << endstr; - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - PopTag(); - mOutput << startstr << "</optics>" << endstr; - PopTag(); - mOutput << startstr << "</camera>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteLightsLibrary() { - if (mScene->HasLights()) { - - mOutput << startstr << "<library_lights>" << endstr; - PushTag(); - - for (size_t a = 0; a < mScene->mNumLights; ++a) - WriteLight(a); - - PopTag(); - mOutput << startstr << "</library_lights>" << endstr; - } -} - -void ColladaExporter::WriteLight(size_t pIndex) { - - const aiLight *light = mScene->mLights[pIndex]; - const std::string lightId = GetObjectUniqueId(AiObjectType::Light, pIndex); - const std::string lightName = GetObjectName(AiObjectType::Light, pIndex); - - mOutput << startstr << "<light id=\"" << lightId << "\" name=\"" - << lightName << "\" >" << endstr; - PushTag(); - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - switch (light->mType) { - case aiLightSource_AMBIENT: - WriteAmbienttLight(light); - break; - case aiLightSource_DIRECTIONAL: - WriteDirectionalLight(light); - break; - case aiLightSource_POINT: - WritePointLight(light); - break; - case aiLightSource_SPOT: - WriteSpotLight(light); - break; - case aiLightSource_AREA: - case aiLightSource_UNDEFINED: - case _aiLightSource_Force32Bit: - break; - } - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - - PopTag(); - mOutput << startstr << "</light>" << endstr; -} - -void ColladaExporter::WritePointLight(const aiLight *const light) { - const aiColor3D &color = light->mColorDiffuse; - mOutput << startstr << "<point>" << endstr; - PushTag(); - mOutput << startstr << "<color sid=\"color\">" - << color.r << " " << color.g << " " << color.b - << "</color>" << endstr; - mOutput << startstr << "<constant_attenuation>" - << light->mAttenuationConstant - << "</constant_attenuation>" << endstr; - mOutput << startstr << "<linear_attenuation>" - << light->mAttenuationLinear - << "</linear_attenuation>" << endstr; - mOutput << startstr << "<quadratic_attenuation>" - << light->mAttenuationQuadratic - << "</quadratic_attenuation>" << endstr; - - PopTag(); - mOutput << startstr << "</point>" << endstr; -} - -void ColladaExporter::WriteDirectionalLight(const aiLight *const light) { - const aiColor3D &color = light->mColorDiffuse; - mOutput << startstr << "<directional>" << endstr; - PushTag(); - mOutput << startstr << "<color sid=\"color\">" - << color.r << " " << color.g << " " << color.b - << "</color>" << endstr; - - PopTag(); - mOutput << startstr << "</directional>" << endstr; -} - -void ColladaExporter::WriteSpotLight(const aiLight *const light) { - - const aiColor3D &color = light->mColorDiffuse; - mOutput << startstr << "<spot>" << endstr; - PushTag(); - mOutput << startstr << "<color sid=\"color\">" - << color.r << " " << color.g << " " << color.b - << "</color>" << endstr; - mOutput << startstr << "<constant_attenuation>" - << light->mAttenuationConstant - << "</constant_attenuation>" << endstr; - mOutput << startstr << "<linear_attenuation>" - << light->mAttenuationLinear - << "</linear_attenuation>" << endstr; - mOutput << startstr << "<quadratic_attenuation>" - << light->mAttenuationQuadratic - << "</quadratic_attenuation>" << endstr; - /* - out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+ - srcLight->mFalloffAngle); - */ - - const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone); - mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">" - << fallOffAngle - << "</falloff_angle>" << endstr; - double temp = light->mAngleOuterCone - light->mAngleInnerCone; - - temp = std::cos(temp); - temp = std::log(temp) / std::log(0.1); - temp = 1 / temp; - mOutput << startstr << "<falloff_exponent sid=\"fall_off_exponent\">" - << temp - << "</falloff_exponent>" << endstr; - - PopTag(); - mOutput << startstr << "</spot>" << endstr; -} - -void ColladaExporter::WriteAmbienttLight(const aiLight *const light) { - - const aiColor3D &color = light->mColorAmbient; - mOutput << startstr << "<ambient>" << endstr; - PushTag(); - mOutput << startstr << "<color sid=\"color\">" - << color.r << " " << color.g << " " << color.b - << "</color>" << endstr; - - PopTag(); - mOutput << startstr << "</ambient>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Reads a single surface entry from the given material keys -bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) { - if (pSrcMat.GetTextureCount(pTexture) > 0) { - aiString texfile; - unsigned int uvChannel = 0; - pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel); - - std::string index_str(texfile.C_Str()); - - if (index_str.size() != 0 && index_str[0] == '*') { - unsigned int index; - - index_str = index_str.substr(1, std::string::npos); - - try { - index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str()); - } catch (std::exception &error) { - throw DeadlyExportError(error.what()); - } - - std::map<unsigned int, std::string>::const_iterator name = textures.find(index); - - if (name != textures.end()) { - poSurface.texture = name->second; - } else { - throw DeadlyExportError("could not find embedded texture at index " + index_str); - } - } else { - poSurface.texture = texfile.C_Str(); - } - - poSurface.channel = uvChannel; - poSurface.exist = true; - } else { - if (pKey) - poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS; - } - return poSurface.exist; -} - -// ------------------------------------------------------------------------------------------------ -// Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version. -static bool isalnum_C(char c) { - return (nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", c)); -} - -// ------------------------------------------------------------------------------------------------ -// Writes an image entry for the given surface -void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &imageId) { - if (!pSurface.texture.empty()) { - mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr; - PushTag(); - mOutput << startstr << "<init_from>"; - - // URL encode image file name first, then XML encode on top - std::stringstream imageUrlEncoded; - for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) { - if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\') - imageUrlEncoded << *it; - else - imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec; - } - mOutput << XMLEscape(imageUrlEncoded.str()); - mOutput << "</init_from>" << endstr; - PopTag(); - mOutput << startstr << "</image>" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes a color-or-texture entry into an effect definition -void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId) { - if (pSurface.exist) { - mOutput << startstr << "<" << pTypeName << ">" << endstr; - PushTag(); - if (pSurface.texture.empty()) { - mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << " " << pSurface.color.g << " " << pSurface.color.b << " " << pSurface.color.a << "</color>" << endstr; - } else { - mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr; - } - PopTag(); - mOutput << startstr << "</" << pTypeName << ">" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the two parameters necessary for referencing a texture in an effect entry -void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId) { - // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture - if (!pSurface.texture.empty()) { - mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr; - PushTag(); - mOutput << startstr << "<surface type=\"2D\">" << endstr; - PushTag(); - mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr; - PopTag(); - mOutput << startstr << "</surface>" << endstr; - PopTag(); - mOutput << startstr << "</newparam>" << endstr; - - mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr; - PushTag(); - mOutput << startstr << "<sampler2D>" << endstr; - PushTag(); - mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr; - PopTag(); - mOutput << startstr << "</sampler2D>" << endstr; - PopTag(); - mOutput << startstr << "</newparam>" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes a scalar property -void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) { - if (pProperty.exist) { - mOutput << startstr << "<" << pTypeName << ">" << endstr; - PushTag(); - mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr; - PopTag(); - mOutput << startstr << "</" << pTypeName << ">" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the material setup -void ColladaExporter::WriteMaterials() { - std::vector<Material> materials; - materials.resize(mScene->mNumMaterials); - - /// collect all materials from the scene - size_t numTextures = 0; - for (size_t a = 0; a < mScene->mNumMaterials; ++a) { - Material &material = materials[a]; - material.id = GetObjectUniqueId(AiObjectType::Material, a); - material.name = GetObjectName(AiObjectType::Material, a); - - const aiMaterial &mat = *(mScene->mMaterials[a]); - aiShadingMode shading = aiShadingMode_Flat; - material.shading_model = "phong"; - if (mat.Get(AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) { - if (shading == aiShadingMode_Phong) { - material.shading_model = "phong"; - } else if (shading == aiShadingMode_Blinn) { - material.shading_model = "blinn"; - } else if (shading == aiShadingMode_NoShading) { - material.shading_model = "constant"; - } else if (shading == aiShadingMode_Gouraud) { - material.shading_model = "lambert"; - } - } - - if (ReadMaterialSurface(material.ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT)) - ++numTextures; - if (ReadMaterialSurface(material.diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE)) - ++numTextures; - if (ReadMaterialSurface(material.specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR)) - ++numTextures; - if (ReadMaterialSurface(material.emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE)) - ++numTextures; - if (ReadMaterialSurface(material.reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE)) - ++numTextures; - if (ReadMaterialSurface(material.transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT)) - ++numTextures; - if (ReadMaterialSurface(material.normal, mat, aiTextureType_NORMALS, nullptr, 0, 0)) - ++numTextures; - - material.shininess.exist = mat.Get(AI_MATKEY_SHININESS, material.shininess.value) == aiReturn_SUCCESS; - material.transparency.exist = mat.Get(AI_MATKEY_OPACITY, material.transparency.value) == aiReturn_SUCCESS; - material.index_refraction.exist = mat.Get(AI_MATKEY_REFRACTI, material.index_refraction.value) == aiReturn_SUCCESS; - } - - // output textures if present - if (numTextures > 0) { - mOutput << startstr << "<library_images>" << endstr; - PushTag(); - for (const Material &mat : materials) { - WriteImageEntry(mat.ambient, mat.id + "-ambient-image"); - WriteImageEntry(mat.diffuse, mat.id + "-diffuse-image"); - WriteImageEntry(mat.specular, mat.id + "-specular-image"); - WriteImageEntry(mat.emissive, mat.id + "-emission-image"); - WriteImageEntry(mat.reflective, mat.id + "-reflective-image"); - WriteImageEntry(mat.transparent, mat.id + "-transparent-image"); - WriteImageEntry(mat.normal, mat.id + "-normal-image"); - } - PopTag(); - mOutput << startstr << "</library_images>" << endstr; - } - - // output effects - those are the actual carriers of information - if (!materials.empty()) { - mOutput << startstr << "<library_effects>" << endstr; - PushTag(); - for (const Material &mat : materials) { - // this is so ridiculous it must be right - mOutput << startstr << "<effect id=\"" << mat.id << "-fx\" name=\"" << mat.name << "\">" << endstr; - PushTag(); - mOutput << startstr << "<profile_COMMON>" << endstr; - PushTag(); - - // write sampler- and surface params for the texture entries - WriteTextureParamEntry(mat.emissive, "emission", mat.id); - WriteTextureParamEntry(mat.ambient, "ambient", mat.id); - WriteTextureParamEntry(mat.diffuse, "diffuse", mat.id); - WriteTextureParamEntry(mat.specular, "specular", mat.id); - WriteTextureParamEntry(mat.reflective, "reflective", mat.id); - WriteTextureParamEntry(mat.transparent, "transparent", mat.id); - WriteTextureParamEntry(mat.normal, "normal", mat.id); - - mOutput << startstr << "<technique sid=\"standard\">" << endstr; - PushTag(); - mOutput << startstr << "<" << mat.shading_model << ">" << endstr; - PushTag(); - - WriteTextureColorEntry(mat.emissive, "emission", mat.id + "-emission-sampler"); - WriteTextureColorEntry(mat.ambient, "ambient", mat.id + "-ambient-sampler"); - WriteTextureColorEntry(mat.diffuse, "diffuse", mat.id + "-diffuse-sampler"); - WriteTextureColorEntry(mat.specular, "specular", mat.id + "-specular-sampler"); - WriteFloatEntry(mat.shininess, "shininess"); - WriteTextureColorEntry(mat.reflective, "reflective", mat.id + "-reflective-sampler"); - WriteTextureColorEntry(mat.transparent, "transparent", mat.id + "-transparent-sampler"); - WriteFloatEntry(mat.transparency, "transparency"); - WriteFloatEntry(mat.index_refraction, "index_of_refraction"); - - if (!mat.normal.texture.empty()) { - WriteTextureColorEntry(mat.normal, "bump", mat.id + "-normal-sampler"); - } - - PopTag(); - mOutput << startstr << "</" << mat.shading_model << ">" << endstr; - PopTag(); - mOutput << startstr << "</technique>" << endstr; - PopTag(); - mOutput << startstr << "</profile_COMMON>" << endstr; - PopTag(); - mOutput << startstr << "</effect>" << endstr; - } - PopTag(); - mOutput << startstr << "</library_effects>" << endstr; - - // write materials - they're just effect references - mOutput << startstr << "<library_materials>" << endstr; - PushTag(); - for (std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) { - const Material &mat = *it; - mOutput << startstr << "<material id=\"" << mat.id << "\" name=\"" << mat.name << "\">" << endstr; - PushTag(); - mOutput << startstr << "<instance_effect url=\"#" << mat.id << "-fx\"/>" << endstr; - PopTag(); - mOutput << startstr << "</material>" << endstr; - } - PopTag(); - mOutput << startstr << "</library_materials>" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the controller library -void ColladaExporter::WriteControllerLibrary() { - mOutput << startstr << "<library_controllers>" << endstr; - PushTag(); - - for (size_t a = 0; a < mScene->mNumMeshes; ++a) { - WriteController(a); - } - - PopTag(); - mOutput << startstr << "</library_controllers>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes a skin controller of the given mesh -void ColladaExporter::WriteController(size_t pIndex) { - const aiMesh *mesh = mScene->mMeshes[pIndex]; - // Is there a skin controller? - if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0) - return; - - const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex); - const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex); - - mOutput << startstr << "<controller id=\"" << idstr << "-skin\" "; - mOutput << "name=\"skinCluster" << pIndex << "\">" << endstr; - PushTag(); - - mOutput << startstr << "<skin source=\"#" << idstr << "\">" << endstr; - PushTag(); - - // bind pose matrix - mOutput << startstr << "<bind_shape_matrix>" << endstr; - PushTag(); - - // I think it is identity in general cases. - aiMatrix4x4 mat; - mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr; - mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr; - mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr; - mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr; - - PopTag(); - mOutput << startstr << "</bind_shape_matrix>" << endstr; - - mOutput << startstr << "<source id=\"" << idstr << "-skin-joints\" name=\"" << namestr << "-skin-joints\">" << endstr; - PushTag(); - - mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">"; - - for (size_t i = 0; i < mesh->mNumBones; ++i) - mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' '; - - mOutput << "</Name_array>" << endstr; - - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - - mOutput << startstr << "<accessor source=\"#" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\" stride=\"" << 1 << "\">" << endstr; - PushTag(); - - mOutput << startstr << "<param name=\"JOINT\" type=\"Name\"></param>" << endstr; - - PopTag(); - mOutput << startstr << "</accessor>" << endstr; - - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - - PopTag(); - mOutput << startstr << "</source>" << endstr; - - std::vector<ai_real> bind_poses; - bind_poses.reserve(mesh->mNumBones * 16); - for (unsigned int i = 0; i < mesh->mNumBones; ++i) - for (unsigned int j = 0; j < 4; ++j) - bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4); - - WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16); - - bind_poses.clear(); - - std::vector<ai_real> skin_weights; - skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones); - for (size_t i = 0; i < mesh->mNumBones; ++i) - for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) - skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight); - - WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size()); - - skin_weights.clear(); - - mOutput << startstr << "<joints>" << endstr; - PushTag(); - - mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstr << "-skin-joints\"></input>" << endstr; - mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstr << "-skin-bind_poses\"></input>" << endstr; - - PopTag(); - mOutput << startstr << "</joints>" << endstr; - - mOutput << startstr << "<vertex_weights count=\"" << mesh->mNumVertices << "\">" << endstr; - PushTag(); - - mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstr << "-skin-joints\" offset=\"0\"></input>" << endstr; - mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstr << "-skin-weights\" offset=\"1\"></input>" << endstr; - - mOutput << startstr << "<vcount>"; - - std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0); - for (size_t i = 0; i < mesh->mNumBones; ++i) - for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) - ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId]; - - for (size_t i = 0; i < mesh->mNumVertices; ++i) - mOutput << num_influences[i] << " "; - - mOutput << "</vcount>" << endstr; - - mOutput << startstr << "<v>"; - - ai_uint joint_weight_indices_length = 0; - std::vector<ai_uint> accum_influences; - accum_influences.reserve(num_influences.size()); - for (size_t i = 0; i < num_influences.size(); ++i) { - accum_influences.push_back(joint_weight_indices_length); - joint_weight_indices_length += num_influences[i]; - } - - ai_uint weight_index = 0; - std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1); - for (unsigned int i = 0; i < mesh->mNumBones; ++i) - for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) { - unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId; - for (ai_uint k = 0; k < num_influences[vId]; ++k) { - if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) { - joint_weight_indices[2 * (accum_influences[vId] + k)] = i; - joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index; - break; - } - } - ++weight_index; - } - - for (size_t i = 0; i < joint_weight_indices.size(); ++i) - mOutput << joint_weight_indices[i] << " "; - - num_influences.clear(); - accum_influences.clear(); - joint_weight_indices.clear(); - - mOutput << "</v>" << endstr; - - PopTag(); - mOutput << startstr << "</vertex_weights>" << endstr; - - PopTag(); - mOutput << startstr << "</skin>" << endstr; - - PopTag(); - mOutput << startstr << "</controller>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the geometry library -void ColladaExporter::WriteGeometryLibrary() { - mOutput << startstr << "<library_geometries>" << endstr; - PushTag(); - - for (size_t a = 0; a < mScene->mNumMeshes; ++a) - WriteGeometry(a); - - PopTag(); - mOutput << startstr << "</library_geometries>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the given mesh -void ColladaExporter::WriteGeometry(size_t pIndex) { - const aiMesh *mesh = mScene->mMeshes[pIndex]; - const std::string geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex); - const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex); - - if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0) - return; - - // opening tag - mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr; - PushTag(); - - mOutput << startstr << "<mesh>" << endstr; - PushTag(); - - // Positions - WriteFloatArray(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices); - // Normals, if any - if (mesh->HasNormals()) - WriteFloatArray(geometryId + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices); - - // texture coords - for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) { - WriteFloatArray(geometryId + "-tex" + ai_to_string(a), mesh->mNumUVComponents[a] == 3 ? FloatType_TexCoord3 : FloatType_TexCoord2, - (ai_real *)mesh->mTextureCoords[a], mesh->mNumVertices); - } - } - - // vertex colors - for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (mesh->HasVertexColors(static_cast<unsigned int>(a))) - WriteFloatArray(geometryId + "-color" + ai_to_string(a), FloatType_Color, (ai_real *)mesh->mColors[a], mesh->mNumVertices); - } - - // assemble vertex structure - // Only write input for POSITION since we will write other as shared inputs in polygon definition - mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices" - << "\">" << endstr; - PushTag(); - mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr; - PopTag(); - mOutput << startstr << "</vertices>" << endstr; - - // count the number of lines, triangles and polygon meshes - int countLines = 0; - int countPoly = 0; - for (size_t a = 0; a < mesh->mNumFaces; ++a) { - if (mesh->mFaces[a].mNumIndices == 2) - countLines++; - else if (mesh->mFaces[a].mNumIndices >= 3) - countPoly++; - } - - // lines - if (countLines) { - mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr; - PushTag(); - mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr; - if (mesh->HasNormals()) - mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr; - for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) - mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " - << "set=\"" << a << "\"" - << " />" << endstr; - } - for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { - if (mesh->HasVertexColors(static_cast<unsigned int>(a))) - mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " - << "set=\"" << a << "\"" - << " />" << endstr; - } - - mOutput << startstr << "<p>"; - for (size_t a = 0; a < mesh->mNumFaces; ++a) { - const aiFace &face = mesh->mFaces[a]; - if (face.mNumIndices != 2) continue; - for (size_t b = 0; b < face.mNumIndices; ++b) - mOutput << face.mIndices[b] << " "; - } - mOutput << "</p>" << endstr; - PopTag(); - mOutput << startstr << "</lines>" << endstr; - } - - // triangle - don't use it, because compatibility problems - - // polygons - if (countPoly) { - mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr; - PushTag(); - mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr; - if (mesh->HasNormals()) - mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr; - for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) - mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " - << "set=\"" << a << "\"" - << " />" << endstr; - } - for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { - if (mesh->HasVertexColors(static_cast<unsigned int>(a))) - mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " - << "set=\"" << a << "\"" - << " />" << endstr; - } - - mOutput << startstr << "<vcount>"; - for (size_t a = 0; a < mesh->mNumFaces; ++a) { - if (mesh->mFaces[a].mNumIndices < 3) continue; - mOutput << mesh->mFaces[a].mNumIndices << " "; - } - mOutput << "</vcount>" << endstr; - - mOutput << startstr << "<p>"; - for (size_t a = 0; a < mesh->mNumFaces; ++a) { - const aiFace &face = mesh->mFaces[a]; - if (face.mNumIndices < 3) continue; - for (size_t b = 0; b < face.mNumIndices; ++b) - mOutput << face.mIndices[b] << " "; - } - mOutput << "</p>" << endstr; - PopTag(); - mOutput << startstr << "</polylist>" << endstr; - } - - // closing tags - PopTag(); - mOutput << startstr << "</mesh>" << endstr; - PopTag(); - mOutput << startstr << "</geometry>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes a float array of the given type -void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) { - size_t floatsPerElement = 0; - switch (pType) { - case FloatType_Vector: floatsPerElement = 3; break; - case FloatType_TexCoord2: floatsPerElement = 2; break; - case FloatType_TexCoord3: floatsPerElement = 3; break; - case FloatType_Color: floatsPerElement = 3; break; - case FloatType_Mat4x4: floatsPerElement = 16; break; - case FloatType_Weight: floatsPerElement = 1; break; - case FloatType_Time: floatsPerElement = 1; break; - default: - return; - } - - std::string arrayId = XMLIDEncode(pIdString) + "-array"; - - mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr; - PushTag(); - - // source array - mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> "; - PushTag(); - - if (pType == FloatType_TexCoord2) { - for (size_t a = 0; a < pElementCount; ++a) { - mOutput << pData[a * 3 + 0] << " "; - mOutput << pData[a * 3 + 1] << " "; - } - } else if (pType == FloatType_Color) { - for (size_t a = 0; a < pElementCount; ++a) { - mOutput << pData[a * 4 + 0] << " "; - mOutput << pData[a * 4 + 1] << " "; - mOutput << pData[a * 4 + 2] << " "; - } - } else { - for (size_t a = 0; a < pElementCount * floatsPerElement; ++a) - mOutput << pData[a] << " "; - } - mOutput << "</float_array>" << endstr; - PopTag(); - - // the usual Collada fun. Let's bloat it even more! - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - mOutput << startstr << "<accessor count=\"" << pElementCount << "\" offset=\"0\" source=\"#" << arrayId << "\" stride=\"" << floatsPerElement << "\">" << endstr; - PushTag(); - - switch (pType) { - case FloatType_Vector: - mOutput << startstr << "<param name=\"X\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"Y\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"Z\" type=\"float\" />" << endstr; - break; - - case FloatType_TexCoord2: - mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr; - break; - - case FloatType_TexCoord3: - mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"P\" type=\"float\" />" << endstr; - break; - - case FloatType_Color: - mOutput << startstr << "<param name=\"R\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr; - mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr; - break; - - case FloatType_Mat4x4: - mOutput << startstr << "<param name=\"TRANSFORM\" type=\"float4x4\" />" << endstr; - break; - - case FloatType_Weight: - mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr; - break; - - // customized, add animation related - case FloatType_Time: - mOutput << startstr << "<param name=\"TIME\" type=\"float\" />" << endstr; - break; - } - - PopTag(); - mOutput << startstr << "</accessor>" << endstr; - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - PopTag(); - mOutput << startstr << "</source>" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the scene library -void ColladaExporter::WriteSceneLibrary() { - // Determine if we are using the aiScene root or our own - std::string sceneName("Scene"); - if (mAdd_root_node) { - mSceneId = MakeUniqueId(mUniqueIds, sceneName, std::string()); - mUniqueIds.insert(mSceneId); - } else { - mSceneId = GetNodeUniqueId(mScene->mRootNode); - sceneName = GetNodeName(mScene->mRootNode); - } - - mOutput << startstr << "<library_visual_scenes>" << endstr; - PushTag(); - mOutput << startstr << "<visual_scene id=\"" + mSceneId + "\" name=\"" + sceneName + "\">" << endstr; - PushTag(); - - if (mAdd_root_node) { - // Export the root node - WriteNode(mScene->mRootNode); - } else { - // Have already exported the root node - for (size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a) - WriteNode(mScene->mRootNode->mChildren[a]); - } - - PopTag(); - mOutput << startstr << "</visual_scene>" << endstr; - PopTag(); - mOutput << startstr << "</library_visual_scenes>" << endstr; -} -// ------------------------------------------------------------------------------------------------ -void ColladaExporter::WriteAnimationLibrary(size_t pIndex) { - const aiAnimation *anim = mScene->mAnimations[pIndex]; - - if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0) - return; - - const std::string animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex); - const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex); - - mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animationNameEscaped + "\">" << endstr; - PushTag(); - - std::string cur_node_idstr; - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim *nodeAnim = anim->mChannels[a]; - - // sanity check - if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) { - continue; - } - - { - cur_node_idstr.clear(); - cur_node_idstr += nodeAnim->mNodeName.data; - cur_node_idstr += std::string("_matrix-input"); - - std::vector<ai_real> frames; - for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - frames.push_back(static_cast<ai_real>(nodeAnim->mPositionKeys[i].mTime)); - } - - WriteFloatArray(cur_node_idstr, FloatType_Time, (const ai_real *)frames.data(), frames.size()); - frames.clear(); - } - - { - cur_node_idstr.clear(); - - cur_node_idstr += nodeAnim->mNodeName.data; - cur_node_idstr += std::string("_matrix-output"); - - std::vector<ai_real> keyframes; - keyframes.reserve(nodeAnim->mNumPositionKeys * 16); - for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; - aiMatrix4x4 ScalingM; // identity - ScalingM[0][0] = Scaling.x; - ScalingM[1][1] = Scaling.y; - ScalingM[2][2] = Scaling.z; - - aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue; - aiMatrix4x4 s = aiMatrix4x4(RotationQ.GetMatrix()); - aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1); - - aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue; - aiMatrix4x4 TranslationM; // identity - TranslationM[0][3] = Translation.x; - TranslationM[1][3] = Translation.y; - TranslationM[2][3] = Translation.z; - - // Combine the above transformations - aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; - - for (unsigned int j = 0; j < 4; ++j) { - keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); - } - } - - WriteFloatArray(cur_node_idstr, FloatType_Mat4x4, (const ai_real *)keyframes.data(), keyframes.size() / 16); - } - - { - std::vector<std::string> names; - for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - if (nodeAnim->mPreState == aiAnimBehaviour_DEFAULT || nodeAnim->mPreState == aiAnimBehaviour_LINEAR || nodeAnim->mPreState == aiAnimBehaviour_REPEAT) { - names.push_back("LINEAR"); - } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) { - names.push_back("STEP"); - } - } - - const std::string cur_node_idstr2 = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); - std::string arrayId = XMLIDEncode(cur_node_idstr2) + "-array"; - - mOutput << startstr << "<source id=\"" << XMLIDEncode(cur_node_idstr2) << "\">" << endstr; - PushTag(); - - // source array - mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> "; - for (size_t aa = 0; aa < names.size(); ++aa) { - mOutput << names[aa] << " "; - } - mOutput << "</Name_array>" << endstr; - - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - - mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr; - PushTag(); - - mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr; - - PopTag(); - mOutput << startstr << "</accessor>" << endstr; - - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - - PopTag(); - mOutput << startstr << "</source>" << endstr; - } - } - - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim *nodeAnim = anim->mChannels[a]; - - { - // samplers - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); - mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr; - PushTag(); - - mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-input")) << "\"/>" << endstr; - mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-output")) << "\"/>" << endstr; - mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-interpolation")) << "\"/>" << endstr; - - PopTag(); - mOutput << startstr << "</sampler>" << endstr; - } - } - - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim *nodeAnim = anim->mChannels[a]; - - { - // channels - mOutput << startstr << "<channel source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; - } - } - - PopTag(); - mOutput << startstr << "</animation>" << endstr; -} -// ------------------------------------------------------------------------------------------------ -void ColladaExporter::WriteAnimationsLibrary() { - if (mScene->mNumAnimations > 0) { - mOutput << startstr << "<library_animations>" << endstr; - PushTag(); - - // start recursive write at the root node - for (size_t a = 0; a < mScene->mNumAnimations; ++a) - WriteAnimationLibrary(a); - - PopTag(); - mOutput << startstr << "</library_animations>" << endstr; - } -} -// ------------------------------------------------------------------------------------------------ -// Helper to find a bone by name in the scene -aiBone *findBone(const aiScene *scene, const aiString &name) { - for (size_t m = 0; m < scene->mNumMeshes; m++) { - aiMesh *mesh = scene->mMeshes[m]; - for (size_t b = 0; b < mesh->mNumBones; b++) { - aiBone *bone = mesh->mBones[b]; - if (name == bone->mName) { - return bone; - } - } - } - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// Helper to find the node associated with a bone in the scene -const aiNode *findBoneNode(const aiNode *aNode, const aiBone *bone) { - if (aNode && bone && aNode->mName == bone->mName) { - return aNode; - } - - if (aNode && bone) { - for (unsigned int i = 0; i < aNode->mNumChildren; ++i) { - aiNode *aChild = aNode->mChildren[i]; - const aiNode *foundFromChild = nullptr; - if (aChild) { - foundFromChild = findBoneNode(aChild, bone); - if (foundFromChild) { - return foundFromChild; - } - } - } - } - - return nullptr; -} - -const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) { - std::set<const aiNode *> topParentBoneNodes; - if (mesh && mesh->mNumBones > 0) { - for (unsigned int i = 0; i < mesh->mNumBones; ++i) { - aiBone *bone = mesh->mBones[i]; - - const aiNode *node = findBoneNode(scene->mRootNode, bone); - if (node) { - while (node->mParent && findBone(scene, node->mParent->mName) != nullptr) { - node = node->mParent; - } - topParentBoneNodes.insert(node); - } - } - } - - if (!topParentBoneNodes.empty()) { - const aiNode *parentBoneNode = *topParentBoneNodes.begin(); - if (topParentBoneNodes.size() == 1) { - return parentBoneNode; - } else { - for (auto it : topParentBoneNodes) { - if (it->mParent) return it->mParent; - } - return parentBoneNode; - } - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// Recursively writes the given node -void ColladaExporter::WriteNode(const aiNode *pNode) { - // If the node is associated with a bone, it is a joint node (JOINT) - // otherwise it is a normal node (NODE) - // Assimp-specific: nodes with no name cannot be associated with bones - const char *node_type; - bool is_joint, is_skeleton_root = false; - if (pNode->mName.length == 0 || nullptr == findBone(mScene, pNode->mName)) { - node_type = "NODE"; - is_joint = false; - } else { - node_type = "JOINT"; - is_joint = true; - if (!pNode->mParent || nullptr == findBone(mScene, pNode->mParent->mName)) { - is_skeleton_root = true; - } - } - - const std::string node_id = GetNodeUniqueId(pNode); - const std::string node_name = GetNodeName(pNode); - mOutput << startstr << "<node "; - if (is_skeleton_root) { - mFoundSkeletonRootNodeID = node_id; // For now, only support one skeleton in a scene. - } - mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id + "\" " : ""); - mOutput << "name=\"" << node_name - << "\" type=\"" << node_type - << "\">" << endstr; - PushTag(); - - // write transformation - we can directly put the matrix there - // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards - aiMatrix4x4 mat = pNode->mTransformation; - - // If this node is a Camera node, the camera coordinate system needs to be multiplied in. - // When importing from Collada, the mLookAt is set to 0, 0, -1, and the node transform is unchanged. - // When importing from a different format, mLookAt is set to 0, 0, 1. Therefore, the local camera - // coordinate system must be changed to matche the Collada specification. - for (size_t i = 0; i < mScene->mNumCameras; i++) { - if (mScene->mCameras[i]->mName == pNode->mName) { - aiMatrix4x4 sourceView; - mScene->mCameras[i]->GetCameraMatrix(sourceView); - - aiMatrix4x4 colladaView; - colladaView.a1 = colladaView.c3 = -1; // move into -z space. - mat *= (sourceView * colladaView); - break; - } - } - - // customized, sid should be 'matrix' to match with loader code. - //mOutput << startstr << "<matrix sid=\"transform\">"; - mOutput << startstr << "<matrix sid=\"matrix\">"; - - mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; - mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; - mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; - mOutput << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4; - mOutput << "</matrix>" << endstr; - - if (pNode->mNumMeshes == 0) { - //check if it is a camera node - for (size_t i = 0; i < mScene->mNumCameras; i++) { - if (mScene->mCameras[i]->mName == pNode->mName) { - mOutput << startstr << "<instance_camera url=\"#" << GetObjectUniqueId(AiObjectType::Camera, i) << "\"/>" << endstr; - break; - } - } - //check if it is a light node - for (size_t i = 0; i < mScene->mNumLights; i++) { - if (mScene->mLights[i]->mName == pNode->mName) { - mOutput << startstr << "<instance_light url=\"#" << GetObjectUniqueId(AiObjectType::Light, i) << "\"/>" << endstr; - break; - } - } - - } else - // instance every geometry - for (size_t a = 0; a < pNode->mNumMeshes; ++a) { - const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[a]]; - // do not instantiate mesh if empty. I wonder how this could happen - if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0) - continue; - - const std::string meshId = GetObjectUniqueId(AiObjectType::Mesh, pNode->mMeshes[a]); - - if (mesh->mNumBones == 0) { - mOutput << startstr << "<instance_geometry url=\"#" << meshId << "\">" << endstr; - PushTag(); - } else { - mOutput << startstr - << "<instance_controller url=\"#" << meshId << "-skin\">" - << endstr; - PushTag(); - - // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node. - // use the first bone to find skeleton root - const aiNode *skeletonRootBoneNode = findSkeletonRootNode(mScene, mesh); - if (skeletonRootBoneNode) { - mFoundSkeletonRootNodeID = GetNodeUniqueId(skeletonRootBoneNode); - } - mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr; - } - mOutput << startstr << "<bind_material>" << endstr; - PushTag(); - mOutput << startstr << "<technique_common>" << endstr; - PushTag(); - mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << GetObjectUniqueId(AiObjectType::Material, mesh->mMaterialIndex) << "\">" << endstr; - PushTag(); - for (size_t aa = 0; aa < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++aa) { - if (mesh->HasTextureCoords(static_cast<unsigned int>(aa))) - // semantic as in <texture texcoord=...> - // input_semantic as in <input semantic=...> - // input_set as in <input set=...> - mOutput << startstr << "<bind_vertex_input semantic=\"CHANNEL" << aa << "\" input_semantic=\"TEXCOORD\" input_set=\"" << aa << "\"/>" << endstr; - } - PopTag(); - mOutput << startstr << "</instance_material>" << endstr; - PopTag(); - mOutput << startstr << "</technique_common>" << endstr; - PopTag(); - mOutput << startstr << "</bind_material>" << endstr; - - PopTag(); - if (mesh->mNumBones == 0) - mOutput << startstr << "</instance_geometry>" << endstr; - else - mOutput << startstr << "</instance_controller>" << endstr; - } - - // recurse into subnodes - for (size_t a = 0; a < pNode->mNumChildren; ++a) - WriteNode(pNode->mChildren[a]); - - PopTag(); - mOutput << startstr << "</node>" << endstr; -} - -void ColladaExporter::CreateNodeIds(const aiNode *node) { - GetNodeUniqueId(node); - for (size_t a = 0; a < node->mNumChildren; ++a) - CreateNodeIds(node->mChildren[a]); -} - -std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) { - // Use the pointer as the key. This is safe because the scene is immutable. - auto idIt = mNodeIdMap.find(node); - if (idIt != mNodeIdMap.cend()) - return idIt->second; - - // Prefer the requested Collada Id if extant - std::string idStr; - aiString origId; - if (node->mMetaData && node->mMetaData->Get(AI_METADATA_COLLADA_ID, origId)) { - idStr = origId.C_Str(); - } else { - idStr = node->mName.C_Str(); - } - // Make sure the requested id is valid - if (idStr.empty()) - idStr = "node"; - else - idStr = XMLIDEncode(idStr); - - // Ensure it's unique - idStr = MakeUniqueId(mUniqueIds, idStr, std::string()); - mUniqueIds.insert(idStr); - mNodeIdMap.insert(std::make_pair(node, idStr)); - return idStr; -} - -std::string ColladaExporter::GetNodeName(const aiNode *node) { - - return XMLEscape(node->mName.C_Str()); -} - -std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) { - // Find the Node that is this Bone - const aiNode *boneNode = findBoneNode(mScene->mRootNode, bone); - if (boneNode == nullptr) - return std::string(); - - return GetNodeUniqueId(boneNode); -} - -std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) { - auto idIt = GetObjectIdMap(type).find(pIndex); - if (idIt != GetObjectIdMap(type).cend()) - return idIt->second; - - // Not seen this object before, create and add - NameIdPair result = AddObjectIndexToMaps(type, pIndex); - return result.second; -} - -std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) { - auto objectName = GetObjectNameMap(type).find(pIndex); - if (objectName != GetObjectNameMap(type).cend()) - return objectName->second; - - // Not seen this object before, create and add - NameIdPair result = AddObjectIndexToMaps(type, pIndex); - return result.first; -} - -// Determine unique id and add the name and id to the maps -// @param type object type -// @param index object index -// @param name in/out. Caller to set the original name if known. -// @param idStr in/out. Caller to set the preferred id if known. -ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType type, size_t index) { - - std::string name; - std::string idStr; - std::string idPostfix; - - // Get the name and id postfix - switch (type) { - case AiObjectType::Mesh: name = mScene->mMeshes[index]->mName.C_Str(); break; - case AiObjectType::Material: name = mScene->mMaterials[index]->GetName().C_Str(); break; - case AiObjectType::Animation: name = mScene->mAnimations[index]->mName.C_Str(); break; - case AiObjectType::Light: - name = mScene->mLights[index]->mName.C_Str(); - idPostfix = "-light"; - break; - case AiObjectType::Camera: - name = mScene->mCameras[index]->mName.C_Str(); - idPostfix = "-camera"; - break; - case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type"); - } - - if (name.empty()) { - // Default ids if empty name - switch (type) { - case AiObjectType::Mesh: idStr = std::string("mesh_"); break; - case AiObjectType::Material: idStr = std::string("material_"); break; // This one should never happen - case AiObjectType::Animation: idStr = std::string("animation_"); break; - case AiObjectType::Light: idStr = std::string("light_"); break; - case AiObjectType::Camera: idStr = std::string("camera_"); break; - case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type"); - } - idStr.append(ai_to_string(index)); - } else { - idStr = XMLIDEncode(name); - } - - if (!name.empty()) - name = XMLEscape(name); - - idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix); - - // Add to maps - mUniqueIds.insert(idStr); - GetObjectIdMap(type).insert(std::make_pair(index, idStr)); - GetObjectNameMap(type).insert(std::make_pair(index, name)); - - return std::make_pair(name, idStr); -} - -} // end of namespace Assimp - -#endif -#endif diff --git a/libs/assimp/code/AssetLib/Collada/ColladaExporter.h b/libs/assimp/code/AssetLib/Collada/ColladaExporter.h deleted file mode 100644 index 56415fb..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaExporter.h +++ /dev/null @@ -1,257 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file ColladaExporter.h - * Declares the exporter class to write a scene to a Collada file - */ -#ifndef AI_COLLADAEXPORTER_H_INC -#define AI_COLLADAEXPORTER_H_INC - -#include <assimp/ai_assert.h> -#include <assimp/material.h> - -#include <array> -#include <map> -#include <sstream> -#include <unordered_set> -#include <vector> - -struct aiScene; -struct aiNode; -struct aiLight; -struct aiBone; - -namespace Assimp { - -class IOSystem; - -/// Helper class to export a given scene to a Collada file. Just for my personal -/// comfort when implementing it. -class ColladaExporter { -public: - /// Constructor for a specific scene to export - ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file); - - /// Destructor - virtual ~ColladaExporter(); - -protected: - /// Starts writing the contents - void WriteFile(); - - /// Writes the asset header - void WriteHeader(); - - /// Writes the embedded textures - void WriteTextures(); - - /// Writes the material setup - void WriteMaterials(); - - /// Writes the cameras library - void WriteCamerasLibrary(); - - // Write a camera entry - void WriteCamera(size_t pIndex); - - /// Writes the cameras library - void WriteLightsLibrary(); - - // Write a camera entry - void WriteLight(size_t pIndex); - void WritePointLight(const aiLight *const light); - void WriteDirectionalLight(const aiLight *const light); - void WriteSpotLight(const aiLight *const light); - void WriteAmbienttLight(const aiLight *const light); - - /// Writes the controller library - void WriteControllerLibrary(); - - /// Writes a skin controller of the given mesh - void WriteController(size_t pIndex); - - /// Writes the geometry library - void WriteGeometryLibrary(); - - /// Writes the given mesh - void WriteGeometry(size_t pIndex); - - //enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight }; - // customized to add animation related type - enum FloatDataType { FloatType_Vector, - FloatType_TexCoord2, - FloatType_TexCoord3, - FloatType_Color, - FloatType_Mat4x4, - FloatType_Weight, - FloatType_Time }; - - /// Writes a float array of the given type - void WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount); - - /// Writes the scene library - void WriteSceneLibrary(); - - // customized, Writes the animation library - void WriteAnimationsLibrary(); - void WriteAnimationLibrary(size_t pIndex); - std::string mFoundSkeletonRootNodeID = "skeleton_root"; // will be replaced by found node id in the WriteNode call. - - /// Recursively writes the given node - void WriteNode(const aiNode *pNode); - - /// Enters a new xml element, which increases the indentation - void PushTag() { startstr.append(" "); } - /// Leaves an element, decreasing the indentation - void PopTag() { - ai_assert(startstr.length() > 1); - startstr.erase(startstr.length() - 2); - } - - void CreateNodeIds(const aiNode *node); - - /// Get or Create a unique Node ID string for the given Node - std::string GetNodeUniqueId(const aiNode *node); - std::string GetNodeName(const aiNode *node); - - std::string GetBoneUniqueId(const aiBone *bone); - - enum class AiObjectType { - Mesh, - Material, - Animation, - Light, - Camera, - Count, - }; - /// Get or Create a unique ID string for the given scene object index - std::string GetObjectUniqueId(AiObjectType type, size_t pIndex); - /// Get or Create a name string for the given scene object index - std::string GetObjectName(AiObjectType type, size_t pIndex); - - typedef std::map<size_t, std::string> IndexIdMap; - typedef std::pair<std::string, std::string> NameIdPair; - NameIdPair AddObjectIndexToMaps(AiObjectType type, size_t pIndex); - - // Helpers - inline IndexIdMap &GetObjectIdMap(AiObjectType type) { return mObjectIdMap[static_cast<size_t>(type)]; } - inline IndexIdMap &GetObjectNameMap(AiObjectType type) { return mObjectNameMap[static_cast<size_t>(type)]; } - -private: - std::unordered_set<std::string> mUniqueIds; // Cache of used unique ids - std::map<const void *, std::string> mNodeIdMap; // Cache of encoded node and bone ids - std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectIdMap; // Cache of encoded unique IDs - std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectNameMap; // Cache of encoded names - -public: - /// Stringstream to write all output into - std::stringstream mOutput; - - /// The IOSystem for output - IOSystem *mIOSystem; - - /// Path of the directory where the scene will be exported - const std::string mPath; - - /// Name of the file (without extension) where the scene will be exported - const std::string mFile; - - /// The scene to be written - const aiScene *const mScene; - std::string mSceneId; - bool mAdd_root_node = false; - - /// current line start string, contains the current indentation for simple stream insertion - std::string startstr; - /// current line end string for simple stream insertion - const std::string endstr; - - // pair of color and texture - texture precedences color - struct Surface { - bool exist; - aiColor4D color; - std::string texture; - size_t channel; - Surface() { - exist = false; - channel = 0; - } - }; - - struct Property { - bool exist; - ai_real value; - Property() : - exist(false), - value(0.0) {} - }; - - // summarize a material in an convenient way. - struct Material { - std::string id; - std::string name; - std::string shading_model; - Surface ambient, diffuse, specular, emissive, reflective, transparent, normal; - Property shininess, transparency, index_refraction; - - Material() {} - }; - - std::map<unsigned int, std::string> textures; - -public: - /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions - /// Reads a single surface entry from the given material keys - bool ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex); - /// Writes an image entry for the given surface - void WriteImageEntry(const Surface &pSurface, const std::string &imageId); - /// Writes the two parameters necessary for referencing a texture in an effect entry - void WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId); - /// Writes a color-or-texture entry into an effect definition - void WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId); - /// Writes a scalar property - void WriteFloatEntry(const Property &pProperty, const std::string &pTypeName); -}; - -} // namespace Assimp - -#endif // !! AI_COLLADAEXPORTER_H_INC diff --git a/libs/assimp/code/AssetLib/Collada/ColladaHelper.cpp b/libs/assimp/code/AssetLib/Collada/ColladaHelper.cpp deleted file mode 100644 index 0fb172f..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaHelper.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -/** Helper structures for the Collada loader */ - -#include "ColladaHelper.h" - -#include <assimp/ParsingUtils.h> -#include <assimp/commonMetaData.h> - -namespace Assimp { -namespace Collada { - -const MetaKeyPairVector MakeColladaAssimpMetaKeys() { - MetaKeyPairVector result; - result.emplace_back("authoring_tool", AI_METADATA_SOURCE_GENERATOR); - result.emplace_back("copyright", AI_METADATA_SOURCE_COPYRIGHT); - return result; -} - -const MetaKeyPairVector &GetColladaAssimpMetaKeys() { - static const MetaKeyPairVector result = MakeColladaAssimpMetaKeys(); - return result; -} - -const MetaKeyPairVector MakeColladaAssimpMetaKeysCamelCase() { - MetaKeyPairVector result = MakeColladaAssimpMetaKeys(); - for (auto &val : result) { - ToCamelCase(val.first); - } - return result; -} - -const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase() { - static const MetaKeyPairVector result = MakeColladaAssimpMetaKeysCamelCase(); - return result; -} - -// ------------------------------------------------------------------------------------------------ -// Convert underscore_separated to CamelCase: "authoring_tool" becomes "AuthoringTool" -void ToCamelCase(std::string &text) { - if (text.empty()) - return; - // Capitalise first character - auto it = text.begin(); - (*it) = ai_toupper(*it); - ++it; - for (/*started above*/; it != text.end(); /*iterated below*/) { - if ((*it) == '_') { - it = text.erase(it); - if (it != text.end()) - (*it) = ai_toupper(*it); - } else { - // Make lower case - (*it) = ai_tolower(*it); - ++it; - } - } -} - -} // namespace Collada -} // namespace Assimp diff --git a/libs/assimp/code/AssetLib/Collada/ColladaHelper.h b/libs/assimp/code/AssetLib/Collada/ColladaHelper.h deleted file mode 100644 index 31d7b5a..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaHelper.h +++ /dev/null @@ -1,679 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** Helper structures for the Collada loader */ - -#ifndef AI_COLLADAHELPER_H_INC -#define AI_COLLADAHELPER_H_INC - -#include <assimp/light.h> -#include <assimp/material.h> -#include <assimp/mesh.h> - -#include <cstdint> -#include <map> -#include <set> -#include <vector> - -struct aiMaterial; - -namespace Assimp { -namespace Collada { - -/// Collada file versions which evolved during the years ... -enum FormatVersion { - FV_1_5_n, - FV_1_4_n, - FV_1_3_n -}; - -/// Transformation types that can be applied to a node -enum TransformType { - TF_LOOKAT, - TF_ROTATE, - TF_TRANSLATE, - TF_SCALE, - TF_SKEW, - TF_MATRIX -}; - -/// Different types of input data to a vertex or face -enum InputType { - IT_Invalid, - IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data. - IT_Position, - IT_Normal, - IT_Texcoord, - IT_Color, - IT_Tangent, - IT_Bitangent -}; - -/// Supported controller types -enum ControllerType { - Skin, - Morph -}; - -/// Supported morph methods -enum MorphMethod { - Normalized, - Relative -}; - -/// Common metadata keys as <Collada, Assimp> -using MetaKeyPair = std::pair<std::string, std::string>; -using MetaKeyPairVector = std::vector<MetaKeyPair>; - -/// Collada as lower_case (native) -const MetaKeyPairVector &GetColladaAssimpMetaKeys(); - -// Collada as CamelCase (used by Assimp for consistency) -const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase(); - -/// Convert underscore_separated to CamelCase "authoring_tool" becomes "AuthoringTool" -void ToCamelCase(std::string &text); - -/// Contains all data for one of the different transformation types -struct Transform { - std::string mID; ///< SID of the transform step, by which anim channels address their target node - TransformType mType; - ai_real f[16]; ///< Interpretation of data depends on the type of the transformation -}; - -/// A collada camera. -struct Camera { - Camera() : - mOrtho(false), - mHorFov(10e10f), - mVerFov(10e10f), - mAspect(10e10f), - mZNear(0.1f), - mZFar(1000.f) {} - - /// Name of camera - std::string mName; - - /// True if it is an orthographic camera - bool mOrtho; - - /// Horizontal field of view in degrees - ai_real mHorFov; - - /// Vertical field of view in degrees - ai_real mVerFov; - - /// Screen aspect - ai_real mAspect; - - /// Near& far z - ai_real mZNear, mZFar; -}; - -#define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f - -/** A collada light source. */ -struct Light { - Light() : - mType(aiLightSource_UNDEFINED), - mAttConstant(1.f), - mAttLinear(0.f), - mAttQuadratic(0.f), - mFalloffAngle(180.f), - mFalloffExponent(0.f), - mPenumbraAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET), - mOuterAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET), - mIntensity(1.f) {} - - /// Type of the light source aiLightSourceType + ambient - unsigned int mType; - - /// Color of the light - aiColor3D mColor; - - /// Light attenuation - ai_real mAttConstant, mAttLinear, mAttQuadratic; - - /// Spot light falloff - ai_real mFalloffAngle; - ai_real mFalloffExponent; - - // ----------------------------------------------------- - // FCOLLADA extension from here - - /// ... related stuff from maja and max extensions - ai_real mPenumbraAngle; - ai_real mOuterAngle; - - /// Common light intensity - ai_real mIntensity; -}; - -/** Short vertex index description */ -struct InputSemanticMapEntry { - InputSemanticMapEntry() : - mSet(0), - mType(IT_Invalid) {} - - /// Index of set, optional - unsigned int mSet; - - /// Type of referenced vertex input - InputType mType; -}; - -/// Table to map from effect to vertex input semantics -struct SemanticMappingTable { - /// Name of material - std::string mMatName; - - /// List of semantic map commands, grouped by effect semantic name - using InputSemanticMap = std::map<std::string, InputSemanticMapEntry>; - InputSemanticMap mMap; - - /// For std::find - bool operator==(const std::string &s) const { - return s == mMatName; - } -}; - -/// A reference to a mesh inside a node, including materials assigned to the various subgroups. -/// The ID refers to either a mesh or a controller which specifies the mesh -struct MeshInstance { - ///< ID of the mesh or controller to be instanced - std::string mMeshOrController; - - ///< Map of materials by the subgroup ID they're applied to - std::map<std::string, SemanticMappingTable> mMaterials; -}; - -/// A reference to a camera inside a node -struct CameraInstance { - ///< ID of the camera - std::string mCamera; -}; - -/// A reference to a light inside a node -struct LightInstance { - ///< ID of the camera - std::string mLight; -}; - -/// A reference to a node inside a node -struct NodeInstance { - ///< ID of the node - std::string mNode; -}; - -/// A node in a scene hierarchy -struct Node { - std::string mName; - std::string mID; - std::string mSID; - Node *mParent; - std::vector<Node *> mChildren; - - /// Operations in order to calculate the resulting transformation to parent. - std::vector<Transform> mTransforms; - - /// Meshes at this node - std::vector<MeshInstance> mMeshes; - - /// Lights at this node - std::vector<LightInstance> mLights; - - /// Cameras at this node - std::vector<CameraInstance> mCameras; - - /// Node instances at this node - std::vector<NodeInstance> mNodeInstances; - - /// Root-nodes: Name of primary camera, if any - std::string mPrimaryCamera; - - /// Constructor. Begin with a zero parent - Node() : - mParent(nullptr) { - // empty - } - - /// Destructor: delete all children subsequently - ~Node() { - for (std::vector<Node *>::iterator it = mChildren.begin(); it != mChildren.end(); ++it) { - delete *it; - } - } -}; - -/// Data source array: either floats or strings -struct Data { - bool mIsStringArray; - std::vector<ai_real> mValues; - std::vector<std::string> mStrings; -}; - -/// Accessor to a data array -struct Accessor { - size_t mCount; // in number of objects - size_t mSize; // size of an object, in elements (floats or strings, mostly 1) - size_t mOffset; // in number of values - size_t mStride; // Stride in number of values - std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore. - size_t mSubOffset[4]; // Sub-offset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on. - // For example, SubOffset[0] denotes which of the values inside the object is the vector X component. - std::string mSource; // URL of the source array - mutable const Data *mData; // Pointer to the source array, if resolved. nullptr else - - Accessor() { - mCount = 0; - mSize = 0; - mOffset = 0; - mStride = 0; - mData = nullptr; - mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0; - } -}; - -/// A single face in a mesh -struct Face { - std::vector<size_t> mIndices; -}; - -/// An input channel for mesh data, referring to a single accessor -struct InputChannel { - InputType mType; // Type of the data - size_t mIndex; // Optional index, if multiple sets of the same data type are given - size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better. - std::string mAccessor; // ID of the accessor where to read the actual values from. - mutable const Accessor *mResolved; // Pointer to the accessor, if resolved. nullptr else - - InputChannel() { - mType = IT_Invalid; - mIndex = 0; - mOffset = 0; - mResolved = nullptr; - } -}; - -/// Subset of a mesh with a certain material -struct SubMesh { - std::string mMaterial; ///< subgroup identifier - size_t mNumFaces; ///< number of faces in this sub-mesh -}; - -/// Contains data for a single mesh -struct Mesh { - Mesh(const std::string &id) : - mId(id) { - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - mNumUVComponents[i] = 2; - } - } - - const std::string mId; - std::string mName; - - // just to check if there's some sophisticated addressing involved... - // which we don't support, and therefore should warn about. - std::string mVertexID; - - // Vertex data addressed by vertex indices - std::vector<InputChannel> mPerVertexData; - - // actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed - std::vector<aiVector3D> mPositions; - std::vector<aiVector3D> mNormals; - std::vector<aiVector3D> mTangents; - std::vector<aiVector3D> mBitangents; - std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; - - unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - - // Faces. Stored are only the number of vertices for each face. - // 1 == point, 2 == line, 3 == triangle, 4+ == poly - std::vector<size_t> mFaceSize; - - // Position indices for all faces in the sequence given in mFaceSize - - // necessary for bone weight assignment - std::vector<size_t> mFacePosIndices; - - // Sub-meshes in this mesh, each with a given material - std::vector<SubMesh> mSubMeshes; -}; - -/// Which type of primitives the ReadPrimitives() function is going to read -enum PrimitiveType { - Prim_Invalid, - Prim_Lines, - Prim_LineStrip, - Prim_Triangles, - Prim_TriStrips, - Prim_TriFans, - Prim_Polylist, - Prim_Polygon -}; - -/// A skeleton controller to deform a mesh with the use of joints -struct Controller { - // controller type - ControllerType mType; - - // Morphing method if type is Morph - MorphMethod mMethod; - - // the URL of the mesh deformed by the controller. - std::string mMeshId; - - // accessor URL of the joint names - std::string mJointNameSource; - - ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases - ai_real mBindShapeMatrix[16]; - - // accessor URL of the joint inverse bind matrices - std::string mJointOffsetMatrixSource; - - // input channel: joint names. - InputChannel mWeightInputJoints; - // input channel: joint weights - InputChannel mWeightInputWeights; - - // Number of weights per vertex. - std::vector<size_t> mWeightCounts; - - // JointIndex-WeightIndex pairs for all vertices - std::vector<std::pair<size_t, size_t>> mWeights; - - std::string mMorphTarget; - std::string mMorphWeight; -}; - -/// A collada material. Pretty much the only member is a reference to an effect. -struct Material { - std::string mName; - std::string mEffect; -}; - -/// Type of the effect param -enum ParamType { - Param_Sampler, - Param_Surface -}; - -/// A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them -struct EffectParam { - ParamType mType; - std::string mReference; // to which other thing the param is referring to. -}; - -/// Shading type supported by the standard effect spec of Collada -enum ShadeType { - Shade_Invalid, - Shade_Constant, - Shade_Lambert, - Shade_Phong, - Shade_Blinn -}; - -/// Represents a texture sampler in collada -struct Sampler { - Sampler() : - mWrapU(true), - mWrapV(true), - mMirrorU(), - mMirrorV(), - mOp(aiTextureOp_Multiply), - mUVId(UINT_MAX), - mWeighting(1.f), - mMixWithPrevious(1.f) {} - - /// Name of image reference - std::string mName; - - /// Wrap U? - bool mWrapU; - - /// Wrap V? - bool mWrapV; - - /// Mirror U? - bool mMirrorU; - - /// Mirror V? - bool mMirrorV; - - /// Blend mode - aiTextureOp mOp; - - /// UV transformation - aiUVTransform mTransform; - - /// Name of source UV channel - std::string mUVChannel; - - /// Resolved UV channel index or UINT_MAX if not known - unsigned int mUVId; - - // OKINO/MAX3D extensions from here - // ------------------------------------------------------- - - /// Weighting factor - ai_real mWeighting; - - /// Mixing factor from OKINO - ai_real mMixWithPrevious; -}; - -/// A collada effect. Can contain about anything according to the Collada spec, -/// but we limit our version to a reasonable subset. -struct Effect { - /// Shading mode - ShadeType mShadeType; - - /// Colors - aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular, - mTransparent, mReflective; - - /// Textures - Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular, - mTexTransparent, mTexBump, mTexReflective; - - /// Scalar factory - ai_real mShininess, mRefractIndex, mReflectivity; - ai_real mTransparency; - bool mHasTransparency; - bool mRGBTransparency; - bool mInvertTransparency; - - /// local params referring to each other by their SID - using ParamLibrary = std::map<std::string, Collada::EffectParam>; - ParamLibrary mParams; - - // MAX3D extensions - // --------------------------------------------------------- - // Double-sided? - bool mDoubleSided, mWireframe, mFaceted; - - Effect() : - mShadeType(Shade_Phong), - mEmissive(0, 0, 0, 1), - mAmbient(0.1f, 0.1f, 0.1f, 1), - mDiffuse(0.6f, 0.6f, 0.6f, 1), - mSpecular(0.4f, 0.4f, 0.4f, 1), - mTransparent(0, 0, 0, 1), - mShininess(10.0f), - mRefractIndex(1.f), - mReflectivity(0.f), - mTransparency(1.f), - mHasTransparency(false), - mRGBTransparency(false), - mInvertTransparency(false), - mDoubleSided(false), - mWireframe(false), - mFaceted(false) { - } -}; - -/// An image, meaning texture -struct Image { - std::string mFileName; - - /// Embedded image data - std::vector<uint8_t> mImageData; - - /// File format hint of embedded image data - std::string mEmbeddedFormat; -}; - -/// An animation channel. -struct AnimationChannel { - /// URL of the data to animate. Could be about anything, but we support only the - /// "NodeID/TransformID.SubElement" notation - std::string mTarget; - - /// Source URL of the time values. Collada calls them "input". Meh. - std::string mSourceTimes; - /// Source URL of the value values. Collada calls them "output". - std::string mSourceValues; - /// Source URL of the IN_TANGENT semantic values. - std::string mInTanValues; - /// Source URL of the OUT_TANGENT semantic values. - std::string mOutTanValues; - /// Source URL of the INTERPOLATION semantic values. - std::string mInterpolationValues; -}; - -/// An animation. Container for 0-x animation channels or 0-x animations -struct Animation { - /// Anim name - std::string mName; - - /// the animation channels, if any - std::vector<AnimationChannel> mChannels; - - /// the sub-animations, if any - std::vector<Animation *> mSubAnims; - - /// Destructor - ~Animation() { - for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) { - delete *it; - } - } - - /// Collect all channels in the animation hierarchy into a single channel list. - void CollectChannelsRecursively(std::vector<AnimationChannel> &channels) { - channels.insert(channels.end(), mChannels.begin(), mChannels.end()); - - for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) { - Animation *pAnim = (*it); - pAnim->CollectChannelsRecursively(channels); - } - } - - /// Combine all single-channel animations' channel into the same (parent) animation channel list. - void CombineSingleChannelAnimations() { - CombineSingleChannelAnimationsRecursively(this); - } - - void CombineSingleChannelAnimationsRecursively(Animation *pParent) { - std::set<std::string> childrenTargets; - bool childrenAnimationsHaveDifferentChannels = true; - - for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) { - Animation *anim = *it; - CombineSingleChannelAnimationsRecursively(anim); - - if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 && - childrenTargets.find(anim->mChannels[0].mTarget) == childrenTargets.end()) { - childrenTargets.insert(anim->mChannels[0].mTarget); - } else { - childrenAnimationsHaveDifferentChannels = false; - } - - ++it; - } - - // We only want to combine animations if they have different channels - if (childrenAnimationsHaveDifferentChannels) { - for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) { - Animation *anim = *it; - - pParent->mChannels.push_back(anim->mChannels[0]); - - it = pParent->mSubAnims.erase(it); - - delete anim; - continue; - } - } - } -}; - -/// Description of a collada animation channel which has been determined to affect the current node -struct ChannelEntry { - const Collada::AnimationChannel *mChannel; ///< the source channel - std::string mTargetId; - std::string mTransformId; // the ID of the transformation step of the node which is influenced - size_t mTransformIndex; // Index into the node's transform chain to apply the channel to - size_t mSubElement; // starting index inside the transform data - - // resolved data references - const Collada::Accessor *mTimeAccessor; ///> Collada accessor to the time values - const Collada::Data *mTimeData; ///> Source data array for the time values - const Collada::Accessor *mValueAccessor; ///> Collada accessor to the key value values - const Collada::Data *mValueData; ///> Source datat array for the key value values - - ChannelEntry() : - mChannel(), - mTransformIndex(), - mSubElement(), - mTimeAccessor(), - mTimeData(), - mValueAccessor(), - mValueData() {} -}; - -} // end of namespace Collada -} // end of namespace Assimp - -#endif // AI_COLLADAHELPER_H_INC diff --git a/libs/assimp/code/AssetLib/Collada/ColladaLoader.cpp b/libs/assimp/code/AssetLib/Collada/ColladaLoader.cpp deleted file mode 100644 index 775ba44..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaLoader.cpp +++ /dev/null @@ -1,1828 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the Collada loader */ - -#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER - -#include "ColladaLoader.h" -#include "ColladaParser.h" -#include <assimp/ColladaMetaData.h> -#include <assimp/CreateAnimMesh.h> -#include <assimp/ParsingUtils.h> -#include <assimp/SkeletonMeshBuilder.h> -#include <assimp/ZipArchiveIOSystem.h> -#include <assimp/anim.h> -#include <assimp/fast_atof.h> -#include <assimp/importerdesc.h> -#include <assimp/scene.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/Importer.hpp> - -#include <numeric> - -namespace Assimp { - -using namespace Assimp::Formatter; -using namespace Assimp::Collada; - -static const aiImporterDesc desc = { - "Collada Importer", - "", - "", - "http://collada.org", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportCompressedFlavour, - 1, - 3, - 1, - 5, - "dae xml zae" -}; - -static const float kMillisecondsFromSeconds = 1000.f; - -// Add an item of metadata to a node -// Assumes the key is not already in the list -template <typename T> -inline void AddNodeMetaData(aiNode *node, const std::string &key, const T &value) { - if (nullptr == node->mMetaData) { - node->mMetaData = new aiMetadata(); - } - node->mMetaData->Add(key, value); -} - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ColladaLoader::ColladaLoader() : - mFileName(), - mMeshIndexByID(), - mMaterialIndexByName(), - mMeshes(), - newMats(), - mCameras(), - mLights(), - mTextures(), - mAnims(), - noSkeletonMesh(false), - ignoreUpDirection(false), - useColladaName(false), - mNodeNameCounter(0) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ColladaLoader::~ColladaLoader() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { - // Look for a DAE file inside, but don't extract it - ZipArchiveIOSystem zip_archive(pIOHandler, pFile); - if (zip_archive.isOpen()) { - return !ColladaParser::ReadZaeManifest(zip_archive).empty(); - } - - static const char *tokens[] = { "<collada" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); -} - -// ------------------------------------------------------------------------------------------------ -void ColladaLoader::SetupProperties(const Importer *pImp) { - noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0; - ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0; - useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Get file extension list -const aiImporterDesc *ColladaLoader::GetInfo() const { - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - mFileName = pFile; - - // clean all member arrays - just for safety, it should work even if we did not - mMeshIndexByID.clear(); - mMaterialIndexByName.clear(); - mMeshes.clear(); - mTargetMeshes.clear(); - newMats.clear(); - mLights.clear(); - mCameras.clear(); - mTextures.clear(); - mAnims.clear(); - - // parse the input file - ColladaParser parser(pIOHandler, pFile); - - if (!parser.mRootNode) { - throw DeadlyImportError("Collada: File came out empty. Something is wrong here."); - } - - // reserve some storage to avoid unnecessary reallocs - newMats.reserve(parser.mMaterialLibrary.size() * 2u); - mMeshes.reserve(parser.mMeshLibrary.size() * 2u); - - mCameras.reserve(parser.mCameraLibrary.size()); - mLights.reserve(parser.mLightLibrary.size()); - - // create the materials first, for the meshes to find - BuildMaterials(parser, pScene); - - // build the node hierarchy from it - pScene->mRootNode = BuildHierarchy(parser, parser.mRootNode); - - // ... then fill the materials with the now adjusted settings - FillMaterials(parser, pScene); - - // Apply unit-size scale calculation - - pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, - 0, parser.mUnitSize, 0, 0, - 0, 0, parser.mUnitSize, 0, - 0, 0, 0, 1); - if (!ignoreUpDirection) { - // Convert to Y_UP, if different orientation - if (parser.mUpDirection == ColladaParser::UP_X) { - pScene->mRootNode->mTransformation *= aiMatrix4x4( - 0, -1, 0, 0, - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - } else if (parser.mUpDirection == ColladaParser::UP_Z) { - pScene->mRootNode->mTransformation *= aiMatrix4x4( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); - } - } - - // Store scene metadata - if (!parser.mAssetMetaData.empty()) { - const size_t numMeta(parser.mAssetMetaData.size()); - pScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta)); - size_t i = 0; - for (auto it = parser.mAssetMetaData.cbegin(); it != parser.mAssetMetaData.cend(); ++it, ++i) { - pScene->mMetaData->Set(static_cast<unsigned int>(i), (*it).first, (*it).second); - } - } - - StoreSceneMeshes(pScene); - StoreSceneMaterials(pScene); - StoreSceneTextures(pScene); - StoreSceneLights(pScene); - StoreSceneCameras(pScene); - StoreAnimations(pScene, parser); - - // If no meshes have been loaded, it's probably just an animated skeleton. - if (0u == pScene->mNumMeshes) { - if (!noSkeletonMesh) { - SkeletonMeshBuilder hero(pScene); - } - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } -} - -// ------------------------------------------------------------------------------------------------ -// 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(); - - // find a name for the new node. It's more complicated than you might think - node->mName.Set(FindNameForNode(pNode)); - // if we're not using the unique IDs, hold onto them for reference and export - if (useColladaName) { - if (!pNode->mID.empty()) { - AddNodeMetaData(node, AI_METADATA_COLLADA_ID, aiString(pNode->mID)); - } - if (!pNode->mSID.empty()) { - AddNodeMetaData(node, AI_METADATA_COLLADA_SID, aiString(pNode->mSID)); - } - } - - // calculate the transformation matrix for it - node->mTransformation = pParser.CalculateResultTransform(pNode->mTransforms); - - // now resolve node instances - std::vector<const Node*> instances; - ResolveNodeInstances(pParser, pNode, instances); - - // add children. first the *real* ones - node->mNumChildren = static_cast<unsigned int>(pNode->mChildren.size() + instances.size()); - node->mChildren = new aiNode *[node->mNumChildren]; - - for (size_t a = 0; a < pNode->mChildren.size(); ++a) { - node->mChildren[a] = BuildHierarchy(pParser, pNode->mChildren[a]); - node->mChildren[a]->mParent = node; - } - - // ... and finally the resolved node instances - for (size_t a = 0; a < instances.size(); ++a) { - node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy(pParser, instances[a]); - node->mChildren[pNode->mChildren.size() + a]->mParent = node; - } - - BuildMeshesForNode(pParser, pNode, node); - BuildCamerasForNode(pParser, pNode, node); - BuildLightsForNode(pParser, pNode, node); - - return node; -} - -// ------------------------------------------------------------------------------------------------ -// Resolve node instances -void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode, - std::vector<const Node*> &resolved) { - // 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) { - // find the corresponding node in the library - const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.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); - } - if (nullptr == nd) { - ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode); - } else { - // attach this node to the list of children - resolved.push_back(nd); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Resolve UV channels -void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) { - SemanticMappingTable::InputSemanticMap::const_iterator it = table.mMap.find(sampler.mUVChannel); - if (it == table.mMap.end()) { - return; - } - - if (it->second.mType != IT_Texcoord) { - ASSIMP_LOG_ERROR("Collada: Unexpected effect input mapping"); - } - - sampler.mUVId = it->second.mSet; -} - -// ------------------------------------------------------------------------------------------------ -// Builds lights for the given node and references them -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); - if (srcLightIt == pParser.mLightLibrary.end()) { - ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping."); - continue; - } - const Collada::Light *srcLight = &srcLightIt->second; - - // now fill our ai data structure - aiLight *out = new aiLight(); - out->mName = pTarget->mName; - out->mType = (aiLightSourceType)srcLight->mType; - - // collada lights point in -Z by default, rest is specified in node transform - out->mDirection = aiVector3D(0.f, 0.f, -1.f); - - out->mAttenuationConstant = srcLight->mAttConstant; - out->mAttenuationLinear = srcLight->mAttLinear; - out->mAttenuationQuadratic = srcLight->mAttQuadratic; - - out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor * srcLight->mIntensity; - if (out->mType == aiLightSource_AMBIENT) { - out->mColorDiffuse = out->mColorSpecular = aiColor3D(0, 0, 0); - out->mColorAmbient = srcLight->mColor * srcLight->mIntensity; - } else { - // collada doesn't differentiate between these color types - out->mColorDiffuse = out->mColorSpecular = srcLight->mColor * srcLight->mIntensity; - out->mColorAmbient = aiColor3D(0, 0, 0); - } - - // convert falloff angle and falloff exponent in our representation, if given - if (out->mType == aiLightSource_SPOT) { - out->mAngleInnerCone = AI_DEG_TO_RAD(srcLight->mFalloffAngle); - - // ... some extension magic. - if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - ai_epsilon)) { - // ... some deprecation magic. - if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - ai_epsilon)) { - // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess .... - // epsilon chosen to be 0.1 - float f = 1.0f; - if ( 0.0f != srcLight->mFalloffExponent ) { - f = 1.f / srcLight->mFalloffExponent; - } - out->mAngleOuterCone = std::acos(std::pow(0.1f, f)) + - out->mAngleInnerCone; - } else { - out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD(srcLight->mPenumbraAngle); - if (out->mAngleOuterCone < out->mAngleInnerCone) - std::swap(out->mAngleInnerCone, out->mAngleOuterCone); - } - } else { - out->mAngleOuterCone = AI_DEG_TO_RAD(srcLight->mOuterAngle); - } - } - - // add to light list - mLights.push_back(out); - } -} - -// ------------------------------------------------------------------------------------------------ -// Builds cameras for the given node and references them -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); - if (srcCameraIt == pParser.mCameraLibrary.end()) { - ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping."); - continue; - } - const Collada::Camera *srcCamera = &srcCameraIt->second; - - // orthographic cameras not yet supported in Assimp - if (srcCamera->mOrtho) { - ASSIMP_LOG_WARN("Collada: Orthographic cameras are not supported."); - } - - // now fill our ai data structure - aiCamera *out = new aiCamera(); - out->mName = pTarget->mName; - - // collada cameras point in -Z by default, rest is specified in node transform - out->mLookAt = aiVector3D(0.f, 0.f, -1.f); - - // near/far z is already ok - out->mClipPlaneFar = srcCamera->mZFar; - out->mClipPlaneNear = srcCamera->mZNear; - - // ... but for the rest some values are optional - // and we need to compute the others in any combination. - if (srcCamera->mAspect != 10e10f) { - out->mAspect = srcCamera->mAspect; - } - - if (srcCamera->mHorFov != 10e10f) { - out->mHorizontalFOV = srcCamera->mHorFov; - - if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) { - out->mAspect = std::tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) / - std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); - } - - } else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { - out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(std::atan(srcCamera->mAspect * - std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); - } - - // Collada uses degrees, we use radians - out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV); - - // add to camera list - mCameras.push_back(out); - } -} - -// ------------------------------------------------------------------------------------------------ -// Builds meshes for the given node and references them -void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) { - // accumulated mesh references by this node - std::vector<size_t> newMeshRefs; - newMeshRefs.reserve(pNode->mMeshes.size()); - - // add a mesh for each subgroup in each collada mesh - for (const MeshInstance &mid : pNode->mMeshes) { - const Mesh *srcMesh = nullptr; - const Controller *srcController = nullptr; - - // find the referred mesh - ColladaParser::MeshLibrary::const_iterator 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); - if (srcContrIt != pParser.mControllerLibrary.end()) { - srcController = &srcContrIt->second; - srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId); - if (srcMeshIt != pParser.mMeshLibrary.end()) { - srcMesh = srcMeshIt->second; - } - } - - if (nullptr == srcMesh) { - ASSIMP_LOG_WARN("Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping."); - continue; - } - } else { - // ID found in the mesh library -> direct reference to an unskinned mesh - srcMesh = srcMeshIt->second; - } - - // build a mesh for each of its subgroups - size_t vertexStart = 0, faceStart = 0; - for (size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm) { - const Collada::SubMesh &submesh = srcMesh->mSubMeshes[sm]; - if (submesh.mNumFaces == 0) { - continue; - } - - // find material assigned to this submesh - std::string meshMaterial; - std::map<std::string, SemanticMappingTable>::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial); - - const Collada::SemanticMappingTable *table = nullptr; - if (meshMatIt != mid.mMaterials.end()) { - table = &meshMatIt->second; - meshMaterial = table->mMatName; - } else { - ASSIMP_LOG_WARN("Collada: No material specified for subgroup <", submesh.mMaterial, "> in geometry <", - mid.mMeshOrController, ">."); - if (!mid.mMaterials.empty()) { - meshMaterial = mid.mMaterials.begin()->second.mMatName; - } - } - - // 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); - unsigned int matIdx = 0; - if (matIt != mMaterialIndexByName.end()) { - matIdx = static_cast<unsigned int>(matIt->second); - } - - if (table && !table->mMap.empty()) { - std::pair<Collada::Effect *, aiMaterial *> &mat = newMats[matIdx]; - - // Iterate through all texture channels assigned to the effect and - // check whether we have mapping information for it. - ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent, *table); - ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table); - } - - // built lookup index of the Mesh-Submesh-Material combination - 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); - if (dstMeshIt != mMeshIndexByID.end()) { - newMeshRefs.push_back(dstMeshIt->second); - } else { - // else we have to add the mesh to the collection and store its newly assigned index at the node - aiMesh *dstMesh = CreateMesh(pParser, srcMesh, submesh, srcController, vertexStart, faceStart); - - // store the mesh, and store its new index in the node - newMeshRefs.push_back(mMeshes.size()); - mMeshIndexByID[index] = mMeshes.size(); - mMeshes.push_back(dstMesh); - vertexStart += dstMesh->mNumVertices; - faceStart += submesh.mNumFaces; - - // assign the material index - std::map<std::string, size_t>::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial); - if (subMatIt != mMaterialIndexByName.end()) { - dstMesh->mMaterialIndex = static_cast<unsigned int>(subMatIt->second); - } else { - dstMesh->mMaterialIndex = matIdx; - } - if (dstMesh->mName.length == 0) { - dstMesh->mName = mid.mMeshOrController; - } - } - } - } - - // now place all mesh references we gathered in the target node - pTarget->mNumMeshes = static_cast<unsigned int>(newMeshRefs.size()); - if (!newMeshRefs.empty()) { - struct UIntTypeConverter { - unsigned int operator()(const size_t &v) const { - return static_cast<unsigned int>(v); - } - }; - - pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes]; - std::transform(newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes, UIntTypeConverter()); - } -} - -// ------------------------------------------------------------------------------------------------ -// Find mesh from either meshes or morph target meshes -aiMesh *ColladaLoader::findMesh(const std::string &meshid) { - if (meshid.empty()) { - return nullptr; - } - - for (auto & mMeshe : mMeshes) { - if (std::string(mMeshe->mName.data) == meshid) { - return mMeshe; - } - } - - for (auto & mTargetMeshe : mTargetMeshes) { - if (std::string(mTargetMeshe->mName.data) == meshid) { - return mTargetMeshe; - } - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh -aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrcMesh, const SubMesh &pSubMesh, - const Controller *pSrcController, size_t pStartVertex, size_t pStartFace) { - std::unique_ptr<aiMesh> dstMesh(new aiMesh); - - if (useColladaName) { - dstMesh->mName = pSrcMesh->mName; - } else { - dstMesh->mName = pSrcMesh->mId; - } - - if (pSrcMesh->mPositions.empty()) { - return dstMesh.release(); - } - - // count the vertices addressed by its faces - const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, - pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); - - // copy positions - dstMesh->mNumVertices = static_cast<unsigned int>(numVertices); - dstMesh->mVertices = new aiVector3D[numVertices]; - std::copy(pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() + pStartVertex + numVertices, dstMesh->mVertices); - - // normals, if given. HACK: (thom) Due to the glorious Collada spec we never - // know if we have the same number of normals as there are positions. So we - // also ignore any vertex attribute if it has a different count - if (pSrcMesh->mNormals.size() >= pStartVertex + numVertices) { - dstMesh->mNormals = new aiVector3D[numVertices]; - std::copy(pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() + pStartVertex + numVertices, dstMesh->mNormals); - } - - // tangents, if given. - if (pSrcMesh->mTangents.size() >= pStartVertex + numVertices) { - dstMesh->mTangents = new aiVector3D[numVertices]; - std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents); - } - - // bitangents, 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); - } - - // same for texture coords, as many as we have - // empty slots are not allowed, need to pack and adjust UV indexes accordingly - for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices) { - dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; - for (size_t b = 0; b < numVertices; ++b) { - dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex + b]; - } - - dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; - ++real; - } - } - - // same for vertex colors, as many as we have. again the same packing to avoid empty slots - for (size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { - if (pSrcMesh->mColors[a].size() >= pStartVertex + numVertices) { - dstMesh->mColors[real] = new aiColor4D[numVertices]; - std::copy(pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices, dstMesh->mColors[real]); - ++real; - } - } - - // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex - size_t vertex = 0; - dstMesh->mNumFaces = static_cast<unsigned int>(pSubMesh.mNumFaces); - dstMesh->mFaces = new aiFace[dstMesh->mNumFaces]; - for (size_t a = 0; a < dstMesh->mNumFaces; ++a) { - size_t s = pSrcMesh->mFaceSize[pStartFace + a]; - aiFace &face = dstMesh->mFaces[a]; - face.mNumIndices = static_cast<unsigned int>(s); - face.mIndices = new unsigned int[s]; - for (size_t b = 0; b < s; ++b) { - face.mIndices[b] = static_cast<unsigned int>(vertex++); - } - } - - // create morph target meshes if any - std::vector<aiMesh *> targetMeshes; - std::vector<float> targetWeights; - Collada::MorphMethod method = Normalized; - - for (std::map<std::string, Controller>::const_iterator it = pParser.mControllerLibrary.begin(); - it != pParser.mControllerLibrary.end(); ++it) { - const Controller &c = it->second; - const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId); - - if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName) { - const Collada::Accessor &targetAccessor = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, c.mMorphTarget); - const Collada::Accessor &weightAccessor = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, c.mMorphWeight); - const Collada::Data &targetData = pParser.ResolveLibraryReference(pParser.mDataLibrary, targetAccessor.mSource); - const Collada::Data &weightData = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightAccessor.mSource); - - // take method - method = c.mMethod; - - if (!targetData.mIsStringArray) { - throw DeadlyImportError("target data must contain id. "); - } - if (weightData.mIsStringArray) { - throw DeadlyImportError("target weight data must not be textual "); - } - - for (const auto & mString : targetData.mStrings) { - const Mesh *targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, mString); - - aiMesh *aimesh = findMesh(useColladaName ? targetMesh->mName : targetMesh->mId); - if (!aimesh) { - if (targetMesh->mSubMeshes.size() > 1) { - throw DeadlyImportError("Morphing target mesh must be a single"); - } - aimesh = CreateMesh(pParser, targetMesh, targetMesh->mSubMeshes.at(0), nullptr, 0, 0); - mTargetMeshes.push_back(aimesh); - } - targetMeshes.push_back(aimesh); - } - for (float mValue : weightData.mValues) { - targetWeights.push_back(mValue); - } - } - } - if (!targetMeshes.empty() && targetWeights.size() == targetMeshes.size()) { - std::vector<aiAnimMesh *> animMeshes; - for (unsigned int i = 0; i < targetMeshes.size(); ++i) { - aiMesh *targetMesh = targetMeshes.at(i); - aiAnimMesh *animMesh = aiCreateAnimMesh(targetMesh); - float weight = targetWeights[i]; - animMesh->mWeight = weight == 0 ? 1.0f : weight; - animMesh->mName = targetMesh->mName; - animMeshes.push_back(animMesh); - } - dstMesh->mMethod = (method == Relative) ? aiMorphingMethod_MORPH_RELATIVE : aiMorphingMethod_MORPH_NORMALIZED; - dstMesh->mAnimMeshes = new aiAnimMesh *[animMeshes.size()]; - dstMesh->mNumAnimMeshes = static_cast<unsigned int>(animMeshes.size()); - for (unsigned int i = 0; i < animMeshes.size(); ++i) { - dstMesh->mAnimMeshes[i] = animMeshes.at(i); - } - } - - // create bones if given - if (pSrcController && pSrcController->mType == Collada::Skin) { - // resolve references - joint names - const Collada::Accessor &jointNamesAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mJointNameSource); - const Collada::Data &jointNames = pParser.ResolveLibraryReference(pParser.mDataLibrary, jointNamesAcc.mSource); - // joint offset matrices - const Collada::Accessor &jointMatrixAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); - const Collada::Data &jointMatrices = pParser.ResolveLibraryReference(pParser.mDataLibrary, jointMatrixAcc.mSource); - // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider - const Collada::Accessor &weightNamesAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); - if (&weightNamesAcc != &jointNamesAcc) - throw DeadlyImportError("Temporary implementational laziness. If you read this, please report to the author."); - // vertex weights - const Collada::Accessor &weightsAcc = pParser.ResolveLibraryReference(pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); - const Collada::Data &weights = pParser.ResolveLibraryReference(pParser.mDataLibrary, weightsAcc.mSource); - - if (!jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) { - throw DeadlyImportError("Data type mismatch while resolving mesh joints"); - } - // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex - if (pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) { - throw DeadlyImportError("Unsupported vertex_weight addressing scheme. "); - } - - // create containers to collect the weights for each bone - size_t numBones = jointNames.mStrings.size(); - std::vector<std::vector<aiVertexWeight>> dstBones(numBones); - - // build a temporary array of pointers to the start of each vertex's weights - using IndexPairVector = std::vector<std::pair<size_t, size_t>>; - std::vector<IndexPairVector::const_iterator> weightStartPerVertex; - weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end()); - - IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); - for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) { - weightStartPerVertex[a] = pit; - pit += pSrcController->mWeightCounts[a]; - } - - // now for each vertex put the corresponding vertex weights into each bone's weight collection - for (size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) { - // which position index was responsible for this vertex? that's also the index by which - // 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]; - size_t pairCount = pSrcController->mWeightCounts[orgIndex]; - - for (size_t b = 0; b < pairCount; ++b, ++iit) { - const size_t jointIndex = iit->first; - const size_t vertexIndex = iit->second; - ai_real weight = 1.0f; - if (!weights.mValues.empty()) { - weight = ReadFloat(weightsAcc, weights, vertexIndex, 0); - } - - // one day I gonna kill that XSI Collada exporter - if (weight > 0.0f) { - aiVertexWeight w; - w.mVertexId = static_cast<unsigned int>(a - pStartVertex); - w.mWeight = weight; - dstBones[jointIndex].push_back(w); - } - } - } - - // count the number of bones which influence vertices of the current submesh - size_t numRemainingBones = 0; - for (const auto & dstBone : dstBones) { - if (!dstBone.empty()) { - ++numRemainingBones; - } - } - - // create bone array and copy bone weights one by one - dstMesh->mNumBones = static_cast<unsigned int>(numRemainingBones); - dstMesh->mBones = new aiBone *[numRemainingBones]; - size_t boneCount = 0; - for (size_t a = 0; a < numBones; ++a) { - // omit bones without weights - if (dstBones[a].empty()) { - continue; - } - - // create bone with its weights - aiBone *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); - bone->mOffsetMatrix.a3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 2); - bone->mOffsetMatrix.a4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 3); - bone->mOffsetMatrix.b1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 4); - bone->mOffsetMatrix.b2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 5); - bone->mOffsetMatrix.b3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 6); - bone->mOffsetMatrix.b4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 7); - bone->mOffsetMatrix.c1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 8); - bone->mOffsetMatrix.c2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 9); - bone->mOffsetMatrix.c3 = ReadFloat(jointMatrixAcc, jointMatrices, a, 10); - bone->mOffsetMatrix.c4 = ReadFloat(jointMatrixAcc, jointMatrices, a, 11); - bone->mNumWeights = static_cast<unsigned int>(dstBones[a].size()); - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - std::copy(dstBones[a].begin(), dstBones[a].end(), bone->mWeights); - - // apply bind shape matrix to offset matrix - aiMatrix4x4 bindShapeMatrix; - bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0]; - bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1]; - bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2]; - bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3]; - bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4]; - bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5]; - bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6]; - bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7]; - bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8]; - bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9]; - bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10]; - bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11]; - bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12]; - bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13]; - bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14]; - bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15]; - bone->mOffsetMatrix *= bindShapeMatrix; - - // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name. - // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID, - // and replace the bone's name by the node's name so that the user can use the standard - // find-by-name method to associate nodes with bones. - const Collada::Node *bnode = FindNode(pParser.mRootNode, bone->mName.data); - if (nullptr == bnode) { - bnode = FindNodeBySID(pParser.mRootNode, bone->mName.data); - } - - // assign the name that we would have assigned for the source node - if (nullptr != bnode) { - bone->mName.Set(FindNameForNode(bnode)); - } else { - ASSIMP_LOG_WARN("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"."); - } - - // and insert bone - dstMesh->mBones[boneCount++] = bone; - } - } - - return dstMesh.release(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all meshes in the given scene -void ColladaLoader::StoreSceneMeshes(aiScene *pScene) { - pScene->mNumMeshes = static_cast<unsigned int>(mMeshes.size()); - if (mMeshes.empty()) { - return; - } - pScene->mMeshes = new aiMesh *[mMeshes.size()]; - std::copy(mMeshes.begin(), mMeshes.end(), pScene->mMeshes); - mMeshes.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all cameras in the given scene -void ColladaLoader::StoreSceneCameras(aiScene *pScene) { - pScene->mNumCameras = static_cast<unsigned int>(mCameras.size()); - if (mCameras.empty()) { - return; - } - pScene->mCameras = new aiCamera *[mCameras.size()]; - std::copy(mCameras.begin(), mCameras.end(), pScene->mCameras); - mCameras.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all lights in the given scene -void ColladaLoader::StoreSceneLights(aiScene *pScene) { - pScene->mNumLights = static_cast<unsigned int>(mLights.size()); - if (mLights.empty()) { - return; - } - pScene->mLights = new aiLight *[mLights.size()]; - std::copy(mLights.begin(), mLights.end(), pScene->mLights); - mLights.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all textures in the given scene -void ColladaLoader::StoreSceneTextures(aiScene *pScene) { - pScene->mNumTextures = static_cast<unsigned int>(mTextures.size()); - if (mTextures.empty()) { - return; - } - pScene->mTextures = new aiTexture *[mTextures.size()]; - std::copy(mTextures.begin(), mTextures.end(), pScene->mTextures); - mTextures.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all materials in the given scene -void ColladaLoader::StoreSceneMaterials(aiScene *pScene) { - pScene->mNumMaterials = static_cast<unsigned int>(newMats.size()); - if (newMats.empty()) { - return; - } - pScene->mMaterials = new aiMaterial *[newMats.size()]; - for (unsigned int i = 0; i < newMats.size(); ++i) { - pScene->mMaterials[i] = newMats[i].second; - } - newMats.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Stores all animations -void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParser) { - // recursively collect all animations from the collada scene - StoreAnimations(pScene, pParser, &pParser.mAnims, ""); - - // catch special case: many animations with the same length, each affecting only a single node. - // we need to unite all those single-node-anims to a proper combined animation - for (size_t a = 0; a < mAnims.size(); ++a) { - aiAnimation *templateAnim = mAnims[a]; - - if (templateAnim->mNumChannels == 1) { - // search for other single-channel-anims with the same duration - std::vector<size_t> collectedAnimIndices; - for (size_t b = a + 1; b < mAnims.size(); ++b) { - aiAnimation *other = mAnims[b]; - if (other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && - other->mTicksPerSecond == templateAnim->mTicksPerSecond) - collectedAnimIndices.push_back(b); - } - - // We only want to combine the animations if they have different channels - std::set<std::string> animTargets; - animTargets.insert(templateAnim->mChannels[0]->mNodeName.C_Str()); - bool collectedAnimationsHaveDifferentChannels = true; - for (unsigned long long collectedAnimIndice : collectedAnimIndices) { - aiAnimation *srcAnimation = mAnims[(int)collectedAnimIndice]; - std::string channelName = std::string(srcAnimation->mChannels[0]->mNodeName.C_Str()); - if (animTargets.find(channelName) == animTargets.end()) { - animTargets.insert(channelName); - } else { - collectedAnimationsHaveDifferentChannels = false; - break; - } - } - - if (!collectedAnimationsHaveDifferentChannels) { - continue; - } - - // if there are other animations which fit the template anim, combine all channels into a single anim - if (!collectedAnimIndices.empty()) { - aiAnimation *combinedAnim = new aiAnimation(); - combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a)); - combinedAnim->mDuration = templateAnim->mDuration; - combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond; - combinedAnim->mNumChannels = static_cast<unsigned int>(collectedAnimIndices.size() + 1); - combinedAnim->mChannels = new aiNodeAnim *[combinedAnim->mNumChannels]; - // add the template anim as first channel by moving its aiNodeAnim to the combined animation - combinedAnim->mChannels[0] = templateAnim->mChannels[0]; - templateAnim->mChannels[0] = nullptr; - delete templateAnim; - // combined animation replaces template animation in the anim array - mAnims[a] = combinedAnim; - - // move the memory of all other anims to the combined anim and erase them from the source anims - for (size_t b = 0; b < collectedAnimIndices.size(); ++b) { - aiAnimation *srcAnimation = mAnims[collectedAnimIndices[b]]; - combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0]; - srcAnimation->mChannels[0] = nullptr; - delete srcAnimation; - } - - // in a second go, delete all the single-channel-anims that we've stripped from their channels - // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one - while (!collectedAnimIndices.empty()) { - mAnims.erase(mAnims.begin() + collectedAnimIndices.back()); - collectedAnimIndices.pop_back(); - } - } - } - } - - // now store all anims in the scene - if (!mAnims.empty()) { - pScene->mNumAnimations = static_cast<unsigned int>(mAnims.size()); - pScene->mAnimations = new aiAnimation *[mAnims.size()]; - std::copy(mAnims.begin(), mAnims.end(), pScene->mAnimations); - } - - mAnims.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Constructs the animations for the given source anim -void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Animation *pSrcAnim, const std::string &pPrefix) { - std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName; - - // create nested animations, if given - for (auto mSubAnim : pSrcAnim->mSubAnims) { - StoreAnimations(pScene, pParser, mSubAnim, animName); - } - - // create animation channels, if any - if (!pSrcAnim->mChannels.empty()) { - CreateAnimation(pScene, pParser, pSrcAnim, animName); - } -} - -struct MorphTimeValues { - float mTime; - struct key { - float mWeight; - unsigned int mValue; - }; - std::vector<key> mKeys; -}; - -void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value) { - MorphTimeValues::key k; - k.mValue = value; - k.mWeight = weight; - if (values.empty() || time < values[0].mTime) { - MorphTimeValues val; - val.mTime = time; - val.mKeys.push_back(k); - values.insert(values.begin(), val); - return; - } - if (time > values.back().mTime) { - MorphTimeValues val; - val.mTime = time; - val.mKeys.push_back(k); - values.insert(values.end(), val); - return; - } - for (unsigned int i = 0; i < values.size(); i++) { - if (std::abs(time - values[i].mTime) < ai_epsilon) { - values[i].mKeys.push_back(k); - return; - } else if (time > values[i].mTime && time < values[i + 1].mTime) { - MorphTimeValues val; - val.mTime = time; - val.mKeys.push_back(k); - values.insert(values.begin() + i, val); - return; - } - } -} - -static float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value) { - for (auto mKey : values[key].mKeys) { - if (mKey.mValue == value) { - return mKey.mWeight; - } - } - // no value at key found, try to interpolate if present at other keys. if not, return zero - // TODO: interpolation - return 0.0f; -} - -// ------------------------------------------------------------------------------------------------ -// Constructs the animation for the given source anim -void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Animation *pSrcAnim, const std::string &pName) { - // collect a list of animatable nodes - std::vector<const aiNode *> nodes; - CollectNodes(pScene->mRootNode, nodes); - - std::vector<aiNodeAnim *> anims; - std::vector<aiMeshMorphAnim *> morphAnims; - - for (auto node : nodes) { - // find all the collada anim channels which refer to the current node - std::vector<ChannelEntry> entries; - std::string nodeName = node->mName.data; - - // find the collada node corresponding to the aiNode - const Node *srcNode = FindNode(pParser.mRootNode, nodeName); - if (!srcNode) { - continue; - } - - // now check all channels if they affect the current node - std::string targetID, subElement; - for (std::vector<AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin(); - cit != pSrcAnim->mChannels.end(); ++cit) { - const AnimationChannel &srcChannel = *cit; - ChannelEntry entry; - - // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others - // find the slash that separates the node name - there should be only one - std::string::size_type slashPos = srcChannel.mTarget.find('/'); - if (slashPos == std::string::npos) { - std::string::size_type targetPos = srcChannel.mTarget.find(srcNode->mID); - if (targetPos == std::string::npos) { - continue; - } - - // not node transform, but something else. store as unknown animation channel for now - entry.mChannel = &(*cit); - entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(), - srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length()); - if (entry.mTargetId.front() == '-') { - entry.mTargetId = entry.mTargetId.substr(1); - } - entries.push_back(entry); - continue; - } - if (srcChannel.mTarget.find('/', slashPos + 1) != std::string::npos) { - continue; - } - - targetID.clear(); - targetID = srcChannel.mTarget.substr(0, slashPos); - if (targetID != srcNode->mID) { - continue; - } - - // find the dot that separates the transformID - there should be only one or zero - std::string::size_type dotPos = srcChannel.mTarget.find('.'); - if (dotPos != std::string::npos) { - if (srcChannel.mTarget.find('.', dotPos + 1) != std::string::npos) { - continue; - } - - entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, dotPos - slashPos - 1); - - subElement.clear(); - subElement = srcChannel.mTarget.substr(dotPos + 1); - if (subElement == "ANGLE") - entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle - else if (subElement == "X") - entry.mSubElement = 0; - else if (subElement == "Y") - entry.mSubElement = 1; - else if (subElement == "Z") - entry.mSubElement = 2; - else - ASSIMP_LOG_WARN("Unknown anim subelement <", subElement, ">. Ignoring"); - } else { - // no sub-element following, transformId is remaining string - entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1); - } - - std::string::size_type bracketPos = srcChannel.mTarget.find('('); - if (bracketPos != std::string::npos) { - entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1); - subElement.clear(); - subElement = srcChannel.mTarget.substr(bracketPos); - - if (subElement == "(0)(0)") - entry.mSubElement = 0; - else if (subElement == "(1)(0)") - entry.mSubElement = 1; - else if (subElement == "(2)(0)") - entry.mSubElement = 2; - else if (subElement == "(3)(0)") - entry.mSubElement = 3; - else if (subElement == "(0)(1)") - entry.mSubElement = 4; - else if (subElement == "(1)(1)") - entry.mSubElement = 5; - else if (subElement == "(2)(1)") - entry.mSubElement = 6; - else if (subElement == "(3)(1)") - entry.mSubElement = 7; - else if (subElement == "(0)(2)") - entry.mSubElement = 8; - else if (subElement == "(1)(2)") - entry.mSubElement = 9; - else if (subElement == "(2)(2)") - entry.mSubElement = 10; - else if (subElement == "(3)(2)") - entry.mSubElement = 11; - else if (subElement == "(0)(3)") - entry.mSubElement = 12; - else if (subElement == "(1)(3)") - entry.mSubElement = 13; - else if (subElement == "(2)(3)") - entry.mSubElement = 14; - else if (subElement == "(3)(3)") - entry.mSubElement = 15; - } - - // determine which transform step is affected by this channel - entry.mTransformIndex = SIZE_MAX; - for (size_t a = 0; a < srcNode->mTransforms.size(); ++a) - if (srcNode->mTransforms[a].mID == entry.mTransformId) - entry.mTransformIndex = a; - - if (entry.mTransformIndex == SIZE_MAX) { - if (entry.mTransformId.find("morph-weights") == std::string::npos) { - continue; - } - entry.mTargetId = entry.mTransformId; - entry.mTransformId = std::string(); - } - - entry.mChannel = &(*cit); - entries.push_back(entry); - } - - // if there's no channel affecting the current node, we skip it - if (entries.empty()) { - continue; - } - - // resolve the data pointers for all anim channels. Find the minimum time while we're at it - ai_real startTime = ai_real(1e20), endTime = ai_real(-1e20); - for (ChannelEntry & e : entries) { - e.mTimeAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceTimes); - e.mTimeData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mTimeAccessor->mSource); - e.mValueAccessor = &pParser.ResolveLibraryReference(pParser.mAccessorLibrary, e.mChannel->mSourceValues); - e.mValueData = &pParser.ResolveLibraryReference(pParser.mDataLibrary, e.mValueAccessor->mSource); - - // time count and value count must match - if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount) { - throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\"."); - } - - if (e.mTimeAccessor->mCount > 0) { - // find bounding times - startTime = std::min(startTime, ReadFloat(*e.mTimeAccessor, *e.mTimeData, 0, 0)); - endTime = std::max(endTime, ReadFloat(*e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount - 1, 0)); - } - } - - std::vector<aiMatrix4x4> resultTrafos; - if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) { - // create a local transformation chain of the node's transforms - std::vector<Collada::Transform> transforms = srcNode->mTransforms; - - // now for every unique point in time, find or interpolate the key values for that time - // and apply them to the transform chain. Then the node's present transformation can be calculated. - ai_real time = startTime; - while (1) { - for (ChannelEntry & e : entries) { - // find the keyframe behind the current point in time - size_t pos = 0; - ai_real postTime = 0.0; - while (1) { - if (pos >= e.mTimeAccessor->mCount) { - break; - } - postTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos, 0); - if (postTime >= time) { - break; - } - ++pos; - } - - pos = std::min(pos, e.mTimeAccessor->mCount - 1); - - // read values from there - ai_real temp[16]; - for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) { - temp[c] = ReadFloat(*e.mValueAccessor, *e.mValueData, pos, c); - } - - // if not exactly at the key time, interpolate with previous value set - if (postTime > time && pos > 0) { - ai_real preTime = ReadFloat(*e.mTimeAccessor, *e.mTimeData, pos - 1, 0); - ai_real factor = (time - postTime) / (preTime - postTime); - - for (size_t c = 0; c < e.mValueAccessor->mSize; ++c) { - ai_real v = ReadFloat(*e.mValueAccessor, *e.mValueData, pos - 1, c); - temp[c] += (v - temp[c]) * factor; - } - } - - // Apply values to current transformation - std::copy(temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); - } - - // Calculate resulting transformation - aiMatrix4x4 mat = pParser.CalculateResultTransform(transforms); - - // out of laziness: we store the time in matrix.d4 - mat.d4 = time; - resultTrafos.push_back(mat); - - // find next point in time to evaluate. That's the closest frame larger than the current in any channel - ai_real nextTime = ai_real(1e20); - for (ChannelEntry & channelElement : entries) { - // find the next time value larger than the current - size_t pos = 0; - while (pos < channelElement.mTimeAccessor->mCount) { - const ai_real t = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); - if (t > time) { - nextTime = std::min(nextTime, t); - break; - } - ++pos; - } - - // https://github.com/assimp/assimp/issues/458 - // Sub-sample axis-angle channels if the delta between two consecutive - // key-frame angles is >= 180 degrees. - if (transforms[channelElement.mTransformIndex].mType == TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) { - const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0); - const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0); - const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0); - const ai_real last_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos - 1, 0); - const ai_real last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time); - const ai_real delta = std::abs(cur_key_angle - last_eval_angle); - if (delta >= 180.0) { - const int subSampleCount = static_cast<int>(std::floor(delta / 90.0)); - if (cur_key_time != time) { - const ai_real nextSampleTime = time + (cur_key_time - time) / subSampleCount; - nextTime = std::min(nextTime, nextSampleTime); - } - } - } - } - - // no more keys on any channel after the current time -> we're done - if (nextTime > 1e19) { - break; - } - - // else construct next key-frame at this following time point - time = nextTime; - } - } - - // build an animation channel for the given node out of these trafo keys - if (!resultTrafos.empty()) { - aiNodeAnim *dstAnim = new aiNodeAnim; - dstAnim->mNodeName = nodeName; - dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size()); - dstAnim->mNumRotationKeys = static_cast<unsigned int>(resultTrafos.size()); - dstAnim->mNumScalingKeys = static_cast<unsigned int>(resultTrafos.size()); - dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; - dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; - dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; - - for (size_t a = 0; a < resultTrafos.size(); ++a) { - aiMatrix4x4 mat = resultTrafos[a]; - double time = double(mat.d4); // remember? time is stored in mat.d4 - mat.d4 = 1.0f; - - dstAnim->mPositionKeys[a].mTime = time * kMillisecondsFromSeconds; - dstAnim->mRotationKeys[a].mTime = time * kMillisecondsFromSeconds; - dstAnim->mScalingKeys[a].mTime = time * kMillisecondsFromSeconds; - mat.Decompose(dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); - } - - anims.push_back(dstAnim); - } else { - ASSIMP_LOG_WARN("Collada loader: found empty animation channel, ignored. Please check your exporter."); - } - - if (!entries.empty() && entries.front().mTimeAccessor->mCount > 0) { - std::vector<ChannelEntry> morphChannels; - for (ChannelEntry & e : entries) { - // skip non-transform types - if (e.mTargetId.empty()) { - continue; - } - - if (e.mTargetId.find("morph-weights") != std::string::npos) { - morphChannels.push_back(e); - } - } - if (!morphChannels.empty()) { - // either 1) morph weight animation count should contain morph target count channels - // or 2) one channel with morph target count arrays - // assume first - - aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim; - morphAnim->mName.Set(nodeName); - - std::vector<MorphTimeValues> morphTimeValues; - int morphAnimChannelIndex = 0; - for (ChannelEntry & e : morphChannels) { - std::string::size_type apos = e.mTargetId.find('('); - std::string::size_type bpos = e.mTargetId.find(')'); - - // If unknown way to specify weight -> ignore this animation - if (apos == std::string::npos || bpos == std::string::npos) { - continue; - } - - // weight target can be in format Weight_M_N, Weight_N, WeightN, or some other way - // we ignore the name and just assume the channels are in the right order - for (unsigned int i = 0; i < e.mTimeData->mValues.size(); i++) { - insertMorphTimeValue(morphTimeValues, e.mTimeData->mValues[i], e.mValueData->mValues[i], morphAnimChannelIndex); - } - - ++morphAnimChannelIndex; - } - - morphAnim->mNumKeys = static_cast<unsigned int>(morphTimeValues.size()); - morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys]; - for (unsigned int key = 0; key < morphAnim->mNumKeys; key++) { - morphAnim->mKeys[key].mNumValuesAndWeights = static_cast<unsigned int>(morphChannels.size()); - morphAnim->mKeys[key].mValues = new unsigned int[morphChannels.size()]; - morphAnim->mKeys[key].mWeights = new double[morphChannels.size()]; - - morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime * kMillisecondsFromSeconds; - for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); ++valueIndex) { - morphAnim->mKeys[key].mValues[valueIndex] = valueIndex; - morphAnim->mKeys[key].mWeights[valueIndex] = getWeightAtKey(morphTimeValues, key, valueIndex); - } - } - - morphAnims.push_back(morphAnim); - } - } - } - - if (!anims.empty() || !morphAnims.empty()) { - aiAnimation *anim = new aiAnimation; - anim->mName.Set(pName); - anim->mNumChannels = static_cast<unsigned int>(anims.size()); - if (anim->mNumChannels > 0) { - anim->mChannels = new aiNodeAnim *[anims.size()]; - std::copy(anims.begin(), anims.end(), anim->mChannels); - } - anim->mNumMorphMeshChannels = static_cast<unsigned int>(morphAnims.size()); - if (anim->mNumMorphMeshChannels > 0) { - anim->mMorphMeshChannels = new aiMeshMorphAnim *[anim->mNumMorphMeshChannels]; - std::copy(morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels); - } - anim->mDuration = 0.0f; - for (auto & a : anims) { - anim->mDuration = std::max(anim->mDuration, a->mPositionKeys[a->mNumPositionKeys - 1].mTime); - anim->mDuration = std::max(anim->mDuration, a->mRotationKeys[a->mNumRotationKeys - 1].mTime); - anim->mDuration = std::max(anim->mDuration, a->mScalingKeys[a->mNumScalingKeys - 1].mTime); - } - for (auto & morphAnim : morphAnims) { - anim->mDuration = std::max(anim->mDuration, morphAnim->mKeys[morphAnim->mNumKeys - 1].mTime); - } - anim->mTicksPerSecond = 1000.0; - mAnims.push_back(anim); - } -} - -// ------------------------------------------------------------------------------------------------ -// Add a texture to a material structure -void ColladaLoader::AddTexture(aiMaterial &mat, - const ColladaParser &pParser, - const Effect &effect, - const Sampler &sampler, - aiTextureType type, - unsigned int idx) { - // first of all, basic file name - const aiString name = FindFilenameForEffectTexture(pParser, effect, sampler.mName); - mat.AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, type, idx); - - // mapping mode - int map = aiTextureMapMode_Clamp; - if (sampler.mWrapU) { - map = aiTextureMapMode_Wrap; - } - if (sampler.mWrapU && sampler.mMirrorU) { - map = aiTextureMapMode_Mirror; - } - - mat.AddProperty(&map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx); - - map = aiTextureMapMode_Clamp; - if (sampler.mWrapV) { - map = aiTextureMapMode_Wrap; - } - if (sampler.mWrapV && sampler.mMirrorV) { - map = aiTextureMapMode_Mirror; - } - - mat.AddProperty(&map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx); - - // UV transformation - mat.AddProperty(&sampler.mTransform, 1, - _AI_MATKEY_UVTRANSFORM_BASE, type, idx); - - // Blend mode - mat.AddProperty((int *)&sampler.mOp, 1, - _AI_MATKEY_TEXBLEND_BASE, type, idx); - - // Blend factor - mat.AddProperty((ai_real *)&sampler.mWeighting, 1, - _AI_MATKEY_TEXBLEND_BASE, type, idx); - - // UV source index ... if we didn't resolve the mapping, it is actually just - // a guess but it works in most cases. We search for the frst occurrence of a - // number in the channel name. We assume it is the zero-based index into the - // UV channel array of all corresponding meshes. It could also be one-based - // for some exporters, but we won't care of it unless someone complains about. - if (sampler.mUVId != UINT_MAX) { - map = sampler.mUVId; - } else { - map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { - if (IsNumeric(*it)) { - map = strtoul10(&(*it)); - break; - } - } - if (-1 == map) { - ASSIMP_LOG_WARN("Collada: unable to determine UV channel for texture"); - map = 0; - } - } - mat.AddProperty(&map, 1, _AI_MATKEY_UVWSRC_BASE, type, idx); -} - -// ------------------------------------------------------------------------------------------------ -// Fills materials from the collada material definitions -void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pScene*/) { - for (auto &elem : newMats) { - aiMaterial &mat = (aiMaterial &)*elem.second; - Collada::Effect &effect = *elem.first; - - // resolve shading mode - int shadeMode; - if (effect.mFaceted) { - shadeMode = aiShadingMode_Flat; - } else { - switch (effect.mShadeType) { - case Collada::Shade_Constant: - shadeMode = aiShadingMode_NoShading; - break; - case Collada::Shade_Lambert: - shadeMode = aiShadingMode_Gouraud; - break; - case Collada::Shade_Blinn: - shadeMode = aiShadingMode_Blinn; - break; - case Collada::Shade_Phong: - shadeMode = aiShadingMode_Phong; - break; - - default: - ASSIMP_LOG_WARN("Collada: Unrecognized shading mode, using gouraud shading"); - shadeMode = aiShadingMode_Gouraud; - break; - } - } - mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_SHADING_MODEL); - - // double-sided? - shadeMode = effect.mDoubleSided; - mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_TWOSIDED); - - // wire-frame? - shadeMode = effect.mWireframe; - mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME); - - // add material colors - mat.AddProperty(&effect.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); - mat.AddProperty(&effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat.AddProperty(&effect.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - mat.AddProperty(&effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); - mat.AddProperty(&effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE); - - // scalar properties - mat.AddProperty(&effect.mShininess, 1, AI_MATKEY_SHININESS); - mat.AddProperty(&effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY); - mat.AddProperty(&effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); - - // transparency, a very hard one. seemingly not all files are following the - // specification here (1.0 transparency => completely opaque)... - // therefore, we let the opportunity for the user to manually invert - // the transparency if necessary and we add preliminary support for RGB_ZERO mode - if (effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) { - // handle RGB transparency completely, cf Collada specs 1.5.0 pages 249 and 304 - if (effect.mRGBTransparency) { - // use luminance as defined by ISO/CIE color standards (see ITU-R Recommendation BT.709-4) - effect.mTransparency *= (0.212671f * effect.mTransparent.r + - 0.715160f * effect.mTransparent.g + - 0.072169f * effect.mTransparent.b); - - effect.mTransparent.a = 1.f; - - mat.AddProperty(&effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT); - } else { - effect.mTransparency *= effect.mTransparent.a; - } - - if (effect.mInvertTransparency) { - effect.mTransparency = 1.f - effect.mTransparency; - } - - // Is the material finally transparent ? - if (effect.mHasTransparency || effect.mTransparency < 1.f) { - mat.AddProperty(&effect.mTransparency, 1, AI_MATKEY_OPACITY); - } - } - - // add textures, if given - if (!effect.mTexAmbient.mName.empty()) { - // It is merely a light-map - AddTexture(mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); - } - - if (!effect.mTexEmissive.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); - - if (!effect.mTexSpecular.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR); - - if (!effect.mTexDiffuse.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE); - - if (!effect.mTexBump.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS); - - if (!effect.mTexTransparent.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY); - - if (!effect.mTexReflective.mName.empty()) - AddTexture(mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION); - } -} - -// ------------------------------------------------------------------------------------------------ -// Constructs materials from the collada material definitions -void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) { - newMats.reserve(pParser.mMaterialLibrary.size()); - - for (ColladaParser::MaterialLibrary::const_iterator 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); - if (effIt == pParser.mEffectLibrary.end()) - continue; - Effect &effect = effIt->second; - - // create material - aiMaterial *mat = new aiMaterial; - aiString name(material.mName.empty() ? matIt->first : material.mName); - mat->AddProperty(&name, AI_MATKEY_NAME); - - // store the material - mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.emplace_back(&effect, mat); - } - // ScenePreprocessor generates a default material automatically if none is there. - // All further code here in this loader works well without a valid material so - // we can safely let it to ScenePreprocessor. -} - -// ------------------------------------------------------------------------------------------------ -// Resolves the texture name for the given effect texture entry and loads the texture data -aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParser, - const Effect &pEffect, const std::string &pName) { - aiString result; - - // recurse through the param references until we end up at an image - std::string name = pName; - while (1) { - // the given string is a param entry. Find it - Effect::ParamLibrary::const_iterator 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; - - // else recurse on - name = it->second.mReference; - } - - // find the image referred by this name in the image library of the scene - ColladaParser::ImageLibrary::const_iterator 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, "\"."); - - //set default texture file name - result.Set(name + ".jpg"); - ColladaParser::UriDecodePath(result); - return result; - } - - // if this is an embedded texture image setup an aiTexture for it - if (!imIt->second.mImageData.empty()) { - aiTexture *tex = new aiTexture(); - - // Store embedded texture name reference - tex->mFilename.Set(imIt->second.mFileName.c_str()); - result.Set(imIt->second.mFileName); - - // setup format hint - if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) { - ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters"); - } - strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3); - - // and copy texture data - tex->mHeight = 0; - tex->mWidth = static_cast<unsigned int>(imIt->second.mImageData.size()); - tex->pcData = (aiTexel *)new char[tex->mWidth]; - memcpy(tex->pcData, &imIt->second.mImageData[0], tex->mWidth); - - // and add this texture to the list - mTextures.push_back(tex); - return result; - } - - if (imIt->second.mFileName.empty()) { - throw DeadlyImportError("Collada: Invalid texture, no data or file reference given"); - } - - result.Set(imIt->second.mFileName); - - 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 { - size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset; - ai_assert(pos < pData.mStrings.size()); - return pData.mStrings[pos]; -} - -// ------------------------------------------------------------------------------------------------ -// Collects all nodes into the given array -void ColladaLoader::CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const { - poNodes.push_back(pNode); - for (size_t a = 0; a < pNode->mNumChildren; ++a) { - CollectNodes(pNode->mChildren[a], poNodes); - } -} - -// ------------------------------------------------------------------------------------------------ -// Finds a node in the collada scene by the given name -const Node *ColladaLoader::FindNode(const Node *pNode, const std::string &pName) const { - if (pNode->mName == pName || pNode->mID == pName) - return pNode; - - for (auto a : pNode->mChildren) { - const Collada::Node *node = FindNode(a, pName); - if (node) { - return node; - } - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// Finds a node in the collada scene by the given SID -const Node *ColladaLoader::FindNodeBySID(const Node *pNode, const std::string &pSID) const { - if (nullptr == pNode) { - return nullptr; - } - - if (pNode->mSID == pSID) { - return pNode; - } - - for (auto a : pNode->mChildren) { - const Collada::Node *node = FindNodeBySID(a, pSID); - if (node) { - return node; - } - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// Finds a proper unique name for a node derived from the collada-node's properties. -// The name must be unique for proper node-bone association. -std::string ColladaLoader::FindNameForNode(const Node *pNode) { - // If explicitly requested, just use the collada name. - if (useColladaName) { - if (!pNode->mName.empty()) { - return pNode->mName; - } else { - return format() << "$ColladaAutoName$_" << mNodeNameCounter++; - } - } else { - // Now setup the name of the assimp node. The collada name might not be - // unique, so we use the collada ID. - if (!pNode->mID.empty()) - return pNode->mID; - else if (!pNode->mSID.empty()) - return pNode->mSID; - else { - // No need to worry. Unnamed nodes are no problem at all, except - // if cameras or lights need to be assigned to them. - return format() << "$ColladaAutoName$_" << mNodeNameCounter++; - } - } -} - -} // Namespace Assimp - -#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Collada/ColladaLoader.h b/libs/assimp/code/AssetLib/Collada/ColladaLoader.h deleted file mode 100644 index 246abed..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaLoader.h +++ /dev/null @@ -1,249 +0,0 @@ -/** Defines the collada loader class */ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#ifndef AI_COLLADALOADER_H_INC -#define AI_COLLADALOADER_H_INC - -#include "ColladaParser.h" -#include <assimp/BaseImporter.h> - -struct aiNode; -struct aiCamera; -struct aiLight; -struct aiTexture; -struct aiAnimation; - -namespace Assimp { - -struct ColladaMeshIndex { - std::string mMeshID; - size_t mSubMesh; - std::string mMaterial; - ColladaMeshIndex(const std::string &pMeshID, size_t pSubMesh, const std::string &pMaterial) : - mMeshID(pMeshID), mSubMesh(pSubMesh), mMaterial(pMaterial) { - ai_assert(!pMeshID.empty()); - } - - bool operator<(const ColladaMeshIndex &p) const { - if (mMeshID == p.mMeshID) { - if (mSubMesh == p.mSubMesh) - return mMaterial < p.mMaterial; - else - return mSubMesh < p.mSubMesh; - } else { - return mMeshID < p.mMeshID; - } - } -}; - -/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing - * more useless stuff, so I limited the data to what I think is useful for games. -*/ -class ColladaLoader : public BaseImporter { -public: - /// The class constructor. - ColladaLoader(); - - /// The class destructor. - ~ColladaLoader() override; - - /// Returns whether the class can handle the format of the given file. - /// @see BaseImporter::CanRead() for more details. - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; - -protected: - /// See #BaseImporter::GetInfo for the details - const aiImporterDesc *GetInfo() const override; - - /// See #BaseImporter::SetupProperties for the details - void SetupProperties(const Importer *pImp) override; - - /// See #BaseImporter::InternReadFile for the details - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; - - /** Recursively constructs a scene node for the given parser node and returns it. */ - aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode); - - /** Resolve node instances */ - void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode, - std::vector<const Collada::Node *> &resolved); - - /** Builds meshes for the given node and references them */ - void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode, - aiNode *pTarget); - - aiMesh *findMesh(const std::string &meshid); - - /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */ - aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh, - const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace); - - /** Builds cameras for the given node and references them */ - void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode, - aiNode *pTarget); - - /** Builds lights for the given node and references them */ - void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode, - aiNode *pTarget); - - /** Stores all meshes in the given scene */ - void StoreSceneMeshes(aiScene *pScene); - - /** Stores all materials in the given scene */ - void StoreSceneMaterials(aiScene *pScene); - - /** Stores all lights in the given scene */ - void StoreSceneLights(aiScene *pScene); - - /** Stores all cameras in the given scene */ - void StoreSceneCameras(aiScene *pScene); - - /** Stores all textures in the given scene */ - void StoreSceneTextures(aiScene *pScene); - - /** Stores all animations - * @param pScene target scene to store the anims - */ - void StoreAnimations(aiScene *pScene, const ColladaParser &pParser); - - /** Stores all animations for the given source anim and its nested child animations - * @param pScene target scene to store the anims - * @param pSrcAnim the source animation to process - * @param pPrefix Prefix to the name in case of nested animations - */ - void StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pPrefix); - - /** Constructs the animation for the given source anim */ - void CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pName); - - /** Constructs materials from the collada material definitions */ - void BuildMaterials(ColladaParser &pParser, aiScene *pScene); - - /** 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, - const Collada::Sampler &sampler, - aiTextureType type, unsigned int idx = 0); - - /** Resolves the texture name for the given effect texture entry */ - 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; - - /** Recursively collects all nodes into the given array */ - void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const; - - /** Finds a node in the collada scene by the given name */ - const Collada::Node *FindNode(const Collada::Node *pNode, const std::string &pName) const; - /** Finds a node in the collada scene by the given SID */ - const Collada::Node *FindNodeBySID(const Collada::Node *pNode, const std::string &pSID) const; - - /** Finds a proper name for a node derived from the collada-node's properties */ - std::string FindNameForNode(const Collada::Node *pNode); - -protected: - /** Filename, for a verbose error message */ - std::string mFileName; - - /** Which mesh-material compound was stored under which mesh ID */ - std::map<ColladaMeshIndex, size_t> mMeshIndexByID; - - /** Which material was stored under which index in the scene */ - std::map<std::string, size_t> mMaterialIndexByName; - - /** Accumulated meshes for the target scene */ - std::vector<aiMesh *> mMeshes; - - /** Accumulated morph target meshes */ - std::vector<aiMesh *> mTargetMeshes; - - /** Temporary material list */ - std::vector<std::pair<Collada::Effect *, aiMaterial *>> newMats; - - /** Temporary camera list */ - std::vector<aiCamera *> mCameras; - - /** Temporary light list */ - std::vector<aiLight *> mLights; - - /** Temporary texture list */ - std::vector<aiTexture *> mTextures; - - /** Accumulated animations for the target scene */ - std::vector<aiAnimation *> mAnims; - - bool noSkeletonMesh; - bool ignoreUpDirection; - bool useColladaName; - - /** Used by FindNameForNode() to generate unique node names */ - unsigned int mNodeNameCounter; -}; - -} // end of namespace Assimp - -#endif // AI_COLLADALOADER_H_INC diff --git a/libs/assimp/code/AssetLib/Collada/ColladaParser.cpp b/libs/assimp/code/AssetLib/Collada/ColladaParser.cpp deleted file mode 100644 index 922d1f6..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaParser.cpp +++ /dev/null @@ -1,2402 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ColladaParser.cpp - * @brief Implementation of the Collada parser helper - */ - -#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER - -#include "ColladaParser.h" -#include <assimp/ParsingUtils.h> -#include <assimp/StringUtils.h> -#include <assimp/ZipArchiveIOSystem.h> -#include <assimp/commonMetaData.h> -#include <assimp/fast_atof.h> -#include <assimp/light.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/IOSystem.hpp> -#include <memory> - -using namespace Assimp; -using namespace Assimp::Collada; -using namespace Assimp::Formatter; - -static void ReportWarning(const char *msg, ...) { - ai_assert(nullptr != msg); - - va_list args; - va_start(args, msg); - - char szBuffer[3000]; - const int iLen = vsprintf(szBuffer, msg, args); - ai_assert(iLen > 0); - - va_end(args); - 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) { - found_index = i; - return true; - } - } - found_index = std::numeric_limits<size_t>::max(); - - return false; -} - -static void readUrlAttribute(XmlNode &node, std::string &url) { - url.clear(); - if (!XmlParser::getStdStrAttribute(node, "url", url)) { - return; - } - if (url[0] != '#') { - throw DeadlyImportError("Unknown reference format"); - } - url = url.c_str() + 1; -} - -// ------------------------------------------------------------------------------------------------ -// 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) { - if (nullptr == pIOHandler) { - throw DeadlyImportError("IOSystem is nullptr."); - } - - std::unique_ptr<IOStream> daefile; - std::unique_ptr<ZipArchiveIOSystem> zip_archive; - - // Determine type - std::string extension = BaseImporter::GetExtension(pFile); - if (extension != "dae") { - zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile)); - } - - if (zip_archive && zip_archive->isOpen()) { - std::string dae_filename = ReadZaeManifest(*zip_archive); - - if (dae_filename.empty()) { - throw DeadlyImportError("Invalid ZAE"); - } - - 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.get() == nullptr) { - throw DeadlyImportError("Failed to open file '", pFile, "'."); - } - } - - // generate a XML reader for it - if (!mXmlParser.parse(daefile.get())) { - throw DeadlyImportError("Unable to read file, malformed XML"); - } - // start reading - XmlNode node = mXmlParser.getRootNode(); - XmlNode colladaNode = node.child("COLLADA"); - if (colladaNode.empty()) { - return; - } - - // Read content and embedded textures - ReadContents(colladaNode); - if (zip_archive && zip_archive->isOpen()) { - ReadEmbeddedTextures(*zip_archive); - } -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ColladaParser::~ColladaParser() { - for (auto &it : mNodeLibrary) { - delete it.second; - } - for (auto &it : mMeshLibrary) { - delete it.second; - } -} - -// ------------------------------------------------------------------------------------------------ -// Read a ZAE manifest and return the filename to attempt to open -std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { - // Open the manifest - std::unique_ptr<IOStream> manifestfile(zip_archive.Open("manifest.xml")); - if (manifestfile == nullptr) { - // No manifest, hope there is only one .DAE inside - std::vector<std::string> file_list; - zip_archive.getFileListExtension(file_list, "dae"); - - if (file_list.empty()) { - return std::string(); - } - - return file_list.front(); - } - XmlParser manifestParser; - if (!manifestParser.parse(manifestfile.get())) { - return std::string(); - } - - XmlNode root = manifestParser.getRootNode(); - const std::string &name = root.name(); - if (name != "dae_root") { - root = *manifestParser.findNode("dae_root"); - if (nullptr == root) { - return std::string(); - } - std::string v; - XmlParser::getValueAsString(root, v); - aiString ai_str(v); - UriDecodePath(ai_str); - return std::string(ai_str.C_Str()); - } - - return std::string(); -} - -// ------------------------------------------------------------------------------------------------ -// Convert a path read from a collada file to the usual representation -void ColladaParser::UriDecodePath(aiString &ss) { - // TODO: collada spec, p 22. Handle URI correctly. - // For the moment we're just stripping the file:// away to make it work. - // Windows doesn't seem to be able to find stuff like - // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' - if (0 == strncmp(ss.data, "file://", 7)) { - ss.length -= 7; - memmove(ss.data, ss.data + 7, ss.length); - ss.data[ss.length] = '\0'; - } - - // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... - // I need to filter it without destroying linux paths starting with "/somewhere" - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { - --ss.length; - ::memmove(ss.data, ss.data + 1, ss.length); - ss.data[ss.length] = 0; - } - - // find and convert all %xy special chars - char *out = ss.data; - for (const char *it = ss.data; it != ss.data + ss.length; /**/) { - if (*it == '%' && (it + 3) < ss.data + ss.length) { - // separate the number to avoid dragging in chars from behind into the parsing - char mychar[3] = { it[1], it[2], 0 }; - size_t nbr = strtoul16(mychar); - it += 3; - *out++ = (char)(nbr & 0xFF); - } else { - *out++ = *it++; - } - } - - // adjust length and terminator of the shortened string - *out = 0; - ai_assert(out > ss.data); - ss.length = static_cast<ai_uint32>(out - ss.data); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the contents of the file -void ColladaParser::ReadContents(XmlNode &node) { - const std::string name = node.name(); - if (name == "COLLADA") { - std::string version; - if (XmlParser::getStdStrAttribute(node, "version", version)) { - aiString v; - v.Set(version.c_str()); - mAssetMetaData.emplace(AI_METADATA_SOURCE_FORMAT_VERSION, v); - if (!::strncmp(version.c_str(), "1.5", 3)) { - mFormat = FV_1_5_n; - ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n"); - } else if (!::strncmp(version.c_str(), "1.4", 3)) { - mFormat = FV_1_4_n; - ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n"); - } else if (!::strncmp(version.c_str(), "1.3", 3)) { - mFormat = FV_1_3_n; - ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n"); - } - } - ReadStructure(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") { - ReadAssetInfo(currentNode); - } else if (currentName == "library_animations") { - ReadAnimationLibrary(currentNode); - } else if (currentName == "library_animation_clips") { - ReadAnimationClipLibrary(currentNode); - } else if (currentName == "library_controllers") { - ReadControllerLibrary(currentNode); - } else if (currentName == "library_images") { - ReadImageLibrary(currentNode); - } else if (currentName == "library_materials") { - ReadMaterialLibrary(currentNode); - } else if (currentName == "library_effects") { - ReadEffectLibrary(currentNode); - } else if (currentName == "library_geometries") { - ReadGeometryLibrary(currentNode); - } else if (currentName == "library_visual_scenes") { - ReadSceneLibrary(currentNode); - } else if (currentName == "library_lights") { - ReadLightLibrary(currentNode); - } else if (currentName == "library_cameras") { - ReadCameraLibrary(currentNode); - } else if (currentName == "library_nodes") { - ReadSceneNode(currentNode, nullptr); /* some hacking to reuse this piece of code */ - } else if (currentName == "scene") { - ReadScene(currentNode); - } - } - - PostProcessRootAnimations(); - PostProcessControllers(); -} - -// ------------------------------------------------------------------------------------------------ -// Reads asset information such as coordinate system information and legal blah -void ColladaParser::ReadAssetInfo(XmlNode &node) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "unit") { - mUnitSize = 1.f; - std::string tUnitSizeString; - if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) { - try { - fast_atoreal_move<ai_real>(tUnitSizeString.data(), mUnitSize); - } catch (const DeadlyImportError& die) { - std::string warning("Collada: Failed to parse meter parameter to real number. Exception:\n"); - warning.append(die.what()); - ASSIMP_LOG_WARN(warning.data()); - } - } - } else if (currentName == "up_axis") { - std::string v; - if (!XmlParser::getValueAsString(currentNode, v)) { - continue; - } - if (v == "X_UP") { - mUpDirection = UP_X; - } else if (v == "Z_UP") { - mUpDirection = UP_Z; - } else { - mUpDirection = UP_Y; - } - } else if (currentName == "contributor") { - for (XmlNode currentChildNode : currentNode.children()) { - ReadMetaDataItem(currentChildNode, mAssetMetaData); - } - } else { - ReadMetaDataItem(currentNode, mAssetMetaData); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// 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) { - if (node.empty()) { - return; - } - - std::string animName; - if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - if (!XmlParser::getStdStrAttribute(node, "id", animName)) { - animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size()); - } - } - - std::pair<std::string, std::vector<std::string>> clip; - clip.first = animName; - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "instance_animation") { - std::string url; - readUrlAttribute(currentNode, url); - clip.second.push_back(url); - } - - if (clip.second.size() > 0) { - mAnimationClipLibrary.push_back(clip); - } - } -} - -void ColladaParser::PostProcessControllers() { - std::string meshId; - for (auto &it : mControllerLibrary) { - meshId = it.second.mMeshId; - if (meshId.empty()) { - continue; - } - - ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId); - while (findItr != mControllerLibrary.end()) { - meshId = findItr->second.mMeshId; - findItr = mControllerLibrary.find(meshId); - } - - it.second.mMeshId = meshId; - } -} - -// ------------------------------------------------------------------------------------------------ -// Re-build animations from animation clip library, if present, otherwise combine single-channel animations -void ColladaParser::PostProcessRootAnimations() { - if (mAnimationClipLibrary.empty()) { - mAnims.CombineSingleChannelAnimations(); - return; - } - - Animation temp; - for (auto &it : mAnimationClipLibrary) { - std::string clipName = it.first; - - Animation *clip = new Animation(); - clip->mName = clipName; - - temp.mSubAnims.push_back(clip); - - for (const std::string &animationID : it.second) { - AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); - - if (animation != mAnimationLibrary.end()) { - Animation *pSourceAnimation = animation->second; - pSourceAnimation->CollectChannelsRecursively(clip->mChannels); - } - } - } - - mAnims = temp; - - // Ensure no double deletes. - temp.mSubAnims.clear(); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the animation library -void ColladaParser::ReadAnimationLibrary(XmlNode &node) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "animation") { - ReadAnimation(currentNode, &mAnims); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an animation into the given parent structure -void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { - if (node.empty()) { - return; - } - - // an <animation> element may be a container for grouping sub-elements or an animation channel - // this is the channel collection by ID, in case it has channels - using ChannelMap = std::map<std::string, AnimationChannel>; - ChannelMap channels; - // this is the anim container in case we're a container - Animation *anim = nullptr; - - // optional name given as an attribute - std::string animName; - if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - animName = "animation"; - } - - std::string animID; - pugi::xml_attribute idAttr = node.attribute("id"); - if (idAttr) { - animID = idAttr.as_string(); - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "animation") { - if (!anim) { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back(anim); - } - - // recurse into the sub-element - ReadAnimation(currentNode, anim); - } else if (currentName == "source") { - ReadSource(currentNode); - } else if (currentName == "sampler") { - 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; - ReadAnimationSampler(currentNode, newChannel->second); - } - } else if (currentName == "channel") { - std::string source_name, target; - XmlParser::getStdStrAttribute(currentNode, "source", source_name); - XmlParser::getStdStrAttribute(currentNode, "target", target); - if (source_name[0] == '#') { - source_name = source_name.substr(1, source_name.size() - 1); - } - ChannelMap::iterator cit = channels.find(source_name); - if (cit != channels.end()) { - cit->second.mTarget = target; - } - } - } - - // it turned out to have channels - add them - if (!channels.empty()) { - if (nullptr == anim) { - anim = new Animation; - anim->mName = animName; - pParent->mSubAnims.push_back(anim); - } - - for (const auto &channel : channels) { - anim->mChannels.push_back(channel.second); - } - - if (idAttr) { - mAnimationLibrary[animID] = anim; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// 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) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName != "controller") { - continue; - } - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { - mControllerLibrary[id] = Controller(); - ReadController(currentNode, mControllerLibrary[id]); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a controller into the given mesh structure -void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controller) { - // initial values - controller.mType = Skin; - controller.mMethod = Normalized; - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "morph") { - controller.mType = Morph; - controller.mMeshId = currentNode.attribute("source").as_string(); - int methodIndex = currentNode.attribute("method").as_int(); - if (methodIndex > 0) { - std::string method; - XmlParser::getValueAsString(currentNode, method); - - if (method == "RELATIVE") { - controller.mMethod = Relative; - } - } - } else if (currentName == "skin") { - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { - controller.mMeshId = id.substr(1, id.size() - 1); - } - } else if (currentName == "bind_shape_matrix") { - std::string v; - XmlParser::getValueAsString(currentNode, v); - const char *content = v.c_str(); - for (unsigned int a = 0; a < 16; a++) { - SkipSpacesAndLineEnd(&content); - // read a number - content = fast_atoreal_move<ai_real>(content, controller.mBindShapeMatrix[a]); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); - } - } else if (currentName == "source") { - ReadSource(currentNode); - } else if (currentName == "joints") { - ReadControllerJoints(currentNode, controller); - } else if (currentName == "vertex_weights") { - ReadControllerWeights(currentNode, controller); - } else if (currentName == "targets") { - for (XmlNode currentChildNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - const std::string ¤tChildName = currentChildNode.name(); - if (currentChildName == "input") { - const char *semantics = currentChildNode.attribute("semantic").as_string(); - const char *source = currentChildNode.attribute("source").as_string(); - if (strcmp(semantics, "MORPH_TARGET") == 0) { - controller.mMorphTarget = source + 1; - } else if (strcmp(semantics, "MORPH_WEIGHT") == 0) { - controller.mMorphWeight = source + 1; - } - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// 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 <joints> data <input> 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 <joints> data <input> 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 <vertex_weights> data <input> 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 <vertex_weights> data <input> element"); - } - } else if (currentName == "vcount" && vertexCount > 0) { - const char *text = currentNode.text().as_string(); - size_t numWeights = 0; - for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) { - if (*text == 0) { - throw DeadlyImportError("Out of data while reading <vcount>"); - } - - *it = strtoul10(text, &text); - numWeights += *it; - SkipSpacesAndLineEnd(&text); - } - // 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(); - for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) { - if (text == 0) { - throw DeadlyImportError("Out of data while reading <vertex_weights>"); - } - it->first = strtoul10(text, &text); - SkipSpacesAndLineEnd(&text); - if (*text == 0) { - throw DeadlyImportError("Out of data while reading <vertex_weights>"); - } - it->second = strtoul10(text, &text); - SkipSpacesAndLineEnd(&text); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the image library contents -void ColladaParser::ReadImageLibrary(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)) { - mImageLibrary[id] = Image(); - // read on from there - ReadImage(currentNode, mImageLibrary[id]); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an image entry into the given image -void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == "image") { - // Ignore - continue; - } else if (currentName == "init_from") { - if (mFormat == FV_1_4_n) { - // FIX: C4D exporter writes empty <init_from/> tags - if (!currentNode.empty()) { - // element content is filename - hopefully - const char *sz = currentNode.text().as_string(); - if (nullptr != sz) { - aiString filepath(sz); - UriDecodePath(filepath); - pImage.mFileName = filepath.C_Str(); - } - } - if (!pImage.mFileName.length()) { - pImage.mFileName = "unknown_texture"; - } - } - } else if (mFormat == FV_1_5_n) { - std::string value; - XmlNode refChild = currentNode.child("ref"); - XmlNode hexChild = currentNode.child("hex"); - if (refChild) { - // element content is filename - hopefully - if (XmlParser::getValueAsString(refChild, value)) { - aiString filepath(value); - UriDecodePath(filepath); - pImage.mFileName = filepath.C_Str(); - } - } else if (hexChild && !pImage.mFileName.length()) { - // embedded image. get format - pImage.mEmbeddedFormat = hexChild.attribute("format").as_string(); - if (pImage.mEmbeddedFormat.empty()) { - ASSIMP_LOG_WARN("Collada: Unknown image file format"); - } - - XmlParser::getValueAsString(hexChild, value); - const char *data = value.c_str(); - // hexadecimal-encoded binary octets. First of all, find the - // required buffer size to reserve enough storage. - const char *cur = data; - while (!IsSpaceOrNewLine(*cur)) { - ++cur; - } - - const unsigned int size = (unsigned int)(cur - data) * 2; - pImage.mImageData.resize(size); - for (unsigned int i = 0; i < size; ++i) { - pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1)); - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the material library -void ColladaParser::ReadMaterialLibrary(XmlNode &node) { - std::map<std::string, int> names; - for (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<std::string, int>::iterator it = names.find(name); - if (it != names.end()) { - std::ostringstream strStream; - strStream << ++it->second; - name.append(" " + strStream.str()); - } else { - names[name] = 0; - } - - mMaterialLibrary[id].mName = name; - } - - ReadMaterial(currentNode, mMaterialLibrary[id]); - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the light library -void ColladaParser::ReadLightLibrary(XmlNode &node) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "light") { - std::string id; - if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { - ReadLight(currentNode, mLightLibrary[id] = Light()); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the camera library -void ColladaParser::ReadCameraLibrary(XmlNode &node) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "camera") { - std::string id; - if (!XmlParser::getStdStrAttribute(currentNode, "id", id)) { - continue; - } - - // create an entry and store it in the library under its ID - Camera &cam = mCameraLibrary[id]; - std::string name; - if (!XmlParser::getStdStrAttribute(currentNode, "name", name)) { - continue; - } - if (!name.empty()) { - cam.mName = name; - } - ReadCamera(currentNode, cam); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// 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(); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.r); - SkipSpacesAndLineEnd(&content); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.g); - SkipSpacesAndLineEnd(&content); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b); - SkipSpacesAndLineEnd(&content); - } else if (currentName == "constant_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttConstant); - } else if (currentName == "linear_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttLinear); - } else if (currentName == "quadratic_attenuation") { - XmlParser::getValueAsFloat(currentNode, pLight.mAttQuadratic); - } else if (currentName == "falloff_angle") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle); - } else if (currentName == "falloff_exponent") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffExponent); - } - // FCOLLADA extensions - // ------------------------------------------------------- - else if (currentName == "outer_cone") { - XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle); - } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone - XmlParser::getValueAsFloat(currentNode, pLight.mPenumbraAngle); - } else if (currentName == "intensity") { - XmlParser::getValueAsFloat(currentNode, pLight.mIntensity); - } - else if (currentName == "falloff") { - XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle); - } else if (currentName == "hotspot_beam") { - XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle); - } - // OpenCOLLADA extensions - // ------------------------------------------------------- - else if (currentName == "decay_falloff") { - XmlParser::getValueAsFloat(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::getValueAsFloat(currentNode, camera.mHorFov); - } else if (currentName == "yfov" || currentName == "ymag") { - XmlParser::getValueAsFloat(currentNode, camera.mVerFov); - } else if (currentName == "aspect_ratio") { - XmlParser::getValueAsFloat(currentNode, camera.mAspect); - } else if (currentName == "znear") { - XmlParser::getValueAsFloat(currentNode, camera.mZNear); - } else if (currentName == "zfar") { - XmlParser::getValueAsFloat(currentNode, camera.mZFar); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the effect library -void ColladaParser::ReadEffectLibrary(XmlNode &node) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "effect") { - // read ID. Do I have to repeat my ranting about "optional" attributes? - std::string id; - XmlParser::getStdStrAttribute(currentNode, "id", id); - - // create an entry and store it in the library under its ID - mEffectLibrary[id] = Effect(); - - // read on from there - ReadEffect(currentNode, mEffectLibrary[id]); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry into the given effect -void ColladaParser::ReadEffect(XmlNode &node, Collada::Effect &pEffect) { - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "profile_COMMON") { - ReadEffectProfileCommon(currentNode, pEffect); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an COMMON effect profile -void ColladaParser::ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect) { - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string currentName = currentNode.name(); - if (currentName == "newparam") { - // save ID - std::string sid = currentNode.attribute("sid").as_string(); - pEffect.mParams[sid] = EffectParam(); - ReadEffectParam(currentNode, pEffect.mParams[sid]); - } else if (currentName == "technique" || currentName == "extra") { - // just syntactic sugar - } else if (mFormat == FV_1_4_n && currentName == "image") { - // read ID. Another entry which is "optional" by design but obligatory in reality - std::string id = currentNode.attribute("id").as_string(); - - // create an entry and store it in the library under its ID - mImageLibrary[id] = Image(); - - // read on from there - ReadImage(currentNode, mImageLibrary[id]); - } else if (currentName == "phong") - pEffect.mShadeType = Shade_Phong; - else if (currentName == "constant") - pEffect.mShadeType = Shade_Constant; - else if (currentName == "lambert") - pEffect.mShadeType = Shade_Lambert; - else if (currentName == "blinn") - pEffect.mShadeType = Shade_Blinn; - - /* Color + texture properties */ - else if (currentName == "emission") - ReadEffectColor(currentNode, pEffect.mEmissive, pEffect.mTexEmissive); - else if (currentName == "ambient") - ReadEffectColor(currentNode, pEffect.mAmbient, pEffect.mTexAmbient); - else if (currentName == "diffuse") - ReadEffectColor(currentNode, pEffect.mDiffuse, pEffect.mTexDiffuse); - else if (currentName == "specular") - ReadEffectColor(currentNode, pEffect.mSpecular, pEffect.mTexSpecular); - else if (currentName == "reflective") { - ReadEffectColor(currentNode, pEffect.mReflective, pEffect.mTexReflective); - } else if (currentName == "transparent") { - pEffect.mHasTransparency = true; - const char *opaque = currentNode.attribute("opaque").as_string(); - //const char *opaque = mReader->getAttributeValueSafe("opaque"); - - if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) { - pEffect.mRGBTransparency = true; - } - - // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure... - if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) { - pEffect.mInvertTransparency = true; - } - - ReadEffectColor(currentNode, pEffect.mTransparent, pEffect.mTexTransparent); - } else if (currentName == "shininess") - ReadEffectFloat(currentNode, pEffect.mShininess); - else if (currentName == "reflectivity") - ReadEffectFloat(currentNode, pEffect.mReflectivity); - - /* Single scalar properties */ - else if (currentName == "transparency") - ReadEffectFloat(currentNode, pEffect.mTransparency); - else if (currentName == "index_of_refraction") - ReadEffectFloat(currentNode, pEffect.mRefractIndex); - - // GOOGLEEARTH/OKINO extensions - // ------------------------------------------------------- - else if (currentName == "double_sided") - XmlParser::getValueAsBool(currentNode, pEffect.mDoubleSided); - - // FCOLLADA extensions - // ------------------------------------------------------- - else if (currentName == "bump") { - aiColor4D dummy; - ReadEffectColor(currentNode, dummy, pEffect.mTexBump); - } - - // MAX3D extensions - // ------------------------------------------------------- - else if (currentName == "wireframe") { - XmlParser::getValueAsBool(currentNode, pEffect.mWireframe); - } else if (currentName == "faceted") { - XmlParser::getValueAsBool(currentNode, pEffect.mFaceted); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Read texture wrapping + UV transform settings from a profile==Maya chunk -void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) { - if (node.empty()) { - return; - } - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - // MAYA extensions - // ------------------------------------------------------- - if (currentName == "wrapU") { - XmlParser::getValueAsBool(currentNode, out.mWrapU); - } else if (currentName == "wrapV") { - XmlParser::getValueAsBool(currentNode, out.mWrapV); - } else if (currentName == "mirrorU") { - XmlParser::getValueAsBool(currentNode, out.mMirrorU); - } else if (currentName == "mirrorV") { - XmlParser::getValueAsBool(currentNode, out.mMirrorV); - } else if (currentName == "repeatU") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.x); - } else if (currentName == "repeatV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.y); - } else if (currentName == "offsetU") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.x); - } else if (currentName == "offsetV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.y); - } else if (currentName == "rotateUV") { - XmlParser::getValueAsFloat(currentNode, out.mTransform.mRotation); - } else if (currentName == "blend_mode") { - std::string v; - XmlParser::getValueAsString(currentNode, v); - const char *sz = v.c_str(); - // http://www.feelingsoftware.com/content/view/55/72/lang,en/ - // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE - if (0 == ASSIMP_strincmp(sz, "ADD", 3)) - out.mOp = aiTextureOp_Add; - else if (0 == ASSIMP_strincmp(sz, "SUBTRACT", 8)) - out.mOp = aiTextureOp_Subtract; - else if (0 == ASSIMP_strincmp(sz, "MULTIPLY", 8)) - out.mOp = aiTextureOp_Multiply; - else { - ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode"); - } - } - // OKINO extensions - // ------------------------------------------------------- - else if (currentName == "weighting") { - XmlParser::getValueAsFloat(currentNode, out.mWeighting); - } else if (currentName == "mix_with_previous_layer") { - XmlParser::getValueAsFloat(currentNode, out.mMixWithPrevious); - } - // MAX3D extensions - // ------------------------------------------------------- - else if (currentName == "amount") { - XmlParser::getValueAsFloat(currentNode, out.mWeighting); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a color or a texture defining that color -void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &pSampler) { - if (node.empty()) { - return; - } - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "color") { - // text content contains 4 floats - std::string v; - XmlParser::getValueAsString(currentNode, v); - const char *content = v.c_str(); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.r); - SkipSpacesAndLineEnd(&content); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.g); - SkipSpacesAndLineEnd(&content); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.b); - SkipSpacesAndLineEnd(&content); - - content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.a); - SkipSpacesAndLineEnd(&content); - } else if (currentName == "texture") { - // get name of source texture/sampler - XmlParser::getStdStrAttribute(currentNode, "texture", pSampler.mName); - - // get name of UV source channel. Specification demands it to be there, but some exporters - // don't write it. It will be the default UV channel in case it's missing. - XmlParser::getStdStrAttribute(currentNode, "texcoord", pSampler.mUVChannel); - - // as we've read texture, the color needs to be 1,1,1,1 - pColor = aiColor4D(1.f, 1.f, 1.f, 1.f); - } else if (currentName == "technique") { - std::string profile; - XmlParser::getStdStrAttribute(currentNode, "profile", profile); - - // Some extensions are quite useful ... ReadSamplerProperties processes - // several extensions in MAYA, OKINO and MAX3D profiles. - if (!::strcmp(profile.c_str(), "MAYA") || !::strcmp(profile.c_str(), "MAX3D") || !::strcmp(profile.c_str(), "OKINO")) { - // get more information on this sampler - ReadSamplerProperties(currentNode, pSampler); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect entry containing a float -void ColladaParser::ReadEffectFloat(XmlNode &node, ai_real &pFloat) { - pFloat = 0.f; - XmlNode floatNode = node.child("float"); - if (floatNode.empty()) { - return; - } - XmlParser::getValueAsFloat(floatNode, pFloat); -} - -// ------------------------------------------------------------------------------------------------ -// Reads an effect parameter specification of any kind -void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam) { - if (node.empty()) { - return; - } - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "surface") { - // image ID given inside <init_from> tags - XmlNode initNode = currentNode.child("init_from"); - if (initNode) { - std::string v; - XmlParser::getValueAsString(initNode, v); - pParam.mType = Param_Surface; - pParam.mReference = v.c_str(); - } - } else if (currentName == "sampler2D" && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) { - // surface ID is given inside <source> tags - const char *content = currentNode.value(); - pParam.mType = Param_Sampler; - pParam.mReference = content; - } else if (currentName == "sampler2D") { - // surface ID is given inside <instance_image> tags - std::string url; - XmlParser::getStdStrAttribute(currentNode, "url", url); - if (url[0] != '#') { - throw DeadlyImportError("Unsupported URL format in instance_image"); - } - pParam.mType = Param_Sampler; - pParam.mReference = url.c_str() + 1; - } else if (currentName == "source") { - const char *source = currentNode.child_value(); - if (nullptr != source) { - pParam.mReference = source; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the geometry library contents -void ColladaParser::ReadGeometryLibrary(XmlNode &node) { - if (node.empty()) { - return; - } - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "geometry") { - // read ID. Another entry which is "optional" by design but obligatory in reality - - std::string id; - XmlParser::getStdStrAttribute(currentNode, "id", id); - // create a mesh and store it in the library under its (resolved) ID - // Skip and warn if ID is not unique - if (mMeshLibrary.find(id) == mMeshLibrary.cend()) { - std::unique_ptr<Mesh> mesh(new Mesh(id)); - - XmlParser::getStdStrAttribute(currentNode, "name", mesh->mName); - - // read on from there - ReadGeometry(currentNode, *mesh); - // Read successfully, add to library - mMeshLibrary.insert({ id, mesh.release() }); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a geometry from the geometry library. -void ColladaParser::ReadGeometry(XmlNode &node, Collada::Mesh &pMesh) { - if (node.empty()) { - return; - } - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "mesh") { - ReadMesh(currentNode, pMesh); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh from the geometry library -void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { - if (node.empty()) { - return; - } - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "source") { - ReadSource(currentNode); - } else if (currentName == "vertices") { - ReadVertexData(currentNode, pMesh); - } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" || - currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || - currentName == "tristrips") { - ReadIndexData(currentNode, pMesh); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a source element -void ColladaParser::ReadSource(XmlNode &node) { - if (node.empty()) { - return; - } - - std::string sourceID; - XmlParser::getStdStrAttribute(node, "id", sourceID); - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "float_array" || currentName == "IDREF_array" || currentName == "Name_array") { - ReadDataArray(currentNode); - } else if (currentName == "technique_common") { - XmlNode technique = currentNode.child("accessor"); - if (!technique.empty()) { - ReadAccessor(technique, sourceID); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a data array holding a number of floats, and stores it in the global library -void ColladaParser::ReadDataArray(XmlNode &node) { - std::string name = node.name(); - bool isStringArray = (name == "IDREF_array" || name == "Name_array"); - - // read attributes - std::string id; - XmlParser::getStdStrAttribute(node, "id", id); - unsigned int count = 0; - XmlParser::getUIntAttribute(node, "count", count); - std::string v; - XmlParser::getValueAsString(node, v); - v = ai_trim(v); - const char *content = v.c_str(); - - // read values and store inside an array in the data library - mDataLibrary[id] = Data(); - Data &data = mDataLibrary[id]; - data.mIsStringArray = isStringArray; - - // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them - if (content) { - if (isStringArray) { - data.mStrings.reserve(count); - std::string s; - - for (unsigned int a = 0; a < count; a++) { - if (*content == 0) { - throw DeadlyImportError("Expected more values while reading IDREF_array contents."); - } - - s.clear(); - while (!IsSpaceOrNewLine(*content)) - s += *content++; - data.mStrings.push_back(s); - - SkipSpacesAndLineEnd(&content); - } - } else { - data.mValues.reserve(count); - - for (unsigned int a = 0; a < count; a++) { - if (*content == 0) { - throw DeadlyImportError("Expected more values while reading float_array contents."); - } - - // read a number - ai_real value; - content = fast_atoreal_move<ai_real>(content, value); - data.mValues.push_back(value); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads an accessor and stores it in the global library -void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { - // read accessor attributes - std::string source; - XmlParser::getStdStrAttribute(node, "source", source); - if (source[0] != '#') { - throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <accessor> element."); - } - int count = 0; - XmlParser::getIntAttribute(node, "count", count); - - unsigned int offset = 0; - if (XmlParser::hasAttribute(node, "offset")) { - XmlParser::getUIntAttribute(node, "offset", offset); - } - unsigned int stride = 1; - if (XmlParser::hasAttribute(node, "stride")) { - XmlParser::getUIntAttribute(node, "stride", stride); - } - // store in the library under the given ID - mAccessorLibrary[pID] = Accessor(); - Accessor &acc = mAccessorLibrary[pID]; - acc.mCount = count; - acc.mOffset = offset; - acc.mStride = stride; - acc.mSource = source.c_str() + 1; // ignore the leading '#' - acc.mSize = 0; // gets incremented with every param - - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "param") { - // read data param - std::string name; - if (XmlParser::hasAttribute(currentNode, "name")) { - XmlParser::getStdStrAttribute(currentNode, "name", name); - - // analyse for common type components and store it's sub-offset in the corresponding field - - // Cartesian coordinates - if (name == "X") - acc.mSubOffset[0] = acc.mParams.size(); - else if (name == "Y") - acc.mSubOffset[1] = acc.mParams.size(); - else if (name == "Z") - acc.mSubOffset[2] = acc.mParams.size(); - - /* RGBA colors */ - else if (name == "R") - acc.mSubOffset[0] = acc.mParams.size(); - else if (name == "G") - acc.mSubOffset[1] = acc.mParams.size(); - else if (name == "B") - acc.mSubOffset[2] = acc.mParams.size(); - else if (name == "A") - acc.mSubOffset[3] = acc.mParams.size(); - - /* UVWQ (STPQ) texture coordinates */ - else if (name == "S") - acc.mSubOffset[0] = acc.mParams.size(); - else if (name == "T") - acc.mSubOffset[1] = acc.mParams.size(); - else if (name == "P") - acc.mSubOffset[2] = acc.mParams.size(); - /* Generic extra data, interpreted as UV data, too*/ - else if (name == "U") - acc.mSubOffset[0] = acc.mParams.size(); - else if (name == "V") - acc.mSubOffset[1] = acc.mParams.size(); - } - if (XmlParser::hasAttribute(currentNode, "type")) { - // read data type - // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types - // which should be tested for here. - std::string type; - - XmlParser::getStdStrAttribute(currentNode, "type", type); - if (type == "float4x4") - acc.mSize += 16; - else - acc.mSize += 1; - } - - acc.mParams.push_back(name); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-vertex mesh data into the given mesh -void ColladaParser::ReadVertexData(XmlNode &node, Mesh &pMesh) { - // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about - XmlParser::getStdStrAttribute(node, "id", pMesh.mVertexID); - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "input") { - ReadInputChannel(currentNode, pMesh.mPerVertexData); - } else { - throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <vertices>"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads input declarations of per-index mesh data into the given mesh -void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) { - std::vector<size_t> vcount; - std::vector<InputChannel> perIndexData; - - unsigned int numPrimitives = 0; - XmlParser::getUIntAttribute(node, "count", numPrimitives); - // read primitive count from the attribute - //int attrCount = GetAttribute("count"); - //size_t numPrimitives = (size_t)mReader->getAttributeValueAsInt(attrCount); - // some mesh types (e.g. tristrips) don't specify primitive count upfront, - // so we need to sum up the actual number of primitives while we read the <p>-tags - size_t actualPrimitives = 0; - SubMesh subgroup; - if (XmlParser::hasAttribute(node, "material")) { - XmlParser::getStdStrAttribute(node, "material", subgroup.mMaterial); - } - - // distinguish between polys and triangles - std::string elementName = node.name(); - PrimitiveType primType = Prim_Invalid; - if (elementName == "lines") - primType = Prim_Lines; - else if (elementName == "linestrips") - primType = Prim_LineStrip; - else if (elementName == "polygons") - primType = Prim_Polygon; - else if (elementName == "polylist") - primType = Prim_Polylist; - else if (elementName == "triangles") - primType = Prim_Triangles; - else if (elementName == "trifans") - primType = Prim_TriFans; - else if (elementName == "tristrips") - primType = Prim_TriStrips; - - ai_assert(primType != Prim_Invalid); - - // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives - XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); - XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); - if (currentName == "input") { - ReadInputChannel(currentNode, perIndexData); - } else if (currentName == "vcount") { - if (!currentNode.empty()) { - if (numPrimitives) // It is possible to define a mesh without any primitives - { - // case <polylist> - specifies the number of indices for each polygon - std::string v; - XmlParser::getValueAsString(currentNode, v); - const char *content = v.c_str(); - vcount.reserve(numPrimitives); - for (unsigned int a = 0; a < numPrimitives; a++) { - if (*content == 0) { - throw DeadlyImportError("Expected more values while reading <vcount> contents."); - } - // read a number - vcount.push_back((size_t)strtoul10(content, &content)); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); - } - } - } - } else if (currentName == "p") { - if (!currentNode.empty()) { - // now here the actual fun starts - these are the indices to construct the mesh data from - actualPrimitives += ReadPrimitives(currentNode, pMesh, perIndexData, numPrimitives, vcount, primType); - } - } else if (currentName == "extra") { - // skip - } else if (currentName == "ph") { - // skip - } else { - throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <", elementName, ">"); - } - } - -#ifdef ASSIMP_BUILD_DEBUG - if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip && - primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'. - ai_assert(actualPrimitives == numPrimitives); - } -#endif - - // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh - subgroup.mNumFaces = actualPrimitives; - pMesh.mSubMeshes.push_back(subgroup); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a single input channel element and stores it in the given array, if valid -void ColladaParser::ReadInputChannel(XmlNode &node, std::vector<InputChannel> &poChannels) { - InputChannel channel; - - // read semantic - std::string semantic; - XmlParser::getStdStrAttribute(node, "semantic", semantic); - channel.mType = GetTypeForSemantic(semantic); - - // read source - std::string source; - XmlParser::getStdStrAttribute(node, "source", source); - if (source[0] != '#') { - throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <input> element."); - } - channel.mAccessor = source.c_str() + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only - - // read index offset, if per-index <input> - if (XmlParser::hasAttribute(node, "offset")) { - XmlParser::getUIntAttribute(node, "offset", (unsigned int &)channel.mOffset); - } - - // read set if texture coordinates - if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { - unsigned int attrSet = 0; - if (XmlParser::getUIntAttribute(node, "set", attrSet)) - channel.mIndex = attrSet; - } - - // store, if valid type - if (channel.mType != IT_Invalid) - poChannels.push_back(channel); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a <p> primitive index list and assembles the mesh data into the given mesh -size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<InputChannel> &pPerIndexChannels, - size_t pNumPrimitives, const std::vector<size_t> &pVCount, PrimitiveType pPrimType) { - // determine number of indices coming per vertex - // find the offset index for all per-vertex channels - size_t numOffsets = 1; - size_t perVertexOffset = SIZE_MAX; // invalid value - for (const InputChannel &channel : pPerIndexChannels) { - numOffsets = std::max(numOffsets, channel.mOffset + 1); - if (channel.mType == IT_Vertex) - perVertexOffset = channel.mOffset; - } - - // determine the expected number of indices - size_t expectedPointCount = 0; - switch (pPrimType) { - case Prim_Polylist: { - for (size_t i : pVCount) - expectedPointCount += i; - break; - } - case Prim_Lines: - expectedPointCount = 2 * pNumPrimitives; - break; - case Prim_Triangles: - expectedPointCount = 3 * pNumPrimitives; - break; - default: - // other primitive types don't state the index count upfront... we need to guess - break; - } - - // and read all indices into a temporary array - std::vector<size_t> indices; - if (expectedPointCount > 0) { - indices.reserve(expectedPointCount * numOffsets); - } - - // It is possible to not contain any indices - if (pNumPrimitives > 0) { - std::string v; - XmlParser::getValueAsString(node, v); - const char *content = v.c_str(); - SkipSpacesAndLineEnd(&content); - while (*content != 0) { - // read a value. - // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. - int value = std::max(0, strtol10(content, &content)); - indices.push_back(size_t(value)); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); - } - } - - // complain if the index count doesn't fit - if (expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) { - if (pPrimType == Prim_Lines) { - // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines' - ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets); - pNumPrimitives = (indices.size() / numOffsets) / 2; - } else { - throw DeadlyImportError("Expected different index count in <p> element."); - } - } else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0) { - throw DeadlyImportError("Expected different index count in <p> element."); - } - - // find the data for all sources - for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) { - InputChannel &input = *it; - if (input.mResolved) { - continue; - } - - // find accessor - input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if necessary - const Accessor *acc = input.mResolved; - if (!acc->mData) { - acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource); - } - } - // and the same for the per-index channels - for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { - InputChannel &input = *it; - if (input.mResolved) { - continue; - } - - // ignore vertex pointer, it doesn't refer to an accessor - if (input.mType == IT_Vertex) { - // warn if the vertex channel does not refer to the <vertices> element in the same mesh - if (input.mAccessor != pMesh.mVertexID) { - throw DeadlyImportError("Unsupported vertex referencing scheme."); - } - continue; - } - - // find accessor - input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor); - // resolve accessor's data pointer as well, if necessary - const Accessor *acc = input.mResolved; - if (!acc->mData) { - acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource); - } - } - - // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p> - size_t numPrimitives = pNumPrimitives; - if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) - numPrimitives = 1; - // For continued primitives, the given count is actually the number of <p>'s inside the parent tag - if (pPrimType == Prim_TriStrips) { - size_t numberOfVertices = indices.size() / numOffsets; - numPrimitives = numberOfVertices - 2; - } - if (pPrimType == Prim_LineStrip) { - size_t numberOfVertices = indices.size() / numOffsets; - numPrimitives = numberOfVertices - 1; - } - - pMesh.mFaceSize.reserve(numPrimitives); - pMesh.mFacePosIndices.reserve(indices.size() / numOffsets); - - size_t polylistStartVertex = 0; - for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++) { - // determine number of points for this primitive - size_t numPoints = 0; - switch (pPrimType) { - case Prim_Lines: - numPoints = 2; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_LineStrip: - numPoints = 2; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Triangles: - numPoints = 3; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_TriStrips: - numPoints = 3; - ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - case Prim_Polylist: - numPoints = pVCount[currentPrimitive]; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices); - polylistStartVertex += numPoints; - break; - case Prim_TriFans: - case Prim_Polygon: - numPoints = indices.size() / numOffsets; - for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++) - CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - break; - default: - // LineStrip is not supported due to expected index unmangling - throw DeadlyImportError("Unsupported primitive type."); - break; - } - - // store the face size to later reconstruct the face from - pMesh.mFaceSize.push_back(numPoints); - } - - // if I ever get my hands on that guy who invented this steaming pile of indirection... - return numPrimitives; -} - -///@note This function won't work correctly if both PerIndex and PerVertex channels have same channels. -///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates. -///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior -void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh &pMesh, - std::vector<InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices) { - // calculate the base offset of the vertex whose attributes we ant to copy - size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets; - - // don't overrun the boundaries of the index list - ai_assert((baseOffset + numOffsets - 1) < indices.size()); - - // extract per-vertex channels using the global per-vertex offset - for (std::vector<InputChannel>::iterator 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<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) { - ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh); - } - - // store the vertex-data index for later assignment of bone vertex weights - pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]); -} - -void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh &pMesh, std::vector<InputChannel> &pPerIndexChannels, - size_t currentPrimitive, const std::vector<size_t> &indices) { - if (currentPrimitive % 2 != 0) { - //odd tristrip triangles need their indices mangled, to preserve winding direction - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } else { //for non tristrips or even tristrip triangles - CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices); - } -} - -// ------------------------------------------------------------------------------------------------ -// Extracts a single object from an input channel and stores it in the appropriate mesh data array -void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) { - // ignore vertex referrer - we handle them that separate - if (pInput.mType == IT_Vertex) { - return; - } - - const Accessor &acc = *pInput.mResolved; - if (pLocalIndex >= acc.mCount) { - throw DeadlyImportError("Invalid data index (", pLocalIndex, "/", acc.mCount, ") in primitive specification"); - } - - // get a pointer to the start of the data object referred to by the accessor and the local index - const ai_real *dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride; - - // assemble according to the accessors component sub-offset list. We don't care, yet, - // what kind of object exactly we're extracting here - ai_real obj[4]; - for (size_t c = 0; c < 4; ++c) { - obj[c] = dataObject[acc.mSubOffset[c]]; - } - - // now we reinterpret it according to the type we're reading here - switch (pInput.mType) { - case IT_Position: // ignore all position streams except 0 - there can be only one position - if (pInput.mIndex == 0) { - pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); - } - break; - case IT_Normal: - // pad to current vertex count if necessary - if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1) - pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); - - // ignore all normal streams except 0 - there can be only one normal - if (pInput.mIndex == 0) { - pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); - } - break; - case IT_Tangent: - // pad to current vertex count if necessary - if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1) - pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); - - // ignore all tangent streams except 0 - there can be only one tangent - if (pInput.mIndex == 0) { - pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); - } - break; - case IT_Bitangent: - // pad to current vertex count if necessary - if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) { - pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); - } - - // ignore all bitangent streams except 0 - there can be only one bitangent - if (pInput.mIndex == 0) { - pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); - } - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { - // pad to current vertex count if necessary - if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - - pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { - pMesh.mNumUVComponents[pInput.mIndex] = 3; - } - } else { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { - // pad to current vertex count if necessary - if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { - result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh.mColors[pInput.mIndex].push_back(result); - } else { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the library of node hierarchies and scene parts -void ColladaParser::ReadSceneLibrary(XmlNode &node) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "visual_scene") { - // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then? - std::string id; - XmlParser::getStdStrAttribute(currentNode, "id", id); - - // read name if given. - std::string attrName = "Scene"; - if (XmlParser::hasAttribute(currentNode, "name")) { - XmlParser::getStdStrAttribute(currentNode, "name", attrName); - } - - // create a node and store it in the library under its ID - Node *sceneNode = new Node; - sceneNode->mID = id; - sceneNode->mName = attrName; - mNodeLibrary[sceneNode->mID] = sceneNode; - - ReadSceneNode(currentNode, sceneNode); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a scene node's contents including children and stores it in the given node -void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) { - // quit immediately on <bla/> elements - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "node") { - Node *child = new Node; - if (XmlParser::hasAttribute(currentNode, "id")) { - XmlParser::getStdStrAttribute(currentNode, "id", child->mID); - } - if (XmlParser::hasAttribute(currentNode, "sid")) { - XmlParser::getStdStrAttribute(currentNode, "id", child->mSID); - } - if (XmlParser::hasAttribute(currentNode, "name")) { - XmlParser::getStdStrAttribute(currentNode, "name", child->mName); - } - if (pNode) { - pNode->mChildren.push_back(child); - child->mParent = pNode; - } else { - // no parent node given, probably called from <library_nodes> element. - // create new node in node library - mNodeLibrary[child->mID] = child; - } - - // read on recursively from there - ReadSceneNode(currentNode, child); - continue; - } else if (!pNode) { - // For any further stuff we need a valid node to work on - continue; - } - if (currentName == "lookat") { - ReadNodeTransformation(currentNode, pNode, TF_LOOKAT); - } else if (currentName == "matrix") { - ReadNodeTransformation(currentNode, pNode, TF_MATRIX); - } else if (currentName == "rotate") { - ReadNodeTransformation(currentNode, pNode, TF_ROTATE); - } else if (currentName == "scale") { - ReadNodeTransformation(currentNode, pNode, TF_SCALE); - } else if (currentName == "skew") { - ReadNodeTransformation(currentNode, pNode, TF_SKEW); - } else if (currentName == "translate") { - ReadNodeTransformation(currentNode, pNode, TF_TRANSLATE); - } else if (currentName == "render" && pNode->mParent == nullptr && 0 == pNode->mPrimaryCamera.length()) { - // ... scene evaluation or, in other words, postprocessing pipeline, - // or, again in other words, a turing-complete description how to - // render a Collada scene. The only thing that is interesting for - // us is the primary camera. - if (XmlParser::hasAttribute(currentNode, "camera_node")) { - std::string s; - XmlParser::getStdStrAttribute(currentNode, "camera_node", s); - if (s[0] != '#') { - ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera"); - } else { - pNode->mPrimaryCamera = s.c_str() + 1; - } - } - } else if (currentName == "instance_node") { - // find the node in the library - if (XmlParser::hasAttribute(currentNode, "url")) { - std::string s; - XmlParser::getStdStrAttribute(currentNode, "url", s); - if (s[0] != '#') { - ASSIMP_LOG_ERROR("Collada: Unresolved reference format of node"); - } else { - pNode->mNodeInstances.push_back(NodeInstance()); - pNode->mNodeInstances.back().mNode = s.c_str() + 1; - } - } - } else if (currentName == "instance_geometry" || currentName == "instance_controller") { - // Reference to a mesh or controller, with possible material associations - ReadNodeGeometry(currentNode, pNode); - } else if (currentName == "instance_light") { - // Reference to a light, name given in 'url' attribute - if (XmlParser::hasAttribute(currentNode, "url")) { - std::string url; - XmlParser::getStdStrAttribute(currentNode, "url", url); - if (url[0] != '#') { - throw DeadlyImportError("Unknown reference format in <instance_light> element"); - } - - pNode->mLights.push_back(LightInstance()); - pNode->mLights.back().mLight = url.c_str() + 1; - } - } else if (currentName == "instance_camera") { - // Reference to a camera, name given in 'url' attribute - if (XmlParser::hasAttribute(currentNode, "url")) { - std::string url; - XmlParser::getStdStrAttribute(currentNode, "url", url); - if (url[0] != '#') { - throw DeadlyImportError("Unknown reference format in <instance_camera> element"); - } - pNode->mCameras.push_back(CameraInstance()); - pNode->mCameras.back().mCamera = url.c_str() + 1; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// 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 const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; - std::string value; - XmlParser::getValueAsString(node, value); - const char *content = value.c_str(); - - // 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); - // read a number - content = fast_atoreal_move<ai_real>(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 -void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl) { - std::string name = node.name(); - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "bind_vertex_input") { - Collada::InputSemanticMapEntry vn; - - // effect semantic - if (XmlParser::hasAttribute(currentNode, "semantic")) { - std::string s; - XmlParser::getStdStrAttribute(currentNode, "semantic", s); - XmlParser::getUIntAttribute(currentNode, "input_semantic", (unsigned int &)vn.mType); - } - std::string s; - XmlParser::getStdStrAttribute(currentNode, "semantic", s); - - // input semantic - XmlParser::getUIntAttribute(currentNode, "input_semantic", (unsigned int &)vn.mType); - - // index of input set - if (XmlParser::hasAttribute(currentNode, "input_set")) { - XmlParser::getUIntAttribute(currentNode, "input_set", vn.mSet); - } - - tbl.mMap[s] = vn; - } else if (currentName == "bind") { - ASSIMP_LOG_WARN("Collada: Found unsupported <bind> element"); - } - } -} - -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()) { - std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str())); - if (image_file) { - image.mImageData.resize(image_file->FileSize()); - image_file->Read(image.mImageData.data(), image_file->FileSize(), 1); - image.mEmbeddedFormat = BaseImporter::GetExtension(image.mFileName); - if (image.mEmbeddedFormat == "jpeg") { - image.mEmbeddedFormat = "jpg"; - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads a mesh reference in a node and adds it to the node's mesh list -void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) { - // referred mesh is given as an attribute of the <instance_geometry> element - std::string url; - XmlParser::getStdStrAttribute(node, "url", url); - if (url[0] != '#') { - throw DeadlyImportError("Unknown reference format"); - } - - Collada::MeshInstance instance; - instance.mMeshOrController = url.c_str() + 1; // skipping the leading # - - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "bind_material") { - XmlNode techNode = currentNode.child("technique_common"); - if (techNode) { - for (XmlNode instanceMatNode = techNode.child("instance_material"); instanceMatNode; instanceMatNode = instanceMatNode.next_sibling()) - { - const std::string &instance_name = instanceMatNode.name(); - if (instance_name == "instance_material") - { - // read ID of the geometry subgroup and the target material - std::string group; - XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group); - XmlParser::getStdStrAttribute(instanceMatNode, "target", url); - const char *urlMat = url.c_str(); - Collada::SemanticMappingTable s; - if (urlMat[0] == '#') - urlMat++; - - s.mMatName = urlMat; - // store the association - instance.mMaterials[group] = s; - ReadMaterialVertexInputBinding(instanceMatNode, s); - } - } - } - } - } - - // store it - pNode->mMeshes.push_back(instance); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the collada scene -void ColladaParser::ReadScene(XmlNode &node) { - if (node.empty()) { - return; - } - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == "instance_visual_scene") { - // should be the first and only occurrence - if (mRootNode) { - throw DeadlyImportError("Invalid scene containing multiple root nodes in <instance_visual_scene> element"); - } - - // read the url of the scene to instance. Should be of format "#some_name" - std::string url; - XmlParser::getStdStrAttribute(currentNode, "url", url); - if (url[0] != '#') { - throw DeadlyImportError("Unknown reference format in <instance_visual_scene> element"); - } - - // find the referred scene, skip the leading # - NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1); - if (sit == mNodeLibrary.end()) { - throw DeadlyImportError("Unable to resolve visual_scene reference \"", std::string(url), "\" in <instance_visual_scene> element."); - } - mRootNode = sit->second; - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Calculates the resulting transformation from all the given transform steps -aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector<Transform> &pTransforms) const { - aiMatrix4x4 res; - - for (std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) { - const Transform &tf = *it; - switch (tf.mType) { - case TF_LOOKAT: { - aiVector3D pos(tf.f[0], tf.f[1], tf.f[2]); - aiVector3D dstPos(tf.f[3], tf.f[4], tf.f[5]); - aiVector3D up = aiVector3D(tf.f[6], tf.f[7], tf.f[8]).Normalize(); - aiVector3D dir = aiVector3D(dstPos - pos).Normalize(); - aiVector3D right = (dir ^ up).Normalize(); - - res *= aiMatrix4x4( - right.x, up.x, -dir.x, pos.x, - right.y, up.y, -dir.y, pos.y, - right.z, up.z, -dir.z, pos.z, - 0, 0, 0, 1); - break; - } - case TF_ROTATE: { - aiMatrix4x4 rot; - ai_real angle = tf.f[3] * ai_real(AI_MATH_PI) / ai_real(180.0); - aiVector3D axis(tf.f[0], tf.f[1], tf.f[2]); - aiMatrix4x4::Rotation(angle, axis, rot); - res *= rot; - break; - } - case TF_TRANSLATE: { - aiMatrix4x4 trans; - aiMatrix4x4::Translation(aiVector3D(tf.f[0], tf.f[1], tf.f[2]), trans); - res *= trans; - break; - } - case TF_SCALE: { - aiMatrix4x4 scale(tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - res *= scale; - break; - } - case TF_SKEW: - // TODO: (thom) - ai_assert(false); - break; - case TF_MATRIX: { - aiMatrix4x4 mat(tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7], - tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]); - res *= mat; - break; - } - default: - ai_assert(false); - break; - } - } - - return res; -} - -// ------------------------------------------------------------------------------------------------ -// Determines the input data type for the given semantic string -Collada::InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) { - if (semantic.empty()) { - ASSIMP_LOG_WARN("Vertex input type is empty."); - return IT_Invalid; - } - - if (semantic == "POSITION") - return IT_Position; - else if (semantic == "TEXCOORD") - return IT_Texcoord; - else if (semantic == "NORMAL") - return IT_Normal; - else if (semantic == "COLOR") - return IT_Color; - else if (semantic == "VERTEX") - return IT_Vertex; - else if (semantic == "BINORMAL" || semantic == "TEXBINORMAL") - return IT_Bitangent; - else if (semantic == "TANGENT" || semantic == "TEXTANGENT") - return IT_Tangent; - - ASSIMP_LOG_WARN("Unknown vertex input type \"", semantic, "\". Ignoring."); - return IT_Invalid; -} - -#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Collada/ColladaParser.h b/libs/assimp/code/AssetLib/Collada/ColladaParser.h deleted file mode 100644 index 1598293..0000000 --- a/libs/assimp/code/AssetLib/Collada/ColladaParser.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - Open Asset Import Library (assimp) - ---------------------------------------------------------------------- - - Copyright (c) 2006-2022, assimp team - - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ---------------------------------------------------------------------- - */ - -/** @file ColladaParser.h - * @brief Defines the parser helper class for the collada loader - */ - -#pragma once -#ifndef AI_COLLADAPARSER_H_INC -#define AI_COLLADAPARSER_H_INC - -#include "ColladaHelper.h" -#include <assimp/TinyFormatter.h> -#include <assimp/ai_assert.h> -#include <assimp/XmlParser.h> - -#include <map> - -namespace Assimp { - -class ZipArchiveIOSystem; - -// ------------------------------------------------------------------------------------------ -/** Parser helper class for the Collada loader. - * - * Does all the XML reading and builds internal data structures from it, - * but leaves the resolving of all the references to the loader. - */ -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; - - /** Constructor from XML file */ - ColladaParser(IOSystem *pIOHandler, const std::string &pFile); - - /** Destructor */ - ~ColladaParser(); - - /** 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 */ - void ReadContents(XmlNode &node); - - /** Reads the structure of the file */ - void ReadStructure(XmlNode &node); - - /** Reads asset information such as coordinate system information and legal blah */ - void ReadAssetInfo(XmlNode &node); - - /** 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 */ - void ReadAnimationLibrary(XmlNode &node); - - /** Reads the animation clip library */ - void ReadAnimationClipLibrary(XmlNode &node); - - /** Unwrap controllers dependency hierarchy */ - void PostProcessControllers(); - - /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */ - void PostProcessRootAnimations(); - - /** 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 */ - void ReadControllerLibrary(XmlNode &node); - - /** 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(XmlNode &node); - - /** Reads an image entry into the given image */ - void ReadImage(XmlNode &node, Collada::Image &pImage); - - /** 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 */ - void ReadCameraLibrary(XmlNode &node); - - /** Reads a camera entry into the given camera */ - void ReadCamera(XmlNode &node, Collada::Camera &pCamera); - - /** 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 */ - void ReadEffectLibrary(XmlNode &node); - - /** Reads an effect entry into the given effect*/ - void ReadEffect(XmlNode &node, Collada::Effect &pEffect); - - /** Reads an COMMON effect profile */ - void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect); - - /** Read sampler properties */ - void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler); - - /** 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 */ - void ReadEffectFloat(XmlNode &node, ai_real &pFloat); - - /** Reads an effect parameter specification of any kind */ - void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam); - - /** Reads the geometry library contents */ - void ReadGeometryLibrary(XmlNode &node); - - /** Reads a geometry from the geometry library. */ - void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh); - - /** 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. - */ - 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. - */ - 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 - */ - void ReadAccessor(XmlNode &node, const std::string &pID); - - /** 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 */ - void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh); - - /** 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 */ - 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 */ - 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 */ - 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 */ - void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh); - - /** 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 */ - 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 */ - void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode); - - /** Reads the collada scene */ - void ReadScene(XmlNode &node); - - // Processes bind_vertex_input and bind elements - void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl); - - /** Reads embedded textures from a ZAE archive*/ - void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive); - -protected: - /** 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 */ - Collada::InputType GetTypeForSemantic(const std::string &pSemantic); - - /** 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 - std::string mFileName; - - // 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. */ - using DataLibrary = std::map<std::string, Collada::Data> ; - DataLibrary mDataLibrary; - - /** 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 */ - using MeshLibrary = std::map<std::string, Collada::Mesh *>; - MeshLibrary mMeshLibrary; - - /** 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 */ - using ImageLibrary = std::map<std::string, Collada::Image> ; - ImageLibrary mImageLibrary; - - /** Effect library: surface attributes by ID */ - using EffectLibrary = std::map<std::string, Collada::Effect> ; - EffectLibrary mEffectLibrary; - - /** Material library: surface material by ID */ - using MaterialLibrary = std::map<std::string, Collada::Material> ; - MaterialLibrary mMaterialLibrary; - - /** Light library: surface light by ID */ - using LightLibrary = std::map<std::string, Collada::Light> ; - LightLibrary mLightLibrary; - - /** Camera library: surface material by ID */ - using CameraLibrary = std::map<std::string, Collada::Camera> ; - CameraLibrary mCameraLibrary; - - /** Controller library: joint controllers by ID */ - using ControllerLibrary = std::map<std::string, Collada::Controller> ; - ControllerLibrary mControllerLibrary; - - /** Animation library: animation references by ID */ - using AnimationLibrary = std::map<std::string, Collada::Animation *> ; - AnimationLibrary mAnimationLibrary; - - /** 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. */ - Collada::Node *mRootNode; - - /** Root animation container */ - Collada::Animation mAnims; - - /** Size unit: how large compared to a meter */ - ai_real mUnitSize; - - /** Which is the up vector */ - enum { UP_X, - UP_Y, - UP_Z } mUpDirection; - - /** Asset metadata (global for scene) */ - StringMetaData mAssetMetaData; - - /** Collada file format version */ - Collada::FormatVersion mFormat; -}; - -// ------------------------------------------------------------------------------------------------ -// Finds the item in the given library by its reference, throws if not found -template <typename Type> -const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const { - typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL); - if (it == pLibrary.end()) { - throw DeadlyImportError("Unable to resolve library reference \"", pURL, "\"."); - } - return it->second; -} - -} // end of namespace Assimp - -#endif // AI_COLLADAPARSER_H_INC |