======================================================================================================
                                  Guardian Soft DirectX Library
======================================================================================================
Programmed by: Antonio Maiorano
Compiled with: Microsoft Visual C++ 6.0



======================================================================================================
                  Graphical View of Guardian Soft DirectX Library Files and Objects
======================================================================================================
Files:

                                  gs_common                            
                                      |
                                  gs_debug
                        ______________|_______________
                       |              |               |
            gs_directdraw      gs_directinput  gs_directmusic  gs_winstandard  gs_timer
            gs_surface         gs_keyboard     gs_directsound          |          |
            gs_font                   |               |                |          |
            gs_deviceenumerator       |               |                |          |
                       |______________|_______________|________________|__________|
                                      |
                                  gs_helper
                                      |
                                    gs_lib
                                      |
                                your application



Objects:

Level 1:   DirectDrawObject  DirectInputObject  DirectSoundObject  WindowObject  TimerObject
                   |                   |                  |
                   |                   |                  |
Level 2:      SurfaceObject      KeyboardObject   DirectMusicObject


*NOTE*: Objects on Level 1 must be created before objects on Level 2.




======================================================================================================
                   How to set up your project in Microsoft Developer Studio
======================================================================================================
Setting up your project in Developer Studio is easy, and can save you a lot of time. Follow the
simple set of instructions below and you'll be able to use the GS DirectX Library in no time!

1) Place the library's file structure in a convenient location where it should never have to change
   place (i.e. where you installed Visual Studio).

2) Open Developer Studio and create a new project. To do this, click on "File->New...", select the
   "Projects" tab, select "Win32 Application", set the location of the project in the "Location" text
   box, and the project name in the "Project Name" text box.

   Note that the Project Name will also be the subdirectory in the Location you specified.
   As an example, let's say we choose "C:\Programming\Games" as our Location, and "TestDX" as our
   Project Name, then the project will be in "C:\Programming\Games\TestDX". This example will be
   used to explain the next steps.

2) Now we need to set up the development environment to use the library. This is a four step process:

  2.1) We need to let Developer Studio know where the header and library files are located so that
       it compiles properly. To do this, select "Tools->Options..." and select "Directories" tab,
       and for "Include files", add the path to the "include" folder of the library. Then for
       "Library files", add the path to the "lib" folder. Note that this step only needs to be done
       once in Developer Studio.

  2.2) There are two different types of lib files - one for Debug and one for Release. We need to
       set up the project to compile with the correct version of the lib file. To to this, select
	   "Project->Settings...", make sure your project is selected in the left-most window, select
	   the "Link" tab, and then for "Win32 Debug" add "gsdxlib_debug.lib" to the "Object/library
	   modules" edit box. For "Win32 Release" add "gsdxlib.lib" to the edit box.

  2.3) Because of MFC compatibility, we need to change the project settings so that it compiles with
       the multi-threaded DLL libraries in Debug. Select "Project->Settings...", select the "C/C++"
	   tab, select "Code Generation" from the "Category" pull-down menu. Now you need to make sure
	   your specific project is selected in the left window and select "Win32 Debug" from the
	   pull-down menu above this window. From the "Use run-time library" pull-down, select
	   "Debug Multithreaded DLL".

3) You should create a directory in your Project folder called "Code" where you will save your
   code. Now create a .cpp file where you will put your WinMain() (i.e. "Winmain.cpp").  Save the
   file into your Code directory and add it to your project.

That's it! Now you can use all the classes and methods available in the library. For an example of
how to use the library, refer to the TestDX demo program that comes with this libary, in the
"GS DirectX Library\samples\TestDX" directory.

Happy coding!


======================================================================================================
					How to use the library
======================================================================================================

Rules of thumb:

1) Make sure that you are ALWAYS drawing to the Primary Surface (SCREEN), especially in Windowed
   mode, otherwise Windows will eventually call a repaint on the window, and you'll see the actual
   white window instead of the image you copied to the Primary Surface. This means your "idle loops"
   are not really idle - they must be idle and drawing loops.


==========
Singletons
==========

The way the library is designed is based for the most part on the idea of "singletons". As the name
implies, there can only be one instance of a singleton object in an entire application. Typical
examples of singletons are the keyboard or mouse on a system, or a file manager in an operating system.
In this library, the singleton objects are the following:

DirectDrawObject
PrimarySurfaceObject
DirectInputObject
KeyboardObject
DirectSoundObject
DirectMusicObject
MouseObject

So what does this all mean? How does this change the way I use these objects? Simple - the constructors
for these objects are not public, so you cannot do this:

DirectDrawObject DDrawObject;

The compiler will not allow this because it cannot construct this object. So how do you get access to
this object? Simple, you use a special member function called "Instance()" to retrieve the single
instance of this object as follows:

