ikhierarchy.h

Go to the documentation of this file.
00001 /**********************************************************************
00002  *<
00003     FILE: IKHierarchy.h
00004 
00005     DESCRIPTION:  Geometrical representation of the ik problem. Note that
00006                   this file should not dependent on Max SDK, except for
00007                   some math classes, such as Matrix3, Point3, etc.
00008 
00009     CREATED BY: Jianmin Zhao
00010 
00011     HISTORY: created 16 March 2000
00012 
00013  *> Copyright (c) 1994, All Rights Reserved.
00014  **********************************************************************/
00015 #pragma once
00016 
00017 #include "maxheap.h"
00018 #include "coreexp.h"
00019 #include "point2.h"
00020 #include "point3.h"
00021 #include "maxtypes.h"
00022 #include "matrix3.h"
00023 
00024 // forward declarations
00025 class IIKChainControl;
00026 
00027 namespace IKSys
00028 {
00029 //--------------
00037   class ZeroPlaneMap : public MaxHeapOperators {
00038   public:
00044     virtual Point3 operator()(const Point3& EEAxis) const =0;
00048     virtual ~ZeroPlaneMap() {}
00049   };
00050 
00051 const Interface_ID kGoalID(0x53937e2, 0x2be92941);
00052 const Interface_ID kHIIKGoalID(0x2497c0e, 0x376f602a);
00053 const Interface_ID kSplineIKGoalID(0x4ee7cd9, 0x68a54886);
00054 
00056 const Interface_ID IID_SPLINE_IKGOAL2(0x163a2f78, 0xb3027de);
00057 
00058 enum SAParentSpace {
00059     kSAInGoal,
00060     kSAInStartJoint,
00061     kSANotApplicable = 1000
00062 };
00063 
00064 // A LinkChain consists of a RootLink and a number of Links.
00065 // A RootLink consists of a rotation plus a rigidExtend. It transforms
00066 // like this:
00067 //  To_Coordinate_Frame = rigidExtend * rotXYZ * From_Coordinate_Frame.
00068 // where rotXYZ = Rot_x(rotXYZ[0]) * Rot_y(rotXYZ[1]) * Rot_z(rotXYZ[2]).
00069 // 
00070 // * Note that not all the x, y, and z, are degrees of freedom. Only
00071 // Active() ones are. We put the whole rotation here so that some
00072 // solver may choose to use it as a full rotation and then clamp the
00073 // result to the permissible range.
00074 // 
00075 // * LinkMatrix(bool include_rot) returns rigidExtend if include_rot is
00076 // false and returns the whole matrix from the From_Coordinate_Fram to
00077 // To_Coordinate_Frame, i.e., rigidExtend*rotXYZ.rotXYZ are not all degrees of freedom. Only the active ones are. 
00078 //
00079 // * Matrix3& ApplyLinkMatrix(Matrix3& mat, bool) applies the LinkMatrix() to
00080 // the input matrix from the left, i.e., mat = LinkMatrix(bool)*mat,
00081 // and returns the reference to the input matrix.
00082 //
00083 // * Matrix3& RotateByAxis(Matrix3&, unsigned i) pre-applies the
00084 // rotation about x, y, or z (corresponding to i=0,1,or 2).
00085 // Therefore, starting with the identity matrix, mat,
00086 //  ApplyLinkMatrix(
00087 //      RotateByAxis(
00088 //          RotateByAxis(
00089 //              RotateByAxis(mat, 2),
00090 //              1),
00091 //          0),
00092 //      false)
00093 //  should equal to LinkMatrix(true).
00094 //
00122   class RootLink : public MaxHeapOperators {
00123   public:
00127     RootLink():flags(7){} // x,y,z, are all active. No joint limits.
00128     Point3      rotXYZ;
00129     Point3      initXYZ;
00130     Point3      llimits;
00131     Point3      ulimits;
00132     Matrix3     rigidExtend;
00141     bool        GetActive(unsigned i) const { return flags&(1<<i)?true:false;}
00150     bool        GetLLimited(unsigned i) const { return flags&(1<<(i+3))?true:false;}
00159     bool        GetULimited(unsigned i) const { return flags&(1<<(i+6))?true:false;}
00160     
00174     CoreExport Matrix3& RotateByAxis(Matrix3& mat, unsigned i) const;
00182     CoreExport Matrix3  LinkMatrix(bool include_rot) const;
00194     CoreExport Matrix3& ApplyLinkMatrix(Matrix3& mat, bool include_rot) const;
00195     // Set methods:
00196     //
00204     CoreExport void SetActive(unsigned i, bool s);
00212     CoreExport void SetLLimited(unsigned i, bool s);
00220     CoreExport void SetULimited(unsigned i, bool s);
00221   private:
00222     unsigned    flags;
00223   };
00224 
00225 // A Link is a 1-dof rotation followed by a rigidExtend. The dof
00226 // axis is specified by dofAxis. It is always active.
00227 // 
00228 // * LinkMatrix(true) == rigidExtend * Rotation(dofAxis, dofValue).
00229 //   LinkMatrix(false) == rigidExtend.
00230 //
00231 // * Matrix3& ApplyLinkMatrix(Matrix3& mat, bool) pre-applies the
00232 // LinkMatrix(bool) to the input matrix, mat.
00233 //
00234 // * A typical 3-dof (xyz) joint is decomposed into three links. z and
00235 // y dofs don't have rigid extension, called NullLink(). Let's use
00236 //      ++o
00237 // to denote NullLink() and
00238 //      ---o
00239 // to denote !NullLink(). Then, a 3-dof joint will be decomposed into
00240 // three Links, as:
00241 //      ---o++o++o
00242 //         x  y  z
00243 //
00244 // * For an xyz rotation joint, if y is not active (Active unchecked),
00245 // then y will be absorbed into the z-link, as:
00246 //      ---o---o
00247 //         x   z
00248 // In this case, the z-link is not NullLink(). But its length is
00249 // zero. It is called ZeroLengh() link.
00250 //
00278   class Link : public MaxHeapOperators {
00279   public:
00283     Link():rigidExtend(0),dofAxis(RotZ){}
00287     CoreExport ~Link();
00288     enum DofAxis {
00289       TransX,
00290       TransY,
00291       TransZ,
00292       RotX,
00293       RotY,
00294       RotZ
00295     };
00296     DofAxis     dofAxis;
00297     float       dofValue;
00298     float       initValue;
00299     Point2      limits;
00304     bool        NullLink() const {return rigidExtend?false:true;}
00307     bool        ZeroLength() const {
00308       return NullLink() ? true :
00309         (rigidExtend->GetIdentFlags() & POS_IDENT) ? true : false; }
00314     bool        LLimited() const { return llimited?true:false; }
00319     bool        ULimited() const { return ulimited?true:false; }
00320     
00325     CoreExport  Matrix3     DofMatrix() const;
00332     CoreExport  Matrix3&    DofMatrix(Matrix3& mat) const;  
00340     CoreExport  Matrix3     LinkMatrix(bool include_dof =true) const;
00351     CoreExport  Matrix3&    ApplyLinkMatrix(Matrix3& mat, bool include_dof =true) const;
00352     
00353     // Set methods:
00354     //
00360     void        SetLLimited(bool s) { llimited = s?1:0; }
00366     void        SetULimited(bool s) { ulimited = s?1:0; }
00367     
00372     CoreExport  void        SetRigidExtend(const Matrix3& mat);
00373 
00374   private:
00375     Matrix3*    rigidExtend;
00376     byte        llimited : 1;
00377     byte        ulimited : 1;
00378   };
00379 
00380 // A LinkChain consists of a RootLink and LinkCount() of Links.
00381 // 
00382 // * parentMatrix is where the root joint starts with respect to the
00383 // world. It should not concern the solver. Solvers should derive their
00384 // solutions in the parent space.
00385 //
00386 // * goal is represented in the parent space, i.e.,
00387 //      goal_in_world = goal * parentMatrix
00388 //
00389 // * Bone(): The Link of index i may be a NullLink(). Bone(i) gives
00390 // the index j so that j >= i and LinkOf(j).NullLink() is false. If j
00391 // >= LinkCount() means that the chain ends up with NullLink().
00392 //
00393 // * PreBone(i) gives the index, j, so that j < i and LinkOf(j) is not
00394 // NullLink(). For the following 3-dof joint:
00395 //      ---o++o++o---o
00396 //            i
00397 // Bone(i) == i+1, and PreBone(i) == i-2. Therefore, degrees of
00398 // freedom of LinkOf(i) == Bone(i) - PreBone(i).
00399 // 
00400 // * A typical two bone chain with elbow being a ball joint has this
00401 // structure:
00402 //      ---o++o++o---O
00403 //         2  1  0   rootLink
00404 // It has 3 links in addition to the root link.
00405 //
00406 // * A two-bone chain with the elbow being a hinge joint has this
00407 // structure:
00408 //      ---o---O
00409 //         0   rootLink
00410 // It has one link. Geometrically, the axis of LinkOf(0) should be
00411 // perpendicular to the two bones.
00412 //
00413 // * The matrix at the end effector is
00414 //      End_Effector_matrix == LinkOf(n-1).LinkMatrix(true) * ... *
00415 //          LinkOf(0).LinkMatrix(true) * rootLink.LinkMatrix(true).
00416 //
00417 // * swivelAngle, chainNormal, and defaultZeroMap concerns solvers that
00418 // answer true to IKSolver::UseSwivelAngle().
00419 //
00420 // * chainNormal is the normal to the plane that is intrinsic to the
00421 // chain when it is constructed. It is represented in the object space
00422 // of the root joint.
00423 //
00424 // * A zero-map is a map that maps the end effector axis (EEA) to a
00425 // plane normal perpendicular to the EEA. The IK System will provide a
00426 // default one to the solver. However, a solver may choose to use its
00427 // own.
00428 //
00429 // * Given the swivelAngle, the solver is asked to adjust the rotation
00430 // at the root joint, root_joint_rotation, so that:
00431 // (A)  EEA stays fixed
00432 // (B)  chainNormal * root_joint_rotation
00433 //      == zeroMap(EEA) * RotationAboutEEA(swivelAngle)
00434 // By definition, zeroMap(EEA) is always perpendicular to EEA. At the
00435 // initial pose, chainNormal is also guarranteed to be perpendicular
00436 // to zeroMap(EEA). When it is not, root_joint_rotation has to
00437 // maintain (A) absolutely and satisfy (B) as good as it is possible.
00438 //
00453   class LinkChain : public MaxHeapOperators {
00454   public:
00455     LinkChain():links(0),linkCount(0) {}
00456     CoreExport  LinkChain(unsigned lc);
00457     CoreExport  virtual     ~LinkChain();
00458     virtual void* GetInterface(ULONG i) const { UNUSED_PARAM(i); return NULL; }
00464     Matrix3     parentMatrix;
00465     RootLink    rootLink;
00471     const Link& LinkOf(unsigned i) const {return links[i];}
00474     Link&       LinkOf(unsigned i) {return links[i];}
00477     unsigned    LinkCount() const { return linkCount; }
00486     CoreExport  int         PreBone(unsigned i) const;
00491     CoreExport  unsigned    Bone(unsigned i) const;
00492 
00497     BaseInterface* GetIKGoal() { return ikGoal; }
00502     void SetIKGoal(BaseInterface* ikgoal) { ikGoal = ikgoal; }
00504     CoreExport  void ReleaseIKGoal();
00505   protected:
00506     CoreExport  void SetLinkCount(unsigned lc);
00507   private:
00508     Link*       links;
00509     unsigned    linkCount;
00510     BaseInterface* ikGoal;
00511   };
00512 
00513 //
00514 // A convenience class that helps to iterate over the LinkChain on
00515 // the basis of joint.
00516 // Joint is defined as follows.
00517 // (A) The RootLink is a rotaional joint.
00518 // (B) A consecutive series of Link's of the same type
00519 // (sliding v. rotational) of which only the last Link may have
00520 // NullLink() being false.
00521 // Steps to iterate, supposing linkChain is a LinkChain:
00522 // * IterJoint iter(linkChain); -- make an iterator.
00523 // * iter.InitJointAngles(); -- Set all the link variables to initial values.
00524 // * iter.SetSkipSliding(true); -- If you want to skip sliding joints.
00525 //      If your solver does not use sliding joint, they won't be present
00526 //      in the linkChain and there is no need to call it.
00527 // * iter.Begin(true/false); -- Begin the iteration and this is the first
00528 //      joint. Pass true as the argument if you want the first joint to be
00529 //      place in the world, according to linkChain.parentMatrix.
00530 // * iter.GetJointType(); -- Is this joint rotational or sliding?
00531 // * iter.DofCount(); -- How many degrees of freedom does it have?
00532 // * iter.GetJointAxes(); -- Axes of each degree of freedom. It is represented
00533 //      as char[3]. The usual xyz joint is "xyz". If this 1D of y, it is
00534 //      "__y", 2D of xy as "_xy", etc.
00535 // * iter.ProximalFrame(); -- The base reference frame of this joint. The
00536 //      joint axes are relative to it.
00537 // * iter.DistalFrame(); -- The reference frame that the joint brings the
00538 //      base frame to.
00539 // * iter.Pivot() -- The pivot of this joint.
00540 // * iter.DistalEnd() -- The end point the rigid link attached to this joint.
00541 //      If this is not the last joint, it is the pivot of the next joint.
00542 // * iter.SetJointAngles(ang); -- Assign "ang" to joint angles. ang is a
00543 //      Point3. If this is not a 3D joint, values are retrieved from ang
00544 //      by name. For "_zx" joint, for instance, ang.z will be assigned to
00545 //      the z-axis, and ang.x will be assigned to the x-axis.
00546 //      After it is called, DistalFrame() and DistalEnd() will be updated.
00547 // * iter.Next(); -- This brings to the next joint, if returns true. It is
00548 //      the last joint if returns false.
00549 // 
00569 class IterJoint : public MaxHeapOperators
00570 {
00571 public:
00572     struct JointAxes : public MaxHeapOperators {
00573         char& operator[](int i) { return mAxes[i]; }
00574         char mAxes[3];
00575     };
00576     enum JointType
00577     {
00578         SlidingJoint,
00579         RotationalJoint
00580     };
00581 
00582     IterJoint(LinkChain& lc)
00583         : mLinkChain(lc) , mSkipSlide(false)
00584     {
00585         // Empty ctor
00586     }
00587     
00589     CoreExport  void            InitJointAngles();
00590     
00598     void            SetSkipSliding(bool skip) { mSkipSlide = skip; }
00599     
00604     CoreExport  void            Begin(bool in_world);
00605 
00607     CoreExport  JointType       GetJointType() const;
00608 
00614     int             DofCount() const { return mBegin == -1 ? 3: mEnd - mBegin; }
00615 
00617     CoreExport  JointAxes       GetJointAxes() const;
00618 
00625     const Matrix3&  ProximalFrame() const { return mMat0; }
00626 
00629     const Matrix3&  DistalFrame() const { return mMat; }
00630 
00633     Point3          Pivot() const { return mMat0.GetTrans(); }
00634     
00637     Point3          DistalEnd() const { return mMat.GetTrans(); }
00638 
00642     CoreExport  void            SetJointAngles(const Point3&);
00643 
00645     CoreExport  Point3          GetJointAngles() const;
00646 
00650     CoreExport  bool            Next();
00651 
00652 protected:
00653     CoreExport  void            SkipSliding();
00654     
00655 private:
00656     // Unimplemented assignment operator
00657     IterJoint& operator=(const IterJoint& );
00658     Matrix3     mMat0;
00659     Matrix3     mMat;
00660     LinkChain&  mLinkChain;
00661     bool        mSkipSlide;
00662     short       mBegin;
00663     short       mEnd;
00664     short       mNext;
00665 };
00666 
00667 // IK Goals:
00668 //
00677 class IIKGoal : public BaseInterface {
00678 public:
00686     BaseInterface* GetInterface(Interface_ID id) { if (id == kGoalID) return this; else return BaseInterface::GetInterface(id); }
00689     Interface_ID    GetID() { return kGoalID; }
00693     LifetimeType    LifetimeControl() { return wantsRelease; }
00695     CoreExport void ReleaseInterface();
00699     virtual         ~IIKGoal() {}
00700 };
00701 
00716 class IHIIKGoal: public IIKGoal {
00717 public:
00718     // Due to BaseInterface
00719     BaseInterface* GetInterface(Interface_ID id) { if (id == kHIIKGoalID) return this; else return IIKGoal::GetInterface(id); }
00720     Interface_ID GetID() { return kHIIKGoalID; }
00721 
00727     virtual bool        UseVHTarget() const =0;
00729     virtual float       SwivelAngle() const =0;
00731     virtual const Point3& VHTarget() const =0;
00734     virtual SAParentSpace SwivelAngleParent() const =0;
00738     virtual const Point3& ChainNormal() const =0;
00741     virtual const ZeroPlaneMap* DefaultZeroMap() const =0;
00743     virtual Matrix3& Goal() =0;
00744 };
00745 
00746 
00753 class ISplineIKGoal: public IIKGoal {
00754 public:
00755     // Due to BaseInterface
00756     BaseInterface* GetInterface(Interface_ID id) { if (id == kSplineIKGoalID) return this; else return IIKGoal::GetInterface(id); }
00757     Interface_ID GetID() { return kSplineIKGoalID; }
00758 
00760     virtual float               StartParam()                    const = 0;
00761     virtual float               EndParam()                      const = 0;
00762     //watje
00768     virtual Point3              SplinePosAt(float, BOOL, BOOL = FALSE)      const = 0;
00773     virtual Point3              SplineTangentAt(float, BOOL)    const = 0;
00777     virtual const Matrix3&      Goal()                          const = 0;
00780     virtual INode*              GetGoalNode()                   const = 0;
00783     virtual IIKChainControl*    GetChainControl()                     = 0;
00787     virtual float               GetSplineLength()               const = 0;
00790     virtual float               TwistHStartAngle()              const = 0;
00793     virtual float               TwistHEndAngle()                const = 0;
00796     virtual INode*              StartJoint()                    const = 0;
00799     virtual INode*              EndJoint()                      const = 0;
00802     virtual const ZeroPlaneMap* DefaultZeroMap()                const = 0;
00804     virtual BOOL                IsClosed()                      const = 0;
00805     virtual const Matrix3&      TwistParent()                   const = 0;
00806 };
00807 
00809 
00812 // Supported by class SplineIKGoal
00813 class ISplineIKGoal2: public ISplineIKGoal {
00814 public:
00815     Interface_ID GetID() { return IID_SPLINE_IKGOAL2; }
00816 
00819     virtual const TimeValue&    GetSolveTime()  const = 0;
00820 };
00821 
00822 CoreExport IterJoint::JointType DofType(Link::DofAxis axis); 
00823 
00824 }; // namespace IKSys