Where was this used?Where was this used?
Ragdoll prerequisitesRagdoll prerequisites
Animation system needs to be capable of procedural animation (ideal) employ callbacks that allow
manipulation on a per bone basis (less-ideal) Ragdoll system hijacks the skeleton
entirely NITROSystem has an animation system
that employs per bone callbacks Collision detection system
A Simple Particle SystemA Simple Particle System Nodes – points that get updated by
physics Constraints – apply further
modification to the position of a node (or nodes)
(particle system demo)
What is the basic idea?What is the basic idea?
Use a particle system to represent the skeleton
Apply appropriate constraints to ensure valid skeleton configurations
Derive rotation and translation info from the configuration of the nodes
Procedurally animate
NodesNodes
struct Node{
Vec position;Vec lastPos;Vec accel;float invMass;
};
Updating NodesUpdating Nodes
Accumulate Forcesvoid AccumulateForce(Node* n, Vec* F){
n->accel += Scale(F, n->invMass);}void ApplyGlobalAcceleration(Vec* a){
(loop over all nodes n){
n->accel += *a;}
}
Updating NodesUpdating Nodes
Use the Verlet Integration formula to update nodes
Xn+1 = 2Xn – Xn-1 + Ant2 Xn+1 = Xn + (1-f)(Xn – Xn-1) + Ant2
This formula has several advantages Numerically stable Easy to apply constraints
Updating NodesUpdating Nodes
void Integrate(float t2, float f){ (loop over all nodes n) { Vec tmp = n->position;
n->position += (1.0-f)*(n->position-n->lastPos); n->position += n->accel*t2;
n->lastPos = tmp; n->accel = 0;
}}
Applying ConstraintsApplying Constraints
Always apply constraints after updating the nodes
The underlying strategy for applying a constraint is Determine where a node needs to be Put it there Verlet Integration takes care of the rest
ConstraintsConstraints
Preventing floor penetration is an example of a global constraintvoid FloorConstraint(float height){
(loop over all nodes n){
if(n->position.y < height){
n->position.y = height;}
}}
ConstraintsConstraints
Examples of local constraints areenum ConstraintTypes{
ANCHOR,STICK,LINE,PLANE,SPLINE,…
};
ConstraintsConstraints
struct Constraint{
int type;Node* n1;Node* n2;Vec* anchor;float maxLength;float minLength;…
}
ConstraintsConstraints
void ApplyAnchor(Constraint* c){
c->n1->position = *c->anchor;}
Code for applying a stick constraint is included in the handout
Note: Applying 2 constraints to the same node generally results in one of the constraints being violated
ConstraintsConstraints
void ApplyConstraints(int iterations){ for(int i=0; i<iterations; i++) { //apply local constraints (loop over all constraints c) { switch(c->type) { case ANCHOR: ApplyAnchor(c); break; case STICK: ApplyStick(c); break; … } }
//apply global constraints FloorConstraint(0.0f);
}}
ConstraintsConstraints
Some types of constraints require a change in velocity, i.e. Bounce
To change the velocity of the particle you can: Modify the value of the lastPos (instantaneous
impulse) Accumulate a force on the particle that will
change its velocity on the next frame (penalty force)
The Verlet SystemThe Verlet System
The Verlet System is a set of Nodes and Constraintsstruct VerletConfig{
Vec gravAccel;int iterations;float airFriction;…
};
Updating the Verlet SystemUpdating the Verlet System
void UpdateVerlet(VerletConfig* conf, float t){
float t2 = t*t;
//Accumulate ForcesApplyGlobalAcceleration(&conf-
>gravAccel);(Accumulate local forces)
//IntegrateIntegrate(t2, conf->airFriction);
//Apply ConstraintsApplyConstraints(conf->iterations);
}
Animation PrimerAnimation Primer
What do we need in order to create an animation?
What do we need in order to create an animation? We need to calculate a model space
representation of each bone of the animation Translation information is carried by the
position of the node Rotation information can be derived by
the positions of nearby nodes
Future mechanism for obtaining information about rotations
Future mechanism for obtaining information about rotations Allow a Verlet Node to carry information about its
orientation in the form of a quaternionstruct Node{
…Quat rotation;Quat lastRot;Quat angularAccel;…
}; Update the quaternions using the Non-Abelian Verlet
formula Rn+1 = (Wt(Rn(Rn-1)-1)1/t)tRn
BradsBrads
A structure that contains all of the information necessary to calculate a full bone matrix from a set of 4 Verlet Nodes
A Brad is a brass fastener that attaches several sheets of paper together
Brad Moss is a designer that made a suggestion that motivated the idea of this object
BradsBrads
struct Brad{
Node* a;Node* b;Node* c1;Node* c2;Mtx43* bone;int bAxis; //0-X, 1-Y, 2-Zint cAxis;
};
BradsBrads
Node A Node B B Axis Node Ca Node Cb C Axis
Revised Update RoutineRevised Update Routine
void UpdateVerlet(VerletConfig* conf, float t){
float t2 = t*t;
//Accumulate ForcesApplyGlobalAcceleration(&conf->gravAccel);(Accumulate local forces)
//IntegrateIntegrate(t2, conf->airFriction);
//Apply ConstraintsApplyConstraints(conf->iterations);
//Update All BradsUpdateAllBrads();
}
Articulate CollisionArticulate Collision
We used a standard swept sphere collision for each node
Note: Node positions were in world space
The spheres were so small that the swept sphere routine would fail due to imprecission
Fixed Point math primerFixed Point math primer
5.71.2
114570
6.84
0.30.3
90
0.09
FX32FX32
FX32 is a 32 bit Fixed Point data type with 12 bits of decimal precision
As a mnemonic device we will create a unit called FX =4096
A decimal number like 5.7 can be represented in FX32 format as 5.7FX = 23347.2 -> 23347
Every time you multiplying 2 FX32 numbers you accumulate an extra power of FX
Therefore you must divide by FX whenever you multiply
Basically, if ab=c then (aFX*bFX)/FX = cFX
Sample multiplication routineSample multiplication routine
fx32 FX_MUL(fx32 a, fx32 b){
return (a*b)>>12;}
FX32FX32
The range of FX32 is about +/-500000 The smallest number is 1/FX = 0.00024 The smallest FX32 number that can be
multiplied by itself and get a non-zero result is 0.015FX = 64
If you let 1FX represent 1 meter, then the precision limit is a few centimeters
Sphere Intersection TestSphere Intersection Test
BOOL SphereIntersection(VecFx32* center, fx32 radius, VecFx32* point){
fx32 r2, d2;VecFx32 diff;
r2 = FX_MUL(radius, radius);
VEC_Subtract(center, point, &diff);d2 = VEC_DotProduct(&diff, &diff);
return (d2 < r2);}
Radius > 1 Meter – Good results Radius < 1 Meter – Not so good Radius < 0.5 Meters – Pretty crappy
How do we get around this?How do we get around this? Create a new fixed point data type with
higher precision Fx32e has 27 bits of precision Uncertainty of multiplication is on the order of
microns rather than centimeters (a micron is 1000 times smaller than a millimeter)
Use a regular integer multiplication if you can get away with it (don’t divide by FX after you multiply)
Example:Example:
We start with 4 numbers that are related by ab > cd If we try to evaluate this comparison using FX32
multiplication we have (aFX*bFX)/FX > (cFX*dFX)/FX
Truncation error might cause this comparison to evaluate incorrectly
If we try to evaluate this comparison using integer multiplication we have aFX*bFX > cFX*dFX
There is still truncation error, but it is significantly smaller. On the order of millimeters rather than ten centimeters
A Better Sphere Intersection TestA Better Sphere Intersection Test
BOOL SphereIntersection(VecFx32* center, fx32 radius, VecFx32* point){
fx32 r2, d2;VecFx32 diff;
r2 = radius*radius;
VEC_Subtract(center, point, &diff);d2 = diff.x*diff.x + diff.y*diff.y + diff.z*diff.z;
return (d2 < r2);}
Radius > 1mm – Good results Radius < 1mm – Not so good Radius < 0.5mm – Pretty crappy
More than 2 MultsMore than 2 Mults
If you are going to perform more than 2 FX32 mults, you can get some extra mileage out of integer multiplies
If you want to multiply three numbers abc using FX32 multiplication you have ((aFX*bFX)/FX)*cFX)/FX
Instead use integer mults and divide by FX2 at the end
This is (aFX*bFX*cFX)/(FX*FX)
Sample 3 mult RoutineSample 3 mult Routine
fx32 FX_MUL3(fx32 a, fx32 b, fx32 c){
return (a*b*c)>>24;}
Philosophy of using integer multipyPhilosophy of using integer multipy fx32 numbers accumulate a power of FX for every integer
multiply An fx32 can endure 2 powers of FX before overflowing An fx64 can endure 5 powers of FX before overflowing Perform as many integer multiplies as possible before
dividing out the powers of FX If you are comparing two numbers that have accumulated
the same powers of FX (homogeneous), perform the comparison without dividing out the powers of FX
If the numbers you are comparing are not homogeneous, multiply by FX until the powers are equal, then perform the comparison
Try not to overflow
Rotation MathRotation Math
In order for a matrix M to represent a rotation it must be OrthoNormal. This means that its inverse is equal to its transpose.
One direct result of a matrix being OrthoNormal is that det(M) = +/- 1
If det(M) = 1 then the matrix represents a rotation
If det(M) = -1 then the matrix represents a reflection.
Reflections and RotationsReflections and Rotations If you multiply two OrthoNormal reflection
matrices S1, S2, then the product is also OrthoNormal
The determinant of this product is:det(S1S2) = det(S1)det(S2) = (-1)(-1) = 1
The short story? 2 consecutive reflections form a rotation.
Reflection RotationsReflection Rotations
Rotations are unique (almost) If the result of 2 reflections places a
vector where it needs to be, then the reflections are equivalent to the corresponding rotation
Goal: find a reflection algorithm to replace a rotation algorithm
Find 2 ReflectionsFind 2 Reflections
Find 2 ReflectionsFind 2 Reflections
Reflect about a line that bisects the initial and final vectors
Reflect about the final vector
Reflection about a lineReflection about a line
The component of the vector that is parallel to the line does not get reflected
Split the vector into 2 components V = Vline + Vreflect
The reflected vector is V’ = Vline - Vreflect
To find Vline you need the normal n in the direction of the line, then Vline = n*dot(V,n)
To find Vreflect, just take Vreflect = V – Vline
The reflected vector is V’ = 2*n*dot(V,n) - V
Sample Reflection FunctionSample Reflection Function
void ReflectOnLine(VecFx32* in, VecFx32* line, VecFx32* out){
fx32 dp;VecFx32 tmp;
dp = VEC_DotProduct(in, line);dp <<= 1; //dp *= 2VEC_Scale(line, &tmp, dp);
VEC_Subtract(&tmp, in, out);}
Sample 2 Reflection FunctionSample 2 Reflection Functionvoid ReflectOn2Lines(VecFx32* in, VecFx32* line1, VecFx32* line2, VecFx32* out){
fx32 dp;VecFx32 tmp, result;
dp = VEC_DotProduct(in, line1);dp <<= 1;VEC_Scale(line1, &tmp, dp);
VEC_Subtract(&tmp, in, &result);
dp = VEC_DotProduct(&result, line2);dp <<= 1;VEC_Scale(line2, &tmp, dp);
VEC_Subtract(&tmp, &result, out);}
Reflection UsesReflection Uses
Very good for applying a rotation to a small number of vectors
Use it to build rotation matrices, when you know the initial and final vectors. Example: apply the minimum rotation to a
matrix in order to get the “Look” component to point in the right direction
Source code for building orientation matrices is included in the handout
The EndThe End
Thank you to: Sensory Sweep Brad Moss Micah Neilson GDC ‘08
Top Related