Sunday, 7 February 2016

Adding a skybox (part 20)

Okay, I'm back with an other installment. Before we get to our skybox, I've starting cleaning up some code and polishing a few things up so lets look at that first.

A new folder structure


One thing that was really starting to annoy me was dumping all the files into our resources folder and together with our .exe on Windows. One thing that really makes this a pain is that we are dealing with different path delimiters on each platform. I'm not entirely happy with how I solved things yet but at least we've now got a bit more of a manageable structure. Fonts go into Fonts, Model files into Models, Textures into Textures and Shaders into Shaders.

Also on Windows I decided to just create a Resources folder to put everything into. I haven't tested the Windows build yet so expect some typos or things I've forgotten to change in the makefile.

For Windows I'll eventually change the working folder to the Resources folder so we can make it work in line with our Mac code and maybe look into a path parser that replaces path delimiters for the correct platform. We'll see.

Materials and shaders


I've made a start in better structuring the materials and how they use shaders. It is now the material that is leading and sets up our information OpenGL needs to render the material and the shader to use is simply a property of the material now. That means that if we don't know what material to use for an object we simply use a default material we've added to our material list at the start (obviously none should be missing).

For now there is a little loop in load_objects that assigns the shaders to our materials:
  // assign shaders to our materials
  node = materials->first;
  while (node != NULL) {
    mat = (material * ) node->data;

    if (mat->reflectMap != NULL) {  
      mat->shader = &reflectShader;
    } else if (mat->diffuseMap != NULL) {          
      mat->shader = &texturedShader;
    } else {
      mat->shader = &colorShader;
    };
    
    node = node->next;
  };
This is a bit of a place holder for the time being, this still needs to change to something better.

Texturemaps


One thing that was a bit of an eyesore was that I was loading the same texture maps multiple times in my material loader as they were reused for different materials.
I've given the texture map loading code it's own place in a new library called "texturemap.h".

It works along the same line as all the other support libraries we've created so far but has one new trick up its sleeve. It maintains a linked list of all texture maps currently loaded when texture maps are loaded by calling "getTextureMapByFileName".
As we do not know whether we are getting a texture map returned that was already loaded or a new texture map the rule is that if the function calling this function wishes to retain the texture map, it needs to do so.

In this I'm following a rule that that comes from objective-C (where I first encountered retain/release) that a function that begins with "new" returns an already retained object and that a function that begins with "get" returns an object that needs to be retained.
We've got no need for autoreleasing objects just yet and I'm hoping we won't but I'm starting to wonder if I shouldn't create some sort of base library for my retain/release approach. Another day...

When we look at material.h we can see that we no longer use texture id's directly but go through our texturemap object. We can also see that we no longer have our load functions but instead have the functions "matSetDiffuseMap" and "matSetReflectMap" that set a pointer to our texturemap object.
If you look at the code for these two methods you again see a trick that stems from obj-C's property structure where we nicely release our old map and retain our new one.

As a result we can do something like this (as we indeed do for our skybox):
matSetDiffuseMap(mat, getTextureMapByFileName("skybox.png", GL_LINEAR, GL_CLAMP_TO_EDGE));
Our getTextureMapByFileName function checks i we've already loaded our map and if not loads it, our matSetDiffuseMap assigns it to our material and retains it. If loading fails a NULL pointer is returned by getTextureMapByFileName but matSetDiffuseMap deals with this just fine.

