/Languages/IronPython/Samples/Direct3D/readme.htm
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
- <html>
- <head>
- <meta http-equiv=Content-Type content="text/html; charset=windows-1252">
- <meta name=Generator content="Microsoft Word 12 (filtered)">
- <title>3D Graphics in IronPython</title>
- <style>
- <!--
- /* Font Definitions */
- @font-face
- {font-family:"Cambria Math";
- panose-1:2 4 5 3 5 4 6 3 2 4;}
- @font-face
- {font-family:Tahoma;
- panose-1:2 11 6 4 3 5 4 4 2 4;}
- @font-face
- {font-family:Consolas;
- panose-1:2 11 6 9 2 2 4 3 2 4;}
- /* Style Definitions */
- span.MsoHyperlink
- {color:blue;
- text-decoration:underline;}
- p.Offset, div.Offset
- {
- mso-style-name: Offset;
- margin-top: 0in;
- margin-right: .5in;
- margin-bottom: 0in;
- margin-left: .5in;
- margin-bottom: .0001pt;
- border: none;
- padding: 0in;
- font-size: 12.0pt;
- font-family: Verdana;
- }
- p.OffsetHeader, div.OffsetHeader
- {
- mso-style-name: "Offset Header";
- margin-top: 0in;
- margin-right: .5in;
- margin-bottom: 0in;
- margin-left: .5in;
- margin-bottom: .0001pt;
- text-align: center;
- border: none;
- padding: 0in;
- font-size: 12.0pt;
- font-family: Verdana;
- font-weight: bold;
- }
- @page Section1
- {size:8.5in 11.0in;
- margin:1.0in 1.25in 1.0in 1.25in;}
- /* List Definitions */
- -->
- </style>
- <link rel="stylesheet" type="text/css" href="../Samples.css">
- </head>
- <body lang=EN-US link=blue vlink=purple>
- <div class=Section1>
- <hr />
- <p class="CopyrightText">
- Information in this document is subject to change without notice. The example companies,
- organizations, products, people, and events depicted herein are fictitious. No association
- with any real company, organization, product, person or event is intended or should
- be inferred. Complying with all applicable copyright laws is the responsibility
- of the user. Without limiting the rights under copyright, no part of this document
- may be reproduced, stored in or introduced into a retrieval system, or transmitted
- in any form or by any means (electronic, mechanical, photocopying, recording, or
- otherwise), or for any purpose, without the express written permission of Microsoft
- Corporation.</p>
- <p class="CopyrightText">
- </p>
- <p class="CopyrightText">
- Microsoft may have patents, patent applications, trademarked, copyrights, or other
- intellectual property rights covering subject matter in this document. Except as
- expressly provided in any written license agreement from Microsoft, the furnishing
- of this document does not give you any license to these patents, trademarks, copyrights,
- or other intellectual property.</p>
- <p class="CopyrightText">
- </p>
- <div class="Section1">
- <p class="CopyrightText">� Microsoft Corporation. All rights
- reserved.</p>
- <p class="CopyrightText"> </p>
- <p class="CopyrightText">Microsoft, MS-DOS, MS, Windows, Windows NT,
- MSDN, Active Directory, BizTalk, SQL Server, SharePoint, Outlook, PowerPoint, FrontPage, Visual Basic,
- Visual C++, Visual J++, Visual InterDev, Visual SourceSafe, Visual C#, Visual J#, and Visual Studio
- are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other
- countries.</p>
- <p class="CopyrightText"> </p>
- <p class="CopyrightText">Other product and company names herein may
- be the trademarks of their respective owners.</div>
- <hr>
- <p class="body"> </p>
- <p class="CopyrightText">This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- copy of the license can be found in the License.html file at the root of this distribution. If
- you cannot locate the Apache License, Version 2.0, please send an email to
- ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- by the terms of the Apache License, Version 2.0.</p>
- <p class="CopyrightText">
- </p>
- <p class="CopyrightText">
- </p>
- <p class=Normal><b>
- <span style='font-size:16.0pt;font-family:"Arial","sans-serif"'>3D
- Graphics in IronPython</span></b></p>
- <p class=Normal><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'>A
- Flexible DirectX Framework in IronPython</span></p>
- <p class=Normal><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'>By
- Lee Culver</span></p>
- <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <p class=Toc1><span
- class=MsoHyperlink><a href="#_Toc140655628">Getting Started<span
- style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655629">Introduction<span
- style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655630">Prerequisites<span
- style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655631">How to Use
- this Tutorial<span style='color:windowtext;display:none;text-decoration:none'> 2</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655632">Objectives
- for this Tutorial<span style='color:windowtext;display:none;text-decoration:
- none'> </span><span
- style='color:windowtext;display:none;text-decoration:none'>2</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655633">Final Note<span
- style='color:windowtext;display:none;text-decoration:none'>. 2</span></a></span></p>
- <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655634">Creating the
- Framework for a DirectX Application<span style='color:windowtext;display:none;
- text-decoration:none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
- </div>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655635">Loading
- Assemblies and Importing Them<span style='color:windowtext;display:none;
- text-decoration:none'>.. </span>
- <span
- style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
- <div class=Section1>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655636">The
- RenderWindow Class<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655637">Code
- Checkpoint 1<span style='color:windowtext;display:none;text-decoration:none'>. 6</span></a></span></p>
- <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655638">Your First
- DirectX Application (in IronPython)<span style='color:windowtext;display:none;
- text-decoration:none'> </span><span
- style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655639">Setting up
- DirectX<span style='color:windowtext;display:none;text-decoration:none'>.. 6</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655640">Code
- Checkpoint 2<span style='color:windowtext;display:none;text-decoration:none'>. 9</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655641">An
- Introduction to Duck Typing<span style='color:windowtext;display:none;
- text-decoration:none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655642">Loading
- Meshes<span style='color:windowtext;display:none;text-decoration:none'>. 10</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655643">Rendering
- Meshes<span style='color:windowtext;display:none;text-decoration:none'>. 11</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655644">Code
- Checkpoint 3<span style='color:windowtext;display:none;text-decoration:none'>. 13</span></a></span></p>
- <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655645">Cameras,
- Moving Objects, and Events<span style='color:windowtext;display:none;
- text-decoration:none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>13</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655646">The Event
- System<span style='color:windowtext;display:none;text-decoration:none'>.. 14</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655647">Code
- Checkpoint 4<span style='color:windowtext;display:none;text-decoration:none'>. 17</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655648">Exercises<span
- style='color:windowtext;display:none;text-decoration:none'>. 17</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655649">Moving
- Objects on Screen<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>17</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655650">Rotating
- Objects on the Screen<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>20</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655651">Putting it
- All Together<span style='color:windowtext;display:none;text-decoration:none'> 20</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655652">Cameras<span
- style='color:windowtext;display:none;text-decoration:none'>. 21</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655653">Code
- Checkpoint<span style='color:windowtext;display:none;text-decoration:none'> 23</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655654">Exercises<span
- style='color:windowtext;display:none;text-decoration:none'>. 23</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655655">Framework
- Optimization<span style='color:windowtext;display:none;text-decoration:none'>. 24</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655656">Adding Other
- Types of Objects<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>26</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655657">A Better
- Main Function<span style='color:windowtext;display:none;text-decoration:none'>. 28</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655658">Code Checkpoint
- 6<span style='color:windowtext;display:none;text-decoration:none'>. 29</span></a></span></p>
- <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655659">Cool Things
- to Do with the Project<span style='color:windowtext;display:none;text-decoration:
- none'> </span><span
- style='color:windowtext;display:none;text-decoration:none'>30</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655660">Moving
- Objects Around, Part I<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>30</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655661">Moving
- Objects Around, Part II<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>31</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655662">Moving
- Objects Around, Part III<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>33</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655663">Auto
- Tracking<span style='color:windowtext;display:none;text-decoration:none'>. 35</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655664">Gravity and
- Free Floating Bodies<span style='color:windowtext;display:none;text-decoration:
- none'>. </span><span
- style='color:windowtext;display:none;text-decoration:none'>36</span></a></span></p>
- <p class=Toc1><span class=MsoHyperlink><a href="#_Toc140655665">Where to Go
- from Here<span style='color:windowtext;display:none;text-decoration:none'>. 40</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655666">Managed
- DirectX and XNA<span style='color:windowtext;display:none;text-decoration:none'>.. 40</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655667">IronPython<span
- style='color:windowtext;display:none;text-decoration:none'>. 41</span></a></span></p>
- <p class=Toc2><span class=MsoHyperlink><a href="#_Toc140655668">Extending
- the Framework<span style='color:windowtext;display:none;text-decoration:none'>. 41</span></a></span></p>
- <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <h1><a name="_Toc140655628">Getting Started</a></h1>
- <p class=Normal> </p>
- <h2><a name="_Toc140655629">Introduction</a></h2>
- <p class=Normal>In this tutorial we will be creating a highly flexible
- Python graphical framework as an introduction to IronPython.� By the end of
- this tutorial you will have a better understanding of IronPython, a basic grasp
- of DirectX, and a better understanding of how IronPython utilizes and interacts
- with the .NET framework. </p>
- <h2><a name="_Toc140655630">Prerequisites</a></h2>
- <p class=Normal>This tutorial assumes only a basic knowledge of the Python
- syntax and how to use some of its basic constructs (such as lists and tuples).�
- No prior knowledge of DirectX or Windows.Forms is required, though a basic
- grasp of the .Net platform will go a long way to understanding a few sections
- of code.</p>
- <p class=Normal> </p>
- <p class=Normal>We will be building this application from scratch, and all
- portions of the code will be fully explained.� Note that if you are already
- familiar with Python, you should probably skip the sections labeled �For Python
- Beginners.�</p>
- <p class=Normal> </p>
- <p class="body">
- Further, you will need the following:</p>
- <ul>
- <li class="normal">IronPython 2.6 distribution
- <ul>
- <li>If you are new to IronPython, please go over the tutorial
- that comes with this distribution</li>
- <li>Download from
- <a href="http://ironpython.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=12482">here</a></li>
- </ul>
- </li>
- <li class="normal">DirectX End-user Runtime and a compatible video card<ul>
- <li>Download from
- <a href="http://www.microsoft.com/games/en-US/aboutgfw/Pages/directx10-a.aspx">here</a>.</li>
- </ul>
- </li>
- </ul>
- <p class=Normal> </p><p class=Normal><b><font size="3">Important Note </font></b></p>
- <p class=Normal>If you've previously installed a DirectX SDK there's a good chance you might have the
- Managed DirectX 2.0 Beta assembly installed. This assembly is incompatible with the Python sample code
- distributed with this tutorial and cannot be uninstalled. If you experience difficulties running the
- samples (particularly when there are error messages stating that the <i>Matrix</i> class has no
- <i>LookAtLH</i> method) and are sure you meet the prerequisites above, please replace all occurrences
- of:</p><p class=Normal><b> </b><i>clr.AddReference(�Microsoft.DirectX�)</i></p>
- <p class=Normal>with:</p><p class=Normal><b> </b><i>clr.AddReferenceByName('Microsoft.DirectX, Version=1.0.2902.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')</i></p>
- <p class=Normal> </p><p class=Normal>throughout the samples. This change basically means that the
- sample code will use Managed DirectX 1 instead of the 2.0 beta which would normally have precedence.</p>
- <h2><a name="_Toc140655631">How to Use this Tutorial</a></h2>
- <p class=Normal>This tutorial will start from a small set of code and slowly
- build a full framework from scratch.� Throughout this tutorial you should be
- slowly adding code to a Python source file of your own and watching the results
- as we build it.� There is no substitute for actual programming; I highly
- recommend that you resist the urge to simply read along.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655632">Objectives for this Tutorial</a></h2>
- <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
- them inside of a .NET form. The framework will provide methods for positioning and rotating objects and the camera, as well as examples of using the
- framework to implement more advanced functionality such as having objects move around a track and have cameras auto-track objects in the scene. Lastly, we
- will implement a small scale demo which demonstrates Newton�s law of universal
- gravitation, which will allow you to specify an arbitrary number of free
- floating bodies in space (planets), and watch them interact with each other.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655633">Final Note</a></h2>
- <p class=Normal>Throughout this tutorial be very careful to keep the same
- indentation levels that have been used here.� In Python, white space is a part of
- the language, and you must take care to ensure that everything is correctly
- aligned or you will receive errors.� I suggest setting your text editor of
- choice to use 4 spaces instead of tabs (which I have done for all code
- presented here).</p>
- <p class=Normal> </p>
- <p class=Normal>Lastly, if you are getting compile errors, check to make
- sure you have not inserted extra returns in the middle of statements.� The code
- may be slightly skewed if you have word wrap turned on.</p>
- <p class=Normal> </p>
- <h1><a name="_Toc140655634">Creating the Framework for a DirectX Application</a></h1>
- <p class=Normal> </p>
- <h3><a name="_Toc140655635">Loading Assemblies and Importing Them</a></h3>
- <p class=Normal> </p>
- <p class=Normal>The first thing we will do is add a few references to
- assemblies we will be using.� Create a new file called �tutorial.py� and add
- the following code to it:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>import clr</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>clr.AddReferenceByPartialName("System.Drawing")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>clr.AddReferenceByPartialName("System.Windows.Forms")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>clr.AddReferenceByPartialName("Microsoft.DirectX")</p><p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>clr.AddReferenceByPartialName("Microsoft.DirectX.Direct3D")<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
- </span>clr.AddReferenceByPartialName("Microsoft.DirectX.Direct3DX")</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>If you do not add the reference to assemblies that you use,
- the IronPython interpreter will not know how to load them.� If you run into
- problems where an import statement does not give the expected results, check to
- make sure you have referenced all of the appropriate assemblies first.� Note
- that IronPython automatically adds the �System� library.</p><p class=Normal> </p><p class=Normal>If you encounter
- difficulties running the snippet of code above, please see the <b>Important Notes</b> section in the
- <a href="#_Toc140655630">Prerequisites</a>.</p>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to IronPython?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>The clr import is not just a library; it actually changes the
- dynamics of several basic constructs.� To see this first hand, open up a
- fresh IronPython interpreter and run this code:</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'> </p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>print
- dir([])</span></p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>import
- clr</span></p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'>print
- dir([])</span></p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Notice that it adds a number of functions which makes IronPython
- very similar to .NET (though all of the Python functions are still there).�
- This also replaces the standard Python string with the .Net string class.�
- Throughout this tutorial I will be using the IronPython methods instead of
- the standard Python methods as this produces a more consistent code base.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>Next we will import all of the libraries we will need for
- this tutorial.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>import System</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>from System import Drawing</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>from System.Windows import Forms</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>from Microsoft import DirectX</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>from Microsoft.DirectX import Direct3D</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>In this tutorial I have kept namespaces and never imported
- everything into the global namespace.� This will make the rest of the tutorial
- slightly more verbose, but it will also be very explicit as to which functions
- we are calling.</p>
- <p class=Normal> </p>
- <h3><a name="_Toc140655636">The RenderWindow Class</a></h3>
- <p class=Normal> </p>
- <p class=Normal>The next thing we need to do is to create a new Form to
- house our application.� In IronPython we can natively derive from any .Net
- class which is not sealed or a value type.� We will define a constructor which
- takes in a single argument (a class which contains a �Paused� attribute).�
- Though I am calling this variable sceneManager here, and we will be providing a
- class called SceneManager later, we could easily swap it for any other class
- which understands how to pause and unpause the system:</p>
- <p class=Normal><span style='font-size:10.0pt;font-family:"Courier New"'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class RenderWindow(Forms.Form):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, sceneManager):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.SceneManager = sceneManager</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Text = "IronPython Direct3D"</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.ClientSize = Drawing.Size(640, 480)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Here we have set our own instance variable SceneManager to
- be the sceneManager variable that was passed in.� We have also set the �Text�
- and �ClientSize� properties, which are properties defined by the Forms.Form
- class.</p>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>When defining a class�s method in Python, you always add a
- parameter for the object you are currently operating on.� In languages such
- as C# or C++ this parameter is implicitly passed as the �this� parameter.�
- Note that we cannot access a class�s member methods or data without using
- this self parameter.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>Next, we want our window to close (and the application to
- exit) whenever the escape key is pressed.� We will override the OnKeyDown
- method of Forms.Form to do this.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnKeyDown(self, args):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if args.KeyCode ==
- System.Windows.Forms.Keys.Escape:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Dispose()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Finally, we want the application to pause and stop rendering
- any time the application is minimized or not visible.� For now we will set the
- Paused variable on the SceneManager variable we have created.� We will actually
- implement the code for this later.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnResize(self, args):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.SceneManager.Paused = not self.Visible or
- \</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� (self.WindowState ==
- Forms.FormWindowState.Minimized)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Next we are going to add the shell of the SceneManager
- class.� The first three methods are very simple (and we will revisit their
- implementations later).� The first is the constructor, the second is a method
- which sets up DirectX, and the third is the Render method, which is called
- from within our render loop.� We will cover these in detail later.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class SceneManager(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pass</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def InitGraphics(self, handle):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Render(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pass</p>
- </div>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Whenever we define a method or class which has no body, we
- always must use the pass keyword.� This keyword has no meaning outside of
- saying �empty code block.�</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'> </p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Note that we have returned True from InitGraphics.� One
- important thing to note about Python is that any time you do not return a
- value, you are implicitly returning the None type (which is roughly
- equivalent to null, with a few exceptions).� This is why you will see some
- code paths in Python which have return statements and some which do not.�
- This is perfectly valid, and generally accepted as normal.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>Next we need to create the render loop for the application.�
- This loop will pump the Windows.Forms event queue and call the Render method:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Go(self, window):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� while window.Created:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Render()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� Forms.Application.DoEvents()</p>
- </div>
- <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'> </span></p>
- <p class=Normal>Finally, we will create the main function which creates the
- basic objects and starts the render loop.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>def main():</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� sceneManager = SceneManager()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� window = RenderWindow(sceneManager)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� if not sceneManager.InitGraphics(window.Handle):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� Forms.MessageBox.Show("Could not init
- Direct3D.")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� window.Show()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sceneManager.Go(window)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>if __name__ == '__main__':</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� main()</p>
- </div>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>In a Python script it is ok to have code which runs at the file
- level (that is, not inside of a function, not inside of a class).� This code
- is run every time the code is imported.� If you want to have the code run
- only when the module is explicitly ran (not imported) we use the built in
- __name__ variable to determine if we have been explicitly invoked from the
- command line.� It will always equal �__main__� if we have been run from the
- command line.� If the module was loaded using the �import� statement,
- __name__ would be the name of the file otherwise.</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'> </p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>We have explicitly created a main function so that if this code
- is imported into an IronPython console, the main function can be invoked
- explicitly.� If we replaced the call to main with the contents of the main
- function, there would be no way to run the code outside of actually running
- it from the command line (and not importing it).� This is sometimes
- desirable, but not in our application.� We will be using this much later in
- the tutorial when we actually import our framework from a separate file.</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'> </p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Notice also that creating a class does not use the �new� keyword
- like C# and C++ do.� The syntax for creating a new class is the exact same
- as calling a function.� This is by design.� In many cases we can use function
- calls and class constructors interchangeably when we are passing functions as
- parameters.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655637">Code Checkpoint 1</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Run the code from within IronPython.� You should see an empty
- Windows.Forms window.� If you are having problems with the code, you should
- compare your source file with checkpoint1.py.</p>
- <p class=Normal> </p>
- <h1><a name="_Toc140655638">Your First DirectX Application (in IronPython)</a></h1>
- <p class=Normal> </p>
- <p class=Normal>In this section we will create a basic scene in DirectX.� By
- the end of this section, we will have created a DirectX context, rendered a
- mesh to the scene, and explored the Python concept of Duck Typing along the
- way.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655639">Setting up DirectX</a></h2>
- <p class=Normal>This section will deal exclusively with the SceneManager
- class. �The first thing we need to do is initialize a few variables which we
- will be using in this code.� Be sure to always remove the �pass� statement when
- we add code to empty methods.</p>
- <p class=Normal> </p>
- <p class=Normal>We will add three variables:� The first will contain our
- DirectX device when we have created it.� The second will determine if the
- DirectX context is paused (in case we are minimized or hidden).� The third will
- be the background color for the DirectX rendering device.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class SceneManager(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Device = None</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Paused = False</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����� ��self.Background = System.Drawing.Color.Black</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Next we will fill in the InitGraphics method.� This
- section of code creates the DirectX device we will use in this tutorial.</p>
- <p class=Normal> </p>
- <p class=Normal>The first thing we will do is set up the parameters that we
- will pass to DirectX.� We will set DirectX to be in windowed mode, set the swap
- effect for drawing frames, and enable the auto depth stencil (so that DirectX
- tracks the depth of our objects for us).� For more information on the specifics
- on these properties and parameters, consult the DirectX documentation.� Since
- creating a DirectX device requires a window handle to place it in, we will take
- in a �handle� parameter to use this.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def InitGraphics(self, handle):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> params = Direct3D.PresentParameters()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>� ������params.Windowed = True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������params.SwapEffect =
- Direct3D.SwapEffect.Discard</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������params.EnableAutoDepthStencil = True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������params.AutoDepthStencilFormat =
- Direct3D.DepthFormat.D16</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to actually create the Direct3D device.� Note
- that we are passing in the handle parameter to the constructor of the Direct3D
- device.� This is what tells DirectX to use the window that we have created.� We
- also pass in the PresentParameters object we just created. ��For more specifics
- on the Device creation parameters, consult the managed DirectX documentation. </p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Device = Direct3D.Device(0,
- Direct3D.DeviceType.Hardware, handle,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>�������������������������������������
- Direct3D.CreateFlags.SoftwareVertexProcessing,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>���� ���������������������������������params)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>After creating the device, we need to set a couple of
- properties on the Device.� The first thing we need to do is enable the
- ZBuffer.� If we did not do this, we could not draw in 3 dimensions (which is
- basically the point of this tutorial).� We also need to set the ambient light
- to be a full white.� There are multiple types of lighting which DirectX uses:
- diffuse, ambient, specular and emissive.� A full treatise on lighting would be
- far beyond the scope of this document.� For this tutorial we are going to
- simply set the ambient light to full (white), and force every mesh we load to
- use ambient lighting.� This, effectively, turns off lighting so we do not have
- to worry about it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Device.RenderState.ZBufferEnable =
- True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Device.RenderState.Ambient =
- Drawing.Color.White</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Next we need to set up two of the three matricies which
- DirectX uses to render the scene.� The Projection matrix is the most
- complicated.� It basically transforms the 3D matrix of objects into a 2
- dimensional plane to display on the screen.� We will not have to deal with this
- matrix after we have set it the first time.</p>
- <p class=Normal> </p>
- <p class=Normal>The second matrix DirectX uses is the View matrix which
- defines where the camera is looking.� DirectX has some very nice methods which
- allow us to deal with cameras in a very natural way.� The
- Matrix.LookAtLH takes in three parameters.� The first is the position
- of the camera.� The second is what the camera looks at.� The third is the �up�
- vector, which is the up direction for the camera.� Setting the up vector
- incorrectly would result in the scene being rotated in strange ways.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Device.Transform.Projection =
- DirectX.Matrix.PerspectiveFovLH(System.Math.PI/4.0, 1, 1, 100)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Device.Transform.View =
- DirectX.Matrix.LookAtLH(DirectX.Vector3(<span style='font-size:10.0pt'>0,
- 3, -5</span>), DirectX.Vector3(0, 0, 0), DirectX.Vector3(0, 1, 0))</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The rest of our code simply sets the Paused property to be
- false.� We will return true to indicate no error occurred.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������# ensure we are not paused</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Paused = False</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The last thing we need to do is render the scene.� For now
- this code is very basic, but we will be adding to it later.� First, we only
- render if the Device has already been created and we are not paused.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 0in 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Render(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.Device is None or self.Paused:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� return</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>At the beginning of each render, we clear the scene with a
- specific background color (which we set in the constructor).</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> self.Device.Clear(Direct3D.ClearFlags.Target | Direct3D.ClearFlags.ZBuffer,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Background, </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> 1, </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> 0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Device.BeginScene()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� # scene render here</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Device.EndScene()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Device.Present()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Run the application.� We have now created a DirectX render
- loop in under 100 lines of IronPython code.� Now we need to make something
- useful out of it�</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655640">Code Checkpoint 2</a></h2>
- <p class=Normal>Run your Python application.� At this point you should have
- the same window as the previous checkpoint, but with a black DirectX context.�
- If you are having problems with the code, you should compare your source file
- with checkpoint2.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655641">An Introduction to Duck Typing</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Now that we have an application running, what we need to do is
- load a mesh and render it in the scene.� To do this, DirectX needs four pieces
- of information: the Mesh, the World Matrix (to position the object), a list of
- materials, and a list of textures.� We will provide one extra constraint that
- we will use: every object we render in the scene will need a Name so that we
- can place it into a dictionary for easy access.</p>
- <p class=Normal> </p>
- <p class=Normal>A common approach to solving this problem in strongly typed
- languages such as C++ and C# is to define an interface which classes must
- implement to be used by the SceneManager.� This is a good approach for these
- languages, but it can be extraordinarily limiting when using a more flexible
- language such as IronPython.� The concept of an interface still exists in
- IronPython, but instead of being an explicit interface, it is a more
- contractual negotiation between objects which interact with each other.</p>
- <p class=Normal> </p>
- <p class=Normal>In this case, we need to implement an interface for our
- �Renderable� objects.� The na�ve approach (for python programming) would be to
- define an explicit class to handle this (this code should not be added to the
- project):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class IRenderable:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Name = ""</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Mesh = None</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Textures = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Materials = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetWorldMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pass</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>def function(renderable):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� if not isinstance(renderable, IRenderable):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� raise TypeError</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� # use the instance</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>This is certainly a valid solution, but completely
- unnecessary.� Instead, the Python way is to simply define the required
- methods and attributes and only use inheritance when code is actually being
- shared between classes.� This can lead to many interesting uses of a class
- which were not anticipated by the programmer who wrote the original code.� The
- fact that we have not constrained the �Name� attribute to be a string type
- means we can use any hashable object (such as integers, strings, the None type,
- classes, instances of classes�) to access scene objects.� This style of
- programming also has the obvious benefit of less coding overhead.� We do not
- have to define the interface in the code, nor do we have to explicitly declare it,
- so long as we implement all of the correct functionality in an object.</p>
- <p class=Normal> </p>
- <p class=Normal>A good example of this would be the RenderWindow class.� We
- pass the scene manager into the RenderWindow class so that the window can pause
- the scene manager when it is not being shown.� From the RenderWindow�s
- perspective, it doesn�t care what object it is given so long as that object
- understands what is meant when the programmer sets the Paused variable.� This
- can be seen as an implicit interface, containing only the Paused property as
- the member of the interface.� As briefly mentioned before, we could easily swap
- the object we pass to RenderWindow with a different one if we so choose (so
- long as the new object understood what to do with that Paused property),
- without ever having to change the RenderWindow class.</p>
- <p class=Normal> </p>
- <p class=Normal>This is what is meant when Python programmers refer to �Duck
- Typing.�� The idea is �if it walks like a duck, quacks like a duck, it might as
- well be a duck.�� Or to use a less obtuse metaphor, if a class implements
- everything that is needed to perform a task, do we really care what class it <b>actually</b>
- is, or what interfaces it claims to implement?</p>
- <p class=Normal> </p>
- <p class=Normal>Along with the general flexibility of the syntax and
- constructs of the language, Duck Typing is the primary reason that Python as a
- whole is so lightweight.� A change to a method in one class only has to be
- reflected in the other classes which rely on this method.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655642">Loading Meshes</a></h2>
- <p class=Normal><br>
- With this in mind, we will now set out to implement our first iteration at
- rendering meshes.� The first thing we will do is create a new class which loads
- a mesh and implements the interface we have outlined.� The constructor for our
- class will take in three items.� The first is the DirectX device to use to load
- it, the second is the name of the object, and the last is the .x file we are
- loading.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class MeshRenderable(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, device, name, file):<span
- style='font-size:10.0pt'> </span></p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'><span style='font-size:10.0pt'>������� self.Name =
- name</span></p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to load the actual mesh itself.� The major
- parameters we are passing are the Direct3D device, the filename to load the
- mesh from, and an empty list of materials which DirectX will populate .� Please
- consult the managed DirectX documentation for more information on the other
- parameters, if you are curious.</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> materials = clr.Reference[System.Array[Direct3D.ExtendedMaterial]](())<span
- style="background-color: #C0C0C0; background-position: 0% 0%"><br>
- </span>self.Mesh = Direct3D.Mesh.FromFile(file,<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
-
- </span>Direct3D.MeshFlags.SystemMemory,<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
-
- </span>device, materials)<span style="background-color: #C0C0C0; background-position: 0% 0%"><br>
- </span>materials = materials.Value</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that we have loaded the mesh into self.Mesh and loaded a
- list of materials into the �materials� variable, we will build a list of
- preloaded Materials and Textures.� As mentioned previously, we are going to use
- only the ambient lighting for this tutorial and we implement this by setting
- the ambient color to be the diffuse color on the mesh.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Materials = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Textures = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for i in range(materials.Length):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� # load material, set color</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� material = materials[i].Material3D</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� material.AmbientColor =
- material.DiffuseColor</p>
- </div>
- <p class=Normal>���� </p>
- <p class=Normal> </p>
- <p class=Normal>The material itself only provides us with the texture�s file
- name� We actually have to load the texture ourselves.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� texture = None</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� texFile = materials[i].TextureFilename</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if texFile is not None and texFile.Length:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� texture = Direct3D.TextureLoader.FromFile(device, texFile)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Next we add the material and the texture to their respective
- lists.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Materials.append(material)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Textures.append(texture)</p>
- </div>
- <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'> </span></p>
- <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'> </span></p>
- <p class=Normal>Lastly we need to implement the <span style='font-size:10.0pt;
- font-family:Consolas'>GetWorldMatrix</span> method.� For now, we will simply
- return the identity matrix, because we have not implemented any positioning
- code.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetWorldMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return DirectX.Matrix.Identity</p>
- </div>
- <p class=Normal> </p>
- <h2><a name="_Toc140655643">Rendering Meshes</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Now that we have code for loading a mesh, we need to add
- some code to actually render it.� Along the way, we will want to collect the
- objects into a common place, and the SceneManager class seems like an obvious
- place to do this.</p>
- <p class=Normal> </p>
- <p class=Normal>First, add a new dictionary to the SceneManager class called
- �Objects.�</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class SceneManager(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Device = None</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Paused = False</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Background = System.Drawing.Color.Black</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Objects = {}</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>To abstract away some of the details of loading a mesh we
- will provide a mechanism for loading textures.� This method will take in the
- name of the object and the filename to load, store the object in the Objects
- dictionary, and return the Mesh (in case the caller actually needed to
- manipulate the object after creating it).</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def LoadMesh(self, name, filename):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� mesh = MeshRenderable(self.Device, name,
- filename)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Objects[mesh.Name] = mesh</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������return mesh</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to actually render the Objects.� Find the
- comment in the Render method which says �scene render here.�� We will implement
- this by walking through all of the values in the Objects dictionary.� For each
- object that has the �GetWorldMatrix� attribute, we will assume that it is a
- mesh and render it as such.</p>
- <p class=Normal> </p>
- <p class=Normal>To accomplish this we will use Python�s generator
- expressions to iterate through the Objects.� To determine which objects have
- the �GetWorldMatrix� method we will use Python�s hasattr function.� The hasattr
- function returns true if the first parameter passed into it contains an
- attribute which is named the second parameter.� In Python, using reflection is
- as natural as breathing, and learning to use it effectively is the key to
- writing clean and concise code.</p>
- <p class=Normal> </p>
- <p class=Normal>Between the self.Device.BeginScene() and
- self.Device.EndScene() calls, add the following code:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for mesh in (x for x in self.Objects.Values if
- hasattr(x, "GetWorldMatrix")):</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>There is one catch, we have only determined that the
- GetWorldMatrix exists, not if it is a callable method.� Python implements a
- handy method for determining if an attribute is callable:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if callable(mesh.GetWorldMatrix):</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note we could have added the callable if statement to the
- end of the generator expression by adding the �and� boolean operator.� As an exercise,
- make this change now.</p>
- <p class=Normal> </p>
- <p class=Normal>The process for rendering a mesh is to specify the world
- matrix, then for each subset of a mesh, set the material and texture then
- render the subset.� We can actually share mesh data between multiple objects by
- simply using the same Direct3D.Mesh objects and setting the world matrix for
- each render.� For now, we will simply render the mesh without this optimization.
- </p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� self.Device.Transform.World =
- mesh.GetWorldMatrix()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� materials = mesh.Materials</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� textures = mesh.Textures</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� for i in range(len(materials)):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� self.Device.Material =
- materials[i]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� if i < len(textures):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������������������� self.Device.SetTexture(0,
- textures[i])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������������������� self.Device.SetTexture(0,
- None)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� mesh.Mesh.DrawSubset(i)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note here that we have only set the texture if there is
- actually a texture for this particular material.� There must be a material for
- the subset we are drawing, but there does not have to be a texture for that
- particular material.</p>
- <h2><a name="_Toc140655644">Code Checkpoint 3</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Save your work and run the file.� For all of our new
- changes, we don�t actually have anything new to show for it yet.� Find the main
- function and add a call to the LoadMesh method as follows:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� if not sceneManager.InitGraphics(window.Handle):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� Forms.MessageBox.Show("Could not init
- Direct3D.")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sceneManager.LoadMesh("tiger",
- "tiger.x")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� window.Show()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sceneManager.Go(window)</p>
- </div>
- <p class=Normal><br>
- Now run the application.� You should see a tiger facing the camera.� The next
- thing we will do is implement a flexible event system to move the tiger.�
- Remove the call to LoadMesh from the main function, as that was only to
- demonstrate that we are indeed loading the mesh.</p>
- <h1><a name="_Toc140655645">Cameras, Moving Objects, and Events</a></h1>
- <p class=Normal> </p>
- <p class=Normal>In this section of the tutorial we will be exploring an event
- system which allows us to manipulate the scene by responding to events such as
- the scene being created or a frame being rendered.� We will also add
- positioning code and rotational code, so you can arbitrarily move and rotate
- objects.� Lastly, we will implement a camera system which allows us to set
- cameras throughout our scene and switch between them at will.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655646">The Event System</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Though our event system will be able to handle any arbitrary
- events we create later, we will start with three basic events: OnSceneCreate, OnSceneBegin, and OnFrame.� The OnFrame method will be called
- just before rendering a frame.� This will allow us to reposition objects (or do
- other updates) which require action every frame.� The OnSceneCreate method will
- be called before the scene starts rendering, and it is used for creating and
- adding objects (such as meshes and cameras) to the scene.� The OnSceneBegin
- event will be fired right after the OnSceneCreate event has finished.� The
- OnSceneBegin method is used to move and position objects which have not been
- created by that event handler.� This is an important distinction since we are
- not guaranteeing anything about the order in which event handlers of the same
- type are called.� By offering these two events separately, we can allow a
- single listener to create an object that another object uses without worrying
- about the order in which events are passed.</p>
- <p class=Normal> </p>
- <p class=Normal>The OnSceneCreate and OnSceneBegin events will accept a
- single parameter (the scene manager who is calling them), and the OnFrame events
- will accept a parameter for the number of seconds since the last frame was
- rendered.� Lastly, events return values indicating whether or not they have completed their
- operations.� By returning True an
- object signifies that it should no longer receive any events.</p>
- <p class=Normal> </p>
- <p class=Normal>To implement this, we will start by creating a dictionary
- which holds the event name as a key, and a list of event handlers as the
- value.� Find the SceneManager constructor and add this code:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Listeners = {"OnFrame" : [],</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������������� "OnSceneBegin" :
- [],</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������������� "OnSceneCreate" :
- []}</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>We will add two methods for adding listeners to the class.�
- The first is the AddListenerByName method, which takes in an event listener and
- a name of the event.� The implementation is very straight-forward (raising a
- TypeError if we are given an event listener which is not callable):</p>
- <p class=Normal> </p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'>��� </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def AddListenerByName(self, key, function):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if not callable(function):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� raise TypeError, "Object not
- callable."</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Listeners[key].Add(function)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>This is good to have, but this would quickly become tedious
- to use if we had a large number of objects, each registering for multiple
- events.� What we would like to do is to inspect an object, and register their
- event handlers based on the function names.</p>
- <p class=Normal> </p>
- <p class=Normal>This is extraordinarily easy to do in Python.� We will walk
- through each of the keys in the Listeners dictionary, and if the object has a
- function by that name, we will register it as an event listener:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def AddListener(self, obj):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for key in self.Listeners.Keys:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if hasattr(obj, str(key)):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� function = GetAttr(obj, str(key))</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� self.AddListenerByName(key, function)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Only four lines of actual code to walk the object and pull
- out the functions we are looking for.</p>
- <p class=Normal> </p>
- <p class=Normal>Now we need to add a method for actually firing the
- events.� As I mentioned earlier, each event will return whether or not it has
- completed its operations.� In practice this means that if we are complete we
- return True, if we are not complete (or if the listener never completes�if it
- should be registered for the entire lifetime of the SceneManager), then we can
- either return False, or simply not return anything at all.� Since all methods
- implicitly return the None type, and since None always evaluates to false in a
- Boolean expression, this allows us to implicitly return false when we do not
- return at all.� While difficult to explain the reasoning for this concisely, it
- will make our listener code easier to write.</p>
- <p class=Normal> </p>
- <p class=Normal>To implement a method which fires events, then removes
- objects which return True, we will call every function in the appropriate
- listener list, keeping track of each function which returned true.</p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __FireEvent(self, event, arg):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� listeners = self.Listeners[event]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� toRemove = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for f in listeners:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if f(arg):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� toRemove.Add(f) </p>
- </div>
- <p class=Normal>������� </p>
- <p class=Normal>Now we walk the toRemove list and remove them.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for f in toRemove:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� listeners.Remove(f)</p>
- </div>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Whenever you prefix a method or variable with two underscores,
- that method or variable is �hidden.�� This is roughly equivalent to declaring
- it as �private� in a C++ or C#.� The major difference is the variable or
- method is still actually there, it�s just difficult to modify unless you
- understand the internals of how Python hides it.� This is implemented such
- that it is possible to have a �friend� class as you do in other languages
- (allowing you to access an object�s private data), but at the same time
- letting you know that you are doing something you really should try not to.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>Lastly, we need to actually call the __FireEvent method to
- send the events.� To do this we will modify SceneManager�s Go method, so clear
- out the contents of it.� We�ll start by firing the scene creation and scene
- begin events:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Go(self, window):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__FireEvent("OnSceneCreate",
- self)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__FireEvent("OnSceneBegin",
- self)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Next we need to add code for firing the OnFrame event.� The
- trick is we have to keep track of how long it has been since we last fired a
- frame event.� We�ll use the System.Environment.TickCount (measured in
- milliseconds) to do this.</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� lastTick = System.Environment.TickCount</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����� ��while window.Created:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� currTick = System.Environment.TickCount</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.__FireEvent("OnFrame",
- (currTick-lastTick)/1000.0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� lastTick = currTick</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Render()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� Forms.Application.DoEvents()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note that the extra �.0� in the __FireEvent call is very
- important.� Both currTick and lastTick are integer values and currTick-lastTick
- is certainly less than 1000.� If we did not divide it by a floating point
- number we would always be passing 0 (the integer division value).</p>
- <p class=Normal> </p>
- <p class=Normal>We will now create a very simple event listener which adds
- our tiger mesh to the scene.� We will be expanding this event listener in later
- sections.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class SceneCreator(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneCreate(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Tiger = sm.LoadMesh("Tiger",
- "tiger.x")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now, we need to register this listener.� In general, we
- could have many listeners in our application.� What we want to do now is to
- make the main function take in an optional parameter which defines a sequence
- of objects to register as listeners.� Find the main function and change its
- function header:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>def main(listeners = [SceneCreator]):</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>This creates an optional parameter �listeners� which, by
- default, contains our SceneCreator class.</p>
- <p class=Normal> </p>
- <p class=Normal>As you see, we have passed in a class instead of an instance
- of the class.� What we want is for our function to iterate over the sequence we
- are given, adding instances of the objects to the scene manager.� If an entry
- in the sequence is a class, we want to create an instance of it and add it.� Find
- the �else� clause in the main function, and add the following code:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for obj in listeners:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if callable(obj):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� obj = obj()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� sceneManager.AddListener(obj)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� window.Show()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sceneManager.Go(window)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note that we have assumed here that a callable object is a
- constructor (or, at least, returns an instance of a listener object).� This
- works �well enough� in most cases.� The biggest problem we can run into is the
- fact that objects in Python can implement the __call__ method, which makes an
- object instance act like a function.� If we added a class like this to the
- parameter list in main, we could run into trouble.� There are ways around this,
- but I have left it out of this tutorial as this is a bit outside what we are
- exploring in Python.� Feel free to look up Python�s documentation for more
- information related to classes.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655647">Code Checkpoint 4</a></h2>
- <p class=Normal>Save your work and run the file.� You should now see a tiger
- standing in front of the camera.� If you are having trouble at this point,
- compare your code to checkpoint4.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655648">Exercises</a></h2>
- <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. If you
- are new to Python, I suggest you implement these methods as an exercise. This is not required to move onto the next section, as we will not be making use
- of any of these.</p>
- <p class=Normal> </p>
- <p class=Normal>First, implement a RemoveListenerByName method which takes
- in a name and a listener and unregisters that listener from the specified
- event.</p>
- <p class=Normal> </p>
- <p class=Normal>Implement a RemoveListener method which unregisters a
- listener from all events.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655649">Moving Objects on Screen</a></h2>
- <p class=Normal> </p>
- <p class=Normal>The next thing we will implement is moving and rotating objects
- in the scene.� To do this we will create two classes which will be subclassed
- by other scene objects to share the functionality.</p>
- <p class=Normal> </p>
- <p class=Normal>The first thing we will implement is a PositionableObject
- class.� This class will provide a Position property (containing a
- DirectX.Vector3 object), and a Translate method.� The Position property will
- always contain a Vector3 instance that we can always directly pass into
- a DirectX function without having to convert it.� To do this, we will add a
- function at the top level of the file which takes in a parameter and if the
- parameter is of sequence type, we�ll convert it to a Vector3.� Be sure this
- function is added near the beginning of the file.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>def Vectorize(v):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� if type(v)==list:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� v = DirectX.Vector3(v[0], v[1], v[2])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� return v</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Now we will implement the PositionableObject class using a
- Python property object for the Position variable.� To define a property in
- Python, we first define a get method, a set method, a delete method, and a
- hidden variable to contain the value.� (Note we will always call
- the Vectorize function before actually assigning to the __Position variable.)</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class PositionableObject(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = DirectX.Vector3(0, 0, 0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetPosition(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetPosition(self, pos): </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = Vectorize(pos)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __DelPosition(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = DirectX.Vector3(0, 0, 0)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>To create the actual property itself, we call the property
- function:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Position = property(__GetPosition, __SetPosition,
- __DelPosition)</p>
- </div>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>The property function takes in 4 parameters.� The first is the
- get method, the second is the set method, the third is the delete
- method, and the fourth is a help string.� Only the first parameter (the get
- method) is required.� If we do not specify a delete method, this will
- create a property which throws an AttributeError if the user tries to delete
- it.� If we do not supply a set property then the variable will be read-only.�
- The help string is also completely optional.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>Next, we will implement the Translate method.� This is also
- straight forward, converting parameter then translating it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Translate(self, amount):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� amount = Vectorize(amount)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position += amount</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>At this point you may wonder, �Why do we bother with a Translate
- method?�� The problem we are faced with is that we are trying to support a
- �native� Python solution to this problem�that is, we would like our movable
- objects to work with lists in addition to DirectX.Vector3 objects.� This
- works fine until we try to use them in conjunction with the Vector3 class
- (which has no idea how to deal with a python list).� For example, this code
- works fine (do not add this to the project):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>b = DirectX.Vector3(3, 2, 1)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>c = a + b</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>This, however, will fail (do not add this to the project):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>b = [3, 2, 1]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>c = a + b</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>DirectX.Vector3 objects simply have no idea how to deal with
- lists (or any other object than Vector3s, for that matter).� A common solution
- to this problem is to subclass the object and redefine its math operations such
- that it would convert from a list to a Vector3 automatically.� This is unfortunately
- not possible since .Net does not support subclassing sealed or value types (and
- DirectX.Vector3 is a value type).</p>
- <p class=Normal> </p>
- <p class=Normal>Our only option at this point is to add a Translate method
- which knows how to convert between sequence types and Vector3 objects, and add
- the result.� For example (do not add this to the project):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'># this will work</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>somePositionableObject.Position += DirectX.Vector3(1,
- 2, 3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'># this will not work</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>somePositionableObject.Position += 1, 2, 3</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'># this will work, which is why we created it</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>a = 1, 2, 3</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>somePositionableObject.Translate(a)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'># this works too </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>a = DirectX.Vector3(1, 2, 3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>somePositionableObject.Translate(a)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The last thing we need to add is a read-only property called
- PositionMatrix, which (predictably) will return a matrix based on the position.�
- We need this because all of DirectX�s positioning code relies on matrices and
- 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
- use this to create the PositionMatrix property:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetPositionMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return DirectX.Matrix.Translation(self.Position)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� PositionMatrix = property(__GetPositionMatrix)</p>
- </div>
- <p class=Normal> </p>
- <h2><a name="_Toc140655650">Rotating Objects on the Screen</a></h2>
- <p class=Normal> </p>
- <p class=Normal>The next thing we will do is add a class which handles rotating
- objects.� The RotatableObject will act quite differently from PositionableObject
- in the sense that we do not have a single property we are interacting with.�
- Instead we are going to implement four simple methods to perform rotations.�
- The Pitch method will rotate the object around the x-axis, the Yaw method
- will rotate the object around the y-axis, the Roll method will rotate the
- object around the z-axis, and the ResetOrientation method will revert all
- orientation changes to the object.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class RotatableObject(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.RotationMatrix = DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def ResetOrientation(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.RotationMatrix = DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Pitch(self, p):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.RotationMatrix *=
- DirectX.Matrix.RotationX(p)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Yaw(self, y):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.RotationMatrix *=
- DirectX.Matrix.RotationY(y)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Roll(self, r):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.RotationMatrix *=
- DirectX.Matrix.RotationZ(r)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>That is all that is required for adding object rotation.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655651">Putting it All Together</a></h2>
- <p class=Normal> </p>
- <p class=Normal>The final thing we need to do before actually playing with
- our new code is have our MeshRenderable class actually implement these two base
- classes.� Modify the MeshRenderable class to inherit from the
- PositionableObject and RotatableObjects, making sure to call the base
- constructors:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class MeshRenderable(PositionableObject,
- RotatableObject):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, device, name, file):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� PositionableObject.__init__(self)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� RotatableObject.__init__(self)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Next, change the GetWorldMatrix method to return the
- rotation matrix multiplied by the position matrix (remember that matrix
- multiplication is not commutative, and it matters greatly the order in which
- you multiply these two matrices).</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetWorldMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.RotationMatrix *
- self.PositionMatrix</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we can finally put this to use.� We have already loaded
- a tiger mesh in our SceneCreator class.� Now we will create a new class to
- position and animate the tiger.� Have the tiger rotate around the y-axis.� To
- do this, we�ll add a frame event which calls the Yaw method, passing in the
- elapsed time as the amount to rotate it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class TigerAnimator(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneBegin(self, sceneManager):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Tiger =
- sceneManager.Objects["Tiger"]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Tiger.Yaw(elapsed)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Finally, we need to add this listener to the SceneManager
- class.� Since objects which affect a scene are parts of the scene too, it seems
- natural to have the SceneCreator class actually instantiate this object.</p>
- <p class=Normal> </p>
- <p class=Normal>Update SceneCreator�s OnSceneCreate method to add this
- listener:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneCreate(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Tiger = sm.LoadMesh("Tiger",
- "tiger.x")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(TigerAnimator())</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <h2><a name="_Toc140655652">Cameras</a></h2>
- <p class=Normal> </p>
- <p class=Normal>The last major scene object we will be adding to our framework
- is a Camera object.� We will introduce a new variable in the SceneManager class
- called ActiveCamera to store the camera actually being used to view the
- scene.� We will store inactive cameras in SceneManager�s �Objects� dictionary
- so that they may be accessed by any object which has a reference to the scene
- manager.</p>
- <p class=Normal> </p>
- <p class=Normal>Our camera class will be a bit different from our object
- positioning and rotation code.� Instead of rotating the camera around various
- axes to face the objects we wish to look at, we will instead implement a camera
- class which always �looks� at a given position.� Nine times out of ten, this is
- what we wanted to do with a camera anyway (have it look at an object).� In a
- full framework we would need to add a camera which did a bit more than look at
- a given position, but this will work for now.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class Camera(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, name):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Name = name</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__LookAtVector = DirectX.Vector3(0, 0,
- -1)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = DirectX.Vector3(0, 0, 0)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The next bit of code calculates a matrix based on the
- position of the camera and the position we want the camera to look at.� It
- creates two properties (one for Position, one for LookAtVector), and updates
- the matrix every time either of these are changed.</p>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __UpdateMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Matrix =
- DirectX.Matrix.LookAtLH(self.__Position, self.__LookAtVector,
- DirectX.Vector3(0, 1, 0))</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetLookAtVector(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__LookAtVector</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetLookAtVector(self, v):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� ����v = Vectorize(v)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__LookAtVector = v</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__UpdateMatrix()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetPosition(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetPosition(self, pos):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pos = Vectorize(pos)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = pos</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__UpdateMatrix()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Position = property(__GetPosition, __SetPosition)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� LookAtVector = property(__GetLookAtVector,
- __SetLookAtVector)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def LookAt(self, loc):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.LookAtVector = loc</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Finally, we will return the matrix using the GetViewMatrix method.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetViewMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.Matrix</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to update the SceneManager class to use the new
- camera class.� Find the SceneManager class�s constructor and add an
- ActiveCamera variable:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.ActiveCamera = None</p>
- </div>
- <p class=Normal> </p>
- <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
- Objects dictionary, returning the result.� We will have an added constraint
- that if no camera has been set to be active, we will set the newly created
- camera to be the active camera.� This lets us ignore setting
- the ActiveCamera variable if the application only uses one camera.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def CreateCamera(self, name):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam = Camera(name)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Objects[cam.Name] = cam</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.ActiveCamera is None:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.ActiveCamera = cam</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return cam</p>
- </div>
- <p class=Normal> </p>
- <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
- camera.� Add the following code between the return statement at the beginning
- and before the call to self.Device.Clear in SceneManager�s Render class:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.ActiveCamera is not None:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Device.Transform.View = self.ActiveCamera.GetViewMatrix()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Finally, we need to add code to create a default camera and
- have it look at the Tiger object.� Add the following code to SceneCreator�s OnSceneCreate method:</p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam = sm.CreateCamera("Player Cam")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.Position = [0, 3, -5]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.LookAt(self.Tiger.Position)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>That�s it.� If you run your application now, you will notice
- that really nothing has changed.� This is because I cheated a little in the
- beginning and set up the view matrix to be in the exact same position and look
- at the tiger anyway.� If you wish to explore the camera a bit, try moving it
- (or moving it around each frame).</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655653">Code Checkpoint 5</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Run your application.� You should see a tiger onscreen
- rotating.� If this is not the case, compare your code with checkpoint5.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655654">Exercises</a></h2>
- <p class=Normal> </p>
- <p class=Normal>We have introduced a lot of functionality in this section of
- the tutorial, and not made use of any of it.� I would recommend that you take
- some time to play with the framework we have created.</p>
- <p class=Normal> </p>
- <p class=Normal>Try adding more tiger meshes to the scene (remember to give
- them unique names when you call LoadMesh).� Position them in different places
- around the scene.� Add new OnFrame code to rotate them at different speeds, in
- different directions, and in multiple different axes.</p>
- <p class=Normal> </p>
- <p class=Normal>Try moving an object around by setting its position
- incrementally every frame.� Define movement speed to be a variable which can be
- changed.� Move this change into a generic class of its own which takes in a
- name of the object and the speed as a parameter to the constructor.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655655">Framework Optimization</a></h2>
- <p class=Normal>At this point I would like to point out that there are
- several places in the code which can be optimized, but I did not want to muddle
- the tutorial with too much extra code which are not strictly necessary to
- understand what is going on.� We will now eliminate a large number of the
- calculations done every frame.</p>
- <p class=Normal> </p>
- <p class=Normal>The GetViewMatrix classes and GetWorldMatrix classes
- multiply the rotation and position matrices every time they are called.� It
- would be better if this calculation were only done when either the
- PositionMatrix or the RotationMatrix variables are actually updated, and the
- result of this multiplication be cached in a local variable.</p>
- <p class=Normal> </p>
- <p class=Normal>One simple solution to this problem would be to implement a
- lightweight event system which notifies a listener whenever these matrices
- change.� This is a valid solution, but the PositionableObject and
- RotatableObject classes were designed to be lightweight and completely
- orthogonal to any other component in the system.� In other words, a perfect
- mixin we can add to any class.</p>
- <p class=Normal> </p>
- <p class=Normal>We can actually do much better than an event system thanks
- to the elegance Python affords us.� Notice that PositionMatrix and
- RotationMatrix are simply variables.� If we created a PositionMatrix and
- RotationMatrix property in the child class, we could intercept the variable
- assignment and perform the calculations then.� The first thing we need to
- change is to modify the PositionableObject class so that it no longer uses a
- property for PositionMatrix, but a variable instead. �Every time we update the
- position we will rebuild the matrix.� Here is the PositionableObject class
- after we have made this change:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class PositionableObject(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = DirectX.Vector3(0, 0, 0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.PositionMatrix = DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetPosition(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetPosition(self, pos):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pos = Vectorize(pos)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = pos</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.PositionMatrix = DirectX.Matrix.Translation(self.__Position)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __DelPosition(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position = DirectX.Vector3(0, 0, 0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.PositionMatrix = DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Translate(self, amount):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� amount = Vectorize(amount)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__Position += amount</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.PositionMatrix =
- DirectX.Matrix.Translation(self.__Position)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Position = property(__GetPosition, __SetPosition,
- __DelPosition)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we will build a class which combines the
- PositionableObject and RotatableObject such that every time either the
- PostionMatrix or the RotationMatrix changes, it will update a variable called
- Matrix to contain the combined matrix.� I�ll call this class SceneObject, for
- lack of a better term.� We�ll start by defining the constructor for this class.�
- Note we have to set our local variables first since they will actually be used
- within the constructors of the two ancestor classes:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class SceneObject(PositionableObject,
- RotatableObject):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Matrix = DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__PositionMatrix =
- DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__RotationMatrix =
- DirectX.Matrix.Identity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� PositionableObject.__init__(self)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� RotatableObject.__init__(self)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Then we need to create the get and set properties for
- PositionMatrix and RotationMatrix.� The only thing to note here is that
- whenever we call the set method we update the Matrix variable as well.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetPositionMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__PositionMatrix</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetPositionMatrix(self, value):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__PositionMatrix = value</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Matrix = self.__RotationMatrix *
- self.__PositionMatrix</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __GetRotationMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.__RotationMatrix</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __SetRotationMatrix(self, value):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������ �self.__RotationMatrix = value</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Matrix = self.__RotationMatrix *
- self.__PositionMatrix</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� PositionMatrix = property(__GetPositionMatrix,
- __SetPositionMatrix)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� RotationMatrix = property(__GetRotationMatrix,
- __SetRotationMatrix)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to update any subclasses which wish to make use
- of this functionality.� Currently the only thing we need to update is the MeshRenderable class.� Update the class definition, making sure to replace the
- constructor calls to PositionableObject and RotatableObject with one to
- SceneObject:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class MeshRenderable(SceneObject):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, device, name, file):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� SceneObject.__init__(self)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Name = name</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� # ...</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Lastly, update the GetWorldMatrix method:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetWorldMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.Matrix</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Run the application.� Though there is no graphical
- difference in the program, we have eliminated many calculations performed with each frame.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655656">Adding Other Types of Objects</a></h2>
- <p class=Normal> </p>
- <p class=Normal>We can load meshes with ease, but often we will want to have
- just a simple box, or sphere, or other basic object in our scene.� This also
- comes in handy when we need to test code which acts on objects.� To start, we
- will create a class which represents basic objects.� We�ll use a dictionary to keep
- track of the constructors for each type of object:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class BasicObject(SceneObject):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� ObjectTypes = {'cylinder' :
- Direct3D.Mesh.Cylinder,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������ 'polygon' : Direct3D.Mesh.Polygon,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������ 'sphere' : Direct3D.Mesh.Sphere,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>���� ��������������'teapot' : Direct3D.Mesh.Teapot,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������ 'torus' : Direct3D.Mesh.Torus,</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������ 'box' : Direct3D.Mesh.Box</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������ }</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we will create the constructor.� We will take in the
- DirectX device, the type of the object, the name of the object, a color for the
- object, and then a variable number of other parameters to pass to the
- object�s constructor.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, device, type, name, color,
- *params):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� SceneObject.__init__(self)</p>
- </div>
- <p class=Normal> </p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Python allows you to define a function with a variable number of
- arguments by placing a star (*) in front of the variable which will hold the
- rest of the parameters.� This must always follow all of the regular
- parameters.</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'> </p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Python also allows you to define a parameter which accepts named
- parameters to the function and places them in a dictionary.� This is done by
- placing a double star (**) in front of the parameter which will receive all
- named attributes.� See the Python documentation for examples and more information
- on using these constructs.</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal> </p>
- <p class=Normal>For the type parameter, we will allow the user to either
- pass in a constructor for the type (callable) or a string to look up in the
- ObjectTypes dictionary:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if not callable(type):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� type = self.ObjectTypes[type]</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we will create the object, create a material, and set
- the color.� Note we are expanding the params tuple into a parameter list for
- the constructor:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Name = name</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Mesh = type(device, *params)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� color = Direct3D.ColorValue.FromColor(color)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� material = Direct3D.Material()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� material.AmbientColor = color</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Materials = [material]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Textures = []</p>
- </div>
- <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'> </span></p>
- <div>
- <table cellspacing=0 cellpadding=0 hspace=0 vspace=0 align=right>
- <tr>
- <td valign=top align=left style='padding-top:0in;padding-right:0in;
- padding-bottom:0in;padding-left:0in'>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- margin-left:.5in;margin-right:.5in'>
- <p class=OffsetHeader style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>New to Python?</p>
- <p class=Offset style='margin:0in;margin-bottom:.0001pt;border:none;
- padding:0in'>Python allows you to �expand� a list into parameters for a function.�
- Let�s say a function takes in 3 parameters.� It is perfectly valid to pass in
- the first parameter normally, then specify a list as the second argument and
- �expand� it by placing a star (*) in front of the parameter (as we have done
- above when calling type).</p>
- </div>
- </td>
- </tr>
- </table>
- </div>
- <br clear=ALL>
- <p class=Normal><span style='font-size:10.0pt;font-family:Consolas'> </span></p>
- <p class=Normal>Lastly, we need to provide the GetWorldMatrix method so
- that the SceneManager knows how to render it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def GetWorldMatrix(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return self.Matrix</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that the class is implemented, we need to add a method
- to the SceneManager which creates the class and adds it to the Objects
- dictionary.� This is very similar to the LoadMesh method, and should be
- nothing new aside from the variable arguments, and argument expansion:</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def LoadBasicObject(self, type, name, color,
- *args):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>� ������mesh = BasicObject(self.Device, type,
- name, color, *args)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������self.Objects[mesh.Name] = mesh</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������return mesh</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>That�s it!� Now we can load up basic objects.� Temporarily
- clear out the code in the SceneCreator class and add this in its place:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneCreate(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� teapot =
- sm.LoadBasicObject("teapot", "teapot", Drawing.Color.White)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam = sm.CreateCamera("Player Cam")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.Position = [0, 3, -5]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.LookAt(teapot.Position)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Run the code and check it out.� We can also load other
- objects which have parameters to their constructors:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.LoadBasicObject("box", "some
- box", Drawing.Color.Green, 1, 1, 1)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.LoadBasicObject("sphere",
- "some sphere", Drawing.Color.Blue, 1, 25, 25)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>See the managed DirectX documentation for more information
- on the parameters for these constructors.</p>
- <p class=Normal> </p>
- <p class=Normal>You should now replace the OnSceneCreate method with the
- previous one (which loads the tiger and animates it), as this was just example
- code to ensure our code works.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655657">A Better Main Function</a></h2>
- <p class=Normal> </p>
- <p class=Normal>There is one last thing I feel we should look at before
- finishing up with this tutorial: the main function. �The main function we
- currently have works, but it�s a bit rough around the edges.� Instead, what I
- would like to do is create a singleton class which keeps track of our
- SceneManager object so we do not have to keep an explicit pointer to it.� We�ll
- also add threading support in the mix to allow us to work with our framework
- from the console.� Remove the main function from this program entirely.</p>
- <p class=Normal> </p>
- <p class=Normal>We will not <i>exactly</i> use a singleton for this class.�
- Python has a better pattern to use which maps more naturally to its language
- constructs.� What we will do instead is to create an object whose instances all
- share the same dictionary (that is, all of them share the same variables and
- methods).� This is known as the Borg pattern, and it takes only a couple of lines
- to implement:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class Root(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� __State = {}</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.__dict__ = self.__ State</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note we must be very careful when defining the constructor of
- any Borg class.� After we assign the __dict__ variable, we have synced up with
- the global object and should not re-initialize the object.� This means that if
- we need to initialize any variables in the init method, we need to be careful
- to only do it the first time we create an instance of this object.� This is
- usually accomplished by a boolean variable which specifies if we have
- initialized the class or not.� Our Root class does not require any
- initialization, so we are in the clear.</p>
- <p class=Normal> </p>
- <p class=Normal>To see this pattern in action, try this code (do not add
- this to the framework, instead use �import * from framework� in an IronPython
- console):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>borg1 = Root()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>borg2 = Root()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>borg1.Test = �Hello World!�</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>print borg2.Test</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we will add a new main function inside the root class.�
- Note that we have kept the listeners parameter, but removed the SceneCreator
- default value from it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def Main(self, listeners = []):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.SceneManager = SceneManager()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Window = RenderWindow(self.SceneManager)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if not
- self.SceneManager.InitGraphics(self.Window.Handle):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� Forms.MessageBox.Show("Could not init
- Direct3D.")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� for obj in listeners:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� if callable(obj):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� obj = obj()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� self.SceneManager.AddListener(obj)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Window.Show()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� ����self.SceneManager.Go(self.Window)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The last thing we will add to our Root class is a threaded
- call to Main.� This will allow us to play with our framework from the console.�
- The Python API gives us a very easy to use �thread� library.� This library implements
- a function called start_new_thread which takes in a function to start in a new
- thread, and a list with the arguments to the function. </p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def ThreadMain(self, listeners = []):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� import thread</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� args = (listeners,)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� thread.start_new_thread(self.Main, args)</p>
- </div>
- <p class=Normal>���� </p>
- <p class=Normal>Finally, we need to update the call to the main class.�
- Change the call to be this:</p>
- <p class=Normal>�� </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>if __name__ == '__main__':</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Root().Main([SceneCreator])</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>If you run the application now, you should see the same
- result as we had before starting this section.� The reason we took the time to
- implement this change is so that we can now launch the framework in a
- background thread and manipulate the contents of the scene from the console.</p>
- <p class=Normal> </p>
- <p class=Normal>To test this out, open up a Python console and type out the
- following code (do not add this to the project):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> from framework import *</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> root = Root()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> root.ThreadMain()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> sm = root.SceneManager</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> tiger = sm.LoadMesh("Tiger",
- "tiger.x")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> class TigerRotator(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>...���� def OnFrame(self, ticks):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>...�������� tiger.Yaw(ticks)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>...</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>>>> sm.AddListener(TigerRotator())</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>You should see the tiger rotating in the scene, and we did
- all of this from the console alone.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655658">Code Checkpoint 6</a></h2>
- <p class=Normal> </p>
- <p class=Normal>We have implemented a complex change to the code.� Be sure
- that your application is working properly at this point.� If it is not, compare
- your project with checkpoint6.py.</p>
- <p class=Normal> </p>
- <h1><a name="_Toc140655659">Cool Things to Do with the Project</a></h1>
- <p class=Normal> </p>
- <p class=Normal>We have spent a good amount of time creating our framework,
- and there are many other things we could add to it.� Instead of this, I thought
- it would be much more fun to try it out a bit.</p>
- <p class=Normal> </p>
- <p class=Normal>For this portion of the tutorial we are no longer going to
- change the contents of the Python file we have been building.� Instead we will
- be making a new file which contains all of our demo code we will be creating.� <b>Take
- the file you have been making up to this point and rename it to framework.py.</b></p>
- <p class=Normal> </p>
- <p class=Normal>Create a new file called demo.py, and place the following
- contents into it:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>import clr</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>from framework import *</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class DemoSceneCreator(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneCreate(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam = sm.CreateCamera("Player Cam")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.Position = [0, 3, -5]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� box = sm.LoadBasicObject("box",
- "box 1", Drawing.Color.Red, 0.25, 0.25, 0.25)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� box.Position = [-1, 0, 0]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����� ��</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� box = sm.LoadBasicObject("box",
- "box 2", Drawing.Color.Green, 0.25, 0.25, 0.25)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� box.Position = [0, 0, 0]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam.LookAt(box.Position)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� box = sm.LoadBasicObject("box",
- "box 3", Drawing.Color.Blue, 0.25, 0.25, 0.25)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>���� ���box.Position = [1, 0, 0]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>if __name__ == '__main__':</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� main([DemoSceneCreator])</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>If you run this file now you should see three boxes
- and the view should be centered on the green one.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655660">Moving Objects Around, Part I</a></h2>
- <p class=Normal> </p>
- <p class=Normal>The first thing we will add is a class to move an object in
- a specific direction.� We will create an object which stores four pieces of
- information: the object to act on, the direction to move in, the speed at which
- to move, and the distance to travel before stopping.� Here is the first piece
- of this class:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class DirectionalMover(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj, direction, speed,
- distance):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Speed = speed</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Distance = distance</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Direction = Vectorize(direction)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Direction.Normalize()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The only major thing to note here is that we have normalized
- the direction vector, meaning that the total length of the vector is equal to
- one.</p>
- <p class=Normal> </p>
- <p class=Normal>Now we will add code to move the object every frame.� The
- first thing we will do is calculate the amount with which we wish to move it
- this frame.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� toTravel = self.Speed * elapsed</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>We have to be careful that we do not overshoot the distance
- we wish to travel.� To keep track of this, we will constantly subtract the
- toTravel variable from the Distance variable.� Once our Distance variable is
- less than the amount we would travel this frame, we translate the rest of the
- distance and return True (to remove ourselves from the listener pool):</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.Distance < toTravel:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Object.Translate(self.Direction *
- self.Distance)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Otherwise, subtract, and then translate:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Distance -= toTravel</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Object.Translate(self.Direction *
- toTravel)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>That�s it!� Now let�s test it out.� Add the following
- method to the DemoSceneCreator class:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneBegin(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� dm = DirectionalMover(sm.Objects["box 1"],
- [1, 0, 0], 0.3, 3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(dm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� dm = DirectionalMover(sm.Objects["box 2"],
- [0, 1, 0], 0.2, 2)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(dm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� dm = DirectionalMover(sm.Objects["box 3"],
- [0, 0, 1], 1, 10)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(dm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now run the application.� You should see the three boxes
- moving in different directions, reaching different locations simultaneously
- (after 10 seconds).</p>
- <p class=Normal> </p>
- <p class=Normal>If you are having trouble with this example, compare your
- code with the contents of demo1.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655661">Moving Objects Around, Part II</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Sometimes it is not convenient to simply move an object in a
- direction.� Often we need to move an object to a location instead.� What we
- would like, in this case, is an object mover which takes in a position to move
- to, and a speed at which to move.</p>
- <p class=Normal> </p>
- <p class=Normal>One approach to this would be to use the class we have
- already created and simply figure out the distance between the current position
- and the position we wish to move to.� This would be a valid approach in some
- situations, but what if the position of the object was changed by some other
- object in between the time the mover object was created, and the time at which
- the object reached its destination?� Then the object would be off-target by the
- time it reached its destination.</p>
- <p class=Normal> </p>
- <p class=Normal>To remedy this problem, we will create a new class to
- perform this action.� This is the constructor for this class:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class PositionalMover(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj, destination, speed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Dest = Vectorize(destination)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Speed = speed</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>In the OnFrame method, we need to calculate the distance we
- will travel that frame, the distance we have left to travel (so we do not
- overshoot our destination), and the direction we will travel in.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� toTravel = self.Speed * elapsed</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� direction = self.Dest - self.Object.Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� distance = direction.Length()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� direction.Normalize()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Once we have this information, the rest of our method should
- look very familiar.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 0in 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if distance < toTravel:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Object.Translate(direction *
- distance)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� return True</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� else:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Object.Translate(direction *
- toTravel)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Note that we are doing a lot more calculations per frame in
- PositionalMover than we did in DirectionalMover (normalizing a vector is a
- costly operation as it involves a square root).� We have to be careful how
- often we use this class, as it could possibly bog down our application if we
- overused it.� However, this would not really become a concern until we are
- moving a large amount of objects with this class.</p>
- <p class=Normal> </p>
- <p class=Normal>Now lets test our class.� Clear out the contents of
- DemoSceneCreator�s OnSceneBegin method, if you have not already, and add this:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneBegin(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PositionalMover(sm.Objects["box 1"],
- [2, 0, 0], 0.3)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PositionalMover(sm.Objects["box 2"],
- [0, 2, 0], 0.2)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PositionalMover(sm.Objects["box 3"],
- [1, 0, 10], 1)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The results should be exactly the same as before.� We have
- moved the boxes to the same locations at the same speed.� Try playing around
- with the locations the boxes move to, and the speed at which they move.� Try
- adding both a PositonalMover and DirectionalMover affectors to the same object
- (be sure to register both listeners with the SceneManager) and watch the
- effects.</p>
- <p class=Normal> </p>
- <p class=Normal>If you are having trouble with this example, compare your
- code with the contents of demo2.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655662">Moving Objects Around, Part III</a></h2>
- <p class=Normal> </p>
- <p class=Normal>This is very cool, but still, there are many situations
- where simply moving from a location to another location would not be enough.� Sometimes
- we need to set up a list of points for an object (or character) to walk between.�
- This can be accomplished by keeping track of a set of points (and speeds at
- which to travel to these points) and delegating the actual movement task to
- PositionalMover.</p>
- <p class=Normal> </p>
- <p class=Normal>We will implement this class as PointMover, for lack of a
- better term.� The constructor for this object will take in only one parameter,
- the object to move.� The object will also keep track of a list of points to
- walk between, and the current PositionableMover object (which is doing the
- actual work).</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class PointMover(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Points = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Current = None</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to create a method to add points for the
- object to move to.� This method will take two parameters: the point to
- move to and the speed at which to move.� We�ll simply throw this into the
- Points list as a tuple.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def AddPoint(self, point, speed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Points.append((point, speed))</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to implement a method which sets the Current
- variable by taking the first item in the list and creating a PositionalMover
- for it.� We will return True if we are successful in creating the object:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def NextPoint(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if len(self.Points):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� dest, speed = self.Points[0]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� del self.Points[0]</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� self.Current =
- PositionalMover(self.Object, dest, speed)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Notice that we have not added a return False to the end of
- this method.� This would be valid, but unnecessary.� Whenever you do not
- return a value, you are implicitly returning None.� Since None evaluates to be
- false, you actually <b>are</b> returning false implicitly whenever you do not
- return anything.� (Or more specifically, you are returning an expression which
- evaluates to False.)</p>
- <p class=Normal> </p>
- <p class=Normal>Now that we have all of the helper methods that are
- required, we are ready to create the OnFrame method.� The first part of the
- method will handle the first call to the method (in which case self.Current
- is None).� If it is None then we will attempt to set it by calling NextPoint.�
- If NextPoint returns False then the user never bothered to add any points, and
- we will return True to tell SceneManager that we are done:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.Current is None:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if not self.NextPoint():</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that we have setup self.Current, we need to actually
- move the object.� As soon as self.Current.OnFrame returns false, we need to
- move onto the point�if there are any left.� This is the rest of the method:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if self.Current.OnFrame(elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>�������� ���return not self.NextPoint()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that we have implemented this class, it�s time to test
- it.� Clear out the contents of DemoSceneCreator�s OnSceneBegin method, if you
- have not already.� Place this code there:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneBegin(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 1"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [2, -1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [0, 1, 2], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [0, 0, 10], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm) </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 2"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [2, 1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-1, -3, 5], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-1, 0, 10], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm) </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 3"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-2, -1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [1, -3, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [1, 0, 10], 1.15 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Run the program to watch the cubes dart around the scene.� Take
- some time to play around with the points the objects move to, and the speed at
- which they move.</p>
- <p class=Normal> </p>
- <p class=Normal>If you are having trouble with this example, compare your
- code with the contents of demo3.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655663">Auto Tracking</a></h2>
- <p class=Normal> </p>
- <p class=Normal>It would be very useful in many situations to make the
- camera (or other object) �track� something on screen.� That is, every time the
- object moves, the camera is readjusted to face it.� Fortunately for us, this is
- a nearly trivial thing to implement with the framework we have created:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class AutoTrack(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj, target):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Target = target</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object.LookAt(self.Target.Position)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Replace DemoSceneCreator�s OnSceneBegin method with the
- following code:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneBegin(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 1"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [2, -1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� ����pm.AddPoint( [0, 1, 2], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [0, 0, 10], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 2"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [2, 1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-1, -3, 5], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-1, 0, 10], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>� ������sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm = PointMover(sm.Objects["box 3"])</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [-2, -1, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [1, -3, 0], 1 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pm.AddPoint( [1, 0, 10], 1.15 )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(pm)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener( AutoTrack(sm.Objects["Player
- Cam"], sm.Objects["box 3"]) )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'> </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Run the demo.� This probably looks very strange.� You will
- see one box not moving, while the other two fly around.� This shows that our
- camera is �tracking� the object we have specified.� To confirm this, comment
- out the line which adds the AutoTrack to the SceneManager.� The boxes move as
- normal.� The reason this looks so strange is because we have not set a
- background to contrast the movement of the boxes.� This would look a lot more natural
- in a real-world application.</p>
- <p class=Normal> </p>
- <p class=Normal>If you are having trouble with this example, compare your
- code with the contents of demo4.py.</p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655664">Gravity and Free Floating Bodies</a></h2>
- <p class=Normal> </p>
- <p class=Normal>I have saved the best part of the tutorial for last.� In
- this section, we will implement a gravity simulator.� The simulator will allow
- you to create any number of free floating bodies in space (like planets) by setting
- their mass, position, initial velocity, and color (the color is just for visual
- distinction).</p>
- <p class=Normal> </p>
- <p class=Normal>The first thing we need to do is to create a class which
- understands how to apply the physics of gravity to a particular object.� This
- object will keep track of an object�s Mass, Velocity, Acceleration, and a list
- of objects which are affecting the object.� Additionally we will keep track of
- the object�s radius to handle what happens when objects get too close to each
- other.� The constructor for this class is very simple, taking in a few of these
- as parameters and setting up variables:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class GravityAffector(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj, mass, radius,
- initialVelocity):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Radius = radius</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Affectors = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Mass = mass</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Velocity = Vectorize(initialVelocity)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Acceleration = DirectX.Vector3(0,0,0)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to create a method which figures out exactly
- how much acceleration is being applied to an object at any given moment.� The
- result will be stored in the self.Acceleration variable.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def ApplyGravity(self):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Acceleration = DirectX.Vector3(0,0,0)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we need to loop through all of the objects affecting the
- current object and apply Newton�s law of universal gravitation to the current
- object.� To simplify a lot of later code, we will assume the self.Affectors
- list could possibly contain the object we are acting on.� In this case we�ll
- simply ignore it.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for obj in self.Affectors:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� if obj != self.Object:</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>To calculate the affect of gravity, we need to know the
- direction (unit vector) that the force is acting in, and the squared distance
- between the two objects.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� direction = obj.Object.Position -
- self.Object.Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� distSquared =
- direction.LengthSquared()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� direction.Normalize()</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Finally, we need to calculate the change in acceleration due
- to this object.� There is one small catch though.� We are not doing any
- collision detection in this piece of code, and if objects are too close to each
- other the gravitation formulas explode, giving accelerations which should be
- technically impossible.� To fix this problem, we only apply gravity when the
- squared distance is greater than the radius of the object.� So long as the
- radius is set to be a reasonable amount (say 0.05 units), this will fix the
- problem of planets having acceleration they should not have when �colliding.�</p>
- <p class=Normal style='text-autospace:none'><span style='font-size:10.0pt;
- font-family:Consolas'> </span></p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� if distSquared > self.Radius:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������������������� self.Acceleration += direction *
- (obj.Mass / distSquared)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Any physicist who just read that probably physically cringed
- at this equation.� This is not exactly Newton�s law of universal gravitation.�
- However, since we are arbitrarily defining the mass of our objects, this should
- be good enough for our simulation.</p>
- <p class=Normal> </p>
- <p class=Normal>Finally, during each frame we need to apply the acceleration
- to the velocity of the object, and apply the velocity to the position of the
- object.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Velocity += self.Acceleration * elapsed</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object.Translate(self.Velocity * elapsed)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>We now need to ensure that the acceleration of all objects is
- calculated before we apply it.� We do not want to move any object before all
- other objects get the position of the object.� In theory, the small amount of
- motion should not affect the overall mechanics of the scene, but in practice
- our scene will have few enough objects (close enough together) that this will
- actually impact it.</p>
- <p class=Normal> </p>
- <p class=Normal>To solve this, we will create a GravityManager class which
- calls the ApplyGravity methods of all objects before it calls the OnFrame method.� Here is the code for this class:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class GravityManager(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, objs = []):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Objects = objs</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for obj in self.Objects:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� obj.ApplyGravity()</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for obj in self.Objects:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� obj.OnFrame(elapsed)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we are almost ready to create our scene, but there is
- one final snag we must correct before we are ready to do that.� When we start
- our simulation up, all of the objects we have created will go flying around the
- scene.� We need the camera to watch all of them so that we can see what is
- going on.� The AutoTrack class we have already created seems like a good
- choice, but objects could move too far to actually be seen.</p>
- <p class=Normal> </p>
- <p class=Normal>Our solution will be a new auto tracking class which finds
- the midpoint of all objects on the screen and looks at that instead.� It will
- also always stay a specified distance from the midpoint, so objects have a
- harder time moving off camera.� I will warn you up front that this is not a
- complete solution (objects will still fall off screen eventually), but it
- should at least allow us to watch our simulation for longer than our original
- AutoTrack class would.</p>
- <p class=Normal> </p>
- <p class=Normal>The constructor for this class should take in the object we
- will be operating on (the camera in this case), the list of objects to find the
- midpoint for, and a distance (offset) to stay from this midpoint.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class MidpointAutoTrack(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def __init__(self, obj, others, offset):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object = obj</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Others = others</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Offset = Vectorize(offset)</p>
- </div>
- <p class=Normal> </p>
- <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
- the self.Others variable is a sequence.� If it is not we will assume the user
- just passed in a singular object.� Otherwise, we will store the length of our
- self.Others sequence in the l variable:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnFrame(self, elapsed): </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� l = None</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� pos = DirectX.Vector3(0, 0, 0)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if type(self.Others)==list:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� l = len(self.Others)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� if l is None:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� pos = self.Others.Position</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>We need to handle the case where the length is equal to one
- specially:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� elif l == 1:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� pos = self.Others[0].Position</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>If we did not do this, we would get a divide by zero error
- in the code which handles the midpoint calculation:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� elif l > 1:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� for o in self.Others:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��������������� pos += o.Position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� pos.X /= l-1</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� pos.Y /= l-1</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� pos.Z /= l-1</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that we have calculated the position, we need to set the
- position of the current object and have it look at the calculated position:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object.Position = pos + self.Offset</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� self.Object.LookAt(pos)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>This completes our MidpointAutoTrack class.</p>
- <p class=Normal> </p>
- <p class=Normal>Now that we have all of the code we need, let�s create the
- scene.� The first thing we will do is create a list which contains information
- for all of the objects we will place in the scene.� By building this list first
- (and then using this list to actually create the scene), we are making it much
- easier to add and remove objects from the scene later.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>class GravityDemoCreator(object):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� def OnSceneCreate(self, sm):</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� objs = []</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Next we will populate the objs list with tuples containing
- the color, mass, position, and velocity of each object in the scene.� Note that
- I have purposefully commented out the last line.� The first couple of times you
- run the code you should only have three objects in the scene (since the gravity
- for this is much more stable).� You should uncomment this line to add a fourth
- after you have played with the code with only 3 objects.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� # add: color, mass, poition, velocity</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� objs.Add( (Drawing.Color.Red,��� 1, [-1, 0,
- 0], [0.05, 0.05, 0]) )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� objs.Add( (Drawing.Color.Green,� 1, [0, 1,
- 0], �[0.1, 0, 0]) )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� objs.Add( (Drawing.Color.Blue,�� 1, [1, 0, 1], �[-0.1, 0, 0]) )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� #objs.Add( (Drawing.Color.White, 1,�[0, 0,
- 1], [0, 0, 0]) )</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now we will loop through the objects, placing the created object
- in a list called finalObjects, and placing a GravityAffector for the objects
- into a list called affectors:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� radius = 0.05</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� affectors = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� finalObjects = []</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� i = 0</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for color, mass, position, velocity in objs:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� sphere =
- sm.LoadBasicObject("sphere", "planet " + str(i), color,
- radius, 25, 25)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� sphere.Position = position</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� affectors.Add( GravityAffector(sphere,
- mass, radius, velocity) )</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� finalObjects.Add(sphere)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� i += 1</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Now that we have created all of the GravityAffector objects,
- we need to set them to affect each other.� We do that by setting the Affectors
- variable on all of them.� After this is done, we need to add create the GravityManager
- class and add it to the SceneManager.</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� for g in affectors:</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>����������� g.Affectors = affectors</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� gm = GravityManager(affectors)</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(gm)</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>The last thing we need to do in this method is to create the
- camera and have it autotrack the midpoint of all of these items:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� cam = sm.CreateCamera("Player Cam")</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� sm.AddListener(MidpointAutoTrack(cam,
- finalObjects, [0, 0, 15]))</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� </p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>������� return True</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal> </p>
- <p class=Normal>Finally, we need to update the call to the main function
- such that it uses the GravityDemoCreator.� Find that section of code and change
- it so it reads:</p>
- <p class=Normal> </p>
- <div style='border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
- background:silver;margin-left:.25in;margin-right:.25in'>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>if __name__ == '__main__':</p>
- <p class=Code-Highlighted style='margin:0in;margin-bottom:.0001pt;background:silver;
- border:none;padding:0in'>��� Root().Main([GravityDemoCreator])</p>
- </div>
- <p class=Normal> </p>
- <p class=Normal>Run the application and watch the simulation.� Be sure to
- run it multiple times, as you will get different results each time.</p>
- <p class=Normal> </p>
- <p class=Normal>After running this several times, you may notice some
- peculiar things happening with our simulation.� First and foremost, why do we
- get different results each time we run the program?� Nowhere did we make a call
- to a random number generator�� The reason for this is floating point rounding
- errors.� If you add code to dump the current velocity and acceleration of each
- object to a file every frame, you will notice that after a frame or two small
- rounding errors explode into large changes to the overall system.� Chaos theory
- at work.� We could fix this by using a more accurate floating point class (and
- chopping off the rounding error portions based on the accuracy of this class).</p>
- <p class=Normal> </p>
- <p class=Normal>Another strange thing is, if you run the program long
- enough, you may notice that some objects occasionally �wobble� in ways which
- seem unnatural.� This is due to the fact that our camera is not actually
- stationary, but moving and changing directions every frame to keep the moving
- objects on screen.� A lot of the weirdness of object �wobble� goes away if you
- simply turn off camera movement�but good luck keeping them on screen if you do
- not move the camera to keep up with them.</p>
- <p class=Normal> </p>
- <p class=Normal>Try adding more objects to the system and changing the mass
- of objects (along with the other initial conditions of the system).</p>
- <p class=Normal> </p>
- <p class=Normal>If you are having trouble with this example, compare your
- code with the contents of GravityDemo.py.</p>
- <p class=Normal> </p>
- <h1><a name="_Toc140655665">Where to Go from Here</a></h1>
- <p class=Normal> </p>
- <p class=Normal>I hope you have enjoyed working through this tutorial as
- much as I have enjoyed making it.� There are several resources I would like to
- point out if you are interested in further study of IronPython or DirectX. </p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655666">Managed DirectX and XNA</a></h2>
- <p class=Normal> </p>
- <p class=Normal>Managed DirectX 1 (which this tutorial makes heavy use of) is not scheduled for any future updates.� Instead,
- Microsoft made a new managed 3D library built on top of DirectX
- called XNA.� You can find more information on XNA on their web site:</p>
- <p class=Normal><a href="http://www.microsoft.com/xna/">http://www.microsoft.com/xna/</a></p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655667">IronPython</a></h2>
- <p class=Normal> </p>
- <p class=Normal>You can find more information on IronPython by visiting the
- IronPython Codeplex site:</p>
- <p class=Normal><a href="http://www.codeplex.com/IronPython">
- http://www.codeplex.com/IronPython</a></p>
- <p class=Normal> </p>
- <p class=Normal>You can subscribe to the IronPython mailing list for any
- questions you have about the project:</p>
- <p class=Normal><a
- href="http://lists.ironpython.com/listinfo.cgi/users-ironpython.com">http://lists.ironpython.com/listinfo.cgi/users-ironpython.com</a></p>
- <p class=Normal> </p>
- <p class=Normal>There are also several blogs from the people who use
- IronPython on a daily basis.� I suggest starting here:</p>
- <p class=Normal><a href="http://blogs.msdn.com/ironpython/">
- http://blogs.msdn.com/ironpython/</a></p>
- <p class=Normal> </p>
- <h2><a name="_Toc140655668">Extending the Framework</a></h2>
- <p class=Normal> </p>
- <p class=Normal>If you are a graphics hobbyist like I am, you may wish to
- continue extending this framework.� It already has the basic features for
- positioning and rotating objects, however, what it truly lacks is extra
- functionality to handle more complex meshes and materials.� I have implemented
- 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.
- If you are interested, read through the framework Python module to get a good feel for the updates.</p>
- </div>
- </body>
- </html>