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

* Add text to ensure that float parsing in colladata works right
This commit is contained in:
Kim Kulling
2025-04-10 15:30:12 +02:00
committed by GitHub
parent 0ae66d2703
commit 2f3e72413f
8 changed files with 555 additions and 537 deletions

View File

@@ -89,6 +89,14 @@ inline void AddNodeMetaData(aiNode *node, const std::string &key, const T &value
node->mMetaData->Add(key, value);
}
// ------------------------------------------------------------------------------------------------
// Reads a float value from an accessor and its data array.
static ai_real ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) {
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
ai_assert(pos < pData.mValues.size());
return pData.mValues[pos];
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ColladaLoader::ColladaLoader() :
@@ -152,7 +160,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
throw DeadlyImportError("Collada: File came out empty. Something is wrong here.");
}
// reserve some storage to avoid unnecessary reallocs
// reserve some storage to avoid unnecessary reallocates
newMats.reserve(parser.mMaterialLibrary.size() * 2u);
mMeshes.reserve(parser.mMeshLibrary.size() * 2u);
@@ -224,7 +232,7 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
// Recursively constructs a scene node for the given parser node and returns it.
aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode) {
// create a node for it
aiNode *node = new aiNode();
auto *node = new aiNode();
// find a name for the new node. It's more complicated than you might think
node->mName.Set(FindNameForNode(pNode));
@@ -272,24 +280,24 @@ aiNode *ColladaLoader::BuildHierarchy(const ColladaParser &pParser, const Collad
// ------------------------------------------------------------------------------------------------
// Resolve node instances
void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Node *pNode,
std::vector<const Node*> &resolved) {
std::vector<const Node*> &resolved) const {
// reserve enough storage
resolved.reserve(pNode->mNodeInstances.size());
// ... and iterate through all nodes to be instanced as children of pNode
for (const auto &nodeInst : pNode->mNodeInstances) {
for (const auto &[mNode] : pNode->mNodeInstances) {
// find the corresponding node in the library
const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode);
const auto itt = pParser.mNodeLibrary.find(mNode);
const Node *nd = itt == pParser.mNodeLibrary.end() ? nullptr : (*itt).second;
// FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
// need to check for both name and ID to catch all. To avoid breaking valid files,
// the workaround is only enabled when the first attempt to resolve the node has failed.
if (nullptr == nd) {
nd = FindNode(pParser.mRootNode, nodeInst.mNode);
nd = FindNode(pParser.mRootNode, mNode);
}
if (nullptr == nd) {
ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode);
ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", mNode);
} else {
// attach this node to the list of children
resolved.push_back(nd);
@@ -299,8 +307,8 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Nod
// ------------------------------------------------------------------------------------------------
// Resolve UV channels
void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) {
SemanticMappingTable::InputSemanticMap::const_iterator it = table.mMap.find(sampler.mUVChannel);
static void ApplyVertexToEffectSemanticMapping(Sampler &sampler, const SemanticMappingTable &table) {
const auto it = table.mMap.find(sampler.mUVChannel);
if (it == table.mMap.end()) {
return;
}
@@ -317,7 +325,7 @@ void ColladaLoader::ApplyVertexToEffectSemanticMapping(Sampler &sampler, const S
void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
for (const LightInstance &lid : pNode->mLights) {
// find the referred light
ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight);
auto srcLightIt = pParser.mLightLibrary.find(lid.mLight);
if (srcLightIt == pParser.mLightLibrary.end()) {
ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping.");
continue;
@@ -325,7 +333,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node
const Collada::Light *srcLight = &srcLightIt->second;
// now fill our ai data structure
aiLight *out = new aiLight();
auto out = new aiLight();
out->mName = pTarget->mName;
out->mType = (aiLightSourceType)srcLight->mType;
@@ -382,7 +390,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node
void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node *pNode, aiNode *pTarget) {
for (const CameraInstance &cid : pNode->mCameras) {
// find the referred light
ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera);
auto srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera);
if (srcCameraIt == pParser.mCameraLibrary.end()) {
ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping.");
continue;
@@ -395,7 +403,7 @@ void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node
}
// now fill our ai data structure
aiCamera *out = new aiCamera();
auto *out = new aiCamera();
out->mName = pTarget->mName;
// collada cameras point in -Z by default, rest is specified in node transform
@@ -445,10 +453,10 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
const Controller *srcController = nullptr;
// find the referred mesh
ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController);
auto srcMeshIt = pParser.mMeshLibrary.find(mid.mMeshOrController);
if (srcMeshIt == pParser.mMeshLibrary.end()) {
// if not found in the mesh-library, it might also be a controller referring to a mesh
ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController);
auto srcContrIt = pParser.mControllerLibrary.find(mid.mMeshOrController);
if (srcContrIt != pParser.mControllerLibrary.end()) {
srcController = &srcContrIt->second;
srcMeshIt = pParser.mMeshLibrary.find(srcController->mMeshId);
@@ -462,7 +470,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
continue;
}
} else {
// ID found in the mesh library -> direct reference to an unskinned mesh
// ID found in the mesh library -> direct reference to a not skinned mesh
srcMesh = srcMeshIt->second;
}
@@ -476,7 +484,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
// find material assigned to this submesh
std::string meshMaterial;
std::map<std::string, SemanticMappingTable>::const_iterator meshMatIt = mid.mMaterials.find(submesh.mMaterial);
auto meshMatIt = mid.mMaterials.find(submesh.mMaterial);
const Collada::SemanticMappingTable *table = nullptr;
if (meshMatIt != mid.mMaterials.end()) {
@@ -492,7 +500,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
// OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
// given. The only mapping stuff which we do actually support is the UV channel.
std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find(meshMaterial);
auto matIt = mMaterialIndexByName.find(meshMaterial);
unsigned int matIdx = 0;
if (matIt != mMaterialIndexByName.end()) {
matIdx = static_cast<unsigned int>(matIt->second);
@@ -515,7 +523,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
ColladaMeshIndex index(mid.mMeshOrController, sm, meshMaterial);
// if we already have the mesh at the library, just add its index to the node's array
std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find(index);
auto dstMeshIt = mMeshIndexByID.find(index);
if (dstMeshIt != mMeshIndexByID.end()) {
newMeshRefs.push_back(dstMeshIt->second);
} else {
@@ -530,7 +538,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
faceStart += submesh.mNumFaces;
// assign the material index
std::map<std::string, size_t>::const_iterator subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
auto subMatIt = mMaterialIndexByName.find(submesh.mMaterial);
if (subMatIt != mMaterialIndexByName.end()) {
dstMesh->mMaterialIndex = static_cast<unsigned int>(subMatIt->second);
} else {
@@ -618,7 +626,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
std::copy(pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + pStartVertex + numVertices, dstMesh->mTangents);
}
// bitangents, if given.
// bi-tangents, if given.
if (pSrcMesh->mBitangents.size() >= pStartVertex + numVertices) {
dstMesh->mBitangents = new aiVector3D[numVertices];
std::copy(pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + pStartVertex + numVertices, dstMesh->mBitangents);
@@ -664,7 +672,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
std::vector<float> targetWeights;
Collada::MorphMethod method = Normalized;
for (std::map<std::string, Controller>::const_iterator it = pParser.mControllerLibrary.begin();
for (auto it = pParser.mControllerLibrary.begin();
it != pParser.mControllerLibrary.end(); ++it) {
const Controller &c = it->second;
const Collada::Mesh *baseMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, c.mMeshId);
@@ -754,7 +762,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
weightStartPerVertex.resize(pSrcController->mWeightCounts.size(), pSrcController->mWeights.end());
IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
auto pit = pSrcController->mWeights.begin();
for (size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) {
weightStartPerVertex[a] = pit;
pit += pSrcController->mWeightCounts[a];
@@ -766,7 +774,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
// the controller assigns the vertex weights
size_t orgIndex = pSrcMesh->mFacePosIndices[a];
// find the vertex weights for this vertex
IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
auto iit = weightStartPerVertex[orgIndex];
size_t pairCount = pSrcController->mWeightCounts[orgIndex];
for (size_t b = 0; b < pairCount; ++b, ++iit) {
@@ -807,7 +815,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
}
// create bone with its weights
aiBone *bone = new aiBone;
auto bone = new aiBone;
bone->mName = ReadString(jointNamesAcc, jointNames, a);
bone->mOffsetMatrix.a1 = ReadFloat(jointMatrixAcc, jointMatrices, a, 0);
bone->mOffsetMatrix.a2 = ReadFloat(jointMatrixAcc, jointMatrices, a, 1);
@@ -973,7 +981,7 @@ void ColladaLoader::StoreAnimations(aiScene *pScene, const ColladaParser &pParse
// if there are other animations which fit the template anim, combine all channels into a single anim
if (!collectedAnimIndices.empty()) {
aiAnimation *combinedAnim = new aiAnimation();
auto *combinedAnim = new aiAnimation();
combinedAnim->mName = aiString(std::string("combinedAnim_") + char('0' + a));
combinedAnim->mDuration = templateAnim->mDuration;
combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
@@ -1040,7 +1048,7 @@ struct MorphTimeValues {
};
void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value) {
MorphTimeValues::key k;
MorphTimeValues::key k{};
k.mValue = value;
k.mWeight = weight;
if (values.empty() || time < values[0].mTime) {
@@ -1106,7 +1114,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// now check all channels if they affect the current node
std::string targetID, subElement;
for (std::vector<AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
for (auto cit = pSrcAnim->mChannels.begin();
cit != pSrcAnim->mChannels.end(); ++cit) {
const AnimationChannel &srcChannel = *cit;
ChannelEntry entry;
@@ -1349,7 +1357,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// build an animation channel for the given node out of these trafo keys
if (!resultTrafos.empty()) {
aiNodeAnim *dstAnim = new aiNodeAnim;
auto *dstAnim = new aiNodeAnim;
dstAnim->mNodeName = nodeName;
dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size());
dstAnim->mNumRotationKeys = static_cast<unsigned int>(resultTrafos.size());
@@ -1391,7 +1399,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
// or 2) one channel with morph target count arrays
// assume first
aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim;
auto *morphAnim = new aiMeshMorphAnim;
morphAnim->mName.Set(nodeName);
std::vector<MorphTimeValues> morphTimeValues;
@@ -1434,7 +1442,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
}
if (!anims.empty() || !morphAnims.empty()) {
aiAnimation *anim = new aiAnimation;
auto anim = new aiAnimation;
anim->mName.Set(pName);
anim->mNumChannels = static_cast<unsigned int>(anims.size());
if (anim->mNumChannels > 0) {
@@ -1514,7 +1522,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
map = sampler.mUVId;
} else {
map = -1;
for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
for (auto it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
if (IsNumeric(*it)) {
map = strtoul10(&(*it));
break;
@@ -1532,7 +1540,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
// Fills materials from the collada material definitions
void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pScene*/) {
for (auto &elem : newMats) {
aiMaterial &mat = (aiMaterial &)*elem.second;
auto &mat = (aiMaterial &)*elem.second;
Collada::Effect &effect = *elem.first;
// resolve shading mode
@@ -1642,17 +1650,17 @@ void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pSce
void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) {
newMats.reserve(pParser.mMaterialLibrary.size());
for (ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin();
for (auto matIt = pParser.mMaterialLibrary.begin();
matIt != pParser.mMaterialLibrary.end(); ++matIt) {
const Material &material = matIt->second;
// a material is only a reference to an effect
ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect);
auto effIt = pParser.mEffectLibrary.find(material.mEffect);
if (effIt == pParser.mEffectLibrary.end())
continue;
Effect &effect = effIt->second;
// create material
aiMaterial *mat = new aiMaterial;
auto *mat = new aiMaterial;
aiString name(material.mName.empty() ? matIt->first : material.mName);
mat->AddProperty(&name, AI_MATKEY_NAME);
@@ -1675,7 +1683,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
std::string name = pName;
while (true) {
// the given string is a param entry. Find it
Effect::ParamLibrary::const_iterator it = pEffect.mParams.find(name);
auto it = pEffect.mParams.find(name);
// if not found, we're at the end of the recursion. The resulting string should be the image ID
if (it == pEffect.mParams.end())
break;
@@ -1685,7 +1693,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
}
// find the image referred by this name in the image library of the scene
ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find(name);
auto imIt = pParser.mImageLibrary.find(name);
if (imIt == pParser.mImageLibrary.end()) {
ASSIMP_LOG_WARN("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\".");
@@ -1697,7 +1705,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
// if this is an embedded texture image setup an aiTexture for it
if (!imIt->second.mImageData.empty()) {
aiTexture *tex = new aiTexture();
auto *tex = new aiTexture();
// Store embedded texture name reference
tex->mFilename.Set(imIt->second.mFileName.c_str());
@@ -1729,14 +1737,6 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
return result;
}
// ------------------------------------------------------------------------------------------------
// Reads a float value from an accessor and its data array.
ai_real ColladaLoader::ReadFloat(const Accessor &pAccessor, const Data &pData, size_t pIndex, size_t pOffset) const {
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
ai_assert(pos < pData.mValues.size());
return pData.mValues[pos];
}
// ------------------------------------------------------------------------------------------------
// Reads a string value from an accessor and its data array.
const std::string &ColladaLoader::ReadString(const Accessor &pAccessor, const Data &pData, size_t pIndex) const {

View File

@@ -109,7 +109,7 @@ protected:
/// Resolve node instances
void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
std::vector<const Collada::Node *> &resolved);
std::vector<const Collada::Node *> &resolved) const;
/// Builds meshes for the given node and references them
void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
@@ -166,10 +166,6 @@ protected:
/** Fill materials from the collada material definitions */
void FillMaterials(const ColladaParser &pParser, aiScene *pScene);
/** Resolve UV channel mappings*/
void ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler,
const Collada::SemanticMappingTable &table);
/** Add a texture and all of its sampling properties to a material*/
void AddTexture(aiMaterial &mat, const ColladaParser &pParser,
const Collada::Effect &effect,
@@ -180,22 +176,13 @@ protected:
aiString FindFilenameForEffectTexture(const ColladaParser &pParser,
const Collada::Effect &pEffect, const std::string &pName);
/** Reads a float value from an accessor and its data array.
* @param pAccessor The accessor to use for reading
* @param pData The data array to read from
* @param pIndex The index of the element to retrieve
* @param pOffset Offset into the element, for multipart elements such as vectors or matrices
* @return the specified value
*/
ai_real ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const;
/** Reads a string value from an accessor and its data array.
* @param pAccessor The accessor to use for reading
* @param pData The data array to read from
* @param pIndex The index of the element to retrieve
* @return the specified value
*/
const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
[[nodiscard]] const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
/** Recursively collects all nodes into the given array */
void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const;
@@ -208,7 +195,7 @@ protected:
/** Finds a proper name for a node derived from the collada-node's properties */
std::string FindNameForNode(const Collada::Node *pNode);
protected:
private:
/** Filename, for a verbose error message */
std::string mFileName;

View File

@@ -61,6 +61,7 @@ using namespace Assimp;
using namespace Assimp::Collada;
using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------
static void ReportWarning(const char *msg, ...) {
ai_assert(nullptr != msg);
@@ -75,6 +76,7 @@ static void ReportWarning(const char *msg, ...) {
ASSIMP_LOG_WARN("Validation warning: ", std::string(szBuffer, iLen));
}
// ------------------------------------------------------------------------------------------------
static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVector &key_renaming, size_t &found_index) {
for (size_t i = 0; i < key_renaming.size(); ++i) {
if (key_renaming[i].first == collada_key) {
@@ -87,6 +89,7 @@ static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVecto
return false;
}
// ------------------------------------------------------------------------------------------------
static void readUrlAttribute(XmlNode &node, std::string &url) {
url.clear();
if (!XmlParser::getStdStrAttribute(node, "url", url)) {
@@ -98,23 +101,319 @@ static void readUrlAttribute(XmlNode &node, std::string &url) {
url = url.c_str() + 1;
}
// ------------------------------------------------------------------------------------------------
// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
static void ReadNodeTransformation(XmlNode &node, Node *pNode, TransformType pType) {
if (node.empty()) {
return;
}
std::string tagName = node.name();
Transform tf;
tf.mType = pType;
// read SID
if (XmlParser::hasAttribute(node, "sid")) {
XmlParser::getStdStrAttribute(node, "sid", tf.mID);
}
// how many parameters to read per transformation type
static constexpr unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
std::string value;
XmlParser::getValueAsString(node, value);
const char *content = value.c_str();
const char *end = value.c_str() + value.size();
// read as many parameters and store in the transformation
for (unsigned int a = 0; a < sNumParameters[pType]; a++) {
// skip whitespace before the number
SkipSpacesAndLineEnd(&content, end);
// read a number
content = fast_atoreal_move<ai_real>(content, tf.f[a]);
}
// place the transformation at the queue of the node
pNode->mTransforms.push_back(tf);
}
// ------------------------------------------------------------------------------------------------
// Reads a single string metadata item
static void ReadMetaDataItem(XmlNode &node, ColladaParser::StringMetaData &metadata) {
const MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase();
const std::string name = node.name();
if (name.empty()) {
return;
}
std::string v;
if (!XmlParser::getValueAsString(node, v)) {
return;
}
v = ai_trim(v);
aiString aistr;
aistr.Set(v);
std::string camel_key_str(name);
ToCamelCase(camel_key_str);
size_t found_index;
if (FindCommonKey(camel_key_str, key_renaming, found_index)) {
metadata.emplace(key_renaming[found_index].second, aistr);
} else {
metadata.emplace(camel_key_str, aistr);
}
}
// ------------------------------------------------------------------------------------------------
// Reads an animation sampler into the given anim channel
static void ReadAnimationSampler(const XmlNode &node, AnimationChannel &pChannel) {
for (XmlNode &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 joint definitions for the given controller
static void ReadControllerJoints(const XmlNode &node, 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");
}
}
}
}
// ------------------------------------------------------------------------------------------------
static void ReadControllerWeightsInput(const XmlNode &currentNode, Controller &pController) {
InputChannel channel;
const char *attrSemantic = currentNode.attribute("semantic").as_string();
const char *attrSource = currentNode.attribute("source").as_string();
channel.mOffset = currentNode.attribute("offset").as_int();
// local URLS always start with a '#'. We don't support global URLs
if (attrSource[0] != '#') {
throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of <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");
}
}
// ------------------------------------------------------------------------------------------------
static void ReadControllerWeightsVCount(const XmlNode &currentNode, Controller &pController) {
const std::string stdText = currentNode.text().as_string();
const char *text = stdText.c_str();
const char *end = text + stdText.size();
size_t numWeights = 0;
for (auto it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
if (*text == 0) {
throw DeadlyImportError("Out of data while reading <vcount>");
}
*it = strtoul10(text, &text);
numWeights += *it;
SkipSpacesAndLineEnd(&text, end);
}
// reserve weight count
pController.mWeights.resize(numWeights);
}
// ------------------------------------------------------------------------------------------------
static void ReadControllerWeightsJoint2verts(XmlNode &currentNode, Controller &pController) {
// read JointIndex - WeightIndex pairs
std::string stdText;
XmlParser::getValueAsString(currentNode, stdText);
const char *text = stdText.c_str();
const char *end = text + stdText.size();
for (auto it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
if (text == nullptr) {
throw DeadlyImportError("Out of data while reading <vertex_weights>");
}
SkipSpacesAndLineEnd(&text, end);
it->first = strtoul10(text, &text);
SkipSpacesAndLineEnd(&text, end);
if (*text == 0) {
throw DeadlyImportError("Out of data while reading <vertex_weights>");
}
it->second = strtoul10(text, &text);
SkipSpacesAndLineEnd(&text, end);
}
}
// ------------------------------------------------------------------------------------------------
// Reads the joint weights for the given controller
static void ReadControllerWeights(XmlNode &node, Controller &pController) {
// Read vertex count from attributes and resize the array accordingly
int vertexCount = 0;
XmlParser::getIntAttribute(node, "count", vertexCount);
pController.mWeightCounts.resize(vertexCount);
for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name();
if (currentName == "input") {
ReadControllerWeightsInput(currentNode, pController);
} else if (currentName == "vcount" && vertexCount > 0) {
ReadControllerWeightsVCount(currentNode, pController);
} else if (currentName == "v" && vertexCount > 0) {
ReadControllerWeightsJoint2verts(currentNode, pController);
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a material entry into the given material
static void ReadMaterial(const XmlNode &node, Material &pMaterial) {
for (XmlNode &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
static void ReadLight(XmlNode &node, Light &pLight) {
XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
XmlNode currentNode;
// TODO: Check the current technique and skip over unsupported extra techniques
while (xmlIt.getNext(currentNode)) {
const std::string &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();
const char *end = content + v.size();
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.r);
SkipSpacesAndLineEnd(&content, end);
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.g);
SkipSpacesAndLineEnd(&content, end);
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b);
SkipSpacesAndLineEnd(&content, end);
} else if (currentName == "constant_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttConstant);
} else if (currentName == "linear_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttLinear);
} else if (currentName == "quadratic_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttQuadratic);
} else if (currentName == "falloff_angle") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle);
} else if (currentName == "falloff_exponent") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffExponent);
}
// FCOLLADA extensions
// -------------------------------------------------------
else if (currentName == "outer_cone") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
} else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone
XmlParser::getValueAsReal(currentNode, pLight.mPenumbraAngle);
} else if (currentName == "intensity") {
XmlParser::getValueAsReal(currentNode, pLight.mIntensity);
} else if (currentName == "falloff") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
} else if (currentName == "hotspot_beam") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle);
}
// OpenCOLLADA extensions
// -------------------------------------------------------
else if (currentName == "decay_falloff") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a camera entry into the given light
static void ReadCamera(XmlNode &node, Camera &camera) {
XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
XmlNode currentNode;
while (xmlIt.getNext(currentNode)) {
const std::string &currentName = currentNode.name();
if (currentName == "orthographic") {
camera.mOrtho = true;
} else if (currentName == "xfov" || currentName == "xmag") {
XmlParser::getValueAsReal(currentNode, camera.mHorFov);
} else if (currentName == "yfov" || currentName == "ymag") {
XmlParser::getValueAsReal(currentNode, camera.mVerFov);
} else if (currentName == "aspect_ratio") {
XmlParser::getValueAsReal(currentNode, camera.mAspect);
} else if (currentName == "znear") {
XmlParser::getValueAsReal(currentNode, camera.mZNear);
} else if (currentName == "zfar") {
XmlParser::getValueAsReal(currentNode, camera.mZFar);
}
}
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
mFileName(pFile),
mXmlParser(),
mDataLibrary(),
mAccessorLibrary(),
mMeshLibrary(),
mNodeLibrary(),
mImageLibrary(),
mEffectLibrary(),
mMaterialLibrary(),
mLightLibrary(),
mCameraLibrary(),
mControllerLibrary(),
mRootNode(nullptr),
mAnims(),
mUnitSize(1.0f),
mUpDirection(UP_Y),
mFormat(FV_1_5_n) {
@@ -122,13 +421,13 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
throw DeadlyImportError("IOSystem is nullptr.");
}
std::unique_ptr<IOStream> daefile;
std::unique_ptr<IOStream> daeFile;
std::unique_ptr<ZipArchiveIOSystem> zip_archive;
// Determine type
std::string extension = BaseImporter::GetExtension(pFile);
const std::string extension = BaseImporter::GetExtension(pFile);
if (extension != "dae") {
zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile));
zip_archive = std::make_unique<ZipArchiveIOSystem>(pIOHandler, pFile);
}
if (zip_archive && zip_archive->isOpen()) {
@@ -138,24 +437,24 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
throw DeadlyImportError("Invalid ZAE");
}
daefile.reset(zip_archive->Open(dae_filename.c_str()));
if (daefile == nullptr) {
daeFile.reset(zip_archive->Open(dae_filename.c_str()));
if (daeFile == nullptr) {
throw DeadlyImportError("Invalid ZAE manifest: '", dae_filename, "' is missing");
}
} else {
// attempt to open the file directly
daefile.reset(pIOHandler->Open(pFile));
if (daefile == nullptr) {
daeFile.reset(pIOHandler->Open(pFile));
if (daeFile == nullptr) {
throw DeadlyImportError("Failed to open file '", pFile, "'.");
}
}
// generate a XML reader for it
if (!mXmlParser.parse(daefile.get())) {
if (!mXmlParser.parse(daeFile.get())) {
throw DeadlyImportError("Unable to read file, malformed XML");
}
// start reading
XmlNode node = mXmlParser.getRootNode();
const XmlNode node = mXmlParser.getRootNode();
XmlNode colladaNode = node.child("COLLADA");
if (colladaNode.empty()) {
return;
@@ -190,14 +489,14 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
zip_archive.getFileListExtension(file_list, "dae");
if (file_list.empty()) {
return std::string();
return {};
}
return file_list.front();
}
XmlParser manifestParser;
if (!manifestParser.parse(manifestfile.get())) {
return std::string();
return {};
}
XmlNode root = manifestParser.getRootNode();
@@ -205,7 +504,7 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
if (name != "dae_root") {
root = *manifestParser.findNode("dae_root");
if (nullptr == root) {
return std::string();
return {};
}
std::string v;
XmlParser::getValueAsString(root, v);
@@ -214,7 +513,7 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
return std::string(ai_str.C_Str());
}
return std::string();
return {};
}
// ------------------------------------------------------------------------------------------------
@@ -246,7 +545,7 @@ void ColladaParser::UriDecodePath(aiString &ss) {
char mychar[3] = { it[1], it[2], 0 };
size_t nbr = strtoul16(mychar);
it += 3;
*out++ = (char)(nbr & 0xFF);
*out++ = static_cast<char>(nbr & 0xFF);
} else {
*out++ = *it++;
}
@@ -261,12 +560,11 @@ void ColladaParser::UriDecodePath(aiString &ss) {
// ------------------------------------------------------------------------------------------------
// Reads the contents of the file
void ColladaParser::ReadContents(XmlNode &node) {
const std::string name = node.name();
if (name == "COLLADA") {
if (const std::string name = node.name(); name == "COLLADA") {
std::string version;
if (XmlParser::getStdStrAttribute(node, "version", version)) {
aiString v;
v.Set(version.c_str());
v.Set(version);
mAssetMetaData.emplace(AI_METADATA_SOURCE_FORMAT_VERSION, v);
if (!::strncmp(version.c_str(), "1.5", 3)) {
mFormat = FV_1_5_n;
@@ -287,8 +585,7 @@ void ColladaParser::ReadContents(XmlNode &node) {
// Reads the structure of the file
void ColladaParser::ReadStructure(XmlNode &node) {
for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name();
if (currentName == "asset") {
if (const std::string &currentName = currentNode.name(); currentName == "asset") {
ReadAssetInfo(currentNode);
} else if (currentName == "library_animations") {
ReadAnimationLibrary(currentNode);
@@ -329,8 +626,7 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
}
for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name();
if (currentName == "unit") {
if (const std::string &currentName = currentNode.name(); currentName == "unit") {
mUnitSize = 1.f;
std::string tUnitSizeString;
if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) {
@@ -364,35 +660,6 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
}
}
// ------------------------------------------------------------------------------------------------
// Reads a single string metadata item
void ColladaParser::ReadMetaDataItem(XmlNode &node, StringMetaData &metadata) {
const Collada::MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase();
const std::string name = node.name();
if (name.empty()) {
return;
}
std::string v;
if (!XmlParser::getValueAsString(node, v)) {
return;
}
v = ai_trim(v);
aiString aistr;
aistr.Set(v);
std::string camel_key_str(name);
ToCamelCase(camel_key_str);
size_t found_index;
if (FindCommonKey(camel_key_str, key_renaming, found_index)) {
metadata.emplace(key_renaming[found_index].second, aistr);
} else {
metadata.emplace(camel_key_str, aistr);
}
}
// ------------------------------------------------------------------------------------------------
// Reads the animation clips
void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
@@ -424,15 +691,16 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
}
}
// ------------------------------------------------------------------------------------------------
// The controller post processing step
void ColladaParser::PostProcessControllers() {
std::string meshId;
for (auto &it : mControllerLibrary) {
meshId = it.second.mMeshId;
std::string meshId = it.second.mMeshId;
if (meshId.empty()) {
continue;
}
ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
auto findItr = mControllerLibrary.find(meshId);
while (findItr != mControllerLibrary.end()) {
meshId = findItr->second.mMeshId;
findItr = mControllerLibrary.find(meshId);
@@ -454,13 +722,13 @@ void ColladaParser::PostProcessRootAnimations() {
for (auto &it : mAnimationClipLibrary) {
std::string clipName = it.first;
Animation *clip = new Animation();
auto *clip = new Animation();
clip->mName = clipName;
temp.mSubAnims.push_back(clip);
for (const std::string &animationID : it.second) {
AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
auto animation = mAnimationLibrary.find(animationID);
if (animation != mAnimationLibrary.end()) {
Animation *pSourceAnimation = animation->second;
@@ -533,7 +801,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
// have it read into a channel
ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
auto newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
ReadAnimationSampler(currentNode, newChannel->second);
}
} else if (currentName == "channel") {
@@ -543,7 +811,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
if (source_name[0] == '#') {
source_name = source_name.substr(1, source_name.size() - 1);
}
ChannelMap::iterator cit = channels.find(source_name);
auto cit = channels.find(source_name);
if (cit != channels.end()) {
cit->second.mTarget = target;
}
@@ -568,40 +836,6 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
}
}
// ------------------------------------------------------------------------------------------------
// Reads an animation sampler into the given anim channel
void ColladaParser::ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel) {
for (XmlNode &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) {
@@ -614,8 +848,7 @@ void ColladaParser::ReadControllerLibrary(XmlNode &node) {
if (currentName != "controller") {
continue;
}
std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
if (std::string id; XmlParser::getStdStrAttribute(currentNode, "id", id)) {
mControllerLibrary[id] = Controller();
ReadController(currentNode, mControllerLibrary[id]);
}
@@ -632,13 +865,11 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle
XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
XmlNode currentNode;
while (xmlIt.getNext(currentNode)) {
const std::string &currentName = currentNode.name();
if (currentName == "morph") {
if (const std::string &currentName = currentNode.name(); currentName == "morph") {
controller.mType = Morph;
std::string id = currentNode.attribute("source").as_string();
controller.mMeshId = id.substr(1, id.size() - 1);
int methodIndex = currentNode.attribute("method").as_int();
if (methodIndex > 0) {
if (const int methodIndex = currentNode.attribute("method").as_int(); methodIndex > 0) {
std::string method;
XmlParser::getValueAsString(currentNode, method);
@@ -647,8 +878,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle
}
}
} else if (currentName == "skin") {
std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "source", id)) {
if (std::string id; XmlParser::getStdStrAttribute(currentNode, "source", id)) {
controller.mMeshId = id.substr(1, id.size() - 1);
}
} else if (currentName == "bind_shape_matrix") {
@@ -656,10 +886,10 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle
XmlParser::getValueAsString(currentNode, v);
const char *content = v.c_str();
const char *end = content + v.size();
for (unsigned int a = 0; a < 16; a++) {
for (float & a : controller.mBindShapeMatrix) {
SkipSpacesAndLineEnd(&content, end);
// read a number
content = fast_atoreal_move<ai_real>(content, controller.mBindShapeMatrix[a]);
content = fast_atoreal_move<ai_real>(content, a);
// skip whitespace after it
SkipSpacesAndLineEnd(&content, end);
}
@@ -686,108 +916,13 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle
}
}
// ------------------------------------------------------------------------------------------------
// Reads the joint definitions for the given controller
void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pController) {
for (XmlNode &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 std::string stdText = currentNode.text().as_string();
const char *text = stdText.c_str();
const char *end = text + stdText.size();
size_t numWeights = 0;
for (std::vector<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, end);
}
// reserve weight count
pController.mWeights.resize(numWeights);
} else if (currentName == "v" && vertexCount > 0) {
// read JointIndex - WeightIndex pairs
std::string stdText;
XmlParser::getValueAsString(currentNode, stdText);
const char *text = stdText.c_str();
const char *end = text + stdText.size();
for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
if (text == nullptr) {
throw DeadlyImportError("Out of data while reading <vertex_weights>");
}
SkipSpacesAndLineEnd(&text, end);
it->first = strtoul10(text, &text);
SkipSpacesAndLineEnd(&text, end);
if (*text == 0) {
throw DeadlyImportError("Out of data while reading <vertex_weights>");
}
it->second = strtoul10(text, &text);
SkipSpacesAndLineEnd(&text, end);
}
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads the image library contents
void ColladaParser::ReadImageLibrary(XmlNode &node) {
void ColladaParser::ReadImageLibrary(const XmlNode &node) {
for (XmlNode &currentNode : node.children()) {
const std::string &currentName = currentNode.name();
if (currentName == "image") {
std::string id;
if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
if (std::basic_string<char> id; XmlParser::getStdStrAttribute(currentNode, "id", id)) {
mImageLibrary[id] = Image();
// read on from there
ReadImage(currentNode, mImageLibrary[id]);
@@ -798,7 +933,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) {
// ------------------------------------------------------------------------------------------------
// Reads an image entry into the given image
void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
void ColladaParser::ReadImage(const XmlNode &node, Collada::Image &pImage) const {
for (XmlNode &currentNode : node.children()) {
const std::string currentName = currentNode.name();
if (currentName == "image") {
@@ -861,13 +996,13 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
// Reads the material library
void ColladaParser::ReadMaterialLibrary(XmlNode &node) {
std::map<std::string, int> names;
for (XmlNode &currentNode : node.children()) {
for (const 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);
auto it = names.find(name);
if (it != names.end()) {
std::ostringstream strStream;
strStream << ++it->second;
@@ -922,107 +1057,6 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) {
}
}
// ------------------------------------------------------------------------------------------------
// Reads a material entry into the given material
void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
for (XmlNode &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();
const char *end = content + v.size();
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.r);
SkipSpacesAndLineEnd(&content, end);
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.g);
SkipSpacesAndLineEnd(&content, end);
content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b);
SkipSpacesAndLineEnd(&content, end);
} else if (currentName == "constant_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttConstant);
} else if (currentName == "linear_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttLinear);
} else if (currentName == "quadratic_attenuation") {
XmlParser::getValueAsReal(currentNode, pLight.mAttQuadratic);
} else if (currentName == "falloff_angle") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle);
} else if (currentName == "falloff_exponent") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffExponent);
}
// FCOLLADA extensions
// -------------------------------------------------------
else if (currentName == "outer_cone") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
} else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone
XmlParser::getValueAsReal(currentNode, pLight.mPenumbraAngle);
} else if (currentName == "intensity") {
XmlParser::getValueAsReal(currentNode, pLight.mIntensity);
}
else if (currentName == "falloff") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
} else if (currentName == "hotspot_beam") {
XmlParser::getValueAsReal(currentNode, pLight.mFalloffAngle);
}
// OpenCOLLADA extensions
// -------------------------------------------------------
else if (currentName == "decay_falloff") {
XmlParser::getValueAsReal(currentNode, pLight.mOuterAngle);
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a camera entry into the given light
void ColladaParser::ReadCamera(XmlNode &node, Collada::Camera &camera) {
XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
XmlNode currentNode;
while (xmlIt.getNext(currentNode)) {
const std::string &currentName = currentNode.name();
if (currentName == "orthographic") {
camera.mOrtho = true;
} else if (currentName == "xfov" || currentName == "xmag") {
XmlParser::getValueAsReal(currentNode, camera.mHorFov);
} else if (currentName == "yfov" || currentName == "ymag") {
XmlParser::getValueAsReal(currentNode, camera.mVerFov);
} else if (currentName == "aspect_ratio") {
XmlParser::getValueAsReal(currentNode, camera.mAspect);
} else if (currentName == "znear") {
XmlParser::getValueAsReal(currentNode, camera.mZNear);
} else if (currentName == "zfar") {
XmlParser::getValueAsReal(currentNode, camera.mZFar);
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads the effect library
void ColladaParser::ReadEffectLibrary(XmlNode &node) {
@@ -1733,7 +1767,6 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
expectedPointCount = 3 * pNumPrimitives;
break;
default:
// other primitive types don't state the index count upfront... we need to guess
break;
}
@@ -1775,7 +1808,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
}
// find the data for all sources
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
for (auto it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
InputChannel &input = *it;
if (input.mResolved) {
continue;
@@ -1794,7 +1827,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
}
}
// and the same for the per-index channels
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
for (auto it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
InputChannel &input = *it;
if (input.mResolved) {
continue;
@@ -1902,11 +1935,11 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n
ai_assert((baseOffset + numOffsets - 1) < indices.size());
// extract per-vertex channels using the global per-vertex offset
for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
for (auto it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
}
// and extract per-index channels using there specified offset
for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
for (auto it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
}
@@ -2173,40 +2206,6 @@ void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) {
}
}
// ------------------------------------------------------------------------------------------------
// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, TransformType pType) {
if (node.empty()) {
return;
}
std::string tagName = node.name();
Transform tf;
tf.mType = pType;
// read SID
if (XmlParser::hasAttribute(node, "sid")) {
XmlParser::getStdStrAttribute(node, "sid", tf.mID);
}
// how many parameters to read per transformation type
static constexpr unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
std::string value;
XmlParser::getValueAsString(node, value);
const char *content = value.c_str();
const char *end = value.c_str() + value.size();
// read as many parameters and store in the transformation
for (unsigned int a = 0; a < sNumParameters[pType]; a++) {
// skip whitespace before the number
SkipSpacesAndLineEnd(&content, end);
// read a number
content = fast_atoreal_move<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
@@ -2244,9 +2243,7 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman
void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) {
// Attempt to load any undefined Collada::Image in ImageLibrary
for (auto &it : mImageLibrary) {
Collada::Image &image = it.second;
if (image.mImageData.empty()) {
if (Image &image = it.second; image.mImageData.empty()) {
std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));
if (image_file) {
image.mImageData.resize(image_file->FileSize());
@@ -2329,7 +2326,7 @@ void ColladaParser::ReadScene(XmlNode &node) {
}
// find the referred scene, skip the leading #
NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1);
auto sit = mNodeLibrary.find(url.c_str() + 1);
if (sit == mNodeLibrary.end()) {
throw DeadlyImportError("Unable to resolve visual_scene reference \"", std::string(std::move(url)), "\" in <instance_visual_scene> element.");
}
@@ -2401,7 +2398,7 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector<Transform>
// ------------------------------------------------------------------------------------------------
// Determines the input data type for the given semantic string
Collada::InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) {
InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) {
if (semantic.empty()) {
ASSIMP_LOG_WARN("Vertex input type is empty.");
return IT_Invalid;

View File

@@ -48,7 +48,6 @@
#define AI_COLLADAPARSER_H_INC
#include "ColladaHelper.h"
#include <assimp/TinyFormatter.h>
#include <assimp/ai_assert.h>
#include <assimp/XmlParser.h>
@@ -67,268 +66,240 @@ class ZipArchiveIOSystem;
class ColladaParser {
friend class ColladaLoader;
/** Converts a path read from a collada file to the usual representation */
static void UriDecodePath(aiString &ss);
public:
/// Map for generic metadata as aiString.
using StringMetaData = std::map<std::string, aiString>;
protected:
/** Map for generic metadata as aiString */
typedef std::map<std::string, aiString> StringMetaData;
/** Constructor from XML file */
/// Constructor from XML file.
ColladaParser(IOSystem *pIOHandler, const std::string &pFile);
/** Destructor */
/// Destructor
~ColladaParser();
/** Attempts to read the ZAE manifest and returns the DAE to open */
/// Attempts to read the ZAE manifest and returns the DAE to open
static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
/** Reads the contents of the file */
/// Reads the contents of the file
void ReadContents(XmlNode &node);
/** Reads the structure of the file */
/// Reads the structure of the file
void ReadStructure(XmlNode &node);
/** Reads asset information such as coordinate system information and legal blah */
/// Reads asset information such as coordinate system information and legal blah
void ReadAssetInfo(XmlNode &node);
/** Reads contributor information such as author and legal blah */
/// Reads contributor information such as author and legal blah
void ReadContributorInfo(XmlNode &node);
/** Reads generic metadata into provided map and renames keys for Assimp */
void ReadMetaDataItem(XmlNode &node, StringMetaData &metadata);
/** Reads the animation library */
/// Reads the animation library
void ReadAnimationLibrary(XmlNode &node);
/** Reads the animation clip library */
/// Reads the animation clip library
void ReadAnimationClipLibrary(XmlNode &node);
/** Unwrap controllers dependency hierarchy */
/// Unwrap controllers dependency hierarchy
void PostProcessControllers();
/** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */
/// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
void PostProcessRootAnimations();
/** Reads an animation into the given parent structure */
/// Reads an animation into the given parent structure
void ReadAnimation(XmlNode &node, Collada::Animation *pParent);
/** Reads an animation sampler into the given anim channel */
void ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel);
/** Reads the skeleton controller library */
/// Reads the skeleton controller library
void ReadControllerLibrary(XmlNode &node);
/** Reads a controller into the given mesh structure */
/// Reads a controller into the given mesh structure
void ReadController(XmlNode &node, Collada::Controller &pController);
/** Reads the joint definitions for the given controller */
void ReadControllerJoints(XmlNode &node, Collada::Controller &pController);
/// Reads the image library contents
void ReadImageLibrary(const XmlNode &node);
/** Reads the joint weights for the given controller */
void ReadControllerWeights(XmlNode &node, Collada::Controller &pController);
/// Reads an image entry into the given image
void ReadImage(const XmlNode &node, Collada::Image &pImage) const;
/** Reads the image library contents */
void ReadImageLibrary(XmlNode &node);
/** Reads an image entry into the given image */
void ReadImage(XmlNode &node, Collada::Image &pImage);
/** Reads the material library */
/// Reads the material library
void ReadMaterialLibrary(XmlNode &node);
/** Reads a material entry into the given material */
void ReadMaterial(XmlNode &node, Collada::Material &pMaterial);
/** Reads the camera library */
/// Reads the camera library
void ReadCameraLibrary(XmlNode &node);
/** Reads a camera entry into the given camera */
void ReadCamera(XmlNode &node, Collada::Camera &pCamera);
/** Reads the light library */
/// Reads the light library
void ReadLightLibrary(XmlNode &node);
/** Reads a light entry into the given light */
void ReadLight(XmlNode &node, Collada::Light &pLight);
/** Reads the effect library */
/// Reads the effect library
void ReadEffectLibrary(XmlNode &node);
/** Reads an effect entry into the given effect*/
/// Reads an effect entry into the given effect
void ReadEffect(XmlNode &node, Collada::Effect &pEffect);
/** Reads an COMMON effect profile */
/// Reads an COMMON effect profile
void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect);
/** Read sampler properties */
/// Read sampler properties
void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler);
/** Reads an effect entry containing a color or a texture defining that color */
/// Reads an effect entry containing a color or a texture defining that color
void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler);
/** Reads an effect entry containing a float */
/// Reads an effect entry containing a float
void ReadEffectFloat(XmlNode &node, ai_real &pFloat);
/** Reads an effect parameter specification of any kind */
/// Reads an effect parameter specification of any kind
void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam);
/** Reads the geometry library contents */
/// Reads the geometry library contents
void ReadGeometryLibrary(XmlNode &node);
/** Reads a geometry from the geometry library. */
/// Reads a geometry from the geometry library.
void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh);
/** Reads a mesh from the geometry library */
/// Reads a mesh from the geometry library
void ReadMesh(XmlNode &node, Collada::Mesh &pMesh);
/** Reads a source element - a combination of raw data and an accessor defining
* things that should not be redefinable. Yes, that's another rant.
*/
/// Reads a source element - a combination of raw data and an accessor defining
///things that should not be definable. Yes, that's another rant.
void ReadSource(XmlNode &node);
/** Reads a data array holding a number of elements, and stores it in the global library.
* Currently supported are array of floats and arrays of strings.
*/
/// Reads a data array holding a number of elements, and stores it in the global library.
/// Currently supported are array of floats and arrays of strings.
void ReadDataArray(XmlNode &node);
/** Reads an accessor and stores it in the global library under the given ID -
* accessors use the ID of the parent <source> element
*/
/// Reads an accessor and stores it in the global library under the given ID -
/// accessors use the ID of the parent <source> element
void ReadAccessor(XmlNode &node, const std::string &pID);
/** Reads input declarations of per-vertex mesh data into the given mesh */
/// Reads input declarations of per-vertex mesh data into the given mesh
void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh);
/** Reads input declarations of per-index mesh data into the given mesh */
/// Reads input declarations of per-index mesh data into the given mesh
void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh);
/** Reads a single input channel element and stores it in the given array, if valid */
/// Reads a single input channel element and stores it in the given array, if valid
void ReadInputChannel(XmlNode &node, std::vector<Collada::InputChannel> &poChannels);
/** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
/// Reads a <p> primitive index list and assembles the mesh data into the given mesh
size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
size_t pNumPrimitives, const std::vector<size_t> &pVCount, Collada::PrimitiveType pPrimType);
/** Copies the data for a single primitive into the mesh, based on the InputChannels */
/// Copies the data for a single primitive into the mesh, based on the InputChannels
void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
size_t currentPrimitive, const std::vector<size_t> &indices);
/** Reads one triangle of a tristrip into the mesh */
/// Reads one triangle of a tristrip into the mesh
void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh,
std::vector<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices);
/** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
/// Extracts a single object from an input channel and stores it in the appropriate mesh data array
void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
/** Reads the library of node hierarchies and scene parts */
/// Reads the library of node hierarchies and scene parts
void ReadSceneLibrary(XmlNode &node);
/** Reads a scene node's contents including children and stores it in the given node */
/// Reads a scene node's contents including children and stores it in the given node
void ReadSceneNode(XmlNode &node, Collada::Node *pNode);
/** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
void ReadNodeTransformation(XmlNode &node, Collada::Node *pNode, Collada::TransformType pType);
/** Reads a mesh reference in a node and adds it to the node's mesh list */
/// Reads a mesh reference in a node and adds it to the node's mesh list
void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode);
/** Reads the collada scene */
/// Reads the collada scene
void ReadScene(XmlNode &node);
// Processes bind_vertex_input and bind elements
/// Processes bind_vertex_input and bind elements
void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl);
/** Reads embedded textures from a ZAE archive*/
/// Reads embedded textures from a ZAE archive
void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
protected:
/** Calculates the resulting transformation from all the given transform steps */
/// Converts a path read from a collada file to the usual representation
static void UriDecodePath(aiString &ss);
/// Calculates the resulting transformation from all the given transform steps
aiMatrix4x4 CalculateResultTransform(const std::vector<Collada::Transform> &pTransforms) const;
/** Determines the input data type for the given semantic string */
/// Determines the input data type for the given semantic string
Collada::InputType GetTypeForSemantic(const std::string &pSemantic);
/** Finds the item in the given library by its reference, throws if not found */
/// Finds the item in the given library by its reference, throws if not found
template <typename Type>
const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
protected:
// Filename, for a verbose error message
private:
/// Filename, for a verbose error message
std::string mFileName;
// XML reader, member for everyday use
/// XML reader, member for everyday use
XmlParser mXmlParser;
/** All data arrays found in the file by ID. Might be referred to by actually
everyone. Collada, you are a steaming pile of indirection. */
/// All data arrays found in the file by ID. Might be referred to by actually
/// everyone. Collada, you are a steaming pile of indirection.
using DataLibrary = std::map<std::string, Collada::Data> ;
DataLibrary mDataLibrary;
/** Same for accessors which define how the data in a data array is accessed. */
/// Same for accessors which define how the data in a data array is accessed.
using AccessorLibrary = std::map<std::string, Collada::Accessor> ;
AccessorLibrary mAccessorLibrary;
/** Mesh library: mesh by ID */
/// Mesh library: mesh by ID
using MeshLibrary = std::map<std::string, Collada::Mesh *>;
MeshLibrary mMeshLibrary;
/** node library: root node of the hierarchy part by ID */
/// node library: root node of the hierarchy part by ID
using NodeLibrary = std::map<std::string, Collada::Node *>;
NodeLibrary mNodeLibrary;
/** Image library: stores texture properties by ID */
/// Image library: stores texture properties by ID
using ImageLibrary = std::map<std::string, Collada::Image> ;
ImageLibrary mImageLibrary;
/** Effect library: surface attributes by ID */
/// Effect library: surface attributes by ID
using EffectLibrary = std::map<std::string, Collada::Effect> ;
EffectLibrary mEffectLibrary;
/** Material library: surface material by ID */
/// Material library: surface material by ID
using MaterialLibrary = std::map<std::string, Collada::Material> ;
MaterialLibrary mMaterialLibrary;
/** Light library: surface light by ID */
/// Light library: surface light by ID
using LightLibrary = std::map<std::string, Collada::Light> ;
LightLibrary mLightLibrary;
/** Camera library: surface material by ID */
/// Camera library: surface material by ID
using CameraLibrary = std::map<std::string, Collada::Camera> ;
CameraLibrary mCameraLibrary;
/** Controller library: joint controllers by ID */
/// Controller library: joint controllers by ID
using ControllerLibrary = std::map<std::string, Collada::Controller> ;
ControllerLibrary mControllerLibrary;
/** Animation library: animation references by ID */
/// Animation library: animation references by ID
using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
AnimationLibrary mAnimationLibrary;
/** Animation clip library: clip animation references by ID */
/// Animation clip library: clip animation references by ID
using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
AnimationClipLibrary mAnimationClipLibrary;
/** Pointer to the root node. Don't delete, it just points to one of
the nodes in the node library. */
/// Pointer to the root node. Don't delete, it just points to one of the nodes in the node library.
Collada::Node *mRootNode;
/** Root animation container */
/// Root animation container
Collada::Animation mAnims;
/** Size unit: how large compared to a meter */
/// Size unit: how large compared to a meter
ai_real mUnitSize;
/** Which is the up vector */
/// Which is the up vector
enum { UP_X,
UP_Y,
UP_Z } mUpDirection;
/** Asset metadata (global for scene) */
/// Asset metadata (global for scene)
StringMetaData mAssetMetaData;
/** Collada file format version */
/// Collada file format version
Collada::FormatVersion mFormat;
};