While all our materials will release their texture maps as they get destroyed it is important to remember our cache still retains all the loaded textures. We must not forget to unload this as well as we clean up at the end:
void unload_objects() {
  // free our object data
  tmapReleaseCachedTextureMaps();
  ...

Our skybox


So first off, what is a skybox? This is a technique often applied to create our far away background, say the map of the sky (what's in a name right?). By projecting our background on a very large box for which we're on the inside we create a background that automatically adjusts itself as we move our camera around.

Seeing we've got a tie-bomber a sensible skybox would be a star scape but I wanted something that shows off the technique a little better. Doing a bit of googling there are hundreds if not thousands of graphics available if you're not able to make one yourself. I came across RB Whitaker's site who's written up a lot of interesting graphics things but using C# and XNA. He also has a some graphics he's kindly donated to the public domain so there you go.

We often see the texture maps laid out in a T shape to minimize the number of borders that don't line up nicely. This is important because we interpolate our textures when rendering. I've however gone for a 2x3 layout to minimize wasting space in my texture but we have to keep in mind that we can't move our textures up to each border as we get some strange artifacts on the edges.


You'll see in the code where we generate our cube mesh I've left a 2 pixel border around each image.
The image itself show the front, back, left, right and finally top and bottom images in 3 rows. They are simply mapped on a box or cube.

Three more things set our skybox apart from rendering other objects.
First is that it's inside out. Since we are inside of our cube we need to see it's insides.
Two is that we don't want to apply any lighting but render the cube as is. We've thus got our own shader in the form of the shader programs "skybox.vs" and "skybox.fs". The code should be easy to read by now.
The third is that we're not using our model matrix but always center our skybox on the user. The idea is that the background is infinitely away so it only moves as you look around.

Infinity itself isn't really possible so the size of our skybox is dictated by our far plane. Now our far plane in our example stands at 10,000.0, our box is also 10,000 wide, high and deep. The observant amongst you will thus say that it's only going to be at a distance of 5,000 and that would be true, but as the box rotates the furthest point would be just over 8,500 (the furthest point is at 5000,5000,5000, calculate the distance to that point using pythagoras theorem).

This is one reason why some use a sphere as a "skybox" but a sphere is harder to texture without getting artifacts at the poles.  The principles stay the same.

The code for adding the skybox can be found in load_objects:
  ...
  // add the skybox last so it renders at the end
  mat = newMaterial("skybox");                // create a material for our skybox
  mat->shader = &skyboxShader;                 // use our skybox shader, this will cause our lighting and positioning to be ignored!!!
  matSetDiffuseMap(mat, getTextureMapByFileName("skybox.png", GL_LINEAR, GL_CLAMP_TO_EDGE)); // load our texture map (courtesy of http://rbwhitaker.wikidot.com/texture-library)
  mesh = newMesh(24, 36);                     // init our cube with enough space for our buffers
  strcpy(mesh->name,"skybox");                // set name to cube
  meshSetMaterial(mesh, mat);                 // assign our material
  matRelease(mat);                            // and release it, our mesh now owns it
  meshMakeCube(mesh, 10000.0, 10000.0, 10000.0, true);  // create our cube, we make it as large as possible
  meshFlipFaces(mesh);                        // turn the mesh inside out
  meshCopyToGL(mesh, true);                   // copy our cube data to the GPU
  llistAddTo(meshes, mesh);                   // add it to our list
  meshRelease(mesh);                          // and release it, our list now owns it
  ...
Note that small changes were added to our cube generation code to handle the correct texture mapping.

As the comment mentions we also add our skybox last. It can even make sense to leave it our of our render buffer and just render it separately at the end of our render loop. While the parallel processing on our GPU may result in more being rendered then we expect the skybox covers the entire screen while at the end of our render loop many object will already obscure it. Rendering it last does increase the chance of our Z-buffer discarding many pixels. Still the shader is simple enough for this to potentially not have much of an impact.

And here is the end result (sorry no movie):




And yes yes, before anyone complains, obviously I'm still using the star scape as a reflection map isn't the proper thing to do. I simply haven't had the time to create a reflection map out of our skybox. That might be a nice thing to actually automate, I'll give that some thought as that in itself would be a nice topic for a tutorial.

One last thing, I did try and see how this came out in stereoscopic mode and I was pretty happy. I have no idea if it will keep holding up if we find a need to push the far plane further out.

Download the source here

What's next?


As I mentioned in my last write-up, I'll be working on a separate project for a bit and as a result enhancing our little engine as I go. Hopefully as a result I'll be able to write up some more sessions like this one as I add more things.

One that is nearly ready to go and an important step forward is to not use our meshes directly but indirectly through instances of those meshes and these will be held within a 'tree'.
This will allow us to render the same object many times while positioned at different locations.
So next up, we'll change our single tie-bomber to a fleet of tie-bombers :)




Wednesday, 3 February 2016

Using a joystick (part 19)

Ok, it's time for another little sidetrack here:)

I bought myself a little USB 4 axis gamepad and decided to have a look at the cross platform joystick input functions that are part of GLFW.

First off, for those people using Mac, Windows guys have a one up on us. Joystick support seems to be a lot better on Windows. You can use XBox one controllers right off the bat, many bluetooth controllers work, etc.
On Mac it seems a bit more of a secret art. There are many 3rd party drivers you can install to make things work. Interestingly enough it's more that a lot of it seems a bit hidden. I was glad to find out the gamepad I bought worked without a problem with GLFW even though Mac OS X itself seemed oblivious to it being connected.

The gamepad I got is a $20 wired Logitech F310. Important here is that it has two modes that may exist on other gamepads as well. There is a switch on the back label X <=> D.
X stands for something like XInput and is the default and seems to be directly related to DirectX support, needless to say it does not work on the Mac.
D stands for DirectInput, it needs to be selected before connecting the device to the Mac and then GLFW will pick it up straight away.

Now when you look at my checked in source code you'll see that I've written a wrapper for the GLFW joystick code but in this particular case, that really is extreme overkill. Again my reasoning for this is that I want to be able to use a different framework for platforms that GLFW doesn't support.

In this case it is absolute overkill and I'll just discuss the GLFW calls here.
GLFW has defined a number of constants from GLFW_JOYSTICK_1 to GLFW_JOYSTICK_LAST to index each joystick. Basically the first joystick you connect to your system will be GLFW_JOYSTICK_1, the second will be GLFW_JOYSTICK_2, up until the number of joysticks you've got connected. Once numbered a joystick will remain accessibly through that constant so even if I disconnect joystick #1, joystick #2 will remain joystick #2.

To find out if a joystick is connected you simply call:
if (glfwJoystickPresent(GLFW_JOYSTICK_1) == GL_TRUE) {
  // process our joystick info

Now an interesting difference with the joysticks is that unlike the keyboard or mouse, you don't get any events that the user made any input. Instead you poll the joystick state. That may sound wasteful but it is not, a user may be holding a control stick firmly in a certain direction and you'll need to react to that, it is far more likely you will need to react to the joystick in every frame.

This is something you would add for each joystick in your update function (engineUpdate in our example). Good practice is to query your input devices, do whatever changing to positions of object you need to do, etc. in an update function, then call your render function to render the result.

For this also note that we send pSecondsPassed to our update function. This is a fairly precise timer and if you keep it's value from last frame you can obtain a delta of time elapsed. It's often a good idea to use this delta in your adjustments so that on slower machines the users input will seem just as responsive as on faster machines. I'm not doing this in our current example however.

On a quick side note purely for informational purpose you can call glfwGetJoystickName if you wish, it will return a device name for your joystick.

The important things on the joystick are the buttons and axes. Now here is where things get a little tricky because every devices has a different layout. You may thus need to react differently depending on the number of axes and buttons are available and maybe offer the user with the ability to map them to certain actions.
We can generally be certain that most joysticks have 2 axes and 2 buttons that are the primary controls and that further controls are logically added.

GLFW allows you to get the current state for all axes with the function glfwGetJoystickAxes. It returns a pointer to a buffer of floats, each entry referring to an axis with its value between -1.0 and 1.0.
The state of our buttons can be queried by calling glfwGetJoystickButtons. This returns a pointer to a buffer of ints, one entry per button with the value 0 or 1, one being the button is pressed.

For example you could do:
if (glfwJoystickPresent(GLFW_JOYSTICK_1) == GL_TRUE) {
  int axesCount, buttonCount;
  float * axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axesCount);
  int * buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttonCount);
  
  // process our joystick info
  ...
};
It's no more difficult then that.

Our sample application now allows for both joystick and keyboard input to rotate our Tie-bomber.

Download the source here

What's next

So I've gotten a bit sidetracked on a new project that I hope to be able to tell more about in the near future so that's put building my platformer game on the backburner a bit.

Not to worry however, this side project is using GLFW and using my tutorial as a base and I'm planning to feed back lessons learned into this series.

More to come soon!

Thursday, 28 January 2016

Adding a setup window to our tutorial project

I've just checked in some changes to our tutorial project that I'm going to keep aside of our tutorial series. I'm not entirely sure if I'm going to keep this, enhance it or replace it.

The problem I kept running into is that we had hard coded our window mode and window size in our main.c source code. This mainly because writing a GUI could be a tutorial series all by itself and it detracts to what I'm trying to do here.

But then my eye fell on this library called AntTweakBar which is listed on the GLFW site. It is a really neat little library but it has a few rather troublesome drawbacks:
  1. it doesn't compile on the Mac anymore, lucky these were easy to fix up after a bit of reading on the support forum
  2. the mappings to GLFW are still based on GLFW 2.7, not 3.0. I've got them working except for our keyboard mappings. To properly fix this I'll also have to recompile the windows binaries
  3. there isn't a lot of freedom in the controls and layout of the window
  4. it is no longer maintained
Also the library compiled as a dylib which I wasn't too fond of. I changed the makefile to output a static library instead and that is included in the mac build on my github site.

I'm planning to do more with the library as time goes by and do a proper tutorial writeup if I do enjoy using it. If so I might be tempted to start enhancing it. But equally so I may end up replacing it.

So for now I'm just going to give a very brief overview of the changes that I've checked into our tutorial project.
First off, all the new support code is embedded into two new files:
  • setup.h - our header
  • setup.c - our source code
Yes, no single file approach for this one.
The library has a single public function which is SetupGLFW and all it does is open up a window that provides 3 dropdowns:
  • choosing the monitor to use for running in fullscreen, or the option to choosing windowed mode
  • choosing the resolution (if fullscreen)
  • choosing the stereo mode for optional 3D stereo rendering
After the user makes a selection a structure is populated which can be used in the calling function to properly setup a window like so:
  ...
  if (!SetupGLFW(&info)) {
    glfwTerminate();
    exit(EXIT_FAILURE);
  };
  
  // make sure we're using OpenGL 3.2+
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  if (info.monitor != NULL) {
    glfwWindowHint(GLFW_RED_BITS, info.vidmode.redBits);
    glfwWindowHint(GLFW_GREEN_BITS, info.vidmode.greenBits);
    glfwWindowHint(GLFW_BLUE_BITS, info.vidmode.blueBits);
    glfwWindowHint(GLFW_REFRESH_RATE, info.vidmode.refreshRate);
    if (info.stereomode == 2) {
      glfwWindowHint(GLFW_STEREO, GL_TRUE);    
    };

    errorlog(0, "Initializing fullscreen mode %i, %i - %i",info.vidmode.width, info.vidmode.height, info.stereomode);
    window = glfwCreateWindow(info.vidmode.width, info.vidmode.height, "GLFW Tutorial", info.monitor, NULL);
  } else {
    errorlog(0, "Initializing windowed mode %i, %i - %i",info.vidmode.width, info.vidmode.height, info.stereomode);
    window = glfwCreateWindow(info.vidmode.width, info.vidmode.height, "GLFW Tutorial", NULL, NULL);
  };
  
  if (window == NULL) {
    errorlog(-1, "Couldn''t construct GLFW window");
  } else {
    ...

The end result looks like this:

Tuesday, 26 January 2016

Stereoscopic rendering (part 18)

Ok, I decided to take a little fun sidestep. Those who've been following my blog for awhile know I've played around with this before. Back then it was just a bit of fun but a month or two ago Ian Munsie dropped by our little game developers meetup at the North Sydney TAFE and showed of the fun he was having with stereoscopic rendering. He brought in his AlienWare laptop that has a 3D capable screen using active shutter glasses and demoed a copy of Abe's Oddessy that had been enhanced for stereoscopic rendering. Needless to say it very much impressed me and I very much believe the industry gave up on 3D stereoscopic games way too early.

As I have a 3D TV I've played a number of titles on my PS3 and XBOX 360 that allow stereoscopic rendering but in order to cope with the additional work often go down to a 720p resolution and it does suffer but is still a lot of fun. The demo Ian gave was at full resolution of his laptop (full 1024p I'm guessing) and simply looked rock solid.

Unfortunately my AlienWare laptop does not have stereoscopic support (though the NVidia chip may be able to output a 3D signal over HDMI which I hope to test later on) nor does my trusty Macbook.
My Panasonic 3D TV however does have the capability to use a split screen signal.

It is incredibly easy to convert our little example to allow stereoscopic rendering. It is also incredibly easy to do it wrong. Below are two links which explain this stuff way better then I possibly could:
There are basically 3 things that we need to know about to do things right.
The first is that it is not as simple as moving the camera position slightly. The eye looks forward in a parallel direction but the created frustrum is slightly skewed. Have a read through the NVidia document as it explains it way better then I could possibly word it and more importantly, it has pictures :)

