XNA 3D Primer 978-0-470-59693-7


195 75 485KB

English Pages 42

Report DMCA / Copyright

DOWNLOAD PDF FILE

Recommend Papers

XNA  3D Primer
 978-0-470-59693-7

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

Michael C. Neel

®

XNA 3D Primer Wiley Publishing, Inc.

Updates, source code, and Wrox technical support at www.wrox.com

XNA® 3D Primer Published by Wiley Publishing, Inc. 10475 Crosspoint Boulevard Indianapolis, IN 46256 www.wiley.com Copyright ©2010 by Wiley Publishing, Inc., Indianapolis, Indiana ISBN: 978-0-470-59693-7 No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030, (201) 748-6011, fax (201) 748-6008, or online at

http://www.wiley.com/go/permissions. Limit of Liability/Disclaimer of Warranty: The publisher and the author make no representations or warranties with respect to the accuracy or completeness of the contents of this work and specifically disclaim all warranties, including without limitation warranties of fitness for a particular purpose. No warranty may be created or extended by sales or promotional materials. The advice and strategies contained herein may not be suitable for every situation. This work is sold with the understanding that the publisher is not engaged in rendering legal, accounting, or other professional services. If professional assistance is required, the services of a competent professional person should be sought. Neither the publisher nor the author shall be liable for damages arising herefrom. The fact that an organization or Website is referred to in this work as a citation and/or a potential source of further information does not mean that the author or the publisher endorses the information the organization or Website may provide or recommendations it may make. Further, readers should be aware that Internet Websites listed in this work may have changed or disappeared between when this work was written and when it is read. For general information on our other products and services please contact our Customer Care Department within the United States at (877) 762-2974, outside the United States at (317) 572-3993 or fax (317) 572-4002. Trademarks: Wiley, the Wiley logo, Wrox, the Wrox logo, Wrox Programmer to Programmer, and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries, and may not be used without written permission. XNA is a registered trademark of Microsoft Corporation. All other trademarks are the property of their respective owners. Wiley Publishing, Inc., is not associated with any product or vendor mentioned in this Wrox Blox. This PDF should be viewed with Acrobat Reader 6.0 and later, Acrobat Professional 6.0 and later, or Adobe Digital Editions. Usage Rights for Wiley Wrox Blox. Any Wiley Wrox Blox you purchase from this site will come with certain restrictions that allow Wiley to protect the copyrights of its products. After you purchase and download this title, you: • • • • • •

Are entitled to three downloads Are entitled to make a backup copy of the file for your own use Are entitled to print the Wrox Blox for your own use Are entitled to make annotations and comments in the Wrox Blox file for your own use May not lend, sell or give the Wrox Blox to another user May not place the Wrox Blox file on a network or any file sharing service for use by anyone other than yourself or allow anyone other than yourself to access it • May not copy the Wrox Blox file other than as allowed above • May not copy, redistribute, or modify any portion of the Wrox Blox contents in any way without prior permission from Wiley

If you have any questions about these restrictions, you may contact Customer Care at (877) 762-2974 (8 a.m. - 5 p.m. EST, Monday - Friday). If you have any issues related to Technical Support, please contact us at 800-762-2974 (United States only) or 317-572-3994 (International) 8 a.m. - 8 p.m. EST, Monday - Friday) Associate Publisher Jim Minatel Project Editor Kristin Vorce Technical Editor Chris G. Williams Senior Production Editor Debra Banninger

Copy Editor Cate Caffrey Editorial Director Robyn B. Siesky Editorial Manager Mary Beth Wakefield Marketing Manager David Mayhew

Production Manager Tim Tate Vice President and Executive Group Publisher Richard Swadley Vice President and Executive Publisher Barry Pruett Proofreader Nancy Carrasco

Contents Who Is This Book For? 3D Overview Basic 3D Math Right-Hand Rule Working with Matrices Identity, Scale, Rotate, Orbit, Translate (ISROT) Working with Vectors Unit Vectors Working with Quaternions

Controlling the Camera Basic Camera Follow Camera ViewPorts BoundingFrustum

3D Models Modeling Programs and Formats Loading a Model

Collision Detection Skeletal Animations Extending the Content Pipeline Manipulating Bones at Run Time Using Model Animations

About Michael C. Neel

1 2 4 4 5 6 7 10 12

13 13 19 20 22

23 23 24

27 29 29 31 36

39

