Browse Source

Updating script APIs, making sure sample compiles, see thread on the forum for more info

TheComet 8 years ago
parent
commit
c43b269845

+ 7 - 2
Source/ThirdParty/ik/include/ik/chain_tree.h

@@ -15,7 +15,11 @@ C_HEADER_BEGIN
 
 struct chain_t
 {
-    /* list of ik_node_t* references that belong to this chain */
+    /*
+     * List of ik_node_t* references that belong to this chain.
+     * NOTE: The first node in this list is the effector (i.e. the *end* of the
+     * chain). The nodes are in reverse.
+     */
     ordered_vector_t nodes;
     /* list of chain_t objects */
     ordered_vector_t children;
@@ -26,9 +30,10 @@ struct chain_island_t
     chain_t       root_chain;
 
     /*
-     * List of ik_node_t* objects. This list contains the leaf nodes of IK
+     * List of ik_node_t* objects. This list contains the child nodes of IK
      * effectors, the children of which aren't part of the IK problem but need
      * to be properly updated to match the new transform of the solved tree.
+     * TODO Is this implemented yet?
      */
     ordered_vector_t transform_dependent_nodes;
 };

+ 8 - 0
Source/ThirdParty/ik/include/ik/solver.h

@@ -184,6 +184,14 @@ IK_PUBLIC_API void
 ik_solver_iterate_tree(ik_solver_t* solver,
                        ik_solver_iterate_node_cb_func callback);
 
+/*!
+ * @brief Iterates just the nodes that are being affected by the solver. Useful
+ * for a more optimized write-back of the solution data.
+ */
+IK_PUBLIC_API void
+ik_solver_iterate_chain_tree(ik_solver_t* solver,
+                             ik_solver_iterate_node_cb_func callback);
+
 /*!
  * @brief Sets the solved positions and rotations equal to the original
  * positions and rotations for every node in the tree.

+ 5 - 5
Source/ThirdParty/ik/src/chain_tree.c

@@ -412,8 +412,8 @@ calculate_global_rotations_of_children(chain_t* chain)
     /*
      * Assuming there was more than 1 child chain and assuming we aren't the
      * root node, then the child chains we just iterated must share the same
-     * base node as our tip node. Average the accumulated quaternion and set
-     * this node's correct solved rotation.
+     * base node (which is our tip node). Average the accumulated quaternion
+     * and set this node's correct solved rotation.
      */
     if (average_count > 0 && ordered_vector_count(&chain->nodes) != 0)
     {
@@ -458,15 +458,15 @@ calculate_global_rotations(chain_t* chain)
 
     /*
      * Calculates the "global" (world) angles of each joint and writes them to
-     * each node->solved_rotation slot.
+     * each node->rotation slot.
      *
      * The angle between the original and solved segments are calculated using
      * standard vector math (dot product). The axis of rotation is calculated
      * with the cross product. From this data, a quaternion is constructed,
      * describing this delta rotation. Finally, in order to make the rotations
      * global instead of relative, the delta rotation is multiplied with
-     * node->rotation, which should be a quaternion describing the node's
-     * global rotation in the unsolved tree.
+     * node->original_rotation, which should be a quaternion describing the
+     * node's global rotation in the unsolved tree.
      *
      * The rotation of the base joint in the chain is returned so it can be
      * averaged by parent chains.

+ 39 - 0
Source/ThirdParty/ik/src/solver.c

@@ -11,6 +11,7 @@
 #include "ik/solver_jacobian_inverse.h"
 #include "ik/solver_jacobian_transpose.h"
 #include <string.h>
+#include <assert.h>
 
 static int
 recursively_get_all_effector_nodes(ik_node_t* node, ordered_vector_t* effector_nodes_list);
@@ -225,6 +226,44 @@ ik_solver_iterate_tree(ik_solver_t* solver,
     iterate_tree_recursive(solver->tree, callback);
 }
 
+/* ------------------------------------------------------------------------- */
+static void
+iterate_chain_tree_recursive(chain_t* chain,
+                             ik_solver_iterate_node_cb_func callback)
+{
+    /*
+     * Iterate the chain tree breadth first. Note that we exclude the base node
+     * in each chain, because otherwise the same node would be passed to the
+     * callback multiple times. The base node is shared by the parent chain's
+     * effector as well as with other chains in the same depth.
+     */
+    int idx = ordered_vector_count(&chain->nodes);
+    assert(idx > 0); // chains must have at least 2 nodes in them
+    while (idx--)
+    {
+        callback(*(ik_node_t**)ordered_vector_get_element(&chain->nodes, idx));
+    }
+
+    ORDERED_VECTOR_FOR_EACH(&chain->children, chain_t, child)
+        iterate_chain_tree_recursive(child, callback);
+    ORDERED_VECTOR_END_EACH
+}
+void ik_solver_iterate_chain_tree(ik_solver_t* solver,
+                                  ik_solver_iterate_node_cb_func callback)
+{
+    ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
+        /*
+         * The root node is excluded by the recursive function, so we must
+         * do the callback here
+         */
+        int idx = ordered_vector_count(&island->root_chain.nodes) - 1;
+        ik_node_t* root = *(ik_node_t**)ordered_vector_get_element(&island->root_chain.nodes, idx);
+        callback(root);
+
+        iterate_chain_tree_recursive(&island->root_chain, callback);
+    ORDERED_VECTOR_END_EACH
+}
+
 /* ------------------------------------------------------------------------- */
 static void
 reset_active_pose_recursive(ik_node_t* node)

+ 1 - 1
Source/ThirdParty/ik/src/solver_FABRIK.c

@@ -516,7 +516,7 @@ solver_FABRIK_solve(ik_solver_t* solver)
             assert(ordered_vector_count(&root_chain->nodes) > 1);
 
             root_position = (*(ik_node_t**)ordered_vector_get_element(&root_chain->nodes,
-                    ordered_vector_count(&root_chain->nodes) - 1))->original_position;
+                    ordered_vector_count(&root_chain->nodes) - 1))->position;
 
             if (solver->flags & SOLVER_CALCULATE_TARGET_ROTATIONS)
                 solve_chain_forwards_with_target_rotation(root_chain);

