Explorar o código

fixes #2058 - It probably still has some bugs that need fixing, and the script bindings need updating

TheComet %!s(int64=8) %!d(string=hai) anos
pai
achega
1f679d6756

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

@@ -147,6 +147,10 @@ ik_solver_destroy_tree(ik_solver_t* solver);
  * @note Needs to be called whenever the tree changes in any way. I.e. if you
  * remove nodes or add nodes, or if you remove effectors or add effectors,
  * you must call this again before calling the solver.
+ * @return Returns non-zero if any of the chain trees are invalid for any
+ * reason. If this happens, check the log for error messages.
+ * @warning If this functions fails, the internal structures are in an
+ * undefined state. You cannot solve the tree in this state.
  */
 IK_PUBLIC_API int
 ik_solver_rebuild_chain_trees(ik_solver_t* solver);

+ 1 - 1
Source/Urho3D/AngelScript/IKAPI.cpp

@@ -57,7 +57,7 @@ static void RegisterIKSolver(asIScriptEngine* engine)
     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", "void RebuildData()", asMETHOD(IKSolver, RebuildChains), 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);
     engine->RegisterObjectMethod("IKSolver", "void Solve()", asMETHOD(IKSolver, Solve), asCALL_THISCALL);

BIN=BIN
Source/Urho3D/IK/.IKSolver.cpp.kate-swp


+ 7 - 15
Source/Urho3D/IK/IKConstraint.cpp

@@ -37,7 +37,7 @@ extern const char* IK_CATEGORY;
 // ----------------------------------------------------------------------------
 IKConstraint::IKConstraint(Context* context) :
     Component(context),