XNA 3D Primer In early computer graphics, graphic adapters were 2D (two-dimensional) only. Games created a frame by setting pixel colors in the graphics adapter’s memory, then telling the graphics adapter to make the buffer active in a simple process called frame buffering. If a game was in 3D (three dimensions), it meant that the programmers wrote 3D-to-2D conversion methods, sometimes in C, often in Assembly, to generate 2D frames from 3D sources. Fortunately, it’s no longer 1980, and graphics adapter advancements include processing 3D images. With XNA, it’s possible to write high-performance 3D games in managed code that will run on a Windows PC and Xbox 360. 3D game development has never been easier, but that doesn’t mean it is easy. Learning the terms and concepts in 3D graphics programming should qualify as a foreign language credit.

Who Is This Book For? This Wrox Blox is an introduction to 3D game programming with XNA, but it is not an introduction to XNA. You should be familiar with the basics of XNA (working through a few tutorials at http://creators.xna.com should be sufficient) and also be comfortable with C# and .NET 3.5. Although this Wrox Blox covers the concepts of 3D at a high level, it does not dive into details such as calculating vector cross-products. The focus is on the methods in the XNA Framework and when you should use them. This is a primer on the world of 3D programming, but you may need to explore some areas more deeply if you intend to complete a fully working 3D game. Hopefully, this Wrox Blox will have provided you with enough tools to learn what you don’t know.

XNA 3D Primer

3D Over view In XNA the basic steps to using 3D content in your game are:

1. . 3. 4. 5.

Loading or creating the model Setting up the camera Configuring the graphics device Configuring the basic effect (or shader) Rendering the result

A simple example of a game rendering a 3D object is as follows: public class GameMain : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; VertexPositionColor[] diamond; Matrix cameraProjection, cameraView; public GameMain() { graphics = new GraphicsDeviceManager(this); } protected override void Initialize() { graphics.PreferredBackBufferHeight = 450; graphics.PreferredBackBufferWidth = 450; graphics.ApplyChanges(); cameraProjection = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver2, 1f, 1f, 10000f); cameraView = Matrix.CreateLookAt( new Vector3(0, 0, 4), Vector3.Zero, Vector3.Up); base.Initialize(); } protected override void LoadContent() { diamond = new VertexPositionColor[] { new VertexPositionColor(new Vector3(0, 0, 1), Color.Red), new VertexPositionColor(new Vector3(-1, 0, 0), Color.Green), new VertexPositionColor(new Vector3(0, 1, 0), Color.Blue), new VertexPositionColor(new Vector3(0, 0, -1), Color.Red), new VertexPositionColor(new Vector3(0, 1, 0), Color.Green), new VertexPositionColor(new Vector3(1, 0, 0), Color.Blue), new VertexPositionColor(new Vector3(0, 0, 1), Color.Red), new VertexPositionColor(new Vector3(0, -1, 0), Color.Green), new VertexPositionColor(new Vector3(-1, 0, 0), Color.Blue), new VertexPositionColor(new Vector3(0, -1, 0), Color.Red), new VertexPositionColor(new Vector3(0, 0, -1), Color.Green), new VertexPositionColor(new Vector3(1, 0, 0), Color.Blue), }; }

XNA 3D Primer protected override void Update(GameTime gameTime) { KeyboardState keys = Keyboard.GetState(); if (keys.IsKeyDown(Keys.Escape)) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration( graphics.GraphicsDevice, VertexPositionColor.VertexElements); BasicEffect effect = new BasicEffect(graphics.GraphicsDevice, null); effect.VertexColorEnabled = true; effect.Projection = cameraProjection; effect.View = cameraView; effect.World = Matrix.Identity; effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); effect.GraphicsDevice.DrawUserPrimitives( PrimitiveType.TriangleStrip, diamond, 0, diamond.Length - 2); pass.End(); } effect.End(); base.Draw(gameTime); } }

This code is available for download in the example solution as part of the “1_3D_Overview” project. The class starts by creating a GraphicsDeviceManager, which provides access to the video card. In Initialize, the PreferredBackBufferHeight and PreferredBackBufferWidth properties are set to the desired window height and width, respectively. The next lines describe the camera, which is covered in detail later, but for now, know that the projection is the lens of the camera and the view is the position and orientation. In LoadContent you create a simple list of vertices with color information. A vertex is a point on a polygon, and in this case, it is a polygon of a three-dimensional diamond. It is common in 3D programming to refer to a vertex collection as a mesh. A model refers to a mesh or collection of meshes with color or texture data, and possibly animation data as well. In the Draw method, setting the VertexDeclaration tells the GraphicsDevice what type of data is being sent. Before the data can be displayed, a BasicEffect shader is configured. A shader is code that will run on the GPU of the graphics adapter, saving the CPU for other tasks such as physics calculations, input handling, artificial intelligence, and so on. Shaders for XNA are written in High-Level Shader Language (HLSL), which is similar to C. Shaders commonly have the extension .fx.

3