The second is the distance between the eyes, the "intraocular distance". Every persons eyes are spaced slightly differently and getting this value wrong can create a very uncomfortable experience or even ruin the effect completely. The NVidia document mentions this distance on average is 6.5cm which is the value I'm using and seems to work well on my TV screen but it is likely something you'd want to make configurable. Also it's not a given a unit in your 3D world is actually equal to 1 cm so play around with what works.

The last thing that we're introducing is the "screen projection plane" or convergence distance. This is the distance from the eye(s) at which something rendered overlaps. If you render something at that distance it would be rendered in the same place for both eyes. Anything closer to the view will seem to pop out of the screen, anything after that will seem to be behind the screen.

Obviously things popping out of the screen is the bit that gives the 3D thrill, but it is also the one that can confuse the eyes if it isn't fully visible as the 3D effect is ruined if part of the object falls outside of our viewing frustrum as a different part of the object is obscured for each eye.
For games having the convergence distance be relatively close and having everything seem to be behind the screen is possibly a good practice.

The biggest drawback of stereoscopic rendering is that we need to do everything twice, everything needs to be rendered for each eye. Now there is a lot that can be reused like shadow maps, occlusion checks (with the footnote you might end up rendering something twice that is only visible for one eye, though that should already be a red flag), calculating model matrices effected by animation, etc.
We have none of those in our example yet but basically we would do those as part of rendering our left eye, and then reusing the information for our right eye, or doing it as part of our engineUpdate call which we do before our rendering happens.

