Files
OpenSceneGraph/src/osgPlugins/dae/daeWAnimations.cpp
2016-05-26 09:48:16 +01:00

491 lines
27 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>
#include <osgDB/ConvertUTF>
using namespace osgDAE;
void daeWriter::writeAnimations( osg::Node &wnode )
{
const std::string nodeNameUTF( _pluginOptions.namesUseCodepage ? osgDB::convertStringFromCurrentCodePageToUTF8(wnode.getName()) : wnode.getName() );
osg::Callback* ncb = wnode.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 ai = 0; ai < animationList.size(); ai++)
{
domAnimation* pDomAnimation = daeSafeCast< domAnimation >( _domLibraryAnimations->add( COLLADA_ELEMENT_ANIMATION ) );
domAnimation* pMainDomAnimation = pDomAnimation;
osg::ref_ptr<osgAnimation::Animation> animation = animationList[ai];
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() );
std::string channelNameUTF( _pluginOptions.namesUseCodepage ? osgDB::convertStringFromCurrentCodePageToUTF8(channelName) : channelName );
// 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";
channelNameUTF = channelName;
}
animationName = uniquify( channelName );
pDomAnimation->setId(channelNameUTF.c_str());
}
std::string sourceName( channelNameUTF + "_sampler" );
std::string inputSourceName( channelNameUTF + "_input" );
std::string outputSourceName( channelNameUTF + "_output" );
std::string interpolationSourceName( channelNameUTF + "_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 (channelNameUTF.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 (channelNameUTF.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);
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));
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 = nodeNameUTF + "/rotateX.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
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);
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));
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 = nodeNameUTF + "/rotateY.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
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);
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));
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 = nodeNameUTF + "/rotateZ.ANGLE";
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
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);
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));
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 = nodeNameUTF;
if (channelNameUTF.find("position") != std::string::npos)
{
domChannelTargetName += "/translate";
}
else if (channelNameUTF.find("scale") != std::string::npos)
{
domChannelTargetName += "/scale";
}
pDomChannel->setTarget(domChannelTargetName.c_str());
}
else
{
OSG_WARN << "Could not find animation target '" << targetName << "'" << std::endl;
}
}
}
}
}
}
}