XNA 3D Primer At a glance, shaders fall into two types — vertex shaders and pixel shaders. A vertex shader applies transformations to the vertices being rendered such as changing their position, and a pixel shader processes color information to determine the color of the pixels on screen. Shaders are covered in greater detail in the animation section; for now, just know that BasicEffect is a vertex and pixel shader included with the XNA Framework. The location of our diamond on screen is determined by the Projection, View, and World matrices set on effect. The projection and view matrices come from our camera, and the World matrix is how the model space maps to world space. Use this matrix to adjust a mesh or model to world space, such as scaling up a model that was too small compared to the other models in the game. All of this comes together to produce the rather boring screen shown in Figure 1.

Figure 1

Basic 3D Math Before I can improve upon the first example, I must cover some 3D math. Please stay with me — I promise this won’t be like that college calculus class with the evil professor who lived to fail students!

Right-Hand Rule The coordinate system used in XNA 3D follows the Right-Hand Rule. This is a simple mnemonic to remember which direction is positive in 3D space. To use this mnemonic, hold your right hand so that you are looking at your palm. Make a fist and extend your thumb to the right, index finger up, and middle finger straight so that it is pointing directly at you. Your fingers are now pointing in the positive directions of each axis.

4

XNA 3D Primer Another way to remember this (one that I prefer) is to look at the screen and raise your right hand. Your right hand is the positive direction for the X-, Y-, and Z-axes. X values increase as you move to the right, Y values increase as you move up, and Z values increase as you move toward the front of the screen. For the positive rotation direction around an axis, picture yourself standing behind the screen. With your right hand, reach out and grab an axis. The direction your fingers wrap around the axis is the positive direction when rotating around that axis.

Working with Matrices At the heart of 3D math is the matrix. A matrix can be thought of as a machine on a conveyer belt, processing vertices. Each matrix is programmed to perform a transformation to a vertex, and can be combined with other matrices to perform multiple transformations at once. Strictly speaking, you are using 4×4 matrices; each matrix is a table for values with four columns and four rows. XNA provides several static methods on the Matrix structure for creating matrices, so I won’t get into the details of how to perform matrix math manually. Some of the common methods used to create matrices are in the following table:

Matrix Static Method

Notes

Matrix.CreateScale

Changes the scale (size)

Matrix.CreateRotationX

Rotation around an axis

Matrix.CreateRotationY Matrix.CreateRotationZ Matrix.CreateFromAxisAngle Matrix.CreateFromQuaternion Matrix.CreateTranslation

Translates (moves) the given amount

Matrix.CreateLookAt

Used to create a View matrix

Matrix.CreatePerspective

Used to create a Perspective matrix

Matrix.CreatePerspectiveFieldOfView Matrix.CreatePerspectiveOffCenter Matrix.CreateOrthographic Matrix.CreateOrthographicOffCenter Matrix.CreateWorld

Used to create a World matrix

In addition to these, there are methods for matrix math and specialized methods such as Matrix.CreateShadow, which will flatten your mesh to a Plane given a light direction. All of these

methods (and all other similar methods in the XNA Framework) assume that (0, 0, 0) is the X, Y, Z point from which we are referencing. When I say to translate (move) to (1, 2, 4), I am really saying “from (0, 0, 0)

5

XNA 3D Primer move to (1, 2, 4).” Because of this, there is a general rule you should follow when combining matrices called ISROT (Identity, Scale, Rotate, Orbit, Translate), which is discussed later.

Decimals, Doubles, and Floats, Oh My! You may have noticed I’ve been using floats instead of the more common .NET decimal type. Decimals are added, subtracted, multiplied, divided, and so on in software. The reason for this is what is known as the inaccuracies of floats. At the CPU level, everything is binary, which is fine for integers, but there are problems converting floating point numbers to binary format — 1.0 might become 1.00000001 or 0.9999999. In the world of 3D graphics programming, this is OK. In the world of business applications, this is not acceptable. Imagine telling someone their account is off by a few pennies because of a computer conversion! So why is 3D programming OK with these missing pennies? The short answer is “performance.” Floats and doubles perform calculations on the CPU itself in a single step, whereas decimals are done in multiple steps: one step for the whole number and one step for the fractional number, then combining the result in a final step. Given the number of calculations in a 3D game running at 60 frames per second, the performance difference is significant. Avoid decimals unless absolutely needed. When writing code, a float can be used where a double is required. A double-to-float conversion requires a case and includes a loss of precision. In general, this loss isn’t significant, but avoid conversions if possible. One other area to be aware of is testing the values of floats and doubles. The following code may never be called since the value in myFloat will have lost some accuracy! if(myFloat == 1.5f) // do something

Consider using >= and