/* Copyright (C) 2005-2007 Feeling Software Inc. Portions of the code are: Copyright (C) 2005-2007 Sony Computer Entertainment America MIT License: http://www.opensource.org/licenses/mit-license.php */ /* Based on the FS Import classes: Copyright (C) 2005-2006 Feeling Software Inc Copyright (C) 2005-2006 Autodesk Media Entertainment MIT License: http://www.opensource.org/licenses/mit-license.php */ #include "StdAfx.h" #include "FAXColladaParser.h" #include "FUtils/FUDaeEnum.h" #include "FUtils/FUStringConversion.h" namespace FUDaeParser { // Returns the first child node with a given id xmlNode* FindChildById(xmlNode* parent, const fm::string& id) { if (parent != NULL && !id.empty()) { const char* localId = id.c_str(); if (localId[0] == '#') ++localId; for (xmlNode* child = parent->children; child != NULL; child = child->next) { if (child->type == XML_ELEMENT_NODE) { fm::string nodeId = ReadNodeId(child); if (nodeId == localId) return child; } } } return NULL; } // Returns the first child node of a given "ref" property xmlNode* FindChildByRef(xmlNode* parent, const char* ref) { return FindChildByProperty(parent, DAE_REF_ATTRIBUTE, ref); } // Returns the first child with the given 'sid' value within a given XML hierarchy xmlNode* FindHierarchyChildById(xmlNode* hierarchyRoot, const char* id) { xmlNode* found = NULL; for (xmlNode* child = hierarchyRoot->children; child != NULL && found == NULL; child = child->next) { if (child->type != XML_ELEMENT_NODE) continue; if (ReadNodeId(child) == id) return child; found = FindHierarchyChildById(child, id); } return found; } // Returns the first child with the given 'sid' value within a given XML hierarchy xmlNode* FindHierarchyChildBySid(xmlNode* hierarchyRoot, const char* sid) { if (hierarchyRoot == NULL) return NULL; if (ReadNodeProperty(hierarchyRoot, DAE_SID_ATTRIBUTE) == sid) return hierarchyRoot; xmlNode* found = NULL; for (xmlNode* child = hierarchyRoot->children; child != NULL && found == NULL; child = child->next) { if (child->type != XML_ELEMENT_NODE) continue; //if (ReadNodeProperty(child, DAE_SID_ATTRIBUTE) == sid) return child; found = FindHierarchyChildBySid(child, sid); } return found; } // Returns the first technique node with a given profile xmlNode* FindTechnique(xmlNode* parent, const char* profile) { if (parent != NULL) { xmlNodeList techniqueNodes; FindChildrenByType(parent, DAE_TECHNIQUE_ELEMENT, techniqueNodes); size_t techniqueNodeCount = techniqueNodes.size(); for (size_t i = 0; i < techniqueNodeCount; ++i) { xmlNode* techniqueNode = techniqueNodes[i]; fm::string techniqueProfile = ReadNodeProperty(techniqueNode, DAE_PROFILE_ATTRIBUTE); if (techniqueProfile == profile) return techniqueNode; } } return NULL; } // Returns the accessor node for a given source node xmlNode* FindTechniqueAccessor(xmlNode* parent) { xmlNode* techniqueNode = FindChildByType(parent, DAE_TECHNIQUE_COMMON_ELEMENT); return FindChildByType(techniqueNode, DAE_ACCESSOR_ELEMENT); } // Returns a list of parameter names and nodes held by a given XML node void FindParameters(xmlNode* parent, StringList& parameterNames, xmlNodeList& parameterNodes) { if (parent == NULL || parameterNames.size() != parameterNodes.size()) return; size_t originalCount = parameterNodes.size(); for (xmlNode* child = parent->children; child != NULL; child = child->next) { if (child->type != XML_ELEMENT_NODE) continue; // Drop the technique and exta elements that may be found if (IsEquivalent(child->name, DAE_TECHNIQUE_ELEMENT) || IsEquivalent(child->name, DAE_EXTRA_ELEMENT)) continue; // Buffer this parameter node parameterNodes.push_back(child); } // Retrieve all the parameter's names size_t parameterNodeCount = parameterNodes.size(); parameterNames.resize(parameterNodeCount); for (size_t i = originalCount; i < parameterNodeCount; ++i) { xmlNode* node = parameterNodes[i]; parameterNames[i] = (const char*) node->name; } } // Retrieves a list of floats from a source node // Returns the data's stride. uint32 ReadSource(xmlNode* sourceNode, FloatList& array) { uint32 stride = 0; if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); stride = ReadNodeStride(accessorNode); array.resize(ReadNodeCount(accessorNode) * stride); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToFloatList(arrayContent, array); } return stride; } // Retrieves a list of signed integers from a source node void ReadSource(xmlNode* sourceNode, Int32List& array) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); array.resize(ReadNodeCount(accessorNode)); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToInt32List(arrayContent, array); } } // Retrieves a list of strings from a source node void ReadSource(xmlNode* sourceNode, StringList& array) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); array.resize(ReadNodeCount(accessorNode)); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_NAME_ARRAY_ELEMENT); if (arrayNode == NULL) arrayNode = FindChildByType(sourceNode, DAE_IDREF_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToStringList(arrayContent, array); } } // Retrieves a list of points from a source node void ReadSource(xmlNode* sourceNode, FMVector3List& array) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); array.resize(ReadNodeCount(accessorNode)); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToPointList(arrayContent, array); } } // Retrieves a list of matrices from a source node void ReadSource(xmlNode* sourceNode, FMMatrix44List& array) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); array.resize(ReadNodeCount(accessorNode)); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToMatrixList(arrayContent, array); } } // Retrieves a series of interleaved floats from a source node void ReadSourceInterleaved(xmlNode* sourceNode, fm::pvector& arrays) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); uint32 count = ReadNodeCount(accessorNode); for (fm::pvector::iterator it = arrays.begin(); it != arrays.end(); ++it) { (*it)->resize(count); } // Use the stride to pad the interleaved float lists or remove extra elements uint32 stride = ReadNodeStride(accessorNode); while (stride < arrays.size()) arrays.pop_back(); while (stride > arrays.size()) arrays.push_back(NULL); // Read and parse the float array xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToInterleavedFloatList(arrayContent, arrays); } } uint32 ReadSourceInterleaved(xmlNode* sourceNode, fm::pvector& arrays) { uint32 stride = 1; if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); uint32 count = ReadNodeCount(accessorNode); for (fm::pvector::iterator it = arrays.begin(); it != arrays.end(); ++it) { (*it)->resize(count); } // Backward Compatibility: if the stride is exactly half the expected value, // then we have the old 1D tangents that we need to parse correctly. stride = ReadNodeStride(accessorNode); if (stride > 0 && stride == arrays.size()) { // Read and parse the float array xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* value = ReadNodeContentDirect(arrayNode); for (size_t i = 0; i < count && *value != 0; ++i) { for (size_t j = 0; j < stride && *value != 0; ++j) { arrays[j]->at(i) = FMVector2(FUStringConversion::ToFloat(&value), 0.0f); } } while (*value != 0) { for (size_t i = 0; i < stride && *value != 0; ++i) { arrays[i]->push_back(FMVector2(FUStringConversion::ToFloat(&value), 0.0f)); } } } else { // Use the stride to pad the interleaved float lists or remove extra elements while (stride < arrays.size() * 2) arrays.pop_back(); while (stride > arrays.size() * 2) arrays.push_back(NULL); // Read and parse the float array xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* value = ReadNodeContentDirect(arrayNode); for (size_t i = 0; i < count && *value != 0; ++i) { for (size_t j = 0; 2 * j < stride && *value != 0; ++j) { if (arrays[j] != NULL) { arrays[j]->at(i).u = FUStringConversion::ToFloat(&value); arrays[j]->at(i).v = FUStringConversion::ToFloat(&value); } else { FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); } } } while (*value != 0) { for (size_t i = 0; 2 * i < stride && *value != 0; ++i) { if (arrays[i] != NULL) { FMVector2 v; v.u = FUStringConversion::ToFloat(&value); v.v = FUStringConversion::ToFloat(&value); arrays[i]->push_back(v); } else { FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); } } } } } return stride; } uint32 ReadSourceInterleaved(xmlNode* sourceNode, fm::pvector& arrays) { uint32 stride = 1; if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); uint32 count = ReadNodeCount(accessorNode); for (fm::pvector::iterator it = arrays.begin(); it != arrays.end(); ++it) { (*it)->resize(count); } // Backward Compatibility: if the stride is exactly half the expected value, // then we have the old 1D tangents that we need to parse correctly. stride = ReadNodeStride(accessorNode); if (stride > 0 && stride == arrays.size()) { // Read and parse the float array xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* value = ReadNodeContentDirect(arrayNode); for (size_t i = 0; i < count && *value != 0; ++i) { for (size_t j = 0; j < stride && *value != 0; ++j) { arrays[j]->at(i) = FMVector3(FUStringConversion::ToFloat(&value), 0.0f, 0.0f); } } while (*value != 0) { for (size_t i = 0; i < stride && *value != 0; ++i) { arrays[i]->push_back(FMVector3(FUStringConversion::ToFloat(&value), 0.0f, 0.0f)); } } } else { // Use the stride to pad the interleaved float lists or remove extra elements while (stride < arrays.size() * 3) arrays.pop_back(); while (stride > arrays.size() * 3) arrays.push_back(NULL); // Read and parse the float array xmlNode* arrayNode = FindChildByType(sourceNode, DAE_FLOAT_ARRAY_ELEMENT); const char* value = ReadNodeContentDirect(arrayNode); for (size_t i = 0; i < count && *value != 0; ++i) { for (size_t j = 0; 3 * j < stride && *value != 0; ++j) { if (arrays[j] != NULL) { arrays[j]->at(i).x = FUStringConversion::ToFloat(&value); arrays[j]->at(i).y = FUStringConversion::ToFloat(&value); arrays[j]->at(i).z = FUStringConversion::ToFloat(&value); } else { FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); } } } while (*value != 0) { for (size_t i = 0; 2 * i < stride && *value != 0; ++i) { if (arrays[i] != NULL) { FMVector3 v; v.x = FUStringConversion::ToFloat(&value); v.y = FUStringConversion::ToFloat(&value); v.z = FUStringConversion::ToFloat(&value); arrays[i]->push_back(v); } else { FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); FUStringConversion::ToFloat(&value); } } } } } return stride; } // Retrieves a series of interpolation values from a source node void ReadSourceInterpolation(xmlNode* sourceNode, UInt32List& array) { if (sourceNode != NULL) { // Get the accessor's count xmlNode* accessorNode = FindTechniqueAccessor(sourceNode); uint32 count = ReadNodeCount(accessorNode); array.resize(count); // Backward compatibility: drop the unwanted interpolation values. // Before, we exported one interpolation token for each dimension of a merged curve. // Now, we export one interpolation token for each key of a merged curve. uint32 stride = ReadNodeStride(accessorNode); StringList stringArray(count * stride); xmlNode* arrayNode = FindChildByType(sourceNode, DAE_NAME_ARRAY_ELEMENT); const char* arrayContent = ReadNodeContentDirect(arrayNode); FUStringConversion::ToStringList(arrayContent, stringArray); for (uint32 i = 0; i < count; ++i) { array[i] = (uint32) FUDaeInterpolation::FromString(stringArray[i * stride]); } } } // Retrieves the target property of a targeting node, split into its pointer and its qualifier(s) void ReadNodeTargetProperty(xmlNode* targetingNode, fm::string& pointer, fm::string& qualifier) { fm::string target = ReadNodeProperty(targetingNode, DAE_TARGET_ATTRIBUTE); FUStringConversion::SplitTarget(target, pointer, qualifier); } // Calculate the target pointer for a targetable node void CalculateNodeTargetPointer(xmlNode* target, fm::string& pointer) { if (target != NULL) { // The target node should have either a subid or an id if (HasNodeProperty(target, DAE_ID_ATTRIBUTE)) { pointer = ReadNodeId(target); return; } else if (!HasNodeProperty(target, DAE_SID_ATTRIBUTE)) { pointer.clear(); return; } // Generate a list of parent nodes up to the first properly identified parent xmlNodeList traversal; traversal.reserve(16); traversal.push_back(target); xmlNode* current = target->parent; while (current != NULL) { traversal.push_back(current); if (HasNodeProperty(current, DAE_ID_ATTRIBUTE)) break; current = current->parent; } // The top parent should have the ID property FUSStringBuilder builder; intptr_t nodeCount = (intptr_t) traversal.size(); builder.append(ReadNodeId(traversal[nodeCount - 1])); if (builder.empty()) { pointer.clear(); return; } // Build up the target string for (intptr_t i = nodeCount - 2; i >= 0; --i) { xmlNode* node = traversal[i]; fm::string subId = ReadNodeProperty(node, DAE_SID_ATTRIBUTE); if (!subId.empty()) { builder.append('/'); builder.append(subId); } } pointer = builder.ToString(); } else pointer.clear(); } // Parse the Url attribute off a node FUUri ReadNodeUrl(xmlNode* node, const char* attribute) { fm::string uriString = ReadNodeProperty(node, attribute); return FUUri(TO_FSTRING(uriString)); } // Parse the count attribute off a node uint32 ReadNodeCount(xmlNode* node) { fm::string countString = ReadNodeProperty(node, DAE_COUNT_ATTRIBUTE); return FUStringConversion::ToUInt32(countString); } // Parse the stride attribute off a node uint32 ReadNodeStride(xmlNode* node) { fm::string strideString = ReadNodeProperty(node, DAE_STRIDE_ATTRIBUTE); uint32 stride = FUStringConversion::ToUInt32(strideString); if (stride == 0) stride = 1; return stride; } // Pre-buffer the children of a node, with their ids for performance optimization void ReadChildrenIds(xmlNode* node, FAXNodeIdPairList& pairs) { // To avoid unnecessary memory copies: // Start with calculating the maximum child count uint32 nodeCount = 0; for (xmlNode* child = node->children; child != NULL; child = child->next) { if (child->type == XML_ELEMENT_NODE) ++nodeCount; } // Now, buffer the child nodes and their ids pairs.reserve(nodeCount); for (xmlNode* child = node->children; child != NULL; child = child->next) { if (child->type == XML_ELEMENT_NODE) { FAXNodeIdPair* it = pairs.insert(pairs.end(), FAXNodeIdPair()); it->first = child; it->second = ReadNodePropertyCRC(child, DAE_ID_ATTRIBUTE); } } } // Skip the pound(#) character from a COLLADA id string const char* SkipPound(const fm::string& id) { const char* s = id.c_str(); if (s == NULL) return NULL; else if (*s == '#') ++s; return s; } }