Dev Diary #1 – SubTile Meshes

I’m currently working on another update for Look Out Below!, focusing on performance and user experience as we prepare to launch it for free with ads. One of the places that suffered performance issue the most was over the cities, which I suspect is due to the high number of draw calls in this area. In this page on the Unity manual they describe about the CPU cost or drawing an object on screen and how reducing the number of draw calls can increase performance. In Look Out Below! we use a lot of tiled textures which are great because they take up very little memory but still provide plenty of texture resolution, normally a building with have the same texture tiled across it 20 times or so, quick example below.But a big part or reducing draw calls is atlasing, as each tiled texture requires a different material and therefore a different draw call, even if its applied to the same mesh (on a submesh), where as with atlasing many textures are combined together into one big texture, and the meshes UVs are moved to the correct part of the texture (this is what Unity’s lightmaps are, large texture atlases that contain and combine many small textures generated for each mesh), for example out trees use a single texture. Unfortunately for us, tiled textures and texture atlases don’t mix well, tiling relies on continuous UVs that go beyond the normal 0,1 UV range, and atlasing relies on each mesh having its UVs confined to a small area of the UV range.

Our solution is to tile within a smaller UV range using a shader. The shader uses a faces vertex colors to specify a range to tile in. In our 3D package we have multiple materials assigned to a mesh that have a name like “WindowMat_6_6″, on import this tells the importer that faces with this material applied need to have vertex colors. In this example the importer will assign (0.83333, 0, 0.1666,0.1666) as the vertex colors, which in the shader will equate to the last sixth of the texture (i.e. texture 6 of 6 assuming the textures are laid out horizontally). Here it it in action; Now the building which was previously 6 tiled textures and 6 draw calls can be rendered in one draw call, providing a performance boost without actually sacrificing any quality. One thing I should mention is while we still use tiling on the Y axis it’s not so much tiling as mirroring in the x axis, this is so that the difference in UV sample position isn’t so large as to cause extreme mip-mapping, for us this isn’t an issue as our textures are mostly symmetrical on the x axis anyway but it does limit it’s usefulness in other situations.

Heres the simplest form of the shader we’re using, it tiles in Y and mirrors in x based on vertex colors;

I’ve mentioned in a previous post about MeshBatcher.cs, a class that will combine multiple meshes, that share the same material and lightmap, into a single draw call and while using this class on the cities would certainly would also have reduced our draw calls when applied to multiple buildings sharing the same material, it adds to the memory as the mesh data for a building is duplicated for each batch and we wanted to keep the download size as low as possible, which means sharing as much mesh data as possible. So that’s about it for my first Dev Diary, we’re also still working on Kindred and other smaller projects, be sure to support us by buying Look Out Below! in the meantime!

UPDATE:

I’ve cleaned up our importer a bit (though its still pretty filthy) so I can show the importer code that goes with that shader. Theres a few steps to go through in you 3D package I’ll go through as well, check out the image below;

This church model has 3 materials assigned, the materials are named to match up to the position of the tile in the texture. It also has an extra attribute called “Unity” with the value “subtile”. Unity allows us to detect these extra attributes be extending the AssetPostprocessor class and using the OnPostprocessGameObjectWithUserProperties method. In our importer we go through the objects with the unity attribute, parse the value and if its “subtile” we start subtiling the mesh.

There are somethings to bear in mind with this implementation, firstly the models material import settings must be “From Models Material” to get correct material names, and secondly all materials must be part of a subtile.

Here’s our subtile importer (it’s actually just a part of our much larger importer which I’ll cover some of the features of in the future)