Friday, September 21, 2007

Game States in GE2

This time I would like to explain a little about the game state system used in Galactic Engine 2. It is working well right now, however wasn't been tested in fairly complicated states so it may contain some bugs.

Game states are used by the game developer to write the logic for the engine to use.
There can be many game states  however only one is active. The user can decide which state he
wants to use and when. The game states system for the GE2 was inspired by the OGRE implementation and has similar logic behind it.

In the GE2 GameState is base abstract class. It receives the events from the engine or input system. The user can the decide what he wants to do with the received data.

Here are some functions that base abstract GameState class has:


void OnConstruct();
void OnDeConstruct();

//called when entering this state or leaving it
void OnEntry();
void OnExit();

//draws opengl stuff
void OnDraw();

//updates game logic
void OnUpdate();


//Keyboard
void OnKeyDown ( const GE2_Key &key );
void OnKeyUp ( const GE2_Key &key);

//Mouse
void OnMoseMove (float &x, float &y, float &deltax, float &deltay);
void OnMoseDown (float &x, float &y, Uint8& mouseButton);
void OnMoseUp (float &x, float &y, Uint8& mouseButton);

void OnQuit();



When user wants to use GameState he needs to inherit the base GameState class
and make his own, like this:

class MyGameState : public GameState

and override the function that he wants to use. For example if I want to draw a simple OpenGL drawing. I need to do this:


void MyGameState::OnDraw()
{
   //Drawing Code here
}

and if I want to get a keyboard event then something like this:


void MyGameState::OnKeyDown( const GE2_Key &key )
{
    if( key == input.keys.F1 )
    {
           //Change to Full Screen
           video.SetFullScreen( !video.IsFullScreen() );
    }
}

Now when the game state is defined all is needed is to pass it to the engine. This
is required for any application of GE2. For example:


void gameStart()
{
     //create my own game state
     MyGameState myState;

     stateManager.Start( &myState );
}

int main( int argc, char* argv[] )
{
    //initiate engine through this function
    global.startEngine( gameStart );

    return 0;
}


The gameStart() is required as separate function so the engine can check for memory leaks after the gameState ends, and also catch any exception automatically.

Another thing game states are used for is the switching between states. Unfortunately I haven't tried this feature thoroughly , so it may cause some problems later, but the logic behind is really similar with OGRE. To switch to another state the user needs to call:


//pushes new state on the stack and activates it
stateManager.PushState( &myState );

//removes the last state from the stack and deactivates it
MyGameState *myState = stateManager.PopStat();


When the state is put on the stack the first time then OnConstruct() is called when it is removed then OnDeConstruct() is used. These functions are called only once so it is safe here to initialise all the variables or destruct them.

The OnEntry() and OnExit() are used when the states are switched between.

That's probably all there is.

Tuesday, September 04, 2007

Gradient calculation explained

In my previous post I have described how the gradient helped to solve few issues when creating skins for the GUI system. This time I would like to explain how the gradient is calculated.

Previous generation GUI system was really primitive when it comes to drawing shapes. The biggest problem was probably calculating the color gradients. Because the shapes were created by raw GL_POLYGON data, each point parameters should be specified. This means for each point I had to write its UV coordinates, color value and position. Calculating the color was one of the most problematical parts of this process.


Let’s for example say I have a simple rectangle with 4 points.



Now If I wanted to make a good looking gradient color, I had to manually calculate each point. For such simple rectangle this is not hard.

For example let's pick the gradient top color RED=RGBA{1.0,0.0,0.0,1.0} and bottom with 0.0 alpha RED={1.0,0.0,0.0,0.0}


I can easily calculate the 4 points of the shape:


Point1 = RGBA{1.0,0.0,0.0,1.0}

Point2 = RGBA{1.0,0.0,0.0,1.0}


Point3 = RGBA{1.0,0.0,0.0,0.0}

Point4 = RGBA{1.0,0.0,0.0,0.0}


The result is something like this:




Usually however more complex shapes are used to define appearance of the GUI widgets. Take a simple rectangle with an arc on one of the edges. This makes calculation difficult especially when some day you would like to change the vertical gradient color to horizontal or even to linear gradient with angular rotation. Calculating colors now is really the job you would want to do the least.



The new GUI system tries to solve this problem, by calculating the colors automatically. Because the drawing of shapes is abstracted (user is using lines, circles, rectangles not the GL_POLYGON points directly) it is possible for the GUI system to calculate the points.


The only thing that user needs to define is the initial 2 gradient colors (start, end) and gradient type or angular rotation. The GUI system would do everything else.


I would like to describe this in more detail, how the vertex colors are calculated.


I am using 2 basic points for this. These points hold the initial color values and also the position relative to each other. This makes it possible to create different gradient widths and heights.




