3D Engine Construction and Design Workshop: Lesson 2 – Getting out of Console

The rudimentary engine skeleton structure, interfacing with both DirectX and Window’s API, starting a game loop, as well as an in depth discussion on individual engine components.

There is one more step we left unconfigured from the last lesson, but
it’s not a terribly long one. Since we are using the express version of
Visual C++ we’re going to need to setup the Platform SDK.

You can
follow along their instructions 1, 2, 3 and 4, but we won’t need to do
step 5. Basically you need to download the platform SDK, then like after
we installed DirectX go to the Options menu, and add folders to the
executable, include, and library directories (corresponding to the files
we just installed.) Lastly, we update a small file
“corewin_express.vsprops” in our Visual Studio 8/VC/vProjectDefaults/
folder to include a few more libraries. It’s pretty self explanatory,
the instructions are linked above and you can contact me if you need
more information.


NOTE: A lot of the functionality we’ll be dealing with today you can
look at as a black box, you don’t need to know how it works you can just
use it and focus on the rest of the engine. In fact, this source code I
only wrote once, and have built every 3d engine off of it since. You
should probably read it, but don’t stress too much over the specifics.
If you REALLY have the desire to know (I know I would, I hate people
telling me not to worry about it) I recommend reading Riemer’s Tutorial
on
this same subject. He also wrote it in C#

for those of you who are just having trouble with the language barrier.

Introduction


After all the hard work getting Visual C++ and DirectX installed, it’s
time to actually get down to programming. In this lesson, we will be
building the skeleton of our engine class and having it interface with
windows for us. By the end of this lesson you will have initialized
DirectX and windows, and be ready for the next lesson where we will
begin rendering things to the screen. Believe it or not after this
lesson everything gets a lot easier. The learning curve is sharp, and
since I’m trying to quickly run through this initialization stuff I’m
not taking as much time as I should explaining it. However, it’s
important to remind you that this workshop is about engine design, so
the specific in’s and outs are beyond the scope of our purpose.


Unlike the last lesson, this time I’m going to intentionally move fast
and throw a lot of information at you. You are encouraged to ask
questions and do individual research for the topics that you don’t
understand. Unlike a full fledged tutorial, these workshops are meant to
expose you as quickly as possible to the subject. I will do my best to
provide links to other sources for more information where I can.


I recommend you download the source code first, and have it open infront
of you as you follow along. I will not be going through line by line.

Engine.h

Lets dive right in and open up “Engine.h” where the magic happens:


#pragma once


#include


#include


#include


#include


#include


#include “consts.h”


#pragma comment(lib, “d3dx9.lib”)


#pragma comment(lib, “d3d9.lib”)


#pragma comment(lib, “Winmm.lib”)


LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );


namespace DistortedStudios


{


class cEngine


{


public:


cEngine( );


~cEngine( );


unsigned int flags;


int Initialize( HINSTANCE hInstance, int width, int height, bool windowed);


int BeginMessageLoop( );


void Render( float timeDelta );


IDirect3DDevice9* getDevice( )
{ return pd3dDevice; }


private:


IDirect3DDevice9* pd3dDevice;


HWND winHandle;


};


}

Lemma 2.1 – Pointer Arrays are Bad


If you’ve cruised around any C++ forum you’ll undoubtedly run into the
old dinosaur of a coder who refuses to use STL containers like string
and vector. Instead he’ll still be using the age old technique of
building pointer arrays. If you don’t know what pointers are, bug
Churroe about it because that’s his department. But I’ll give you a
basic idea: You allocate sequential memory blocks and then build an
array by physically manipulating the data in memory. The problem with
this is, it leads to memory leaks – ie – situations where you reserve
memory for your data and don’t release it. It also causes a lot of
problems managing the data. Now, the point of me telling you this is
that you /should very rarely ever deal with pointer arrays/. STL
containers are /not/ slower, in fact – in certain situations they are
faster. They do not cause you to over allocate and waste memory (they
manage thier own memory) and they will never cause memory leaks (unless
you do something crazy with them). So no matter what people tell you:
STL is your friend – use it .


