PageRenderTime 86ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/Samples/Direct3D/readme.htm

http://github.com/IronLanguages/main
HTML | 5154 lines | 3500 code | 1606 blank | 48 comment | 0 complexity | c43fd7937dd862529aa3ab5214c8e60d MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. <html>
  2. <head>
  3. <meta http-equiv=Content-Type content="text/html; charset=windows-1252">
  4. <meta name=Generator content="Microsoft Word 12 (filtered)">
  5. <title>3D Graphics in IronPython</title>
  6. <style>
  7. <!--
  8. /* Font Definitions */
  9. @font-face
  10. {font-family:"Cambria Math";
  11. panose-1:2 4 5 3 5 4 6 3 2 4;}
  12. @font-face
  13. {font-family:Tahoma;
  14. panose-1:2 11 6 4 3 5 4 4 2 4;}
  15. @font-face
  16. {font-family:Consolas;
  17. panose-1:2 11 6 9 2 2 4 3 2 4;}
  18. /* Style Definitions */
  19. span.MsoHyperlink
  20. {color:blue;
  21. text-decoration:underline;}
  22. p.Offset, div.Offset
  23. {
  24. mso-style-name: Offset;
  25. margin-top: 0in;
  26. margin-right: .5in;
  27. margin-bottom: 0in;
  28. margin-left: .5in;
  29. margin-bottom: .0001pt;
  30. border: none;
  31. padding: 0in;
  32. font-size: 12.0pt;
  33. font-family: Verdana;
  34. }
  35. p.OffsetHeader, div.OffsetHeader
  36. {
  37. mso-style-name: "Offset Header";
  38. margin-top: 0in;
  39. margin-right: .5in;
  40. margin-bottom: 0in;
  41. margin-left: .5in;
  42. margin-bottom: .0001pt;
  43. text-align: center;
  44. border: none;
  45. padding: 0in;
  46. font-size: 12.0pt;
  47. font-family: Verdana;
  48. font-weight: bold;
  49. }
  50. @page Section1
  51. {size:8.5in 11.0in;
  52. margin:1.0in 1.25in 1.0in 1.25in;}
  53. /* List Definitions */
  54. -->
  55. </style>
  56. <link rel="stylesheet" type="text/css" href="../Samples.css">
  57. </head>
  58. <body lang=EN-US link=blue vlink=purple>
  59. <div class=Section1>
  60. <hr />
  61. <p class="CopyrightText">
  62. Information in this document is subject to change without notice. The example companies,
  63. organizations, products, people, and events depicted herein are fictitious. No association
  64. with any real company, organization, product, person or event is intended or should
  65. be inferred. Complying with all applicable copyright laws is the responsibility
  66. of the user. Without limiting the rights under copyright, no part of this document
  67. may be reproduced, stored in or introduced into a retrieval system, or transmitted
  68. in any form or by any means (electronic, mechanical, photocopying, recording, or
  69. otherwise), or for any purpose, without the express written permission of Microsoft
  70. Corporation.</p>
  71. <p class="CopyrightText">
  72. &nbsp;</p>
  73. <p class="CopyrightText">
  74. Microsoft may have patents, patent applications, trademarked, copyrights, or other
  75. intellectual property rights covering subject matter in this document. Except as
  76. expressly provided in any written license agreement from Microsoft, the furnishing
  77. of this document does not give you any license to these patents, trademarks, copyrights,
  78. or other intellectual property.</p>
  79. <p class="CopyrightText">
  80. &nbsp;</p>
  81. <div class="Section1">
  82. <p class="CopyrightText">© Microsoft Corporation. All rights
  83. reserved.</p>
  84. <p class="CopyrightText">&nbsp;</p>
  85. <p class="CopyrightText">Microsoft, MS-DOS, MS, Windows, Windows NT,
  86. MSDN, Active Directory, BizTalk, SQL Server, SharePoint, Outlook, PowerPoint, FrontPage, Visual Basic,
  87. Visual C++, Visual J++, Visual InterDev, Visual SourceSafe, Visual C#, Visual J#,&nbsp; and Visual Studio
  88. are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other
  89. countries.</p>
  90. <p class="CopyrightText">&nbsp;</p>
  91. <p class="CopyrightText">Other product and company names herein may
  92. be the trademarks of their respective owners.</div>
  93. <hr>
  94. <p class="body">&nbsp;</p>
  95. <p class="CopyrightText">This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  96. copy of the license can be found in the License.html file at the root of this distribution. If
  97. you cannot locate the Apache License, Version 2.0, please send an email to
  98. ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  99. by the terms of the Apache License, Version 2.0.</p>
  100. <p class="CopyrightText">
  101. &nbsp;</p>
  102. <p class="CopyrightText">
  103. &nbsp;</p>
  104. <p class=Normal><b>
  105. <span style='font-size:16.0pt;font-family:"Arial","sans-serif"'>3D
  106. Graphics in IronPython</span></b></p>
  107. <p class=Normal><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'>A
  108. Flexible DirectX Framework in IronPython</span></p>
  109. <p class=Normal><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'>By
  110. Lee Culver</span></p>
  111. <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  112. <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  113. <p class=Toc1><span
  114. class=MsoHyperlink><a href="#_Toc140655628">Getting Started<span
  115. style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
  116. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655629">Introduction<span
  117. style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
  118. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655630">Prerequisites<span
  119. style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
  120. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655631">How to Use
  121. this Tutorial<span style='color:windowtext;display:none;text-decoration:none'> 2</span></a></span></p>
  122. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655632">Objectives
  123. for this Tutorial<span style='color:windowtext;display:none;text-decoration:
  124. none'> </span><span
  125. style='color:windowtext;display:none;text-decoration:none'>2</span></a></span></p>
  126. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655633">Final Note<span
  127. style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
  128. <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655634">Creating the
  129. Framework for a DirectX Application<span style='color:windowtext;display:none;
  130. text-decoration:none'>. </span><span
  131. style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
  132. </div>
  133. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655635">Loading
  134. Assemblies and Importing Them<span style='color:windowtext;display:none;
  135. text-decoration:none'>.. </span>
  136. <span
  137. style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
  138. <div class=Section1>
  139. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655636">The
  140. RenderWindow Class<span style='color:windowtext;display:none;text-decoration:
  141. none'>. </span><span
  142. style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
  143. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655637">Code
  144. Checkpoint 1<span style='color:windowtext;display:none;text-decoration:none'>. 6</span></a></span></p>
  145. <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655638">Your First
  146. DirectX Application (in IronPython)<span style='color:windowtext;display:none;
  147. text-decoration:none'> </span><span
  148. style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
  149. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655639">Setting up
  150. DirectX<span style='color:windowtext;display:none;text-decoration:none'>.. 6</span></a></span></p>
  151. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655640">Code
  152. Checkpoint 2<span style='color:windowtext;display:none;text-decoration:none'>. 9</span></a></span></p>
  153. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655641">An
  154. Introduction to Duck Typing<span style='color:windowtext;display:none;
  155. text-decoration:none'>. </span><span
  156. style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
  157. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655642">Loading
  158. Meshes<span style='color:windowtext;display:none;text-decoration:none'>. 10</span></a></span></p>
  159. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655643">Rendering
  160. Meshes<span style='color:windowtext;display:none;text-decoration:none'>. 11</span></a></span></p>
  161. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655644">Code
  162. Checkpoint 3<span style='color:windowtext;display:none;text-decoration:none'>. 13</span></a></span></p>
  163. <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655645">Cameras,
  164. Moving Objects, and Events<span style='color:windowtext;display:none;
  165. text-decoration:none'>. </span><span
  166. style='color:windowtext;display:none;text-decoration:none'>13</span></a></span></p>
  167. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655646">The Event
  168. System<span style='color:windowtext;display:none;text-decoration:none'>.. 14</span></a></span></p>
  169. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655647">Code
  170. Checkpoint 4<span style='color:windowtext;display:none;text-decoration:none'>. 17</span></a></span></p>
  171. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655648">Exercises<span
  172. style='color:windowtext;display:none;text-decoration:none'>. 17</span></a></span></p>
  173. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655649">Moving
  174. Objects on Screen<span style='color:windowtext;display:none;text-decoration:
  175. none'>. </span><span
  176. style='color:windowtext;display:none;text-decoration:none'>17</span></a></span></p>
  177. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655650">Rotating
  178. Objects on the Screen<span style='color:windowtext;display:none;text-decoration:
  179. none'>. </span><span
  180. style='color:windowtext;display:none;text-decoration:none'>20</span></a></span></p>
  181. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655651">Putting it
  182. All Together<span style='color:windowtext;display:none;text-decoration:none'> 20</span></a></span></p>
  183. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655652">Cameras<span
  184. style='color:windowtext;display:none;text-decoration:none'>. 21</span></a></span></p>
  185. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655653">Code
  186. Checkpoint<span style='color:windowtext;display:none;text-decoration:none'> 23</span></a></span></p>
  187. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655654">Exercises<span
  188. style='color:windowtext;display:none;text-decoration:none'>. 23</span></a></span></p>
  189. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655655">Framework
  190. Optimization<span style='color:windowtext;display:none;text-decoration:none'>. 24</span></a></span></p>
  191. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655656">Adding Other
  192. Types of Objects<span style='color:windowtext;display:none;text-decoration:
  193. none'>. </span><span
  194. style='color:windowtext;display:none;text-decoration:none'>26</span></a></span></p>
  195. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655657">A Better
  196. Main Function<span style='color:windowtext;display:none;text-decoration:none'>. 28</span></a></span></p>
  197. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655658">Code Checkpoint
  198. 6<span style='color:windowtext;display:none;text-decoration:none'>. 29</span></a></span></p>
  199. <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655659">Cool Things
  200. to Do with the Project<span style='color:windowtext;display:none;text-decoration:
  201. none'> </span><span
  202. style='color:windowtext;display:none;text-decoration:none'>30</span></a></span></p>
  203. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655660">Moving
  204. Objects Around, Part I<span style='color:windowtext;display:none;text-decoration:
  205. none'>. </span><span
  206. style='color:windowtext;display:none;text-decoration:none'>30</span></a></span></p>
  207. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655661">Moving
  208. Objects Around, Part II<span style='color:windowtext;display:none;text-decoration:
  209. none'>. </span><span
  210. style='color:windowtext;display:none;text-decoration:none'>31</span></a></span></p>
  211. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655662">Moving
  212. Objects Around, Part III<span style='color:windowtext;display:none;text-decoration:
  213. none'>. </span><span
  214. style='color:windowtext;display:none;text-decoration:none'>33</span></a></span></p>
  215. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655663">Auto
  216. Tracking<span style='color:windowtext;display:none;text-decoration:none'>. 35</span></a></span></p>
  217. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655664">Gravity and
  218. Free Floating Bodies<span style='color:windowtext;display:none;text-decoration:
  219. none'>. </span><span
  220. style='color:windowtext;display:none;text-decoration:none'>36</span></a></span></p>
  221. <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655665">Where to Go
  222. from Here<span style='color:windowtext;display:none;text-decoration:none'>. 40</span></a></span></p>
  223. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655666">Managed
  224. DirectX and XNA<span style='color:windowtext;display:none;text-decoration:none'>.. 40</span></a></span></p>
  225. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655667">IronPython<span
  226. style='color:windowtext;display:none;text-decoration:none'>. 41</span></a></span></p>
  227. <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655668">Extending
  228. the Framework<span style='color:windowtext;display:none;text-decoration:none'>. 41</span></a></span></p>
  229. <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  230. <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  231. <h1><a name="_Toc140655628">Getting Started</a></h1>
  232. <p class=Normal>&nbsp;</p>
  233. <h2><a name="_Toc140655629">Introduction</a></h2>
  234. <p class=Normal>In this tutorial we will be creating a highly flexible
  235. Python graphical framework as an introduction to IronPython.  By the end of
  236. this tutorial you will have a better understanding of IronPython, a basic grasp
  237. of DirectX, and a better understanding of how IronPython utilizes and interacts
  238. with the .NET framework. </p>
  239. <h2><a name="_Toc140655630">Prerequisites</a></h2>
  240. <p class=Normal>This tutorial assumes only a basic knowledge of the Python
  241. syntax and how to use some of its basic constructs (such as lists and tuples). 
  242. No prior knowledge of DirectX or Windows.Forms is required, though a basic
  243. grasp of the .Net platform will go a long way to understanding a few sections
  244. of code.</p>
  245. <p class=Normal>&nbsp;</p>
  246. <p class=Normal>We will be building this application from scratch, and all
  247. portions of the code will be fully explained.  Note that if you are already
  248. familiar with Python, you should probably skip the sections labeled For Python
  249. Beginners.</p>
  250. <p class=Normal>&nbsp;</p>
  251. <p class="body">
  252. Further, you will need the following:</p>
  253. <ul>
  254. <li class="normal">IronPython 2.6 distribution
  255. <ul>
  256. <li>If you are new to IronPython, please go over the tutorial
  257. that comes with this distribution</li>
  258. <li>Download from
  259. <a href="http://ironpython.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=12482">here</a></li>
  260. </ul>
  261. </li>
  262. <li class="normal">DirectX End-user Runtime and a compatible video card<ul>
  263. <li>Download from
  264. <a href="http://www.microsoft.com/games/en-US/aboutgfw/Pages/directx10-a.aspx">here</a>.</li>
  265. </ul>
  266. </li>
  267. </ul>
  268. <p class=Normal>&nbsp;</p><p class=Normal><b><font size="3">Important Note </font></b></p>
  269. <p class=Normal>If you've previously installed a DirectX SDK there's a good chance you might have the
  270. Managed DirectX 2.0 Beta assembly installed.&nbsp; This assembly is incompatible with the Python sample code
  271. distributed with this tutorial and cannot be uninstalled.&nbsp; If you experience difficulties running the
  272. samples (particularly when there are error messages stating that the <i>Matrix</i> class has no
  273. <i>LookAtLH</i> method) and are sure you meet the prerequisites above, please replace all occurrences
  274. of:</p><p class=Normal><b>&nbsp;&nbsp;&nbsp; </b><i>clr.AddReference(Microsoft.DirectX)</i></p>
  275. <p class=Normal>with:</p><p class=Normal><b>&nbsp;&nbsp;&nbsp; </b><i>clr.AddReferenceByName('Microsoft.DirectX, Version=1.0.2902.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')</i></p>
  276. <p class=Normal>&nbsp;</p><p class=Normal>throughout the samples.&nbsp; This change basically means that the
  277. sample code will use Managed DirectX 1 instead of the 2.0 beta which would normally have precedence.</p>
  278. <h2><a name="_Toc140655631">How to Use this Tutorial</a></h2>
  279. <p class=Normal>This tutorial will start from a small set of code and slowly
  280. build a full framework from scratch.  Throughout this tutorial you should be
  281. slowly adding code to a Python source file of your own and watching the results
  282. as we build it.  There is no substitute for actual programming; I highly
  283. recommend that you resist the urge to simply read along.</p>
  284. <p class=Normal>&nbsp;</p>
  285. <h2><a name="_Toc140655632">Objectives for this Tutorial</a></h2>
  286. <p class=Normal>By the end of the first part of this tutorial we will build a framework which will allow you to import any number of DirectX meshes and view
  287. them inside of a .NET form.&nbsp; The framework will provide methods for positioning and rotating objects and the camera, as well as examples of using the
  288. framework to implement more advanced functionality such as having objects move around a track and have cameras auto-track objects in the scene.&nbsp; Lastly, we
  289. will implement a small scale demo which demonstrates Newtons law of universal
  290. gravitation, which will allow you to specify an arbitrary number of free
  291. floating bodies in space (planets), and watch them interact with each other.</p>
  292. <p class=Normal>&nbsp;</p>
  293. <h2><a name="_Toc140655633">Final Note</a></h2>
  294. <p class=Normal>Throughout this tutorial be very careful to keep the same
  295. indentation levels that have been used here.  In Python, white space is a part of
  296. the language, and you must take care to ensure that everything is correctly
  297. aligned or you will receive errors.  I suggest setting your text editor of
  298. choice to use 4 spaces instead of tabs (which I have done for all code
  299. presented here).</p>
  300. <p class=Normal>&nbsp;</p>
  301. <p class=Normal>Lastly, if you are getting compile errors, check to make
  302. sure you have not inserted extra returns in the middle of statements.  The code
  303. may be slightly skewed if you have word wrap turned on.</p>
  304. <p class=Normal>&nbsp;</p>
  305. <h1><a name="_Toc140655634">Creating the Framework for a DirectX Application</a></h1>
  306. <p class=Normal>&nbsp;</p>
  307. <h3><a name="_Toc140655635">Loading Assemblies and Importing Them</a></h3>
  308. <p class=Normal>&nbsp;</p>
  309. <p class=Normal>The first thing we will do is add a few references to
  310. assemblies we will be using.  Create a new file called tutorial.py and add
  311. the following code to it:</p>
  312. <p class=Normal>&nbsp;</p>
  313. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  314. background:silver;margin-left:.25in;margin-right:.25in'>
  315. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  316. border:none;padding:0in'>import clr</p>
  317. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  318. border:none;padding:0in'>clr.AddReferenceByPartialName(&quot;System.Drawing&quot;)</p>
  319. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  320. border:none;padding:0in'>clr.AddReferenceByPartialName(&quot;System.Windows.Forms&quot;)</p>
  321. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  322. border:none;padding:0in'>clr.AddReferenceByPartialName(&quot;Microsoft.DirectX&quot;)</p><p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  323. border:none;padding:0in'>clr.AddReferenceByPartialName(&quot;Microsoft.DirectX.Direct3D&quot;)<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
  324. </span>clr.AddReferenceByPartialName(&quot;Microsoft.DirectX.Direct3DX&quot;)</p>
  325. </div>
  326. <p class=Normal>&nbsp;</p>
  327. <p class=Normal>If you do not add the reference to assemblies that you use,
  328. the IronPython interpreter will not know how to load them.  If you run into
  329. problems where an import statement does not give the expected results, check to
  330. make sure you have referenced all of the appropriate assemblies first.  Note
  331. that IronPython automatically adds the System library.</p><p class=Normal>&nbsp;</p><p class=Normal>If you encounter
  332. difficulties running the snippet of code above, please see the <b>Important Notes</b> section in the
  333. <a href="#_Toc140655630">Prerequisites</a>.</p>
  334. <p class=Normal>&nbsp;</p>
  335. <div>
  336. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  337. <tr>
  338. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  339. padding-bottom:0in;padding-left:0in'>
  340. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  341. margin-left:.5in;margin-right:.5in'>
  342. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  343. padding:0in'>New to IronPython?</p>
  344. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  345. padding:0in'>The clr import is not just a library; it actually changes the
  346. dynamics of several basic constructs.  To see this first hand, open up a
  347. fresh IronPython interpreter and run this code:</p>
  348. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  349. padding:0in'>&nbsp;</p>
  350. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  351. padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>print
  352. dir([])</span></p>
  353. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  354. padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>import
  355. clr</span></p>
  356. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  357. padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>print
  358. dir([])</span></p>
  359. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  360. padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  361. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  362. padding:0in'>Notice that it adds a number of functions which makes IronPython
  363. very similar to .NET (though all of the Python functions are still there). 
  364. This also replaces the standard Python string with the .Net string class. 
  365. Throughout this tutorial I will be using the IronPython methods instead of
  366. the standard Python methods as this produces a more consistent code base.</p>
  367. </div>
  368. </td>
  369. </tr>
  370. </table>
  371. </div>
  372. <br clear=ALL>
  373. <p class=Normal>&nbsp;</p>
  374. <p class=Normal>Next we will import all of the libraries we will need for
  375. this tutorial.</p>
  376. <p class=Normal>&nbsp;</p>
  377. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  378. background:silver;margin-left:.25in;margin-right:.25in'>
  379. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  380. border:none;padding:0in'>import System</p>
  381. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  382. border:none;padding:0in'>from System import Drawing</p>
  383. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  384. border:none;padding:0in'>from System.Windows import Forms</p>
  385. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  386. border:none;padding:0in'>from Microsoft import DirectX</p>
  387. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  388. border:none;padding:0in'>from Microsoft.DirectX import Direct3D</p>
  389. </div>
  390. <p class=Normal>&nbsp;</p>
  391. <p class=Normal>In this tutorial I have kept namespaces and never imported
  392. everything into the global namespace.  This will make the rest of the tutorial
  393. slightly more verbose, but it will also be very explicit as to which functions
  394. we are calling.</p>
  395. <p class=Normal>&nbsp;</p>
  396. <h3><a name="_Toc140655636">The RenderWindow Class</a></h3>
  397. <p class=Normal>&nbsp;</p>
  398. <p class=Normal>The next thing we need to do is to create a new Form to
  399. house our application.  In IronPython we can natively derive from any .Net
  400. class which is not sealed or a value type.  We will define a constructor which
  401. takes in a single argument (a class which contains a Paused attribute). 
  402. Though I am calling this variable sceneManager here, and we will be providing a
  403. class called SceneManager later, we could easily swap it for any other class
  404. which understands how to pause and unpause the system:</p>
  405. <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'>&nbsp;</span></p>
  406. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  407. background:silver;margin-left:.25in;margin-right:.25in'>
  408. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  409. border:none;padding:0in'>class RenderWindow(Forms.Form):</p>
  410. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  411. border:none;padding:0in'>    def __init__(self, sceneManager):</p>
  412. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  413. border:none;padding:0in'>        self.SceneManager = sceneManager</p>
  414. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  415. border:none;padding:0in'>        self.Text = &quot;IronPython Direct3D&quot;</p>
  416. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  417. border:none;padding:0in'>        self.ClientSize = Drawing.Size(640, 480)</p>
  418. </div>
  419. <p class=Normal>&nbsp;</p>
  420. <p class=Normal>Here we have set our own instance variable SceneManager to
  421. be the sceneManager variable that was passed in.  We have also set the Text
  422. and ClientSize properties, which are properties defined by the Forms.Form
  423. class.</p>
  424. <p class=Normal>&nbsp;</p>
  425. <div>
  426. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  427. <tr>
  428. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  429. padding-bottom:0in;padding-left:0in'>
  430. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  431. margin-left:.5in;margin-right:.5in'>
  432. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  433. padding:0in'>New to Python?</p>
  434. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  435. padding:0in'>When defining a class’s method in Python, you always add a
  436. parameter for the object you are currently operating on.  In languages such
  437. as C# or C++ this parameter is implicitly passed as the this parameter. 
  438. Note that we cannot access a classs member methods or data without using
  439. this self parameter.</p>
  440. </div>
  441. </td>
  442. </tr>
  443. </table>
  444. </div>
  445. <br clear=ALL>
  446. <p class=Normal>&nbsp;</p>
  447. <p class=Normal>Next, we want our window to close (and the application to
  448. exit) whenever the escape key is pressed.  We will override the OnKeyDown
  449. method of Forms.Form to do this.</p>
  450. <p class=Normal>&nbsp;</p>
  451. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  452. background:silver;margin-left:.25in;margin-right:.25in'>
  453. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  454. border:none;padding:0in'>    def OnKeyDown(self, args):</p>
  455. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  456. border:none;padding:0in'>        if args.KeyCode ==
  457. System.Windows.Forms.Keys.Escape:</p>
  458. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  459. border:none;padding:0in'>            self.Dispose()</p>
  460. </div>
  461. <p class=Normal>&nbsp;</p>
  462. <p class=Normal>Finally, we want the application to pause and stop rendering
  463. any time the application is minimized or not visible.  For now we will set the
  464. Paused variable on the SceneManager variable we have created.  We will actually
  465. implement the code for this later.</p>
  466. <p class=Normal>&nbsp;</p>
  467. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  468. background:silver;margin-left:.25in;margin-right:.25in'>
  469. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  470. border:none;padding:0in'>    def OnResize(self, args):</p>
  471. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  472. border:none;padding:0in'>        self.SceneManager.Paused = not self.Visible or
  473. \</p>
  474. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  475. border:none;padding:0in'>                (self.WindowState ==
  476. Forms.FormWindowState.Minimized)</p>
  477. </div>
  478. <p class=Normal>&nbsp;</p>
  479. <p class=Normal>Next we are going to add the shell of the SceneManager
  480. class.  The first three methods are very simple (and we will revisit their
  481. implementations later).  The first is the constructor, the second is a method
  482. which sets up DirectX, and the third is the Render method, which is called
  483. from within our render loop.  We will cover these in detail later.</p>
  484. <p class=Normal>&nbsp;</p>
  485. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  486. background:silver;margin-left:.25in;margin-right:.25in'>
  487. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  488. border:none;padding:0in'>class SceneManager(object):</p>
  489. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  490. border:none;padding:0in'>    def __init__(self):</p>
  491. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  492. border:none;padding:0in'>        pass</p>
  493. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  494. border:none;padding:0in'>&nbsp;</p>
  495. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  496. border:none;padding:0in'>    def InitGraphics(self, handle):</p>
  497. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  498. border:none;padding:0in'>        return True</p>
  499. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  500. border:none;padding:0in'>&nbsp;</p>
  501. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  502. border:none;padding:0in'>    def Render(self):</p>
  503. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  504. border:none;padding:0in'>        pass</p>
  505. </div>
  506. <p class=Normal>&nbsp;</p>
  507. <div>
  508. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  509. <tr>
  510. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  511. padding-bottom:0in;padding-left:0in'>
  512. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  513. margin-left:.5in;margin-right:.5in'>
  514. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  515. padding:0in'>New to Python?</p>
  516. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  517. padding:0in'>Whenever we define a method or class which has no body, we
  518. always must use the pass keyword.  This keyword has no meaning outside of
  519. saying empty code block.</p>
  520. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  521. padding:0in'>&nbsp;</p>
  522. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  523. padding:0in'>Note that we have returned True from InitGraphics.  One
  524. important thing to note about Python is that any time you do not return a
  525. value, you are implicitly returning the None type (which is roughly
  526. equivalent to null, with a few exceptions).  This is why you will see some
  527. code paths in Python which have return statements and some which do not. 
  528. This is perfectly valid, and generally accepted as normal.</p>
  529. </div>
  530. </td>
  531. </tr>
  532. </table>
  533. </div>
  534. <br clear=ALL>
  535. <p class=Normal>&nbsp;</p>
  536. <p class=Normal>Next we need to create the render loop for the application. 
  537. This loop will pump the Windows.Forms event queue and call the Render method:</p>
  538. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  539. font-family:Consolas'>&nbsp;</span></p>
  540. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  541. background:silver;margin-left:.25in;margin-right:.25in'>
  542. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  543. border:none;padding:0in'>    def Go(self, window):</p>
  544. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  545. border:none;padding:0in'>        while window.Created:</p>
  546. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  547. border:none;padding:0in'>            self.Render()</p>
  548. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  549. border:none;padding:0in'>            Forms.Application.DoEvents()</p>
  550. </div>
  551. <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'>&nbsp;</span></p>
  552. <p class=Normal>Finally, we will create the main function which creates the
  553. basic objects and starts the render loop.</p>
  554. <p class=Normal>&nbsp;</p>
  555. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  556. background:silver;margin-left:.25in;margin-right:.25in'>
  557. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  558. border:none;padding:0in'>def main():</p>
  559. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  560. border:none;padding:0in'>    sceneManager = SceneManager()</p>
  561. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  562. border:none;padding:0in'>    window = RenderWindow(sceneManager)</p>
  563. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  564. border:none;padding:0in'>&nbsp;</p>
  565. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  566. border:none;padding:0in'>    if not sceneManager.InitGraphics(window.Handle):</p>
  567. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  568. border:none;padding:0in'>        Forms.MessageBox.Show(&quot;Could not init
  569. Direct3D.&quot;)</p>
  570. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  571. border:none;padding:0in'>&nbsp;</p>
  572. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  573. border:none;padding:0in'>    else:</p>
  574. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  575. border:none;padding:0in'>        window.Show()</p>
  576. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  577. border:none;padding:0in'>        sceneManager.Go(window)</p>
  578. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  579. border:none;padding:0in'>&nbsp;</p>
  580. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  581. border:none;padding:0in'>if __name__ == '__main__':</p>
  582. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  583. border:none;padding:0in'>    main()</p>
  584. </div>
  585. <p class=Normal>&nbsp;</p>
  586. <div>
  587. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  588. <tr>
  589. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  590. padding-bottom:0in;padding-left:0in'>
  591. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  592. margin-left:.5in;margin-right:.5in'>
  593. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  594. padding:0in'>New to Python?</p>
  595. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  596. padding:0in'>In a Python script it is ok to have code which runs at the file
  597. level (that is, not inside of a function, not inside of a class).  This code
  598. is run every time the code is imported.  If you want to have the code run
  599. only when the module is explicitly ran (not imported) we use the built in
  600. __name__ variable to determine if we have been explicitly invoked from the
  601. command line.  It will always equal __main__ if we have been run from the
  602. command line.  If the module was loaded using the import statement,
  603. __name__ would be the name of the file otherwise.</p>
  604. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  605. padding:0in'>&nbsp;</p>
  606. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  607. padding:0in'>We have explicitly created a main function so that if this code
  608. is imported into an IronPython console, the main function can be invoked
  609. explicitly.  If we replaced the call to main with the contents of the main
  610. function, there would be no way to run the code outside of actually running
  611. it from the command line (and not importing it).  This is sometimes
  612. desirable, but not in our application.  We will be using this much later in
  613. the tutorial when we actually import our framework from a separate file.</p>
  614. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  615. padding:0in'>&nbsp;</p>
  616. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  617. padding:0in'>Notice also that creating a class does not use the “new” keyword
  618. like C# and C++ do.  The syntax for creating a new class is the exact same
  619. as calling a function.  This is by design.  In many cases we can use function
  620. calls and class constructors interchangeably when we are passing functions as
  621. parameters.</p>
  622. </div>
  623. </td>
  624. </tr>
  625. </table>
  626. </div>
  627. <br clear=ALL>
  628. <p class=Normal>&nbsp;</p>
  629. <p class=Normal>&nbsp;</p>
  630. <h2><a name="_Toc140655637">Code Checkpoint 1</a></h2>
  631. <p class=Normal>&nbsp;</p>
  632. <p class=Normal>Run the code from within IronPython.  You should see an empty
  633. Windows.Forms window.  If you are having problems with the code, you should
  634. compare your source file with checkpoint1.py.</p>
  635. <p class=Normal>&nbsp;</p>
  636. <h1><a name="_Toc140655638">Your First DirectX Application (in IronPython)</a></h1>
  637. <p class=Normal>&nbsp;</p>
  638. <p class=Normal>In this section we will create a basic scene in DirectX.  By
  639. the end of this section, we will have created a DirectX context, rendered a
  640. mesh to the scene, and explored the Python concept of Duck Typing along the
  641. way.</p>
  642. <p class=Normal>&nbsp;</p>
  643. <h2><a name="_Toc140655639">Setting up DirectX</a></h2>
  644. <p class=Normal>This section will deal exclusively with the SceneManager
  645. class.  The first thing we need to do is initialize a few variables which we
  646. will be using in this code.  Be sure to always remove the pass statement when
  647. we add code to empty methods.</p>
  648. <p class=Normal>&nbsp;</p>
  649. <p class=Normal>We will add three variables:  The first will contain our
  650. DirectX device when we have created it.  The second will determine if the
  651. DirectX context is paused (in case we are minimized or hidden).  The third will
  652. be the background color for the DirectX rendering device.</p>
  653. <p class=Normal>&nbsp;</p>
  654. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  655. background:silver;margin-left:.25in;margin-right:.25in'>
  656. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  657. border:none;padding:0in'>class SceneManager(object):</p>
  658. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  659. border:none;padding:0in'>    def __init__(self):</p>
  660. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  661. border:none;padding:0in'>        self.Device = None</p>
  662. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  663. border:none;padding:0in'>        self.Paused = False</p>
  664. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  665. border:none;padding:0in'>        self.Background = System.Drawing.Color.Black</p>
  666. </div>
  667. <p class=Normal>&nbsp;</p>
  668. <p class=Normal>Next we will fill in the InitGraphics method.  This
  669. section of code creates the DirectX device we will use in this tutorial.</p>
  670. <p class=Normal>&nbsp;</p>
  671. <p class=Normal>The first thing we will do is set up the parameters that we
  672. will pass to DirectX.  We will set DirectX to be in windowed mode, set the swap
  673. effect for drawing frames, and enable the auto depth stencil (so that DirectX
  674. tracks the depth of our objects for us).  For more information on the specifics
  675. on these properties and parameters, consult the DirectX documentation.  Since
  676. creating a DirectX device requires a window handle to place it in, we will take
  677. in a handle parameter to use this.</p>
  678. <p class=Normal>&nbsp;</p>
  679. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  680. background:silver;margin-left:.25in;margin-right:.25in'>
  681. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  682. border:none;padding:0in'>    def InitGraphics(self, handle):</p>
  683. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  684. border:none;padding:0in'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params = Direct3D.PresentParameters()</p>
  685. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  686. border:none;padding:0in'>        params.Windowed = True</p>
  687. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  688. border:none;padding:0in'>        params.SwapEffect =
  689. Direct3D.SwapEffect.Discard</p>
  690. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  691. border:none;padding:0in'>        params.EnableAutoDepthStencil = True</p>
  692. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  693. border:none;padding:0in'>        params.AutoDepthStencilFormat =
  694. Direct3D.DepthFormat.D16</p>
  695. </div>
  696. <p class=Normal>&nbsp;</p>
  697. <p class=Normal>Now we need to actually create the Direct3D device.  Note
  698. that we are passing in the handle parameter to the constructor of the Direct3D
  699. device.  This is what tells DirectX to use the window that we have created.  We
  700. also pass in the PresentParameters object we just created.   For more specifics
  701. on the Device creation parameters, consult the managed DirectX documentation. </p>
  702. <p class=Normal>&nbsp;</p>
  703. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  704. background:silver;margin-left:.25in;margin-right:.25in'>
  705. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  706. border:none;padding:0in'>        self.Device = Direct3D.Device(0,
  707. Direct3D.DeviceType.Hardware, handle,</p>
  708. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  709. border:none;padding:0in'>                                     
  710. Direct3D.CreateFlags.SoftwareVertexProcessing,</p>
  711. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  712. border:none;padding:0in'>                                      params)</p>
  713. </div>
  714. <p class=Normal>&nbsp;</p>
  715. <p class=Normal>After creating the device, we need to set a couple of
  716. properties on the Device.  The first thing we need to do is enable the
  717. ZBuffer.  If we did not do this, we could not draw in 3 dimensions (which is
  718. basically the point of this tutorial).  We also need to set the ambient light
  719. to be a full white.  There are multiple types of lighting which DirectX uses:
  720. diffuse, ambient, specular and emissive.  A full treatise on lighting would be
  721. far beyond the scope of this document.  For this tutorial we are going to
  722. simply set the ambient light to full (white), and force every mesh we load to
  723. use ambient lighting.  This, effectively, turns off lighting so we do not have
  724. to worry about it.</p>
  725. <p class=Normal>&nbsp;</p>
  726. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  727. background:silver;margin-left:.25in;margin-right:.25in'>
  728. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  729. border:none;padding:0in'>        self.Device.RenderState.ZBufferEnable =
  730. True</p>
  731. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  732. border:none;padding:0in'>        self.Device.RenderState.Ambient =
  733. Drawing.Color.White</p>
  734. </div>
  735. <p class=Normal>&nbsp;</p>
  736. <p class=Normal>&nbsp;</p>
  737. <p class=Normal>Next we need to set up two of the three matricies which
  738. DirectX uses to render the scene.  The Projection matrix is the most
  739. complicated.  It basically transforms the 3D matrix of objects into a 2
  740. dimensional plane to display on the screen.  We will not have to deal with this
  741. matrix after we have set it the first time.</p>
  742. <p class=Normal>&nbsp;</p>
  743. <p class=Normal>The second matrix DirectX uses is the View matrix which
  744. defines where the camera is looking.  DirectX has some very nice methods which
  745. allow us to deal with cameras in a very natural way.  The
  746. Matrix.LookAtLH takes in three parameters.  The first is the position
  747. of the camera.  The second is what the camera looks at.  The third is the up
  748. vector, which is the up direction for the camera.  Setting the up vector
  749. incorrectly would result in the scene being rotated in strange ways.</p>
  750. <p class=Normal>&nbsp;</p>
  751. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  752. background:silver;margin-left:.25in;margin-right:.25in'>
  753. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  754. border:none;padding:0in'>        self.Device.Transform.Projection =
  755. DirectX.Matrix.PerspectiveFovLH(System.Math.PI/4.0, 1, 1, 100)</p>
  756. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  757. border:none;padding:0in'>        self.Device.Transform.View =
  758. DirectX.Matrix.LookAtLH(DirectX.Vector3(<span style='font-size:10.0pt'>0,
  759. 3, -5</span>), DirectX.Vector3(0, 0, 0), DirectX.Vector3(0, 1, 0))</p>
  760. </div>
  761. <p class=Normal>&nbsp;</p>
  762. <p class=Normal>The rest of our code simply sets the Paused property to be
  763. false.  We will return true to indicate no error occurred.</p>
  764. <p class=Normal>&nbsp;</p>
  765. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  766. background:silver;margin-left:.25in;margin-right:.25in'>
  767. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  768. border:none;padding:0in'>        # ensure we are not paused</p>
  769. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  770. border:none;padding:0in'>        self.Paused = False</p>
  771. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  772. border:none;padding:0in'>        return True</p>
  773. </div>
  774. <p class=Normal>&nbsp;</p>
  775. <p class=Normal>The last thing we need to do is render the scene.  For now
  776. this code is very basic, but we will be adding to it later.  First, we only
  777. render if the Device has already been created and we are not paused.</p>
  778. <p class=Normal>&nbsp;</p>
  779. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 0in 4.0pt;
  780. background:silver;margin-left:.25in;margin-right:.25in'>
  781. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  782. border:none;padding:0in'>    def Render(self):</p>
  783. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  784. border:none;padding:0in'>        if self.Device is None or self.Paused:</p>
  785. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  786. border:none;padding:0in'>            return</p>
  787. </div>
  788. <p class=Normal>&nbsp;</p>
  789. <p class=Normal>At the beginning of each render, we clear the scene with a
  790. specific background color (which we set in the constructor).</p>
  791. <p class=Normal>&nbsp;</p>
  792. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  793. background:silver;margin-left:.25in;margin-right:.25in'>
  794. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  795. border:none;padding:0in'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.Device.Clear(Direct3D.ClearFlags.Target | Direct3D.ClearFlags.ZBuffer,</p>
  796. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  797. border:none;padding:0in'>           &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.Background, </p>
  798. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  799. border:none;padding:0in'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1, </p>
  800. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  801. border:none;padding:0in'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0)</p>
  802. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  803. border:none;padding:0in'>        self.Device.BeginScene()</p>
  804. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  805. border:none;padding:0in'>&nbsp;</p>
  806. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  807. border:none;padding:0in'>        # scene render here</p>
  808. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  809. border:none;padding:0in'>&nbsp;</p>
  810. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  811. border:none;padding:0in'>        self.Device.EndScene()</p>
  812. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  813. border:none;padding:0in'>        self.Device.Present()</p>
  814. </div>
  815. <p class=Normal>&nbsp;</p>
  816. <p class=Normal>Run the application.  We have now created a DirectX render
  817. loop in under 100 lines of IronPython code.  Now we need to make something
  818. useful out of it</p>
  819. <p class=Normal>&nbsp;</p>
  820. <h2><a name="_Toc140655640">Code Checkpoint 2</a></h2>
  821. <p class=Normal>Run your Python application.  At this point you should have
  822. the same window as the previous checkpoint, but with a black DirectX context. 
  823. If you are having problems with the code, you should compare your source file
  824. with checkpoint2.py.</p>
  825. <p class=Normal>&nbsp;</p>
  826. <h2><a name="_Toc140655641">An Introduction to Duck Typing</a></h2>
  827. <p class=Normal>&nbsp;</p>
  828. <p class=Normal>Now that we have an application running, what we need to do is
  829. load a mesh and render it in the scene.  To do this, DirectX needs four pieces
  830. of information: the Mesh, the World Matrix (to position the object), a list of
  831. materials, and a list of textures.  We will provide one extra constraint that
  832. we will use: every object we render in the scene will need a Name so that we
  833. can place it into a dictionary for easy access.</p>
  834. <p class=Normal>&nbsp;</p>
  835. <p class=Normal>A common approach to solving this problem in strongly typed
  836. languages such as C++ and C# is to define an interface which classes must
  837. implement to be used by the SceneManager.  This is a good approach for these
  838. languages, but it can be extraordinarily limiting when using a more flexible
  839. language such as IronPython.  The concept of an interface still exists in
  840. IronPython, but instead of being an explicit interface, it is a more
  841. contractual negotiation between objects which interact with each other.</p>
  842. <p class=Normal>&nbsp;</p>
  843. <p class=Normal>In this case, we need to implement an interface for our
  844. Renderable objects.  The naïve approach (for python programming) would be to
  845. define an explicit class to handle this (this code should not be added to the
  846. project):</p>
  847. <p class=Normal>&nbsp;</p>
  848. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  849. background:silver;margin-left:.25in;margin-right:.25in'>
  850. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  851. border:none;padding:0in'>class IRenderable:</p>
  852. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  853. border:none;padding:0in'>    Name = &quot;&quot;</p>
  854. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  855. border:none;padding:0in'>    Mesh = None</p>
  856. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  857. border:none;padding:0in'>    Textures = []</p>
  858. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  859. border:none;padding:0in'>    Materials = []</p>
  860. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  861. border:none;padding:0in'>&nbsp;</p>
  862. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  863. border:none;padding:0in'>    def GetWorldMatrix(self):</p>
  864. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  865. border:none;padding:0in'>        pass</p>
  866. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  867. border:none;padding:0in'>&nbsp;</p>
  868. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  869. border:none;padding:0in'>def function(renderable):</p>
  870. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  871. border:none;padding:0in'>    if not isinstance(renderable, IRenderable):</p>
  872. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  873. border:none;padding:0in'>        raise TypeError</p>
  874. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  875. border:none;padding:0in'>    # use the instance</p>
  876. </div>
  877. <p class=Normal>&nbsp;</p>
  878. <p class=Normal>This is certainly a valid solution, but completely
  879. unnecessary.  Instead, the Python way is to simply define the required
  880. methods and attributes and only use inheritance when code is actually being
  881. shared between classes.  This can lead to many interesting uses of a class
  882. which were not anticipated by the programmer who wrote the original code.  The
  883. fact that we have not constrained the Name attribute to be a string type
  884. means we can use any hashable object (such as integers, strings, the None type,
  885. classes, instances of classes) to access scene objects.  This style of
  886. programming also has the obvious benefit of less coding overhead.  We do not
  887. have to define the interface in the code, nor do we have to explicitly declare it,
  888. so long as we implement all of the correct functionality in an object.</p>
  889. <p class=Normal>&nbsp;</p>
  890. <p class=Normal>A good example of this would be the RenderWindow class.  We
  891. pass the scene manager into the RenderWindow class so that the window can pause
  892. the scene manager when it is not being shown.  From the RenderWindows
  893. perspective, it doesnt care what object it is given so long as that object
  894. understands what is meant when the programmer sets the Paused variable.  This
  895. can be seen as an implicit interface, containing only the Paused property as
  896. the member of the interface.  As briefly mentioned before, we could easily swap
  897. the object we pass to RenderWindow with a different one if we so choose (so
  898. long as the new object understood what to do with that Paused property),
  899. without ever having to change the RenderWindow class.</p>
  900. <p class=Normal>&nbsp;</p>
  901. <p class=Normal>This is what is meant when Python programmers refer to Duck
  902. Typing.  The idea is if it walks like a duck, quacks like a duck, it might as
  903. well be a duck.  Or to use a less obtuse metaphor, if a class implements
  904. everything that is needed to perform a task, do we really care what class it <b>actually</b>
  905. is, or what interfaces it claims to implement?</p>
  906. <p class=Normal>&nbsp;</p>
  907. <p class=Normal>Along with the general flexibility of the syntax and
  908. constructs of the language, Duck Typing is the primary reason that Python as a
  909. whole is so lightweight.  A change to a method in one class only has to be
  910. reflected in the other classes which rely on this method.</p>
  911. <p class=Normal>&nbsp;</p>
  912. <h2><a name="_Toc140655642">Loading Meshes</a></h2>
  913. <p class=Normal><br>
  914. With this in mind, we will now set out to implement our first iteration at
  915. rendering meshes.  The first thing we will do is create a new class which loads
  916. a mesh and implements the interface we have outlined.  The constructor for our
  917. class will take in three items.  The first is the DirectX device to use to load
  918. it, the second is the name of the object, and the last is the .x file we are
  919. loading.</p>
  920. <p class=Normal>&nbsp;</p>
  921. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  922. background:silver;margin-left:.25in;margin-right:.25in'>
  923. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  924. border:none;padding:0in'>class MeshRenderable(object):</p>
  925. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  926. border:none;padding:0in'>    def __init__(self, device, name, file):<span
  927. style='font-size:10.0pt'> </span></p>
  928. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  929. border:none;padding:0in'><span style='font-size:10.0pt'>        self.Name =
  930. name</span></p>
  931. </div>
  932. <p class=Normal>&nbsp;</p>
  933. <p class=Normal>Now we need to load the actual mesh itself.  The major
  934. parameters we are passing are the Direct3D device, the filename to load the
  935. mesh from, and an empty list of materials which DirectX will populate .  Please
  936. consult the managed DirectX documentation for more information on the other
  937. parameters, if you are curious.</p>
  938. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  939. font-family:Consolas'>&nbsp;</span></p>
  940. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  941. background:silver;margin-left:.25in;margin-right:.25in'>
  942. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  943. border:none;padding:0in'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; materials = clr.Reference[System.Array[Direct3D.ExtendedMaterial]](())<span
  944. style="background-color: #C0C0C0; background-position: 0% 0%"><br>
  945. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>self.Mesh = Direct3D.Mesh.FromFile(file,<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
  946. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  947. </span>Direct3D.MeshFlags.SystemMemory,<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
  948. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  949. </span>device, materials)<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
  950. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>materials = materials.Value</p>
  951. </div>
  952. <p class=Normal>&nbsp;</p>
  953. <p class=Normal>Now that we have loaded the mesh into self.Mesh and loaded a
  954. list of materials into the materials variable, we will build a list of
  955. preloaded Materials and Textures.  As mentioned previously, we are going to use
  956. only the ambient lighting for this tutorial and we implement this by setting
  957. the ambient color to be the diffuse color on the mesh.</p>
  958. <p class=Normal>&nbsp;</p>
  959. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  960. background:silver;margin-left:.25in;margin-right:.25in'>
  961. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  962. border:none;padding:0in'>        self.Materials = []</p>
  963. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  964. border:none;padding:0in'>        self.Textures = []</p>
  965. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  966. border:none;padding:0in'>        for i in range(materials.Length):</p>
  967. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  968. border:none;padding:0in'>            # load material, set color</p>
  969. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  970. border:none;padding:0in'>            material = materials[i].Material3D</p>
  971. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  972. border:none;padding:0in'>            material.AmbientColor =
  973. material.DiffuseColor</p>
  974. </div>
  975. <p class=Normal>     </p>
  976. <p class=Normal>&nbsp;</p>
  977. <p class=Normal>The material itself only provides us with the textures file
  978. name  We actually have to load the texture ourselves.</p>
  979. <p class=Normal>&nbsp;</p>
  980. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  981. background:silver;margin-left:.25in;margin-right:.25in'>
  982. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  983. border:none;padding:0in'>            texture = None</p>
  984. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  985. border:none;padding:0in'>            texFile = materials[i].TextureFilename</p>
  986. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  987. border:none;padding:0in'>            if texFile is not None and texFile.Length:</p>
  988. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  989. border:none;padding:0in'>                texture = Direct3D.TextureLoader.FromFile(device, texFile)</p>
  990. </div>
  991. <p class=Normal>&nbsp;</p>
  992. <p class=Normal>&nbsp;</p>
  993. <p class=Normal>Next we add the material and the texture to their respective
  994. lists.</p>
  995. <p class=Normal>&nbsp;</p>
  996. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  997. background:silver;margin-left:.25in;margin-right:.25in'>
  998. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  999. border:none;padding:0in'>            self.Materials.append(material)</p>
  1000. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1001. border:none;padding:0in'>            self.Textures.append(texture)</p>
  1002. </div>
  1003. <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'>&nbsp;</span></p>
  1004. <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'>&nbsp;</span></p>
  1005. <p class=Normal>Lastly we need to implement the <span style='font-size:10.0pt;
  1006. font-family:Consolas'>GetWorldMatrix</span> method.  For now, we will simply
  1007. return the identity matrix, because we have not implemented any positioning
  1008. code.</p>
  1009. <p class=Normal>&nbsp;</p>
  1010. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1011. background:silver;margin-left:.25in;margin-right:.25in'>
  1012. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1013. border:none;padding:0in'>    def GetWorldMatrix(self):</p>
  1014. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1015. border:none;padding:0in'>        return DirectX.Matrix.Identity</p>
  1016. </div>
  1017. <p class=Normal>&nbsp;</p>
  1018. <h2><a name="_Toc140655643">Rendering Meshes</a></h2>
  1019. <p class=Normal>&nbsp;</p>
  1020. <p class=Normal>Now that we have code for loading a mesh, we need to add
  1021. some code to actually render it.  Along the way, we will want to collect the
  1022. objects into a common place, and the SceneManager class seems like an obvious
  1023. place to do this.</p>
  1024. <p class=Normal>&nbsp;</p>
  1025. <p class=Normal>First, add a new dictionary to the SceneManager class called
  1026. Objects.</p>
  1027. <p class=Normal>&nbsp;</p>
  1028. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1029. background:silver;margin-left:.25in;margin-right:.25in'>
  1030. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1031. border:none;padding:0in'>class SceneManager(object):</p>
  1032. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1033. border:none;padding:0in'>    def __init__(self):</p>
  1034. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1035. border:none;padding:0in'>        self.Device = None</p>
  1036. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1037. border:none;padding:0in'>        self.Paused = False</p>
  1038. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1039. border:none;padding:0in'>        self.Background = System.Drawing.Color.Black</p>
  1040. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1041. border:none;padding:0in'>        self.Objects = {}</p>
  1042. </div>
  1043. <p class=Normal>&nbsp;</p>
  1044. <p class=Normal>To abstract away some of the details of loading a mesh we
  1045. will provide a mechanism for loading textures.  This method will take in the
  1046. name of the object and the filename to load, store the object in the Objects
  1047. dictionary, and return the Mesh (in case the caller actually needed to
  1048. manipulate the object after creating it).</p>
  1049. <p class=Normal>&nbsp;</p>
  1050. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1051. background:silver;margin-left:.25in;margin-right:.25in'>
  1052. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1053. border:none;padding:0in'>    def LoadMesh(self, name, filename):</p>
  1054. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1055. border:none;padding:0in'>        mesh = MeshRenderable(self.Device, name,
  1056. filename)</p>
  1057. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1058. border:none;padding:0in'>        self.Objects[mesh.Name] = mesh</p>
  1059. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1060. border:none;padding:0in'>        return mesh</p>
  1061. </div>
  1062. <p class=Normal>&nbsp;</p>
  1063. <p class=Normal>Now we need to actually render the Objects.  Find the
  1064. comment in the Render method which says scene render here.  We will implement
  1065. this by walking through all of the values in the Objects dictionary.  For each
  1066. object that has the GetWorldMatrix attribute, we will assume that it is a
  1067. mesh and render it as such.</p>
  1068. <p class=Normal>&nbsp;</p>
  1069. <p class=Normal>To accomplish this we will use Pythons generator
  1070. expressions to iterate through the Objects.  To determine which objects have
  1071. the GetWorldMatrix method we will use Pythons hasattr function.  The hasattr
  1072. function returns true if the first parameter passed into it contains an
  1073. attribute which is named the second parameter.  In Python, using reflection is
  1074. as natural as breathing, and learning to use it effectively is the key to
  1075. writing clean and concise code.</p>
  1076. <p class=Normal>&nbsp;</p>
  1077. <p class=Normal>Between the self.Device.BeginScene() and
  1078. self.Device.EndScene() calls, add the following code:</p>
  1079. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1080. font-family:Consolas'>&nbsp;</span></p>
  1081. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1082. background:silver;margin-left:.25in;margin-right:.25in'>
  1083. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1084. border:none;padding:0in'>        for mesh in (x for x in self.Objects.Values if
  1085. hasattr(x, &quot;GetWorldMatrix&quot;)):</p>
  1086. </div>
  1087. <p class=Normal>&nbsp;</p>
  1088. <p class=Normal>There is one catch, we have only determined that the
  1089. GetWorldMatrix exists, not if it is a callable method.  Python implements a
  1090. handy method for determining if an attribute is callable:</p>
  1091. <p class=Normal>&nbsp;</p>
  1092. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1093. background:silver;margin-left:.25in;margin-right:.25in'>
  1094. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1095. border:none;padding:0in'>            if callable(mesh.GetWorldMatrix):</p>
  1096. </div>
  1097. <p class=Normal>&nbsp;</p>
  1098. <p class=Normal>Note we could have added the callable if statement to the
  1099. end of the generator expression by adding the and boolean operator.  As an exercise,
  1100. make this change now.</p>
  1101. <p class=Normal>&nbsp;</p>
  1102. <p class=Normal>The process for rendering a mesh is to specify the world
  1103. matrix, then for each subset of a mesh, set the material and texture then
  1104. render the subset.  We can actually share mesh data between multiple objects by
  1105. simply using the same Direct3D.Mesh objects and setting the world matrix for
  1106. each render.  For now, we will simply render the mesh without this optimization.
  1107. </p>
  1108. <p class=Normal>&nbsp;</p>
  1109. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1110. background:silver;margin-left:.25in;margin-right:.25in'>
  1111. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1112. border:none;padding:0in'>                self.Device.Transform.World =
  1113. mesh.GetWorldMatrix()</p>
  1114. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1115. border:none;padding:0in'>                </p>
  1116. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1117. border:none;padding:0in'>                materials = mesh.Materials</p>
  1118. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1119. border:none;padding:0in'>                textures = mesh.Textures</p>
  1120. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1121. border:none;padding:0in'>                </p>
  1122. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1123. border:none;padding:0in'>                for i in range(len(materials)):</p>
  1124. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1125. border:none;padding:0in'>                    self.Device.Material =
  1126. materials[i]</p>
  1127. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1128. border:none;padding:0in'>&nbsp;</p>
  1129. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1130. border:none;padding:0in'>                    if i &lt; len(textures):</p>
  1131. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1132. border:none;padding:0in'>                        self.Device.SetTexture(0,
  1133. textures[i])</p>
  1134. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1135. border:none;padding:0in'>                    else:</p>
  1136. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1137. border:none;padding:0in'>                        self.Device.SetTexture(0,
  1138. None)</p>
  1139. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1140. border:none;padding:0in'>&nbsp;</p>
  1141. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1142. border:none;padding:0in'>                    mesh.Mesh.DrawSubset(i)</p>
  1143. </div>
  1144. <p class=Normal>&nbsp;</p>
  1145. <p class=Normal>Note here that we have only set the texture if there is
  1146. actually a texture for this particular material.  There must be a material for
  1147. the subset we are drawing, but there does not have to be a texture for that
  1148. particular material.</p>
  1149. <h2><a name="_Toc140655644">Code Checkpoint 3</a></h2>
  1150. <p class=Normal>&nbsp;</p>
  1151. <p class=Normal>Save your work and run the file.  For all of our new
  1152. changes, we dont actually have anything new to show for it yet.  Find the main
  1153. function and add a call to the LoadMesh method as follows:</p>
  1154. <p class=Normal>&nbsp;</p>
  1155. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1156. background:silver;margin-left:.25in;margin-right:.25in'>
  1157. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1158. border:none;padding:0in'>    if not sceneManager.InitGraphics(window.Handle):</p>
  1159. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1160. border:none;padding:0in'>        Forms.MessageBox.Show(&quot;Could not init
  1161. Direct3D.&quot;)</p>
  1162. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1163. border:none;padding:0in'>&nbsp;</p>
  1164. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1165. border:none;padding:0in'>    else:</p>
  1166. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1167. border:none;padding:0in'>        sceneManager.LoadMesh(&quot;tiger&quot;,
  1168. &quot;tiger.x&quot;)</p>
  1169. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1170. border:none;padding:0in'>        window.Show()</p>
  1171. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1172. border:none;padding:0in'>        sceneManager.Go(window)</p>
  1173. </div>
  1174. <p class=Normal><br>
  1175. Now run the application.  You should see a tiger facing the camera.  The next
  1176. thing we will do is implement a flexible event system to move the tiger. 
  1177. Remove the call to LoadMesh from the main function, as that was only to
  1178. demonstrate that we are indeed loading the mesh.</p>
  1179. <h1><a name="_Toc140655645">Cameras, Moving Objects, and Events</a></h1>
  1180. <p class=Normal>&nbsp;</p>
  1181. <p class=Normal>In this section of the tutorial we will be exploring an event
  1182. system which allows us to manipulate the scene by responding to events such as
  1183. the scene being created or a frame being rendered.  We will also add
  1184. positioning code and rotational code, so you can arbitrarily move and rotate
  1185. objects.  Lastly, we will implement a camera system which allows us to set
  1186. cameras throughout our scene and switch between them at will.</p>
  1187. <p class=Normal>&nbsp;</p>
  1188. <h2><a name="_Toc140655646">The Event System</a></h2>
  1189. <p class=Normal>&nbsp;</p>
  1190. <p class=Normal>Though our event system will be able to handle any arbitrary
  1191. events we create later, we will start with three basic events: OnSceneCreate, OnSceneBegin, and OnFrame.  The OnFrame method will be called
  1192. just before rendering a frame.  This will allow us to reposition objects (or do
  1193. other updates) which require action every frame.  The OnSceneCreate method will
  1194. be called before the scene starts rendering, and it is used for creating and
  1195. adding objects (such as meshes and cameras) to the scene.  The OnSceneBegin
  1196. event will be fired right after the OnSceneCreate event has finished.  The
  1197. OnSceneBegin method is used to move and position objects which have not been
  1198. created by that event handler.  This is an important distinction since we are
  1199. not guaranteeing anything about the order in which event handlers of the same
  1200. type are called.  By offering these two events separately, we can allow a
  1201. single listener to create an object that another object uses without worrying
  1202. about the order in which events are passed.</p>
  1203. <p class=Normal>&nbsp;</p>
  1204. <p class=Normal>The OnSceneCreate and OnSceneBegin events will accept a
  1205. single parameter (the scene manager who is calling them), and the OnFrame events
  1206. will accept a parameter for the number of seconds since the last frame was
  1207. rendered.  Lastly, events return values indicating whether or not they have completed their
  1208. operations.  By returning True an
  1209. object signifies that it should no longer receive any events.</p>
  1210. <p class=Normal>&nbsp;</p>
  1211. <p class=Normal>To implement this, we will start by creating a dictionary
  1212. which holds the event name as a key, and a list of event handlers as the
  1213. value.  Find the SceneManager constructor and add this code:</p>
  1214. <p class=Normal>&nbsp;</p>
  1215. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1216. background:silver;margin-left:.25in;margin-right:.25in'>
  1217. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1218. border:none;padding:0in'>        self.Listeners = {&quot;OnFrame&quot; : [],</p>
  1219. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1220. border:none;padding:0in'>                          &quot;OnSceneBegin&quot; :
  1221. [],</p>
  1222. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1223. border:none;padding:0in'>                          &quot;OnSceneCreate&quot; :
  1224. []}</p>
  1225. </div>
  1226. <p class=Normal>&nbsp;</p>
  1227. <p class=Normal>We will add two methods for adding listeners to the class. 
  1228. The first is the AddListenerByName method, which takes in an event listener and
  1229. a name of the event.  The implementation is very straight-forward (raising a
  1230. TypeError if we are given an event listener which is not callable):</p>
  1231. <p class=Normal>&nbsp;</p>
  1232. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1233. font-family:Consolas'>    </span></p>
  1234. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1235. background:silver;margin-left:.25in;margin-right:.25in'>
  1236. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1237. border:none;padding:0in'>    def AddListenerByName(self, key, function):</p>
  1238. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1239. border:none;padding:0in'>        if not callable(function):</p>
  1240. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1241. border:none;padding:0in'>            raise TypeError, &quot;Object not
  1242. callable.&quot;</p>
  1243. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1244. border:none;padding:0in'>&nbsp;</p>
  1245. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1246. border:none;padding:0in'>        self.Listeners[key].Add(function)</p>
  1247. </div>
  1248. <p class=Normal>&nbsp;</p>
  1249. <p class=Normal>This is good to have, but this would quickly become tedious
  1250. to use if we had a large number of objects, each registering for multiple
  1251. events.  What we would like to do is to inspect an object, and register their
  1252. event handlers based on the function names.</p>
  1253. <p class=Normal>&nbsp;</p>
  1254. <p class=Normal>This is extraordinarily easy to do in Python.  We will walk
  1255. through each of the keys in the Listeners dictionary, and if the object has a
  1256. function by that name, we will register it as an event listener:</p>
  1257. <p class=Normal>&nbsp;</p>
  1258. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1259. background:silver;margin-left:.25in;margin-right:.25in'>
  1260. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1261. border:none;padding:0in'>    def AddListener(self, obj):</p>
  1262. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1263. border:none;padding:0in'>        for key in self.Listeners.Keys:</p>
  1264. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1265. border:none;padding:0in'>            if hasattr(obj, str(key)):</p>
  1266. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1267. border:none;padding:0in'>                function = GetAttr(obj, str(key))</p>
  1268. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1269. border:none;padding:0in'>                self.AddListenerByName(key, function)</p>
  1270. </div>
  1271. <p class=Normal>&nbsp;</p>
  1272. <p class=Normal>Only four lines of actual code to walk the object and pull
  1273. out the functions we are looking for.</p>
  1274. <p class=Normal>&nbsp;</p>
  1275. <p class=Normal>Now we need to add a method for actually firing the
  1276. events.  As I mentioned earlier, each event will return whether or not it has
  1277. completed its operations.  In practice this means that if we are complete we
  1278. return True, if we are not complete (or if the listener never completesif it
  1279. should be registered for the entire lifetime of the SceneManager), then we can
  1280. either return False, or simply not return anything at all.  Since all methods
  1281. implicitly return the None type, and since None always evaluates to false in a
  1282. Boolean expression, this allows us to implicitly return false when we do not
  1283. return at all.  While difficult to explain the reasoning for this concisely, it
  1284. will make our listener code easier to write.</p>
  1285. <p class=Normal>&nbsp;</p>
  1286. <p class=Normal>To implement a method which fires events, then removes
  1287. objects which return True, we will call every function in the appropriate
  1288. listener list, keeping track of each function which returned true.</p>
  1289. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1290. background:silver;margin-left:.25in;margin-right:.25in'>
  1291. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1292. border:none;padding:0in'>    def __FireEvent(self, event, arg):</p>
  1293. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1294. border:none;padding:0in'>        listeners = self.Listeners[event]</p>
  1295. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1296. border:none;padding:0in'>        toRemove = []</p>
  1297. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1298. border:none;padding:0in'>        for f in listeners:</p>
  1299. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1300. border:none;padding:0in'>            if f(arg):</p>
  1301. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1302. border:none;padding:0in'>                toRemove.Add(f) </p>
  1303. </div>
  1304. <p class=Normal>        </p>
  1305. <p class=Normal>Now we walk the toRemove list and remove them.</p>
  1306. <p class=Normal>&nbsp;</p>
  1307. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1308. background:silver;margin-left:.25in;margin-right:.25in'>
  1309. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1310. border:none;padding:0in'>        for f in toRemove:</p>
  1311. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1312. border:none;padding:0in'>            listeners.Remove(f)</p>
  1313. </div>
  1314. <p class=Normal>&nbsp;</p>
  1315. <div>
  1316. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  1317. <tr>
  1318. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  1319. padding-bottom:0in;padding-left:0in'>
  1320. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1321. margin-left:.5in;margin-right:.5in'>
  1322. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  1323. padding:0in'>New to Python?</p>
  1324. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  1325. padding:0in'>Whenever you prefix a method or variable with two underscores,
  1326. that method or variable is hidden.  This is roughly equivalent to declaring
  1327. it as private in a C++ or C#.  The major difference is the variable or
  1328. method is still actually there, its just difficult to modify unless you
  1329. understand the internals of how Python hides it.  This is implemented such
  1330. that it is possible to have a friend class as you do in other languages
  1331. (allowing you to access an objects private data), but at the same time
  1332. letting you know that you are doing something you really should try not to.</p>
  1333. </div>
  1334. </td>
  1335. </tr>
  1336. </table>
  1337. </div>
  1338. <br clear=ALL>
  1339. <p class=Normal>&nbsp;</p>
  1340. <p class=Normal>Lastly, we need to actually call the __FireEvent method to
  1341. send the events.  To do this we will modify SceneManagers Go method, so clear
  1342. out the contents of it.  Well start by firing the scene creation and scene
  1343. begin events:</p>
  1344. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1345. font-family:Consolas'>&nbsp;</span></p>
  1346. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1347. background:silver;margin-left:.25in;margin-right:.25in'>
  1348. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1349. border:none;padding:0in'>    def Go(self, window):</p>
  1350. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1351. border:none;padding:0in'>        self.__FireEvent(&quot;OnSceneCreate&quot;,
  1352. self)</p>
  1353. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1354. border:none;padding:0in'>        self.__FireEvent(&quot;OnSceneBegin&quot;,
  1355. self)</p>
  1356. </div>
  1357. <p class=Normal>&nbsp;</p>
  1358. <p class=Normal>Next we need to add code for firing the OnFrame event.  The
  1359. trick is we have to keep track of how long it has been since we last fired a
  1360. frame event.  Well use the System.Environment.TickCount (measured in
  1361. milliseconds) to do this.</p>
  1362. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1363. font-family:Consolas'>&nbsp;</span></p>
  1364. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1365. background:silver;margin-left:.25in;margin-right:.25in'>
  1366. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1367. border:none;padding:0in'>        lastTick = System.Environment.TickCount</p>
  1368. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1369. border:none;padding:0in'>        </p>
  1370. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1371. border:none;padding:0in'>        while window.Created:</p>
  1372. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1373. border:none;padding:0in'>            currTick = System.Environment.TickCount</p>
  1374. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1375. border:none;padding:0in'>            self.__FireEvent(&quot;OnFrame&quot;,
  1376. (currTick-lastTick)/1000.0)</p>
  1377. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1378. border:none;padding:0in'>            lastTick = currTick</p>
  1379. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1380. border:none;padding:0in'>            </p>
  1381. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1382. border:none;padding:0in'>            self.Render()</p>
  1383. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1384. border:none;padding:0in'>            Forms.Application.DoEvents()</p>
  1385. </div>
  1386. <p class=Normal>&nbsp;</p>
  1387. <p class=Normal>Note that the extra .0 in the __FireEvent call is very
  1388. important.  Both currTick and lastTick are integer values and currTick-lastTick
  1389. is certainly less than 1000.  If we did not divide it by a floating point
  1390. number we would always be passing 0 (the integer division value).</p>
  1391. <p class=Normal>&nbsp;</p>
  1392. <p class=Normal>We will now create a very simple event listener which adds
  1393. our tiger mesh to the scene.  We will be expanding this event listener in later
  1394. sections.</p>
  1395. <p class=Normal>&nbsp;</p>
  1396. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1397. background:silver;margin-left:.25in;margin-right:.25in'>
  1398. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1399. border:none;padding:0in'>class SceneCreator(object):</p>
  1400. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1401. border:none;padding:0in'>    def OnSceneCreate(self, sm):</p>
  1402. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1403. border:none;padding:0in'>        self.Tiger = sm.LoadMesh(&quot;Tiger&quot;,
  1404. &quot;tiger.x&quot;)</p>
  1405. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1406. border:none;padding:0in'>        return True</p>
  1407. </div>
  1408. <p class=Normal>&nbsp;</p>
  1409. <p class=Normal>Now, we need to register this listener.  In general, we
  1410. could have many listeners in our application.  What we want to do now is to
  1411. make the main function take in an optional parameter which defines a sequence
  1412. of objects to register as listeners.  Find the main function and change its
  1413. function header:</p>
  1414. <p class=Normal>&nbsp;</p>
  1415. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1416. background:silver;margin-left:.25in;margin-right:.25in'>
  1417. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1418. border:none;padding:0in'>def main(listeners = [SceneCreator]):</p>
  1419. </div>
  1420. <p class=Normal>&nbsp;</p>
  1421. <p class=Normal>This creates an optional parameter listeners which, by
  1422. default, contains our SceneCreator class.</p>
  1423. <p class=Normal>&nbsp;</p>
  1424. <p class=Normal>As you see, we have passed in a class instead of an instance
  1425. of the class.  What we want is for our function to iterate over the sequence we
  1426. are given, adding instances of the objects to the scene manager.  If an entry
  1427. in the sequence is a class, we want to create an instance of it and add it.  Find
  1428. the else clause in the main function, and add the following code:</p>
  1429. <p class=Normal>&nbsp;</p>
  1430. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1431. background:silver;margin-left:.25in;margin-right:.25in'>
  1432. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1433. border:none;padding:0in'>    else:</p>
  1434. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1435. border:none;padding:0in'>        for obj in listeners:</p>
  1436. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1437. border:none;padding:0in'>            if callable(obj):</p>
  1438. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1439. border:none;padding:0in'>                obj = obj()</p>
  1440. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1441. border:none;padding:0in'>            sceneManager.AddListener(obj)</p>
  1442. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1443. border:none;padding:0in'>        </p>
  1444. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1445. border:none;padding:0in'>        window.Show()</p>
  1446. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1447. border:none;padding:0in'>        sceneManager.Go(window)</p>
  1448. </div>
  1449. <p class=Normal>&nbsp;</p>
  1450. <p class=Normal>Note that we have assumed here that a callable object is a
  1451. constructor (or, at least, returns an instance of a listener object).  This
  1452. works well enough in most cases.  The biggest problem we can run into is the
  1453. fact that objects in Python can implement the __call__ method, which makes an
  1454. object instance act like a function.  If we added a class like this to the
  1455. parameter list in main, we could run into trouble.  There are ways around this,
  1456. but I have left it out of this tutorial as this is a bit outside what we are
  1457. exploring in Python.  Feel free to look up Pythons documentation for more
  1458. information related to classes.</p>
  1459. <p class=Normal>&nbsp;</p>
  1460. <h2><a name="_Toc140655647">Code Checkpoint 4</a></h2>
  1461. <p class=Normal>Save your work and run the file.  You should now see a tiger
  1462. standing in front of the camera.  If you are having trouble at this point,
  1463. compare your code to checkpoint4.py.</p>
  1464. <p class=Normal>&nbsp;</p>
  1465. <h2><a name="_Toc140655648">Exercises</a></h2>
  1466. <p class=Normal>Before moving on, I would like to note that we have skipped over a couple of useful methods which a full application would need.&nbsp; If you
  1467. are new to Python, I suggest you implement these methods as an exercise.&nbsp; This is not required to move onto the next section, as we will not be making use
  1468. of any of these.</p>
  1469. <p class=Normal>&nbsp;</p>
  1470. <p class=Normal>First, implement a RemoveListenerByName method which takes
  1471. in a name and a listener and unregisters that listener from the specified
  1472. event.</p>
  1473. <p class=Normal>&nbsp;</p>
  1474. <p class=Normal>Implement a RemoveListener method which unregisters a
  1475. listener from all events.</p>
  1476. <p class=Normal>&nbsp;</p>
  1477. <h2><a name="_Toc140655649">Moving Objects on Screen</a></h2>
  1478. <p class=Normal>&nbsp;</p>
  1479. <p class=Normal>The next thing we will implement is moving and rotating objects
  1480. in the scene.  To do this we will create two classes which will be subclassed
  1481. by other scene objects to share the functionality.</p>
  1482. <p class=Normal>&nbsp;</p>
  1483. <p class=Normal>The first thing we will implement is a PositionableObject
  1484. class.  This class will provide a Position property (containing a
  1485. DirectX.Vector3 object), and a Translate method.  The Position property will
  1486. always contain a Vector3 instance that we can always directly pass into
  1487. a DirectX function without having to convert it.  To do this, we will add a
  1488. function at the top level of the file which takes in a parameter and if the
  1489. parameter is of sequence type, well convert it to a Vector3.  Be sure this
  1490. function is added near the beginning of the file.</p>
  1491. <p class=Normal>&nbsp;</p>
  1492. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1493. background:silver;margin-left:.25in;margin-right:.25in'>
  1494. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1495. border:none;padding:0in'>def Vectorize(v):</p>
  1496. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1497. border:none;padding:0in'>    if type(v)==list:</p>
  1498. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1499. border:none;padding:0in'>        v = DirectX.Vector3(v[0], v[1], v[2])</p>
  1500. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1501. border:none;padding:0in'>    return v</p>
  1502. </div>
  1503. <p class=Normal>&nbsp;</p>
  1504. <p class=Normal>&nbsp;</p>
  1505. <p class=Normal>Now we will implement the PositionableObject class using a
  1506. Python property object for the Position variable.  To define a property in
  1507. Python, we first define a get method, a set method, a delete method, and a
  1508. hidden variable to contain the value.  (Note we will always call
  1509. the Vectorize function before actually assigning to the __Position variable.)</p>
  1510. <p class=Normal>&nbsp;</p>
  1511. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1512. background:silver;margin-left:.25in;margin-right:.25in'>
  1513. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1514. border:none;padding:0in'>class PositionableObject(object):</p>
  1515. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1516. border:none;padding:0in'>    def __init__(self):</p>
  1517. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1518. border:none;padding:0in'>        self.__Position = DirectX.Vector3(0, 0, 0)</p>
  1519. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1520. border:none;padding:0in'>    </p>
  1521. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1522. border:none;padding:0in'>    def __GetPosition(self):</p>
  1523. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1524. border:none;padding:0in'>        return self.__Position</p>
  1525. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1526. border:none;padding:0in'>    </p>
  1527. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1528. border:none;padding:0in'>    def __SetPosition(self, pos): </p>
  1529. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1530. border:none;padding:0in'>        self.__Position = Vectorize(pos)</p>
  1531. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1532. border:none;padding:0in'>        </p>
  1533. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1534. border:none;padding:0in'>    def __DelPosition(self):</p>
  1535. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1536. border:none;padding:0in'>        self.__Position = DirectX.Vector3(0, 0, 0)</p>
  1537. </div>
  1538. <p class=Normal>&nbsp;</p>
  1539. <p class=Normal>To create the actual property itself, we call the property
  1540. function:</p>
  1541. <p class=Normal>&nbsp;</p>
  1542. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1543. background:silver;margin-left:.25in;margin-right:.25in'>
  1544. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1545. border:none;padding:0in'>    Position = property(__GetPosition, __SetPosition,
  1546. __DelPosition)</p>
  1547. </div>
  1548. <p class=Normal>&nbsp;</p>
  1549. <div>
  1550. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  1551. <tr>
  1552. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  1553. padding-bottom:0in;padding-left:0in'>
  1554. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1555. margin-left:.5in;margin-right:.5in'>
  1556. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  1557. padding:0in'>New to Python?</p>
  1558. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  1559. padding:0in'>The property function takes in 4 parameters.  The first is the
  1560. get method, the second is the set method, the third is the delete
  1561. method, and the fourth is a help string.  Only the first parameter (the get
  1562. method) is required.  If we do not specify a delete method, this will
  1563. create a property which throws an AttributeError if the user tries to delete
  1564. it.  If we do not supply a set property then the variable will be read-only. 
  1565. The help string is also completely optional.</p>
  1566. </div>
  1567. </td>
  1568. </tr>
  1569. </table>
  1570. </div>
  1571. <br clear=ALL>
  1572. <p class=Normal>&nbsp;</p>
  1573. <p class=Normal>Next, we will implement the Translate method.  This is also
  1574. straight forward, converting parameter then translating it.</p>
  1575. <p class=Normal>&nbsp;</p>
  1576. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1577. background:silver;margin-left:.25in;margin-right:.25in'>
  1578. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1579. border:none;padding:0in'>    def Translate(self, amount):</p>
  1580. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1581. border:none;padding:0in'>        amount = Vectorize(amount)</p>
  1582. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1583. border:none;padding:0in'>        self.__Position += amount</p>
  1584. </div>
  1585. <p class=Normal>&nbsp;</p>
  1586. <p class=Normal>At this point you may wonder, Why do we bother with a Translate
  1587. method?  The problem we are faced with is that we are trying to support a
  1588. native Python solution to this problemthat is, we would like our movable
  1589. objects to work with lists in addition to DirectX.Vector3 objects.  This
  1590. works fine until we try to use them in conjunction with the Vector3 class
  1591. (which has no idea how to deal with a python list).  For example, this code
  1592. works fine (do not add this to the project):</p>
  1593. <p class=Normal>&nbsp;</p>
  1594. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1595. background:silver;margin-left:.25in;margin-right:.25in'>
  1596. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1597. border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
  1598. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1599. border:none;padding:0in'>b = DirectX.Vector3(3, 2, 1)</p>
  1600. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1601. border:none;padding:0in'>c = a + b</p>
  1602. </div>
  1603. <p class=Normal>&nbsp;</p>
  1604. <p class=Normal>This, however, will fail (do not add this to the project):</p>
  1605. <p class=Normal>&nbsp;</p>
  1606. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1607. background:silver;margin-left:.25in;margin-right:.25in'>
  1608. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1609. border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
  1610. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1611. border:none;padding:0in'>b = [3, 2, 1]</p>
  1612. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1613. border:none;padding:0in'>c = a + b</p>
  1614. </div>
  1615. <p class=Normal>&nbsp;</p>
  1616. <p class=Normal>DirectX.Vector3 objects simply have no idea how to deal with
  1617. lists (or any other object than Vector3s, for that matter).  A common solution
  1618. to this problem is to subclass the object and redefine its math operations such
  1619. that it would convert from a list to a Vector3 automatically.  This is unfortunately
  1620. not possible since .Net does not support subclassing sealed or value types (and
  1621. DirectX.Vector3 is a value type).</p>
  1622. <p class=Normal>&nbsp;</p>
  1623. <p class=Normal>Our only option at this point is to add a Translate method
  1624. which knows how to convert between sequence types and Vector3 objects, and add
  1625. the result.  For example (do not add this to the project):</p>
  1626. <p class=Normal>&nbsp;</p>
  1627. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1628. background:silver;margin-left:.25in;margin-right:.25in'>
  1629. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1630. border:none;padding:0in'># this will work</p>
  1631. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1632. border:none;padding:0in'>somePositionableObject.Position += DirectX.Vector3(1,
  1633. 2, 3)</p>
  1634. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1635. border:none;padding:0in'>&nbsp;</p>
  1636. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1637. border:none;padding:0in'># this will not work</p>
  1638. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1639. border:none;padding:0in'>somePositionableObject.Position += 1, 2, 3</p>
  1640. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1641. border:none;padding:0in'>&nbsp;</p>
  1642. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1643. border:none;padding:0in'># this will work, which is why we created it</p>
  1644. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1645. border:none;padding:0in'>a = 1, 2, 3</p>
  1646. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1647. border:none;padding:0in'>somePositionableObject.Translate(a)</p>
  1648. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1649. border:none;padding:0in'>&nbsp;</p>
  1650. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1651. border:none;padding:0in'># this works too </p>
  1652. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1653. border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
  1654. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1655. border:none;padding:0in'>somePositionableObject.Translate(a)</p>
  1656. </div>
  1657. <p class=Normal>&nbsp;</p>
  1658. <p class=Normal>The last thing we need to add is a read-only property called
  1659. PositionMatrix, which (predictably) will return a matrix based on the position. 
  1660. We need this because all of DirectXs positioning code relies on matrices and
  1661. not vectors.  To implement this we will use the DirectX.Matrix.Translation method to build a translation matrix based on the position vector, and then
  1662. use this to create the PositionMatrix property:</p>
  1663. <p class=Normal>&nbsp;</p>
  1664. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1665. background:silver;margin-left:.25in;margin-right:.25in'>
  1666. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1667. border:none;padding:0in'>    def __GetPositionMatrix(self):</p>
  1668. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1669. border:none;padding:0in'>        return DirectX.Matrix.Translation(self.Position)</p>
  1670. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1671. border:none;padding:0in'>    </p>
  1672. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1673. border:none;padding:0in'>    PositionMatrix = property(__GetPositionMatrix)</p>
  1674. </div>
  1675. <p class=Normal>&nbsp;</p>
  1676. <h2><a name="_Toc140655650">Rotating Objects on the Screen</a></h2>
  1677. <p class=Normal>&nbsp;</p>
  1678. <p class=Normal>The next thing we will do is add a class which handles rotating
  1679. objects.  The RotatableObject will act quite differently from PositionableObject
  1680. in the sense that we do not have a single property we are interacting with. 
  1681. Instead we are going to implement four simple methods to perform rotations. 
  1682. The Pitch method will rotate the object around the x-axis, the Yaw method
  1683. will rotate the object around the y-axis, the Roll method will rotate the
  1684. object around the z-axis, and the ResetOrientation method will revert all
  1685. orientation changes to the object.</p>
  1686. <p class=Normal>&nbsp;</p>
  1687. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1688. background:silver;margin-left:.25in;margin-right:.25in'>
  1689. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1690. border:none;padding:0in'>class RotatableObject(object):</p>
  1691. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1692. border:none;padding:0in'>    def __init__(self):</p>
  1693. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1694. border:none;padding:0in'>        self.RotationMatrix = DirectX.Matrix.Identity</p>
  1695. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1696. border:none;padding:0in'>    </p>
  1697. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1698. border:none;padding:0in'>    def ResetOrientation(self):</p>
  1699. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1700. border:none;padding:0in'>        self.RotationMatrix = DirectX.Matrix.Identity</p>
  1701. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1702. border:none;padding:0in'>    </p>
  1703. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1704. border:none;padding:0in'>    def Pitch(self, p):</p>
  1705. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1706. border:none;padding:0in'>        self.RotationMatrix *=
  1707. DirectX.Matrix.RotationX(p)</p>
  1708. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1709. border:none;padding:0in'>    </p>
  1710. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1711. border:none;padding:0in'>    def Yaw(self, y):</p>
  1712. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1713. border:none;padding:0in'>        self.RotationMatrix *=
  1714. DirectX.Matrix.RotationY(y)</p>
  1715. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1716. border:none;padding:0in'>    </p>
  1717. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1718. border:none;padding:0in'>    def Roll(self, r):</p>
  1719. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1720. border:none;padding:0in'>        self.RotationMatrix *=
  1721. DirectX.Matrix.RotationZ(r)</p>
  1722. </div>
  1723. <p class=Normal>&nbsp;</p>
  1724. <p class=Normal>That is all that is required for adding object rotation.</p>
  1725. <p class=Normal>&nbsp;</p>
  1726. <h2><a name="_Toc140655651">Putting it All Together</a></h2>
  1727. <p class=Normal>&nbsp;</p>
  1728. <p class=Normal>The final thing we need to do before actually playing with
  1729. our new code is have our MeshRenderable class actually implement these two base
  1730. classes.  Modify the MeshRenderable class to inherit from the
  1731. PositionableObject and RotatableObjects, making sure to call the base
  1732. constructors:</p>
  1733. <p class=Normal>&nbsp;</p>
  1734. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1735. background:silver;margin-left:.25in;margin-right:.25in'>
  1736. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1737. border:none;padding:0in'>class MeshRenderable(PositionableObject,
  1738. RotatableObject):</p>
  1739. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1740. border:none;padding:0in'>    def __init__(self, device, name, file):</p>
  1741. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1742. border:none;padding:0in'>        PositionableObject.__init__(self)</p>
  1743. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1744. border:none;padding:0in'>        RotatableObject.__init__(self)</p>
  1745. </div>
  1746. <p class=Normal>&nbsp;</p>
  1747. <p class=Normal>Next, change the GetWorldMatrix method to return the
  1748. rotation matrix multiplied by the position matrix (remember that matrix
  1749. multiplication is not commutative, and it matters greatly the order in which
  1750. you multiply these two matrices).</p>
  1751. <p class=Normal>&nbsp;</p>
  1752. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1753. background:silver;margin-left:.25in;margin-right:.25in'>
  1754. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1755. border:none;padding:0in'>    def GetWorldMatrix(self):</p>
  1756. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1757. border:none;padding:0in'>        return self.RotationMatrix *
  1758. self.PositionMatrix</p>
  1759. </div>
  1760. <p class=Normal>&nbsp;</p>
  1761. <p class=Normal>Now we can finally put this to use.  We have already loaded
  1762. a tiger mesh in our SceneCreator class.  Now we will create a new class to
  1763. position and animate the tiger.  Have the tiger rotate around the y-axis.  To
  1764. do this, well add a frame event which calls the Yaw method, passing in the
  1765. elapsed time as the amount to rotate it.</p>
  1766. <p class=Normal>&nbsp;</p>
  1767. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1768. background:silver;margin-left:.25in;margin-right:.25in'>
  1769. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1770. border:none;padding:0in'>class TigerAnimator(object):</p>
  1771. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1772. border:none;padding:0in'>    def OnSceneBegin(self, sceneManager):</p>
  1773. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1774. border:none;padding:0in'>        self.Tiger =
  1775. sceneManager.Objects[&quot;Tiger&quot;]</p>
  1776. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1777. border:none;padding:0in'>        return True</p>
  1778. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1779. border:none;padding:0in'>        </p>
  1780. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1781. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  1782. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1783. border:none;padding:0in'>        self.Tiger.Yaw(elapsed)</p>
  1784. </div>
  1785. <p class=Normal>&nbsp;</p>
  1786. <p class=Normal>Finally, we need to add this listener to the SceneManager
  1787. class.  Since objects which affect a scene are parts of the scene too, it seems
  1788. natural to have the SceneCreator class actually instantiate this object.</p>
  1789. <p class=Normal>&nbsp;</p>
  1790. <p class=Normal>Update SceneCreators OnSceneCreate method to add this
  1791. listener:</p>
  1792. <p class=Normal>&nbsp;</p>
  1793. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1794. background:silver;margin-left:.25in;margin-right:.25in'>
  1795. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1796. border:none;padding:0in'>    def OnSceneCreate(self, sm):</p>
  1797. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1798. border:none;padding:0in'>        self.Tiger = sm.LoadMesh(&quot;Tiger&quot;,
  1799. &quot;tiger.x&quot;)</p>
  1800. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1801. border:none;padding:0in'>        sm.AddListener(TigerAnimator())</p>
  1802. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1803. border:none;padding:0in'>        return True</p>
  1804. </div>
  1805. <p class=Normal>&nbsp;</p>
  1806. <h2><a name="_Toc140655652">Cameras</a></h2>
  1807. <p class=Normal>&nbsp;</p>
  1808. <p class=Normal>The last major scene object we will be adding to our framework
  1809. is a Camera object.  We will introduce a new variable in the SceneManager class
  1810. called ActiveCamera to store the camera actually being used to view the
  1811. scene.  We will store inactive cameras in SceneManagers Objects dictionary
  1812. so that they may be accessed by any object which has a reference to the scene
  1813. manager.</p>
  1814. <p class=Normal>&nbsp;</p>
  1815. <p class=Normal>Our camera class will be a bit different from our object
  1816. positioning and rotation code.  Instead of rotating the camera around various
  1817. axes to face the objects we wish to look at, we will instead implement a camera
  1818. class which always looks at a given position.  Nine times out of ten, this is
  1819. what we wanted to do with a camera anyway (have it look at an object).  In a
  1820. full framework we would need to add a camera which did a bit more than look at
  1821. a given position, but this will work for now.</p>
  1822. <p class=Normal>&nbsp;</p>
  1823. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1824. background:silver;margin-left:.25in;margin-right:.25in'>
  1825. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1826. border:none;padding:0in'>class Camera(object):</p>
  1827. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1828. border:none;padding:0in'>    def __init__(self, name):</p>
  1829. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1830. border:none;padding:0in'>        self.Name = name</p>
  1831. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1832. border:none;padding:0in'>        self.__LookAtVector = DirectX.Vector3(0, 0,
  1833. -1)</p>
  1834. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1835. border:none;padding:0in'>        self.__Position = DirectX.Vector3(0, 0, 0)</p>
  1836. </div>
  1837. <p class=Normal>&nbsp;</p>
  1838. <p class=Normal>The next bit of code calculates a matrix based on the
  1839. position of the camera and the position we want the camera to look at.  It
  1840. creates two properties (one for Position, one for LookAtVector), and updates
  1841. the matrix every time either of these are changed.</p>
  1842. <p class=Normal>&nbsp;</p>
  1843. <p class=Normal>&nbsp;</p>
  1844. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1845. background:silver;margin-left:.25in;margin-right:.25in'>
  1846. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1847. border:none;padding:0in'>    def __UpdateMatrix(self):</p>
  1848. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1849. border:none;padding:0in'>        self.Matrix =
  1850. DirectX.Matrix.LookAtLH(self.__Position, self.__LookAtVector,
  1851. DirectX.Vector3(0, 1, 0))</p>
  1852. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1853. border:none;padding:0in'>    </p>
  1854. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1855. border:none;padding:0in'>    def __GetLookAtVector(self):</p>
  1856. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1857. border:none;padding:0in'>        return self.__LookAtVector</p>
  1858. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1859. border:none;padding:0in'>    </p>
  1860. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1861. border:none;padding:0in'>    def __SetLookAtVector(self, v):</p>
  1862. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1863. border:none;padding:0in'>        v = Vectorize(v)</p>
  1864. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1865. border:none;padding:0in'>        self.__LookAtVector = v</p>
  1866. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1867. border:none;padding:0in'>        self.__UpdateMatrix()</p>
  1868. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1869. border:none;padding:0in'>    </p>
  1870. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1871. border:none;padding:0in'>    def __GetPosition(self):</p>
  1872. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1873. border:none;padding:0in'>        return self.__Position</p>
  1874. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1875. border:none;padding:0in'>    </p>
  1876. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1877. border:none;padding:0in'>    def __SetPosition(self, pos):</p>
  1878. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1879. border:none;padding:0in'>        pos = Vectorize(pos)</p>
  1880. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1881. border:none;padding:0in'>        self.__Position = pos</p>
  1882. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1883. border:none;padding:0in'>        self.__UpdateMatrix()</p>
  1884. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1885. border:none;padding:0in'>    </p>
  1886. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1887. border:none;padding:0in'>    Position = property(__GetPosition, __SetPosition)</p>
  1888. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1889. border:none;padding:0in'>    LookAtVector = property(__GetLookAtVector,
  1890. __SetLookAtVector)</p>
  1891. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1892. border:none;padding:0in'>&nbsp;</p>
  1893. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1894. border:none;padding:0in'>    def LookAt(self, loc):</p>
  1895. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1896. border:none;padding:0in'>        self.LookAtVector = loc</p>
  1897. </div>
  1898. <p class=Normal>&nbsp;</p>
  1899. <p class=Normal>Finally, we will return the matrix using the GetViewMatrix method.</p>
  1900. <p class=Normal>&nbsp;</p>
  1901. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1902. background:silver;margin-left:.25in;margin-right:.25in'>
  1903. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1904. border:none;padding:0in'>    def GetViewMatrix(self):</p>
  1905. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1906. border:none;padding:0in'>        return self.Matrix</p>
  1907. </div>
  1908. <p class=Normal>&nbsp;</p>
  1909. <p class=Normal>Now we need to update the SceneManager class to use the new
  1910. camera class.  Find the SceneManager classs constructor and add an
  1911. ActiveCamera variable:</p>
  1912. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1913. font-family:Consolas'>&nbsp;</span></p>
  1914. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1915. background:silver;margin-left:.25in;margin-right:.25in'>
  1916. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1917. border:none;padding:0in'>        self.ActiveCamera = None</p>
  1918. </div>
  1919. <p class=Normal>&nbsp;</p>
  1920. <p class=Normal>Now we will add a CreateCamera method to SceneManager which creates a camera of the given name and adds it to the
  1921. Objects dictionary, returning the result.  We will have an added constraint
  1922. that if no camera has been set to be active, we will set the newly created
  1923. camera to be the active camera.  This lets us ignore setting
  1924. the ActiveCamera variable if the application only uses one camera.</p>
  1925. <p class=Normal>&nbsp;</p>
  1926. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1927. background:silver;margin-left:.25in;margin-right:.25in'>
  1928. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1929. border:none;padding:0in'>    def CreateCamera(self, name):</p>
  1930. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1931. border:none;padding:0in'>        cam = Camera(name)</p>
  1932. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1933. border:none;padding:0in'>        self.Objects[cam.Name] = cam</p>
  1934. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1935. border:none;padding:0in'>        </p>
  1936. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1937. border:none;padding:0in'>        if self.ActiveCamera is None:</p>
  1938. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1939. border:none;padding:0in'>            self.ActiveCamera = cam</p>
  1940. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1941. border:none;padding:0in'>        </p>
  1942. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1943. border:none;padding:0in'>        return cam</p>
  1944. </div>
  1945. <p class=Normal>&nbsp;</p>
  1946. <p class=Normal>To finish updating the SceneManager class to use cameras, we need to update the Render method to set the View matrix based on the active
  1947. camera.  Add the following code between the return statement at the beginning
  1948. and before the call to self.Device.Clear in SceneManagers Render class:</p>
  1949. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  1950. font-family:Consolas'>&nbsp;</span></p>
  1951. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1952. background:silver;margin-left:.25in;margin-right:.25in'>
  1953. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1954. border:none;padding:0in'>        if self.ActiveCamera is not None:</p>
  1955. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1956. border:none;padding:0in'>            self.Device.Transform.View = self.ActiveCamera.GetViewMatrix()</p>
  1957. </div>
  1958. <p class=Normal>&nbsp;</p>
  1959. <p class=Normal>Finally, we need to add code to create a default camera and
  1960. have it look at the Tiger object.  Add the following code to SceneCreators OnSceneCreate method:</p>
  1961. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  1962. background:silver;margin-left:.25in;margin-right:.25in'>
  1963. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1964. border:none;padding:0in'>        cam = sm.CreateCamera(&quot;Player Cam&quot;)</p>
  1965. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1966. border:none;padding:0in'>        cam.Position = [0, 3, -5]</p>
  1967. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  1968. border:none;padding:0in'>        cam.LookAt(self.Tiger.Position)</p>
  1969. </div>
  1970. <p class=Normal>&nbsp;</p>
  1971. <p class=Normal>Thats it.  If you run your application now, you will notice
  1972. that really nothing has changed.  This is because I cheated a little in the
  1973. beginning and set up the view matrix to be in the exact same position and look
  1974. at the tiger anyway.  If you wish to explore the camera a bit, try moving it
  1975. (or moving it around each frame).</p>
  1976. <p class=Normal>&nbsp;</p>
  1977. <h2><a name="_Toc140655653">Code Checkpoint 5</a></h2>
  1978. <p class=Normal>&nbsp;</p>
  1979. <p class=Normal>Run your application.  You should see a tiger onscreen
  1980. rotating.  If this is not the case, compare your code with checkpoint5.py.</p>
  1981. <p class=Normal>&nbsp;</p>
  1982. <h2><a name="_Toc140655654">Exercises</a></h2>
  1983. <p class=Normal>&nbsp;</p>
  1984. <p class=Normal>We have introduced a lot of functionality in this section of
  1985. the tutorial, and not made use of any of it.  I would recommend that you take
  1986. some time to play with the framework we have created.</p>
  1987. <p class=Normal>&nbsp;</p>
  1988. <p class=Normal>Try adding more tiger meshes to the scene (remember to give
  1989. them unique names when you call LoadMesh).  Position them in different places
  1990. around the scene.  Add new OnFrame code to rotate them at different speeds, in
  1991. different directions, and in multiple different axes.</p>
  1992. <p class=Normal>&nbsp;</p>
  1993. <p class=Normal>Try moving an object around by setting its position
  1994. incrementally every frame.  Define movement speed to be a variable which can be
  1995. changed.  Move this change into a generic class of its own which takes in a
  1996. name of the object and the speed as a parameter to the constructor.</p>
  1997. <p class=Normal>&nbsp;</p>
  1998. <h2><a name="_Toc140655655">Framework Optimization</a></h2>
  1999. <p class=Normal>At this point I would like to point out that there are
  2000. several places in the code which can be optimized, but I did not want to muddle
  2001. the tutorial with too much extra code which are not strictly necessary to
  2002. understand what is going on.  We will now eliminate a large number of the
  2003. calculations done every frame.</p>
  2004. <p class=Normal>&nbsp;</p>
  2005. <p class=Normal>The GetViewMatrix classes and GetWorldMatrix classes
  2006. multiply the rotation and position matrices every time they are called.  It
  2007. would be better if this calculation were only done when either the
  2008. PositionMatrix or the RotationMatrix variables are actually updated, and the
  2009. result of this multiplication be cached in a local variable.</p>
  2010. <p class=Normal>&nbsp;</p>
  2011. <p class=Normal>One simple solution to this problem would be to implement a
  2012. lightweight event system which notifies a listener whenever these matrices
  2013. change.  This is a valid solution, but the PositionableObject and
  2014. RotatableObject classes were designed to be lightweight and completely
  2015. orthogonal to any other component in the system.  In other words, a perfect
  2016. mixin we can add to any class.</p>
  2017. <p class=Normal>&nbsp;</p>
  2018. <p class=Normal>We can actually do much better than an event system thanks
  2019. to the elegance Python affords us.  Notice that PositionMatrix and
  2020. RotationMatrix are simply variables.  If we created a PositionMatrix and
  2021. RotationMatrix property in the child class, we could intercept the variable
  2022. assignment and perform the calculations then.  The first thing we need to
  2023. change is to modify the PositionableObject class so that it no longer uses a
  2024. property for PositionMatrix, but a variable instead.  Every time we update the
  2025. position we will rebuild the matrix.  Here is the PositionableObject class
  2026. after we have made this change:</p>
  2027. <p class=Normal>&nbsp;</p>
  2028. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2029. background:silver;margin-left:.25in;margin-right:.25in'>
  2030. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2031. border:none;padding:0in'>class PositionableObject(object):</p>
  2032. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2033. border:none;padding:0in'>    def __init__(self):</p>
  2034. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2035. border:none;padding:0in'>        self.__Position = DirectX.Vector3(0, 0, 0)</p>
  2036. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2037. border:none;padding:0in'>        self.PositionMatrix = DirectX.Matrix.Identity</p>
  2038. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2039. border:none;padding:0in'>    </p>
  2040. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2041. border:none;padding:0in'>    def __GetPosition(self):</p>
  2042. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2043. border:none;padding:0in'>        return self.__Position</p>
  2044. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2045. border:none;padding:0in'>    </p>
  2046. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2047. border:none;padding:0in'>    def __SetPosition(self, pos):</p>
  2048. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2049. border:none;padding:0in'>        pos = Vectorize(pos)</p>
  2050. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2051. border:none;padding:0in'>        self.__Position = pos</p>
  2052. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2053. border:none;padding:0in'>        self.PositionMatrix = DirectX.Matrix.Translation(self.__Position)</p>
  2054. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2055. border:none;padding:0in'>        </p>
  2056. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2057. border:none;padding:0in'>    def __DelPosition(self):</p>
  2058. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2059. border:none;padding:0in'>        self.__Position = DirectX.Vector3(0, 0, 0)</p>
  2060. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2061. border:none;padding:0in'>        self.PositionMatrix = DirectX.Matrix.Identity</p>
  2062. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2063. border:none;padding:0in'>    </p>
  2064. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2065. border:none;padding:0in'>    def Translate(self, amount):</p>
  2066. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2067. border:none;padding:0in'>        amount = Vectorize(amount)</p>
  2068. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2069. border:none;padding:0in'>        self.__Position += amount</p>
  2070. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2071. border:none;padding:0in'>        self.PositionMatrix =
  2072. DirectX.Matrix.Translation(self.__Position)</p>
  2073. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2074. border:none;padding:0in'>    </p>
  2075. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2076. border:none;padding:0in'>    Position = property(__GetPosition, __SetPosition,
  2077. __DelPosition)</p>
  2078. </div>
  2079. <p class=Normal>&nbsp;</p>
  2080. <p class=Normal>Now we will build a class which combines the
  2081. PositionableObject and RotatableObject such that every time either the
  2082. PostionMatrix or the RotationMatrix changes, it will update a variable called
  2083. Matrix to contain the combined matrix.  Ill call this class SceneObject, for
  2084. lack of a better term.  Well start by defining the constructor for this class. 
  2085. Note we have to set our local variables first since they will actually be used
  2086. within the constructors of the two ancestor classes:</p>
  2087. <p class=Normal>&nbsp;</p>
  2088. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2089. background:silver;margin-left:.25in;margin-right:.25in'>
  2090. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2091. border:none;padding:0in'>class SceneObject(PositionableObject,
  2092. RotatableObject):</p>
  2093. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2094. border:none;padding:0in'>    def __init__(self):</p>
  2095. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2096. border:none;padding:0in'>        self.Matrix = DirectX.Matrix.Identity</p>
  2097. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2098. border:none;padding:0in'>        self.__PositionMatrix =
  2099. DirectX.Matrix.Identity</p>
  2100. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2101. border:none;padding:0in'>        self.__RotationMatrix =
  2102. DirectX.Matrix.Identity</p>
  2103. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2104. border:none;padding:0in'>        </p>
  2105. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2106. border:none;padding:0in'>        PositionableObject.__init__(self)</p>
  2107. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2108. border:none;padding:0in'>        RotatableObject.__init__(self)</p>
  2109. </div>
  2110. <p class=Normal>&nbsp;</p>
  2111. <p class=Normal>Then we need to create the get and set properties for
  2112. PositionMatrix and RotationMatrix.  The only thing to note here is that
  2113. whenever we call the set method we update the Matrix variable as well.</p>
  2114. <p class=Normal>&nbsp;</p>
  2115. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2116. background:silver;margin-left:.25in;margin-right:.25in'>
  2117. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2118. border:none;padding:0in'>    def __GetPositionMatrix(self):</p>
  2119. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2120. border:none;padding:0in'>        return self.__PositionMatrix</p>
  2121. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2122. border:none;padding:0in'>    </p>
  2123. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2124. border:none;padding:0in'>    def __SetPositionMatrix(self, value):</p>
  2125. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2126. border:none;padding:0in'>        self.__PositionMatrix = value</p>
  2127. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2128. border:none;padding:0in'>        self.Matrix = self.__RotationMatrix *
  2129. self.__PositionMatrix</p>
  2130. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2131. border:none;padding:0in'>    </p>
  2132. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2133. border:none;padding:0in'>    def __GetRotationMatrix(self):</p>
  2134. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2135. border:none;padding:0in'>        return self.__RotationMatrix</p>
  2136. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2137. border:none;padding:0in'>    </p>
  2138. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2139. border:none;padding:0in'>    def __SetRotationMatrix(self, value):</p>
  2140. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2141. border:none;padding:0in'>        self.__RotationMatrix = value</p>
  2142. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2143. border:none;padding:0in'>        self.Matrix = self.__RotationMatrix *
  2144. self.__PositionMatrix</p>
  2145. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2146. border:none;padding:0in'>        </p>
  2147. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2148. border:none;padding:0in'>    PositionMatrix = property(__GetPositionMatrix,
  2149. __SetPositionMatrix)</p>
  2150. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2151. border:none;padding:0in'>    RotationMatrix = property(__GetRotationMatrix,
  2152. __SetRotationMatrix)</p>
  2153. </div>
  2154. <p class=Normal>&nbsp;</p>
  2155. <p class=Normal>Now we need to update any subclasses which wish to make use
  2156. of this functionality.  Currently the only thing we need to update is the MeshRenderable class.  Update the class definition, making sure to replace the
  2157. constructor calls to PositionableObject and RotatableObject with one to
  2158. SceneObject:</p>
  2159. <p class=Normal>&nbsp;</p>
  2160. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2161. background:silver;margin-left:.25in;margin-right:.25in'>
  2162. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2163. border:none;padding:0in'>class MeshRenderable(SceneObject):</p>
  2164. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2165. border:none;padding:0in'>    def __init__(self, device, name, file):</p>
  2166. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2167. border:none;padding:0in'>        SceneObject.__init__(self)</p>
  2168. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2169. border:none;padding:0in'>        self.Name = name</p>
  2170. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2171. border:none;padding:0in'>        # ...</p>
  2172. </div>
  2173. <p class=Normal>&nbsp;</p>
  2174. <p class=Normal>Lastly, update the GetWorldMatrix method:</p>
  2175. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  2176. font-family:Consolas'>&nbsp;</span></p>
  2177. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2178. background:silver;margin-left:.25in;margin-right:.25in'>
  2179. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2180. border:none;padding:0in'>    def GetWorldMatrix(self):</p>
  2181. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2182. border:none;padding:0in'>        return self.Matrix</p>
  2183. </div>
  2184. <p class=Normal>&nbsp;</p>
  2185. <p class=Normal>&nbsp;</p>
  2186. <p class=Normal>Run the application.  Though there is no graphical
  2187. difference in the program, we have eliminated many calculations performed with each frame.</p>
  2188. <p class=Normal>&nbsp;</p>
  2189. <h2><a name="_Toc140655656">Adding Other Types of Objects</a></h2>
  2190. <p class=Normal>&nbsp;</p>
  2191. <p class=Normal>We can load meshes with ease, but often we will want to have
  2192. just a simple box, or sphere, or other basic object in our scene.  This also
  2193. comes in handy when we need to test code which acts on objects.  To start, we
  2194. will create a class which represents basic objects.  Well use a dictionary to keep
  2195. track of the constructors for each type of object:</p>
  2196. <p class=Normal>&nbsp;</p>
  2197. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2198. background:silver;margin-left:.25in;margin-right:.25in'>
  2199. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2200. border:none;padding:0in'>class BasicObject(SceneObject):</p>
  2201. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2202. border:none;padding:0in'>    ObjectTypes = {'cylinder' :
  2203. Direct3D.Mesh.Cylinder,</p>
  2204. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2205. border:none;padding:0in'>                   'polygon' : Direct3D.Mesh.Polygon,</p>
  2206. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2207. border:none;padding:0in'>                   'sphere' : Direct3D.Mesh.Sphere,</p>
  2208. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2209. border:none;padding:0in'>                   'teapot' : Direct3D.Mesh.Teapot,</p>
  2210. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2211. border:none;padding:0in'>                   'torus' : Direct3D.Mesh.Torus,</p>
  2212. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2213. border:none;padding:0in'>                   'box' : Direct3D.Mesh.Box</p>
  2214. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2215. border:none;padding:0in'>                   }</p>
  2216. </div>
  2217. <p class=Normal>&nbsp;</p>
  2218. <p class=Normal>Now we will create the constructor.  We will take in the
  2219. DirectX device, the type of the object, the name of the object, a color for the
  2220. object, and then a variable number of other parameters to pass to the
  2221. objects constructor.</p>
  2222. <p class=Normal>&nbsp;</p>
  2223. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2224. background:silver;margin-left:.25in;margin-right:.25in'>
  2225. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2226. border:none;padding:0in'>    def __init__(self, device, type, name, color,
  2227. *params):</p>
  2228. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2229. border:none;padding:0in'>        SceneObject.__init__(self)</p>
  2230. </div>
  2231. <p class=Normal>&nbsp;</p>
  2232. <div>
  2233. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  2234. <tr>
  2235. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  2236. padding-bottom:0in;padding-left:0in'>
  2237. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2238. margin-left:.5in;margin-right:.5in'>
  2239. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  2240. padding:0in'>New to Python?</p>
  2241. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  2242. padding:0in'>Python allows you to define a function with a variable number of
  2243. arguments by placing a star (*) in front of the variable which will hold the
  2244. rest of the parameters.  This must always follow all of the regular
  2245. parameters.</p>
  2246. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  2247. padding:0in'>&nbsp;</p>
  2248. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  2249. padding:0in'>Python also allows you to define a parameter which accepts named
  2250. parameters to the function and places them in a dictionary.  This is done by
  2251. placing a double star (**) in front of the parameter which will receive all
  2252. named attributes.  See the Python documentation for examples and more information
  2253. on using these constructs.</p>
  2254. </div>
  2255. </td>
  2256. </tr>
  2257. </table>
  2258. </div>
  2259. <br clear=ALL>
  2260. <p class=Normal>&nbsp;</p>
  2261. <p class=Normal>For the type parameter, we will allow the user to either
  2262. pass in a constructor for the type (callable) or a string to look up in the
  2263. ObjectTypes dictionary:</p>
  2264. <p class=Normal>&nbsp;</p>
  2265. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2266. background:silver;margin-left:.25in;margin-right:.25in'>
  2267. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2268. border:none;padding:0in'>        if not callable(type):</p>
  2269. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2270. border:none;padding:0in'>            type = self.ObjectTypes[type]</p>
  2271. </div>
  2272. <p class=Normal>&nbsp;</p>
  2273. <p class=Normal>Now we will create the object, create a material, and set
  2274. the color.  Note we are expanding the params tuple into a parameter list for
  2275. the constructor:</p>
  2276. <p class=Normal>&nbsp;</p>
  2277. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2278. background:silver;margin-left:.25in;margin-right:.25in'>
  2279. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2280. border:none;padding:0in'>        self.Name = name</p>
  2281. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2282. border:none;padding:0in'>        self.Mesh = type(device, *params)</p>
  2283. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2284. border:none;padding:0in'>        </p>
  2285. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2286. border:none;padding:0in'>        color = Direct3D.ColorValue.FromColor(color)</p>
  2287. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2288. border:none;padding:0in'>        material = Direct3D.Material()</p>
  2289. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2290. border:none;padding:0in'>        material.AmbientColor = color</p>
  2291. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2292. border:none;padding:0in'>&nbsp;</p>
  2293. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2294. border:none;padding:0in'>        self.Materials = [material]</p>
  2295. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2296. border:none;padding:0in'>        self.Textures = []</p>
  2297. </div>
  2298. <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'>&nbsp;</span></p>
  2299. <div>
  2300. <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
  2301. <tr>
  2302. <td valign=top align=left style='padding-top:0in;padding-right:0in;
  2303. padding-bottom:0in;padding-left:0in'>
  2304. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2305. margin-left:.5in;margin-right:.5in'>
  2306. <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
  2307. padding:0in'>New to Python?</p>
  2308. <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
  2309. padding:0in'>Python allows you to “expand” a list into parameters for a function. 
  2310. Lets say a function takes in 3 parameters.  It is perfectly valid to pass in
  2311. the first parameter normally, then specify a list as the second argument and
  2312. expand it by placing a star (*) in front of the parameter (as we have done
  2313. above when calling type).</p>
  2314. </div>
  2315. </td>
  2316. </tr>
  2317. </table>
  2318. </div>
  2319. <br clear=ALL>
  2320. <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'>&nbsp;</span></p>
  2321. <p class=Normal>Lastly, we need to provide the GetWorldMatrix method so
  2322. that the SceneManager knows how to render it.</p>
  2323. <p class=Normal>&nbsp;</p>
  2324. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2325. background:silver;margin-left:.25in;margin-right:.25in'>
  2326. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2327. border:none;padding:0in'>    def GetWorldMatrix(self):</p>
  2328. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2329. border:none;padding:0in'>        return self.Matrix</p>
  2330. </div>
  2331. <p class=Normal>&nbsp;</p>
  2332. <p class=Normal>Now that the class is implemented, we need to add a method
  2333. to the SceneManager which creates the class and adds it to the Objects
  2334. dictionary.  This is very similar to the LoadMesh method, and should be
  2335. nothing new aside from the variable arguments, and argument expansion:</p>
  2336. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  2337. font-family:Consolas'>&nbsp;</span></p>
  2338. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2339. background:silver;margin-left:.25in;margin-right:.25in'>
  2340. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2341. border:none;padding:0in'>    def LoadBasicObject(self, type, name, color,
  2342. *args):</p>
  2343. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2344. border:none;padding:0in'>        mesh = BasicObject(self.Device, type,
  2345. name, color, *args)</p>
  2346. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2347. border:none;padding:0in'>        self.Objects[mesh.Name] = mesh</p>
  2348. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2349. border:none;padding:0in'>        return mesh</p>
  2350. </div>
  2351. <p class=Normal>&nbsp;</p>
  2352. <p class=Normal>Thats it!  Now we can load up basic objects.  Temporarily
  2353. clear out the code in the SceneCreator class and add this in its place:</p>
  2354. <p class=Normal>&nbsp;</p>
  2355. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2356. background:silver;margin-left:.25in;margin-right:.25in'>
  2357. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2358. border:none;padding:0in'>    def OnSceneCreate(self, sm):</p>
  2359. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2360. border:none;padding:0in'>        teapot =
  2361. sm.LoadBasicObject(&quot;teapot&quot;, &quot;teapot&quot;, Drawing.Color.White)</p>
  2362. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2363. border:none;padding:0in'>        </p>
  2364. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2365. border:none;padding:0in'>        cam = sm.CreateCamera(&quot;Player Cam&quot;)</p>
  2366. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2367. border:none;padding:0in'>        cam.Position = [0, 3, -5]</p>
  2368. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2369. border:none;padding:0in'>        cam.LookAt(teapot.Position)</p>
  2370. </div>
  2371. <p class=Normal>&nbsp;</p>
  2372. <p class=Normal>Run the code and check it out.  We can also load other
  2373. objects which have parameters to their constructors:</p>
  2374. <p class=Normal>&nbsp;</p>
  2375. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2376. background:silver;margin-left:.25in;margin-right:.25in'>
  2377. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2378. border:none;padding:0in'>        sm.LoadBasicObject(&quot;box&quot;, &quot;some
  2379. box&quot;, Drawing.Color.Green, 1, 1, 1)</p>
  2380. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2381. border:none;padding:0in'>        sm.LoadBasicObject(&quot;sphere&quot;,
  2382. &quot;some sphere&quot;, Drawing.Color.Blue, 1, 25, 25)</p>
  2383. </div>
  2384. <p class=Normal>&nbsp;</p>
  2385. <p class=Normal>See the managed DirectX documentation for more information
  2386. on the parameters for these constructors.</p>
  2387. <p class=Normal>&nbsp;</p>
  2388. <p class=Normal>You should now replace the OnSceneCreate method with the
  2389. previous one (which loads the tiger and animates it), as this was just example
  2390. code to ensure our code works.</p>
  2391. <p class=Normal>&nbsp;</p>
  2392. <h2><a name="_Toc140655657">A Better Main Function</a></h2>
  2393. <p class=Normal>&nbsp;</p>
  2394. <p class=Normal>There is one last thing I feel we should look at before
  2395. finishing up with this tutorial: the main function.  The main function we
  2396. currently have works, but its a bit rough around the edges.  Instead, what I
  2397. would like to do is create a singleton class which keeps track of our
  2398. SceneManager object so we do not have to keep an explicit pointer to it.  Well
  2399. also add threading support in the mix to allow us to work with our framework
  2400. from the console.  Remove the main function from this program entirely.</p>
  2401. <p class=Normal>&nbsp;</p>
  2402. <p class=Normal>We will not <i>exactly</i> use a singleton for this class. 
  2403. Python has a better pattern to use which maps more naturally to its language
  2404. constructs.  What we will do instead is to create an object whose instances all
  2405. share the same dictionary (that is, all of them share the same variables and
  2406. methods).  This is known as the Borg pattern, and it takes only a couple of lines
  2407. to implement:</p>
  2408. <p class=Normal>&nbsp;</p>
  2409. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2410. background:silver;margin-left:.25in;margin-right:.25in'>
  2411. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2412. border:none;padding:0in'>class Root(object):</p>
  2413. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2414. border:none;padding:0in'>    __State = {}</p>
  2415. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2416. border:none;padding:0in'>    def __init__(self):</p>
  2417. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2418. border:none;padding:0in'>        self.__dict__ = self.__ State</p>
  2419. </div>
  2420. <p class=Normal>&nbsp;</p>
  2421. <p class=Normal>Note we must be very careful when defining the constructor of
  2422. any Borg class.  After we assign the __dict__ variable, we have synced up with
  2423. the global object and should not re-initialize the object.  This means that if
  2424. we need to initialize any variables in the init method, we need to be careful
  2425. to only do it the first time we create an instance of this object.  This is
  2426. usually accomplished by a boolean variable which specifies if we have
  2427. initialized the class or not.  Our Root class does not require any
  2428. initialization, so we are in the clear.</p>
  2429. <p class=Normal>&nbsp;</p>
  2430. <p class=Normal>To see this pattern in action, try this code (do not add
  2431. this to the framework, instead use import * from framework in an IronPython
  2432. console):</p>
  2433. <p class=Normal>&nbsp;</p>
  2434. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2435. background:silver;margin-left:.25in;margin-right:.25in'>
  2436. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2437. border:none;padding:0in'>borg1 = Root()</p>
  2438. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2439. border:none;padding:0in'>borg2 = Root()</p>
  2440. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2441. border:none;padding:0in'>borg1.Test = “Hello World!”</p>
  2442. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2443. border:none;padding:0in'>print borg2.Test</p>
  2444. </div>
  2445. <p class=Normal>&nbsp;</p>
  2446. <p class=Normal>Now we will add a new main function inside the root class. 
  2447. Note that we have kept the listeners parameter, but removed the SceneCreator
  2448. default value from it.</p>
  2449. <p class=Normal>&nbsp;</p>
  2450. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2451. background:silver;margin-left:.25in;margin-right:.25in'>
  2452. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2453. border:none;padding:0in'>    def Main(self, listeners = []):</p>
  2454. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2455. border:none;padding:0in'>        self.SceneManager = SceneManager()</p>
  2456. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2457. border:none;padding:0in'>        self.Window = RenderWindow(self.SceneManager)</p>
  2458. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2459. border:none;padding:0in'>&nbsp;</p>
  2460. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2461. border:none;padding:0in'>        if not
  2462. self.SceneManager.InitGraphics(self.Window.Handle):</p>
  2463. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2464. border:none;padding:0in'>            Forms.MessageBox.Show(&quot;Could not init
  2465. Direct3D.&quot;)</p>
  2466. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2467. border:none;padding:0in'>&nbsp;</p>
  2468. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2469. border:none;padding:0in'>        else:</p>
  2470. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2471. border:none;padding:0in'>            for obj in listeners:</p>
  2472. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2473. border:none;padding:0in'>                if callable(obj):</p>
  2474. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2475. border:none;padding:0in'>                    obj = obj()</p>
  2476. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2477. border:none;padding:0in'>                self.SceneManager.AddListener(obj)</p>
  2478. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2479. border:none;padding:0in'>            </p>
  2480. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2481. border:none;padding:0in'>            self.Window.Show()</p>
  2482. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2483. border:none;padding:0in'>            self.SceneManager.Go(self.Window)</p>
  2484. </div>
  2485. <p class=Normal>&nbsp;</p>
  2486. <p class=Normal>The last thing we will add to our Root class is a threaded
  2487. call to Main.  This will allow us to play with our framework from the console. 
  2488. The Python API gives us a very easy to use thread library.  This library implements
  2489. a function called start_new_thread which takes in a function to start in a new
  2490. thread, and a list with the arguments to the function. </p>
  2491. <p class=Normal>&nbsp;</p>
  2492. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2493. background:silver;margin-left:.25in;margin-right:.25in'>
  2494. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2495. border:none;padding:0in'>    def ThreadMain(self, listeners = []):</p>
  2496. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2497. border:none;padding:0in'>        import thread</p>
  2498. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2499. border:none;padding:0in'>        args = (listeners,)</p>
  2500. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2501. border:none;padding:0in'>        thread.start_new_thread(self.Main, args)</p>
  2502. </div>
  2503. <p class=Normal>     </p>
  2504. <p class=Normal>Finally, we need to update the call to the main class. 
  2505. Change the call to be this:</p>
  2506. <p class=Normal>   </p>
  2507. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2508. background:silver;margin-left:.25in;margin-right:.25in'>
  2509. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2510. border:none;padding:0in'>if __name__ == '__main__':</p>
  2511. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2512. border:none;padding:0in'>    Root().Main([SceneCreator])</p>
  2513. </div>
  2514. <p class=Normal>&nbsp;</p>
  2515. <p class=Normal>If you run the application now, you should see the same
  2516. result as we had before starting this section.  The reason we took the time to
  2517. implement this change is so that we can now launch the framework in a
  2518. background thread and manipulate the contents of the scene from the console.</p>
  2519. <p class=Normal>&nbsp;</p>
  2520. <p class=Normal>To test this out, open up a Python console and type out the
  2521. following code (do not add this to the project):</p>
  2522. <p class=Normal>&nbsp;</p>
  2523. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2524. background:silver;margin-left:.25in;margin-right:.25in'>
  2525. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2526. border:none;padding:0in'>&gt;&gt;&gt; from framework import *</p>
  2527. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2528. border:none;padding:0in'>&gt;&gt;&gt; root = Root()</p>
  2529. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2530. border:none;padding:0in'>&gt;&gt;&gt; root.ThreadMain()</p>
  2531. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2532. border:none;padding:0in'>&gt;&gt;&gt; sm = root.SceneManager</p>
  2533. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2534. border:none;padding:0in'>&gt;&gt;&gt; tiger = sm.LoadMesh(&quot;Tiger&quot;,
  2535. &quot;tiger.x&quot;)</p>
  2536. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2537. border:none;padding:0in'>&gt;&gt;&gt; class TigerRotator(object):</p>
  2538. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2539. border:none;padding:0in'>...     def OnFrame(self, ticks):</p>
  2540. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2541. border:none;padding:0in'>...         tiger.Yaw(ticks)</p>
  2542. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2543. border:none;padding:0in'>...</p>
  2544. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2545. border:none;padding:0in'>&gt;&gt;&gt; sm.AddListener(TigerRotator())</p>
  2546. </div>
  2547. <p class=Normal>&nbsp;</p>
  2548. <p class=Normal>You should see the tiger rotating in the scene, and we did
  2549. all of this from the console alone.</p>
  2550. <p class=Normal>&nbsp;</p>
  2551. <h2><a name="_Toc140655658">Code Checkpoint 6</a></h2>
  2552. <p class=Normal>&nbsp;</p>
  2553. <p class=Normal>We have implemented a complex change to the code.  Be sure
  2554. that your application is working properly at this point.  If it is not, compare
  2555. your project with checkpoint6.py.</p>
  2556. <p class=Normal>&nbsp;</p>
  2557. <h1><a name="_Toc140655659">Cool Things to Do with the Project</a></h1>
  2558. <p class=Normal>&nbsp;</p>
  2559. <p class=Normal>We have spent a good amount of time creating our framework,
  2560. and there are many other things we could add to it.  Instead of this, I thought
  2561. it would be much more fun to try it out a bit.</p>
  2562. <p class=Normal>&nbsp;</p>
  2563. <p class=Normal>For this portion of the tutorial we are no longer going to
  2564. change the contents of the Python file we have been building.  Instead we will
  2565. be making a new file which contains all of our demo code we will be creating.  <b>Take
  2566. the file you have been making up to this point and rename it to framework.py.</b></p>
  2567. <p class=Normal>&nbsp;</p>
  2568. <p class=Normal>Create a new file called demo.py, and place the following
  2569. contents into it:</p>
  2570. <p class=Normal>&nbsp;</p>
  2571. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2572. background:silver;margin-left:.25in;margin-right:.25in'>
  2573. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2574. border:none;padding:0in'>import clr</p>
  2575. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2576. border:none;padding:0in'>from framework import *</p>
  2577. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2578. border:none;padding:0in'>&nbsp;</p>
  2579. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2580. border:none;padding:0in'>class DemoSceneCreator(object):</p>
  2581. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2582. border:none;padding:0in'>    def OnSceneCreate(self, sm):</p>
  2583. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2584. border:none;padding:0in'>        cam = sm.CreateCamera(&quot;Player Cam&quot;)</p>
  2585. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2586. border:none;padding:0in'>        cam.Position = [0, 3, -5]</p>
  2587. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2588. border:none;padding:0in'>        </p>
  2589. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2590. border:none;padding:0in'>        box = sm.LoadBasicObject(&quot;box&quot;,
  2591. &quot;box 1&quot;, Drawing.Color.Red, 0.25, 0.25, 0.25)</p>
  2592. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2593. border:none;padding:0in'>        box.Position = [-1, 0, 0]</p>
  2594. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2595. border:none;padding:0in'>        </p>
  2596. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2597. border:none;padding:0in'>        box = sm.LoadBasicObject(&quot;box&quot;,
  2598. &quot;box 2&quot;, Drawing.Color.Green, 0.25, 0.25, 0.25)</p>
  2599. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2600. border:none;padding:0in'>        box.Position = [0, 0, 0]</p>
  2601. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2602. border:none;padding:0in'>        cam.LookAt(box.Position)</p>
  2603. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2604. border:none;padding:0in'>        </p>
  2605. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2606. border:none;padding:0in'>        box = sm.LoadBasicObject(&quot;box&quot;,
  2607. &quot;box 3&quot;, Drawing.Color.Blue, 0.25, 0.25, 0.25)</p>
  2608. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2609. border:none;padding:0in'>        box.Position = [1, 0, 0]</p>
  2610. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2611. border:none;padding:0in'>&nbsp;</p>
  2612. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2613. border:none;padding:0in'>        return True</p>
  2614. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2615. border:none;padding:0in'>&nbsp;</p>
  2616. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2617. border:none;padding:0in'>if __name__ == '__main__':</p>
  2618. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2619. border:none;padding:0in'>    main([DemoSceneCreator])</p>
  2620. </div>
  2621. <p class=Normal>&nbsp;</p>
  2622. <p class=Normal>If you run this file now you should see three boxes
  2623. and the view should be centered on the green one.</p>
  2624. <p class=Normal>&nbsp;</p>
  2625. <h2><a name="_Toc140655660">Moving Objects Around, Part I</a></h2>
  2626. <p class=Normal>&nbsp;</p>
  2627. <p class=Normal>The first thing we will add is a class to move an object in
  2628. a specific direction.  We will create an object which stores four pieces of
  2629. information: the object to act on, the direction to move in, the speed at which
  2630. to move, and the distance to travel before stopping.  Here is the first piece
  2631. of this class:</p>
  2632. <p class=Normal>&nbsp;</p>
  2633. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2634. background:silver;margin-left:.25in;margin-right:.25in'>
  2635. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2636. border:none;padding:0in'>class DirectionalMover(object):</p>
  2637. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2638. border:none;padding:0in'>    def __init__(self, obj, direction, speed,
  2639. distance):</p>
  2640. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2641. border:none;padding:0in'>        self.Object = obj</p>
  2642. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2643. border:none;padding:0in'>        self.Speed = speed</p>
  2644. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2645. border:none;padding:0in'>        self.Distance = distance</p>
  2646. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2647. border:none;padding:0in'>        self.Direction = Vectorize(direction)</p>
  2648. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2649. border:none;padding:0in'>        self.Direction.Normalize()</p>
  2650. </div>
  2651. <p class=Normal>&nbsp;</p>
  2652. <p class=Normal>The only major thing to note here is that we have normalized
  2653. the direction vector, meaning that the total length of the vector is equal to
  2654. one.</p>
  2655. <p class=Normal>&nbsp;</p>
  2656. <p class=Normal>Now we will add code to move the object every frame.  The
  2657. first thing we will do is calculate the amount with which we wish to move it
  2658. this frame.</p>
  2659. <p class=Normal>&nbsp;</p>
  2660. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2661. background:silver;margin-left:.25in;margin-right:.25in'>
  2662. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2663. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  2664. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2665. border:none;padding:0in'>        toTravel = self.Speed * elapsed</p>
  2666. </div>
  2667. <p class=Normal>&nbsp;</p>
  2668. <p class=Normal>We have to be careful that we do not overshoot the distance
  2669. we wish to travel.  To keep track of this, we will constantly subtract the
  2670. toTravel variable from the Distance variable.  Once our Distance variable is
  2671. less than the amount we would travel this frame, we translate the rest of the
  2672. distance and return True (to remove ourselves from the listener pool):</p>
  2673. <p class=Normal>&nbsp;</p>
  2674. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2675. background:silver;margin-left:.25in;margin-right:.25in'>
  2676. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2677. border:none;padding:0in'>        if self.Distance &lt; toTravel:</p>
  2678. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2679. border:none;padding:0in'>            self.Object.Translate(self.Direction *
  2680. self.Distance)</p>
  2681. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2682. border:none;padding:0in'>            return True</p>
  2683. </div>
  2684. <p class=Normal>&nbsp;</p>
  2685. <p class=Normal>Otherwise, subtract, and then translate:</p>
  2686. <p class=Normal>&nbsp;</p>
  2687. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2688. background:silver;margin-left:.25in;margin-right:.25in'>
  2689. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2690. border:none;padding:0in'>        else:</p>
  2691. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2692. border:none;padding:0in'>            self.Distance -= toTravel</p>
  2693. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2694. border:none;padding:0in'>            self.Object.Translate(self.Direction *
  2695. toTravel)</p>
  2696. </div>
  2697. <p class=Normal>&nbsp;</p>
  2698. <p class=Normal>Thats it!  Now lets test it out.  Add the following
  2699. method to the DemoSceneCreator class:</p>
  2700. <p class=Normal>&nbsp;</p>
  2701. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2702. background:silver;margin-left:.25in;margin-right:.25in'>
  2703. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2704. border:none;padding:0in'>    def OnSceneBegin(self, sm):</p>
  2705. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2706. border:none;padding:0in'>        dm = DirectionalMover(sm.Objects[&quot;box 1&quot;],
  2707. [1, 0, 0], 0.3, 3)</p>
  2708. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2709. border:none;padding:0in'>        sm.AddListener(dm)</p>
  2710. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2711. border:none;padding:0in'>        </p>
  2712. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2713. border:none;padding:0in'>        dm = DirectionalMover(sm.Objects[&quot;box 2&quot;],
  2714. [0, 1, 0], 0.2, 2)</p>
  2715. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2716. border:none;padding:0in'>        sm.AddListener(dm)</p>
  2717. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2718. border:none;padding:0in'>        </p>
  2719. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2720. border:none;padding:0in'>        dm = DirectionalMover(sm.Objects[&quot;box 3&quot;],
  2721. [0, 0, 1], 1, 10)</p>
  2722. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2723. border:none;padding:0in'>        sm.AddListener(dm)</p>
  2724. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2725. border:none;padding:0in'>&nbsp;</p>
  2726. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2727. border:none;padding:0in'>        return True</p>
  2728. </div>
  2729. <p class=Normal>&nbsp;</p>
  2730. <p class=Normal>Now run the application.  You should see the three boxes
  2731. moving in different directions, reaching different locations simultaneously
  2732. (after 10 seconds).</p>
  2733. <p class=Normal>&nbsp;</p>
  2734. <p class=Normal>If you are having trouble with this example, compare your
  2735. code with the contents of demo1.py.</p>
  2736. <p class=Normal>&nbsp;</p>
  2737. <h2><a name="_Toc140655661">Moving Objects Around, Part II</a></h2>
  2738. <p class=Normal>&nbsp;</p>
  2739. <p class=Normal>Sometimes it is not convenient to simply move an object in a
  2740. direction.  Often we need to move an object to a location instead.  What we
  2741. would like, in this case, is an object mover which takes in a position to move
  2742. to, and a speed at which to move.</p>
  2743. <p class=Normal>&nbsp;</p>
  2744. <p class=Normal>One approach to this would be to use the class we have
  2745. already created and simply figure out the distance between the current position
  2746. and the position we wish to move to.  This would be a valid approach in some
  2747. situations, but what if the position of the object was changed by some other
  2748. object in between the time the mover object was created, and the time at which
  2749. the object reached its destination?  Then the object would be off-target by the
  2750. time it reached its destination.</p>
  2751. <p class=Normal>&nbsp;</p>
  2752. <p class=Normal>To remedy this problem, we will create a new class to
  2753. perform this action.  This is the constructor for this class:</p>
  2754. <p class=Normal>&nbsp;</p>
  2755. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2756. background:silver;margin-left:.25in;margin-right:.25in'>
  2757. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2758. border:none;padding:0in'>class PositionalMover(object):</p>
  2759. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2760. border:none;padding:0in'>    def __init__(self, obj, destination, speed):</p>
  2761. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2762. border:none;padding:0in'>        self.Object = obj</p>
  2763. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2764. border:none;padding:0in'>        self.Dest = Vectorize(destination)</p>
  2765. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2766. border:none;padding:0in'>        self.Speed = speed</p>
  2767. </div>
  2768. <p class=Normal>&nbsp;</p>
  2769. <p class=Normal>In the OnFrame method, we need to calculate the distance we
  2770. will travel that frame, the distance we have left to travel (so we do not
  2771. overshoot our destination), and the direction we will travel in.</p>
  2772. <p class=Normal>&nbsp;</p>
  2773. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2774. background:silver;margin-left:.25in;margin-right:.25in'>
  2775. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2776. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  2777. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2778. border:none;padding:0in'>        toTravel = self.Speed * elapsed</p>
  2779. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2780. border:none;padding:0in'>        direction = self.Dest - self.Object.Position</p>
  2781. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2782. border:none;padding:0in'>        distance = direction.Length()</p>
  2783. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2784. border:none;padding:0in'>        direction.Normalize()</p>
  2785. </div>
  2786. <p class=Normal>&nbsp;</p>
  2787. <p class=Normal>&nbsp;</p>
  2788. <p class=Normal>Once we have this information, the rest of our method should
  2789. look very familiar.</p>
  2790. <p class=Normal>&nbsp;</p>
  2791. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 0in 4.0pt;
  2792. background:silver;margin-left:.25in;margin-right:.25in'>
  2793. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2794. border:none;padding:0in'>        if distance &lt; toTravel:</p>
  2795. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2796. border:none;padding:0in'>            self.Object.Translate(direction *
  2797. distance)</p>
  2798. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2799. border:none;padding:0in'>            return True</p>
  2800. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2801. border:none;padding:0in'>        </p>
  2802. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2803. border:none;padding:0in'>        else:</p>
  2804. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2805. border:none;padding:0in'>            self.Object.Translate(direction *
  2806. toTravel)</p>
  2807. </div>
  2808. <p class=Normal>&nbsp;</p>
  2809. <p class=Normal>Note that we are doing a lot more calculations per frame in
  2810. PositionalMover than we did in DirectionalMover (normalizing a vector is a
  2811. costly operation as it involves a square root).  We have to be careful how
  2812. often we use this class, as it could possibly bog down our application if we
  2813. overused it.  However, this would not really become a concern until we are
  2814. moving a large amount of objects with this class.</p>
  2815. <p class=Normal>&nbsp;</p>
  2816. <p class=Normal>Now lets test our class.  Clear out the contents of
  2817. DemoSceneCreators OnSceneBegin method, if you have not already, and add this:</p>
  2818. <p class=Normal>&nbsp;</p>
  2819. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2820. background:silver;margin-left:.25in;margin-right:.25in'>
  2821. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2822. border:none;padding:0in'>    def OnSceneBegin(self, sm):</p>
  2823. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2824. border:none;padding:0in'>        pm = PositionalMover(sm.Objects[&quot;box 1&quot;],
  2825. [2, 0, 0], 0.3)</p>
  2826. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2827. border:none;padding:0in'>        sm.AddListener(pm)</p>
  2828. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2829. border:none;padding:0in'>        </p>
  2830. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2831. border:none;padding:0in'>        pm = PositionalMover(sm.Objects[&quot;box 2&quot;],
  2832. [0, 2, 0], 0.2)</p>
  2833. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2834. border:none;padding:0in'>        sm.AddListener(pm)</p>
  2835. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2836. border:none;padding:0in'>        </p>
  2837. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2838. border:none;padding:0in'>        pm = PositionalMover(sm.Objects[&quot;box 3&quot;],
  2839. [1, 0, 10], 1)</p>
  2840. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2841. border:none;padding:0in'>        sm.AddListener(pm)</p>
  2842. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2843. border:none;padding:0in'>        </p>
  2844. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2845. border:none;padding:0in'>        return True</p>
  2846. </div>
  2847. <p class=Normal>&nbsp;</p>
  2848. <p class=Normal>The results should be exactly the same as before.  We have
  2849. moved the boxes to the same locations at the same speed.  Try playing around
  2850. with the locations the boxes move to, and the speed at which they move.  Try
  2851. adding both a PositonalMover and DirectionalMover affectors to the same object
  2852. (be sure to register both listeners with the SceneManager) and watch the
  2853. effects.</p>
  2854. <p class=Normal>&nbsp;</p>
  2855. <p class=Normal>If you are having trouble with this example, compare your
  2856. code with the contents of demo2.py.</p>
  2857. <p class=Normal>&nbsp;</p>
  2858. <h2><a name="_Toc140655662">Moving Objects Around, Part III</a></h2>
  2859. <p class=Normal>&nbsp;</p>
  2860. <p class=Normal>This is very cool, but still, there are many situations
  2861. where simply moving from a location to another location would not be enough.  Sometimes
  2862. we need to set up a list of points for an object (or character) to walk between. 
  2863. This can be accomplished by keeping track of a set of points (and speeds at
  2864. which to travel to these points) and delegating the actual movement task to
  2865. PositionalMover.</p>
  2866. <p class=Normal>&nbsp;</p>
  2867. <p class=Normal>We will implement this class as PointMover, for lack of a
  2868. better term.  The constructor for this object will take in only one parameter,
  2869. the object to move.  The object will also keep track of a list of points to
  2870. walk between, and the current PositionableMover object (which is doing the
  2871. actual work).</p>
  2872. <p class=Normal>&nbsp;</p>
  2873. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2874. background:silver;margin-left:.25in;margin-right:.25in'>
  2875. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2876. border:none;padding:0in'>class PointMover(object):</p>
  2877. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2878. border:none;padding:0in'>    def __init__(self, obj):</p>
  2879. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2880. border:none;padding:0in'>        self.Object = obj</p>
  2881. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2882. border:none;padding:0in'>        self.Points = []</p>
  2883. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2884. border:none;padding:0in'>        self.Current = None</p>
  2885. </div>
  2886. <p class=Normal>&nbsp;</p>
  2887. <p class=Normal>Now we need to create a method to add points for the
  2888. object to move to.  This method will take two parameters: the point to
  2889. move to and the speed at which to move.  Well simply throw this into the
  2890. Points list as a tuple.</p>
  2891. <p class=Normal>&nbsp;</p>
  2892. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2893. background:silver;margin-left:.25in;margin-right:.25in'>
  2894. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2895. border:none;padding:0in'>    def AddPoint(self, point, speed):</p>
  2896. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2897. border:none;padding:0in'>        self.Points.append((point, speed))</p>
  2898. </div>
  2899. <p class=Normal>&nbsp;</p>
  2900. <p class=Normal>Now we need to implement a method which sets the Current
  2901. variable by taking the first item in the list and creating a PositionalMover
  2902. for it.  We will return True if we are successful in creating the object:</p>
  2903. <p class=Normal>&nbsp;</p>
  2904. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2905. background:silver;margin-left:.25in;margin-right:.25in'>
  2906. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2907. border:none;padding:0in'>    def NextPoint(self):</p>
  2908. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2909. border:none;padding:0in'>        if len(self.Points):</p>
  2910. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2911. border:none;padding:0in'>            dest, speed = self.Points[0]</p>
  2912. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2913. border:none;padding:0in'>            del self.Points[0]</p>
  2914. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2915. border:none;padding:0in'>            self.Current =
  2916. PositionalMover(self.Object, dest, speed)</p>
  2917. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2918. border:none;padding:0in'>            return True</p>
  2919. </div>
  2920. <p class=Normal>&nbsp;</p>
  2921. <p class=Normal>Notice that we have not added a return False to the end of
  2922. this method.  This would be valid, but unnecessary.  Whenever you do not
  2923. return a value, you are implicitly returning None.  Since None evaluates to be
  2924. false, you actually <b>are</b> returning false implicitly whenever you do not
  2925. return anything.  (Or more specifically, you are returning an expression which
  2926. evaluates to False.)</p>
  2927. <p class=Normal>&nbsp;</p>
  2928. <p class=Normal>Now that we have all of the helper methods that are
  2929. required, we are ready to create the OnFrame method.  The first part of the
  2930. method will handle the first call to the method (in which case self.Current
  2931. is None).  If it is None then we will attempt to set it by calling NextPoint. 
  2932. If NextPoint returns False then the user never bothered to add any points, and
  2933. we will return True to tell SceneManager that we are done:</p>
  2934. <p class=Normal>&nbsp;</p>
  2935. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2936. background:silver;margin-left:.25in;margin-right:.25in'>
  2937. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2938. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  2939. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2940. border:none;padding:0in'>        if self.Current is None:</p>
  2941. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2942. border:none;padding:0in'>            if not self.NextPoint():</p>
  2943. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2944. border:none;padding:0in'>                return True</p>
  2945. </div>
  2946. <p class=Normal>&nbsp;</p>
  2947. <p class=Normal>Now that we have setup self.Current, we need to actually
  2948. move the object.  As soon as self.Current.OnFrame returns false, we need to
  2949. move onto the pointif there are any left.  This is the rest of the method:</p>
  2950. <p class=Normal>&nbsp;</p>
  2951. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2952. background:silver;margin-left:.25in;margin-right:.25in'>
  2953. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2954. border:none;padding:0in'>        if self.Current.OnFrame(elapsed):</p>
  2955. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2956. border:none;padding:0in'>            return not self.NextPoint()</p>
  2957. </div>
  2958. <p class=Normal>&nbsp;</p>
  2959. <p class=Normal>Now that we have implemented this class, its time to test
  2960. it.  Clear out the contents of DemoSceneCreators OnSceneBegin method, if you
  2961. have not already.  Place this code there:</p>
  2962. <p class=Normal>&nbsp;</p>
  2963. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  2964. background:silver;margin-left:.25in;margin-right:.25in'>
  2965. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2966. border:none;padding:0in'>    def OnSceneBegin(self, sm):</p>
  2967. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2968. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 1&quot;])</p>
  2969. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2970. border:none;padding:0in'>        pm.AddPoint( [2, -1, 0], 1 )</p>
  2971. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2972. border:none;padding:0in'>        pm.AddPoint( [0, 1, 2], 1 )</p>
  2973. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2974. border:none;padding:0in'>        pm.AddPoint( [0, 0, 10], 1 )</p>
  2975. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2976. border:none;padding:0in'>        sm.AddListener(pm) </p>
  2977. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2978. border:none;padding:0in'>        </p>
  2979. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2980. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 2&quot;])</p>
  2981. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2982. border:none;padding:0in'>        pm.AddPoint( [2, 1, 0], 1 )</p>
  2983. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2984. border:none;padding:0in'>        pm.AddPoint( [-1, -3, 5], 1 )</p>
  2985. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2986. border:none;padding:0in'>        pm.AddPoint( [-1, 0, 10], 1 )</p>
  2987. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2988. border:none;padding:0in'>        sm.AddListener(pm) </p>
  2989. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2990. border:none;padding:0in'>        </p>
  2991. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2992. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 3&quot;])</p>
  2993. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2994. border:none;padding:0in'>        pm.AddPoint( [-2, -1, 0], 1 )</p>
  2995. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2996. border:none;padding:0in'>        pm.AddPoint( [1, -3, 0], 1 )</p>
  2997. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  2998. border:none;padding:0in'>        pm.AddPoint( [1, 0, 10], 1.15 )</p>
  2999. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3000. border:none;padding:0in'>        sm.AddListener(pm)</p>
  3001. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3002. border:none;padding:0in'>&nbsp;</p>
  3003. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3004. border:none;padding:0in'>        return True</p>
  3005. </div>
  3006. <p class=Normal>&nbsp;</p>
  3007. <p class=Normal>Run the program to watch the cubes dart around the scene.  Take
  3008. some time to play around with the points the objects move to, and the speed at
  3009. which they move.</p>
  3010. <p class=Normal>&nbsp;</p>
  3011. <p class=Normal>If you are having trouble with this example, compare your
  3012. code with the contents of demo3.py.</p>
  3013. <p class=Normal>&nbsp;</p>
  3014. <h2><a name="_Toc140655663">Auto Tracking</a></h2>
  3015. <p class=Normal>&nbsp;</p>
  3016. <p class=Normal>It would be very useful in many situations to make the
  3017. camera (or other object) track something on screen.  That is, every time the
  3018. object moves, the camera is readjusted to face it.  Fortunately for us, this is
  3019. a nearly trivial thing to implement with the framework we have created:</p>
  3020. <p class=Normal>&nbsp;</p>
  3021. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3022. background:silver;margin-left:.25in;margin-right:.25in'>
  3023. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3024. border:none;padding:0in'>class AutoTrack(object):</p>
  3025. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3026. border:none;padding:0in'>    def __init__(self, obj, target):</p>
  3027. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3028. border:none;padding:0in'>        self.Object = obj</p>
  3029. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3030. border:none;padding:0in'>        self.Target = target</p>
  3031. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3032. border:none;padding:0in'>    </p>
  3033. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3034. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  3035. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3036. border:none;padding:0in'>        self.Object.LookAt(self.Target.Position)</p>
  3037. </div>
  3038. <p class=Normal>&nbsp;</p>
  3039. <p class=Normal>Replace DemoSceneCreators OnSceneBegin method with the
  3040. following code:</p>
  3041. <p class=Normal>&nbsp;</p>
  3042. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3043. background:silver;margin-left:.25in;margin-right:.25in'>
  3044. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3045. border:none;padding:0in'>    def OnSceneBegin(self, sm):</p>
  3046. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3047. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 1&quot;])</p>
  3048. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3049. border:none;padding:0in'>        pm.AddPoint( [2, -1, 0], 1 )</p>
  3050. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3051. border:none;padding:0in'>        pm.AddPoint( [0, 1, 2], 1 )</p>
  3052. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3053. border:none;padding:0in'>        pm.AddPoint( [0, 0, 10], 1 )</p>
  3054. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3055. border:none;padding:0in'>        sm.AddListener(pm)</p>
  3056. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3057. border:none;padding:0in'>&nbsp;</p>
  3058. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3059. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 2&quot;])</p>
  3060. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3061. border:none;padding:0in'>        pm.AddPoint( [2, 1, 0], 1 )</p>
  3062. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3063. border:none;padding:0in'>        pm.AddPoint( [-1, -3, 5], 1 )</p>
  3064. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3065. border:none;padding:0in'>        pm.AddPoint( [-1, 0, 10], 1 )</p>
  3066. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3067. border:none;padding:0in'>        sm.AddListener(pm)</p>
  3068. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3069. border:none;padding:0in'>&nbsp;</p>
  3070. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3071. border:none;padding:0in'>        pm = PointMover(sm.Objects[&quot;box 3&quot;])</p>
  3072. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3073. border:none;padding:0in'>        pm.AddPoint( [-2, -1, 0], 1 )</p>
  3074. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3075. border:none;padding:0in'>        pm.AddPoint( [1, -3, 0], 1 )</p>
  3076. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3077. border:none;padding:0in'>        pm.AddPoint( [1, 0, 10], 1.15 )</p>
  3078. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3079. border:none;padding:0in'>        sm.AddListener(pm)</p>
  3080. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3081. border:none;padding:0in'>        </p>
  3082. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3083. border:none;padding:0in'>        sm.AddListener( AutoTrack(sm.Objects[&quot;Player
  3084. Cam&quot;], sm.Objects[&quot;box 3&quot;]) )</p>
  3085. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3086. border:none;padding:0in'>&nbsp;</p>
  3087. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3088. border:none;padding:0in'>        return True</p>
  3089. </div>
  3090. <p class=Normal>&nbsp;</p>
  3091. <p class=Normal>Run the demo.  This probably looks very strange.  You will
  3092. see one box not moving, while the other two fly around.  This shows that our
  3093. camera is tracking the object we have specified.  To confirm this, comment
  3094. out the line which adds the AutoTrack to the SceneManager.  The boxes move as
  3095. normal.  The reason this looks so strange is because we have not set a
  3096. background to contrast the movement of the boxes.  This would look a lot more natural
  3097. in a real-world application.</p>
  3098. <p class=Normal>&nbsp;</p>
  3099. <p class=Normal>If you are having trouble with this example, compare your
  3100. code with the contents of demo4.py.</p>
  3101. <p class=Normal>&nbsp;</p>
  3102. <h2><a name="_Toc140655664">Gravity and Free Floating Bodies</a></h2>
  3103. <p class=Normal>&nbsp;</p>
  3104. <p class=Normal>I have saved the best part of the tutorial for last.  In
  3105. this section, we will implement a gravity simulator.  The simulator will allow
  3106. you to create any number of free floating bodies in space (like planets) by setting
  3107. their mass, position, initial velocity, and color (the color is just for visual
  3108. distinction).</p>
  3109. <p class=Normal>&nbsp;</p>
  3110. <p class=Normal>The first thing we need to do is to create a class which
  3111. understands how to apply the physics of gravity to a particular object.  This
  3112. object will keep track of an objects Mass, Velocity, Acceleration, and a list
  3113. of objects which are affecting the object.  Additionally we will keep track of
  3114. the objects radius to handle what happens when objects get too close to each
  3115. other.  The constructor for this class is very simple, taking in a few of these
  3116. as parameters and setting up variables:</p>
  3117. <p class=Normal>&nbsp;</p>
  3118. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3119. background:silver;margin-left:.25in;margin-right:.25in'>
  3120. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3121. border:none;padding:0in'>class GravityAffector(object):</p>
  3122. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3123. border:none;padding:0in'>    def __init__(self, obj, mass, radius,
  3124. initialVelocity):</p>
  3125. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3126. border:none;padding:0in'>        self.Object = obj</p>
  3127. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3128. border:none;padding:0in'>        self.Radius = radius</p>
  3129. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3130. border:none;padding:0in'>        self.Affectors = []</p>
  3131. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3132. border:none;padding:0in'>        self.Mass = mass</p>
  3133. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3134. border:none;padding:0in'>        self.Velocity = Vectorize(initialVelocity)</p>
  3135. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3136. border:none;padding:0in'>        self.Acceleration = DirectX.Vector3(0,0,0)</p>
  3137. </div>
  3138. <p class=Normal>&nbsp;</p>
  3139. <p class=Normal>Now we need to create a method which figures out exactly
  3140. how much acceleration is being applied to an object at any given moment.  The
  3141. result will be stored in the self.Acceleration variable.</p>
  3142. <p class=Normal>&nbsp;</p>
  3143. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3144. background:silver;margin-left:.25in;margin-right:.25in'>
  3145. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3146. border:none;padding:0in'>    def ApplyGravity(self):</p>
  3147. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3148. border:none;padding:0in'>        self.Acceleration = DirectX.Vector3(0,0,0)</p>
  3149. </div>
  3150. <p class=Normal>&nbsp;</p>
  3151. <p class=Normal>Now we need to loop through all of the objects affecting the
  3152. current object and apply Newtons law of universal gravitation to the current
  3153. object.  To simplify a lot of later code, we will assume the self.Affectors
  3154. list could possibly contain the object we are acting on.  In this case well
  3155. simply ignore it.</p>
  3156. <p class=Normal>&nbsp;</p>
  3157. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3158. background:silver;margin-left:.25in;margin-right:.25in'>
  3159. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3160. border:none;padding:0in'>        for obj in self.Affectors:</p>
  3161. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3162. border:none;padding:0in'>            if obj != self.Object:</p>
  3163. </div>
  3164. <p class=Normal>&nbsp;</p>
  3165. <p class=Normal>To calculate the affect of gravity, we need to know the
  3166. direction (unit vector) that the force is acting in, and the squared distance
  3167. between the two objects.</p>
  3168. <p class=Normal>&nbsp;</p>
  3169. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3170. background:silver;margin-left:.25in;margin-right:.25in'>
  3171. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3172. border:none;padding:0in'>                direction = obj.Object.Position -
  3173. self.Object.Position</p>
  3174. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3175. border:none;padding:0in'>                distSquared =
  3176. direction.LengthSquared()</p>
  3177. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3178. border:none;padding:0in'>                direction.Normalize()</p>
  3179. </div>
  3180. <p class=Normal>&nbsp;</p>
  3181. <p class=Normal>Finally, we need to calculate the change in acceleration due
  3182. to this object.  There is one small catch though.  We are not doing any
  3183. collision detection in this piece of code, and if objects are too close to each
  3184. other the gravitation formulas explode, giving accelerations which should be
  3185. technically impossible.  To fix this problem, we only apply gravity when the
  3186. squared distance is greater than the radius of the object.  So long as the
  3187. radius is set to be a reasonable amount (say 0.05 units), this will fix the
  3188. problem of planets having acceleration they should not have when colliding.</p>
  3189. <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
  3190. font-family:Consolas'>&nbsp;</span></p>
  3191. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3192. background:silver;margin-left:.25in;margin-right:.25in'>
  3193. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3194. border:none;padding:0in'>                if distSquared &gt; self.Radius:</p>
  3195. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3196. border:none;padding:0in'>                    self.Acceleration += direction *
  3197. (obj.Mass / distSquared)</p>
  3198. </div>
  3199. <p class=Normal>&nbsp;</p>
  3200. <p class=Normal>Any physicist who just read that probably physically cringed
  3201. at this equation.  This is not exactly Newtons law of universal gravitation. 
  3202. However, since we are arbitrarily defining the mass of our objects, this should
  3203. be good enough for our simulation.</p>
  3204. <p class=Normal>&nbsp;</p>
  3205. <p class=Normal>Finally, during each frame we need to apply the acceleration
  3206. to the velocity of the object, and apply the velocity to the position of the
  3207. object.</p>
  3208. <p class=Normal>&nbsp;</p>
  3209. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3210. background:silver;margin-left:.25in;margin-right:.25in'>
  3211. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3212. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  3213. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3214. border:none;padding:0in'>        self.Velocity += self.Acceleration * elapsed</p>
  3215. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3216. border:none;padding:0in'>        self.Object.Translate(self.Velocity * elapsed)</p>
  3217. </div>
  3218. <p class=Normal>&nbsp;</p>
  3219. <p class=Normal>We now need to ensure that the acceleration of all objects is
  3220. calculated before we apply it.  We do not want to move any object before all
  3221. other objects get the position of the object.  In theory, the small amount of
  3222. motion should not affect the overall mechanics of the scene, but in practice
  3223. our scene will have few enough objects (close enough together) that this will
  3224. actually impact it.</p>
  3225. <p class=Normal>&nbsp;</p>
  3226. <p class=Normal>To solve this, we will create a GravityManager class which
  3227. calls the ApplyGravity methods of all objects before it calls the OnFrame method.  Here is the code for this class:</p>
  3228. <p class=Normal>&nbsp;</p>
  3229. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3230. background:silver;margin-left:.25in;margin-right:.25in'>
  3231. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3232. border:none;padding:0in'>class GravityManager(object):</p>
  3233. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3234. border:none;padding:0in'>    def __init__(self, objs = []):</p>
  3235. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3236. border:none;padding:0in'>        self.Objects = objs</p>
  3237. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3238. border:none;padding:0in'>    </p>
  3239. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3240. border:none;padding:0in'>    def OnFrame(self, elapsed):</p>
  3241. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3242. border:none;padding:0in'>        for obj in self.Objects:</p>
  3243. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3244. border:none;padding:0in'>            obj.ApplyGravity()</p>
  3245. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3246. border:none;padding:0in'>        for obj in self.Objects:</p>
  3247. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3248. border:none;padding:0in'>            obj.OnFrame(elapsed)</p>
  3249. </div>
  3250. <p class=Normal>&nbsp;</p>
  3251. <p class=Normal>Now we are almost ready to create our scene, but there is
  3252. one final snag we must correct before we are ready to do that.  When we start
  3253. our simulation up, all of the objects we have created will go flying around the
  3254. scene.  We need the camera to watch all of them so that we can see what is
  3255. going on.  The AutoTrack class we have already created seems like a good
  3256. choice, but objects could move too far to actually be seen.</p>
  3257. <p class=Normal>&nbsp;</p>
  3258. <p class=Normal>Our solution will be a new auto tracking class which finds
  3259. the midpoint of all objects on the screen and looks at that instead.  It will
  3260. also always stay a specified distance from the midpoint, so objects have a
  3261. harder time moving off camera.  I will warn you up front that this is not a
  3262. complete solution (objects will still fall off screen eventually), but it
  3263. should at least allow us to watch our simulation for longer than our original
  3264. AutoTrack class would.</p>
  3265. <p class=Normal>&nbsp;</p>
  3266. <p class=Normal>The constructor for this class should take in the object we
  3267. will be operating on (the camera in this case), the list of objects to find the
  3268. midpoint for, and a distance (offset) to stay from this midpoint.</p>
  3269. <p class=Normal>&nbsp;</p>
  3270. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3271. background:silver;margin-left:.25in;margin-right:.25in'>
  3272. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3273. border:none;padding:0in'>class MidpointAutoTrack(object):</p>
  3274. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3275. border:none;padding:0in'>    def __init__(self, obj, others, offset):</p>
  3276. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3277. border:none;padding:0in'>        self.Object = obj</p>
  3278. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3279. border:none;padding:0in'>        self.Others = others</p>
  3280. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3281. border:none;padding:0in'>        self.Offset = Vectorize(offset)</p>
  3282. </div>
  3283. <p class=Normal>&nbsp;</p>
  3284. <p class=Normal>The OnFrame method will calculate the midpoint for the objects and have the camera object look at it.  We will first check to see if
  3285. the self.Others variable is a sequence.  If it is not we will assume the user
  3286. just passed in a singular object.  Otherwise, we will store the length of our
  3287. self.Others sequence in the l variable:</p>
  3288. <p class=Normal>&nbsp;</p>
  3289. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3290. background:silver;margin-left:.25in;margin-right:.25in'>
  3291. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3292. border:none;padding:0in'>    def OnFrame(self, elapsed): </p>
  3293. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3294. border:none;padding:0in'>        l = None</p>
  3295. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3296. border:none;padding:0in'>        pos = DirectX.Vector3(0, 0, 0)</p>
  3297. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3298. border:none;padding:0in'>        </p>
  3299. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3300. border:none;padding:0in'>        if type(self.Others)==list:</p>
  3301. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3302. border:none;padding:0in'>            l = len(self.Others)</p>
  3303. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3304. border:none;padding:0in'>        </p>
  3305. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3306. border:none;padding:0in'>        if l is None:</p>
  3307. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3308. border:none;padding:0in'>            pos = self.Others.Position</p>
  3309. </div>
  3310. <p class=Normal>&nbsp;</p>
  3311. <p class=Normal>We need to handle the case where the length is equal to one
  3312. specially:</p>
  3313. <p class=Normal>&nbsp;</p>
  3314. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3315. background:silver;margin-left:.25in;margin-right:.25in'>
  3316. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3317. border:none;padding:0in'>        elif l == 1:</p>
  3318. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3319. border:none;padding:0in'>            pos = self.Others[0].Position</p>
  3320. </div>
  3321. <p class=Normal>&nbsp;</p>
  3322. <p class=Normal>If we did not do this, we would get a divide by zero error
  3323. in the code which handles the midpoint calculation:</p>
  3324. <p class=Normal>&nbsp;</p>
  3325. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3326. background:silver;margin-left:.25in;margin-right:.25in'>
  3327. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3328. border:none;padding:0in'>        elif l &gt; 1:</p>
  3329. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3330. border:none;padding:0in'>            for o in self.Others:</p>
  3331. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3332. border:none;padding:0in'>                pos += o.Position</p>
  3333. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3334. border:none;padding:0in'>            </p>
  3335. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3336. border:none;padding:0in'>            pos.X /= l-1</p>
  3337. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3338. border:none;padding:0in'>            pos.Y /= l-1</p>
  3339. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3340. border:none;padding:0in'>            pos.Z /= l-1</p>
  3341. </div>
  3342. <p class=Normal>&nbsp;</p>
  3343. <p class=Normal>Now that we have calculated the position, we need to set the
  3344. position of the current object and have it look at the calculated position:</p>
  3345. <p class=Normal>&nbsp;</p>
  3346. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3347. background:silver;margin-left:.25in;margin-right:.25in'>
  3348. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3349. border:none;padding:0in'>        self.Object.Position = pos + self.Offset</p>
  3350. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3351. border:none;padding:0in'>        self.Object.LookAt(pos)</p>
  3352. </div>
  3353. <p class=Normal>&nbsp;</p>
  3354. <p class=Normal>This completes our MidpointAutoTrack class.</p>
  3355. <p class=Normal>&nbsp;</p>
  3356. <p class=Normal>Now that we have all of the code we need, lets create the
  3357. scene.  The first thing we will do is create a list which contains information
  3358. for all of the objects we will place in the scene.  By building this list first
  3359. (and then using this list to actually create the scene), we are making it much
  3360. easier to add and remove objects from the scene later.</p>
  3361. <p class=Normal>&nbsp;</p>
  3362. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3363. background:silver;margin-left:.25in;margin-right:.25in'>
  3364. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3365. border:none;padding:0in'>class GravityDemoCreator(object):</p>
  3366. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3367. border:none;padding:0in'>    def OnSceneCreate(self, sm):</p>
  3368. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3369. border:none;padding:0in'>        objs = []</p>
  3370. </div>
  3371. <p class=Normal>&nbsp;</p>
  3372. <p class=Normal>Next we will populate the objs list with tuples containing
  3373. the color, mass, position, and velocity of each object in the scene.  Note that
  3374. I have purposefully commented out the last line.  The first couple of times you
  3375. run the code you should only have three objects in the scene (since the gravity
  3376. for this is much more stable).  You should uncomment this line to add a fourth
  3377. after you have played with the code with only 3 objects.</p>
  3378. <p class=Normal>&nbsp;</p>
  3379. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3380. background:silver;margin-left:.25in;margin-right:.25in'>
  3381. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3382. border:none;padding:0in'>        # add: color, mass, poition, velocity</p>
  3383. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3384. border:none;padding:0in'>        objs.Add( (Drawing.Color.Red,    1, [-1, 0,
  3385. 0], [0.05, 0.05, 0]) )</p>
  3386. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3387. border:none;padding:0in'>        objs.Add( (Drawing.Color.Green,  1, [0, 1,
  3388. 0],  [0.1, 0, 0]) )</p>
  3389. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3390. border:none;padding:0in'>        objs.Add( (Drawing.Color.Blue,   1, [1, 0, 1],  [-0.1, 0, 0]) )</p>
  3391. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3392. border:none;padding:0in'>        #objs.Add( (Drawing.Color.White, 1, [0, 0,
  3393. 1],&nbsp; [0, 0, 0]) )</p>
  3394. </div>
  3395. <p class=Normal>&nbsp;</p>
  3396. <p class=Normal>Now we will loop through the objects, placing the created object
  3397. in a list called finalObjects, and placing a GravityAffector for the objects
  3398. into a list called affectors:</p>
  3399. <p class=Normal>&nbsp;</p>
  3400. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3401. background:silver;margin-left:.25in;margin-right:.25in'>
  3402. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3403. border:none;padding:0in'>        radius = 0.05</p>
  3404. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3405. border:none;padding:0in'>        affectors = []</p>
  3406. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3407. border:none;padding:0in'>        finalObjects = []</p>
  3408. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3409. border:none;padding:0in'>        </p>
  3410. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3411. border:none;padding:0in'>        i = 0</p>
  3412. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3413. border:none;padding:0in'>        for color, mass, position, velocity in objs:</p>
  3414. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3415. border:none;padding:0in'>            sphere =
  3416. sm.LoadBasicObject(&quot;sphere&quot;, &quot;planet &quot; + str(i), color,
  3417. radius, 25, 25)</p>
  3418. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3419. border:none;padding:0in'>            sphere.Position = position</p>
  3420. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3421. border:none;padding:0in'>            </p>
  3422. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3423. border:none;padding:0in'>            affectors.Add( GravityAffector(sphere,
  3424. mass, radius, velocity) )</p>
  3425. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3426. border:none;padding:0in'>            finalObjects.Add(sphere)</p>
  3427. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3428. border:none;padding:0in'>            </p>
  3429. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3430. border:none;padding:0in'>            i += 1</p>
  3431. </div>
  3432. <p class=Normal>&nbsp;</p>
  3433. <p class=Normal>Now that we have created all of the GravityAffector objects,
  3434. we need to set them to affect each other.  We do that by setting the Affectors
  3435. variable on all of them.  After this is done, we need to add create the GravityManager
  3436. class and add it to the SceneManager.</p>
  3437. <p class=Normal>&nbsp;</p>
  3438. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3439. background:silver;margin-left:.25in;margin-right:.25in'>
  3440. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3441. border:none;padding:0in'>        for g in affectors:</p>
  3442. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3443. border:none;padding:0in'>            g.Affectors = affectors</p>
  3444. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3445. border:none;padding:0in'>        </p>
  3446. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3447. border:none;padding:0in'>        gm = GravityManager(affectors)</p>
  3448. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3449. border:none;padding:0in'>        sm.AddListener(gm)</p>
  3450. </div>
  3451. <p class=Normal>&nbsp;</p>
  3452. <p class=Normal>The last thing we need to do in this method is to create the
  3453. camera and have it autotrack the midpoint of all of these items:</p>
  3454. <p class=Normal>&nbsp;</p>
  3455. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3456. background:silver;margin-left:.25in;margin-right:.25in'>
  3457. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3458. border:none;padding:0in'>        cam = sm.CreateCamera(&quot;Player Cam&quot;)</p>
  3459. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3460. border:none;padding:0in'>        sm.AddListener(MidpointAutoTrack(cam,
  3461. finalObjects, [0, 0, 15]))</p>
  3462. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3463. border:none;padding:0in'>        </p>
  3464. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3465. border:none;padding:0in'>        return True</p>
  3466. </div>
  3467. <p class=Normal>&nbsp;</p>
  3468. <p class=Normal>&nbsp;</p>
  3469. <p class=Normal>Finally, we need to update the call to the main function
  3470. such that it uses the GravityDemoCreator.  Find that section of code and change
  3471. it so it reads:</p>
  3472. <p class=Normal>&nbsp;</p>
  3473. <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
  3474. background:silver;margin-left:.25in;margin-right:.25in'>
  3475. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3476. border:none;padding:0in'>if __name__ == '__main__':</p>
  3477. <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
  3478. border:none;padding:0in'>    Root().Main([GravityDemoCreator])</p>
  3479. </div>
  3480. <p class=Normal>&nbsp;</p>
  3481. <p class=Normal>Run the application and watch the simulation.  Be sure to
  3482. run it multiple times, as you will get different results each time.</p>
  3483. <p class=Normal>&nbsp;</p>
  3484. <p class=Normal>After running this several times, you may notice some
  3485. peculiar things happening with our simulation.  First and foremost, why do we
  3486. get different results each time we run the program?  Nowhere did we make a call
  3487. to a random number generator  The reason for this is floating point rounding
  3488. errors.  If you add code to dump the current velocity and acceleration of each
  3489. object to a file every frame, you will notice that after a frame or two small
  3490. rounding errors explode into large changes to the overall system.  Chaos theory
  3491. at work.  We could fix this by using a more accurate floating point class (and
  3492. chopping off the rounding error portions based on the accuracy of this class).</p>
  3493. <p class=Normal>&nbsp;</p>
  3494. <p class=Normal>Another strange thing is, if you run the program long
  3495. enough, you may notice that some objects occasionally wobble in ways which
  3496. seem unnatural.  This is due to the fact that our camera is not actually
  3497. stationary, but moving and changing directions every frame to keep the moving
  3498. objects on screen.  A lot of the weirdness of object wobble goes away if you
  3499. simply turn off camera movementbut good luck keeping them on screen if you do
  3500. not move the camera to keep up with them.</p>
  3501. <p class=Normal>&nbsp;</p>
  3502. <p class=Normal>Try adding more objects to the system and changing the mass
  3503. of objects (along with the other initial conditions of the system).</p>
  3504. <p class=Normal>&nbsp;</p>
  3505. <p class=Normal>If you are having trouble with this example, compare your
  3506. code with the contents of GravityDemo.py.</p>
  3507. <p class=Normal>&nbsp;</p>
  3508. <h1><a name="_Toc140655665">Where to Go from Here</a></h1>
  3509. <p class=Normal>&nbsp;</p>
  3510. <p class=Normal>I hope you have enjoyed working through this tutorial as
  3511. much as I have enjoyed making it.  There are several resources I would like to
  3512. point out if you are interested in further study of IronPython or DirectX. </p>
  3513. <p class=Normal>&nbsp;</p>
  3514. <h2><a name="_Toc140655666">Managed DirectX and XNA</a></h2>
  3515. <p class=Normal>&nbsp;</p>
  3516. <p class=Normal>Managed DirectX 1 (which this tutorial makes heavy use of) is not scheduled for any future updates.  Instead,
  3517. Microsoft made a new managed 3D library built on top of DirectX
  3518. called XNA.  You can find more information on XNA on their web site:</p>
  3519. <p class=Normal><a href="http://www.microsoft.com/xna/">http://www.microsoft.com/xna/</a></p>
  3520. <p class=Normal>&nbsp;</p>
  3521. <h2><a name="_Toc140655667">IronPython</a></h2>
  3522. <p class=Normal>&nbsp;</p>
  3523. <p class=Normal>You can find more information on IronPython by visiting the
  3524. IronPython Codeplex site:</p>
  3525. <p class=Normal><a href="http://www.codeplex.com/IronPython">
  3526. http://www.codeplex.com/IronPython</a></p>
  3527. <p class=Normal>&nbsp;</p>
  3528. <p class=Normal>You can subscribe to the IronPython mailing list for any
  3529. questions you have about the project:</p>
  3530. <p class=Normal><a
  3531. href="http://lists.ironpython.com/listinfo.cgi/users-ironpython.com">http://lists.ironpython.com/listinfo.cgi/users-ironpython.com</a></p>
  3532. <p class=Normal>&nbsp;</p>
  3533. <p class=Normal>There are also several blogs from the people who use
  3534. IronPython on a daily basis.  I suggest starting here:</p>
  3535. <p class=Normal><a href="http://blogs.msdn.com/ironpython/">
  3536. http://blogs.msdn.com/ironpython/</a></p>
  3537. <p class=Normal>&nbsp;</p>
  3538. <h2><a name="_Toc140655668">Extending the Framework</a></h2>
  3539. <p class=Normal>&nbsp;</p>
  3540. <p class=Normal>If you are a graphics hobbyist like I am, you may wish to
  3541. continue extending this framework.  It already has the basic features for
  3542. positioning and rotating objects, however, what it truly lacks is extra
  3543. functionality to handle more complex meshes and materials.  I have implemented
  3544. some of these things in the framework.py file which comes with this tutorial, but I did not have enough time to roll these changes back into the tutorial.&nbsp;
  3545. If you are interested, read through the framework Python module to get a good feel for the updates.</p>
  3546. </div>
  3547. </body>
  3548. </html>