Browse Source

Merge branch 'InverseKinematics' of https://github.com/TheComet93/Urho3D into TheComet93-InverseKinematics

Yao Wei Tjong 姚伟忠 8 years ago
parent
commit
a8dcfa2be9
68 changed files with 8296 additions and 4 deletions
  1. 3 0
      CMake/Modules/UrhoCommon.cmake
  2. 5 0
      Source/CMakeLists.txt
  3. 33 0
      Source/Samples/45_InverseKinematics/CMakeLists.txt
  4. 300 0
      Source/Samples/45_InverseKinematics/InverseKinematics.cpp
  5. 86 0
      Source/Samples/45_InverseKinematics/InverseKinematics.h
  6. 59 0
      Source/ThirdParty/ik/CMakeLists.txt
  7. 21 0
      Source/ThirdParty/ik/LICENSE
  8. 105 0
      Source/ThirdParty/ik/README.md
  9. 98 0
      Source/ThirdParty/ik/cmake/Urho3D/CMakeLists.txt
  10. 121 0
      Source/ThirdParty/ik/cmake/templates/VisualStudioUserFile.vcproj.user.in
  11. 23 0
      Source/ThirdParty/ik/cmake/templates/VisualStudioUserFile.vcxproj.user.in
  12. 91 0
      Source/ThirdParty/ik/ik/CMakeLists.txt
  13. 1 0
      Source/ThirdParty/ik/ik/include/ik/.gitignore
  14. 21 0
      Source/ThirdParty/ik/ik/include/ik/backtrace.h
  15. 226 0
      Source/ThirdParty/ik/ik/include/ik/bst_vector.h
  16. 36 0
      Source/ThirdParty/ik/ik/include/ik/config.h.in
  17. 105 0
      Source/ThirdParty/ik/ik/include/ik/effector.h
  18. 120 0
      Source/ThirdParty/ik/ik/include/ik/export.h.in
  19. 8 0
      Source/ThirdParty/ik/ik/include/ik/ik.h
  20. 33 0
      Source/ThirdParty/ik/ik/include/ik/log.h
  21. 57 0
      Source/ThirdParty/ik/ik/include/ik/memory.h
  22. 175 0
      Source/ThirdParty/ik/ik/include/ik/node.h
  23. 296 0
      Source/ThirdParty/ik/ik/include/ik/ordered_vector.h
  24. 813 0
      Source/ThirdParty/ik/ik/include/ik/pstdint.h
  25. 101 0
      Source/ThirdParty/ik/ik/include/ik/quat.h
  26. 226 0
      Source/ThirdParty/ik/ik/include/ik/solver.h
  27. 39 0
      Source/ThirdParty/ik/ik/include/ik/solver_FABRIK.h
  28. 13 0
      Source/ThirdParty/ik/ik/include/ik/solver_jacobian_inverse.h
  29. 13 0
      Source/ThirdParty/ik/ik/include/ik/solver_jacobian_transpose.h
  30. 50 0
      Source/ThirdParty/ik/ik/include/ik/vec3.h
  31. 253 0
      Source/ThirdParty/ik/ik/src/bst_vector.c
  32. 44 0
      Source/ThirdParty/ik/ik/src/effector.c
  33. 97 0
      Source/ThirdParty/ik/ik/src/log.c
  34. 281 0
      Source/ThirdParty/ik/ik/src/memory.c
  35. 160 0
      Source/ThirdParty/ik/ik/src/node.c
  36. 351 0
      Source/ThirdParty/ik/ik/src/ordered_vector.c
  37. 15 0
      Source/ThirdParty/ik/ik/src/platform/linux/backtrace_linux.c
  38. 147 0
      Source/ThirdParty/ik/ik/src/quat.c
  39. 181 0
      Source/ThirdParty/ik/ik/src/solver.c
  40. 814 0
      Source/ThirdParty/ik/ik/src/solver_FABRIK.c
  41. 92 0
      Source/ThirdParty/ik/ik/src/vec3.c
  42. 103 0
      Source/Urho3D/AngelScript/IKAPI.cpp
  43. 3 0
      Source/Urho3D/AngelScript/Script.cpp
  44. 4 0
      Source/Urho3D/AngelScript/ScriptAPI.h
  45. 3 3
      Source/Urho3D/CMakeLists.txt
  46. 51 1
      Source/Urho3D/Core/Context.cpp
  47. 6 0
      Source/Urho3D/Core/Context.h
  48. 7 0
      Source/Urho3D/Engine/Engine.cpp
  49. 42 0
      Source/Urho3D/IK/IK.cpp
  50. 49 0
      Source/Urho3D/IK/IK.h
  51. 120 0
      Source/Urho3D/IK/IKConstraint.cpp
  52. 72 0
      Source/Urho3D/IK/IKConstraint.h
  53. 60 0
      Source/Urho3D/IK/IKConverters.cpp
  54. 41 0
      Source/Urho3D/IK/IKConverters.h
  55. 342 0
      Source/Urho3D/IK/IKEffector.cpp
  56. 185 0
      Source/Urho3D/IK/IKEffector.h
  57. 37 0
      Source/Urho3D/IK/IKEvents.h
  58. 596 0
      Source/Urho3D/IK/IKSolver.cpp
  59. 254 0
      Source/Urho3D/IK/IKSolver.h
  60. 6 0
      Source/Urho3D/LuaScript/LuaScript.cpp
  61. 13 0
      Source/Urho3D/LuaScript/pkgs/IK/IKConstraint.pkg
  62. 43 0
      Source/Urho3D/LuaScript/pkgs/IK/IKEffector.pkg
  63. 40 0
      Source/Urho3D/LuaScript/pkgs/IK/IKSolver.pkg
  64. 6 0
      Source/Urho3D/LuaScript/pkgs/IKLuaAPI.pkg
  65. 237 0
      bin/Data/LuaScripts/45_InverseKinematics.lua
  66. 251 0
      bin/Data/Scripts/45_InverseKinematics.as
  67. BIN
      bin/Data/Textures/Editor/EditorIcons.png
  68. 12 0
      bin/Data/UI/EditorIcons.xml

+ 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 (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_IK "Enable inverse kinematics support" TRUE)
 option (URHO3D_LUA "Enable additional Lua scripting support" TRUE)
 option (URHO3D_NAVIGATION "Enable navigation support" TRUE)
 # Urho's Network subsystem depends on kNet library which uses C++ exceptions feature
@@ -352,6 +353,7 @@ if (URHO3D_CLANG_TOOLS)
             URHO3D_ANGELSCRIPT
             URHO3D_DATABASE_SQLITE
             URHO3D_FILEWATCHER
+            URHO3D_IK
             URHO3D_LOGGING
             URHO3D_LUA
             URHO3D_NAVIGATION
