Merge remote-tracking branch 'TheComet93/iss1957'

This commit is contained in:
Lasse Öörni 2017-08-01 11:24:48 +03:00
Родитель e6d9e45ab5 ea593632a5
Коммит 0df18d3f54
48 изменённых файлов: 2951 добавлений и 1485 удалений

Просмотреть файл

@ -139,16 +139,17 @@ void InverseKinematics::CreateScene()
Node* spine = jackNode_->GetChild("Bip01_Spine", true);
solver_ = spine->CreateComponent<IKSolver>();
// Disable auto-solving, which means we need to call Solve() manually
solver_->EnableAutoSolve(false);
// Two-bone solver is more efficient and more stable than FABRIK (but only
// works for two bones, obviously).
solver_->SetAlgorithm(IKSolver::TWO_BONE);
// 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);
// Disable auto-solving, which means we need to call Solve() manually
solver_->SetFeature(IKSolver::AUTO_SOLVE, false);
// Only enable this so the debug draw shows us the pose before solving.
// This should NOT be enabled for any other reason (it does nothing and is
// a waste of performance).
solver_->SetFeature(IKSolver::UPDATE_ORIGINAL_POSE, true);
// Create the camera.
cameraRotateNode_ = scene_->CreateChild("CameraRotate");

37
Source/ThirdParty/ik/CMakeLists.txt поставляемый
Просмотреть файл

@ -19,11 +19,12 @@
# THE SOFTWARE.
#
include (CheckIncludeFiles)
# Define target name
set (TARGET_NAME ik)
include (CheckIncludeFiles)
check_include_files (stdint.h HAVE_STDINT_H)
check_include_files (stdint.h IK_HAVE_STDINT_H)
# Memory debugging options, non-DEBUG and multi-config generator will set the default to FALSE
if (CMAKE_BUILD_TYPE STREQUAL Debug)
@ -32,8 +33,36 @@ endif ()
option (IK_MEMORY_DEBUGGING "Global switch for memory options. Keep track of the number of allocations and de-allocations and prints a report when the program shuts down" ${DEFAULT_MEMORY_DEBUGGING})
cmake_dependent_option (IK_MEMORY_BACKTRACE "Generate backtraces for every malloc(), making it easy to track down memory leaks" "${DEFAULT_MEMORY_DEBUGGING}" "IK_MEMORY_DEBUGGING AND NOT WEB" FALSE)
# Need to set IK_PLATFORM for dllimport/dllexport
if (WIN32)
set (IK_PLATFORM "WINDOWS")
elseif (APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set (IK_PLATFORM "OSX")
elseif (IOS)
set (IK_PLATFORM "IOS")
elseif (UNIX)
set (IK_PLATFORM "LINUX")
else ()
set (IK_PLATFORM "ANDROID")
endif ()
# Enable restrict keyword in quaternion and vector operations if not in debug
# Only do this if IK_RESTRICT is not cached yet.
get_cmake_property (CACHED_VARS VARIABLES)
list (FIND CACHED_VARS "IK_RESTRICT" RESULT)
if (${RESULT} MATCHES -1)
foreach (RESTRICT_KEYWORD restrict __restrict __restrict__)
check_c_source_compiles ("int test (void *${RESTRICT_KEYWORD} x); int main (void) {return 0;}" IK_RESTRICT_${RESTRICT_KEYWORD})
if (IK_RESTRICT_${RESTRICT_KEYWORD})
set (IK_RESTRICT ${RESTRICT_KEYWORD})
break ()
endif ()
endforeach ()
set (IK_RESTRICT ${IK_RESTRICT} CACHE STRING "Restrict Keyword (may be empty)")
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")
option (IK_DOT_OUTPUT "When enabled, the generated chains are dumped to DOT for debug purposes" OFF)
# Define source files
define_source_files (GLOB_CPP_PATTERNS src/*.c GLOB_H_PATTERNS include/ik/*.h)
@ -42,7 +71,7 @@ if (IK_MEMORY_BACKTRACE)
endif ()
# Define generated source files
set (IK_BUILD_TYPE STATIC) # Urho always builds its 3rd-party as STATIC lib
set (IK_LIB_TYPE STATIC) # Urho always builds its 3rd-party as STATIC lib
configure_file (include/ik/export.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/generated/ik/export.h)
configure_file (include/ik/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/generated/ik/config.h)

42
Source/ThirdParty/ik/include/ik/bst_vector.h поставляемый
Просмотреть файл

@ -13,15 +13,15 @@
C_HEADER_BEGIN
struct bstv_hash_value_t
typedef struct bstv_hash_value_t
{
uint32_t hash;
void* value;
};
} bstv_hash_value_t;
struct bstv_t
{
struct ordered_vector_t vector;
ordered_vector_t vector;
};
/*!
@ -29,7 +29,7 @@ struct bstv_t
* @return Returns the newly created bstv object. It must be freed with
* bstv_destroy() when no longer required.
*/
IK_PUBLIC_API struct bstv_t*
IK_PUBLIC_API bstv_t*
bstv_create(void);
/*!
@ -40,7 +40,7 @@ bstv_create(void);
* @param[in] bstv The bstv object to initialise.
*/
IK_PUBLIC_API void
bstv_construct(struct bstv_t* bstv);
bstv_construct(bstv_t* bstv);
/*!
* @brief Destroys an existing bstv object and FREEs the underlying memory.
@ -48,7 +48,7 @@ bstv_construct(struct bstv_t* bstv);
* @param[in] bstv The bstv object to destroy.
*/
IK_PUBLIC_API void
bstv_destroy(struct bstv_t* bstv);
bstv_destroy(bstv_t* bstv);
/*!
* @brief Inserts an element into the bstv using a hashed key.
@ -69,7 +69,7 @@ bstv_destroy(struct bstv_t* bstv);
* 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);
bstv_insert(bstv_t* bstv, uint32_t hash, void* value);
/*!
* @brief Sets the value bstvped to the specified hash in the bstv.
@ -79,7 +79,7 @@ bstv_insert(struct bstv_t* bstv, uint32_t hash, void* value);
* @param[in] value The new value to set.
*/
IK_PUBLIC_API void
bstv_set(struct bstv_t* bstv, uint32_t hash, void* value);
bstv_set(bstv_t* bstv, uint32_t hash, void* value);
/*!
* @brief Looks for an element in the bstv and returns it if found.
@ -93,7 +93,7 @@ bstv_set(struct bstv_t* bstv, uint32_t hash, void* value);
* hash exists, use bstv_key_exists() instead.
*/
IK_PUBLIC_API void*
bstv_find(const struct bstv_t* bstv, uint32_t hash);
bstv_find(const bstv_t* bstv, uint32_t hash);
/*!
* @brief Looks for an element in the bstv and returns a pointer to the element
@ -105,7 +105,7 @@ bstv_find(const struct bstv_t* bstv, uint32_t hash);
* @param[in] hash The has to search for.
*/
IK_PUBLIC_API void**
bstv_find_ptr(const struct bstv_t* bstv, uint32_t hash);
bstv_find_ptr(const bstv_t* bstv, uint32_t hash);
/*!
* @brief Finds the specified element in the bstv and returns its key.
@ -116,7 +116,7 @@ bstv_find_ptr(const struct bstv_t* bstv, uint32_t hash);
* otherwise.
*/
IK_PUBLIC_API uint32_t
bstv_find_element(const struct bstv_t* bstv, const void* value);
bstv_find_element(const bstv_t* bstv, const void* value);
/*!
* @brief Gets any element from the bstv.
@ -126,7 +126,7 @@ bstv_find_element(const struct bstv_t* bstv, const void* value);
* @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);
bstv_get_any_element(const bstv_t* bstv);
/*!
* @brief Returns 1 if the specified hash exists, 0 if otherwise.
@ -135,7 +135,7 @@ bstv_get_any_element(const struct bstv_t* bstv);
* @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);
bstv_hash_exists(bstv_t* bstv, uint32_t hash);
/*!
* @brief Returns a hash that does not yet exist in the bstv.
@ -144,7 +144,7 @@ bstv_hash_exists(struct bstv_t* bstv, uint32_t hash);
* @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);
bstv_find_unused_hash(bstv_t* bstv);
/*!
* @brief Erases an element from the bstv using a hash.
@ -162,10 +162,10 @@ bstv_find_unused_hash(struct bstv_t* bstv);
* bstv.
*/
IK_PUBLIC_API void*
bstv_erase(struct bstv_t* bstv, uint32_t hash);
bstv_erase(bstv_t* bstv, uint32_t hash);
IK_PUBLIC_API void*
bstv_erase_element(struct bstv_t* bstv, void* value);
bstv_erase_element(bstv_t* bstv, void* value);
/*!
* @brief Erases the entire bstv, including the underlying memory.
@ -175,10 +175,10 @@ bstv_erase_element(struct bstv_t* bstv, void* value);
* @param[in] bstv The bstv to clear.
*/
IK_PUBLIC_API void
bstv_clear(struct bstv_t* bstv);
bstv_clear(bstv_t* bstv);
IK_PUBLIC_API void
bstv_clear_free(struct bstv_t* bstv);
bstv_clear_free(bstv_t* bstv);
/*!
* @brief Returns the number of elements in the specified bstv.
@ -201,8 +201,8 @@ bstv_clear_free(struct bstv_t* bstv);
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); \
((hash_v = ((bstv_hash_value_t*) (bstv)->vector.data)[i_##var_v].hash) || 1) && \
((var_v = (var_t*)((bstv_hash_value_t*)(bstv)->vector.data)[i_##var_v].value) || 1); \
++i_##var_v) {
/*!
@ -218,7 +218,7 @@ bstv_clear_free(struct bstv_t* bstv);
* @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); \
ordered_vector_erase_element(&(bstv)->vector, ((bstv_hash_value_t*)(bstv)->vector.data) + i_##var_v); \
--i_##var_v; } while(0)
C_HEADER_END

127
Source/ThirdParty/ik/include/ik/chain_tree.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,127 @@
/*!
* @file chain.h
* @brief Manages synthesising the user specified tree of nodes (ik_node_t)
* into a structure more optimal for solvers.
*
*
*/
#ifndef IK_CHAIN_H
#define IK_CHAIN_H
#include "ik/config.h"
#include "ik/ordered_vector.h"
C_HEADER_BEGIN
struct chain_t
{
/* list of ik_node_t* references that belong to this chain */
ordered_vector_t nodes;
/* list of chain_t objects */
ordered_vector_t children;
};
struct chain_island_t
{
chain_t root_chain;
/*
* List of ik_node_t* objects. This list contains the leaf nodes of IK
* effectors, the children of which aren't part of the IK problem but need
* to be properly updated to match the new transform of the solved tree.
*/
ordered_vector_t transform_dependent_nodes;
};
struct chain_tree_t
{
ordered_vector_t islands; /* list of chain_island_t objects */
};
void
chain_tree_construct(chain_tree_t* chain_trees);
void
chain_tree_destruct(chain_tree_t* chain_trees);
void
chain_island_construct(chain_island_t* chain_island);
void
chain_island_destruct(chain_island_t* chain_island);
chain_t*
chain_create(void);
void
chain_destroy(chain_t* chain);
/*!
* @brief Initialises an allocated chain object.
*/
void
chain_construct(chain_t* chain);
/*!
* @brief Destroys and frees all members, but does not deallocate the chain
* object itself.
*/
void
chain_destruct(chain_t* chain);
/*!
* @brief Clears all children and nodes.
*/
void
chain_clear_free(chain_t* chain);
/*!
* @brief Breaks down the relevant nodes of the scene graph into a tree 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 tree.
*/
int
rebuild_chain_tree(ik_solver_t* solver);
void
calculate_segment_lengths(chain_tree_t* chain_tree);
/*!
* @brief Counts all of the chains in the tree, excluding the root chain.
*/
int
count_chains_exclude_root(chain_tree_t* chain_tree);
void
calculate_global_rotations(chain_t* chain);
#ifdef IK_DOT_OUTPUT
/*!
* @brief Dumps the chain tree to DOT format.
* @param[in] root The root node of the user created tree. This is a parameter
* because the root chain does not necessarily hold the root node of the tree
* because the root node doesn't have to be part of the IK problem.
* @note Doesn't necessarily have to be the root node, it will dump the tree
* beginning at this node.
* @param[in] chain Usually the root chain. Doesn't necessarily have to be the
* root, in which case it will dump beginning at this chain.
* @param[in] file_name The name of the file to dump to.
*/
void
dump_to_dot(ik_node_t* root, chain_tree_t* chain_tree, const char* file_name);
#endif /* IK_DOT_OUTPUT */
C_HEADER_END
#endif /* IK_CHAIN_H */

34
Source/ThirdParty/ik/include/ik/config.h.in поставляемый
Просмотреть файл

@ -3,34 +3,34 @@
* --------------------------------------------------------------*/
#ifndef IK_CONFIG_HPP
# define IK_CONFIG_HPP
#define IK_CONFIG_HPP
/* --------------------------------------------------------------
* build settings
* --------------------------------------------------------------*/
# define OFF 0
# define ON 1
#define ik_real @IK_REAL@
#define IK_RESTRICT @IK_RESTRICT@
# 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@
#cmakedefine IK_HAVE_STDINT_H
#cmakedefine IK_MEMORY_DEBUGGING
#cmakedefine IK_DOT_OUTPUT
#ifdef IK_MEMORY_DEBUGGING
#cmakedefine IK_MEMORY_BACKTRACE
#endif
/* --------------------------------------------------------------
* common include files
* --------------------------------------------------------------*/
# include "ik/export.h"
# include <stddef.h>
#include "ik/export.h"
#include "ik/types.h"
# if HAVE_STDINT_H == ON
# include <stdint.h>
# else
# include "ik/pstdint.h"
# endif
#ifdef IK_HAVE_STDINT_H
#include <stdint.h>
#else
#include "ik/pstdint.h"
#endif
#endif /* IK_CONFIG_HPP */

58
Source/ThirdParty/ik/include/ik/constraint.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,58 @@
#ifndef IK_CONSTRAINT_H
#define IK_CONSTRAINT_H
#include "ik/config.h"
C_HEADER_BEGIN
typedef void (*ik_constraint_apply_func)(ik_node_t*);
typedef enum ik_constraint_type_e
{
IK_CONSTRAINT_STIFF,
IK_CONSTRAINT_HINGE,
IK_CONSTRAINT_CONE
} ik_constraint_type_e;
struct ik_constraint_t
{
ik_constraint_type_e type;
ik_constraint_apply_func apply;
};
/*!
* @brief Creates a new constraint object. It can be attached to any node in the
* tree using ik_node_attach_constraint().
*/
IK_PUBLIC_API ik_constraint_t*
ik_constraint_create(ik_constraint_type_e constraint_type);
/*!
* @brief Sets the type of constraint to enforce.
* @note The tree must be rebuilt only if you change to or from the "stiff"
* constraint (IK_CONSTRAINT_STIFF). Switching to any other constraint does not
* require a rebuild. The reason for this is because the stiff constraint
* causes the node to be excluded entirely from the chain tree, and determining
* this requires a rebuild.
*/
IK_PUBLIC_API void
ik_constraint_set(ik_constraint_t* constraint, ik_constraint_type_e constraint_type);
/*!
* @brief Allows the user to specify a custom callback function for enforcing
* a constraint.
*/
IK_PUBLIC_API void
ik_constraint_set_custom(ik_constraint_t* constraint, ik_constraint_apply_func callback);
/*!
* @brief Destroys and frees a constraint object. This should **NOT** be called
* on constraints that are attached to nodes. Use ik_node_destroy_constraint()
* instead.
*/
IK_PUBLIC_API void
ik_constraint_destroy(ik_constraint_t* constraint);
C_HEADER_END
#endif /* IK_CONSTRAINT_H */

16
Source/ThirdParty/ik/include/ik/effector.h поставляемый
Просмотреть файл

@ -7,9 +7,7 @@
C_HEADER_BEGIN
struct ik_node_t;
enum effector_flags_e
typedef enum effector_flags_e
{
/*!
* @brief Causes intermediary weight values to rotate the target around the
@ -17,10 +15,8 @@ enum effector_flags_e
* appealing if the solved tree diverges a lot from the original tree
* during weight transitions.
*/
EFFECTOR_WEIGHT_NLERP = 0x01,
EFFECTOR_INHERIT_PARENT_ROTATION = 0x02
};
EFFECTOR_WEIGHT_NLERP = 0x01
} effector_flgs_e;
/*!
* @brief Specifies how a chain of nodes should be solved. The effector can
@ -83,14 +79,14 @@ struct ik_effector_t
* @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_PUBLIC_API 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);
ik_effector_construct(ik_effector_t* effector);
/*!
* @brief Destroys and frees an effector object. This should **NOT** be called
@ -98,7 +94,7 @@ ik_effector_construct(struct ik_effector_t* effector);
* instead.
*/
IK_PUBLIC_API void
ik_effector_destroy(struct ik_effector_t* effector);
ik_effector_destroy(ik_effector_t* effector);
C_HEADER_END

104
Source/ThirdParty/ik/include/ik/export.h.in поставляемый
Просмотреть файл

@ -2,59 +2,56 @@
* 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.
* - IK_LIB_TYPE : Set to either SHARED or STATIC.
* - IK_PLATFORM : Set to WINDOWS, OSX, LINUX, ANDROID, or IOS
* Global definitions (non substitution)
* - PROJECT_NAME_BUILDING : Define this if the library is being
* built.
* - IK_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 IK_@IK_LIB_TYPE@
# define IK_PLATFORM_@IK_PLATFORM@
/* --------------------------------------------------------------
* 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
# if !defined(IK_SHARED) && !defined(IK_STATIC)
# error Please define IK_SHARED or IK_STATIC.
# endif
/* DLL platforms */
# if defined(IK_SHARED) && defined(IK_PLATFORM_WINDOWS)
# if defined(__GNUC__)
/* cygwin visbibility */
# define IK_HELPER_API_EXPORT __attribute__ ((dllexport))
# define IK_HELPER_API_IMPORT __attribute__ ((dllimport))
# 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
/* msvc visibility */
# define IK_HELPER_API_EXPORT __declspec(dllexport)
# define IK_HELPER_API_IMPORT __declspec(dllimport)
# endif
# elif defined(IK_STATIC)
/* static build */
# define IK_HELPER_PRIVATE_API
/* Other platforms, just assume it's GCC/clang */
# elif defined(IK_SHARED) && defined(__GNUC__) && __GNUC__ >= 4
/* gcc 4+ visibility */
# define IK_HELPER_API_EXPORT __attribute__ ((visibility ("default")))
# define IK_HELPER_API_IMPORT __attribute__ ((visibility ("default")))
# define IK_HELPER_PRIVATE_API __attribute__ ((visibility ("hidden")))
/*
* All other cases:
* + gcc lower than 4 doesn't have any explicit visibility, everything is exported
* + static libs don't need visibility macros
*/
# else
# define IK_HELPER_API_EXPORT
# define IK_HELPER_API_IMPORT
# define IK_HELPER_API_LOCAL
# else
# error Please define IK_SHARED or IK_STATIC
# define IK_HELPER_PRIVATE_API
# endif
/*
@ -66,39 +63,14 @@
# 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
# define IK_PRIVATE_API IK_HELPER_PRIVATE_API
/* --------------------------------------------------------------
* typeof support
* Disable MSVC warnings
* --------------------------------------------------------------*/
# if defined(__GNUC__)
# define TYPEOF(x) __typeof__(x)
# else
# undef TYPEOF
# if defined(IK_PLATFORM_WINDOWS) && !defined(__GNUC__)
# pragma warning(disable: 4996) /* 'strcpy': This function or variable may be unsafe */
# endif
/* --------------------------------------------------------------

6
Source/ThirdParty/ik/include/ik/log.h поставляемый
Просмотреть файл

@ -7,14 +7,14 @@ C_HEADER_BEGIN
typedef void (*ik_log_cb_func)(const char*);
enum ik_log_e
typedef enum ik_log_e
{
IK_LOG_NONE,
IK_LOG_STDOUT
};
} ik_log_e;
IK_PUBLIC_API void
ik_log_init(enum ik_log_e options);
ik_log_init(ik_log_e options);
IK_PUBLIC_API void
ik_log_deinit(void);

4
Source/ThirdParty/ik/include/ik/memory.h поставляемый
Просмотреть файл

@ -3,7 +3,7 @@
#include "ik/config.h"
#if IK_MEMORY_DEBUGGING == ON
#ifdef IK_MEMORY_DEBUGGING
# define MALLOC malloc_wrapper
# define FREE free_wrapper
#else
@ -33,7 +33,7 @@ ik_memory_init(void);
IK_PUBLIC_API uintptr_t
ik_memory_deinit(void);
#if IK_MEMORY_DEBUGGING == ON
#ifdef IK_MEMORY_DEBUGGING
/*!
* @brief Does the same thing as a normal call to malloc(), but does some
* additional work to monitor and track down memory leaks.

110
Source/ThirdParty/ik/include/ik/node.h поставляемый
Просмотреть файл

@ -8,8 +8,6 @@
C_HEADER_BEGIN
struct ik_effector_t;
/*!
* @brief Represents one node in the tree to be solved.
*/
@ -28,8 +26,8 @@ struct ik_node_t
* // 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());
* ik_solver_t* solver = ik_solver_create(SOLVER_FABRIK);
* ik_node_t* ikNode = ik_node_create(node->GetID());
* ikNode->user_data = node; // Store pointer to your own node object
*
* // ---- elsewhere ------
@ -48,7 +46,7 @@ struct ik_node_t
* be set and retrieved at any time.
* @note The default value is (0, 0, 0).
*/
vec3_t position;
vec3_t original_position;
/*!
* @brief The initial global rotation (in world space).
@ -56,27 +54,29 @@ struct ik_node_t
* angle computations enabled (SOLVER_CALCULATE_FINAL_ANGLES).
* @note The default value is the identity quaternion.
*/
quat_t initial_rotation;
/*!
* @brief After the solver is executed, the solved global (world) position
* is stored here and can be retrieved.
*/
vec3_t position;
/*!
* @brief After the solver is executed, the solved global (world) rotation
* is stored here and can be retrieved.
*/
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.
* @note Don't change this if this node has a parent. If you need to change
* the guid then unlink the node, change it, and re-add it as a child.
*/
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
@ -84,33 +84,38 @@ struct ik_node_t
* node->effector->target_position or node->effector->target_rotation.
* @note May be NULL.
*/
struct ik_effector_t* effector;
ik_effector_t* effector;
ik_constraint_t* constraint;
ik_real stiffness;
ik_real rotation_weight;
/* Private data */
ik_real segment_length;
struct ik_node_t* parent;
struct bstv_t children;
ik_node_t* parent;
bstv_t children; /* ik_node_t objects */
};
/*!
* @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_PUBLIC_API 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);
ik_node_construct(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);
ik_node_destruct(ik_node_t* node);
/*!
* @brief Destructs and frees the node, destroying all children in the process.
@ -118,7 +123,7 @@ ik_node_destruct(struct ik_node_t* node);
* @note You will need to rebuild the solver's tree before solving.
*/
IK_PUBLIC_API void
ik_node_destroy(struct ik_node_t* node);
ik_node_destroy(ik_node_t* node);
/*!
* @brief Attaches a node as a child to another node. The parent node gains
@ -126,7 +131,7 @@ ik_node_destroy(struct ik_node_t* node);
* @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);
ik_node_add_child(ik_node_t* node, ik_node_t* child);
/*!
* @brief Unlinks a node from the tree, without destroying anything. All
@ -135,7 +140,7 @@ ik_node_add_child(struct ik_node_t* node, struct ik_node_t* child);
* @note You will need to rebuild the solver's tree before solving.
*/
IK_PUBLIC_API void
ik_node_unlink(struct ik_node_t* node);
ik_node_unlink(ik_node_t* node);
/*!
* @brief Searches recursively for a node in a tree with the specified global
@ -143,8 +148,8 @@ ik_node_unlink(struct ik_node_t* node);
* @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);
IK_PUBLIC_API ik_node_t*
ik_node_find_child(ik_node_t* node, uint32_t guid);
/*!
* @brief Attaches an effector object to the node. The node gains ownership
@ -153,22 +158,63 @@ ik_node_find_child(struct ik_node_t* node, uint32_t guid);
* @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);
ik_node_attach_effector(ik_node_t* node, ik_effector_t* effector);
/*!
* @brief Removes and destroys the node's effector, if it exists. The attribute
* @brief Removes and destroys the node's effector, if it exists. The field
* 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);
ik_node_destroy_effector(ik_node_t* node);
/*!
* @brief The constraint is attached to the specified node, but applies to the
* parent of this node. In other words, if you wish to constraint the rotation
* of node A then you must attach said constraint to the **child** of node A.
*
* Constraints are a bit strange in how they are stored. They don't apply to
* single nodes, rather, they apply to entire segments (edges connecting nodes).
* This is not apparent in a single chain of nodes, but becomes apparent if you
* consider a tree structure.
*
* A C
* \ /
* B
* |
* D
*
* If you wanted to constraint the rotation of D, then you would add a
* constraint to node B. If you wanted to constraint the rotation of the
* segment B-A then you would add a constraint to node A.
*
* @param[in] node The child of the node you wish to constrain.
* @param[in] constraint The constraint object. The node gains ownership of
* the constraint and is responsible for its deallocation. If the node already
* owns a constraint, then it is first destroyed.
*/
IK_PUBLIC_API void
ik_node_attach_constraint(ik_node_t* node, ik_constraint_t* constraint);
/*!
* @brief Removes and destroys the node's constraint, if it exists. The field
* node->constraint is set to NULL.
*/
IK_PUBLIC_API void
ik_node_destroy_constraint(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);
ik_node_dump_to_dot(ik_node_t* node, const char* file_name);
IK_PUBLIC_API void
ik_node_global_to_local(ik_node_t* node);
IK_PUBLIC_API void
ik_node_local_to_global(ik_node_t* node);
C_HEADER_END

