Loading
Pre-Created Objects
Author
:
Jack Hoxley
Written : 22nd December 2000
Contact : [Web]
[Email]
Download : Graph_08.Zip
[209 Kb]
Contents
of this lesson:
1. Introduction
2. Making 3D Objects
3. Loading a pre-created object
4. Rendering our object
1.
Introduction
Up
till this point we've been manually generating any geometry that we need - squares
and cubes mostly. You may or may not have given any thought to how commercial
/ proper games work when generating animation and complex 3D models. It doesn't
take a genius to realise that these models are not hard-coded into the game
- doing that would require 1000's of lines for each frame of an animation -
not a very clever way of doing things.
Instead
we load objects in that have been created in another program, usually by a 3D
artist. We read the file, load any relevent textures and materials and hold
it in memory - until we want to render it. There is another advantage as well
- you can edit the file after the final compilation of the executable. If you
decide that you want to lower the triangle count a bit you can open up the file,
play around with it and then save it - and the effects will be mirrored in the
game the next time you play it. You can also have different versions of a model
- low detail, normal detail and high detail - so you can allow the user to play
the game on a low end computer, but if they have performance hardware they'll
be able to get something a little bit more special...
From now onwards we'll be using externally created objects rather than generating
our own hard-coded geometry. Read the next section on how to create your own
geometry.
2.
Making 3D Objects
The
first thing to remember is that you'll need a basic knowledge of how 3D models
work - if you've read all the previous lessons then you should have a reasonable
idea; but if you still dont get it I suggest you go looking around the internet
for some basic 3D-modelling tutorials (It's too bigger a topic to cover here).
Secondly
you're going to need some software. There are hundreds of cheap / free 3D modellers
around, but if you're serious about making good models you'd be wise to look
into one of the more professional packages (such as 3D Studio Max or Lightwave).
Or, if you have no 3D-modelling skills you could go looking for an artist who's
good at 3D work - and hope he/she has the relevent software.
Thirdly
you'll be needing a convertor. Direct3D uses it's own format ".X"
files - there is a convertor included in the SDK for converting ".3DS"
files to ".X" files, but you'll need to get it from the microsoft
site (I do believe it would infringe on the EULA agreement if I uploaded it
here...).
Whilst
there are many other things to bare in mind, the last important one for the
programmer is the model complexity. Depending on how your engine is designed
you'll need to impose some realistic limits on the models that can be used.
For example, if you're aiming for a middle-range specification (Low end 3D card
and reasonable processor) you'll probably be wanting to keep the triangle count
in each scene to around 3000 (give or take a litte); and if your game has a
landscape in it that takes up 1500 of those triangles you have to find a way
of fitting all your players and/or world objects into the remaining 1500 triangles...
3.
Loading a pre-created object
Thankfullly,
the very basics of loading an object into Direct3D is quite simple; it's only
when you get onto animation and skinning it gets complicated. As mentioned above
we need an object, which I've done - and you can see in the sample file. All
it is is a sphere with "DirectX 4 VB" rotating around and around...
I made it in 3DSMax 2, then used the SDK convertor (Conv3ds.exe) to convert
it into an X-File. If you have this utility you should use this command line:
"Conv3DS -m -V2 file.3ds", the "-V2" part is optional, but
it provides you with some basic information, you can add other parameters, but
you must remember to keep the "-m" in there.
So
now we have an object to play around with. I'm going to rewrite a part of the
"Lesson 07 : An introduction to Lighting" to use objects - partly
because the lighting is already setup in there; it's also much easier to see
the affect of lighting in this example than in the proper example (because of
the more complex geometry). Here's the new code to go in the declarations section,
note that we no longer have to play around with custom vertex formats (or vertices
full stop.).
'###########
'##
MESH
##
'##########
Dim Mesh As D3DXMesh
'##############################
'## TEXTURES AND MATERIALS ##
'##############################
Dim MeshMaterials() As D3DMATERIAL8 ' Mesh Material data
Dim MeshTextures() As Direct3DTexture8 ' Mesh Textures
Dim nMaterials As Long 'How may materials/textures we have.... |
|
|
|
The
"Mesh" object is what we're going to use to hold all our information,
we'll be using the D3DX library to help us as well (note the "D3DX"
prefix to the variable name). Then there are the materials and textures - both
open ended arrays; After we've loaded the object into memory we'll query it
as to how many textures and materials there are and resize these arrays accordingly.
The
next part is a complete re-write of the "InitGeometry( )" routine,
everything except the error handler has been changed in this procedure - so
here goes:
Private
Function
InitialiseGeometry()
As
Boolean
On
Error
GoTo
BailOut: '//Setup our Error handler
'//0. Any variables required
Dim
mtrlBuffer
As
D3DXBuffer '//Holds some useful data for us...
Dim I
As
Long 'Loop variable
Dim
TextureFile
As
String 'the texture required
'//1. Get the data from the file
Set
Mesh =
D3DX.LoadMeshFromX(App.Path
&
"\lesson08.x",
D3DXMESH_MANAGED,
D3DDevice,
Nothing,
mtrlBuffer,
nMaterials)
If
Mesh
Is
Nothing
Then
GoTo
BailOut: '//Dont continue if the above call did not work
'//2. Allocate the space required for the materials and textures used:
ReDim
MeshMaterials(nMaterials)
As
D3DMATERIAL8
ReDim
MeshTextures(nMaterials)
As
Direct3DTexture8
'//3. Now we fill our arrays with the information required
For I
= 0 To
nMaterials
- 1
'//Get D3DX to copy the data that we loaded from the file into our structure
D3DX.BufferGetMaterial
mtrlBuffer,
I,
MeshMaterials(I)
'//Fill in the missing gaps - the Ambient properties
MeshMaterials(I).Ambient
=
MeshMaterials(I).diffuse
'//get the name of the
texture used for this part of the mesh
TextureFile
=
D3DX.BufferGetTextureName(mtrlBuffer,
I)
'//Now create the texture
If
TextureFile
<>
""
Then 'Dont try to create a texture from an empty string
Set
MeshTextures(I)
=
D3DX.CreateTextureFromFileEx(D3DDevice,
App.Path
&
"\"
&
TextureFile,
128,
128,
D3DX_DEFAULT,
_
0,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_FILTER_LINEAR,
_
D3DX_FILTER_LINEAR,
0,
ByVal
0,
ByVal
0)
End If
Next I
'//4. Output some info to the debug window - not required, just interesting
Debug.Print
"Number
of
Faces
in
mesh:
"
&
Mesh.GetNumFaces
Debug.Print
"Number
of
Vertices
in
mesh:
"
&
Mesh.GetNumVertices
InitialiseGeometry
= True
Exit
Function
BailOut:
Debug.Print
"Error
occured
in
InitGeometry()
Code"
InitialiseGeometry
=
False
End
Function |
|
|
|
It's
all fairly explanatory - and as D3DX does most of the hard work we can sit back
and do very little. The last part of (3) can be changed slightly to suit your
needs - where this example uses CreateTextureFromFileEx( ) you could either
rework it to use different sizes, transparencies and so on, or you could replace
it with the old CreateTextureFromFile( ) method - if you really dont know much
about the texture, or just plain dont care.
At
this point in time we have a valid object in memory - something we can now play
with.
4.
Rendering our object
Rendering
our object isn't greatly difficult either. Although you still need to manage
any matrices and transformations that you want. An interesting thing to note
is how the lighting affects the frame rate; as mentioned in the previous lesson,
different lights have different costs - this was quite difficult to see when
we were using such a small geometry set (8 vertices), but as we're now using
100's you'll notice the difference much more. For example, on my computer I
get 170fps for Directional Lights, 160fps for point lights and 145fps for spot
lights - a definate trend downwards there.
As
you've already noticed (I hope) we've loaded our object as parts - we have a
number of materials and textures, whilst you haven't seen it yet, the mesh is
divided into a certain number of parts (1 for each texture/material combination).
As we're going to render these in a loop it is perfectly possible to apply different
transformations to each section - if you know which section is which (trial
and error really - or you could write a configuration file format). Essentially,
therefore, you could have every object in your game in one massive file - as
long as you knew which order they came in - but this makes for difficult editing
later on.
For
I = 0
To
nMaterials
- 1
'//Setup the renderer for this part of the mesh
D3DDevice.SetMaterial
MeshMaterials(I)
D3DDevice.SetTexture
0,
MeshTextures(I)
'//Draw the current part of the mesh
Mesh.DrawSubset
I
Next I |
|
|
|
Not
greatly complicated really - just remember that this part must go between a
Device.BeginScene and Device.EndScene call. Hopefully you should
see something like this:
With the Green SpotLight
With the Blue point light
With the top-down white directional light
You
can download the source code for this lesson from the top of the page, which
i suggest you do...
After
you've got that all sorted, onto Lesson 09 : Advanced
Geometry Part 2 - Using Index Buffers to store Geometry
|