-    ikNode_(NULL),
+    ikConstraintNode_(NULL),
     stiffness_(0.0f),
     stretchiness_(0.0f)
 {
@@ -68,7 +68,7 @@ float IKConstraint::GetStiffness() const
 void IKConstraint::SetStiffness(float stiffness)
 {
     stiffness_ = Clamp(stiffness, 0.0f, 1.0f);
-    if (ikNode_ != NULL)
+    if (ikConstraintNode_ != NULL)
         /* TODO ikNode_->stiffness = stiffness_; */
         ;
 }
@@ -83,7 +83,7 @@ float IKConstraint::GetStretchiness() const
 void IKConstraint::SetStretchiness(float stretchiness)
 {
     stretchiness_ = Clamp(stretchiness, 0.0f, 1.0f);
-    if (ikNode_)
+    if (ikConstraintNode_)
         /* TODO ikNode_->stretchiness = stretchiness_;*/
         ;
 }
@@ -98,7 +98,7 @@ const Vector2& IKConstraint::GetLengthConstraints() const
 void IKConstraint::SetLengthConstraints(const Vector2& lengthConstraints)
 {
     lengthConstraints_ = lengthConstraints;
-    if (ikNode_ != NULL)
+    if (ikConstraintNode_ != NULL)
     {
         /* TODO
         ikNode_->min_length = lengthConstraints_.x_;
@@ -107,25 +107,17 @@ void IKConstraint::SetLengthConstraints(const Vector2& lengthConstraints)
 }
 
 // ----------------------------------------------------------------------------
-void IKConstraint::SetIKNode(ik_node_t* node)
+void IKConstraint::SetIKConstraintNode(ik_node_t* constraintNode)
 {
-    if (ikNode_ != NULL)
+    ikConstraintNode_ = constraintNode;
+    if (constraintNode != 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

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

@@ -60,9 +60,9 @@ private:
     friend class IKSolver;
 
     /// Intended to be used only by IKSolver
-    void SetIKNode(ik_node_t* effector);
+    void SetIKConstraintNode(ik_node_t* constraintNode);
 
-    ik_node_t* ikNode_;
+    ik_node_t* ikConstraintNode_;
 
     float stiffness_;
     float stretchiness_;

+ 26 - 25
Source/Urho3D/IK/IKEffector.cpp

@@ -29,6 +29,7 @@
 #include "../Scene/SceneEvents.h"
 #include "../IO/Log.h"
 
+#include <ik/node.h>
 #include <ik/effector.h>
 #include <ik/solver.h>
 #include <ik/util.h>
@@ -41,7 +42,7 @@ extern const char* IK_CATEGORY;
 // ----------------------------------------------------------------------------
 IKEffector::IKEffector(Context* context) :
     Component(context),
-    ikEffector_(NULL),
+    ikEffectorNode_(NULL),
     chainLength_(0),
     weight_(1.0f),
     rotationWeight_(1.0),
@@ -117,8 +118,8 @@ const Vector3& IKEffector::GetTargetPosition() const
 void IKEffector::SetTargetPosition(const Vector3& targetPosition)
 {
     targetPosition_ = targetPosition;
-    if (ikEffector_ != NULL)
-        ikEffector_->target_position = Vec3Urho2IK(targetPosition);
+    if (ikEffectorNode_ != NULL)
+        ikEffectorNode_->effector->target_position = Vec3Urho2IK(targetPosition);
 }
 
 // ----------------------------------------------------------------------------
@@ -131,8 +132,8 @@ const Quaternion& IKEffector::GetTargetRotation() const
 void IKEffector::SetTargetRotation(const Quaternion& targetRotation)
 {
     targetRotation_ = targetRotation;
-    if (ikEffector_)
-        ikEffector_->target_rotation = QuatUrho2IK(targetRotation);
+    if (ikEffectorNode_)
+        ikEffectorNode_->effector->target_rotation = QuatUrho2IK(targetRotation);
 }
 
 // ----------------------------------------------------------------------------
@@ -157,9 +158,9 @@ unsigned int IKEffector::GetChainLength() const
 void IKEffector::SetChainLength(unsigned chainLength)
 {
     chainLength_ = chainLength;
-    if (ikEffector_ != NULL)
+    if (ikEffectorNode_ != NULL)
     {
-        ikEffector_->chain_length = chainLength;
+        ikEffectorNode_->effector->chain_length = chainLength;
         solver_->MarkChainsNeedUpdating();
     }
 }
@@ -174,8 +175,8 @@ float IKEffector::GetWeight() const
 void IKEffector::SetWeight(float weight)
 {
     weight_ = Clamp(weight, 0.0f, 1.0f);
-    if (ikEffector_ != NULL)
-        ikEffector_->weight = weight_;
+    if (ikEffectorNode_ != NULL)
+        ikEffectorNode_->effector->weight = weight_;
 }
 
 // ----------------------------------------------------------------------------
@@ -188,9 +189,9 @@ float IKEffector::GetRotationWeight() const
 void IKEffector::SetRotationWeight(float weight)
 {
     rotationWeight_ = Clamp(weight, 0.0f, 1.0f);
-    if (ikEffector_ != NULL)
+    if (ikEffectorNode_ != NULL)
     {
-        ikEffector_->rotation_weight = rotationWeight_;
+        ikEffectorNode_->rotation_weight = rotationWeight_;
         ik_calculate_rotation_weight_decays(&solver_->solver_->chain_tree);
     }
 }
@@ -205,10 +206,10 @@ float IKEffector::GetRotationDecay() const
 void IKEffector::SetRotationDecay(float decay)
 {
     rotationDecay_ = Clamp(decay, 0.0f, 1.0f);
-    if (ikEffector_ != NULL)
+    if (ikEffectorNode_ != NULL)
     {
         ik_calculate_rotation_weight_decays(&solver_->solver_->chain_tree);
-        ikEffector_->rotation_decay = rotationDecay_;
+        ikEffectorNode_->effector->rotation_decay = rotationDecay_;
     }
 }
 
@@ -222,11 +223,11 @@ bool IKEffector::WeightedNlerpEnabled() const
 void IKEffector::EnableWeightedNlerp(bool enable)
 {
     weightedNlerp_ = enable;
-    if (ikEffector_ != NULL)
+    if (ikEffectorNode_ != NULL)
     {
-        ikEffector_->flags &= ~EFFECTOR_WEIGHT_NLERP;
+        ikEffectorNode_->effector->flags &= ~EFFECTOR_WEIGHT_NLERP;
         if (enable)
-            ikEffector_->flags |= EFFECTOR_WEIGHT_NLERP;
+            ikEffectorNode_->effector->flags |= EFFECTOR_WEIGHT_NLERP;
     }
 }
 
@@ -308,17 +309,17 @@ void IKEffector::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 }
 
 // ----------------------------------------------------------------------------
-void IKEffector::SetIKEffector(ik_effector_t* effector)
+void IKEffector::SetIKEffectorNode(ik_node_t* effectorNode)
 {
-    ikEffector_ = effector;
-    if (effector)
+    ikEffectorNode_ = effectorNode;
+    if (effectorNode)
     {
-        effector->target_position = Vec3Urho2IK(targetPosition_);
-        effector->target_rotation = QuatUrho2IK(targetRotation_);
-        effector->weight = weight_;
-        effector->rotation_weight = rotationWeight_;
-        effector->rotation_decay = rotationDecay_;
-        effector->chain_length = chainLength_;
+        ikEffectorNode_->effector->target_position = Vec3Urho2IK(targetPosition_);
+        ikEffectorNode_->effector->target_rotation = QuatUrho2IK(targetRotation_);
+        ikEffectorNode_->effector->weight = weight_;
+        ikEffectorNode_->effector->rotation_weight = rotationWeight_;
+        ikEffectorNode_->effector->rotation_decay = rotationDecay_;
+        ikEffectorNode_->effector->chain_length = chainLength_;
         EnableWeightedNlerp(weightedNlerp_);
     }
 }

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

@@ -160,13 +160,13 @@ private:
     /// Intended to be used only by IKSolver
     void SetIKSolver(IKSolver* solver);
     /// Intended to be used only by IKSolver
-    void SetIKEffector(ik_effector_t* effector);
+    void SetIKEffectorNode(ik_node_t* effector);
     /// Intended to be used by IKSolver. Copies the positions/rotations of the target node into the effector
     void UpdateTargetNodePosition();
 
     WeakPtr<Node> targetNode_;
     WeakPtr<IKSolver> solver_;
-    ik_effector_t* ikEffector_;
+    ik_node_t* ikEffectorNode_;
 
     String targetName_;
     Vector3 targetPosition_;

+ 262 - 129
Source/Urho3D/IK/IKSolver.cpp

@@ -50,7 +50,9 @@ IKSolver::IKSolver(Context* context) :
     solver_(NULL),
     algorithm_(FABRIK),
     features_(AUTO_SOLVE | JOINT_ROTATIONS | UPDATE_ACTIVE_POSE),
-    chainsAreDirty_(false)
+    chainTreesNeedUpdating_(false),
+    treeNeedsRebuild(true),
+    solverTreeValid_(false)
 {
     context_->RequireIK();
 
@@ -68,7 +70,7 @@ IKSolver::~IKSolver()
     // Destroying the solver tree will destroy the effector objects, so remove
     // any references any of the IKEffector objects could be holding
     for (PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
-        (*it)->SetIKEffector(NULL);
+        (*it)->SetIKEffectorNode(NULL);
 
     ik_solver_destroy(solver_);
     context_->ReleaseIK();
@@ -214,10 +216,171 @@ void IKSolver::SetTolerance(float tolerance)
 }
 
 // ----------------------------------------------------------------------------
-void IKSolver::RebuildChains()
+ik_node_t* IKSolver::CreateIKNodeFromUrhoNode(const Node* node)
 {
-    ik_solver_rebuild_chain_trees(solver_);
+    ik_node_t* ikNode = ik_node_create(node->GetID());
+
+    // Set initial position/rotation and pass in Node* as user data for later
+    ikNode->original_position = Vec3Urho2IK(node->GetWorldPosition());
+    ikNode->original_rotation = QuatUrho2IK(node->GetWorldRotation());
+    ikNode->user_data = (void*)node;
+
+    /*
+     * If Urho's node has an effector, also create and attach one to the
+     * library's node. At this point, the IKEffector component shouldn't be
+     * holding a reference to any internal effector. Check this for debugging
+     * purposes and log if it does.
+     */
+    IKEffector* effector = node->GetComponent<IKEffector>();
+    if (effector != NULL)
+    {
+#ifdef DEBUG
+        if (effector->ikEffectorNode_ != NULL)
+            URHO3D_LOGWARNINGF("[ik] IKEffector (attached to node \"%s\") has a reference to a possibly invalid internal effector. Should be NULL.", effector->GetNode()->GetName().CString());
+#endif
+        ik_effector_t* ikEffector = ik_effector_create();
+        ik_node_attach_effector(ikNode, ikEffector); // ownership of effector
+
+        effector->SetIKSolver(this);
+        effector->SetIKEffectorNode(ikNode);
+    }
+
+    // Exact same deal with the constraint
+    IKConstraint* constraint = node->GetComponent<IKConstraint>();
+    if (constraint != NULL)
+    {
+#ifdef DEBUG
+        if (constraint->ikConstraintNode_ != NULL)
+            URHO3D_LOGWARNINGF("[ik] IKConstraint (attached to node \"%s\") has a reference to a possibly invalid internal constraint. Should be NULL.", constraint->GetNode()->GetName().CString());
+#endif
+
+        constraint->SetIKConstraintNode(ikNode);
+    }
+
+    return ikNode;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::DestroyTree()
+{
+    ik_solver_destroy_tree(solver_);
+    effectorList_.Clear();
+    constraintList_.Clear();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RebuildTree()
+{
+    assert (node_ != NULL);
+
+    // Destroy current tree and set a new root node
+    DestroyTree();
+    ik_node_t* ikRoot = CreateIKNodeFromUrhoNode(node_);
+    ik_solver_set_tree(solver_, ikRoot);
+
+    /*
+     * Collect all effectors and constraints from children, and filter them to
+     * make sure they are in our subtree.
+     */
+    node_->GetComponents<IKEffector>(effectorList_, true);
+    node_->GetComponents<IKConstraint>(constraintList_, true);
+    for (PODVector<IKEffector*>::Iterator it = effectorList_.Begin(); it != effectorList_.End();)
+    {
+        if (ComponentIsInOurSubtree(*it))
+        {
+            BuildTreeToEffector((*it));
+            ++it;
+        }
+        else
+        {
+            it = effectorList_.Erase(it);
+        }
+    }
+    for (PODVector<IKConstraint*>::Iterator it = constraintList_.Begin(); it != constraintList_.End();)
+    {
+        if (ComponentIsInOurSubtree(*it))
+            ++it;
+        else
+            it = constraintList_.Erase(it);
+    }
+
+    treeNeedsRebuild = false;
+    MarkChainsNeedUpdating();
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::BuildTreeToEffector(IKEffector* effector)
+{
+    /*
+     * NOTE: This function makes the assumption that the node the effector is
+     * attached to is -- without a doubt -- in our subtree (by using
+     * ComponentIsInOurSubtree() first). If this is not the case, the program
+     * will abort.
+     */
+
+    /*
+     * we need to build tree up to the node where this effector was added. Do
+     * this by following the chain of parent nodes until we hit a node that
+     * exists in the solver's subtree. Then iterate backwards again and add each
+     * missing node to the solver's tree.
+     */
+    const Node* iterNode = effector->GetNode();
+    ik_node_t* ikNode;
+    PODVector<const Node*> missingNodes;
+    while ((ikNode = ik_node_find_child(solver_->tree, iterNode->GetID())) == NULL)
+    {
+        missingNodes.Push(iterNode);
+        iterNode = iterNode->GetParent();
+
+        // Assert the assumptions made (described in the beginning of this function)
+        assert(iterNode != NULL);
+        assert (iterNode->HasComponent<IKSolver>() == false || iterNode == node_);
+    }
+
+    while (missingNodes.Size() > 0)
+    {
+        iterNode = missingNodes.Back();
+        missingNodes.Pop();
+
+        ik_node_t* ikChildNode = CreateIKNodeFromUrhoNode(iterNode);
+        ik_node_add_child(ikNode, ikChildNode);
+
+        ikNode = ikChildNode;
+    }
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::ComponentIsInOurSubtree(Component* component) const
+{
+    const Node* iterNode = component->GetNode();
+    while (true)
+    {
+        // Note part of our subtree
+        if (iterNode == NULL)
+            return false;
+        // Reached the root node, it's part of our subtree!
+        if (iterNode == node_)
+            return true;
+        // Path to us is being blocked by another solver
+        Component* otherSolver = iterNode->GetComponent<IKSolver>();
+        if (otherSolver != NULL && otherSolver != component)
+            return false;
+
+        iterNode = iterNode->GetParent();
+    }
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RebuildChainTrees()
+{
+    solverTreeValid_ = (ik_solver_rebuild_chain_trees(solver_) == 0);
     ik_calculate_rotation_weight_decays(&solver_->chain_tree);
+
+    chainTreesNeedUpdating_ = false;
 }
 
 // ----------------------------------------------------------------------------
@@ -237,11 +400,14 @@ void IKSolver::Solve()
 {
     URHO3D_PROFILE(IKSolve);
 
-    if (chainsAreDirty_)
-    {
-        RebuildChains();
-        chainsAreDirty_ = false;
-    }
+    if (treeNeedsRebuild)
+        RebuildTree();
+
+    if (chainTreesNeedUpdating_)
+        RebuildChainTrees();
+
+    if (IsSolverTreeValid() == false)
+        return;
 
     if (features_ & UPDATE_ORIGINAL_POSE)
         ApplySceneToOriginalPose();
@@ -322,7 +488,19 @@ void IKSolver::ApplyOriginalPoseToActivePose()
 // ----------------------------------------------------------------------------
 void IKSolver::MarkChainsNeedUpdating()
 {
-    chainsAreDirty_ = true;
+    chainTreesNeedUpdating_ = true;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::MarkTreeNeedsRebuild()
+{
+    treeNeedsRebuild = true;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::IsSolverTreeValid() const
+{
+    return solverTreeValid_;
 }
 
 // ----------------------------------------------------------------------------
@@ -355,122 +533,55 @@ void IKSolver::OnNodeSet(Node* node)
 }
 
 // ----------------------------------------------------------------------------
-ik_node_t* IKSolver::CreateIKNode(const Node* node)
+void IKSolver::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
 {
-    ik_node_t* ikNode = ik_node_create(node->GetID());
-
-    // Set initial position/rotation and pass in Node* as user data for later
-    ikNode->original_position = Vec3Urho2IK(node->GetWorldPosition());
-    ikNode->original_rotation = QuatUrho2IK(node->GetWorldRotation());
-    ikNode->user_data = (void*)node;
+    using namespace ComponentAdded;
+    (void)eventType;
 
-    // If the node has an effector, it needs access to the ikNode
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+    Component* component = static_cast<Component*>(eventData[P_COMPONENT].GetPtr());
 
-    // If the node has a constraint, it needs access to the ikNode
-    IKConstraint* constraint = node->GetComponent<IKConstraint>();
-    if (constraint != NULL)
+    /*
+     * When a solver gets added into the scene, any parent solver's tree will
+     * be invalidated. We need to find all parent solvers (by iterating up the
+     * tree) and mark them as such.
+     */
+    if (component->GetType() == IKSolver::GetTypeStatic())
     {
-        constraint->SetIKNode(ikNode);
-        constraintList_.Push(constraint);
-    }
-
-    return ikNode;
-}
-
-// ----------------------------------------------------------------------------
-void IKSolver::DestroyTree()
-{
-    ik_solver_destroy_tree(solver_);
-    effectorList_.Clear();
-    constraintList_.Clear();
-}
-
-// ----------------------------------------------------------------------------
-void IKSolver::RebuildTree()
-{
-    assert (node_ != NULL);
-
-    DestroyTree();
+        for (Node* iterNode = node; iterNode != NULL; iterNode = iterNode->GetParent())
+        {
+            IKSolver* parentSolver = iterNode->GetComponent<IKSolver>();
+            if (parentSolver != NULL)
+                parentSolver->MarkTreeNeedsRebuild();
 
-    ik_node_t* ikRoot = CreateIKNode(node_);
-    ik_solver_set_tree(solver_, ikRoot);
+        }
 
-    PODVector<Node*> effectorNodes;
-    node_->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
-    for (PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
-    {
-        BuildTreeToEffector((*it)->GetComponent<IKEffector>());
+        return; // No need to continue processing effectors or constraints
     }
-}
-
-// ----------------------------------------------------------------------------
-bool IKSolver::BuildTreeToEffector(IKEffector* effector)
-{
-    // May need to build tree up to the node where this effector was added. Do
-    // this by following the chain of parent nodes until we hit a node that
-    // exists in the solver's tree. Then iterate backwards again and add each
-    // missing node to the solver's tree.
-    PODVector<const Node*> missingNodes;
-    const Node* iterNode = effector->GetNode();
-    ik_node_t* ikNode = ik_node_find_child(solver_->tree, effector->GetNode()->GetID());
-    while (ikNode == NULL)
-    {
-        missingNodes.Push(iterNode);
-        iterNode = iterNode->GetParent();
-        if (iterNode == NULL)
-            return false; // The effector is in a different branch of the tree, unrelated to us. Abort.
-        if (iterNode->HasComponent<IKSolver>() && iterNode != node_)
-            return false; // A direct path to the effector is being blocked by another solver
-        ikNode = ik_node_find_child(solver_->tree, iterNode->GetID());
-    }
-    while (missingNodes.Size() > 0)
-    {
-        iterNode = missingNodes.Back();
-        missingNodes.Pop();
-        ik_node_t* ikChildNode = CreateIKNode(iterNode);
-        ik_node_add_child(ikNode, ikChildNode);
-        ikNode = ikChildNode;
-    }
-
-    // The tip of the tree is the effector. The solver library has ownership of
-    // the effector object, but our IKEffector object also needs to know about
-    // 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->SetIKSolver(this);
-    effectorList_.Push(effector);
-
-    MarkChainsNeedUpdating();
-
-    return true;
-}
-
-// ----------------------------------------------------------------------------
-void IKSolver::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
-{
-    using namespace ComponentAdded;
-    (void)eventType;
 
     if (solver_->tree == NULL)
         return;
 
-    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+    /*
+     * Update tree if component is an effector and is part of our subtree.
+     */
+    if (component->GetType() == IKEffector::GetTypeStatic())
+    {
+        // Not interested in components that won't be part of our
+        if (ComponentIsInOurSubtree(component) == false)
+            return;
 
-    // Handle case when the added component is an effector
-    IKEffector* maybeEffector = static_cast<IKEffector*>(node->GetComponent<IKEffector>());
-    if (maybeEffector != NULL && maybeEffector->GetType() == IKEffector::GetTypeStatic())
-        BuildTreeToEffector(maybeEffector);
+        BuildTreeToEffector(static_cast<IKEffector*>(component));
+        effectorList_.Push(static_cast<IKEffector*>(component));
+        return;
+    }
 
-    IKConstraint* constraint = static_cast<IKConstraint*>(node->GetComponent<IKConstraint>());
-    if (constraint != NULL)
+    if (component->GetType() == IKConstraint::GetTypeStatic())
     {
-        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
-        if (ikNode != NULL)
-        {
-            constraint->SetIKNode(ikNode);
-            constraintList_.Push(constraint);
-        }
+        if (ComponentIsInOurSubtree(component) == false)
+            return;
+
+        constraintList_.Push(static_cast<IKConstraint*>(component));
     }
 }
 
@@ -485,32 +596,53 @@ void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventDat
     Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
     Component* component = static_cast<Component*>(eventData[P_COMPONENT].GetPtr());
 
+    /*
+     * When a solver gets added into the scene, any parent solver's tree will
+     * be invalidated. We need to find all parent solvers (by iterating up the
+     * tree) and mark them as such.
+     */
+    if (component->GetType() == IKSolver::GetTypeStatic())
+    {
+        for (Node* iterNode = node; iterNode != NULL; iterNode = iterNode->GetParent())
+        {
+            IKSolver* parentSolver = iterNode->GetComponent<IKSolver>();
+            if (parentSolver != NULL)
+                parentSolver->MarkTreeNeedsRebuild();
+
+        }
+
+        return; // No need to continue processing effectors or constraints
+    }
+
     // If an effector was removed, the tree will have to be rebuilt.
     if (component->GetType() == IKEffector::GetTypeStatic())
     {
-        IKEffector* effector = static_cast<IKEffector*>(component);
-        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
-        if (ikNode == NULL) // The effector is in an unrelated branch of the tree, abort.
+        if (ComponentIsInOurSubtree(component) == false)
             return;
 
+        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+        assert(ikNode != NULL);
+
         ik_node_destroy_effector(ikNode);
-        effector->SetIKEffector(NULL);
-        effectorList_.RemoveSwap(effector);
+        static_cast<IKEffector*>(component)->SetIKEffectorNode(NULL);
+        effectorList_.RemoveSwap(static_cast<IKEffector*>(component));
 
         ApplyOriginalPoseToScene();
-        MarkChainsNeedUpdating();
+        MarkTreeNeedsRebuild();
+        return;
     }
 
     // Remove the ikNode* reference the IKConstraint was holding
     if (component->GetType() == IKConstraint::GetTypeStatic())
     {
-        IKConstraint* constraint = static_cast<IKConstraint*>(component);
-        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
-        if (ikNode == NULL) // The constraint is in an unrelated branch of the tree, abort.
+        if (ComponentIsInOurSubtree(component) == false)
             return;
 
-        constraint->SetIKNode(NULL);  // NOTE: Should restore default settings to the node
-        constraintList_.RemoveSwap(constraint);
+        ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+        assert(ikNode != NULL);
+
+        static_cast<IKConstraint*>(component)->SetIKConstraintNode(NULL);
+        constraintList_.RemoveSwap(static_cast<IKConstraint*>(component));
     }
 }
 
@@ -528,19 +660,20 @@ void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
     node->GetComponents<IKEffector>(effectors, true);
     for (PODVector<IKEffector*>::ConstIterator it = effectors.Begin(); it != effectors.End(); ++it)
     {
-        // A direct path to the effector may be blocked by another IKSolver.
-        // If this is so, simply ignore this component and continue. The child
-        // solver will take good care of it.
-        if (BuildTreeToEffector(*it) == false)
+        if (ComponentIsInOurSubtree(*it) == false)
             continue;
 
-        effectorList_.Push((*it));
+        BuildTreeToEffector(*it);
+        effectorList_.Push(*it);
     }
 
     PODVector<IKConstraint*> constraints;
     node->GetComponents<IKConstraint>(constraints, true);
     for (PODVector<IKConstraint*>::ConstIterator it = constraints.Begin(); it != constraints.End(); ++it)
     {
+        if (ComponentIsInOurSubtree(*it) == false)
+            continue;
+
         constraintList_.Push(*it);
     }
 }
@@ -560,7 +693,7 @@ void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
     node->GetComponents<IKEffector>(effectors, true);
     for (PODVector<IKEffector*>::ConstIterator it = effectors.Begin(); it != effectors.End(); ++it)
     {
-        (*it)->SetIKEffector(NULL);
+        (*it)->SetIKEffectorNode(NULL);
         effectorList_.RemoveSwap(*it);
     }
 

+ 19 - 4
Source/Urho3D/IK/IKSolver.h

@@ -238,7 +238,7 @@ public:
      * adding or removing effectors, etc.).
      * @note This gets called  automatically for you in Solve().
      */
-    void RebuildChains();
+    void RebuildChainTrees();
 
     /*!
      * @brief Unusual, but if you have a tree with translational motions such
@@ -293,15 +293,20 @@ public:
 private:
     friend class IKEffector;
 
-    /// Causes the solver tree to be rebuilt before solving the next time. Intended to be used by 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. Urho3D'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* CreateIKNode(const Node* node);
+    ik_node_t* CreateIKNodeFromUrhoNode(const Node* node);
 
     /// Destroys the solver's tree
     void DestroyTree();
@@ -309,6 +314,14 @@ private:
     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);
@@ -339,7 +352,9 @@ private:
     ik_solver_t* solver_;
     Algorithm algorithm_;
     unsigned features_;
-    bool chainsAreDirty_;
+    bool chainTreesNeedUpdating_;
+    bool treeNeedsRebuild;
+    bool solverTreeValid_;
 };
 
 } // namespace Urho3D

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/IK/IKSolver.pkg

@@ -23,7 +23,7 @@ class IKSolver : public Component
     bool GetFeature(Feature feature) const;
     void SetFeature(Feature feature, bool enable);
 
-    void RebuildData();
+    void RebuildChainTrees();
     void RecalculateSegmentLengths();
     void CalculateJointRotations();
     void Solve();