30
MAR
2010

Racing Restart

As is painfully obvious from the lack of posts on this blog, I pretty much abandoned my racing game for the iPhone. However, I still have the code and I am once again motivated to work on it. So here we go!

First of all, I have started to create my 3D content in Cheetah3D. The main reason for this is that Cheetah3D is very easy to use and it is scriptable using Javascript. So far I have created a script that generates a road mesh for me and I have imported meshes from a racing game I created for Windows in 2003.

Below is an image rendered by Cheetah3D, which might not look like much, but I am just getting started!

First Render


14
FEB
2010

My Thoughts on the iPad

It seems like a lot of people were disappointed or simply underwhelmed by the iPad. I think they are missing the point. The iPad isn't really about a hardware revolution; it's about refinement of the already revolutionary multi-touch user interface and App Store software distribution model.

The App Store allows the individual and small companies to release software to the public without investing money and time in any kind of payment and distribution infrastructure. The fact that both EA and a 15 year old can release a game, and get paid for it, in the exact same way, levels the playing field for the first time.

For the user, the App Store provides a comprehensive and complete catalog of software. If it exists, it's in the App Store. If it's in the App Store, it's safe to download. No viruses, no hassle, no dependencies. The App Store simply takes software distribution into the 21st century.

Many people have gripes with the closedness of the App Store. I can understand that perspective, but I think that the closedness is actually a feature and not a problem. The App Store control minimizes the effort the user has to put into his/her software purchases, making the purchase more likely.

There is a fine line between control in the interest of the user and in the interest of competition stiflement, but so far I think Apple has succeeded in keeping on the right side of the line, albeit with a few notable transgressions.

The multi-touch user interface made mainstream by the iPhone and iPad Touch devices is now a proven type of user interface. Millions of people use multi-touch enabled devices every day. It is time to leverage that familiarity and create software that is almost as natural to use as a sheet of paper. Personally, I'm hoping that the iPad and its inevitable successors will eventually make the mouse-and-keyboard combo fade into obscurity.


30
OCT
2009

Simple Vertex Shader for 2D Graphics

As I mentioned in an earlier post, OpenGL ES 2.x replaces the fixed function pipeline of OpenGL ES 1.x with a programmable pipeline. This means that you have to write a vertex shader program and a fragment shader program that perform the operations that the fixed function pipeline used to do. What you gain is an unprecedented level of flexibility as you are no longer restricted to hardwired calculations in the GPU.

The vertex shader processes polygon vertices and the fragment shader processes rasterized pixels. There are lots of tutorials and documentation on the basics of shaders, so I won't go into all the details. Suffice it to say that the vertex shader program is run once for each vertex in the polygon and the fragment shader is run for each polygon pixel to be drawn. Instead, I will show you a couple of examples of extremely basic vertex shaders.

Each polygon vertex has a position in 3D space. One of the primary responsibilities of the vertex shader is to project the vertex position into 2D space. By default, the OpenGL 2D coordinate system of the iPhone looks like this:

Without view projection matrix

This is an orthographic projection, which means there is no sense of perspective and the depth component of the vertex position is more or less ignored. For a (non-degenerate) polygon to be visible in the default coordinate system, at least one of its vertices would have to have an XY position within the range shown in the image.

The following vertex shader simply copies the position attribute of the vertex to the gl_Position variable, which is the predefined vertex position that will be used by the hardware when the polygon is rasterized and drawn.

attribute vec4 position;
 
void main()
{
    gl_Position = position;
}

This is probably really the simplest possible vertex shader you can write without hardcoding the position. However, you rarely want to use the default coordinate system, because it has an awkward aspect ratio and no perspective projection.

Let's say we want to create a 2D game where the coordinate system matches the resolution of the iPhone screen, which is 320x480 pixels. We wouldn't have to worry about perspective in this case, since we're not going to do 3D, so it's ok to still have an orthographic projection. Basically, we want a coordinate system that is more common when dealing with 2D pixel graphics in general, where the upper left corner is the origin. Here's an illustration of the coordinate system we want:

With view projection matrix

To accomplish this, we would have to create a view projection matrix that could be used to project the vertex position from our coordinate system to the default coordinate system. To perform the projection transformation in the vertex shader, we simply multiply the vertex position with the view projection matrix:

