Browse Source

Adding IK library to Urho3D, enable/disable it with -DURHO3D_IK

TheComet 8 years ago
parent
commit
cf30fb98c0

+ 3 - 0
CMake/Modules/UrhoCommon.cmake

@@ -104,6 +104,7 @@ option (URHO3D_C++11 "Enable C++11 standard")
 cmake_dependent_option (IOS "Setup build for iOS platform" FALSE "XCODE" FALSE)
 cmake_dependent_option (IOS "Setup build for iOS platform" FALSE "XCODE" FALSE)
 cmake_dependent_option (URHO3D_64BIT "Enable 64-bit build, the default is set based on the native ABI of the chosen compiler toolchain" "${NATIVE_64BIT}" "NOT MSVC AND NOT ANDROID AND NOT (ARM AND NOT IOS) AND NOT WEB AND NOT POWERPC" "${NATIVE_64BIT}")     # Intentionally only enable the option for iOS but not for tvOS as the latter is 64-bit only
 cmake_dependent_option (URHO3D_64BIT "Enable 64-bit build, the default is set based on the native ABI of the chosen compiler toolchain" "${NATIVE_64BIT}" "NOT MSVC AND NOT ANDROID AND NOT (ARM AND NOT IOS) AND NOT WEB AND NOT POWERPC" "${NATIVE_64BIT}")     # Intentionally only enable the option for iOS but not for tvOS as the latter is 64-bit only
 option (URHO3D_ANGELSCRIPT "Enable AngelScript scripting support" TRUE)
 option (URHO3D_ANGELSCRIPT "Enable AngelScript scripting support" TRUE)
+option (URHO3D_IK "Enable inverse kinematics support" TRUE)
 option (URHO3D_LUA "Enable additional Lua scripting support" TRUE)
 option (URHO3D_LUA "Enable additional Lua scripting support" TRUE)
 option (URHO3D_NAVIGATION "Enable navigation support" TRUE)
 option (URHO3D_NAVIGATION "Enable navigation support" TRUE)
 # Urho's Network subsystem depends on kNet library which uses C++ exceptions feature
 # Urho's Network subsystem depends on kNet library which uses C++ exceptions feature
@@ -345,6 +346,7 @@ if (URHO3D_CLANG_TOOLS)
             URHO3D_ANGELSCRIPT
             URHO3D_ANGELSCRIPT
             URHO3D_DATABASE_SQLITE
             URHO3D_DATABASE_SQLITE
             URHO3D_FILEWATCHER
             URHO3D_FILEWATCHER
+            URHO3D_IK
             URHO3D_LOGGING
             URHO3D_LOGGING
             URHO3D_LUA
             URHO3D_LUA
             URHO3D_NAVIGATION
             URHO3D_NAVIGATION
