Ultimate 3D 2.1 Tech Demo - Programming notes

The Ultimate 3D 2.1 Tech Demo is a quite huge project with lots of components. Therefore some special tricks were needed to realize it. The best example for this is the way the different objects are managed. To help you understand these special techniques I will give some rough descriptions and notes about them here.


The object management

Organizing objects in 3D-space in Game Maker's room editor is not easy. Actually it's almost impossible. You have to set up the z-coordinate through code for every single object and you don't really know how things will look in 3D-space before you run the application. And if you want to move a group of objects in relation to another group of objects this can't be done without replacing every single object. Despite you can't control in which order the objects get created that well. The best way to solve this problem is programming a custom room editor, but that's not easy and I didn't have the time to do it. For this reason I needed another solution and found one, which is still quite elegant.

If you take a look at the only room of the Tech Demo you'll notice that it contains only two objects. The control object and a so called room_manager. The sprite of the room manager describes it's function quite well: "The room manager creates everything." All objects in the Tech Demo get created by other objects through code. This has a couple of advantages:
- You have full control about the order objects get created in
- You can manipulate the orientation of every object immediately after it's creation
- You can set up variables individually for every single object
- You can manage objects through other objects easily

The clear disadvantage of this technique is that all positions need to be entered as numerical values, but with some try and fail you ususally find the right values quite fast. This technique is not ideal and if you are up to a big project you should better work with a self-made or extern room editor. Maybe I will make one for use with Ultimate 3D some day, but for now we are up to the Tech Demo and to the techniques, that are used by it, so lets have a look at them in detail.

A common problem in object management is the following: You have lots of individual rooms and of course all of them are connected through doors. If one room changes it's dimensions all other rooms have to move, too, to reach that all rooms are still connected correctly. Since there's just a lineary structure with one room behind eachother in the Tech Demo this can be coded quite easy using the math functions of Ultimate 3D.

Every room has a transformation in relation to the previous room and this relative transformation is known (it derives from the dimensions of the room models). If you know all relative transformations all you need to do to get absolute transformations (from room space to world space) is multiplying the relative transformations (this means the same as transforming them by eachother). This is done in a simple loop in the code.

Unfortunately, now that the absolute room transformations are determined another problem comes up. Room 9 contains a terrain and terrains can't have a rotation. Anyway the absolute transformation matrix of room 9 will most likely contain a transformation, so it can't be applied correctly. The solution to this problem is quite simple. If one part can't change it's orientation to match all other parts, all other parts will have to change their orientation to match this part. For this reason the inverse transformation of room 9 is determined and all absolute room transformations get multiplied by it. After that the absolute transformation of room 9 will be the identity matrix (no transformation) and all other rooms will match to it.

Now that all room transformations have been determined the rooms can be created. Every room has a room creator object. This object will create all objects that belong to this room in it's create event and it will destroy all of them in its destroy event. Despite it sets up their transformation and their room index correctly using the script AddObjectToRoom(...) and sets up additional variables for the objects if necessary. Having one object for every room is very useful. You always know where to search for the creation code of particular objects and you can remove complete rooms by deleting only one single object (which comes in handy when memory is being freed up for the outdoor environment).

That's basically all you should know about the object management. The only objects that get handled in a slightly different way (through the transition_creator) are those that belong to doors. But the transition manager works in a very similar way and therefore it shouldn't be hard to understand it once you have understood the way the room_manager works in.


The use of the portal engine

The Ultimate 3D 2.1 Tech Demo consists of many rooms being separated through doors. Most of the time the doors are closed and therefore only a single room is visible. Taking advantage from this fact can lead to a giant performance gain and the portal engine of Ultimate 3D offers a quite easy way to do it. Instead of rendering nine rooms all the time only one or two rooms need to be rendered simultaneously. The use of the portal engine in the Tech Demo is quite exemplary and therefore I want to describe it a bit more detailed.

The object that manages all portals is the portal_manager. Before reading the following explanations you should have read the chapter about the advanced culling functions in the help file. All information that is needed to initialize the portals is written into a couple of arrays for each portal that is to be created. This information is it's center, it's radius and it's orientation. It's given in the space of the room it leads out of. After that a loop creates all portals from this information. The orientation gets transformed from room space to world space using some of the math functions of Ultimate 3D and then portals get created from it. If you want to see the portals you can remove the comment from the piece of code that's commented out. Then a model object showing a green circle will be created for every portal to make it visible. This can be helpful to understand the way the portals work in.

Now that the portals have been created the room index of the camera will be automatically always set up correctly. In the step event there's some additional code that sets the portals to opened or to closed dependent on the state of the door that belongs to them. Despite these usual portals there is one portal that is quite special and can't use the usual portal engine.

