Saturday, 26 December 2015

Interaction with the 2D environment (part 11)

Sorry for the long pause between this and the last article. I simply haven't had any time in the past months to spend on this project. But the holiday season has started and I finally found a few nights to take this to the next step.

This will be the last step in this 2D adventure but it leaves us with a fully functional 2D engine that allows our character to walk around a 2D map. While there are plenty of things to do from here it is a good starting point to go from.

A lot has changed in the source to get to this next level so this is a bit of a read. I'll try to handle each area one step at a time.

Housekeeping

Obviously some housekeeping had to be done to make some of the code written so far more generic and reusable. Some of these we'll look into in more depth but in broad strokes:
  • tilemap.h (and the associated shaders) has been updated to allow any sized map to be used without having to make changes to the code. We thus store the size of the map and size of our tiles image file in our structure and send this information to the shaders
  • spritesheep.h (and the associated shaders) has similarly been updated
  • all of the hardcoded data in engine.h and engine.c has been moved into a new header file called gamedata.h. Obviously in a real game this information should be stored in files that can be loaded as they are needed to create a game with multiple levels instead of stored inside source files but for our example code this is easier. I moved everything into a separate file to set this data apart.
I'm still using globals for various things though I have moved more into structures where this made sense for re-usability. As I've mentioned many times before this is mainly for readability of the code and to keep things simple but a few exceptions set aside this code could do with removing a few of the globals in the long run.

A new map

We're no longer using the sample desert map from our tiled map editor but a map I've flung together from some screenshots from the first level of the game flashback. This leaves a lot to be desired and really needs to be cleaned up but for our purpose it does well.
I've put the original maps you can edit in the tilemap editor within the folder structure in the github repository (Support/Flashback map) but they are not used directly. Instead the CSV exporter was used to export the maps and embed them in the gamedata.h header file.
Note that the map has two layers, one layer is the map we're actually rendering to screen in the game, the other allows us to mark where in our map our character can walk and where he can climb up or down. This is done by using a simple colored tileset so we can use the indexes it creates:
0 = empty space
1 = conrad can walk here
2 = conrad can climb up when facing left
3 = conrad can climb up when facing right
This is still very simplistic as I've limited the actions Conrad can do but if all his animations were implemented we'd probably need a few more to indicate where Conrad can only roll or where he can jump. More on this later.

Our map is now also scaled 1:1 so each tile is rendered as a 32.0 x 32.0 tile in our coordinate system. We also render Conrad at this scale which is very important because our animations line up with our tiles. Conrad will thus always be rendered at the right side of a tile.

The same applies to our currentPos global variable that holds Conrads position which is now a position within our tilemap (measured from the center of our tilemap). Our default position of -18, 9 (which is now set in our engineLoad function) puts Conrad at the left bottom of our map.

Enhancements to the action map

This is probably where the largest changes have taken place. The action map that was introduced in the last write-up has been changed drastically.

First it no longer is a single array through which we scan. Instead every animation has its own followup action map. This is simply a speed up because instead of having to scan through the entire map, we only scan through those entries that actually can be a followup action to our current animation.