A new matrix function in math3d.h


First thing is first, we need a new function for calculating a projection matrix that is adjusted for each eye. Now I've gone down the route to have our view matrix based on our mono view and have the adjustment for each eye fully embedded in the projection matrix. There is an argument for adding the needed translation to the view matrix and we may end up doing so but for now this seems to make sense.

Anyway our new function is pretty much a refactor as it's presented in the OpenGL document linked earlier on in this writeup:
// Applies a 3D projection matrix for stereo scopic rendering
// Same parameters as mat4Projection but with additional:
// - pIOD = intraocular distance, distance between the two eyes (on average 6.5cm)
// - pProjPlane = projection plane, distance from eye at which our frustrums intersect (also known as convergence)
// - pMode = 0 => center eye, 1 => left eye, 2 => right eye (so 0 results in same projection as mat4Projection)
mat4* mat4Stereo(mat4* pMatrix, MATH3D_FLOAT pFOV, MATH3D_FLOAT pAspect, MATH3D_FLOAT pZNear, MATH3D_FLOAT pZFar, float pIOD, float pProjPlane, int pMode) {
  MATH3D_FLOAT left, right, modeltranslation, ymax, xmax, frustumshift;
  vec3 tmpVector;

  ymax = pZNear * tan(pFOV * PI / 360.0f);
  xmax = ymax * pAspect;
  frustumshift = (pIOD/2)*pZNear/pProjPlane;

  switch (pMode) {
    case 1: { // left eye
      left = -xmax + frustumshift;
      right = xmax + frustumshift;
      modeltranslation = pIOD / 2.0;
    }; break;
    case 2: { // right eye
      left = -xmax - frustumshift;
      right = xmax - frustumshift;
      modeltranslation = -pIOD / 2.0;
    }; break;
    default: {
      left = -xmax;
      right = xmax;
      modeltranslation = 0.0;
    }; break;
  };
  
  mat4Frustum(pMatrix, left, right, -ymax, ymax, pZNear, pZFar);
  mat4Translate(pMatrix, vec3Set(&tmpVector, modeltranslation, 0.0, 0.0));
  
  return pMatrix;
};
Note that if you specify pMode as 0 our IOD and projection plane have no effect and the function is as if we're calling mat4Projection and I'm using that fact later on in the implementation.

Changes to main.c


As we may extent this later on or even completely replace our main container by something else I've decided to put the logic for whether we render stereoscopic inside our main.c file.
For now there is no switching between modes, we simply have a variable called stereo_mode with the values:
0 = mono rendering
1 = stereo rendering in split screen
2 = stereo rendering with full left/right back buffers (untested)

For option 2 we need to give another window hint to initialize our stereo buffers, the main render loop now looks like this:
      ...
      ratio = (float) frameWidth / (float) frameHeight;
      
      switch (stereo_mode) {
        case 1: {
          // clear our viewport
          glClearColor(0.1, 0.1, 0.1, 1.0);
          glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);      

          // render split screen 3D
          int halfWidth = frameWidth / 2;

          // set our viewport
          glViewport(0, 0, halfWidth, frameHeight);
      
          // and render left
          engineRender(halfWidth, frameHeight, ratio, 1);          
          
          // set our viewport
          glViewport(halfWidth, 0, halfWidth, frameHeight);
      
          // and render right
          engineRender(halfWidth, frameHeight, ratio, 2);          
        }; break;
        case 2: {
          // render hardware left/right buffers
          
          // clear our viewport
          glDrawBuffer(GL_BACK);
          glClearColor(0.1, 0.1, 0.1, 1.0);
          glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);                

          // set our viewport
          glViewport(0, 0, frameWidth, frameHeight);
          
          // render left
          glDrawBuffer(GL_BACK_LEFT);
          engineRender(frameWidth, frameHeight, ratio, 1);
               
          // render right
          glDrawBuffer(GL_BACK_RIGHT);
          engineRender(frameWidth, frameHeight, ratio, 2);          
        }; break;
        default: {
          // render normal...

          // clear our viewport
          glClearColor(0.1, 0.1, 0.1, 1.0);
          glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);      
          
          // set our viewport
          glViewport(0, 0, frameWidth, frameHeight);
      
          // and render
          engineRender(frameWidth, frameHeight, ratio, 0);          
        }; break;
      };
      
      // swap our buffers around so the user sees our new frame
      glfwSwapBuffers(window);
      glfwPollEvents();

      ...
Note that I've moved the screen ratio outside of our engineRender function, mainly for our splitscreen option as we're now only have half our space but still need our normal aspect ratio to properly render things.

Note that our glClear ignores our glViewPort in our split screen rendering which is why we do this upfront. With our left/right buffer we set it to updating the full backbuffer when clearing.
Next we either set our viewport to the right half of the screen or select the correct back buffer for each eye and call engineRender for each eye.

Changes to engine.c


