-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathblog_animation_poses.html
96 lines (69 loc) · 8.04 KB
/
blog_animation_poses.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Gabor Makes Games</title>
<meta name="author" content="Gabor Szauer">
<link rel="stylesheet" type="text/css" href="css/shared.css"><link rel="stylesheet" type="text/css" href="css/navigation.css"><link rel="stylesheet" type="text/css" href="css/font-raleway.css"><link rel="stylesheet" type="text/css" href="css/font-oxygen.css"><link rel="stylesheet" type="text/css" href="css/font-worksans.css"><link rel="stylesheet" type="text/css" href="css/codepretty/skins/desert.css"><script type="text/javascript" src="js/codepretty/prettify.js"></script><script type="text/javascript" src="js/navigation.js"></script><!-- Global site tag (gtag.js) - Google Analytics --><script async src="https://www.googletagmanager.com/gtag/js?id=UA-96941899-3"></script><script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-96941899-3');</script> <link rel="stylesheet" type="text/css" href="css/blog.css">
</head>
<body onload="MainNavOnLoad();PR.prettyPrint();"> <div class="nav"> <ul class="menu"> <li class="logo"><a href="https://gabormakesgames.com">Gabor Makes Games</a></li> <li class="item"><a id="main-nav-active" href="blog.html">Blog</a></li> <li class="item"><a href="books.html">Books</a></li> <li class="item"><a href="https://github.com/gszauer/">Github</a></li> <li class="item"><a href="https://twitter.com/gszauer">@gszauer</a></li> <li class="toggle"><a href="#">Open Menu</a></li> </ul></div>
<div id="blog">
<div id="sidebar"><a class="sidebar-item sidebar-first sidebar-gap" href="blog.html">Back to blog</a></li><a class="sidebar-item sidebar-first" href="animation.html">Introduction</a><a class="sidebar-item" href="blog_animation_curves.html">Curves</a><a class="sidebar-item" href="blog_animation_pose.html">Pose Generation</a><a class="sidebar-item sidebar-tab" href="blog_animation_frames.html">Frames</a><a class="sidebar-item sidebar-tab" href="blog_animation_tracks.html">Tracks</a><a class="sidebar-item sidebar-tab" href="blog_animation_transformtracks.html">Transform Track</a><a class="sidebar-item sidebar-tab sidebar-active " href="blog_animation_poses.html">Poses</a><a class="sidebar-item sidebar-tab" href="blog_animation_clips.html">Clips</a><a class="sidebar-item sidebar-tab" href="blog_animation_sample.html">Sample</a><a class="sidebar-item" href="blog_animation_skin.html">Skinning</a><a class="sidebar-item sidebar-tab" href="blog_animation_skinspace.html">Skin Space</a><a class="sidebar-item sidebar-tab" href="blog_animation_smoothskin.html">Smooth Skinning</a><a class="sidebar-item" href="blog_animation_blend.html">Pose Modification</a></div>
<div id="content">
<h1>Poses</h1>
<p>Think of a pose as the skeleton of an animated character at a specific point in time. In practice, a pose is a hierarchy (directed acyclic graph or DAG) of transform objects. The state of each transform affects all of its children. We often say that a skeleton is composed of joints or bones, in practice both joints and bones are represented by Transform objects.</p>
<p>There are many strategies for storing the parent child relationship of a Pose, the one used in <strong>Hands-On C++ Game Animation Programming</strong> is keeping two parallel vectors. One vector contains transform objects, the other contains integers that represent the parent of that transform. Not all joints have parents, if a joint doesn’t have a parent, it’s parent value will be nagative.</p>
<pre class="prettyprint linenums">class Pose {
protected:
std::vector<Transform> mJoints;
std::vector<int> mParents;
// Rest of class</pre>
<p>Each of the joints in the pose class exists in it's own local space. To get the world position of a joint, you must combine it with the local transform of all the joints parents up to the root node. That operation looks like this:</p>
<pre class="prettyprint linenums">Transform Pose::GetGlobalTransform(unsigned int index) {
Transform result = mJoints[index];
for (int parent = mParents[index]; parent >= 0; parent = mParents[parent]) {
result = combine(mJoints[parent], result);
}
return result;
}</pre>
<p>The <code>GetMatrixPalette</code> function converts the Pose into a linear array of 4x4 matrices. These matrices can then be used to skin a mesh.</p>
<p>So long as the parent of a joint has a lower index than the joint its-self, we can re-use the <code>mat4</code> that was already calculated for the parent joint. If a parent has a higher index than one of it's children, this optimization falls appare. The code below handles the optimized case and falls back to the unoptimized case if needed.</p>
<pre class="prettyprint linenums">void Pose::GetMatrixPalette(std::vector<mat4>& out) {
int size = (int)Size();
if ((int)out.size() != size) {
out.resize(size);
}
int i = 0;
for (; i < size; ++i) {
int parent = mParents[i];
if (parent > i) {
break;
}
mat4 global = transformToMat4(mJoints[i]);
if (parent >= 0) {
global = out[parent] * global;
}
out[i] = global;
}
for (; i < size; ++i) {
Transform t = GetGlobalTransform(i);
out[i] = transformToMat4(t);
}
}</pre>
<p>When we think of a skeleton or pose it’s easy to think of a model that has one root node and many nodes that branch of it. In practice, it’s not uncommon to have two or three root nodes. This might take some getting used to as its rather unintuitive. 3DCC packages like to package up models in a way that the first node of the skeleton is a root node, but there is also a root node that all skinned meshes are children of.</p>
<p>There are two essential poses for an animated character: the <strong>current pose</strong> (sometimes called <strong>animated pose</strong>) and the <strong>rest pose</strong>. The rest pose is the default configuration of all bones. There are actually many more poses, there is no standard terminology and some or all of these poses can be used in an animation system. The poses that you should be aware of are:</p>
<h2>Bind Pose</h2>
<p>The bind pose is the pose that a character is skinned to. Much more information on this will be covered in the skinning section. The idea is, this pose matches the general shape that a model was modelled as.</p>
<h2>Inverse Bind Pose</h2>
<p>The inverse bind pose is exactly what it sounds like, the inverse of the bind pose. The inverse bind pose is needed for skinning, the skinning section later will cover the inverse bind pose in depth.</p>
<h2>Rest (Reference) Pose</h2>
<p>Ideally, the rest / reference pose should be the same as the bind pose. This isn't always the case tough. The Rest pose is the pose that the model started to be aniamted from. If there are any joints that are not animated, they need to contain the transform of the joint from the rest pose.</p>
<h2>Animated Pose</h2>
<p>This is the pose that a character mesh will deform to match. This pose is the result of sampling an animation clip. More info on this will be provided in the clips section.</p>
<h2>Skeleton</h2>
<p>There are a lot of potential poses to keep track of, some animation systems create a <code>Skeleton</code> class to bundle together the bind pose, inverse bind pose, rest pose and joint naames. A <code>Skeleton</code> class is used in the book.</p>
<p>It is worth re-iterating that <strong>not all animations affect every joint</strong> of a character. This means some animations might not change the value of a joint because we are only storing transform tracks for joints that are animated.</p>
</div>
</div>
</body>
</html>