Hi Renato / others

I've finished my animation splitting system, seems to do the job for me. I
used osgDB XmlPaser to allow an xml file to be used for config and have
successfully loaded my original long animation file and split it
into separate 'takes'.

Attached is the cpp and an example config file. I think it's pretty straight
forward.

Let me know if you get stuck
Cheers
Tom

PS
At the moment exporting to .osgb seems to be the only format that works.
Source file can be whatever you previously had e.g. fbx
//
//Simple animation split util, will load an xml config file from ./splitConfig.xml and
//use it to specify a source file, output/destination file and a set of new split animations
//key use of this tool is to split one long animatiob from 3ds max into multiple 'takes'
//

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/FileUtils>
#include <osgDB/XmlParser>

#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/AnimationManagerBase>

//
//finds and returns the fist AnimationManagerBase in the subgraph
struct AnimationManagerFinder : public osg::NodeVisitor
{
    osg::ref_ptr<osgAnimation::BasicAnimationManager> _am;
    AnimationManagerFinder() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
    void apply(osg::Node& node) {
        if (_am.valid())
            return;
        if (node.getUpdateCallback()) {
            osgAnimation::AnimationManagerBase* b = dynamic_cast<osgAnimation::AnimationManagerBase*>(node.getUpdateCallback());
            if (b) {
                _am = new osgAnimation::BasicAnimationManager(*b);
				node.setUpdateCallback(_am.get());
                return;
            }
        }
        traverse(node);
    }
};

//
//Finds an Animation by name, returning a pointer to the animation
//and a pointer to the manager it was found in
struct FindOsgAnimationByName : public osg::NodeVisitor
{
	//the name of the source animation we are looking for
	std::string _sourceAnimationName;

	//used to return the animation and the manager it was stored in
	osgAnimation::Animation* p_ani;
	osgAnimation::AnimationManagerBase* p_manager;

	FindOsgAnimationByName(std::string sourceAnimationName) 
		: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
		p_ani(NULL),
		p_manager(NULL),
		_sourceAnimationName(sourceAnimationName)
	{
	}
	void apply(osg::Node& node) {

		if (node.getUpdateCallback()) {
			osgAnimation::AnimationManagerBase* b = dynamic_cast<osgAnimation::AnimationManagerBase*>(node.getUpdateCallback());
			if (b) {
				//we have found a valid osgAnimation manager, now look for the single long animation inside with the desired name
				osgAnimation::AnimationList aniList = b->getAnimationList();
				for(unsigned int i=0; i<aniList.size(); i++){
					if(aniList[i]->getName() == _sourceAnimationName){
						p_manager = b;
						p_ani = aniList[i].get();
						return;
					}
				}
			}
		}
		traverse(node);
	}
};

class AnimationUtils
{
public:

	//returns the index of the keyframe closest to the passed time
	//returns -1 if the time is out of range of the key container
	template <typename ContainerType>
	static int GetNearestKeyFrameIndex(ContainerType* keyContainer, double time)
	{
		if(!keyContainer){return -1;}

		int closestFrame = -1;
		double closestDiff = 99999.99f;

		//loop all the keys
		for(unsigned int i=0; i<keyContainer->size(); i++)
		{
			double diff = fabs(time - (*keyContainer)[i].getTime());
			if( diff < closestDiff){
				closestFrame = i;
				closestDiff = diff;
			}
		}
		return closestFrame;
	}

