Adding IK library to Urho3D, enable/disable it with -DURHO3D_IK
This commit is contained in:
parent
eeb4eab788
commit
cf30fb98c0
@ -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
|
||||
@ -345,6 +346,7 @@ if (URHO3D_CLANG_TOOLS)
|
||||
URHO3D_ANGELSCRIPT
|
||||
URHO3D_DATABASE_SQLITE
|
||||
URHO3D_FILEWATCHER
|
||||
URHO3D_IK
|
||||
URHO3D_LOGGING
|
||||
URHO3D_LUA
|
||||
URHO3D_NAVIGATION
|
||||
@ -395,6 +397,7 @@ foreach (OPT
|
||||
URHO3D_ANGELSCRIPT
|
||||
URHO3D_DATABASE
|
||||
URHO3D_FILEWATCHER
|
||||
URHO3D_IK
|
||||
URHO3D_LOGGING
|
||||
URHO3D_LUA
|
||||
URHO3D_MINIDUMPS
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
@ -446,7 +446,7 @@ install_header_files (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION ${DEST_
|
||||
# Generate the include-all-headers header file even though we do not encourage Urho3D library users to use it
|
||||
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)
|
||||
|
@ -32,11 +32,20 @@
|
||||
#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;
|
||||
// Keeps track of how many times IK was initialised
|
||||
#ifdef URHO3D_IK
|
||||
static int ikInitCounter = 0;
|
||||
#endif
|
||||
|
||||
void EventReceiverGroup::BeginSendEvent()
|
||||
{
|
||||
@ -225,7 +234,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 +275,37 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::ReleaseIK()
|
||||
{
|
||||
--ikInitCounter;
|
||||
|
||||
if (ikInitCounter == 0)
|
||||
{
|
||||
URHO3D_LOGDEBUG("De-initialising Inverse Kinematics library");
|
||||
ik_log_deinit();
|
||||
ik_memory_deinit();
|
||||
}
|
||||
|
||||
if (ikInitCounter < 0)
|
||||
URHO3D_LOGERROR("Too many calls to Context::ReleaseIK()");
|
||||
}
|
||||
#endif
|
||||
|
||||
void Context::CopyBaseAttributes(StringHash baseType, StringHash derivedType)
|
||||
{
|
||||
// Prevent endless loop if mistakenly copying attributes from same class as derived
|
||||
|
@ -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);
|
||||
|
@ -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
Source/Urho3D/IK/IK.cpp
Normal file
42
Source/Urho3D/IK/IK.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#include "IK.h"
|
||||
#include "IKConstraint.h"
|
||||
#include "IKEffector.h"
|
||||
#include "IKSolver.h"
|
||||
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
const char* IK_CATEGORY = "Inverse Kinematics";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void RegisterIKLibrary(Context* context)
|
||||
{
|
||||
IKConstraint::RegisterObject(context);
|
||||
IKEffector::RegisterObject(context);
|
||||
IKSolver::RegisterObject(context);
|
||||
}
|
||||
|
||||
} // namespace Urho3D
|
55
Source/Urho3D/IK/IK.h
Normal file
55
Source/Urho3D/IK/IK.h
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
/*
|
||||
* TODO IK todo
|
||||
* - Initial pose is not saved when saving scene in editor. Instead, the
|
||||
* solved state of the chain(s) are saved.
|
||||
* - Target angle in addition to target position -> use weighted angles
|
||||
* approach
|
||||
* - Add an IKEffector weight parameter, so the user can specify
|
||||
* (between [0..1]) how much influence the solved chains have.
|
||||
* - Apply angles from chain objects back to scene nodes (currently only
|
||||
* translations are applied).
|
||||
* - Add support for enabling/disabling initial pose to support animated
|
||||
* models as well as scene nodes.
|
||||
* - Pole targets?
|
||||
* - Add support for manually updating initial pose.
|
||||
* - Add support for having the initial pose update every time it's solved
|
||||
* -> incremental solving so to speak.
|
||||
* - Apply bullet constraints to joints.
|
||||
* - Script bindings.
|
||||
* - Optimise.
|
||||
* - Profile.
|
||||
*/
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
class Context;
|
||||
|
||||
/*!
|
||||
* Registers all IK systems to the specified context.
|
||||
*/
|
||||
void RegisterIKLibrary(Context* context);
|
||||
|
||||
} // namespace Urho3D
|
51
Source/Urho3D/IK/IKConstraint.cpp
Normal file
51
Source/Urho3D/IK/IKConstraint.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#include "IK.h"
|
||||
#include "IKConstraint.h"
|
||||
#include "../Core/Context.h"
|
||||
#include "../Scene/Node.h"
|
||||
#include "../Scene/SceneEvents.h"
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
extern const char* IK_CATEGORY;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKConstraint::IKConstraint(Context* context) :
|
||||
Component(context)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKConstraint::~IKConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKConstraint::RegisterObject(Context* context)
|
||||
{
|
||||
context->RegisterFactory<IKConstraint>(IK_CATEGORY);
|
||||
}
|
||||
|
||||
} // namespace Urho3D
|
57
Source/Urho3D/IK/IKConstraint.h
Normal file
57
Source/Urho3D/IK/IKConstraint.h
Normal file
@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Scene/Component.h"
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
class Context;
|
||||
class Node;
|
||||
|
||||
class IKConstraint : public Component
|
||||
{
|
||||
URHO3D_OBJECT(IKConstraint, Component)
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Constructs a new IK constraint.
|
||||
*/
|
||||
IKConstraint(Context* context);
|
||||
|
||||
/*!
|
||||
* Destructs he IK constraint.
|
||||
*/
|
||||
virtual ~IKConstraint();
|
||||
|
||||
/*!
|
||||
* Registers this class as an object factory.
|
||||
*/
|
||||
static void RegisterObject(Context* context);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace Urho3D
|
201
Source/Urho3D/IK/IKEffector.cpp
Normal file
201
Source/Urho3D/IK/IKEffector.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#include "../IK/IK.h"
|
||||
#include "../IK/IKEffector.h"
|
||||
#include "../IK/IKSolver.h"
|
||||
#include "../IK/IKEvents.h"
|
||||
#include "../Core/Context.h"
|
||||
#include "../Graphics/DebugRenderer.h"
|
||||
#include "../Scene/Node.h"
|
||||
#include "../Scene/Scene.h"
|
||||
#include "../Scene/SceneEvents.h"
|
||||
#include <ik/effector.h>
|
||||
#include <ik/log.h>
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
extern const char* IK_CATEGORY;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKEffector::IKEffector(Context* context) :
|
||||
Component(context),
|
||||
ikEffector_(NULL),
|
||||
chainLength_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKEffector::~IKEffector()
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::RegisterObject(Context* context)
|
||||
{
|
||||
context->RegisterFactory<IKEffector>(IK_CATEGORY);
|
||||
|
||||
URHO3D_ACCESSOR_ATTRIBUTE("Chain Length", GetChainLength, SetChainLength, unsigned, true, AM_DEFAULT);
|
||||
URHO3D_ACCESSOR_ATTRIBUTE("Target Node", GetTargetName, SetTargetName, String, String::EMPTY, AM_DEFAULT);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
Node* IKEffector::GetTargetNode() const
|
||||
{
|
||||
return targetNode_;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetTargetNode(Node* targetNode)
|
||||
{
|
||||
using namespace IKEffectorTargetChanged;
|
||||
|
||||
VariantMap eventData;
|
||||
eventData[P_EFFECTORNODE] = node_; // The effector node that has changed targets
|
||||
eventData[P_TARGETNODE] = targetNode_; // The new target node. NOTE: Can be NULL (means no target)
|
||||
SendEvent(E_IKEFFECTORTARGETCHANGED, eventData);
|
||||
|
||||
// Finally change the target node
|
||||
targetNode_ = targetNode;
|
||||
targetName_ = targetNode != NULL ? targetNode->GetName() : "";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
const String& IKEffector::GetTargetName() const
|
||||
{
|
||||
return targetName_;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetTargetName(const String& nodeName)
|
||||
{
|
||||
targetName_ = nodeName;
|
||||
targetNode_ = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetTargetPosition(const Vector3& targetPosition)
|
||||
{
|
||||
targetPosition_ = targetPosition;
|
||||
if (ikEffector_ != NULL)
|
||||
{
|
||||
ikEffector_->target_position.v.x = targetPosition.x_;
|
||||
ikEffector_->target_position.v.y = targetPosition.y_;
|
||||
ikEffector_->target_position.v.z = targetPosition.z_;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::UpdateTargetNodePosition()
|
||||
{
|
||||
if (targetNode_ == NULL)
|
||||
{
|
||||
SetTargetNode(node_->GetScene()->GetChild(targetName_, true));
|
||||
if (targetNode_ == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
SetTargetPosition(targetNode_->GetWorldPosition());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
unsigned int IKEffector::GetChainLength() const
|
||||
{
|
||||
return chainLength_;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetChainLength(unsigned chainLength)
|
||||
{
|
||||
chainLength_ = chainLength;
|
||||
if (ikEffector_ != NULL)
|
||||
{
|
||||
ikEffector_->chain_length = chainLength;
|
||||
solver_->MarkSolverTreeDirty();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetEffector(ik_effector_t* effector)
|
||||
{
|
||||
ikEffector_ = effector;
|
||||
if (effector)
|
||||
{
|
||||
effector->chain_length = chainLength_;
|
||||
effector->target_position.v.x = targetPosition_.x_;
|
||||
effector->target_position.v.y = targetPosition_.y_;
|
||||
effector->target_position.v.z = targetPosition_.z_;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::SetSolver(IKSolver* solver)
|
||||
{
|
||||
solver_ = solver;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKEffector::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
|
||||
{
|
||||
// Calculate average length of all segments so we can determine the radius
|
||||
// of the debug spheres to draw
|
||||
int chainLength = chainLength_ == 0 ? -1 : chainLength_;
|
||||
Node* a = node_;
|
||||
float averageLength = 0.0f;
|
||||
unsigned numberOfSegments = 0;
|
||||
while (a && a != a->GetScene() && chainLength-- != 0)
|
||||
{
|
||||
averageLength += a->GetPosition().Length();
|
||||
++numberOfSegments;
|
||||
a = a->GetParent();
|
||||
}
|
||||
averageLength /= numberOfSegments;
|
||||
|
||||
// connect all chained nodes together with lines
|
||||
chainLength = chainLength_ == 0 ? -1 : chainLength_;
|
||||
a = node_;
|
||||
Node* b = a->GetParent();
|
||||
debug->AddSphere(
|
||||
Sphere(a->GetWorldPosition(), averageLength * 0.1),
|
||||
Color::YELLOW,
|
||||
depthTest
|
||||
);
|
||||
while (b && b != b->GetScene() && chainLength-- != 0)
|
||||
{
|
||||
debug->AddLine(
|
||||
a->GetWorldPosition(),
|
||||
b->GetWorldPosition(),
|
||||
Color::WHITE,
|
||||
depthTest
|
||||
);
|
||||
debug->AddSphere(
|
||||
Sphere(b->GetWorldPosition(), averageLength * 0.1),
|
||||
Color::YELLOW,
|
||||
depthTest
|
||||
);
|
||||
a = b;
|
||||
b = b->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Urho3D
|
104
Source/Urho3D/IK/IKEffector.h
Normal file
104
Source/Urho3D/IK/IKEffector.h
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Scene/Component.h"
|
||||
#include "../Scene/Scene.h"
|
||||
|
||||
struct ik_effector_t;
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
class Context;
|
||||
class IKSolver;
|
||||
|
||||
class IKEffector : public Component
|
||||
{
|
||||
URHO3D_OBJECT(IKEffector, Component)
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs a new IK effector.
|
||||
IKEffector(Context* context);
|
||||
|
||||
/// Destructs he IK effector.
|
||||
virtual ~IKEffector();
|
||||
|
||||
/*!
|
||||
* Registers this class as an object factory.
|
||||
*/
|
||||
static void RegisterObject(Context* context);
|
||||
|
||||
/// Retrieves the node that is being used as a target. Can be NULL.
|
||||
Node* GetTargetNode() const;
|
||||
|
||||
/*!
|
||||
* @brief The position of the target node provides the target position of
|
||||
* the effector node.
|
||||
*
|
||||
* The IK chain is solved such that the node to which this component is
|
||||
* attached to will try to move to the position of the target node.
|
||||
* @param targetNode A scene node that acts as a target. Specifying NULL
|
||||
* will erase the target and cause the solver to ignore this chain.
|
||||
* @note You will get very strange behaviour if you specify a target node
|
||||
* that is part the IK chain being solved for (circular dependency). Don't
|
||||
* do that.
|
||||
*/
|
||||
void SetTargetNode(Node* targetNode);
|
||||
|
||||
/*!
|
||||
* @brief Retrieves the name of the target node. The node doesn't
|
||||
* necessarily have to exist in the scene graph.
|
||||
*/
|
||||
const String& GetTargetName() const;
|
||||
|
||||
/*!
|
||||
* @brief Sets the name of the target node. The node doesn't necessarily
|
||||
* have to exist in the scene graph. When a node is created that matches
|
||||
* this name, it is selected as the target.
|
||||
*/
|
||||
void SetTargetName(const String& nodeName);
|
||||
|
||||
void SetTargetPosition(const Vector3& targetPosition);
|
||||
|
||||
void UpdateTargetNodePosition();
|
||||
|
||||
unsigned GetChainLength() const;
|
||||
void SetChainLength(unsigned chainLength);
|
||||
|
||||
virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
|
||||
|
||||
void SetSolver(IKSolver* solver);
|
||||
void SetEffector(ik_effector_t* effector);
|
||||
|
||||
private:
|
||||
String targetName_;
|
||||
Vector3 targetPosition_;
|
||||
WeakPtr<Node> targetNode_;
|
||||
WeakPtr<IKSolver> solver_;
|
||||
ik_effector_t* ikEffector_;
|
||||
unsigned chainLength_;
|
||||
};
|
||||
|
||||
} // namespace Urho3D
|
37
Source/Urho3D/IK/IKEvents.h
Normal file
37
Source/Urho3D/IK/IKEvents.h
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Core/Object.h"
|
||||
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
URHO3D_EVENT(E_IKEFFECTORTARGETCHANGED, IKEffectorTargetChanged)
|
||||
{
|
||||
URHO3D_PARAM(P_EFFECTORNODE, EffectorNode); // (Node*) The effector node that has changed targets
|
||||
URHO3D_PARAM(P_TARGETNODE, TargetNode); // (Node*) The new target node. NOTE: Can be NULL (means no target)
|
||||
}
|
||||
|
||||
}
|
375
Source/Urho3D/IK/IKSolver.cpp
Normal file
375
Source/Urho3D/IK/IKSolver.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#include "../IK/IKSolver.h"
|
||||
#include "../IK/IK.h"
|
||||
#include "../IK/IKEvents.h"
|
||||
|
||||
#include "../Core/Context.h"
|
||||
#include "../Core/Profiler.h"
|
||||
#include "../IO/Log.h"
|
||||
#include "../Scene/Node.h"
|
||||
#include "../Scene/Scene.h"
|
||||
#include "../Scene/SceneEvents.h"
|
||||
#include "../Graphics/Animation.h"
|
||||
#include "../Graphics/AnimationState.h"
|
||||
|
||||
#include <ik/solver.h>
|
||||
#include <ik/node.h>
|
||||
#include <ik/effector.h>
|
||||
#include <ik/log.h>
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
|
||||
extern const char* IK_CATEGORY;;
|
||||
extern const char* SUBSYSTEM_CATEGORY;
|
||||
|
||||
static void HandleIKLog(const char* msg)
|
||||
{
|
||||
URHO3D_LOGINFOF("[IK] %s", msg);
|
||||
}
|
||||
static vec3_t Vec3Urho2IK(const Vector3& urho)
|
||||
{
|
||||
vec3_t ret;
|
||||
ret.v.x = urho.x_;
|
||||
ret.v.y = urho.y_;
|
||||
ret.v.z = urho.z_;
|
||||
return ret;
|
||||
}
|
||||
static Vector3 Vec3IK2Urho(const vec3_t* ik)
|
||||
{
|
||||
return Vector3(ik->v.x, ik->v.y, ik->v.z);
|
||||
}
|
||||
static ik_node_t* CreateIKNode(const Node* node)
|
||||
{
|
||||
ik_node_t* ikNode = ik_node_create(node->GetID());
|
||||
ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
|
||||
ikNode->user_data = (void*)node;
|
||||
return ikNode;
|
||||
}
|
||||
static bool ChildrenHaveEffector(const Node* node)
|
||||
{
|
||||
if (node->HasComponent<IKEffector>())
|
||||
return true;
|
||||
|
||||
const Vector<SharedPtr<Node> >& children = node->GetChildren();
|
||||
for (Vector<SharedPtr<Node> >::ConstIterator it = children.Begin(); it != children.End(); ++it)
|
||||
{
|
||||
if (ChildrenHaveEffector(it->Get()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
static void ApplyResultCallback(ik_node_t* ikNode, vec3_t position, quat_t rotation)
|
||||
{
|
||||
Node* node = (Node*)ikNode->user_data;
|
||||
node->SetWorldPosition(Vec3IK2Urho(&ikNode->solved_position));
|
||||
node->SetWorldRotation(Quaternion(
|
||||
rotation.q.w,
|
||||
rotation.q.x,
|
||||
rotation.q.y,
|
||||
rotation.q.z
|
||||
));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKSolver::IKSolver(Context* context) :
|
||||
Component(context),
|
||||
solver_(NULL),
|
||||
solverTreeNeedsRebuild_(false)
|
||||
{
|
||||
context_->RequireIK();
|
||||
solver_ = ik_solver_create(SOLVER_FABRIK);
|
||||
solver_->apply_result = ApplyResultCallback;
|
||||
solver_->build_mode = SOLVER_EXCLUDE_ROOT;
|
||||
ik_log_register_listener(HandleIKLog);
|
||||
|
||||
SubscribeToEvent(E_COMPONENTADDED, URHO3D_HANDLER(IKSolver, HandleComponentAdded));
|
||||
SubscribeToEvent(E_COMPONENTREMOVED, URHO3D_HANDLER(IKSolver, HandleComponentRemoved));
|
||||
SubscribeToEvent(E_NODEADDED, URHO3D_HANDLER(IKSolver, HandleNodeAdded));
|
||||
SubscribeToEvent(E_NODEREMOVED, URHO3D_HANDLER(IKSolver, HandleNodeRemoved));
|
||||
SubscribeToEvent(E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKSolver::~IKSolver()
|
||||
{
|
||||
// Destroying the solver tree will destroy the effector objects, so remove
|
||||
// any references any of the IKEffector objects could be holding
|
||||
for(PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
|
||||
(*it)->SetEffector(NULL);
|
||||
|
||||
ik_log_unregister_listener(HandleIKLog);
|
||||
ik_solver_destroy(solver_);
|
||||
context_->ReleaseIK();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::RegisterObject(Context* context)
|
||||
{
|
||||
context->RegisterFactory<IKSolver>(SUBSYSTEM_CATEGORY);
|
||||
|
||||
static const char* algorithmNames[] = {
|
||||
"FABRIK",
|
||||
/* not implemented
|
||||
"Jacobian Inverse",
|
||||
"Jacobian Transpose",*/
|
||||
NULL
|
||||
};
|
||||
|
||||
URHO3D_ACCESSOR_ATTRIBUTE("Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
|
||||
URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, SOLVER_FABRIK, AM_DEFAULT);
|
||||
URHO3D_ACCESSOR_ATTRIBUTE("Max Iterations", GetMaximumIterations, SetMaximumIterations, unsigned, 1000, AM_DEFAULT);
|
||||
URHO3D_ACCESSOR_ATTRIBUTE("Convergence Tolerance", GetTolerance, SetTolerance, float, 0.1f, AM_DEFAULT);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::SetAlgorithm(IKSolver::Algorithm algorithm)
|
||||
{
|
||||
URHO3D_LOGERROR("Failed to set algorithm: Not implemented");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
IKSolver::Algorithm IKSolver::GetAlgorithm() const
|
||||
{
|
||||
return FABRIK;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::SetMaximumIterations(unsigned iterations)
|
||||
{
|
||||
solver_->max_iterations = iterations;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
unsigned IKSolver::GetMaximumIterations() const
|
||||
{
|
||||
return solver_->max_iterations;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::SetTolerance(float tolerance)
|
||||
{
|
||||
if(tolerance < M_EPSILON)
|
||||
tolerance = M_EPSILON;
|
||||
solver_->tolerance = tolerance;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
float IKSolver::GetTolerance() const
|
||||
{
|
||||
return solver_->tolerance;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::MarkSolverTreeDirty()
|
||||
{
|
||||
solverTreeNeedsRebuild_ = true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/*
|
||||
* This next section maintains the internal list of effector nodes. Whenever
|
||||
* nodes are deleted or added to the scene, or whenever components are added
|
||||
* or removed from nodes, we must check to see which of those nodes are/were
|
||||
* IK effector nodes and update our internal list accordingly.
|
||||
*
|
||||
* Unfortunately, E_COMPONENTREMOVED and E_COMPONENTADDED do not fire when a
|
||||
* parent node is removed/added containing child effector nodes, so we must
|
||||
* also monitor E_NODEREMOVED AND E_NODEADDED.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::OnSceneSet(Scene* scene)
|
||||
{
|
||||
if (scene == NULL)
|
||||
{
|
||||
ik_solver_destroy_tree(solver_);
|
||||
}
|
||||
else
|
||||
{
|
||||
RebuildTree();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::RebuildTree()
|
||||
{
|
||||
Node* scene = GetScene();
|
||||
assert(scene != NULL);
|
||||
|
||||
ik_node_t* ikRoot = ik_node_create(scene->GetID());
|
||||
ik_solver_set_tree(solver_, ikRoot);
|
||||
|
||||
PODVector<Node*> effectorNodes;
|
||||
scene->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
|
||||
for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
|
||||
{
|
||||
BuildTreeToEffector(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::BuildTreeToEffector(const Node* node)
|
||||
{
|
||||
// Check if the component that was added is an IK effector. If not, then it
|
||||
// does not concern us.
|
||||
IKEffector* effector = static_cast<IKEffector*>(node->GetComponent<IKEffector>());
|
||||
if(effector == NULL || effector->GetType() != IKEffector::GetTypeStatic())
|
||||
return;
|
||||
|
||||
// May need to build tree up to the node where this effector was added. Do
|
||||
// this by following the chain of parent nodes until we hit a node that
|
||||
// exists in the solver's tree. Then iterate backwards again and add each
|
||||
// missing node to the solver's tree.
|
||||
PODVector<const Node*> missingNodes;
|
||||
const Node* iterNode = node;
|
||||
ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
|
||||
while (ikNode == NULL)
|
||||
{
|
||||
missingNodes.Push(iterNode);
|
||||
iterNode = iterNode->GetParent();
|
||||
ikNode = ik_node_find_child(solver_->tree, iterNode->GetID());
|
||||
}
|
||||
while (missingNodes.Size() > 0)
|
||||
{
|
||||
iterNode = missingNodes.Back();
|
||||
missingNodes.Pop();
|
||||
ik_node_t* ikChildNode = CreateIKNode(iterNode);
|
||||
ik_node_add_child(ikNode, ikChildNode);
|
||||
ikNode = ikChildNode;
|
||||
}
|
||||
|
||||
// The tip of the tree is the effector. The solver library has ownership of
|
||||
// the effector object, but our IKEffector object also needs to know about
|
||||
// it.
|
||||
ik_effector_t* ikEffector = ik_effector_create();
|
||||
ik_node_attach_effector(ikNode, ikEffector); // ownership of effector
|
||||
effector->SetEffector(ikEffector); // "weak" reference to effector
|
||||
effector->SetSolver(this);
|
||||
effectorList_.Push(effector);
|
||||
|
||||
MarkSolverTreeDirty();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace ComponentAdded;
|
||||
(void)eventType;
|
||||
|
||||
if(solver_->tree == NULL)
|
||||
return;
|
||||
|
||||
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
|
||||
BuildTreeToEffector(node);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace ComponentRemoved;
|
||||
(void)eventType;
|
||||
|
||||
if(solver_->tree == NULL)
|
||||
return;
|
||||
|
||||
// Check if the component that was removed was an IK effector. If not,
|
||||
// then it does not concern us.
|
||||
IKEffector* effector = static_cast<IKEffector*>(eventData[P_COMPONENT].GetPtr());
|
||||
if(effector->GetType() != IKEffector::GetTypeStatic())
|
||||
return;
|
||||
|
||||
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
|
||||
ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
|
||||
ik_node_destroy_effector(ikNode);
|
||||
effector->SetEffector(NULL);
|
||||
effectorList_.Remove(effector); // TODO RemoveSwap()
|
||||
|
||||
MarkSolverTreeDirty();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace NodeAdded;
|
||||
(void)eventType;
|
||||
|
||||
if(solver_->tree == NULL)
|
||||
return;
|
||||
|
||||
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
|
||||
|
||||
PODVector<Node*> effectorNodes;
|
||||
node->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
|
||||
for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
|
||||
{
|
||||
BuildTreeToEffector(*it);
|
||||
effectorList_.Push((*it)->GetComponent<IKEffector>());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace NodeRemoved;
|
||||
(void)eventType;
|
||||
|
||||
if(solver_->tree == NULL)
|
||||
return;
|
||||
|
||||
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
|
||||
|
||||
PODVector<Node*> effectorNodes;
|
||||
node->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
|
||||
for(PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it)
|
||||
{
|
||||
effectorList_.Remove((*it)->GetComponent<IKEffector>());
|
||||
}
|
||||
|
||||
ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
|
||||
if(ikNode != NULL)
|
||||
ik_node_destroy(ikNode);
|
||||
|
||||
MarkSolverTreeDirty();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IKSolver::HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
URHO3D_PROFILE(SolveIK);
|
||||
|
||||
if (solverTreeNeedsRebuild_)
|
||||
{
|
||||
ik_solver_rebuild_data(solver_);
|
||||
solverTreeNeedsRebuild_ = false;
|
||||
}
|
||||
|
||||
for(PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
|
||||
{
|
||||
(*it)->UpdateTargetNodePosition();
|
||||
}
|
||||
|
||||
ik_solver_solve(solver_);
|
||||
}
|
||||
|
||||
} // namespace Urho3D
|
134
Source/Urho3D/IK/IKSolver.h
Normal file
134
Source/Urho3D/IK/IKSolver.h
Normal file
@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2008-2016 the Urho3D project.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IK/IKEffector.h"
|
||||
#include "../Scene/Component.h"
|
||||
|
||||
struct ik_solver_t;
|
||||
struct ik_node_t;
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
class AnimationState;
|
||||
|
||||
/*!
|
||||
* @brief Marks the root or "beginning" of an IK chain or multiple IK chains.
|
||||
* The solving algorithm can be set along with other solver related parameters.
|
||||
* The IK problem is solved starting from the node this component is attached
|
||||
* to and ending at all nodes that have an IKEffector component attached.
|
||||
*/
|
||||
class IKSolver : public Component
|
||||
{
|
||||
URHO3D_OBJECT(IKSolver, Component)
|
||||
|
||||
public:
|
||||
|
||||
enum Algorithm
|
||||
{
|
||||
FABRIK,
|
||||
JACOBIAN_INVERSE,
|
||||
JACOBIAN_TRANSPOSE
|
||||
};
|
||||
|
||||
/// Construct an IK root component.
|
||||
IKSolver(Context* context);
|
||||
/// Default destructor.
|
||||
virtual ~IKSolver();
|
||||
/// Registers this class to the context.
|
||||
static void RegisterObject(Context* context);
|
||||
|
||||
/*!
|
||||
* @brief Selects the solver algorithm. Default is FABRIK.
|
||||
*
|
||||
* The currently supported solvers are listed below.
|
||||
* + **FABRIK**: This is a fairly new and highly efficient inverse
|
||||
* kinematic solving algorithm. It requires the least iterations to
|
||||
* reach its goal, it does not suffer from singularities (nearly no
|
||||
* violent snapping about), and it always converges.
|
||||
*/
|
||||
void SetAlgorithm(Algorithm algorithm);
|
||||
Algorithm GetAlgorithm() const;
|
||||
|
||||
/*!
|
||||
* @brief Sets the maximum number of iterations the solver is allowed to
|
||||
* perform before applying the result.
|
||||
*
|
||||
* Depending on the algorithm, you may want higher or lower values.
|
||||
* FABRIK looks decent after only 10 iterations, whereas Jacobian based
|
||||
* methods often require more than a 100.
|
||||
*
|
||||
* The default value is 50.
|
||||
*
|
||||
* @note Most algorithms have a convergence criteria at which the solver
|
||||
* will stop iterating, so most of the time the maximum number of
|
||||
* iterations isn't even reached.
|
||||
*
|
||||
* @param iterations Number of iterations. Must be greater than 0. Higher
|
||||
* values yield more accurate results, but at the cost of performance.
|
||||
*/
|
||||
void SetMaximumIterations(unsigned iterations);
|
||||
|
||||
/*!
|
||||
* @brief Returns the configured maximum number of iterations.
|
||||
*/
|
||||
unsigned GetMaximumIterations() const;
|
||||
|
||||
/*!
|
||||
* @brief Sets the distance at which the effector is "close enough" to the
|
||||
* target node, at which point the algorithm will stop iterating.
|
||||
*
|
||||
* @param tolerance The distance to set. Smaller values yield more accurate
|
||||
* results, but at the cost of more iterations. Generally you'll want to
|
||||
* specify a number that is about 1/100th to 1/1000th of the total size of
|
||||
* the IK chain, e.g. if your human character has a leg that is 1 Urho3D
|
||||
* unit long, a good starting tolerance would be 0.01.
|
||||
*/
|
||||
void SetTolerance(float tolerance);
|
||||
|
||||
/*!
|
||||
* @brief Returns the configured tolerance.
|
||||
*/
|
||||
float GetTolerance() const;
|
||||
|
||||
void MarkSolverTreeDirty();
|
||||
|
||||
private:
|
||||
virtual void OnSceneSet(Scene* scene);
|
||||
/// Causes the entire tree to be rebuilt
|
||||
void RebuildTree();
|
||||
void BuildTreeToEffector(const Node* node);
|
||||
|
||||
void HandleComponentAdded(StringHash eventType, VariantMap& eventData);
|
||||
void HandleComponentRemoved(StringHash eventType, VariantMap& eventData);
|
||||
void HandleNodeAdded(StringHash eventType, VariantMap& eventData);
|
||||
void HandleNodeRemoved(StringHash eventType, VariantMap& eventData);
|
||||
/// Invokes the IK solver
|
||||
void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
|
||||
|
||||
PODVector<IKEffector*> effectorList_;
|
||||
ik_solver_t* solver_;
|
||||
bool solverTreeNeedsRebuild_;
|
||||
};
|
||||
|
||||
} // namespace Urho3D
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@ -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" />
|
||||
|
Loading…
Reference in New Issue
Block a user