attribute vec4 position;
 
uniform mat4 viewProjectionMatrix;
 
void main()
{
    gl_Position = viewProjectionMatrix * position;
}

As you may have noticed, the position is represented by a vector with 4 components instead of 3. This is because the vector has to be expressed in homogenous coordinates for affine matrix transformations to be possible. If this makes no sense to you, it is safe (most of the time) to just accept that there has to be a 1 in the fourth component of the vector.

The last remaining piece of the puzzle is to create the view projection matrix, so that it can be fed to the vertex shader and bound to the viewProjectionMatrix variable. Those of you who have a perverted math fetish may go ahead and calculate the transformation matrix manually, but the rest of us will use a preexisting math library function instead. I highly recommend the free GLGX library for matrix and vector operations. It was created specifically for use with OpenGL. Moreover, GLGX is heavily inspired by the DirectX utility library D3DX. For almost every function in D3DX, there is a corresponding GLGX function.

Using GLGX, you would create the view projection matrix by using the following function call:

GLGXMatrixOrthoOffCenter2D(&viewProjectionMatrix, 0.0, 320.0, 480.0, 0.0);

I'm too lazy to go into how to bind the C matrix to the matrix variable in the vertex shader, so I'll leave it as an exercise to the reader for now.


23
OCT
2009

Broadened Horizons

People at work (and elsewhere) sometimes ask me if I'm a C or a Java programmer. Sometimes they even assume that I don't know how to write C code, based on the product written in Java that I am currently spending most of my time on. This annoys me a great deal.

Before I was asked these questions I never even considered people to be programmers of one specific language. It would be like asking a carpenter if he (or she) is a hammer carpenter or a screwdriver carpenter. In order to be a good developer, or a good carpenter for that matter, you should really know how to use more than one tool. You don't want to be a one trick pony.

It's important to broaden your horizons and see what kind of different languages and paradigms are out there. Learning a new language can really put things into perspective and make you think about programming in a new way.

For example, learning REBOL was the best thing I ever did, because it taught me new concepts that I didn't even know existed. I have been able to use this knowledge in other languages where these concepts may not have been obvious to me otherwise.

I have tried somewhere around 20 different programming languages and I encourage you to do the same, if you haven't already. It will be worth it.


23
OCT
2009

Debugging OpenGL

Hardware accelerated graphics code is hard to debug. This is a fact. I know that from experience. Some of the most frustrating bugs I have ever encountered have been 3D related.

Over the years I have learned that catching the bugs early saves a lot of time and work in the end. It may also prevent you from going insane. I value my sanity, so I have decided to make my debug build help me as much as possible.

Apple recommends that you check the OpenGL error flag often and I concur. However, calling glGetError() too often results in a performance penalty. For this reason, I only check for GL errors in my debug build, where I actually check for errors after every single GL function call. That's how dedicated I am to squashing those pesky little bugs! (I like the movie Starship Troopers because it's all about bug killing.)

To be more specific, I have wrapped glGetError() in a function called checkGL() which programmatically triggers a breakpoint whenever an error has been detected.

Here's some code that you may adopt as your own, as long as you don't blame me if something goes wrong. Yes, this is a disclaimer. If you use any of my code, you are doing so at your own risk, I take no responsibility whatsoever. That said, I don't think there are any problems with this code...

Here's the header file (GLError.h):

#ifdef __cplusplus
extern "C" {
#endif
 
#if TARGET_IPHONE_SIMULATOR
#define BREAKPOINT __asm__ volatile ("int3");
#else
#define BREAKPOINT __asm__ volatile ("bkpt 1")
#endif
 
#ifdef DEBUG_OPENGL
#define CHECK_GL checkGL()
void checkGL(void);
#else
#define CHECK_GL
#endif
 
#ifdef __cplusplus
}
#endif

And here's the implementation file (GLError.m):

#import "GLError.h"
 
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
 
