|
|
@@ -2160,7 +2160,10 @@ create one by attaching the IKEffector component to a node.
|
|
|
|
|
|
\code{.cpp}
|
|
|
Node* handNode = modelNode->GetChild("Hand.R", true);
|
|
|
-IKEffector* effector = handNode->CreateComponent<IKEffector>();
|
|
|
+
|
|
|
+IKEffector* effector = handNode->CreateComponent<IKEffector>(); // C++
|
|
|
+IKEffector@ effector = handNode.CreateComponent("IKEffector"); // AngelScript
|
|
|
+local effector = handNode:CreateComponent("IKEffector") -- Lua
|
|
|
\endcode
|
|
|
|
|
|
You can then give the effector a target position (for example, the position of
|
|
|
@@ -2168,7 +2171,9 @@ an apple) using IKEffector::SetTargetPosition or you can tell the effector to
|
|
|
automatically track the position of a node with IKEffector::SetTargetNode.
|
|
|
|
|
|
\code{.cpp}
|
|
|
-effector->SetTargetPosition(appleNode->GetWorldPosition());
|
|
|
+effector->SetTargetPosition(appleNode->GetWorldPosition()); // C++
|
|
|
+effector.targetPosition = appleNode.worldPosition; // AngelScript
|
|
|
+effector.targetPosition = appleNode.worldPosition -- Lua
|
|
|
\endcode
|
|
|
|
|
|
If enabled, you can also tell the effector to try and match a target rotation
|
|
|
@@ -2186,6 +2191,8 @@ which means all nodes right down to the base node are affected.
|
|
|
|
|
|
\code{.cpp}
|
|
|
effector->SetChainLength(2); // Humans have two bones in their arms
|
|
|
+effector.chainLength = 2; // AngelScript
|
|
|
+effector.chainLength = 2 -- Lua
|
|
|
\endcode
|
|
|
|
|
|
Effectors have a \a weight parameter (use IKEffector::SetWeight) indicating
|
|
|
@@ -2196,8 +2203,8 @@ switch from animation to IK.
|
|
|
|
|
|
\code{.cpp}
|
|
|
effector->SetWeight(SomeSplineFunction()); // C++
|
|
|
-effector.SetWeight(SomeSplineFunction()); // AngelScript
|
|
|
-effector:SetWeight(SomeSplineFunction()); // Lua
|
|
|
+effector.weight = SomeSplineFunction(); // AngelScript
|
|
|
+effector.weight = SomeSplineFunction(); -- Lua
|
|
|
\endcode
|
|
|
|
|
|
If you've played around with the weight parameter, you may have noticed that
|
|
|
@@ -2210,13 +2217,13 @@ IKEffector::SetFeature.
|
|
|
\code{.cpp}
|
|
|
effector->SetFeature(IKEffector::WEIGHT_NLERP, true); // C++
|
|
|
effector.WEIGHT_NLERP = true; // AngelScript
|
|
|
-effector.WEIGHT_NLERP = true // Lua
|
|
|
+effector.WEIGHT_NLERP = true -- Lua
|
|
|
\endcode
|
|
|
|
|
|
Note that the script bindings work a little differently in this regard. The
|
|
|
features can be enabled/disabled directly on the effector object as attributes
|
|
|
rather than having to call SetFeature. This is for convenience (but may be
|
|
|
-changed in the future due to inconsistency).
|
|
|
+changed in the future due to script API inconsistency).
|
|
|
|
|
|
\section iksolvers Solvers
|
|
|
|
|
|
@@ -2232,7 +2239,7 @@ You can create a solver by attaching an IKSolver component to a node:
|
|
|
\code{.cpp}
|
|
|
IKSolver* solver = modelNode->CreateComponent<IKSolver>(); // C++
|
|
|
IKSolver@ solver = modelNode.CreateComponent("IKSolver"); // AngelScript
|
|
|
-local solver = modelNode:CreateComponent("IKSolver") // Lua
|
|
|
+local solver = modelNode:CreateComponent("IKSolver") -- Lua
|
|
|
\endcode
|
|
|
|
|
|
The first thing you'll want to do is select the appropriate algorithm. As of
|
|
|
@@ -2248,7 +2255,7 @@ You can set the algorithm using:
|
|
|
\code{.cpp}
|
|
|
solver->SetAlgorithm(IKSolver::FABRIK); // C++
|
|
|
solver.algorithm = IKAlgorithm::FABRIK; // AngelScript
|
|
|
-solver.algorithm = IKSolver.FABRIK // Lua
|
|
|
+solver.algorithm = IKSolver.FABRIK -- Lua
|
|
|
\endcode
|
|
|
|
|
|
If you chose an iterative algorithm, then you might also want to tweak the
|
|
|
@@ -2267,7 +2274,7 @@ solver->SetTolerance(0.02); // Good value is 100th of your chain length.
|
|
|
solver.maximumIterations = 20; // AngelScript
|
|
|
solver.tolerance = 0.02;
|
|
|
|
|
|
-solver.maximumIterations = 20 // Lua
|
|
|
+solver.maximumIterations = 20 -- Lua
|
|
|
solver.tolerance = 0.02
|
|
|
\endcode
|
|
|
|
|
|
@@ -2293,9 +2300,9 @@ desired if you want to "hook in" right between when the animation has updated,
|
|
|
but before inverse kinematics is calculated.
|
|
|
|
|
|
\code{.cpp}
|
|
|
-solver->SetFeature(IKFeature::AUTO_SOLVE, false); // C++
|
|
|
-solver.AUTO_SOLVE = false; // AngelScript
|
|
|
-solver.AUTO_SOLVE = false // Lua
|
|
|
+solver->SetFeature(IKSolver::AUTO_SOLVE, false); // C++
|
|
|
+solver.AUTO_SOLVE = false; // AngelScript
|
|
|
+solver.AUTO_SOLVE = false -- Lua
|
|
|
\endcode
|
|
|
|
|
|
And here's how you manually invoke the solver.
|
|
|
@@ -2330,12 +2337,79 @@ function HandleSceneDrawableUpdateFinished(eventType, eventData)
|
|
|
end
|
|
|
\endcode
|
|
|
|
|
|
-\subsection iksolverjointrotations JOINT_ROTATIONS
|
|
|
+\subsection iksolverjointrotations IKSolver::JOINT_ROTATIONS
|
|
|
+
|
|
|
+\code{.cpp}
|
|
|
+solver->SetFeature(IKSolver::JOINT_ROTATIONS, false); // C++
|
|
|
+solver.JOINT_ROTATIONS = false; // AngelScript
|
|
|
+solver.JOINT_ROTATIONS = false -- Lua
|
|
|
+\endcode
|
|
|
+
|
|
|
+This is should be enabled if you are using IK on skinned models (or otherwise
|
|
|
+node structures that need rotations). If you don't care about node rotations,
|
|
|
+you can disable this feature and get a small performance boost.
|
|
|
+
|
|
|
+When disabled, all nodes will simply keep their original orientation in the
|
|
|
+world, only their positions will change.
|
|
|
+
|
|
|
+The solver calculates joint rotations after the solution has converged by
|
|
|
+comparing the solved tree with the original tree as a way to compute delta
|
|
|
+angles. These are then multiplied by the original rotations to obtain the
|
|
|
+final joint rotations.
|
|
|
|
|
|
\subsection iksolvertargetrotations TARGET_ROTATIONS
|
|
|
|
|
|
+\code{.cpp}
|
|
|
+solver->SetFeature(IKSolver::TARGET_ROTATIONS, false); // C++
|
|
|
+solver.TARGET_ROTATIONS = false; // AngelScript
|
|
|
+solver.TARGET_ROTATIONS = false -- Lua
|
|
|
+\endcode
|
|
|
+
|
|
|
+Enabling this will cause the orientation of the effector node
|
|
|
+(IKEffector::SetTargetRotation) to be considered during solving. This means
|
|
|
+that the effector node will try to match the rotation of the target as best as
|
|
|
+possible. If the target is out of reach or just within reach, the chain will
|
|
|
+reach out and start to ignore the target rotation in favour of reaching its
|
|
|
+target.
|
|
|
+
|
|
|
+Disabling this feature causes IKEffector::SetTargetRotation to have no effect.
|
|
|
+
|
|
|
\subsection iksolvertrees UPDATE_ORIGINAL_POSE, UPDATE_ACTIVE_POSE, and USE_ORIGINAL_POSE
|
|
|
|
|
|
+These options can be quite confusing to understand.
|
|
|
+
|
|
|
+The solver actually stores \a two \a trees, not one. There is an \a active \a
|
|
|
+tree, which is kind of like the "workbench". The solver uses the active tree
|
|
|
+for its initial condition but also writes the solution back into the active
|
|
|
+tree (i.e. the tree is solved in-place, rather than cloning).
|
|
|
+
|
|
|
+Then there is the \a original \a tree, which is set once during creation and
|
|
|
+then never changed (at least not by default).
|
|
|
+
|
|
|
+You can control which tree the solver should use for its initial condition. If
|
|
|
+you enable USE_ORIGINAL_POSE, then the solver will first copy all
|
|
|
+positions/rotations from the original tree into the active tree before
|
|
|
+solving. Thus, the solution will tend to "snap back" into its original
|
|
|
+configuration if it can.
|
|
|
+
|
|
|
+If you disable USE_ORIGINAL_POSE, then the solver will use the active tree
|
|
|
+instead. The active tree will contain whatever pose was solved last. Thus, the
|
|
|
+solution will tend to be more "continuous".
|
|
|
+
|
|
|
+Very important: Note that the active tree is NOT updated by Urho3D unless you
|
|
|
+enable UPDATE_ACTIVE_POSE (this is enabled by default). If UPDATE_ACTIVE_POSE
|
|
|
+is disabled, then any nodes that have moved outside of IKSolver's control will
|
|
|
+effectively be \a ignored. Thus, if your model is animated, you very likely
|
|
|
+want this enabled.
|
|
|
+
|
|
|
+UPDATE_ORIGINAL_POSE isn't really required, but is here for debugging
|
|
|
+purposes. You can update the original pose either by enabling this feature or
|
|
|
+by explicitely calling IKSolver::ApplySceneToOriginalPose.
|
|
|
+
|
|
|
+\subsection iksolverconstraints CONSTRAINTS
|
|
|
+
|
|
|
+This feature is not yet implemented and is planned for a future release.
|
|
|
+
|
|
|
\page UI User interface
|
|
|
|
|
|
Urho3D implements a simple, hierarchical user interface system based on rectangular elements. The elements provided are:
|