Просмотреть файл

@ -30,7 +30,7 @@ struct ordered_vector_t
* 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*
IK_PUBLIC_API ordered_vector_t*
ordered_vector_create(const uint32_t element_size);
/*!
@ -42,7 +42,7 @@ ordered_vector_create(const uint32_t element_size);
* 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,
ordered_vector_construct(ordered_vector_t* vector,
const uint32_t element_size);
/*!
@ -51,7 +51,7 @@ ordered_vector_construct(struct ordered_vector_t* vector,
* @param[in] vector The vector to destroy.
*/
IK_PUBLIC_API void
ordered_vector_destroy(struct ordered_vector_t* vector);
ordered_vector_destroy(ordered_vector_t* vector);
/*!
* @brief Erases all elements in a vector.
@ -61,14 +61,14 @@ ordered_vector_destroy(struct ordered_vector_t* vector);
* @param[in] vector The vector to clear.
*/
IK_PUBLIC_API void
ordered_vector_clear(struct ordered_vector_t* vector);
ordered_vector_clear(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);
ordered_vector_clear_free(ordered_vector_t* vector);
/*!
* @brief Sets the size of the vector to exactly the size specified. If the
@ -80,7 +80,7 @@ ordered_vector_clear_free(struct ordered_vector_t* vector);
* @return Returns -1 on failure, 0 on success.
*/
IK_PUBLIC_API int
ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size);
ordered_vector_resize(ordered_vector_t* vector, uint32_t size);
/*!
* @brief Gets the number of elements that have been inserted into the vector.
@ -102,7 +102,7 @@ ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size);
* otherwise.
*/
IK_PUBLIC_API int
ordered_vector_push(struct ordered_vector_t* vector, void* data);
ordered_vector_push(ordered_vector_t* vector, void* data);
/*!
* @brief Allocates space for a new element at the head of the vector, but does
@ -116,14 +116,14 @@ ordered_vector_push(struct ordered_vector_t* vector, void* data);
* warning and use with caution.
*/
IK_PUBLIC_API void*
ordered_vector_push_emplace(struct ordered_vector_t* vector);
ordered_vector_push_emplace(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);
ordered_vector_push_vector(ordered_vector_t* vector, ordered_vector_t* source_vector);
/*!
* @brief Removes an element from the back (end) of the vector.
@ -136,7 +136,7 @@ ordered_vector_push_vector(struct ordered_vector_t* vector, struct ordered_vecto
* If there are no elements to pop, NULL is returned.
*/
IK_PUBLIC_API void*
ordered_vector_pop(struct ordered_vector_t* vector);
ordered_vector_pop(ordered_vector_t* vector);
/*!
* @brief Returns the very last element of the vector.
@ -149,7 +149,7 @@ ordered_vector_pop(struct ordered_vector_t* vector);
* If there are no elements in the vector, NULL is returned.
*/
IK_PUBLIC_API void*
ordered_vector_back(const struct ordered_vector_t* vector);
ordered_vector_back(const ordered_vector_t* vector);
/*!
* @brief Allocates space for a new element at the specified index, but does
@ -166,7 +166,7 @@ ordered_vector_back(const struct ordered_vector_t* vector);
* @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);
ordered_vector_insert_emplace(ordered_vector_t* vector, uint32_t index);
/*!
* @brief Inserts (copies) a new element at the specified index.
@ -181,7 +181,7 @@ ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index);
* 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);
ordered_vector_insert(ordered_vector_t* vector, uint32_t index, void* data);
/*!
* @brief Erases the specified element from the vector.
@ -191,7 +191,7 @@ ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* dat
* ranges from **0** to **ordered_vector_count()-1**.
*/
IK_PUBLIC_API void
ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index);
ordered_vector_erase_index(ordered_vector_t* vector, uint32_t index);
/*!
* @brief Removes the element in the vector pointed to by **element**.
@ -199,7 +199,7 @@ ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index);
* @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);
ordered_vector_erase_element(ordered_vector_t* vector, void* element);
/*!
* @brief Gets a pointer to the specified element in the vector.
@ -215,17 +215,17 @@ ordered_vector_erase_element(struct ordered_vector_t* vector, void* element);
* returned.
*/
IK_PUBLIC_API void*
ordered_vector_get_element(struct ordered_vector_t*, uint32_t index);
ordered_vector_get_element(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)
* ordered_vector_t* some_vector = (a vector containing elements of type "bar")
* ORDERED_VECTOR_FOR_EACH(some_vector, bar, element)
* {
* do_something_with(element); ("element" is now of type "struct bar*")
* do_something_with(element); ("element" is now of type "bar*")
* }
* ```
* @param[in] vector A pointer to the vector to iterate.
@ -252,8 +252,8 @@ ordered_vector_get_element(struct ordered_vector_t*, uint32_t index);
* @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).
* example, if your vector is storing ```type_t*``` objects then
* var_type should equal ```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
@ -277,9 +277,9 @@ ordered_vector_get_element(struct ordered_vector_t*, uint32_t index);
* @warning Only call this while iterating.
* Example:
* ```
* ORDERED_VECTOR_FOR_EACH(some_vector, struct bar, element)
* ORDERED_VECTOR_FOR_EACH(some_vector, bar, element)
* {
* ORDERED_VECTOR_ERASE_IN_FOR_LOOP(some_vector, struct bar, element);
* ORDERED_VECTOR_ERASE_IN_FOR_LOOP(some_vector, bar, element);
* }
* ```
* @param[in] vector The vector to erase from.

136
Source/ThirdParty/ik/include/ik/solver.h поставляемый
Просмотреть файл

@ -2,33 +2,31 @@
#define IK_SOLVER_H
#include "ik/config.h"
#include "ik/chain_tree.h"
#include "ik/ordered_vector.h"
#include "ik/vec3.h"
#include "ik/quat.h"
#include "ik/vec3.h"
C_HEADER_BEGIN
struct ik_effector_t;
struct ik_node_t;
struct ik_solver_t;
typedef void (*ik_solver_destruct_func)(ik_solver_t*);
typedef int (*ik_solver_rebuild_data_func)(ik_solver_t*);
typedef int (*ik_solver_solve_func)(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_iterate_node_cb_func)(ik_node_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
typedef enum solver_algorithm_e
{
SOLVER_FABRIK
SOLVER_ONE_BONE,
SOLVER_TWO_BONE,
SOLVER_FABRIK,
SOLVER_MSD
/* TODO Not implemented
SOLVER_JACOBIAN_INVERSE,
SOLVER_JACOBIAN_TRANSPOSE */
};
} solver_algorithm_e;
enum solver_flags_e
typedef enum solver_flags_e
{
/*!
* @brief Causes the root node in the tree to be excluded from the list of
@ -37,59 +35,29 @@ enum solver_flags_e
*/
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,
SOLVER_ENABLE_CONSTRAINTS = 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
};
SOLVER_CALCULATE_TARGET_ROTATIONS = 0x04
} solver_flags_e;
/*!
* @brief This is a base struct for all solvers.
* @brief This is a base 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;
#define SOLVER_DATA_HEAD \
int32_t max_iterations; \
float tolerance; \
uint8_t flags; \
\
/* Derived structure callbacks */ \
ik_solver_destruct_func destruct; \
ik_solver_rebuild_data_func rebuild_data; \
ik_solver_solve_func solve; \
\
ordered_vector_t effector_nodes_list; \
ik_node_t* tree; \
/* list of ik_chain_tree_t objects (allocated in-place) */ \
chain_tree_t chain_tree;
struct ik_solver_t
{
SOLVER_DATA_HEAD
@ -138,15 +106,15 @@ struct ik_solver_t
* @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);
IK_PUBLIC_API ik_solver_t*
ik_solver_create(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);
ik_solver_destroy(ik_solver_t* solver);
/*!
* @brief Sets the tree to solve. The solver takes ownership of the tree, so
@ -155,7 +123,7 @@ ik_solver_destroy(struct ik_solver_t* solver);
* 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);
ik_solver_set_tree(ik_solver_t* solver, ik_node_t* root);
/*!
* @brief The solver releases any references to a previously set tree and
@ -163,15 +131,15 @@ ik_solver_set_tree(struct ik_solver_t* solver, struct ik_node_t* root);
* 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);
IK_PUBLIC_API ik_node_t*
ik_solver_unlink_tree(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);
ik_solver_destroy_tree(ik_solver_t* solver);
/*!
* @brief Causes the set tree to be processed into more optimal data structures
@ -181,7 +149,7 @@ ik_solver_destroy_tree(struct ik_solver_t* solver);
* you must call this again before calling the solver.
*/
IK_PUBLIC_API int
ik_solver_rebuild_data(struct ik_solver_t* solver);
ik_solver_rebuild_data(ik_solver_t* solver);
/*!
* @brief Unusual, but if you have a tree with translational motions such that
@ -191,7 +159,7 @@ ik_solver_rebuild_data(struct ik_solver_t* solver);
* @note This function gets called by ik_solver_rebuild_data().
*/
IK_PUBLIC_API void
ik_solver_recalculate_segment_lengths(struct ik_solver_t* solver);
ik_solver_recalculate_segment_lengths(ik_solver_t* solver);
/*!
* @brief Solves the IK problem. The node solutions will be provided via a
@ -199,27 +167,25 @@ ik_solver_recalculate_segment_lengths(struct ik_solver_t* solver);
* solver->apply_result.
*/
IK_PUBLIC_API int
ik_solver_solve(struct ik_solver_t* solver);
ik_solver_solve(ik_solver_t* solver);
IK_PUBLIC_API void
ik_solver_calculate_joint_rotations(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.
* @brief Iterates all nodes in the internal tree, breadth first, and passes
* each node to the specified callback function.
*/
IK_PUBLIC_API void
ik_solver_iterate_tree(struct ik_solver_t* solver);
ik_solver_iterate_tree(ik_solver_t* solver,
ik_solver_iterate_node_cb_func callback);
/*!
* @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.
* positions and rotations for every node in the tree.
*/
IK_PUBLIC_API void
ik_solver_reset_solved_data(struct ik_solver_t* solver);
ik_solver_reset_to_original_pose(ik_solver_t* solver);
C_HEADER_END

29
Source/ThirdParty/ik/include/ik/solver_1bone.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
#ifndef IK_SOLVER_1BONE_H
#define IK_SOLVER_1BONE_H
#include "ik/config.h"
#include "ik/ordered_vector.h"
#include "ik/solver.h"
C_HEADER_BEGIN
struct one_bone_t
{
SOLVER_DATA_HEAD
};
int
solver_1bone_construct(ik_solver_t* solver);
void
solver_1bone_destruct(ik_solver_t* solver);
int
solver_1bone_rebuild(ik_solver_t* solver);
int
solver_1bone_solve(ik_solver_t* solver);
C_HEADER_END
#endif /* IK_SOLVER_1BONE_H */

29
Source/ThirdParty/ik/include/ik/solver_2bone.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
#ifndef IK_SOLVER_2BONE_H
#define IK_SOLVER_2BONE_H
#include "ik/config.h"
#include "ik/ordered_vector.h"
#include "ik/solver.h"
C_HEADER_BEGIN
struct two_bone_t
{
SOLVER_DATA_HEAD
};
int
solver_2bone_construct(ik_solver_t* solver);
void
solver_2bone_destruct(ik_solver_t* solver);
int
solver_2bone_rebuild(ik_solver_t* solver);
int
solver_2bone_solve(ik_solver_t* solver);
C_HEADER_END
#endif /* IK_SOLVER_2BONE_H */

Просмотреть файл

@ -7,32 +7,19 @@
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);
int
solver_FABRIK_construct(ik_solver_t* solver);
void
solver_FABRIK_destroy(struct ik_solver_t* solver);
solver_FABRIK_destruct(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);
solver_FABRIK_solve(ik_solver_t* solver);
C_HEADER_END

