Merge remote-tracking branch 'TheComet93/iss1957'
This commit is contained in:
Коммит
0df18d3f54
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/* --------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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");
|
||||
|
|
Загрузка…
Ссылка в новой задаче