@@ -402,6 +404,7 @@ foreach (OPT
         URHO3D_ANGELSCRIPT
         URHO3D_DATABASE
         URHO3D_FILEWATCHER
+        URHO3D_IK
         URHO3D_LOGGING
         URHO3D_LUA
         URHO3D_MINIDUMPS

+ 5 - 0
Source/CMakeLists.txt

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

+ 33 - 0
Source/Samples/45_InverseKinematics/CMakeLists.txt

@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2008-2017 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.
+#
+
+# Define target name
+set (TARGET_NAME 45_InverseKinematics)
+
+# Define source files
+define_source_files (EXTRA_H_FILES ${COMMON_SAMPLE_H_FILES})
+
+# Setup target with resource copying
+setup_main_executable ()
+
+# Setup test cases
+setup_test ()

+ 300 - 0
Source/Samples/45_InverseKinematics/InverseKinematics.cpp

@@ -0,0 +1,300 @@
+//
+// Copyright (c) 2008-2017 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 <Urho3D/Core/CoreEvents.h>
+#include <Urho3D/Engine/Engine.h>
+#include <Urho3D/Graphics/AnimatedModel.h>
+#include <Urho3D/Graphics/AnimationController.h>
+#include <Urho3D/Graphics/Camera.h>
+#include <Urho3D/Graphics/Graphics.h>
+#include <Urho3D/Graphics/Material.h>
+#include <Urho3D/Graphics/Octree.h>
+#include <Urho3D/Graphics/DebugRenderer.h>
+#include <Urho3D/Graphics/RibbonTrail.h>
+#include <Urho3D/IK/IKEffector.h>
+#include <Urho3D/IK/IKSolver.h>
+#include <Urho3D/Input/Input.h>
+#include <Urho3D/Math/Matrix2.h>
+#include <Urho3D/Physics/PhysicsWorld.h>
+#include <Urho3D/Physics/CollisionShape.h>
+#include <Urho3D/Physics/RigidBody.h>
+#include <Urho3D/Resource/ResourceCache.h>
+#include <Urho3D/Scene/Scene.h>
+#include <Urho3D/UI/Font.h>
+#include <Urho3D/UI/Text.h>
+#include <Urho3D/UI/UI.h>
+#include <Urho3D/UI/Text3D.h>
+
+#include "InverseKinematics.h"
+
+#include <Urho3D/DebugNew.h>
+
+URHO3D_DEFINE_APPLICATION_MAIN(InverseKinematics)
+
+InverseKinematics::InverseKinematics(Context* context) :
+    Sample(context)
+{
+}
+
+void InverseKinematics::Start()
+{
+    // Execute base class startup
+    Sample::Start();
+
+    // Create the scene content
+    CreateScene();
+
+    // Create the UI content
+    CreateInstructions();
+
+    // Setup the viewport for displaying the scene
+    SetupViewport();
+
+    // Hook up to the frame update events
+    SubscribeToEvents();
+
+    // Set the mouse mode to use in the sample
+    Sample::InitMouseMode(MM_RELATIVE);
+
+    GetSubsystem<Input>()->SetMouseVisible(true);
+}
+
+void InverseKinematics::CreateScene()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    scene_ = new Scene(context_);
+
+    // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
+    scene_->CreateComponent<Octree>();
+    scene_->CreateComponent<DebugRenderer>();
+    scene_->CreateComponent<PhysicsWorld>();
+
+    // Create scene node & StaticModel component for showing a static plane
+    floorNode_ = scene_->CreateChild("Plane");
+    floorNode_->SetScale(Vector3(50.0f, 1.0f, 50.0f));
+    StaticModel* planeObject = floorNode_->CreateComponent<StaticModel>();
+    planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl"));
+    planeObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml"));
+
+    // Set up collision, we need to raycast to determine foot height
+    floorNode_->CreateComponent<RigidBody>();
+    CollisionShape* col = floorNode_->CreateComponent<CollisionShape>();
+    col->SetBox(Vector3(1, 0, 1));
+
+    // Create a directional light to the world.
+    Node* lightNode = scene_->CreateChild("DirectionalLight");
+    lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f)); // The direction vector does not need to be normalized
+    Light* light = lightNode->CreateComponent<Light>();
+    light->SetLightType(LIGHT_DIRECTIONAL);
+    light->SetCastShadows(true);
+    light->SetShadowBias(BiasParameters(0.00005f, 0.5f));
+    // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
+    light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f));
+
+    // Load Jack model
+    jackNode_ = scene_->CreateChild("Jack");
+    jackNode_->SetRotation(Quaternion(0.0f, 270.0f, 0.0f));
+    AnimatedModel* jack = jackNode_->CreateComponent<AnimatedModel>();
+    jack->SetModel(cache->GetResource<Model>("Models/Jack.mdl"));
+    jack->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml"));
+    jack->SetCastShadows(true);
+
+    // Create animation controller and play walk animation
+    jackAnimCtrl_ = jackNode_->CreateComponent<AnimationController>();
+    jackAnimCtrl_->PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.0f);
+
+    // We need to attach two inverse kinematic effectors to Jack's feet to
+    // control the grounding.
+    leftFoot_  = jackNode_->GetChild("Bip01_L_Foot", true);
+    rightFoot_ = jackNode_->GetChild("Bip01_R_Foot", true);
+    leftEffector_  = leftFoot_->CreateComponent<IKEffector>();
+    rightEffector_ = rightFoot_->CreateComponent<IKEffector>();
+    // Control 2 segments up to the hips
+    leftEffector_->SetChainLength(2);
+    rightEffector_->SetChainLength(2);
+
+    // For the effectors to work, an IKSolver needs to be attached to one of
+    // the parent nodes. Typically, you want to place the solver as close as
+    // possible to the effectors for optimal performance. Since in this case
+    // we're solving the legs only, we can place the solver at the spine.
+    Node* spine = jackNode_->GetChild("Bip01_Spine", true);
+    solver_ = jackNode_->CreateComponent<IKSolver>();
+
+    // Disable auto-solving, which means we need to call Solve() manually
+    solver_->EnableAutoSolve(false);
+
+    // When this is enabled, the solver will use the current positions of the
+    // nodes in the skeleton as its basis every frame. If you disable this, then
+    // the solver will store the initial positions of the nodes once and always
+    // use those positions for calculating solutions.
+    // With animated characters you generally want to continuously update the
+    // initial positions.
+    solver_->EnableUpdatePose(true);
+
+    // Create the camera.
+    cameraRotateNode_ = scene_->CreateChild("CameraRotate");
+    cameraNode_ = cameraRotateNode_->CreateChild("Camera");
+    cameraNode_->CreateComponent<Camera>();
+
+    // Set an initial position for the camera scene node above the plane
+    cameraNode_->SetPosition(Vector3(0, 0, -4));
+    cameraRotateNode_->SetPosition(Vector3(0, 0.4, 0));
+    pitch_ = 20;
+    yaw_ = 50;
+}
+
+void InverseKinematics::CreateInstructions()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    UI* ui = GetSubsystem<UI>();
+
+    // Construct new Text object, set string to display and font to use
+    Text* instructionText = ui->GetRoot()->CreateChild<Text>();
+    instructionText->SetText("Left-Click and drag to look around\nRight-Click and drag to change incline\nPress space to reset floor\nPress D to draw debug geometry");
+    instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
+
+    // Position the text relative to the screen center
+    instructionText->SetHorizontalAlignment(HA_CENTER);
+    instructionText->SetVerticalAlignment(VA_CENTER);
+    instructionText->SetPosition(0, ui->GetRoot()->GetHeight() / 4);
+}
+
+void InverseKinematics::SetupViewport()
+{
+    Renderer* renderer = GetSubsystem<Renderer>();
+
+    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
+    // at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
+    // use, but now we just use full screen and default render path configured in the engine command line options
+    SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
+    renderer->SetViewport(0, viewport);
+}
+
+void InverseKinematics::UpdateCameraAndFloor(float timeStep)
+{
+    // Do not move if the UI has a focused element (the console)
+    if (GetSubsystem<UI>()->GetFocusElement())
+        return;
+
+    Input* input = GetSubsystem<Input>();
+
+    // Movement speed as world units per second
+    const float MOVE_SPEED = 20.0f;
+    // Mouse sensitivity as degrees per pixel
+    const float MOUSE_SENSITIVITY = 0.1f;
+
+    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
+    if (input->GetMouseButtonDown(MOUSEB_LEFT))
+    {
+        IntVector2 mouseMove = input->GetMouseMove();
+        yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
+        pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
+        pitch_ = Clamp(pitch_, -90.0f, 90.0f);
+    }
+
+    if (input->GetMouseButtonDown(MOUSEB_RIGHT))
+    {
+        IntVector2 mouseMoveInt = input->GetMouseMove();
+        Vector2 mouseMove = Matrix2(
+            -Cos(yaw_), Sin(yaw_),
+            Sin(yaw_),  Cos(yaw_)
+        ) * Vector2(mouseMoveInt.y_, -mouseMoveInt.x_);
+        floorPitch_ += MOUSE_SENSITIVITY * mouseMove.x_;
+        floorPitch_ = Clamp(floorPitch_, -90.0f, 90.0f);
+        floorRoll_ += MOUSE_SENSITIVITY * mouseMove.y_;
+    }
+
+    if (input->GetKeyPress(KEY_SPACE))
+    {
+        floorPitch_ = 0;
+        floorRoll_ = 0;
+    }
+
+    if (input->GetKeyPress(KEY_D))
+    {
+        drawDebug_ = !drawDebug_;
+    }
+
+    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+    cameraRotateNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
+    floorNode_->SetRotation(Quaternion(floorPitch_, 0, floorRoll_));
+}
+
+void InverseKinematics::SubscribeToEvents()
+{
+    // Subscribe HandleUpdate() function for processing update events
+    SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(InverseKinematics, HandleUpdate));
+    SubscribeToEvent(E_POSTRENDERUPDATE, URHO3D_HANDLER(InverseKinematics, HandlePostRenderUpdate));
+    SubscribeToEvent(E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(InverseKinematics, HandleSceneDrawableUpdateFinished));
+}
+
+void InverseKinematics::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace Update;
+
+    // Take the frame time step, which is stored as a float
+    float timeStep = eventData[P_TIMESTEP].GetFloat();
+
+    // Move the camera, scale movement with time step
+    UpdateCameraAndFloor(timeStep);
+}
+
+void InverseKinematics::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (drawDebug_)
+        solver_->DrawDebugGeometry(false);
+}
+
+void InverseKinematics::HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData)
+{
+    PhysicsWorld* phyWorld = scene_->GetComponent<PhysicsWorld>();
+    Vector3 leftFootPosition = leftFoot_->GetWorldPosition();
+    Vector3 rightFootPosition = rightFoot_->GetWorldPosition();
+
+    // Cast ray down to get the normal of the underlying surface
+    PhysicsRaycastResult result;
+    phyWorld->RaycastSingle(result, Ray(leftFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2);
+    if (result.body_)
+    {
+        // Cast again, but this time along the normal. Set the target position
+        // to the ray intersection
+        phyWorld->RaycastSingle(result, Ray(leftFootPosition + result.normal_, -result.normal_), 2);
+        // The foot node has an offset relative to the root node
+        float footOffset = leftFoot_->GetWorldPosition().y_ - jackNode_->GetWorldPosition().y_;
+        leftEffector_->SetTargetPosition(result.position_ + result.normal_ * footOffset);
+        // Rotate foot according to normal
+        leftFoot_->Rotate(Quaternion(Vector3(0, 1, 0), result.normal_), TS_WORLD);
+    }
+
+    // Same deal with the right foot
+    phyWorld->RaycastSingle(result, Ray(rightFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2);
+    if (result.body_)
+    {
+        phyWorld->RaycastSingle(result, Ray(rightFootPosition + result.normal_, -result.normal_), 2);
+        float footOffset = rightFoot_->GetWorldPosition().y_ - jackNode_->GetWorldPosition().y_;
+        rightEffector_->SetTargetPosition(result.position_ + result.normal_ * footOffset);
+        rightFoot_->Rotate(Quaternion(Vector3(0, 1, 0), result.normal_), TS_WORLD);
+    }
+
+    solver_->Solve();
+}

+ 86 - 0
Source/Samples/45_InverseKinematics/InverseKinematics.h

@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2008-2017 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 "Sample.h"
+
+namespace Urho3D
+{
+class AnimationController;
+class Node;
+class IKEffector;
+class IKSolver;
+class Scene;
+}
+
+/// Inverse Kinematics demo.
+/// This sample demonstrates how to adjust the position of animated feet so they match the ground's angle using IK
+class InverseKinematics : public Sample
+{
+    URHO3D_OBJECT(InverseKinematics, Sample);
+
+public:
+    /// Construct.
+    InverseKinematics(Context* context);
+
+    /// Setup after engine initialization and before running the main loop.
+    virtual void Start();
+
+protected:
+    /// Animation controller of Jack.
+    SharedPtr<Urho3D::AnimationController> jackAnimCtrl_;
+    /// Inverse kinematic effectors and solver
+    SharedPtr<Urho3D::IKEffector> leftEffector_;
+    SharedPtr<Urho3D::IKEffector> rightEffector_;
+    SharedPtr<Urho3D::IKSolver> solver_;
+    /// Need references to these nodes to calculate foot angles and offsets
+    SharedPtr<Urho3D::Node> leftFoot_;
+    SharedPtr<Urho3D::Node> rightFoot_;
+    SharedPtr<Urho3D::Node> jackNode_;
+    /// So we can rotate the floor
+    SharedPtr<Urho3D::Node> floorNode_;
+    float floorPitch_;
+    float floorRoll_;
+    /// Whether or not to draw debug geometry
+    bool drawDebug_;
+
+private:
+    /// Construct the scene content.
+    void CreateScene();
+    /// Construct an instruction text to the UI.
+    void CreateInstructions();
+    /// Set up a viewport for displaying the scene.
+    void SetupViewport();
+    /// Read input and moves the camera.
+    void UpdateCameraAndFloor(float timeStep);
+    /// Subscribe to application-wide logic update events.
+    void SubscribeToEvents();
+    /// Handle the logic update event.
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
+    /// Draw debug geometry
+    void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData);
+    /// Process IK logic
+    void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
+
+    SharedPtr<Node> cameraRotateNode_;
+};

+ 59 - 0
Source/ThirdParty/ik/CMakeLists.txt

@@ -0,0 +1,59 @@
+cmake_minimum_required (VERSION 2.6)
+
+# If the user specifies -DCMAKE_BUILD_TYPE on the command line, take their
+# definition and dump it in the cache along with proper documentation, otherwise
+# set MAKE_BUILD_TYPE to Debug prior to calling PROJECT()
+if (DEFINED CMAKE_BUILD_TYPE)
+    set (CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
+else()
+    set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
+endif()
+message (STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+
+###############################################################################
+# Project name
+###############################################################################
+
+project ("ik" C)
+
+# Make macs happy
+set (CMAKE_MACOSX_RPATH OFF)
+
+###############################################################################
+# compiler definitions and flags
+###############################################################################
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+    add_definitions (-W -Wall -fno-omit-frame-pointer)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+    add_definitions (-W -Wall -fno-omit-frame-pointer)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Intel")
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+    add_definitions (-D_CRT_SECURE_NO_WARNINGS)
+    if (CMAKE_BUILD_TYPE MATCHES Debug)
+        STRING(REGEX REPLACE "/MDd" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
+        add_definitions (-MTd)
+    elseif (CMAKE_BUILD_TYPE MATCHES Release)
+        STRING(REGEX REPLACE "/MD" "" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
+        add_definitions (-MT)
+    endif ()
+endif ()
+
+if (CMAKE_BUILD_TYPE MATCHES Debug)
+    add_definitions(-DDEBUG)
+endif ()
+
+###############################################################################
+# Dependency settings
+###############################################################################
+
+message (STATUS "------------------------------------------------------------")
+message (STATUS "Global settings")
+message (STATUS "------------------------------------------------------------")
+
+add_subdirectory ("ik")
+
+option (IK_TESTS "Whether to build unit tests or not (requires C++)" OFF)
+if (IK_TESTS)
+    add_subdirectory ("tests")
+endif ()

+ 21 - 0
Source/ThirdParty/ik/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Alex Murray.
+
+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.

+ 105 - 0
Source/ThirdParty/ik/README.md

@@ -0,0 +1,105 @@
+Inverse Kinematics Library
+==========================
+
+Very much a work-in-progress. In its current state, this library  is  not  yet
+usable. Please come back later :)
+
+The  goal  of  this  library is to provide a fast,  lightweight  and  flexible
+solution to solving the inverse kinematics problem.
+
+Overview
+--------
+
+One of the challenges was to design an interface  which  could  work  with any
+scene graph/skeleton/animation system.  The  library  provides  a leightweight
+interface for building a tree  and specifying positions and rotations for each
+node.  The tree also holds information on effector targets and  chain  length.
+The  tree is then preprocessed into a more optimal form for the  solver.  This
+preprocessing step is necessary  whenever the tree is altered or effectors are
+added or removed,  which,  in  practice,  should only occur once. Invoking the
+solver  will  cause  a  series  of  computations  to  occur  on  the  internal
+structures, the results of which will then be  mapped  back  onto the original
+tree structure specified by the user. The user can then iterate  the  tree and
+apply   the   solved   positions   and   rotations   back   to    his    scene
+graph/skeleton/animation structure.
+
+All  of the code was written in C89 and has no dependencies other than  the  C
+standard  library.  Memory  debugging  facilities are in place to track memory
+leaks.  On  linux,  backtraces can be generated to the respective malloc() and
+free() calls.
+
+Example usage
+-------------
+
+Here is a minimal working example that probably satisfies your needs.
+
+```cpp
+#include <ik/ik.h>
+
+static void results_callback(struct ik_node_t* ikNode)
+{
+    /* Extract our scene graph node again */
+    Node* node = (Node)ikNode->user_data;
+
+    /* Apply results back to our engine's tree */
+    node->SetWorldPosition(ikNode->solved_position);
+    node->SetWorldRotation(ikNode->solved_rotation);
+}
+
+int main()
+{
+    /* Create a tree that splits into two arms */
+    struct node_t* root = node_create(0);
+    struct node_t* child1 = node_create(1);
+    struct node_t* child2 = node_create(2);
+    struct node_t* child3 = node_create(3);
+    struct node_t* child4 = node_create(4);
+    struct node_t* child5 = node_create(5);
+    struct node_t* child6 = node_create(6);
+    node_add_child(root, child1);
+    node_add_child(child1, child2);
+    node_add_child(child2, child3);
+    node_add_child(child3, child4);
+    node_add_child(child2, child5);
+    node_add_child(child5, child6);
+
+    /* Lets assume we are developing a game engine that has its own scene graph,
+     * and lets assume it has the same structure as the tree created above.
+     */
+    Node* sceneRoot = GetSceneRoot();
+
+    /* Store a pointer to each engine node to user_data so we can use it later */
+    root->user_data = sceneRoot;
+    child1->user_data = sceneRoot->GetChild(1);
+    child2->user_data = sceneRoot->GetChild(2);
+    child3->user_data = sceneRoot->GetChild(3);
+    child4->user_data = sceneRoot->GetChild(4);
+    child5->user_data = sceneRoot->GetChild(5);
+    child6->user_data = sceneRoot->GetChild(6);
+
+    /* Attach an effector on each arm */
+    struct effector_t* eff1 = effector_create();
+    struct effector_t* eff2 = effector_create();
+    node_attach_effector(child4, eff1);
+    node_attach_effector(child6, eff2);
+
+    /* Each arm is composed of 3 nodes (2 segments), and we only want to control
+     * that portion of the tree. */
+    eff1->chain_length = 2;
+    eff2->chain_length = 2;
+
+    /* Create a solver and set up the results callback function, which gets
+     * called once for every computed result. */
+    struct ik_solver_t* solver = ik_solver_create(SOLVER_FABRIK);
+    solver->apply_result = results_callback;
+
+    /* We want to calculate rotations as well as positions */
+    solver->flags |= SOLVER_CALCULATE_FINAL_ROTATIONS;
+
+    /* Assign our tree to the solver and rebuild the data */
+    ik_solver_set_tree(solver, root);
+    ik_solver_rebuild_data(solver);
+    ik_solver_solve(solver);
+}
+
+```

+ 98 - 0
Source/ThirdParty/ik/cmake/Urho3D/CMakeLists.txt

@@ -0,0 +1,98 @@
+# Copyright (c) 2008-2017 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 (CheckIncludeFiles)
+
+set (IK_BUILD_TYPE "STATIC")
+check_include_files ("stdint.h" HAVE_STDINT_H)
+
+# memory debugging
+if (CMAKE_BUILD_TYPE MATCHES Debug)
+    option (IK_MEMORY_DEBUGGING "Global switch for memory options. Keeps track of the number of allocations and de-allocations and prints a report when the program shuts down" ON)
+else ()
+    option (IK_MEMORY_DEBUGGING "Global switch for memory options. Keeps track of the number of allocations and de-allocations and prints a report when the program shuts down" OFF)
+endif ()
+if (IK_MEMORY_DEBUGGING)
+    if(${CMAKE_BUILD_TYPE} MATCHES Debug)
+        option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" ON)
+    else ()
+        option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" OFF)
+    endif ()
+else ()
+    option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" OFF)
+endif ()
+
+set (IK_REAL "float" CACHE STRING "Type to use for real numbers")
+option (IK_DOT_OUTPUT "When enabled, the generated chains are dumped to DOT for debug purposes" OFF)
+
+configure_file ("../../ik/include/ik/export.h.in"
+                "../../include/ik/gen/export.h")
+configure_file ("../../ik/include/ik/config.h.in"
+                "../../include/ik/gen/config.h")
+
+###############################################################################
+# compiler flags
+###############################################################################
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+    add_definitions (-W -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -fno-math-errno -ffast-math)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+    add_definitions (-W -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -fno-math-errno -ffast-math)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Intel")
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+    add_definitions (-D_CRT_SECURE_NO_WARNINGS)
+    if (CMAKE_BUILD_TYPE MATCHES Debug)
+        STRING(REGEX REPLACE "/MDd" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
+        add_definitions (-MTd)
+    elseif (CMAKE_BUILD_TYPE MATCHES Release)
+        STRING(REGEX REPLACE "/MD" "" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
+        add_definitions (-MT)
+    endif ()
+endif ()
+
+# The library is being built
+add_definitions (-DIK_BUILDING)
+
+###############################################################################
+# source files and library definition
+###############################################################################
+
+file (GLOB IK_HEADERS "../../ik/include/ik/*.h")
+file (GLOB IK_SOURCES "../../ik/src/*.c")
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    file (GLOB IK_PLATFORM_SOURCES "../../ik/src/platform/linux/*.c")
+endif ()
+list (APPEND IK_SOURCES ${IK_PLATFORM_SOURCES})
+
+set (TARGET_NAME IK)
+set (SOURCE_FILES ${IK_SOURCES} ${IK_HEADERS})
+set (INCLUDE_DIRS ../../ik/include ${CMAKE_CURRENT_BINARY_DIR}/../../include)
+
+setup_library ()
+
+###############################################################################
+# install targets
+###############################################################################
+
+# Install headers for building and using the Urho3D library
+install_header_files (DIRECTORY ../../ik/include/ik/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/ik)
+install_header_files (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../include/ik/gen/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/ik/gen)

+ 121 - 0
Source/ThirdParty/ik/cmake/templates/VisualStudioUserFile.vcproj.user.in

@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioUserFile
+	ProjectType="Visual C++"
+	Version="8.00"
+	ShowAllFiles="false"
+	>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			>
+			<DebugSettings
+				Command="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/$(TargetFileName)"
+				WorkingDirectory="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"
+				CommandArguments=""
+				Attach="false"
+				DebuggerType="3"
+				Remote="1"
+				RemoteMachine=""
+				RemoteCommand=""
+				HttpUrl=""
+				PDBPath=""
+				SQLDebugging=""
+				Environment=""
+				EnvironmentMerge="true"
+				DebuggerFlavor="0"
+				MPIRunCommand=""
+				MPIRunArguments=""
+				MPIRunWorkingDirectory=""
+				ApplicationCommand=""
+				ApplicationArguments=""
+				ShimCommand=""
+				MPIAcceptMode=""
+				MPIAcceptFilter=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			>
+			<DebugSettings
+				Command="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/$(TargetFileName)"
+				WorkingDirectory="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"
+				CommandArguments=""
+				Attach="false"
+				DebuggerType="3"
+				Remote="1"
+				RemoteMachine=""
+				RemoteCommand=""
+				HttpUrl=""
+				PDBPath=""
+				SQLDebugging=""
+				Environment=""
+				EnvironmentMerge="true"
+				DebuggerFlavor="0"
+				MPIRunCommand=""
+				MPIRunArguments=""
+				MPIRunWorkingDirectory=""
+				ApplicationCommand=""
+				ApplicationArguments=""
+				ShimCommand=""
+				MPIAcceptMode=""
+				MPIAcceptFilter=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="RelWithDebInfo|Win32"
+			>
+			<DebugSettings
+				Command="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/$(TargetFileName)"
+				WorkingDirectory="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"
+				CommandArguments=""
+				Attach="false"
+				DebuggerType="3"
+				Remote="1"
+				RemoteMachine=""
+				RemoteCommand=""
+				HttpUrl=""
+				PDBPath=""
+				SQLDebugging=""
+				Environment=""
+				EnvironmentMerge="true"
+				DebuggerFlavor="0"
+				MPIRunCommand=""
+				MPIRunArguments=""
+				MPIRunWorkingDirectory=""
+				ApplicationCommand=""
+				ApplicationArguments=""
+				ShimCommand=""
+				MPIAcceptMode=""
+				MPIAcceptFilter=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="MinSizeRel|Win32"
+			>
+			<DebugSettings
+				Command="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/$(TargetFileName)"
+				WorkingDirectory="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"
+				CommandArguments=""
+				Attach="false"
+				DebuggerType="3"
+				Remote="1"
+				RemoteMachine=""
+				RemoteCommand=""
+				HttpUrl=""
+				PDBPath=""
+				SQLDebugging=""
+				Environment=""
+				EnvironmentMerge="true"
+				DebuggerFlavor="0"
+				MPIRunCommand=""
+				MPIRunArguments=""
+				MPIRunWorkingDirectory=""
+				ApplicationCommand=""
+				ApplicationArguments=""
+				ShimCommand=""
+				MPIAcceptMode=""
+				MPIAcceptFilter=""
+			/>
+		</Configuration>
+	</Configurations>
+</VisualStudioUserFile>

+ 23 - 0
Source/ThirdParty/ik/cmake/templates/VisualStudioUserFile.vcxproj.user.in

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+	<LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+    <LocalDebuggerWorkingDirectory>@CMAKE_RUNTIME_OUTPUT_DIRECTORY@</LocalDebuggerWorkingDirectory>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+    <LocalDebuggerWorkingDirectory>@CMAKE_RUNTIME_OUTPUT_DIRECTORY@</LocalDebuggerWorkingDirectory>
+	<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">
+    <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+    <LocalDebuggerWorkingDirectory>@CMAKE_RUNTIME_OUTPUT_DIRECTORYR@</LocalDebuggerWorkingDirectory>
+	<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">
+    <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+    <LocalDebuggerWorkingDirectory>@CMAKE_RUNTIME_OUTPUT_DIRECTORY@</LocalDebuggerWorkingDirectory>
+	<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+</Project>

+ 91 - 0
Source/ThirdParty/ik/ik/CMakeLists.txt

@@ -0,0 +1,91 @@
+include (CheckIncludeFiles)
+
+set (IK_BUILD_TYPE "STATIC")
+check_include_files ("stdint.h" HAVE_STDINT_H)
+
+# memory debugging
+if (CMAKE_BUILD_TYPE MATCHES Debug)
+    option (IK_MEMORY_DEBUGGING "Global switch for memory options. Keeps track of the number of allocations and de-allocations and prints a report when the program shuts down" ON)
+else ()
+    option (IK_MEMORY_DEBUGGING "Global switch for memory options. Keeps track of the number of allocations and de-allocations and prints a report when the program shuts down" OFF)
+endif ()
+if (IK_MEMORY_DEBUGGING)
+    if(${CMAKE_BUILD_TYPE} MATCHES Debug)
+        option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" ON)
+    else ()
+        option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" OFF)
+    endif ()
+else ()
+    option (IK_MEMORY_BACKTRACE "Generates backtraces for every malloc(), making it easy to track down memory leaks" OFF)
+endif ()
+
+set (IK_REAL "float" CACHE STRING "Type to use for real numbers")
+option (IK_DOT_OUTPUT "When enabled, the generated chains are dumped to DOT for debug purposes" OFF)
+
+configure_file ("include/ik/export.h.in"
+                "include/ik/gen/export.h")
+configure_file ("include/ik/config.h.in"
+                "include/ik/gen/config.h")
+
+include_directories ("${CMAKE_CURRENT_BINARY_DIR}/include")
+
+###############################################################################
+# compiler flags
+###############################################################################
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+    add_definitions (-W -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -fno-math-errno -ffast-math)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+    add_definitions (-W -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -fno-math-errno -ffast-math)
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Intel")
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+    add_definitions (-D_CRT_SECURE_NO_WARNINGS)
+    if (CMAKE_BUILD_TYPE MATCHES Debug)
+        STRING(REGEX REPLACE "/MDd" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG})
+        add_definitions (-MTd)
+    elseif (CMAKE_BUILD_TYPE MATCHES Release)
+        STRING(REGEX REPLACE "/MD" "" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE})
+        add_definitions (-MT)
+    endif ()
+endif ()
+
+###############################################################################
+# source files and library definition
+###############################################################################
+
+file (GLOB IK_HEADERS "include/ik/*.h")
+file (GLOB IK_SOURCES "src/*.c")
+
+list (APPEND IK_HEADERS
+    "include/ik/config.h.in"
+    "include/ik/export.h.in")
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    file (GLOB IK_PLATFORM_SOURCES "src/platform/linux/*.c")
+endif ()
+list (APPEND IK_SOURCES ${IK_PLATFORM_SOURCES})
+
+include_directories ("include")
+
+add_library (ik ${IK_BUILD_TYPE}
+    ${IK_HEADERS}
+    ${IK_SOURCES}
+)
+
+# The library is being built
+add_definitions (-DIK_BUILDING)
+
+###############################################################################
+# install targets
+###############################################################################
+
+install (
+    TARGETS
+        ik
+    LIBRARY DESTINATION
+        "lib"
+    RUNTIME DESTINATION
+        "bin"
+    ARCHIVE DESTINATION
+        "lib"
+)

+ 1 - 0
Source/ThirdParty/ik/ik/include/ik/.gitignore

@@ -0,0 +1 @@
+/gen

+ 21 - 0
Source/ThirdParty/ik/ik/include/ik/backtrace.h

@@ -0,0 +1,21 @@
+#ifndef LIGHTSHIP_UTIL_BACKTRACE_H
+#define LIGHTSHIP_UTIL_BACKTRACE_H
+
+#define BACKTRACE_SIZE 64
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+/*!
+ * @brief Generates a backtrace.
+ * @param[in] size The maximum number of frames to walk.
+ * @return Returns an array of char* arrays.
+ * @note The returned array must be freed manually with FREE(returned_array).
+ */
+IK_PUBLIC_API char**
+get_backtrace(int* size);
+
+C_HEADER_END
+
+#endif /* LIGHTSHIP_UTIL_BACKTRACE_H */

+ 226 - 0
Source/ThirdParty/ik/ik/include/ik/bst_vector.h

@@ -0,0 +1,226 @@
+/*!
+ * @file bst_vector.h
+ * @brief Implements a container of ordered key-value pairs stored in a vector
+ * (ordered by key). The hash is computed from a key (string) provided by the
+ * user.
+ */
+
+#ifndef IK_BST_VECTOR_H
+#define IK_BST_VECTOR_H
+
+#include "ik/gen/config.h"
+#include "ik/ordered_vector.h"
+
+C_HEADER_BEGIN
+
+struct bstv_hash_value_t
+{
+    uint32_t hash;
+    void*    value;
+};
+
+struct bstv_t
+{
+    struct ordered_vector_t   vector;
+};
+
+/*!
+ * @brief Creates a new bstv object.
+ * @return Returns the newly created bstv object. It must be freed with
+ * bstv_destroy() when no longer required.
+ */
+IK_PUBLIC_API struct bstv_t*
+bstv_create(void);
+
+/*!
+ * @brief Initialises an existing bstv object.
+ * @note This does **not** FREE existing elements. If you have elements in your
+ * bstv and call this, those elements will be lost and a memory leak will have
+ * been created.
+ * @param[in] bstv The bstv object to initialise.
+ */
+IK_PUBLIC_API void
+bstv_construct(struct bstv_t* bstv);
+
+/*!
+ * @brief Destroys an existing bstv object and FREEs the underlying memory.
+ * @note Elements inserted into the bstv are not FREEd.
+ * @param[in] bstv The bstv object to destroy.
+ */
+IK_PUBLIC_API void
+bstv_destroy(struct bstv_t* bstv);
+
+/*!
+ * @brief Inserts an element into the bstv using a hashed key.
+ *
+ * @warning There is no way to test for hash collisions since this function
+ * doesn't have access to the key which generated the hash. It is highly
+ * discouraged to mix bstv_insert_hash() and bstv_insert(). Use one or the other.
+ *
+ * @note Complexity is O(log2(n)) to find the insertion point.
+ *
+ * @param[in] bstv The bstv object to insert into.
+ * @param[in] hash A unique key to assign to the element being inserted. The
+ * key must not exist in the bstv, or the element will not be inserted.
+ * @param[in] value The data to insert into the bstv.
+ * @note The value is **not** copied into the bstv, only referenced. For this
+ * reason don't insert stack allocated items into the bstv.
+ * @return Returns 0 if insertion was successful. Returns 1 if the key already
+ * existed (in which case nothing is inserted). Returns -1 on failure.
+ */
+IK_PUBLIC_API int
+bstv_insert(struct bstv_t* bstv, uint32_t hash, void* value);
+
+/*!
+ * @brief Sets the value bstvped to the specified hash in the bstv.
+ * @note If the hash is not found, this function silently fails.
+ * @param[in] bstv A pointer to the bstv object to change the value of.
+ * @param[in] hash The unique key associated with the value you want to change.
+ * @param[in] value The new value to set.
+ */
+IK_PUBLIC_API void
+bstv_set(struct bstv_t* bstv, uint32_t hash, void* value);
+
+/*!
+ * @brief Looks for an element in the bstv and returns it if found.
+ * @note Complexity is O(log2(n)).
+ * @param[in] bstv The bstv to search in.
+ * @param[in] hash The hash to search for.
+ * @return Returns the data associated with the specified hash. If the hash is
+ * not found in the bstv, then NULL is returned.
+ * @note Potential pitfall: The value could be NULL even if the hash was found,
+ * as NULL is a valid thing for a value to be. If you are checking to see if a
+ * hash exists, use bstv_key_exists() instead.
+ */
+IK_PUBLIC_API void*
+bstv_find(const struct bstv_t* bstv, uint32_t hash);
+
+/*!
+ * @brief Looks for an element in the bstv and returns a pointer to the element
+ * in the structure. This is useful if you need to store data directly in the
+ * memory occupied by the pointer and wish to modify it.
+ * @note The returned pointer can be invalidated if any insertions or deletions
+ * are performed.
+ * @param[in] bstv The bstv to search in.
+ * @param[in] hash The has to search for.
+ */
+IK_PUBLIC_API void**
+bstv_find_ptr(const struct bstv_t* bstv, uint32_t hash);
+
+/*!
+ * @brief Finds the specified element in the bstv and returns its key.
+ * @note Complexity is O(n).
+ * @param[in] bstv The bstv to search.
+ * @param[in] value The value to search for.
+ * @return Returns the key if it was successfully found, or MAP_INVALID_KEY if
+ * otherwise.
+ */
+IK_PUBLIC_API uint32_t
+bstv_find_element(const struct bstv_t* bstv, const void* value);
+
+/*!
+ * @brief Gets any element from the bstv.
+ *
+ * This is useful when you want to iterate and remove all items from the bstv
+ * at the same time.
+ * @return Returns an element as a void pointer. Which element is random.
+ */
+IK_PUBLIC_API void*
+bstv_get_any_element(const struct bstv_t* bstv);
+
+/*!
+ * @brief Returns 1 if the specified hash exists, 0 if otherwise.
+ * @param bstv The bstv to find the hash in.
+ * @param hash The hash to search for.
+ * @return 0 if the hash was found, -1 if the hash was not found.
+ */
+IK_PUBLIC_API int
+bstv_hash_exists(struct bstv_t* bstv, uint32_t hash);
+
+/*!
+ * @brief Returns a hash that does not yet exist in the bstv.
+ * @note Complexity is O(n)
+ * @param[in] bstv The bstv to generate a hash from.
+ * @return Returns a hash that does not yet exist in the bstv.
+ */
+IK_PUBLIC_API uint32_t
+bstv_find_unused_hash(struct bstv_t* bstv);
+
+/*!
+ * @brief Erases an element from the bstv using a hash.
+ * @warning It is highly discouraged to mix bstv_erase_using_hash() and
+ * bstv_erase_using_key(). Use bstv_erase_using_hash() if you used
+ * bstv_insert_using_hash(). Use bstv_erase_using_key() if you used
+ * bstv_insert_using_key().
+ * @note Complexity is O(log2(n))
+ * @param[in] bstv The bstv to erase from.
+ * @param[in] hash The hash that bstvs to the element to remove from the bstv.
+ * @return Returns the data assocated with the specified hash. If the hash is
+ * not found in the bstv, NULL is returned.
+ * @note The bstv only holds references to values and does **not** FREE them. It
+ * is up to the programmer to correctly free the elements being erased from the
+ * bstv.
+ */
+IK_PUBLIC_API void*
+bstv_erase(struct bstv_t* bstv, uint32_t hash);
+
+IK_PUBLIC_API void*
+bstv_erase_element(struct bstv_t* bstv, void* value);
+
+/*!
+ * @brief Erases the entire bstv, including the underlying memory.
+ * @note This does **not** FREE existing elements. If you have elements in your
+ * bstv and call this, those elements will be lost and a memory leak will have
+ * been created.
+ * @param[in] bstv The bstv to clear.
+ */
+IK_PUBLIC_API void
+bstv_clear(struct bstv_t* bstv);
+
+IK_PUBLIC_API void
+bstv_clear_free(struct bstv_t* bstv);
+
+/*!
+ * @brief Returns the number of elements in the specified bstv.
+ * @param[in] bstv The bstv to count the elements of.
+ * @return The number of elements in the specified bstv.
+ */
+#define bstv_count(bstv) ((bstv)->vector.count)
+
+/*!
+ * @brief Iterates over the specified bstv's elements and opens a FOR_EACH
+ * scope.
+ * @param[in] bstv The bstv to iterate.
+ * @param[in] var_type The type of data being held in the bstv.
+ * @param[in] var The name to give the variable pointing to the current
+ * element.
+ */
+#define BSTV_FOR_EACH(bstv, var_t, hash_v, var_v) {                                                      \
+    uint32_t i_##var_v;                                                                                  \
+    uint32_t hash_v;                                                                                     \
+    var_t* var_v;                                                                                        \
+    for(i_##var_v = 0;                                                                                   \
+        i_##var_v != bstv_count(bstv) &&                                                                 \
+            ((hash_v = ((struct bstv_hash_value_t*) (bstv)->vector.data)[i_##var_v].hash) || 1) &&       \
+            ((var_v  = (var_t*)((struct bstv_hash_value_t*)(bstv)->vector.data)[i_##var_v].value) || 1); \
+        ++i_##var_v) {
+
+/*!
+ * @brief Closes a for each scope previously opened by BSTV_FOR_EACH.
+ */
+#define BSTV_END_EACH }}
+
+/*!
+ * @brief Will erase the current selected item in a for loop from the bstv.
+ * @note This does not free the data being referenced by the bstv. You will have
+ * to erase that manually (either before or after this operation, it doesn't
+ * matter).
+ * @param[in] bstv A pointer to the bstv object currently being iterated.
+ */
+#define BSTV_ERASE_CURRENT_ITEM_IN_FOR_LOOP(bstv, var_v) do { \
+    ordered_vector_erase_element(&(bstv)->vector, ((struct bstv_hash_value_t*)(bstv)->vector.data) + i_##var_v); \
+    --i_##var_v; } while(0)
+
+C_HEADER_END
+
+#endif /* IK_BST_VECTOR_H */

+ 36 - 0
Source/ThirdParty/ik/ik/include/ik/config.h.in

@@ -0,0 +1,36 @@
+/* --------------------------------------------------------------
+ * Configures prerequisits for this library
+ * --------------------------------------------------------------*/
+
+#ifndef IK_CONFIG_HPP
+#   define IK_CONFIG_HPP
+
+    /* --------------------------------------------------------------
+     * build settings
+     * --------------------------------------------------------------*/
+
+#   define OFF    0
+#   define ON     1
+
+#   define HAVE_STDINT_H @HAVE_STDINT_H@
+#   define ik_real @IK_REAL@
+#   define IK_MEMORY_DEBUGGING @IK_MEMORY_DEBUGGING@
+#   if IK_MEMORY_DEBUGGING == ON
+#       define IK_MEMORY_BACKTRACE @IK_MEMORY_BACKTRACE@
+#   endif
+#   define IK_DOT_OUTPUT @IK_DOT_OUTPUT@
+
+    /* --------------------------------------------------------------
+     * common include files
+     * --------------------------------------------------------------*/
+
+#   include "ik/gen/export.h"
+#   include <stddef.h>
+
+#   if HAVE_STDINT_H == ON
+#       include <stdint.h>
+#   else
+#       include "ik/pstdint.h"
+#   endif
+
+#endif /* IK_CONFIG_HPP */

+ 105 - 0
Source/ThirdParty/ik/ik/include/ik/effector.h

@@ -0,0 +1,105 @@
+#ifndef EFFECTOR_H
+#define EFFECTOR_H
+
+#include "ik/gen/config.h"
+#include "ik/vec3.h"
+#include "ik/quat.h"
+
+C_HEADER_BEGIN
+
+struct ik_node_t;
+
+enum effector_flags_e
+{
+    /*!
+     * @brief Causes intermediary weight values to rotate the target around the
+     * chain's root instead of linearly interpolating the target. Can be more
+     * appealing if the solved tree diverges a lot from the original tree
+     * during weight transitions.
+     */
+    EFFECTOR_WEIGHT_NLERP            = 0x01,
+
+    EFFECTOR_INHERIT_PARENT_ROTATION = 0x02
+};
+
+/*!
+ * @brief Specifies how a chain of nodes should be solved. The effector can
+ * be attached to any node in a tree using ik_node_attach_effector(). The
+ * effector specifies the target position and rotation of that node, as well
+ * as how much influence the solver has on the tree (weight) and how many
+ * child nodes are affected (chain_length).
+ */
+struct ik_effector_t
+{
+    /*!
+     * @brief Can be set at any point, and should be updated whenever you have
+     * a new target position to solve for. Specifies the global (world)
+     * position where the node it is attached to should head for.
+     * @note Default value is (0, 0, 0).
+     */
+    vec3_t   target_position;
+
+    /*!
+     * @brief Can be set at any point, and should be updated whenever you have
+     * a new target rotation to solve for. Specifies the global (world)
+     * rotation where the node it is attached to should head for.
+     * @note Default value is the identity quaternion.
+     */
+    quat_t   target_rotation;
+
+    /*!
+     * @brief Specifies how much influence the solver has on the chain of
+     * nodes. A value of 0.0 will cause the solver to completely ignore the
+     * chain, while a value of 1.0 will cause the solver to try to place the
+     * target node directly at target_position/target_rotation.
+     *
+     * This is useful for blending the solver in and out. For instance, if you
+     * wanted to ground the legs of an animated character, you would want the
+     * solver to do nothing during the time when the foot is in the air
+     * (weight=0.0) and be fully active when the foot is on the ground
+     * (weight=1.0).
+     */
+    ik_real  weight;
+
+    ik_real rotation_weight;
+    ik_real rotation_decay;
+
+    /*!
+     * @brief Specifies how many parent nodes should be affected. A value of
+     * 0 means all of the parents, including the root node.
+     * @note Changing the chain length requires the solver tree to be rebuilt
+     * with ik_solver_rebuild_tree().
+     */
+    uint16_t chain_length;
+
+    /*!
+     * @brief Various behavioural settings. Check the enum effector_flags_e for
+     * more information.
+     */
+    uint8_t flags;
+};
+
+/*!
+ * @brief Creates a new effector object. It can be attached to any node in the
+ * tree using ik_node_attach_effector().
+ */
+IK_PUBLIC_API struct ik_effector_t*
+ik_effector_create(void);
+
+/*!
+ * @brief Constructs a previously allocated effector object.
+ */
+IK_PUBLIC_API void
+ik_effector_construct(struct ik_effector_t* effector);
+
+/*!
+ * @brief Destroys and frees an effector object. This should **NOT** be called
+ * on effectors that are attached to nodes. Use ik_node_destroy_effector()
+ * instead.
+ */
+IK_PUBLIC_API void
+ik_effector_destroy(struct ik_effector_t* effector);
+
+C_HEADER_END
+
+#endif /* EFFECTOR_H */

+ 120 - 0
Source/ThirdParty/ik/ik/include/ik/export.h.in

@@ -0,0 +1,120 @@
+/* ----------------------------------------------------------------
+ * Export and visibility amcros
+ * ----------------------------------------------------------------
+ * Substitution variables:
+ *  - PROJECT_NAME          : All-caps variable identifying the
+ *                            name of the project being built.
+ *  - BUILD_TYPE            : Set to either SHARED or STATIC.
+ * Global definitions (non substitution)
+ *  - PROJECT_NAME_BUILDING : Define this if the library is being
+ *                            built.
+ * ------------------------------------------------------------- */
+
+#ifndef IK_EXPORT_H
+#   define IK_EXPORT_H
+
+    /* set @BUILD_TYPE@ to SHARED or STATIC */
+#   define IK_@IK_BUILD_TYPE@
+
+    /* --------------------------------------------------------------
+     * define visibility macros
+     * --------------------------------------------------------------*/
+
+    /* define platform dependent and build dependent visibility macro helpers */
+#   if defined(IK_SHARED)
+#       if defined(IK_PLATFORM_WINDOWS)
+#           if defined(__GNUC__)
+                /* cygwin visbibility */
+#               define IK_HELPER_API_EXPORT __attribute__ ((dllexport))
+#               define IK_HELPER_API_IMPORT __attribute__ ((dllimport))
+#           else
+                /* msvc visibility */
+#               define IK_HELPER_API_EXPORT __declspec(dllexport)
+#               define IK_HELPER_API_IMPORT __declspec(dllimport)
+                /* disable warnings */
+#               pragma warning(disable: 4996) /* 'strcpy': This function or variable may be unsafe */
+#           endif
+#           define IK_HELPER_API_LOCAL
+#       else
+#           if __GNUC__ >= 4
+                /* gcc 4+ visibility */
+#               define IK_HELPER_API_EXPORT __attribute__ ((visibility ("default")))
+#               define IK_HELPER_API_IMPORT __attribute__ ((visibility ("default")))
+#               define IK_HELPER_API_LOCAL  __attribute__ ((visibility ("hidden")))
+#           else
+                /* gcc lower than 4 doesn't have any explicit visibility, everything is exported */
+#               define IK_HELPER_API_EXPORT
+#               define IK_HELPER_API_IMPORT
+#               define IK_HELPER_API_LOCAL
+#           endif
+#       endif
+#   elif defined(IK_STATIC)
+        /* static build */
+#       define IK_HELPER_API_EXPORT
+#       define IK_HELPER_API_IMPORT
+#       define IK_HELPER_API_LOCAL
+#   else
+#       error Please define IK_SHARED or IK_STATIC
+#   endif
+
+    /*
+     * define public API macro, depending on whether the library is being
+     * built or being linked against.
+     */
+#   if defined(IK_BUILDING) /* defined by CMake when library is being compiled */
+#       define IK_PUBLIC_API IK_HELPER_API_EXPORT
+#   else
+#       define IK_PUBLIC_API IK_HELPER_API_IMPORT
+#   endif
+
+    /*
+     * define local visibility macro. If we're testing, everything
+     * is visible
+     */
+#   if defined(TESTING)
+#       define IK_LOCAL_API IK_PUBLIC_API
+#   else
+#       define IK_LOCAL_API IK_HELPER_API_LOCAL
+#   endif
+
+    /*
+     * define class member visibility macros. If we're testing, everything
+     * is public
+     */
+#   if defined(TESTING)
+#       define PUBLIC public
+#       define PROTECTED public
+#       define PRIVATE public
+#   else
+#       define PUBLIC public
+#       define PROTECTED protected
+#       define PRIVATE private
+#   endif
+
+    /* --------------------------------------------------------------
+     * typeof support
+     * --------------------------------------------------------------*/
+
+#   if defined(__GNUC__)
+#       define TYPEOF(x) __typeof__(x)
+#   else
+#       undef TYPEOF
+#   endif
+
+    /* --------------------------------------------------------------
+     * C linkage
+     * --------------------------------------------------------------*/
+
+#   ifdef __cplusplus
+#       define C_HEADER_BEGIN extern "C" {
+#   else
+#       define C_HEADER_BEGIN
+#   endif
+
+#   ifdef __cplusplus
+#       define C_HEADER_END }
+#   else
+#       define C_HEADER_END
+#   endif
+
+#endif /* IK_EXPORT_H */

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

@@ -0,0 +1,8 @@
+#ifndef IK_LIB_H
+#define IK_LIB_H
+
+#include "ik/solver.h"
+#include "ik/node.h"
+#include "ik/effector.h"
+
+#endif /* IK_LIB_H */

+ 33 - 0
Source/ThirdParty/ik/ik/include/ik/log.h

@@ -0,0 +1,33 @@
+#ifndef IK_LOG_H
+#define IK_LOG_H
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+typedef void (*ik_log_cb_func)(const char*);
+
+enum ik_log_e
+{
+    IK_LOG_NONE,
+    IK_LOG_STDOUT
+};
+
+IK_PUBLIC_API void
+ik_log_init(enum ik_log_e options);
+
+IK_PUBLIC_API void
+ik_log_deinit(void);
+
+IK_PUBLIC_API void
+ik_log_register_listener(ik_log_cb_func callback);
+
+IK_PUBLIC_API void
+ik_log_unregister_listener(ik_log_cb_func callback);
+
+IK_PUBLIC_API void
+ik_log_message(const char* fmt, ...);
+
+C_HEADER_END
+
+#endif /* IK_LOG_H */

+ 57 - 0
Source/ThirdParty/ik/ik/include/ik/memory.h

@@ -0,0 +1,57 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include "ik/gen/config.h"
+
+#if IK_MEMORY_DEBUGGING == ON
+#   define MALLOC malloc_wrapper
+#   define FREE   free_wrapper
+#else
+#   include <stdlib.h>
+#   define MALLOC malloc
+#   define FREE   free
+#endif
+
+C_HEADER_BEGIN
+
+/*!
+ * @brief Initialises the memory system.
+ *
+ * In release mode this does nothing. In debug mode it will initialise
+ * memory reports and backtraces, if enabled.
+ */
+IK_PUBLIC_API void
+ik_memory_init(void);
+
+/*!
+ * @brief De-initialises the memory system.
+ *
+ * In release mode this does nothing. In debug mode this will output the memory
+ * report and print backtraces, if enabled.
+ * @return Returns the number of memory leaks.
+ */
+IK_PUBLIC_API uintptr_t
+ik_memory_deinit(void);
+
+#if IK_MEMORY_DEBUGGING == ON
+/*!
+ * @brief Does the same thing as a normal call to malloc(), but does some
+ * additional work to monitor and track down memory leaks.
+ */
+IK_PUBLIC_API void*
+malloc_wrapper(intptr_t size);
+
+/*!
+ * @brief Does the same thing as a normal call to fee(), but does some
+ * additional work to monitor and track down memory leaks.
+ */
+IK_PUBLIC_API void
+free_wrapper(void* ptr);
+#endif /* IK_MEMORY_DEBUGGING */
+
+IK_PUBLIC_API void
+mutated_string_and_hex_dump(void* data, intptr_t size_in_bytes);
+
+C_HEADER_END
+
+#endif /* MEMORY_H */

+ 175 - 0
Source/ThirdParty/ik/ik/include/ik/node.h

@@ -0,0 +1,175 @@
+#ifndef IK_NODE_H
+#define IK_NODE_H
+
+#include "ik/gen/config.h"
+#include "ik/bst_vector.h"
+#include "ik/vec3.h"
+#include "ik/quat.h"
+
+C_HEADER_BEGIN
+
+struct ik_effector_t;
+
+/*!
+ * @brief Represents one node in the tree to be solved.
+ */
+struct ik_node_t
+{
+    /*!
+     * @brief Allows the user of this library to store custom data per node
+     * @note Can be set and retrieved directly without issue.
+     *
+     * This is especially useful in c++ applications which need to store the
+     * "this" pointer to their own scene graph nodes. The user data can be
+     * accessed in callback functions to make object calls again.
+     *
+     * For instance:
+     * ```cpp
+     * // A node in your scene graph
+     * MyNode* node = GetMyNode();
+     *
+     * struct ik_solver_t* solver = ik_solver_create(SOLVER_FABRIK);
+     * struct ik_node_t* ikNode = ik_node_create(node->GetID());
+     * ikNode->user_data = node; // Store pointer to your own node object
+     *
+     * // ---- elsewhere ------
+     * static void ApplyResultsCallback(ik_node_t* ikNode)
+     * {
+     *     Node* node = (Node*)ikNode->user_data; // Extract your own node object again
+     *     node->SetPosition(ikNode->solved_position);
+     * }
+     * ```
+     */
+    void* user_data;
+
+    /*!
+     * @brief The initial global position (in world space).
+     * @note Must be set by the user to get correct results. This value can
+     * be set and retrieved at any time.
+     * @note The default value is (0, 0, 0).
+     */
+    vec3_t position;
+
+    /*!
+     * @brief The initial global rotation (in world space).
+     * @note Must be set by the user to get correct results if the solver has
+     * angle computations enabled (SOLVER_CALCULATE_FINAL_ANGLES).
+     * @note The default value is the identity quaternion.
+     */
+    quat_t rotation;
+
+    /*!
+     * @brief Global identifier for this node. The identifier must be unique
+     * within the tree, but separate trees may re-use the same IDs again. The
+     * ID can later be used to retrieve nodes from the tree again.
+     */
+    uint32_t guid;
+
+    /*!
+     * @brief After the solver is executed, the solved global (world) position
+     * is stored here and can be retrieved.
+     */
+    vec3_t solved_position;
+
+    /*!
+     * @brief After the solver is executed, the solved global (world) rotation
+     * is stored here and can be retrieved.
+     */
+    quat_t solved_rotation;
+
+    /*!
+     * @brief The end effector object.
+     * @note This pointer should not be changed directly. You can however set
+     * the target position/rotation of the effector by writing to
+     * node->effector->target_position or node->effector->target_rotation.
+     * @note May be NULL.
+     */
+    struct ik_effector_t* effector;
+
+    /* Private data */
+    ik_real segment_length;
+    struct ik_node_t* parent;
+    struct bstv_t children;
+};
+
+/*!
+ * @brief Creates a new node and returns it. Each node requires a tree-unique
+ * ID, which can be used later to search for nodes in the tree.
+ */
+IK_PUBLIC_API struct ik_node_t*
+ik_node_create(uint32_t guid);
+
+/*!
+ * @brief Constructs an already allocated node.
+ */
+IK_PUBLIC_API void
+ik_node_construct(struct ik_node_t* node, uint32_t guid);
+
+/*!
+ * @brief Destructs a node, destroying all children in the process, but does
+ * not deallocate the node object itself.
+ */
+IK_PUBLIC_API void
+ik_node_destruct(struct ik_node_t* node);
+
+/*!
+ * @brief Destructs and frees the node, destroying all children in the process.
+ * If the node was part of a tree, then it will be removed from its parents.
+ * @note You will need to rebuild the solver's tree before solving.
+ */
+IK_PUBLIC_API void
+ik_node_destroy(struct ik_node_t* node);
+
+/*!
+ * @brief Attaches a node as a child to another node. The parent node gains
+ * ownership of the child node and is responsible for deallocating it.
+ * @note You will need to rebuild the solver's tree before solving.
+ */
+IK_PUBLIC_API void
+ik_node_add_child(struct ik_node_t* node, struct ik_node_t* child);
+
+/*!
+ * @brief Unlinks a node from the tree, without destroying anything. All
+ * children of the unlinked node remain in tact and will no longer be
+ * affiliated with the original tree.
+ * @note You will need to rebuild the solver's tree before solving.
+ */
+IK_PUBLIC_API void
+ik_node_unlink(struct ik_node_t* node);
+
+/*!
+ * @brief Searches recursively for a node in a tree with the specified global
+ * identifier.
+ * @return Returns NULL if the node was not found, otherwise the node is
+ * returned.
+ */
+IK_PUBLIC_API struct ik_node_t*
+ik_node_find_child(struct ik_node_t* node, uint32_t guid);
+
+/*!
+ * @brief Attaches an effector object to the node. The node gains ownership
+ * of the effector and is responsible for its deallocation. If the node
+ * already owns an effector, then it is first destroyed.
+ * @note You will need to rebuild the solver's tree before solving.
+ */
+IK_PUBLIC_API void
+ik_node_attach_effector(struct ik_node_t* node, struct ik_effector_t* effector);
+
+/*!
+ * @brief Removes and destroys the node's effector, if it exists. The attribute
+ * node->effector is set to NULL.
+ * @note You will need to rebuild the solver's tree before solving.
+ */
+IK_PUBLIC_API void
+ik_node_destroy_effector(struct ik_node_t* node);
+
+/*!
+ * @brief Dumps all nodes recursively to DOT format. You can use graphviz (
+ * or other compatible tools) to generate a graphic of the tree.
+ */
+IK_PUBLIC_API void
+ik_node_dump_to_dot(struct ik_node_t* node, const char* file_name);
+
+C_HEADER_END
+
+#endif /* IK_NODE_H */

+ 296 - 0
Source/ThirdParty/ik/ik/include/ik/ordered_vector.h

@@ -0,0 +1,296 @@
+/*!
+ * @file ordered_vector.h
+ * @brief Dynamic contiguous sequence container with guaranteed element order.
+ * @page ordered_vector Ordered Vector
+ *
+ * Ordered vectors arrange all inserted elements next to each other in memory.
+ * Because of this, vector access is just as efficient as a normal array, but
+ * they are able to grow and shrink in size automatically.
+ */
+
+#ifndef ORDERED_VECTOR_H
+#define ORDERED_VECTOR_H
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+#define DATA_POINTER_TYPE unsigned char
+struct ordered_vector_t
+{
+    uint32_t element_size;       /* how large one element is in bytes */
+    uint32_t capacity;           /* how many elements actually fit into the allocated space */
+    uint32_t count;              /* number of elements inserted */
+    DATA_POINTER_TYPE* data;     /* pointer to the contiguous section of memory */
+};
+
+/*!
+ * @brief Creates a new vector object. See @ref ordered_vector for details.
+ * @param[in] element_size Specifies the size in bytes of the type of data you want
+ * the vector to store. Typically one would pass sizeof(my_data_type).
+ * @return Returns the newly created vector object.
+ */
+IK_PUBLIC_API struct ordered_vector_t*
+ordered_vector_create(const uint32_t element_size);
+
+/*!
+ * @brief Initialises an existing vector object.
+ * @note This does **not** free existing memory. If you've pushed elements
+ * into your vector and call this, you will have created a memory leak.
+ * @param[in] vector The vector to initialise.
+ * @param[in] element_size Specifies the size in bytes of the type of data you
+ * want the vector to store. Typically one would pass sizeof(my_data_type).
+ */
+IK_PUBLIC_API void
+ordered_vector_construct(struct ordered_vector_t* vector,
+                           const uint32_t element_size);
+
+/*!
+ * @brief Destroys an existing vector object and frees all memory allocated by
+ * inserted elements.
+ * @param[in] vector The vector to destroy.
+ */
+IK_PUBLIC_API void
+ordered_vector_destroy(struct ordered_vector_t* vector);
+
+/*!
+ * @brief Erases all elements in a vector.
+ * @note This does not actually erase the underlying memory, it simply resets
+ * the element counter. If you wish to free the underlying memory, see
+ * ordered_vector_clear_free().
+ * @param[in] vector The vector to clear.
+ */
+IK_PUBLIC_API void
+ordered_vector_clear(struct ordered_vector_t* vector);
+
+/*!
+ * @brief Erases all elements in a vector and frees their memory.
+ * @param[in] vector The vector to clear.
+ */
+IK_PUBLIC_API void
+ordered_vector_clear_free(struct ordered_vector_t* vector);
+
+/*!
+ * @brief Sets the size of the vector to exactly the size specified. If the
+ * vector was smaller then memory will be reallocated. If the vector was larger
+ * then no reallocation will occur. The capacity will remain the same and the
+ * size will be decreased.
+ * @param[in] vector The vector to resize.
+ * @param[in] size The new size of the vector.
+ * @return Returns -1 on failure, 0 on success.
+ */
+IK_PUBLIC_API int
+ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size);
+
+/*!
+ * @brief Gets the number of elements that have been inserted into the vector.
+ */
+#define ordered_vector_count(x) ((x)->count)
+
+/*!
+ * @brief Inserts (copies) a new element at the head of the vector.
+ * @note This can cause a re-allocation of the underlying memory. This
+ * implementation expands the allocated memory by a factor of 2 every time a
+ * re-allocation occurs to cut down on the frequency of re-allocations.
+ * @note If you do not wish to copy data into the vector, but merely make
+ * space, see ordered_vector_push_emplace().
+ * @param[in] vector The vector to push into.
+ * @param[in] data The data to copy into the vector. It is assumed that
+ * sizeof(data) is equal to what was specified when the vector was first
+ * created. If this is not the case then it could cause undefined behaviour.
+ * @return Returns 0 if the data was successfully pushed, -1 if
+ * otherwise.
+ */
+IK_PUBLIC_API int
+ordered_vector_push(struct ordered_vector_t* vector, void* data);
+
+/*!
+ * @brief Allocates space for a new element at the head of the vector, but does
+ * not initialise it.
+ * @warning The returned pointer could be invalidated if any other
+ * vector related function is called, as the underlying memory of the vector
+ * could be re-allocated. Use the pointer immediately after calling this
+ * function.
+ * @param[in] vector The vector to emplace an element into.
+ * @return A pointer to the allocated memory for the requested element. See
+ * warning and use with caution.
+ */
+IK_PUBLIC_API void*
+ordered_vector_push_emplace(struct ordered_vector_t* vector);
+
+/*!
+ * @brief Copies the contents of another vector and pushes it into the vector.
+ * @return Returns 0 if successful, -1 if otherwise.
+ */
+IK_PUBLIC_API int
+ordered_vector_push_vector(struct ordered_vector_t* vector, struct ordered_vector_t* source_vector);
+
+/*!
+ * @brief Removes an element from the back (end) of the vector.
+ * @warning The returned pointer could be invalidated if any other
+ * vector related function is called, as the underlying memory of the vector
+ * could be re-allocated. Use the pointer immediately after calling this
+ * function.
+ * @param[in] vector The vector to pop an element from.
+ * @return A pointer to the popped element. See warning and use with caution.
+ * If there are no elements to pop, NULL is returned.
+ */
+IK_PUBLIC_API void*
+ordered_vector_pop(struct ordered_vector_t* vector);
+
+/*!
+ * @brief Returns the very last element of the vector.
+ * @warning The returned pointer could be invalidated if any other vector
+ * related function is called, as the underlying memory of the vector could be
+ * re-allocated. Use the pointer immediately after calling this function.
+ *
+ * @param[in] vector The vector to return the last element from.
+ * @return A pointer to the last element. See warning and use with caution.
+ * If there are no elements in the vector, NULL is returned.
+ */
+IK_PUBLIC_API void*
+ordered_vector_back(const struct ordered_vector_t* vector);
+
+/*!
+ * @brief Allocates space for a new element at the specified index, but does
+ * not initialise it.
+ * @note This can cause a re-allocation of the underlying memory. This
+ * implementation expands the allocated memory by a factor of 2 every time a
+ * re-allocation occurs to cut down on the frequency of re-allocations.
+ * @warning The returned pointer could be invalidated if any other
+ * vector related function is called, as the underlying memory of the vector
+ * could be re-allocated. Use the pointer immediately after calling this
+ * function.
+ * @param[in] vector The vector to emplace an element into.
+ * @param[in] index Where to insert.
+ * @return A pointer to the emplaced element. See warning and use with caution.
+ */
+IK_PUBLIC_API void*
+ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index);
+
+/*!
+ * @brief Inserts (copies) a new element at the specified index.
+ * @note This can cause a re-allocation of the underlying memory. This
+ * implementation expands the allocated memory by a factor of 2 every time a
+ * re-allocation occurs to cut down on the frequency of re-allocations.
+ * @note If you do not wish to copy data into the vector, but merely make
+ * space, see ordered_vector_insert_emplace().
+ * @param[in] vector The vector to insert into.
+ * @param[in] data The data to copy into the vector. It is assumed that
+ * sizeof(data) is equal to what was specified when the vector was first
+ * created. If this is not the case then it could cause undefined behaviour.
+ */
+IK_PUBLIC_API int
+ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* data);
+
+/*!
+ * @brief Erases the specified element from the vector.
+ * @note This causes all elements with indices greater than **index** to be
+ * re-allocated (shifted 1 element down) so the vector remains contiguous.
+ * @param[in] index The position of the element in the vector to erase. The index
+ * ranges from **0** to **ordered_vector_count()-1**.
+ */
+IK_PUBLIC_API void
+ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index);
+
+/*!
+ * @brief Removes the element in the vector pointed to by **element**.
+ * @param[in] vector The vector from which to erase the data.
+ * @param[in] element A pointer to an element within the vector.
+ */
+IK_PUBLIC_API void
+ordered_vector_erase_element(struct ordered_vector_t* vector, void* element);
+
+/*!
+ * @brief Gets a pointer to the specified element in the vector.
+ * @warning The returned pointer could be invalidated if any other
+ * vector related function is called, as the underlying memory of the vector
+ * could be re-allocated. Use the pointer immediately after calling this
+ * function.
+ * @param[in] vector The vector to get the element from.
+ * @param[in] index The index of the element to get. The index ranges from
+ * **0** to **ordered_vector_count()-1**.
+ * @return [in] A pointer to the element. See warning and use with caution.
+ * If the specified element doesn't exist (index out of bounds), NULL is
+ * returned.
+ */
+IK_PUBLIC_API void*
+ordered_vector_get_element(struct ordered_vector_t*, uint32_t index);
+
+/*!
+ * @brief Convenient macro for iterating a vector's elements.
+ *
+ * Example:
+ * ```
+ * ordered_vector_t* some_vector = (a vector containing elements of type "struct bar")
+ * ORDERED_VECTOR_FOR_EACH(some_vector, struct bar, element)
+ * {
+ *     do_something_with(element);  ("element" is now of type "struct bar*")
+ * }
+ * ```
+ * @param[in] vector A pointer to the vector to iterate.
+ * @param[in] var_type Should be the type of data stored in the vector.
+ * @param[in] var The name of a temporary variable you'd like to use within the
+ * for-loop to reference the current element.
+ */
+#define ORDERED_VECTOR_FOR_EACH(vector, var_type, var) {                     \
+    var_type* var;                                                           \
+    DATA_POINTER_TYPE* internal_##var_end_of_vector = (vector)->data + (vector)->count * (vector)->element_size; \
+    for(var = (var_type*)(vector)->data;                                     \
+        (DATA_POINTER_TYPE*)var != internal_##var_end_of_vector;             \
+        var = (var_type*)(((DATA_POINTER_TYPE*)var) + (vector)->element_size)) {
+
+
+#define ORDERED_VECTOR_FOR_EACH_R(vector, var_type, var) {                   \
+    var_type* var;                                                           \
+    DATA_POINTER_TYPE* internal_##var_start_of_vector = (vector)->data - (vector)->element_size; \
+    for(var = (var_type*)((vector)->data + (vector)->count * (vector)->element_size - (vector)->element_size); \
+        (DATA_POINTER_TYPE*)var != internal_##var_start_of_vector;          \
+        var = (var_type*)(((DATA_POINTER_TYPE*)var) - (vector)->element_size)) {
+
+/*!
+ * @brief Convenient macro for iterating a range of a vector's elements.
+ * @param[in] vector A pointer to the vector to iterate.
+ * @param[in] var_type Should be the type of data stored in the vector. For
+ * example, if your vector is storing ```struct type_t*``` objects then
+ * var_type should equal ```struct type_t``` (without the pointer).
+ * @param[in] var The name of a temporary variable you'd like to use within the
+ * for loop to reference the current element.
+ * @param[in] begin_index The index (starting at 0) of the first element to
+ * start with.
+ * @param[in] end_index The index of the last element to iterate (exclusive).
+ */
+#define ORDERED_VECTOR_FOR_EACH_RANGE(vector, var_type, var, begin_index, end_index) { \
+    var_type* var;                                                                     \
+    DATA_POINTER_TYPE* internal_##var_end_of_vector = (vector)->data + end_index * (vector)->element_size; \
+    for(var = (var_type*)((vector)->data + begin_index * (vector)->element_size);      \
+        (DATA_POINTER_TYPE*)var != internal_##var_end_of_vector;                       \
+        var = (var_type*)(((DATA_POINTER_TYPE*)var) + (vector)->element_size)) {
+
+/*!
+ * @brief Closes a for each scope previously opened by ORDERED_VECTOR_FOR_EACH.
+ */
+#define ORDERED_VECTOR_END_EACH }}
+
+/*!
+ * @brief Convenient macro for erasing an element while iterating a vector.
+ * @warning Only call this while iterating.
+ * Example:
+ * ```
+ * ORDERED_VECTOR_FOR_EACH(some_vector, struct bar, element)
+ * {
+ *     ORDERED_VECTOR_ERASE_IN_FOR_LOOP(some_vector, struct bar, element);
+ * }
+ * ```
+ * @param[in] vector The vector to erase from.
+ * @param[in] var_type Should be the type of data stored in the vector.
+ * @param[in] element The element to erase.
+ */
+#define ORDERED_VECTOR_ERASE_IN_FOR_LOOP(vector, element_type, element)                \
+    ordered_vector_erase_element(vector, element);                                     \
+    element = (element_type*)(((DATA_POINTER_TYPE*)element) - (vector)->element_size); \
+    internal_##var_end_of_vector = (vector)->data + (vector)->count * (vector)->element_size;
+
+C_HEADER_END
+
+#endif /* ORDERED_VECTOR_H */

+ 813 - 0
Source/ThirdParty/ik/ik/include/ik/pstdint.h

@@ -0,0 +1,813 @@
+/*  A portable stdint.h
+ ****************************************************************************
+ *  BSD License:
+ ****************************************************************************
+ *
+ *  Copyright (c) 2005-2014 Paul Hsieh
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************
+ *
+ *  Version 0.1.14
+ *
+ *  The ANSI C standard committee, for the C99 standard, specified the
+ *  inclusion of a new standard include file called stdint.h.  This is
+ *  a very useful and long desired include file which contains several
+ *  very precise definitions for integer scalar types that is
+ *  critically important for making portable several classes of
+ *  applications including cryptography, hashing, variable length
+ *  integer libraries and so on.  But for most developers its likely
+ *  useful just for programming sanity.
+ *
+ *  The problem is that most compiler vendors have decided not to
+ *  implement the C99 standard, and the next C++ language standard
+ *  (which has a lot more mindshare these days) will be a long time in
+ *  coming and its unknown whether or not it will include stdint.h or
+ *  how much adoption it will have.  Either way, it will be a long time
+ *  before all compilers come with a stdint.h and it also does nothing
+ *  for the extremely large number of compilers available today which
+ *  do not include this file, or anything comparable to it.
+ *
+ *  So that's what this file is all about.  Its an attempt to build a
+ *  single universal include file that works on as many platforms as
+ *  possible to deliver what stdint.h is supposed to.  A few things
+ *  that should be noted about this file:
+ *
+ *    1) It is not guaranteed to be portable and/or present an identical
+ *       interface on all platforms.  The extreme variability of the
+ *       ANSI C standard makes this an impossibility right from the
+ *       very get go. Its really only meant to be useful for the vast
+ *       majority of platforms that possess the capability of
+ *       implementing usefully and precisely defined, standard sized
+ *       integer scalars.  Systems which are not intrinsically 2s
+ *       complement may produce invalid constants.
+ *
+ *    2) There is an unavoidable use of non-reserved symbols.
+ *
+ *    3) Other standard include files are invoked.
+ *
+ *    4) This file may come in conflict with future platforms that do
+ *       include stdint.h.  The hope is that one or the other can be
+ *       used with no real difference.
+ *
+ *    5) In the current verison, if your platform can't represent
+ *       int32_t, int16_t and int8_t, it just dumps out with a compiler
+ *       error.
+ *
+ *    6) 64 bit integers may or may not be defined.  Test for their
+ *       presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
+ *       Note that this is different from the C99 specification which
+ *       requires the existence of 64 bit support in the compiler.  If
+ *       this is not defined for your platform, yet it is capable of
+ *       dealing with 64 bits then it is because this file has not yet
+ *       been extended to cover all of your system's capabilities.
+ *
+ *    7) (u)intptr_t may or may not be defined.  Test for its presence
+ *       with the test: #ifdef PTRDIFF_MAX.  If this is not defined
+ *       for your platform, then it is because this file has not yet
+ *       been extended to cover all of your system's capabilities, not
+ *       because its optional.
+ *
+ *    8) The following might not been defined even if your platform is
+ *       capable of defining it:
+ *
+ *       WCHAR_MIN
+ *       WCHAR_MAX
+ *       (u)int64_t
+ *       PTRDIFF_MIN
+ *       PTRDIFF_MAX
+ *       (u)intptr_t
+ *
+ *    9) The following have not been defined:
+ *
+ *       WINT_MIN
+ *       WINT_MAX
+ *
+ *   10) The criteria for defining (u)int_least(*)_t isn't clear,
+ *       except for systems which don't have a type that precisely
+ *       defined 8, 16, or 32 bit types (which this include file does
+ *       not support anyways). Default definitions have been given.
+ *
+ *   11) The criteria for defining (u)int_fast(*)_t isn't something I
+ *       would trust to any particular compiler vendor or the ANSI C
+ *       committee.  It is well known that "compatible systems" are
+ *       commonly created that have very different performance
+ *       characteristics from the systems they are compatible with,
+ *       especially those whose vendors make both the compiler and the
+ *       system.  Default definitions have been given, but its strongly
+ *       recommended that users never use these definitions for any
+ *       reason (they do *NOT* deliver any serious guarantee of
+ *       improved performance -- not in this file, nor any vendor's
+ *       stdint.h).
+ *
+ *   12) The following macros:
+ *
+ *       PRINTF_INTMAX_MODIFIER
+ *       PRINTF_INT64_MODIFIER
+ *       PRINTF_INT32_MODIFIER
+ *       PRINTF_INT16_MODIFIER
+ *       PRINTF_LEAST64_MODIFIER
+ *       PRINTF_LEAST32_MODIFIER
+ *       PRINTF_LEAST16_MODIFIER
+ *       PRINTF_INTPTR_MODIFIER
+ *
+ *       are strings which have been defined as the modifiers required
+ *       for the "d", "u" and "x" printf formats to correctly output
+ *       (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
+ *       (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
+ *       PRINTF_INTPTR_MODIFIER is not defined for some systems which
+ *       provide their own stdint.h.  PRINTF_INT64_MODIFIER is not
+ *       defined if INT64_MAX is not defined.  These are an extension
+ *       beyond what C99 specifies must be in stdint.h.
+ *
+ *       In addition, the following macros are defined:
+ *
+ *       PRINTF_INTMAX_HEX_WIDTH
+ *       PRINTF_INT64_HEX_WIDTH
+ *       PRINTF_INT32_HEX_WIDTH
+ *       PRINTF_INT16_HEX_WIDTH
+ *       PRINTF_INT8_HEX_WIDTH
+ *       PRINTF_INTMAX_DEC_WIDTH
+ *       PRINTF_INT64_DEC_WIDTH
+ *       PRINTF_INT32_DEC_WIDTH
+ *       PRINTF_INT16_DEC_WIDTH
+ *       PRINTF_INT8_DEC_WIDTH
+ *
+ *       Which specifies the maximum number of characters required to
+ *       print the number of that type in either hexadecimal or decimal.
+ *       These are an extension beyond what C99 specifies must be in
+ *       stdint.h.
+ *
+ *  Compilers tested (all with 0 warnings at their highest respective
+ *  settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
+ *  bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
+ *  .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
+ *
+ *  This file should be considered a work in progress.  Suggestions for
+ *  improvements, especially those which increase coverage are strongly
+ *  encouraged.
+ *
+ *  Acknowledgements
+ *
+ *  The following people have made significant contributions to the
+ *  development and testing of this file:
+ *
+ *  Chris Howie
+ *  John Steele Scott
+ *  Dave Thorup
+ *  John Dill
+ *  Florian Wobbe
+ *  Christopher Sean Morrison
+ *
+ */
+
+#include <stddef.h>
+#include <limits.h>
+#include <signal.h>
+
+/*
+ *  For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
+ *  do nothing else.  On the Mac OS X version of gcc this is _STDINT_H_.
+ */
+
+#if ((defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include <stdint.h>
+#define _PSTDINT_H_INCLUDED
+# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__))
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "l"
+#  endif
+#  ifndef PRINTF_INT32_MODIFIER
+#   define PRINTF_INT32_MODIFIER ""
+#  endif
+# else
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+#  ifndef PRINTF_INT32_MODIFIER
+#   define PRINTF_INT32_MODIFIER "l"
+#  endif
+# endif
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+# ifndef PRINTF_INTMAX_MODIFIER
+#  define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INT64_HEX_WIDTH
+#  define PRINTF_INT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_INT32_HEX_WIDTH
+#  define PRINTF_INT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_INT16_HEX_WIDTH
+#  define PRINTF_INT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_INT8_HEX_WIDTH
+#  define PRINTF_INT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_INT64_DEC_WIDTH
+#  define PRINTF_INT64_DEC_WIDTH "20"
+# endif
+# ifndef PRINTF_INT32_DEC_WIDTH
+#  define PRINTF_INT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_INT16_DEC_WIDTH
+#  define PRINTF_INT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_INT8_DEC_WIDTH
+#  define PRINTF_INT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+
+/*
+ *  Something really weird is going on with Open Watcom.  Just pull some of
+ *  these duplicated definitions from Open Watcom's stdint.h file for now.
+ */
+
+# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
+#  if !defined (INT64_C)
+#   define INT64_C(x)   (x + (INT64_MAX - INT64_MAX))
+#  endif
+#  if !defined (UINT64_C)
+#   define UINT64_C(x)  (x + (UINT64_MAX - UINT64_MAX))
+#  endif
+#  if !defined (INT32_C)
+#   define INT32_C(x)   (x + (INT32_MAX - INT32_MAX))
+#  endif
+#  if !defined (UINT32_C)
+#   define UINT32_C(x)  (x + (UINT32_MAX - UINT32_MAX))
+#  endif
+#  if !defined (INT16_C)
+#   define INT16_C(x)   (x)
+#  endif
+#  if !defined (UINT16_C)
+#   define UINT16_C(x)  (x)
+#  endif
+#  if !defined (INT8_C)
+#   define INT8_C(x)   (x)
+#  endif
+#  if !defined (UINT8_C)
+#   define UINT8_C(x)  (x)
+#  endif
+#  if !defined (UINT64_MAX)
+#   define UINT64_MAX  18446744073709551615ULL
+#  endif
+#  if !defined (INT64_MAX)
+#   define INT64_MAX  9223372036854775807LL
+#  endif
+#  if !defined (UINT32_MAX)
+#   define UINT32_MAX  4294967295UL
+#  endif
+#  if !defined (INT32_MAX)
+#   define INT32_MAX  2147483647L
+#  endif
+#  if !defined (INTMAX_MAX)
+#   define INTMAX_MAX INT64_MAX
+#  endif
+#  if !defined (INTMAX_MIN)
+#   define INTMAX_MIN INT64_MIN
+#  endif
+# endif
+#endif
+
+#ifndef _PSTDINT_H_INCLUDED
+#define _PSTDINT_H_INCLUDED
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~(size_t)0)
+#endif
+
+/*
+ *  Deduce the type assignments from limits.h under the assumption that
+ *  integer sizes in bits are powers of 2, and follow the ANSI
+ *  definitions.
+ */
+
+#ifndef UINT8_MAX
+# define UINT8_MAX 0xff
+#endif
+#if !defined(uint8_t) && !defined(_UINT8_T)
+# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
+	typedef unsigned char uint8_t;
+#   define UINT8_C(v) ((uint8_t) v)
+# else
+#   error "Platform not supported"
+# endif
+#endif
+
+#ifndef INT8_MAX
+# define INT8_MAX 0x7f
+#endif
+#ifndef INT8_MIN
+# define INT8_MIN INT8_C(0x80)
+#endif
+#if !defined(int8_t) && !defined(_INT8_T)
+# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
+	typedef signed char int8_t;
+#   define INT8_C(v) ((int8_t) v)
+# else
+#   error "Platform not supported"
+# endif
+#endif
+
+#ifndef UINT16_MAX
+# define UINT16_MAX 0xffff
+#endif
+#if !defined(uint16_t) && !defined(_UINT16_T)
+#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
+  typedef unsigned int uint16_t;
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER ""
+# endif
+# define UINT16_C(v) ((uint16_t) (v))
+#elif (USHRT_MAX == UINT16_MAX)
+  typedef unsigned short uint16_t;
+# define UINT16_C(v) ((uint16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT16_MAX
+# define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN INT16_C(0x8000)
+#endif
+#if !defined(int16_t) && !defined(_INT16_T)
+#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
+  typedef signed int int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT16_MAX)
+  typedef signed short int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef UINT32_MAX
+# define UINT32_MAX (0xffffffffUL)
+#endif
+#if !defined(uint32_t) && !defined(_UINT32_T)
+#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
+  typedef unsigned long uint32_t;
+# define UINT32_C(v) v ## UL
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (UINT_MAX == UINT32_MAX)
+  typedef unsigned int uint32_t;
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+# define UINT32_C(v) v ## U
+#elif (USHRT_MAX == UINT32_MAX)
+  typedef unsigned short uint32_t;
+# define UINT32_C(v) ((unsigned short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX (0x7fffffffL)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN INT32_C(0x80000000)
+#endif
+#if !defined(int32_t) && !defined(_INT32_T)
+#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
+  typedef signed long int32_t;
+# define INT32_C(v) v ## L
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (INT_MAX == INT32_MAX)
+  typedef signed int int32_t;
+# define INT32_C(v) v
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT32_MAX)
+  typedef signed short int32_t;
+# define INT32_C(v) ((short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+/*
+ *  The macro stdint_int64_defined is temporarily used to record
+ *  whether or not 64 integer support is available.  It must be
+ *  defined for any 64 integer extensions for new platforms that are
+ *  added.
+ */
+
+#undef stdint_int64_defined
+#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
+# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S)
+#  define stdint_int64_defined
+   typedef long long int64_t;
+   typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# endif
+#endif
+
+#if !defined (stdint_int64_defined)
+# if defined(__GNUC__)
+#  define stdint_int64_defined
+   __extension__ typedef long long int64_t;
+   __extension__ typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
+#  define stdint_int64_defined
+   typedef long long int64_t;
+   typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
+#  define stdint_int64_defined
+   typedef __int64 int64_t;
+   typedef unsigned __int64 uint64_t;
+#  define UINT64_C(v) v ## UI64
+#  define  INT64_C(v) v ## I64
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "I64"
+#  endif
+# endif
+#endif
+
+#if !defined (LONG_LONG_MAX) && defined (INT64_C)
+# define LONG_LONG_MAX INT64_C (9223372036854775807)
+#endif
+#ifndef ULONG_LONG_MAX
+# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
+#endif
+
+#if !defined (INT64_MAX) && defined (INT64_C)
+# define INT64_MAX INT64_C (9223372036854775807)
+#endif
+#if !defined (INT64_MIN) && defined (INT64_C)
+# define INT64_MIN INT64_C (-9223372036854775808)
+#endif
+#if !defined (UINT64_MAX) && defined (INT64_C)
+# define UINT64_MAX UINT64_C (18446744073709551615)
+#endif
+
+/*
+ *  Width of hexadecimal for number field.
+ */
+
+#ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+#endif
+#ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+#endif
+#ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+#endif
+#ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+#endif
+
+#ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "20"
+#endif
+#ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+#endif
+
+/*
+ *  Ok, lets not worry about 128 bit integers for now.  Moore's law says
+ *  we don't need to worry about that until about 2040 at which point
+ *  we'll have bigger things to worry about.
+ */
+
+#ifdef stdint_int64_defined
+  typedef int64_t intmax_t;
+  typedef uint64_t uintmax_t;
+# define  INTMAX_MAX   INT64_MAX
+# define  INTMAX_MIN   INT64_MIN
+# define UINTMAX_MAX  UINT64_MAX
+# define UINTMAX_C(v) UINT64_C(v)
+# define  INTMAX_C(v)  INT64_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+#   define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+#else
+  typedef int32_t intmax_t;
+  typedef uint32_t uintmax_t;
+# define  INTMAX_MAX   INT32_MAX
+# define UINTMAX_MAX  UINT32_MAX
+# define UINTMAX_C(v) UINT32_C(v)
+# define  INTMAX_C(v)  INT32_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+#   define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
+# endif
+#endif
+
+/*
+ *  Because this file currently only supports platforms which have
+ *  precise powers of 2 as bit sizes for the default integers, the
+ *  least definitions are all trivial.  Its possible that a future
+ *  version of this file could have different definitions.
+ */
+
+#ifndef stdint_least_defined
+  typedef   int8_t   int_least8_t;
+  typedef  uint8_t  uint_least8_t;
+  typedef  int16_t  int_least16_t;
+  typedef uint16_t uint_least16_t;
+  typedef  int32_t  int_least32_t;
+  typedef uint32_t uint_least32_t;
+# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
+# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
+# define  UINT_LEAST8_MAX  UINT8_MAX
+# define   INT_LEAST8_MAX   INT8_MAX
+# define UINT_LEAST16_MAX UINT16_MAX
+# define  INT_LEAST16_MAX  INT16_MAX
+# define UINT_LEAST32_MAX UINT32_MAX
+# define  INT_LEAST32_MAX  INT32_MAX
+# define   INT_LEAST8_MIN   INT8_MIN
+# define  INT_LEAST16_MIN  INT16_MIN
+# define  INT_LEAST32_MIN  INT32_MIN
+# ifdef stdint_int64_defined
+	typedef  int64_t  int_least64_t;
+	typedef uint64_t uint_least64_t;
+#   define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
+#   define UINT_LEAST64_MAX UINT64_MAX
+#   define  INT_LEAST64_MAX  INT64_MAX
+#   define  INT_LEAST64_MIN  INT64_MIN
+# endif
+#endif
+#undef stdint_least_defined
+
+/*
+ *  The ANSI C committee pretending to know or specify anything about
+ *  performance is the epitome of misguided arrogance.  The mandate of
+ *  this file is to *ONLY* ever support that absolute minimum
+ *  definition of the fast integer types, for compatibility purposes.
+ *  No extensions, and no attempt to suggest what may or may not be a
+ *  faster integer type will ever be made in this file.  Developers are
+ *  warned to stay away from these types when using this or any other
+ *  stdint.h.
+ */
+
+typedef   int_least8_t   int_fast8_t;
+typedef  uint_least8_t  uint_fast8_t;
+typedef  int_least16_t  int_fast16_t;
+typedef uint_least16_t uint_fast16_t;
+typedef  int_least32_t  int_fast32_t;
+typedef uint_least32_t uint_fast32_t;
+#define  UINT_FAST8_MAX  UINT_LEAST8_MAX
+#define   INT_FAST8_MAX   INT_LEAST8_MAX
+#define UINT_FAST16_MAX UINT_LEAST16_MAX
+#define  INT_FAST16_MAX  INT_LEAST16_MAX
+#define UINT_FAST32_MAX UINT_LEAST32_MAX
+#define  INT_FAST32_MAX  INT_LEAST32_MAX
+#define   INT_FAST8_MIN   INT_LEAST8_MIN
+#define  INT_FAST16_MIN  INT_LEAST16_MIN
+#define  INT_FAST32_MIN  INT_LEAST32_MIN
+#ifdef stdint_int64_defined
+  typedef  int_least64_t  int_fast64_t;
+  typedef uint_least64_t uint_fast64_t;
+# define UINT_FAST64_MAX UINT_LEAST64_MAX
+# define  INT_FAST64_MAX  INT_LEAST64_MAX
+# define  INT_FAST64_MIN  INT_LEAST64_MIN
+#endif
+
+#undef stdint_int64_defined
+
+/*
+ *  Whatever piecemeal, per compiler thing we can do about the wchar_t
+ *  type limits.
+ */
+
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__)
+# include <wchar.h>
+# ifndef WCHAR_MIN
+#  define WCHAR_MIN 0
+# endif
+# ifndef WCHAR_MAX
+#  define WCHAR_MAX ((wchar_t)-1)
+# endif
+#endif
+
+/*
+ *  Whatever piecemeal, per compiler/platform thing we can do about the
+ *  (u)intptr_t types and limits.
+ */
+
+#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__)
+#  define stdint_intptr_bits 64
+# elif defined (__WATCOMC__) || defined (__TURBOC__)
+#  if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
+#    define stdint_intptr_bits 16
+#  else
+#    define stdint_intptr_bits 32
+#  endif
+# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__)
+#  define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what did Intel do about x86-64? */
+# else
+/* #error "This platform might not be supported yet" */
+# endif
+
+# ifdef stdint_intptr_bits
+#  define stdint_intptr_glue3_i(a,b,c)  a##b##c
+#  define stdint_intptr_glue3(a,b,c)    stdint_intptr_glue3_i(a,b,c)
+#  ifndef PRINTF_INTPTR_MODIFIER
+#    define PRINTF_INTPTR_MODIFIER      stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
+#  endif
+#  ifndef PTRDIFF_MAX
+#    define PTRDIFF_MAX                 stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef PTRDIFF_MIN
+#    define PTRDIFF_MIN                 stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+#  endif
+#  ifndef UINTPTR_MAX
+#    define UINTPTR_MAX                 stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef INTPTR_MAX
+#    define INTPTR_MAX                  stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef INTPTR_MIN
+#    define INTPTR_MIN                  stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+#  endif
+#  ifndef INTPTR_C
+#    define INTPTR_C(x)                 stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
+#  endif
+#  ifndef UINTPTR_C
+#    define UINTPTR_C(x)                stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
+#  endif
+  typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
+  typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t)  intptr_t;
+# else
+/* TODO -- This following is likely wrong for some platforms, and does
+   nothing for the definition of uintptr_t. */
+  typedef ptrdiff_t intptr_t;
+# endif
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+/*
+ *  Assumes sig_atomic_t is signed and we have a 2s complement machine.
+ */
+
+#ifndef SIG_ATOMIC_MAX
+# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
+#endif
+
+#endif
+
+#if defined (__TEST_PSTDINT_FOR_CORRECTNESS)
+
+/*
+ *  Please compile with the maximum warning settings to make sure macros are not
+ *  defined more than once.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define glue3_aux(x,y,z) x ## y ## z
+#define glue3(x,y,z) glue3_aux(x,y,z)
+
+#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0);
+#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0);
+
+#define DECL(us,bits) glue3(DECL,us,) (bits)
+
+#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits)
+
+int main () {
+	DECL(I,8)
+	DECL(U,8)
+	DECL(I,16)
+	DECL(U,16)
+	DECL(I,32)
+	DECL(U,32)
+#ifdef INT64_MAX
+	DECL(I,64)
+	DECL(U,64)
+#endif
+	intmax_t imax = INTMAX_C(0);
+	uintmax_t umax = UINTMAX_C(0);
+	char str0[256], str1[256];
+
+	sprintf (str0, "%d %x\n", 0, ~0);
+
+	sprintf (str1, "%d %x\n",  i8, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1);
+	sprintf (str1, "%u %x\n",  u8, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1);
+	sprintf (str1, "%d %x\n",  i16, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1);
+	sprintf (str1, "%u %x\n",  u16, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1);
+	sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n",  i32, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1);
+	sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n",  u32, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1);
+#ifdef INT64_MAX
+	sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n",  i64, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1);
+#endif
+	sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n",  imax, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1);
+	sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n",  umax, ~0);
+	if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1);
+
+	TESTUMAX(8);
+	TESTUMAX(16);
+	TESTUMAX(32);
+#ifdef INT64_MAX
+	TESTUMAX(64);
+#endif
+
+	return EXIT_SUCCESS;
+}
+
+#endif

+ 101 - 0
Source/ThirdParty/ik/ik/include/ik/quat.h

@@ -0,0 +1,101 @@
+#ifndef QUATERNION_H
+#define QUATERNION_H
+
+#include "ik/gen/config.h"
+#include "ik/vec3.h"
+
+C_HEADER_BEGIN
+
+typedef union quat_t
+{
+    struct {
+        ik_real x;
+        ik_real y;
+        ik_real z;
+        ik_real w;
+    } q;
+    struct {
+        vec3_t v;
+        ik_real w;
+    } vw;
+    ik_real f[4];
+} quat_t;
+
+/*!
+ * @brief Sets the quaternion to its identity rotation.
+ */
+IK_PUBLIC_API void
+quat_set_identity(ik_real* q);
+
+/*!
+ * @brief Adds the elements from one quaternion to another. Required for
+ * averaging multiple quaternions.
+ */
+IK_PUBLIC_API void
+quat_add_quat(ik_real* q1, const ik_real* q2);
+
+/*!
+ * @brief Calculates the magnitude of a quaternion.
+ */
+IK_PUBLIC_API ik_real
+quat_mag(const ik_real* q);
+
+/*!
+ * @brief Inverts the sign of the vector part of the quaternion (conjugation).
+ */
+IK_PUBLIC_API void
+quat_conj(ik_real* q);
+
+/*!
+ * @brief Inverts the sign of all elements (NOT conjugation).
+ */
+IK_PUBLIC_API void
+quat_invert_sign(ik_real* q);
+
+/*!
+ * @brief Normalises the quaternion.
+ */
+IK_PUBLIC_API void
+quat_normalise(ik_real* q);
+
+/*!
+ * @brief Multiplies two quaternions together.
+ */
+IK_PUBLIC_API void
+quat_mul_quat(ik_real* q1, const ik_real* q2);
+
+/*!
+ * @brief Multiplies each component by a constant.
+ */
+IK_PUBLIC_API void
+quat_mul_scalar(ik_real* q, ik_real scalar);
+
+/*!
+ * @brief Divides each component by a constant. If the constant is 0 then the
+ * result will be a unit quaternion.
+ */
+IK_PUBLIC_API void
+quat_div_scalar(ik_real* q, ik_real scalar);
+
+/*!
+ * @brief Calculates the scalar product of two quaternions.
+ */
+IK_PUBLIC_API ik_real
+quat_dot(ik_real* q1, const ik_real* q2);
+
+/*!
+ * @brief Rotations a vector by the specified quaternion.
+ */
+IK_PUBLIC_API void
+quat_rotate_vec(ik_real* v, const ik_real* q);
+
+/*!
+ * @brief Returns 0 if the two quaternions are "close", i.e. if -q has a
+ * similar rotation as q.
+ */
+IK_PUBLIC_API void
+quat_normalise_sign(ik_real* q1);
+
+C_HEADER_END
+
+#endif /* QUATERNION_H */

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

@@ -0,0 +1,226 @@
+#ifndef IK_SOLVER_H
+#define IK_SOLVER_H
+
+#include "ik/gen/config.h"
+#include "ik/ordered_vector.h"
+#include "ik/vec3.h"
+#include "ik/quat.h"
+
+C_HEADER_BEGIN
+
+struct ik_effector_t;
+struct ik_node_t;
+struct ik_solver_t;
+
+typedef void (*ik_solver_destroy_func)(struct ik_solver_t*);
+typedef int (*ik_solver_rebuild_data_func)(struct ik_solver_t*);
+typedef void (*ik_solver_recalculate_segment_lengths_func)(struct ik_solver_t*);
+typedef int (*ik_solver_solve_func)(struct ik_solver_t*);
+
+typedef void (*ik_solver_apply_constraint_cb_func)(struct ik_node_t*);
+typedef void (*ik_solver_apply_result_cb_func)(struct ik_node_t*);
+
+enum solver_algorithm_e
+{
+    SOLVER_FABRIK
+    /* TODO Not implemented
+    SOLVER_JACOBIAN_INVERSE,
+    SOLVER_JACOBIAN_TRANSPOSE */
+};
+
+enum solver_flags_e
+{
+    /*!
+     * @brief Causes the root node in the tree to be excluded from the list of
+     * nodes to solve for. It won't be affected by the solver, but it may still
+     * be passed through to the result callback function.
+     */
+    SOLVER_EXCLUDE_ROOT                   = 0x01,
+
+    /*!
+     * @brief This is a post-processing step which can optionally be enabled.
+     * Causes the correct global angles to be calculated for each node in the
+     * solved tree. The results can be retrieved from node->solved_rotation.
+     * This should definitely be enabled for skinned models.
+     */
+    SOLVER_CALCULATE_FINAL_ROTATIONS      = 0x02,
+
+    /* (not yet implemented)
+     * Calculate node angles for each iteration, which may be useful in the
+     * solver->apply_constraint callback function.
+     */
+    SOLVER_CALCULATE_CONSTRAINT_ROTATIONS = 0x04,
+
+    SOLVER_CALCULATE_TARGET_ROTATIONS     = 0x08,
+
+    /*!
+     * @brief The solver will not reset the solved data to its initial state
+     * before solving. The result is a more "continuous" or "ongoing" solution
+     * to the tree, because it will use the previous solved tree as a bases for
+     * solving the next tree.
+     */
+    SOLVER_SKIP_RESET                     = 0x10,
+
+    /*!
+     * @brief The solver will not call the solver->apply_result callback
+     * function after solving. The results are still calculated. This is useful
+     * if you wish to delay the point at which the solved data is applied. You
+     * can later call ik_solver_iterate_tree() to initiate calls to the
+     * callback function.
+     */
+    SOLVER_SKIP_APPLY                     = 0x20
+};
+
+/*!
+ * @brief This is a base struct for all solvers.
+ */
+#define SOLVER_DATA_HEAD                                             \
+    ik_solver_apply_constraint_cb_func apply_constraint;             \
+    ik_solver_apply_result_cb_func     apply_result;                 \
+                                                                     \
+    int32_t                            max_iterations;               \
+    float                              tolerance;                    \
+    uint8_t                            flags;                        \
+                                                                     \
+    /* Derived structure callbacks */                                \
+    ik_solver_destroy_func             destroy;                      \
+    ik_solver_rebuild_data_func        rebuild_data;                 \
+    ik_solver_recalculate_segment_lengths_func recalculate_segment_lengths; \
+    ik_solver_solve_func               solve;                        \
+                                                                     \
+    struct ordered_vector_t            effector_nodes_list;          \
+    struct ik_node_t*                  tree;
+struct ik_solver_t
+{
+    SOLVER_DATA_HEAD
+};
+
+/*!
+ * @brief Allocates a new solver object according to the specified algorithm.
+ *
+ * Once the solver is created, you can configure the solver to enable/disable
+ * various features depending on your needs.
+ *
+ * The following attributes can be changed at any point.
+ *  + solver->apply_result
+ *       This is the main mechanism with which to obtain the solved data.
+ *       Assign a callback function here and it will be called for every node
+ *       in the tree when a new target position/rotation has been calculated.
+ *       You can use the node->user_data attribute to store external node
+ *       specific data, which can be accessed again the in callback function.
+ *  + solver->max_iterations
+ *       Specifies the maximum number of iterations. The more iterations, the
+ *       more exact the result will be. The default value for the FABRIK solver
+ *       is 20, but you can get away with values as low as 5.
+ *  + solver->tolerance
+ *       This value can be changed at any point. Specifies the acceptable
+ *       distance each effector needs to be to its target position. The solver
+ *       will stop iterating if the effectors are within this distance. The
+ *       default value is 1e-3. Recommended values are 100th of your world
+ *       unit.
+ *  + solver->flags
+ *       Changes the behaviour of the solver. See the enum solver_flags_e for
+ *       more information.
+ *
+ * The following attributes can be accessed (read from) but should not be
+ * modified.
+ *  + solver->tree
+ *       The tree to be solved. You may modify the nodes in the tree.
+ *       @note If you add/remove nodes or if you add/remove effectors, you
+ *       must call ik_solver_rebuild_data() so the internal solver structures
+ *       are updated. Failing to do so may cause segfaults. If you're just
+ *       updating positions/rotations or any of the other public data then
+ *       there is no need to rebuild data.
+ *  + solver->effector_nodes_list
+ *       A vector containing pointers to nodes in the tree which have an
+ *       effector attached to them. You may not modify this list, but you may
+ *       iterate it.
+ * @param[in] algorithm The algorithm to use. Currently, only FABRIK is
+ * supported.
+ */
+IK_PUBLIC_API struct ik_solver_t*
+ik_solver_create(enum solver_algorithm_e algorithm);
+
+/*!
+ * @brief Destroys the solver and all nodes/effectors that are part of the
+ * solver. Any pointers to tree nodes are invalid after this function returns.
+ */
+IK_PUBLIC_API void
+ik_solver_destroy(struct ik_solver_t* solver);
+
+/*!
+ * @brief Sets the tree to solve. The solver takes ownership of the tree, so
+ * destroying the solver will destroy all nodes in the tree. Note that you will
+ * have to call ik_solver_rebuild_data() before being able to solve it. If the
+ * solver already has a tree, then said tree will be destroyed.
+ */
+IK_PUBLIC_API void
+ik_solver_set_tree(struct ik_solver_t* solver, struct ik_node_t* root);
+
+/*!
+ * @brief The solver releases any references to a previously set tree and
+ * returns the root node of said tree. Any proceeding calls that involve the
+ * tree (e.g. solve or rebuild) will have no effect until a new tree is set.
+ * @return If the solver has no tree then NULL is returned.
+ */
+IK_PUBLIC_API struct ik_node_t*
+ik_solver_unlink_tree(struct ik_solver_t* solver);
+
+/*!
+ * @brief The solver releases any references to a previously set tree and
+ * destroys it.
+ */
+IK_PUBLIC_API void
+ik_solver_destroy_tree(struct ik_solver_t* solver);
+
+/*!
+ * @brief Causes the set tree to be processed into more optimal data structures
+ * for solving. Must be called before ik_solver_solve().
+ * @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.
+ */
+IK_PUBLIC_API int
+ik_solver_rebuild_data(struct ik_solver_t* solver);
+
+/*!
+ * @brief Unusual, but if you have a tree with translational motions such that
+ * the distances between nodes changes (perhaps a slider?), you can call this
+ * to re-calculate the segment lengths after assigning new positions to the
+ * nodes.
+ * @note This function gets called by ik_solver_rebuild_data().
+ */
+IK_PUBLIC_API void
+ik_solver_recalculate_segment_lengths(struct ik_solver_t* solver);
+
+/*!
+ * @brief Solves the IK problem. The node solutions will be provided via a
+ * callback function, which can be registered to the solver by assigning it to
+ * solver->apply_result.
+ */
+IK_PUBLIC_API int
+ik_solver_solve(struct ik_solver_t* solver);
+
+/*!
+ * @brief Iterates all nodes in the internal tree, breadth first, and calls the
+ * solver->apply_result callback function for every node.
+ *
+ * This gets called automatically for you by ik_solver_solve() if
+ * SOLVER_SKIP_APPLY is **not** set. This function could also be used to reset
+ * your own scene graph to its initial state by reading the node->position and
+ * node->rotation properties.
+ */
+IK_PUBLIC_API void
+ik_solver_iterate_tree(struct ik_solver_t* solver);
+
+/*!
+ * @brief Sets the solved positions and rotations equal to the original
+ * positions and rotations for every node in the tree. The solver will call
+ * this automatically if SOLVER_SKIP_RESET is **not** set.
+ */
+IK_PUBLIC_API void
+ik_solver_reset_solved_data(struct ik_solver_t* solver);
+
+C_HEADER_END
+
+#endif /* IK_SOLVER_H */

+ 39 - 0
Source/ThirdParty/ik/ik/include/ik/solver_FABRIK.h

@@ -0,0 +1,39 @@
+#ifndef IK_SOLVER_FABRIK_H
+#define IK_SOLVER_FABRIK_H
+
+#include "ik/gen/config.h"
+#include "ik/ordered_vector.h"
+#include "ik/solver.h"
+
+C_HEADER_BEGIN
+
+struct chain_t
+{
+    struct ordered_vector_t nodes;    /* list of node_t* references */
+    struct ordered_vector_t children; /* list of chain_t objects */
+};
+
+struct fabrik_t
+{
+    SOLVER_DATA_HEAD
+    struct chain_t* chain_tree;
+};
+
+struct ik_solver_t*
+solver_FABRIK_create(void);
+
+void
+solver_FABRIK_destroy(struct ik_solver_t* solver);
+
+int
+solver_FABRIK_rebuild_data(struct ik_solver_t* solver);
+
+void
+solver_FABRIK_recalculate_segment_lengths(struct ik_solver_t* solver);
+
+int
+solver_FABRIK_solve(struct ik_solver_t* solver);
+
+C_HEADER_END
+
+#endif /* IK_SOLVER_FABRIK_H */

+ 13 - 0
Source/ThirdParty/ik/ik/include/ik/solver_jacobian_inverse.h

@@ -0,0 +1,13 @@
+#ifndef IK_SOLVER_JACOBIAN_INVERSE_H
+#define IK_SOLVER_JACOBIAN_INVERSE_H
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+char
+ik_solve_jacobian_inverse(void);
+
+C_HEADER_END
+
+#endif /* IK_SOLVER_JACOBIAN_INVERSE_H */

+ 13 - 0
Source/ThirdParty/ik/ik/include/ik/solver_jacobian_transpose.h

@@ -0,0 +1,13 @@
+#ifndef IK_SOLVER_JACOBIAN_TRANSPOSE_H
+#define IK_SOLVER_JACOBIAN_TRANSPOSE_H
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+char
+ik_solve_jacobian_transpose(void);
+
+C_HEADER_END
+
+#endif /* IK_SOLVER_JACOBIAN_TRANSPOSE_H */

+ 50 - 0
Source/ThirdParty/ik/ik/include/ik/vec3.h

@@ -0,0 +1,50 @@
+#ifndef VEC3_H
+#define VEC3_H
+
+#include "ik/gen/config.h"
+
+C_HEADER_BEGIN
+
+typedef union vec3_t
+{
+    struct {
+        ik_real x;
+        ik_real y;
+        ik_real z;
+    } v;
+    ik_real f[3];
+} vec3_t;
+
+IK_PUBLIC_API void
+vec3_set_zero(ik_real* v);
+
+IK_PUBLIC_API void
+vec3_add_vec3(ik_real* v1, const ik_real* v2);
+
+IK_PUBLIC_API void
+vec3_sub_vec3(ik_real* v1, const ik_real* v2);
+
+IK_PUBLIC_API void
+vec3_mul_scalar(ik_real* v1, ik_real scalar);
+
+IK_PUBLIC_API void
+vec3_div_scalar(ik_real* v, ik_real scalar);
+
+IK_PUBLIC_API ik_real
+vec3_length_squared(const ik_real* v);
+
+IK_PUBLIC_API ik_real
+vec3_length(const ik_real* v);
+
+IK_PUBLIC_API void
+vec3_normalise(ik_real* v);
+
+IK_PUBLIC_API ik_real
+vec3_dot(const ik_real* v1, const ik_real* v2);
+
+IK_PUBLIC_API void
+vec3_cross(ik_real* v1, const ik_real* v2);
+
+C_HEADER_END
+
+#endif /* VEC3_H */

+ 253 - 0
Source/ThirdParty/ik/ik/src/bst_vector.c

@@ -0,0 +1,253 @@
+#include "ik/bst_vector.h"
+#include "ik/memory.h"
+#include <assert.h>
+#include <string.h>
+
+const uint32_t BST_VECTOR_INVALID_HASH = (uint32_t)-1;
+
+/* ------------------------------------------------------------------------- */
+struct bstv_t*
+bstv_create(void)
+{
+    struct bstv_t* bstv;
+    if(!(bstv = (struct bstv_t*)MALLOC(sizeof *bstv)))
+        return NULL;
+    bstv_construct(bstv);
+    return bstv;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+bstv_construct(struct bstv_t* bstv)
+{
+    assert(bstv);
+    ordered_vector_construct(&bstv->vector, sizeof(struct bstv_hash_value_t));
+}
+
+/* ------------------------------------------------------------------------- */
+void
+bstv_destroy(struct bstv_t* bstv)
+{
+    assert(bstv);
+    bstv_clear_free(bstv);
+    FREE(bstv);
+}
+
+/* ------------------------------------------------------------------------- */
+/* algorithm taken from GNU GCC stdlibc++'s lower_bound function, line 2121 in stl_algo.h */
+/* https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a02014.html */
+static struct bstv_hash_value_t*
+bstv_find_lower_bound(const struct bstv_t* bstv, uint32_t hash)
+{
+    uint32_t half;
+    struct bstv_hash_value_t* middle;
+    struct bstv_hash_value_t* data;
+    uint32_t len;
+
+    assert(bstv);
+
+    data = (struct bstv_hash_value_t*)bstv->vector.data;
+    len = bstv->vector.count;
+
+    /* if the vector has no data, return NULL */
+    if(!len)
+        return NULL;
+
+    while(len > 0)
+    {
+        half = len >> 1;
+        middle = data + half;
+        if(middle->hash < hash)
+        {
+            data = middle;
+            ++data;
+            len = len - half - 1;
+        }
+        else
+            len = half;
+    }
+
+    /* if "data" is pointing outside of the valid elements in the vector, also return NULL */
+    if((intptr_t)data >= (intptr_t)bstv->vector.data + (intptr_t)bstv->vector.count * (intptr_t)bstv->vector.element_size)
+        return NULL;
+    else
+        return data;
+}
+
+/* ------------------------------------------------------------------------- */
+int
+bstv_insert(struct bstv_t* bstv, uint32_t hash, void* value)
+{
+    struct bstv_hash_value_t* emplaced_data;
+    struct bstv_hash_value_t* lower_bound;
+
+    assert(bstv);
+
+    /* don't insert reserved hashes */
+    if(hash == BST_VECTOR_INVALID_HASH)
+        return -1;
+
+    /* lookup location in bstv to insert */
+    lower_bound = bstv_find_lower_bound(bstv, hash);
+    if(lower_bound && lower_bound->hash == hash)
+        return 1;
+
+    /* either push back or insert, depending on whether there is already data
+     * in the bstv */
+    if(!lower_bound)
+        emplaced_data = (struct bstv_hash_value_t*)ordered_vector_push_emplace(&bstv->vector);
+    else
+        emplaced_data = ordered_vector_insert_emplace(&bstv->vector,
+                          lower_bound - (struct bstv_hash_value_t*)bstv->vector.data);
+
+    if(!emplaced_data)
+        return -1;
+
+    memset(emplaced_data, 0, sizeof *emplaced_data);
+    emplaced_data->hash = hash;
+    emplaced_data->value = value;
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+bstv_set(struct bstv_t* bstv, uint32_t hash, void* value)
+{
+    struct bstv_hash_value_t* data;
+
+    assert(bstv);
+
+    data = bstv_find_lower_bound(bstv, hash);
+    if(data && data->hash == hash)
+        data->value = value;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+bstv_find(const struct bstv_t* bstv, uint32_t hash)
+{
+    void** result = bstv_find_ptr(bstv, hash);
+    return result == NULL ? NULL : *result;
+}
+
+/* ------------------------------------------------------------------------- */
+void**
+bstv_find_ptr(const struct bstv_t* bstv, uint32_t hash)
+{
+    struct bstv_hash_value_t* data;
+
+    assert(bstv);
+
+    data = bstv_find_lower_bound(bstv, hash);
+    if(!data || data->hash != hash)
+        return NULL;
+    return &data->value;
+}
+
+/* ------------------------------------------------------------------------- */
+uint32_t
+bstv_find_element(const struct bstv_t* bstv, const void* value)
+{
+    assert(bstv);
+
+    ORDERED_VECTOR_FOR_EACH(&bstv->vector, struct bstv_hash_value_t, kv)
+        if(kv->value == value)
+            return kv->hash;
+    ORDERED_VECTOR_END_EACH
+    return BST_VECTOR_INVALID_HASH;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+bstv_get_any_element(const struct bstv_t* bstv)
+{
+    struct bstv_hash_value_t* kv;
+    assert(bstv);
+    kv = (struct bstv_hash_value_t*)ordered_vector_back(&bstv->vector);
+    if(kv)
+        return kv->value;
+    return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+int
+bstv_hash_exists(struct bstv_t* bstv, uint32_t hash)
+{
+    struct bstv_hash_value_t* data;
+
+    assert(bstv);
+
+    data = bstv_find_lower_bound(bstv, hash);
+    if(data && data->hash == hash)
+        return 0;
+    return -1;
+}
+
+/* ------------------------------------------------------------------------- */
+uint32_t
+bstv_find_unused_hash(struct bstv_t* bstv)
+{
+    uint32_t i = 0;
+
+    assert(bstv);
+
+    BSTV_FOR_EACH(bstv, void, key, value)
+        if(i != key)
+            break;
+        ++i;
+    BSTV_END_EACH
+    return i;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+bstv_erase(struct bstv_t* bstv, uint32_t hash)
+{
+    void* value;
+    struct bstv_hash_value_t* data;
+
+    assert(bstv);
+
+    data = bstv_find_lower_bound(bstv, hash);
+    if(!data || data->hash != hash)
+        return NULL;
+
+    value = data->value;
+    ordered_vector_erase_element(&bstv->vector, (DATA_POINTER_TYPE*)data);
+    return value;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+bstv_erase_element(struct bstv_t* bstv, void* value)
+{
+    void* data;
+    uint32_t hash;
+
+    assert(bstv);
+
+    hash = bstv_find_element(bstv, value);
+    if(hash == BST_VECTOR_INVALID_HASH)
+        return NULL;
+
+    data = bstv_find_lower_bound(bstv, hash);
+    ordered_vector_erase_element(&bstv->vector, (DATA_POINTER_TYPE*)data);
+
+    return value;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+bstv_clear(struct bstv_t* bstv)
+{
+    assert(bstv);
+    ordered_vector_clear(&bstv->vector);
+}
+
+/* ------------------------------------------------------------------------- */
+void bstv_clear_free(struct bstv_t* bstv)
+{
+    assert(bstv);
+    ordered_vector_clear_free(&bstv->vector);
+}

+ 44 - 0
Source/ThirdParty/ik/ik/src/effector.c

@@ -0,0 +1,44 @@
+#include "ik/effector.h"
+#include "ik/memory.h"
+#include "ik/node.h"
+#include <string.h>
+
+/* ------------------------------------------------------------------------- */
+struct ik_effector_t*
+ik_effector_create(void)
+{
+    struct ik_effector_t* effector = (struct ik_effector_t*)MALLOC(sizeof *effector);
+    if(effector == NULL)
+        return NULL;
+
+    ik_effector_construct(effector);
+    return effector;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_effector_construct(struct ik_effector_t* effector)
+{
+    memset(effector, 0, sizeof *effector);
+    quat_set_identity(effector->target_rotation.f);
+    effector->weight = 0;
+    effector->rotation_weight = 1.0;
+    effector->rotation_decay = 0.25;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_effector_destroy(struct ik_effector_t* effector)
+{
+    FREE(effector);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+effector_attach(struct ik_effector_t* effector, struct ik_node_t* node)
+{
+    if(node->effector != NULL)
+        ik_effector_destroy(node->effector);
+
+    node->effector = effector;
+}

+ 97 - 0
Source/ThirdParty/ik/ik/src/log.c

@@ -0,0 +1,97 @@
+#include "ik/log.h"
+#include "ik/memory.h"
+#include "ik/ordered_vector.h"
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+static struct log_t
+{
+    struct ordered_vector_t listeners; /* list of ik_log_cb_func */
+    struct ordered_vector_t message_buffer;
+}* g_log = NULL;
+
+static void log_stdout_callback(const char* msg)
+{
+    puts(msg);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_log_init(enum ik_log_e options)
+{
+    if(g_log != NULL)
+        return;
+
+    g_log = (struct log_t*)MALLOC(sizeof *g_log);
+    if(g_log == NULL)
+        return;
+
+    ordered_vector_construct(&g_log->listeners, sizeof(ik_log_cb_func));
+    ordered_vector_construct(&g_log->message_buffer, sizeof(char));
+
+    if(options == IK_LOG_STDOUT)
+        ik_log_register_listener(log_stdout_callback);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_log_deinit(void)
+{
+    if(g_log == NULL)
+        return;
+
+    ordered_vector_clear_free(&g_log->message_buffer);
+    ordered_vector_clear_free(&g_log->listeners);
+    FREE(g_log);
+    g_log = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_log_register_listener(ik_log_cb_func callback)
+{
+    if(g_log != NULL)
+        ordered_vector_push(&g_log->listeners, &callback);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_log_unregister_listener(ik_log_cb_func callback)
+{
+    if(g_log == NULL)
+        return;
+
+    ORDERED_VECTOR_FOR_EACH(&g_log->listeners, ik_log_cb_func, registered_callback)
+        if(callback == *registered_callback)
+        {
+            ordered_vector_erase_element(&g_log->listeners, registered_callback);
+            return;
+        }
+    ORDERED_VECTOR_END_EACH
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_log_message(const char* fmt, ...)
+{
+    va_list va;
+    uintptr_t msg_len;
+
+    if(g_log == NULL)
+        return;
+
+    va_start(va, fmt);
+    msg_len = vsnprintf(NULL, 0, fmt, va);
+    va_end(va);
+
+    if(ordered_vector_resize(&g_log->message_buffer, (msg_len + 1) * sizeof(char)) < 0)
+        return;
+    va_start(va, fmt);
+    vsprintf((char*)g_log->message_buffer.data, fmt, va);
+    va_end(va);
+
+    ORDERED_VECTOR_FOR_EACH(&g_log->listeners, ik_log_cb_func, callback)
+        (*callback)((char*)g_log->message_buffer.data);
+    ORDERED_VECTOR_END_EACH
+}

+ 281 - 0
Source/ThirdParty/ik/ik/src/memory.c

@@ -0,0 +1,281 @@
+#include "ik/memory.h"
+#include "ik/bst_vector.h"
+#include "ik/backtrace.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#define BACKTRACE_OMIT_COUNT 2
+
+#if IK_MEMORY_DEBUGGING == ON
+static uintptr_t g_allocations = 0;
+static uintptr_t d_deg_allocations = 0;
+static uintptr_t g_ignore_bstv_malloc = 0;
+static struct bstv_t report;
+
+struct report_info_t
+{
+    uintptr_t location;
+    uintptr_t size;
+#   if IK_MEMORY_BACKTRACE == ON
+    int backtrace_size;
+    char** backtrace;
+#   endif
+};
+
+/* ------------------------------------------------------------------------- */
+void
+ik_memory_init(void)
+{
+    g_allocations = 0;
+    d_deg_allocations = 0;
+
+    /*
+     * Init bst vector of report objects and force it to allocate by adding
+     * and removing one item. This fixes a bug where the number of memory leaks
+     * would be wrong in the case of MALLOC() never being called.
+     */
+    g_ignore_bstv_malloc = 1;
+        bstv_construct(&report);
+        bstv_insert(&report, 0, NULL); bstv_erase(&report, 0);
+    g_ignore_bstv_malloc = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+malloc_wrapper(intptr_t size)
+{
+    void* p = NULL;
+    struct report_info_t* info = NULL;
+
+    /* breaking from this will clean up and return NULL */
+    for(;;)
+    {
+        /* allocate */
+        p = malloc(size);
+        if(p)
+            ++g_allocations;
+        else
+            break;
+
+        /*
+        * Record allocation info. Call to bstv may allocate memory,
+        * so set flag to ignore the call to malloc() when inserting.
+        */
+        if(!g_ignore_bstv_malloc)
+        {
+            g_ignore_bstv_malloc = 1;
+            info = (struct report_info_t*)malloc(sizeof(struct report_info_t));
+            if(!info)
+            {
+                fprintf(stderr, "[memory] ERROR: malloc() for report_info_t failed"
+                    " -- not enough memory.\n");
+                g_ignore_bstv_malloc = 0;
+                break;
+            }
+
+            /* record the location and size of the allocation */
+            info->location = (uintptr_t)p;
+            info->size = size;
+
+            /* if enabled, generate a backtrace so we know where memory leaks
+            * occurred */
+#   if IK_MEMORY_BACKTRACE == ON
+            if(!(info->backtrace = get_backtrace(&info->backtrace_size)))
+                fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
+#   endif
+
+            /* insert into bstv */
+            if(bstv_insert(&report, (uintptr_t)p, info) == 1)
+            {
+                fprintf(stderr,
+                "[memory] WARNING: Hash collision occurred when inserting\n"
+                "into memory report bstv. On 64-bit systems the pointers are\n"
+                "rounded down to 32-bit unsigned integers, so even though\n"
+                "it's rare, collisions can happen.\n\n"
+                "The matching call to FREE() will generate a warning saying\n"
+                "something is being freed that was never allocated. This is to\n"
+                "be expected and can be ignored.\n");
+#   if IK_MEMORY_BACKTRACE == ON
+                {
+                    char** bt;
+                    int bt_size, i;
+                    if((bt = get_backtrace(&bt_size)))
+                    {
+                        printf("  backtrace to where malloc() was called:\n");
+                        for(i = 0; i < bt_size; ++i)
+                            printf("      %s\n", bt[i]);
+                        printf("  -----------------------------------------\n");
+                        free(bt);
+                    }
+                    else
+                        fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
+                }
+#   endif
+            }
+            g_ignore_bstv_malloc = 0;
+        }
+
+        /* success */
+        return p;
+    }
+
+    /* failure */
+    if(p)
+    {
+        free(p);
+        --g_allocations;
+    }
+
+    if(info)
+    {
+#   if IK_MEMORY_BACKTRACE == ON
+        if(info->backtrace)
+            free(info->backtrace);
+#   endif
+        free(info);
+    }
+
+    return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+free_wrapper(void* ptr)
+{
+    /* find matching allocation and remove from bstv */
+    if(!g_ignore_bstv_malloc)
+    {
+        struct report_info_t* info = (struct report_info_t*)bstv_erase(&report, (uintptr_t)ptr);
+        if(info)
+        {
+#   if IK_MEMORY_BACKTRACE == ON
+            if(info->backtrace)
+                free(info->backtrace);
+            else
+                fprintf(stderr, "[memory] WARNING: free(): Allocation didn't "
+                    "have a backtrace (it was NULL)\n");
+#   endif
+            free(info);
+        }
+        else
+        {
+#   if IK_MEMORY_BACKTRACE == ON
+            char** bt;
+            int bt_size, i;
+            fprintf(stderr, "  -----------------------------------------\n");
+#   endif
+            fprintf(stderr, "  WARNING: Freeing something that was never allocated\n");
+#   if IK_MEMORY_BACKTRACE == ON
+            if((bt = get_backtrace(&bt_size)))
+            {
+                fprintf(stderr, "  backtrace to where free() was called:\n");
+                for(i = 0; i < bt_size; ++i)
+                    fprintf(stderr, "      %s\n", bt[i]);
+                fprintf(stderr, "  -----------------------------------------\n");
+                free(bt);
+            }
+            else
+                fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
+#   endif
+        }
+    }
+
+    if(ptr)
+    {
+        ++d_deg_allocations;
+        free(ptr);
+    }
+    else
+        fprintf(stderr, "Warning: free(NULL)\n");
+}
+
+/* ------------------------------------------------------------------------- */
+uintptr_t
+ik_memory_deinit(void)
+{
+    uintptr_t leaks;
+
+    --g_allocations; /* this is the single allocation still held by the report vector */
+
+    printf("=========================================\n");
+    printf("Inverse Kinematics Memory Report\n");
+    printf("=========================================\n");
+
+    /* report details on any g_allocations that were not de-allocated */
+    if(report.vector.count != 0)
+    {
+        BSTV_FOR_EACH(&report, struct report_info_t, key, info)
+
+            printf("  un-freed memory at %p, size %p\n", (void*)info->location, (void*)info->size);
+            mutated_string_and_hex_dump((void*)info->location, info->size);
+
+#   if IK_MEMORY_BACKTRACE == ON
+            printf("  Backtrace to where malloc() was called:\n");
+            {
+                intptr_t i;
+                for(i = BACKTRACE_OMIT_COUNT; i < info->backtrace_size; ++i)
+                    printf("      %s\n", info->backtrace[i]);
+            }
+            free(info->backtrace); /* this was allocated when malloc() was called */
+            printf("  -----------------------------------------\n");
+#   endif
+            free(info);
+
+        BSTV_END_EACH
+
+        printf("=========================================\n");
+    }
+
+    /* overall report */
+    leaks = (g_allocations > d_deg_allocations ? g_allocations - d_deg_allocations : d_deg_allocations - g_allocations);
+    printf("allocations: %lu\n", g_allocations);
+    printf("deallocations: %lu\n", d_deg_allocations);
+    printf("memory leaks: %lu\n", leaks);
+    printf("=========================================\n");
+
+    ++g_allocations; /* this is the single allocation still held by the report vector */
+    g_ignore_bstv_malloc = 1;
+    bstv_clear_free(&report);
+
+    return leaks;
+}
+
+#else /* IK_MEMORY_DEBUGGING */
+
+void ik_memory_init(void) {}
+uintptr_t ik_memory_deinit(void) { return 0; }
+
+#endif /* IK_MEMORY_DEBUGGING */
+
+/* ------------------------------------------------------------------------- */
+void
+mutated_string_and_hex_dump(void* data, intptr_t length_in_bytes)
+{
+    char* dump;
+    intptr_t i;
+
+    /* allocate and copy data into new buffer */
+    if(!(dump = malloc(length_in_bytes + 1)))
+    {
+        fprintf(stderr, "[memory] WARNING: Failed to malloc() space for dump\n");
+        return;
+    }
+    memcpy(dump, data, length_in_bytes);
+    dump[length_in_bytes] = '\0';
+
+    /* mutate null terminators into dots */
+    for(i = 0; i != length_in_bytes; ++i)
+        if(dump[i] == '\0')
+            dump[i] = '.';
+
+    /* dump */
+    printf("  mutated string dump: %s\n", dump);
+    printf("  hex dump: ");
+    for(i = 0; i != length_in_bytes; ++i)
+        printf(" %02x", (unsigned char)dump[i]);
+    printf("\n");
+
+    free(dump);
+}

+ 160 - 0
Source/ThirdParty/ik/ik/src/node.c

@@ -0,0 +1,160 @@
+#include "ik/effector.h"
+#include "ik/log.h"
+#include "ik/memory.h"
+#include "ik/node.h"
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+/* ------------------------------------------------------------------------- */
+struct ik_node_t*
+ik_node_create(uint32_t guid)
+{
+    struct ik_node_t* node = (struct ik_node_t*)MALLOC(sizeof *node);
+    if(node == NULL)
+        return NULL;
+
+    ik_node_construct(node, guid);
+    return node;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_construct(struct ik_node_t* node, uint32_t guid)
+{
+    memset(node, 0, sizeof *node);
+    bstv_construct(&node->children);
+    quat_set_identity(node->rotation.f);
+    quat_set_identity(node->solved_rotation.f);
+    node->guid = guid;
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ik_node_destroy_recursive(struct ik_node_t* node);
+static void
+ik_node_destruct_recursive(struct ik_node_t* node)
+{
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        ik_node_destroy_recursive(child);
+    BSTV_END_EACH
+
+    if(node->effector)
+        ik_effector_destroy(node->effector);
+
+    bstv_clear_free(&node->children);
+}
+void
+ik_node_destruct(struct ik_node_t* node)
+{
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        ik_node_destroy_recursive(child);
+    BSTV_END_EACH
+
+    if(node->effector)
+        ik_effector_destroy(node->effector);
+
+    ik_node_unlink(node);
+    bstv_clear_free(&node->children);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ik_node_destroy_recursive(struct ik_node_t* node)
+{
+    ik_node_destruct_recursive(node);
+    FREE(node);
+}
+void
+ik_node_destroy(struct ik_node_t* node)
+{
+    ik_node_destruct(node);
+    FREE(node);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_add_child(struct ik_node_t* node, struct ik_node_t* child)
+{
+    child->parent = node;
+    bstv_insert(&node->children, child->guid, child);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_unlink(struct ik_node_t* node)
+{
+    if(node->parent == NULL)
+        return;
+
+    bstv_erase(&node->parent->children, node->guid);
+    node->parent = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+struct ik_node_t*
+ik_node_find_child(struct ik_node_t* node, uint32_t guid)
+{
+    struct ik_node_t* found = bstv_find(&node->children, guid);
+    if(found != NULL)
+        return found;
+
+    if(node->guid == guid)
+        return node;
+
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, child_guid, child)
+        found = ik_node_find_child(child, guid);
+        if(found != NULL)
+            return found;
+    BSTV_END_EACH
+
+    return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_attach_effector(struct ik_node_t* node, struct ik_effector_t* effector)
+{
+    node->effector = effector;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_destroy_effector(struct ik_node_t* node)
+{
+    if(node->effector == NULL)
+        return;
+    ik_effector_destroy(node->effector);
+    node->effector = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+recursively_dump_dot(FILE* fp, struct ik_node_t* node)
+{
+    if(node->effector != NULL)
+        fprintf(fp, "    %d [color=\"1.0 0.5 1.0\"];\n", node->guid);
+
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        fprintf(fp, "    %d -- %d;\n", node->guid, guid);
+        recursively_dump_dot(fp, child);
+    BSTV_END_EACH
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_node_dump_to_dot(struct ik_node_t* node, const char* file_name)
+{
+    FILE* fp = fopen(file_name, "w");
+    if(fp == NULL)
+    {
+        ik_log_message("Failed to open file %s", file_name);
+        return;
+    }
+
+    fprintf(fp, "graph graphname {\n");
+    recursively_dump_dot(fp, node);
+    fprintf(fp, "}\n");
+
+    fclose(fp);
+}

+ 351 - 0
Source/ThirdParty/ik/ik/src/ordered_vector.c

@@ -0,0 +1,351 @@
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "ik/ordered_vector.h"
+#include "ik/memory.h"
+
+/* ----------------------------------------------------------------------------
+ * Static functions
+ * ------------------------------------------------------------------------- */
+
+/*!
+ * @brief Expands the underlying memory.
+ *
+ * This implementation will expand the memory by a factor of 2 each time this
+ * is called. All elements are copied into the new section of memory.
+ * @param[in] insertion_index Set to -1 if no space should be made for element
+ * insertion. Otherwise this parameter specifies the index of the element to
+ * "evade" when re-allocating all other elements.
+ * @param[in] target_size If set to 0, target size is calculated automatically.
+ * Otherwise the vector will expand to the specified target size.
+ * @note No checks are performed to make sure the target size is large enough.
+ */
+static int
+ordered_vector_expand(struct ordered_vector_t *vector,
+                      uintptr_t insertion_index,
+                      uint32_t target_size);
+
+/* ----------------------------------------------------------------------------
+ * Exported functions
+ * ------------------------------------------------------------------------- */
+struct ordered_vector_t*
+ordered_vector_create(const uint32_t element_size)
+{
+    struct ordered_vector_t* vector;
+    if(!(vector = (struct ordered_vector_t*)MALLOC(sizeof(struct ordered_vector_t))))
+        return NULL;
+    ordered_vector_construct(vector, element_size);
+    return vector;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_construct(struct ordered_vector_t* vector, const uint32_t element_size)
+{
+    assert(vector);
+    memset(vector, 0, sizeof(struct ordered_vector_t));
+    vector->element_size = element_size;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_destroy(struct ordered_vector_t* vector)
+{
+    assert(vector);
+    ordered_vector_clear_free(vector);
+    FREE(vector);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_clear(struct ordered_vector_t* vector)
+{
+    assert(vector);
+    /*
+     * No need to free or overwrite existing memory, just reset the counter
+     * and let future insertions overwrite
+     */
+    vector->count = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_clear_free(struct ordered_vector_t* vector)
+{
+    assert(vector);
+
+    if(vector->data)
+        FREE(vector->data);
+
+    vector->data = NULL;
+    vector->count = 0;
+    vector->capacity = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size)
+{
+    int result = 0;
+
+    assert(vector);
+
+    if(vector->count < size)
+        result = ordered_vector_expand(vector, -1, size);
+    vector->count = size;
+
+    return result;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+ordered_vector_push_emplace(struct ordered_vector_t* vector)
+{
+    void* data;
+
+    assert(vector);
+
+    if(vector->count == vector->capacity)
+        if(ordered_vector_expand(vector, -1, 0) < 0)
+            return NULL;
+    data = vector->data + (vector->element_size * vector->count);
+    ++(vector->count);
+    return data;
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ordered_vector_push(struct ordered_vector_t* vector, void* data)
+{
+    void* emplaced;
+
+    assert(vector);
+    assert(data);
+
+    emplaced = ordered_vector_push_emplace(vector);
+    if(!emplaced)
+        return -1;
+    memcpy(emplaced, data, vector->element_size);
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ordered_vector_push_vector(struct ordered_vector_t* vector, struct ordered_vector_t* source_vector)
+{
+    assert(vector);
+    assert(source_vector);
+
+    /* make sure element sizes are equal */
+    if(vector->element_size != source_vector->element_size)
+        return -1;
+
+    /* make sure there's enough space in the target vector */
+    if(vector->count + source_vector->count > vector->capacity)
+        if(ordered_vector_expand(vector, -1, vector->count + source_vector->count) < 0)
+            return -1;
+
+    /* copy data */
+    memcpy(vector->data + (vector->count * vector->element_size),
+           source_vector->data,
+           source_vector->count * vector->element_size);
+    vector->count += source_vector->count;
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+ordered_vector_pop(struct ordered_vector_t* vector)
+{
+    assert(vector);
+
+    if(!vector->count)
+        return NULL;
+
+    --(vector->count);
+    return vector->data + (vector->element_size * vector->count);
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+ordered_vector_back(const struct ordered_vector_t* vector)
+{
+    assert(vector);
+
+    if(!vector->count)
+        return NULL;
+
+    return vector->data + (vector->element_size * (vector->count - 1));
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index)
+{
+    uint32_t offset;
+
+    assert(vector);
+
+    /*
+     * Normally the last valid index is (capacity-1), but in this case it's valid
+     * because it's possible the user will want to insert at the very end of
+     * the vector.
+     */
+    if(index > vector->count)
+        return NULL;
+
+    /* re-allocate? */
+    if(vector->count == vector->capacity)
+    {
+        if(ordered_vector_expand(vector, index, 0) < 0)
+            return NULL;
+    }
+    else
+    {
+        /* shift all elements up by one to make space for insertion */
+        uint32_t total_size = vector->count * vector->element_size;
+        offset = vector->element_size * index;
+        memmove((void*)((intptr_t)vector->data + offset + vector->element_size),
+                (void*)((intptr_t)vector->data + offset),
+                total_size - offset);
+    }
+
+    /* return pointer to memory of new element */
+    ++vector->count;
+    return (void*)(vector->data + index * vector->element_size);
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* data)
+{
+    void* emplaced;
+
+    assert(vector);
+    assert(data);
+
+    emplaced = ordered_vector_insert_emplace(vector, index);
+    if(!emplaced)
+        return -1;
+    memcpy(emplaced, data, vector->element_size);
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index)
+{
+    assert(vector);
+
+    if(index >= vector->count)
+        return;
+
+    if(index == vector->count - 1)
+        /* last element doesn't require memory shifting, just pop it */
+        ordered_vector_pop(vector);
+    else
+    {
+        /* shift memory right after the specified element down by one element */
+        uint32_t offset = vector->element_size * index;  /* offset to the element being erased in bytes */
+        uint32_t total_size = vector->element_size * vector->count; /* total current size in bytes */
+        memmove((void*)((intptr_t)vector->data + offset),   /* target is to overwrite the element specified by index */
+                (void*)((intptr_t)vector->data + offset + vector->element_size),    /* copy beginning from one element ahead of element to be erased */
+                total_size - offset - vector->element_size);     /* copying number of elements after element to be erased */
+        --vector->count;
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ordered_vector_erase_element(struct ordered_vector_t* vector, void* element)
+{
+    uintptr_t last_element;
+
+    assert(vector);
+    last_element = (uintptr_t)vector->data + (vector->count-1) * vector->element_size;
+    assert(element);
+    assert((uintptr_t)element >= (uintptr_t)vector->data);
+    assert((uintptr_t)element <= (uintptr_t)last_element);
+
+    if(element != (void*)last_element)
+    {
+        memmove(element,    /* target is to overwrite the element */
+                (void*)((uintptr_t)element + vector->element_size), /* read everything from next element */
+                last_element - (uintptr_t)element);
+    }
+    --vector->count;
+}
+
+/* ------------------------------------------------------------------------- */
+void*
+ordered_vector_get_element(struct ordered_vector_t* vector, uint32_t index)
+{
+    assert(vector);
+
+    if(index >= vector->count)
+        return NULL;
+    return vector->data + (vector->element_size * index);
+}
+
+/* ----------------------------------------------------------------------------
+ * Static functions
+ * ------------------------------------------------------------------------- */
+static int
+ordered_vector_expand(struct ordered_vector_t *vector,
+                      uintptr_t insertion_index,
+                      uint32_t target_count)
+{
+    uintptr_t new_count;
+    DATA_POINTER_TYPE* old_data;
+    DATA_POINTER_TYPE* new_data;
+
+    /* expand by factor 2, or adopt target count if it is not 0 */
+    if(target_count)
+        new_count = target_count;
+    else
+        new_count = vector->capacity << 1;
+
+    /*
+     * If vector hasn't allocated anything yet, just allocated the requested
+     * amount of memory and return immediately.
+     */
+    if(!vector->data)
+    {
+        new_count = (new_count == 0 ? 2 : new_count);
+        vector->data = MALLOC(new_count * vector->element_size);
+        if(!vector->data)
+            return -1;
+        vector->capacity = new_count;
+        return 0;
+    }
+
+    /* prepare for reallocating data */
+    old_data = vector->data;
+    new_data = (DATA_POINTER_TYPE*)MALLOC(new_count * vector->element_size);
+    if(!new_data)
+        return -1;
+
+    /* if no insertion index is required, copy all data to new memory */
+    if(insertion_index == (uintptr_t)-1 || insertion_index >= new_count)
+        memcpy(new_data, old_data, vector->count * vector->element_size);
+
+    /* keep space for one element at the insertion index */
+    else
+    {
+        /* copy old data up until right before insertion offset */
+        uint32_t offset = vector->element_size * insertion_index;
+        uint32_t total_size = vector->element_size * vector->count;
+        memcpy(new_data, old_data, offset);
+        /* copy the remaining amount of old data shifted one element ahead */
+        memcpy((void*)((intptr_t)new_data + offset + vector->element_size),
+               (void*)((intptr_t)old_data + offset),
+               total_size - offset);
+    }
+
+    vector->data = new_data;
+    vector->capacity = new_count;
+    FREE(old_data);
+
+    return 0;
+}

+ 15 - 0
Source/ThirdParty/ik/ik/src/platform/linux/backtrace_linux.c

@@ -0,0 +1,15 @@
+#include "ik/backtrace.h"
+#include <execinfo.h>
+
+/* ------------------------------------------------------------------------- */
+char**
+get_backtrace(int* size)
+{
+    void* array[BACKTRACE_SIZE];
+    char** strings;
+
+    *size = backtrace(array, BACKTRACE_SIZE);
+    strings = backtrace_symbols(array, *size);
+
+    return strings;
+}

+ 147 - 0
Source/ThirdParty/ik/ik/src/quat.c

@@ -0,0 +1,147 @@
+#include "ik/quat.h"
+#include <math.h>
+#include <string.h>
+
+/* ------------------------------------------------------------------------- */
+void
+quat_set_identity(ik_real* q)
+{
+    memset(q, 0, sizeof(ik_real) * 3);
+    q[3] = 1;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_add_quat(ik_real* q1, const ik_real* q2)
+{
+    q1[0] += q2[0];
+    q1[1] += q2[1];
+    q1[2] += q2[2];
+    q1[3] += q2[3];
+}
+
+/* ------------------------------------------------------------------------- */
+ik_real
+quat_mag(const ik_real* q)
+{
+    return sqrt(q[3]*q[3] + q[2]*q[2] + q[1]*q[1] + q[0]*q[0]);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_conj(ik_real* q)
+{
+    q[0] = -q[0];
+    q[1] = -q[1];
+    q[2] = -q[2];
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_invert_sign(ik_real* q)
+{
+    q[0] = -q[0];
+    q[1] = -q[1];
+    q[2] = -q[2];
+    q[3] = -q[3];
+}
+
+
+/* ------------------------------------------------------------------------- */
+void
+quat_normalise(ik_real* q)
+{
+    ik_real mag = quat_mag(q);
+    if(mag != 0.0)
+        mag = 1.0 / mag;
+    q[0] *= mag;
+    q[1] *= mag;
+    q[2] *= mag;
+    q[3] *= mag;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_mul_quat(ik_real* q1, const ik_real* q2)
+{
+    ik_real v1[3];
+    ik_real v2[3];
+    memcpy(v1, q1, sizeof(ik_real) * 3);
+    memcpy(v2, q2, sizeof(ik_real) * 3);
+
+    vec3_mul_scalar(v1, q2[3]);
+    vec3_mul_scalar(v2, q1[3]);
+    q1[3] = q1[3]*q2[3] - vec3_dot(q1, q2);
+    vec3_cross(q1, q2);
+    vec3_add_vec3(q1, v1);
+    vec3_add_vec3(q1, v2);
+
+    quat_normalise(q1);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_mul_scalar(ik_real* q, ik_real scalar)
+{
+    q[0] *= scalar;
+    q[1] *= scalar;
+    q[2] *= scalar;
+    q[3] *= scalar;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_div_scalar(ik_real* q, ik_real scalar)
+{
+    if(scalar == 0.0)
+        quat_set_identity(q);
+    else
+    {
+        ik_real rec = 1.0 / scalar;
+        q[0] *= rec;
+        q[1] *= rec;
+        q[2] *= rec;
+        q[3] *= rec;
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+ik_real
+quat_dot(ik_real* q1, const ik_real* q2)
+{
+    return q1[0] * q2[0] +
+           q1[1] * q2[1] +
+           q1[2] * q2[2] +
+           q1[3] * q2[3];
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_rotate_vec(ik_real* v, const ik_real* q)
+{
+    /* P' = RPR' */
+    quat_t result;
+    quat_t conj;
+    quat_t point;
+
+    memcpy(point.f, v, sizeof(ik_real) * 3);
+    point.q.w = 0.0;
+
+    conj = *(quat_t*)q;
+    quat_conj(conj.f);
+
+    result = *(quat_t*)q;
+    quat_mul_quat(result.f, point.f);
+    quat_mul_quat(result.f, conj.f);
+    memcpy(v, result.f, sizeof(ik_real) * 3);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+quat_normalise_sign(ik_real* q1)
+{
+    quat_t unit = {{0, 0, 0, 1}};
+    ik_real dot = quat_dot(q1, unit.f);
+    if(dot < 0.0)
+        quat_invert_sign(q1);
+}

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

@@ -0,0 +1,181 @@
+#include "ik/effector.h"
+#include "ik/log.h"
+#include "ik/memory.h"
+#include "ik/node.h"
+#include "ik/solver.h"
+#include "ik/solver_FABRIK.h"
+#include "ik/solver_jacobian_inverse.h"
+#include "ik/solver_jacobian_transpose.h"
+
+static int
+recursive_get_all_effector_nodes(struct ik_node_t* node, struct ordered_vector_t* effector_nodes_list);
+
+/* ------------------------------------------------------------------------- */
+struct ik_solver_t*
+ik_solver_create(enum solver_algorithm_e algorithm)
+{
+    struct ik_solver_t* solver = NULL;
+
+    switch(algorithm)
+    {
+    case SOLVER_FABRIK:
+        solver = (struct ik_solver_t*)solver_FABRIK_create();
+        break;
+
+    /*case SOLVER_JACOBIAN_INVERSE:
+    case SOLVER_JACOBIAN_TRANSPOSE:
+        break;*/
+    }
+
+    if(solver == NULL)
+        return NULL;
+
+    ordered_vector_construct(&solver->effector_nodes_list, sizeof(struct ik_node_t*));
+
+    return solver;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_solver_destroy(struct ik_solver_t* solver)
+{
+    if(solver->tree)
+        ik_node_destroy(solver->tree);
+
+    ordered_vector_clear_free(&solver->effector_nodes_list);
+
+    solver->destroy(solver);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_solver_set_tree(struct ik_solver_t* solver, struct ik_node_t* root)
+{
+    ik_solver_destroy_tree(solver);
+    solver->tree = root;
+}
+
+/* ------------------------------------------------------------------------- */
+struct ik_node_t*
+ik_solver_unlink_tree(struct ik_solver_t* solver)
+{
+    struct ik_node_t* root = solver->tree;
+    if(root == NULL)
+        return NULL;
+    solver->tree = NULL;
+
+    /*
+     * Effectors are owned by the nodes, but we need to release references to
+     * them.
+     */
+    ordered_vector_clear(&solver->effector_nodes_list);
+
+    return root;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_solver_destroy_tree(struct ik_solver_t* solver)
+{
+    struct ik_node_t* root;
+    if((root = ik_solver_unlink_tree(solver)) == NULL)
+        return;
+    ik_node_destroy(root);
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ik_solver_rebuild_data(struct ik_solver_t* solver)
+{
+    /* If the solver has no tree, then there's nothing to do */
+    if(solver->tree == NULL)
+    {
+        ik_log_message("No tree to work with. Did you forget to set the tree with ik_solver_set_tree()?");
+        return -1;
+    }
+
+    /*
+     * Traverse the entire tree and generate a list of the effectors. This
+     * makes the process of building the chain list for FABRIK much easier.
+     */
+    ik_log_message("Rebuilding effector nodes list");
+    ordered_vector_clear(&solver->effector_nodes_list);
+    if(recursive_get_all_effector_nodes(solver->tree,
+                                        &solver->effector_nodes_list) < 0)
+    {
+        ik_log_message("Ran out of memory while building the effector nodes list");
+        return -1;
+    }
+
+    return solver->rebuild_data(solver);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ik_solver_recalculate_segment_lengths(struct ik_solver_t* solver)
+{
+    solver->recalculate_segment_lengths(solver);
+}
+
+/* ------------------------------------------------------------------------- */
+int
+ik_solver_solve(struct ik_solver_t* solver)
+{
+    return solver->solve(solver);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+iterate_tree_recursive(struct ik_solver_t* solver, struct ik_node_t* node)
+{
+    if(solver->apply_result)
+        solver->apply_result(node);
+
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        iterate_tree_recursive(solver, child);
+    BSTV_END_EACH
+}
+void
+ik_solver_iterate_tree(struct ik_solver_t* solver)
+{
+    if(solver->tree == NULL)
+        return;
+
+    iterate_tree_recursive(solver, solver->tree);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+reset_solved_data_recursive(struct ik_node_t* node)
+{
+    node->solved_position = node->position;
+    node->solved_rotation = node->rotation;
+
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        reset_solved_data_recursive(child);
+    BSTV_END_EACH
+}
+void
+ik_solver_reset_solved_data(struct ik_solver_t* solver)
+{
+    if(solver->tree == NULL)
+        return;
+
+    reset_solved_data_recursive(solver->tree);
+}
+
+/* ------------------------------------------------------------------------- */
+static int
+recursive_get_all_effector_nodes(struct ik_node_t* node, struct ordered_vector_t* effector_nodes_list)
+{
+    if(node->effector != NULL)
+        if(ordered_vector_push(effector_nodes_list, &node) < 0)
+            return -1;
+
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        if(recursive_get_all_effector_nodes(child, effector_nodes_list) < 0)
+            return -1;
+    BSTV_END_EACH
+
+    return 0;
+}

+ 814 - 0
Source/ThirdParty/ik/ik/src/solver_FABRIK.c

@@ -0,0 +1,814 @@
+#include "ik/bst_vector.h"
+#include "ik/effector.h"
+#include "ik/log.h"
+#include "ik/memory.h"
+#include "ik/node.h"
+#include "ik/solver_FABRIK.h"
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+enum node_marking_e
+{
+    MARK_NONE = 0,
+    MARK_SPLIT,
+    MARK_SECTION
+};
+
+struct effector_data_t
+{
+    vec3_t target_direction;
+    ik_real rotation_weight;
+    ik_real rotation_weight_decay;
+};
+
+static void
+chain_construct(struct chain_t* chain);
+static void
+chain_clear_free(struct chain_t* chain);
+static void
+chain_destruct(struct chain_t* chain);
+static void
+calculate_segment_lengths_recursive(struct chain_t* chain);
+
+/* ------------------------------------------------------------------------- */
+static void
+chain_construct(struct chain_t* chain)
+{
+    ordered_vector_construct(&chain->nodes, sizeof(struct ik_node_t*));
+    ordered_vector_construct(&chain->children, sizeof(struct chain_t));
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+chain_clear_free(struct chain_t* chain)
+{
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child_chain)
+        chain_destruct(child_chain);
+    ORDERED_VECTOR_END_EACH
+    ordered_vector_clear_free(&chain->children);
+    ordered_vector_clear_free(&chain->nodes);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+chain_destruct(struct chain_t* chain)
+{
+    chain_clear_free(chain);
+}
+
+/*!
+ * @brief Breaks down the relevant nodes of the scene graph into a list of
+ * chains. FABRIK can then more efficiently solve each chain individually.
+ *
+ * A "sub-base joint" is a node in the scene graph where at least two end
+ * effector nodes eventually join together. FABRIK only works on single
+ * chains of joints at a time. The end position of every sub-base joint is
+ * the average of the resulting multiple positions after running FABRIK on
+ * each chain. Said average position becomes the new target position for
+ * the next chain connected to it.
+ *
+ * This algorithm finds all sub-base joints and generates chains between
+ * base, sub-base joints, and end effectors. These chains are inserted into
+ * the chain list. The order is such that iterating the list from the
+ * beginning results in traversing the sub-base nodes breadth-last. This is
+ * important.
+ *
+ * @note Effectors that are deactivated or invalid are ignored in this search.
+ * So even though a node might share two effectors, if one of them is
+ * deactivated, then the node is no longer considered a sub-base node.
+ */
+static int
+rebuild_chain_tree(struct fabrik_t* solver);
+
+/* ------------------------------------------------------------------------- */
+struct ik_solver_t*
+solver_FABRIK_create(void)
+{
+    struct fabrik_t* solver;
+
+    ik_log_message("Creating FABRIK solver");
+
+    solver = (struct fabrik_t*)MALLOC(sizeof *solver);
+    if(solver == NULL)
+    {
+        ik_log_message("Ran out of memory in solver_FABRIK_create()");
+        goto alloc_solver_filed;
+    }
+    memset(solver, 0, sizeof *solver);
+
+    solver->destroy = solver_FABRIK_destroy;
+    solver->rebuild_data = solver_FABRIK_rebuild_data;
+    solver->recalculate_segment_lengths = solver_FABRIK_recalculate_segment_lengths;
+    solver->solve = solver_FABRIK_solve;
+
+    solver->max_iterations = 20;
+    solver->tolerance = 1e-3;
+
+    solver->chain_tree = (struct chain_t*)MALLOC(sizeof(struct chain_t));
+    if(solver->chain_tree == NULL)
+    {
+        ik_log_message("Ran out of memory in solver_FABRIK_create()");
+        goto alloc_chain_tree_failed;
+    }
+    chain_construct(solver->chain_tree);
+
+    return (struct ik_solver_t*)solver;
+
+    alloc_chain_tree_failed : FREE(solver);
+    alloc_solver_filed      : return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+solver_FABRIK_destroy(struct ik_solver_t* solver)
+{
+    struct fabrik_t* fabrik;
+
+    ik_log_message("Destroying FABRIK solver");
+
+    fabrik = (struct fabrik_t*)solver;
+    chain_destruct(fabrik->chain_tree);
+    FREE(fabrik->chain_tree);
+    FREE(solver);
+}
+
+/* ------------------------------------------------------------------------- */
+int
+solver_FABRIK_rebuild_data(struct ik_solver_t* solver)
+{
+    return rebuild_chain_tree((struct fabrik_t*)solver);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+determine_target_data_from_effector(struct chain_t* chain, vec3_t* target_position)
+{
+    /* Extract effector node and get its effector object */
+    struct ik_node_t* effector_node;
+    struct ik_effector_t* effector;
+    assert(ordered_vector_count(&chain->nodes) > 1);
+    effector_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, 0);
+    assert(effector_node->effector != NULL);
+    effector = effector_node->effector;
+
+    /* lerp using effector weight to get actual target position */
+    *target_position = effector->target_position;
+    vec3_sub_vec3(target_position->f, effector_node->position.f);
+    vec3_mul_scalar(target_position->f, effector->weight);
+    vec3_add_vec3(target_position->f, effector_node->position.f);
+
+    /* Fancy algorithm using nlerp, makes transitions look more natural */
+    if(effector->flags & EFFECTOR_WEIGHT_NLERP && effector->weight < 1.0)
+    {
+        ik_real distance_to_target;
+        vec3_t base_to_effector;
+        vec3_t base_to_target;
+        struct ik_node_t* base_node;
+
+        /* Need distance from base node to target and base to effector node */
+        base_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes,
+                ordered_vector_count(&chain->nodes) - 1);
+        base_to_effector = effector_node->position;
+        base_to_target = effector->target_position;
+        vec3_sub_vec3(base_to_effector.f, base_node->position.f);
+        vec3_sub_vec3(base_to_target.f, base_node->position.f);
+
+        /* The effective distance is a lerp between these two distances */
+        distance_to_target = vec3_length(base_to_target.f) * effector->weight;
+        distance_to_target += vec3_length(base_to_effector.f) * (1.0 - effector->weight);
+
+        /* nlerp the target position by pinning it to the base node */
+        vec3_sub_vec3(target_position->f, base_node->position.f);
+        vec3_normalise(target_position->f);
+        vec3_mul_scalar(target_position->f, distance_to_target);
+        vec3_add_vec3(target_position->f, base_node->position.f);
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+static vec3_t
+solve_chain_forwards_with_target_rotation(struct chain_t* chain, struct effector_data_t* effector_data)
+{
+    int node_count, node_idx;
+    int average_count;
+    vec3_t target_position = {{0, 0, 0}};
+
+    /*
+     * Target position is the average of all solved child chain base positions.
+     */
+    average_count = 0;
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        struct effector_data_t child_effector_data;
+        vec3_t child_base_position = solve_chain_forwards_with_target_rotation(child, &child_effector_data);
+        vec3_add_vec3(target_position.f, child_base_position.f);
+        vec3_add_vec3(effector_data->target_direction.f, child_effector_data.target_direction.f);
+        effector_data->rotation_weight += child_effector_data.rotation_weight;
+        effector_data->rotation_weight_decay += child_effector_data.rotation_weight_decay;
+        ++average_count;
+    ORDERED_VECTOR_END_EACH
+
+    /*
+     * If there are no child chains, then the first node in the chain must
+     * contain an effector. The target position is the effector's target
+     * position. Otherwise, average the data we've been accumulating from the
+     * child chains.
+     */
+    if(average_count == 0)
+    {
+        struct ik_node_t* effector_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, 0);
+        struct ik_effector_t* effector = effector_node->effector;
+        determine_target_data_from_effector(chain, &target_position);
+
+        effector_data->rotation_weight = effector->rotation_weight;
+        effector_data->rotation_weight_decay = effector->rotation_decay;
+        /* TODO This "global direction" could be made configurable if needed */
+        effector_data->target_direction.v.x = 0.0;
+        effector_data->target_direction.v.y = 0.0;
+        effector_data->target_direction.v.z = 1.0;
+        quat_rotate_vec(effector_data->target_direction.f, effector->target_rotation.f);
+
+    }
+    else
+    {
+        ik_real div = 1.0 / average_count;
+        vec3_mul_scalar(target_position.f, div);
+        vec3_mul_scalar(effector_data->target_direction.f, div);
+        effector_data->rotation_weight *= div;
+        effector_data->rotation_weight_decay *= div;
+    }
+
+    /*
+     * Iterate through each segment and apply the FABRIK algorithm.
+     */
+    node_count = ordered_vector_count(&chain->nodes);
+    for(node_idx = 0; node_idx < node_count - 1; ++node_idx)
+    {
+        struct ik_node_t* child_node  = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 0);
+        struct ik_node_t* parent_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 1);
+
+        /* move node to target */
+        child_node->solved_position = target_position;
+
+        /* lerp direction vector and segment vector */
+        vec3_sub_vec3(target_position.f, effector_data->target_direction.f);
+        vec3_sub_vec3(target_position.f, parent_node->solved_position.f);
+        vec3_mul_scalar(target_position.f, effector_data->rotation_weight);
+        vec3_add_vec3(target_position.f, parent_node->solved_position.f);
+
+        vec3_sub_vec3(target_position.f, child_node->solved_position.f);
+        vec3_normalise(target_position.f);
+        vec3_mul_scalar(target_position.f, child_node->segment_length);
+        vec3_add_vec3(target_position.f, child_node->solved_position.f);
+
+        effector_data->rotation_weight *= effector_data->rotation_weight_decay;
+    }
+
+    return target_position;
+}
+
+/* ------------------------------------------------------------------------- */
+static vec3_t
+solve_chain_forwards(struct chain_t* chain)
+{
+    int node_count, node_idx;
+    int average_count;
+    vec3_t target_position = {{0, 0, 0}};
+
+    /*
+     * Target position is the average of all solved child chain base positions.
+     */
+    average_count = 0;
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        vec3_t child_base_position = solve_chain_forwards(child);
+        vec3_add_vec3(target_position.f, child_base_position.f);
+        ++average_count;
+    ORDERED_VECTOR_END_EACH
+
+    /*
+     * If there are no child chains, then the first node in the chain must
+     * contain an effector. The target position is the effector's target
+     * position. Otherwise, average the data we've been accumulating from the
+     * child chains.
+     */
+    if(average_count == 0)
+        determine_target_data_from_effector(chain, &target_position);
+    else
+        vec3_div_scalar(target_position.f, average_count);
+
+    /*
+     * Iterate through each segment and apply the FABRIK algorithm.
+     */
+    node_count = ordered_vector_count(&chain->nodes);
+    for(node_idx = 0; node_idx < node_count - 1; ++node_idx)
+    {
+        struct ik_node_t* child_node  = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 0);
+        struct ik_node_t* parent_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 1);
+
+        /* move node to target */
+        child_node->solved_position = target_position;
+
+        /* point segment to previous node and set target position to its end */
+        vec3_sub_vec3(target_position.f, parent_node->solved_position.f); /* parent points to child */
+        vec3_normalise(target_position.f);                                /* normalise */
+        vec3_mul_scalar(target_position.f, -child_node->segment_length);  /* child points to parent */
+        vec3_add_vec3(target_position.f, child_node->solved_position.f);  /* attach to child -- this is the new target */
+    }
+
+    return target_position;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+solve_chain_backwards(struct chain_t* chain, vec3_t target_position)
+{
+    int node_idx = ordered_vector_count(&chain->nodes) - 1;
+
+    /*
+     * The base node must be set to the target position before iterating.
+     */
+    if(node_idx > 1)
+    {
+        struct ik_node_t* base_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx);
+        base_node->solved_position = target_position;
+    }
+
+    /*
+     * Iterate through each segment the other way around and apply the FABRIK
+     * algorithm.
+     */
+    while(node_idx-- > 0)
+    {
+        struct ik_node_t* child_node  = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 0);
+        struct ik_node_t* parent_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 1);
+
+        /* point segment to child node and set target position to its beginning */
+        vec3_sub_vec3(target_position.f, child_node->solved_position.f);  /* child points to parent */
+        vec3_normalise(target_position.f);                                /* normalise */
+        vec3_mul_scalar(target_position.f, -child_node->segment_length);  /* parent points to child */
+        vec3_add_vec3(target_position.f, parent_node->solved_position.f); /* attach to parent -- this is the new target */
+
+        /* move node to target */
+        child_node->solved_position = target_position;
+    }
+
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        solve_chain_backwards(child, target_position);
+    ORDERED_VECTOR_END_EACH
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+calculate_global_rotations(struct chain_t* chain)
+{
+    /*
+     * Calculates the "global" (world) angles of each joint and writes them to
+     * each node->solved_rotation slot.
+     *
+     * The angle between the original and solved segments are calculated using
+     * standard vector math (dot product). The axis of rotation is calculated
+     * with the cross product. From this data, a quaternion is constructed,
+     * describing this delta rotation. Finally, in order to make the rotations
+     * global instead of relative, the delta rotation is multiplied with
+     * node->rotation, which should be a quaternion describing the node's
+     * global rotation in the unsolved tree.
+     *
+     * The rotation of the base joint in the chain is returned so it can be
+     * averaged by parent chains.
+     */
+
+    int node_idx;
+    int average_count;
+    quat_t average_rotation = {{0, 0, 0, 0}};
+
+    /* Recursive into children chains */
+    average_count = 0;
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        quat_t rotation;
+        calculate_global_rotations(child);
+
+        /* Note: All chains that aren't the root chain *MUST* have at least two nodes */
+        assert(ordered_vector_count(&child->nodes) >= 2);
+        rotation = (*(struct ik_node_t**)
+                ordered_vector_get_element(&child->nodes,
+                    ordered_vector_count(&child->nodes) - 1))->solved_rotation;
+
+        /*
+         * Averaging quaternions taken from here
+         * http://wiki.unity3d.com/index.php/Averaging_Quaternions_and_Vectors
+         */
+        quat_normalise_sign(rotation.f);
+        quat_add_quat(average_rotation.f, rotation.f);
+        ++average_count;
+    ORDERED_VECTOR_END_EACH
+
+    /*
+     * Assuming there was more than 1 child chain and assuming we aren't the
+     * root node, then the child chains we just iterated must share the same
+     * base node as our tip node. Average the accumulated quaternion and set
+     * this node's correct solved rotation.
+     */
+    if(average_count > 0 && ordered_vector_count(&chain->nodes) != 0)
+    {
+        quat_div_scalar(average_rotation.f, average_count);
+        quat_normalise(average_rotation.f);
+        (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, 0))
+            ->solved_rotation = average_rotation;
+    }
+
+    node_idx = ordered_vector_count(&chain->nodes) - 1;
+    while(node_idx-- > 0)
+    {
+        ik_real cos_a, sin_a, angle, denominator;
+        struct ik_node_t* child_node  = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 0);
+        struct ik_node_t* parent_node = *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 1);
+
+        /* calculate vectors for original and solved segments */
+        vec3_t segment_original = child_node->position;
+        vec3_t segment_solved   = child_node->solved_position;
+        vec3_sub_vec3(segment_original.f, parent_node->position.f);
+        vec3_sub_vec3(segment_solved.f, parent_node->solved_position.f);
+
+        /*
+         * Calculate angle between original segment and solved segment. If the
+         * angle is 0 or 180, we don't do anything. The solved rotation is
+         * initially set to the original rotation.
+         */
+        denominator = 1.0 / vec3_length(segment_original.f) / vec3_length(segment_solved.f);
+        cos_a = vec3_dot(segment_original.f, segment_solved.f) * denominator;
+        if(cos_a >= -1.0 && cos_a <= 1.0)
+        {
+            /* calculate axis of rotation and write it to the quaternion's vector section */
+            parent_node->solved_rotation.vw.v = segment_original;
+            vec3_cross(parent_node->solved_rotation.vw.v.f, segment_solved.f);
+            vec3_normalise(parent_node->solved_rotation.f);
+
+            /* quaternion's vector needs to be weighted with sin_a */
+            angle = acos(cos_a);
+            cos_a = cos(angle * 0.5);
+            sin_a = sin(angle * 0.5);
+            vec3_mul_scalar(parent_node->solved_rotation.f, sin_a);
+            parent_node->solved_rotation.q.w = cos_a;
+
+            /*
+             * Apply initial global rotation to calculated delta rotation to
+             * obtain the solved global rotation.
+             */
+            quat_mul_quat(parent_node->solved_rotation.f, parent_node->rotation.f);
+        }
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+int
+solver_FABRIK_solve(struct ik_solver_t* solver)
+{
+    struct fabrik_t* fabrik = (struct fabrik_t*)solver;
+    int iteration;
+    ik_real tolerance_squared = solver->tolerance * solver->tolerance;
+
+    if(!(solver->flags & SOLVER_SKIP_RESET))
+        ik_solver_reset_solved_data(solver);
+
+    iteration = solver->max_iterations;
+    while(iteration--)
+    {
+        vec3_t root_position;
+        struct effector_data_t effector_data;
+
+        ORDERED_VECTOR_FOR_EACH(&fabrik->chain_tree->children, struct chain_t, chain)
+
+            assert(ordered_vector_count(&chain->nodes) > 1);
+            root_position = (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes,
+                    ordered_vector_count(&chain->nodes) - 1))->position;
+
+            if(solver->flags & SOLVER_CALCULATE_TARGET_ROTATIONS)
+                solve_chain_forwards_with_target_rotation(chain, &effector_data);
+            else
+                solve_chain_forwards(chain);
+
+            solve_chain_backwards(chain, root_position);
+        ORDERED_VECTOR_END_EACH
+
+        ORDERED_VECTOR_FOR_EACH(&fabrik->effector_nodes_list, struct ik_node_t*, pnode)
+            vec3_t diff = (*pnode)->solved_position;
+            vec3_sub_vec3(diff.f, (*pnode)->effector->target_position.f);
+            if(vec3_length_squared(diff.f) > tolerance_squared)
+                goto continue_iterating;
+        ORDERED_VECTOR_END_EACH
+
+        break;
+        continue_iterating:;
+    }
+
+    if(solver->flags & SOLVER_CALCULATE_FINAL_ROTATIONS)
+        calculate_global_rotations(fabrik->chain_tree);
+
+    if(!(solver->flags & SOLVER_SKIP_APPLY))
+        ik_solver_iterate_tree(solver);
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+solver_FABRIK_recalculate_segment_lengths(struct ik_solver_t* solver)
+{
+    struct fabrik_t* fabrik = (struct fabrik_t*)solver;
+    calculate_segment_lengths_recursive(fabrik->chain_tree);
+}
+
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------------- */
+int
+mark_involved_nodes(struct fabrik_t* solver, struct bstv_t* involved_nodes)
+{
+    /*
+     * Traverse the chain of parents starting at each effector node and ending
+     * at the root node of the tree and mark every node on the way. Each
+     * effector specifies a maximum chain length, which means it's possible
+     * that we won't hit the root node.
+     */
+    struct ordered_vector_t* effector_nodes_list = &solver->effector_nodes_list;
+    ORDERED_VECTOR_FOR_EACH(effector_nodes_list, struct ik_node_t*, p_effector_node)
+
+        /*
+         * Set up chain length counter. If the chain length is 0 then it is
+         * infinitely long. Set the counter to -1 in this case to skip the
+         * escape condition.
+         */
+        int chain_length_counter;
+        struct ik_node_t* node = *p_effector_node;
+        assert(node->effector != NULL);
+        chain_length_counter = node->effector->chain_length == 0 ? -1 : (int)node->effector->chain_length;
+
+        /*
+         * Mark nodes that are at the base of the chain differently, so the
+         * chains can be split correctly later. Section markings will overwrite
+         * break markings.
+         */
+        for(; node != NULL; node = node->parent)
+        {
+            enum node_marking_e* current_marking;
+            enum node_marking_e marking = MARK_SECTION;
+            if(chain_length_counter == 0)
+                marking = MARK_SPLIT;
+
+            current_marking = (enum node_marking_e*)bstv_find_ptr(involved_nodes, node->guid);
+            if(current_marking == NULL)
+            {
+                if(bstv_insert(involved_nodes, node->guid, (void*)(intptr_t)marking) < 0)
+                {
+                    ik_log_message("Ran out of memory while marking involved nodes");
+                    return -1;
+                }
+            }
+            else
+            {
+                if(chain_length_counter != 0)
+                    *current_marking = marking;
+            }
+
+            if(chain_length_counter-- == 0)
+                break;
+        }
+    ORDERED_VECTOR_END_EACH
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+static int
+recursively_build_chain_tree(struct chain_t* chain_current,
+                             struct ik_node_t* node_base,
+                             struct ik_node_t* node_current,
+                             struct bstv_t* involved_nodes)
+{
+    int marked_children_count;
+    struct ik_node_t* child_node_base = node_base;
+    struct chain_t* child_chain = chain_current;
+
+    /* can remove the mark from the set to speed up future checks */
+    enum node_marking_e marking =
+        (enum node_marking_e)(intptr_t)bstv_erase(involved_nodes, node_current->guid);
+
+    switch(marking)
+    {
+        /*
+         * If this node was marked as the base of a chain then split the chain at
+         * this point by moving the pointer to the base node down the tree to us.
+         */
+        case MARK_SPLIT:
+            child_node_base = node_current;
+            break;
+        /*
+         * If this node is not marked at all, cut off any previous chain but
+         * continue (fall through) as if a section was marked. It's possible
+         * that there are isolated chains somewhere further down the tree.
+         */
+        case MARK_NONE:
+            node_base = node_current;
+
+        case MARK_SECTION:
+            /*
+             * If the current node has at least two children marked as sections
+             * or if the current node is an effector node, but only if the base
+             * node is not equal to this node (that is, we need to avoid chains
+             * that would have less than 2 nodes), then we must also split the
+             * chain at this point.
+             */
+            marked_children_count = 0;
+            BSTV_FOR_EACH(&node_current->children, struct ik_node_t, child_guid, child)
+                if((enum node_marking_e)(intptr_t)bstv_find(involved_nodes, child_guid) == MARK_SECTION)
+                    if(++marked_children_count == 2)
+                        break;
+            BSTV_END_EACH
+            if((marked_children_count == 2 || node_current->effector != NULL) && node_current != node_base)
+            {
+                /*
+                 * Emplace a chain object into the current chain's vector of children
+                 * and initialise it.
+                 */
+                struct ik_node_t* node;
+                child_chain = ordered_vector_push_emplace(&chain_current->children);
+                if(child_chain == NULL)
+                    return -1;
+                chain_construct(child_chain);
+
+                /*
+                 * Add points to all nodes that are part of this chain into the chain's
+                 * list, starting with the end node.
+                 */
+                for(node = node_current; node != node_base; node = node->parent)
+                    ordered_vector_push(&child_chain->nodes, &node);
+                ordered_vector_push(&child_chain->nodes, &node_base);
+
+                /*
+                 * Update the base node to be this node so deeper chains are built back
+                 * to this node
+                 */
+                child_node_base = node_current;
+            }
+            break;
+    }
+
+    /* Recurse into children of the current node. */
+    BSTV_FOR_EACH(&node_current->children, struct ik_node_t, child_guid, child_node)
+        if(recursively_build_chain_tree(
+                child_chain,
+                child_node_base,
+                child_node,
+                involved_nodes) < 0)
+            return -1;
+    BSTV_END_EACH
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+calculate_segment_lengths_recursive(struct chain_t* chain)
+{
+    int last_idx = ordered_vector_count(&chain->nodes) - 1;
+    while(last_idx-- > 0)
+    {
+        struct ik_node_t* child_node =
+            *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 0);
+        struct ik_node_t* parent_node =
+            *(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 1);
+
+        vec3_t diff = child_node->position;
+        vec3_sub_vec3(diff.f, parent_node->position.f);
+        child_node->segment_length = vec3_length(diff.f);
+    }
+
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        calculate_segment_lengths_recursive(child);
+    ORDERED_VECTOR_END_EACH
+}
+
+/* ------------------------------------------------------------------------- */
+static int
+count_chains(struct chain_t* chain)
+{
+    int counter = 1;
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        counter += count_chains(child);
+    ORDERED_VECTOR_END_EACH
+    return counter;
+}
+
+/* ------------------------------------------------------------------------- */
+#if IK_DOT_OUTPUT == ON
+static void
+dump_chain(struct chain_t* chain, FILE* fp)
+{
+    int last_idx = ordered_vector_count(&chain->nodes) - 1;
+    if(last_idx > 0)
+    {
+        fprintf(fp, "    %d [shape=record];\n",
+            (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, 0))->guid);
+        fprintf(fp, "    %d [shape=record];\n",
+            (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx))->guid);
+    }
+
+    while(last_idx-- > 0)
+    {
+        fprintf(fp, "    %d -- %d [color=\"1.0 0.5 1.0\"];\n",
+            (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 0))->guid,
+            (*(struct ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 1))->guid);
+    }
+
+    ORDERED_VECTOR_FOR_EACH(&chain->children, struct chain_t, child)
+        dump_chain(child, fp);
+    ORDERED_VECTOR_END_EACH
+}
+static void
+dump_node(struct ik_node_t* node, FILE* fp)
+{
+    if(node->effector != NULL)
+        fprintf(fp, "    %d [color=\"0.6 0.5 1.0\"];\n", node->guid);
+    BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
+        fprintf(fp, "    %d -- %d;\n", node->guid, guid);
+        dump_node(child, fp);
+    BSTV_END_EACH
+}
+static void
+dump_to_dot(struct ik_node_t* node, struct chain_t* chain, const char* file_name)
+{
+    FILE* fp = fopen(file_name, "w");
+    if(fp == NULL)
+        return;
+
+    fprintf(fp, "graph chain_tree {\n");
+    dump_node(node, fp);
+    dump_chain(chain, fp);
+    fprintf(fp, "}\n");
+
+    fclose(fp);
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+static int
+rebuild_chain_tree(struct fabrik_t* solver)
+{
+    struct bstv_t involved_nodes;
+    int involved_nodes_count;
+#if IK_DOT_OUTPUT == ON
+    char buffer[20];
+    static int file_name_counter = 0;
+#endif
+
+    /*
+     * Build a set of all nodes that are in a direct path with all of the
+     * effectors.
+     */
+    bstv_construct(&involved_nodes);
+    if(mark_involved_nodes(solver, &involved_nodes) < 0)
+        goto mark_involved_nodes_failed;
+    involved_nodes_count = bstv_count(&involved_nodes);
+
+    /*
+     * The user can choose to set the root node as a chain terminator (default)
+     * or choose to exclude the root node, in which case each immediate child
+     * of the tree is a chain terminator. In this case we need to build the
+     * chain tree for each child individually.
+     */
+    chain_clear_free(solver->chain_tree);
+    if(solver->flags & SOLVER_EXCLUDE_ROOT)
+    {
+        BSTV_FOR_EACH(&solver->tree->children, struct ik_node_t, guid, child)
+            recursively_build_chain_tree(solver->chain_tree, child, child, &involved_nodes);
+        BSTV_END_EACH
+    }
+    else
+    {
+        recursively_build_chain_tree(solver->chain_tree, solver->tree, solver->tree, &involved_nodes);
+    }
+
+    /* Pre-compute offsets for each node in the chain tree in relation to their
+     * parents */
+    calculate_segment_lengths_recursive(solver->chain_tree);
+
+    /* DEBUG: Save chain tree to DOT */
+#if IK_DOT_OUTPUT == ON
+    sprintf(buffer, "tree%d.dot", file_name_counter++);
+    dump_to_dot(solver->tree, solver->chain_tree, buffer);
+#endif
+
+    ik_log_message("There are %d effector(s) involving %d node(s). %d chain(s) were created",
+                   ordered_vector_count(&solver->effector_nodes_list),
+                   involved_nodes_count,
+                   count_chains(solver->chain_tree) - 1); /* don't count root chain which always exists */
+
+    bstv_clear_free(&involved_nodes);
+
+    return 0;
+
+    mark_involved_nodes_failed : bstv_clear_free(&involved_nodes);
+    return -1;
+}

+ 92 - 0
Source/ThirdParty/ik/ik/src/vec3.c

@@ -0,0 +1,92 @@
+#include "ik/vec3.h"
+#include <string.h>
+#include <math.h>
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_set_zero(ik_real* v)
+{
+    memset(v, 0, sizeof *v);
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_add_vec3(ik_real* v1, const ik_real* v2)
+{
+    v1[0] += v2[0];
+    v1[1] += v2[1];
+    v1[2] += v2[2];
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_sub_vec3(ik_real* v1, const ik_real* v2)
+{
+    v1[0] -= v2[0];
+    v1[1] -= v2[1];
+    v1[2] -= v2[2];
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_mul_scalar(ik_real* v, ik_real scalar)
+{
+    v[0] *= scalar;
+    v[1] *= scalar;
+    v[2] *= scalar;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_div_scalar(ik_real* v, ik_real scalar)
+{
+    v[0] /= scalar;
+    v[1] /= scalar;
+    v[2] /= scalar;
+}
+
+/* ------------------------------------------------------------------------- */
+ik_real
+vec3_length_squared(const ik_real* v)
+{
+    return vec3_dot(v, v);
+}
+
+/* ------------------------------------------------------------------------- */
+ik_real
+vec3_length(const ik_real* v)
+{
+    return sqrt(vec3_length_squared(v));
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_normalise(ik_real* v)
+{
+    ik_real length = vec3_length(v);
+    if(length != 0.0)
+        length = 1.0 / length;
+    v[0] *= length;
+    v[1] *= length;
+    v[2] *= length;
+}
+
+/* ------------------------------------------------------------------------- */
+ik_real
+vec3_dot(const ik_real* v1, const ik_real* v2)
+{
+    return v1[0] * v2[0] +
+           v1[1] * v2[1] +
+           v1[2] * v2[2];
+}
+
+/* ------------------------------------------------------------------------- */
+void
+vec3_cross(ik_real* v1, const ik_real* v2)
+{
+    ik_real v1x = v1[1] * v2[2] - v2[1] * v1[2];
+    ik_real v1z = v1[0] * v2[1] - v2[0] * v1[1];
+    v1[1]       = v1[2] * v2[0] - v2[2] * v1[0];
+    v1[0] = v1x;
+    v1[2] = v1z;
+}

+ 103 - 0
Source/Urho3D/AngelScript/IKAPI.cpp

@@ -0,0 +1,103 @@
+//
+// Copyright (c) 2008-2017 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.
+//
+
+#ifdef URHO3D_PHYSICS
+
+#include "../Precompiled.h"
+
+#include "../AngelScript/APITemplates.h"
+#include "../IK/IKSolver.h"
+#include "../IK/IKEffector.h"
+#include "../IK/IKConstraint.h"
+
+namespace Urho3D
+{
+
+static void RegisterIKSolver(asIScriptEngine* engine)
+{
+    engine->RegisterEnum("IKAlgorithm");
+    engine->RegisterEnumValue("IKAlgorithm", "FABRIK", IKSolver::FABRIK);
+
+    RegisterComponent<IKSolver>(engine, "IKSolver");
+    engine->RegisterObjectMethod("IKSolver", "IKAlgorithm get_algorithm() const", asMETHOD(IKSolver, GetAlgorithm), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_algorithm(IKAlgorithm)", asMETHOD(IKSolver, SetAlgorithm), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "uint get_maximumIterations() const", asMETHOD(IKSolver, GetMaximumIterations), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_maximumIterations(uint)", asMETHOD(IKSolver, SetMaximumIterations), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "float get_tolerance() const", asMETHOD(IKSolver, GetTolerance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_tolerance(float)", asMETHOD(IKSolver, SetTolerance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "bool get_boneRotations() const", asMETHOD(IKSolver, BoneRotationsEnabled), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKSolver", "void set_boneRotations(bool)", asMETHOD(IKSolver, EnableBoneRotations), 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_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 DrawDebugGeometry(bool)", asMETHODPR(IKSolver, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
+}
+
+static void RegisterIKEffector(asIScriptEngine* engine)
+{
+    RegisterComponent<IKEffector>(engine, "IKEffector");
+    engine->RegisterObjectMethod("IKEffector", "Node@+ get_targetNode() const", asMETHOD(IKEffector, GetTargetNode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_targetNode(Node@+)", asMETHOD(IKEffector, SetTargetNode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "String& get_targetName() const", asMETHOD(IKEffector, GetTargetName), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_targetName(const String&in)", asMETHOD(IKEffector, SetTargetName), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "Vector3& get_targetPosition() const", asMETHOD(IKEffector, GetTargetPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_targetPosition(Vector3&in)", asMETHOD(IKEffector, SetTargetPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "Quaternion& get_targetRotation() const", asMETHOD(IKEffector, GetTargetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_targetRotation(Quaternion&in)", asMETHOD(IKEffector, SetTargetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "uint get_chainLength() const", asMETHOD(IKEffector, GetChainLength), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_chainLength(uint)", asMETHOD(IKEffector, SetChainLength), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "float get_weight() const", asMETHOD(IKEffector, GetWeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_weight(float)", asMETHOD(IKEffector, SetWeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "float get_rotationWeight() const", asMETHOD(IKEffector, GetRotationWeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_rotationWeight(float)", asMETHOD(IKEffector, SetRotationWeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "float get_rotationDecay() const", asMETHOD(IKEffector, GetRotationDecay), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_rotationDecay(float)", asMETHOD(IKEffector, SetRotationDecay), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "bool get_weightedNlerp() const", asMETHOD(IKEffector, WeightedNlerpEnabled), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "void set_weightedNlerp(bool)", asMETHOD(IKEffector, EnableWeightedNlerp), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IKEffector", "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);
+}
+
+static void RegisterIKConstraint(asIScriptEngine* engine)
+{
+    RegisterComponent<IKConstraint>(engine, "IKConstraint");
+}
+
+void RegisterIKAPI(asIScriptEngine* engine)
+{
+    RegisterIKSolver(engine);
+    RegisterIKEffector(engine);
+    RegisterIKConstraint(engine);
+}
+
+}
+
+#endif

+ 3 - 0
Source/Urho3D/AngelScript/Script.cpp

@@ -126,6 +126,9 @@ Script::Script(Context* context) :
 #ifdef URHO3D_DATABASE
     RegisterDatabaseAPI(scriptEngine_);
 #endif
+#ifdef URHO3D_IK
+    RegisterIKAPI(scriptEngine_);
+#endif
 #ifdef URHO3D_PHYSICS
     RegisterPhysicsAPI(scriptEngine_);
 #endif

+ 4 - 0
Source/Urho3D/AngelScript/ScriptAPI.h

@@ -55,6 +55,10 @@ void RegisterNetworkAPI(asIScriptEngine* engine);
 /// Register the Database library to script.
 void RegisterDatabaseAPI(asIScriptEngine* engine);
 #endif
+#ifdef URHO3D_IK
+/// Register the inverse kinematics library to script
+void RegisterIKAPI(asIScriptEngine* engine);
+#endif
 #ifdef URHO3D_PHYSICS
 /// Register the Physics library to script.
 void RegisterPhysicsAPI(asIScriptEngine* engine);

+ 3 - 3
Source/Urho3D/CMakeLists.txt

@@ -97,7 +97,7 @@ if (WIN32)
 endif ()
 
 # Define source files
-foreach (DIR Navigation Network Physics Urho2D)
+foreach (DIR IK Navigation Network Physics Urho2D)
     string (TOUPPER URHO3D_${DIR} OPT)
     if (NOT ${OPT})
         list (APPEND EXCLUDED_SOURCE_DIRS ${DIR})
@@ -210,7 +210,7 @@ if (URHO3D_LUA)
     # Use the host tool to generate source files for tolua++ API binding
     file (MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/LuaScript/generated)
     file (GLOB API_PKG_FILES LuaScript/pkgs/*.pkg)
-    foreach (DIR Navigation Network Database Physics Urho2D)
+    foreach (DIR IK Navigation Network Database Physics Urho2D)
         string (TOUPPER URHO3D_${DIR} OPT)
         if (NOT ${OPT})
             list (REMOVE_ITEM API_PKG_FILES ${CMAKE_CURRENT_SOURCE_DIR}/LuaScript/pkgs/${DIR}LuaAPI.pkg)
@@ -448,7 +448,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
 list (SORT 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})
     if (URHO_HEADERS MATCHES "(include/Urho3D/${SUB}[^;]+)")
         list (FIND URHO_HEADERS ${CMAKE_MATCH_1} FOUND_INDEX)

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

@@ -32,12 +32,29 @@
 #include <SDL/SDL.h>
 #endif
 
+#ifdef URHO3D_IK
+#include <ik/memory.h>
+#include <ik/log.h>
+#endif
+
 namespace Urho3D
 {
 
 // Keeps track of how many times SDL was initialised so we know when to call SDL_Quit().
 static int sdlInitCounter = 0;
 
+#ifdef URHO3D_IK
+// Keeps track of how many times IK was initialised
+static int ikInitCounter = 0;
+
+// Reroute all messages from the ik library to the Urho3D log
+static void HandleIKLog(const char* msg)
+{
+    URHO3D_LOGINFOF("[IK] %s", msg);
+}
+#endif
+
+
 void EventReceiverGroup::BeginSendEvent()
 {
     ++inSend_;
@@ -225,7 +242,7 @@ bool Context::RequireSDL(unsigned int sdlFlags)
     ++sdlInitCounter;
 
     // Need to call SDL_Init() at least once before SDL_InitSubsystem()
-    if (sdlInitCounter == 0)
+    if (sdlInitCounter == 1)
     {
         URHO3D_LOGDEBUG("Initialising SDL");
         if (SDL_Init(0) != 0)
@@ -266,6 +283,39 @@ void Context::ReleaseSDL()
 #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);
+        ik_log_register_listener(HandleIKLog);
+    }
+}
+
+void Context::ReleaseIK()
+{
+    --ikInitCounter;
+
+    if (ikInitCounter == 0)
+    {
+        URHO3D_LOGDEBUG("De-initialising Inverse Kinematics library");
+        ik_log_unregister_listener(HandleIKLog);
+        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)
 {
     // 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);
     /// Indicate that you are done with using SDL. Must be called after using RequireSDL().
     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.
     void CopyBaseAttributes(StringHash baseType, StringHash derivedType);

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

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

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

@@ -0,0 +1,49 @@
+//
+// 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
+ *  - 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.
+ *
+ * FUTURE
+ *  - Support for "stretchiness" with min/max lengths.
+ *  - Support for "stiffness" factor, describes how well a bone rotates.
+ *  - Apply bullet constraints to joints.
+ */
+
+namespace Urho3D
+{
+
+class Context;
+
+/// Registers all IK systems to the specified context.
+void RegisterIKLibrary(Context* context);
+
+} // namespace Urho3D

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

@@ -0,0 +1,120 @@
+//
+// 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/IKConstraint.h"
+#include "../Core/Context.h"
+#include "../Scene/Node.h"
+#include "../Scene/SceneEvents.h"
+#include <ik/node.h>
+
+namespace Urho3D
+{
+
+extern const char* IK_CATEGORY;
+
+// ----------------------------------------------------------------------------
+IKConstraint::IKConstraint(Context* context) :
+    Component(context),
+    stiffness_(0.0f),
+    stretchiness_(0.0f)
+{
+}
+
+// ----------------------------------------------------------------------------
+IKConstraint::~IKConstraint()
+{
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKConstraint>(IK_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Stiffness", GetStiffness, SetStiffness, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Stretchiness", GetStretchiness, SetStretchiness, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Length Constraints", GetLengthConstraints, SetLengthConstraints, Vector2, Vector2::ZERO, AM_DEFAULT);
+}
+
+// ----------------------------------------------------------------------------
+float IKConstraint::GetStiffness() const
+{
+    return stiffness_;
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::SetStiffness(float stiffness)
+{
+    stiffness_ = Clamp(stiffness, 0.0f, 1.0f);
+    if (ikNode_ != NULL)
+        /* TODO ikNode_->stiffness = stiffness_; */
+        ;
+}
+
+// ----------------------------------------------------------------------------
+float IKConstraint::GetStretchiness() const
+{
+    return stretchiness_;
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::SetStretchiness(float stretchiness)
+{
+    stretchiness_ = Clamp(stretchiness, 0.0f, 1.0f);
+    if (ikNode_)
+        /* TODO ikNode_->stretchiness = stretchiness_;*/
+        ;
+}
+
+// ----------------------------------------------------------------------------
+const Vector2& IKConstraint::GetLengthConstraints() const
+{
+    return lengthConstraints_;
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::SetLengthConstraints(const Vector2& lengthConstraints)
+{
+    lengthConstraints_ = lengthConstraints;
+    if (ikNode_ != NULL)
+    {
+        /* TODO
+        ikNode_->min_length = lengthConstraints_.x_;
+        ikNode_->max_length = lengthConstraints_.y_;*/
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKConstraint::SetIKNode(ik_node_t* node)
+{
+    ikNode_ = node;
+    if (node)
+    {
+        /* TODO
+        node->stiffness = stiffness_;
+        node->stretchiness = stretchiness_;
+        node->min_length = lengthConstraints_.x_;
+        node->max_length = lengthConstraints_.y_;*/
+    }
+}
+
+} // namespace Urho3D

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

@@ -0,0 +1,72 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+
+struct ik_node_t;
+
+namespace Urho3D
+{
+
+class Context;
+class Node;
+
+class URHO3D_API 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);
+
+    float GetStiffness() const;
+    void SetStiffness(float stiffness);
+
+    float GetStretchiness() const;
+    void SetStretchiness(float stretchiness);
+
+    const Vector2& GetLengthConstraints() const;
+    void SetLengthConstraints(const Vector2& lengthConstraints);
+
+private:
+    friend class IKSolver;
+
+    /// Intended to be used only by IKSolver
+    void SetIKNode(ik_node_t* effector);
+
+    ik_node_t* ikNode_;
+
+    float stiffness_;
+    float stretchiness_;
+    Vector2 lengthConstraints_;
+};
+
+} // namespace Urho3D

+ 60 - 0
Source/Urho3D/IK/IKConverters.cpp

@@ -0,0 +1,60 @@
+//
+// 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/IKConverters.h"
+
+namespace Urho3D {
+
+// ----------------------------------------------------------------------------
+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;
+}
+
+// ----------------------------------------------------------------------------
+Vector3 Vec3IK2Urho(const vec3_t* ik)
+{
+    return Vector3(ik->v.x, ik->v.y, ik->v.z);
+}
+
+// ----------------------------------------------------------------------------
+quat_t QuatUrho2IK(const Quaternion& urho)
+{
+    quat_t ret;
+    ret.q.x = urho.x_;
+    ret.q.y = urho.y_;
+    ret.q.z = urho.z_;
+    ret.q.w = urho.w_;
+    return ret;
+}
+
+// ----------------------------------------------------------------------------
+Quaternion QuatIK2Urho(const quat_t* ik)
+{
+    return Quaternion(ik->q.w, ik->q.x, ik->q.y, ik->q.z);
+}
+
+}

+ 41 - 0
Source/Urho3D/IK/IKConverters.h

@@ -0,0 +1,41 @@
+//
+// 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 "../Math/Vector3.h"
+#include "../Math/Quaternion.h"
+#include <ik/vec3.h>
+#include <ik/quat.h>
+
+namespace Urho3D {
+
+/// Converts from an Urho3D Vector3 to an IK vec3_t
+vec3_t Vec3Urho2IK(const Vector3& urho);
+/// Converts from an IK vec3_t to an Urho3D Vector3
+Vector3 Vec3IK2Urho(const vec3_t* ik);
+/// Converts from an Urho3D quaternion to an IK quat_t
+quat_t QuatUrho2IK(const Quaternion& urho);
+/// Converts from an IK quat_t to an Urho3D quaternion
+Quaternion QuatIK2Urho(const quat_t* ik);
+
+}

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

@@ -0,0 +1,342 @@
+//
+// 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/IKEffector.h"
+#include "../IK/IKSolver.h"
+#include "../IK/IKConverters.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),
+    weight_(1.0f),
+    rotationWeight_(1.0),
+    rotationDecay_(0.25),
+    weightedNlerp_(false),
+    inheritParentRotation_(false)
+{
+}
+
+// ----------------------------------------------------------------------------
+IKEffector::~IKEffector()
+{
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKEffector>(IK_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Target Node", GetTargetName, SetTargetName, String, String::EMPTY, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Chain Length", GetChainLength, SetChainLength, unsigned, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Target Position", GetTargetPosition, SetTargetPosition, Vector3, Vector3::ZERO, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Target Rotation", GetTargetRotationEuler, SetTargetRotationEuler, Vector3, Vector3::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Weight", GetWeight, SetWeight, float, 1.0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Rotation Weight", GetRotationWeight, SetRotationWeight, float, 1.0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Rotation Decay", GetRotationDecay, SetRotationDecay, float, 0.25, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Nlerp Weight", WeightedNlerpEnabled, EnableWeightedNlerp, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Inherit Parent Rotation", InheritParentRotationEnabled, EnableInheritParentRotation, bool, false, AM_DEFAULT);
+}
+
+// ----------------------------------------------------------------------------
+Node* IKEffector::GetTargetNode() const
+{
+    return targetNode_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetNode(Node* targetNode)
+{
+    using namespace IKEffectorTargetChanged;
+
+    VariantMap& eventData = GetEventDataMap();
+    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;
+}
+
+// ----------------------------------------------------------------------------
+const Vector3& IKEffector::GetTargetPosition() const
+{
+    return targetPosition_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetPosition(const Vector3& targetPosition)
+{
+    targetPosition_ = targetPosition;
+    if (ikEffector_ != NULL)
+        ikEffector_->target_position = Vec3Urho2IK(targetPosition);
+}
+
+// ----------------------------------------------------------------------------
+const Quaternion& IKEffector::GetTargetRotation() const
+{
+    return targetRotation_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetRotation(const Quaternion& targetRotation)
+{
+    targetRotation_ = targetRotation;
+    if (ikEffector_)
+        ikEffector_->target_rotation = QuatUrho2IK(targetRotation);
+}
+
+// ----------------------------------------------------------------------------
+Vector3 IKEffector::GetTargetRotationEuler() const
+{
+    return targetRotation_.EulerAngles();
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetTargetRotationEuler(const Vector3& targetRotation)
+{
+    SetTargetRotation(Quaternion(targetRotation.x_, targetRotation.y_, targetRotation.z_));
+}
+
+// ----------------------------------------------------------------------------
+unsigned int IKEffector::GetChainLength() const
+{
+    return chainLength_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetChainLength(unsigned chainLength)
+{
+    chainLength_ = chainLength;
+    if (ikEffector_ != NULL)
+    {
+        ikEffector_->chain_length = chainLength;
+        solver_->MarkSolverTreeDirty();
+    }
+}
+
+// ----------------------------------------------------------------------------
+float IKEffector::GetWeight() const
+{
+    return weight_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetWeight(float weight)
+{
+    weight_ = Clamp(weight, 0.0f, 1.0f);
+    if (ikEffector_ != NULL)
+        ikEffector_->weight = weight_;
+}
+
+// ----------------------------------------------------------------------------
+float IKEffector::GetRotationWeight() const
+{
+    return rotationWeight_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetRotationWeight(float weight)
+{
+    rotationWeight_ = Clamp(weight, 0.0f, 1.0f);
+    if (ikEffector_ != NULL)
+        ikEffector_->rotation_weight = rotationWeight_;
+}
+
+// ----------------------------------------------------------------------------
+float IKEffector::GetRotationDecay() const
+{
+    return rotationDecay_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetRotationDecay(float decay)
+{
+    rotationDecay_ = Clamp(decay, 0.0f, 1.0f);
+    if (ikEffector_ != NULL)
+        ikEffector_->rotation_decay = rotationDecay_;
+}
+
+// ----------------------------------------------------------------------------
+bool IKEffector::WeightedNlerpEnabled() const
+{
+    return weightedNlerp_;
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::EnableWeightedNlerp(bool enable)
+{
+    weightedNlerp_ = enable;
+    if (ikEffector_ != NULL)
+    {
+        ikEffector_->flags &= ~EFFECTOR_WEIGHT_NLERP;
+        if (enable)
+            ikEffector_->flags |= EFFECTOR_WEIGHT_NLERP;
+    }
+}
+
+// ----------------------------------------------------------------------------
+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()
+{
+    if (targetNode_ == NULL)
+    {
+        SetTargetNode(node_->GetScene()->GetChild(targetName_, true));
+        if (targetNode_ == NULL)
+            return;
+    }
+
+    SetTargetPosition(targetNode_->GetWorldPosition());
+    SetTargetRotation(targetNode_->GetWorldRotation());
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::DrawDebugGeometry(bool depthTest)
+{
+    DebugRenderer* debug = GetScene()->GetComponent<DebugRenderer>();
+    if (debug)
+        DrawDebugGeometry(debug, depthTest);
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    Node* terminationNode;
+    if (solver_ == NULL)
+        terminationNode = GetScene();
+    else
+        terminationNode = solver_->GetNode();
+
+    // 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 != terminationNode->GetParent() && 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.1f),
+        Color::YELLOW,
+        depthTest
+    );
+    while (b && b != terminationNode->GetParent() && chainLength-- != 0)
+    {
+        debug->AddLine(
+            a->GetWorldPosition(),
+            b->GetWorldPosition(),
+            Color::WHITE,
+            depthTest
+        );
+        debug->AddSphere(
+            Sphere(b->GetWorldPosition(), averageLength * 0.1f),
+            Color::YELLOW,
+            depthTest
+        );
+        a = b;
+        b = b->GetParent();
+    }
+
+    Vector3 direction = targetRotation_ * Vector3::FORWARD;
+    direction = direction * averageLength + targetPosition_;
+    debug->AddSphere(Sphere(targetPosition_, averageLength * 0.2f), Color(255, 128, 0), depthTest);
+    debug->AddLine(targetPosition_, direction, Color(255, 128, 0), depthTest);
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetIKEffector(ik_effector_t* effector)
+{
+    ikEffector_ = effector;
+    if (effector)
+    {
+        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_;
+        EnableWeightedNlerp(weightedNlerp_);
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKEffector::SetIKSolver(IKSolver* solver)
+{
+    solver_ = solver;
+}
+
+} // namespace Urho3D

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

@@ -0,0 +1,185 @@
+//
+// 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 URHO3D_API 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.
+     * @note This clears the existing target node.
+     */
+    void SetTargetName(const String& nodeName);
+
+    /// Returns the current target position in world space.
+    const Vector3& GetTargetPosition() const;
+    /// Sets the current target position. If the effector has a target node then this will have no effect.
+    void SetTargetPosition(const Vector3& targetPosition);
+
+    /// Gets the current target rotation in world space.
+    const Quaternion& GetTargetRotation() const;
+    /// Sets the current target rotation. If the effector has a target node then this will have no effect.
+    void SetTargetRotation(const Quaternion& targetRotation);
+
+    /// Required for the editor, get the target rotation in euler angles
+    Vector3 GetTargetRotationEuler() const;
+    /// Required for the editor, sets the target rotation in euler angles
+    void SetTargetRotationEuler(const Vector3& targetRotation);
+
+    /// Returns the number of segments that will be affected by this effector. 0 Means all nodes between this effector and the next IKSolver.
+    unsigned GetChainLength() const;
+    /// Sets the number of segments that will be affected. 0 Means all nodes between this effector and the next IKSolver.
+    void SetChainLength(unsigned chainLength);
+
+    /// How strongly the effector affects the solution.
+    float GetWeight() const;
+
+    /*!
+     * @brief Sets how much influence the effector has on the solution.
+     *
+     * You can use this value to smoothly transition between a solved pose and
+     * an initial pose  For instance, lifting a foot off of the ground or
+     * letting go of an object.
+     */
+    void SetWeight(float weight);
+
+    /// How strongly the target node's rotation influences the solution
+    float GetRotationWeight() const;
+    /*!
+     * @brief Sets how much influence the target rotation should have on the
+     * solution. A value of 1 means to match the target rotation exactly, if
+     * possible. A value of 0 means to not match it at all.
+     * @note The solver must have target rotation enabled for this to have
+     * any effect. See IKSolver::EnableTargetRotation().
+     */
+    void SetRotationWeight(float weight);
+
+    /// Retrieves the rotation decay factor. See SetRotationDecay() for info.
+    float GetRotationDecay() const;
+
+    /*!
+     * @brief A factor with which to control the target rotation influence of
+     * the next segments down the chain.
+     *
+     * For example, if this is set to 0.5 and the rotation weight is set to
+     * 1.0, then the first segment will match the target rotation exactly, the
+     * next segment will match it only 50%, the next segment 25%, the next
+     * 12.5%, etc. This parameter makes long chains look more natural when
+     * matching a target rotation.
+     */
+    void SetRotationDecay(float decay);
+
+    /// Whether or not to nlerp instead of lerp when transitioning with the weight parameter
+    bool WeightedNlerpEnabled() const;
+
+    /*!
+     * @brief If you set the effector weight (see SetWeight()) to a value in
+     * between 0 and 1, the default behaviour is to linearly interpolate the
+     * effector's target position. If the solved tree and the initial tree
+     * are far apart, this can look very strange, especially if you are
+     * controlling limbs on a character that are designed to rotation. Enabling
+     * this causes a rotational based interpolation (nlerp) around the chain's
+     * base node and makes transitions look much more natural.
+     */
+    void EnableWeightedNlerp(bool enable);
+
+    bool InheritParentRotationEnabled() const;
+    void EnableInheritParentRotation(bool enable);
+
+    void DrawDebugGeometry(bool depthTest);
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+private:
+    friend class IKSolver;
+
+    /// Intended to be used only by IKSolver
+    void SetIKSolver(IKSolver* solver);
+    /// Intended to be used only by IKSolver
+    void SetIKEffector(ik_effector_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_;
+
+    String targetName_;
+    Vector3 targetPosition_;
+    Quaternion targetRotation_;
+    unsigned chainLength_;
+    float weight_;
+    float rotationWeight_;
+    float rotationDecay_;
+    bool weightedNlerp_;
+    bool inheritParentRotation_;
+};
+
+} // 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)
+}
+
+}

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

@@ -0,0 +1,596 @@
+//
+// 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/IKConstraint.h"
+#include "../IK/IKEvents.h"
+#include "../IK/IKEffector.h"
+#include "../IK/IKConverters.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 "../Graphics/DebugRenderer.h"
+
+#include <ik/solver.h>
+#include <ik/node.h>
+#include <ik/effector.h>
+#include <ik/log.h>
+
+namespace Urho3D
+{
+
+extern const char* IK_CATEGORY;
+
+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;
+    }
+}
+
+// ----------------------------------------------------------------------------
+IKSolver::IKSolver(Context* context) :
+    Component(context),
+    solver_(NULL),
+    solverTreeNeedsRebuild_(false),
+    updateInitialPose_(false),
+    autoSolveEnabled_(true)
+{
+    context_->RequireIK();
+
+    SetAlgorithm(FABRIK);
+
+    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));
+}
+
+// ----------------------------------------------------------------------------
+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);
+
+    ik_solver_destroy(solver_);
+    context_->ReleaseIK();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RegisterObject(Context* context)
+{
+    context->RegisterFactory<IKSolver>(IK_CATEGORY);
+
+    static const char* algorithmNames[] = {
+        "FABRIK",
+        /* not implemented
+        "Jacobian Inverse",
+        "Jacobian Transpose",*/
+        NULL
+    };
+
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, SOLVER_FABRIK, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Iterations", GetMaximumIterations, SetMaximumIterations, unsigned, 20, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Convergence Tolerance", GetTolerance, SetTolerance, float, 0.001, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Bone Rotations", BoneRotationsEnabled, EnableBoneRotations, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Target Rotation", TargetRotationEnabled, EnableTargetRotation, 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("Auto Solve", AutoSolveEnabled, EnableAutoSolve, bool, true, AM_DEFAULT);
+}
+
+// ----------------------------------------------------------------------------
+IKSolver::Algorithm IKSolver::GetAlgorithm() const
+{
+    return algorithm_;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetAlgorithm(IKSolver::Algorithm algorithm)
+{
+    algorithm_ = algorithm;
+
+    if (solver_ != NULL)
+        ik_solver_destroy(solver_);
+
+    switch (algorithm_)
+    {
+        case FABRIK: solver_ = ik_solver_create(SOLVER_FABRIK); break;
+    }
+
+    solver_->flags = SOLVER_CALCULATE_FINAL_ROTATIONS;
+}
+
+// ----------------------------------------------------------------------------
+unsigned IKSolver::GetMaximumIterations() const
+{
+    return solver_->max_iterations;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetMaximumIterations(unsigned iterations)
+{
+    solver_->max_iterations = iterations;
+}
+
+// ----------------------------------------------------------------------------
+float IKSolver::GetTolerance() const
+{
+    return solver_->tolerance;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::SetTolerance(float tolerance)
+{
+    if (tolerance < M_EPSILON)
+        tolerance = M_EPSILON;
+    solver_->tolerance = tolerance;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::BoneRotationsEnabled() const
+{
+    return (solver_->flags & SOLVER_CALCULATE_FINAL_ROTATIONS) != 0;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableBoneRotations(bool enable)
+{
+    solver_->flags &= ~SOLVER_CALCULATE_FINAL_ROTATIONS;
+    if (enable)
+        solver_->flags |= SOLVER_CALCULATE_FINAL_ROTATIONS;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::TargetRotationEnabled() const
+{
+    return (solver_->flags & SOLVER_CALCULATE_TARGET_ROTATIONS) != 0;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableTargetRotation(bool enable)
+{
+    solver_->flags &= ~SOLVER_CALCULATE_TARGET_ROTATIONS;
+    if (enable)
+        solver_->flags |= SOLVER_CALCULATE_TARGET_ROTATIONS;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::ContinuousSolvingEnabled() const
+{
+    return (solver_->flags & SOLVER_SKIP_RESET) != 0;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableContinuousSolving(bool enable)
+{
+    solver_->flags &= ~SOLVER_SKIP_RESET;
+    if (enable)
+        solver_->flags |= SOLVER_SKIP_RESET;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::UpdatePoseEnabled() const
+{
+    return updateInitialPose_;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableUpdatePose(bool enable)
+{
+    updateInitialPose_ = enable;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::MarkSolverTreeDirty()
+{
+    solverTreeNeedsRebuild_ = true;
+}
+
+// ----------------------------------------------------------------------------
+bool IKSolver::AutoSolveEnabled() const
+{
+    return autoSolveEnabled_;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::EnableAutoSolve(bool enable)
+{
+    if (autoSolveEnabled_ == enable)
+        return;
+
+    if (enable)
+        SubscribeToEvent(GetScene(), E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
+    else
+        UnsubscribeFromEvent(GetScene(), E_SCENEDRAWABLEUPDATEFINISHED);
+
+    autoSolveEnabled_ = 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);
+
+    if (solverTreeNeedsRebuild_)
+    {
+        ik_solver_rebuild_data(solver_);
+        solverTreeNeedsRebuild_ = false;
+    }
+
+    if (updateInitialPose_)
+        UpdateInitialPose();
+
+    for (PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
+    {
+        (*it)->UpdateTargetNodePosition();
+    }
+
+    solver_->apply_result = ApplySolvedDataCallback;
+    ik_solver_solve(solver_);
+}
+
+// ----------------------------------------------------------------------------
+static void ApplyInitialDataCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    node->SetWorldRotation(QuatIK2Urho(&ikNode->rotation));
+    node->SetWorldPosition(Vec3IK2Urho(&ikNode->position));
+}
+void IKSolver::ResetToInitialPose()
+{
+    solver_->apply_result = ApplyInitialDataCallback;
+    ik_solver_iterate_tree(solver_);
+}
+
+// ----------------------------------------------------------------------------
+static void UpdateInitialPoseCallback(ik_node_t* ikNode)
+{
+    Node* node = (Node*)ikNode->user_data;
+    ikNode->rotation = QuatUrho2IK(node->GetWorldRotation());
+    ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
+}
+void IKSolver::UpdateInitialPose()
+{
+    solver_->apply_result = UpdateInitialPoseCallback;
+    ik_solver_iterate_tree(solver_);
+}
+
+// ----------------------------------------------------------------------------
+/*
+ * 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 (autoSolveEnabled_)
+        SubscribeToEvent(scene, E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::OnNodeSet(Node* node)
+{
+    ResetToInitialPose();
+    DestroyTree();
+
+    if (node != NULL)
+        RebuildTree();
+}
+
+// ----------------------------------------------------------------------------
+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->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);
+
+    return ikNode;
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::DestroyTree()
+{
+    ik_solver_destroy_tree(solver_);
+    effectorList_.Clear();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::RebuildTree()
+{
+    assert(node_ != NULL);
+
+    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);
+    }
+}
+
+// ----------------------------------------------------------------------------
+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->SetIKEffector(ikEffector);           // "weak" reference to effector
+    effector->SetIKSolver(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;
+
+    if (solver_->tree == NULL)
+        return;
+
+    // If an effector was removed, the tree will have to be rebuilt.
+    Component* component = static_cast<Component*>(eventData[P_COMPONENT].GetPtr());
+    if (component->GetType() == IKEffector::GetTypeStatic())
+    {
+        IKEffector* effector = static_cast<IKEffector*>(component);
+        Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+        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);
+
+        ResetToInitialPose();
+        MarkSolverTreeDirty();
+    }
+
+    // Remove the ikNode* reference the IKConstraint was holding
+    if (component->GetType() == IKConstraint::GetTypeStatic())
+    {
+        IKConstraint* constraint = static_cast<IKConstraint*>(component);
+        constraint->SetIKNode(NULL);  // NOTE: Should restore default settings to the node
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
+{
+    using namespace NodeAdded;
+
+    if (solver_->tree == NULL)
+        return;
+
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+
+    PODVector<Node*> nodes;
+    node->GetChildrenWithComponent<IKEffector>(nodes, true);
+    for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
+    {
+        BuildTreeToEffector(*it);
+        effectorList_.Push((*it)->GetComponent<IKEffector>());
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
+{
+    using namespace NodeRemoved;
+
+    if (solver_->tree == NULL)
+        return;
+
+    Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
+
+    // Remove cached IKEffectors from our list
+    PODVector<Node*> nodes;
+    node->GetChildrenWithComponent<IKEffector>(nodes, true);
+    for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
+    {
+        IKEffector* effector = (*it)->GetComponent<IKEffector>();
+        effector->SetIKEffector(NULL);
+        effectorList_.RemoveSwap(effector);
+    }
+
+    // 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.
+    ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
+    if (ikNode != NULL)
+    {
+        if (ikNode == solver_->tree)
+            ik_solver_destroy_tree(solver_);
+        else
+            ik_node_destroy(ikNode);
+
+        MarkSolverTreeDirty();
+    }
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData)
+{
+    Solve();
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::DrawDebugGeometry(bool depthTest)
+{
+    DebugRenderer* debug = GetScene()->GetComponent<DebugRenderer>();
+    if (debug)
+        DrawDebugGeometry(debug, depthTest);
+}
+
+// ----------------------------------------------------------------------------
+void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    // Draws all scene segments
+    for (PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
+        (*it)->DrawDebugGeometry(debug, depthTest);
+
+    ORDERED_VECTOR_FOR_EACH(&solver_->effector_nodes_list, ik_node_t*, pnode)
+        ik_effector_t* effector = (*pnode)->effector;
+
+        // Calculate average length of all segments so we can determine the radius
+        // of the debug spheres to draw
+        int chainLength = effector->chain_length == 0 ? -1 : effector->chain_length;
+        ik_node_t* a = *pnode;
+        ik_node_t* b = a->parent;
+        float averageLength = 0.0f;
+        unsigned numberOfSegments = 0;
+        while (b && chainLength-- != 0)
+        {
+            vec3_t v = a->position;
+            vec3_sub_vec3(v.f, b->position.f);
+            averageLength += vec3_length(v.f);
+            ++numberOfSegments;
+            a = b;
+            b = b->parent;
+        }
+        averageLength /= numberOfSegments;
+
+        // connect all chained nodes together with lines
+        chainLength = effector->chain_length == 0 ? -1 : effector->chain_length;
+        a = *pnode;
+        b = a->parent;
+        debug->AddSphere(
+            Sphere(Vec3IK2Urho(&a->position), averageLength * 0.1f),
+            Color(0, 0, 255),
+            depthTest
+        );
+        debug->AddSphere(
+            Sphere(Vec3IK2Urho(&a->solved_position), averageLength * 0.1f),
+            Color(255, 128, 0),
+            depthTest
+        );
+        while (b && chainLength-- != 0)
+        {
+            debug->AddLine(
+                Vec3IK2Urho(&a->position),
+                Vec3IK2Urho(&b->position),
+                Color(0, 255, 255),
+                depthTest
+            );
+            debug->AddSphere(
+                Sphere(Vec3IK2Urho(&b->position), averageLength * 0.1f),
+                Color(0, 0, 255),
+                depthTest
+            );
+            debug->AddLine(
+                Vec3IK2Urho(&a->solved_position),
+                Vec3IK2Urho(&b->solved_position),
+                Color(255, 0, 0),
+                depthTest
+            );
+            debug->AddSphere(
+                Sphere(Vec3IK2Urho(&b->solved_position), averageLength * 0.1f),
+                Color(255, 128, 0),
+                depthTest
+            );
+            a = b;
+            b = b->parent;
+        }
+    ORDERED_VECTOR_END_EACH
+}
+
+} // namespace Urho3D

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

@@ -0,0 +1,254 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+
+struct ik_solver_t;
+struct ik_node_t;
+
+namespace Urho3D
+{
+class AnimationState;
+class IKConstraint;
+class IKEffector;
+
+/*!
+ * @brief Marks the root or "beginning" of an IK chain or multiple IK chains.
+ * The solving algorithm can be set along with other solver related parameters.
+ * The IK problem is solved starting from the node this component is attached
+ * to and ending at all nodes that have an IKEffector component attached.
+ */
+class URHO3D_API IKSolver : public Component
+{
+    URHO3D_OBJECT(IKSolver, Component)
+
+public:
+
+    enum Algorithm
+    {
+        FABRIK
+        /* not implemented yet
+        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);
+
+    /// Returns the active algorithm
+    Algorithm GetAlgorithm() const;
+
+    /*!
+     * @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);
+
+    /// Returns the configured maximum number of iterations.
+    unsigned GetMaximumIterations() const;
+
+    /*!
+     * @brief Sets the maximum number of iterations the solver is allowed to
+     * perform before applying the result.
+     *
+     * Depending on the algorithm, you may want higher or lower values.
+     * FABRIK looks decent after only 10 iterations, whereas Jacobian based
+     * methods often require more than a 100.
+     *
+     * The default value is 20.
+     *
+     * @note Most algorithms have a convergence criteria at which the solver
+     * will stop iterating, so most of the time the maximum number of
+     * iterations isn't even reached.
+     *
+     * @param[in] iterations Number of iterations. Must be greater than 0. Higher
+     * values yield more accurate results, but at the cost of performance.
+     */
+    void SetMaximumIterations(unsigned iterations);
+
+    /// Returns the configured tolerance.
+    float GetTolerance() const;
+
+    /*!
+     * @brief Sets the distance at which the effector is "close enough" to the
+     * target node, at which point the algorithm will stop iterating.
+     *
+     * @param tolerance The distance to set. Smaller values yield more accurate
+     * results, but at the cost of more iterations. Generally you'll want to
+     * specify a number that is about 1/100th to 1/1000th of the total size of
+     * the IK chain, e.g. if your human character has a leg that is 1 Urho3D
+     * unit long, a good starting tolerance would be 0.01.
+     */
+    void SetTolerance(float tolerance);
+
+    /// Whether or not rotations should be calculated.
+    bool BoneRotationsEnabled() const;
+
+    /*!
+     * @brief When enabled, final joint rotations are calculated as a post
+     * processing step. If you are using IK on a model with skinning, you will
+     * want to enable this or it will look wrong. If you disable this, then
+     * you will get a slight performance boost (less calculations are required)
+     * but only the node positions are updated. This can be useful for scene
+     * IK (perhaps a chain of platforms, where each platform should retain its
+     * initial world rotation?)
+     */
+    void EnableBoneRotations(bool enable);
+
+    /// Whether or not target rotation is enabled
+    bool TargetRotationEnabled() const;
+
+    /*!
+     * @brief When enabled, the effector will try to match the target's
+     * rotation as well as the effectors position. When disabled, the target
+     * node will reach the effector with any rotation necessary.
+     *
+     * If the target position goes out of range of the effector then the
+     * rotation will no longer be matched. The chain will try to reach out to
+     * reach the target position, even if it means rotating towards it.
+     */
+    void EnableTargetRotation(bool enable);
+
+    /// Whether or not continuous solving is enabled or not.
+    bool ContinuousSolvingEnabled() const;
+
+    /*!
+     * @brief When enabled, the solver will refrain from applying the initial
+     * pose before solving. The result is that it will use the previously
+     * solved tree as a basis for the new calculation instead of using the
+     * initial tree. This can be useful if you want to simulate chains or
+     * something similar. When disabled, the solver will use the initial
+     * positions/rotations which where set when the solver was first created.
+     *
+     * If you call UpdateInitialPose() then the initial tree will be matched to
+     * the current nodes in the scene graph.
+     *
+     * If you call ResetToInitialPose() then you will do the opposite of
+     * UpdateInitialPose() -- the initial pose is applied back to the scene
+     * graph.
+     *
+     * If you enable pose updating with EnableUpdatePose(), then the initial
+     * tree will automatically be matched to the current nodes in the scene
+     * graph.
+     */
+    void EnableContinuousSolving(bool enable);
+
+    /// Whether or not the initial pose is updated for every solution
+    bool UpdatePoseEnabled() const;
+
+    /*!
+     * @brief When enabled, the current Urho3D node positions and rotations in
+     * the scene graph will be copied into the solver's initial tree right
+     * before solving. This should generally be enabled for animated models
+     * so the solver refers to the current frame of animation rather than to
+     * the animation's initial pose.
+     *
+     * When disabled, the initial pose will remain unmodified. The initial pose
+     * 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);
+
+    /// Whether or not the solver should be invoked automatically
+    bool AutoSolveEnabled() const;
+
+    /*!
+     * @brief Mostly exists because of the editor. When enabled, the solver
+     * will be invoked automatically for you. If you need to do additional
+     * calculations before being able to set the effector target data, you will
+     * want to disable this and call Solve() manually.
+     */
+    void EnableAutoSolve(bool enable);
+
+    /*!
+     * @brief Invokes the solver. The solution is applied back to the scene
+     * graph automatically.
+     * @note You will want to register to E_SCENEDRAWABLEUPDATEFINISHED and
+     * call this method there. This is right after the animations have been
+     * applied.
+     */
+    void Solve();
+
+    /*!
+     * @brief Causes the initial tree to be applied back to Urho3D's scene
+     * graph. This is what gets called when continuous solving is disabled.
+     */
+    void ResetToInitialPose();
+
+    /*!
+     * @brief Causes the current scene graph data to be copied into the solvers
+     * initial pose. This should generally be called before solving if you
+     * are using IK on an animated model. If you don't update the initial pose,
+     * then the result will be a "continuous solution", where the solver will
+     * use the previously calculated tree as a basis for the new solution.
+     */
+    void UpdateInitialPose();
+
+    /// Causes the solver tree to be rebuilt before solving the next time.
+    void MarkSolverTreeDirty();
+
+    void DrawDebugGeometry(bool depthTest);
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+private:
+    /// 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);
+
+    /// Destroys the solver's tree
+    void DestroyTree();
+    /// Builds the solver's tree to match the scene graph's tree. If a tree already exists, it is first destroyed
+    void RebuildTree();
+    /// Builds a chain of nodes up to the specified node and adds an effector. Thus, the specified node must have an IKEffector attached.
+    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_;
+    Algorithm algorithm_;
+    bool solverTreeNeedsRebuild_;
+    bool updateInitialPose_;
+    bool autoSolveEnabled_;
+};
+
+} // namespace Urho3D

+ 6 - 0
Source/Urho3D/LuaScript/LuaScript.cpp

@@ -63,6 +63,9 @@ extern int tolua_NetworkLuaAPI_open(lua_State*);
 #ifdef URHO3D_DATABASE
 extern int tolua_DatabaseLuaAPI_open(lua_State*);
 #endif
+#ifdef URHO3D_IK
+extern int tolua_IKLuaAPI_open(lua_State*);
+#endif
 #ifdef URHO3D_PHYSICS
 extern int tolua_PhysicsLuaAPI_open(lua_State*);
 #endif
@@ -115,6 +118,9 @@ LuaScript::LuaScript(Context* context) :
 #ifdef URHO3D_DATABASE
     tolua_DatabaseLuaAPI_open(luaState_);
 #endif
+#ifdef URHO3D_IK
+    tolua_IKLuaAPI_open(luaState_);
+#endif
 #ifdef URHO3D_PHYSICS
     tolua_PhysicsLuaAPI_open(luaState_);
 #endif

+ 13 - 0
Source/Urho3D/LuaScript/pkgs/IK/IKConstraint.pkg

@@ -0,0 +1,13 @@
+$#include "IK/IKConstraint.h"
+
+class IKConstraint : public Component
+{
+    float GetStiffness() const;
+    void SetStiffness(float stiffness);
+
+    float GetStretchiness() const;
+    void SetStretchiness(float stretchiness);
+
+    const Vector2& GetLengthConstraints() const;
+    void SetLengthConstraints(const Vector2& lengthConstraints);
+};

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

@@ -0,0 +1,43 @@
+$#include "IK/IKEffector.h"
+
+class IKEffector : public Component
+{
+    Node* GetTargetNode() const;
+    void SetTargetNode(Node* targetNode);
+
+    const String& GetTargetName() const;
+    void SetTargetName(const String& nodeName);
+
+    const Vector3& GetTargetPosition() const;
+    void SetTargetPosition(const Vector3& targetPosition);
+
+    const Quaternion& GetTargetRotation() const;
+    void SetTargetRotation(const Quaternion& targetRotation);
+
+    unsigned GetChainLength() const;
+    void SetChainLength(unsigned chainLength);
+
+    float GetWeight() const;
+    void SetWeight(float weight);
+
+    float GetRotationWeight() const;
+    void SetRotationWeight(float weight);
+
+    float GetRotationDecay() const;
+    void SetRotationDecay(float decay);
+
+    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;
+    tolua_property__get_set Quaternion targetRotation;
+    tolua_property__get_set unsigned chainLength;
+    tolua_property__get_set float weight;
+    tolua_property__get_set float rotationWeight;
+    tolua_property__get_set float rotationDecay;
+};

+ 40 - 0
Source/Urho3D/LuaScript/pkgs/IK/IKSolver.pkg

@@ -0,0 +1,40 @@
+$#include "IK/IKSolver.h"
+
+class IKSolver : public Component
+{
+    enum Algorithm
+    {
+        FABRIK
+    };
+
+    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 TargetRotationEnabled() const;
+    void EnableTargetRotation(bool enable);
+
+    bool ContinuousSolvingEnabled() const;
+    void EnableContinuousSolving(bool enable);
+
+    bool UpdatePoseEnabled() const;
+    void EnableUpdatePose(bool enable);
+
+    bool AutoSolveEnabled() const;
+    void EnableAutoSolve(bool enable);
+
+    void Solve();
+    void ResetToInitialPose();
+    void UpdateInitialPose();
+
+    tolua_property__get_set Algorithm algorithm;
+    tolua_property__get_set unsigned maximumIterations;
+    tolua_property__get_set float tolerance;
+};
+

+ 6 - 0
Source/Urho3D/LuaScript/pkgs/IKLuaAPI.pkg

@@ -0,0 +1,6 @@
+$pfile "IK/IKSolver.pkg"
+$pfile "IK/IKConstraint.pkg"
+$pfile "IK/IKEffector.pkg"
+
+$using namespace Urho3D;
+$#pragma warning(disable:4800)

+ 237 - 0
bin/Data/LuaScripts/45_InverseKinematics.lua

@@ -0,0 +1,237 @@
+-- Ribbon trail demo.
+-- This sample demonstrates how to use both trail types of RibbonTrail component.
+
+require "LuaScripts/Utilities/Sample"
+
+local jackAnimCtrl_
+local cameraRotateNode_
+local floorNode_
+local leftFoot_
+local rightFoot_
+local leftEffector_
+local rightEffector_
+local solver_
+local floorPitch_ = 0.0
+local floorRoll_ = 0.0
+local drawDebug_ = false
+
+function Start()
+    cache.autoReloadResources = true
+
+    -- Execute the common startup for samples
+    SampleStart()
+
+    -- Create the scene content
+    CreateScene()
+
+    -- Create the UI content
+    CreateInstructions()
+
+    -- Setup the viewport for displaying the scene
+    SetupViewport()
+
+    -- Set the mouse mode to use in the sample
+    SampleInitMouseMode(MM_RELATIVE)
+
+    -- Hook up to the frame update events
+    SubscribeToEvents()
+end
+
+function CreateScene()
+    scene_ = Scene()
+
+    -- Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
+    scene_:CreateComponent("Octree")
+    scene_:CreateComponent("DebugRenderer")
+    scene_:CreateComponent("PhysicsWorld")
+
+    -- Create scene node & StaticModel component for showing a static plane
+    floorNode_ = scene_:CreateChild("Plane")
+    floorNode_.scale = Vector3(50.0, 1.0, 50.0)
+    local planeObject = floorNode_:CreateComponent("StaticModel")
+    planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
+    planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
+
+    -- Set up collision, we need to raycast to determine foot height
+    floorNode_:CreateComponent("RigidBody")
+    local col = floorNode_:CreateComponent("CollisionShape")
+    col:SetBox(Vector3(1, 0, 1))
+
+    -- Create a directional light to the world.
+    local lightNode = scene_:CreateChild("DirectionalLight")
+    lightNode.direction = Vector3(0.6, -1.0, 0.8) -- The direction vector does not need to be normalized
+    local light = lightNode:CreateComponent("Light")
+    light.lightType = LIGHT_DIRECTIONAL
+    light.castShadows = true
+    light.shadowBias = BiasParameters(0.00005, 0.5)
+    -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
+    light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
+
+    -- Load Jack animated model
+    jackNode_ = scene_:CreateChild("Jack")
+    jackNode_.rotation = Quaternion(0.0, 270.0, 0.0)
+    jack = jackNode_:CreateComponent("AnimatedModel")
+    jack.model = cache:GetResource("Model", "Models/Jack.mdl")
+    jack.material = cache:GetResource("Material", "Materials/Jack.xml")
+    jack.castShadows = true
+
+    -- Create animation controller and play walk animation
+    jackAnimCtrl_ = jackNode_:CreateComponent("AnimationController")
+    jackAnimCtrl_:PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.0)
+
+    -- We need to attach two inverse kinematic effectors to Jack's feet to
+    -- control the grounding.
+    leftFoot_  = jackNode_:GetChild("Bip01_L_Foot", true);
+    rightFoot_ = jackNode_:GetChild("Bip01_R_Foot", true);
+    leftEffector_  = leftFoot_:CreateComponent("IKEffector")
+    rightEffector_ = rightFoot_:CreateComponent("IKEffector")
+    -- Control 2 segments up to the hips
+    leftEffector_.chainLength = 2;
+    rightEffector_.chainLength = 2;
+
+    -- For the effectors to work, an IKSolver needs to be attached to one of
+    -- the parent nodes. Typically, you want to place the solver as close as
+    -- possible to the effectors for optimal performance. Since in this case
+    -- we're solving the legs only, we can place the solver at the spine.
+    local spine = jackNode_:GetChild("Bip01_Spine", true);
+    solver_ = spine:CreateComponent("IKSolver");
+
+    -- Disable auto-solving, which means we can call Solve() manually.
+    solver_:EnableAutoSolve(false);
+
+    -- When this is enabled, the solver will use the current positions of the
+    -- nodes in the skeleton as its basis every frame. If you disable this, then
+    -- the solver will store the initial positions of the nodes once and always
+    -- use those positions for calculating solutions.
+    -- With animated characters you generally want to continuously update the
+    -- initial positions.
+    solver_:EnableUpdatePose(true);
+
+    -- Create the camera.
+    cameraRotateNode_ = scene_:CreateChild("CameraRotate")
+    cameraNode = cameraRotateNode_:CreateChild("Camera")
+    cameraNode:CreateComponent("Camera")
+
+    -- Set an initial position for the camera scene node above the plane
+    cameraNode.position = Vector3(0.0, 0.0, -4.0)
+    cameraRotateNode_.position = Vector3(0.0, 0.4, 0.0)
+    pitch = 20.0
+    yaw = 50.0
+end
+
+function CreateInstructions()
+    -- Construct new Text object, set string to display and font to use
+    local instructionText = ui.root:CreateChild("Text")
+    instructionText:SetText("Left-Click and drag to look around\nRight-Click and drag to change incline\nPress space to reset floor\nPress D to draw debug geometry")
+    instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
+
+    -- Position the text relative to the screen center
+    instructionText.horizontalAlignment = HA_CENTER
+    instructionText.verticalAlignment = VA_CENTER
+    instructionText:SetPosition(0, ui.root.height / 4)
+end
+
+function SetupViewport()
+    -- Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
+    -- at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
+    -- use, but now we just use full screen and default render path configured in the engine command line options
+    local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
+    renderer:SetViewport(0, viewport)
+end
+
+function UpdateCameraAndFloor(timeStep)
+    -- Do not move if the UI has a focused element (the console)
+    if ui.focusElement ~= nil then
+        return
+    end
+
+    -- Movement speed as world units per second
+    local MOVE_SPEED = 20.0
+    -- Mouse sensitivity as degrees per pixel
+    local MOUSE_SENSITIVITY = 0.1
+
+    -- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
+    if input:GetMouseButtonDown(MOUSEB_LEFT) then
+        local mouseMove = input.mouseMove
+        yaw = yaw +MOUSE_SENSITIVITY * mouseMove.x
+        pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
+        pitch = Clamp(pitch, -90.0, 90.0)
+    end
+
+    if input:GetMouseButtonDown(MOUSEB_RIGHT) then
+        local mouseMoveInt = input.mouseMove
+        local mouseMove = Vector2()
+        mouseMove.x = -Cos(yaw) * mouseMoveInt.y - Sin(yaw) * mouseMoveInt.x
+        mouseMove.y = Sin(yaw) * mouseMoveInt.y - Cos(yaw) * mouseMoveInt.x
+
+        floorPitch_ = floorPitch_ + MOUSE_SENSITIVITY * mouseMove.x
+        floorPitch_ = Clamp(floorPitch_, -90.0, 90.0)
+        floorRoll_ = floorRoll_ + MOUSE_SENSITIVITY * mouseMove.y
+    end
+
+    if input:GetKeyPress(KEY_SPACE) then
+        floorPitch_ = 0.0
+        floorRoll_ = 0.0
+    end
+
+    if input:GetKeyPress(KEY_D) then
+        drawDebug_ = not drawDebug_
+    end
+
+    -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+    cameraRotateNode_.rotation = Quaternion(pitch, yaw, 0.0)
+    floorNode_.rotation = Quaternion(floorPitch_, 0.0, floorRoll_)
+end
+
+function SubscribeToEvents()
+    -- Subscribe HandleUpdate() function for processing update events
+    SubscribeToEvent("Update", "HandleUpdate")
+    SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate")
+    SubscribeToEvent("SceneDrawableUpdateFinished", "HandleSceneDrawableUpdateFinished")
+end
+
+function HandleUpdate(eventType, eventData)
+    -- Take the frame time step, which is stored as a float
+    local timeStep = eventData["TimeStep"]:GetFloat()
+
+    -- Move the camera, scale movement with time step
+    UpdateCameraAndFloor(timeStep)
+end
+
+function HandlePostRenderUpdate(eventType, eventData)
+    if drawDebug_ then
+        solver_:DrawDebugGeometry(false)
+    end
+end
+
+function HandleSceneDrawableUpdateFinished(eventType, eventData)
+    local physicsWorld = scene_:GetComponent("PhysicsWorld")
+    local leftFootPosition = leftFoot_.worldPosition
+    local rightFootPosition = rightFoot_.worldPosition
+
+    -- Cast ray down to get the normal of the underlying surface
+    local result = physicsWorld:RaycastSingle(Ray(leftFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2)
+    if result.body then
+        -- Cast again, but this time along the normal. Set the target position
+        -- to the ray intersection
+        local oppositeNormal = result.normal * -1
+        result = physicsWorld:RaycastSingle(Ray(leftFootPosition + result.normal, oppositeNormal), 2)
+        -- The foot node has an offset relative to the root node
+        footOffset = leftFoot_.worldPosition.y - jackNode_.worldPosition.y
+        leftEffector_.targetPosition = result.position + result.normal * footOffset
+        -- Rotate foot according to normal
+        leftFoot_:Rotate(Quaternion(Vector3(0, 1, 0), result.normal), TS_WORLD)
+    end
+
+    -- Same deal with the right foot
+    result = physicsWorld:RaycastSingle(Ray(rightFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2)
+    if result.body then
+        local oppositeNormal = result.normal * -1
+        result = physicsWorld:RaycastSingle(Ray(rightFootPosition + result.normal, oppositeNormal), 2)
+        footOffset = rightFoot_.worldPosition.y - jackNode_.worldPosition.y
+        rightEffector_.targetPosition = result.position + result.normal * footOffset
+        rightFoot_:Rotate(Quaternion(Vector3(0, 1, 0), result.normal), TS_WORLD)
+    end
+
+    solver_:Solve()
+end

+ 251 - 0
bin/Data/Scripts/45_InverseKinematics.as

@@ -0,0 +1,251 @@
+// Inverse Kinematics
+// This sample demonstrates how to use the IK solver to create "grounders" for a walking character on a slope.
+
+#include "Scripts/Utilities/Sample.as"
+
+AnimationController@ jackAnimCtrl_;
+Node@ cameraRotateNode_;
+Node@ floorNode_;
+Node@ leftFoot_;
+Node@ rightFoot_;
+Node@ jackNode_;
+IKEffector@ leftEffector_;
+IKEffector@ rightEffector_;
+IKSolver@ solver_;
+float floorPitch_ = 0.0f;
+float floorRoll_ = 0.0f;
+bool drawDebug_ = false;
+
+void Start()
+{
+    cache.autoReloadResources = true;
+
+    // Execute the common startup for samples
+    SampleStart();
+
+    // Create the scene content
+    CreateScene();
+
+    // Create the UI content
+    CreateInstructions();
+
+    // Setup the viewport for displaying the scene
+    SetupViewport();
+
+    // Set the mouse mode to use in the sample
+    SampleInitMouseMode(MM_RELATIVE);
+
+    // Hook up to the frame update events
+    SubscribeToEvents();
+}
+
+void CreateScene()
+{
+    scene_ = Scene();
+
+    // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
+    scene_.CreateComponent("Octree");
+    scene_.CreateComponent("DebugRenderer");
+    scene_.CreateComponent("PhysicsWorld");
+
+    // Create scene node & StaticModel component for showing a static plane
+    floorNode_ = scene_.CreateChild("Plane");
+    floorNode_.scale = Vector3(50.0f, 1.0f, 50.0f);
+    StaticModel@ planeObject = floorNode_.CreateComponent("StaticModel");
+    planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
+    planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
+
+    // Set up collision, we need to raycast to determine foot height
+    floorNode_.CreateComponent("RigidBody");
+    CollisionShape@ col = floorNode_.CreateComponent("CollisionShape");
+    col.SetBox(Vector3(1, 0, 1));
+
+    // Create a directional light to the world.
+    Node@ lightNode = scene_.CreateChild("DirectionalLight");
+    lightNode.direction = Vector3(0.6f, -1.0f, 0.8f); // The direction vector does not need to be normalized
+    Light@ light = lightNode.CreateComponent("Light");
+    light.lightType = LIGHT_DIRECTIONAL;
+    light.castShadows = true;
+    light.shadowBias = BiasParameters(0.00005f, 0.5f);
+    // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
+    light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
+
+    // Load Jack animated model
+    jackNode_ = scene_.CreateChild("Jack");
+    jackNode_.rotation = Quaternion(0.0f, 270.0f, 0.0f);
+    AnimatedModel@ jack = jackNode_.CreateComponent("AnimatedModel");
+    jack.model = cache.GetResource("Model", "Models/Jack.mdl");
+    jack.material = cache.GetResource("Material", "Materials/Jack.xml");
+    jack.castShadows = true;
+
+    // Create animation controller and play walk animation
+    jackAnimCtrl_ = jackNode_.CreateComponent("AnimationController");
+    jackAnimCtrl_.PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.0f);
+
+    // We need to attach two inverse kinematic effectors to Jack's feet to
+    // control the grounding.
+    leftFoot_  = jackNode_.GetChild("Bip01_L_Foot", true);
+    rightFoot_ = jackNode_.GetChild("Bip01_R_Foot", true);
+    leftEffector_  = leftFoot_.CreateComponent("IKEffector");
+    rightEffector_ = rightFoot_.CreateComponent("IKEffector");
+    // Control 2 segments up to the hips
+    leftEffector_.chainLength = 2;
+    rightEffector_.chainLength = 2;
+
+    // For the effectors to work, an IKSolver needs to be attached to one of
+    // the parent nodes. Typically, you want to place the solver as close as
+    // possible to the effectors for optimal performance. Since in this case
+    // we're solving the legs only, we can place the solver at the spine.
+    Node@ spine = jackNode_.GetChild("Bip01_Spine", true);
+    solver_ = spine.CreateComponent("IKSolver");
+
+    // Disable auto-solving, which means we can call Solve() manually.
+    solver_.autoSolve = false;
+
+    // When this is enabled, the solver will use the current positions of the
+    // nodes in the skeleton as its basis every frame. If you disable this, then
+    // the solver will store the initial positions of the nodes once and always
+    // use those positions for calculating solutions.
+    // With animated characters you generally want to continuously update the
+    // initial positions.
+    solver_.updatePose = true;
+
+    // Create the camera.
+    cameraRotateNode_ = scene_.CreateChild("CameraRotate");
+    cameraNode = cameraRotateNode_.CreateChild("Camera");
+    cameraNode.CreateComponent("Camera");
+
+    // Set an initial position for the camera scene node above the plane
+    cameraNode.position = Vector3(0.0f, 0.0f, -4.0f);
+    cameraRotateNode_.position = Vector3(0.0f, 0.4f, 0.0f);
+    pitch = 20.0f;
+    yaw = 50.0f;
+}
+
+void CreateInstructions()
+{
+    // Construct new Text object, set string to display and font to use
+    Text@ instructionText = ui.root.CreateChild("Text");
+    instructionText.text = "Left-Click and drag to look around\nRight-Click and drag to change incline\nPress space to reset floor\nPress D to draw debug geometry";
+    instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
+
+    // Position the text relative to the screen center
+    instructionText.horizontalAlignment = HA_CENTER;
+    instructionText.verticalAlignment = VA_CENTER;
+    instructionText.SetPosition(0, ui.root.height / 4);
+}
+
+void SetupViewport()
+{
+    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
+    // at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
+    // use, but now we just use full screen and default render path configured in the engine command line options
+    Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
+    renderer.viewports[0] = viewport;
+}
+
+void UpdateCameraAndFloor(float timeStep)
+{
+    // Do not move if the UI has a focused element (the console)
+    if (ui.focusElement !is null)
+        return;
+
+    // Movement speed as world units per second
+    const float MOVE_SPEED = 20.0f;
+    // Mouse sensitivity as degrees per pixel
+    const float MOUSE_SENSITIVITY = 0.1f;
+
+    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
+    if (input.mouseButtonDown[MOUSEB_LEFT])
+    {
+        IntVector2 mouseMove = input.mouseMove;
+        yaw += MOUSE_SENSITIVITY * mouseMove.x;
+        pitch += MOUSE_SENSITIVITY * mouseMove.y;
+        pitch = Clamp(pitch, -90.0f, 90.0f);
+    }
+
+    if (input.mouseButtonDown[MOUSEB_RIGHT])
+    {
+        IntVector2 mouseMoveInt = input.mouseMove;
+        Vector2 mouseMove = Matrix2(
+            -Cos(yaw), Sin(yaw),
+            Sin(yaw),  Cos(yaw)
+        ) * Vector2(mouseMoveInt.y, -mouseMoveInt.x);
+        floorPitch_ += MOUSE_SENSITIVITY * mouseMove.x;
+        floorPitch_ = Clamp(floorPitch_, -90.0f, 90.0f);
+        floorRoll_ += MOUSE_SENSITIVITY * mouseMove.y;
+    }
+
+    if (input.keyPress[KEY_SPACE])
+    {
+        floorPitch_ = 0.0f;
+        floorRoll_ = 0.0f;
+    }
+
+    if (input.keyPress[KEY_D])
+    {
+        drawDebug_ = !drawDebug_;
+    }
+
+    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+    cameraRotateNode_.rotation = Quaternion(pitch, yaw, 0.0f);
+    floorNode_.rotation = Quaternion(floorPitch_, 0.0f, floorRoll_);
+}
+
+void SubscribeToEvents()
+{
+    // Subscribe HandleUpdate() function for processing update events
+    SubscribeToEvent("Update", "HandleUpdate");
+    SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
+    SubscribeToEvent("SceneDrawableUpdateFinished", "HandleSceneDrawableUpdateFinished");
+}
+
+void HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Take the frame time step, which is stored as a float
+    float timeStep = eventData["TimeStep"].GetFloat();
+
+    // Move the camera, scale movement with time step
+    UpdateCameraAndFloor(timeStep);
+}
+
+void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (drawDebug_)
+        solver_.DrawDebugGeometry(false);
+}
+
+void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData)
+{
+    Vector3 leftFootPosition = leftFoot_.worldPosition;
+    Vector3 rightFootPosition = rightFoot_.worldPosition;
+
+    // Cast ray down to get the normal of the underlying surface
+    PhysicsRaycastResult result = scene_.physicsWorld.RaycastSingle(Ray(leftFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2);
+    if (result.body !is null)
+    {
+        // Cast again, but this time along the normal. Set the target position
+        // to the ray intersection
+        result = scene_.physicsWorld.RaycastSingle(Ray(leftFootPosition + result.normal, -result.normal), 2);
+        // The foot node has an offset relative to the root node
+        float footOffset = leftFoot_.worldPosition.y - jackNode_.worldPosition.y;
+        leftEffector_.targetPosition = result.position + result.normal * footOffset;
+        // Rotate foot according to normal
+        leftFoot_.Rotate(Quaternion(Vector3(0, 1, 0), result.normal), TS_WORLD);
+    }
+
+    // Same deal with the right foot
+    result = scene_.physicsWorld.RaycastSingle(Ray(rightFootPosition + Vector3(0, 1, 0), Vector3(0, -1, 0)), 2);
+    if (result.body !is null)
+    {
+        result = scene_.physicsWorld.RaycastSingle(Ray(rightFootPosition + result.normal, -result.normal), 2);
+        float footOffset = rightFoot_.worldPosition.y - jackNode_.worldPosition.y;
+        rightEffector_.targetPosition = result.position + result.normal * footOffset;
+        rightFoot_.Rotate(Quaternion(Vector3(0, 1, 0), result.normal), TS_WORLD);
+    }
+
+    solver_.Solve();
+}
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

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="Image Rect" value="48 16 62 30" />
     </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">
         <attribute name="Texture" value="Texture2D;Textures/Editor/EditorIcons.png" />
         <attribute name="Image Rect" value="48 0 62 14" />