20
Source/ThirdParty/ik/include/ik/solver_MSD.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
#include "ik/config.h"
#include "ik/solver.h"
C_HEADER_BEGIN
struct msd_t
{
SOLVER_DATA_HEAD
};
int
solver_MSD_construct(ik_solver_t* solver);
void
solver_MSD_destruct(ik_solver_t* solver);
int
solver_MSD_solve(ik_solver_t* solver);
C_HEADER_END

21
Source/ThirdParty/ik/include/ik/types.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
#ifndef IK_TYPES_H
#define IK_TYPES_H
/* IK specific types */
typedef struct chain_t chain_t;
typedef struct chain_island_t chain_island_t;
typedef struct chain_tree_t chain_tree_t;
typedef struct ik_constraint_t ik_constraint_t;
typedef struct ik_effector_t ik_effector_t;
typedef struct ik_node_t ik_node_t;
typedef struct ik_solver_t ik_solver_t;
typedef struct fabrik_t fabrik_t;
typedef struct two_bone_t two_bone_t;
typedef struct one_bone_t one_bone_t;
typedef struct msd_t msd_t;
/* containers */
typedef struct ordered_vector_t ordered_vector_t;
typedef struct bstv_t bstv_t;
#endif /* IK_TYPES_H */

13
Source/ThirdParty/ik/include/ik/util.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
#ifndef IK_UTIL_H
#define IK_UTIL_H
#include "ik/config.h"
C_HEADER_BEGIN
IK_PUBLIC_API void
ik_calculate_rotation_weight_decays(chain_tree_t* chain_tree);
C_HEADER_END
#endif /* IK_UTIL_H */

11
Source/ThirdParty/ik/include/ik/vec3.h поставляемый
Просмотреть файл

@ -45,6 +45,17 @@ vec3_dot(const ik_real* v1, const ik_real* v2);
IK_PUBLIC_API void
vec3_cross(ik_real* v1, const ik_real* v2);
/*!
* @brief Calculates the angle between two vectors. If the angle is 0 or 180,
* the delta rotation is set to identity.
* @param[out] q A contiguous array of 4 ik_floats representing a quaternion.
* The result is written to this. Any previous data is overwritten.
* @param[in] v1 The first vector.
* @param[in] v2 The second vector.
*/
IK_PUBLIC_API void
vec3_angle(ik_real* q, const ik_real* v1, const ik_real* v2);
C_HEADER_END
#endif /* VEC3_H */

104
Source/ThirdParty/ik/src/bst_vector.c поставляемый
Просмотреть файл