Besides using our pRatio instead of calculating it we simply switch over our projection matrix call to our new mat4Stereo function:
  ...
  // distance between eyes is on average 6.5 cm, this should be setable
  mat4Stereo(&matrices.projection, 45.0, pRatio, 1.0, 10000.0, 6.5, 200.0, pMode);
  ...
As mentioned we've hard coded our IOD to 6.5. I've also set our convergence distance to 200.0, as our tie bomber is rendered roughly 550.0 away from our eye that seemed a pretty sound option.

One unexpected little thing was moving the camera around so the little earth globe is positioned right in front of the camera it actually really nicely popped out of the screen :)

I have not done anything to the projection matrix for our FPS counter. Obviously when rendering stereoscopic more care needs to be taken with this as bad positioning can be very intrusive. You can either start rendering this using a proper Z positioning or make sure that everything that is being rendered is indeed behind the convergence distance as rendering 2D the way that is currently implemented positions everything at that distance.

Below is a stereo rendered video of the end result, it actually worked great playing it on youtube on my 3D TV, I even managed to play it on my phone with google cardboard. 


Now I wish I had a Oculus Rift lying around to hook up to my laptop :)

Download the source code here

So where from here?


So obviously this is a very basic stereoscopic setup. There are a few improvements needed such as making a few things configurable and when rendering for a HMD adjusting the image for lens distortion needs to be added.

What's next


As I mentioned in my previous post I've got some ground to cover at this end in creating some 3D content before I can move on with my platformer. I'll probably throw up a few more of these intermediate posts though coming out of the summer holidays I'm not sure how much spare time I'll have in the coming weeks. We'll see:)



Sunday, 24 January 2016

Fixing my NVidia troubles....

Owh what a ride on Windows. The latest check-in on Github now compiles and runs correctly (for me). Older ones probably will work fine for most people as well though I did miss out on a few files in the makefile. Do note that I did upgrade to the latest Visual Studio Express so if you're on something older you may need to grab older build files from the GLFW website.

The problems I was having were all related to my recent Windows 10 upgrade. After a lot of headaches and googling I found that I wasn't the only person having troubles. To be exact some people got badly stuck halfway through the Windows 10 upgrade.

I guess I got lucky as I'm using an AlienWare laptop with both an Intel graphics card and an NVidia GT 555M and the intel card being used by default to save on power usage (and thus battery life).

Anyways, something went wrong in the drivers. Something went even more wrong in the automatic driver update which kept telling me I had the latest drivers but that was a pretty lie.

NVidia fixed the issues a while ago and released new drivers and as soon as I installed those, all my problems (including a few that had been haunting me with certain games) went away.

Saturday, 23 January 2016

GLFW 3.1.2, GLEW 1.13.0 and latest stb_images

I've updated my GLFW tutorial files to use these latest versions of GLFW, GLEW and STB. I've also made several changes to make the source compile on Windows. These changes have now been checked into the master branch.

Unfortunately compiling GLFW on my new Windows 10 machine has proven to be troublesome and it keeps crashing. Even bringing it back to the basic example of GLFW it is proving not to be very happy. I think it's related to Windows 10 and still using VC 2010 compilers as the older examples won't run properly either.

So sorry to you windows guys out there, it's all still dodgy. If anyone has any suggestions please feel free to provide feedback on my github page.

Friday, 22 January 2016

Loading models #2 (part 17)

Well it sometimes helps going places were there is no internet, I managed to write most of the model loading code over the weekend. I was originally planning on doing this in two more parts but here we go with an extra long write-up and it thus took me a little while longer before this was ready:)

First off, I made two structural changes to the mesh object that need to be highlighted.

The first one is that I added a retain count to the object. When you create a mesh object the retain count is set to 1. If you pass the pointer around and store the pointer in an array you can call meshRetain to increase the retain count.
When you no longer need to keep the pointer to the object you call meshRelease, this decreases the retain count. Once the retain count reaches 0 the memory is freed up.

The second change is that I changed the two arrays that hold the mesh data. I introduced a new support library called dynamicarray.h that manages an array that holds data. A very simple implementation where you would use std::vector in C++.

The release/retain approach I've started retrofitting to a number of structs where I found this handy.

Also before I forget, I've compiled GLFW 3.1.2 for Mac and removed the windows files until I get the change to compile GLFW for windows so please be aware of that if you're on windows.

New support libraries

I've added a number of new support libraries with rudimentary code to do some common tasks. I'll either enhance them as time goes by or replace them if I find existing C libraries that others have created that save me the trouble. The really cool ones I have are usually C++ libraries, alas.
I'll briefly discuss them here but point to the code within for further details as they are really out of the scope of what we're trying to learn here.

As with the other libraries, they are once again single file implementations.

errorlog.h

This needs very little explanation, I got tired of copying the pointer to the error log function around so I placed it in a library that I'm using everywhere. I kinda disliked forcing an error log on everyone who uses the libraries but it's pretty easy to gut it or replace it with your own internals.

I haven't retrofitted it into 2D libraries yet but will soon.

varchar.h

Standard C libraries work pretty well with a fixed sized string but for variable length strings there is a lot of remembering to reallocate buffers and whatnot. Here is a simple implementation of an object that resizes the buffer containing the string for you. While the structure does have a length variable to indicate how long the string is, the string is also zero terminated so it can be used with standard C string functions. It's basically a poor mans std::string implementation. It also uses a retain counter.

Basically only the variable text, which contains the string, should be accessed directly as a read only variable or things will start falling apart. Note that this can be NULL pointer if no string is loaded.

So far the following functions are available:
newVarchar allocates memory for a new string object returning a pointer and initializing the struct held within.
varcharRetain increases the retain count.
varcharRelease decreases the retain count and frees up the memory once retain count reaches zero.
varcharCmp compares our string with a zero terminated string similar in function to strcmp.
varcharPos tries to find the position of a zero terminated string within our string.
varcharMid returns a substring of our string as a new varchar object (which must be released!).
varcharAppend appends a string at the end of our string.
varcharTrim removes and spaces and tabs from the start and end of the string.

