/* -*-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. */ #ifndef OSGANIMATION_BONE_H #define OSGANIMATION_BONE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgAnimation { // A bone can't have more than one parent Bone, so sharing a part of Bone's hierarchy // has not sense. You can share the entire hierarchie but not only a part of class OSGANIMATION_EXPORT Bone : public osg::Transform { public: typedef osg::ref_ptr PointerType; typedef std::map BoneMap; typedef osg::Matrix MatrixType; META_Node(osgAnimation, Bone); Bone(const Bone& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY); Bone(const std::string& name = ""); void setDefaultUpdateCallback(const std::string& name = ""); struct BoneMapVisitor : public osg::NodeVisitor { BoneMap _map; BoneMapVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} META_NodeVisitor("osgAnimation","BoneMapVisitor") void apply(osg::Node&) { return; } void apply(osg::Transform& node) { Bone* bone = dynamic_cast(&node); if (bone) { _map[bone->getName()] = bone; traverse(node); } } }; struct FindNearestParentAnimationManager : public osg::NodeVisitor { osg::ref_ptr _manager; FindNearestParentAnimationManager() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS) {} void apply(osg::Node& node) { if (_manager.valid()) return; osg::NodeCallback* callback = node.getUpdateCallback(); while (callback) { _manager = dynamic_cast(callback); if (_manager.valid()) return; callback = callback->getNestedCallback(); } traverse(node); } }; class OSGANIMATION_EXPORT UpdateBone : public AnimationUpdateCallback { public: osg::ref_ptr _position; osg::ref_ptr _quaternion; osg::ref_ptr _scale; public: META_Object(osgAnimation, UpdateBone); UpdateBone(const UpdateBone& apc,const osg::CopyOp& copyop); UpdateBone(const std::string& name = "") { setName(name); _quaternion = new osgAnimation::QuatTarget; _position = new osgAnimation::Vec3Target; _scale = new osgAnimation::Vec3Target; } void update(osgAnimation::Bone& bone) { bone.setTranslation(_position->getValue()); bone.setRotation(_quaternion->getValue()); bone.setScale(_scale->getValue()); bone.dirtyBound(); } bool needLink() const { // the idea is to return true if nothing is linked return !((_position->getCount() + _quaternion->getCount() + _scale->getCount()) > 3); } bool link(osgAnimation::Channel* channel) { if (channel->getName().find("quaternion") != std::string::npos) { osgAnimation::QuatSphericalLinearChannel* qc = dynamic_cast(channel); if (qc) { qc->setTarget(_quaternion.get()); return true; } } else if (channel->getName().find("position") != std::string::npos) { osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); if (vc) { vc->setTarget(_position.get()); return true; } } else if (channel->getName().find("scale") != std::string::npos) { osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); if (vc) { vc->setTarget(_scale.get()); return true; } } else { std::cerr << "Channel " << channel->getName() << " does not contain a valid symbolic name for this class" << std::endl; } return false; } /** Callback method called by the NodeVisitor when visiting a node.*/ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) { Bone* b = dynamic_cast(node); if (b && !_manager.valid()) { FindNearestParentAnimationManager finder; if (b->getParents().size() > 1) { osg::notify(osg::WARN) << "A Bone should not have multi parent ( " << b->getName() << " ) has parents "; osg::notify(osg::WARN) << "( " << b->getParents()[0]->getName(); for (int i = 1; i < (int)b->getParents().size(); i++) osg::notify(osg::WARN) << ", " << b->getParents()[i]->getName(); osg::notify(osg::WARN) << ")" << std::endl; return; } b->getParents()[0]->accept(finder); if (!finder._manager.valid()) { osg::notify(osg::WARN) << "Warning can't update Bone, path to parent AnimationManagerBase not found" << std::endl; return; } _manager = finder._manager.get(); } updateLink(); update(*b); } traverse(node,nv); } }; virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; Bone* getBoneParent(); const Bone* getBoneParent() const; void setTranslation(const osg::Vec3& trans) { _position = trans;} void setRotation(const osg::Quat& quat) { _rotation = quat;} void setScale(const osg::Vec3& scale) { _scale = scale;} const osg::Vec3& getTranslation() const { return _position;} const osg::Quat& getRotation() const { return _rotation;} osg::Matrix getMatrixInBoneSpace() const { return (osg::Matrix(getRotation())) * osg::Matrix::translate(getTranslation()) * _bindInBoneSpace;} const osg::Matrix& getBindMatrixInBoneSpace() const { return _bindInBoneSpace; } const osg::Matrix& getMatrixInSkeletonSpace() const { return _boneInSkeletonSpace; } const osg::Matrix& getInvBindMatrixInSkeletonSpace() const { return _invBindInSkeletonSpace;} void setMatrixInSkeletonSpace(const osg::Matrix& matrix) { _boneInSkeletonSpace = matrix; } void setBindMatrixInBoneSpace(const osg::Matrix& matrix) { _bindInBoneSpace = matrix; _needToRecomputeBindMatrix = true; } inline bool needToComputeBindMatrix() { return _needToRecomputeBindMatrix;} virtual void computeBindMatrix(); bool needLink() const; void setNeedToComputeBindMatrix(bool state) { _needToRecomputeBindMatrix = state; } /** Add Node to Group. * If node is not NULL and is not contained in Group then increment its * reference count, add it to the child list and dirty the bounding * sphere to force it to recompute on next getBound() and return true for success. * Otherwise return false. Scene nodes can't be added as child nodes. */ virtual bool addChild( Node *child ); BoneMap getBoneMap(); protected: osg::Vec3 _position; osg::Quat _rotation; osg::Vec3 _scale; // flag to recompute bind pose bool _needToRecomputeBindMatrix; // bind data osg::Matrix _bindInBoneSpace; osg::Matrix _invBindInSkeletonSpace; // bone updated osg::Matrix _boneInSkeletonSpace; }; inline bool Bone::computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor*) const { if (_referenceFrame==RELATIVE_RF) matrix.preMult(getMatrixInBoneSpace()); else matrix = getMatrixInBoneSpace(); return true; } inline bool Bone::computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor*) const { if (_referenceFrame==RELATIVE_RF) matrix.postMult(osg::Matrix::inverse(getMatrixInBoneSpace())); else matrix = osg::Matrix::inverse(getMatrixInBoneSpace()); return true; } } #endif