The include statements are pretty self explanitory. We’re going to be
using windows.h header files to interface with windows, d3d9 are the
primary DirectX includes, and d3dx9 are some helper functions and
classes, (these will be discussed more at length in a later lesson). We
are also using the STL (standard template library) classes String and
Vector. While we won’t actually be using either yet, it’s there to
remind us that, unlike regular C, we will not be using dynamically
allocated char arrays for strings, nor pointer array’s for data storage.
If you don’t know what I’m talking about, that’s fine. Because it means
that you can’t mess it up (^_~).


There are some other lines of code in here that you may not be familiar
with (or are familiar with but don’t know what it means exactly, or have
used but in a different way.) Like #pragma. Pragma is a precompiler
directive, basically, it’s a way of communicating with your compiler as
it does it’s thing on your code. You’re able to give it certain
instructions this way, to effect the final result of the code. For those
of you who used an older version of C++, or have seen C++ code before
you might remember those crazy #define directives. #pragma once operates
the same as #ifndef FILENAME #define FILENAME #endif. It’s just
shorthand for “this file will only get included once.” The reason this
is important is that by practicing modularity we can code ourselves into
trouble. This component needs to include some other file, which is
including a third file, which needs to include the first file. If a file
is included twice, then you get “redefinition” compile time errors.
Pragma once allows us to put #include THISFILE in as many headers as we
want, because the compiler will make sure to only include it once.


You’ll also see the #pragma comment command. This is just me being lazy.
Remember all the setting up we did before setting those include paths
and library paths? Well, this is just telling the compiler to do some
including without us bothering to deal with all that. Depending on how
you set up, you may or may not need these. I put them in anyway, because
precompiler directives are removed before you compile so they don’t do
any harm.

Lemma 2.2 – Namespaces


Some of you coming from C# may be familiar with namespaces, although in
C++ you won’t see them as often as you do in C#. The reason we use
namespaces is to prevent naming conflicts throughout our code. For
example, we may use another library which has a class named “cEngine”.
Before namespaces, programmers had adopted a simple naming convention,
and we would be required to name things more descriptively, ie.
cDistortedStudiosEngine. With namespaces we can write simply cEngine
inside our namespace and it will protect it from being confused with
anybody else. We would access it with the :: operator, ie.
DistortedStudios::cEngine( ).


For more information take a look at The CPP tutorial.


Similarly I believe a few of you are coming from the land of C#. I don’t
know much about it, to be honest (if one of you wants to do a C#
workshop I’d love to participate) but I do notice that you guys just
abuse the hell out of namespace (^o^). We’ll be using it a lot more
sparingly, and only because this is a /design/ workshop. I generally
don’t even bother, but it’s good programming practice to do it.


Finally we get to our engine code. More specifically our header, or
class prototype. (Let’s ignore that funny looking function called
WinProc and the other custom include “consts.h” for now). We have a
constructor and destructor (standard fare in any object oriented
language). Every time an object of this class is created, the constructor
is called. We use it to initialize any variables, specifically pointers
(because pointers sometimes point to strange things if you don’t set
them to NULL right away!) Similarly the destructor is called when our
class is destroyed. Back when we used pointer arrays this was where we
would delete them. We still find use for them though, even though we’ve
evolved beyond that primitive state!


The unsigned int is used for flags. What are flags? We aren’t realy sure
(O.o)a What I mean is, we use them whenever we need them, they are
pretty all purpose, to pass data around. For example sometimes I use
them in collision detection to decide which objects have been checked
against who (so we don’t check for the same collision twice). Or I could
use it to pass information from one class to another class. How do they
work? Check the lemma because while it isn’t really important, I think
it’s cool – especially we get to use the holy grail of nerd-dom: binary
operations!

Lemma 2.3 – Bitwise Badass


Generally I use the flags data member to store binary data to represent
different things. It’s hard to explain, but let me try and give you an
example. In our everyday lives most of us generally work in a decimal
system, that is deci (10) mal (meaurement). 0-9 are our digits, and each
space represents the next order. so: 124, means (4 x10^0) = 4 + (2×10^1)
= 20 + (1×10^2) = 100 = 124. Similarly binary is base 2, so 1010 means:
(0×2^0) = 0 + (1×2^1) = 2 + (0×2^3) = 0 + (1×2^4) = 8 = 2 + 8 = 10. Now
if you notice, since there are only two states any position can have (on
or off, if you will) we can easily test this for flags.