Our animation structure now has a new member called followUpActions which is a pointer to an action_map array. 
// now define our animations
// note that movements are multiples of 32
animation conradAnim[] = {
    0,    0,  false,   0.0,  0.0, conradLookLeftActions,                        //  0 - look left
    0,    6,  false,   0.0,  0.0, conradLeftToRightActions,                     //  1 - turn from looking left to looking right
...
Note also that our moveX and moveY members now refer to our movement within our map and are multiplied by 32.0 to get our movement on screen. I've cheated a little with some of the animations which is something that should be cleaned up at some point.

Our first entry in our animation array refers to Conrad simply standing still and looking to the left. The conradLookLeftActions array being pointed to as the followUpActions member is an array that contains all followup actions possible when Conrad is looking to the left and looks as follows:
// Now we define all the followup actions for each of our actions
action_map conradLookLeftActions[] = {
  CR_ANIM_TURN_LEFT_TO_RIGHT, keysRight, conradCanStand,                        // turn left to right if our right key is pressed
  CR_ANIM_WALK_LEFT_1, keysLeft, conradCanWalkLeft,                             // start walking left
  CR_ANIM_GET_DOWN_LEFT, keysDown, conradCanCrouch,                             // start getting down facing left
  CR_ANIM_CLIMB_DOWN_LEFT, keysDown, conradCanGoDownLeft,                       // start climbing down facing left
  CR_ANIM_JUMP_UP_LEFT, keysUp, conradCanStand,                                 // jump up facing left
  CR_ANIM_LOOK_LEFT, NULL, conradCanStand,                                      // keep looking left if no key is pressed
  CR_ANIM_FALLING_LEFT, NULL, NULL,                                             // must be falling..
  CR_END, NULL, NULL,                                                           // end (we shouldn't get here)
};
Note that our array ends with an entry "CR_END, NULL, NULL". In theory we will never evaluate this but it is a safety net, kind of like a NULL terminator.

The first member is our followup action, the second member are the keypresses associated with our followup action (we had this already in a slightly different form) and our third member points to structures that check our second layer in our map to see if the movement is allowed (i.e. Conrad can't walk to the left if there is a tree in the way).
When our second or third member is NULL it is ignored and the action is possible.
We'll look at these two members more closely further on.

In our previous tutorial the loop that checked our action map was embedded in our engineUpdate function. I've moved the logic into new functions to make it easier to maintain. In our engineUpdate routine we know what the current animation is and thus have a pointer to the action map for that specific animation. We simply call a new function called getNextAnim with a pointer to that map which will return the next animation we should play:
...
  nextAnim = getNextAnim(conradAnim[currentAnim].followUpActions);
...
Couldn't be simpler. The magic happens in getNextAnim which simply looks at each entry in our action map until it finds the first one that can be used:
// obtain our next animation type
GLint getNextAnim(action_map *followUpActions) {
  GLint nextAnim = -1;

  if (followUpActions != NULL) {
    // check our action map
    while ((nextAnim == -1) & (followUpActions->startAnimation != CR_END)) {
      // assume this will be our next animation until proven differently
      nextAnim = followUpActions->startAnimation;
              
      // check if we're missing any keys
      if (!areKeysPressed(followUpActions->keys)) {
        // keep looking...
        nextAnim = -1;
        followUpActions++; // check next
      } else if (!canMoveThere(followUpActions->moveMap)) {
        // keep looking...
        nextAnim = -1;
        followUpActions++; // check next
      };
    };    
  };  
  
  return nextAnim;
};

We check our keypresses first and whether we can move to that location.
The keypress logic remains nearly the same as the previous implementation but instead of hardcoding up to 5 keypresses we now use a zero terminated array. Our first entry in our action map points to the array called keysRight which is defined as:
int keysRight[]     = { GLFW_KEY_RIGHT, 0 };
Our canMoveThere function works very similarly but has a slightly more complex structure it points at, here for example is our conradCanWalkLeft array and our idCanWalk array it references:
unsigned char idCanWalk[] = { 1, 2, 3, 0};
...
move_map conradCanWalkLeft[] = {
  -1,  0, idCanWalk,
   0,  0, NULL,
};
The first entry in conradCanWalkLeft checks if conrad can move one tile to the left. It does this by checking if the value of the tile in our second map appears in our idCanWalk array which is once again a zero terminated array.
Note that as a result we can't check for a zero value for our tile which is fine as we've defined zero to be empty space. 

Moving out of the screen

The last thing I hadn't addressed in the previous version was Conrad walking off screen. As Conrad is now constraint within our map I simply update the view when Conrad is about to leave the screen. There are nicer ways to handle this but for our purpose this will do fine. The code was added right after our followup animation check.



Where too from here?

We now have a working but very basic engine for a platform game. Obviously there is a need to complete the animations that are missing and tweak the existing ones. One good example is to break the roll up into 4 steps instead of rolling 4 tiles at once. The original game allowed you to roll even if there were only 2 tiles between Conrad and a wall but he would simply bounce off the wall. This could be easily triggered by using another tile type in the second map to indicate the wall and adding in that once you attempt to roll there, the bounce off the wall animation starts playing. Similarly we can't jump off a ledge or do long jumps yet which are really cool to add.

Another issue to tackle is that Conrad is currently drawn over the background map which isn't always correct:
Here Conrad is standing in front of the thingy on the ground while he should be standing behind it. This can be solved by either having a stencil for our image and tweaking the Z-value when drawing the tiled map or it could be done by creating multiple layers in our map having a background layer drawn behind Conrad and a foreground layer drawn in front of Conrad.

Finally completely missing here are the bad guys and other obstructions. For now I'll leave that up to your own imagination :)

Download the source code here

What's next?

As I mentioned up top, this is the last write-up I've planned on the subject of 2D within the confines of this tutorial. I probably will write additional blog posts to clear up certain things I've done here and I may tweak the examples as time goes by.

Next I wish to jump back into the world of 3D which I've left alone for far too long but we're not going to leave our platform example. We'll have to sidetrack a little to explain some basics but then we're coming right back to our platform game and we'll look into transforming it into a 3D environment.

Sunday, 20 September 2015

Rendering sprites #3 (part 10)

So it is time for the next step and get some basic user interaction going. Before we get there however, lets look at some of the mechanics involved.

In our previous post we looked at the individual animations that make up the movements of our main character. One thing you may have noticed is that the sprite sheet I'm using has actually been scaled up to 200%. I'm not sure if that was done in the original game or by the person who extracted the graphics. I've left it as is as this is just to demonstrate the techniques and with todays high resolutions screens we would probably want to use higher quality images anyway.
There are however two key observations we can make from the sprites and the animations:
  1. Each animation should play in full and is then followed up by the next animation to create fluid motion. Some animations are split into multiple ones. Walking for instance has 3 animations, taking the first step, stopping at the end of the first step and continuing with the 2nd step. Play the first and 3rd in a repeated sequence and our character keeps on walking. If our character needs to stop moving after the first animation we need to play the second one to round the animation off, but we can stop on a dime if we're stopping after the 2nd step.
  2. If the character moves, the character moves a fixed amount. The movement is always a multiple of 32 (or likely 16 in the original game).So after taking one step (1st + 2nd animation) we'll have moved 32 pixels. If we take two steps (1st + 3rd animation) we'll have moved 64 pixels. I've not checked the animation for running yet but my guess is the movement will be either double or triple that.
    Similarly climbing or falling we're moving up (or down) 96 pixels (3 * 32).
There is a very important reason for these fixed movements, they align with the background used in the original flashback game and this makes it far easier to check what movements can be done. For instance for climbing we can simply check if we're on a background tile where climbing makes sense. It also improves game play as the player doesn't need to go through the frustration of being a pixel to far to the left or right. In this part of the write-up we won't worry about this yet but it will become clear in the next part.

The one animation that stands out is rolling but I think that is caused by either missing a part of the animation or that I've gotten something wrong (I am trying to reverse engineer the animations used here). I've left it in the source code for now but I will likely remove it once we go to the next part to ensure we only have animations that work with our background.

Going from one animation to the next 

So looking at our animations it is important to realize that only certain animations follow others and the animation that should follow is controlled by two other parameters:
  • What is allowed, i.e. we can't walk left if there is a wall
  • What input is given by the user
We'll leave the interaction with the environment for the next part and focus on user input first. We'll also limit ourselve to based keyboard input.

As far as making our engine aware of the keyboard input I've taken out my temporary keyboard interface from the previous part and exposed the glfwGetKey method through a call back. Remember I didn't want to put any GLFW specific code in the game engine itself. I'm not 100% sure I'm going to keep this working the way it does right now but it will suffice for the time being. This function simply checks if a specific key is currently being pressed. Unlike our previous example we're not reacting on the keypress itself but on the current state of the keyboard. GLFW should simply be keeping a keymap of the keys currently pressed so this check should be pretty quick.

Instead of programming all the individual keypresses and reactions to them we're going to use a lookup table with a state engine. A state engine is nothing more then a fancy word for defining in what state our character currently is in i.e. if he's standing still, walking, jumping, facing left or right, etc.

Our state engine has three variables that track it:
  • currentAnim, the current animation playing, which is equal to what our character is doing. This relates directly to our animation enumeration we already introduced in the last post.
  • currentSprite, the current sprite within the animation that is currently being displayed.
  • currentPos, the current position within our game world our character occupies.
After our animation finishes playing we'll adjust the position according to the movement recorded in our animation table and check our action lookup table for the next action/animation we want to invoke.

This lookup table is defined using the following struct:
// structure to control our characters with
typedef struct action_map {
  GLint     animationEnding;        // when this animation ends
  GLint     startAnimation;         // start this animation
  int       keys[5];                // if these keys are pressed (ignoring zeroes)
} action_map;

As we can see our action lookup table is defined by the animation that has just finished playing, the animation that should follow and up to 5 keys that the user should be pressing to make this happen.
We'll be checking our table top down and the first entry that matches our current state wins.

Here is a part of the table:
// and define our action map, the first action that matches defines our next animation
action_map conradActions[] = {
  // looking left
  CR_ANIM_LOOK_LEFT, CR_ANIM_TURN_LEFT_TO_RIGHT, { GLFW_KEY_RIGHT , 0, 0, 0, 0},                  // turn left to right if our right key is pressed
  CR_ANIM_LOOK_LEFT, CR_ANIM_WALK_LEFT_1, { GLFW_KEY_LEFT, 0, 0, 0, 0},                           // start walking right
  CR_ANIM_LOOK_LEFT, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                         // keep looking left if no key is pressed
  CR_ANIM_TURN_LEFT_TO_RIGHT, CR_ANIM_LOOK_RIGHT, { 0, 0, 0, 0, 0},                               // turn to looking right once finished

  // walking left
  CR_ANIM_WALK_LEFT_1, CR_ANIM_WALK_LEFT_2, { GLFW_KEY_LEFT, 0, 0, 0, 0},                         // keep walking left
  CR_ANIM_WALK_LEFT_1, CR_ANIM_STOP_WALK_LEFT, { 0, 0, 0, 0, 0},                                  // stop walking left if no key is pressed
  CR_ANIM_WALK_LEFT_2, CR_ANIM_WALK_LEFT_1, { GLFW_KEY_LEFT, 0, 0, 0, 0},                         // keep walking left
  CR_ANIM_WALK_LEFT_2, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                       // finished walking left if no key is pressed
  CR_ANIM_STOP_WALK_LEFT, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                    // finished walking left if no key is pressed

Now this is only a small section of our table but lets look at these entries more closely to try and explain how this works.

The first entry applies when our current animation is CR_ANIM_LOOK_LEFT which is a single frame animation of our character looking left. The animation that follows is CR_ANIM_TURN_LEFT_TO_RIGHT which is an animation that makes the character turn around. The key listed is GLFW_KEY_RIGHT which means this animation is triggered if the user is pressing the right arrow key. Note that we don't care about any other keys but also that we don't check any other keys. I.e. if they user is pressing the right and up key this is still the first entry that matches and this is what will happen. The order in which the animations are specified in this table are thus very important.

The second entry triggers our walking animation. If the character is currently standing still looking left (CR_ANIM_LOOK_LEFT) and the user has the left arrow key pressed, we start our walking animation.

The third entry is the action if the character is looking left and the user has no (matching) keys pressed. We'll just keep playing the same standing still animation over and over again until something else happens.

The next set of entries define the walk itself. These go through the combinations of cycling through the 1st (CR_ANIM_WALK_LEFT_1) and 3rd (CR_ANIM_WALK_LEFT_2) animations we talked about in our introduction above. If the user lets go of the left key depending on which animation is currently playing we either directly got back to standing still (CR_ANIM_LOOK_LEFT) or we first play our stop walking animation (CR_ANUM_STOP_WALK_LEFT).

A lookup table like this lets us easily add in more animations and how we should react to the users input.

Note that I have no way of knowing if the original game uses a similar approach or something very different. Also I've kept things simple, there are a few easy optimizations to speed things up, especially animations defined later in the table mean we have to do a lot of tests before we end up at the right one but that is for another day.

Our new update routine


Our final step is actually reacting to our keyboard commands and making it all work. For this we've enhanced our engineUpdate routine. The start of our routine is still pretty similar. I've changed the animation speed to roughly 12 frames a second but other then that we simply cycle through the sprites. Once our animation recycles we set animReset and do our keyboard check.

This part has changed:
    // if our animation was reset we have a chance to change the animation
    if (animReset) {
      int nextAnim = -1;
      
      // if our current animation was played forward, we add our movement
      if (conradAnim[currentAnim].firstSprite<=conradAnim[currentAnim].lastSprite) {
        currentPos.x += conradAnim[currentAnim].moveX * SPRITE_SCALE;
        currentPos.y += conradAnim[currentAnim].moveY * SPRITE_SCALE;        
      };

Once our animation has finished we need to check if we need to add the movement to our current position. We only do this if our animation was played in the normal direction.

      // check our action map
      for (int a = 0; a < MAX_ACTIONS && nextAnim == -1; a++) {
        if (conradActions[a].animationEnding == currentAnim) {
          // assume this will be our next animation until proven differently
          nextAnim = conradActions[a].startAnimation;
                    
          // check if we're missing any keys
          for (int k = 0; k < 5 && nextAnim != -1; k++) {
            int key = conradActions[a].keys[k];
            if (key == 0) {
              // no need to check further
              k = 4;
            } else if (!engineKeyPressedCallback(key)) {
              nextAnim = -1;
            };
          };
        };
      };

      // if -1  We're missing something
      currentAnim = nextAnim == -1 ? 0 : nextAnim;
      currentSprite = conradAnim[currentAnim].firstSprite;

This is our main logic scanning our table until we found the correct animation and keypresses. We just loop through our entire table until we find the right one. If we can't find anything our table is incomplete, oops, so we just default back to our first animation. 

      // if our new animation is going to be played in reverse, we subtract our movement
      if (conradAnim[currentAnim].firstSprite>conradAnim[currentAnim].lastSprite) {
        currentPos.x -= conradAnim[currentAnim].moveX * SPRITE_SCALE;
        currentPos.y -= conradAnim[currentAnim].moveY * SPRITE_SCALE;        
      };

Finally, if we're playing an animation in reverse we need to subtract our movement first. I've only used this for backwards rolls.

If you compile and run the sample application at this point in time you should be able to make Conrad walk left and right, roll, and making him climb. He still lacks a way to get back down :)


Download the source code here

So where from here?

Well the next step is putting a proper background in place and make Conrad interact with it so he can only climb where it makes sense and drop back down.

It may take awhile before I get this done. First off I need to spend some time on getting the graphics sorted out but I've also got a few other things on my plate so I may not be working on this any time soon.






Saturday, 5 September 2015

Rendering sprites #2 (part 9)

So we're taking baby steps going forward. First because I want to detail how I'm slowly progressing to something that allows the user to fully control the character, but also because time is too short to do it all at once:) I'm trying to find and use every spare few minutes I can find between family, work and playing games. Owh and I'm still hard at work building a HALO suit, that's turning out a much larger project then I initially thought.

First off, I made a few more changes on the work done so far just to complete it. I ended up enhancing spritesheet.h so that sprites are centered but you add an offset to position it properly. This also allows the animation to give flexibility on the movement.

Our sprite struct thus is now defined as:
// structure to keep data about our spritesheet
typedef struct sprite {
  GLfloat left;                   // left position of our sprite in our texture
  GLfloat top;                    // top position of our sprite in our texture
  GLfloat width;                  // width of our sprite
  GLfloat height;                 // height of our sprite
  GLfloat offsetx;                // horizontal offset
  GLfloat offsety;                // vertical offset
} sprite;

Our array of sprites now defines about the first 4 rows in our spritesheet. That is as far as I'll go for now. Eventually I'll add the climb in because I need that for this little demo but I'll leave most of the other animations alone.

Defining animations

The next step is to bring some structure to our animations. We're going to break up all the animations into small pieces that can be sequenced depending on the users input. For now we'll just switch between them so we can see if we've got them right but eventually after each animation ends we'll check for user input and determine what the next animation is.

I've define a struct in engine.h that allows me to define one animations sequence:
typedef struct animation {
  GLint     firstSprite;            // first sprite of animation
  GLint     lastSprite;             // last sprite of animation
  bool      flip;                   // flip sprite horizontally
  GLfloat   moveX;                  // add this to our X after anim is finished
  GLfloat   moveY;                  // add this to our Y after anim is finished  
} animation;

To make it easier to use my animation I've also added an enum to engine.h that lists all the different animations:
enum conrad_animations {
  CR_ANIM_LOOK_LEFT,
  CR_ANIM_TURN_LEFT_TO_RIGHT,
  CR_ANIM_LOOK_RIGHT,
  CR_ANIM_TURN_RIGHT_TO_LEFT,

...

  CR_ANIM_JUMP_UP_LEFT,
  CR_ANIM_COME_DOWN_LEFT,

  CR_ANIM_COUNT
};

The animations themselves are then defined in an array in engine.c:
// now define our animations
animation conradAnim[] = {
    0,    0,  false,   0.0,  0.0,    //  0 - look left
    0,    6,  false,   0.0,  0.0,    //  1 - turn from looking left to looking right
    6,    6,  false,   0.0,  0.0,    //  2 - look right
    6,    0,  false,   0.0,  0.0,    //  3 - turn from looking right to looking left

...

   50,   66,  false,   0.0,  0.0,    // 16 - jump up (facing right)
   67,   78,  false,   0.0,  0.0,    // 17 - coming back down (facing right)   
};

The code for displaying the animation hasn't changed much. We're just cycling through a single animation instead of all defined sprites.

I've turned off the back ground for now and I'm displaying all sprites in the current selected animation on the left and right slightly overlapped to help with changing the offset of each sprite. Until we actually start with controlling the character we won't know if we've gotten these right.



The final change at this point is changing the animation. I've removed the camera control (this will be changes anyway) and for the time being am sending key presses to the engine (again I'm trying to leave any GLFW specific code out of the engine). In the engine I'm simply keeping a stack of unhandled keypresses which I check at the end of each animation. This won't be the final solution for when we start to control our character but it does the job for cycling through the animations without loosing keypresses when the animation hasn't finished yet.

Download the source code here

So where from here?

The next step is simple, to react on keyboard input and actually control the character.

At first we won't worry about any character interaction with his surroundings and implement basic walk left, walk right, roll left, roll right and straight jump logic. I may through in a few more animations just for the heck of it.

After that we'll add a more appropriate background and start looking at properly interacting with it.

Thursday, 3 September 2015

Making timer and worker events in Omnis safe

Timer events and the new worker objects in Omnis Studio are two very powerful functions in Omnis. Both use the same internal logic to kick of logic at a given time, for timers this will happen at either fixed intervals or when a specific amount of time has passed, for worker objects the run logic once results have been received from the database in a background thread and the data is ready for the application to deal with.

This however is not without consequence. These events can interrupt whatever code is currently running in the foreground thread and as a result introduce very hard to find bugs if the interruption changes something the code being interrupted wasn't expecting.

TigerLogic have done a good job to ensure these interruptions don't happen willy nilly as code can only be interrupted at specific times. It is however unclear what interruptions are possible. After some experimentation I've found the following scenarios will be interrupted by timer and worker code:
- when the main thread is paused on an enter data command
- at the end of a loop
- when constructing a new window
I'm sure I've forgotten some others.

Generally speaking this works great unless you rely on globals.

The main exception to that however are worker object. They have one big drawback, a worker can not interrupt another worker. Omnis tends to crash when that happens. If you have more then one worker active at a given time and you're in the middle of processing the results of the first worker object (which you'll likely do within a loop), the 2nd worker can interrupt that loop as it has finished retrieving its result set. 9 out of 10 times this will crash your application.