@@ -395,6 +397,7 @@ foreach (OPT
         URHO3D_ANGELSCRIPT
         URHO3D_ANGELSCRIPT
         URHO3D_DATABASE
         URHO3D_DATABASE
         URHO3D_FILEWATCHER
         URHO3D_FILEWATCHER
+        URHO3D_IK
         URHO3D_LOGGING
         URHO3D_LOGGING
         URHO3D_LUA
         URHO3D_LUA
         URHO3D_MINIDUMPS
         URHO3D_MINIDUMPS

+ 5 - 0
Source/CMakeLists.txt

@@ -88,6 +88,11 @@ if (URHO3D_DATABASE_SQLITE)
     add_subdirectory (ThirdParty/SQLite)
     add_subdirectory (ThirdParty/SQLite)
 endif ()
 endif ()
 
 
+if (URHO3D_IK)
+    # IK lib provides its own Urho3D specific CMakeLists.txt
+    add_subdirectory (ThirdParty/ik/cmake/Urho3D)
+endif ()
+
 if (URHO3D_NAVIGATION)
 if (URHO3D_NAVIGATION)
     add_subdirectory (ThirdParty/Detour)
     add_subdirectory (ThirdParty/Detour)
     add_subdirectory (ThirdParty/DetourCrowd)
     add_subdirectory (ThirdParty/DetourCrowd)

+ 2 - 2
Source/Urho3D/CMakeLists.txt

@@ -97,7 +97,7 @@ if (WIN32)
 endif ()
 endif ()
 
 
 # Define source files
 # Define source files
-foreach (DIR Navigation Network Physics Urho2D)
+foreach (DIR IK Navigation Network Physics Urho2D)
     string (TOUPPER URHO3D_${DIR} OPT)
     string (TOUPPER URHO3D_${DIR} OPT)
     if (NOT ${OPT})
     if (NOT ${OPT})
         list (APPEND EXCLUDED_SOURCE_DIRS ${DIR})
         list (APPEND EXCLUDED_SOURCE_DIRS ${DIR})
@@ -446,7 +446,7 @@ install_header_files (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION ${DEST_
 # Generate the include-all-headers header file even though we do not encourage Urho3D library users to use it
 # Generate the include-all-headers header file even though we do not encourage Urho3D library users to use it
 list (SORT URHO_HEADERS)
 list (SORT URHO_HEADERS)
 list (REVERSE URHO_HEADERS)
 list (REVERSE URHO_HEADERS)
-set (OPTIONAL_SUBS AngelScript Database Lua Navigation Network Physics Urho2D)
+set (OPTIONAL_SUBS AngelScript Database IK Lua Navigation Network Physics Urho2D)
 foreach (SUB ${OPTIONAL_SUBS})
 foreach (SUB ${OPTIONAL_SUBS})
     if (URHO_HEADERS MATCHES "(include/Urho3D/${SUB}[^;]+)")
     if (URHO_HEADERS MATCHES "(include/Urho3D/${SUB}[^;]+)")
         list (FIND URHO_HEADERS ${CMAKE_MATCH_1} FOUND_INDEX)
         list (FIND URHO_HEADERS ${CMAKE_MATCH_1} FOUND_INDEX)

+ 41 - 1
Source/Urho3D/Core/Context.cpp

@@ -32,11 +32,20 @@
 #include <SDL/SDL.h>
 #include <SDL/SDL.h>
 #endif
 #endif
 
 
+#ifdef URHO3D_IK
+#include <ik/memory.h>
+#include <ik/log.h>
+#endif
+
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
 // Keeps track of how many times SDL was initialised so we know when to call SDL_Quit().
 // Keeps track of how many times SDL was initialised so we know when to call SDL_Quit().
 static int sdlInitCounter = 0;
 static int sdlInitCounter = 0;
+// Keeps track of how many times IK was initialised
+#ifdef URHO3D_IK
+static int ikInitCounter = 0;
+#endif
 
 
 void EventReceiverGroup::BeginSendEvent()
 void EventReceiverGroup::BeginSendEvent()
 {
 {
@@ -225,7 +234,7 @@ bool Context::RequireSDL(unsigned int sdlFlags)
     ++sdlInitCounter;
     ++sdlInitCounter;
 
 
     // Need to call SDL_Init() at least once before SDL_InitSubsystem()
     // Need to call SDL_Init() at least once before SDL_InitSubsystem()
-    if (sdlInitCounter == 0)
+    if (sdlInitCounter == 1)
     {
     {
         URHO3D_LOGDEBUG("Initialising SDL");
         URHO3D_LOGDEBUG("Initialising SDL");
         if (SDL_Init(0) != 0)
         if (SDL_Init(0) != 0)
@@ -266,6 +275,37 @@ void Context::ReleaseSDL()
 #endif
 #endif
 }
 }
 
 
+#ifdef URHO3D_IK
+void Context::RequireIK()
+{
+    // Always increment, the caller must match with ReleaseSDL(), regardless of
+    // what happens.
+    ++ikInitCounter;
+
+    if (ikInitCounter == 1)
+    {
+        URHO3D_LOGDEBUG("Initialising Inverse Kinematics library");
+        ik_memory_init();
+        ik_log_init(IK_LOG_NONE);
+    }
+}
+
+void Context::ReleaseIK()
+{
+    --ikInitCounter;
+
+    if (ikInitCounter == 0)
+    {
+        URHO3D_LOGDEBUG("De-initialising Inverse Kinematics library");
+        ik_log_deinit();
+        ik_memory_deinit();
+    }
+
+    if (ikInitCounter < 0)
+        URHO3D_LOGERROR("Too many calls to Context::ReleaseIK()");
+}
+#endif
+
 void Context::CopyBaseAttributes(StringHash baseType, StringHash derivedType)
 void Context::CopyBaseAttributes(StringHash baseType, StringHash derivedType)
 {
 {
     // Prevent endless loop if mistakenly copying attributes from same class as derived
     // Prevent endless loop if mistakenly copying attributes from same class as derived

+ 6 - 0
Source/Urho3D/Core/Context.h

@@ -100,6 +100,12 @@ public:
     bool RequireSDL(unsigned int sdlFlags);
     bool RequireSDL(unsigned int sdlFlags);
     /// Indicate that you are done with using SDL. Must be called after using RequireSDL().
     /// Indicate that you are done with using SDL. Must be called after using RequireSDL().
     void ReleaseSDL();
     void ReleaseSDL();
+#ifdef URHO3D_IK
+    /// Initialises the IK library, if not already. This call must be matched with ReleaseIK() when the IK library is no longer required.
+    void RequireIK();
+    /// Indicate that you are done with using the IK library.
+    void ReleaseIK();
+#endif
 
 
     /// Copy base class attributes to derived class.
     /// Copy base class attributes to derived class.
     void CopyBaseAttributes(StringHash baseType, StringHash derivedType);
     void CopyBaseAttributes(StringHash baseType, StringHash derivedType);

+ 7 - 0
Source/Urho3D/Engine/Engine.cpp

@@ -38,6 +38,9 @@
 #include "../IO/FileSystem.h"
 #include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/PackageFile.h"
 #include "../IO/PackageFile.h"
+#ifdef URHO3D_IK
+#include "../IK/IK.h"
+#endif
 #ifdef URHO3D_NAVIGATION
 #ifdef URHO3D_NAVIGATION
 #include "../Navigation/NavigationMesh.h"
 #include "../Navigation/NavigationMesh.h"
 #endif
 #endif
@@ -139,6 +142,10 @@ Engine::Engine(Context* context) :
     // Register object factories for libraries which are not automatically registered along with subsystem creation
     // Register object factories for libraries which are not automatically registered along with subsystem creation
     RegisterSceneLibrary(context_);
     RegisterSceneLibrary(context_);
 
 
+#ifdef URHO3D_IK
+    RegisterIKLibrary(context_);
+#endif
+
 #ifdef URHO3D_PHYSICS
 #ifdef URHO3D_PHYSICS
     RegisterPhysicsLibrary(context_);
     RegisterPhysicsLibrary(context_);
 #endif
 #endif

+ 42 - 0
Source/Urho3D/IK/IK.cpp

@@ -0,0 +1,42 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "IK.h"
+#include "IKConstraint.h"
+#include "IKEffector.h"
+#include "IKSolver.h"
+
+
+namespace Urho3D
+{
+
+const char* IK_CATEGORY = "Inverse Kinematics";
+
+// ----------------------------------------------------------------------------
+void RegisterIKLibrary(Context* context)
+{
+    IKConstraint::RegisterObject(context);
+    IKEffector::RegisterObject(context);
+    IKSolver::RegisterObject(context);
+}
+
+} // namespace Urho3D

+ 55 - 0
Source/Urho3D/IK/IK.h

@@ -0,0 +1,55 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/*
+ * TODO IK todo
+ *  - Initial pose is not saved when saving scene in editor. Instead, the
+ *    solved state of the chain(s) are saved.
+ *  - Target angle in addition to target position -> use weighted angles
+ *    approach
+ *  - Add an IKEffector weight parameter, so the user can specify
+ *    (between [0..1]) how much influence the solved chains have.
+ *  - Apply angles from chain objects back to scene nodes (currently only
+ *    translations are applied).
+ *  - Add support for enabling/disabling initial pose to support animated
+ *    models as well as scene nodes.
+ *  - Pole targets?
+ *  - Add support for manually updating initial pose.
+ *  - Add support for having the initial pose update every time it's solved
+ *    -> incremental solving so to speak.
+ *  - Apply bullet constraints to joints.
+ *  - Script bindings.
+ *  - Optimise.
+ *  - Profile.
+ */
+
+namespace Urho3D
+{
+
+class Context;
+
+/*!
+ * Registers all IK systems to the specified context.
+ */
+void RegisterIKLibrary(Context* context);
+
+} // namespace Urho3D

+ 51 - 0
Source/Urho3D/IK/IKConstraint.cpp

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "IK.h"
+#include "IKConstraint.h"
+#include "../Core/Context.h"
+#include "../Scene/Node.h"
+#include "../Scene/SceneEvents.h"
+
+namespace Urho3D
+{
+
+extern const char* IK_CATEGORY;
+
+// ----------------------------------------------------------------------------
+IKConstraint::IKConstraint(Context* context) :
+    Component(context)
+{
+}
+
+// ----------------------------------------------------------------------------
+IKConstraint::~IKConstraint()
+{
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKConstraint>(IK_CATEGORY);
+}
+
+} // namespace Urho3D

+ 57 - 0
Source/Urho3D/IK/IKConstraint.h

@@ -0,0 +1,57 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+
+namespace Urho3D
+{
+
+class Context;
+class Node;
+
+class IKConstraint : public Component
+{
+    URHO3D_OBJECT(IKConstraint, Component)
+
+public:
+
+    /*!
+     * Constructs a new IK constraint.
+     */
+    IKConstraint(Context* context);
+
+    /*!
+     * Destructs he IK constraint.
+     */
+    virtual ~IKConstraint();
+
+    /*!
+     * Registers this class as an object factory.
+     */
+    static void RegisterObject(Context* context);
+
+private:
+};
+
+} // namespace Urho3D

+ 201 - 0
Source/Urho3D/IK/IKEffector.cpp

@@ -0,0 +1,201 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../IK/IK.h"
+#include "../IK/IKEffector.h"
+#include "../IK/IKSolver.h"
+#include "../IK/IKEvents.h"
+#include "../Core/Context.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../Scene/Node.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+#include <ik/effector.h>
+#include <ik/log.h>
+
+namespace Urho3D
+{
+
+extern const char* IK_CATEGORY;
+
+// ----------------------------------------------------------------------------
+IKEffector::IKEffector(Context* context) :
+    Component(context),
+    ikEffector_(NULL),
+    chainLength_(0)
+{
+}
+
+// ----------------------------------------------------------------------------
+IKEffector::~IKEffector()
+{
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKEffector>(IK_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Chain Length", GetChainLength, SetChainLength, unsigned, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Target Node", GetTargetName, SetTargetName, String, String::EMPTY, AM_DEFAULT);
+}
+
+// ----------------------------------------------------------------------------
+Node* IKEffector::GetTargetNode() const
+{
+    return targetNode_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetNode(Node* targetNode)
+{
+    using namespace IKEffectorTargetChanged;
+
+    VariantMap eventData;
+    eventData[P_EFFECTORNODE] = node_;       // The effector node that has changed targets
+    eventData[P_TARGETNODE]   = targetNode_; // The new target node. NOTE: Can be NULL (means no target)
+    SendEvent(E_IKEFFECTORTARGETCHANGED, eventData);
+
+    // Finally change the target node
+    targetNode_ = targetNode;
+    targetName_ = targetNode != NULL ? targetNode->GetName() : "";
+}
+
+// ----------------------------------------------------------------------------
+const String& IKEffector::GetTargetName() const
+{
+    return targetName_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetName(const String& nodeName)
+{
+    targetName_ = nodeName;
+    targetNode_ = NULL;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetPosition(const Vector3& targetPosition)
+{
+    targetPosition_ = targetPosition;
+    if (ikEffector_ != NULL)
+    {
+        ikEffector_->target_position.v.x = targetPosition.x_;
+        ikEffector_->target_position.v.y = targetPosition.y_;
+        ikEffector_->target_position.v.z = targetPosition.z_;
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::UpdateTargetNodePosition()
+{
+    if (targetNode_ == NULL)
+    {
+        SetTargetNode(node_->GetScene()->GetChild(targetName_, true));
+        if (targetNode_ == NULL)
+            return;
+    }
+
+    SetTargetPosition(targetNode_->GetWorldPosition());
+}
+
+// ----------------------------------------------------------------------------
+unsigned int IKEffector::GetChainLength() const
+{
+    return chainLength_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetChainLength(unsigned chainLength)
+{
+    chainLength_ = chainLength;
+    if (ikEffector_ != NULL)
+    {
+        ikEffector_->chain_length = chainLength;
+        solver_->MarkSolverTreeDirty();
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetEffector(ik_effector_t* effector)
+{
+    ikEffector_ = effector;
+    if (effector)
+    {
+        effector->chain_length = chainLength_;
+        effector->target_position.v.x = targetPosition_.x_;
+        effector->target_position.v.y = targetPosition_.y_;
+        effector->target_position.v.z = targetPosition_.z_;
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetSolver(IKSolver* solver)
+{
+    solver_ = solver;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    // Calculate average length of all segments so we can determine the radius
+    // of the debug spheres to draw
+    int chainLength = chainLength_ == 0 ? -1 : chainLength_;
+    Node* a = node_;
+    float averageLength = 0.0f;
+    unsigned numberOfSegments = 0;
+    while (a && a != a->GetScene() && chainLength-- != 0)
+    {
+        averageLength += a->GetPosition().Length();
+        ++numberOfSegments;
+        a = a->GetParent();
+    }
+    averageLength /= numberOfSegments;
+
+    // connect all chained nodes together with lines
+    chainLength = chainLength_ == 0 ? -1 : chainLength_;
+    a = node_;
+    Node* b = a->GetParent();
+    debug->AddSphere(
+        Sphere(a->GetWorldPosition(), averageLength * 0.1),
+        Color::YELLOW,
+        depthTest
+    );
+    while (b && b != b->GetScene() && chainLength-- != 0)
+    {
+        debug->AddLine(
+            a->GetWorldPosition(),
+            b->GetWorldPosition(),
+            Color::WHITE,
+            depthTest
+        );
+        debug->AddSphere(
+            Sphere(b->GetWorldPosition(), averageLength * 0.1),
+            Color::YELLOW,
+            depthTest
+        );
+        a = b;
+        b = b->GetParent();
+    }
+}
+
+} // namespace Urho3D

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

@@ -0,0 +1,104 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+#include "../Scene/Scene.h"
+
+struct ik_effector_t;
+
+namespace Urho3D
+{
+
+class Context;
+class IKSolver;
+
+class IKEffector : public Component
+{
+    URHO3D_OBJECT(IKEffector, Component)
+
+public:
+
+    /// Constructs a new IK effector.
+    IKEffector(Context* context);
+
+    /// Destructs he IK effector.
+    virtual ~IKEffector();
+
+    /*!
+     * Registers this class as an object factory.
+     */
+    static void RegisterObject(Context* context);
+
+    /// Retrieves the node that is being used as a target. Can be NULL.
+    Node* GetTargetNode() const;
+
+    /*!
+     * @brief The position of the target node provides the target position of
+     * the effector node.
+     *
+     * The IK chain is solved such that the node to which this component is
+     * attached to will try to move to the position of the target node.
+     * @param targetNode A scene node that acts as a target. Specifying NULL
+     * will erase the target and cause the solver to ignore this chain.
+     * @note You will get very strange behaviour if you specify a target node
+     * that is part the IK chain being solved for (circular dependency). Don't
+     * do that.
+     */
+    void SetTargetNode(Node* targetNode);
+
+    /*!
+     * @brief Retrieves the name of the target node. The node doesn't
+     * necessarily have to exist in the scene graph.
+     */
+    const String& GetTargetName() const;
+
+    /*!
+     * @brief Sets the name of the target node. The node doesn't necessarily
+     * have to exist in the scene graph. When a node is created that matches
+     * this name, it is selected as the target.
+     */
+    void SetTargetName(const String& nodeName);
+
+    void SetTargetPosition(const Vector3& targetPosition);
+
+    void UpdateTargetNodePosition();
+
+    unsigned GetChainLength() const;
+    void SetChainLength(unsigned chainLength);
+
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+    void SetSolver(IKSolver* solver);
+    void SetEffector(ik_effector_t* effector);
+
+private:
+    String targetName_;
+    Vector3 targetPosition_;
+    WeakPtr<Node> targetNode_;
+    WeakPtr<IKSolver> solver_;
+    ik_effector_t* ikEffector_;
+    unsigned chainLength_;
+};
+
+} // namespace Urho3D

+ 37 - 0
Source/Urho3D/IK/IKEvents.h

@@ -0,0 +1,37 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Core/Object.h"
+
+
+namespace Urho3D
+{
+
+URHO3D_EVENT(E_IKEFFECTORTARGETCHANGED, IKEffectorTargetChanged)
+{
+    URHO3D_PARAM(P_EFFECTORNODE, EffectorNode);      // (Node*) The effector node that has changed targets
+    URHO3D_PARAM(P_TARGETNODE,   TargetNode);        // (Node*) The new target node. NOTE: Can be NULL (means no target)
+}
+
+}

+ 375 - 0
Source/Urho3D/IK/IKSolver.cpp

@@ -0,0 +1,375 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../IK/IKSolver.h"
+#include "../IK/IK.h"
+#include "../IK/IKEvents.h"
+
+#include "../Core/Context.h"
+#include "../Core/Profiler.h"
+#include "../IO/Log.h"
+#include "../Scene/Node.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+#include "../Graphics/Animation.h"
+#include "../Graphics/AnimationState.h"
+
+#include <ik/solver.h>
+#include <ik/node.h>
+#include <ik/effector.h>
+#include <ik/log.h>
+
+namespace Urho3D
+{
+
+extern const char* IK_CATEGORY;;
+extern const char* SUBSYSTEM_CATEGORY;
+
+static void HandleIKLog(const char* msg)
+{
+    URHO3D_LOGINFOF("[IK] %s", msg);
+}
+static vec3_t Vec3Urho2IK(const Vector3& urho)
+{
+    vec3_t ret;
+    ret.v.x = urho.x_;
+    ret.v.y = urho.y_;
+    ret.v.z = urho.z_;
+    return ret;
+}
+static Vector3 Vec3IK2Urho(const vec3_t* ik)
+{
+    return Vector3(ik->v.x, ik->v.y, ik->v.z);
+}
+static ik_node_t* CreateIKNode(const Node* node)
+{
+    ik_node_t* ikNode = ik_node_create(node->GetID());
+    ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
+    ikNode->user_data = (void*)node;
+    return ikNode;
+}
+static bool ChildrenHaveEffector(const Node* node)
+{
+    if (node->HasComponent<IKEffector>())
+        return true;
+
+    const Vector<SharedPtr<Node> >& children = node->GetChildren();
+    for (Vector<SharedPtr<Node> >::ConstIterator it = children.Begin(); it != children.End(); ++it)
+    {
+        if (ChildrenHaveEffector(it->Get()))
+            return true;
+    }
+}
+static void ApplyResultCallback(ik_node_t* ikNode, vec3_t position, quat_t rotation)
+{
+    Node* node = (Node*)ikNode->user_data;
+    node->SetWorldPosition(Vec3IK2Urho(&ikNode->solved_position));
+    node->SetWorldRotation(Quaternion(
+        rotation.q.w,
+        rotation.q.x,
+        rotation.q.y,
+        rotation.q.z
+    ));
+}
+
+// ----------------------------------------------------------------------------
+IKSolver::IKSolver(Context* context) :
+    Component(context),
+    solver_(NULL),
+    solverTreeNeedsRebuild_(false)
+{
+    context_->RequireIK();
+    solver_ = ik_solver_create(SOLVER_FABRIK);
+    solver_->apply_result = ApplyResultCallback;
+    solver_->build_mode = SOLVER_EXCLUDE_ROOT;
+    ik_log_register_listener(HandleIKLog);
+
+    SubscribeToEvent(E_COMPONENTADDED,              URHO3D_HANDLER(IKSolver, HandleComponentAdded));
+    SubscribeToEvent(E_COMPONENTREMOVED,            URHO3D_HANDLER(IKSolver, HandleComponentRemoved));
+    SubscribeToEvent(E_NODEADDED,                   URHO3D_HANDLER(IKSolver, HandleNodeAdded));
+    SubscribeToEvent(E_NODEREMOVED,                 URHO3D_HANDLER(IKSolver, HandleNodeRemoved));
+    SubscribeToEvent(E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
+}
+
+// ----------------------------------------------------------------------------
+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)->SetEffector(NULL);
+
+    ik_log_unregister_listener(HandleIKLog);
+    ik_solver_destroy(solver_);
+    context_->ReleaseIK();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKSolver>(SUBSYSTEM_CATEGORY);
+
+    static const char* algorithmNames[] = {
+        "FABRIK",
+        /* not implemented
+        "Jacobian Inverse",
+        "Jacobian Transpose",*/
+        NULL
+    };
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, SOLVER_FABRIK, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Iterations", GetMaximumIterations, SetMaximumIterations, unsigned, 1000, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Convergence Tolerance", GetTolerance, SetTolerance, float, 0.1f, AM_DEFAULT);
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetAlgorithm(IKSolver::Algorithm algorithm)
+{
+    URHO3D_LOGERROR("Failed to set algorithm: Not implemented");
+}
+
+// ----------------------------------------------------------------------------
+IKSolver::Algorithm IKSolver::GetAlgorithm() const
+{
+    return FABRIK;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetMaximumIterations(unsigned iterations)
+{
+    solver_->max_iterations = iterations;
+}
+
+// ----------------------------------------------------------------------------
+unsigned IKSolver::GetMaximumIterations() const
+{
+    return solver_->max_iterations;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetTolerance(float tolerance)
+{
+    if(tolerance < M_EPSILON)
+        tolerance = M_EPSILON;
+    solver_->tolerance = tolerance;
+}
+
+// ----------------------------------------------------------------------------
+float IKSolver::GetTolerance() const
+{
+    return solver_->tolerance;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::MarkSolverTreeDirty()
+{
+    solverTreeNeedsRebuild_ = true;
+}
+
+// ----------------------------------------------------------------------------
+/*
+ * This next section maintains the internal list of effector nodes. Whenever
+ * nodes are deleted or added to the scene, or whenever components are added
+ * or removed from nodes, we must check to see which of those nodes are/were
+ * IK effector nodes and update our internal list accordingly.
+ *
+ * Unfortunately, E_COMPONENTREMOVED and E_COMPONENTADDED do not fire when a
+ * parent node is removed/added containing child effector nodes, so we must
+ * also monitor E_NODEREMOVED AND E_NODEADDED.
+ */
+
+// ----------------------------------------------------------------------------
+void IKSolver::OnSceneSet(Scene* scene)
+{
+    if (scene == NULL)
+    {
+        ik_solver_destroy_tree(solver_);
+    }
+    else
+    {
+        RebuildTree();
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RebuildTree()
+{
+    Node* scene = GetScene();
+    assert(scene != NULL);
+
+    ik_node_t* ikRoot = ik_node_create(scene->GetID());
+    ik_solver_set_tree(solver_, ikRoot);
+
+    PODVector<Node*> effectorNodes;
+    scene->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
+    for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
+    {
+        BuildTreeToEffector(*it);
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::BuildTreeToEffector(const Node* node)
+{
+    // Check if the component that was added is an IK effector. If not, then it
+    // does not concern us.
+    IKEffector* effector = static_cast<IKEffector*>(node->GetComponent<IKEffector>());
+    if(effector == NULL || effector->GetType() != IKEffector::GetTypeStatic())
+        return;
+
+    // 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 = node;
+    ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+    while (ikNode == NULL)
+    {
+        missingNodes.Push(iterNode);
+        iterNode = iterNode->GetParent();
+        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->SetEffector(ikEffector);           // "weak" reference to effector
+    effector->SetSolver(this);
+    effectorList_.Push(effector);
+
+    MarkSolverTreeDirty();
+}
+
+// ----------------------------------------------------------------------------
+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());
+    BuildTreeToEffector(node);
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventData)
+{
+    using namespace ComponentRemoved;
+    (void)eventType;
+
+    if(solver_->tree == NULL)
+        return;
+
+    // Check if the component that was removed was an IK effector. If not,
+    // then it does not concern us.
+    IKEffector* effector = static_cast<IKEffector*>(eventData[P_COMPONENT].GetPtr());
+    if(effector->GetType() != IKEffector::GetTypeStatic())
+        return;
+
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+    ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+    ik_node_destroy_effector(ikNode);
+    effector->SetEffector(NULL);
+    effectorList_.Remove(effector); // TODO RemoveSwap()
+
+    MarkSolverTreeDirty();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
+{
+    using namespace NodeAdded;
+    (void)eventType;
+
+    if(solver_->tree == NULL)
+        return;
+
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+
+    PODVector<Node*> effectorNodes;
+    node->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
+    for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
+    {
+        BuildTreeToEffector(*it);
+        effectorList_.Push((*it)->GetComponent<IKEffector>());
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
+{
+    using namespace NodeRemoved;
+    (void)eventType;
+
+    if(solver_->tree == NULL)
+        return;
+
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+
+    PODVector<Node*> effectorNodes;
+    node->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
+    for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
+    {
+        effectorList_.Remove((*it)->GetComponent<IKEffector>());
+    }
+
+    ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+    if(ikNode != NULL)
+        ik_node_destroy(ikNode);
+
+    MarkSolverTreeDirty();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData)
+{
+    URHO3D_PROFILE(SolveIK);
+
+    if (solverTreeNeedsRebuild_)
+    {
+        ik_solver_rebuild_data(solver_);
+        solverTreeNeedsRebuild_ = false;
+    }
+
+    for(PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
+    {
+        (*it)->UpdateTargetNodePosition();
+    }
+
+    ik_solver_solve(solver_);
+}
+
+} // namespace Urho3D

+ 134 - 0
Source/Urho3D/IK/IKSolver.h

@@ -0,0 +1,134 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../IK/IKEffector.h"
+#include "../Scene/Component.h"
+
+struct ik_solver_t;
+struct ik_node_t;
+
+namespace Urho3D
+{
+class AnimationState;
+
+/*!
+ * @brief Marks the root or "beginning" of an IK chain or multiple IK chains.
+ * The solving algorithm can be set along with other solver related parameters.
+ * The IK problem is solved starting from the node this component is attached
+ * to and ending at all nodes that have an IKEffector component attached.
+ */
+class IKSolver : public Component
+{
+    URHO3D_OBJECT(IKSolver, Component)
+
+public:
+
+    enum Algorithm
+    {
+        FABRIK,
+        JACOBIAN_INVERSE,
+        JACOBIAN_TRANSPOSE
+    };
+
+    /// Construct an IK root component.
+    IKSolver(Context* context);
+    /// Default destructor.
+    virtual ~IKSolver();
+    /// Registers this class to the context.
+    static void RegisterObject(Context* context);
+
+    /*!
+     * @brief Selects the solver algorithm. Default is FABRIK.
+     *
+     * The currently supported solvers are listed below.
+     *   + **FABRIK**: This is a fairly new and highly efficient inverse
+     *     kinematic solving algorithm. It requires the least iterations to
+     *     reach its goal, it does not suffer from singularities (nearly no
+     *     violent snapping about), and it always converges.
+     */
+    void SetAlgorithm(Algorithm algorithm);
+    Algorithm GetAlgorithm() const;
+
+    /*!
+     * @brief Sets the maximum number of iterations the solver is allowed to
+     * perform before applying the result.
+     *
+     * Depending on the algorithm, you may want higher or lower values.
+     * FABRIK looks decent after only 10 iterations, whereas Jacobian based
+     * methods often require more than a 100.
+     *
+     * The default value is 50.
+     *
+     * @note Most algorithms have a convergence criteria at which the solver
+     * will stop iterating, so most of the time the maximum number of
+     * iterations isn't even reached.
+     *
+     * @param iterations Number of iterations. Must be greater than 0. Higher
+     * values yield more accurate results, but at the cost of performance.
+     */
+    void SetMaximumIterations(unsigned iterations);
+
+    /*!
+     * @brief Returns the configured maximum number of iterations.
+     */
+    unsigned GetMaximumIterations() const;
+
+    /*!
+     * @brief Sets the distance at which the effector is "close enough" to the
+     * target node, at which point the algorithm will stop iterating.
+     *
+     * @param tolerance The distance to set. Smaller values yield more accurate
+     * results, but at the cost of more iterations. Generally you'll want to
+     * specify a number that is about 1/100th to 1/1000th of the total size of
+     * the IK chain, e.g. if your human character has a leg that is 1 Urho3D
+     * unit long, a good starting tolerance would be 0.01.
+     */
+    void SetTolerance(float tolerance);
+
+    /*!
+     * @brief Returns the configured tolerance.
+     */
+    float GetTolerance() const;
+
+    void MarkSolverTreeDirty();
+
+private:
+    virtual void OnSceneSet(Scene* scene);
+    /// Causes the entire tree to be rebuilt
+    void RebuildTree();
+    void BuildTreeToEffector(const Node* node);
+
+    void HandleComponentAdded(StringHash eventType, VariantMap& eventData);
+    void HandleComponentRemoved(StringHash eventType, VariantMap& eventData);
+    void HandleNodeAdded(StringHash eventType, VariantMap& eventData);
+    void HandleNodeRemoved(StringHash eventType, VariantMap& eventData);
+    /// Invokes the IK solver
+    void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
+
+    PODVector<IKEffector*> effectorList_;
+    ik_solver_t* solver_;
+    bool solverTreeNeedsRebuild_;
+};
+
+} // namespace Urho3D

BIN
bin/Data/Textures/Editor/EditorIcons.png


+ 12 - 0
bin/Data/UI/EditorIcons.xml

@@ -107,6 +107,18 @@
         <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
         <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
         <attribute name="Image Rect" value="48 16 62 30" />
         <attribute name="Image Rect" value="48 16 62 30" />
     </element>
     </element>
+    <element type="IKSolver">
+        <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
+        <attribute name="Image Rect" value="160 0 174 14" />
+    </element>
+    <element type="IKConstraint">
+        <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
+        <attribute name="Image Rect" value="160 0 174 14" />
+    </element>
+    <element type="IKEffector">
+        <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
+        <attribute name="Image Rect" value="192 16 206 30" />
+    </element>
     <element type="Light">
     <element type="Light">
         <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
         <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
         <attribute name="Image Rect" value="48 0 62 14" />
         <attribute name="Image Rect" value="48 0 62 14" />