Răsfoiți Sursa

Merging updated Urho3D wrappers and script bindings from my own test branch

TheComet 8 ani în urmă
părinte
comite
a6930ec06f

+ 10 - 6
Source/Urho3D/AngelScript/IKAPI.cpp

@@ -46,17 +46,23 @@ static void RegisterIKSolver(asIScriptEngine* engine)
     engine->RegisterObjectMethod("IKSolver", "void set_tolerance(float)", asMETHOD(IKSolver, SetTolerance), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "bool get_boneRotations() const", asMETHOD(IKSolver, BoneRotationsEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_boneRotations(bool)", asMETHOD(IKSolver, EnableBoneRotations), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_constraints() const", asMETHOD(IKSolver, ConstraintsEnabled), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_constraints(bool)", asMETHOD(IKSolver, EnableConstraints), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "bool get_targetRotation() const", asMETHOD(IKSolver, TargetRotationEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_targetRotation(bool)", asMETHOD(IKSolver, EnableTargetRotation), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "bool get_continuousSolving() const", asMETHOD(IKSolver, ContinuousSolvingEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_continuousSolving(bool)", asMETHOD(IKSolver, EnableContinuousSolving), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "bool get_updatePose() const", asMETHOD(IKSolver, UpdatePoseEnabled), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "void set_updatePose(bool)", asMETHOD(IKSolver, EnableUpdatePose), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_autoUpdateInitialPose() const", asMETHOD(IKSolver, AutoUpdateInitialPoseEnabled), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_autoUpdateInitialPose(bool)", asMETHOD(IKSolver, EnableAutoUpdateInitialPose), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "bool get_autoSolve() const", asMETHOD(IKSolver, AutoSolveEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void set_autoSolve(bool)", asMETHOD(IKSolver, EnableAutoSolve), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void Solve()", asMETHOD(IKSolver, Solve), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "void ResetToInitialPose()", asMETHOD(IKSolver, ResetToInitialPose), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKSolver", "void UpdateInitialPose()", asMETHOD(IKSolver, UpdateInitialPose), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ApplyInitialPoseToScene()", asMETHOD(IKSolver, ApplyInitialPoseToScene), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ApplySceneToInitialPose()", asMETHOD(IKSolver, ApplySceneToInitialPose), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ApplySolvedPoseToScene()", asMETHOD(IKSolver, ApplySolvedPoseToScene), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ApplySceneToSolvedPose()", asMETHOD(IKSolver, ApplySceneToSolvedPose), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ResetSolvedPoseToInitialPose()", asMETHOD(IKSolver, ResetSolvedPoseToInitialPose), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void ApplyConstraints(Node@+)", asMETHODPR(IKSolver, ApplyConstraints, (Node*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKSolver", "void DrawDebugGeometry(bool)", asMETHODPR(IKSolver, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
 }
 
@@ -81,8 +87,6 @@ static void RegisterIKEffector(asIScriptEngine* engine)
     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", "bool get_inheritParentRotation() const", asMETHOD(IKEffector, InheritParentRotationEnabled), asCALL_THISCALL);
-    engine->RegisterObjectMethod("IKEffector", "void set_inheritParentRotation(bool)", asMETHOD(IKEffector, EnableInheritParentRotation), asCALL_THISCALL);
     engine->RegisterObjectMethod("IKEffector", "void DrawDebugGeometry(bool)", asMETHODPR(IKEffector, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
 }
 

+ 2 - 1
Source/Urho3D/IK/IK.h

@@ -24,13 +24,14 @@
  * TODO
  *  - Add support for manually updating initial pose.
  *  - Lua script bindings crash.
- *  - Implement inherit parent rotations in IKEffector.
  *  - 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.

+ 12 - 2
Source/Urho3D/IK/IKConstraint.cpp

@@ -26,6 +26,7 @@
 #include "../Scene/Node.h"
 #include "../Scene/SceneEvents.h"
 
+#include <ik/constraint.h>
 #include <ik/node.h>
 
 namespace Urho3D
@@ -36,6 +37,7 @@ extern const char* IK_CATEGORY;
 // ----------------------------------------------------------------------------
 IKConstraint::IKConstraint(Context* context) :
     Component(context),
+    ikNode_(NULL),
     stiffness_(0.0f),
     stretchiness_(0.0f)
 {
@@ -107,15 +109,23 @@ void IKConstraint::SetLengthConstraints(const Vector2& lengthConstraints)
 // ----------------------------------------------------------------------------
 void IKConstraint::SetIKNode(ik_node_t* node)
 {
-    ikNode_ = node;
-    if (node)
+    if (ikNode_ != NULL)
     {
+        ik_node_destroy_constraint(ikNode_);
+    }
+
+    if (node != NULL)
+    {
+        ik_constraint_t* constraint = ik_constraint_create(IK_CONSTRAINT_STIFF);
+        ik_node_attach_constraint(node, constraint);
         /* TODO
         node->stiffness = stiffness_;
         node->stretchiness = stretchiness_;
         node->min_length = lengthConstraints_.x_;
         node->max_length = lengthConstraints_.y_;*/
     }
+
+    ikNode_ = node;
 }
 
 } // namespace Urho3D

+ 11 - 19
Source/Urho3D/IK/IKEffector.cpp

@@ -27,8 +27,11 @@
 #include "../IK/IKEvents.h"
 #include "../IK/IKSolver.h"
 #include "../Scene/SceneEvents.h"
+#include "../IO/Log.h"
 
 #include <ik/effector.h>
+#include <ik/solver.h>
+#include <ik/util.h>
 
 namespace Urho3D
 {
@@ -46,11 +49,13 @@ IKEffector::IKEffector(Context* context) :
     weightedNlerp_(false),
     inheritParentRotation_(false)
 {
+    URHO3D_LOGDEBUG("IKEffector created");
 }
 
 // ----------------------------------------------------------------------------
 IKEffector::~IKEffector()
 {
+    URHO3D_LOGDEBUG("IKEffector destroyed");
 }
 
 // ----------------------------------------------------------------------------
@@ -66,7 +71,6 @@ void IKEffector::RegisterObject(Context* context)
     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("Inherit Parent Rotation", InheritParentRotationEnabled, EnableInheritParentRotation, bool, false, AM_DEFAULT);
 }
 
 // ----------------------------------------------------------------------------
@@ -185,7 +189,10 @@ void IKEffector::SetRotationWeight(float weight)
 {
     rotationWeight_ = Clamp(weight, 0.0f, 1.0f);
     if (ikEffector_ != NULL)
+    {
         ikEffector_->rotation_weight = rotationWeight_;
+        ik_calculate_rotation_weight_decays(solver_->solver_->chain_tree);
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -199,7 +206,10 @@ void IKEffector::SetRotationDecay(float decay)
 {
     rotationDecay_ = Clamp(decay, 0.0f, 1.0f);
     if (ikEffector_ != NULL)
+    {
+        ik_calculate_rotation_weight_decays(solver_->solver_->chain_tree);
         ikEffector_->rotation_decay = rotationDecay_;
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -220,24 +230,6 @@ void IKEffector::EnableWeightedNlerp(bool enable)
     }
 }
 
-// ----------------------------------------------------------------------------
-bool IKEffector::InheritParentRotationEnabled() const
-{
-    return inheritParentRotation_;
-}
-
-// ----------------------------------------------------------------------------
-void IKEffector::EnableInheritParentRotation(bool enable)
-{
-    inheritParentRotation_ = enable;
-    if(ikEffector_ != NULL)
-    {
-        ikEffector_->flags &= ~EFFECTOR_INHERIT_PARENT_ROTATION;
-        if (enable)
-            ikEffector_->flags |= EFFECTOR_INHERIT_PARENT_ROTATION;
-    }
-}
-
 // ----------------------------------------------------------------------------
 void IKEffector::UpdateTargetNodePosition()
 {

+ 0 - 3
Source/Urho3D/IK/IKEffector.h

@@ -151,9 +151,6 @@ public:
      */
     void EnableWeightedNlerp(bool enable);
 
-    bool InheritParentRotationEnabled() const;
-    void EnableInheritParentRotation(bool enable);
-
     void DrawDebugGeometry(bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
 

+ 163 - 41
Source/Urho3D/IK/IKSolver.cpp

@@ -37,6 +37,7 @@
 #include <ik/effector.h>
 #include <ik/node.h>
 #include <ik/solver.h>
+#include <ik/util.h>
 
 namespace Urho3D
 {
@@ -58,11 +59,33 @@ static bool ChildrenHaveEffector(const Node* node)
     return false;
 }
 
+static void ApplyConstraintsCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    IKConstraint* constraint = node->GetComponent<IKConstraint>();
+    if (constraint == NULL)
+        return;
+
+    quat_set_identity(ikNode->rotation.f);
+}
+
+static void ApplyConstraintsCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    IKConstraint* constraint = node->GetComponent<IKConstraint>();
+    if (constraint == NULL)
+        return;
+
+    quat_set_identity(ikNode->rotation.f);
+}
+
 // ----------------------------------------------------------------------------
 IKSolver::IKSolver(Context* context) :
     Component(context),
     solver_(NULL),
+    algorithm_(FABRIK),
     solverTreeNeedsRebuild_(false),
+    continuousSolvingEnabled_(false),
     updateInitialPose_(false),
     autoSolveEnabled_(true)
 {
@@ -95,6 +118,8 @@ void IKSolver::RegisterObject(Context* context)
 
     static const char* algorithmNames[] = {
         "FABRIK",
+        "2 Bone Trig",
+        "1 Bone Trig",
         /* not implemented
         "Jacobian Inverse",
         "Jacobian Transpose",*/
@@ -106,8 +131,9 @@ void IKSolver::RegisterObject(Context* context)
     URHO3D_ACCESSOR_ATTRIBUTE("Convergence Tolerance", GetTolerance, SetTolerance, float, 0.001, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Bone Rotations", BoneRotationsEnabled, EnableBoneRotations, bool, true, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Target Rotation", TargetRotationEnabled, EnableTargetRotation, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Constraints", ConstraintsEnabled, EnableConstraints, bool, false, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Continuous Solving", ContinuousSolvingEnabled, EnableContinuousSolving, bool, false, AM_DEFAULT);
-    URHO3D_ACCESSOR_ATTRIBUTE("Update Pose", UpdatePoseEnabled, EnableUpdatePose, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Update Initial Pose", AutoUpdateInitialPoseEnabled, EnableAutoUpdateInitialPose, bool, false, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Auto Solve", AutoSolveEnabled, EnableAutoSolve, bool, true, AM_DEFAULT);
 }
 
@@ -122,15 +148,33 @@ void IKSolver::SetAlgorithm(IKSolver::Algorithm algorithm)
 {
     algorithm_ = algorithm;
 
+    /* We need to rebuild the tree so make sure that the scene is in the
+     * initial pose when this occurs.*/
+    if (node_ != NULL)
+        ApplyInitialPoseToScene();
+
+    // Initial flags for when there is no solver to destroy
+    uint8_t initialFlags = SOLVER_CALCULATE_FINAL_ROTATIONS;
+
+    // Destroys the tree and the solver
     if (solver_ != NULL)
+    {
+        initialFlags = solver_->flags;
+        DestroyTree();
         ik_solver_destroy(solver_);
+    }
 
     switch (algorithm_)
     {
-        case FABRIK: solver_ = ik_solver_create(SOLVER_FABRIK); break;
+        case FABRIK   : solver_ = ik_solver_create(SOLVER_FABRIK);   break;
+        case TWO_BONE : solver_ = ik_solver_create(SOLVER_TWO_BONE); break;
+        case ONE_BONE : solver_ = ik_solver_create(SOLVER_ONE_BONE); break;
     }
 
-    solver_->flags = SOLVER_CALCULATE_FINAL_ROTATIONS;
+    solver_->flags = initialFlags;
+
+    if (node_ != NULL)
+        RebuildTree();
 }
 
 // ----------------------------------------------------------------------------
@@ -173,6 +217,20 @@ void IKSolver::EnableBoneRotations(bool enable)
         solver_->flags |= SOLVER_CALCULATE_FINAL_ROTATIONS;
 }
 
+// ----------------------------------------------------------------------------
+bool IKSolver::ConstraintsEnabled() const
+{
+    return (solver_->flags & SOLVER_ENABLE_CONSTRAINTS);
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableConstraints(bool enable)
+{
+    solver_->flags &= ~SOLVER_ENABLE_CONSTRAINTS;
+    if (enable)
+        solver_->flags |= SOLVER_ENABLE_CONSTRAINTS;
+}
+
 // ----------------------------------------------------------------------------
 bool IKSolver::TargetRotationEnabled() const
 {
@@ -190,25 +248,23 @@ void IKSolver::EnableTargetRotation(bool enable)
 // ----------------------------------------------------------------------------
 bool IKSolver::ContinuousSolvingEnabled() const
 {
-    return (solver_->flags & SOLVER_SKIP_RESET) != 0;
+    return continuousSolvingEnabled_;
 }
 
 // ----------------------------------------------------------------------------
 void IKSolver::EnableContinuousSolving(bool enable)
 {
-    solver_->flags &= ~SOLVER_SKIP_RESET;
-    if (enable)
-        solver_->flags |= SOLVER_SKIP_RESET;
+    continuousSolvingEnabled_ = enable;
 }
 
 // ----------------------------------------------------------------------------
-bool IKSolver::UpdatePoseEnabled() const
+bool IKSolver::AutoUpdateInitialPoseEnabled() const
 {
     return updateInitialPose_;
 }
 
 // ----------------------------------------------------------------------------
-void IKSolver::EnableUpdatePose(bool enable)
+void IKSolver::EnableAutoUpdateInitialPose(bool enable)
 {
     updateInitialPose_ = enable;
 }
@@ -240,12 +296,6 @@ void IKSolver::EnableAutoSolve(bool enable)
 }
 
 // ----------------------------------------------------------------------------
-static void ApplySolvedDataCallback(ik_node_t* ikNode)
-{
-    Node* node = (Node*)ikNode->user_data;
-    node->SetWorldRotation(QuatIK2Urho(&ikNode->solved_rotation));
-    node->SetWorldPosition(Vec3IK2Urho(&ikNode->solved_position));
-}
 void IKSolver::Solve()
 {
     URHO3D_PROFILE(IKSolve);
@@ -253,45 +303,78 @@ void IKSolver::Solve()
     if (solverTreeNeedsRebuild_)
     {
         ik_solver_rebuild_data(solver_);
+        ik_calculate_rotation_weight_decays(solver_->chain_tree);
         solverTreeNeedsRebuild_ = false;
     }
 
     if (updateInitialPose_)
-        UpdateInitialPose();
+        ApplySceneToInitialPose();
+
+    if (continuousSolvingEnabled_ == false)
+        ResetSolvedPoseToInitialPose();
 
     for (PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
     {
         (*it)->UpdateTargetNodePosition();
     }
 
-    solver_->apply_result = ApplySolvedDataCallback;
     ik_solver_solve(solver_);
+
+    ApplySolvedPoseToScene();
+}
+
+// ----------------------------------------------------------------------------
+static void ApplyInitialPoseToSceneCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    node->SetWorldRotation(QuatIK2Urho(&ikNode->initial_rotation));
+    node->SetWorldPosition(Vec3IK2Urho(&ikNode->initial_position));
+}
+void IKSolver::ApplyInitialPoseToScene()
+{
+    ik_solver_iterate_tree(solver_, ApplyInitialPoseToSceneCallback);
+}
+
+// ----------------------------------------------------------------------------
+static void ApplySceneToInitialPoseCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    ikNode->initial_rotation = QuatUrho2IK(node->GetWorldRotation());
+    ikNode->initial_position = Vec3Urho2IK(node->GetWorldPosition());
+}
+void IKSolver::ApplySceneToInitialPose()
+{
+    ik_solver_iterate_tree(solver_, ApplySceneToInitialPoseCallback);
 }
 
 // ----------------------------------------------------------------------------
-static void ApplyInitialDataCallback(ik_node_t* ikNode)
+static void ApplySolvedPoseToSceneCallback(ik_node_t* ikNode)
 {
     Node* node = (Node*)ikNode->user_data;
     node->SetWorldRotation(QuatIK2Urho(&ikNode->rotation));
     node->SetWorldPosition(Vec3IK2Urho(&ikNode->position));
 }
-void IKSolver::ResetToInitialPose()
+void IKSolver::ApplySolvedPoseToScene()
 {
-    solver_->apply_result = ApplyInitialDataCallback;
-    ik_solver_iterate_tree(solver_);
+    ik_solver_iterate_tree(solver_, ApplySolvedPoseToSceneCallback);
 }
 
 // ----------------------------------------------------------------------------
-static void UpdateInitialPoseCallback(ik_node_t* ikNode)
+static void ApplySceneToSolvedPoseCallback(ik_node_t* ikNode)
 {
     Node* node = (Node*)ikNode->user_data;
     ikNode->rotation = QuatUrho2IK(node->GetWorldRotation());
     ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
 }
-void IKSolver::UpdateInitialPose()
+void IKSolver::ApplySceneToSolvedPose()
 {
-    solver_->apply_result = UpdateInitialPoseCallback;
-    ik_solver_iterate_tree(solver_);
+    ik_solver_iterate_tree(solver_, ApplySceneToSolvedPoseCallback);
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::ResetSolvedPoseToInitialPose()
+{
+    ik_solver_reset_solved_data(solver_);
 }
 
 // ----------------------------------------------------------------------------
@@ -316,7 +399,7 @@ void IKSolver::OnSceneSet(Scene* scene)
 // ----------------------------------------------------------------------------
 void IKSolver::OnNodeSet(Node* node)
 {
-    ResetToInitialPose();
+    ApplyInitialPoseToScene();
     DestroyTree();
 
     if (node != NULL)
@@ -329,14 +412,17 @@ ik_node_t* IKSolver::CreateIKNode(const Node* node)
     ik_node_t* ikNode = ik_node_create(node->GetID());
 
     // Set initial position/rotation and pass in Node* as user data for later
-    ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
-    ikNode->rotation = QuatUrho2IK(node->GetWorldRotation());
+    ikNode->initial_position = Vec3Urho2IK(node->GetWorldPosition());
+    ikNode->initial_rotation = QuatUrho2IK(node->GetWorldRotation());
     ikNode->user_data = (void*)node;
 
     // If the node has a constraint, it needs access to the ikNode
     IKConstraint* constraint = node->GetComponent<IKConstraint>();
     if (constraint != NULL)
+    {
         constraint->SetIKNode(ikNode);
+        constraintList_.Push(constraint);
+    }
 
     return ikNode;
 }
@@ -351,7 +437,7 @@ void IKSolver::DestroyTree()
 // ----------------------------------------------------------------------------
 void IKSolver::RebuildTree()
 {
-    assert(node_ != NULL);
+    assert (node_ != NULL);
 
     ik_node_t* ikRoot = CreateIKNode(node_);
     ik_solver_set_tree(solver_, ikRoot);
@@ -400,7 +486,7 @@ void IKSolver::BuildTreeToEffector(const Node* node)
     // it.
     ik_effector_t* ikEffector = ik_effector_create();
     ik_node_attach_effector(ikNode, ikEffector); // ownership of effector
-    effector->SetIKEffector(ikEffector);           // "weak" reference to effector
+    effector->SetIKEffector(ikEffector);         // "weak" reference to effector
     effector->SetIKSolver(this);
     effectorList_.Push(effector);
 
@@ -418,6 +504,17 @@ void IKSolver::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
 
     Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
     BuildTreeToEffector(node);
+
+    IKConstraint* constraint = static_cast<IKConstraint*>(node->GetComponent<IKConstraint>());
+    if (constraint != NULL)
+    {
+        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+        if (ikNode != NULL)
+        {
+            constraint->SetIKNode(ikNode);
+            constraintList_.Push(constraint);
+        }
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -440,7 +537,7 @@ void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventDat
         effector->SetIKEffector(NULL);
         effectorList_.RemoveSwap(effector);
 
-        ResetToInitialPose();
+        ApplyInitialPoseToScene();
         MarkSolverTreeDirty();
     }
 
@@ -449,6 +546,7 @@ void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventDat
     {
         IKConstraint* constraint = static_cast<IKConstraint*>(component);
         constraint->SetIKNode(NULL);  // NOTE: Should restore default settings to the node
+        constraintList_.RemoveSwap(constraint);
     }
 }
 
@@ -469,6 +567,12 @@ void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
         BuildTreeToEffector(*it);
         effectorList_.Push((*it)->GetComponent<IKEffector>());
     }
+
+    node->GetChildrenWithComponent<IKConstraint>(nodes, true);
+    for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
+    {
+        constraintList_.Push((*it)->GetComponent<IKConstraint>());
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -491,6 +595,13 @@ void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
         effectorList_.RemoveSwap(effector);
     }
 
+    node->GetChildrenWithComponent<IKConstraint>(nodes, true);
+    for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
+    {
+        IKConstraint* constraint = (*it)->GetComponent<IKConstraint>();
+        constraintList_.RemoveSwap(constraint);
+    }
+
     // Special case, if the node being destroyed is the root node, destroy the
     // solver's tree instead of destroying the single node. Calling
     // ik_node_destroy() on the solver's root node will cause segfaults.
@@ -512,6 +623,17 @@ void IKSolver::HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMa
     Solve();
 }
 
+// ----------------------------------------------------------------------------
+void IKSolver::ApplyConstraints(Node* tree)
+{
+    for (PODVector<IKConstraint*>::ConstIterator it = constraintList_.Begin(); it != constraintList_.End(); ++it)
+    {
+        IKConstraint* constraint = *it;
+        Node* node = constraint->GetNode();
+        node->SetRotation(Quaternion::IDENTITY);
+    }
+}
+
 // ----------------------------------------------------------------------------
 void IKSolver::DrawDebugGeometry(bool depthTest)
 {
@@ -539,8 +661,8 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
         unsigned numberOfSegments = 0;
         while (b && chainLength-- != 0)
         {
-            vec3_t v = a->position;
-            vec3_sub_vec3(v.f, b->position.f);
+            vec3_t v = a->initial_position;
+            vec3_sub_vec3(v.f, b->initial_position.f);
             averageLength += vec3_length(v.f);
             ++numberOfSegments;
             a = b;
@@ -553,36 +675,36 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
         a = *pnode;
         b = a->parent;
         debug->AddSphere(
-            Sphere(Vec3IK2Urho(&a->position), averageLength * 0.1f),
+            Sphere(Vec3IK2Urho(&a->initial_position), averageLength * 0.1f),
             Color(0, 0, 255),
             depthTest
         );
         debug->AddSphere(
-            Sphere(Vec3IK2Urho(&a->solved_position), averageLength * 0.1f),
+            Sphere(Vec3IK2Urho(&a->position), averageLength * 0.1f),
             Color(255, 128, 0),
             depthTest
         );
         while (b && chainLength-- != 0)
         {
             debug->AddLine(
-                Vec3IK2Urho(&a->position),
-                Vec3IK2Urho(&b->position),
+                Vec3IK2Urho(&a->initial_position),
+                Vec3IK2Urho(&b->initial_position),
                 Color(0, 255, 255),
                 depthTest
             );
             debug->AddSphere(
-                Sphere(Vec3IK2Urho(&b->position), averageLength * 0.1f),
+                Sphere(Vec3IK2Urho(&b->initial_position), averageLength * 0.1f),
                 Color(0, 0, 255),
                 depthTest
             );
             debug->AddLine(
-                Vec3IK2Urho(&a->solved_position),
-                Vec3IK2Urho(&b->solved_position),
+                Vec3IK2Urho(&a->position),
+                Vec3IK2Urho(&b->position),
                 Color(255, 0, 0),
                 depthTest
             );
             debug->AddSphere(
-                Sphere(Vec3IK2Urho(&b->solved_position), averageLength * 0.1f),
+                Sphere(Vec3IK2Urho(&b->position), averageLength * 0.1f),
                 Color(255, 128, 0),
                 depthTest
             );

+ 34 - 9
Source/Urho3D/IK/IKSolver.h

@@ -47,7 +47,9 @@ public:
 
     enum Algorithm
     {
-        FABRIK
+        FABRIK,
+        TWO_BONE,
+        ONE_BONE
         /* not implemented yet
         JACOBIAN_INVERSE,
         JACOBIAN_TRANSPOSE*/
@@ -125,6 +127,18 @@ public:
      */
     void EnableBoneRotations(bool enable);
 
+    /// Whether or not constraints have any effect on the result
+    bool ConstraintsEnabled() const;
+
+    /*!
+     * @brief 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 Urho3D's scene graph every
+     * iteration, while calling ApplyConstraints(). Disabling constraints means
+     * ApplyConstraints() is never called.
+     */
+    void EnableConstraints(bool enable);
+
     /// Whether or not target rotation is enabled
     bool TargetRotationEnabled() const;
 
@@ -139,7 +153,7 @@ public:
      */
     void EnableTargetRotation(bool enable);
 
-    /// Whether or not continuous solving is enabled or not.
+    /// Whether or not continuous solving is enabled.
     bool ContinuousSolvingEnabled() const;
 
     /*!
@@ -163,8 +177,8 @@ public:
      */
     void EnableContinuousSolving(bool enable);
 
-    /// Whether or not the initial pose is updated for every solution
-    bool UpdatePoseEnabled() const;
+    /// Whether or not the initial pose is updated when calling Solve()
+    bool AutoUpdateInitialPoseEnabled() const;
 
     /*!
      * @brief When enabled, the current Urho3D node positions and rotations in
@@ -177,7 +191,7 @@ public:
      * is set when the solver is first created. You can manually update the
      * initial pose at any time by calling UpdateInitialPose().
      */
-    void EnableUpdatePose(bool enable);
+    void EnableAutoUpdateInitialPose(bool enable);
 
     /// Whether or not the solver should be invoked automatically
     bool AutoSolveEnabled() const;
@@ -203,7 +217,7 @@ public:
      * @brief Causes the initial tree to be applied back to Urho3D's scene
      * graph. This is what gets called when continuous solving is disabled.
      */
-    void ResetToInitialPose();
+    void ApplyInitialPoseToScene();
 
     /*!
      * @brief Causes the current scene graph data to be copied into the solvers
@@ -212,15 +226,24 @@ public:
      * then the result will be a "continuous solution", where the solver will
      * use the previously calculated tree as a basis for the new solution.
      */
-    void UpdateInitialPose();
+    void ApplySceneToInitialPose();
 
-    /// Causes the solver tree to be rebuilt before solving the next time.
-    void MarkSolverTreeDirty();
+    void ApplySolvedPoseToScene();
+
+    void ApplySceneToSolvedPose();
+
+    void ResetSolvedPoseToInitialPose();
+
+    virtual void ApplyConstraints(Node* tree);
 
     void DrawDebugGeometry(bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
 
 private:
+    friend class IKEffector;
+
+    /// Causes the solver tree to be rebuilt before solving the next time. Intended to be used by IKEffector.
+    void MarkSolverTreeDirty();
     /// Subscribe to drawable update finished event here
     virtual void OnSceneSet(Scene* scene);
     /// Destroys and creates the tree
@@ -244,9 +267,11 @@ private:
     void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
 
     PODVector<IKEffector*> effectorList_;
+    PODVector<IKConstraint*> constraintList_;
     ik_solver_t* solver_;
     Algorithm algorithm_;
     bool solverTreeNeedsRebuild_;
+    bool continuousSolvingEnabled_;
     bool updateInitialPose_;
     bool autoSolveEnabled_;
 };

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

@@ -29,9 +29,6 @@ class IKEffector : public Component
     bool WeightedNlerpEnabled() const;
     void EnableWeightedNlerp(bool enable);
 
-    bool InheritParentRotationEnabled() const;
-    void EnableInheritParentRotation(bool enable);
-
     tolua_property__get_set Node* targetNode;
     tolua_property__get_set String targetName;
     tolua_property__get_set Vector3 targetPosition;

+ 16 - 5
Source/Urho3D/LuaScript/pkgs/IK/IKSolver.pkg

@@ -9,31 +9,42 @@ class IKSolver : public Component
 
     Algorithm GetAlgorithm() const;
     void SetAlgorithm(Algorithm algorithm);
+
     unsigned GetMaximumIterations() const;
     void SetMaximumIterations(unsigned iterations);
+
     float GetTolerance() const;
     void SetTolerance(float tolerance);
 
     bool BoneRotationsEnabled() const;
     void EnableBoneRotations(bool enable);
 
+    bool ConstraintsEnabled() const;
+    void EnableConstraints(bool enable);
+
     bool TargetRotationEnabled() const;
     void EnableTargetRotation(bool enable);
 
     bool ContinuousSolvingEnabled() const;
     void EnableContinuousSolving(bool enable);
 
-    bool UpdatePoseEnabled() const;
-    void EnableUpdatePose(bool enable);
+    bool AutoUpdateInitialPoseEnabled() const;
+    void EnableAutoUpdateInitialPose(bool enable);
 
+    /// Whether or not the solver should be invoked automatically
     bool AutoSolveEnabled() const;
     void EnableAutoSolve(bool enable);
 
     void Solve();
-    void ResetToInitialPose();
-    void UpdateInitialPose();
 
-    void MarkSolverTreeDirty();
+    void ApplyInitialPoseToScene();
+    void ApplySceneToInitialPose();
+    void ApplySolvedPoseToScene();
+    void ApplySceneToSolvedPose();
+    void ResetSolvedPoseToInitialPose();
+
+    virtual void ApplyConstraints(Node* tree);
+
     void DrawDebugGeometry(bool depthTest);
 
     tolua_property__get_set Algorithm algorithm;