293 lines
11 KiB
C++
293 lines
11 KiB
C++
/*
|
|
Copyright (C) 2005-2006 Feeling Software Inc.
|
|
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 "FCDocument/FCDocument.h"
|
|
#include "FCDocument/FCDAnimated.h"
|
|
#include "FCDocument/FCDAnimation.h"
|
|
#include "FCDocument/FCDAnimationChannel.h"
|
|
#include "FCDocument/FCDAnimationCurve.h"
|
|
#include "FCDocument/FCDAnimationMultiCurve.h"
|
|
#include "FUtils/FUDaeEnum.h"
|
|
#include "FUtils/FUDaeParser.h"
|
|
#include "FUtils/FUDaeWriter.h"
|
|
#include "FUtils/FUStringConversion.h"
|
|
using namespace FUDaeParser;
|
|
using namespace FUDaeWriter;
|
|
|
|
FCDAnimationChannel::FCDAnimationChannel(FCDocument* document, FCDAnimation* _parent) : FCDObject(document, "FCDAnimationChannel")
|
|
{
|
|
parent = _parent;
|
|
}
|
|
|
|
FCDAnimationChannel::~FCDAnimationChannel()
|
|
{
|
|
CLEAR_POINTER_VECTOR(curves);
|
|
parent = NULL;
|
|
}
|
|
|
|
FCDAnimationCurve* FCDAnimationChannel::AddCurve()
|
|
{
|
|
FCDAnimationCurve* curve = new FCDAnimationCurve(GetDocument(), this);
|
|
curves.push_back(curve);
|
|
return curve;
|
|
}
|
|
|
|
void FCDAnimationChannel::ReleaseCurve(FCDAnimationCurve* curve)
|
|
{
|
|
FCDAnimationCurveList::iterator itC = std::find(curves.begin(), curves.end(), curve);
|
|
if (itC != curves.end())
|
|
{
|
|
// TODO: IMPLEMENT THIS. NEED RTTI and memory management. In other words, I need time!!! :(.
|
|
// delete *itC;
|
|
curves.erase(itC);
|
|
}
|
|
}
|
|
|
|
// Consider this animated as the curve's driver
|
|
bool FCDAnimationChannel::LinkDriver(FCDAnimated* animated)
|
|
{
|
|
bool driver = !driverPointer.empty();
|
|
driver = driver && animated->GetTargetPointer() == driverPointer;
|
|
if (driver && driverQualifier >= 0 && (uint32) driverQualifier < animated->GetValueCount())
|
|
{
|
|
// Retrieve the value pointer for the driver
|
|
for (FCDAnimationCurveList::iterator itC = curves.begin(); itC != curves.end(); ++itC)
|
|
{
|
|
(*itC)->SetDriver(animated->GetValue((uint32) driverQualifier));
|
|
}
|
|
}
|
|
return driver;
|
|
}
|
|
FUStatus FCDAnimationChannel::CheckDriver()
|
|
{
|
|
FUStatus status;
|
|
if (!driverPointer.empty() && !curves.empty() && curves.front()->GetDriver() == NULL)
|
|
{
|
|
status.Fail(FS("Unable to find animation curve driver: ") + TO_FSTRING(driverPointer) + FS(" for animation: ") + TO_FSTRING(parent->GetDaeId()));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Load a Collada animation channel from the XML document
|
|
FUStatus FCDAnimationChannel::LoadFromXML(xmlNode* channelNode)
|
|
{
|
|
FUStatus status;
|
|
|
|
// Read the channel-specific ID
|
|
string daeId = ReadNodeId(channelNode);
|
|
string samplerId = ReadNodeSource(channelNode);
|
|
ReadNodeTargetProperty(channelNode, targetPointer, targetQualifier);
|
|
|
|
xmlNode* samplerNode = parent->FindChildById(samplerId);
|
|
if (samplerNode == NULL || !IsEquivalent(samplerNode->name, DAE_SAMPLER_ELEMENT))
|
|
{
|
|
return status.Fail(FS("Unable to find sampler node for channel node: ") + TO_FSTRING(daeId), channelNode->line);
|
|
}
|
|
|
|
// Find and process the sources
|
|
xmlNode* inputSource = NULL,* outputSource = NULL,* inTangentSource = NULL,* outTangentSource = NULL;
|
|
xmlNode* outTangentWeightSource = NULL,* inTangentWeightSource = NULL,* interpolationSource = NULL;
|
|
xmlNodeList samplerInputNodes;
|
|
FindChildrenByType(samplerNode, DAE_INPUT_ELEMENT, samplerInputNodes);
|
|
for (size_t i = 0; i < samplerInputNodes.size(); ++i) // Don't use iterator here because we are possibly appending source nodes in the loop
|
|
{
|
|
xmlNode* inputNode = samplerInputNodes[i];
|
|
string sourceId = ReadNodeSource(inputNode);
|
|
xmlNode* sourceNode = parent->FindChildById(sourceId);
|
|
string sourceSemantic = ReadNodeSemantic(inputNode);
|
|
|
|
if (sourceSemantic == DAE_INPUT_ANIMATION_INPUT) inputSource = sourceNode;
|
|
else if (sourceSemantic == DAE_OUTPUT_ANIMATION_INPUT) outputSource = sourceNode;
|
|
else if (sourceSemantic == DAE_INTANGENT_ANIMATION_INPUT) inTangentSource = sourceNode;
|
|
else if (sourceSemantic == DAE_OUTTANGENT_ANIMATION_INPUT) outTangentSource = sourceNode;
|
|
else if (sourceSemantic == DAEMAYA_INTANGENTWEIGHT_ANIMATION_INPUT) inTangentWeightSource = sourceNode;
|
|
else if (sourceSemantic == DAEMAYA_OUTTANGENTWEIGHT_ANIMATION_INPUT) outTangentWeightSource = sourceNode;
|
|
else if (sourceSemantic == DAE_INTERPOLATION_ANIMATION_INPUT) interpolationSource = sourceNode;
|
|
}
|
|
if (inputSource == NULL || outputSource == NULL)
|
|
{
|
|
return status.Fail(FS("Missing INPUT or OUTPUT sources in animation channel: ") + TO_FSTRING(parent->GetDaeId()), samplerNode->line);
|
|
}
|
|
|
|
// Calculate the number of curves that in contained by this channel
|
|
xmlNode* outputAccessor = FindTechniqueAccessor(outputSource);
|
|
string accessorStrideString = ReadNodeProperty(outputAccessor, DAE_STRIDE_ATTRIBUTE);
|
|
uint32 curveCount = FUStringConversion::ToUInt32(accessorStrideString);
|
|
if (curveCount == 0) curveCount = 1;
|
|
|
|
// Create the animation curves
|
|
curves.reserve(curveCount);
|
|
for (uint32 i = 0; i < curveCount; ++i) AddCurve();
|
|
|
|
// Read in the animation curves
|
|
// The input keys are shared by all the curves
|
|
ReadSource(inputSource, curves.front()->GetKeys());
|
|
for (uint32 i = 1; i < curveCount; ++i) curves[i]->GetKeys() = curves.front()->GetKeys();
|
|
|
|
// Read in the interleaved outputs and tangents as floats
|
|
#define READ_SOURCE_INTERLEAVED(sourceNode, curveArrayPtr) \
|
|
if (sourceNode != NULL) { \
|
|
vector<FloatList*> arrays(curveCount); \
|
|
for (uint32 i = 0; i < curveCount; ++i) { \
|
|
arrays[i] = &(curves[i]->curveArrayPtr()); } \
|
|
ReadSourceInterleaved(sourceNode, arrays); \
|
|
}
|
|
|
|
READ_SOURCE_INTERLEAVED(outputSource, GetKeyValues)
|
|
READ_SOURCE_INTERLEAVED(inTangentSource, GetInTangents)
|
|
READ_SOURCE_INTERLEAVED(outTangentSource, GetOutTangents)
|
|
READ_SOURCE_INTERLEAVED(inTangentWeightSource, GetInTangentWeights)
|
|
READ_SOURCE_INTERLEAVED(outTangentWeightSource, GetOutTangentWeights)
|
|
#undef READ_SOURCE_INTERLEAVED
|
|
|
|
// Read in the interleaved interpolation values, parsing the tokens directly
|
|
if (interpolationSource != NULL)
|
|
{
|
|
vector<UInt32List*> arrays(curveCount);
|
|
for (uint32 i = 0; i < curveCount; ++i) arrays[i] = &(curves[i]->GetInterpolations());
|
|
ReadSourceInterpolationInterleaved(interpolationSource, arrays);
|
|
}
|
|
|
|
// Read in the pre/post-infinity type
|
|
xmlNodeList mayaParameterNodes; StringList mayaParameterNames;
|
|
xmlNode* mayaTechnique = FindTechnique(inputSource, DAEMAYA_MAYA_PROFILE);
|
|
FindParameters(mayaTechnique, mayaParameterNames, mayaParameterNodes);
|
|
size_t parameterCount = mayaParameterNodes.size();
|
|
for (size_t i = 0; i < parameterCount; ++i)
|
|
{
|
|
xmlNode* parameterNode = mayaParameterNodes[i];
|
|
const string& paramName = mayaParameterNames[i];
|
|
const char* content = ReadNodeContentDirect(parameterNode);
|
|
|
|
if (paramName == DAEMAYA_PREINFINITY_PARAMETER || paramName == DAEMAYA_PREINFINITY_PARAMETER1_3)
|
|
{
|
|
for (FCDAnimationCurveList::iterator itC = curves.begin(); itC != curves.end(); ++itC)
|
|
{
|
|
(*itC)->SetPreInfinity(FUDaeInfinity::FromString(content));
|
|
}
|
|
}
|
|
else if (paramName == DAEMAYA_POSTINFINITY_PARAMETER || paramName == DAEMAYA_POSTINFINITY_PARAMETER1_3)
|
|
{
|
|
for (FCDAnimationCurveList::iterator itC = curves.begin(); itC != curves.end(); ++itC)
|
|
{
|
|
(*itC)->SetPostInfinity(FUDaeInfinity::FromString(content));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Look for driven-key input target
|
|
if (paramName == DAE_INPUT_ELEMENT)
|
|
{
|
|
string semantic = ReadNodeSemantic(parameterNode);
|
|
if (semantic == DAEMAYA_DRIVER_INPUT)
|
|
{
|
|
string fullDriverTarget = ReadNodeSource(parameterNode);
|
|
const char* driverTarget = FUDaeParser::SkipPound(fullDriverTarget);
|
|
if (driverTarget != NULL)
|
|
{
|
|
string driverQualifierValue;
|
|
FUDaeParser::SplitTarget(driverTarget, driverPointer, driverQualifierValue);
|
|
driverQualifier = FUDaeParser::ReadTargetMatrixElement(driverQualifierValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ready the curves for usage/evaluation.
|
|
for (uint32 i = 0; i < curveCount; ++i) curves[i]->Ready();
|
|
|
|
return status;
|
|
}
|
|
|
|
// Write out the animation curves for an animation channel to a COLLADA document
|
|
void FCDAnimationChannel::WriteToXML(xmlNode* parentNode) const
|
|
{
|
|
string baseId = CleanId(targetPointer);
|
|
|
|
// Check for curve merging
|
|
uint32 realCurveCount = 0;
|
|
FCDAnimationCurve* masterCurve = NULL;
|
|
bool mergeCurves = true;
|
|
for (FCDAnimationCurveList::const_iterator itC = curves.begin(); itC != curves.end(); ++itC)
|
|
{
|
|
FCDAnimationCurve* curve = (*itC);
|
|
if ((*itC) != NULL)
|
|
{
|
|
++realCurveCount;
|
|
if (masterCurve == NULL)
|
|
{
|
|
masterCurve = curve;
|
|
if (masterCurve->GetDriver() != NULL) break;
|
|
}
|
|
else
|
|
{
|
|
// Check preliminary information, before verifying the individual keys: key count, infinity types and such..
|
|
const FloatList& masterKeys = masterCurve->GetKeys();
|
|
const FloatList& curveKeys = curve->GetKeys();
|
|
size_t keyCount = masterKeys.size();
|
|
mergeCurves &= curveKeys.size() == keyCount && curve->GetPostInfinity() == masterCurve->GetPostInfinity() && curve->GetPreInfinity() == masterCurve->GetPreInfinity();
|
|
for (size_t k = 0; k < keyCount && mergeCurves; ++k)
|
|
{
|
|
mergeCurves = curveKeys[k] == masterKeys[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mergeCurves && realCurveCount > 1)
|
|
{
|
|
// HACK: Will need to merge the channel and animated classes.
|
|
FloatList defaultValues(curves.size(), 0.0f);
|
|
|
|
// Merge and export the curves
|
|
FCDAnimationMultiCurve* multiCurve = FCDAnimationMultiCurve::MergeCurves(curves, defaultValues);
|
|
multiCurve->WriteSourceToXML(parentNode, baseId);
|
|
multiCurve->WriteSamplerToXML(parentNode, baseId);
|
|
multiCurve->WriteChannelToXML(parentNode, baseId, targetPointer);
|
|
SAFE_DELETE(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 = curves.size();
|
|
StringList ids; ids.resize(curves.size());
|
|
FUSStringBuilder curveId;
|
|
for (size_t c = 0; c < curveCount; ++c)
|
|
{
|
|
if (curves[c] != NULL)
|
|
{
|
|
// Generate a valid id for this curve
|
|
curveId.set(baseId);
|
|
if (curves[c]->GetTargetElement() >= 0)
|
|
{
|
|
curveId.append('_'); curveId.append(curves[c]->GetTargetElement()); curveId.append('_');
|
|
}
|
|
curveId.append(curves[c]->GetTargetQualifier());
|
|
ids[c] = CleanId(curveId.ToCharPtr());
|
|
|
|
// Write out the curve's sources
|
|
curves[c]->WriteSourceToXML(parentNode, ids[c]);
|
|
}
|
|
}
|
|
for (size_t c = 0; c < curveCount; ++c)
|
|
{
|
|
if (curves[c] != NULL) curves[c]->WriteSamplerToXML(parentNode, ids[c]);
|
|
}
|
|
for (size_t c = 0; c < curveCount; ++c)
|
|
{
|
|
if (curves[c] != NULL) curves[c]->WriteChannelToXML(parentNode, ids[c], targetPointer.c_str());
|
|
}
|
|
}
|
|
}
|