	//// Helper method for resampling channels
	template <typename ChannelType, typename ContainerType>
	static osg::ref_ptr<ChannelType> ResampleChannel(ChannelType* sourceChannel, unsigned int startFrame, unsigned int endFrame, int fps)
	{
		osg::ref_ptr<ChannelType> newChannel = NULL;
		if(!sourceChannel){
			return newChannel;
		}

		//get the key frame container from the source channel
		ContainerType* sourceKeyCont  = sourceChannel->getSamplerTyped()->getKeyframeContainerTyped();

		if (sourceKeyCont)
		{
			OSG_INFO << "OsgAnimationTools ResampleChannel INFO: Resampling source channel '" << sourceChannel->getName() 
					 << "', from startFrame '" << startFrame << "' to endFrame '" << endFrame << ". Total frames in source channel '" << sourceKeyCont->size() << "'." << std::endl;
			
			if(startFrame >= sourceKeyCont->size() || endFrame >= sourceKeyCont->size())
			{
				OSG_WARN << "OsgAnimationTools ResampleChannel ERROR: startFrame or endFrame is out of range," << std::endl
						 << "                                         startFrame '" << startFrame << "', endFrame '" << endFrame << ". Total frames in source channel '" << sourceKeyCont->size() << "'." << std::endl;

				return newChannel;
			}

			//determine the copy direction, i.e. lets see if we can copy frames in reverse as could come in handy
			unsigned int from=startFrame;
			unsigned int to=endFrame;
			if(startFrame > endFrame){
				from = endFrame;
				to = startFrame;
			}

			//get our frames as a times 
			double fromTime = from == 0 ? 0.0f : (float)from/(float)fps;
			double toTime = to == 0 ? 0.0f : (float)to/(float)fps;

			//find the true key frame numbers as some animation channels contain more then the actual fps
			from = GetNearestKeyFrameIndex<ContainerType>(sourceKeyCont, fromTime);
			if(from == -1){return newChannel;}
			to = GetNearestKeyFrameIndex<ContainerType>(sourceKeyCont, toTime);
			if(to == -1){return newChannel;}

			//get the offset time form the keys
			double offsetToZero = (*sourceKeyCont)[from].getTime();

			//create our new channel with same type, name and target name
			newChannel = new ChannelType();
			newChannel->setName(sourceChannel->getName());
			newChannel->setTargetName(sourceChannel->getTargetName());

			//get the new channels key containter, also creating the sampler and container if required
			ContainerType* destKeyCont = newChannel->getOrCreateSampler()->getOrCreateKeyframeContainer();

			//now copy all the frames between fromTime and toTime
			for (unsigned int k=from; k<=to; k++) 
			{
				//push source frame onto destination container
				destKeyCont->push_back((*sourceKeyCont)[k]);
				//adjust the new frames time so animation starts from 0.0
				double sourceTime = (*sourceKeyCont)[k].getTime();
				(*destKeyCont)[destKeyCont->size()-1].setTime(sourceTime-offsetToZero);
			}

			return newChannel;

		}else{
			OSG_WARN << "OsgAnimationTools ResampleChannel ERROR: source channel contains no key frame container," << std::endl;
			return newChannel;
		}
		return newChannel;
	}