@ -6,11 +6,11 @@
const uint32_t BST_VECTOR_INVALID_HASH = (uint32_t)-1;
/* ------------------------------------------------------------------------- */
struct bstv_t*
bstv_t*
bstv_create(void)
{
struct bstv_t* bstv;
if(!(bstv = (struct bstv_t*)MALLOC(sizeof *bstv)))
bstv_t* bstv;
if (!(bstv = (bstv_t*)MALLOC(sizeof *bstv)))
return NULL;
bstv_construct(bstv);
return bstv;
@ -18,15 +18,15 @@ bstv_create(void)
/* ------------------------------------------------------------------------- */
void
bstv_construct(struct bstv_t* bstv)
bstv_construct(bstv_t* bstv)
{
assert(bstv);
ordered_vector_construct(&bstv->vector, sizeof(struct bstv_hash_value_t));
ordered_vector_construct(&bstv->vector, sizeof(bstv_hash_value_t));
}
/* ------------------------------------------------------------------------- */
void
bstv_destroy(struct bstv_t* bstv)
bstv_destroy(bstv_t* bstv)
{
assert(bstv);
bstv_clear_free(bstv);
@ -36,28 +36,28 @@ bstv_destroy(struct bstv_t* 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)
static bstv_hash_value_t*
bstv_find_lower_bound(const bstv_t* bstv, uint32_t hash)
{
uint32_t half;
struct bstv_hash_value_t* middle;
struct bstv_hash_value_t* data;
bstv_hash_value_t* middle;
bstv_hash_value_t* data;
uint32_t len;
assert(bstv);
data = (struct bstv_hash_value_t*)bstv->vector.data;
data = (bstv_hash_value_t*)bstv->vector.data;
len = bstv->vector.count;
/* if the vector has no data, return NULL */
if(!len)
/* if (the vector has no data, return NULL */
if (!len)
return NULL;
while(len > 0)
while (len > 0)
{
half = len >> 1;
middle = data + half;
if(middle->hash < hash)
if (middle->hash < hash)
{
data = middle;
++data;
@ -67,8 +67,8 @@ bstv_find_lower_bound(const struct bstv_t* bstv, uint32_t hash)
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)
/* 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;
@ -76,31 +76,31 @@ bstv_find_lower_bound(const struct bstv_t* bstv, uint32_t hash)
/* ------------------------------------------------------------------------- */
int
bstv_insert(struct bstv_t* bstv, uint32_t hash, void* value)
bstv_insert(bstv_t* bstv, uint32_t hash, void* value)
{
struct bstv_hash_value_t* emplaced_data;
struct bstv_hash_value_t* lower_bound;
bstv_hash_value_t* emplaced_data;
bstv_hash_value_t* lower_bound;
assert(bstv);
/* don't insert reserved hashes */
if(hash == BST_VECTOR_INVALID_HASH)
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)
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);
if (!lower_bound)
emplaced_data = (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);
lower_bound - (bstv_hash_value_t*)bstv->vector.data);
if(!emplaced_data)
if (!emplaced_data)
return -1;
memset(emplaced_data, 0, sizeof *emplaced_data);
@ -112,20 +112,20 @@ bstv_insert(struct bstv_t* bstv, uint32_t hash, void* value)
/* ------------------------------------------------------------------------- */
void
bstv_set(struct bstv_t* bstv, uint32_t hash, void* value)
bstv_set(bstv_t* bstv, uint32_t hash, void* value)
{
struct bstv_hash_value_t* data;
bstv_hash_value_t* data;
assert(bstv);
data = bstv_find_lower_bound(bstv, hash);
if(data && data->hash == hash)
if (data && data->hash == hash)
data->value = value;
}
/* ------------------------------------------------------------------------- */
void*
bstv_find(const struct bstv_t* bstv, uint32_t hash)
bstv_find(const bstv_t* bstv, uint32_t hash)
{
void** result = bstv_find_ptr(bstv, hash);
return result == NULL ? NULL : *result;
@ -133,26 +133,26 @@ bstv_find(const struct bstv_t* bstv, uint32_t hash)
/* ------------------------------------------------------------------------- */
void**
bstv_find_ptr(const struct bstv_t* bstv, uint32_t hash)
bstv_find_ptr(const bstv_t* bstv, uint32_t hash)
{
struct bstv_hash_value_t* data;
bstv_hash_value_t* data;
assert(bstv);
data = bstv_find_lower_bound(bstv, hash);
if(!data || data->hash != hash)
if (!data || data->hash != hash)
return NULL;
return &data->value;
}
/* ------------------------------------------------------------------------- */
uint32_t
bstv_find_element(const struct bstv_t* bstv, const void* value)
bstv_find_element(const bstv_t* bstv, const void* value)
{
assert(bstv);
ORDERED_VECTOR_FOR_EACH(&bstv->vector, struct bstv_hash_value_t, kv)
if(kv->value == value)
ORDERED_VECTOR_FOR_EACH(&bstv->vector, bstv_hash_value_t, kv)
if (kv->value == value)
return kv->hash;
ORDERED_VECTOR_END_EACH
return BST_VECTOR_INVALID_HASH;
@ -160,40 +160,40 @@ bstv_find_element(const struct bstv_t* bstv, const void* value)
/* ------------------------------------------------------------------------- */
void*
bstv_get_any_element(const struct bstv_t* bstv)
bstv_get_any_element(const bstv_t* bstv)
{
struct bstv_hash_value_t* kv;
bstv_hash_value_t* kv;
assert(bstv);
kv = (struct bstv_hash_value_t*)ordered_vector_back(&bstv->vector);
if(kv)
kv = (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)
bstv_hash_exists(bstv_t* bstv, uint32_t hash)
{
struct bstv_hash_value_t* data;
bstv_hash_value_t* data;
assert(bstv);
data = bstv_find_lower_bound(bstv, hash);
if(data && data->hash == hash)
if (data && data->hash == hash)
return 0;
return -1;
}
/* ------------------------------------------------------------------------- */
uint32_t
bstv_find_unused_hash(struct bstv_t* bstv)
bstv_find_unused_hash(bstv_t* bstv)
{
uint32_t i = 0;
assert(bstv);
BSTV_FOR_EACH(bstv, void, key, value)
if(i != key)
if (i != key)
break;
++i;
BSTV_END_EACH
@ -202,15 +202,15 @@ bstv_find_unused_hash(struct bstv_t* bstv)
/* ------------------------------------------------------------------------- */
void*
bstv_erase(struct bstv_t* bstv, uint32_t hash)
bstv_erase(bstv_t* bstv, uint32_t hash)
{
void* value;
struct bstv_hash_value_t* data;
bstv_hash_value_t* data;
assert(bstv);
data = bstv_find_lower_bound(bstv, hash);
if(!data || data->hash != hash)
if (!data || data->hash != hash)
return NULL;
value = data->value;
@ -220,7 +220,7 @@ bstv_erase(struct bstv_t* bstv, uint32_t hash)
/* ------------------------------------------------------------------------- */
void*
bstv_erase_element(struct bstv_t* bstv, void* value)
bstv_erase_element(bstv_t* bstv, void* value)
{
void* data;
uint32_t hash;
@ -228,7 +228,7 @@ bstv_erase_element(struct bstv_t* bstv, void* value)
assert(bstv);
hash = bstv_find_element(bstv, value);
if(hash == BST_VECTOR_INVALID_HASH)
if (hash == BST_VECTOR_INVALID_HASH)
return NULL;
data = bstv_find_lower_bound(bstv, hash);
@ -239,14 +239,14 @@ bstv_erase_element(struct bstv_t* bstv, void* value)
/* ------------------------------------------------------------------------- */
void
bstv_clear(struct bstv_t* bstv)
bstv_clear(bstv_t* bstv)
{
assert(bstv);
ordered_vector_clear(&bstv->vector);
}
/* ------------------------------------------------------------------------- */
void bstv_clear_free(struct bstv_t* bstv)
void bstv_clear_free(bstv_t* bstv)
{
assert(bstv);
ordered_vector_clear_free(&bstv->vector);

550
Source/ThirdParty/ik/src/chain_tree.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,550 @@
#include "ik/bst_vector.h"
#include "ik/effector.h"
#include "ik/log.h"
#include "ik/memory.h"
#include "ik/node.h"
#include "ik/ordered_vector.h"
#include "ik/solver.h"
#include <assert.h>
#include <stdio.h>
enum node_marking_e
{
MARK_NONE = 0,
MARK_SPLIT,
MARK_SECTION
};
/* ------------------------------------------------------------------------- */
void
chain_tree_construct(chain_tree_t* chain_tree)
{
ordered_vector_construct(&chain_tree->islands, sizeof(chain_island_t));
}
/* ------------------------------------------------------------------------- */
void
chain_tree_destruct(chain_tree_t* chain_tree)
{
ORDERED_VECTOR_FOR_EACH(&chain_tree->islands, chain_island_t, island)
chain_island_destruct(island);
ORDERED_VECTOR_END_EACH
ordered_vector_clear_free(&chain_tree->islands);
}
/* ------------------------------------------------------------------------- */
void
chain_island_construct(chain_island_t* chain_tree)
{
chain_construct(&chain_tree->root_chain);
ordered_vector_construct(&chain_tree->transform_dependent_nodes, sizeof(ik_node_t*));
}
/* ------------------------------------------------------------------------- */
void
chain_island_destruct(chain_island_t* chain_tree)
{
ordered_vector_clear_free(&chain_tree->transform_dependent_nodes);
chain_destruct(&chain_tree->root_chain);
}
/* ------------------------------------------------------------------------- */
chain_t*
chain_create(void)
{
chain_t* chain = (chain_t*)MALLOC(sizeof *chain);
if (chain == NULL)
{
ik_log_message("Failed to allocate chain: out of memory");
return NULL;
}
chain_construct(chain);
return chain;
}
/* ------------------------------------------------------------------------- */
void
chain_destroy(chain_t* chain)
{
chain_destruct(chain);
FREE(chain);
}
/* ------------------------------------------------------------------------- */
void
chain_construct(chain_t* chain)
{
ordered_vector_construct(&chain->nodes, sizeof(ik_node_t*));
ordered_vector_construct(&chain->children, sizeof(chain_t));
}
/* ------------------------------------------------------------------------- */
void
chain_clear_free(chain_t* chain)
{
chain_destruct(chain); /* does the same thing as de*/
}
/* ------------------------------------------------------------------------- */
void
chain_destruct(chain_t* chain)
{
ORDERED_VECTOR_FOR_EACH(&chain->children, 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 int
count_chains_recursive(chain_t* chain)
{
int counter = 1;
ORDERED_VECTOR_FOR_EACH(&chain->children, chain_t, child)
counter += count_chains_recursive(child);
ORDERED_VECTOR_END_EACH
return counter;
}
int
count_chains_exclude_root(chain_tree_t* chain_tree)
{
int counter = 1;
ORDERED_VECTOR_FOR_EACH(&chain_tree->islands, chain_island_t, island)
counter += count_chains_recursive(&island->root_chain);
ORDERED_VECTOR_END_EACH
return counter - 1; /* exclude root chain */
}
/* ------------------------------------------------------------------------- */
static int
mark_involved_nodes(ik_solver_t* solver, 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.
*/
ordered_vector_t* effector_nodes_list = &solver->effector_nodes_list;
ORDERED_VECTOR_FOR_EACH(effector_nodes_list, 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;
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.
*
* Additionally, there is a special constraint (IK_CONSTRAINT_STIFF)
* that restricts all rotations of a node. If this constraint is
* imposed on a particular node, mark it differently as well so the
* surrounding nodes can be combined into a single bone properly later.
*
* NOTE: The node->constraint field specifies constraints for
* the *parent* node, not for the current node. However, we will be
* marking the *current* node, not the parent node.
*/
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(chain_tree_t* chain_tree,
chain_t* chain_current,
ik_node_t* node_base,
ik_node_t* node_current,
bstv_t* involved_nodes)
{
int marked_children_count;
ik_node_t* child_node_base = node_base;
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 the current node and set the current chain to NULL so a new
* island is created (this is necessary because all children of this
* node are necessarily part of an isolated tree).
*/
case MARK_SPLIT:
child_node_base = node_current;
chain_current = NULL;
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;
/* falling through on purpose */
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, 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)
{
ik_node_t* node;
if (chain_current == NULL)
{
/*
* If this is the first chain in the island, create and
* initialise it in the chain tree.
*/
chain_island_t* island = ordered_vector_push_emplace(&chain_tree->islands);
if (island == NULL)
{
ik_log_message("Failed to create chain island: Ran out of memory");
return -1;
}
chain_island_construct(island);
child_chain = &island->root_chain;
}
else
{
/*
* This is not the first chain of the island, so create a
* new child chain in the current chain and initialise it.
*/
child_chain = ordered_vector_push_emplace(&chain_current->children);
if (child_chain == NULL)
{
ik_log_message("Failed to create child chain: Ran out of memory");
return -1;
}
chain_construct(child_chain);
}
/*
* Add pointers 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, ik_node_t, child_guid, child_node)
if (recursively_build_chain_tree(
chain_tree,
child_chain,
child_node_base,
child_node,
involved_nodes) < 0)
return -1;
BSTV_END_EACH
return 0;
}
/* ------------------------------------------------------------------------- */
int
rebuild_chain_tree(ik_solver_t* solver)
{
bstv_t involved_nodes;
int involved_nodes_count;
#ifdef IK_DOT_OUTPUT
char buffer[20];
static int file_name_counter = 0;
#endif
/* Clear all existing chain trees */
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
chain_island_destruct(island);
ORDERED_VECTOR_END_EACH
ordered_vector_clear_free(&solver->chain_tree.islands);
/*
* 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);
recursively_build_chain_tree(&solver->chain_tree, NULL, solver->tree, solver->tree, &involved_nodes);
/* Pre-compute offsets for each node in the chain tree in relation to their
* parents */
calculate_segment_lengths(&solver->chain_tree);
/* DEBUG: Save chain tree to DOT */
#ifdef IK_DOT_OUTPUT
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_exclude_root(&solver->chain_tree));
bstv_clear_free(&involved_nodes);
return 0;
mark_involved_nodes_failed : bstv_clear_free(&involved_nodes);
return -1;
}
/* ------------------------------------------------------------------------- */
static void
calculate_segment_lengths_in_island(chain_t* island)
{
int last_idx = ordered_vector_count(&island->nodes) - 1;
while (last_idx-- > 0)
{
ik_node_t* child_node =
*(ik_node_t**)ordered_vector_get_element(&island->nodes, last_idx + 0);
ik_node_t* parent_node =
*(ik_node_t**)ordered_vector_get_element(&island->nodes, last_idx + 1);
vec3_t diff = child_node->original_position;
vec3_sub_vec3(diff.f, parent_node->original_position.f);
child_node->segment_length = vec3_length(diff.f);
}
ORDERED_VECTOR_FOR_EACH(&island->children, chain_t, child)
calculate_segment_lengths_in_island(child);
ORDERED_VECTOR_END_EACH
}
void
calculate_segment_lengths(chain_tree_t* chain_tree)
{
/* TODO: Implement again, take into consideration merged bones */
ORDERED_VECTOR_FOR_EACH(&chain_tree->islands, chain_island_t, island)
calculate_segment_lengths_in_island(&island->root_chain);
ORDERED_VECTOR_END_EACH
}
/* ------------------------------------------------------------------------- */
static void
calculate_global_rotations_of_children(chain_t* chain)
{
int average_count;
quat_t average_rotation = {{0, 0, 0, 0}};
/* Recurse into children chains */
average_count = 0;
ORDERED_VECTOR_FOR_EACH(&chain->children, 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 = (*(ik_node_t**)
ordered_vector_get_element(&child->nodes,
ordered_vector_count(&child->nodes) - 1))->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);
(*(ik_node_t**)ordered_vector_get_element(&chain->nodes, 0))
->rotation = average_rotation;
}
}
/* ------------------------------------------------------------------------- */
static void
calculate_delta_rotation_of_each_segment(chain_t* chain)
{
int node_idx;
/*
* Calculate all of the delta angles of the joints. The resulting delta (!)
* angles will be written to node->rotation
*/
node_idx = ordered_vector_count(&chain->nodes) - 1;
while (node_idx-- > 0)
{
ik_node_t* child_node = *(ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx + 0);
ik_node_t* parent_node = *(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->original_position;
vec3_t segment_solved = child_node->position;
vec3_sub_vec3(segment_original.f, parent_node->original_position.f);
vec3_sub_vec3(segment_solved.f, parent_node->position.f);
vec3_angle(parent_node->rotation.f, segment_original.f, segment_solved.f);
}
}
/* ------------------------------------------------------------------------- */
void
calculate_global_rotations(chain_t* chain)
{
int node_idx;
/*
* 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.
*/
calculate_global_rotations_of_children(chain);
calculate_delta_rotation_of_each_segment(chain);
/*
* At this point, all nodes have calculated their delta angles *except* for
* the end effector nodes, which remain untouched. It makes sense to copy
* the delta rotation of the parent node into the effector node by default.
*/
node_idx = ordered_vector_count(&chain->nodes);
if (node_idx > 1)
{
ik_node_t* effector_node = *(ik_node_t**)ordered_vector_get_element(&chain->nodes, 0);
ik_node_t* parent_node = *(ik_node_t**)ordered_vector_get_element(&chain->nodes, 1);
effector_node->rotation.q = parent_node->rotation.q;
}
/*
* Finally, apply initial global rotations to calculated delta rotations to
* obtain the solved global rotations.
*/
ORDERED_VECTOR_FOR_EACH(&chain->nodes, ik_node_t*, pnode)
ik_node_t* node = *pnode;
quat_mul_quat(node->rotation.f, node->initial_rotation.f);
ORDERED_VECTOR_END_EACH
}
/* ------------------------------------------------------------------------- */
#ifdef IK_DOT_OUTPUT
static void
dump_chain(ik_chain_t* chain, FILE* fp)
{
int last_idx = ordered_vector_count(&chain->nodes) - 1;
if (last_idx > 0)
{
fprintf(fp, " %d [shape=record];\n",
(*(ik_node_t**)ordered_vector_get_element(&chain->nodes, 0))->guid);
fprintf(fp, " %d [shape=record];\n",
(*(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",
(*(ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 0))->guid,
(*(ik_node_t**)ordered_vector_get_element(&chain->nodes, last_idx + 1))->guid);
}
ORDERED_VECTOR_FOR_EACH(&chain->children, ik_chain_t, child)
dump_chain(child, fp);
ORDERED_VECTOR_END_EACH
}
static void
dump_node(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, ik_node_t, guid, child)
fprintf(fp, " %d -- %d;\n", node->guid, guid);
dump_node(child, fp);
BSTV_END_EACH
}
void
dump_to_dot(ik_node_t* node, ik_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

94
Source/ThirdParty/ik/src/constraint.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,94 @@
#include "ik/constraint.h"
#include "ik/log.h"
#include "ik/memory.h"
#include "ik/node.h"
#include <string.h>
#include <assert.h>
static void
apply_stiff(ik_node_t* node);
static void
apply_hinge(ik_node_t* node);
static void
apply_cone(ik_node_t* node);
/* ------------------------------------------------------------------------- */
ik_constraint_t*
ik_constraint_create(ik_constraint_type_e constraint_type)
{
ik_constraint_t* constraint = (ik_constraint_t*)MALLOC(sizeof *constraint);
if (constraint == NULL)
{
ik_log_message("Failed to allocate constraint: Out of memory");
return NULL;
}
memset(constraint, 0, sizeof *constraint);
ik_constraint_set(constraint, constraint_type);
return constraint;
}
/* ------------------------------------------------------------------------- */
void
ik_constraint_set(ik_constraint_t* constraint, ik_constraint_type_e constraint_type)
{
switch (constraint_type)
{
case IK_CONSTRAINT_STIFF:
constraint->apply = apply_stiff;
break;
case IK_CONSTRAINT_HINGE:
constraint->apply = apply_hinge;
break;
case IK_CONSTRAINT_CONE:
constraint->apply = apply_cone;
break;
}
constraint->type = constraint_type;
}
/* ------------------------------------------------------------------------- */
void
ik_constraint_set_custom(ik_constraint_t* constraint, ik_constraint_apply_func callback)
{
constraint->apply = callback;
}
/* ------------------------------------------------------------------------- */
void
ik_constraint_destroy(ik_constraint_t* constraint)
{
FREE(constraint);
}
/* ------------------------------------------------------------------------- */
/* Constraint implementations (static) */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
static void
apply_stiff(ik_node_t* node)
{
/*
* The stiff constraint should never actually be reached, because joints
* that have a stiff constraint will be excluded from the chain tree
* entirely. This function exists solely to debug the chain tree.
*/
assert(1);
}
/* ------------------------------------------------------------------------- */
static void
apply_hinge(ik_node_t* node)
{
}
/* ------------------------------------------------------------------------- */
static void
apply_cone(ik_node_t* node)
{
}

20
Source/ThirdParty/ik/src/effector.c поставляемый
Просмотреть файл

@ -4,11 +4,11 @@
#include <string.h>
/* ------------------------------------------------------------------------- */
struct ik_effector_t*
ik_effector_t*
ik_effector_create(void)
{
struct ik_effector_t* effector = (struct ik_effector_t*)MALLOC(sizeof *effector);
if(effector == NULL)
ik_effector_t* effector = (ik_effector_t*)MALLOC(sizeof *effector);
if (effector == NULL)
return NULL;
ik_effector_construct(effector);
@ -17,7 +17,7 @@ ik_effector_create(void)
/* ------------------------------------------------------------------------- */
void
ik_effector_construct(struct ik_effector_t* effector)
ik_effector_construct(ik_effector_t* effector)
{
memset(effector, 0, sizeof *effector);
quat_set_identity(effector->target_rotation.f);
@ -28,17 +28,7 @@ ik_effector_construct(struct ik_effector_t* effector)
/* ------------------------------------------------------------------------- */
void
ik_effector_destroy(struct ik_effector_t* effector)
ik_effector_destroy(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;
}

30
Source/ThirdParty/ik/src/log.c поставляемый
Просмотреть файл

@ -5,11 +5,13 @@
#include <string.h>
#include <stdio.h>
static struct log_t
typedef struct log_t
{
struct ordered_vector_t listeners; /* list of ik_log_cb_func */
struct ordered_vector_t message_buffer;
}* g_log = NULL;
ordered_vector_t listeners; /* list of ik_log_cb_func */
ordered_vector_t message_buffer;
} log_t;
static log_t* g_log = NULL;
static void log_stdout_callback(const char* msg)
{
@ -20,17 +22,17 @@ static void log_stdout_callback(const char* msg)
void
ik_log_init(enum ik_log_e options)
{
if(g_log != NULL)
if (g_log != NULL)
return;
g_log = (struct log_t*)MALLOC(sizeof *g_log);
if(g_log == NULL)
g_log = (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)
if (options == IK_LOG_STDOUT)
ik_log_register_listener(log_stdout_callback);
}
@ -38,7 +40,7 @@ ik_log_init(enum ik_log_e options)
void
ik_log_deinit(void)
{
if(g_log == NULL)
if (g_log == NULL)
return;
ordered_vector_clear_free(&g_log->message_buffer);
@ -51,7 +53,7 @@ ik_log_deinit(void)
void
ik_log_register_listener(ik_log_cb_func callback)
{
if(g_log != NULL)
if (g_log != NULL)
ordered_vector_push(&g_log->listeners, &callback);
}
@ -59,11 +61,11 @@ ik_log_register_listener(ik_log_cb_func callback)
void
ik_log_unregister_listener(ik_log_cb_func callback)
{
if(g_log == NULL)
if (g_log == NULL)
return;
ORDERED_VECTOR_FOR_EACH(&g_log->listeners, ik_log_cb_func, registered_callback)
if(callback == *registered_callback)
if (callback == *registered_callback)
{
ordered_vector_erase_element(&g_log->listeners, registered_callback);
return;
@ -78,14 +80,14 @@ ik_log_message(const char* fmt, ...)
va_list va;
uintptr_t msg_len;
if(g_log == NULL)
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)
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);

80
Source/ThirdParty/ik/src/memory.c поставляемый
Просмотреть файл

@ -8,21 +8,21 @@
#define BACKTRACE_OMIT_COUNT 2
#if IK_MEMORY_DEBUGGING == ON
#ifdef IK_MEMORY_DEBUGGING
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;
static bstv_t report;
struct report_info_t
typedef struct report_info_t
{
uintptr_t location;
uintptr_t size;
# if IK_MEMORY_BACKTRACE == ON
# ifdef IK_MEMORY_BACKTRACE
int backtrace_size;
char** backtrace;
# endif
};
} report_info_t;
/* ------------------------------------------------------------------------- */
void
@ -47,14 +47,14 @@ void*
malloc_wrapper(intptr_t size)
{
void* p = NULL;
struct report_info_t* info = NULL;
report_info_t* info = NULL;
/* breaking from this will clean up and return NULL */
for(;;)
for (;;)
{
/* allocate */
p = malloc(size);
if(p)
if (p)
++g_allocations;
else
break;
@ -63,11 +63,11 @@ malloc_wrapper(intptr_t size)
* 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)
if (!g_ignore_bstv_malloc)
{
g_ignore_bstv_malloc = 1;
info = (struct report_info_t*)malloc(sizeof(struct report_info_t));
if(!info)
info = (report_info_t*)malloc(sizeof(report_info_t));
if (!info)
{
fprintf(stderr, "[memory] ERROR: malloc() for report_info_t failed"
" -- not enough memory.\n");
@ -79,15 +79,15 @@ malloc_wrapper(intptr_t size)
info->location = (uintptr_t)p;
info->size = size;
/* if enabled, generate a backtrace so we know where memory leaks
/* 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)))
# ifdef IK_MEMORY_BACKTRACE
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)
if (bstv_insert(&report, (uintptr_t)p, info) == 1)
{
fprintf(stderr,
"[memory] WARNING: Hash collision occurred when inserting\n"
@ -97,14 +97,14 @@ malloc_wrapper(intptr_t size)
"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
# ifdef IK_MEMORY_BACKTRACE
{
char** bt;
int bt_size, i;
if((bt = get_backtrace(&bt_size)))
if ((bt = get_backtrace(&bt_size)))
{
printf(" backtrace to where malloc() was called:\n");
for(i = 0; i < bt_size; ++i)
for (i = 0; i < bt_size; ++i)
printf(" %s\n", bt[i]);
printf(" -----------------------------------------\n");
free(bt);
@ -122,16 +122,16 @@ malloc_wrapper(intptr_t size)
}
/* failure */
if(p)
if (p)
{
free(p);
--g_allocations;
}
if(info)
if (info)
{
# if IK_MEMORY_BACKTRACE == ON
if(info->backtrace)
# ifdef IK_MEMORY_BACKTRACE
if (info->backtrace)
free(info->backtrace);
# endif
free(info);
@ -145,13 +145,13 @@ void
free_wrapper(void* ptr)
{
/* find matching allocation and remove from bstv */
if(!g_ignore_bstv_malloc)
if (!g_ignore_bstv_malloc)
{
struct report_info_t* info = (struct report_info_t*)bstv_erase(&report, (uintptr_t)ptr);
if(info)
report_info_t* info = (report_info_t*)bstv_erase(&report, (uintptr_t)ptr);
if (info)
{
# if IK_MEMORY_BACKTRACE == ON
if(info->backtrace)
# ifdef IK_MEMORY_BACKTRACE
if (info->backtrace)
free(info->backtrace);
else
fprintf(stderr, "[memory] WARNING: free(): Allocation didn't "
@ -161,17 +161,17 @@ free_wrapper(void* ptr)
}
else
{
# if IK_MEMORY_BACKTRACE == ON
# ifdef IK_MEMORY_BACKTRACE
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)))
# ifdef IK_MEMORY_BACKTRACE
if ((bt = get_backtrace(&bt_size)))
{
fprintf(stderr, " backtrace to where free() was called:\n");
for(i = 0; i < bt_size; ++i)
for (i = 0; i < bt_size; ++i)
fprintf(stderr, " %s\n", bt[i]);
fprintf(stderr, " -----------------------------------------\n");
free(bt);
@ -182,7 +182,7 @@ free_wrapper(void* ptr)
}
}
if(ptr)
if (ptr)
{
++d_deg_allocations;
free(ptr);
@ -204,18 +204,18 @@ ik_memory_deinit(void)
printf("=========================================\n");
/* report details on any g_allocations that were not de-allocated */
if(report.vector.count != 0)
if (report.vector.count != 0)
{
BSTV_FOR_EACH(&report, struct report_info_t, key, info)
BSTV_FOR_EACH(&report, 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
# ifdef IK_MEMORY_BACKTRACE
printf(" Backtrace to where malloc() was called:\n");
{
intptr_t i;
for(i = BACKTRACE_OMIT_COUNT; i < info->backtrace_size; ++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 */
@ -257,7 +257,7 @@ mutated_string_and_hex_dump(void* data, intptr_t length_in_bytes)
intptr_t i;
/* allocate and copy data into new buffer */
if(!(dump = malloc(length_in_bytes + 1)))
if (!(dump = malloc(length_in_bytes + 1)))
{
fprintf(stderr, "[memory] WARNING: Failed to malloc() space for dump\n");
return;
@ -266,14 +266,14 @@ mutated_string_and_hex_dump(void* data, intptr_t 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')
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)
for (i = 0; i != length_in_bytes; ++i)
printf(" %02x", (unsigned char)dump[i]);
printf("\n");

147
Source/ThirdParty/ik/src/node.c поставляемый
Просмотреть файл

@ -1,3 +1,4 @@
#include "ik/constraint.h"
#include "ik/effector.h"
#include "ik/log.h"
#include "ik/memory.h"
@ -7,11 +8,11 @@
#include <stdio.h>
/* ------------------------------------------------------------------------- */
struct ik_node_t*
ik_node_t*
ik_node_create(uint32_t guid)
{
struct ik_node_t* node = (struct ik_node_t*)MALLOC(sizeof *node);
if(node == NULL)
ik_node_t* node = (ik_node_t*)MALLOC(sizeof *node);
if (node == NULL)
return NULL;
ik_node_construct(node, guid);
@ -20,39 +21,43 @@ ik_node_create(uint32_t guid)
/* ------------------------------------------------------------------------- */
void
ik_node_construct(struct ik_node_t* node, uint32_t guid)
ik_node_construct(ik_node_t* node, uint32_t guid)
{
memset(node, 0, sizeof *node);
bstv_construct(&node->children);
quat_set_identity(node->initial_rotation.f);
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);
ik_node_destroy_recursive(ik_node_t* node);
static void
ik_node_destruct_recursive(struct ik_node_t* node)
ik_node_destruct_recursive(ik_node_t* node)
{
BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
ik_node_destroy_recursive(child);
BSTV_END_EACH
if(node->effector)
if (node->effector)
ik_effector_destroy(node->effector);
if (node->constraint)
ik_constraint_destroy(node->constraint);
bstv_clear_free(&node->children);
}
void
ik_node_destruct(struct ik_node_t* node)
ik_node_destruct(ik_node_t* node)
{
BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
ik_node_destroy_recursive(child);
BSTV_END_EACH
if(node->effector)
if (node->effector)
ik_effector_destroy(node->effector);
if (node->constraint)
ik_constraint_destroy(node->constraint);
ik_node_unlink(node);
bstv_clear_free(&node->children);
@ -60,13 +65,13 @@ ik_node_destruct(struct ik_node_t* node)
/* ------------------------------------------------------------------------- */
static void
ik_node_destroy_recursive(struct ik_node_t* node)
ik_node_destroy_recursive(ik_node_t* node)
{
ik_node_destruct_recursive(node);
FREE(node);
}
void
ik_node_destroy(struct ik_node_t* node)
ik_node_destroy(ik_node_t* node)
{
ik_node_destruct(node);
FREE(node);
@ -74,7 +79,7 @@ ik_node_destroy(struct ik_node_t* node)
/* ------------------------------------------------------------------------- */
void
ik_node_add_child(struct ik_node_t* node, struct ik_node_t* child)
ik_node_add_child(ik_node_t* node, ik_node_t* child)
{
child->parent = node;
bstv_insert(&node->children, child->guid, child);
@ -82,9 +87,9 @@ ik_node_add_child(struct ik_node_t* node, struct ik_node_t* child)
/* ------------------------------------------------------------------------- */
void
ik_node_unlink(struct ik_node_t* node)
ik_node_unlink(ik_node_t* node)
{
if(node->parent == NULL)
if (node->parent == NULL)
return;
bstv_erase(&node->parent->children, node->guid);
@ -92,19 +97,19 @@ ik_node_unlink(struct ik_node_t* node)
}
/* ------------------------------------------------------------------------- */
struct ik_node_t*
ik_node_find_child(struct ik_node_t* node, uint32_t guid)
ik_node_t*
ik_node_find_child(ik_node_t* node, uint32_t guid)
{
struct ik_node_t* found = bstv_find(&node->children, guid);
if(found != NULL)
ik_node_t* found = bstv_find(&node->children, guid);
if (found != NULL)
return found;
if(node->guid == guid)
if (node->guid == guid)
return node;
BSTV_FOR_EACH(&node->children, struct ik_node_t, child_guid, child)
BSTV_FOR_EACH(&node->children, ik_node_t, child_guid, child)
found = ik_node_find_child(child, guid);
if(found != NULL)
if (found != NULL)
return found;
BSTV_END_EACH
@ -113,29 +118,52 @@ ik_node_find_child(struct ik_node_t* node, uint32_t guid)
/* ------------------------------------------------------------------------- */
void
ik_node_attach_effector(struct ik_node_t* node, struct ik_effector_t* effector)
ik_node_attach_effector(ik_node_t* node, ik_effector_t* effector)
{
if (node->effector != NULL)
ik_effector_destroy(node->effector);
node->effector = effector;
}
/* ------------------------------------------------------------------------- */
void
ik_node_destroy_effector(struct ik_node_t* node)
ik_node_destroy_effector(ik_node_t* node)
{
if(node->effector == NULL)
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)
void
ik_node_attach_constraint(ik_node_t* node, ik_constraint_t* constraint)
{
if(node->effector != NULL)
if (node->constraint != NULL)
ik_constraint_destroy(node->constraint);
node->constraint = constraint;
}
/* ------------------------------------------------------------------------- */
void
ik_node_destroy_constraint(ik_node_t* node)
{
if (node->constraint == NULL)
return;
ik_constraint_destroy(node->constraint);
node->constraint = NULL;
}
/* ------------------------------------------------------------------------- */
static void
recursively_dump_dot(FILE* fp, 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)
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
fprintf(fp, " %d -- %d;\n", node->guid, guid);
recursively_dump_dot(fp, child);
BSTV_END_EACH
@ -143,10 +171,10 @@ recursively_dump_dot(FILE* fp, struct ik_node_t* node)
/* ------------------------------------------------------------------------- */
void
ik_node_dump_to_dot(struct ik_node_t* node, const char* file_name)
ik_node_dump_to_dot(ik_node_t* node, const char* file_name)
{
FILE* fp = fopen(file_name, "w");
if(fp == NULL)
if (fp == NULL)
{
ik_log_message("Failed to open file %s", file_name);
return;
@ -158,3 +186,56 @@ ik_node_dump_to_dot(struct ik_node_t* node, const char* file_name)
fclose(fp);
}
/* ------------------------------------------------------------------------- */
static void
local_to_global_recursive(ik_node_t* node, vec3_t acc_pos, quat_t acc_rot)
{
vec3_t position;
quat_t rotation;
quat_rotate_vec(node->position.f, acc_rot.f);
position = node->position;
vec3_add_vec3(node->position.f, acc_pos.f);
vec3_add_vec3(acc_pos.f, position.f);
rotation = node->rotation;
quat_mul_quat(node->rotation.f, acc_rot.f);
quat_mul_quat(acc_rot.f, rotation.f);
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
local_to_global_recursive(child, acc_pos, acc_rot);
BSTV_END_EACH
}
void
ik_node_local_to_global(ik_node_t* node)
{
vec3_t acc_pos = {{0, 0, 0}};
quat_t acc_rot = {{0, 0, 0, 1}};
local_to_global_recursive(node, acc_pos, acc_rot);
}
/* ------------------------------------------------------------------------- */
static void
global_to_local_recursive(ik_node_t* node, vec3_t acc_pos, quat_t acc_rot)
{
quat_t inv_rotation = acc_rot;
quat_conj(inv_rotation.f);
quat_mul_quat(node->rotation.f, inv_rotation.f);
quat_mul_quat(acc_rot.f, node->rotation.f);
vec3_sub_vec3(node->position.f, acc_pos.f);
vec3_add_vec3(acc_pos.f, node->position.f);
quat_rotate_vec(node->position.f, inv_rotation.f);
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
global_to_local_recursive(child, acc_pos, acc_rot);
BSTV_END_EACH
}
void
ik_node_global_to_local(ik_node_t* node)
{
vec3_t acc_pos = {{0, 0, 0}};
quat_t acc_rot = {{0, 0, 0, 1}};
global_to_local_recursive(node, acc_pos, acc_rot);
}

94
Source/ThirdParty/ik/src/ordered_vector.c поставляемый
Просмотреть файл

@ -13,7 +13,7 @@
*
* 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
* @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.
@ -21,18 +21,18 @@
* @note No checks are performed to make sure the target size is large enough.
*/
static int
ordered_vector_expand(struct ordered_vector_t *vector,
ordered_vector_expand(ordered_vector_t *vector,
uintptr_t insertion_index,
uint32_t target_size);
/* ----------------------------------------------------------------------------
* Exported functions
* ------------------------------------------------------------------------- */
struct ordered_vector_t*
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))))
ordered_vector_t* vector;
if (!(vector = (ordered_vector_t*)MALLOC(sizeof(ordered_vector_t))))
return NULL;
ordered_vector_construct(vector, element_size);
return vector;
@ -40,16 +40,16 @@ ordered_vector_create(const uint32_t element_size)
/* ------------------------------------------------------------------------- */
void
ordered_vector_construct(struct ordered_vector_t* vector, const uint32_t element_size)
ordered_vector_construct(ordered_vector_t* vector, const uint32_t element_size)
{
assert(vector);
memset(vector, 0, sizeof(struct ordered_vector_t));
memset(vector, 0, sizeof(ordered_vector_t));
vector->element_size = element_size;
}
/* ------------------------------------------------------------------------- */
void
ordered_vector_destroy(struct ordered_vector_t* vector)
ordered_vector_destroy(ordered_vector_t* vector)
{
assert(vector);
ordered_vector_clear_free(vector);
@ -58,7 +58,7 @@ ordered_vector_destroy(struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
void
ordered_vector_clear(struct ordered_vector_t* vector)
ordered_vector_clear(ordered_vector_t* vector)
{
assert(vector);
/*
@ -70,11 +70,11 @@ ordered_vector_clear(struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
void
ordered_vector_clear_free(struct ordered_vector_t* vector)
ordered_vector_clear_free(ordered_vector_t* vector)
{
assert(vector);
if(vector->data)
if (vector->data)
FREE(vector->data);
vector->data = NULL;
@ -84,13 +84,13 @@ ordered_vector_clear_free(struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
int
ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size)
ordered_vector_resize(ordered_vector_t* vector, uint32_t size)
{
int result = 0;
assert(vector);
if(vector->count < size)
if (vector->count < size)
result = ordered_vector_expand(vector, -1, size);
vector->count = size;
@ -99,14 +99,14 @@ ordered_vector_resize(struct ordered_vector_t* vector, uint32_t size)
/* ------------------------------------------------------------------------- */
void*
ordered_vector_push_emplace(struct ordered_vector_t* vector)
ordered_vector_push_emplace(ordered_vector_t* vector)
{
void* data;
assert(vector);
if(vector->count == vector->capacity)
if(ordered_vector_expand(vector, -1, 0) < 0)
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);
@ -115,7 +115,7 @@ ordered_vector_push_emplace(struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
int
ordered_vector_push(struct ordered_vector_t* vector, void* data)
ordered_vector_push(ordered_vector_t* vector, void* data)
{
void* emplaced;
@ -123,7 +123,7 @@ ordered_vector_push(struct ordered_vector_t* vector, void* data)
assert(data);
emplaced = ordered_vector_push_emplace(vector);
if(!emplaced)
if (!emplaced)
return -1;
memcpy(emplaced, data, vector->element_size);
return 0;
@ -131,18 +131,18 @@ ordered_vector_push(struct ordered_vector_t* vector, void* data)
/* ------------------------------------------------------------------------- */
int
ordered_vector_push_vector(struct ordered_vector_t* vector, struct ordered_vector_t* source_vector)
ordered_vector_push_vector(ordered_vector_t* vector, ordered_vector_t* source_vector)
{
assert(vector);
assert(source_vector);
/* make sure element sizes are equal */
if(vector->element_size != source_vector->element_size)
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)
if (vector->count + source_vector->count > vector->capacity)
if (ordered_vector_expand(vector, -1, vector->count + source_vector->count) < 0)
return -1;
/* copy data */
@ -156,11 +156,11 @@ ordered_vector_push_vector(struct ordered_vector_t* vector, struct ordered_vecto
/* ------------------------------------------------------------------------- */
void*
ordered_vector_pop(struct ordered_vector_t* vector)
ordered_vector_pop(ordered_vector_t* vector)
{
assert(vector);
if(!vector->count)
if (!vector->count)
return NULL;
--(vector->count);
@ -169,11 +169,11 @@ ordered_vector_pop(struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
void*
ordered_vector_back(const struct ordered_vector_t* vector)
ordered_vector_back(const ordered_vector_t* vector)
{
assert(vector);
if(!vector->count)
if (!vector->count)
return NULL;
return vector->data + (vector->element_size * (vector->count - 1));
@ -181,7 +181,7 @@ ordered_vector_back(const struct ordered_vector_t* vector)
/* ------------------------------------------------------------------------- */
void*
ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index)
ordered_vector_insert_emplace(ordered_vector_t* vector, uint32_t index)
{
uint32_t offset;
@ -192,13 +192,13 @@ ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index)
* because it's possible the user will want to insert at the very end of
* the vector.
*/
if(index > vector->count)
if (index > vector->count)
return NULL;
/* re-allocate? */
if(vector->count == vector->capacity)
if (vector->count == vector->capacity)
{
if(ordered_vector_expand(vector, index, 0) < 0)
if (ordered_vector_expand(vector, index, 0) < 0)
return NULL;
}
else
@ -218,7 +218,7 @@ ordered_vector_insert_emplace(struct ordered_vector_t* vector, uint32_t index)
/* ------------------------------------------------------------------------- */
int
ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* data)
ordered_vector_insert(ordered_vector_t* vector, uint32_t index, void* data)
{
void* emplaced;
@ -226,7 +226,7 @@ ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* dat
assert(data);
emplaced = ordered_vector_insert_emplace(vector, index);
if(!emplaced)
if (!emplaced)
return -1;
memcpy(emplaced, data, vector->element_size);
return 0;
@ -234,14 +234,14 @@ ordered_vector_insert(struct ordered_vector_t* vector, uint32_t index, void* dat
/* ------------------------------------------------------------------------- */
void
ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index)
ordered_vector_erase_index(ordered_vector_t* vector, uint32_t index)
{
assert(vector);
if(index >= vector->count)
if (index >= vector->count)
return;
if(index == vector->count - 1)
if (index == vector->count - 1)
/* last element doesn't require memory shifting, just pop it */
ordered_vector_pop(vector);
else
@ -258,7 +258,7 @@ ordered_vector_erase_index(struct ordered_vector_t* vector, uint32_t index)
/* ------------------------------------------------------------------------- */
void
ordered_vector_erase_element(struct ordered_vector_t* vector, void* element)
ordered_vector_erase_element(ordered_vector_t* vector, void* element)
{
uintptr_t last_element;
@ -268,7 +268,7 @@ ordered_vector_erase_element(struct ordered_vector_t* vector, void* element)
assert((uintptr_t)element >= (uintptr_t)vector->data);
assert((uintptr_t)element <= (uintptr_t)last_element);
if(element != (void*)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 */
@ -279,11 +279,11 @@ ordered_vector_erase_element(struct ordered_vector_t* vector, void* element)
/* ------------------------------------------------------------------------- */
void*
ordered_vector_get_element(struct ordered_vector_t* vector, uint32_t index)
ordered_vector_get_element(ordered_vector_t* vector, uint32_t index)
{
assert(vector);
if(index >= vector->count)
if (index >= vector->count)
return NULL;
return vector->data + (vector->element_size * index);
}
@ -292,7 +292,7 @@ ordered_vector_get_element(struct ordered_vector_t* vector, uint32_t index)
* Static functions
* ------------------------------------------------------------------------- */
static int
ordered_vector_expand(struct ordered_vector_t *vector,
ordered_vector_expand(ordered_vector_t *vector,
uintptr_t insertion_index,
uint32_t target_count)
{
@ -300,8 +300,8 @@ ordered_vector_expand(struct ordered_vector_t *vector,
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)
/* 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;
@ -310,11 +310,11 @@ ordered_vector_expand(struct ordered_vector_t *vector,
* If vector hasn't allocated anything yet, just allocated the requested
* amount of memory and return immediately.
*/
if(!vector->data)
if (!vector->data)
{
new_count = (new_count == 0 ? 2 : new_count);
vector->data = MALLOC(new_count * vector->element_size);
if(!vector->data)
if (!vector->data)
return -1;
vector->capacity = new_count;
return 0;
@ -323,11 +323,11 @@ ordered_vector_expand(struct ordered_vector_t *vector,
/* prepare for reallocating data */
old_data = vector->data;
new_data = (DATA_POINTER_TYPE*)MALLOC(new_count * vector->element_size);
if(!new_data)
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)
/* 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 */

42
Source/ThirdParty/ik/src/quat.c поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
/* ------------------------------------------------------------------------- */
void
quat_set_identity(ik_real* q)
quat_set_identity(ik_real* IK_RESTRICT q)
{
memset(q, 0, sizeof(ik_real) * 3);
q[3] = 1;
@ -12,7 +12,7 @@ quat_set_identity(ik_real* q)
/* ------------------------------------------------------------------------- */
void
quat_add_quat(ik_real* q1, const ik_real* q2)
quat_add_quat(ik_real* IK_RESTRICT q1, const ik_real* IK_RESTRICT q2)
{
q1[0] += q2[0];
q1[1] += q2[1];
@ -22,14 +22,14 @@ quat_add_quat(ik_real* q1, const ik_real* q2)
/* ------------------------------------------------------------------------- */
ik_real
quat_mag(const ik_real* q)
quat_mag(const ik_real* IK_RESTRICT 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)
quat_conj(ik_real* IK_RESTRICT q)
{
q[0] = -q[0];
q[1] = -q[1];
@ -38,7 +38,7 @@ quat_conj(ik_real* q)
/* ------------------------------------------------------------------------- */
void
quat_invert_sign(ik_real* q)
quat_invert_sign(ik_real* IK_RESTRICT q)
{
q[0] = -q[0];
q[1] = -q[1];
@ -49,10 +49,10 @@ quat_invert_sign(ik_real* q)
/* ------------------------------------------------------------------------- */
void
quat_normalise(ik_real* q)
quat_normalise(ik_real* IK_RESTRICT q)
{
ik_real mag = quat_mag(q);
if(mag != 0.0)
if (mag != 0.0)
mag = 1.0 / mag;
q[0] *= mag;
q[1] *= mag;
@ -61,8 +61,8 @@ quat_normalise(ik_real* q)
}
/* ------------------------------------------------------------------------- */
void
quat_mul_quat(ik_real* q1, const ik_real* q2)
static void
mul_quat_no_normalise(ik_real* IK_RESTRICT q1, const ik_real* IK_RESTRICT q2)
{
ik_real v1[3];
ik_real v2[3];
@ -75,13 +75,17 @@ quat_mul_quat(ik_real* q1, const ik_real* q2)
vec3_cross(q1, q2);
vec3_add_vec3(q1, v1);
vec3_add_vec3(q1, v2);
}
void
quat_mul_quat(ik_real* IK_RESTRICT q1, const ik_real* IK_RESTRICT q2)
{
mul_quat_no_normalise(q1, q2);
quat_normalise(q1);
}
/* ------------------------------------------------------------------------- */
void
quat_mul_scalar(ik_real* q, ik_real scalar)
quat_mul_scalar(ik_real* IK_RESTRICT q, ik_real scalar)
{
q[0] *= scalar;
q[1] *= scalar;
@ -91,9 +95,9 @@ quat_mul_scalar(ik_real* q, ik_real scalar)
/* ------------------------------------------------------------------------- */
void
quat_div_scalar(ik_real* q, ik_real scalar)
quat_div_scalar(ik_real* IK_RESTRICT q, ik_real scalar)
{
if(scalar == 0.0)
if (scalar == 0.0)
quat_set_identity(q);
else
{
@ -107,7 +111,7 @@ quat_div_scalar(ik_real* q, ik_real scalar)
/* ------------------------------------------------------------------------- */
ik_real
quat_dot(ik_real* q1, const ik_real* q2)
quat_dot(ik_real* IK_RESTRICT q1, const ik_real* IK_RESTRICT q2)
{
return q1[0] * q2[0] +
q1[1] * q2[1] +
@ -117,7 +121,7 @@ quat_dot(ik_real* q1, const ik_real* q2)
/* ------------------------------------------------------------------------- */
void
quat_rotate_vec(ik_real* v, const ik_real* q)
quat_rotate_vec(ik_real* IK_RESTRICT v, const ik_real* IK_RESTRICT q)
{
/* P' = RPR' */
quat_t result;
@ -131,17 +135,17 @@ quat_rotate_vec(ik_real* v, const ik_real* q)
quat_conj(conj.f);
result = *(quat_t*)q;
quat_mul_quat(result.f, point.f);
quat_mul_quat(result.f, conj.f);
mul_quat_no_normalise(result.f, point.f);
mul_quat_no_normalise(result.f, conj.f);
memcpy(v, result.f, sizeof(ik_real) * 3);
}
/* ------------------------------------------------------------------------- */
void
quat_normalise_sign(ik_real* q1)
quat_normalise_sign(ik_real* IK_RESTRICT q1)
{
quat_t unit = {{0, 0, 0, 1}};
ik_real dot = quat_dot(q1, unit.f);
if(dot < 0.0)
if (dot < 0.0)
quat_invert_sign(q1);
}

193
Source/ThirdParty/ik/src/solver.c поставляемый
Просмотреть файл

@ -1,66 +1,128 @@
#include "ik/chain_tree.h"
#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_2bone.h"
#include "ik/solver_1bone.h"
#include "ik/solver_MSD.h"
#include "ik/solver_jacobian_inverse.h"
#include "ik/solver_jacobian_transpose.h"
#include <string.h>
static int
recursive_get_all_effector_nodes(struct ik_node_t* node, struct ordered_vector_t* effector_nodes_list);
recursively_get_all_effector_nodes(ik_node_t* node, ordered_vector_t* effector_nodes_list);
/* ------------------------------------------------------------------------- */
struct ik_solver_t*
ik_solver_t*
ik_solver_create(enum solver_algorithm_e algorithm)
{
struct ik_solver_t* solver = NULL;
uintptr_t solver_size = 0;
int (*solver_construct)(ik_solver_t*) = NULL;
ik_solver_t* solver = NULL;
switch(algorithm)
/*
* Determine the correct size and confunction, depending on the
* selected algorithm.
*/
switch (algorithm)
{
case SOLVER_FABRIK:
solver = (struct ik_solver_t*)solver_FABRIK_create();
solver_size = sizeof(fabrik_t);
solver_construct = solver_FABRIK_construct;
break;
/*case SOLVER_JACOBIAN_INVERSE:
case SOLVER_TWO_BONE:
solver_size = sizeof(two_bone_t);
solver_construct = solver_2bone_construct;
break;
case SOLVER_ONE_BONE:
solver_size = sizeof(one_bone_t);
solver_construct = solver_1bone_construct;
break;
case SOLVER_MSD:
solver_size = sizeof(msd_t);
solver_construct = solver_MSD_construct;
break;
/*
case SOLVER_JACOBIAN_INVERSE:
case SOLVER_JACOBIAN_TRANSPOSE:
break;*/
}
if(solver == NULL)
return NULL;
if (solver_construct == NULL)
{
ik_log_message("Unknown algorithm \"%d\" was specified", algorithm);
goto alloc_solver_failed;
}
ordered_vector_construct(&solver->effector_nodes_list, sizeof(struct ik_node_t*));
/*
* Allocate the solver, initialise to 0 and initialise the base fields
* before calling the construct() callback for the specific solver.
*/
solver = (ik_solver_t*)MALLOC(solver_size);
if (solver == NULL)
{
ik_log_message("Failed to allocate solver: ran out of memory");
goto alloc_solver_failed;
}
memset(solver, 0, solver_size);
ordered_vector_construct(&solver->effector_nodes_list, sizeof(ik_node_t*));
chain_tree_construct(&solver->chain_tree);
/* Now call derived construction */
if (solver_construct(solver) < 0)
goto construct_derived_solver_failed;
/* Derived destruct callback must be set */
if (solver->destruct == NULL)
{
ik_log_message("Derived solvers MUST implement the destruct() callback");
goto derived_didnt_implement_destruct;
}
return solver;
derived_didnt_implement_destruct :
construct_derived_solver_failed : FREE(solver);
alloc_solver_failed : return NULL;
}
/* ------------------------------------------------------------------------- */
void
ik_solver_destroy(struct ik_solver_t* solver)
ik_solver_destroy(ik_solver_t* solver)
{
if(solver->tree)
solver->destruct(solver);
if (solver->tree)
ik_node_destroy(solver->tree);
chain_tree_destruct(&solver->chain_tree);
ordered_vector_clear_free(&solver->effector_nodes_list);
solver->destroy(solver);
FREE(solver);
}
/* ------------------------------------------------------------------------- */
void
ik_solver_set_tree(struct ik_solver_t* solver, struct ik_node_t* root)
ik_solver_set_tree(ik_solver_t* solver, 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)
ik_node_t*
ik_solver_unlink_tree(ik_solver_t* solver)
{
struct ik_node_t* root = solver->tree;
if(root == NULL)
ik_node_t* root = solver->tree;
if (root == NULL)
return NULL;
solver->tree = NULL;
@ -75,20 +137,20 @@ ik_solver_unlink_tree(struct ik_solver_t* solver)
/* ------------------------------------------------------------------------- */
void
ik_solver_destroy_tree(struct ik_solver_t* solver)
ik_solver_destroy_tree(ik_solver_t* solver)
{
struct ik_node_t* root;
if((root = ik_solver_unlink_tree(solver)) == NULL)
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)
ik_solver_rebuild_data(ik_solver_t* solver)
{
/* If the solver has no tree, then there's nothing to do */
if(solver->tree == NULL)
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;
@ -100,80 +162,99 @@ ik_solver_rebuild_data(struct ik_solver_t* solver)
*/
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)
if (recursively_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);
/* now build the chain tree */
if (rebuild_chain_tree(solver) < 0)
return -1;
if (solver->rebuild_data != NULL)
return solver->rebuild_data(solver);
return 0;
}
/* ------------------------------------------------------------------------- */
void
ik_solver_recalculate_segment_lengths(struct ik_solver_t* solver)
ik_solver_recalculate_segment_lengths(ik_solver_t* solver)
{
solver->recalculate_segment_lengths(solver);
calculate_segment_lengths(&solver->chain_tree);
}
/* ------------------------------------------------------------------------- */
int
ik_solver_solve(struct ik_solver_t* solver)
ik_solver_solve(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)
ik_solver_calculate_joint_rotations(ik_solver_t* solver)
{
if(solver->tree == NULL)
return;
iterate_tree_recursive(solver, solver->tree);
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
calculate_global_rotations(&island->root_chain);
ORDERED_VECTOR_END_EACH
}
/* ------------------------------------------------------------------------- */
static void
reset_solved_data_recursive(struct ik_node_t* node)
iterate_tree_recursive(ik_node_t* node,
ik_solver_iterate_node_cb_func callback)
{
node->solved_position = node->position;
node->solved_rotation = node->rotation;
callback(node);
BSTV_FOR_EACH(&node->children, struct ik_node_t, guid, child)
reset_solved_data_recursive(child);
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
iterate_tree_recursive(child, callback);
BSTV_END_EACH
}
void
ik_solver_reset_solved_data(struct ik_solver_t* solver)
ik_solver_iterate_tree(ik_solver_t* solver,
ik_solver_iterate_node_cb_func callback)
{
if(solver->tree == NULL)
if (solver->tree == NULL)
{
ik_log_message("Warning: Tried iterating the tree, but no tree was set");
return;
}
iterate_tree_recursive(solver->tree, callback);
}
/* ------------------------------------------------------------------------- */
static void
reset_active_pose_recursive(ik_node_t* node)
{
node->position = node->original_position;
node->rotation = node->initial_rotation;
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
reset_active_pose_recursive(child);
BSTV_END_EACH
}
void
ik_solver_reset_to_original_pose(ik_solver_t* solver)
{
if (solver->tree == NULL)
return;
reset_solved_data_recursive(solver->tree);
reset_active_pose_recursive(solver->tree);
}
/* ------------------------------------------------------------------------- */
static int
recursive_get_all_effector_nodes(struct ik_node_t* node, struct ordered_vector_t* effector_nodes_list)
recursively_get_all_effector_nodes(ik_node_t* node, ordered_vector_t* effector_nodes_list)
{
if(node->effector != NULL)
if(ordered_vector_push(effector_nodes_list, &node) < 0)
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)
BSTV_FOR_EACH(&node->children, ik_node_t, guid, child)
if (recursively_get_all_effector_nodes(child, effector_nodes_list) < 0)
return -1;
BSTV_END_EACH

76
Source/ThirdParty/ik/src/solver_1bone.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,76 @@
#include "ik/chain_tree.h"
#include "ik/effector.h"
#include "ik/log.h"
#include "ik/node.h"
#include "ik/solver_1bone.h"
#include <stddef.h>
#include <assert.h>
/* ------------------------------------------------------------------------- */
int
solver_1bone_construct(ik_solver_t* solver)
{
one_bone_t* one_bone = (one_bone_t*)solver;
/* set up derived functions */
one_bone->destruct = solver_1bone_destruct;
one_bone->rebuild_data = solver_1bone_rebuild;
one_bone->solve = solver_1bone_solve;
return 0;
}
/* ------------------------------------------------------------------------- */
void
solver_1bone_destruct(ik_solver_t* solver)
{
}
/* ------------------------------------------------------------------------- */
int
solver_1bone_rebuild(ik_solver_t* solver)
{
/*
* We need to assert that there really are only chains of length 1 and no
* sub chains.
*/
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
if (ordered_vector_count(&island->root_chain.nodes) != 2) /* 2 nodes = 1 bone */
{
ik_log_message("ERROR: Your tree has chains that are longer than 1 bone. Are you sure you selected the correct solver algorithm?");
return -1;
}
if (ordered_vector_count(&island->root_chain.children) > 0)
{
ik_log_message("ERROR: Your tree has child chains. This solver does not support arbitrary trees. You will need to switch to another algorithm (e.g. FABRIK)");
return -1;
}
ORDERED_VECTOR_END_EACH
return 0;
}
/* ------------------------------------------------------------------------- */
int
solver_1bone_solve(ik_solver_t* solver)
{
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
ik_node_t* node_tip;
ik_node_t* node_base;
chain_t* root_chain = &island->root_chain;
assert(ordered_vector_count(&root_chain->nodes) > 1);
node_tip = *(ik_node_t**)ordered_vector_get_element(&root_chain->nodes, 0);
node_base = *(ik_node_t**)ordered_vector_get_element(&root_chain->nodes, 1);
assert(node_tip->effector != NULL);
node_tip->position = node_tip->effector->target_position;
vec3_sub_vec3(node_tip->position.f, node_base->position.f);
vec3_normalise(node_tip->position.f);
vec3_mul_scalar(node_tip->position.f, node_tip->segment_length);
vec3_add_vec3(node_tip->position.f, node_base->position.f);
ORDERED_VECTOR_END_EACH
return 0;
}

138
Source/ThirdParty/ik/src/solver_2bone.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,138 @@
#include "ik/effector.h"
#include "ik/node.h"
#include "ik/log.h"
#include "ik/solver_2bone.h"
#include <assert.h>
#include <math.h>
#include <stddef.h>
/* ------------------------------------------------------------------------- */
int
solver_2bone_construct(ik_solver_t* solver)
{
two_bone_t* two_bone = (two_bone_t*)solver;
/* set up derived functions */
two_bone->destruct = solver_2bone_destruct;
two_bone->rebuild_data = solver_2bone_rebuild;
two_bone->solve = solver_2bone_solve;
return 0;
}
/* ------------------------------------------------------------------------- */
void
solver_2bone_destruct(ik_solver_t* solver)
{
}
/* ------------------------------------------------------------------------- */
int
solver_2bone_rebuild(ik_solver_t* solver)
{
/*
* We need to assert that there really are only chains of length 1 and no
* sub chains.
*/
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
if (ordered_vector_count(&island->root_chain.nodes) != 3) /* 3 nodes = 2 bones */
{
ik_log_message("ERROR: Your tree has chains that are longer or shorter than 2 bones. Are you sure you selected the correct solver algorithm?");
return -1;
}
if (ordered_vector_count(&island->root_chain.children) > 0)
{
ik_log_message("ERROR: Your tree has child chains. This solver does not support arbitrary trees. You will need to switch to another algorithm (e.g. FABRIK)");
return -1;
}
ORDERED_VECTOR_END_EACH
return 0;
}
/* ------------------------------------------------------------------------- */
int
solver_2bone_solve(ik_solver_t* solver)
{
ORDERED_VECTOR_FOR_EACH(&solver->chain_tree.islands, chain_island_t, island)
ik_node_t* node_tip;
ik_node_t* node_mid;
ik_node_t* node_base;
vec3_t to_target;
ik_real a, b, c, aa, bb, cc;
assert(ordered_vector_count(&island->root_chain.nodes) > 2);
node_tip = *(ik_node_t**)ordered_vector_get_element(&island->root_chain.nodes, 0);
node_mid = *(ik_node_t**)ordered_vector_get_element(&island->root_chain.nodes, 1);
node_base = *(ik_node_t**)ordered_vector_get_element(&island->root_chain.nodes, 2);
assert(node_tip->effector != NULL);
to_target = node_tip->effector->target_position;
vec3_sub_vec3(to_target.f, node_base->position.f);
/*
* Form a triangle from the two segment lengths so we can calculate the
* angles. Here's some visual help.
*
* target *--.__ a
* \ --.___ (unknown position, needs solving)
* \ _-
* c \ _-
* \- b
* base
*
*/
a = node_tip->segment_length;
b = node_mid->segment_length;
aa = a*a;
bb = b*b;
cc = vec3_length_squared(to_target.f);
c = sqrt(cc);
/* check if in reach */
if (c < a + b)
{
/* Cosine law to get base angle (alpha) */
quat_t alpha_rotation;
ik_real alpha = acos((bb + cc - aa) / (2.0 * node_mid->segment_length * sqrt(cc)));
ik_real cos_a = cos(alpha * 0.5);
ik_real sin_a = sin(alpha * 0.5);
/* Cross product of both segment vectors defines axis of rotation */
alpha_rotation.vw.v = node_tip->position;
vec3_sub_vec3(alpha_rotation.f, node_mid->position.f); /* top segment */
vec3_sub_vec3(node_mid->position.f, node_base->position.f); /* bottom segment */
vec3_cross(alpha_rotation.f, node_mid->position.f);
/*
* Set up quaternion describing the rotation of alpha. Need to
* normalise vec3 component of quaternion so rotation is correct.
*/
vec3_normalise(alpha_rotation.f);
vec3_mul_scalar(alpha_rotation.f, sin_a);
alpha_rotation.q.w = cos_a;
/* Rotate side c and scale to length of side b to get the unknown position */
node_mid->position = to_target;
vec3_normalise(node_mid->position.f);
vec3_mul_scalar(node_mid->position.f, node_mid->segment_length);
quat_rotate_vec(node_mid->position.f, alpha_rotation.f);
vec3_add_vec3(node_mid->position.f, node_base->position.f);
node_tip->position = node_tip->effector->target_position;
}
else
{
/* Just point both segments at target */
vec3_normalise(to_target.f);
node_mid->position = to_target;
node_tip->position = to_target;
vec3_mul_scalar(node_mid->position.f, node_mid->segment_length);
vec3_mul_scalar(node_tip->position.f, node_tip->segment_length);
vec3_add_vec3(node_mid->position.f, node_base->position.f);
vec3_add_vec3(node_tip->position.f, node_mid->position.f);
}
ORDERED_VECTOR_END_EACH
return 0;
}

967
Source/ThirdParty/ik/src/solver_FABRIK.c поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

24
Source/ThirdParty/ik/src/solver_MSD.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
#include "ik/solver_MSD.h"
/* ------------------------------------------------------------------------- */
int
solver_MSD_construct(ik_solver_t* solver)
{
solver->destruct = solver_MSD_destruct;
solver->solve = solver_MSD_solve;
return 0;
}
/* ------------------------------------------------------------------------- */
void
solver_MSD_destruct(ik_solver_t* solver)
{
}
/* ------------------------------------------------------------------------- */
int
solver_MSD_solve(ik_solver_t* solver)
{
return 0;
}

98
Source/ThirdParty/ik/src/util.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,98 @@
#include "ik/chain_tree.h"
#include "ik/effector.h"
#include "ik/node.h"
#include "ik/util.h"
#include <assert.h>
typedef struct effector_data_t
{
ik_real rotation_weight;
ik_real rotation_weight_decay;
} effector_data_t;
/* ------------------------------------------------------------------------- */
static effector_data_t
calculate_rotation_weight_decays_recursive(chain_t* chain)
{
int average_count;
int node_idx, node_count;
effector_data_t effector_data;
/*
* Find the rotation weight of this chain's last node by averaging the
* rotation weight of all child chain's first nodes.
*
* If there are no child chains, then the first node in the chain must
* contain an effector. Initialise the weight parameters from said
* effector.
*
* If there are child chains then average the effector data we've
* accumulated.
*/
average_count = 0;
ORDERED_VECTOR_FOR_EACH(&chain->children, chain_t, child)
effector_data_t child_eff_data =
calculate_rotation_weight_decays_recursive(child);
effector_data.rotation_weight += child_eff_data.rotation_weight;
effector_data.rotation_weight_decay += child_eff_data.rotation_weight_decay;
++average_count;
ORDERED_VECTOR_END_EACH
if (average_count == 0)
{
ik_node_t* effector_node = *(ik_node_t**)ordered_vector_get_element(&chain->nodes, 0);
ik_effector_t* effector = effector_node->effector;
effector_data.rotation_weight = effector->rotation_weight;
effector_data.rotation_weight_decay = effector->rotation_decay;
}
else
{
ik_real div = 1.0 / average_count;
effector_data.rotation_weight *= div;
effector_data.rotation_weight_decay *= div;
}
/*
* With the rotation weight of the last node calculated, we can now iterate
* the nodes in the chain and set each rotation weight, decaying a little
* bit every time. Note that we exclude the last node, because it will
* be handled by the parent chain. If there is no parent chain then the
* non-recursive caller of this function will set the rotation weight of
* the root node.
*/
node_count = ordered_vector_count(&chain->nodes) - 1;
for (node_idx = 0; node_idx < node_count; ++node_idx)
{
ik_node_t* node = *(ik_node_t**)ordered_vector_get_element(&chain->nodes, node_idx);
node->rotation_weight = effector_data.rotation_weight;
effector_data.rotation_weight *= effector_data.rotation_weight_decay;
}
/* Rotation weight is now equal to that of this chain's base node */
return effector_data;
}
/* ------------------------------------------------------------------------- */
void
ik_calculate_rotation_weight_decays(chain_tree_t* chain_tree)
{
/*
* The recursive version of this function does not set the rotation weight
* of the first node in every tree that gets passed to it, but it does
* return the rotation weight that *would have been set* (which gets used
* recursively to calculate the average rotation weight in the case of
* multiple child chains).
*
* For these reasons we iterate the chain islands and set the first node in
* each island to the returned rotation weight.
*/
ORDERED_VECTOR_FOR_EACH(&chain_tree->islands, chain_island_t, island)
ik_node_t* root_node;
effector_data_t effector_data = calculate_rotation_weight_decays_recursive(&island->root_chain);
int last_idx = ordered_vector_count(&island->root_chain.nodes) - 1;
assert(last_idx > 0);
root_node = *(ik_node_t**)ordered_vector_get_element(&island->root_chain.nodes, last_idx);
root_node->rotation_weight = effector_data.rotation_weight;
ORDERED_VECTOR_END_EACH
}

64
Source/ThirdParty/ik/src/vec3.c поставляемый
Просмотреть файл

@ -1,17 +1,18 @@
#include "ik/vec3.h"
#include "ik/quat.h"
#include <string.h>
#include <math.h>
/* ------------------------------------------------------------------------- */
void
vec3_set_zero(ik_real* v)
vec3_set_zero(ik_real* IK_RESTRICT v)
{
memset(v, 0, sizeof *v);
}
/* ------------------------------------------------------------------------- */
void
vec3_add_vec3(ik_real* v1, const ik_real* v2)
vec3_add_vec3(ik_real* IK_RESTRICT v1, const ik_real* IK_RESTRICT v2)
{
v1[0] += v2[0];
v1[1] += v2[1];
@ -20,7 +21,7 @@ vec3_add_vec3(ik_real* v1, const ik_real* v2)
/* ------------------------------------------------------------------------- */
void
vec3_sub_vec3(ik_real* v1, const ik_real* v2)
vec3_sub_vec3(ik_real* IK_RESTRICT v1, const ik_real* IK_RESTRICT v2)
{
v1[0] -= v2[0];
v1[1] -= v2[1];
@ -29,7 +30,7 @@ vec3_sub_vec3(ik_real* v1, const ik_real* v2)
/* ------------------------------------------------------------------------- */
void
vec3_mul_scalar(ik_real* v, ik_real scalar)
vec3_mul_scalar(ik_real* IK_RESTRICT v, ik_real scalar)
{
v[0] *= scalar;
v[1] *= scalar;
@ -38,7 +39,7 @@ vec3_mul_scalar(ik_real* v, ik_real scalar)
/* ------------------------------------------------------------------------- */
void
vec3_div_scalar(ik_real* v, ik_real scalar)
vec3_div_scalar(ik_real* IK_RESTRICT v, ik_real scalar)
{
v[0] /= scalar;
v[1] /= scalar;
@ -47,33 +48,39 @@ vec3_div_scalar(ik_real* v, ik_real scalar)
/* ------------------------------------------------------------------------- */
ik_real
vec3_length_squared(const ik_real* v)
vec3_length_squared(const ik_real* IK_RESTRICT v)
{
return vec3_dot(v, v);
}
/* ------------------------------------------------------------------------- */
ik_real
vec3_length(const ik_real* v)
vec3_length(const ik_real* IK_RESTRICT v)
{
return sqrt(vec3_length_squared(v));
}
/* ------------------------------------------------------------------------- */
void
vec3_normalise(ik_real* v)
vec3_normalise(ik_real* IK_RESTRICT v)
{
ik_real length = vec3_length(v);
if(length != 0.0)
if (length != 0.0)
{
length = 1.0 / length;
v[0] *= length;
v[1] *= length;
v[2] *= length;
v[0] *= length;
v[1] *= length;
v[2] *= length;
}
else
{
v[0] = 1;
}
}
/* ------------------------------------------------------------------------- */
ik_real
vec3_dot(const ik_real* v1, const ik_real* v2)
vec3_dot(const ik_real* IK_RESTRICT v1, const ik_real* IK_RESTRICT v2)
{
return v1[0] * v2[0] +
v1[1] * v2[1] +
@ -82,7 +89,7 @@ vec3_dot(const ik_real* v1, const ik_real* v2)
/* ------------------------------------------------------------------------- */
void
vec3_cross(ik_real* v1, const ik_real* v2)
vec3_cross(ik_real* IK_RESTRICT v1, const ik_real* IK_RESTRICT v2)
{
ik_real v1x = v1[1] * v2[2] - v2[1] * v1[2];
ik_real v1z = v1[0] * v2[1] - v2[0] * v1[1];
@ -90,3 +97,32 @@ vec3_cross(ik_real* v1, const ik_real* v2)
v1[0] = v1x;
v1[2] = v1z;
}
/* ------------------------------------------------------------------------- */
void
vec3_angle(ik_real* IK_RESTRICT q, const ik_real* IK_RESTRICT v1, const ik_real* IK_RESTRICT v2)
{
ik_real cos_a, sin_a, angle, denominator;
denominator = 1.0 / vec3_length(v1) / vec3_length(v2);
cos_a = vec3_dot(v1, v2) * denominator;
if (cos_a >= -1.0 && cos_a <= 1.0)
{
/* calculate axis of rotation and write it to the quaternion's vector section */
memcpy(q, v1, sizeof(ik_real) * 3);
vec3_cross(q, v2);
vec3_normalise(q);
/* 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(q, sin_a);
q[3] = cos_a; /* w component */
}
else
{
/* Important! otherwise garbage happens when applying initial rotations */
quat_set_identity(q);
}
}

Просмотреть файл

@ -35,28 +35,37 @@ namespace Urho3D
static void RegisterIKSolver(asIScriptEngine* engine)
{
engine->RegisterEnum("IKAlgorithm");
engine->RegisterEnumValue("IKAlgorithm", "ONE_BONE", IKSolver::ONE_BONE);
engine->RegisterEnumValue("IKAlgorithm", "TWO_BONE", IKSolver::TWO_BONE);
engine->RegisterEnumValue("IKAlgorithm", "FABRIK", IKSolver::FABRIK);
engine->RegisterEnum("IKFeature");
engine->RegisterEnumValue("IKFeature", "JOINT_ROTATIONS", IKSolver::JOINT_ROTATIONS);
engine->RegisterEnumValue("IKFeature", "TARGET_ROTATIONS", IKSolver::TARGET_ROTATIONS);
engine->RegisterEnumValue("IKFeature", "UPDATE_ORIGINAL_POSE", IKSolver::UPDATE_ORIGINAL_POSE);
engine->RegisterEnumValue("IKFeature", "UPDATE_ACTIVE_POSE", IKSolver::UPDATE_ACTIVE_POSE);
engine->RegisterEnumValue("IKFeature", "USE_ORIGINAL_POSE", IKSolver::USE_ORIGINAL_POSE);
engine->RegisterEnumValue("IKFeature", "CONSTRAINTS", IKSolver::CONSTRAINTS);
engine->RegisterEnumValue("IKFeature", "AUTO_SOLVE", IKSolver::AUTO_SOLVE);
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", "void SetFeature(IKFeature,bool)", asMETHOD(IKSolver, SetFeature), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "bool GetFeature(IKFeature) const", asMETHOD(IKSolver, GetFeature), 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 RebuildData()", asMETHOD(IKSolver, RebuildData), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void RecalculateSegmentLengths()", asMETHOD(IKSolver, RecalculateSegmentLengths), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void CalculateJointRotations()", asMETHOD(IKSolver, CalculateJointRotations), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void Solve()", asMETHOD(IKSolver, Solve), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ResetToInitialPose()", asMETHOD(IKSolver, ResetToInitialPose), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void UpdateInitialPose()", asMETHOD(IKSolver, UpdateInitialPose), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ApplyOriginalPoseToScene()", asMETHOD(IKSolver, ApplyOriginalPoseToScene), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ApplySceneToInitialPose()", asMETHOD(IKSolver, ApplySceneToOriginalPose), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ApplyActivePoseToScene()", asMETHOD(IKSolver, ApplyActivePoseToScene), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ApplySceneToActivePose()", asMETHOD(IKSolver, ApplySceneToActivePose), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void ApplyOriginalPoseToActivePose()", asMETHOD(IKSolver, ApplyOriginalPoseToActivePose), asCALL_THISCALL);
engine->RegisterObjectMethod("IKSolver", "void DrawDebugGeometry(bool)", asMETHODPR(IKSolver, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
}
@ -81,8 +90,6 @@ static void RegisterIKEffector(asIScriptEngine* engine)
engine->RegisterObjectMethod("IKEffector", "void set_rotationDecay(float)", asMETHOD(IKEffector, SetRotationDecay), asCALL_THISCALL);
engine->RegisterObjectMethod("IKEffector", "bool get_weightedNlerp() const", asMETHOD(IKEffector, WeightedNlerpEnabled), asCALL_THISCALL);
engine->RegisterObjectMethod("IKEffector", "void set_weightedNlerp(bool)", asMETHOD(IKEffector, EnableWeightedNlerp), asCALL_THISCALL);
engine->RegisterObjectMethod("IKEffector", "bool get_inheritParentRotation() const", asMETHOD(IKEffector, InheritParentRotationEnabled), asCALL_THISCALL);
engine->RegisterObjectMethod("IKEffector", "void set_inheritParentRotation(bool)", asMETHOD(IKEffector, EnableInheritParentRotation), asCALL_THISCALL);
engine->RegisterObjectMethod("IKEffector", "void DrawDebugGeometry(bool)", asMETHODPR(IKEffector, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
}

Просмотреть файл

@ -33,7 +33,7 @@ const char* IK_CATEGORY = "Inverse Kinematics";
// ----------------------------------------------------------------------------
void RegisterIKLibrary(Context* context)
{
IKConstraint::RegisterObject(context);
//IKConstraint::RegisterObject(context);
IKEffector::RegisterObject(context);
IKSolver::RegisterObject(context);
}

Просмотреть файл

@ -24,13 +24,14 @@
* TODO
* - Add support for manually updating initial pose.
* - Lua script bindings crash.
* - Implement inherit parent rotations in IKEffector.
* - Optimise.
* - Profile.
* - Documentation.
* - Move log callback into context init function.
* - Bug when enabling continuous mode and IKSolver is placed somewhere
* on part of the model's bones.
* - Possible optimisation: Don't normalise quaternion in quat_mul_quat(),
* normalise it manually after multiplying all quaternions.
*
* FUTURE
* - Support for "stretchiness" with min/max lengths.

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -37,6 +37,7 @@
#include <ik/effector.h>
#include <ik/node.h>
#include <ik/solver.h>
#include <ik/util.h>
namespace Urho3D
{
@ -58,13 +59,23 @@ static bool ChildrenHaveEffector(const Node* node)
return false;
}
static void ApplyConstraintsCallback(ik_node_t* ikNode)
{
Node* node = (Node*)ikNode->user_data;
IKConstraint* constraint = node->GetComponent<IKConstraint>();
if (constraint == NULL)
return;
quat_set_identity(ikNode->rotation.f);
}
// ----------------------------------------------------------------------------
IKSolver::IKSolver(Context* context) :
Component(context),
solver_(NULL),
solverTreeNeedsRebuild_(false),
updateInitialPose_(false),
autoSolveEnabled_(true)
algorithm_(FABRIK),
features_(AUTO_SOLVE | JOINT_ROTATIONS | UPDATE_ACTIVE_POSE),
solverTreeNeedsRebuild_(false)
{
context_->RequireIK();
@ -94,21 +105,26 @@ void IKSolver::RegisterObject(Context* context)
context->RegisterFactory<IKSolver>(IK_CATEGORY);
static const char* algorithmNames[] = {
"1 Bone",
"2 Bone",
"FABRIK",
/* not implemented
/* not implemented,
"MSD (Mass/Spring/Damper)",
"Jacobian Inverse",
"Jacobian Transpose",*/
NULL
};
URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, SOLVER_FABRIK, AM_DEFAULT);
URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Algorithm", GetAlgorithm, SetAlgorithm, Algorithm, algorithmNames, 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);
URHO3D_ACCESSOR_ATTRIBUTE("Joint Rotations", GetFeature_JOINT_ROTATIONS, SetFeature_JOINT_ROTATIONS, bool, true, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Target Rotations", GetFeature_TARGET_ROTATIONS, SetFeature_TARGET_ROTATIONS, bool, false, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Update Original Pose", GetFeature_UPDATE_ORIGINAL_POSE, SetFeature_UPDATE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Update Active Pose", GetFeature_UPDATE_ACTIVE_POSE, SetFeature_UPDATE_ACTIVE_POSE, bool, true, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Use Original Pose", GetFeature_USE_ORIGINAL_POSE, SetFeature_USE_ORIGINAL_POSE, bool, false, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Enable Constraints", GetFeature_CONSTRAINTS, SetFeature_CONSTRAINTS, bool, false, AM_DEFAULT);
URHO3D_ACCESSOR_ATTRIBUTE("Auto Solve", GetFeature_AUTO_SOLVE, SetFeature_AUTO_SOLVE, bool, true, AM_DEFAULT);
}
// ----------------------------------------------------------------------------
@ -122,15 +138,78 @@ void IKSolver::SetAlgorithm(IKSolver::Algorithm algorithm)
{
algorithm_ = algorithm;
/* We need to rebuild the tree so make sure that the scene is in the
* initial pose when this occurs.*/
if (node_ != NULL)
ApplyOriginalPoseToScene();
// Initial flags for when there is no solver to destroy
uint8_t initialFlags = 0;
// Destroys the tree and the solver
if (solver_ != NULL)
{
initialFlags = solver_->flags;
DestroyTree();
ik_solver_destroy(solver_);
}
switch (algorithm_)
{
case FABRIK: solver_ = ik_solver_create(SOLVER_FABRIK); break;
case ONE_BONE : solver_ = ik_solver_create(SOLVER_ONE_BONE); break;
case TWO_BONE : solver_ = ik_solver_create(SOLVER_TWO_BONE); break;
case FABRIK : solver_ = ik_solver_create(SOLVER_FABRIK); break;
/*case MSD : solver_ = ik_solver_create(SOLVER_MSD); break;*/
}
solver_->flags = SOLVER_CALCULATE_FINAL_ROTATIONS;
solver_->flags = initialFlags;
if (node_ != NULL)
RebuildTree();
}
// ----------------------------------------------------------------------------
bool IKSolver::GetFeature(Feature feature) const
{
return (features_ & feature) != 0;
}
// ----------------------------------------------------------------------------
void IKSolver::SetFeature(Feature feature, bool enable)
{
switch (feature)
{
case CONSTRAINTS:
{
solver_->flags &= ~SOLVER_ENABLE_CONSTRAINTS;
if (enable)
solver_->flags |= SOLVER_ENABLE_CONSTRAINTS;
} break;
case TARGET_ROTATIONS:
{
solver_->flags &= ~SOLVER_CALCULATE_TARGET_ROTATIONS;
if (enable)
solver_->flags |= SOLVER_CALCULATE_TARGET_ROTATIONS;
} break;
case AUTO_SOLVE:
{
if (((features_ & AUTO_SOLVE) != 0) == enable)
break;
if (enable)
SubscribeToEvent(GetScene(), E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
else
UnsubscribeFromEvent(GetScene(), E_SCENEDRAWABLEUPDATEFINISHED);
} break;
default: break;
}
features_ &= ~feature;
if (enable)
features_ |= feature;
}
// ----------------------------------------------------------------------------
@ -160,138 +239,115 @@ void IKSolver::SetTolerance(float tolerance)
}
// ----------------------------------------------------------------------------
bool IKSolver::BoneRotationsEnabled() const
void IKSolver::RebuildData()
{
return (solver_->flags & SOLVER_CALCULATE_FINAL_ROTATIONS) != 0;
ik_solver_rebuild_data(solver_);
ik_calculate_rotation_weight_decays(&solver_->chain_tree);
}
// ----------------------------------------------------------------------------
void IKSolver::EnableBoneRotations(bool enable)
void IKSolver::RecalculateSegmentLengths()
{
solver_->flags &= ~SOLVER_CALCULATE_FINAL_ROTATIONS;
if (enable)
solver_->flags |= SOLVER_CALCULATE_FINAL_ROTATIONS;
ik_solver_recalculate_segment_lengths(solver_);
}
// ----------------------------------------------------------------------------
bool IKSolver::TargetRotationEnabled() const
void IKSolver::CalculateJointRotations()
{
return (solver_->flags & SOLVER_CALCULATE_TARGET_ROTATIONS) != 0;
ik_solver_calculate_joint_rotations(solver_);
}
// ----------------------------------------------------------------------------
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_);
RebuildData();
solverTreeNeedsRebuild_ = false;
}
if (updateInitialPose_)
UpdateInitialPose();
if (features_ & UPDATE_ORIGINAL_POSE)
ApplySceneToOriginalPose();
if (features_ & UPDATE_ACTIVE_POSE)
ApplySceneToActivePose();
if (features_ & USE_ORIGINAL_POSE)
ApplyOriginalPoseToActivePose();
for (PODVector<IKEffector*>::ConstIterator it = effectorList_.Begin(); it != effectorList_.End(); ++it)
{
(*it)->UpdateTargetNodePosition();
}
solver_->apply_result = ApplySolvedDataCallback;
ik_solver_solve(solver_);
if (features_ & JOINT_ROTATIONS)
ik_solver_calculate_joint_rotations(solver_);
ApplyActivePoseToScene();
}
// ----------------------------------------------------------------------------
static void ApplyInitialDataCallback(ik_node_t* ikNode)
static void ApplyInitialPoseToSceneCallback(ik_node_t* ikNode)
{
Node* node = (Node*)ikNode->user_data;
node->SetWorldRotation(QuatIK2Urho(&ikNode->initial_rotation));
node->SetWorldPosition(Vec3IK2Urho(&ikNode->original_position));
}
void IKSolver::ApplyOriginalPoseToScene()
{
ik_solver_iterate_tree(solver_, ApplyInitialPoseToSceneCallback);
}
// ----------------------------------------------------------------------------
static void ApplySceneToInitialPoseCallback(ik_node_t* ikNode)
{
Node* node = (Node*)ikNode->user_data;
ikNode->initial_rotation = QuatUrho2IK(node->GetWorldRotation());
ikNode->original_position = Vec3Urho2IK(node->GetWorldPosition());
}
void IKSolver::ApplySceneToOriginalPose()
{
ik_solver_iterate_tree(solver_, ApplySceneToInitialPoseCallback);
}
// ----------------------------------------------------------------------------
static void ApplySolvedPoseToSceneCallback(ik_node_t* ikNode)
{
Node* node = (Node*)ikNode->user_data;
node->SetWorldRotation(QuatIK2Urho(&ikNode->rotation));
node->SetWorldPosition(Vec3IK2Urho(&ikNode->position));
}
void IKSolver::ResetToInitialPose()
void IKSolver::ApplyActivePoseToScene()
{
solver_->apply_result = ApplyInitialDataCallback;
ik_solver_iterate_tree(solver_);
ik_solver_iterate_tree(solver_, ApplySolvedPoseToSceneCallback);
}
// ----------------------------------------------------------------------------
static void UpdateInitialPoseCallback(ik_node_t* ikNode)
static void ApplySceneToSolvedPoseCallback(ik_node_t* ikNode)
{
Node* node = (Node*)ikNode->user_data;
ikNode->rotation = QuatUrho2IK(node->GetWorldRotation());
ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
}
void IKSolver::UpdateInitialPose()
void IKSolver::ApplySceneToActivePose()
{
solver_->apply_result = UpdateInitialPoseCallback;
ik_solver_iterate_tree(solver_);
ik_solver_iterate_tree(solver_, ApplySceneToSolvedPoseCallback);
}
// ----------------------------------------------------------------------------
void IKSolver::ApplyOriginalPoseToActivePose()
{
ik_solver_reset_to_original_pose(solver_);
}
// ----------------------------------------------------------------------------
void IKSolver::MarkSolverTreeDirty()
{
solverTreeNeedsRebuild_ = true;
}
// ----------------------------------------------------------------------------
@ -309,14 +365,14 @@ void IKSolver::UpdateInitialPose()
// ----------------------------------------------------------------------------
void IKSolver::OnSceneSet(Scene* scene)
{
if (autoSolveEnabled_)
if (features_ & AUTO_SOLVE)
SubscribeToEvent(scene, E_SCENEDRAWABLEUPDATEFINISHED, URHO3D_HANDLER(IKSolver, HandleSceneDrawableUpdateFinished));
}
// ----------------------------------------------------------------------------
void IKSolver::OnNodeSet(Node* node)
{
ResetToInitialPose();
ApplyOriginalPoseToScene();
DestroyTree();
if (node != NULL)
@ -329,14 +385,17 @@ ik_node_t* IKSolver::CreateIKNode(const Node* node)
ik_node_t* ikNode = ik_node_create(node->GetID());
// Set initial position/rotation and pass in Node* as user data for later
ikNode->position = Vec3Urho2IK(node->GetWorldPosition());
ikNode->rotation = QuatUrho2IK(node->GetWorldRotation());
ikNode->original_position = Vec3Urho2IK(node->GetWorldPosition());
ikNode->initial_rotation = QuatUrho2IK(node->GetWorldRotation());
ikNode->user_data = (void*)node;
// If the node has a constraint, it needs access to the ikNode
IKConstraint* constraint = node->GetComponent<IKConstraint>();
if (constraint != NULL)
{
constraint->SetIKNode(ikNode);
constraintList_.Push(constraint);
}
return ikNode;
}
@ -351,10 +410,10 @@ void IKSolver::DestroyTree()
// ----------------------------------------------------------------------------
void IKSolver::RebuildTree()
{
assert(node_ != NULL);
assert (node_ != NULL);
ik_node_t* ikRoot = CreateIKNode(node_);
ik_solver_set_tree(solver_, ikRoot);
ik_solver_set_tree(solver_, ikRoot); // Deletes the old tree with all effectors + constraints
PODVector<Node*> effectorNodes;
node_->GetChildrenWithComponent<IKEffector>(effectorNodes, true);
@ -384,6 +443,8 @@ void IKSolver::BuildTreeToEffector(const Node* node)
{
missingNodes.Push(iterNode);
iterNode = iterNode->GetParent();
if (iterNode == NULL) // The effector is in a different branch of the tree, unrelated to us. Abort.
return;
ikNode = ik_node_find_child(solver_->tree, iterNode->GetID());
}
while (missingNodes.Size() > 0)
@ -400,7 +461,7 @@ void IKSolver::BuildTreeToEffector(const Node* node)
// it.
ik_effector_t* ikEffector = ik_effector_create();
ik_node_attach_effector(ikNode, ikEffector); // ownership of effector
effector->SetIKEffector(ikEffector); // "weak" reference to effector
effector->SetIKEffector(ikEffector); // "weak" reference to effector
effector->SetIKSolver(this);
effectorList_.Push(effector);
@ -418,6 +479,17 @@ void IKSolver::HandleComponentAdded(StringHash eventType, VariantMap& eventData)
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
BuildTreeToEffector(node);
IKConstraint* constraint = static_cast<IKConstraint*>(node->GetComponent<IKConstraint>());
if (constraint != NULL)
{
ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
if (ikNode != NULL)
{
constraint->SetIKNode(ikNode);
constraintList_.Push(constraint);
}
}
}
// ----------------------------------------------------------------------------
@ -435,12 +507,14 @@ void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventDat
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);
if (ikNode == NULL) // The effector is in an unrelated branch of the tree, abort.
return;
ik_node_destroy_effector(ikNode);
effector->SetIKEffector(NULL);
effectorList_.RemoveSwap(effector);
ResetToInitialPose();
ApplyOriginalPoseToScene();
MarkSolverTreeDirty();
}
@ -448,7 +522,13 @@ void IKSolver::HandleComponentRemoved(StringHash eventType, VariantMap& eventDat
if (component->GetType() == IKConstraint::GetTypeStatic())
{
IKConstraint* constraint = static_cast<IKConstraint*>(component);
Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr());
ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID());
if (ikNode == NULL) // The effector is in an unrelated branch of the tree, abort.
return;
constraint->SetIKNode(NULL); // NOTE: Should restore default settings to the node
constraintList_.RemoveSwap(constraint);
}
}
@ -469,6 +549,12 @@ void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
BuildTreeToEffector(*it);
effectorList_.Push((*it)->GetComponent<IKEffector>());
}
node->GetChildrenWithComponent<IKConstraint>(nodes, true);
for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
{
constraintList_.Push((*it)->GetComponent<IKConstraint>());
}
}
// ----------------------------------------------------------------------------
@ -491,6 +577,13 @@ void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
effectorList_.RemoveSwap(effector);
}
node->GetChildrenWithComponent<IKConstraint>(nodes, true);
for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it)
{
IKConstraint* constraint = (*it)->GetComponent<IKConstraint>();
constraintList_.RemoveSwap(constraint);
}
// Special case, if the node being destroyed is the root node, destroy the
// solver's tree instead of destroying the single node. Calling
// ik_node_destroy() on the solver's root node will cause segfaults.
@ -539,8 +632,8 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
unsigned numberOfSegments = 0;
while (b && chainLength-- != 0)
{
vec3_t v = a->position;
vec3_sub_vec3(v.f, b->position.f);
vec3_t v = a->original_position;
vec3_sub_vec3(v.f, b->original_position.f);
averageLength += vec3_length(v.f);
++numberOfSegments;
a = b;
@ -553,36 +646,36 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
a = *pnode;
b = a->parent;
debug->AddSphere(
Sphere(Vec3IK2Urho(&a->position), averageLength * 0.1f),
Sphere(Vec3IK2Urho(&a->original_position), averageLength * 0.1f),
Color(0, 0, 255),
depthTest
);
debug->AddSphere(
Sphere(Vec3IK2Urho(&a->solved_position), averageLength * 0.1f),
Sphere(Vec3IK2Urho(&a->position), averageLength * 0.1f),
Color(255, 128, 0),
depthTest
);
while (b && chainLength-- != 0)
{
debug->AddLine(
Vec3IK2Urho(&a->position),
Vec3IK2Urho(&b->position),
Vec3IK2Urho(&a->original_position),
Vec3IK2Urho(&b->original_position),
Color(0, 255, 255),
depthTest
);
debug->AddSphere(
Sphere(Vec3IK2Urho(&b->position), averageLength * 0.1f),
Sphere(Vec3IK2Urho(&b->original_position), averageLength * 0.1f),
Color(0, 0, 255),
depthTest
);
debug->AddLine(
Vec3IK2Urho(&a->solved_position),
Vec3IK2Urho(&b->solved_position),
Vec3IK2Urho(&a->position),
Vec3IK2Urho(&b->position),
Color(255, 0, 0),
depthTest
);
debug->AddSphere(
Sphere(Vec3IK2Urho(&b->solved_position), averageLength * 0.1f),
Sphere(Vec3IK2Urho(&b->position), averageLength * 0.1f),
Color(255, 128, 0),
depthTest
);
@ -592,4 +685,67 @@ void IKSolver::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
ORDERED_VECTOR_END_EACH
}
// ----------------------------------------------------------------------------
// Need these wrapper functions flags of GetFeature/SetFeature can be correctly
// exposed to the editor
// ----------------------------------------------------------------------------
bool IKSolver::GetFeature_JOINT_ROTATIONS() const
{
return (features_ & JOINT_ROTATIONS);
}
bool IKSolver::GetFeature_TARGET_ROTATIONS() const
{
return (features_ & TARGET_ROTATIONS);
}
bool IKSolver::GetFeature_UPDATE_ORIGINAL_POSE() const
{
return (features_ & UPDATE_ORIGINAL_POSE);
}
bool IKSolver::GetFeature_UPDATE_ACTIVE_POSE() const
{
return (features_ & UPDATE_ACTIVE_POSE);
}
bool IKSolver::GetFeature_USE_ORIGINAL_POSE() const
{
return (features_ & USE_ORIGINAL_POSE);
}
bool IKSolver::GetFeature_CONSTRAINTS() const
{
return (features_ & CONSTRAINTS);
}
bool IKSolver::GetFeature_AUTO_SOLVE() const
{
return (features_ & AUTO_SOLVE);
}
void IKSolver::SetFeature_JOINT_ROTATIONS(bool enable)
{
SetFeature(JOINT_ROTATIONS, enable);
}
void IKSolver::SetFeature_TARGET_ROTATIONS(bool enable)
{
SetFeature(TARGET_ROTATIONS, enable);
}
void IKSolver::SetFeature_UPDATE_ORIGINAL_POSE(bool enable)
{
SetFeature(UPDATE_ORIGINAL_POSE, enable);
}
void IKSolver::SetFeature_UPDATE_ACTIVE_POSE(bool enable)
{
SetFeature(UPDATE_ACTIVE_POSE, enable);
}
void IKSolver::SetFeature_USE_ORIGINAL_POSE(bool enable)
{
SetFeature(USE_ORIGINAL_POSE, enable);
}
void IKSolver::SetFeature_CONSTRAINTS(bool enable)
{
SetFeature(CONSTRAINTS, enable);
}
void IKSolver::SetFeature_AUTO_SOLVE(bool enable)
{
SetFeature(AUTO_SOLVE, enable);
}
} // namespace Urho3D

Просмотреть файл

@ -47,12 +47,124 @@ public:
enum Algorithm
{
ONE_BONE = 0,
TWO_BONE,
FABRIK
/* not implemented yet
MSD,
JACOBIAN_INVERSE,
JACOBIAN_TRANSPOSE*/
};
enum Feature
{
/*!
* @brief Should be enabled if your model uses skinning or if you are
* generally interested in correct joint rotations. Has a minor
* performance impact.
*
* 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?)
*/
JOINT_ROTATIONS = 0x01,
/*!
* @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.
*/
TARGET_ROTATIONS = 0x02,
/*!
* When the solver is first initialized, it will copy the positions
* and rotations of the current Urho3D scene graph into an internal
* structure. This is referred to as the "original pose" and will by
* default never change for the duration of the solver's life cycle.
* When the solver is destroyed, the original pose is applied back to
* Urho3D's scene graph so the nodes are restored to whatever they were
* before the solver was created.
*
* By enabling UPDATE_ORIGINAL_POSE, the original pose will be updated
* right before solving to reflect the current Urho3D scene graph. As
* a consequence, there will no longer be an original pose to restore
* when the solver is destroyed.
*
* When disabled, the original pose will remain unmodified. The original
* pose is set when the solver is first created. You can manually update the
* original pose at any time by calling UpdateInitialPose().
*/
UPDATE_ORIGINAL_POSE = 0x04,
/*!
* @brief Should be enabled if you are using IK on an animated model,
* along with disabling USE_ORIGINAL_POSE.
*
* The "active pose" has two purposes: The solver uses it as the
* initial tree to derive a solution from, and at the same time uses it
* to store the solution into. Thus, the typical solving process is:
* 1) The active pose needs to be updated to reflect a preferred
* initial condition (such as the current frame of animation)
* 2) Call Solve()
* 3) The active pose now holds the solution, so it must be applied
* back to the Urho3D scene graph.
*
* When enabled, the active pose is updated right before solving to
* reflect the current state of the Urho3D scene graph.
*
* When disabled, the active pose will simply remain as it was since
* the last time Solve() was called.
*
* @note This option conflicts with USE_ORIGINAL_POSE. Make sure to
* disable USE_ORIGINAL_POSE if you enable this feature.
*/
UPDATE_ACTIVE_POSE = 0x08,
/*!
* @brief Choose between using the original pose or the active pose as
* a basis for a solution.
*
* When enabled, the solver will copy the original pose
* (see UPDATE_ORIGINAL_POSE) into the active pose before solving (and
* thus use the original pose as a basis for a solution).
*
* @note This option conflicts with UPDATE_ACTIVE_POSE. If you enable
* this feature, make sure to disable UPDATE_ACTIVE_POSE.
*
* If both UPDATE_ACTIVE_POSE and USE_ORIGINAL_POSE are disabled, then
* the solver will use the previously solved tree as a basis for the new
* calculation. The result is a more "continuous" solution that unfolds
* over time. This can be useful if you want to simulate chains or
* something similar.
*/
USE_ORIGINAL_POSE = 0x10,
/*!
* Due to the somewhat unfortunate performance impacts, the solver
* does not enable constraints by default. Enabling constraints causes
* the solver's tree to be written to and from Urho3D's scene graph every
* iteration, while calling ApplyConstraints(). Disabling constraints means
* ApplyConstraints() is never called.
*/
CONSTRAINTS = 0x20,
/*!
* 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.
*/
AUTO_SOLVE = 0x40
};
/// Construct an IK root component.
IKSolver(Context* context);
/// Default destructor.
@ -64,16 +176,25 @@ public:
Algorithm GetAlgorithm() const;
/*!
* @brief Selects the solver algorithm. Default is FABRIK.
* @brief Selects the solver algorithm. Default is FABRIK. Note that this
* may not be the most efficient algorithm available. The specialized
* solvers will be a lot faster.
*
* 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.
* + **2 Bone**: A specialized solver optimized for 2 bone problems (such
* as a human leg)
* + **1 Bone**: A specialized solver optimized for 1 bone problems (such
* as a look-at target, e.g. eyes or a head)
*/
void SetAlgorithm(Algorithm algorithm);
bool GetFeature(Feature feature) const;
void SetFeature(Feature feature, bool enable);
/// Returns the configured maximum number of iterations.
unsigned GetMaximumIterations() const;
@ -111,91 +232,38 @@ public:
*/
void SetTolerance(float tolerance);
/// Whether or not rotations should be calculated.
bool BoneRotationsEnabled() const;
/*!
* @brief Updates the solver's internal data structures, which is required
* whenever the tree is modified in any way (e.g. adding or removing nodes,
* adding or removing effectors, etc.).
* @note This gets called automatically for you in Solve().
*/
void RebuildData();
/*!
* @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?)
* @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 recalculate the segment lengths after assigning new
* positions to the nodes.
* @note This function gets called by RebuildData() and by extension in
* Solve().
*/
void EnableBoneRotations(bool enable);
/// Whether or not target rotation is enabled
bool TargetRotationEnabled() const;
void RecalculateSegmentLengths();
/*!
* @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.
* @brief Skinned models require joint rotations to be calculated so
* skinning works correctly. This is automatically enabled by default with
* the feature flag JOINT_ROTATIONS.
*/
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);
void CalculateJointRotations();
/*!
* @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.
* @note By default this is called automatically for you if the feature
* flag AUTO_SOLVE is set. For more complex IK problems you can disable
* that flag and call Solve() in response to E_SCENEDRAWABLEUPDATEFINISHED.
* This is right after the animations have been applied.
*/
void Solve();
@ -203,7 +271,7 @@ public:
* @brief Causes the initial tree to be applied back to Urho3D's scene
* graph. This is what gets called when continuous solving is disabled.
*/
void ResetToInitialPose();
void ApplyOriginalPoseToScene();
/*!
* @brief Causes the current scene graph data to be copied into the solvers
@ -211,16 +279,22 @@ public:
* 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.
*
* @note This is
*/
void UpdateInitialPose();
/// Causes the solver tree to be rebuilt before solving the next time.
void MarkSolverTreeDirty();
void ApplySceneToOriginalPose();
void ApplyActivePoseToScene();
void ApplySceneToActivePose();
void ApplyOriginalPoseToActivePose();
void DrawDebugGeometry(bool depthTest);
virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
private:
friend class IKEffector;
/// Causes the solver tree to be rebuilt before solving the next time. Intended to be used by IKEffector.
void MarkSolverTreeDirty();
/// Subscribe to drawable update finished event here
virtual void OnSceneSet(Scene* scene);
/// Destroys and creates the tree
@ -243,12 +317,29 @@ private:
/// Invokes the IK solver
void HandleSceneDrawableUpdateFinished(StringHash eventType, VariantMap& eventData);
/// Need these wrapper functions flags of GetFeature/SetFeature can be correctly exposed to the editor
bool GetFeature_JOINT_ROTATIONS() const;
bool GetFeature_TARGET_ROTATIONS() const;
bool GetFeature_UPDATE_ORIGINAL_POSE() const;
bool GetFeature_UPDATE_ACTIVE_POSE() const;
bool GetFeature_USE_ORIGINAL_POSE() const;
bool GetFeature_CONSTRAINTS() const;
bool GetFeature_AUTO_SOLVE() const;
void SetFeature_JOINT_ROTATIONS(bool enable);
void SetFeature_TARGET_ROTATIONS(bool enable);
void SetFeature_UPDATE_ORIGINAL_POSE(bool enable);
void SetFeature_UPDATE_ACTIVE_POSE(bool enable);
void SetFeature_USE_ORIGINAL_POSE(bool enable);
void SetFeature_CONSTRAINTS(bool enable);
void SetFeature_AUTO_SOLVE(bool enable);
PODVector<IKEffector*> effectorList_;
PODVector<IKConstraint*> constraintList_;
ik_solver_t* solver_;
Algorithm algorithm_;
unsigned features_;
bool solverTreeNeedsRebuild_;
bool updateInitialPose_;
bool autoSolveEnabled_;
};
} // namespace Urho3D

Просмотреть файл

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

Просмотреть файл

@ -4,40 +4,39 @@ class IKSolver : public Component
{
enum Algorithm
{
ONE_BONE = 0,
TWO_BONE,
FABRIK
};
Algorithm GetAlgorithm() const;
void SetAlgorithm(Algorithm algorithm);
unsigned GetMaximumIterations() const;
void SetMaximumIterations(unsigned iterations);
float GetTolerance() const;
void SetTolerance(float tolerance);
enum Feature
{
JOINT_ROTATIONS = 0x01,
TARGET_ROTATIONS = 0x02,
UPDATE_ORIGINAL_POSE = 0x04,
UPDATE_ACTIVE_POSE = 0x08,
USE_ORIGINAL_POSE = 0x10,
CONSTRAINTS = 0x20,
AUTO_SOLVE = 0x40
};
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);
bool GetFeature(Feature feature) const;
void SetFeature(Feature feature, bool enable);
void RebuildData();
void RecalculateSegmentLengths();
void CalculateJointRotations();
void Solve();
void ResetToInitialPose();
void UpdateInitialPose();
void MarkSolverTreeDirty();
void ApplyOriginalPoseToScene();
void ApplySceneToOriginalPose();
void ApplyActivePoseToScene();
void ApplySceneToActivePose();
void ApplyOriginalPoseToActivePose();
void DrawDebugGeometry(bool depthTest);
tolua_property__get_set Algorithm algorithm;
tolua_property__get_set unsigned maximumIterations;
tolua_property__get_set float tolerance;
};

Просмотреть файл

@ -81,31 +81,32 @@ function CreateScene()
-- 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);
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;
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");
local spine = jackNode_:GetChild("Bip01_Spine", true)
solver_ = spine:CreateComponent("IKSolver")
-- Two-bone solver is more efficient and more stable than FABRIK (but only
-- works for two bones, obviously).
solver_.algorithm = IKSolver.TWO_BONE
-- Disable auto-solving, which means we can call Solve() manually.
solver_:EnableAutoSolve(false);
solver_:SetFeature(IKSolver.AUTO_SOLVE, 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);
-- Only enable this so the debug draw shows us the pose before solving.
-- This should NOT be enabled for any other reason (it does nothing and is
-- a waste of performance).
solver_:SetFeature(IKSolver.UPDATE_ORIGINAL_POSE, true)
-- Create the camera.
cameraRotateNode_ = scene_:CreateChild("CameraRotate")

Просмотреть файл

@ -99,16 +99,17 @@ void CreateScene()
Node@ spine = jackNode_.GetChild("Bip01_Spine", true);
solver_ = spine.CreateComponent("IKSolver");
// Disable auto-solving, which means we can call Solve() manually.
solver_.autoSolve = false;
// Two-bone solver is more efficient and more stable than FABRIK (but only
// works for two bones, obviously).
solver_.algorithm = IKAlgorithm::TWO_BONE;
// 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;
// Disable auto-solving, which means we can call Solve() manually.
solver_.SetFeature(IKFeature::AUTO_SOLVE, false);
// Only enable this so the debug draw shows us the pose before solving.
// This should NOT be enabled for any other reason (it does nothing and is
// a waste of performance).
solver_.SetFeature(IKFeature::UPDATE_ORIGINAL_POSE, true);
// Create the camera.
cameraRotateNode_ = scene_.CreateChild("CameraRotate");