As time goes by I'll add further string functions that makes sense, this hardly even covers the basics but it was all I need for now.

dynamicarray.h

I mentioned this in my introduction, this is a simplified implementation of a dynamic array similar to std::vector. It can hold any type of data but seeing C doesn't understand templating we can only tell our logic how many bytes long each entry in our array is. It is up to us to properly cast this data.

As such you should treat all variables in our struct as private and not access them directly.

newDynArray allocates memory for a new array, you specify the size in bytes for each entry.
dynArrayFree frees up the memory held by the array. If the data you placed within contains pointers those are NOT freed.
dynArrayCheckSize increases the amount of memory allocated for the array if required, handy to give a starting size and prevent unnecessary calls to realloc.
dynArrayPush adds a new entry to our array, increases the size of memory allocated if required. The contents of the data is copied into the array.
dynArrayDataAtIndex returns a pointer to a location within the array so you can either extract or change the data for that entry.

Again there are a few basic functions still missing, like being able to remove an entry or merge two arrays but again, this is all I need for now. One that we'll add soonish is the ability to quick-sort our array.

linkedlist.h

This is maybe one of the more complex support libraries. A linked list is an array where each entry in the array is allocated separately and the list is formed by the entries pointing to each other like a chain.
The big advantage of this is that it is fairly inexpensive to make changes to rather large lists, there is no need to reallocate all the data.
The disadvantage is that it is harder to traverse the list, you can basically only loop from the first to the last, and from the last to the first entry (and this later only only because I've implemented both a next and a previous pointer). It can thus be expensive to find an entry somewhere in the middle of the list.

There are two structs here, one is a container for meta data about our list, the second is a struct allocated for each entry. The data in these structures should be treated as read only.

The most common code fragment you'll need is to loop through all the entries of a linked list and you do this as follows:
llistNode * node = myList->first;
while (node != NULL) {
  mystruct * object = (mystruct *) node->data;

  // do your thing here

  // next!
  node = node->next;
};

I've chosen to store a pointer to the data 'held' within the list instead of copying the data like we've done with our dynamic array. As a result I've made the implementation optionally aware of the retain and release functions of our of our objects. When a pointer to an object supporting retain/release is added to our linked list, the object is automatically retained. If we remove an entry, or get rid of the linked list all together, the relevant objects are released.

newLlist allocates memory for our new linked list object, you can optionally provide a retain and release function for your entries.
llistFree frees up the memory for our new linked list object, note that if a release function was provided when the linked list was created, the release function is called for every entry currently in the linked list.
llistAddTo adds an entry to our linked list. If a retain function was provided it is called.
llistRemove finds the entry that points to the specified object and removes it. Only if both a retain and release function was provided do we release the object.
llistMerge adds all entries from one list to another. If a retain function was specified the objects are also retained. The source list is not modified.
llistDataAtIndex returns the pointer to the object (data) added at this index in our linked list.

Wavefront


We're nearly ready to start enhancing our application to actually load 3D object data. There are many formats out there each with their strengths and weaknesses. The biggest issue I always run into, especially with more modern modeling software, is that those tools are designed for creating rendered animations with a focus on realism and not always best suited for use in games. Often the formats are bloated with functionality that gets in the way of what we're doing. Formats are usually designed to fit well within the functionality offered by the modeling software, not optimized to how the hardware can most easily render the data.

One thing I often find myself doing is creating loaders for easily available file formats but then writing the data back out in a format that matches the engine I've build.

The format I've chosen for our tutorial is an oldy but goldy file format created by Wavefront. There are two things that really attract me to this format:
1) it's text which makes it easily readable and easy to learn and understand,
2) it's very too the point, it just contains mesh data, not other stuff we don't need.

It has only got two drawbacks:
1) it allows polygons with any number of vertices. We thus need to split them into triangles which doesn't always give a nice result. We could off course load the polygons as patches and use a geometry shader, but that is a topic for a tutorial far in the future.
2) our positions, normals and texture coordinates are 3 separate arrays which each vertex of our polygons maintains separate indices for. As we now know OpenGL doesn't work this way and we thus need to rearrange the data. This can be time consuming. Also all objects contained within a single file share a single array (even though they are often split up) which again we don't do.

Now there really are two formats that we're dealing with. An .mtl file which describes the materials used, and the .obj file which contains the actual mesh data.

We're going to look at the first one first but as we didn't have materials yet in our system we need to design those first.

Owh, and before I forget, for our example today I managed to get my hands on a Star Wars Tie-Bomber 3D model modeled by someone named Glenn Campbell. The model was downloaded from this great site called TF3DM. This is like a marketplace for 3d models with loads of free models and paid models to chose from. Many of the free models are only for non-commercial use but that suits us just fine. I chose the tie-bomber mostly because it was relatively small.
The only downside is that it doesn't use any texture mapping but as we've already applied texture mapping to our globe it's not a big miss. I have added (but not tested) all the code to load texture maps and texture coordinates for our meshes. I could be mixing up with 3DS files but it may be the texture will have ended up upside down:)

Materials


I've consciously kept materials apart from the shaders that actually bring those materials to life. A material describes the properties of the materials that forms our 3D objects, the shader then uses that to draw it appropriately. All the logic is contained in a new library called materials.h

Now we do need to talk about the shaders first.
You'll see that I've now got 3 separate shaders. Our texture based shader we had before, a single color shader and a reflection shader (more on this later). I could have written a single shader with if statements but due to the way graphic cards work even the "false" condition can end up being executed and its results discarded if the parallel processing deems it can use some idle core to do that work on the assumption the condition may be true. If we do expensive lookups that 9 out of 10 times are not needed, we're just burning GPU cycles for nothing.
The effects can however be combined so in theory we need a large combination of shaders to do our rendering.
This is something we'll look into at some later stage as it's a topic on its own but our approach will remain similar to what we're doing now. For each material we'll chose the best shader for the job. Right now we do that in our render loop but we may move that to an initialization stage at some point.