	//
	//Create and return a new animation based on the start end frames of the source animation
	//creating all the relevant channels etc
	static osg::ref_ptr<osgAnimation::Animation> ResampleAnimation(osgAnimation::Animation* source, 
																	int startFrame, int endFrame, int fps, 
																	std::string newName)
	{
		osg::ref_ptr<osgAnimation::Animation> newAnimation = NULL;
		if(!source){
			return newAnimation;
		}

		newAnimation = new osgAnimation::Animation();
		newAnimation->setName(newName);

		//loop all the source channels
		osgAnimation::ChannelList sourceChannels = source->getChannels();
		for(unsigned int i=0; i<sourceChannels.size(); i++)
		{
			//clone the channel type, name and target
			//this is a dumb method but I cant find a way to just clone the channel type,
			//so instead we copy the whole channel, keys and all, then delete the ones we don't want :(
			osgAnimation::Channel* pChannel = sourceChannels[i].get();
			osg::ref_ptr<osgAnimation::Channel> resampledChannel = NULL;

			        //osgAnimation::Channel* pChannel = anim.getChannels()[i].get();

			osgAnimation::DoubleLinearChannel* pDlc = dynamic_cast<osgAnimation::DoubleLinearChannel*>(pChannel);
			if (pDlc)
			{
				resampledChannel = ResampleChannel<osgAnimation::DoubleLinearChannel, osgAnimation::DoubleKeyframeContainer>(pDlc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::FloatLinearChannel* pFlc = dynamic_cast<osgAnimation::FloatLinearChannel*>(pChannel);
			if (pFlc)
			{
				resampledChannel = ResampleChannel<osgAnimation::FloatLinearChannel, osgAnimation::FloatKeyframeContainer>(pFlc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec2LinearChannel* pV2lc = dynamic_cast<osgAnimation::Vec2LinearChannel*>(pChannel);
			if (pV2lc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec2LinearChannel, osgAnimation::Vec2KeyframeContainer>(pV2lc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec3LinearChannel* pV3lc = dynamic_cast<osgAnimation::Vec3LinearChannel*>(pChannel);
			if (pV3lc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec3LinearChannel, osgAnimation::Vec3KeyframeContainer>(pV3lc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec4LinearChannel* pV4lc = dynamic_cast<osgAnimation::Vec4LinearChannel*>(pChannel);
			if (pV4lc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec4LinearChannel, osgAnimation::Vec4KeyframeContainer>(pV4lc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::QuatSphericalLinearChannel* pQslc = dynamic_cast<osgAnimation::QuatSphericalLinearChannel*>(pChannel);
			if (pQslc)
			{
				resampledChannel = ResampleChannel<osgAnimation::QuatSphericalLinearChannel, osgAnimation::QuatKeyframeContainer>(pQslc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::FloatCubicBezierChannel* pFcbc = dynamic_cast<osgAnimation::FloatCubicBezierChannel*>(pChannel);
			if (pFcbc)
			{
				resampledChannel = ResampleChannel<osgAnimation::FloatCubicBezierChannel, osgAnimation::FloatCubicBezierKeyframeContainer>(pFcbc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::DoubleCubicBezierChannel* pDcbc = dynamic_cast<osgAnimation::DoubleCubicBezierChannel*>(pChannel);
			if (pDcbc)
			{
				resampledChannel = ResampleChannel<osgAnimation::DoubleCubicBezierChannel, osgAnimation::DoubleCubicBezierKeyframeContainer>(pDcbc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec2CubicBezierChannel* pV2cbc = dynamic_cast<osgAnimation::Vec2CubicBezierChannel*>(pChannel);
			if (pV2cbc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec2CubicBezierChannel, osgAnimation::Vec2CubicBezierKeyframeContainer>(pV2cbc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec3CubicBezierChannel* pV3cbc = dynamic_cast<osgAnimation::Vec3CubicBezierChannel*>(pChannel);
			if (pV3cbc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec3CubicBezierChannel, osgAnimation::Vec3CubicBezierKeyframeContainer>(pV3cbc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
			osgAnimation::Vec4CubicBezierChannel* pV4cbc = dynamic_cast<osgAnimation::Vec4CubicBezierChannel*>(pChannel);
			if (pV4cbc)
			{
				resampledChannel = ResampleChannel<osgAnimation::Vec4CubicBezierChannel, osgAnimation::Vec4CubicBezierKeyframeContainer>(pV4cbc, startFrame, endFrame, fps);
				newAnimation->addChannel(resampledChannel.get());
				continue;
			}
		}//loop channel

		return newAnimation;
	}
};


//
//Loads an xml config file used to
//Specify a source and destination model file
//Specifiy a target animation used to create new resampled animations based on start end frames
class AnimationSplitter
{
public:
	//map new animation names to start end times in the original animation
	//these are loaded in from the config file
	typedef std::map<std::string, osg::Vec2> NameToStartEndTimeMap;
	typedef std::pair<std::string, osg::Vec2> NameToStartEndTimePair;
	//map an original animation name to its NameToStartEndTimeMap split version
	typedef std::map<std::string, NameToStartEndTimeMap> SourceToSplitMap;
	typedef std::pair<std::string, NameToStartEndTimeMap> SourceToSplitPair;

	AnimationSplitter(std::string config)
		: _fps(30)
	{

		_ready = ReadXMLConfigHeaderFromFile(config);
		if(!_ready){
			OSG_WARN << "OsgAnimationTools ERROR: an error occured initalizing OsgAnimationTools config. Check your config file" << std::endl;
		}
		OSG_INFO << "OsgAnimationTools INFO: Config found and loaded, sourceFile: '" << _sourceFile << "', destinationFile: '" << _destinationFile << "', FPS: '" << _fps << "'." << std::endl;

		//load the source file
		OSG_INFO << "OsgAnimationTools INFO: Loading sourceFile..." << std::endl;

		_sourceNode = osgDB::readNodeFile(_sourceFile);
		if(!_sourceNode.get())
		{
			OSG_WARN << "OsgAnimationTools ERROR: Failed to load sourceFile '" << _sourceFile << "'. OsgAnimationTools can not continue." << std::endl;
			return;
		}

		OSG_INFO << "OsgAnimationTools INFO: SourceFile loaded successfully" << std::endl;

		OSG_INFO << "OsgAnimationTools INFO: Checking for AnimationSplit info in config file..." << std::endl;
		bool foundSplit = ReadAniSplitFromConfigNode();
		if(!foundSplit || _animationSplits.size() == 0){
			OSG_WARN << "OsgAnimationTools ERROR: No AnimationSplit nodes found in Config, nothing to do." << std::endl;
			return;		
		}

		//now do the split
		if(!FindAndSplitAnimations()){
			OSG_WARN << "OsgAnimationTools ERROR: Failed to Split animations." << std::endl;
			return;					
		}

		//it worked so save the result
		osgDB::writeNodeFile(*_sourceNode.get(), _destinationFile);


	}

protected:

	//Finds the root <OsgAnimationTools> node and reads the source and destination properties from it
	bool ReadXMLConfigHeaderFromFile(std::string configFile)
	{
		//check the file exists
		if(!osgDB::fileExists(configFile))
		{
			OSG_WARN << "OsgAnimationTools XML Config ERROR: Failed to read xml file '" << configFile << "'," << std::endl
					 << "                                        The file does not exists." << std::endl;
			return false;
		}

		//allocate the document node
		osgDB::XmlNode* doc = new osgDB::XmlNode;
		osgDB::XmlNode* root = 0;

		//open the file with xmlinput
		osgDB::XmlNode::Input input;
		input.open(configFile);
		input.readAllDataIntoBuffer();

		//read the file into out document
		doc->read(input);

		//iterate over the document nodes and try and find a OsgAnimationSplitter node to
		//use as a root
		for(osgDB::XmlNode::Children::iterator itr = doc->children.begin();
			itr != doc->children.end() && !root;
			++itr)
		{
			if ((*itr)->name=="OsgAnimationTools") root = (*itr);
		}

		if (root == NULL)
		{
			OSG_WARN << "OsgAnimationTools XML Config ERROR: Failed to read xml file '" << configFile << "'," << std::endl
					 << "                                        OsgAnimationTools Config file must contain a valid <OsgAnimationTools> node." << std::endl;
			return false;
		}

		//we have our head node
		_configNode = root;
		
		//the head node should contain source and destination file properties
		if(_configNode->properties.count("sourceFile") > 0)
		{
			_sourceFile = _configNode->properties["sourceFile" ];
		}else{
			OSG_WARN << "OsgAnimationTools XML Config ERROR: Failed to read xml file '" << configFile << "'," << std::endl
					 << "                                        OsgAnimationTools node must contain a 'sourceFile' property." << std::endl;
			return false;		
		}

		if(_configNode->properties.count("destinationFile") > 0)
		{
			_destinationFile = _configNode->properties["destinationFile" ];
		}else{
			OSG_WARN << "OsgAnimationTools XML Config ERROR: Failed to read xml file '" << configFile << "'," << std::endl
		             << "                                        OsgAnimationTools node must contain a 'destinationFile' property." << std::endl;
			return false;		
		}

		return true;
	}

	//
	//Reads in any AnimationSplit nodes from our configNode,
	bool ReadAniSplitFromConfigNode(){

		//config must have already been read from file
		if(!_configNode.get()){
			OSG_WARN << "OsgAnimationTools AnimationSplit XML Config ERROR: Config file must have been loaded first via call to 'ReadXMLConfigHeaderFromFile'. Can not continue." << std::endl;
			return false;
		}

		//search root children for any <AnimationSplit> nodes
		for(unsigned int i=0; i<_configNode->children.size(); i++)
		{
			//if its name is animation split then we have a vslid split to load
			if(_configNode->children[i]->name == "AnimationSplit")
			{
				NameToStartEndTimeMap splitAni;
				osgDB::XmlNode* splitNode = _configNode->children[i];
				std::string sourceAniName = "";
				int splitCount = 0;

				//an animation split head node contains a sourceAnimation name and a number of splits contained within it's children
				if(splitNode->properties.count("sourceAnimation") > 0)
				{
					sourceAniName = splitNode->properties["sourceAnimation"];
				}else{
					OSG_WARN << "OsgAnimationTools AnimationSplit XML Config ERROR: AnimationSplit nodes must contain a 'sourceAnimation' property." << std::endl;
					return false;		
				}
				if(splitNode->properties.count("fps") > 0)
				{
					std::string fpsStr = splitNode->properties["fps"];
					//not too safe
					_fps = atoi(fpsStr.c_str());

				}else{
					OSG_WARN << "OsgAnimationTools AnimationSplit XML Config ERROR: AnimationSplit nodes must contain a 'splitCount' property." << std::endl;
					return false;		
				}

				//we have the target animation and number of expected splits, now try each child as a NEW_ANIMATION
				for(unsigned int t=0; t<splitNode->children.size(); t++)
				{
					//check name matches
					if(splitNode->children[t]->name == "NewAnimation")
					{
						NameToStartEndTimePair newAnimation;
						if(!ReadNewAnimationFromNode(splitNode->children[t],newAnimation)){
							OSG_WARN << "OsgAnimationTools AnimationSplit XML Config ERROR: Failed to read <NewAnimation> node, can not continue." << std::endl;
							return false;
						}
						//add the new animation to this split 
						splitAni.insert(newAnimation);
					}else{
						OSG_WARN << "OsgAnimationTools AnimationSplit XML Config ERROR: Children of <AnimationSplit> should be of type <NewAnimation>. The config can not be passed." << std::endl;
						return false;	
					}
				}

				//add the new split to our list of splits
				_animationSplits.insert(SourceToSplitPair(sourceAniName,splitAni));
			}//end of split
		}//loop next child

		return true;
	}

	//
	//reads in a single newAnimation as part of a SplitAnimation
	bool ReadNewAnimationFromNode(osgDB::XmlNode* node, NameToStartEndTimePair& newAnimationParams)
	{
		//name should be new animation
		if(!node){
			OSG_WARN << "OsgAnimationTools ReadNewAnimationFromNode ERROR: Null node passed." << std::endl;	
			return false;
		}
		if(node->name != "NewAnimation"){
			OSG_WARN << "OsgAnimationTools ReadNewAnimationFromNode ERROR: Node is not a <NewAnimation> Node." << std::endl;	
			return false;
		}

		std::string newName="";
		int start,end=-1;
		//contains 3 properties, start/end frame, new animation name
		if(node->properties.count("name") > 0)
		{
			newName = node->properties["name"];
		}else{
			OSG_WARN << "OsgAnimationTools ReadNewAnimationFromNode ERROR: <NewAnimation> nodes must contain a 'name' property." << std::endl;
			return false;		
		}
		if(node->properties.count("startFrame") > 0)
		{
			std::string startFrameStr = node->properties["startFrame"];
			//convert to frame
			start = atoi(startFrameStr.c_str());
		}else{
			OSG_WARN << "OsgAnimationTools ReadNewAnimationFromNode ERROR: <NewAnimation> nodes must contain a 'startFrame' property." << std::endl;
			return false;		
		}
		if(node->properties.count("endFrame") > 0)
		{
			std::string endFrameStr = node->properties["endFrame"];
			//convert to frame
			end = atoi(endFrameStr.c_str());
		}else{
			OSG_WARN << "OsgAnimationTools ReadNewAnimationFromNode ERROR: <NewAnimation> nodes must contain a 'startFrame' property." << std::endl;
			return false;		
		}

		//store info in the passed NameToStartEndTimePair
		newAnimationParams.first = newName;
		newAnimationParams.second = osg::Vec2(start, end);
		return true;
	}

	//
	//Find the source animation in our source node, then use it to create the new split versions
	//we should have already read the config and filled our _animationSplits list
	bool FindAndSplitAnimations()
	{
		if(_animationSplits.size() == 0){
			return false;
		}
		SourceToSplitMap::iterator splitItr = _animationSplits.begin();
		for( ;splitItr!=_animationSplits.end(); splitItr++)
		{
			//get the name of the source animation
			FindOsgAnimationByName findAniVis((*splitItr).first);
			_sourceNode->accept(findAniVis);
			if(findAniVis.p_ani != NULL)
			{
				//the animation was found, we need to copy it so it can be replaced with the new split animations
				osg::ref_ptr<osgAnimation::Animation> originalAnimation = osg::clone(findAniVis.p_ani, osg::CopyOp::DEEP_COPY_ALL);
				//delete the original (at this point findAniVis.p_ani will probably become invalid, which is ok as we copied it above)
				findAniVis.p_manager->unregisterAnimation(findAniVis.p_ani);

				//now iterate over this splits new aniations
				NameToStartEndTimeMap::iterator newAnimsItr = (*splitItr).second.begin();
				for( ;newAnimsItr!=(*splitItr).second.end(); newAnimsItr++)
				{
					//get the split info
					std::string newName = (*newAnimsItr).first;
					osg::Vec2 startEndVec = (*newAnimsItr).second;
					osg::ref_ptr<osgAnimation::Animation> newAnimation = AnimationUtils::ResampleAnimation(originalAnimation.get(), (int)startEndVec.x(),(int)startEndVec.y(),_fps,newName);
					if(newAnimation.get()){
						findAniVis.p_manager->registerAnimation(newAnimation.get());
					}else{
						return false;
					}
				}
			}else{
				OSG_WARN << "OsgAnimationTools FindAndSplitAnimations ERROR: Failed to find animation named '" << (*splitItr).first << "', can not perform split." << std::endl;
				return false;	
			}
		}
		return true;
	}

protected:

	//was the config found and loaded
	bool _ready;

	//root xml node for the OsgAnimation tools config
	osg::ref_ptr<osgDB::XmlNode> _configNode;

	//the head node of the xml config should give a source and destination file for the conversion
	std::string _sourceFile;
	std::string _destinationFile;

	//the node loaded from source
	osg::ref_ptr<osg::Node> _sourceNode;

	//the fps of the source file, this is needed to convert the passed frames to a time
	int _fps;

	//animation splits
	SourceToSplitMap _animationSplits;
};


int main(int argc, char** argv)
{
	AnimationSplitter spiltter("./splitConfig.xml");
	return 0;
}
<?xml version="1.0" encoding="ISO-8859-15"?>
<OsgAnimationTools sourceFile='./Data/Models/Terrorist/terrorist.FBX' destinationFile='./splitExport.osgb' fps='30'>
	<AnimationSplit sourceAnimation='Take 001'>
		<NewAnimation name='staying' startFrame='0' endFrame='49'/>
		<NewAnimation name='fire_standing' startFrame='50' endFrame='89'/>
		<NewAnimation name='running' startFrame='90' endFrame='111'/>
		<NewAnimation name='walking' startFrame='112' endFrame='147'/>
		<NewAnimation name='grenade_throw' startFrame='148' endFrame='187'/>
		<NewAnimation name='hide_behind_wall' startFrame='188' endFrame='213'/>
		<NewAnimation name='from_standing_to_squat' startFrame='214' endFrame='241'/>
		<NewAnimation name='fire_sguating' startFrame='242' endFrame='291'/>
		<NewAnimation name='from_sguating_to_stand' startFrame='292' endFrame='313'/>
		<NewAnimation name='jump_down' startFrame='314' endFrame='359'/>
		<NewAnimation name='fire_lying' startFrame='360' endFrame='379'/>
		<NewAnimation name='stand_up' startFrame='380' endFrame='425'/>
		<NewAnimation name='dying_on_belt' startFrame='426' endFrame='532'/>
		<NewAnimation name='dying_on_spin' startFrame='533' endFrame='568'/>
		<NewAnimation name='jump' startFrame='569' endFrame='614'/>
	</AnimationSplit>
</OsgAnimationTools>
_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to