summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Collada
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-02-12 23:53:22 -0600
committersanine <sanine.not@pm.me>2023-02-12 23:53:22 -0600
commitf1fe73d1909a2448a004a88362a1a532d0d4f7c3 (patch)
treeab37ae3837e2f858de2932bcee9f26e69fab3db1 /libs/assimp/code/AssetLib/Collada
parentf567ea1e2798fd3156a416e61f083ea3e6b95719 (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.cpp1748
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaExporter.h257
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaHelper.cpp99
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaHelper.h679
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaLoader.cpp1828
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaLoader.h249
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaParser.cpp2402
-rw-r--r--libs/assimp/code/AssetLib/Collada/ColladaParser.h348
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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentChildName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : 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 &currentNode : 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentName = 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 &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentName = 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 &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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 &currentName = 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 &currentNode : node.children()) {
- const std::string &currentName = 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