It is therefor important to check if you are interrupting code that could potentially lead to issues and delay the action till later. At the start of my $completed function for my worker I cache the result set and check whether I can process it. If so I process it right away, if not I start a timer to check if I can process it a short time later.

In both my worker $completed code and timer $timerproc code I call the following functions that I've placed in a code class:
cCode/ERR
  ; Bypass error handler, we ignore any errors while this is active
  SEA continue execution

cCode/CanRunTimer(pvExpectedLineCount = 2, pvSendToTraceLog = kFalse)
  Begin reversible block
    ;  Temporarily replace our error handler with a dummy one, sys(192) tends to send bogus issues to our error handler..
    Load error handler cCode/ERR
  End reversible block

  Calculate lvList as sys(192)
  If lvList.$linecount<=pvExpectedLineCount
    ;  We can run it, just $event/$completed/$timerproc and CanRunTimer are on the stack
    Quit method kTrue
  Else If pos('Enter data',lvList.[pvExpectedLineCount+1].linetext)=1
    ;  We can run it, we've interupted an enter data!
    Quit method kTrue
  Else If pos('ALLOW TIMER',lvList.[pvExpectedLineCount+1].linetext)<>0
    ;  Our comment says we are allowed to interupt here!!
    Quit method kTrue
  End If

  ;  !BAS! Timer can interupt END FOR, END WHILE, and some other situation. We DON'T want this as we may be in the middle of processing stuff!
  If pvSendtoTraceLog
    Calculate lvList.$line as pvExpectedLineCount+1
    Send to trace log (Diagnostic message) {Can't run queued timer, stackcount = [lvList.$linecount], interupts [lvList.classitem().$name].[lvList.method]/[lvList.line] = [lvList.linetext]}
  End If
  Quit method kFalse
This method will return true if I can safely process my timer code, and false if I should delay the action.

There are only 3 situations I deem safe:
- if all that is on the stack is my $event, $completed or $timerproc method and my CanRunTimer
- if we are interrupting an "enter data" command
- if we are interrupting a command that contains the "ALLOW TIMER" text (which I can put in a comment)

I've also made it optional to sent any other situation as a diagnostic message to the trace log. If a timer seemingly never runs I can check if I'm being too strict in letting timers run their course.

A timer method would then look something like this:
Timer.$event
  On evTimer
    Do code method cCode/CanRunTimer Returns lvCanRun
    If lvCanRun
      ;  Do your stuff
      ...
    Else
      ;  Restart our timer (omit this if you have $autoreset turned on)
      Calculate $cinst.$objs.Timer.$timervalue as 200 
      Do $cinst.$objs.Timer.$starttimer()
    End If

One last note on the subject. My workers tent to start a timer that delays processing of the results for 1ms regardless of any other running code. First of it means that I only need to implement this check in one place but more importantly it allows $completed to finish and clean up. Workers will also crash Omnis if you start another worker or do any other form of communication with the database even on completely unrelated session objects. By allowing $completed to finish and handling the result set when the timer object fires you gain a lot of stability in your application.

Sunday, 30 August 2015

Rendering sprites #1 (part 8)

As I mentioned in my previous post, life has been a bit busy but I finally found a few hours this weekend to continue onto the next subject. This one will take a few parts to get through and I'm afraid my schedule is only going to keep me as occupied as before but I'll try my best to follow this one up in due time.

Sprites have been the corner stone for 2D games since before I was born and in fact our tile map approach for our background is a basic sprite implementation. Once you start using sprites for the characters and other things under the gamers (or computer AIs) control it is only the way they are applied that differ. Also with modern computers most of the limitations that plagued earlier games have gone. At the core however a sprite is nothing more then a fancy name for a bitmap.

Most games have dozens if not hundreds of these individual bitmaps that make up all the game graphics and these more often then not are all combined into a single image that is loaded in one go. Often the sprites all had fixed sizes just like in our tile map so that it was easy to address them by an index within the larger image instead of keeping coordinates for each sprite not to mention that old hardware often had build in support for blitting (copying to screen) such fixed layout images but we'll see that the example I'm going for requires a bit more flexibility then was possible in those days.

Initially I was thinking about making a simple side scrolling shooter, something like a toned down R-type or something. The nice thing here is that the ship requires only a handful of sprites often without much animation in them just showing a different image depending on how the ship is moving. But I wanted to do something a little more fun but something as it turned out, a little harder to find graphics for (I'm not a graphics artist and thus have to rely on what can be found on the web for these types of examples).

As a kid I loved playing platformers like Prince of Persia, Another World and Flashback. I do believe Prince of Persia used sprites but Another World used 2D vector graphics because the amount of storage required for all the images was challenging for computers of the day to say the least.
Flashback as the spiritual successor of Another World seemed to be using the same technique but did use fully drawn backgrounds. I was surprised to find a full spritesheet for the main character Conrad on the internet. I'm not sure if this was due to someone screen capturing this or whether they are indeed using sprites for drawing Conrad in the actual game foregoing 2D vector graphics (by then computers had a lot more memory to play with).

Anyway I chose to use this sprite sheet because it is one of the few ones I could find that has the range of animations I wanted to use in this example.
Needless to say the Conrad character is owned by Delphine and is their copyright so I hope I'm not overstepping any boundaries here as I'm not actually trying to build a commercial game but showcasing a technique I hope I'm in the clear.

House keeping

So first off I've done a little house keeping. I'm not sure how much of this ended up in previous check-ins as it was a while ago but I've stripped out some of the keyboard commands as we'll be taking this into a different direction and moved all the tilemap logic into a single tilemap.h include file using the single file implementation approach we've been using for other parts. I've also reverted the tilemap logic back to its original implementation.

With the tilemap logic the initialization and render code has mostly moved into the tilemap.h file but I've left loading the texture maps themselves in the main engine code.
The shader code itself still resides in separate shader files that are loaded into memory. As it will be reused in other parts I've added callback hooks for a simple file loader function.

All our data for a tile map is neatly gathered up into a single struct.

Our sprite sheet implementation

For our spritesheet logic I'm following the single file implementation approach from the start and this code can all be found in our spritesheet.h file. Together with this we have our spritesheet.vs and spritesheet.fs shader files. I won't list the full shader code here, they explain themselves well.
I'll also not discuss our full spritesheet.h implementation as it mostly follows along the same lines as we've discussed for our tilemap but I'll highlite a few parts of the source code.

First off are our two structs.

The first one, simply called sprite, is a straight forward left/top, width/height structure. This identifies the location of each sprite in our master bitmap, you'll see when looking at our conrad.png file that our sprites aren't uniformly sized so we need to identify the location of each sprite manually.
I'm storing the width and height instead of the right and bottom values as this makes it easier to write the shader and it allows us to use the width and height to determine the size of the bitmap we draw on screen.
Also note that normally you would use the center of the bitmap to position the bitmap on screen but we use the center bottom of the bitmap, this will become important later on.

The second structure is called spritesheet and defines all the information we need to render sprites in our spritesheet. Most of its contents is set after calling spLoad (kind of like a constructor) which loads the shader files and retrieves the uniform IDs. Our sprite texture (conrad.png) however is loaded within engine.c itself.
Last but not least we have a couple of variables which maintain an array of sprite structures which can be filled using the spAddSprite and spAddSprites functions and again, those are called from engine.c.

This allows us to use our spritesheet implementation for multiple sprite sheets without making any assumptions of the contents of the image we're using.

Next we have our forward defines of all our functions followed by the actual implementation (don't overlook the #define SPRITE_IMPLEMENTATION added to our main.c).

The main function which are important are:

spLoad: this loads our shaders and initializes our structure, think of this as a constructor for our structure.
spUnLoad: unloads our shader and frees up any memory we've allocated, think of this as a destructor.
spAddSprite and spAddSprites: which adds sprite definitions to our structure so we know which sprites exist within our image.
spRender: which renders a single sprite to screen.

Using our sprite sheet

When we look at engine.c we see our spritesheet in use. For now we're just cycling through all the different sprites.

Note that there are a few changes in this file after we've moved the tilemap logic into its own source file and followed the same path for our spritesheet implementation. At the start we now invoke two macros that setup global variables for our tilemap structure and our spritesheet structure.

Most of the other globals are still in tact and our mapdata is included but after our mapdata we find a new lookup table for all our sprite coordinates. Note that at this point I've only added about 2 and a half lines from our image so the list is incomplete but we'll be adding on to this as we go along.

Next our error callback function has been enhanced to also set the error callback for our tilemap and spritesheet implementations. We also see our font setup and text file loading functions below that.

The load_shaders and unload_shaders functions are the first with major changes. Now that all the code for the tilemap has moved into our tilemap implementation file we simply call that.
The same applies to our spritesheet implementation. For all these functions we parse through a pointer to our related structs.

Our load_objects and unload_objects have similarly been changed but more code is retained here for now. Also note that besides loading the maps for our tilemap we now also load our Conrad.png file.
Our array is copied into our sprite sheet using the spAddSprites function making the initialization of our sprite sheet complete. This may seem a little overkill but just like with our mapdata the idea in the long run is that this data will be loaded from a file on disk instead of hard coded into our source file.

Finally if we scroll all the way down to our render code we'll yet again see that our tile map logic is now called within our implementation and we see a call to our render method for our sprite sheet. Here we simply cycle through all the sprites based on what frame we're rendering. This obviously is only temporary.

Download the source code here

So where from here?

This is only the start. I may add some code snippets up above when I have a chance to reread all the information but I didn't see much point at this time to duplicate what is already on github.

Our next step is to further put structure to the animations for our character so we can identify all the individual animations that we have such as walking, rolling, jumping, etc.

Then we'll start getting some basic control so we can actually control our character.

By this time I'll likely add in a more appropriate tile map and we'll start looking at how we can use the information in our tile map (or if needed augment it) to determine where our character can walk.

To be continued...

Monday, 13 July 2015

Being a little distracted....

It's been a few months since I last posted here, it's been busy times, busy at work, busy at home, but busy fun!

I'm hoping to find some time in the next few weeks to do another blog post in relation to my OpenGL project. I checked in some small cleanup changes that were part of some experimentation I've done but nothing noteworthy.

A couple of weeks ago I started playing HALO and had my daughter walked in (I usually don't start playing more serious games until my kids are in bed) and before I knew it we played through HALO Reach together. Now last year we went to the EB Games Expo and she was mesmerized by the people doing Cosplay there.  So putting 1 and 1 together we started looking into doing HALO cosplay and that is the thing that has really kept me busy, and probably will keep me busy for weeks to come.

Finally, it's pretty much all sorted out, I'll be a guest speaker at EurOmnis again this year and there is plenty of preparation to do for that as well.

So apologies, updates will remain slow, but I still hope to post something new soon.

Sunday, 12 April 2015

Rendering tiles method 2 (part 7)

So I promised a second approach to the same tile rendering demo in my previous post. With this approach we'll put the solution on its head.

This solution isn't necessarily better or faster, it can be depending on how complex your map is and whether you applied the optimizations to the previous method I suggested or not.
It is however a very worth while technique to learn.

What we are doing in essence here is to draw every pixel on screen and figure out what part of our map should be drawn here. We'll be moving part of our solution into our fragment shader. That in itself should sound some alarm bells as this will increase the workload of the GPU however we are removing overhead as we no longer generate a complex mesh nor attempt to draw anything that would have been off screen. It is how this balance shifts that determines whether this approach is better or worse.

As simple as our example is we're probably implementing a slower solution. However when you are looking at multiple layers of the maps drawn over each other it would be possible to combine the logic within a single fragment shader and possibly remove a lot of overhead.

Inverse of a matrix

One new function that was added to our math library is a function that generates the inverse of a matrix. The inverse of a matrix is a matrix that applies the inverse of the translation of that matrix. Say you have a matrix that rotates an object 90 degrees clockwise, the inverse of that matrix would rotate the object 90 degrees counter clockwise.

It isn't always possible to create the inverse of a matrix but generally speaking it works very well.

In our case we're going to take the inverse of our projection matrix. This allows us to take a screen coordinate, apply our inverse matrix and figure out the coordinate for our "model". Remember that in our original example we generated a 40x40 mesh for rendering our map, it is the coordinates within this 40x40 mesh that we calculate.

Applying our inverse matrix to every pixel we are rendering would be very wasteful but luckily for us we're dealing with a linear projection. As a result we only need to calculate our coordinates at each corner of the screen and interpolate those values, something OpenGL is very good at. We'll look into this a little more once we look at our vertex shader.


Where did my model-view-projection matrix go?!?

First however we take a very important sidestep. I've made only minor changes to my sample application. I don't tent to remove things often used as we may change things back for our next example. In our shader loader we are now loading "invtilemap.vs" and "invtilemap.fs" instead of our orignal tilemap shader. While I'm still defining my mvp matrix we are now using the inverse of this matrix in our solution.

What you'll see is that our call:
mvpId = glGetUniformLocation(shaderProgram, "mvp");

will fail even though our mvp variable is defined in our shader, it just isn't used. GLSL will automatically filter out any unused variables during compiling the shader and thus there is no variable to obtain our uniform location for.

There is a lot of debate to be found on the internet about this and various strategies on how to deal with it. It is a good thing to research and make up your own mind about. For now I just log variables I can't find so I can make up my mind whether this is intentional or indicates a fault in my program. Especially when you have multiple shaders you may not want to custom make different loaders but simply call the same code to load the shaders and just assume common variables to be there.

Our shader loader code has only slightly been changed, the only noteworthy change is the addition of getting our uniform for invmvp, our inverse model-view-projection matrix.

In our render function we then see that we calculate the inverse of our matrix and set it in our shader:
    // set our model-view-projection matrix first
    mat4Copy(&mvp, &projection);
    mat4Multiply(&mvp, &view);

    if (mvpId >= 0) {
      glUniformMatrix4fv(mvpId, 1, false, (const GLfloat *) mvp.m);      
    };
    if (invMvpId >= 0) {
      // also want our inverse..
      mat4Inverse(&invmvp, &mvp);
      glUniformMatrix4fv(invMvpId, 1, false, (const GLfloat *) invmvp.m);      
    };

A little further down we also see that our call to glDrawArrays now tells OpenGL to only draw 2 triangles.

Our inverse shaders

This is due to our main change that we are going to draw each pixel on screen once. As a result we need to draw a single square, thus two triangles, that encompass the entire screen. In OpenGL we're dealing with a coordinate system that has (1.0, 1.0) in the top right and (-1.0, -1.0) so that is what we're drawing in our vertex shader:
#version 330

uniform mat4 mvp;
uniform mat4 invmvp;

out vec2 T;

void main() {
  // our triangle primitive
  // 2--------1/5
  // |        /|
  // |      /  |
  // |    /    |
  // |  /      |
  // |/        |
  //0/3--------4

  const vec2 vertices[] = vec2[](
    vec2(-1.0, -1.0),
    vec2( 1.0,  1.0),
    vec2(-1.0,  1.0),
    vec2(-1.0, -1.0),
    vec2( 1.0, -1.0),
    vec2( 1.0,  1.0)
  );

  // Get our vertice
  vec4 V = vec4(vertices[gl_VertexID], 0.0, 1.0);
  
  // and project it as is
  gl_Position = V;
  
  // now apply the inverse of our projection and use the x/y 
  T = (invmvp * V).xy;
}

Notice also that we apply our inverse model-view-projection matrix but only store the x/y into a 2D vector, that is all we're interested in here. As mentioned before, OpenGL will nicely interpolate this value for us.

The real work now happens in our fragment shader so we'll handle that in parts:
#version 330

uniform sampler2D mapdata;
uniform sampler2D tiles;

in vec2 T;
out vec4 fragcolor;
Nothing much special in our header, we now have both texture maps here, our T input from our vertex shader and our fragcolor output.
void main() {
  vec2 to, ti;
  
  // our tiles are 100.0 x 100.0 sized, need to map that
  to = T / 100.0;
  ti = vec2(floor(to.x), floor(to.y));
  to = to - ti;
Here we've defined two variables, "to" which will be our coordinate in our tilemap and "ti" which will be our coordinate in our map data. You may recall from our original solution we created our tiles as 1.0 x 1.0 tiles and multiplied them by 100 to get 100.0 x 100.0 tiles. Here we need to divide them by 100.
We floor "ti" to get integer values and than subtract "ti" from "to" so "to" contains offsets within a single tile.
  // our bitmaps are 32.0 x 32.0 within a 256x256 bitmap:
  to = 31.0 * to / 256.0;
As our tiles are 32x32 tiles within a 256x256 bitmap, just like in our original solution we need to adjust our offset accordingly
  // now add an offset for our tile
  ti += 20.0;
  int tileidx = int(texture(mapdata, (ti + 0.5) / 40.0).r * 256.0);
  int s = tileidx % 8;
  int t = (tileidx - s) / 8;
  to = to + vec2((float(s * 32) + 0.5) / 256.0, (float(t * 32) + 0.5) / 256.0);
And now we use our "ti" value to figure out which tile we need to draw at our pixel. Note that this is pretty much what we originally did in our vertex shader.
  
  // and get out color
  fragcolor = texture(tiles, to);  
}

And voila, we've got the same result as before we started but using a very different way to get there.

So where from here?

There is much less to say about this then the previous technique.

For one, if you want to overlay multiple layers of maps it would be good to look into retrieving the pixel values for each layer in one fragment shader and either picking the right color or blending them. It would be much more effective then rendering each layer separately and having OpenGL blending it for you.

This technique does not lend itself well initially when you start looking at projecting the map using a 3D projection however using the inverse of a 3D projection opens up an entirely different door. Look around on shadertoy.com and you'll find legions of effects that use this approach as a starting point to apply raycasting type techniques. That however is an entirely different subject for another day.

On a related subject, and this applies to the previous example as well, it will not have escaped you that allowing the user to rotate our map isn't exactly looking nice with the map we are using. That option really only works if we have a pure top down map. I just wanted to show what we can do with our view matrix even if it may not be something you wish to use in a real game environment.

What's next?

I'm not sure yet. I've been going back and forth on what type of mini game to use to get to the next stage. I want to start looking into the OpenGL equivalent of sprites. In its simplest forms we'll just be rendering textured quads but it will allow us to go into rendering with blending turned on at one end, and at GLFW and control input on the other.

It may thus take awhile before I make the next blog as I'll be experimenting with a few ideas before I get something worth blogging about.

Saturday, 4 April 2015

Rendering tiles (part 6)

It's finally time to step it up a little and start drawing something usable. As I mentioned in my previous part I want to stick with 2D techniques for awhile.

The first technique I want to look at is two different ways of drawing a 2D tiled map. 2D tiled maps are used in many games to render our background or floor with and are cornerstone to many 2D platformers.

First off, I have to give a wave to Thorbjørn Lindeijer who's behind the excellent http://www.mapeditor.org/. Mapeditor is a tiled map editor that allows you to design your maps that you can subsequently use in your application and the best bit is that it is free to use and supports Windows, Mac and Linux.

Now I'm not going to look into a full loader for the TMX file format that Tiled uses but stick much closer to the basics. I'm going to use the desert sample that comes with Tiled.

There are two parts to our map.

The first are our tiles. For our tiles we have a simple image file in which we have lots of smaller bitmaps all of equal size that we can use repeatedly to make our map:
This looks really handy to the human eye, nice lines in between the tiles, easy to identify, but for a computer they are annoying. Each tile is nicely 32x32 pixels wide and that is something computers like. Another thing OpenGL tends to like, thought this limitation no longer for all hardware, is that texture maps are a magnitude of 2 and are square. You'll see that the png that ended up in our sample code has the black lines removed and is a nice 256x256 texture map.

Another question you may ask is why not create 48 individual image files, one for each tile? Besides it being easier to handle one file instead of 48, it is also an optimization on our hardware. We'll be drawing each of those tiles multiple times and if we'd have to switch between texture maps our hardware will be wasting precious time.

The second part builds our map itself. We've numbered our tiles 0 through 47 (0-7 being the first row, 8-15 the second, etc) and can now create a map of any size where each cell references one of our tiles that needs to be drawn at that cell. Our sample map is 40x40 tiles and all pieced together looks like this:
That's already a pretty big map :) This could still be off a size where you would just load the entire image as a single texture, it's "only" 1280x1280 big (we've lost a few pixels in the export) but that said, this is a very small sample map.
For a real implementation you may use tiles of a much higher resolution and a much larger map.

Tiled stores this information in a format called TMX but for our example logic that format contains way more functionality that I'd want to cover here so we're taking a simpler approach. Tiled luckily has a CSV export to export a map to CSV files.

To make life easy for our sample logic we're taking that CSV file, add some comma's to the end of each lines, and copy-paste it into our source code. This once again is not something you would do for production, you would save the map to a binary file or maybe even to a texture and load that but for our purposes of today, it will suffice:
// map data
unsigned char mapdata[1600] = {
...
};

Now we need to load this information in a way we can use. We are going to do so by loading them as texture maps into our GPU memory.
For our map data this is pretty easy as we already have our data in memory:
  // Need to create our texture objects
  glGenTextures(2, textures);
  
  // now load in our map texture into textures[0]
  glBindTexture(GL_TEXTURE_2D, textures[0]);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 40, 40, 0, GL_RED, GL_UNSIGNED_BYTE, mapdata);

We start with creating two texture objects by calling glGenTextures. Textures is simply an array of unsigned int 2 ints large just like our other GL objects.
We create one for our data map and one for our tile map which we will load in a minute.
We then need to bind the texture we wish to interact with using glBindTexture. Any texture related commands we issue after that effect that texture. First we call glTexParameteri twice to tell OpenGL how we want to filter our image. This determines how OpenGL will interpolate our image if it is rendered at a higher resolution or when it is scaled back. We used GL_NEAREST here as our data map should not be interpolated at all. We also set our image wrapping to clamp to edge. This means that we do not "tile" our image but that the edges of the images are the borders for our texture lookups.
Finally we load our texture map. Note that we're loading it as a single channel image (hence GL_RED).

For our tile map we will use the image loader that can be found in the STB library. I've mentioned STB before as we're using the truetype font logic embedded within to render our text.  It is a wonderful library containing a collection of handy functions to do various things. Loading image files that we can then use as textures is one of those.
  // and we load our tiled map
  data = stbi_load("desert256.png", &x, &y, &comp, 4);
  if (data != 0) {
    glBindTexture(GL_TEXTURE_2D, textures[1]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
 
    stbi_image_free(data);
  };
The code here isn't all that different from loading our mapdata. We are using stbi_load to load our PNG file and we are setting our filter to a linear filter.
After we've loaded our texture into GPU memory we no longer need to retain the copy we just loaded and can free the data by calling stbi_image_free.

Note: we do need to include our image library and add our implementation #define in main.c.  
P.S. Another library to look at is SOIL. SOIL is a library that uses stb_image but adds various file formats and many handy wrappers for loading images directly into OpenGL texture objects.

First method - render code


The first method we'll use to render our map is pretty straight forward but somewhat wasteful. But it has some advantages in flexibility. Basically what we are going to do is take our entire 40x40 map and turn it into a 40x40 mesh (well x2 as we need two triangles to render one tile).

We are not however going to build the mesh using vertex buffers. Instead we're going to off load all this to the GPU. Basically what we are going to do is call glDrawArrays (a simpler version of glDrawElements), tell it to draw triangles and tell it the number of vertices involved (40 tiles wide * 40 tiles high * 2 triangles * 3 vertices) and we're doing this without giving it any data.

The result is simply that our vertex shader gets called for each of our vertices and OpenGL will start rendering triangles for every 3 vertices. We'll need to build all our data within our shaders.

Note: we do still need a vertex array object to encapsulate our state even though we hardly load any state.

Before we get to our shaders that do the heavy lifting this is the code in our render loop that eventually will result in our map being drawn:
    // now tell it which textures to use
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[0]);
    glUniform1i(mapdataId, 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, textures[1]);
    glUniform1i(tileId, 1);
    
    // and draw our triangles
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 40 * 40 * 3 * 2);
    glBindVertexArray(0);
Here we see that we inform our shader of our two textures we are about to use. We do this by assigning our textures to be the active textures. We can only have a limited number of textures active at any given time, the number depending on the capabilities of the hardware. The GL_TEXTUREn constants may only be defined up to a limited amount but you can access any active texture index by using GL_TEXTURE0 + n instead.

Drawing our map is now a question of making our VAO active (which is pretty much an empty VAO) and calling glDrawArrays.

Note that earlier on in our source code we're loading different shaders and retrieving our mapdataId and tileId uniforms.

Shaders


We're going to stick with our reverse order and look at our fragment shader first. Just to recap, our fragment shader gets called for every pixel we draw on screen to determine its color:
#version 330

uniform sampler2D tiles;

in vec2 T;
out vec4 fragcolor;

void main() {
  fragcolor = texture(tiles, T);  
}

Note that we have two new variables. The first is a uniform (so set from our C source) but of type sampler. This tells OpenGL this is linked to a texture resource.
The second is a new input called T which is a 2D vector, we'll be setting this in our vertex shader. This is the texture coordinate within our tilemap that we'll be drawing.
Our main function simply looks up our texture value using the texture function and assigns it to fragcolor.

Our vertex shader however has become fairly complex so we'll handle that part by part.
#version 330

uniform mat4 mvp;
uniform sampler2D mapdata;

out vec2 T;
We still have our mvp matrix but new is another sampler, this time for our mapdata, and our texture coordinate output T.
void main() {
  // our triangle primitive
  // 2--------1/5
  // |        /|
  // |      /  |
  // |    /    |
  // |  /      |
  // |/        |
  //0/3--------4

  const vec3 vertices[] = vec3[](
    vec3(-0.5,  0.5, 0.0),
    vec3( 0.5, -0.5, 0.0),
    vec3(-0.5, -0.5, 0.0),
    vec3(-0.5,  0.5, 0.0),
    vec3( 0.5,  0.5, 0.0),
    vec3( 0.5, -0.5, 0.0)
  );

  const vec2 texcoord[] = vec2[](
    vec2(         0.0, 31.0 / 256.0),
    vec2(31.0 / 256.0,          0.0),
    vec2(         0.0,          0.0),
    vec2(         0.0, 31.0 / 256.0),
    vec2(31.0 / 256.0, 31.0 / 256.0),
    vec2(31.0 / 256.0,          0.0)
  );
Much like in C code we can define arrays and that is just what we're doing here. We're using two small arrays to create primitives for the two triangles that make up each tile. The first array contains our vertex coordinates for a uniform square centered on 0.0, 0.0, 0.0. The second array contains the coordinates for one tile as if our tile is our top right most tile. Note that as with everything else our coordinate system is unified so our texture map is 1.0 wide by 1.0 high. As our real texture map is 256x256 we need to divide our coordinates.
  // now figure out for which tile we are handling our vertex
  int v = gl_VertexID % 6;
  int i = (gl_VertexID - v) / 6;
  // and for which cell
  int x = i % 40;
  int y = (i - x) / 40;
gl_VertexID is an index given to our shader that tells us which of our 9600 vertices we're currently handling. By taking modulus 6 of this we know which of our 6 vertices for each tile we are handling, that then gives us i which indicates the tile we are currently dealing with. From i we can then determine the x and y of the cell within our map.
  // figure out our vertex position
  vec4 V = vec4((vertices[v] + vec3(float(x - 20), float(y - 20), 0.0)), 1.0);
  
  // scale it to a usable size
  V.xy *= 100.0; 

  // and project it
  gl_Position = mvp * V;
Here we calculate V which initially builds a 40.0 by 40.0 mesh. We scale this up to a 4000.0 by 4000.0 map and finally project it onscreen using our mvp. Remember we set our mvp so that the height of our window is considered to be 1000.0 and the width adjusted for aspect ratio so our map is roughly 4x larger then our screen can display.
  // now figure out our texture coord
  int ti = int(texture(mapdata, vec2((float(x) + 0.5) / 40.0, (float(y) + 0.5) / 40.0)).r * 256.0);
  int s = ti % 8;
  int t = (ti - s) / 8;
  T = texcoord[v] + vec2((float(s * 32) + 0.5) / 256.0, (float(t * 32) + 0.5) / 256.0);
}
Finally using our x,y cell coordinates we lookup our value in our mapdata for that cell. Note again our texture map coordinates being unified so we need to divide the coordinates by 40.0 (the +0.5 is to ensure we get the center of our pixel). The output of our texture function is an RGBA value (vec4) but our map only uses the red channel so we grab only our red value. Finally our color also is unified so we need to multiply by 256.0 to get our actual tile index value (ti). We then modulo 8 our tile index to determine the offset of our tile in our tilemap texture and assign the result to T (again unified, so divide by 256.0).
OpenGL does the rest:)

Navigating our map


If we'd compile of sample at this time OpenGL would nicely draw the center of our map but only about 1/4th of our map but we have no way to interact with it so it is time to look at some basic keyboard interaction.


We'll add support for WASD controls for moving up/left/down/right. We'll also add support for rotating our map using our O and P keys.

There are two ways of handling this. When we press our key our keyboard callback function gets called with a GLFW_PRESS action, when we release the key we get a GLFW_RELEASE action but more importantly if we keep our key pressed we'll also get GLFW_REPEAT calls every couple of ticks. Checking for GLFW_PRESS and/or GLFW_REPEAT is one way to handle this (and this we will use), the other is checking the status of the key by calling glfwGetKey within our update loop.

Finally important to note is that we will do all our key handling in our main.c but add actions to our engine for our movements. This allows us to call the same actions based on other inputs as well (say a mouse, or joystick, or touch) at some later stage without needing to make changes to our core game engine.

We're going to start with our O and P keys for rotating our map.

For this we're going to define our view matrix properly in our source code first as this is what we'll be modifying using our keystrokes (we're kinda changing our 'camera', the map doesn't move, it is our viewpoint to the map that changes).
All we need to do to kick this off is change our view matrix to be a global variable and to initialize it as an identity matrix in engineLoad instead of in our render loop.

For rotating we add the following method to our engine.c source:
void engineViewRotate(float pAngle) {
  vec3 axis;
  mat4 rotate;
  
  mat4Identity(&rotate);
  mat4Rotate(&rotate, pAngle, vec3Set(&axis, 0.0, 0.0, 1.0));
  mat4Multiply(&rotate, &view);
  mat4Copy(&view, &rotate);
};
And then in our main.c we change our key callback to:
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
  if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
    glfwSetWindowShouldClose(window, GL_TRUE);
  } else if ((action == GLFW_PRESS) || (action == GLFW_REPEAT)) {
    switch (key) {
      case GLFW_KEY_O: {
        engineViewRotate(-1.0);
      } break;
      case GLFW_KEY_P: {
        engineViewRotate( 1.0);        
      } break;
      default: {
        // ignore
      } break;
    };
  };
};
Thanks to being able to rotate our map our WASD function gets a bit more complex because we need to take our rotation into account. Thankfully we can use our view matrix to help us here. We'll use a small part of our matrix to rotate our movement vector and get the following function to move our view:
void engineViewMove(float pX, float pY) {
  vec3 translate;
  
  // we apply our matrix "transposed" to get counter rotate our movement to our rotation
  vec3Set(&translate
    , pX * view.m[0][0] + pY * view.m[0][1]
    , pX * view.m[1][0] + pY * view.m[1][1]
    , 0.0  
  );
  
  mat4Translate(&view, &translate);  
};
And then enhance our keyboard callback:
      case GLFW_KEY_W: {
        engineViewMove( 0.0,  5.0);        
      } break;
      case GLFW_KEY_S: {
        engineViewMove( 0.0, -5.0);        
      } break;
      case GLFW_KEY_A: {
        engineViewMove( 5.0,  0.0);        
      } break;
      case GLFW_KEY_D: {
        engineViewMove(-5.0,  0.0);        
      } break;