void checkGL(void)
{   
    GLenum err;
    if (err = glGetError())
    {
        switch (err)
        {
            case GL_INVALID_ENUM:
                NSLog(@"OPENGL ERROR: GL_INVALID_ENUM");
                BREAKPOINT;
                break;
 
            case GL_INVALID_VALUE:
                NSLog(@"OPENGL ERROR: GL_INVALID_VALUE");
                BREAKPOINT;
                break;
 
            case GL_INVALID_OPERATION:
                NSLog(@"OPENGL ERROR: GL_INVALID_OPERATION");
                BREAKPOINT;
                break;
 
            case GL_OUT_OF_MEMORY:
                NSLog(@"OPENGL ERROR: GL_OUT_OF_MEMORY");
                BREAKPOINT;
                break;              
 
            default:
                NSLog(@"OPENGL ERROR: 0x%x", err);
                BREAKPOINT;
                break;
        }
 
    }
}


12
OCT
2009

Shady Business

I was surprised to find that OpenGL ES 2.0 does not have support for a fixed function pipeline (FFP) at all. You have to use shaders. Obviously, shaders are powerful and give you a level of flexibility that is not possible with a FFP. Unfortunately, it also means that you have to implement all the calculations of the FFP yourself, even when all you really need are the simple features that the standard FFP provides out of the box in OpenGL ES 1.x.

Initially, this means more work to get something to show up on the screen, but once you have implemented a basic shader, you will have a template to start with for future shaders, so it's really only a one-shot effort.

I wrote my first shader in a sort of shader assembly language back in 2001. Since then various high level shader languages have appeared, which makes writing shaders much less of a pain in the nether regions. I believe HLSL and GLSL are the most well known shader languages and unless I am mistaken the OpenGL ES has its own variation of GLSL called OpenGL ES Shading Language. I highly recommend learning about the shader assembly instructions even if you are only going to use a high level language, because it gives you a better understanding of the fundamental functionality of the shader hardware.

While I have written a lot of shaders over the last few years, I have exclusively been using HLSL, because I have been using the XNA framework on the Xbox 360. The underlying concepts should essentially be the same so hopefully I will be able to pick up the ES shading language quickly. As soon as I have a working example shader, I will post my findings here.


5
SEP
2009

Self-Modifying Fun

An interesting consequence of code being data in REBOL is that it is very easy to modify code on the fly. You can write code that writes code. It's like in the Terminator movies where machines build machines, except that using REBOL probably won't result in judgement day. I hope!

You can use some of the series functions on instances of the function type as well. If you want to access the body of a function you get the second value of the object. Let me illustrate:

f: func [] [print "I am a function"]

The first block is the parameter block and since this function does not take any parameters as input, it is empty. The second block is the body of the function and it contains the code to print the string "I am a function".

So, let's examine the body of the function by getting the second element of the function instance series. Note that >> is the interactive REBOL prompt and == is the result of whatever operation you execute at the prompt.

>> second :f
== [print "I am a function"]

The : character is used to retrieve the value that f is bound to without executing the function. Calling the function yields the following expected result:

>> f
I am a function

OK, let's modify it to print something else.

>> poke second :f 2 "I am a modified function"
== "I am a modified function"

Poke replaces a value in a series with another value at a given index. In the example above, the value at index 2 in the body of the function is replaced with the value "I am a modified function". Since REBOL uses 1-based indexing, the value at index 2 was previously "I am a function". So let's check the source of the function now.

>> source f
f: func [] [print "I am a modified function"]

It seems like the modification worked. Calling the function yields the following result:

>> f
I am a modified function

Being able to modify code like this on the fly is a powerful feature. But, like Spider-Man's uncle Ben used to say: "With great power comes great responsibility". It is very easy to produce self-modifying code that is downright weird. Here is an example:

f: func [:g] [append second :g [+ 1] 1]

What does this function do? It takes a function as a parameter and adds + 1 at the end of the body of that function and then returns 1. There is nothing keeping us from passing the function f as a parameter to itself, so let's see what happens if we do. The : in the parameter specification suppresses evaluation of the parameter, so we can pass in the word bound to the function f instead of getting its value like we'd do with second :f. Here we go:

>> f f
1
>> f f
2
>> f f
3
>> source f
f: func [:g] [append second :g [+ 1] return 1 + 1 + 1 + 1]

Each call to the function modified the function itself, so the returned value was incremented with one everytime. There's no counter variable or anything, we simply added the code for + 1 to the function each time.


6
AUG
2009

Functionally Functional

How do you declare/define a function in REBOL? There has to be some kind of keyword or specific definition syntax for that purpose, right? Nope, I tell you! Nope!

