Hi Renato My bad, the message is very miss leading and the errors you've received shouldn't have caused any problems.
-The split count issue is from a bad copy and paste. So the splitCount variable shouldn't be there either. -The fps not being read is my bad again. I was heading for a more complex solution with an fps for each split but decided to make it global but forgot to move the reading code, and had in fact just been depending on the default, which was 30. Attached is a revised cpp, give that a try and let me know the results. If you get a crash and no decent error I'd be happy to take a look at the file my end. Tom On 16 February 2011 04:48, Renato Silveira <silveira....@gmail.com> wrote: > Hi Thomas, > > I tested your code and I got an error: > "OsgAnimationTools AnimationSplit XML Config ERROR: AnimationSplit nodes > must contain a 'splitCount' property." > > The function that reads my FPS returns 0, even when I set to 30 in the xml > file. > Also, the function ReadAniSplitFromConfigNode() has a variable "splitCount" > that is never used. > If you can help me, I will be thankful. > > Renato > > > > > On Mon, Feb 14, 2011 at 12:22 AM, Renato Silveira > <silveira....@gmail.com>wrote: > >> Thank you very mutch! >> I will check it. >> >> Renato >> >> >> On Sun, Feb 13, 2011 at 7:06 PM, Thomas Hogarth <thomas.hoga...@gmail.com >> > wrote: >> >>> >>Attached is the cpp and an example config file. I think it's pretty >>> straight forward. >>> >>> Looks like the forum blocked my xml config so here is a copy in the post >>> below >>> >>> <?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> >>> >>> >> >> >> -- >> Renato Silveira (Ph. D. Student) >> >> Informatics Institute - UFRGS >> Porto Alegre - RS - Brazil >> http://www.inf.ufrgs.br/~rsilveira >> > > > > -- > Renato Silveira (Ph. D. Student) > > Informatics Institute - UFRGS > Porto Alegre - RS - Brazil > http://www.inf.ufrgs.br/~rsilveira >
// //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; } if(_configNode->properties.count("fps") > 0) { std::string fpsStr = _configNode->properties["fps"]; //not too safe _fps = atoi(fpsStr.c_str()); }else{ OSG_WARN << "OsgAnimationTools AnimationSplit XML Config WARNING: Your config does not specifiy a 'fps' property. Defaulting to 30 fps" << std::endl; } 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 = ""; //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; } //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; }
_______________________________________________ osg-users mailing list osg-users@lists.openscenegraph.org http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org