The downside is that because we are dealing with vertex points we loose some color values, depending on the number of total vertexes. To better illustrate this imagine the circular gradient that is applied to simple 4 vertex rectangle. Because there are only 4 points it is not possible to describe the transition between them the same way as per pixel calculated image. The only way to improve this is to increase vertex size on the sides and with the shape itself. Another way is to apply texture . In the new GUI system however I decided not to use spherical gradient calculation because of these issues.


So right now I have only 3 gradient types – vertical , horizontal and angular.


The Vertical gradient type is one of the most simplest.

Each shape is created from line. Each line has 2 points. Basically the function loops and calculates color for each point.

Before starting to calculate, we need to get 2 base gradient points – the most top left and bottom right points of the shape.
The position of these points is relative to the maximum size of the shape so I use the shape left,top,width,height values for this.

//here are positions of the gradient points
RenderVertex gradientPos1 = GetVertexPosition( left , top , width , height , gradientPoint1 );
RenderVertex gradientPos2 = GetVertexPosition( left , top , width , height , gradientPoint2 );

As a result I get absolute position x,y on the screen of each point

When I have these 2 points I can find the distance between them in absolute coordinates:

//total horizontal distance between gradient points
distv = (gradientPos2.Y - gradientPos1.Y) ;

//ignore the sign value just incase
distv = fabs( distv );






And when I got this, I can calculate the relative distance between the first top left points and the current point I am calculating:


//now find the position of the point between them 
if( distv == 0.0f )
{
posv = 0.0f;
}
else
{
posv = (guiPoint.Y - gradientPos1.Y ) / distv;
}




Now when I have this posv I can apply it and find the result color interpolation:


//calculate the color value now with simple interpolation
result.R = gradColor1.R * (1.0f - posv) + gradColor2.R * posv;
result.G = gradColor1.G * (1.0f - posv) + gradColor2.G * posv;
result.B = gradColor1.B * (1.0f - posv) + gradColor2.B * posv;
result.A = gradColor1.A * (1.0f - posv) + gradColor2.A * posv;






The same way it works with the horizontal gradient type. This time however instead of using Height we use Width of the shape, but the process itself is exactly the same.




A more complicated is the angular gradient calculation. For this we need additional parameter – angle in degrees. The user specifies the angle within range 0...360.

We start the same way when calculatin vertical or horizontal gradient, but calculate both distances:

//Find horizontal position first

//total horizontal distance between gradient points
disth = (gradientPos2.X - gradientPos1.X) ;

//ignore the sign value just incase
disth = fabs( disth );

//now find the position of the point between gradient points
if( disth == 0.0f )
{
posh = 0.0f;
}
else
{
posh = (guiPoint.X - gradientPos1.X ) / disth;
}

//Now find vertical position

//total vertical distance between gradient points
distv = (gradientPos2.Y - gradientPos1.Y) ;

//ignore the sign value just incase
distv = fabs( distv );

//now find the position of the point between gradient points
if( distv == 0.0f )
{
posv = 0.0f;
}
else
{
posv = (guiPoint.Y - gradientPos1.Y ) / distv;
}



The only thing left to do, is to calculate the rotated distances:


//make the angle within requested range
fangle = fabs(angle);

if( fangle > 360.0f)
{
fangle = 0.0f;
}

//the gltriangle size is 2Pi
fangle = angle * 2;


// 0 .. 90
if( (fangle >= 0)&&(fangle < 180)
//now rotate the point
posRot = posh * trig.GLTriangle( fangle + 180 ) + posv * trig.GLTriangle( fangle );
}
else
// 90 .. 180
if( (fangle >= 180)&&(fangle < 360)
//now rotate the point
posRot = (1.0f - posh) * trig.GLTriangle( fangle + 180 ) + posv * trig.GLTriangle( fangle );
}
else
// 180 .. 270
if( (fangle >= 360)&&(fangle < 540)
//now rotate the point
posRot = (1.0f - posh) * trig.GLTriangle( fangle + 180 ) + (1.0f - posv) * trig.GLTriangle( fangle );
}
else
// 270 .. 0
if( (fangle >= 540) )
{
//now rotate the point
posRot = posh * trig.GLTriangle( fangle + 180 ) + (1.0f - posv) * trig.GLTriangle( fangle );
}


The trig.GLTriangle is a precalculated table of cos and sin values. I also change the sign depending on what angle is currently selected.

With the posRot value I can get the required color value:

result.R = gradColor1.R * (1.0f - posRot) + gradColor2.R * posRot;
result.G = gradColor1.G * (1.0f - posRot) + gradColor2.G * posRot;
result.B = gradColor1.B * (1.0f - posRot) + gradColor2.B * posRot;
result.A = gradColor1.A * (1.0f - posRot) + gradColor2.A * posRot;

That's it.




This is only a small feature of the new GUI system, however it makes a big difference when creating GUI skins, because it takes significiant portion of the work from the designer.