It is the portal between room 7 and room 8 (the worm hole). In this case the solution to the problem is very easy. As the player passes through the portal the room index of the camera gets set from seven to eight through SetObjectRoom(...). This way the worm hole does not cause any problems with the portal engine.


Special things in particular rooms

Lots of rooms contain objects with some interesting coding. In this chapter I want to give some information about anything that's worth it.

Room 1 - The control panel
The control panel in room 1 is a tricky thing. It's a model that's textured with a render target texture. To this render target texture the menu gets drawn whenever some option has been changed. This way the player can change settings in an object that's part of the game. The menu gets drawn by the menu_camera. The options get loaded from the options.ini file (which can be opened in notepad) in the Ultimate3DOptions() script.


Room 3 - The sports car
The sports car uses some really simplistic fake physics. It accelerates with constant speed and has friction to limit the maximum velocity. The steering just rotates the car and changes it's movement direction. There's no inertia. If the car collides with a wall its speed gets reduced and its rotation gets changed to give the illusion of realistic wall-sliding. If the car collides with something at a high speed it gets deformed through the model manipulation functions. I admit that the deformation isn't perfect, but it's a nice way to demonstrate the model manipulation functions. If you plan on making a racing game by yourself you should not use the physics from the Tech Demo. They are just too simplistic and buggy.


Room 4 - The cartoon effect
The cartoon effect is really neat. It is a simple post screen shader, so it can be implemented easily in every project, without changing anything. In the Ultimate 3D 2.1 Tech Demo its implementation is a little bit complicated, because the comic effect is to be used only for particular objects. For that reason a render target texture needs to be drawn, which gets information which objects need to use the comic effect (a white pixel means, this pixel uses the comic effect, a black pixel means it doesn't). If your game uses the comic effect for everything you don't need this. In this case you can simply remove the line that begins with "lrp" in the *.psh file (open it in notepad).


Room 6 - The statues
In room 6 there are two statue models (a great modeling work by MysteriXYZ). Both use pixel-shaders to implement techniques that can be useful in almost every game. One of them has normal mapped specular lighting, the other one has normal mapped cubic environment mapping and both shaders implement per-pixel-diffuse-lighting (with one light source). Since these shader effects are very useful and can be used in other projects easily I want to give some information on how they need to be used here.

For the specular lighting shader you have to set up textures for three texture stages manually. For the second texture stage (stage index 1) you have to set up the normal map that is to be used. It should be loaded through LoadHeightMap(...). Stage two and three need to get the vector normalization map, which always has the index 1009. For the cubic environment mapping shader you have to set up the normal map for the fifth stage (index 4), the height map for the second stage (index 1) and the cube normalization map (texture index 1009) for the last stage (index 5). After that you have to call AddInverseTangentSpaceMatrices() to make Ultimate 3D compute some information, that is needed for this effect. After (not before!) that you can load the shader effect from the corresponding *.ufx file.


Room 7 - The warp effects
The warp effects are based upon a simple post screen shader and the render to texture functions. As input the post screen shader gets the scene texture and a texture render target, which contains information where things need to be warped into which direction. The render target texture gets rendered to by the warp_camera. This camera renders only objects that are set to be in a particular room through SetObjectRoom(...). These objects have the only purpose to create warp at particular places. If the object that is being drawn by the warp_camera is gray, no warp will be at this place, the higher the red-component is the stronger the picture will be warped to the left, the stronger the green-component is the stronger the picture will be warped upwards.


Room 9 - The forest
Rendering forests efficiently in Game Maker is a real challenge. Actually it is impossible. What you can see in the Ultimate 3D 2.1 Tech Demo does not really deserve the naming "efficient", it just deserves a "as efficient as possible in Game Maker". I may add some special features to future versions of Ultimate 3D, which could reduce the things Game Maker needs to do for forest management and therefore could increase the performance, but for now the methods that are being used in the Tech Demo are the best you can get. Mostly three tricks are being used to get it to run at an appropriate speed:
- Only one Game Maker object (the forest_manager) is used to create and manage all tree objects. This is done by handling their information through arrays.
- The trees use primitive objects and are sorted into groups forming quadratic areas. All trees in a quadratic area can be rendered simultaneously by Ultimate 3D. For this reason it is faster than using one model object per tree.
- Dependent on the distance to the camera the quads of trees use lower LoDs. The LoDs can be switched efficiently, because every LoD of every quad uses a different room index.

As I already said, it is far away from being perfect, but it works.



© Christoph Peters. Some rights reserved.

Creative Commons License XHTML 1.0 Transitional