/* -*-c++-*- * Copyright (C) 2008 Cedric Pinson * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace osgDB; using namespace osg; bool Bone_readLocalData(Object& obj, Input& fr) { osgAnimation::Bone& bone = dynamic_cast(obj); osg::Quat att; bool iteratorAdvanced = false; if (fr.matchSequence("bindQuaternion %f %f %f %f")) { fr[1].getFloat(att[0]); fr[2].getFloat(att[1]); fr[3].getFloat(att[2]); fr[4].getFloat(att[3]); fr += 5; iteratorAdvanced = true; } osg::Vec3d pos(0,0,0); if (fr.matchSequence("bindPosition %f %f %f")) { fr[1].getFloat(pos[0]); fr[2].getFloat(pos[1]); fr[3].getFloat(pos[2]); fr += 4; iteratorAdvanced = true; } osg::Vec3d scale(1,1,1); if (fr.matchSequence("bindScale %f %f %f")) { fr[1].getFloat(scale[0]); fr[2].getFloat(scale[1]); fr[3].getFloat(scale[2]); fr += 4; iteratorAdvanced = true; } bone.setBindMatrixInBoneSpace( osg::Matrix(att) * osg::Matrix::translate(pos)); return iteratorAdvanced; } bool Bone_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::Bone& bone = dynamic_cast(obj); osg::Vec3 t; osg::Quat r; osg::Vec3 s; osg::Quat rs; bone.getBindMatrixInBoneSpace().decompose(t,r,s,rs); fw.indent() << "bindQuaternion " << r << std::endl; fw.indent() << "bindPosition " << t << std::endl; fw.indent() << "bindScale " << s << std::endl; return true; } RegisterDotOsgWrapperProxy g_atkBoneProxy ( new osgAnimation::Bone, "osgAnimation::Bone", "Object Node Transform osgAnimation::Bone Group", &Bone_readLocalData, &Bone_writeLocalData ); bool Skeleton_readLocalData(Object& obj, Input& fr) { return false; } bool Skeleton_writeLocalData(const Object& obj, Output& fr) { return true; } RegisterDotOsgWrapperProxy g_atkRootSkeletonProxy ( new osgAnimation::Skeleton, "osgAnimation::Skeleton", "Object Node Transform osgAnimation::Bone osgAnimation::Skeleton Group", &Skeleton_readLocalData, &Skeleton_writeLocalData, DotOsgWrapper::READ_AND_WRITE ); // Helper method for reading channels bool Animation_readChannel(osgAnimation::Channel* pChannel, Input& fr) { bool iteratorAdvanced = false; std::string name = "unknown"; if (fr.matchSequence("name %s")) { if (fr[1].getStr()) name = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } pChannel->setName(name); std::string target = "unknown"; if (fr.matchSequence("target %s")) { if (fr[1].getStr()) target = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } pChannel->setTargetName(target); // we dont need this info float weight = 1.0; if (fr.matchSequence("weight %f")) { fr[1].getFloat(weight); fr += 2; iteratorAdvanced = true; } // pChannel->setWeight(weight); return iteratorAdvanced; } bool Animation_readLocalData(Object& obj, Input& fr) { osgAnimation::Animation& anim = dynamic_cast(obj); bool iteratorAdvanced = false; if (fr.matchSequence("playmode %w")) { if (fr[1].matchWord("ONCE")) anim.setPlaymode(osgAnimation::Animation::ONCE); else if (fr[1].matchWord("STAY")) anim.setPlaymode(osgAnimation::Animation::STAY); else if (fr[1].matchWord("LOOP")) anim.setPlaymode(osgAnimation::Animation::LOOP); else if (fr[1].matchWord("PPONG")) anim.setPlaymode(osgAnimation::Animation::PPONG); fr += 2; iteratorAdvanced = true; } if (fr.matchSequence("weight %f")) { float weight; fr[1].getFloat(weight); fr += 2; iteratorAdvanced = true; anim.setWeight(weight); } if (fr.matchSequence("duration %f")) { float duration; fr[1].getFloat(duration); fr += 2; iteratorAdvanced = true; anim.setDuration(duration); } if (fr.matchSequence("starttime %f")) { float starttime; fr[1].getFloat(starttime); fr += 2; iteratorAdvanced = true; anim.setStartTime(starttime); } int nbChannels = 0; if (fr.matchSequence("num_channels %i")) { fr[1].getInt(nbChannels); fr += 2; iteratorAdvanced = true; } for (int i = 0; i < nbChannels; i++) { if (fr.matchSequence("DoubleLinearChannel {")) { fr += 2; osgAnimation::DoubleLinearChannel* channel = new osgAnimation::DoubleLinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { double v; float time; if (fr.matchSequence("key %f %f")) { fr[1].getFloat(time); fr[2].getFloat(v); fr += 3; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::DoubleKeyframe(time, v)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } else if (fr.matchSequence("FloatLinearChannel {")) { fr += 2; osgAnimation::FloatLinearChannel* channel = new osgAnimation::FloatLinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { float v; float time; if (fr.matchSequence("key %f %f")) { fr[1].getFloat(time); fr[2].getFloat(v); fr += 3; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(time, v)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } else if (fr.matchSequence("Vec2LinearChannel {")) { fr += 2; osgAnimation::Vec2LinearChannel* channel = new osgAnimation::Vec2LinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { osg::Vec2 v; float time; if (fr.matchSequence("key %f %f %f")) { fr[1].getFloat(time); fr[2].getFloat(v[0]); fr[3].getFloat(v[1]); fr += 4; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec2Keyframe(time, v)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } else if (fr.matchSequence("Vec3LinearChannel {")) { fr += 2; osgAnimation::Vec3LinearChannel* channel = new osgAnimation::Vec3LinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { osg::Vec3 v; float time; if (fr.matchSequence("key %f %f %f %f")) { fr[1].getFloat(time); fr[2].getFloat(v[0]); fr[3].getFloat(v[1]); fr[4].getFloat(v[2]); fr += 5; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(time, v)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } else if (fr.matchSequence("Vec4LinearChannel {")) { fr += 2; osgAnimation::Vec4LinearChannel* channel = new osgAnimation::Vec4LinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { osg::Vec4 v; float time; if (fr.matchSequence("key %f %f %f %f %f")) { fr[1].getFloat(time); fr[2].getFloat(v[0]); fr[3].getFloat(v[1]); fr[4].getFloat(v[2]); fr[5].getFloat(v[3]); fr += 6; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec4Keyframe(time, v)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } else if (fr.matchSequence("QuatSphericalLinearChannel {")) { fr += 2; osgAnimation::QuatSphericalLinearChannel* channel = new osgAnimation::QuatSphericalLinearChannel; if (Animation_readChannel(channel, fr)) iteratorAdvanced = true; int nbKeys; if (fr.matchSequence("Keyframes %i {")) { fr[1].getInt(nbKeys); fr += 3; iteratorAdvanced = true; for (int k = 0; k < nbKeys; k++) { osg::Quat q; float time; if (fr.matchSequence("key %f %f %f %f %f")) { fr[1].getFloat(time); fr[2].getFloat(q[0]); fr[3].getFloat(q[1]); fr[4].getFloat(q[2]); fr[5].getFloat(q[3]); fr += 6; channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::QuatKeyframe(time, q)); iteratorAdvanced = true; } } anim.addChannel(channel); if (fr.matchSequence("}")) // keyframes fr += 1; } if (fr.matchSequence("}")) // channel fr += 1; } // Deprecated // Reading of old channel info // Kept here for easy conversion of old .osg data to new format else if (fr.matchSequence("Channel {")) { fr += 2; std::string name = "unknown"; if (fr.matchSequence("name %s")) { if (fr[1].getStr()) name = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } std::string target = "unknown"; if (fr.matchSequence("target %s")) { if (fr[1].getStr()) target = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } std::string type = "unknown"; int nbKeys; if (fr.matchSequence("Keyframes %s %i {")) { if (fr[1].getStr()) type = fr[1].getStr(); fr[2].getInt(nbKeys); fr += 4; iteratorAdvanced = true; osgAnimation::Channel* channel = 0; if (type == "Quat") { osgAnimation::QuatSphericalLinearChannel* c = new osgAnimation::QuatSphericalLinearChannel; c->getOrCreateSampler()->getOrCreateKeyframeContainer(); channel = c; } else if (type == "Vec3") { osgAnimation::Vec3LinearChannel* c = new osgAnimation::Vec3LinearChannel; c->getOrCreateSampler()->getOrCreateKeyframeContainer(); channel = c; } if (channel) { for (int k = 0; k < nbKeys; k++) { if (type == "Quat") { osg::Quat q; float time; fr.matchSequence("key %f %f %f %f %f"); fr[1].getFloat(time); fr[2].getFloat(q[0]); fr[3].getFloat(q[1]); fr[4].getFloat(q[2]); fr[5].getFloat(q[3]); fr += 6; osgAnimation::QuatSphericalLinearChannel* c = dynamic_cast(channel); c->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::QuatKeyframe(time, q)); iteratorAdvanced = true; } else if (type == "Vec3") { osg::Vec3 v; float time; fr.matchSequence("key %f %f %f %f"); fr[1].getFloat(time); fr[2].getFloat(v[0]); fr[3].getFloat(v[1]); fr[4].getFloat(v[2]); fr += 5; osgAnimation::Vec3LinearChannel* c = dynamic_cast(channel); c->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(time, v)); iteratorAdvanced = true; } } channel->setName(name); channel->setTargetName(target); anim.addChannel(channel); } if (fr.matchSequence("}")) // keyframes fr += 1; if (fr.matchSequence("}")) // channel fr += 1; } } } return iteratorAdvanced; } // Helper method for writing channels template void Animation_writeChannel(const std::string& channelString, ChannelType* pChannel, Output& fw) { fw.indent() << channelString.c_str() << " {" << std::endl; fw.moveIn(); fw.indent() << "name \"" << pChannel->getName() << "\"" << std::endl; fw.indent() << "target \"" << pChannel->getTargetName() << "\"" << std::endl; // fw.indent() << "weight " << pChannel->getWeight() << std::endl; ContainerType* kfc = pChannel->getSamplerTyped()->getKeyframeContainerTyped(); if (kfc) { fw.indent() << "Keyframes " << kfc->size() << " {" << std::endl; fw.moveIn(); for (unsigned int k = 0; k < kfc->size(); k++) { fw.indent() << "key " << (*kfc)[k].getTime() << " " << (*kfc)[k].getValue() << std::endl; } fw.moveOut(); fw.indent() << "}" << std::endl; fw.moveOut(); fw.indent() << "}" << std::endl; } } bool Animation_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::Animation& anim = dynamic_cast(obj); switch (anim.getPlayMode()) { case osgAnimation::Animation::ONCE: fw.indent() << "playmode ONCE" << std::endl; break; case osgAnimation::Animation::STAY: fw.indent() << "playmode STAY" << std::endl; break; case osgAnimation::Animation::LOOP: fw.indent() << "playmode LOOP" << std::endl; break; case osgAnimation::Animation::PPONG: fw.indent() << "playmode PPONG" << std::endl; break; default: break; } fw.indent() << "weight " << anim.getWeight() << std::endl; fw.indent() << "duration " << anim.getDuration() << std::endl; fw.indent() << "starttime " << anim.getStartTime() << std::endl; fw.indent() << "num_channels " << anim.getChannels().size() << std::endl; for (unsigned int i = 0; i < anim.getChannels().size(); i++) { osgAnimation::Channel* pChannel = anim.getChannels()[i].get(); osgAnimation::DoubleLinearChannel* pDlc = dynamic_cast(pChannel); if (pDlc) { Animation_writeChannel("DoubleLinearChannel", pDlc, fw); continue; } osgAnimation::FloatLinearChannel* pFlc = dynamic_cast(pChannel); if (pFlc) { Animation_writeChannel("FloatLinearChannel", pFlc, fw); continue; } osgAnimation::Vec2LinearChannel* pV2lc = dynamic_cast(pChannel); if (pV2lc) { Animation_writeChannel("Vec2LinearChannel", pV2lc, fw); continue; } osgAnimation::Vec3LinearChannel* pV3lc = dynamic_cast(pChannel); if (pV3lc) { Animation_writeChannel("Vec3LinearChannel", pV3lc, fw); continue; } osgAnimation::Vec4LinearChannel* pV4lc = dynamic_cast(pChannel); if (pV4lc) { Animation_writeChannel("Vec4LinearChannel", pV4lc, fw); continue; } osgAnimation::QuatSphericalLinearChannel* pQslc = dynamic_cast(pChannel); if (pQslc) { Animation_writeChannel("QuatSphericalLinearChannel", pQslc, fw); continue; } osgAnimation::FloatCubicBezierChannel* pFcbc = dynamic_cast(pChannel); if (pFcbc) { Animation_writeChannel("FloatCubicBezierChannel", pFcbc, fw); continue; } osgAnimation::DoubleCubicBezierChannel* pDcbc = dynamic_cast(pChannel); if (pDcbc) { Animation_writeChannel("DoubleCubicBezierChannel", pDcbc, fw); continue; } osgAnimation::Vec2CubicBezierChannel* pV2cbc = dynamic_cast(pChannel); if (pV2cbc) { Animation_writeChannel("Vec2CubicBezierChannel", pV2cbc, fw); continue; } osgAnimation::Vec3CubicBezierChannel* pV3cbc = dynamic_cast(pChannel); if (pV3cbc) { Animation_writeChannel("Vec3CubicBezierChannel", pV3cbc, fw); continue; } osgAnimation::Vec4CubicBezierChannel* pV4cbc = dynamic_cast(pChannel); if (pV4cbc) { Animation_writeChannel("Vec4CubicBezierChannel", pV4cbc, fw); continue; } } return true; } RegisterDotOsgWrapperProxy g_atkAnimationProxy ( new osgAnimation::Animation, "osgAnimation::Animation", "Object osgAnimation::Animation", &Animation_readLocalData, &Animation_writeLocalData ); bool AnimationManagerBase_readLocalData(osgAnimation::AnimationManagerBase& manager, Input& fr) { int nbAnims = 0; bool iteratorAdvanced = false; if (fr.matchSequence("num_animations %i")) { fr[1].getInt(nbAnims); fr += 2; iteratorAdvanced = true; } for (int i = 0; i < nbAnims; i++) { Object* o = fr.readObject(); osgAnimation::Animation* a = dynamic_cast(o); if (a) { manager.registerAnimation(a); iteratorAdvanced = true; } else osg::notify(osg::WARN)<<"Warning: can't read an animation object"<< std::endl; } return iteratorAdvanced; } bool BasicAnimationManager_readLocalData(Object& obj, Input& fr) { osgAnimation::BasicAnimationManager& manager = dynamic_cast(obj); return AnimationManagerBase_readLocalData(manager, fr); } bool TimelineAnimationManager_readLocalData(Object& obj, Input& fr) { osgAnimation::TimelineAnimationManager& manager = dynamic_cast(obj); return AnimationManagerBase_readLocalData(manager, fr); } bool AnimationManagerBase_writeLocalData(const osgAnimation::AnimationManagerBase& manager, Output& fw) { const osgAnimation::AnimationList& animList = manager.getAnimationList(); fw.indent() << "num_animations " << animList.size() << std::endl; for (osgAnimation::AnimationList::const_iterator it = animList.begin(); it != animList.end(); it++) { if (!fw.writeObject(**it)) osg::notify(osg::WARN)<<"Warning: can't write an animation object"<< std::endl; } return true; } bool BasicAnimationManager_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::BasicAnimationManager& manager = dynamic_cast(obj); return AnimationManagerBase_writeLocalData(manager, fw); } bool TimelineAnimationManager_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::TimelineAnimationManager& manager = dynamic_cast(obj); return AnimationManagerBase_writeLocalData(manager, fw); } RegisterDotOsgWrapperProxy g_BasicAnimationManagerProxy ( new osgAnimation::BasicAnimationManager, "osgAnimation::BasicAnimationManager", "Object NodeCallback osgAnimation::BasicAnimationManager", &BasicAnimationManager_readLocalData, &BasicAnimationManager_writeLocalData, DotOsgWrapper::READ_AND_WRITE ); RegisterDotOsgWrapperProxy g_TimelineAnimationManagerProxy ( new osgAnimation::TimelineAnimationManager, "osgAnimation::TimelineAnimationManager", "Object NodeCallback osgAnimation::TimelineAnimationManager", &TimelineAnimationManager_readLocalData, &TimelineAnimationManager_writeLocalData, DotOsgWrapper::READ_AND_WRITE ); bool RigGeometry_readLocalData(Object& obj, Input& fr) { osgAnimation::RigGeometry& geom = dynamic_cast(obj); osg::ref_ptr vmap = new osgAnimation::VertexInfluenceMap; int nbGroups = 0; bool iteratorAdvanced = false; if (fr.matchSequence("num_influences %i")) { fr[1].getInt(nbGroups); fr += 2; iteratorAdvanced = true; } for (int i = 0; i < nbGroups; i++) { int nbVertexes = 0; std::string name; if (fr.matchSequence("osgAnimation::VertexInfluence %s %i {")) { name = fr[1].getStr(); fr[2].getInt(nbVertexes); fr += 4; iteratorAdvanced = true; } osgAnimation::VertexInfluence vi; vi.setName(name); vi.reserve(nbVertexes); for (int j = 0; j < nbVertexes; j++) { int index = -1; float weight = 1; if (fr.matchSequence("%i %f")) { fr[0].getInt(index); fr[1].getFloat(weight); fr += 2; iteratorAdvanced = true; } vi.push_back(osgAnimation::VertexIndexWeight(index, weight)); } if (fr.matchSequence("}")) { fr+=1; } (*vmap)[name] = vi; } if (!vmap->empty()) geom.setInfluenceMap(vmap.get()); return iteratorAdvanced; } bool RigGeometry_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::RigGeometry& geom = dynamic_cast(obj); const osgAnimation::VertexInfluenceMap* vm = geom.getInfluenceMap(); if (!vm) return true; fw.indent() << "num_influences " << vm->size() << std::endl; fw.moveIn(); for (osgAnimation::VertexInfluenceMap::const_iterator it = vm->begin(); it != vm->end(); it++) { std::string name = it->first; if (name.empty()) name = "Empty"; fw.indent() << "osgAnimation::VertexInfluence \"" << name << "\" " << it->second.size() << " {" << std::endl; fw.moveIn(); const osgAnimation::VertexInfluence& vi = it->second; for (osgAnimation::VertexInfluence::const_iterator itv = vi.begin(); itv != vi.end(); itv++) { fw.indent() << itv->first << " " << itv->second << std::endl; } fw.moveOut(); fw.indent() << "}" << std::endl; } fw.moveOut(); return true; } RegisterDotOsgWrapperProxy g_atkRigGeometryProxy ( new osgAnimation::RigGeometry, "osgAnimation::RigGeometry", "Object Drawable osgAnimation::RigGeometry Geometry", &RigGeometry_readLocalData, &RigGeometry_writeLocalData, DotOsgWrapper::READ_AND_WRITE ); bool MorphGeometry_readLocalData(Object& obj, Input& fr) { osgAnimation::MorphGeometry& geom = dynamic_cast(obj); bool iteratorAdvanced = false; if (fr[0].matchWord("method")) { if (fr[1].matchWord("NORMALIZED")) { geom.setMethod(osgAnimation::MorphGeometry::NORMALIZED); fr+=2; iteratorAdvanced = true; } else if (fr[1].matchWord("RELATIVE")) { geom.setMethod(osgAnimation::MorphGeometry::RELATIVE); fr+=2; iteratorAdvanced = true; } } if (fr[0].matchWord("morphNormals")) { if (fr[1].matchWord("TRUE")) { geom.setMorphNormals(true); fr+=2; iteratorAdvanced = true; } else if (fr[1].matchWord("FALSE")) { geom.setMorphNormals(false); fr+=2; iteratorAdvanced = true; } } int num_morphTargets = 0; if (fr.matchSequence("num_morphTargets %i")) { fr[1].getInt(num_morphTargets); fr += 2; iteratorAdvanced = true; } for (int i = 0; i < num_morphTargets; i++) { if (fr.matchSequence("MorphTarget {")) { int entry = fr[0].getNoNestedBrackets(); fr += 2; iteratorAdvanced = true; while (!fr.eof() && fr[0].getNoNestedBrackets()>entry) { float weight = 1.0; if (fr.matchSequence("weight %f")) { fr[1].getFloat(weight); fr += 2; } osg::Drawable* drawable = NULL; drawable = fr.readDrawable(); osg::Geometry* geometry = dynamic_cast(drawable); if (geometry) geom.addMorphTarget(geometry, weight); } if (fr.matchSequence("}")) fr += 1; } } return iteratorAdvanced; } bool MorphGeometry_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::MorphGeometry& geom = dynamic_cast(obj); switch(geom.getMethod()) { case(osgAnimation::MorphGeometry::NORMALIZED): fw.indent() << "method NORMALIZED"<