The focus thus lies on a structure that retains information for a particular material:
// structure for our material info
typedef struct material {
  unsigned int  retainCount;      // retain count for this object
  char          name[50];         // name of our material
  
  GLfloat       alpha;            // alpha for our material
  vec3          matColor;         // diffuse color for this material
  vec3          matSpecColor;     // specular color for this material
  GLfloat       shininess;        // shininess of this material
  GLuint        textureId;        // id of our texturemap
  GLuint        reflectId;        // id of our reflectionmap
} material;
This structure may grow as we keep adding functionality to our rendering logic but it has all the basics.
Most of the variables in our struct can be changed directly but some, like our texture maps, must be set through the appropriate functions.

Because a material may be shared amongst multiple models and we want to properly manage the memory usage we again use a retain count for this object.

We've then implemented the following methods:
newMaterial allocates memory and initializes a new material.
newMatList returns an empty linked list object properly configured to store material objects into.
matRetain increases the retain count on our object.
matRelease decreases the retain count on our object and frees up memory used once the count reaches zero.
getMatByName searches a linked list containing materials for a material with the given name. Note that if you want to hang on to that material you must retain it!
matLoadTexture loads the specified image file as the texture map for our material.
matLoadReflect loads the specified image file as the reflection map for our material.
matParseMtl loads the materials defined in the specified wavefront mtl data and adds them to a linked list.

This last function, matParseMtl is where the magic happens. Just like with our shaders I've kept loading the material file separate and we thus provide already loaded data to our function.
We also use a materials linked list to store our materials in. There is no protection to ensure a material with the same name doesn't appear more then once in our materials list, we assume we're taking care with loading files for now. We load our material data as follows:
  // create a retainer for materials
  materials = newMatList();
  ...
  // load our mtl file
  text = loadFile("tie-bomber.mtl");
  if (text != NULL) {
    matParseMtl(text, materials);
      
    free(text);
  };
  ...
I'm not going to go through the entire code for loading these files but the parser is fairly simple. We extract each line of the file one by one and place it in a varchar object. We skip any empty lines or lines starting with a # as this is a comment.
We then make sure we trim any unwanted spaces and then find the first space in the string. The text returned here identifies what data we're loading at.
Each new material always starts with a line starting with newmtl and we create a new material object each time we encounter it.
I've added comments for each other type of line that we encounter and currently parse but it should be pretty self explanatory.

Object file


Now that we've loaded our material information we can load our object file. The object file tells us which material file it uses but I'm ignoring this as we've already loaded it.

We've taken the same approach of loading the data into a buffer first and then calling the function meshParseObj that has been added to our mesh3d.h file.

A single wavefront object file can contain any number of "objects" which are subdivided into "groups". Think of this as the objects being cars, bikes, street lamps, etc, while the groups subdivide an object such as a car into the body, wheels, windscreen, etc.
I'm currently ignoring the objects taking a "single file is single object" approach as this requires a layer on top of our mesh objects which we have not yet build.
Just like with our materials we provide a linked list containing mesh3d objects to which we'll add the object we load. We also provide our materials linked list so we can look up materials and assign them to our mesh3d objects.

An important rule is that each mesh3d object is rendered using a single material. So our mesh3d objects really fit well with the definition of groups in the wavefront files.

While a bit crude, and we do support it, both objects and groups are optional and if not specified we should assume a default object is required.

Just like with materials, there is no problem in adding two mesh3d objects into our list that happen to have the same name.

Parsing the object file is remarkably similar to parsing the materials file, it is no surprise the structure of the files are relatively the same. We do see that as we're loading position vertices, normals and texture coordinates we are loading those into temporary arrays.
Then as we're loading faces we're keeping track of combinations of these 3 units for which we've added vertices to our mesh3d object. Especially on large wavefront files this can churn through a fair amount of memory and isn't implemented particularly fast but as I mentioned before for a real production application I would implement some sort of conversion tool to export the data into a format more closely mimicking my 3D engine.

The source is too bulky to present here so I again refer to the comments I've made in the source.

Changes to shaders


Next I needed to enhance the shader implementation. Our reflection shader is the first that works in world space so I needed to ensure my shader had a normal matrix without our view matrix applied and I needed to communicate the location of our eye.

I also decided to add in a number of other standard IDs likely used for our materials to our shaderStdInfo struct and ensure those IDs got setup correctly. I also added our light position but in this case it's a temporary fix. More about that much later in the series.

I've also introduced a structure that simple holds our main 3 matrices (perspective, view and model matrices) called shaderMatrices so it is easy to pass this to our shaderSelectProgram function.
I also introduced a structure called lightSource that holds both our world position of our light and the position adjusted by our view matrix.

But the biggest addition is sending our material to our shaderSelectProgram function to communicate the properties of our material.

This will do fine for what we're doing so far. The biggest issue we're having right now with our shader is that each time we render a mesh, we initialize the entire shader. This is something we'll have to work on as our project becomes more complex. Again however, a topic for later.

As mentioned earlier, we now have our textured shader, a single color shader where instead of getting the color from our texture map we just set our color we now also have a reflection shader. Soon I'll write one session in my tutorial on better organizing our shaders but this split will do us fine for now.

The reflection shader, also often called environment shader, is an old trick that can be very useful. I included it because our model had a number of maps setup this way.

The very last material in our material file, CHROME_GIFMAP probably showcases its use the best as a method for rendering shiny metal. Unfortunately there are only two small parts in the model that use this. The body of the tie-bomber also has a reflection map but the map provided with the model seemed to be a template. I think the original idea was to reflect stars. I've added a starmap but it's far to bright making the effect look very fake but in doing so it serves as a pointer to the limits of this technique.

