/* Copyright (C) 2005-2006 Feeling Software Inc. MIT License: http://www.opensource.org/licenses/mit-license.php */ #include "StdAfx.h" #include "FUtils/FUDaeParser.h" #include "FUtils/FUDaeWriter.h" using namespace FUDaeParser; #define FLOAT_STR_ESTIMATE 12 namespace FUDaeWriter { // List of common accessor types const char* FUDaeAccessor::XYZW[] = { "X", "Y", "Z", "W", 0 }; const char* FUDaeAccessor::RGBA[] = { "R", "G", "B", "A", 0 }; const char* FUDaeAccessor::STPQ[] = { "S", "T", "P", "Q", 0 }; // Write out the element unto the given parent xml tree node. // Check for only one element for this profile. xmlNode* AddExtraTechniqueChild(xmlNode* parent, const char* profile) { xmlNode* techniqueNode = NULL; if (parent != NULL) { xmlNode* extraNode = AddChildOnce(parent, DAE_EXTRA_ELEMENT); techniqueNode = AddTechniqueChild(extraNode, profile); } return techniqueNode; } // Write out the element unto the given parent xml tree node. // Check for only one element for this profile. xmlNode* AddTechniqueChild(xmlNode* parent, const char* profile) { xmlNode* techniqueNode = NULL; if (parent != NULL) { techniqueNode = FindTechnique(parent, profile); if (techniqueNode == NULL) { techniqueNode = AddChild(parent, DAE_TECHNIQUE_ELEMENT); AddAttribute(techniqueNode, DAE_PROFILE_ATTRIBUTE, profile); } } return techniqueNode; } xmlNode* AddParameter(xmlNode* parent, const char* name, const char* type) { xmlNode* parameterNode = AddChild(parent, DAE_PARAMETER_ELEMENT); if (name != NULL) AddAttribute(parameterNode, DAE_NAME_ATTRIBUTE, name); if (type == NULL) type = DAE_FLOAT_TYPE; AddAttribute(parameterNode, DAE_TYPE_ATTRIBUTE, type); return parameterNode; } xmlNode* AddInput(xmlNode* parent, const char* sourceId, const char* semantic, int32 offset, int32 set) { if (sourceId == NULL || *sourceId == 0 || semantic == NULL || *semantic == 0) return NULL; xmlNode* inputNode = AddChild(parent, DAE_INPUT_ELEMENT); AddAttribute(inputNode, DAE_SEMANTIC_ATTRIBUTE, semantic); AddAttribute(inputNode, DAE_SOURCE_ATTRIBUTE, string("#") + sourceId); if (offset >= 0) AddAttribute(inputNode, DAE_OFFSET_ATTRIBUTE, offset); if (set >= 0) AddAttribute(inputNode, DAE_SET_ATTRIBUTE, set); return inputNode; } xmlNode* AddArray(xmlNode* parent, const char* id, const char* arrayType, const char* content, size_t count) { xmlNode* arrayNode = AddChild(parent, arrayType, content); AddAttribute(arrayNode, DAE_ID_ATTRIBUTE, id); AddAttribute(arrayNode, DAE_COUNT_ATTRIBUTE, count); return arrayNode; } xmlNode* AddArray(xmlNode* parent, const char* id, const FMVector3List& values, float lengthFactor) { // Reserve the necessary space within the string builder globalSBuilder.clear(); size_t valueCount = values.size(); globalSBuilder.reserve(valueCount * 3 * FLOAT_STR_ESTIMATE); if (valueCount > 0) { // Write out the values FMVector3List::const_iterator itP = values.begin(); FUStringConversion::ToString(globalSBuilder, *itP, lengthFactor); for (++itP; itP != values.end(); ++itP) { globalSBuilder.append(' '); FUStringConversion::ToString(globalSBuilder, *itP, lengthFactor); } } // Create the typed array node. return AddArray(parent, id, DAE_FLOAT_ARRAY_ELEMENT, globalSBuilder.ToCharPtr(), valueCount * 3); } xmlNode* AddArray(xmlNode* parent, const char* id, const FMMatrix44List& values, float lengthFactor) { globalSBuilder.clear(); size_t valueCount = values.size(); globalSBuilder.reserve(valueCount * 16 * FLOAT_STR_ESTIMATE); if (valueCount > 0) { FMMatrix44List::const_iterator itM = values.begin(); FUStringConversion::ToString(globalSBuilder, *itM, lengthFactor); for (++itM; itM != values.end(); ++itM) { globalSBuilder.append(' '); FUStringConversion::ToString(globalSBuilder, *itM, lengthFactor); } } return AddArray(parent, id, DAE_FLOAT_ARRAY_ELEMENT, globalSBuilder.ToCharPtr(), valueCount * 16); } xmlNode* AddArray(xmlNode* parent, const char* id, const FloatList& values, float lengthFactor) { size_t valueCount = values.size(); globalSBuilder.clear(); globalSBuilder.reserve(valueCount * FLOAT_STR_ESTIMATE); FUStringConversion::ToString(globalSBuilder, values, lengthFactor); return AddArray(parent, id, DAE_FLOAT_ARRAY_ELEMENT, globalSBuilder.ToCharPtr(), valueCount); } xmlNode* AddArray(xmlNode* parent, const char* id, const StringList& values, const char* arrayType) { size_t valueCount = values.size(); globalSBuilder.reserve(valueCount * 18); // Pulled out of a hat globalSBuilder.clear(); if (valueCount > 0) { StringList::const_iterator itV = values.begin(); globalSBuilder.set(*itV); for (++itV; itV != values.end(); ++itV) { globalSBuilder.append(' '); globalSBuilder.append(*itV); } } return AddArray(parent, id, arrayType, globalSBuilder.ToCharPtr(), valueCount); } xmlNode* AddAccessor(xmlNode* parent, const char* arrayId, size_t count, size_t stride, const char** parameters, const char* type) { // Create the accessor element and fill its basic properties xmlNode* accessorNode = AddChild(parent, DAE_ACCESSOR_ELEMENT); AddAttribute(accessorNode, DAE_SOURCE_ATTRIBUTE, string("#") + arrayId); AddAttribute(accessorNode, DAE_COUNT_ATTRIBUTE, count); AddAttribute(accessorNode, DAE_STRIDE_ATTRIBUTE, stride); // Create the stride parameters if (type == NULL) type = DAE_FLOAT_TYPE; if (!IsEquivalent(type, DAE_MATRIX_TYPE)) { size_t p = 0; for (size_t i = 0; i < stride; ++i) { const char* parameter = NULL; if (parameters != NULL) { parameter = parameters[p++]; if (parameter == NULL) { parameter = parameters[0]; p = 0; } } AddParameter(accessorNode, parameter, type); } } else { const char* parameter = (parameters != NULL) ? *parameters : NULL; AddParameter(accessorNode, parameter, type); } return accessorNode; } xmlNode* AddSourceMatrix(xmlNode* parent, const char* id, const FMMatrix44List& values, float lengthFactor) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values, lengthFactor); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 16, NULL, DAE_MATRIX_TYPE); return sourceNode; } xmlNode* AddSourceColor(xmlNode* parent, const char* id, const FMVector3List& values) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 3, FUDaeAccessor::RGBA, DAE_FLOAT_TYPE); return sourceNode; } xmlNode* AddSourceTexcoord(xmlNode* parent, const char* id, const FMVector3List& values) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 3, FUDaeAccessor::STPQ, DAE_FLOAT_TYPE); return sourceNode; } xmlNode* AddSourcePosition(xmlNode* parent, const char* id, const FMVector3List& values, float lengthFactor) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values, lengthFactor); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 3, FUDaeAccessor::XYZW, DAE_FLOAT_TYPE); return sourceNode; } xmlNode* AddSourceFloat(xmlNode* parent, const char* id, const FloatList& values, const char* parameter, float lengthFactor) { return AddSourceFloat(parent, id, values, 1, ¶meter, lengthFactor); } xmlNode* AddSourceFloat(xmlNode* parent, const char* id, const FloatList& values, size_t stride, const char** parameters, float lengthFactor) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values, lengthFactor); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); if (stride == 0) stride = 1; AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size() / stride, stride, parameters, DAE_FLOAT_TYPE); return sourceNode; } xmlNode* AddSourceString(xmlNode* parent, const char* id, const StringList& values, const char* parameter) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values, DAE_NAME_ARRAY_ELEMENT); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 1, ¶meter, DAE_NAME_TYPE); return sourceNode; } xmlNode* AddSourceIDRef(xmlNode* parent, const char* id, const StringList& values, const char* parameter) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); AddArray(sourceNode, arrayId.ToCharPtr(), values, DAE_IDREF_ARRAY_ELEMENT); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), values.size(), 1, ¶meter, DAE_IDREF_TYPE); return sourceNode; } xmlNode* AddSourceInterpolation(xmlNode* parent, const char* id, const FUDaeInterpolationList& interpolations) { xmlNode* sourceNode = AddChild(parent, DAE_SOURCE_ELEMENT); AddAttribute(sourceNode, DAE_ID_ATTRIBUTE, id); FUSStringBuilder arrayId(id); arrayId.append("-array"); globalSBuilder.clear(); size_t valueCount = interpolations.size(); if (valueCount > 0) { FUDaeInterpolationList::const_iterator itI = interpolations.begin(); globalSBuilder.append(FUDaeInterpolation::ToString(*itI)); for (++itI; itI != interpolations.end(); ++itI) { globalSBuilder.append(' '); globalSBuilder.append(FUDaeInterpolation::ToString(*itI)); } } AddArray(sourceNode, arrayId.ToCharPtr(), DAE_NAME_ARRAY_ELEMENT, globalSBuilder.ToCharPtr(), valueCount); xmlNode* techniqueCommonNode = AddChild(sourceNode, DAE_TECHNIQUE_COMMON_ELEMENT); const char* parameter = "INTERPOLATION"; AddAccessor(techniqueCommonNode, arrayId.ToCharPtr(), valueCount, 1, ¶meter, DAE_NAME_TYPE); return sourceNode; } // Clean-up the id and names to match the schema definitions of 'IDref' and 'Name'. string CleanId(const char* c) { globalSBuilder.clear(); if (*c != 0) { // First character: alphabetic or '_'. if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || *c == '_') globalSBuilder += *c; else globalSBuilder += '_'; // Other characters: alphabetic, numeric or '_'. // Otherwise, use HTML extended character write-up: &#; for (++c; *c != 0; ++c) { if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_' || *c == '-') globalSBuilder += *c; else globalSBuilder += '_'; } } return globalSBuilder.ToString(); } fstring CleanName(const fchar* c) { globalBuilder.clear(); if (*c != 0) { // First character: alphabetic, '_' or ':'. if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || *c == '_' || *c == ':') globalBuilder += *c; else globalBuilder += '_'; // Other characters: alphabetic, numeric, '_', ':', '-' or '.'. // Otherwise, use HTML extended character write-up: &#; for (++c; *c != 0; ++c) { if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_' || *c == ':' || *c == '-' || *c == '.') globalBuilder += *c; else { globalBuilder += '%'; globalBuilder += (uint32) *c; } } } return globalBuilder.ToString(); } // Add an 'sid' attribute to the given xml node, ensuring unicity. Returns the final 'sid' value. FCOLLADA_EXPORT string AddNodeSid(xmlNode* node, const char* wantedSid) { // Find the first parent node with an id or sid. If this node has an id, return right away. xmlNode* parentNode = node; for (parentNode = node; parentNode != NULL; parentNode = parentNode->parent) { if (HasNodeProperty(parentNode, DAE_ID_ATTRIBUTE) || HasNodeProperty(parentNode, DAE_SID_ATTRIBUTE)) break; } if (parentNode == node) { if (!HasNodeProperty(parentNode, DAE_SID_ATTRIBUTE)) AddAttribute(node, DAE_SID_ATTRIBUTE, wantedSid); return wantedSid; } if (parentNode == NULL) { // Retrieve the last parent node available for (parentNode = node; parentNode->parent != NULL; parentNode = parentNode->parent) {} } // Check the wanted sid for unicity xmlNode* existingNode = FindHierarchyChildBySid(parentNode, wantedSid); if (existingNode == NULL) { AddAttribute(node, DAE_SID_ATTRIBUTE, wantedSid); return wantedSid; } // Generate new sids with an incremental counter. for (uint32 counter = 2; counter < 100; ++counter) { globalSBuilder.set(wantedSid); globalSBuilder.append(counter); existingNode = FindHierarchyChildBySid(parentNode, globalSBuilder.ToCharPtr()); if (existingNode == NULL) { AddAttribute(node, DAE_SID_ATTRIBUTE, globalSBuilder); return globalSBuilder.ToString(); } } return string(""); } };