+ 20 - 15
Source/Urho3D/AngelScript/IKAPI.cpp

@@ -32,31 +32,37 @@
 namespace Urho3D
 {
 
-static void RegisterIKSolver(asIScriptEngine* engine)
+static void RegisterIKEnumerations(asIScriptEngine* engine)
 {
     engine->RegisterEnum("IKAlgorithm");
     engine->RegisterEnumValue("IKAlgorithm", "ONE_BONE", IKSolver::ONE_BONE);
     engine->RegisterEnumValue("IKAlgorithm", "TWO_BONE", IKSolver::TWO_BONE);
     engine->RegisterEnumValue("IKAlgorithm", "FABRIK", IKSolver::FABRIK);
+}
 
-    engine->RegisterEnum("IKFeature");
-    engine->RegisterEnumValue("IKFeature", "JOINT_ROTATIONS", IKSolver::JOINT_ROTATIONS);
-    engine->RegisterEnumValue("IKFeature", "TARGET_ROTATIONS", IKSolver::TARGET_ROTATIONS);
-    engine->RegisterEnumValue("IKFeature", "UPDATE_ORIGINAL_POSE", IKSolver::UPDATE_ORIGINAL_POSE);
-    engine->RegisterEnumValue("IKFeature", "UPDATE_ACTIVE_POSE", IKSolver::UPDATE_ACTIVE_POSE);
-    engine->RegisterEnumValue("IKFeature", "USE_ORIGINAL_POSE", IKSolver::USE_ORIGINAL_POSE);
-    engine->RegisterEnumValue("IKFeature", "CONSTRAINTS", IKSolver::CONSTRAINTS);
-    engine->RegisterEnumValue("IKFeature", "AUTO_SOLVE", IKSolver::AUTO_SOLVE);
-
+void RegisterIKSolver(asIScriptEngine* engine)
+{
     RegisterComponent<IKSolver>(engine, "IKSolver");
     engine->RegisterObjectMethod("IKSolver", "IKAlgorithm get_algorithm() const", asMETHOD(IKSolver, GetAlgorithm), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_algorithm(IKAlgorithm)", asMETHOD(IKSolver, SetAlgorithm), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "void SetFeature(IKFeature,bool)", asMETHOD(IKSolver, SetFeature), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "bool GetFeature(IKFeature) const", asMETHOD(IKSolver, GetFeature), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "uint get_maximumIterations() const", asMETHOD(IKSolver, GetMaximumIterations), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_maximumIterations(uint)", asMETHOD(IKSolver, SetMaximumIterations), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "float get_tolerance() const", asMETHOD(IKSolver, GetTolerance), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_tolerance(float)", asMETHOD(IKSolver, SetTolerance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_JOINT_ROTATIONS() const", asMETHOD(IKSolver, GetJOINT_ROTATIONS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_JOINT_ROTATIONS(bool)", asMETHOD(IKSolver, SetJOINT_ROTATIONS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_TARGET_ROTATIONS() const", asMETHOD(IKSolver, GetTARGET_ROTATIONS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_TARGET_ROTATIONS(bool)", asMETHOD(IKSolver, SetTARGET_ROTATIONS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_UPDATE_ORIGINAL_POSE() const", asMETHOD(IKSolver, GetUPDATE_ORIGINAL_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_UPDATE_ORIGINAL_POSE(bool)", asMETHOD(IKSolver, SetUPDATE_ORIGINAL_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_UPDATE_ACTIVE_POSE() const", asMETHOD(IKSolver, GetUPDATE_ACTIVE_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_UPDATE_ACTIVE_POSE(bool)", asMETHOD(IKSolver, SetUPDATE_ACTIVE_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_USE_ORIGINAL_POSE() const", asMETHOD(IKSolver, GetUSE_ORIGINAL_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_USE_ORIGINAL_POSE(bool)", asMETHOD(IKSolver, SetUSE_ORIGINAL_POSE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_CONSTRAINTS() const", asMETHOD(IKSolver, GetCONSTRAINTS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_CONSTRAINTS(bool)", asMETHOD(IKSolver, SetCONSTRAINTS), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_AUTO_SOLVE() const", asMETHOD(IKSolver, GetAUTO_SOLVE), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_AUTO_SOLVE(bool)", asMETHOD(IKSolver, SetAUTO_SOLVE), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void RebuildChainTrees()", asMETHOD(IKSolver, RebuildChainTrees), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void RecalculateSegmentLengths()", asMETHOD(IKSolver, RecalculateSegmentLengths), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void CalculateJointRotations()", asMETHOD(IKSolver, CalculateJointRotations), asCALL_THISCALL);
@@ -88,8 +94,6 @@ static void RegisterIKEffector(asIScriptEngine* engine)
     engine->RegisterObjectMethod("IKEffector", "void set_rotationWeight(float)", asMETHOD(IKEffector, SetRotationWeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKEffector", "float get_rotationDecay() const", asMETHOD(IKEffector, GetRotationDecay), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKEffector", "void set_rotationDecay(float)", asMETHOD(IKEffector, SetRotationDecay), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKEffector", "bool get_weightedNlerp() const", asMETHOD(IKEffector, WeightedNlerpEnabled), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKEffector", "void set_weightedNlerp(bool)", asMETHOD(IKEffector, EnableWeightedNlerp), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKEffector", "void DrawDebugGeometry(bool)", asMETHODPR(IKEffector, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
 }
 
@@ -100,9 +104,10 @@ static void RegisterIKConstraint(asIScriptEngine* engine)
 
 void RegisterIKAPI(asIScriptEngine* engine)
 {
+    RegisterIKEnumerations(engine);
     RegisterIKSolver(engine);
     RegisterIKEffector(engine);
-    RegisterIKConstraint(engine);
+    //RegisterIKConstraint(engine);
 }
 
 }

+ 3 - 8
Source/Urho3D/IK/IK.h

@@ -22,21 +22,16 @@
 
 /*
  * TODO
- *  - Add support for manually updating initial pose.
- *  - Lua script bindings crash.
  *  - Optimise.
  *  - Profile.
  *  - Documentation.
- *  - Move log callback into context init function.
- *  - Bug when enabling continuous mode and IKSolver is placed somewhere
- *    on part of the model's bones.
- *  - Possible optimisation: Don't normalise quaternion in quat_mul_quat(),
- *    normalise it manually after multiplying all quaternions.
  *
  * FUTURE
  *  - Support for "stretchiness" with min/max lengths.
  *  - Support for "stiffness" factor, describes how well a bone rotates.
- *  - Apply bullet constraints to joints.
+ *  - Implement constraints.
+ *  - Skip bones when building the tree.
+ *  - Mass/Spring/Damper solver.
  */
 
 namespace Urho3D

+ 53 - 15
Source/Urho3D/IK/IKEffector.cpp

@@ -47,8 +47,7 @@ IKEffector::IKEffector(Context* context) :
     weight_(1.0f),
     rotationWeight_(1.0),
     rotationDecay_(0.25),
-    weightedNlerp_(false),
-    inheritParentRotation_(false)
+    features_(0)
 {
     URHO3D_LOGDEBUG("IKEffector created");
 }
@@ -71,7 +70,8 @@ void IKEffector::RegisterObject(Context* context)
     URHO3D_ACCESSOR_ATTRIBUTE("Weight", GetWeight, SetWeight, float, 1.0, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Rotation Weight", GetRotationWeight, SetRotationWeight, float, 1.0, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Rotation Decay", GetRotationDecay, SetRotationDecay, float, 0.25, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Nlerp Weight", WeightedNlerpEnabled, EnableWeightedNlerp, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Nlerp Weight", GetWEIGHT_NLERP, SetWEIGHT_NLERP, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Inherit Parent Rotation", GetINHERIT_PARENT_ROTATION, SetINHERIT_PARENT_ROTATION, bool, false, AM_DEFAULT);
 }
 
 // ----------------------------------------------------------------------------
@@ -208,27 +208,39 @@ void IKEffector::SetRotationDecay(float decay)
     rotationDecay_ = Clamp(decay, 0.0f, 1.0f);
     if (ikEffectorNode_ != NULL)
     {
-        ik_calculate_rotation_weight_decays(&solver_->solver_->chain_tree);
         ikEffectorNode_->effector->rotation_decay = rotationDecay_;
+        ik_calculate_rotation_weight_decays(&solver_->solver_->chain_tree);
     }
 }
 
 // ----------------------------------------------------------------------------
-bool IKEffector::WeightedNlerpEnabled() const
+void IKEffector::SetFeature(Feature feature, bool enable)
 {
-    return weightedNlerp_;
+    switch (feature)
+    {
+        case WEIGHT_NLERP:
+        {
+            if (ikEffectorNode_ != NULL)
+            {
+                ikEffectorNode_->effector->flags &= ~EFFECTOR_WEIGHT_NLERP;
+                if (enable)
+                    ikEffectorNode_->effector->flags |= EFFECTOR_WEIGHT_NLERP;
+            }
+        } break;
+
+        case INHERIT_PARENT_ROTATION:
+            break;
+    }
+
+    features_ &= ~feature;
+    if (enable)
+        features_ |= feature;
 }
 
 // ----------------------------------------------------------------------------
-void IKEffector::EnableWeightedNlerp(bool enable)
+bool IKEffector::GetFeature(Feature feature) const
 {
-    weightedNlerp_ = enable;
-    if (ikEffectorNode_ != NULL)
-    {
-        ikEffectorNode_->effector->flags &= ~EFFECTOR_WEIGHT_NLERP;
-        if (enable)
-            ikEffectorNode_->effector->flags |= EFFECTOR_WEIGHT_NLERP;
-    }
+    return (features_ & feature) != 0;
 }
 
 // ----------------------------------------------------------------------------
@@ -320,7 +332,9 @@ void IKEffector::SetIKEffectorNode(ik_node_t* effectorNode)
         ikEffectorNode_->effector->rotation_weight = rotationWeight_;
         ikEffectorNode_->effector->rotation_decay = rotationDecay_;
         ikEffectorNode_->effector->chain_length = chainLength_;
-        EnableWeightedNlerp(weightedNlerp_);
+
+        if (features_ & WEIGHT_NLERP)
+            ikEffectorNode_->effector->flags |= EFFECTOR_WEIGHT_NLERP;
     }
 }
 
@@ -330,4 +344,28 @@ void IKEffector::SetIKSolver(IKSolver* solver)
     solver_ = solver;
 }
 
+// ----------------------------------------------------------------------------
+// Need these wrapper functions flags of GetFeature/SetFeature can be correctly
+// exposed to the editor
+// ----------------------------------------------------------------------------
+
+#define DEF_FEATURE_GETTER(feature_name)   \
+bool IKEffector::Get##feature_name() const \
+{                                          \
+    return GetFeature(feature_name);       \
+}
+
+#define DEF_FEATURE_SETTER(feature_name)        \
+void IKEffector::Set##feature_name(bool enable) \
+{                                               \
+    SetFeature(feature_name, enable);           \
+}
+
+DEF_FEATURE_GETTER(WEIGHT_NLERP)
+DEF_FEATURE_GETTER(INHERIT_PARENT_ROTATION)
+
+DEF_FEATURE_SETTER(WEIGHT_NLERP)
+DEF_FEATURE_SETTER(INHERIT_PARENT_ROTATION)
+
+
 } // namespace Urho3D

+ 39 - 18
Source/Urho3D/IK/IKEffector.h

@@ -25,7 +25,7 @@
 #include "../Scene/Component.h"
 #include "../Scene/Scene.h"
 
-struct ik_effector_t;
+typedef struct ik_node_t ik_node_t;
 
 namespace Urho3D
 {
@@ -39,6 +39,28 @@ class URHO3D_API IKEffector : public Component
 
 public:
 
+    enum Feature
+    {
+
+        /*!
+         * If you set the effector weight (see SetWeight()) to a value in
+         * between 0 and 1, the default behaviour is to linearly interpolate
+         * the effector's target position. If the solved tree and the initial
+         * tree are far apart, this can look very strange, especially if you
+         * are controlling limbs on a character that are designed to rotation.
+         * Enabling this causes a rotational based interpolation (nlerp) around
+         * the chain's base node and makes transitions look much more natural.
+         */
+        WEIGHT_NLERP = 0x01,
+
+        /*!
+         * By default the end effector node will retain its global orientation,
+         * even after solving. By enabling this feature, the node will instead
+         * "rotate with" its parent node.
+         */
+        INHERIT_PARENT_ROTATION = 0x02
+    };
+
     /// Constructs a new IK effector.
     IKEffector(Context* context);
 
@@ -48,6 +70,11 @@ public:
     /// Registers this class as an object factory.
     static void RegisterObject(Context* context);
 
+    /// Test if a certain feature is enabled (see IKEffector::Feature)
+    bool GetFeature(Feature feature) const;
+    /// Enable or disable a certain feature (see IKEffector::Feature)
+    void SetFeature(Feature feature, bool enable);
+
     /// Retrieves the node that is being used as a target. Can be NULL.
     Node* GetTargetNode() const;
 
@@ -113,12 +140,13 @@ public:
 
     /// How strongly the target node's rotation influences the solution
     float GetRotationWeight() const;
+
     /*!
      * @brief Sets how much influence the target rotation should have on the
      * solution. A value of 1 means to match the target rotation exactly, if
      * possible. A value of 0 means to not match it at all.
      * @note The solver must have target rotation enabled for this to have
-     * any effect. See IKSolver::EnableTargetRotation().
+     * any effect. See IKSolver::Featire::TARGET_ROTATIONS.
      */
     void SetRotationWeight(float weight);
 
@@ -137,20 +165,6 @@ public:
      */
     void SetRotationDecay(float decay);
 
-    /// Whether or not to nlerp instead of lerp when transitioning with the weight parameter
-    bool WeightedNlerpEnabled() const;
-
-    /*!
-     * @brief If you set the effector weight (see SetWeight()) to a value in
-     * between 0 and 1, the default behaviour is to linearly interpolate the
-     * effector's target position. If the solved tree and the initial tree
-     * are far apart, this can look very strange, especially if you are
-     * controlling limbs on a character that are designed to rotation. Enabling
-     * this causes a rotational based interpolation (nlerp) around the chain's
-     * base node and makes transitions look much more natural.
-     */
-    void EnableWeightedNlerp(bool enable);
-
     void DrawDebugGeometry(bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
 
@@ -164,6 +178,14 @@ private:
     /// Intended to be used by IKSolver. Copies the positions/rotations of the target node into the effector
     void UpdateTargetNodePosition();
 
+public:
+    /// Need these wrapper functions flags of GetFeature/SetFeature can be correctly exposed to the editor and to AngelScript and lua
+    bool GetWEIGHT_NLERP() const;
+    bool GetINHERIT_PARENT_ROTATION() const;
+    void SetWEIGHT_NLERP(bool enable);
+    void SetINHERIT_PARENT_ROTATION(bool enable);
+
+private:
     WeakPtr<Node> targetNode_;
     WeakPtr<IKSolver> solver_;
     ik_node_t* ikEffectorNode_;
@@ -175,8 +197,7 @@ private:
     float weight_;
     float rotationWeight_;
     float rotationDecay_;
-    bool weightedNlerp_;
-    bool inheritParentRotation_;
+    unsigned features_;
 };
 
 } // namespace Urho3D

+ 31 - 61
Source/Urho3D/IK/IKSolver.cpp

@@ -95,13 +95,13 @@ void IKSolver::RegisterObject(Context* context)
     URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, FABRIK, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Max Iterations", GetMaximumIterations, SetMaximumIterations, unsigned, 20, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Convergence Tolerance", GetTolerance, SetTolerance, float, 0.001, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Joint Rotations", GetFeature_JOINT_ROTATIONS, SetFeature_JOINT_ROTATIONS, bool, true, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Target Rotations", GetFeature_TARGET_ROTATIONS, SetFeature_TARGET_ROTATIONS, bool, false, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Update Original Pose", GetFeature_UPDATE_ORIGINAL_POSE, SetFeature_UPDATE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Update Active Pose", GetFeature_UPDATE_ACTIVE_POSE, SetFeature_UPDATE_ACTIVE_POSE, bool, true, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Use Original Pose", GetFeature_USE_ORIGINAL_POSE, SetFeature_USE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Enable Constraints", GetFeature_CONSTRAINTS, SetFeature_CONSTRAINTS, bool, false, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Auto Solve", GetFeature_AUTO_SOLVE, SetFeature_AUTO_SOLVE, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Joint Rotations", GetJOINT_ROTATIONS, SetJOINT_ROTATIONS, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Target Rotations", GetTARGET_ROTATIONS, SetTARGET_ROTATIONS, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Update Original Pose", GetUPDATE_ORIGINAL_POSE, SetUPDATE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Update Active Pose", GetUPDATE_ACTIVE_POSE, SetUPDATE_ACTIVE_POSE, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Use Original Pose", GetUSE_ORIGINAL_POSE, SetUSE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Constraints", GetCONSTRAINTS, SetCONSTRAINTS, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Auto Solve", GetAUTO_SOLVE, SetAUTO_SOLVE, bool, true, AM_DEFAULT);
 }
 
 // ----------------------------------------------------------------------------
@@ -810,62 +810,32 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 // exposed to the editor
 // ----------------------------------------------------------------------------
 
-bool IKSolver::GetFeature_JOINT_ROTATIONS() const
-{
-    return (features_ & JOINT_ROTATIONS);
-}
-bool IKSolver::GetFeature_TARGET_ROTATIONS() const
-{
-    return (features_ & TARGET_ROTATIONS);
-}
-bool IKSolver::GetFeature_UPDATE_ORIGINAL_POSE() const
-{
-    return (features_ & UPDATE_ORIGINAL_POSE);
-}
-bool IKSolver::GetFeature_UPDATE_ACTIVE_POSE() const
-{
-    return (features_ & UPDATE_ACTIVE_POSE);
-}
-bool IKSolver::GetFeature_USE_ORIGINAL_POSE() const
-{
-    return (features_ & USE_ORIGINAL_POSE);
-}
-bool IKSolver::GetFeature_CONSTRAINTS() const
-{
-    return (features_ & CONSTRAINTS);
-}
-bool IKSolver::GetFeature_AUTO_SOLVE() const
-{
-    return (features_ & AUTO_SOLVE);
+#define DEF_FEATURE_GETTER(feature_name) \
+bool IKSolver::Get##feature_name() const \
+{                                        \
+    return GetFeature(feature_name);     \
 }
 
-void IKSolver::SetFeature_JOINT_ROTATIONS(bool enable)
-{
-    SetFeature(JOINT_ROTATIONS, enable);
-}
-void IKSolver::SetFeature_TARGET_ROTATIONS(bool enable)
-{
-    SetFeature(TARGET_ROTATIONS, enable);
-}
-void IKSolver::SetFeature_UPDATE_ORIGINAL_POSE(bool enable)
-{
-    SetFeature(UPDATE_ORIGINAL_POSE, enable);
-}
-void IKSolver::SetFeature_UPDATE_ACTIVE_POSE(bool enable)
-{
-    SetFeature(UPDATE_ACTIVE_POSE, enable);
-}
-void IKSolver::SetFeature_USE_ORIGINAL_POSE(bool enable)
-{
-    SetFeature(USE_ORIGINAL_POSE, enable);
-}
-void IKSolver::SetFeature_CONSTRAINTS(bool enable)
-{
-    SetFeature(CONSTRAINTS, enable);
-}
-void IKSolver::SetFeature_AUTO_SOLVE(bool enable)
-{
-    SetFeature(AUTO_SOLVE, enable);
+#define DEF_FEATURE_SETTER(feature_name)      \
+void IKSolver::Set##feature_name(bool enable) \
+{                                             \
+    SetFeature(feature_name, enable);         \
 }
 
+DEF_FEATURE_GETTER(JOINT_ROTATIONS)
+DEF_FEATURE_GETTER(TARGET_ROTATIONS)
+DEF_FEATURE_GETTER(UPDATE_ORIGINAL_POSE)
+DEF_FEATURE_GETTER(UPDATE_ACTIVE_POSE)
+DEF_FEATURE_GETTER(USE_ORIGINAL_POSE)
+DEF_FEATURE_GETTER(CONSTRAINTS)
+DEF_FEATURE_GETTER(AUTO_SOLVE)
+
+DEF_FEATURE_SETTER(JOINT_ROTATIONS)
+DEF_FEATURE_SETTER(TARGET_ROTATIONS)
+DEF_FEATURE_SETTER(UPDATE_ORIGINAL_POSE)
+DEF_FEATURE_SETTER(UPDATE_ACTIVE_POSE)
+DEF_FEATURE_SETTER(USE_ORIGINAL_POSE)
+DEF_FEATURE_SETTER(CONSTRAINTS)
+DEF_FEATURE_SETTER(AUTO_SOLVE)
+
 } // namespace Urho3D

+ 45 - 25
Source/Urho3D/IK/IKSolver.h

@@ -192,7 +192,9 @@ public:
      */
     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.
@@ -268,23 +270,39 @@ public:
     void Solve();
 
     /*!
-     * @brief Causes the initial tree to be applied back to Urho3D's scene
-     * graph. This is what gets called when continuous solving is disabled.
+     * 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();
 
     /*!
-     * @brief Causes the current scene graph data to be copied into the solvers
-     * initial pose. This should generally be called before solving if you
-     * are using IK on an animated model. If you don't update the initial pose,
-     * then the result will be a "continuous solution", where the solver will
-     * use the previously calculated tree as a basis for the new solution.
-     *
-     * @note This is
+     * 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);
@@ -330,23 +348,25 @@ private:
     /// 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
-    bool GetFeature_JOINT_ROTATIONS() const;
-    bool GetFeature_TARGET_ROTATIONS() const;
-    bool GetFeature_UPDATE_ORIGINAL_POSE() const;
-    bool GetFeature_UPDATE_ACTIVE_POSE() const;
-    bool GetFeature_USE_ORIGINAL_POSE() const;
-    bool GetFeature_CONSTRAINTS() const;
-    bool GetFeature_AUTO_SOLVE() const;
-
-    void SetFeature_JOINT_ROTATIONS(bool enable);
-    void SetFeature_TARGET_ROTATIONS(bool enable);
-    void SetFeature_UPDATE_ORIGINAL_POSE(bool enable);
-    void SetFeature_UPDATE_ACTIVE_POSE(bool enable);
-    void SetFeature_USE_ORIGINAL_POSE(bool enable);
-    void SetFeature_CONSTRAINTS(bool enable);
-    void SetFeature_AUTO_SOLVE(bool enable);
+    /// 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_;

+ 9 - 3
Source/Urho3D/LuaScript/pkgs/IK/IKEffector.pkg

@@ -2,6 +2,12 @@ $#include "IK/IKEffector.h"
 
 class IKEffector : public Component
 {
+    enum Feature
+    {
+        WEIGHT_NLERP = 0x01,
+        INHERIT_PARENT_ROTATION = 0x02
+    };
+
     Node* GetTargetNode() const;
     void SetTargetNode(Node* targetNode);
 
@@ -26,9 +32,6 @@ class IKEffector : public Component
     float GetRotationDecay() const;
     void SetRotationDecay(float decay);
 
-    bool WeightedNlerpEnabled() const;
-    void EnableWeightedNlerp(bool enable);
-
     tolua_property__get_set Node* targetNode;
     tolua_property__get_set String targetName;
     tolua_property__get_set Vector3 targetPosition;
@@ -37,4 +40,7 @@ class IKEffector : public Component
     tolua_property__get_set float weight;
     tolua_property__get_set float rotationWeight;
     tolua_property__get_set float rotationDecay;
+
+    tolua_property__get_set Feature WEIGHT_NLERP;
+    tolua_property__get_set Feature INHERIT_PARENT_ROTATION;
 };

+ 8 - 3
Source/Urho3D/LuaScript/pkgs/IK/IKSolver.pkg

@@ -20,9 +20,6 @@ class IKSolver : public Component
         AUTO_SOLVE = 0x40
     };
 
-    bool GetFeature(Feature feature) const;
-    void SetFeature(Feature feature, bool enable);
-
     void RebuildChainTrees();
     void RecalculateSegmentLengths();
     void CalculateJointRotations();
@@ -39,4 +36,12 @@ class IKSolver : public Component
     tolua_property__get_set Algorithm algorithm;
     tolua_property__get_set unsigned maximumIterations;
     tolua_property__get_set float tolerance;
+
+    tolua_property__get_set Feature JOINT_ROTATIONS;
+    tolua_property__get_set Feature TARGET_ROTATIONS;
+    tolua_property__get_set Feature UPDATE_ORIGINAL_POSE;
+    tolua_property__get_set Feature UPDATE_ACTIVE_POSE;
+    tolua_property__get_set Feature USE_ORIGINAL_POSE;
+    tolua_property__get_set Feature CONSTRAINTS;
+    tolua_property__get_set Feature AUTO_SOLVE;
 };