Lets say 1 = FLAGA (Binary 0001), 2 = FLAGB (Binary 0010), 4 = FLAGC
(Binary 0100), and 8 = FLAGD ( Binary 1000). See what’s happening? The
binary number 1010 means: Flag D + Flag B. We test this by using the
bitwise operators OR and AND. I won’t go into this now, as you’ll see
later how it’s done (^.^).


The rest of this class is pretty easy to understand. The initialize
function just sets everything up, Initializes DirectX and builds a
window for us to render to. It takes a title for the window, the height
and width, an HINSTANCE which will be given to us by our windows main
function, and a boolean deciding whether or not we want it to be
windowed or not, as parameters. Implementation of this function is
probably the most difficult to swallow, and I won’t really explain it a
lot. Read Reimer’s explanation
if
you really want to know.


The BeginMessageLoop function contains our main game loop. Basically, we
run through a frame, check if window’s has sent us any messages, decide
what to do with that message, and start over again. This loop is where
that funny WinProc function comes in. This function is a Windows
Callback function. What that means, basically, is it’s a function
designed by windows, but we write the code for it. Whenever windoes
sends a message to us, it sends it to be processed by our WinProc
method. In the main.cpp you can see that we don’t do very much with most
messages: other than polling for escape to close our program – we just
let window’s default message handler take care of it for us.


The render function, ah! If you look at the implementation it’s actually
nothing at all. It just clears our screen black, starts and stops our
drawing sequence, (we’re not drawing anything yet but if we were it
would go in there) and then presents the screen. This is actually a
little important, so I won’t even put it in a lemma. Any graphics API
makes use of something called “back buffering” which is just a fancy way
of saying “we draw on one surface while showing the user another one”.
This prevents the screen from flickering, which would happen if we
actually watched the engine draw line by line to the screen. Instead it
creates a 2d photo of the scene, puts it on a surface, and shows that to
the user while it’s drawing on another one. The present function, also
known as “flipping” is what actually switches the two surfaces.


So now there are just two more little things to discuss before we can
wrap this lesson up. I’ll go with our main.cpp. As you can see, this
looks a whole lot different than the plain “int main()” function we’re
used to using in C++. This is a special main function exposed by the
windows API, and we don’t have to worry too much about all the fancy
stuff it gives us. As you can see from the main function itself, it’s
pretty bare. Guess what? It doesn’t get any more complicated either.
Remember once we call BeginMessageLoop( ) the engine takes over and
doesn’t return to this main function until the engine has shut down! So
the only thing we could possibly do here is test the return values, and
maybe see if the engine crashed and do some error checking. Otherwise,
the rest of our code will be performed by the engine, and all her
wonderful parts.


Last, but not least is that little header file called “consts”. I can’t
claim that this is “proper” engine design since as far as I know I’m
really the only person who does it. But I think it keeps my code neat
and clean. I use the consts header file to hold all the (you guessed it)
constant variables, debug functions, and flag properties in the game.
It’s important that NO CUSTOM HEADERS are included in the const file,
because the const file will be used by EVERY OTHER HEADER. Even pragma
once won’t save us from compile errors if we cyclicly define things this
way! I also use the consts header to put little helper functions that
might be useful to other classes (like I have a custom int to string
function, since I don’t like the itoa function). Right now const only
has a few return values (when we initialize instead of having it return
1; which doesnt mean much to someone else working on the code, I have it
return RF_CLASS_NOT_REGISTER, which means “Return: Failed – Couldn’t
Register the Class”. This is just for ease of debugging. Next lesson
we’ll be expanding this with some DEBUG macros to help us narrow down
any errors we have.


Alright, you still with me? This is probably going to be the worst
lesson you have to go through (worst writing most certainly) so I
apologize, but you should be proud! You made it through, and we’re
almost at the fun and interesting stuff. Other than the math primer
(that’s painful to me, I don’t know if you guys are good at linear
algebra stuff) all that’s left is just plain simple! So pat yourself on
the back, have a cold beer. It gets better from here.


I’m sorry I couldn’t go as in depth as you may have liked, but like I
said. I really just wanted to get started on the actual purpose of this
workshop. Which we may not actually start with for a little while
longer, since I think the next few lessons will cover our graphics
engine and the mathematics involved.


Until next time, have code will travel.

Leave Your Response