Subscribe

RSS Feed (xml)

Powered By

Powered by Blogger

Google
 
xnahelp.blogspot.com

Jumat, 04 April 2008

Loading 3D Models

The XNA Framework’s Content Pipeline handles loading .X and .FBX files automatically
when they are pasted into the Solution Explorer (or included in the project). This is when
the Content Pipeline goes through the process described in the previous section of
importing, processing, and compiling the data. We then can use our game class and the
Content Manager to read the model information.
We will start by creating a new project, which we can call Load3DObject. We can create
both the Windows and Xbox 360 game projects. After getting our solution set up, we can
then add an existing project to our solution—the XELibrary from last chapter. Once we
import both the Windows and the Xbox 360 XELibrary projects, we need to reference
them inside of the game projects we created. We reference the XELibrary in our Windows
project and the XELibrary_Xbox360 in our Xbox 360 game project.
After we have our initial setup of our solution file completed, we can jump right in and
add a using statement at the top of our Game1.cs class to access our library:
using XELibrary;
We can set up our private member fields to access the game components in our library:
private FPS fps;
private FirstPersonCamera camera;
private InputHandler input;
The following is our constructor, where we initialize the variables we just set:
public Game1()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
input = new InputHandler(this);
Components.Add(input);
camera = new FirstPersonCamera(this);
Components.Add(camera);
#if DEBUG
//draw 60 fps and update as often as possible
fps = new FPS(this, true, false);
#else
fps = new FPS(this, true, false);
#endif
Components.Add(fps);
}
With just that little bit of coding (and setup) we now have access to all of the code in our
library. We automatically have a first person camera now and we can handle input as well
as display our frame rate. Game components aren’t too shabby.
As we add content to our pipeline we could pretty easily clutter up our Solution Explorer
pane with all of the files. To help alleviate that problem, we are going to create a
Contents folder with subfolders under it. To start, we will add two subfolders—Models
and Textures—under the Content folder. This is not required, but it really helps keep the
clutter minimal. After doing this we will need to make sure we include that folder inside
Loading 3D Models 115
6
of our Xbox 360 solution, otherwise the content files won’t compile and won’t be
deployed and will cause the demo to not load.
Now with the preliminary work out of the way, we can actually get down to business and
load a 3D object. Find the Spacewar project that we extracted back in Chapter 1,
“Introducing the XNA Framework and XNA Game Studio Express,” and under the
Contents\Models\ folder we need to copy the asteroid1.x file and copy it into our
Solution Explorer’s Contents\Models\ folder. When we do this, XNA Game Studio
Express flags it as XNA Framework Content. Now we can compile the code, which will
also kick off the Content Pipeline. The Content Pipeline kicked off the Content Importer,
shoved the data into the DOM, and then called the Content Processor, which passed it to
the Content Compiler. So when we compile our game, it not only compiles our code, but
also the content.
The Content Compiler threw an error, “Missing asset … \Content\Textures\asteroid1.tga,”
because the .X file we loaded has a reference to a texture inside of it. It references a sibling
folder by the name of Textures. The following is a portion of the asteroid1.x file where it
references the texture in the texture’s sibling folder:
Material phong1SG {
1.0;1.0;1.0;1.000000;;
18.000000;
0.000000;0.000000;0.000000;;
0.000000;0.000000;0.000000;;
TextureFilename {
“..\\textures\\asteroid1.tga”;
}
}
We have that folder, but we did not grab the .tga texture from the Spacewar project. Let’s
do that now and paste it into our Textures folder. Once we do this we can compile again
and it should compile without issues. After it compiles successfully, we can browse to our
/bin/x86/content/models/ and /textures/ folders to see there are a couple of files that were
created at the time we compiled. The Content Compiler took the files and compiled them
into the .xnb files we see here. The compiler gave us an error when it could not find the
texture that was associated with the .X file. We have corrected that and our code (and
content) now compiles successfully.
We could run the code but nothing would be on our screen because we have not actually
told XNA to load and draw the object. We can get that ball rolling by creating a private
member field in our Game1.cs class as follows:
private Model model;
Now we can initialize this variable by actually loading our model in our code. We do this
inside of the LoadGraphicsContent method. The following code has the entire method:
116 CHAPTER 6 Loading and Texturing 3D Objects
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
// TODO: Load any ResourceManagementMode.Automatic content
model = content.Load(@”Content\Models\asteroid1”);
}
// TODO: Load any ResourceManagementMode.Manual content
}
The only line we added was the statement inside of the condition, but it is important to
discuss this entire method, as we have just breezed over it in the past chapters. This
method is where we will load all of our graphics content. We have two options when we
load our content: We can let XNA handle the memory management of the resources automatically
by loading our content inside of the condition or we can maintain the memory
management ourselves. This method as well as its counterpart, UnloadGraphicsContent,
gets called at appropriate times. What are these appropriate times? This is a good time to
discuss the logic flow of XNA.
The XNA Framework’s logic flow works something like this:
1. The Main application calls the Game Constructor.
2. The Game Constructor will create any game components and call their constructors.
3. The XNA Framework calls the game’s Initialize method.
4. The XNA Framework calls each of the game component’s Initialize methods.
5. The XNA Framework calls each of the Drawable game component’s
LoadGraphicsContent methods.
6. The XNA Framework calls the game’s LoadGraphicsContent method.
7. The XNA Framework calls the game’s Update method.
8. The XNA Framework calls each of the game component’s Update methods.
9. The XNA Framework calls the game’s Draw method.
10. The XNA Framework calls each of the Drawable game component’s Draw methods.
11. Steps 7 through 10 are repeated many times each second.
12. If the device is lost (user moved the window to another monitor, screen resolution
is changed, window is minimized, etc.) then a call to UnloadGraphicsContent is
made.
13. If the device is reset then we start back at step 6 again.
14. The gamer exits the game.
15. The XNA Framework calls the game’s Dispose method.
Loading 3D Models 117
6
16. The game’s Dispose method calls the base object’s Dispose method, which causes …
17. The XNA Framework calls each of the game component’s Dispose methods.
18. The XNA Framework calls the game’s UnloadGraphicsContent method.
19. The game’s Dispose method gets focus back and the game exits.
Something to note about how the XNA Framework calls the game component’s
Initialize method (and LoadGraphicsContent for drawable game components) is this
only happens once when the game’s Initialize method is kicked off by the framework.
If game components are added later, their Initialize (and LoadGraphicsContent)
methods will not be called. This is important to understand when managing game
components.
At this point we have loaded our 3D content. We have created a private member field to
store the model we added. We actually loaded our model in our code and initialized our
variable. Now we just need to draw the model on the screen. We need to add the following
method to draw a model:
private void DrawModel(ref Model m, ref Matrix world)
{
Matrix[] transforms = new Matrix[m.Bones.Count];
m.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in m.Meshes)
{
foreach (BasicEffect be in mesh.Effects)
{
be.EnableDefaultLighting();
be.Projection = camera.Projection;
be.View = camera.View;
be.World = world * mesh.ParentBone.Transform;
}
mesh.Draw();
}
}
Our DrawModel method takes in a reference to our model as well as a reference to the world
matrix we need to apply to our model. The first two statements get the transforms of each
bone in the model. A model can have a parent bone with children bones associated with
it. This is mainly used in animations, but even if a model does not have animations it
might still have bones and so our code should include this unless we know for certain that
our model does not have any children. The transforms array will contain a transformation
matrix of each mesh of the model that contains its position relative to the parent. By
doing this, we can make sure that each ModelMesh of the parent Model will be drawn at the
right location. This actually happens in the last statement inside of our inner foreach loop.
118 CHAPTER 6 Loading and Texturing 3D Objects
We take the world matrix that the mesh is supposed to be transformed with and we multiply
that transformation with the ModelMesh’s parent’s transformation. This is to make sure
that that each ModelMesh is drawn correctly with the parent mesh.
We did not need to create our own BasicEffect object as XNA will apply one to a mesh
we load. We can override this and will see that in Part V of the book when we talk about
the High Level Shader Language and make our own effect files. For now, we just ensure
that the default lighting is enabled on the effect as well as setting our projection and view
matrices to what our Camera game component has updated it to be. When we did this in
the last chapter, we only moved where we are setting those values. Instead of having an
effect that we are explicitly calling, we are tying into the one that XNA applies to the
Model when it loads it. This is also happening inside of the inner foreach loop so we
could apply different effects to children meshes if we wanted to.
The other thing to notice is the fact that we did not reference our texture anywhere. Because
it was inside of the .X file and the Content Compiler stored that information, the Content
Manager knew where to look for the texture and load it automatically. We will see how to
override the texture through code a little later in this chapter. Let’s just get it to draw with
the normal texture for now! To do that, we need to actually call our DrawModel method
inside of our Draw method with the following code:
world = Matrix.CreateTranslation(new Vector3(0,0,-4000));
DrawModel(ref model, ref world);
The model we are loading is rather large and as such we are going to push it way back
into the world. In fact, we made a modification to our Camera class in our XELibrary. We
originally had our near and far planes set up as 0.0001 and 1000.0, respectively. The thing
about the plane values is that they are floating points, which means there is a finite
amount of precision we can have. We can either have the precision before the decimal
point or after the decimal point, but not both. A lot of times programmers will set the far
plane to a very high number but when that happens the depth buffer (also known as a z
buffer) could have a hard time knowing which objects to draw first. As a result during
game play the screen will almost flicker as different vertices are fighting to be drawn first
and the z buffer is confused because it cannot take into account the minor differences in
the locations of those vertices. Of course, the .0001 near plan we originally had set was
not exactly practical either. At this point having a near plane of 1 and a far plane of
10,000 should meet most of our needs without overly stressing our z buffer. We could
have our far plane even further without causing an adverse effect on the depth buffer.
Finally, we need to create our private member world field that we referenced:
private Matrix world;
Now we can compile and run our code and we should see our asteroid object sitting right
in front of us. This is not extremely exciting, but we have just successfully drawn a .X
model complete with a texture.

1 komentar:

Blogger mengatakan...

According to the National Association of Unclaimed Property Administrators, 1 out of 8 people in the U.S. have unclaimed assets... With claims averaging claims of more than $1,000!

Find Federal & State Unclaimed Balances!