Tile World 3 is a game/demo I have made using Java and LWJGL. Light Weight Java Game Library acts just as a bridge between Java and OpenGL. the library itself does not provide any other functionality than what is already available in OpenGL. This did mean that I had to program everything in the demo myself, and this made Tile World 3 a very interesting and very educational project.
The Demo itself consists of a 3D world built of ground tiles with different heights and properties. The world also contains some trees. The user controls a first-person camera and can explore the world by walking, running, swimming, jumping, and sneaking using the mouse and keyboard or an XBox360 controller or similar.
Go to the Tile World 3 download page to try the game/demo yourself.
Java and Games
Java programs run slower than for example C++ programs due to the fact that they run on the “Java Virtual Machine” and not directly on the computer itself. This makes Java not ideal for game programming. To only reason I use Java instead of C++ is that Java is the programming language I am most comfortable with.
The idea with LWJGL is to make the GPU do as much of the work as possible, and at the same time limit the number of calls to the GPU as much as possible since the connection between the Java program and the GPU is relatively slow. This way this game/demo will probably run fine anyway. Minecraft is a well-known game and a great example of using Java and (an older version of) LWJGL to render 3D graphics, and still have decent performance.
Modern OpenGL and GLSL shaders
The demo now uses modern OpenGL (ver 3.3) with GLSL shaders. When I started this project I used old “Legacy OpenGL”. It is easier to get started with the legacy code and rendering something but the performance is bad and it is awkward to work with when the programs become larger. I did also experience a lot of problems that my program simply would not run on some types of GPUs or systems. This was probably because I started to mix in some “modern” OpenGL stuff while still keeping some old “legacy” functions in the code.
After version 8 of the demo (tw3v8) it uses only modern OpenGL and should run fine on NVIDIA, AMD, and Intel GPUs.
The use of shaders also allows for a lot more flexibility than what is possible with old “Legacy OpenGL”. For example. I use “fog” in my fragment shader with a density that is dependent on the radial distance from the player to the fragment. For now, I use a simple shading model that is based on ambient light and diffuse reflections from a directional light representing the sun, but this lighting model can easily be improved. All animations in the demo, for example, water waves, or the trees blowing in the wind, are done by displacements in the fragment shader, directly on the GPU. This helps performance a lot since math functions such as Sin and Cos are not very fast in Java.
3D collision engine
To handle collisions the game a have written a simple “collision engine”. It is based on boxes around all objects. The boxes can have different sizes depending on the type of object, but the boxes can not rotate with the objects, this makes calculations simple.
When for example the player moves, the program checks all boxes for the objects (both ground tiles and entities, such as trees) within a small radius. If the player will intersect with any rectangle after the move, the move is not allowed. The collision check is done independently on each axis to allow for “sliding” against walls etc. while walking.
Tilemap
The demo uses a tile map, it is not a voxel world as it may seem at first glance. This project actually started out as a top-down 2D tilemap game. I later changed it into a 3D-rendered first-person experience since I found it more interesting. The world is loaded and built using an image where every pixel is a tile and the color of the pixel describes the type of tile in that location. Different tiles can have different heights and textures, but all tiles of a certain type will have the same height. The size of the map is currently 512 by 512 tiles, but it can easily be increased by simply loading a bigger tilemap image.
To improve performance while rendering the map is divided up into “chunks” with the size of 16×16 tiles. a VBO (vertex buffer object) containing vertex positions, vertex normals, and texture coordinates, is generated for each chunk the first time it is rendered.
OBJ-models
To import stuff to the game and not rely on coding the position of every single vertex, a have written my own “importer” for OBJ files. OBJ files are an old and simple standard format for saving 3D models in a text file. The text file contains information about vertex positions, vertex normals, and texture coordinates. Since OBJ is a standard format it is possible to easily export models from 3D modeling programs such as Blender or AutoDesk 3DS MAX, and import them directly into the game. The downside to this is that the OBJ format does not support any animations or moments. Also in my application, every single OBJ model that is used in the game needs to have a single associated texture image. one model can not have multiple textures for efferent parts.
The trees and the “collectibles” (torus knots) in the game are imported OBJ models. The “skybox” used in the game is also an imported model with the associated texture. The skybox is actually a cylinder with a flat top in this case but it can easily be swapped out for another model if needed.
Videos of older versions
tw3v1:
first person, using old legacy OpenGL. All animations are done in the java-code. The performance was a big problem, therefore the tiny render distance. Here all tiles are rendered separately, not divided into “chunks” like i did later.
tw3v0b:
one of the first working versions, using old legacy OpenGL. All animations are done in the java-code, and only the tiles visible are rendered, still large performance problems.