DirectDrawObject *pDDrawObject = DirectDrawObject::Instance();

This may seem more complicated, but there are many advantages to using singeltons:

- There is no way you can have more than one instance of a singleton object
- You don't need to keep global variables nor pass a singleton to functions because you can
  always use the "Instance()" function anywhere to retrieve it.

The only disadvantage to using singletons is that their destructors are not automatically called, so
to properly destroy them, you should call the "DestroyInstance()" member function. Of course, even
of you don't, the OS will clean up the unallocated memory, so this isn't a huge problem.

That's it! So now you know what singletons are and how to use them. The sample code shows how to
use them in more detail. After a while, you will become very comfortable with singletons so don't
worry about it if it seems complicated.


===============
Copy Rectangles
===============

One of the aspects of this library that makes it unique is the way you must define "copy rectangles"
when copying from one surface to another. This section explains how this works, and gives some common
examples.

To begin, let's define two surfaces:

SurfaceObject S; // The Source surface
SurfaceObject D; // The Destination surface

The size of each surface will be different in each example I give. I will use the CreateSurface()
function to show you the size in the examples.

Now, there are 3 "copy rectangle" functions defined in the SurfaceObject class:

void SetRect(int x, int y);
void SetRect(int x, int y, int x2, int y2);
void SetRectSize(int x, int y, int width, int height);
void ResetRect(void);

Let's ignore the first one for now. The second and third functions are very similar. They both
define the area on the surface that we call the "copy rectangle". This is the area that will
be copied to another surface. The second function, SetRect(), let's you define the area with
absolute values - in other words, you give the coordinates for the upper-left and lower-right
corners. The third function, SetRectSize(), let's you define the area using the upper-left corner,
and the width and height offset from this corner.

*IMPORANT: The width and height are "inclusive", this means that if you call 
SetRectSize(0, 0, 640, 480);, this is the same as calling SetRect(0, 0, 639, 479);

The fourth function, ResetRect(), simply makes the copy rectangle equal to the size of the surface.


It doesn't matter which one you use, both do the same thing. In certain situations, you may find
that one function is easier to use than the other. In most cases, I believe SetRectSize() will be
the most useful.

Now here's an example of how to copy from one surface to another.

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 1:

S.Create(100, 100);
D.Create(640, 480);

// Copy all of S to D at offset (50, 50) on D
S.ResetRect(); // Make copy rectangle equal to the entire surface size
D.SetRectSize(50, 50, 100, 100); // Set the destination copy rectangle
D.Copy(S); // Copy from S onto D

////////////////////////////////////////////////////////////////////////////////

Ok, let's look at the code now. First we create the two surfaces. We assume there's data on the
surfaces. So first we call ResetRect() on S to make the copy rectangle as large as the entire
surface. Then we set the destination copy rectangle by calling SetRectSize(50, 50, 100, 100) on D.
Now that both copy rectangles are set up, we can call Copy().

Note that when we set the destination rectangle, we included the size of the source rectangle in
order to match the two rectangles. Actually, this step is not only tedious, but unnecessary. The
library has been programmed so that the destination's copy rectangle will be automatically adjusted
to match the size of the source's copy rectangle whenever any of the Copy() functions are called
(execpt for the Copy_Scale() functions, which will discussed later). So that is why there is a version
of SetRect() that only takes the upper-left corner as parameters (that first function).

So here's the above program, but using that first SetRect() function. This one is both easier to
understand and to code.

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 2:

S.Create(100, 100);
D.Create(640, 480);

// Copy all of S to D at offset (50, 50) on D
S.ResetRect(); // Make copy rectangle equal to the entire surface size
D.SetRect(50, 50); // Set the destination offset
D.Copy(S); // Copy from S onto D

////////////////////////////////////////////////////////////////////////////////

Now I will present some more examples.

The following example demonstrates how to copy a certain section of the source surface
to the certain location on the destination surface. Our source surface (S) is a 100x100
surface, and we wish to copy a 10x10 portion of it to the destination surface (D) at offset
(100,100).

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 3:

S.Create(100, 100);
D.Create(640, 480);

S.SetRectSize(20, 30, 10, 10); // Copy a 10x10 portion of S at offset (20,30)
D.SetRect(100, 100);
D.Copy(S);
////////////////////////////////////////////////////////////////////////////////

There you go! Hopefully you're getting the hang of it by now. An important point to notice
is that, as stated before, when you copy to a destination surface, the destination's copy rectangle
will adjust to match the size of the source's copy rectangle (except when calling the Copy_Scale()
suite of functions). If this fact is forgotten, strange side effects may occur.

