(Note: The parts that Thorsten wrote are colored blue.)
There are two components to the COMP 290 Game Programming
Course that I took part in. The first was the development of more advanced
rendering techniques and model loading capabilities into the Wild Magic
software system. The second was helping Thorsten Scheuemann in developing
his game. I will describe the major components of each in this overview.
Adding more rendering effects to the Wild Magic software involved several modifications of the system. The major components are listed below:
Image support for RGB, TGA,
and TIFF images
Image resizing, flipping horizontally and vertically, normal map generation
Single Lightscape VRML object loader
Multiple object loading of Lightscape VRML
Layered effects support for:
View-Dependent Cubic Environment Mapping
Light Maps (and Base and Light maps paired)
Beginnings of Diffuse Bump mapping (unfortunately incomplete)
The game work with Thorsten consisted of creating or painting models related to the game as well as some programming to create the UFO effects. The following is a gallery of models that I painted:
These are models that I created:
Terrain created in Bryce and painted with Nichimen Nendo. Houses, bushes,
and gate were all modeled and painted in Nendo as well.
The complete scene runs at around 50 fps with 20436
triangles per frame (depending on the number of cows, UFOs, and fireballs).
In addition, I added some code to make some effects on the UFO.
Thorsten took the raw elements and created the rest of the game from it. The main parts of the game Thorsten created were:
Integrated an OpenGL stereo
Borderless windowing for game mode
Added dual Hi-Ball tracker support
Placed all objects within the world manually
Implemented deep copies for MgcNodes for duplicating geometry
Created many elements of the game functionality
Scene graph management for UFO circling and capturing of cows
Particle system for UFO destruction
Collisions between the bullets and the cows and UFOs
Changes to the Engine
In this section, I will talk about the new rendering techniques needed for the various parts of the game engine.
More on the New Rendering Methods
A general description of the rendering effects and how to use them can be found here.
The changes that were made
to support various effects, image loading, and model loading will be described
Probably the largest change is the replacement of RGB vertex colors with RGBA colors for the whole engine. This was done because it is potentially useful for people to be able to make objects partially transparent without relying on texture effects. For example, when the user selects and object it might be useful for the object to fade in and out of the world.
The code is based on Wild Magic v0.4 (not 0.6). This is because the changes to move up to 0.6 were enough to make the game late.
The MgcOpenGLRenderer has been altered to incorporate various parts of the MgcTextureEffect and MgcTextureEffectState class. Texture binding also can occurs while an object is being draw the first time (which in hind sight might not be the best route).
There was little regard for streaming with the new files. Most of the streaming code is not correct but still compiles.
No attempt to identify which card the application is running on is attempted. All the code is assumed to be working on a Pentium II with a GeForce 2 MX or GeForce 2 GTS and no checks for missing OpenGL extensions are made.
File Altered or Created
MgcTGAImage -- raw data reader for TGA format NEW
MgcImage -- support for deciding between TGA, RGB, TIFF. However, this also uses the _splitpath function which makes the app NON-PORTABLE (but it sure beats writing that from scratch)
MgcCubeTexture -- support for cube map textures (6 images for 1 texture). This also impacted the OpenGLRenderer in its texture set up. Cube maps can also be asked to create normalization cube maps (for bump and lighting effects). NEW
MgcOpenGLIncludes -- added new includes for TextureEffect (the .pkg file was also altered)
MgcTextureEffect -- ability to specify texturing effects NEW
MgcTextureState -- this shows up as modified but it wasn't changed (maybe used more recent version from 0.5 or 0.6)
MgcOpenGLTextureState -- shows up as modified but it wasn't changed (maybe from 0.5 or 0.6 version)
MgcOpenGLTextureEffectState -- similar to MgcOpenGLTextureState but meant for keeping multiple textures and specifying blending functions NEW
MgcTexture -- added the ability to compress texture (new enums) and to create normal textures out of a given texture (from Kilgard's torus example); also anisotropic filtering can be expressed per texture
MgcMultiTriMesh -- this calls the functions in MgcVRMLGeometry to do things like load single and multiple VRML objects NEW
MgcVRMLGeometry -- contains functions to CountObjects and CountImages in Lightscape VRML objects and append texture sets NEW
MgcFileTools -- these are called by VRMLGeometry and allow searching for words or finding out if one word occurs before another NEW
MgcOpenGLStereoCamera -- Thorsten created this class to enable stereo rendering NEW
MgcRGBImage -- raw data reader for the RGB image format. Note: RGB images are loaded as RGBA with alpha = 0 NEW
MgcString -- added a function to get the raw char * pointer from the MgcString; this was used in the MgcMultiMesh functions to set the names of the object to the textures that are applied to them; so for example "couch.tga" would name the object "couch" or "couch.tga"
MgcWinApplication -- modified so that the window appears without a border and no windows can appear on top of it (pop up window)
MgcTracker -- code to interface with the HiBall tracker and get position and orientation results NEW
ButtonManager -- code to use VRPN buttons to make the gun work NEW
MgcOpenGLRenderer -- lots of modification for various effects as well as the ability of textures not bound to go bind themselves before rendering; this might cause some hiccups in the rendering when a new object is first display
MgcTriMesh -- modified to share normals and add texture sets; each object can have a char * identifier; uses _splitpath so it is NON-PORTABLE
MgcSpatial -- Thorsten changed some of the functions for copying (deep copies)
MgcGeometry -- Thorsten changed some of the functions for copying (deep copies)
MgcNode -- Thorsten changed some of the functions for copying (deep copies)
MgcWglRenderer -- Some changes for glCull calls which were in there for debugging
Other things to note:
- There is a dynamic and a static label for individual objects. All objects loaded with MgcMultiTriMesh are marked as static and will have display lists created out of them in MgcOpenGLRenderer (also texture may be bound at that time as well)
- Objects that have MgcTextureEffects associated with them are not turned into display lists
- The cube maps are assumed to be view dependent meaning that the map is stable in world space which might not be what the user wants. This can easily be altered by not putting the inverse of the affine portion of the modelview matrix in the texture matrix (which is what the renderer does now)
- Currently there is a problem when there are several objects and some of them use multiple textures and some use single textures. The extra textures beyond the first are not changed which means that the render state carries over to the new object being drawn.
- Also when the cows are dropped their textures are replaced by yellow textures from the UFO. I am not sure why this happens but it seems to be because the cows are added and deleted from the scene graph. I believe this is also related to the first problem and some problem with the state pushes and pops in MgcTextureState or MgcTextureEffectState.
- There have been some problems compiling in release mode. This has not happened all the time but when Browse Info was created in Visual C++ then the errors went away. I am not sure if this Visual C++ had the latest service packs though.
- Unfortunately the most interesting effects such as bump maps, mirrors, and shadows were not covered in this semester. Preliminary work on bump mapping was done and it seems to involve the following steps:
- Providing the TextureEffect with 1) the closest light of interest and 2) a height field that can be mapped to the object
- My initial idea was to turn the height field into a normal texture and then use a per-pixel dot produt operation (Register Combiners for nVidia or the ATI
ATIX_texture_env_dot3 extension) with the direction to the light
- Setting up the register combiners for this effect resulted in a black object and I am not sure why (debugging the register combiners is not easy)
- Other forms of this kind of diffuse dot product bump mapping seem to use a normalization cube map rotated to face the light and then dot product with the other normal map describing the bumps on the surface.
- Embossing involves calculating tangents per object and I added a pointer to such tangents in MgcTextureEffect but never used it. Projecting the light on to each triangle will probably make the applications too slow.
- Vertex cache aware triangle stripping code is available from nVidia but it is not obvious how to use their library.
- Sound would have been very nice or just the ability to play random .wav or .mp3 files. Unfortunately ever since A3D has gone under there seems to be a serious sound API gap. Perhaps OpenAL could fill that void but probably a commerical library would do much better.
Making the Game Objects
Thorsten did most of the
work on the game logic and creation of the world. I will describe the tool
chain which worked rather well.
Most models were created with other programs or downloaded off the web. These models were them imported into Nichimen Nendo and painted. Nendo will spit out a .wrl file and a .png which looks like:
Note that the textures cannot be compressed in this form since the colors will bleed onto nearby triangles. These .png files were converted to .tga with Adobe Photoshop. The corresponding .wrl files were modified to point to .tga files. The painted the objects were imported to 3DS Max and exported as .3ds files. The 3DS files were then imported into Lightscape and exported as VRML. Then these files were imported into the Mgc code with the following commands:
// load lookout platform
m_spkPlatformNode = new MgcNode;
m_spkPlatformNode->Scale() = 0.11;
m_spkPlatformNode->Translate() = MgcVector3(2.4, 7.4, -0.20);
spkMultiMesh = new MgcMultiTriMesh();
for(i=0; i<spkMultiMesh->GetNumObjects(); i++)
The drawing loop causes the objects to be display
(and hence a display list to be created) for each object.
This should also serve as the inspiration for a direct 3DS importer (maybe ASCII format) or .X DirectX model loader.
Putting It All Together
Thorsten was reponsible for taking all the objects created and putting them into the environment. In addition adding tracker support and stero viewing support, he was able to create the game logic and state machines necessary to make the game playable. There were state machines for the UFOs and cows which used many different aspects of the scene graphic management system to cut and paste objects and create copies of existing geometry. Thorsten also created the initial idea for the game.
The game play is based on shooting the UFOs before the steal your cows from the land. New UFOs appear if others are destroyed and after a given amount of time your "score" is roughly the number of cows remaining on the ground. This format is good because the number of UFOs or the speed of the bullets can easily make the game easier or more challenging.
With the rendering being taken care of by the game engine and Paul's extensions, I could concentrate almost entirely on the game logic and adding bits and pieces here and there that were needed to accomplish this.
I derived a class called MgcOpenGLStereoCamera from MgcOpenGLCamera. This class has a method to set the distance between the eyes and methods to set up the OpenGL projection matrix for each eye by offsetting the camera position to the left and to the right respectively. This is how the scene is rendered:
Hiball trackers and input device:
The code for interfacing with the Hiball trackers and to query the buttons on the input device was taken from the GLVU library that is used to create VR applications on SGI machines. I took over a class called ButtonManager that manages the querying of the buttons on the portable joystick with almost no changes. I tried to fit the tracking into the Wildmagic framework by creating an MgcTracker class, but right now it is a complete mess and has to be overhauled soon. All these components are placed into a seperate library called MgcTracking.
Deep copies of MgcTriMeshes:
Since the game makes it necessary to display multiple objects of the same type at once (UFOs, cows and fireballs) and the engine doesn't support sharing of geometry at this point, I implemented a copy constructor that creates a deep copy of an MgcTriMesh object. For this to work correctly I had to create custom copy constructors for the base classes MgcGeometry and MgcSpatial, too. A better approach would have been to support data sharing in the scene graph, but time was limited and creating deep copies was a quick solution.
General game logic:
The class GameApp contains all the information
about the game world and it's current state. There are two lists containing
the active UFOs and cows respectively. A third list holds the cows that
are free to be kidnapped by a UFO.
When a game is started, the cows are placed randomly on the terrain by choosing random x and y coordinates and then calculating the z coordinate by intersecting a line in the z direction with the triangle mesh of the terrain. Right now the cows might end up standing in one of the buildings that are placed on the terrain. I want to fix this at some point.
The time till the next UFO is entered into the game is chosen randomly. When this time has passed, a new UFO object is created and again a time for the arrival of the next UFO is picked. There is a maximum number of UFOs that can be active at any time. The GameApp class leaves the task of controlling the cows and UFOs to their respective classes by calling each object's Update() method in the OnIdle() method.
The cow objects are instances of a class called Cow which is derived from MgcNode. This class controls the behavior and movement of the cows. Since the cows don't move unless they are attached to a UFO or falling to the ground, there isn't that much to control. I used a state machine with the states COW_IDLE, COW_KIDNAPPED, COW_FALLING and COW_DEAD. In the COW_FALLING state, the cow is moved along the negative Z axis. This is accomplished by updating the translation of the cow object. The actual cow mesh is attached to a cow object as a child and moves accordingly. This way the mesh can easily replaced by a version depicting a deceased cow when it is shot.
The UFO behavior is implemented as a state machine in a subclass of MgcNode as well. The states are:
The explosion particle system moves each particle along a randomly chosen velocity vector. Each frame this velocity vector is affected by gravity to make the particle fall to the ground. I disabled changing the particle colors as they aged because this caused the UFOs to change color as well. Right now the particle don't move according to elapsed game time. It is assumed that the frame rate is high enough so that the velocity vectors are updated often enough. This will have to be fixed at some point.
Right now, collisions are only detected between
fireballs and cows and fireballs and UFOs. The collision detection is done
every frame and tests the line segment on which a fireball travelled between
the last and current frame against the bounding box and then against the
triangle mesh of an object. Although the collision detection system handles
the moving fireball correctly, the movement of the other objects is ignored,
since the line segment is intersected with the object statically placed
where it was during the previous frame.
Some Game Bugs
The biggest bug by far was the rendering errors we encountered when there were multiple objects imported into the engine. Updating the push/pop functions helped but did not eliminate the problems. The main problem comes from the MgcTextureEffectState management I believe which is basically a copy of the MgcTextureState. The problem that we initially had was using animated textures (which add a time to the MgcTexture class) on the tractor beam and then attaching and detaching that beam based on whether the UFO was near the cow. When the UFO turned on its beam not only did its texture rotate but so did everyone else's! An example is given below:
The textures are all shifting about (even the sky and cows). Taking away the rotating texture seemed to solve part of the problem.
The next problem was using particle effects when blowing up the UFOs. After compensating for the MgcTextureEffect weirdness, the UFOs started to take on the colors of the particles for some parts of the game and then return to their initial state. The vertex colors might be to blame since changing the vertex colors to white also influenced how the UFOs were subsequently drawn. This is a very strange bug (along with the TextureEffectState bug) which seems to involve the pushing and popping of state or the misallocation or some addresses for textures or effects. Both of us tried several methods to correct this but did not understand the state management system enough to predict errors or to pinpoint where the trouble really lies. It could be in how the scene graph is managed, it could be in the definition or copying of textures, it could be in the texture effects pushes ans pops. It is perplexing and is still being pursued by us.
Thorsten and I made substantial changes to the Wild Magic code during this semester and made headway in promoting VR applications on the PC. The use of the Wild Magic tool kit significantly lessened the burden and increased the scope of our plans while creating this environment. We are both looking forward to using this in future games and VR applications as well as taking advantage of more advanced features of the engine such as spline paths and controllers for animation. Although there are some bugs in the current modifications, hopefully they provide some guide as to how the engine can be extended for unique applications.