The technique is very simple, you take the vector from your eye to where it hits the surface, then use our normal to reflect that vector to see where the light that is reflected comes from. You then use a texture map to look up the color. It works amazingly well on curved surfaces but less on flat surfaces.
It is often used to simulate metal as this works well with very blurry maps that don't reveal to much detail and give the effect away. It is also a great way to simulate ambient light by taking an actual spherical map of the surroundings, blur the image and multiply with the ambient color of the material and voila. Another use is for reflections on a shiny or wet floor or on the surface of the water where again the reflection is distorted and you can get away with using a static texture.

But as a true reflective surface like a mirror, this technique often falls short especially on flat surfaces. You'll quickly realize the reflection isn't related to whats around you. But luckily there are better solutions to that which we'll undoubtedly get to in due time.

Changes to render loop


And now on to our last step, actually rendering our whole tie-bomber. Our fixed two meshes we were rendering has now been replaced by a loop that loops through our linked list and renders each mesh individually:
  if (meshes != NULL) {
    int count = 0;
    llistNode * node = meshes->first;
    while (node != NULL) {
      mesh3d * mesh = (mesh3d *) node->data;
      if (mesh == NULL) {
        // skip
      } else if (!mesh->visible) {
        // skip
      } else {
        bool doRender = true;
        // set our model matrix, this calculation will be more complex in due time
        mat4Copy(&matrices.model, &mesh->defModel);

        // select our shader, for now we have 3 shaders that do different types of shading
        // eventually we'll add something that allows us to make combinations
        if (mesh->material == NULL) {
          shaderSelectProgram(&colorShader, &matrices, &sun, NULL);
        } else if (mesh->material->alpha != 1.0) {
          // postpone
          alphaMesh aMesh;
          GLfloat z = 0; // we should calculate this by taking our modelview matrix and taking our Z offset
        
          // copy our mesh info
          aMesh.mesh = mesh;
          mat4Copy(&aMesh.model, &matrices.model);
          aMesh.z = z;
        
          // and push it...
          dynArrayPush(meshesWithAlpha, &aMesh); // this copies our structure
          doRender = false; // we're postponing...
        } else if (mesh->material->reflectId != MAT_NO_TEXTURE) {          
          shaderSelectProgram(&reflectShader, &matrices, &sun, mesh->material);          
        } else if (mesh->material->textureId != MAT_NO_TEXTURE) {          
          shaderSelectProgram(&texturedShader, &matrices, &sun, mesh->material);          
        } else {
          shaderSelectProgram(&colorShader, &matrices, &sun, mesh->material);
        };

        if (doRender) {
          // and render it, if we fail we won't render it again
          mesh->visible = meshRender(mesh);          
        };
      };
    
      node = node->next;
      count++;
    };
  };
Chosing which shader to use is a bit simplistic but fine for where we are now however I'm making an exception for any material that isn't fully opaque but has a level of transparency.

Opaque materials are easy, the Z-buffer will sort things out, but with transparent materials we need to know the underlying color and blend. Also this blending process is relatively expensive as it requires reads from our output buffer so we gather our transparent meshes in a list, and then postpone rendering them till later:
  // now render our alpha meshes
  if (meshesWithAlpha->numEntries > 0) {
    // we should sort meshesWithAlpha by Z
  
    // we blend our colors here...
    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    for (int i = 0; i < meshesWithAlpha->numEntries; i++) {
      alphaMesh * aMesh = dynArrayDataAtIndex(meshesWithAlpha, i);
    
      mat4Copy(&matrices.model, &aMesh->model);
      if (aMesh->mesh->material->reflectId != MAT_NO_TEXTURE) {          
        shaderSelectProgram(&reflectShader, &matrices, &sun, aMesh->mesh->material);          
      } else if (aMesh->mesh->material->textureId != MAT_NO_TEXTURE) {          
        shaderSelectProgram(&texturedShader, &matrices, &sun, aMesh->mesh->material);          
      } else {
        shaderSelectProgram(&colorShader, &matrices, &sun, aMesh->mesh->material);
      };

      aMesh->mesh->visible = meshRender(aMesh->mesh);
    };    
  };
The above code still has various holes in it but I wanted to keep things as simple as I could. As mentioned in the comments we should sort this list by distance, to render transparent meshes that are distant first.

But that is something that we'll be changing in the entire loop. Our initial loop will eventually result in two dynamic arrays, one with opaque meshes, one with transparent meshes, which subsequently are rendered one after the other. I'm getting ahead of myself but this approach has a number of reasons:
  1. meshes will have relationships to each other, a wheel of a car will have a model matrix that positions it relative to the center of the car. When rendering the car we take the model matrix of the car to position the car, but we combine the model matrix of the car with the model matrix of the wheel to position the wheel
  2. meshes may be off screen. We can exclude many models that are part of our world but currently not on screen from the rendering process.
  3. we can sort our opaque meshes in a way that makes best use of our shaders and as mentioned before sort our transparent meshes by distance.
I'm sure I'm leaving some other important ones out.

Well after all this work, we have our tie-bomber....

As you can see it isn't without fault. One problem that isn't apparent in the video is that the model isn't properly centered. This is something I'll need to deal with in the model loading code in due time.
The other issue is that the dark indented areas on the 'wings' aren't fully rendered on one side of the model, at this point I'm not sure what the cause is.

Download the source here

What's next


Well unfortunately I'll likely be taking a bit of a break. I might throw in a few blog posts on several subjects such as shaders and other smaller improvements I'll be making but the next step will be to pick up our platform game again and start porting it to 3D.

The biggest hurdle at this point is that it requires a lot of non-programming work before I can actually build the next step. Copying and pasting images into a tilemap is easy, creating 3D models takes a lot of time. But we'll get there in the long run :)