Files
OpenSceneGraph/src/osgPlugins/dae/daeWAnimations.cpp
Robert Osfield bbca791251 From Michael Platings, "Here's the all-new, all-dancing DAE plugin, with support for reading
osgAnimation. It's been tested with the majority of the samples in the
COLLADA test repository and works with all of them either as well as, or
better than, the version of the plugin currently in SVN.

Known issue: vertex animation (AKA morphing) doesn't work at present,
but that's a relatively unpopular method of animating so it's not high
on my priority list."

Follow up email:
"I've been informed that the previous DAE submission didn't build on
unix, so here's the submission again with the fixes.  Thanks to Gregory Potdevin and Benjamin Bozou.
Also, my apologies to Roland for not crediting his part in making DAE
animation happen, my work was indeed built on top of his work. Thanks
also to Marius Heise and of course Cedric Pinson."

Changes by Robert Osfield, fixed compile issues when compile without C* automatic conversion enabled in ref_ptr<>
and constructor initialization fixes to address some warnings under gcc.
2010-02-26 14:41:50 +00:00

484 lines
26 KiB
C++

/*
* Copyright 2006 Sony Computer Entertainment Inc.
*
* Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://research.scea.com/scea_shared_source_license.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and limitations under the
* License.
*/
#include "daeWriter.h"
#include <dom/domCOLLADA.h>
#include <dom/domNode.h>
#include <dom/domConstants.h>
#include <dom/domLibrary_cameras.h>
#include <dom/domLibrary_lights.h>
#include <dae/domAny.h>
//#include <dom/domVisual_scene.h>
//#include <dom/domLibrary_visual_scenes.h>
#include <osgSim/MultiSwitch>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/UpdateMatrixTransform>
#include <osg/Sequence>
#include <osg/Billboard>
#include <osg/CameraView>
using namespace osgDAE;
void daeWriter::writeAnimations( osg::Node &node )
{
osg::NodeCallback* ncb = node.getUpdateCallback();
if (ncb)
{
osgAnimation::AnimationManagerBase* am = dynamic_cast<osgAnimation::AnimationManagerBase*>(ncb);
if (am)
{
// Create library of animations if not existing yet
if (!_domLibraryAnimations)
{
_domLibraryAnimations = daeSafeCast<domLibrary_animations>( dom->add( COLLADA_ELEMENT_LIBRARY_ANIMATIONS ) );
}
osgAnimation::AnimationList animationList = am->getAnimationList();
for (size_t i = 0; i < animationList.size(); i++)
{
domAnimation* pDomAnimation = daeSafeCast< domAnimation >( _domLibraryAnimations->add( COLLADA_ELEMENT_ANIMATION ) );
domAnimation* pMainDomAnimation = pDomAnimation;
osg::ref_ptr<osgAnimation::Animation> animation = animationList[i];
std::string animationName = animation->getName();
if (animationName.empty())
animationName = "animation";
animationName = uniquify( animationName );
pDomAnimation->setId(animationName.c_str());
// Handle multiple channels within an animation
osgAnimation::ChannelList animationChannels = animation->getChannels();
for (size_t j=0; j < animationChannels.size(); j++)
{
osgAnimation::Channel* channel = animationChannels[j].get();
std::string channelName = channel->getName();
// Wrap each animation channel into it's own child <animation> when more than 1 channel
if (animationChannels.size() > 1)
{
pDomAnimation = daeSafeCast< domAnimation >( pMainDomAnimation->add( COLLADA_ELEMENT_ANIMATION ) );
if (channelName.empty())
channelName = "channel";
animationName = uniquify( channelName );
pDomAnimation->setId(channelName.c_str());
}
std::string sourceName = channelName + "_sampler";
std::string inputSourceName = channelName + "_input";
std::string outputSourceName = channelName + "_output";
std::string interpolationSourceName = channelName + "_interpolation";
// Fill dom sources based on sampler
osgAnimation::Sampler* animationSampler = channel->getSampler();
osgAnimation::KeyframeContainer* kfc = animationSampler->getKeyframeContainer();
int size = kfc->size();
float valueStride = 1;
domListOfFloats timeValues;
domListOfFloats frameValues;
domListOfNames interpolationValues;
osgAnimation::Vec3KeyframeContainer* v3kc = dynamic_cast<osgAnimation::Vec3KeyframeContainer*>(kfc);
if (v3kc)
{
valueStride = 3;
for (size_t i=0; i < v3kc->size(); i++)
{
timeValues.append((*v3kc)[i].getTime());
osg::Vec3 vec = (*v3kc)[i].getValue();
// This needs some serious cleanup
if (channelName.find("euler") != std::string::npos)
{
frameValues.append(osg::RadiansToDegrees(vec.x()));
frameValues.append(osg::RadiansToDegrees(vec.y()));
frameValues.append(osg::RadiansToDegrees(vec.z()));
}
else
{
frameValues.append(vec.x());
frameValues.append(vec.y());
frameValues.append(vec.z());
}
interpolationValues.append("LINEAR");
}
}
osgAnimation::FloatKeyframeContainer* fkc = dynamic_cast<osgAnimation::FloatKeyframeContainer*>(kfc);
if (fkc)
{
valueStride = 1;
for (size_t i=0; i < fkc->size(); i++)
{
timeValues.append((*fkc)[i].getTime());
frameValues.append((*fkc)[i].getValue());
interpolationValues.append("LINEAR");
}
}
// time values
domSource* pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
pDomSource->setId(inputSourceName.c_str());
domFloat_array* pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY));
std::string inputArrayName = inputSourceName + "_array";
pDomFloatArray->setId(inputArrayName.c_str());
pDomFloatArray->setCount(size);
pDomFloatArray->setValue(timeValues);
domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
std::string url = "#" + inputArrayName;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(1);
domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("TIME");
pDomParam->setType(COLLADA_TYPE_FLOAT);
// <source> interpolator type
pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
pDomSource->setId(interpolationSourceName.c_str());
domName_array* pDomNameArray = daeSafeCast< domName_array >(pDomSource->add(COLLADA_ELEMENT_NAME_ARRAY));
std::string interpolationArrayName = interpolationSourceName + "_array";
pDomNameArray->setId(interpolationArrayName.c_str());
pDomNameArray->setCount(size);
pDomNameArray->setValue(interpolationValues);
// <technique_common>
pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
// <accessor count=size stride="1">
pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
url = "#" + interpolationArrayName;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(1);
// <param name="INTERPOLATION" type="name"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("INTERPOLATION");
pDomParam->setType(COLLADA_TYPE_NAME);
// Split up access to the euler float array into three sources, because we need to target three separate transforms
if (channelName.find("euler") != std::string::npos)
{
pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
std::string outputSourceNameX = outputSourceName + "_X";
pDomSource->setId(outputSourceNameX.c_str());
pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY));
std::string outputArrayNameX = outputSourceNameX + "_array";
pDomFloatArray->setId(outputArrayNameX.c_str());
// TODO, flexible handling of different keyframe types, see osg exporter for inspiration
pDomFloatArray->setCount(size * valueStride);
pDomFloatArray->setValue(frameValues);
pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
url = "#" + outputArrayNameX;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(valueStride);
pDomAccessor->setOffset(0);
// <param name="ANGLE" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("ANGLE");
pDomParam->setType(COLLADA_TYPE_FLOAT);
pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
std::string outputSourceNameY = outputSourceName + "_Y";
pDomSource->setId(outputSourceNameY.c_str());
pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY));
std::string outputArrayNameY = outputSourceNameY + "_array";
pDomFloatArray->setId(outputArrayNameY.c_str());
pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
url = "#" + outputArrayNameY;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(valueStride);
pDomAccessor->setOffset(1);
// <param name="ANGLE" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("ANGLE");
pDomParam->setType(COLLADA_TYPE_FLOAT);
pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
std::string outputSourceNameZ = outputSourceName + "_Z";
pDomSource->setId(outputSourceNameZ.c_str());
pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY));
std::string outputArrayNameZ = outputSourceNameZ + "_array";
pDomFloatArray->setId(outputArrayNameZ.c_str());
pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
url = "#" + outputArrayNameZ;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(valueStride);
pDomAccessor->setOffset(2);
// <param name="ANGLE" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("ANGLE");
pDomParam->setType(COLLADA_TYPE_FLOAT);
{
domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER));
std::string sourceNameX = sourceName + "_X";
pDomSampler->setId(sourceNameX.c_str());
// Fill dom sampler based on common semantics
{
domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT);
std::string url = "#" + inputSourceName;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT);
url = "#" + outputSourceNameX;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION);
url = "#" + interpolationSourceName;
pDomInput->setSource(url.c_str());
}
// Set sampler as source
domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL));
std::string url = "#" + sourceNameX;
pDomChannel->setSource(url.c_str());
// targetName contains the name of the updateCallback
std::string targetName = channel->getTargetName();
// based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation"
osg::Node* node = _animatedNodeCollector.getTargetNode(targetName);
if (node)
{
std::string domChannelTargetName = node->getName() + "/rotateX.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl;
}
}
{
domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER));
std::string sourceNameY = sourceName + "_Y";
pDomSampler->setId(sourceNameY.c_str());
// Fill dom sampler based on common semantics
{
domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT);
std::string url = "#" + inputSourceName;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT);
url = "#" + outputSourceNameY;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION);
url = "#" + interpolationSourceName;
pDomInput->setSource(url.c_str());
}
// Set sampler as source
domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL));
std::string url = "#" + sourceNameY;
pDomChannel->setSource(url.c_str());
// targetName contains the name of the updateCallback
std::string targetName = channel->getTargetName();
// based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation"
osg::Node* node = _animatedNodeCollector.getTargetNode(targetName);
if (node)
{
std::string domChannelTargetName = node->getName() + "/rotateY.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl;
}
}
{
domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER));
std::string sourceNameZ = sourceName + "_Z";
pDomSampler->setId(sourceNameZ.c_str());
// Fill dom sampler based on common semantics
{
domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT);
std::string url = "#" + inputSourceName;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT);
url = "#" + outputSourceNameZ;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION);
url = "#" + interpolationSourceName;
pDomInput->setSource(url.c_str());
}
// Set sampler as source
domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL));
std::string url = "#" + sourceNameZ;
pDomChannel->setSource(url.c_str());
// targetName contains the name of the updateCallback
std::string targetName = channel->getTargetName();
// based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation"
osg::Node* node = _animatedNodeCollector.getTargetNode(targetName);
if (node)
{
std::string domChannelTargetName = node->getName() + "/rotateZ.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl;
}
}
}
else
{
// values in keyframecontainer
pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE));
pDomSource->setId(outputSourceName.c_str());
pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY));
std::string outputArrayName = outputSourceName + "_array";
pDomFloatArray->setId(outputArrayName.c_str());
// TODO, flexible handling of different keyframe types, see osg exporter for inspiration
pDomFloatArray->setCount(size * valueStride);
pDomFloatArray->setValue(frameValues);
pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON));
pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR));
url = "#" + outputArrayName;
pDomAccessor->setSource(url.c_str());
pDomAccessor->setCount(size);
pDomAccessor->setStride(valueStride);
if (v3kc)
{
// <param name="X" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("X");
pDomParam->setType(COLLADA_TYPE_FLOAT);
// <param name="Y" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("Y");
pDomParam->setType(COLLADA_TYPE_FLOAT);
// <param name="Z" type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setName("Z");
pDomParam->setType(COLLADA_TYPE_FLOAT);
}
if (fkc)
{
// <param type="float"/>
pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM));
pDomParam->setType(COLLADA_TYPE_FLOAT);
}
domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER));
pDomSampler->setId(sourceName.c_str());
// Fill dom sampler based on common semantics
{
domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT);
std::string url = "#" + inputSourceName;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT);
url = "#" + outputSourceName;
pDomInput->setSource(url.c_str());
pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT));
pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION);
url = "#" + interpolationSourceName;
pDomInput->setSource(url.c_str());
}
// Set sampler as source
domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL));
std::string url = "#" + sourceName;
pDomChannel->setSource(url.c_str());
// targetName contains the name of the updateCallback
std::string targetName = channel->getTargetName();
// based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation"
osg::Node* node = _animatedNodeCollector.getTargetNode(targetName);
if (node)
{
std::string domChannelTargetName = node->getName();
if (channelName.find("position") != std::string::npos)
{
domChannelTargetName += "/translate";
}
else if (channelName.find("scale") != std::string::npos)
{
domChannelTargetName += "/scale";
}
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl;
}
}
}
}
}
}
}