Here's an example of where a side-effect would occur. Here we go into a loop where we want to clear
the D surface to black, then copy S to it at a specific location.

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 4:

S.Create(100, 100);
D.Create(640, 480);

S.ResetRect(); // We will always copy all of S to D
D.SetRect(100, 100); // Always copy to D at (100, 100);
D.Copy(S); // Copy once from S to D

while (1)
{
	D.Fill(0); // Fill up D with black
	D.Copy(S);

	// DisplaySurface() is a user-defined function that displays D to the screen
	DisplaySurface(D);
}
////////////////////////////////////////////////////////////////////////////////

The code may look correct at first glance. We set up the copy rectangles, copy from S
to D, then enter a loop where we fill D with black, and copy S back to D. So what's the
problem? Well, the copy rectangle for D has the same SIZE as the copy rect for S. In other
words, it's 100x100 in size. So when D.Fill(0) is called, only that 100x100 portion will be
filled with black. The correct way to do this is as follows:

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 5:

S.Create(100, 100);
D.Create(640, 480);

S.ResetRect(); // We will always copy all of S to D
D.Copy(S); // Copy once from S to D

while (1)
{
	D.ResetRect(); // We want to fill the entire surface with black
	D.Fill(0); // Fill up D with black

	D.SetRect(100, 100); // Always copy to D at (100, 100);
	D.Copy(S);

	// DisplaySurface() is a user-defined function that displays D to the screen
	DisplaySurface(D);
}
////////////////////////////////////////////////////////////////////////////////

All we did was add two lines to the loop, and remove one from before the loop. Basically,
we want to define the copy rect of D before we fill it, and before we copy from S to it.
From these two examples, you can see that it is important to keep in mind what the current
copy rectangles are when calling functions on the surfaces; otherwise, strange side-effects
may occur.


==============
Tiled Surfaces
==============

Now we come to a topic that is a personal favourite of mine. When designing the lib, I kept in
mind that it would be used to make tile-based games. As such, I decided that common tile-based
functionality should be inherent to the library. For instance, the copying of fixed-size tiles
from one surface to another is one such common functionality, and this is captured in the library
with the use of "tiled surfaces".

The idea is very simple. Say you have a bitmap containing 100x100 tiles, each 16x16 in size. You
would load this bitmap onto a surface of size (100x16)x(100x16) or 1600x1600, and "tile" the surface
into 16x16 tiles. The following snippet of code shows how to do this:

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 1:

SurfaceObject S;
S.Create(100*16, 100*16);
S.LoadBMP("tiles.bmp");
S.SetTiled(16, 16);
////////////////////////////////////////////////////////////////////////////////

Now S is a "tiled" surface. What does this mean? Simply, it means that each 16x16 tile on the surface
can now be identified by a numerical identifier. The first tile in the upper-left corner is always '0',
the one on it's right is '1', and so on. In our example, the tiles are numbered as follows:

0,1,2,...,98,99
100,101,...198,199
...
800,801,...898,899
900,901,...998,999

If this seems complicated, let's use a simpler example. Assume we have 10x5 tiles in a bitmap,
each 32x32. The code for this is as follows:

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 2:

SurfaceObject S;
S.Create(10*32, 5*32);
S.LoadBMP("tiles2.bmp");
S.SetTiled(32, 32);
////////////////////////////////////////////////////////////////////////////////

The tiles are now identified as follows:

 0,  1,  2,  3,  4,  5,  6,  7,  8,  9
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
20, 21, 22, 23, 24, 25, 26, 27, 28, 29
30, 31, 32, 33, 34, 35, 36, 37, 38, 39
40, 41, 42, 43, 44, 45, 46, 47, 48, 49

So from now on, when copying from the "tiled" surface to another surface, you can use the
CopyTile() suite of functions. Here is a sample where we copy some of the tiles to another
surface:

SurfaceObject S;
SurfaceObject D;

////////////////////////////////////////////////////////////////////////////////
EXAMPLE 3:

// Create S, load the tiles onto it, and tile the surface
S.Create(10*32, 5*32);
S.LoadBMP("tiles2.bmp");
S.SetTiled(32, 32);

D.Create(640, 480);

// Copy tiles 0, 5, and 24 to D at different locations

D.SetRect(10, 10);
D.CopyTile(S, 0);

D.SetRect(100, 20);
D.CopyTile(S, 5);

D.SetRect(50, 200);
D.CopyTile(S, 24);

////////////////////////////////////////////////////////////////////////////////

There you go! Easy as pie ; )

Well, that's all for now! Hopefully you understand how copy rectangles and tiled surfaces
work now. If you have any questions, just ask me.




Antonio Maiorano
Guardian Soft Head Programmer
