mirror of
https://github.com/godotengine/godot.git
synced 2024-12-27 11:24:59 +08:00
903 lines
44 KiB
HTML
903 lines
44 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
|
"http://www.w3.org/TR/html4/loose.dtd">
|
|
<html >
|
|
<head><title></title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<meta name="generator" content="TeX4ht (http://www.cse.ohio-state.edu/~gurari/TeX4ht/)">
|
|
<meta name="originator" content="TeX4ht (http://www.cse.ohio-state.edu/~gurari/TeX4ht/)">
|
|
<!-- html -->
|
|
<meta name="src" content="tutorial.tex">
|
|
<meta name="date" content="2009-10-07 00:28:00">
|
|
<link rel="stylesheet" type="text/css" href="tutorial.css">
|
|
</head><body
|
|
>
|
|
<h3 class="sectionHead"><span class="titlemark">1 </span> <a
|
|
id="x1-10001"></a>Introduction to 3D Math</h3>
|
|
<!--l. 27--><p class="noindent" >
|
|
<h4 class="subsectionHead"><span class="titlemark">1.1 </span> <a
|
|
id="x1-20001.1"></a>Introduction</h4>
|
|
<!--l. 29--><p class="noindent" >There are many approaches to understanding the type of 3D math used in video
|
|
games, modelling, ray-tracing, etc. The usual is through vector algebra, matrices, and
|
|
linear transformations and, while they are not completely necesary to understand
|
|
most of the aspects of 3D game programming (from the theorical point of view), they
|
|
provide a common language to communicate with other programmers or
|
|
engineers.
|
|
<!--l. 36--><p class="indent" > This tutorial will focus on explaining all the basic concepts needed for a
|
|
programmer to understand how to develop 3D games without getting too deep into
|
|
algebra. Instead of a math-oriented language, code examples will be given instead
|
|
when possible. The reason for this is that. while programmers may have
|
|
different backgrounds or experience (be it scientific, engineering or self taught),
|
|
code is the most familiar language and the lowest common denominator for
|
|
understanding.
|
|
<!--l. 45--><p class="noindent" >
|
|
<h4 class="subsectionHead"><span class="titlemark">1.2 </span> <a
|
|
id="x1-30001.2"></a>Vectors</h4>
|
|
<!--l. 48--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.1 </span> <a
|
|
id="x1-40001.2.1"></a>Brief Introduction</h5>
|
|
<!--l. 50--><p class="noindent" >When writing 2D games, interfaces and other applications, the typical convention is
|
|
to define coordinates as an <span
|
|
class="ecti-1000">x,y </span>pair, <span
|
|
class="ecti-1000">x </span>representing the horizontal offset and <span
|
|
class="ecti-1000">y </span>the
|
|
vertical one. In most cases, the unit for both is <span
|
|
class="ecti-1000">pixels</span>. This makes sense given the
|
|
screen is just a rectangle in two dimensions.
|
|
<!--l. 56--><p class="indent" > An <span
|
|
class="ecti-1000">x,y </span>pair can be used for two purposes. It can be an absolute position (screen
|
|
cordinate in the previous case), or a relative direction, if we trace an arrow from the
|
|
origin (0,0 coordinates) to it’s position.
|
|
<div class="center"
|
|
>
|
|
<!--l. 60--><p class="noindent" >
|
|
|
|
<div class="tabular">
|
|
<table id="TBL-1" class="tabular"
|
|
cellspacing="0" cellpadding="0"
|
|
><colgroup id="TBL-1-1g"><col
|
|
id="TBL-1-1"><col
|
|
id="TBL-1-2"><col
|
|
id="TBL-1-3"></colgroup><tr
|
|
style="vertical-align:baseline;" id="TBL-1-1-"><td style="white-space:nowrap; text-align:center;" id="TBL-1-1-1"
|
|
class="td11"><img
|
|
src="tutorial0x.png" alt="PIC" class="graphics" width="100.375pt" height="100.375pt" ><!--tex4ht:graphics
|
|
name="tutorial0x.png" src="0_home_red_coding_godot_doc_math_position.eps"
|
|
--></td><td style="white-space:nowrap; text-align:center;" id="TBL-1-1-2"
|
|
class="td11"></td><td style="white-space:nowrap; text-align:center;" id="TBL-1-1-3"
|
|
class="td11"><img
|
|
src="tutorial1x.png" alt="PIC" class="graphics" width="100.375pt" height="100.375pt" ><!--tex4ht:graphics
|
|
name="tutorial1x.png" src="1_home_red_coding_godot_doc_math_direction.eps"
|
|
--></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-1-2-"><td style="white-space:nowrap; text-align:center;" id="TBL-1-2-1"
|
|
class="td11"> <span
|
|
class="ecti-0700">Position </span></td><td style="white-space:nowrap; text-align:center;" id="TBL-1-2-2"
|
|
class="td11"></td><td style="white-space:nowrap; text-align:center;" id="TBL-1-2-3"
|
|
class="td11"> <span
|
|
class="ecti-0700">Direction </span></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-1-3-"><td style="white-space:nowrap; text-align:center;" id="TBL-1-3-1"
|
|
class="td11"> </td>
|
|
</tr></table></div>
|
|
</div>
|
|
<!--l. 67--><p class="indent" > When used as a direction, this pair is called a <span
|
|
class="ecti-1000">vector</span>, and two properties can be
|
|
observed: The first is the <span
|
|
class="ecti-1000">magnitude </span>or <span
|
|
class="ecti-1000">length </span>, and the second is the direction. In
|
|
two dimensions, direction can be an angle. The <span
|
|
class="ecti-1000">magnitude </span>or <span
|
|
class="ecti-1000">length </span>can be computed
|
|
by simply using Pithagoras theorem:
|
|
<div class="center"
|
|
>
|
|
<!--l. 73--><p class="noindent" >
|
|
<div class="tabular"> <table id="TBL-2" class="tabular"
|
|
cellspacing="0" cellpadding="0"
|
|
><colgroup id="TBL-2-1g"><col
|
|
id="TBL-2-1"><col
|
|
id="TBL-2-2"></colgroup><tr
|
|
style="vertical-align:baseline;" id="TBL-2-1-"><td style="white-space:nowrap; text-align:center;" id="TBL-2-1-1"
|
|
class="td11"><img
|
|
src="tutorial2x.png" alt="∘x2-+-y2-" class="sqrt" ></td><td style="white-space:nowrap; text-align:center;" id="TBL-2-1-2"
|
|
class="td11"><img
|
|
src="tutorial3x.png" alt="∘x2-+-y2 +-z2" class="sqrt" ></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-2-2-"><td style="white-space:nowrap; text-align:center;" id="TBL-2-2-1"
|
|
class="td11"> <span
|
|
class="ecti-0700">2D </span></td><td style="white-space:nowrap; text-align:center;" id="TBL-2-2-2"
|
|
class="td11"> <span
|
|
class="ecti-0700">3D </span></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-2-3-"><td style="white-space:nowrap; text-align:center;" id="TBL-2-3-1"
|
|
class="td11"> </td>
|
|
</tr></table></div>
|
|
</div>
|
|
<!--l. 80--><p class="indent" > The direction can be an arbitrary angle from either the <span
|
|
class="ecti-1000">x </span>or <span
|
|
class="ecti-1000">y </span>axis, and could be
|
|
computed by using trigonometry, or just using the usual <span
|
|
class="ecti-1000">atan2 </span>function present in
|
|
most math libraries. However, when dealing with 3D, the direction can’t be described
|
|
as an angle. To separate magnitude and direction, 3D uses the concept of <span
|
|
class="ecti-1000">normal</span>
|
|
<span
|
|
class="ecti-1000">vectors.</span>
|
|
<!--l. 88--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.2 </span> <a
|
|
id="x1-50001.2.2"></a>Implementation</h5>
|
|
<!--l. 90--><p class="noindent" >Vectors are implemented in Godot Engine as a class named <span
|
|
class="ecti-1000">Vector3 </span>for 3D, and as
|
|
both <span
|
|
class="ecti-1000">Vector2</span>, <span
|
|
class="ecti-1000">Point2 </span>or <span
|
|
class="ecti-1000">Size2 </span>in 2D (they are all aliases). They are used for any
|
|
purpose where a pair of 2D or 3D values (described as <span
|
|
class="ecti-1000">x,y </span>or <span
|
|
class="ecti-1000">x,y,z) </span>is needed. This is
|
|
somewhat a standard in most libraries or engines. In the script API, they can be
|
|
instanced like this:
|
|
<!--l. 98-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-5001r1"></a></span>a = Vector3() <br /><span class="label"><a
|
|
id="x1-5002r2"></a></span>a = Vector2( 2.0, 3.4 )
|
|
</div>
|
|
|
|
<!--l. 104--><p class="indent" > Vectors also support the common operators <span
|
|
class="ecti-1000">+, -, / and * </span>for addition,
|
|
substraction, multiplication and division.
|
|
<!--l. 108-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-5003r1"></a></span>a = Vector3(1,2,3) <br /><span class="label"><a
|
|
id="x1-5004r2"></a></span>b = Vector3(4,5,6) <br /><span class="label"><a
|
|
id="x1-5005r3"></a></span>c = Vector3() <br /><span class="label"><a
|
|
id="x1-5006r4"></a></span> <br /><span class="label"><a
|
|
id="x1-5007r5"></a></span>// writing <br /><span class="label"><a
|
|
id="x1-5008r6"></a></span> <br /><span class="label"><a
|
|
id="x1-5009r7"></a></span>c = a + b <br /><span class="label"><a
|
|
id="x1-5010r8"></a></span> <br /><span class="label"><a
|
|
id="x1-5011r9"></a></span>// is the same as writing <br /><span class="label"><a
|
|
id="x1-5012r10"></a></span> <br /><span class="label"><a
|
|
id="x1-5013r11"></a></span>c.x = a.x + b.x <br /><span class="label"><a
|
|
id="x1-5014r12"></a></span>c.y = a.y + b.y <br /><span class="label"><a
|
|
id="x1-5015r13"></a></span>c.z = a.z + b.z <br /><span class="label"><a
|
|
id="x1-5016r14"></a></span> <br /><span class="label"><a
|
|
id="x1-5017r15"></a></span>// both will result in a vector containing (5,7,9). <br /><span class="label"><a
|
|
id="x1-5018r16"></a></span>// the same happens for the rest of the operators.
|
|
</div>
|
|
<!--l. 128--><p class="indent" > Vectors also can perform a wide variety of built-in functions, their most common
|
|
usages will be explored next.
|
|
<!--l. 132--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.3 </span> <a
|
|
id="x1-60001.2.3"></a>Normal Vectors</h5>
|
|
<!--l. 134--><p class="noindent" >Two points ago, it was mentioned that 3D vectors can’t describe their direction as an
|
|
agle (as 2D vectors can). Because of this, <span
|
|
class="ecti-1000">normal vectors </span>become important for
|
|
separating a vector between <span
|
|
class="ecti-1000">direction </span>and <span
|
|
class="ecti-1000">magnitude.</span>
|
|
<!--l. 139--><p class="indent" > A <span
|
|
class="ecti-1000">normal vector </span>is a vector with a <span
|
|
class="ecti-1000">magnitude </span>of <span
|
|
class="ecti-1000">1. </span>This means, no matter where
|
|
the vector is pointing to, it’s length is always <span
|
|
class="ecti-1000">1</span>.
|
|
<div class="tabular">
|
|
<table id="TBL-3" class="tabular"
|
|
cellspacing="0" cellpadding="0"
|
|
><colgroup id="TBL-3-1g"><col
|
|
id="TBL-3-1"></colgroup><tr
|
|
style="vertical-align:baseline;" id="TBL-3-1-"><td style="white-space:nowrap; text-align:center;" id="TBL-3-1-1"
|
|
class="td11"><img
|
|
src="tutorial4x.png" alt="PIC" class="graphics" width="100.375pt" height="100.375pt" ><!--tex4ht:graphics
|
|
name="tutorial4x.png" src="2_home_red_coding_godot_doc_math_normals.eps"
|
|
--></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-3-2-"><td style="white-space:nowrap; text-align:center;" id="TBL-3-2-1"
|
|
class="td11"> <span
|
|
class="ecrm-0700">Normal vectors aroud the origin. </span></td>
|
|
</tr><tr
|
|
style="vertical-align:baseline;" id="TBL-3-3-"><td style="white-space:nowrap; text-align:center;" id="TBL-3-3-1"
|
|
class="td11"> </td> </tr></table>
|
|
</div>
|
|
<!--l. 148--><p class="indent" > Normal vectors have endless uses in 3D graphics programming, so it’s
|
|
recommended to get familiar with them as much as possible.
|
|
<!--l. 152--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.4 </span> <a
|
|
id="x1-70001.2.4"></a>Normalization</h5>
|
|
<!--l. 154--><p class="noindent" >Normalization is the process through which normal vectors are obtained
|
|
from regular vectors. In other words, normalization is used to reduce the
|
|
<span
|
|
class="ecti-1000">magnitude </span>of any vector to <span
|
|
class="ecti-1000">1</span>. (except of course, unless the vector is (0,0,0)
|
|
).
|
|
<!--l. 159--><p class="indent" > To normalize a vector, it must be divided by its magnitude (which should be
|
|
greater than zero):
|
|
<!--l. 163-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-7001r1"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">custom</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">vector</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">created</span> <br /><span class="label"><a
|
|
id="x1-7002r2"></a></span>a = Vector3(4,5,6) <br /><span class="label"><a
|
|
id="x1-7003r3"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">’</span><span
|
|
class="ecti-1000">l</span><span
|
|
class="ecti-1000">’</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">single</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">real</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">number</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">(</span><span
|
|
class="ecti-1000">or</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">scalar</span><span
|
|
class="ecti-1000">)</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">containight</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">length</span> <br /><span class="label"><a
|
|
id="x1-7004r4"></a></span>l = Math.sqrt( a.x<span
|
|
class="cmsy-10">*</span>a.x + a.y<span
|
|
class="cmsy-10">*</span>a.y + a.z<span
|
|
class="cmsy-10">*</span>a.z ) <br /><span class="label"><a
|
|
id="x1-7005r5"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">vector</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">’</span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000">’</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">divided</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">by</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">its</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">length</span><span
|
|
class="ecti-1000">,</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">by</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">performing</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">scalar</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">divide</span> <br /><span class="label"><a
|
|
id="x1-7006r6"></a></span>a = a / l <br /><span class="label"><a
|
|
id="x1-7007r7"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">which</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">same</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">as</span> <br /><span class="label"><a
|
|
id="x1-7008r8"></a></span>a.x = a.x / l <br /><span class="label"><a
|
|
id="x1-7009r9"></a></span>a.y = a.y / l <br /><span class="label"><a
|
|
id="x1-7010r10"></a></span>a.z = a.z / l
|
|
|
|
</div>
|
|
<!--l. 177--><p class="indent" > Vector3 contains two built in functions for normalization:
|
|
<!--l. 180-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-7011r1"></a></span>a = Vector3(4,5,6) <br /><span class="label"><a
|
|
id="x1-7012r2"></a></span>a.normalize() <span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">in</span><span
|
|
class="cmsy-10">-</span><span
|
|
class="ecti-1000">place</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">normalization</span> <br /><span class="label"><a
|
|
id="x1-7013r3"></a></span>b = a.normalized() <span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">returns</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">copy</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">of</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000">,</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">normalized</span>
|
|
</div>
|
|
<!--l. 188--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.5 </span> <a
|
|
id="x1-80001.2.5"></a>Dot Product</h5>
|
|
<!--l. 190--><p class="noindent" >The dot product is, pheraps, the most useful operation that can be applied to 3D
|
|
vectors. In the surface, it’s multiple usages are not very obvious, but in depth it can
|
|
provide very useful information between two vectors (be it direction or just points in
|
|
space).
|
|
<!--l. 195--><p class="indent" > The dot product takes two vectors (<span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b </span>in the example) and returns a scalar
|
|
(single real number):
|
|
<div class="center"
|
|
>
|
|
<!--l. 198--><p class="noindent" >
|
|
<!--l. 199--><p class="noindent" ><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">x</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">x</span></sub> <span
|
|
class="cmr-10">+ </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">y</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">y</span></sub> <span
|
|
class="cmr-10">+ </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">z</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">z</span></sub>
|
|
</div>
|
|
<!--l. 202--><p class="indent" > The same expressed in code:
|
|
<!--l. 205-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-8001r1"></a></span>a = Vector3(...) <br /><span class="label"><a
|
|
id="x1-8002r2"></a></span>b = Vector3(...) <br /><span class="label"><a
|
|
id="x1-8003r3"></a></span> <br /><span class="label"><a
|
|
id="x1-8004r4"></a></span>c = a.x<span
|
|
class="cmsy-10">*</span>b.x + a.y<span
|
|
class="cmsy-10">*</span>b.y + a.z<span
|
|
class="cmsy-10">*</span>b.z <br /><span class="label"><a
|
|
id="x1-8005r5"></a></span> <br /><span class="label"><a
|
|
id="x1-8006r6"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">using</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">built</span><span
|
|
class="cmsy-10">-</span><span
|
|
class="ecti-1000">in</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">dot</span><span
|
|
class="ecti-1000">()</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">function</span> <br /><span class="label"><a
|
|
id="x1-8007r7"></a></span> <br /><span class="label"><a
|
|
id="x1-8008r8"></a></span>c = a.dot(b)
|
|
</div>
|
|
<!--l. 218--><p class="indent" > The dot product presents several useful properties:
|
|
<ul class="itemize1">
|
|
<li class="itemize">If both <span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b </span>parameters to a <span
|
|
class="ecti-1000">dot product </span>are direction vectors, dot
|
|
product will return positive if both point towards the same direction,
|
|
negative if both point towards opposite directions, and zero if they are
|
|
orthogonal (one is perpendicular to the other).
|
|
</li>
|
|
<li class="itemize">If both <span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b </span>parameters to a <span
|
|
class="ecti-1000">dot product </span>are <span
|
|
class="ecti-1000">normalized </span>direction
|
|
vectors, then the dot product will return the cosine of the angle between
|
|
them (ranging from 1 if they are equal, 0 if they are orthogonal, and -1 if
|
|
they are opposed (a == -b)).
|
|
</li>
|
|
<li class="itemize">If <span
|
|
class="ecti-1000">a </span>is a <span
|
|
class="ecti-1000">normalized </span>direction vector and <span
|
|
class="ecti-1000">b </span>is a point, the dot product will
|
|
return the distance from <span
|
|
class="ecti-1000">b </span>to the plane passing through the origin, with
|
|
normal <span
|
|
class="ecti-1000">a (see item about planes)</span>
|
|
|
|
</li>
|
|
<li class="itemize">More uses will be presented later in this tutorial.</li></ul>
|
|
<!--l. 236--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.2.6 </span> <a
|
|
id="x1-90001.2.6"></a>Cross Product</h5>
|
|
<!--l. 238--><p class="noindent" >The <span
|
|
class="ecti-1000">cross product </span>also takes two vectors <span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b</span>, but returns another vector <span
|
|
class="ecti-1000">c </span>that is
|
|
orthogonal to the two previous ones.
|
|
<div class="center"
|
|
>
|
|
<!--l. 242--><p class="noindent" >
|
|
<!--l. 243--><p class="noindent" ><span
|
|
class="cmmi-10">c</span><sub><span
|
|
class="cmmi-7">x</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">x</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">z</span></sub> <span
|
|
class="cmsy-10">- </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">z</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">y</span></sub>
|
|
</div>
|
|
<div class="center"
|
|
>
|
|
<!--l. 246--><p class="noindent" >
|
|
<!--l. 247--><p class="noindent" ><span
|
|
class="cmmi-10">c</span><sub><span
|
|
class="cmmi-7">y</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">z</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">x</span></sub> <span
|
|
class="cmsy-10">- </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">x</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">z</span></sub>
|
|
</div>
|
|
<div class="center"
|
|
>
|
|
<!--l. 250--><p class="noindent" >
|
|
<!--l. 251--><p class="noindent" ><span
|
|
class="cmmi-10">c</span><sub><span
|
|
class="cmmi-7">z</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">x</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">y</span></sub> <span
|
|
class="cmsy-10">- </span><span
|
|
class="cmmi-10">a</span><sub><span
|
|
class="cmmi-7">y</span></sub><span
|
|
class="cmmi-10">b</span><sub><span
|
|
class="cmmi-7">x</span></sub>
|
|
</div>
|
|
<!--l. 254--><p class="indent" > The same in code:
|
|
<!--l. 257-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-9001r1"></a></span>a = Vector3(...) <br /><span class="label"><a
|
|
id="x1-9002r2"></a></span>b = Vector3(...) <br /><span class="label"><a
|
|
id="x1-9003r3"></a></span>c = Vector3(...) <br /><span class="label"><a
|
|
id="x1-9004r4"></a></span> <br /><span class="label"><a
|
|
id="x1-9005r5"></a></span>c.x = a.x<span
|
|
class="cmsy-10">*</span>b.z <span
|
|
class="cmsy-10">-</span> a.z<span
|
|
class="cmsy-10">*</span>b.y <br /><span class="label"><a
|
|
id="x1-9006r6"></a></span>c.y = a.z<span
|
|
class="cmsy-10">*</span>b.x <span
|
|
class="cmsy-10">-</span> a.x<span
|
|
class="cmsy-10">*</span>b.z <br /><span class="label"><a
|
|
id="x1-9007r7"></a></span>c.z = a.x<span
|
|
class="cmsy-10">*</span>b.y <span
|
|
class="cmsy-10">-</span> a.y<span
|
|
class="cmsy-10">*</span>b.x <br /><span class="label"><a
|
|
id="x1-9008r8"></a></span> <br /><span class="label"><a
|
|
id="x1-9009r9"></a></span>// or using the built<span
|
|
class="cmsy-10">-</span>in function <br /><span class="label"><a
|
|
id="x1-9010r10"></a></span> <br /><span class="label"><a
|
|
id="x1-9011r11"></a></span>c = a.cross(b)
|
|
</div>
|
|
<!--l. 273--><p class="indent" > The <span
|
|
class="ecti-1000">cross product </span>also presents several useful properties:
|
|
<ul class="itemize1">
|
|
<li class="itemize">As mentioned, the resulting vector <span
|
|
class="ecti-1000">c </span>is orthogonal to the input vectors <span
|
|
class="ecti-1000">a</span>
|
|
and <span
|
|
class="ecti-1000">b.</span>
|
|
</li>
|
|
<li class="itemize">Since the <span
|
|
class="ecti-1000">cross product </span>is anticommutative, swapping <span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b </span>will result
|
|
in a negated vector <span
|
|
class="ecti-1000">c.</span>
|
|
|
|
</li>
|
|
<li class="itemize">if <span
|
|
class="ecti-1000">a </span>and <span
|
|
class="ecti-1000">b </span>are taken from two of the segmets <span
|
|
class="ecti-1000">AB</span>, <span
|
|
class="ecti-1000">BC </span>or <span
|
|
class="ecti-1000">CA </span>that form a
|
|
3D triangle, the magnitude of the resulting vector divided by 2 is the area
|
|
of that triangle.
|
|
</li>
|
|
<li class="itemize">The direction of the resulting vector <span
|
|
class="ecti-1000">c </span>in the previous triangle example
|
|
determines wether the points A,B and C are arranged in clocwise or
|
|
counter-clockwise order.</li></ul>
|
|
<!--l. 287--><p class="noindent" >
|
|
<h4 class="subsectionHead"><span class="titlemark">1.3 </span> <a
|
|
id="x1-100001.3"></a>Plane</h4>
|
|
<!--l. 290--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.3.1 </span> <a
|
|
id="x1-110001.3.1"></a>Theory</h5>
|
|
<!--l. 292--><p class="noindent" >A plane can be considered as an infinite, flat surface that splits space in two halves,
|
|
usually one named positive and one named negative. In regular mathematics, a plane
|
|
formula is described as:
|
|
<div class="center"
|
|
>
|
|
<!--l. 296--><p class="noindent" >
|
|
<!--l. 297--><p class="noindent" ><span
|
|
class="cmmi-10">ax </span><span
|
|
class="cmr-10">+ </span><span
|
|
class="cmmi-10">by </span><span
|
|
class="cmr-10">+ </span><span
|
|
class="cmmi-10">cz </span><span
|
|
class="cmr-10">+ </span><span
|
|
class="cmmi-10">d</span>
|
|
</div>
|
|
<!--l. 300--><p class="indent" > However, in 3D programming, this form alone is often of little use. For planes to
|
|
become useful, they must be in normalized form.
|
|
<!--l. 303--><p class="indent" > A normalized plane consists of a <span
|
|
class="ecti-1000">normal vector n </span>and a <span
|
|
class="ecti-1000">distance d. </span>To normalize
|
|
a plane, a vector <span
|
|
class="ecti-1000">n </span>and distance <span
|
|
class="ecti-1000">d’ </span>are created this way:
|
|
<!--l. 307--><p class="indent" > <span
|
|
class="cmmi-10">n</span><sub><span
|
|
class="cmmi-7">x</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">a</span>
|
|
<!--l. 309--><p class="indent" > <span
|
|
class="cmmi-10">n</span><sub><span
|
|
class="cmmi-7">y</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">b</span>
|
|
<!--l. 311--><p class="indent" > <span
|
|
class="cmmi-10">n</span><sub><span
|
|
class="cmmi-7">z</span></sub> <span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">c</span>
|
|
<!--l. 313--><p class="indent" > <span
|
|
class="cmmi-10">d</span><span
|
|
class="cmsy-10">′ </span><span
|
|
class="cmr-10">= </span><span
|
|
class="cmmi-10">d</span>
|
|
<!--l. 315--><p class="indent" > Finally, both <span
|
|
class="ecti-1000">n </span>and <span
|
|
class="ecti-1000">d’ </span>are both divided by the magnitude of n.
|
|
<!--l. 318--><p class="indent" > In any case, normalizing planes is not often needed (this was mostly for
|
|
explanation purposes), and normalized planes are useful because they can be created
|
|
and used easily.
|
|
<!--l. 322--><p class="indent" > A normalized plane could be visualized as a plane pointing towards normal <span
|
|
class="ecti-1000">n,</span>
|
|
offseted by <span
|
|
class="ecti-1000">d </span>in the direction of <span
|
|
class="ecti-1000">n</span>.
|
|
<!--l. 325--><p class="indent" > In other words, take <span
|
|
class="ecti-1000">n</span>, multiply it by scalar <span
|
|
class="ecti-1000">d </span>and the resulting point will be part
|
|
of the plane. This may need some thinking, so an example with a 2D normal vector
|
|
(z is 0, so plane is orthogonal to it) is provided:
|
|
<!--l. 330--><p class="indent" > Some operations can be done with normalized planes:
|
|
|
|
<ul class="itemize1">
|
|
<li class="itemize">Given any point <span
|
|
class="ecti-1000">p</span>, the distance from it to a plane can be computed by
|
|
doing: n.dot(p) - d
|
|
</li>
|
|
<li class="itemize">If the resulting distance in the previous point is negative, the point is
|
|
below the plane.
|
|
</li>
|
|
<li class="itemize">Convex polygonal shapes can be defined by enclosing them in planes (the
|
|
physics engine uses this property)</li></ul>
|
|
<!--l. 340--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.3.2 </span> <a
|
|
id="x1-120001.3.2"></a>Implementation</h5>
|
|
<!--l. 342--><p class="noindent" >Godot Engine implements normalized planes by using the <span
|
|
class="ecti-1000">Plane </span>class.
|
|
<!--l. 346-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-12001r1"></a></span>//creates a plane with normal (0,1,0) and distance 5 <br /><span class="label"><a
|
|
id="x1-12002r2"></a></span>p = Plane( Vector3(0,1,0), 5 ) <br /><span class="label"><a
|
|
id="x1-12003r3"></a></span>// get the distance to a point <br /><span class="label"><a
|
|
id="x1-12004r4"></a></span>d = p.distance( Vector3(4,5,6) )
|
|
</div>
|
|
<!--l. 355--><p class="noindent" >
|
|
<h4 class="subsectionHead"><span class="titlemark">1.4 </span> <a
|
|
id="x1-130001.4"></a>Matrices, Quaternions and Coordinate Systems</h4>
|
|
<!--l. 357--><p class="noindent" >It is very often needed to store the location/rotation of something. In 2D, it is often
|
|
enough to store an <span
|
|
class="ecti-1000">x,y </span>location and maybe an angle as the rotation, as that should
|
|
be enough to represent any posible position.
|
|
<!--l. 362--><p class="indent" > In 3D this becomes a little more difficult, as there is nothing as simple as an angle
|
|
to store a 3-axis rotation.
|
|
<!--l. 365--><p class="indent" > The first think that may come to mind is to use 3 angles, one for x, one for y and
|
|
one for z. However this suffers from the problem that it becomes very cumbersome to
|
|
use, as the individual rotations in each axis need to be performed one after another
|
|
(they can’t be performed at the same time), leading to a problem called “gimbal
|
|
lock”. Also, it becomes impossible to accumulate rotations (add a rotation to an
|
|
existing one).
|
|
<!--l. 373--><p class="indent" > To solve this, there are two known diferent approaches that aid in solving
|
|
rotation, <span
|
|
class="ecti-1000">Quaternions </span>and <span
|
|
class="ecti-1000">Oriented Coordinate Systems.</span>
|
|
<!--l. 378--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.4.1 </span> <a
|
|
id="x1-140001.4.1"></a>Oriented Coordinate Systems</h5>
|
|
<!--l. 380--><p class="noindent" ><span
|
|
class="ecti-1000">Oriented Coordinate Systems </span>(<span
|
|
class="ecti-1000">OCS</span>) are a way of representing a coordinate system
|
|
inside the cartesian coordinate system. They are mainly composed of 3 Vectors, one
|
|
for each axis. The first vector is the <span
|
|
class="ecti-1000">x </span>axis, the second the <span
|
|
class="ecti-1000">y </span>axis, and the third is the
|
|
|
|
<span
|
|
class="ecti-1000">z </span>axis. The OCS vectors can be rotated around freely as long as they are kept the
|
|
same length (as changing the length of an axis changes its cale), and as long as they
|
|
remain orthogonal to eachother (as in, the same as the default cartesian system,
|
|
with <span
|
|
class="ecti-1000">y </span>pointing up, <span
|
|
class="ecti-1000">x </span>pointing left and <span
|
|
class="ecti-1000">z </span>pointing front, but all rotated
|
|
together).
|
|
<!--l. 391--><p class="indent" > <span
|
|
class="ecti-1000">Oriented Coordinate Systems </span>are represented in 3D programming as a 3x3 matrix,
|
|
where each row (or column, depending on the implementation) contains one of the
|
|
axis vectors. Transforming a Vector by a rotated OCS Matrix results in the rotation
|
|
being applied to the resulting vector. OCS Matrices can also be multiplied to
|
|
accumulate their transformations.
|
|
<!--l. 397--><p class="indent" > Godot Engine implements OCS Matrices in the <span
|
|
class="ecti-1000">Matrix3 </span>class:
|
|
<!--l. 400-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-14001r1"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000">create</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">3</span><span
|
|
class="ecti-1000">x3</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">matrix</span> <br /><span class="label"><a
|
|
id="x1-14002r2"></a></span>m = Matrix3() <br /><span class="label"><a
|
|
id="x1-14003r3"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000">rotate</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">matrix</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">in</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">y</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">axis</span><span
|
|
class="ecti-1000">,</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">by</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">45</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">degrees</span> <br /><span class="label"><a
|
|
id="x1-14004r4"></a></span>m.rotate( Vector3(0,1,0), Math.deg2rad(45) ) <br /><span class="label"><a
|
|
id="x1-14005r5"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000">transform</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">vector</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">v</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">(</span><span
|
|
class="ecti-1000">xform</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">method</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">used</span><span
|
|
class="ecti-1000">)</span> <br /><span class="label"><a
|
|
id="x1-14006r6"></a></span>v = Vector3(...) <br /><span class="label"><a
|
|
id="x1-14007r7"></a></span>result = m.xform( v )
|
|
</div>
|
|
<!--l. 412--><p class="indent" > However, in most usage cases, one wants to store a translation together with the
|
|
rotation. For this, an <span
|
|
class="ecti-1000">origin </span>vector must be added to the OCS, thus transforming it
|
|
into a 3x4 (or 4x3, depending on preference) matrix. Godot engine implements this
|
|
functionality in the <span
|
|
class="ecti-1000">Transform </span>class:
|
|
<!--l. 419-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-14010r1"></a></span>t = Transform() <br /><span class="label"><a
|
|
id="x1-14011r2"></a></span>//rotate the transform in the y axis, by 45 degrees <br /><span class="label"><a
|
|
id="x1-14012r3"></a></span>t.rotate( Vector3(0,1,0), Math.deg2rad(45) ) <br /><span class="label"><a
|
|
id="x1-14013r4"></a></span>//translate the transform by 5 in the z axis <br /><span class="label"><a
|
|
id="x1-14014r5"></a></span>t.translate( Vector3( 0,0,5 ) ) <br /><span class="label"><a
|
|
id="x1-14015r6"></a></span>//transform a vector v (xform method is used) <br /><span class="label"><a
|
|
id="x1-14016r7"></a></span>v = Vector3(...) <br /><span class="label"><a
|
|
id="x1-14017r8"></a></span>result = t.xform( v )
|
|
</div>
|
|
<!--l. 431--><p class="indent" > Transform contains internally a Matrix3 “basis” and a Vector3 “origin” (which can
|
|
be modified individually).
|
|
<!--l. 435--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.4.2 </span> <a
|
|
id="x1-150001.4.2"></a>Transform Internals</h5>
|
|
<!--l. 437--><p class="noindent" >Internally, the xform() process is quite simple, to apply a 3x3 transform to a vector,
|
|
the transposed axis vectors are used (as using the regular axis vectors will result on
|
|
an inverse of the desired transform):
|
|
<!--l. 442-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-15001r1"></a></span>m = Matrix3(...) <br /><span class="label"><a
|
|
id="x1-15002r2"></a></span>v = Vector3(..) <br /><span class="label"><a
|
|
id="x1-15003r3"></a></span>result = Vector3(...) <br /><span class="label"><a
|
|
id="x1-15004r4"></a></span> <br /><span class="label"><a
|
|
id="x1-15005r5"></a></span>x_axis = m.get_axis(0) <br /><span class="label"><a
|
|
id="x1-15006r6"></a></span>y_axis = m.get_axis(1) <br /><span class="label"><a
|
|
id="x1-15007r7"></a></span>z_axis = m.get_axis(2) <br /><span class="label"><a
|
|
id="x1-15008r8"></a></span> <br /><span class="label"><a
|
|
id="x1-15009r9"></a></span>result.x = Vector3(x_axis.x, y_axis.x, z_axis.x).dot(v) <br /><span class="label"><a
|
|
id="x1-15010r10"></a></span>result.y = Vector3(x_axis.y, y_axis.y, z_axis.y).dot(v) <br /><span class="label"><a
|
|
id="x1-15011r11"></a></span>result.z = Vector3(x_axis.z, y_axis.z, z_axis.z).dot(v) <br /><span class="label"><a
|
|
id="x1-15012r12"></a></span> <br /><span class="label"><a
|
|
id="x1-15013r13"></a></span>// is the same as doing <br /><span class="label"><a
|
|
id="x1-15014r14"></a></span> <br /><span class="label"><a
|
|
id="x1-15015r15"></a></span>result = m.xform(v) <br /><span class="label"><a
|
|
id="x1-15016r16"></a></span> <br /><span class="label"><a
|
|
id="x1-15017r17"></a></span>// if m this was a Transform(), the origin would be added <br /><span class="label"><a
|
|
id="x1-15018r18"></a></span>// like this: <br /><span class="label"><a
|
|
id="x1-15019r19"></a></span> <br /><span class="label"><a
|
|
id="x1-15020r20"></a></span>result = result + t.get_origin()
|
|
</div>
|
|
<!--l. 468--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.4.3 </span> <a
|
|
id="x1-160001.4.3"></a>Using The Transform</h5>
|
|
<!--l. 470--><p class="noindent" >So, it is often desired apply sucessive operations to a transformation. For example,
|
|
let’s a assume that there is a turtle sitting at the origin (the turtle is a logo reference,
|
|
|
|
for those familiar with it). The <span
|
|
class="ecti-1000">y </span>axis is up, and the the turtle’s nose is pointing
|
|
towards the <span
|
|
class="ecti-1000">z </span>axis.
|
|
<!--l. 476--><p class="indent" > The turtle (like many other animals, or vehicles!) can only walk towards the
|
|
direction it’s looking at. So, moving the turtle around a little should be something
|
|
like this:
|
|
<!--l. 481-->
|
|
<div class="lstlisting"><span class="label"><a
|
|
id="x1-16001r1"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">turtle</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">at</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">origin</span> <br /><span class="label"><a
|
|
id="x1-16002r2"></a></span>turtle = Transform() <br /><span class="label"><a
|
|
id="x1-16003r3"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">turtle</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">will</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">walk</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">5</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">units</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">in</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">z</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">axis</span> <br /><span class="label"><a
|
|
id="x1-16004r4"></a></span>turtle.translate( Vector3(0,0,5) ) <br /><span class="label"><a
|
|
id="x1-16005r5"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">turtle</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">eyes</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">a</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">lettuce</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">3</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">units</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">away</span><span
|
|
class="ecti-1000">,</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">will</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">rotate</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">45</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">degrees</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">right</span> <br /><span class="label"><a
|
|
id="x1-16006r6"></a></span>turtle.rotate( Vector3(0,1,0), Math.deg2rad(45) ) <br /><span class="label"><a
|
|
id="x1-16007r7"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">turtle</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">approaches</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">the</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">lettuce</span> <br /><span class="label"><a
|
|
id="x1-16008r8"></a></span>turtle.translate( Vector3(0,0,5) ) <br /><span class="label"><a
|
|
id="x1-16009r9"></a></span><span
|
|
class="ecti-1000">//</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">happy</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">turtle</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">over</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">lettuce</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">is</span><span
|
|
class="ecti-1000"> </span><span
|
|
class="ecti-1000">at</span> <br /><span class="label"><a
|
|
id="x1-16010r10"></a></span>print(turtle.get_origin())
|
|
</div>
|
|
<!--l. 496--><p class="indent" > As can be seen, every new action the turtle takes is based on the previous one it
|
|
took. Had the order of actions been different and the turtle would have never reached
|
|
the lettuce.
|
|
<!--l. 500--><p class="indent" > Transforms are just that, a mean of “accumulating” rotation, translation, scale,
|
|
etc.
|
|
<!--l. 504--><p class="noindent" >
|
|
<h5 class="subsubsectionHead"><span class="titlemark">1.4.4 </span> <a
|
|
id="x1-170001.4.4"></a>A Warning about Numerical Precision</h5>
|
|
<!--l. 506--><p class="noindent" >Performing several actions over a transform will slowly and gradually lead to
|
|
precision loss (objects that draw according to a transform may get jittery, bigger,
|
|
smaller, skewed, etc). This happens due to the nature of floating point numbers. if
|
|
transforms/matrices are created from other kind of values (like a position and
|
|
some angular rotation) this is not needed, but if has been accumulating
|
|
transformations and was never recreated, it can be normalized by calling the
|
|
.orthonormalize() built-in function. This function has little cost and calling it every
|
|
now and then will avoid the effects from precision loss to become visible.
|
|
|
|
</body></html>
|
|
|
|
|
|
|