Some updates

July 26, 2013 —

Lighting update

Been chugging away at improving and extending the Tilemap system from where I left off with it several months ago. It's coming along pretty well. I've been making use of libgdx's excellent support for loading models to add support for creating new tiles from external FBX/OBJ models. This support includes the ability to scale to a smaller/larger size, add a colour tint, and combine multiple models into a single tile mesh at runtime.

Most recently, I've been adding support for specifying tile definitions in a JSON format and being able to load that format at run time. Basically the JSON format allows you to specify properties for a TileMesh object. Settings include things like:

  • Whether it's represented by an arbitrary model(s) or is a simple 1x1x1 cube.
  • The texture(s) to apply. Right now this is specified as indices into a texture atlas.
  • Whether it should be rendered with alpha blending or not.
  • Whether this tile emits light or not.
  • Color tint.

An example file might look like this:

{
    "tiles": [
        {"model": "pot1.g3dj"},

        {
            "cube": true, 
            "textures": {
                "top": 78, 
                "left": 79, 
                "front": 80, 
                "right": 81, 
                "back": 82, 
                "bottom": 83
            }, 
            "faces": ["ALL"], 
            "opaqueSides": ["ALL"]
        },

        {
            "cube": true, 
            "texture": 85, 
            "faces": ["ALL"], 
            "opaqueSides": ["ALL"]
        },

        {
            "models": [
                {
                    "submodel": "block-1x0.5x1.g3dj", 
                    "positionOffset": {"x": 0.0, "y": -0.25, "z": 0.0}
                },
                {
                    "submodel": "pillar2-empty-1x2x1.g3dj", 
                    "positionOffset": {"x": 0.0, "y": 0.5, "z": 0.0}
                },
                {
                    "submodel": "pillar2-middle.g3dj", 
                    "positionOffset": {"x": 0.0, "y": 0.5, "z": 0.0}, 
                    "color": {"r": 1.0, "g": 0.7, "b": 0.7, "a": 1.0}
                }
            ]
        }
    ]
}

Which, when used to make a map, might produce tiles looking like the following:

Lighting update

Building a map is about as simple as assigning tile indices to a 3D array. Each tile position in the 3D tilemap is a Tile object which contains settings for things like:

  • Index of TileMesh to use to render this tile
  • Color tint (multiplied with the base TileMesh's color tint)
  • Current light value (determines brightness when rendered)
  • Rotation (0/90/180/270 around the Y-axis)
  • Whether entities can collide with it
  • Other game-specific flags

Before the tilemap is rendered, a "chunk vertex generator" walks over the map tile by tile adding appropriate vertex information for each to a vertex buffer that can be rendered directly. Only if the map changes does this vertex buffer need to be regenerated (in OpenGL terms, the VBO for this would have a usage of GL_STATIC_DRAW).

The map is subdivided into chunks which for each map can be of an arbitrary size (e.g. 16x16x16 tiles) but each chunk in the map must be sized identically to every other one. This is mainly done to help optimize rendering, since we can skip chunks who's bounding volume doesn't currently lie in the camera's frustum. The VBO's generated by the chunk vertex generator, as you can probably guess from the name, are per chunk, not just one giant VBO for the whole tilemap.

Lighting right now is per-tile. A TileMapLighter runs through the entire map "spreading" light values from tiles which emit light and/or light cast downward from the sky starting at the top of the map in each X/Z column. This is mostly working correctly, but there are still some small outstanding issues with how the light spreading walks through the map. Each tile can specify which (if any) of it's 6 sides are opaque and block light and currently there are some areas where this is not being respected completely.

Smaller common chunks of a tilemap can be extracted out and put into a TilePrefab which is a smaller arbitrarily sized block of tiles which can be placed into a tilemap. So for example, if I'm building a dungeon which has 5 different types of rooms which are reused in many different places, I would create a TilePrefab for each. When I build the map I can simply specify an x/y/z position to place each room at and an optional 0/90/180/270 rotation around the Y-axis to apply at the time the TilePrefab is copied into the TileMap. I still need to write JSON loading/saving for this, but conceivably some maps could be built solely by specifying a list of TilePrefabs and their coordinates within the map.

An area that still needs some more thought put into it is what to do about tiles which exceed the 1x1x1 boundaries. Each tile grid position is 1x1x1, but it is possible, and probably desirable in some cases, to provide a model that is larger then this (e.g. a large tree which would take up say a 3x3x6 tile area for example).

There is a bit of an overlap here with TilePrefabs I think, but after some consideration I don't believe just relying on TilePrefabs would be the best way to handle this as I think it would put too many restrictions on modeling some more detailed tile meshes (everything would need to be chopped up into 1x1x1 sub meshes). What I think I would instead like to do is add some flags to the Tile object that indicate whether the tile is part of a "large" tile and then another flag indicating whether one tile is the "root" of that tile. So in the case of a tile which takes up a 2x2x2 area, maybe the lower south-east corner is the "root" and the remaining tiles all contain a coordinate offset property which could be used to find that root tile in the map. Then when then then "chunk vertex generator" is walking the map building up the vertex buffer it can only look at root tiles and single 1x1x1 tiles which aren't marked as a "large tile." However, there are still some remaining "what-if" scenarios I need to think about for lighting and collision.

Anyway, work on this is definitely progressing. I'd like to put together some silly little demo with what I have sometime next week and just ignore the "large tile" issue discussed above for now. If that happens, I'll post it here.