/* 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 */ #include "StdAfx.h" #include "FArchiveXML.h" #include "FCDocument/FCDocument.h" #include "FCDocument/FCDLibrary.h" #include "FCDocument/FCDAnimated.h" #include "FCDocument/FCDAnimation.h" #include "FCDocument/FCDAnimationClip.h" #include "FCDocument/FCDAnimationKey.h" #include "FCDocument/FCDAnimationChannel.h" #include "FCDocument/FCDAnimationCurve.h" #include "FCDocument/FCDAnimationMultiCurve.h" #include "FCDocument/FCDAnimationCurveTools.h" xmlNode* FArchiveXML::WriteAnimationChannel(FCDObject* object, xmlNode* parentNode) { FCDAnimationChannel* animationChannel = (FCDAnimationChannel*)object; FCDAnimationChannelData& data = FArchiveXML::documentLinkDataMap[animationChannel->GetDocument()].animationChannelData[animationChannel]; //FUAssert(!data.targetPointer.empty(), NULL); fm::string baseId = FCDObjectWithId::CleanId(animationChannel->GetParent()->GetDaeId() + "_" + data.targetPointer); // Check for curve merging uint32 realCurveCount = 0; const FCDAnimationCurve* masterCurve = NULL; FCDAnimationCurveList mergingCurves; mergingCurves.resize(data.defaultValues.size()); bool mergeCurves = true; size_t curveCount = animationChannel->GetCurveCount(); for (size_t i = 0; i < curveCount; ++i) { const FCDAnimationCurve* curve = animationChannel->GetCurve(i); if (curve != NULL) { // Check that we have a default placement for this curve in the default value listing size_t dv; for (dv = 0; dv < data.defaultValues.size(); ++dv) { if (data.defaultValues[dv].curve == curve) { mergingCurves[dv] = const_cast(curve); break; } } mergeCurves &= dv != data.defaultValues.size(); // Check that the curves can be merged correctly. ++realCurveCount; if (masterCurve == NULL) { masterCurve = curve; } else { // Check the infinity types, the keys and the interpolations. size_t curveKeyCount = curve->GetKeyCount(); size_t masterKeyCount = masterCurve->GetKeyCount(); mergeCurves &= masterKeyCount == curveKeyCount; if (!mergeCurves) break; for (size_t j = 0; j < curveKeyCount && mergeCurves; ++j) { const FCDAnimationKey* curveKey = curve->GetKey(j); const FCDAnimationKey* masterKey = masterCurve->GetKey(j); mergeCurves &= IsEquivalent(curveKey->input, masterKey->input); mergeCurves &= curveKey->interpolation == masterKey->interpolation; // Prevent curve having TCB interpolation from merging mergeCurves &= curveKey->interpolation != FUDaeInterpolation::TCB; mergeCurves &= masterKey->interpolation != FUDaeInterpolation::TCB; } if (!mergeCurves) break; mergeCurves &= curve->GetPostInfinity() == masterCurve->GetPostInfinity(); mergeCurves &= curve->GetPreInfinity() == masterCurve->GetPreInfinity(); } // Disallow the merging of any curves with a driver. mergeCurves &= !curve->HasDriver(); } } if (mergeCurves && realCurveCount > 1) { // Prepare the list of default values FloatList values; values.reserve(data.defaultValues.size()); for (FAXAnimationChannelDefaultValueList::iterator itDV = data.defaultValues.begin(); itDV != data.defaultValues.end(); ++itDV) { values.push_back((*itDV).defaultValue); } FUAssert(data.animatedValue != NULL, return parentNode); const char** qualifiers = new const char*[values.size()]; memset(qualifiers, 0, sizeof(char*) * values.size()); for (size_t i = 0; i < values.size() && i < data.animatedValue->GetValueCount(); ++i) { qualifiers[i] = data.animatedValue->GetQualifier(i); } // Merge and export the curves FCDAnimationMultiCurve* multiCurve = FCDAnimationCurveTools::MergeCurves(mergingCurves, values); FArchiveXML::WriteSourceFCDAnimationMultiCurve(multiCurve, parentNode, qualifiers, baseId); FArchiveXML::WriteSamplerFCDAnimationMultiCurve(multiCurve, parentNode, baseId); FArchiveXML::WriteChannelFCDAnimationMultiCurve(multiCurve, parentNode, baseId, data.targetPointer); SAFE_RELEASE(multiCurve); } else { // Interlace the curve's sources, samplers and channels // Generate new ids for each of the curve's data sources, to avoid collision in special cases size_t curveCount = animationChannel->GetCurveCount(); StringList ids; ids.resize(curveCount); FUSStringBuilder curveId; for (size_t c = 0; c < curveCount; ++c) { FCDAnimationCurve* curCurve = animationChannel->GetCurve(c); if (curCurve != NULL) { FCDAnimationCurveData& curveData = FArchiveXML::documentLinkDataMap[curCurve->GetDocument()].animationCurveData[curCurve]; //FUAssert(curveDataIt != FArchiveXML::animationCurveData.end(), NULL); // Generate a valid id for this curve curveId.set(baseId); if (curveData.targetElement >= 0) { curveId.append('_'); curveId.append(curveData.targetElement); curveId.append('_'); } curveId.append(curveData.targetQualifier); ids[c] = FCDObjectWithId::CleanId(curveId.ToCharPtr()); // Write out the curve's sources FArchiveXML::WriteSourceFCDAnimationCurve(animationChannel->GetCurve(c), parentNode, ids[c]); } } for (size_t c = 0; c < curveCount; ++c) { if (animationChannel->GetCurve(c) != NULL) FArchiveXML::WriteSamplerFCDAnimationCurve(animationChannel->GetCurve(c), parentNode, ids[c]); } for (size_t c = 0; c < curveCount; ++c) { if (animationChannel->GetCurve(c) != NULL) FArchiveXML::WriteChannelFCDAnimationCurve(animationChannel->GetCurve(c), parentNode, ids[c], data.targetPointer.c_str()); } } data.defaultValues.clear(); return parentNode; } xmlNode* FArchiveXML::WriteAnimationCurve(FCDObject* UNUSED(object), xmlNode* UNUSED(parentNode)) { // // Currently not reachable // FUBreak; return NULL; } xmlNode* FArchiveXML::WriteAnimationMultiCurve(FCDObject* UNUSED(object), xmlNode* UNUSED(parentNode)) { // // Currently not reachable // FUBreak; return NULL; } xmlNode* FArchiveXML::WriteAnimation(FCDObject* object, xmlNode* parentNode) { FCDAnimation* animation = (FCDAnimation*)object; xmlNode* animationNode = FArchiveXML::WriteToEntityXMLFCDEntity(animation, parentNode, DAE_ANIMATION_ELEMENT); // Write out the local channels size_t channelCount = animation->GetChannelCount(); for (size_t i = 0; i < channelCount; ++i) { FArchiveXML::LetWriteObject(animation->GetChannel(i), animationNode); } // Write out the child animations size_t childCount = animation->GetChildrenCount(); for (size_t i = 0; i < childCount; ++i) { FArchiveXML::LetWriteObject(animation->GetChild(i), animationNode); } FArchiveXML::WriteEntityExtra(animation, animationNode); return animationNode; } xmlNode* FArchiveXML::WriteAnimationClip(FCDObject* object, xmlNode* parentNode) { FCDAnimationClip* animationClip = (FCDAnimationClip*)object; // Create the element and write out its start/end information. xmlNode* clipNode = FArchiveXML::WriteToEntityXMLFCDEntity(animationClip, parentNode, DAE_ANIMCLIP_ELEMENT); AddAttribute(clipNode, DAE_START_ATTRIBUTE, animationClip->GetStart()); AddAttribute(clipNode, DAE_END_ATTRIBUTE, animationClip->GetEnd()); // Build a list of the animations to instantiate // from the list of curves for this clip typedef fm::pvector FCDAnimationConstList; FCDAnimationConstList animations; for (FCDAnimationCurveTrackList::iterator itC = animationClip->GetClipCurves().begin(); itC != animationClip->GetClipCurves().end(); ++itC) { const FCDAnimationChannel* channel = (*itC)->GetParent(); if (channel == NULL) continue; const FCDAnimation* animation = channel->GetParent(); if (animations.find(animation) == animations.end()) { animations.push_back(animation); } } // Instantiate all the animations for (FCDAnimationConstList::iterator itA = animations.begin(); itA != animations.end(); ++itA) { xmlNode* instanceNode = AddChild(clipNode, DAE_INSTANCE_ANIMATION_ELEMENT); AddAttribute(instanceNode, DAE_URL_ATTRIBUTE, fm::string("#") + (*itA)->GetDaeId()); } FArchiveXML::WriteEntityExtra(animationClip, clipNode); return clipNode; } // Writes out a value's animations, if any, to the animation library of a COLLADA XML document. bool FArchiveXML::WriteAnimatedValue(const FCDParameterAnimatable* value, xmlNode* valueNode, const char* wantedSid, int32 arrayElement) { if (value->IsAnimated() && valueNode != NULL) { // Find the value's animations FCDAnimated* animated = const_cast(value->GetAnimated()); if (!animated->HasCurve()) return false; animated->SetArrayElement(arrayElement); FArchiveXML::WriteAnimatedValue(animated, valueNode, wantedSid); return true; } return false; } void FArchiveXML::WriteAnimatedValue(const FCDAnimated* _animated, xmlNode* valueNode, const char* wantedSid) { FCDAnimated* animated = const_cast(_animated); int32 arrayElement = animated->GetArrayElement(); FCDAnimatedData& animatedData = FArchiveXML::documentLinkDataMap[animated->GetDocument()].animatedData[animated]; // Set a sid unto the XML tree node, in order to support animations if (!HasNodeProperty(valueNode, DAE_SID_ATTRIBUTE) && !HasNodeProperty(valueNode, DAE_ID_ATTRIBUTE)) { AddNodeSid(valueNode, wantedSid); } // Calculate the XML tree node's target for the animation channel CalculateNodeTargetPointer(valueNode, animatedData.pointer); if (!animatedData.pointer.empty()) { FCDAnimationChannelList channels; // Enforce the target pointer on all the curves for (size_t i = 0; i < animated->GetValueCount(); ++i) { FCDAnimationCurveTrackList& curves = animated->GetCurves()[i]; for (FCDAnimationCurveTrackList::iterator itC = curves.begin(); itC != curves.end(); ++itC) { FCDAnimationCurveData& curveData = FArchiveXML::documentLinkDataMap[(*itC)->GetDocument()].animationCurveData[*itC]; (*itC)->SetTargetElement(arrayElement); (*itC)->SetTargetQualifier(animated->GetQualifier(i)); curveData.targetElement = arrayElement; curveData.targetQualifier = animated->GetQualifier(i); FCDAnimationChannel* channel = (*itC)->GetParent(); FCDAnimationChannelData& channelData = FArchiveXML::documentLinkDataMap[channel->GetDocument()].animationChannelData[channel]; FUAssert(channel != NULL, continue); channelData.targetPointer = animatedData.pointer; channelData.animatedValue = animated; if (!channels.contains(channel)) channels.push_back(channel); } } // Enforce the default values on the channels. This is used for curve merging. for (FCDAnimationChannelList::iterator itC = channels.begin(); itC != channels.end(); ++itC) { FCDAnimationChannel* channel = (*itC); FCDAnimationChannelData& channelData = FArchiveXML::documentLinkDataMap[channel->GetDocument()].animationChannelData[channel]; for (size_t i = 0; i < animated->GetValueCount(); ++i) { FCDAnimationCurveTrackList& curves = animated->GetCurves()[i]; // Find the curve, if any, that comes from this channel. FCDAnimationCurve* curve = NULL; for (size_t j = 0; j < curves.size() && curve == NULL; ++j) { if (curves[j]->GetParent() == channel) { curve = curves[j]; break; } } float defaultValue = !animated->GetRelativeAnimationFlag() ? *animated->GetValue(i) : 0.0f; channelData.defaultValues.push_back(FAXAnimationChannelDefaultValue(curve, defaultValue)); } } } } void FArchiveXML::WriteSourceFCDAnimationCurve(FCDAnimationCurve* animationCurve, xmlNode* parentNode, const fm::string& baseId) { FCDAnimationCurveDataMap::iterator it = FArchiveXML::documentLinkDataMap[animationCurve->GetDocument()].animationCurveData.find(animationCurve); FUAssert(it != FArchiveXML::documentLinkDataMap[animationCurve->GetDocument()].animationCurveData.end(),); FCDAnimationCurveData& data = it->second; const char* parameter = data.targetQualifier.c_str(); if (*parameter == '.') ++parameter; // Retrieve the source information data bool hasTangents = false, hasTCB = false; for (size_t i = 0; i < animationCurve->GetKeyCount(); ++i) { hasTangents |= animationCurve->GetKey(i)->interpolation == FUDaeInterpolation::BEZIER; hasTCB |= animationCurve->GetKey(i)->interpolation == FUDaeInterpolation::TCB; } // Prebuffer the source information lists size_t keyCount = animationCurve->GetKeyCount(); FloatList inputs; inputs.reserve(keyCount); FloatList outputs; outputs.reserve(keyCount); UInt32List interpolations; interpolations.reserve(keyCount); FMVector2List inTangents; if (hasTangents) inTangents.reserve(keyCount); FMVector2List outTangents; if (hasTangents) outTangents.reserve(keyCount); FMVector3List tcbs; if (hasTCB) tcbs.reserve(keyCount); FMVector2List eases; if (hasTCB) eases.reserve(keyCount); // Retrieve the source information data for (size_t i = 0; i < animationCurve->GetKeyCount(); ++i) { FCDAnimationKey* key = animationCurve->GetKey(i); inputs.push_back(key->input); outputs.push_back(key->output); if (hasTangents) { if (key->interpolation == FUDaeInterpolation::BEZIER) { FCDAnimationKeyBezier* bkey = (FCDAnimationKeyBezier*)key; if (inTangents.size() < interpolations.size()) { // Grow to the correct index inTangents.resize(interpolations.size(), FMVector2::Zero); outTangents.resize(interpolations.size(), FMVector2::Zero); } inTangents.push_back(bkey->inTangent); outTangents.push_back(bkey->outTangent); } else { // Export flat tangents inTangents.push_back(FMVector2(key->input - 0.0001f, key->output)); outTangents.push_back(FMVector2(key->input + 0.0001f, key->output)); } } if (hasTCB) { if (key->interpolation == FUDaeInterpolation::TCB) { FCDAnimationKeyTCB* tkey = (FCDAnimationKeyTCB*)key; if (tcbs.size() < interpolations.size()) { // Grow to the correct index tcbs.resize(interpolations.size(), FMVector3::Zero); eases.resize(interpolations.size(), FMVector2::Zero); } tcbs.push_back(FMVector3(tkey->tension, tkey->continuity, tkey->bias)); eases.push_back(FMVector2(tkey->easeIn, tkey->easeOut)); } else { // Export flat tangents tcbs.push_back(FMVector3(0.5f, 0.5f, 0.5f)); eases.push_back(FMVector2::Zero); } } // Append the interpolation type last, because we use this list's size to correctly // index TCB and Bezier information when exporting mixed-interpolation curves. interpolations.push_back(key->interpolation); } // Export the data arrays. xmlNode* sourceNode = AddSourceFloat(parentNode, baseId + "-input", inputs, "TIME"); AddSourceFloat(parentNode, baseId + "-output", outputs, parameter); AddSourceInterpolation(parentNode, baseId + "-interpolations", *(FUDaeInterpolationList*)(size_t)&interpolations); if (!inTangents.empty() && !outTangents.empty()) { AddSourceTangent(parentNode, baseId + "-intangents", inTangents); AddSourceTangent(parentNode, baseId + "-outtangents", outTangents); } if (!tcbs.empty() && !eases.empty()) { AddSourceFloat(parentNode, baseId + "-tcbs", tcbs); AddSourceFloat(parentNode, baseId + "-eases", eases); } // Export the infinity parameters xmlNode* mayaTechnique = AddTechniqueChild(sourceNode, DAEMAYA_MAYA_PROFILE); fm::string infinityType = FUDaeInfinity::ToString(animationCurve->GetPreInfinity()); AddChild(mayaTechnique, DAEMAYA_PREINFINITY_PARAMETER, infinityType); infinityType = FUDaeInfinity::ToString(animationCurve->GetPostInfinity()); AddChild(mayaTechnique, DAEMAYA_POSTINFINITY_PARAMETER, infinityType); } xmlNode* FArchiveXML::WriteSamplerFCDAnimationCurve(FCDAnimationCurve* animationCurve, xmlNode* parentNode, const fm::string& baseId) { xmlNode* samplerNode = AddChild(parentNode, DAE_SAMPLER_ELEMENT); AddAttribute(samplerNode, DAE_ID_ATTRIBUTE, baseId + "-sampler"); // Retrieve the source information data bool hasTangents = false, hasTCB = false; for (size_t i = 0; i < animationCurve->GetKeyCount(); ++i) { hasTangents |= animationCurve->GetKey(i)->interpolation == FUDaeInterpolation::BEZIER; hasTCB |= animationCurve->GetKey(i)->interpolation == FUDaeInterpolation::TCB; } // Add the sampler inputs AddInput(samplerNode, baseId + "-input", DAE_INPUT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-output", DAE_OUTPUT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-interpolations", DAE_INTERPOLATION_ANIMATION_INPUT); if (hasTangents) { AddInput(samplerNode, baseId + "-intangents", DAE_INTANGENT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-outtangents", DAE_OUTTANGENT_ANIMATION_INPUT); } if (hasTCB) { AddInput(samplerNode, baseId + "-tcbs", DAEFC_TCB_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-eases", DAEFC_EASE_INOUT_ANIMATION_INPUT); } // Add the driver input if (animationCurve->HasDriver()) { FCDAnimatedDataMap::iterator it = FArchiveXML::documentLinkDataMap[animationCurve->GetDriverPtr()->GetDocument()].animatedData.find(animationCurve->GetDriverPtr()); FUAssert(it != FArchiveXML::documentLinkDataMap[animationCurve->GetDriverPtr()->GetDocument()].animatedData.end(),); FCDAnimatedData& data = it->second; FUSStringBuilder builder(data.pointer); int32 driverElement = animationCurve->GetDriverIndex(); if (driverElement >= 0) { builder.append('('); builder.append(driverElement); builder.append(')'); } if (animationCurve->GetDriverIndex() == 0) { builder.append('('); builder.append(animationCurve->GetDriverIndex()); builder.append(')'); } AddInput(samplerNode, builder.ToCharPtr(), DAEMAYA_DRIVER_INPUT); } return samplerNode; } xmlNode* FArchiveXML::WriteChannelFCDAnimationCurve(FCDAnimationCurve* animationCurve, xmlNode* parentNode, const fm::string& baseId, const char* targetPointer) { xmlNode* channelNode = AddChild(parentNode, DAE_CHANNEL_ELEMENT); AddAttribute(channelNode, DAE_SOURCE_ATTRIBUTE, fm::string("#") + baseId + "-sampler"); FCDAnimationCurveDataMap::iterator it = FArchiveXML::documentLinkDataMap[animationCurve->GetDocument()].animationCurveData.find(animationCurve); FUAssert(it != FArchiveXML::documentLinkDataMap[animationCurve->GetDocument()].animationCurveData.end(),); FCDAnimationCurveData& data = it->second; // Generate and export the channel target FUSStringBuilder builder(targetPointer); if (data.targetElement >= 0) { builder.append('('); builder.append(data.targetElement); builder.append(')'); } builder.append(data.targetQualifier); AddAttribute(channelNode, DAE_TARGET_ATTRIBUTE, builder); return channelNode; } // Write out the specific animation elements to the COLLADA XML tree node void FArchiveXML::WriteSourceFCDAnimationMultiCurve(FCDAnimationMultiCurve* animationMultiCurve, xmlNode* parentNode, const char** qualifiers, const fm::string& baseId) { if (animationMultiCurve->GetDimension() == 0) return; // Generate the list of the parameters typedef const char* pchar; pchar* parameters = new pchar[animationMultiCurve->GetDimension()]; for (size_t i = 0; i < animationMultiCurve->GetDimension(); ++i) { parameters[i] = qualifiers[i]; if (*(parameters[i]) == '.') ++(parameters[i]); } if (animationMultiCurve->GetDimension() == 16) { // Hack to avoid parameters on matrices. SAFE_DELETE_ARRAY(parameters); } // Retrieve the source information data bool hasTangents = false, hasTCB = false; for (size_t i = 0; i < animationMultiCurve->GetKeyCount(); ++i) { hasTangents |= animationMultiCurve->GetKey(i)->interpolation == FUDaeInterpolation::BEZIER; hasTCB |= animationMultiCurve->GetKey(i)->interpolation == FUDaeInterpolation::TCB; } // Prebuffer the source information lists size_t keyCount = animationMultiCurve->GetKeyCount(); FloatList inputs; inputs.reserve(keyCount); FloatList outputs; outputs.reserve(keyCount * animationMultiCurve->GetDimension()); UInt32List interpolations; interpolations.reserve(keyCount); FloatList inTangents; if (hasTangents) inTangents.reserve(keyCount * animationMultiCurve->GetDimension() * 2); FloatList outTangents; if (hasTangents) outTangents.reserve(keyCount * animationMultiCurve->GetDimension() * 2); FloatList tcbs; if (hasTCB) tcbs.reserve(keyCount * animationMultiCurve->GetDimension() * 3); FloatList eases; if (hasTCB) eases.reserve(keyCount * animationMultiCurve->GetDimension() * 2); // Retrieve the source information data for (size_t i = 0; i < animationMultiCurve->GetKeyCount(); ++i) { FCDAnimationMKey* key = animationMultiCurve->GetKey(i); inputs.push_back(key->input); for (uint32 i = 0; i < animationMultiCurve->GetDimension(); ++i) { outputs.push_back(key->output[i]); if (hasTangents) { if (key->interpolation == FUDaeInterpolation::BEZIER) { FCDAnimationMKeyBezier* bkey = (FCDAnimationMKeyBezier*)key; if (inTangents.size() * animationMultiCurve->GetDimension() * 2 < interpolations.size()) { // Grow to the correct index inTangents.resize(interpolations.size() * animationMultiCurve->GetDimension() * 2, 0.0f); outTangents.resize(interpolations.size() * animationMultiCurve->GetDimension() * 2, 0.0f); } inTangents.push_back(bkey->inTangent[i].u); inTangents.push_back(bkey->inTangent[i].v); outTangents.push_back(bkey->outTangent[i].u); outTangents.push_back(bkey->outTangent[i].v); } else { // Export flat tangents inTangents.push_back(key->input - 0.0001f); inTangents.push_back(key->output[i]); outTangents.push_back(key->input + 0.0001f); outTangents.push_back(key->output[i]); } } if (hasTCB) { if (key->interpolation == FUDaeInterpolation::TCB) { FCDAnimationMKeyTCB* tkey = (FCDAnimationMKeyTCB*)key; if (tcbs.size() * animationMultiCurve->GetDimension() * 3 < interpolations.size()) { // Grow to the correct index tcbs.resize(interpolations.size() * animationMultiCurve->GetDimension() * 3, 0.0f); eases.resize(interpolations.size() * animationMultiCurve->GetDimension() * 2, 0.0f); } tcbs.push_back(tkey->tension[i]); tcbs.push_back(tkey->continuity[i]); tcbs.push_back(tkey->bias[i]); eases.push_back(tkey->easeIn[i]); eases.push_back(tkey->easeOut[i]); } else { // Export flat tangents tcbs.push_back(0.5f); tcbs.push_back(0.5f); tcbs.push_back(0.5f); eases.push_back(0.0f); eases.push_back(0.0f); } } } // Append the interpolation type last, because we use this list's size to correctly // index TCB and Bezier information when exporting mixed-interpolation curves. interpolations.push_back(key->interpolation); } // Export the data arrays xmlNode* inputSourceNode = AddSourceFloat(parentNode, baseId + "-input", inputs, "TIME"); AddSourceFloat(parentNode, baseId + "-output", outputs, animationMultiCurve->GetDimension(), parameters); AddSourceInterpolation(parentNode, baseId + "-interpolations", *(FUDaeInterpolationList*) (size_t) &interpolations); if (!inTangents.empty()) { AddSourceFloat(parentNode, baseId + "-intangents", inTangents, animationMultiCurve->GetDimension() * 2, FUDaeAccessor::XY); AddSourceFloat(parentNode, baseId + "-outtangents", outTangents, animationMultiCurve->GetDimension() * 2, FUDaeAccessor::XY); } if (!tcbs.empty()) { AddSourceFloat(parentNode, baseId + "-tcbs", tcbs, animationMultiCurve->GetDimension() * 3, FUDaeAccessor::XYZW); AddSourceFloat(parentNode, baseId + "-eases", eases, animationMultiCurve->GetDimension() * 2, FUDaeAccessor::XY); } SAFE_DELETE_ARRAY(parameters); // Export the infinity parameters xmlNode* mayaTechnique = AddTechniqueChild(inputSourceNode, DAEMAYA_MAYA_PROFILE); fm::string infinityType = FUDaeInfinity::ToString(animationMultiCurve->GetPreInfinity()); AddChild(mayaTechnique, DAEMAYA_PREINFINITY_PARAMETER, infinityType); infinityType = FUDaeInfinity::ToString(animationMultiCurve->GetPostInfinity()); AddChild(mayaTechnique, DAEMAYA_POSTINFINITY_PARAMETER, infinityType); } xmlNode* FArchiveXML::WriteSamplerFCDAnimationMultiCurve(FCDAnimationMultiCurve* animationMultiCurve, xmlNode* parentNode, const fm::string& baseId) { xmlNode* samplerNode = AddChild(parentNode, DAE_SAMPLER_ELEMENT); AddAttribute(samplerNode, DAE_ID_ATTRIBUTE, baseId + "-sampler"); // Retrieve the source information data bool hasTangents = false, hasTCB = false; for (size_t i = 0; i < animationMultiCurve->GetKeyCount(); ++i) { hasTangents |= animationMultiCurve->GetKey(i)->interpolation == FUDaeInterpolation::BEZIER; hasTCB |= animationMultiCurve->GetKey(i)->interpolation == FUDaeInterpolation::TCB; } // Add the sampler inputs AddInput(samplerNode, baseId + "-input", DAE_INPUT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-output", DAE_OUTPUT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-interpolations", DAE_INTERPOLATION_ANIMATION_INPUT); if (hasTangents) { AddInput(samplerNode, baseId + "-intangents", DAE_INTANGENT_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-outtangents", DAE_OUTTANGENT_ANIMATION_INPUT); } if (hasTCB) { AddInput(samplerNode, baseId + "-tcbs", DAEFC_TCB_ANIMATION_INPUT); AddInput(samplerNode, baseId + "-eases", DAEFC_EASE_INOUT_ANIMATION_INPUT); } return samplerNode; } xmlNode* FArchiveXML::WriteChannelFCDAnimationMultiCurve(FCDAnimationMultiCurve* animationMultiCurve, xmlNode* parentNode, const fm::string& baseId, const fm::string& pointer) { xmlNode* channelNode = AddChild(parentNode, DAE_CHANNEL_ELEMENT); AddAttribute(channelNode, DAE_SOURCE_ATTRIBUTE, fm::string("#") + baseId + "-sampler"); // Generate and export the full target [no qualifiers] FUSStringBuilder builder(pointer); if (animationMultiCurve->GetTargetElement() >= 0) { builder.append('('); builder.append(animationMultiCurve->GetTargetElement()); builder.append(')'); } AddAttribute(channelNode, DAE_TARGET_ATTRIBUTE, builder); return channelNode; }