-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathblog_transforms.html
59 lines (47 loc) · 5.2 KB
/
blog_transforms.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
<!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 sidebar-active " href="transforms.html">Transform Hierarchies</a></li><a class="sidebar-item" href="blog_transforms_matrices.html">Combining Matrices</a><a class="sidebar-item sidebar-tab" href="blog_transforms_matrix_getters.html">World Space Getters</a></li><a class="sidebar-item sidebar-tab" href="blog_transforms_matrix_setters.html">World Space Setters</a></li><a class="sidebar-item" href="blog_transforms_transforms.html">Combining Transforms</a><a class="sidebar-item sidebar-tab" href="blog_transforms_transform_world.html">World Space</a></li><a class="sidebar-item" href="blog_transforms_summary.html">Summary</a></li></div>
<div id="content">
<h1>Transform Hierarchies</h1>
<p>The transform hierarchy is a core concept for most games / engines. It's pretty easy to slap together something that multiplies some matrices together and works. But to have a versitile transform hierarchy there is some subtlety to the math that often goes overlooked.</p>
<p>In this blog we're going to explore two approaches to building a transform hierarchy, and the unexpected artifacts they produce. Both of these methods will represent a transform as three seperate components: <i>position</i>, <i>rotation</i> and <i>scale</i>. All transforms will be orgonized as a tree / hierarchy. </p>
<p>As expected, the final &qout;world&qout; position of a transform will be the effect of all it's parent's accumulated transformations added to it's own. We will represent a transform in code with a structure similar to the following:</p>
<pre class="prettyprint linenums">struct Transform {
Transform parent; // Reference
Vector position;
Quaternion rotation;
Vector scale;
}</pre>
<p>Graphics API's expect the transformation of an object to be passed as a matrix. Therefore, we need a way to convert a <code>Transform</code> structure to a <i>4x4</i> <code>Matrix</code>. To do this conversion, first extract the basis vectors from the rotation of the transform. Next, scale the basis vectors by the scale of the transform. Finally, stuff the basis vectors and the position vector into a <code>Matrix</code>. The code below demonstrates how to do this:</p>
<pre class="prettyprint linenums">Matrix ToMatrix(Transform transform) {
// First, extract the rotation basis of the transform
Vector x = Vector(1, 0, 0) * transform.rotation; // Vec3 * Quat (right vector)
Vector y = Vector(0, 1, 0) * transform.rotation; // Vec3 * Quat (up vector)
Vector z = Vector(0, 0, 1) * transform.rotation; // Vec3 * Quat (forward vector)
// Next, scale the basis vectors
x = x * transform.scale.x; // Vector * float
y = y * transform.scale.y; // Vector * float
z = z * transform.scale.z; // Vector * float
// Extract the position of the transform
Vector t = transform.position;
// Create matrix
return Matrix(
x.x, x.y, x.z, 0, // X basis (& Scale)
y.x, y.y, y.z, 0, // Y basis (& scale)
z.x, z.y, z.z, 0, // Z basis (& scale)
t.x, t.y, t.z, 1 // Position
);
}</pre>
<p>This makes for the start of a good transform hierarchy. Next, we need to decide how transforms are accumulated, how global or local position / rotation / scale are assigned and how they can be retrieved. The rest of this blog will focus on these topics and the subtle artifacts they introduce.</p>
</div>
</div>
</body>
</html>