So where from here?

So that takes care of our basic approach but it is still far from a complete solution. This is however as far as I will take it for now.

So what are things that you'll want to approve or can do with this next?
  • The most important improvement is that this approach is nice for a 40x40 map but once it gets larger we've got a lot of overhead. Say our map is 1000x1000 cells that is a lot of triangles being created every frame 90% of which are off screen. You could load the 1000x1000 cell map as your texture but only create a, say, 10x10 mesh centered on the screen and use an offset to render only a 10x10 slice of cells in your larger map. That wouldn't take more then a few additional lines of code in the vertex shader to accomplish.
  • In line with that, you'd probably want to convert your mapdata into an image file so you can just load it. Do be sure you don't use a format such as JPEG as it will corrupt your data. 
  • Add multi layered maps. A good optimization is to load the map into a single texture using R for the first layer, B for the second, G for your third and A for your fourth and render your map 4 times (vary the Z!). Do make sure when using the alpha channel that you do not use 0 as the GPU assumes that is transparent and will discard the RGBA value. Do use your alpha channel in your tile map for your overlapping layers, remember that even when blending is turned off, an alpha of zero means the pixel is not drawn. You could even keep 4 separate view matrices to create parallax scrolling.
  • Use a perspective matrix instead of an orthographical matrix and you can create an F-Zero type map. Add a texture map as a height field and you've got the beginnings of a full 3D terrain renderer (we'll definitely revisit that once we start looking at 3D).
  • Finally, add support for other input devices like the mouse (we'll add mouse support later on in the series).
And our end result:


What's next?

As with the previous large write-up I'll be rereading all this over the next couple of days so expect some edits as I find dumb typos and stuff.

The next post we will look at a different technique to render the same map as in this write-up. While the method described here is very flexible it suffers from wasted processing power as we're rendering far more triangles then we actually display on screen. Often enough the added flexibility far outweighs the overhead especially if we apply the enhancement in our first bullet point up above there is another technique which means we only render what we need.


Friday, 27 March 2015

The world, in 2 dimensions (part 5)

Many of these tutorials I've seen come and go jump straight into 3D rendering at this point. I feel however there is still plenty to talk about before we get there. Heck there are still plenty of really nice 2D games being build today and then there are a legion of games where a good mix of both 2D and 3D techniques are applied. Even when you're working on a nice 3D game or 3D application 2D still has a place when it comes to the user interface.

In the following parts of the series we'll look a bit in some handy 2D rendering techniques and learn a little bit more about OpenGL in general along the way.

But first we need to do a bit of house keeping and a bit more preparation then I initially thought.

On the subject of Mac and Windows I'll probably stop splitting these posts in two. We're at a point where things compile on both platforms and while there is a little bit extra needed on Windows in adding stuff to the makefiles manually that's hardly a big deal. I'll try and make the changes as I go but I may lag behind actually testing things on Windows so expect that you may need to correct the makefile. I'll generally be using the issue tracker on my GitHub page to clear up those sorts of issues keeping this writeup relatively clean.

The other is that I'm finally going to strip most of the code from main.c that I feel doesn't belong there. A lot of the code has been moved into engine.h and engine.c. The thing needed a name and I couldn't think of anything better but the main principle we'll be following is that we've got a straight forward interface into whatever it is we're running.

At this point in time that interface consists of 4 methods:
- load, called at the start to load resources
- update, called in our loop and basically should implement updating data structures etc. as a result of either time passing or user interaction
- render, called in our loop to render our stuff
- unload, called to unload resources

The idea here is that you could have multiple sets of these "engines" or "parts" or whatever you wish to call them. You could have one for our start screen that shows game stats and a start game button, and then a second for the game itself or maybe for each level of your game. When the user presses start game you unload the start screen and load the games first level and start calling the update/render functions for the game. Once the player dies or reaches the end of the game and its game over, the game is unloaded and the start screen is reloaded. We may one day bring even more structure to this. We will see. In C++ you would most likely create a base class to define this interface and implement each part of your program as a subclass.

As you'll notice I also left the viewport and clear logic in main.c. Think of an application where we call the render function of our game first but then after that we call a UI render function that renders the UI on top of that. We can swap out the game part depending on the level we're running but reuse the UI part.

The split between update and render logic is simply good structure but won't really come to shine until we start to create some real user interaction.

Off course for now we'll simply be running examples so there isn't any major user interaction and we'll leave that for the future.

Windows in GLFW

At this point I want to go briefly into the subject of windows and GLFW.

GLFW has the ability to open up multiple windows but that is something I won't go into here. For the applications I have for GLFW I've not needed this (yet) and I think it's just a distraction to what we're trying to achieve here.

GLFW also has the ability to open up our main window full screen. What it lacks at this point in time is an easy way to switch between windowed mode and full screen. You could close one window and open another to switch between modes but as this creates a new OpenGL context it means reloading all your resources as well. I don't know if context sharing might be a solution here but I've not seen any samples of code dealing with this. I do hope to add this into our little frame work at some point.

For now I'm pretty happy just having it as a toggle and just starting my application with a setting or switch. At this stage I'm managing it with an #ifdef block but in due course we'll improve on this:
#ifdef GLFW_FULLSCREEN
  window = glfwCreateWindow(1024, 768, "GLFW Tutorial", glfwGetPrimaryMonitor(), NULL);
#else
  window = glfwCreateWindow(640, 480, "GLFW Tutorial", NULL, NULL);
#endif
Note that we always give a screen resolution as the first two parameters. In full screen mode GLFW will find the closest screen mode that will match this resolution. The 3rd parameter is a window title, the fourth identifies the monitor on which we wish to run full screen. The 5th and final parameter allows context sharing between windows but I've yet to experiment with this.

We'll look into querying the OS for available resolutions later. Do note that I noticed some issues with full screen on my retina display, even though the documentation suggest the querying the frame buffer size it seems this isn't working correctly in full screen mode at the this time (or I'm doing it wrong:)).

Window coordinate system and 2D projections

It's time to take a closer look at how our coordinate system works.

Lets start with glViewPort. glViewPort basically tells OpenGL what area of the window we're rendering too using screen coordinates.  Generally speaking you would set this to the full size of your window.

Inside OpenGL however the world is upside down and unified and we have no knowledge of the underlying screen resolution.


As far as OpenGL is concerned the world starts in the bottom left of your screen at (-1.0, -1.0) and ends at the top right at (1.0, 1.0). This always takes some getting used to as historically developers are used to (0.0, 0.0) being at the top left of the screen. This is how Dos, Windows and Mac OS have always handled screen coordinates.

However in 3D having Y pointing 'up' makes sense, in a 3D world going up means going higher so an increasing Y value should equal going up.

Another interesting thing about this approach is that our render area seems kinda square even when most monitors are in some sort of landscape format. If you would draw a square with equal sized sides, say both width and height are 1.0, you'd actually end up with a rectangle as it is all stretched to fit your widescreen monitor.

The orthographic projection that we used in our examples so far allows us to specify what coordinate system we want to use. We specifying a left, right, bottom, top, near and far value and from that a matrix is calculated that handles the transformation from our coordinate system to the one OpenGL requires. We'll ignore the near and far values until the next section but here is how the left, right, bottom and top parameters work.

In our example we saw that we left our top and bottom at -1.0 and 1.0 respectively but we set our left and right based on the aspect ratio of our window size which at 640x480 would be roughly 1.33:
The nice thing is that whether we actually open our window at 640x480, 800x600, 1024x768 or any other resolution, our triangle would take up an equal portion of the screen. If we go wide screen the aspect ration will add space to the sides but our triangle would still have the same relative height.
Also if we were to draw a square, it would actually come out looking square as an equal number of pixels are used for the height and the width of the square.

If the real resolution is important to what you are rendering, which it can be when you are dealing with UI, you can easily adjust your projection matrix to accommodate this:
mat4ortho(&mvp, 0, ScreenWidth, ScreenHeight, 0, 1.0, -1.0);
For our 640x480 window this results in a 1:1 mapping to our screen coordinates:

For our examples going further we'll be using a "virtual" resolution with (0.0, 0.0) at the center of the screen, the height of our screen being 1000 units and the width adjusted by aspect ratio:
mat4Ortho(&mvp, -ratio * 500.0, ratio * 500.0, 500.0f, -500.0f, 1.0f, -1.0f);
We end up with a coordinate system like so for a 4:3 screen:
The main reason for the Y is down choice is for rendering out text but also, as I mentioned before, it's a more natural choice for 2D rendering. It does mean that to render our triangle, we'll have to make it 500x larger as well.

The real resolution is still important to us, if we're on a small screen, say we're working on a small tablet, we might end up with unreadable text if we don't accommodate for this. Also bitmaps may be scaled down too much and look ugly.
Equally so, if we're on a high resolution monitor we may start to get an interface that is pixelated when bitmaps are scared up.
Often this is easily solved by having bitmaps in multiple resolutions and simply selecting the set of bitmaps that best suit the resolution you are rendering to.

The depth buffer

While we haven't used it yet, and its turned off by default, a depth buffer is pretty much a standard thing you get in any modern day rendering engine. We can already see in our example so far, even though all we're doing is 2D rendering, that we are specifying all our coordinates in 3D. Even our orthographic projection had a near and far value that determine our translation into the Z axis.

As long as using our depth buffer is disabled things are simply drawn in the order they are given. This can be good enough in many 2D applications as that is the natural order in which you draw things anyway but even in 2D applications using the Z buffer can be very handy.

With a Z buffer the depth you specify for what you are drawing will determine what will overlap. If you draw two squares that overlap, the square that is "closest" to you will be drawn over the square that is furthest away even if it is drawn first.

Note that in our orthographic projection we defined near as being 1.0 and far as being -1.0 but it actually ends up with 1.0 being furthest away:
I'm not sure why the near/far planes for orthographic projections behave like this, obviously a larger Z value being further back does make sense.

Note that when we start using 3D projections later on we'll find out our near plane defines our closer value but there is lots more going on in this case.

The one situation that you do need to be mindful off is once you start using transparency. We'll discuss this in more detail later on but the order in which you draw does become important when you want to draw something semi transparent as you can't draw "underneath" something you've already drawn.

Rendering text

There is one omission in OpenGL that we should deal with first and that is rendering out text. Yes there were some simple text rendering functions in OpenGL once but you guessed it, we can't use them anymore now that we're using our programmable pipeline.

There are a number of libraries out there that deal with this. Some simple, some bloated, some completely beyond what we need.

We'll be using fontstash which was written by Mikko Mononen and while it originally only had an implementation for OpenGL 1/2 I added OpenGL 3 support to it some time ago. Fontstash uses the truetype to bitmap capabilities of one of the STB libraries to generate a texture with which we can render out text. We'll get back to STB later as we'll be using it to load textures as well but it is worth mentioning that STB now also has an implementation called stb_easy_font that allows direct rendering of text through OpenGL. I haven't looked into that yet as its very new but it sounds like a promising alternative.

For now to demonstrate how to use fontstash we'll add a simple frames per second counter to our interface.

First we need a font to actually use to draw our text in. Fontstash comes with a font called DroidSerif which we're also using in our sample code. For now I'm still putting all these support files in the resources folder, on Mac they are nicely placed within our bundle, on windows they're next to our exe. We'll deal with this another day.

While we include our header files through our engine.h header file we again want our implementation to be compiled along with our main.c file so our first change is way at the top of main.c:
#define FONTSTASH_IMPLEMENTATION
#define GLFONTSTASH_IMPLEMENTATION
In our engine.h we're including the header file for both fontstash itself and our OpenGL 3 implementation:
#include "fontstash/fontstash.h"
#include "fontstash/gl3fontstash.h"
Note that the files themselves are stored in our 3rdparty subfolder.

The rest of our logic is all in our engine.c file. First, way at the top we define a few globals. Again I'm using globals to keep these examples simple, you probably want to put things in a bit more targeted scope in your application.
// and some globals for our fonts
FONScontext * fs = NULL;
int font = FONS_INVALID;
float lineHeight = 0.0f;

// and some runtime variables.
double rotation = 0.0f;
double frames = 0.0f;
double fps = 0.0f;
double lastframes = 0.0f;
double lastsecs = 0.0f;
FONScontext is a structure that holds all the information fontstash needs to render out fonts. This is basically a struct that contains all our data from our raw font data to our vertex array and vertex buffer in OpenGL.
font is simply a variable that holds an index to one of the fonts loaded into our context.
lineHeight is the height of a single line of text for the font we are using.

Besides these 3 variables that we use for our font rendering we've also got a few variables for calculating our FPS.

And a little further down we have added a load and unload function that loads, and unloads, our font:
void load_font() {
  // we start with creating a font context that tells us about the font we'll be rendering
  fs = gl3fonsCreate(512, 512, FONS_ZERO_TOPLEFT);
  if (fs != NULL) {
    // then we load our font
    font = fonsAddFont(fs, "sans", "DroidSerif-Regular.ttf");
    if (font != FONS_INVALID) {
      // setup our font
      fonsSetColor(fs, gl3fonsRGBA(255,255,255,255)); // white
      fonsSetSize(fs, 32.0f); // 32 point font
      fonsSetAlign(fs, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); // left/top aligned
      fonsVertMetrics(fs, NULL, NULL, &lineHeight);
    } else {
      engineErrCallback(-201, "Couldn't load DroidSerif-Regular.ttf");       
    };
  } else {
    engineErrCallback(-200, "Couldn't create our font context");
  };
};

void unload_font() {
  if (fs != NULL) {
    gl3fonsDelete(fs);
    fs = NULL;
  };
};
These two functions are called from our engine load and unload functions.

Next is our engine update function in which we calculate our frames per second value:
void engineUpdate(double pSecondsPassed) {
  rotation = pSecondsPassed * 50.0f;

  frames += 1.0f;
  fps = (frames - lastframes) / (pSecondsPassed - lastsecs);

  if (frames - lastframes > 100.0f) {
    // reset every 100 frames
    lastsecs = pSecondsPassed;
    lastframes = frames;
  };
};
Calculating our fps is simple a matter of dividing the number of frames drawn by the number of seconds that have passed. We reset our counter every 100 frames so our fps is calculated over the last few seconds that have passed.

It is important to know that GLFW limits the frame rate depending on your monitors refresh rate. While our sample application drawing our triangle theoretically could render hundreds of frames a second (if not thousands) my Macbook caps out at 60 fps. Most of the time our application is thus twiddling its thumbs doing absolutely nothing.

Finally at the end of our engines render function, after we've drawn our triangle(s), we draw our FPS counter:
  // change our state a little
  glDisable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendEquation(GL_FUNC_ADD);
  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);  
  
  // now render our FPS
  if ((fs != NULL) && (font != FONS_INVALID)) {
    char info[256];
    
    // we can use our same projection matrix
    gl3fonsProjection(fs, (GLfloat *)projection.m); 

    // what text shall we draw?
    sprintf(info,"FPS: %0.1f", fps);
        
    // and draw some text
    fonsDrawText(fs, -ratio * 500.0f, 460.0f, info, NULL);
  };
Before we render out our text we disable our Z buffer, as we're just drawing our text on top of everything else, but enable blending.
This deserves a little more explanation.
To make text look a little prettier we do not draw the text in solid colors but apply some anti-aliasing to make it all look a bit smoother. As a result we need to blend the color of our text with the underlying colors we have already drawn on screen. This is done by using the alpha channel in our texture map that contains our pre-rendered characters for our font.

Doing so is relatively costly as we're not just writing colors to our screen buffer, we're first reading the current color and applying a blend. As a result by default this behavior is turned off. By default the alpha channel works as an on/off switch. If the alpha value is 0 pixels are simply not drawn. If the alpha value is non-zero the pixel is drawn without any blending. Nice and fast.

For rendering our font we need to turn blending on. More specifically we need to tell OpenGL we want to use standard alpha color blending meaning our alpha value in our color is directly used to blend the underlying color with the color we are drawing. There are other blending modes that allow us to do some really funky color effects but that is a subject for another day.

Next, as the shader embedded in fontstash has no idea of our projection matrix, we need to give it a copy. We're reusing the projection matrix we already have here but obviously we could create one specifically for rendering text. It all depends on what we are doing.

Then we simply use sprintf to create a nicely formatted string and finally we use fonsDrawText to write out our text. This function takes our font context, an left and top coordinate, our text and an optional end of text pointer (if NULL the text is assumed to be zero terminated).

After this we should have a nice FPS counter at the bottom left of our screen.

What's next?

At this point in time we've got our original triangle spinning but with a Z-buffer enabled and an FPS counter, still not all that spectacular. We've done more then I was expecting when I started writing this part so and I suspect the next step will be of equal length.

Next part we're going to dive into one of the most common used techniques for 2D games and that is using a tiled map to draw the background to a game.