To get the look we wanted, we had to be able to take the complex geometry created for the level and show it in a simple way. As PS Vita was our lead platform, we didn’t have the memory or render time to do any post-processing on the map, so we needed to do as much of the work as possible offline, whilst building the level.
I’m going to walk you though how I achieved this right now!
If we take the geometry from this one section, we get a map that looks like this:
This doesn’t look good at all. The overlapping geometry wouldn’t be a problem, but we wanted the map slightly transparent so we could see the game behind. The final look is much better:
Our process lets us get even a small platform section like this, with 953 triangles down to just 131.
It gives us a mesh with no overlaps and a data size reduction of an order of magnitude across the whole game, it also reduced the render time by a pretty significant amount too.
The process actually started with the laser fields in the game. The designers wanted to be able make laser fields of any shape but still have them look nice. We make them up from 2 separate elements; the main field, and the edging which includes a border, highlight and spark animations running along the border.
The designers make fields out of individual blocks to make more complex shapes like the one above. Let’s start with a simple one:
Our collision shapes for the game are squares, right angled triangles and circles, so this diamond is made out of 4 triangles.
When we start thinking about how to find the outline of the group of shapes, we discard any idea of the original shape and we just have a set of points connecting lines.
It is easier to see the lines and points we have here if we explode this a bit.
This collection has 12 lines and 12 points, the points circled are all in exactly the same position, so really we only have 5 points. But we’ll keep the 12 lines as they’re the important part that enables us to tell which edges are on the outside of the shape.
Once we have removed all the identical points in the lines we can count up how many identical lines there are, we define a line by its start and end points, as you can see it quickly becomes obvious which of the lines are unique.
Any line that occurs more than once is not an edge, so we get rid of them and we’re left with only the edge lines. We simply pick a point connected to one of the lines left and join the dots till it comes back to itself, and we have a path that describes the outer edge of the shape.
Our complex shape still has a problem however.
The points on this shape don’t line up, so we need another step in there.
It’s at this stage that it’s essential we discard the idea of the originating shapes we’re dealing with.
The 4 sided squares we started with needs to be broken up. The points are all there, but we’ve made more lines, any lines that ran over points have been broken up into smaller lines, so that when we count them…
We can see the overlapping line segments and thus discard the non-unique ones, giving us the correct outline.
If we use both of these techniques we can break down a larger, more complex shape successfully:
Breaks down to:
And that was all we needed for laser fields, so we stopped there!
When we started thinking about the end graphical requirements for the map, we thought about how to reduce the shapes the map is made from down to something more reasonable. We arrived at the conclusion we could generate a mesh for the map with only the outline as the level visuals made no difference to the visuals of the map.
This was useful as the laser field process was nearly there, with the one major problem of overlaps.
There are a lot of places in the map where geometry overlaps itself, just a small area is highlighted above, and that was the next problem to solve. The solution was, conceptually at least, quite simple.
Divide & Conquer
We’ve got areas with overlapping geometry, so we take all those pieces and resolve them, put the resolved lines back in with the rest and solve as if the overlaps never existed.
Most of this shape is fine.
Apart from these two, so let’s consider them first.
There are intersecting lines here, and no points where they cross, so let’s make them:
Unlike our outline solver, we need to remember the shapes for now, as we are about to make a new one:
And once we’ve done that, we are back to having non-overlapping shapes, so we can solve just like our first problem. We run many smaller steps here were we discard shapes inside other shapes, and ensure that single line overlaps are removed before we do our final solve for this stage.
And then feed this outline back into the original set of geometry. As nothing is overlapping any more, it solves just like the laser fields do.
This solution works for all of our maps.
The last thing our maps have that the laser fields don’t have is internal edges- fortunately we have already solved this problem, we just need to find all the outlines.
This shape breaks down as all the others do to show the unique edges, except now we have many outlines instead of 1.
We calculate which ones are inside others and make the mesh with the inner outlines as holes. We can have shapes within shapes within shapes like this test scenario:
All for an outline
As anyone who has ever tried anything like this will know there are plenty of edge cases and weird anomalies that can make this kind of thing a pain to get right, but this is the process as we created it on paper is what the code does. In spite of its problems that prevent it from being a general solution to reducing geometry, it worked well for our needs and saved us countless hours of time compared to the process if we hadn’t been able to automate the reduction of all the lovely work our artists have created.
Without this process we wouldn’t be able to have the nice outline and alpha vignette, or have the render time left to render the game in the background while you navigate the map.