First, let's take a look at some other languages. In C you define a function by sticking to a specific syntax (There is a subtle difference between declaring and defining a function, but let's not think about that right now). Here's an example:

int say_number(int number) {
    printf("%dn", number);
    return number;
}

Javascript has the function keyword for declaring functions:

function say_number(number) {
    document.write(number);
    return number;
}

The Javascript function keyword can also be used for declaring anonymous functions:

var say_number = function (number) {
    document.writeln(number);
    return number;
}

In REBOL, all functions, and I mean all functions, are anonymous. So, defining a function is like using the Javascript keyword then? No, not quite. Here's the REBOL version:

say-number: func [number] [
    print number
    number
]

I know what you're thinking. You're thinking that func sure looks like a keyword! It's not, I assure you. The func symbol is just a symbol that has been bound to a function. A function that creates functions from its two parameters: a list of parameter symbols and a list of data representing the body of the function.

So, let me reiterate; REBOL uses a function to define functions. I know, right? I'm sure it's like totally blowing your mind right now!


6
AUG
2009

Syntactic Beauty

Do you want to know something that I really like about REBOL? No? Well, too bad, because I'm telling you anyway. What I really like is the syntax of REBOL, or rather its lack of syntax.

Let's say you want to create an associative array in PHP, for example. It might look something like this:

$fruit_colors = array("banana" => "yellow",
                      "apple" => "green");

To print the color of a banana, you would then do something like this:

echo $fruit_colors["banana"];

Obviously, there's a whole lot of syntax going on there. If you have a swedish keyboard layout, like I do, you would have pressed a modifier key, like shift or alt, seven times while typing the last line alone, which strains your fingers. What if we were to do the same thing in REBOL?

fruit-colors: [
    banana yellow
    apple green
]
 
print fruit-colors/banana

It's so clean and beautiful that it makes me want to cry tears of joy. Well, not really. But almost.


30
JUL
2009

iPhone Racer, Part 6

Making the wheels spin is actually a bit more complicated than one might think. The 3D model of the car is divided in five different geometric objects. Four of these objects represent the wheels and the fifth object is the rest of the car.

Each object is defined by a number of vertices (points) in 3D space. If you want to spin a wheel, you have to rotate its vertices around its center axis. The problem is that when you rotate vertices using OpenGL, they are rotated around the origin of the coordinate system. Consequently, if you apply a rotation transform to a wheel, it will not spin around its center axis. So what you have to do is move the wheel to the origin, rotate it, and then move it back to its original position.

Transforms

I extended my OBJ converter script to calculate the center point for each object in the 3D model and then save it along with the geometric data. Then in the car rendering code, I added a translation transform to move the wheel to the origin by subtracting the center point, followed by a rotation, and another translation to move it back by adding the center point again.

The order of the OpenGL transformation calls may look like they're in the wrong order, but this is due to the order in which the transformation matrices are multiplied.

Currently, it takes one separate call to the drawing function for each object in the 3D model, because different transforms have to be applied to the objects. This can be optimized by using the matrix palette OpenGL extension.

Instead of pushing matrices on the OpenGL matrix stack, you precalculate the entire transformation matrix for each object and place them in a matrix palette, which is really an array of matrices. Then each vertex struct is extended with a field that contains an index into the matrix palette, indicating which matrix should be used to transform that particular vertex.

This way you only need one call to the OpenGL draw function to draw the entire model, because no state changes need to occur in between the rendering of the different objects in the model.


 

About This Site

Hello, my name is Martin Johannesson and this is my home on the web. I live in Stockholm, Sweden, where I work as a software engineer at a software company.

Ever since I was a kid and discovered the art of programming on my C64, I've been tinkering with my own little software projects and experiments. This site is one such experiment.
more...

Recent Entries RSS Feed

Tags

Amiga blog C Cocoa game GLGX GLSL iOS iPad iPhone Java jQuery Mac Mac OS X Objective-C OpenAL OpenGL Programming REBOL Shaders Vertex Shader web

Blog Archive

2011: 01 02 03 04 05 06 07 08 09 10 11 12
2010: 01 02 03 04 05 06 07 08 09 10 11 12
2009: 01 02 03 04 05 06 07 08 09 10 11 12

Random Images Load new images

loading
loading
loading
loading
loading
loading
loading
loading
loading
loading
loading
loading