| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- //
- // Copyright (c) 2008-2016 the Urho3D project.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #pragma once
- #include "../Scene/Component.h"
- struct ik_solver_t;
- struct ik_node_t;
- namespace Atomic
- {
- class AnimationState;
- class IKConstraint;
- class IKEffector;
- /*!
- * @brief Marks the root or "beginning" of an IK chain or multiple IK chains.
- * The solving algorithm can be set along with other solver related parameters.
- * The IK problem is solved starting from the node this component is attached
- * to and ending at all nodes that have an IKEffector component attached.
- */
- class ATOMIC_API IKSolver : public Component
- {
- ATOMIC_OBJECT(IKSolver, Component)
- public:
- enum Algorithm
- {
- ONE_BONE = 0,
- TWO_BONE,
- FABRIK
- /* not implemented yet
- MSD,
- JACOBIAN_INVERSE,
- JACOBIAN_TRANSPOSE*/
- };
- enum Feature
- {
- /*!
- * @brief Should be enabled if your model uses skinning or if you are
- * generally interested in correct joint rotations. Has a minor
- * performance impact.
- *
- * When enabled, final joint rotations are calculated as a post
- * processing step. If you are using IK on a model with skinning, you will
- * want to enable this or it will look wrong. If you disable this, then
- * you will get a slight performance boost (less calculations are required)
- * but only the node positions are updated. This can be useful for scene
- * IK (perhaps a chain of platforms, where each platform should retain its
- * initial world rotation?)
- */
- JOINT_ROTATIONS = 0x01,
- /*!
- * @brief When enabled, the effector will try to match the target's
- * rotation as well as the effectors position. When disabled, the target
- * node will reach the effector with any rotation necessary.
- *
- * If the target position goes out of range of the effector then the
- * rotation will no longer be matched. The chain will try to reach out to
- * reach the target position, even if it means rotating towards it.
- */
- TARGET_ROTATIONS = 0x02,
- /*!
- * When the solver is first initialized, it will copy the positions
- * and rotations of the current Atomic scene graph into an internal
- * structure. This is referred to as the "original pose" and will by
- * default never change for the duration of the solver's life cycle.
- * When the solver is destroyed, the original pose is applied back to
- * Atomic's scene graph so the nodes are restored to whatever they were
- * before the solver was created.
- *
- * By enabling UPDATE_ORIGINAL_POSE, the original pose will be updated
- * right before solving to reflect the current Atomic scene graph. As
- * a consequence, there will no longer be an original pose to restore
- * when the solver is destroyed.
- *
- * When disabled, the original pose will remain unmodified. The original
- * pose is set when the solver is first created. You can manually update the
- * original pose at any time by calling UpdateInitialPose().
- */
- UPDATE_ORIGINAL_POSE = 0x04,
- /*!
- * @brief Should be enabled if you are using IK on an animated model,
- * along with disabling USE_ORIGINAL_POSE.
- *
- * The "active pose" has two purposes: The solver uses it as the
- * initial tree to derive a solution from, and at the same time uses it
- * to store the solution into. Thus, the typical solving process is:
- * 1) The active pose needs to be updated to reflect a preferred
- * initial condition (such as the current frame of animation)
- * 2) Call Solve()
- * 3) The active pose now holds the solution, so it must be applied
- * back to the Atomic scene graph.
- *
- * When enabled, the active pose is updated right before solving to
- * reflect the current state of the Atomic scene graph.
- *
- * When disabled, the active pose will simply remain as it was since
- * the last time Solve() was called.
- *
- * @note This option conflicts with USE_ORIGINAL_POSE. Make sure to
- * disable USE_ORIGINAL_POSE if you enable this feature.
- */
- UPDATE_ACTIVE_POSE = 0x08,
- /*!
- * @brief Choose between using the original pose or the active pose as
- * a basis for a solution.
- *
- * When enabled, the solver will copy the original pose
- * (see UPDATE_ORIGINAL_POSE) into the active pose before solving (and
- * thus use the original pose as a basis for a solution).
- *
- * @note This option conflicts with UPDATE_ACTIVE_POSE. If you enable
- * this feature, make sure to disable UPDATE_ACTIVE_POSE.
- *
- * If both UPDATE_ACTIVE_POSE and USE_ORIGINAL_POSE are disabled, then
- * the solver will use the previously solved tree as a basis for the new
- * calculation. The result is a more "continuous" solution that unfolds
- * over time. This can be useful if you want to simulate chains or
- * something similar.
- */
- USE_ORIGINAL_POSE = 0x10,
- /*!
- * Due to the somewhat unfortunate performance impacts, the solver
- * does not enable constraints by default. Enabling constraints causes
- * the solver's tree to be written to and from Atomic's scene graph every
- * iteration, while calling ApplyConstraints(). Disabling constraints means
- * ApplyConstraints() is never called.
- */
- CONSTRAINTS = 0x20,
- /*!
- * Mostly exists because of the editor. When enabled, the solver
- * will be invoked automatically for you. If you need to do additional
- * calculations before being able to set the effector target data, you will
- * want to disable this and call Solve() manually.
- */
- AUTO_SOLVE = 0x40
- };
- /// Construct an IK root component.
- IKSolver(Context* context);
- /// Default destructor.
- virtual ~IKSolver();
- /// Registers this class to the context.
- static void RegisterObject(Context* context);
- /// Returns the active algorithm
- Algorithm GetAlgorithm() const;
- /*!
- * @brief Selects the solver algorithm. Default is FABRIK. Note that this
- * may not be the most efficient algorithm available. The specialized
- * solvers will be a lot faster.
- *
- * The currently supported solvers are listed below.
- * + **FABRIK**: This is a fairly new and highly efficient inverse
- * kinematic solving algorithm. It requires the least iterations to
- * reach its goal, it does not suffer from singularities (nearly no
- * violent snapping about), and it always converges.
- * + **2 Bone**: A specialized solver optimized for 2 bone problems (such
- * as a human leg)
- * + **1 Bone**: A specialized solver optimized for 1 bone problems (such
- * as a look-at target, e.g. eyes or a head)
- */
- void SetAlgorithm(Algorithm algorithm);
- /// Test if a certain feature is enabled (see IKSolver::Feature)
- bool GetFeature(Feature feature) const;
- /// Enable or disable a certain feature (see IKSolver::Feature)
- void SetFeature(Feature feature, bool enable);
- /// Returns the configured maximum number of iterations.
- unsigned GetMaximumIterations() const;
- /*!
- * @brief Sets the maximum number of iterations the solver is allowed to
- * perform before applying the result.
- *
- * Depending on the algorithm, you may want higher or lower values.
- * FABRIK looks decent after only 10 iterations, whereas Jacobian based
- * methods often require more than a 100.
- *
- * The default value is 20.
- *
- * @note Most algorithms have a convergence criteria at which the solver
- * will stop iterating, so most of the time the maximum number of
- * iterations isn't even reached.
- *
- * @param[in] iterations Number of iterations. Must be greater than 0. Higher
- * values yield more accurate results, but at the cost of performance.
- */
- void SetMaximumIterations(unsigned iterations);
- /// Returns the configured tolerance.
- float GetTolerance() const;
- /*!
- * @brief Sets the distance at which the effector is "close enough" to the
- * target node, at which point the algorithm will stop iterating.
- *
- * @param tolerance The distance to set. Smaller values yield more accurate
- * results, but at the cost of more iterations. Generally you'll want to
- * specify a number that is about 1/100th to 1/1000th of the total size of
- * the IK chain, e.g. if your human character has a leg that is 1 Atomic
- * unit long, a good starting tolerance would be 0.01.
- */
- void SetTolerance(float tolerance);
- /*!
- * @brief Updates the solver's internal data structures, which is required
- * whenever the tree is modified in any way (e.g. adding or removing nodes,
- * adding or removing effectors, etc.).
- * @note This gets called automatically for you in Solve().
- */
- void RebuildChainTrees();
- /*!
- * @brief Unusual, but if you have a tree with translational motions such
- * that the distances between nodes changes (perhaps a slider?), you can
- * call this to recalculate the segment lengths after assigning new
- * positions to the nodes.
- * @note This function gets called by RebuildData() and by extension in
- * Solve().
- */
- void RecalculateSegmentLengths();
- /*!
- * @brief Skinned models require joint rotations to be calculated so
- * skinning works correctly. This is automatically enabled by default with
- * the feature flag JOINT_ROTATIONS.
- */
- void CalculateJointRotations();
- /*!
- * @brief Invokes the solver. The solution is applied back to the scene
- * graph automatically.
- * @note By default this is called automatically for you if the feature
- * flag AUTO_SOLVE is set. For more complex IK problems you can disable
- * that flag and call Solve() in response to E_SCENEDRAWABLEUPDATEFINISHED.
- * This is right after the animations have been applied.
- */
- void Solve();
- /*!
- * @brief When enabled, the current Atomic node positions and rotations in
- * Copies the original pose into the scene graph. This will reset the pose
- * to whatever state it had when the IKSolver component was first created,
- * or, if the original pose was updated since then (for example if
- * Feature::UPDATE_ORIGINAL_POSE is set), will reset it to that state.
- */
- void ApplyOriginalPoseToScene();
- /*!
- * Copies the current scene graph data into the solvers original pose. You
- * generally won't need to call this, because it gets called for you
- * automatically if Feature::UPDATE_ORIGINAL_POSE is set.
- */
- void ApplySceneToOriginalPose();
- /*!
- * Copies the solvers current active pose into the scene graph. You
- * generally won't need to call this because it gets called for you
- * automatically in Solve(). This is used to apply the solution back to the
- * scene graph.
- */
- void ApplyActivePoseToScene();
- /*!
- * Copies the current scene graph data into the solvers active pose. You
- * generally won't need to call this because it gets called for you
- * automatically if Feature::UPDATE_ACTIVE_POSE is set.
- */
- void ApplySceneToActivePose();
- /*!
- * Copies the solvers original pose into the solvers active pose. This is
- * used in Solve() automatically if Feature::USE_ORIGINAL_POSE is set.
- */
- void ApplyOriginalPoseToActivePose();
- void DrawDebugGeometry(bool depthTest);
- virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
- private:
- friend class IKEffector;
- /// Indicates that the internal structures of the IK library need to be updated. See the documentation of ik_solver_rebuild_chain_trees() for more info on when this happens.
- void MarkChainsNeedUpdating();
- /// Indicates that the tree structure has changed in some way and needs updating (nodes added or removed, components added or removed)
- void MarkTreeNeedsRebuild();
- /// Returns false if calling Solve() would cause the IK library to abort. Atomic's error handling philosophy is to log an error and continue, not crash.
- bool IsSolverTreeValid() const;
- /// Subscribe to drawable update finished event here
- virtual void OnSceneSet(Scene* scene);
- /// Destroys and creates the tree
- virtual void OnNodeSet(Node* scene);
- /// Creates the ik library node and sets the current rotation/position and user data correctly.
- ik_node_t* CreateIKNodeFromUrhoNode(const Node* node);
- /// Destroys the solver's tree
- void DestroyTree();
- /// Builds the solver's tree to match the scene graph's tree. If a tree already exists, it is first destroyed
- void RebuildTree();
- /// Builds a chain of nodes up to the node of the specified effector component.
- bool BuildTreeToEffector(IKEffector* effector);
- /*!
- * Checks if the specified component is 1) attached to a node that is below
- * the one we are attached to and 2) isn't in the subtree of a child solver.
- * @note This will return false if the component is attached to our root
- * node, because in that case the solver can't do anything to it, it's in
- * the hands of a parent solver (if it exists).
- */
- bool ComponentIsInOurSubtree(Component* component) const;
- void HandleComponentAdded(StringHash eventType, VariantMap& eventData);
- void HandleComponentRemoved(StringHash eventType, VariantMap& eventData);
- void HandleNodeAdded(StringHash eventType, VariantMap& eventData);
- void HandleNodeRemoved(StringHash eventType, VariantMap& eventData);
- /// Invokes the IK solver
- void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
- /// Need these wrapper functions flags of GetFeature/SetFeature can be correctly exposed to the editor and to AngelScript and lua
- public:
- bool GetJOINT_ROTATIONS() const;
- bool GetTARGET_ROTATIONS() const;
- bool GetUPDATE_ORIGINAL_POSE() const;
- bool GetUPDATE_ACTIVE_POSE() const;
- bool GetUSE_ORIGINAL_POSE() const;
- bool GetCONSTRAINTS() const;
- bool GetAUTO_SOLVE() const;
- void SetJOINT_ROTATIONS(bool enable);
- void SetTARGET_ROTATIONS(bool enable);
- void SetUPDATE_ORIGINAL_POSE(bool enable);
- void SetUPDATE_ACTIVE_POSE(bool enable);
- void SetUSE_ORIGINAL_POSE(bool enable);
- void SetCONSTRAINTS(bool enable);
- void SetAUTO_SOLVE(bool enable);
- private:
- PODVector<IKEffector*> effectorList_;
- PODVector<IKConstraint*> constraintList_;
- ik_solver_t* solver_;
- Algorithm algorithm_;
- unsigned features_;
- bool chainTreesNeedUpdating_;
- bool treeNeedsRebuild;
- bool solverTreeValid_;
- };
- } // namespace Atomic
|