545 lines
18 KiB
C++
545 lines
18 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/FCDAnimationChannel.h"
|
|
#include "FCDocument/FCDAnimationCurve.h"
|
|
#include "FCDocument/FCDAnimationMultiCurve.h"
|
|
#include "FCDocument/FCDAnimated.h"
|
|
#include "FUtils/FUDaeParser.h"
|
|
#include "FUtils/FUDaeWriter.h"
|
|
using namespace FUDaeParser;
|
|
using namespace FUDaeWriter;
|
|
|
|
FCDAnimated::FCDAnimated(FCDocument* document, size_t valueCount) : FCDObject(document, "FCDAnimated")
|
|
{
|
|
arrayElement = -1;
|
|
|
|
// Allocate the values/qualifiers/curves arrays
|
|
values.resize(valueCount, NULL);
|
|
qualifiers.resize(valueCount, "");
|
|
curves.resize(valueCount, NULL);
|
|
}
|
|
|
|
FCDAnimated::~FCDAnimated()
|
|
{
|
|
values.clear();
|
|
qualifiers.clear();
|
|
curves.clear();
|
|
}
|
|
|
|
// Assigns a curve to a value of the animated element.
|
|
bool FCDAnimated::SetCurve(size_t index, FCDAnimationCurve* curve)
|
|
{
|
|
FUAssert(index < GetValueCount(), return false);
|
|
curves.at(index) = curve;
|
|
return true;
|
|
}
|
|
|
|
// Removes the curve affecting a value of the animated element.
|
|
bool FCDAnimated::RemoveCurve(size_t index)
|
|
{
|
|
FUAssert(index < GetValueCount(), return false);
|
|
bool hasCurve = curves[index] != NULL;
|
|
curves[index] = NULL;
|
|
return hasCurve;
|
|
}
|
|
|
|
bool FCDAnimated::Link(xmlNode* node)
|
|
{
|
|
bool linked = false;
|
|
|
|
if (node != NULL)
|
|
{
|
|
// Write down the expected target string for the given node
|
|
FUDaeParser::CalculateNodeTargetPointer(node, pointer);
|
|
|
|
// Check if this animated value is used as a driver
|
|
linked |= GetDocument()->LinkDriver(this);
|
|
|
|
// Retrieve the list of the channels pointing to this node
|
|
FCDAnimationChannelList channels;
|
|
GetDocument()->FindAnimationChannels(pointer, channels);
|
|
linked |= ProcessChannels(channels);
|
|
}
|
|
else linked = true;
|
|
|
|
if (linked)
|
|
{
|
|
// Register this animated value with the document
|
|
GetDocument()->RegisterAnimatedValue(this);
|
|
}
|
|
|
|
return linked;
|
|
}
|
|
|
|
bool FCDAnimated::ProcessChannels(FCDAnimationChannelList& channels)
|
|
{
|
|
bool linked = false;
|
|
for (FCDAnimationChannelList::iterator it = channels.begin(); it != channels.end(); ++it)
|
|
{
|
|
FCDAnimationChannel* channel = *it;
|
|
const FCDAnimationCurveList& channelCurves = channel->GetCurves();
|
|
if (channelCurves.empty()) continue;
|
|
|
|
// Retrieve the channel's qualifier and check for a requested matrix element
|
|
string qualifier = channel->GetTargetQualifier();
|
|
if (arrayElement != -1)
|
|
{
|
|
int32 element = ReadTargetMatrixElement(qualifier);
|
|
if (arrayElement != element) continue;
|
|
}
|
|
|
|
if (qualifier.empty())
|
|
{
|
|
// An empty qualifier implies that the channel should provide ALL the curves
|
|
for (size_t i = 0; i < channelCurves.size() && i < curves.size(); ++i)
|
|
{
|
|
curves[i] = channelCurves[i];
|
|
linked = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Attempt to match the qualifier with this animated qualifiers
|
|
size_t index;
|
|
for (index = 0; index < qualifiers.size(); ++index)
|
|
{
|
|
if (qualifiers[index] == qualifier) break;
|
|
}
|
|
|
|
// Check for a matrix element instead
|
|
if (index == qualifiers.size()) index = ReadTargetMatrixElement(qualifier);
|
|
if (index < qualifiers.size())
|
|
{
|
|
curves[index] = channelCurves.front();
|
|
linked = true;
|
|
}
|
|
/* else return status.Fail(FS("Invalid qualifier for animation channel target: ") + TO_FSTRING(pointer)); */
|
|
}
|
|
}
|
|
|
|
if (linked)
|
|
{
|
|
// Now that the curves are imported: set their target information
|
|
for (size_t i = 0; i < curves.size(); ++i)
|
|
{
|
|
if (curves[i] != NULL)
|
|
{
|
|
curves[i]->SetTargetElement(arrayElement);
|
|
curves[i]->SetTargetQualifier(qualifiers[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return linked;
|
|
}
|
|
|
|
const string& FCDAnimated::GetQualifier(size_t index) const
|
|
{
|
|
FUAssert(index < GetValueCount(), return emptyString);
|
|
return qualifiers.at(index);
|
|
}
|
|
|
|
// Retrieves an animated value, given a valid qualifier
|
|
float* FCDAnimated::FindValue(const string& qualifier)
|
|
{
|
|
for (size_t i = 0; i < qualifiers.size(); ++i)
|
|
{
|
|
if (qualifiers[i] == qualifier) return values[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
const float* FCDAnimated::FindValue(const string& qualifier) const
|
|
{
|
|
for (size_t i = 0; i < qualifiers.size(); ++i)
|
|
{
|
|
if (qualifiers[i] == qualifier) return values[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Retrieve the index of a given qualifier
|
|
size_t FCDAnimated::FindQualifier(const char* qualifier) const
|
|
{
|
|
for (size_t i = 0; i < qualifiers.size(); ++i)
|
|
{
|
|
if (qualifiers[i] == qualifier) return i;
|
|
}
|
|
|
|
// Otherwise, check for a matrix element
|
|
string q = qualifier;
|
|
int32 matrixElement = FUDaeParser::ReadTargetMatrixElement(q);
|
|
if (matrixElement >= 0 && matrixElement < (int32) qualifiers.size()) return matrixElement;
|
|
return size_t(-1);
|
|
}
|
|
|
|
// Retrieve the index of a given value pointer
|
|
size_t FCDAnimated::FindValue(const float* value) const
|
|
{
|
|
for (size_t i = 0; i < values.size(); ++i)
|
|
{
|
|
if (values[i] == value) return i;
|
|
}
|
|
return size_t(-1);
|
|
}
|
|
|
|
// Returns whether any of the contained curves are non-NULL
|
|
bool FCDAnimated::HasCurve() const
|
|
{
|
|
FCDAnimationCurveList::const_iterator cit;
|
|
for (cit = curves.begin(); cit != curves.end() && (*cit) == NULL; ++cit) {}
|
|
return cit != curves.end();
|
|
}
|
|
|
|
// Create one multi-curve out of this animated value's single curves
|
|
FCDAnimationMultiCurve* FCDAnimated::CreateMultiCurve() const
|
|
{
|
|
FloatList defaultValues;
|
|
size_t count = values.size();
|
|
defaultValues.resize(count);
|
|
for (size_t i = 0; i < count; ++i) defaultValues[i] = (*values[i]);
|
|
|
|
vector<const FCDAnimationCurve*> toMerge;
|
|
toMerge.resize(count);
|
|
for (size_t i = 0; i < count; ++i) toMerge[i] = curves[i];
|
|
return FCDAnimationMultiCurve::MergeCurves(toMerge, defaultValues);
|
|
}
|
|
|
|
// Create one multi-curve out of the single curves from many FCDAnimated objects
|
|
FCDAnimationMultiCurve* FCDAnimated::CreateMultiCurve(const FCDAnimatedList& toMerge)
|
|
{
|
|
// Calculate the total dimension of the curve to create
|
|
size_t count = 0;
|
|
for (FCDAnimatedList::const_iterator cit = toMerge.begin(); cit != toMerge.end(); ++cit)
|
|
{
|
|
count += (*cit)->GetValueCount();
|
|
}
|
|
|
|
// Generate the list of default values and the list of curves
|
|
FloatList defaultValues(count, 0.0f);
|
|
vector<const FCDAnimationCurve*> curves(count, NULL);
|
|
size_t offset = 0;
|
|
for (FCDAnimatedList::const_iterator cit = toMerge.begin(); cit != toMerge.end(); ++cit)
|
|
{
|
|
size_t localCount = (*cit)->GetValueCount();
|
|
for (size_t i = 0; i < localCount; ++i)
|
|
{
|
|
defaultValues[offset + i] = *(*cit)->GetValue(i);
|
|
curves[offset + i] = (*cit)->GetCurve(i);
|
|
}
|
|
offset += localCount;
|
|
}
|
|
|
|
return FCDAnimationMultiCurve::MergeCurves(curves, defaultValues);
|
|
}
|
|
|
|
// Sample the animated values for a given time
|
|
void FCDAnimated::Evaluate(float time)
|
|
{
|
|
size_t valueCount = values.size();
|
|
size_t curveCount = curves.size();
|
|
size_t count = min(curveCount, valueCount);
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
// Retrieve the curve and the corresponding value
|
|
FCDAnimationCurve* curve = curves[i];
|
|
if (curve == NULL) continue;
|
|
float* value = values[i];
|
|
if (value == NULL) continue;
|
|
|
|
// Evaluate the curve at this time
|
|
(*value) = curve->Evaluate(time);
|
|
}
|
|
}
|
|
|
|
FCDAnimated* FCDAnimated::Clone(FCDocument* document)
|
|
{
|
|
FCDAnimated* clone = new FCDAnimated(document, GetValueCount());
|
|
clone->arrayElement = arrayElement;
|
|
for(size_t i = 0; i < curves.size(); ++i)
|
|
{
|
|
clone->curves[i] = curves[i]->Clone();
|
|
}
|
|
clone->pointer = pointer;
|
|
clone->qualifiers = qualifiers;
|
|
clone->values = values;
|
|
document->RegisterAnimatedValue(clone);
|
|
return clone;
|
|
}
|
|
|
|
|
|
// Clones the whole animated associated with a given value.
|
|
FCDAnimated* FCDAnimated::Clone(FCDocument* document, const float* animatedValue, FloatPtrList& newAnimatedValues)
|
|
{
|
|
const FCDAnimated* toClone = document->FindAnimatedValue(animatedValue);
|
|
if (toClone == NULL) return NULL;
|
|
|
|
FCDAnimated* clone = new FCDAnimated(document, toClone->GetValueCount());
|
|
clone->arrayElement = toClone->arrayElement;
|
|
for(size_t i = 0; i < toClone->curves.size(); ++i)
|
|
{
|
|
clone->curves[i] = toClone->curves[i];
|
|
}
|
|
|
|
clone->pointer = toClone->pointer;
|
|
clone->qualifiers = toClone->qualifiers;
|
|
clone->values = newAnimatedValues;
|
|
document->RegisterAnimatedValue(clone);
|
|
return clone;
|
|
}
|
|
|
|
FCDAnimatedFloat::FCDAnimatedFloat(FCDocument* document, float* value, int32 _arrayElement) : FCDAnimated(document, 1)
|
|
{
|
|
values[0] = value;
|
|
qualifiers[0] = "";
|
|
arrayElement = _arrayElement;
|
|
}
|
|
|
|
FCDAnimatedFloat* FCDAnimatedFloat::Create(FCDocument* document, float* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedFloat* animated = new FCDAnimatedFloat(document, value, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedFloat* FCDAnimatedFloat::Create(FCDocument* document, xmlNode* node, float* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedFloat* animated = new FCDAnimatedFloat(document, value, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedFloat::Clone(FCDocument* document, const float* oldValue, float* newValue)
|
|
{
|
|
FloatPtrList newValues;
|
|
newValues.push_back(newValue);
|
|
return FCDAnimated::Clone(document, oldValue, newValues);
|
|
}
|
|
|
|
FCDAnimatedPoint3::FCDAnimatedPoint3(FCDocument* document, FMVector3* value, int32 _arrayElement) : FCDAnimated(document, 3)
|
|
{
|
|
values[0] = &value->x; values[1] = &value->y; values[2] = &value->z;
|
|
qualifiers[0] = ".X"; qualifiers[1] = ".Y"; qualifiers[2] = ".Z";
|
|
arrayElement = _arrayElement;
|
|
}
|
|
|
|
FCDAnimatedPoint3* FCDAnimatedPoint3::Create(FCDocument* document, FMVector3* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedPoint3* animated = new FCDAnimatedPoint3(document, value, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedPoint3* FCDAnimatedPoint3::Create(FCDocument* document, xmlNode* node, FMVector3* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedPoint3* animated = new FCDAnimatedPoint3(document, value, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedPoint3::Clone(FCDocument* document, const FMVector3* oldValue, FMVector3* newValue)
|
|
{
|
|
FloatPtrList newValues;
|
|
newValues.push_back(&newValue->x); newValues.push_back(&newValue->y); newValues.push_back(&newValue->z);
|
|
return FCDAnimated::Clone(document, &oldValue->x, newValues);
|
|
}
|
|
|
|
FCDAnimatedColor::FCDAnimatedColor(FCDocument* document, FMVector3* value, int32 _arrayElement) : FCDAnimated(document, 3)
|
|
{
|
|
values[0] = &value->x; values[1] = &value->y; values[2] = &value->z;
|
|
qualifiers[0] = ".R"; qualifiers[1] = ".G"; qualifiers[2] = ".B";
|
|
arrayElement = _arrayElement;
|
|
}
|
|
|
|
FCDAnimatedColor* FCDAnimatedColor::Create(FCDocument* document, FMVector3* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedColor* animated = new FCDAnimatedColor(document, value, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedColor* FCDAnimatedColor::Create(FCDocument* document, xmlNode* node, FMVector3* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedColor* animated = new FCDAnimatedColor(document, value, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedColor::Clone(FCDocument* document, const FMVector3* oldValue, FMVector3* newValue)
|
|
{
|
|
FloatPtrList newValues; newValues.push_back(&newValue->x); newValues.push_back(&newValue->y); newValues.push_back(&newValue->z);
|
|
return FCDAnimated::Clone(document, &oldValue->x, newValues);
|
|
}
|
|
|
|
FCDAnimatedAngle::FCDAnimatedAngle(FCDocument* document, float* value, int32 _arrayElement) : FCDAnimated(document, 1)
|
|
{
|
|
values[0] = value;
|
|
qualifiers[0] = ".ANGLE";
|
|
arrayElement = _arrayElement;
|
|
}
|
|
|
|
FCDAnimatedAngle* FCDAnimatedAngle::Create(FCDocument* document, float* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedAngle* animated = new FCDAnimatedAngle(document, value, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedAngle* FCDAnimatedAngle::Create(FCDocument* document, xmlNode* node, float* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedAngle* animated = new FCDAnimatedAngle(document, value, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedAngle::Clone(FCDocument* document, const float* oldValue, float* newValue)
|
|
{
|
|
FloatPtrList newValues;
|
|
newValues.push_back(newValue);
|
|
return FCDAnimated::Clone(document, oldValue, newValues);
|
|
}
|
|
|
|
FCDAnimatedAngleAxis::FCDAnimatedAngleAxis(FCDocument* document, FMVector3* axis, float* angle, int32 _arrayElement) : FCDAnimated(document, 4)
|
|
{
|
|
values[0] = &axis->x; values[1] = &axis->y; values[2] = &axis->z; values[3] = angle;
|
|
qualifiers[0] = ".X"; qualifiers[1] = ".Y"; qualifiers[2] = ".Z"; qualifiers[3] = ".ANGLE";
|
|
arrayElement = _arrayElement;
|
|
}
|
|
|
|
FCDAnimatedAngleAxis* FCDAnimatedAngleAxis::Create(FCDocument* document, FMVector3* axis, float* angle, int32 arrayElement)
|
|
{
|
|
FCDAnimatedAngleAxis* animated = new FCDAnimatedAngleAxis(document, axis, angle, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedAngleAxis* FCDAnimatedAngleAxis::Create(FCDocument* document, xmlNode* node, FMVector3* axis, float* angle, int32 arrayElement)
|
|
{
|
|
FCDAnimatedAngleAxis* animated = new FCDAnimatedAngleAxis(document, axis, angle, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedAngleAxis::Clone(FCDocument* document, const float* oldAngle, FMVector3* newAxis, float* newAngle)
|
|
{
|
|
FloatPtrList newValues;
|
|
newValues.push_back(&newAxis->x); newValues.push_back(&newAxis->y); newValues.push_back(&newAxis->z);
|
|
newValues.push_back(newAngle);
|
|
return FCDAnimated::Clone(document, oldAngle, newValues);
|
|
}
|
|
|
|
FCDAnimatedMatrix::FCDAnimatedMatrix(FCDocument* document, FMMatrix44* value, int32 _arrayElement) : FCDAnimated(document, 16)
|
|
{
|
|
arrayElement = _arrayElement;
|
|
|
|
#define MX_V(a,b) values[b*4+a] = (&(*value)[a][b])
|
|
MX_V(0,0); MX_V(1,0); MX_V(2,0); MX_V(3,0);
|
|
MX_V(0,1); MX_V(1,1); MX_V(2,1); MX_V(3,1);
|
|
MX_V(0,2); MX_V(1,2); MX_V(2,2); MX_V(3,2);
|
|
MX_V(0,3); MX_V(1,3); MX_V(2,3); MX_V(3,3);
|
|
#undef MX_V
|
|
|
|
#define MX_V(a,b) qualifiers[b*4+a] = ("(" #a ")(" #b ")");
|
|
MX_V(0,0); MX_V(0,1); MX_V(0,2); MX_V(0,3);
|
|
MX_V(1,0); MX_V(1,1); MX_V(1,2); MX_V(1,3);
|
|
MX_V(2,0); MX_V(2,1); MX_V(2,2); MX_V(2,3);
|
|
MX_V(3,0); MX_V(3,1); MX_V(3,2); MX_V(3,3);
|
|
#undef MX_V
|
|
}
|
|
|
|
FCDAnimatedMatrix* FCDAnimatedMatrix::Create(FCDocument* document, FMMatrix44* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedMatrix* animated = new FCDAnimatedMatrix(document, value, arrayElement);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedMatrix* FCDAnimatedMatrix::Create(FCDocument* document, xmlNode* node, FMMatrix44* value, int32 arrayElement)
|
|
{
|
|
FCDAnimatedMatrix* animated = new FCDAnimatedMatrix(document, value, arrayElement);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
|
|
FCDAnimated* FCDAnimatedMatrix::Clone(FCDocument* document, const FMMatrix44* oldMx, FMMatrix44* newMx)
|
|
{
|
|
FloatPtrList newValues;
|
|
|
|
#define MX_V(a,b) newValues.push_back(&(*newMx)[a][b]);
|
|
MX_V(0,0); MX_V(0,1); MX_V(0,2); MX_V(0,3);
|
|
MX_V(1,0); MX_V(1,1); MX_V(1,2); MX_V(1,3);
|
|
MX_V(2,0); MX_V(2,1); MX_V(2,2); MX_V(2,3);
|
|
MX_V(3,0); MX_V(3,1); MX_V(3,2); MX_V(3,3);
|
|
#undef MX_V
|
|
|
|
return FCDAnimated::Clone(document, &(*oldMx)[0][0], newValues);
|
|
}
|
|
|
|
FCDAnimatedCustom::FCDAnimatedCustom(FCDocument* document) : FCDAnimated(document, 1)
|
|
{
|
|
dummy = 0.0f;
|
|
}
|
|
|
|
bool FCDAnimatedCustom::Link(xmlNode* node)
|
|
{
|
|
bool linked = false;
|
|
|
|
// Retrieve the list of the channels pointing to this node
|
|
FUDaeParser::CalculateNodeTargetPointer(node, pointer);
|
|
FCDAnimationChannelList channels;
|
|
GetDocument()->FindAnimationChannels(pointer, channels);
|
|
|
|
// Extract all the qualifiers needed to hold these channels
|
|
for (FCDAnimationChannelList::iterator itC = channels.begin(); itC != channels.end(); ++itC)
|
|
{
|
|
FCDAnimationChannel* channel = *itC;
|
|
const FCDAnimationCurveList& channelCurves = channel->GetCurves();
|
|
if (channelCurves.empty()) continue;
|
|
|
|
// Retrieve the channel's qualifier
|
|
string qualifier = channel->GetTargetQualifier();
|
|
if (qualifier.empty())
|
|
{
|
|
// Implies one channel holding multiple curves
|
|
qualifiers.clear();
|
|
qualifiers.resize(channels.size());
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
qualifiers.push_back(qualifier);
|
|
}
|
|
}
|
|
|
|
// Link the curves and check if this animated value is used as a driver
|
|
values.resize(qualifiers.size());
|
|
curves.resize(values.size());
|
|
for (size_t i = 0; i < qualifiers.size(); ++i) { values[i] = &dummy; curves[i] = NULL; }
|
|
linked |= ProcessChannels(channels);
|
|
linked |= GetDocument()->LinkDriver(this);
|
|
|
|
if (linked)
|
|
{
|
|
// Register this animated value with the document
|
|
GetDocument()->RegisterAnimatedValue(this);
|
|
}
|
|
return linked;
|
|
}
|
|
|
|
FCDAnimatedCustom* FCDAnimatedCustom::Create(FCDocument* document)
|
|
{
|
|
FCDAnimatedCustom* animated = new FCDAnimatedCustom(document);
|
|
if (!animated->Link(NULL)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|
|
FCDAnimatedCustom* FCDAnimatedCustom::Create(FCDocument* document, xmlNode* node)
|
|
{
|
|
FCDAnimatedCustom* animated = new FCDAnimatedCustom(document);
|
|
if (!animated->Link(node)) SAFE_DELETE(animated);
|
|
return animated;
|
|
}
|