Welcome everyone! I am Captain Marek and in today’s Captain’s diary entry we have loads of new information about our upcoming Update 1! First, I will tell you all about our new terrain and ocean. Then, Captain Filip will follow with some new features and improvements.
Just to remind everyone, this post covers changes that are not live yet and will come in the Update 1 which is scheduled to arrive around the end of May.
Terrain representation revamp
As COI evolves over time, technical requirements are changing. Our terrain was originally designed to be dynamically expanding over time. New chunks of land could be unlocked as players explored new territories. However, as you might know, such a feature was never available in the game since we have decided that having all land visible and accessible from the very beginning is better and more exciting. If you ever wondered why the terrain boundary has a non-rectangular shape, this is the legacy reason for it.
The issue is that a dynamically expanding terrain tech comes with a lot of code complexity underneath. For example, all terrain data must be stored in chunks and accessing terrain data needs to go through a chunk lookup. So many game code and mechanics depend on the terrain and need to be dealing with this complexity. This was causing not only performance issues but also complex code that is harder to optimize.
As we started to work on the map editor we realized that we need to decide whether the complex terrain tech is something that we will just keep as is, or decide to improve it. This was a “now or never” moment because the more terrain is used the less plausible a representation change would be, especially when we talk about a terrain editor that is tightly connected to the terrain. After a thorough investigation of our options, we decided to go ahead with the revamp of the terrain representation as a long-term investment in the future and longevity of COI. From the fantastic results we got, now it sounds like we made the right decision!
New terrain representation
Before describing the new representation, let’s take a look at how the old terrain worked. In order to query a terrain property (like a height), following steps have to be performed:
Look up a terrain chunk corresponding to the [x, y] coordinate (hashtable lookup).
Verify that a chunk exists, if not, report failure.
Compute a local data index from the original coordinate.
Verify that the local index is valid, if not, report failure.
Return requested value from an array at the local index.
This might not sound like a lot, but the devil is in the details. First, there are many operations needed to be performed for the chunk lookup and coordinate conversion. Second, there are many hops to different memory locations. Let me briefly explain why this is a problem.
You might know that a CPU has on-chip caches to speedup memory access. If a requested memory address is not in a cache, the CPU has to wait for the data to arrive from the main memory (RAM), which could take anywhere from 20-200 CPU cycles. This means that accessing less distinct places in memory can speed up the code significantly. In our example, there were around 10-20 different memory locations accessed.
So what is the new terrain representation and how does it solve these issues? Frankly, it’s quite simple. We just made the terrain a rectangle and each terrain property is stored consecutively in one giant array. For example, if a terrain is 200 x 300 tiles, the height data is stored in an array of 60000 elements. The lookup is quite simple:
Compute an index from the [x, y] coordinate (two arithmetic operations).
Verify that the index is valid, if not, report failure.
Return requested value from an array at the index.
This optimization resulted in 2-10x faster terrain access! You may also ask, how many memory locations are accessed using the new approach? Two! The length of the array (for index verification) and the value itself. A huge improvement and the access code has just three lines! This might sound like an obvious way to store terrain data but keep in mind that this approach does not work if the terrain is not a rectangle and can expand in any direction.
Another benefit of the new rectangular terrain is that there are no surprising boundaries of a map that players need to worry about.
Terrain materials improvements
We haven’t changed only the terrain data representation. Another significant change is how terrain materials are represented. Before, each tile on the terrain had two states: normal and disrupted. For example, dirt was a disrupted state of grass. Excavators left disrupted rock and ores after mining.
The first issue was that the disrupted state was just a visual distinction, the two states had the same properties such as collapse angles. This for example meant that a mined rock could be dumped as steep of a pile as the mountain it came from. Or that too steep mountains started collapsing all over the place after the first dig of an excavator.
The second issue was that the disrupted state was not set per material layer but per entire tile, affecting all material layers. This meant that if for example a dirt fell on a tile with a rock and then fell elsewhere, the uncovered rock below was suddenly disrupted as well. This was especially an issue when materials were falling down from cliffs, leaving disrupted “trails”.
To improve on this, normal and disrupted materials are now represented as separate layers. This solved all of the above problems: normal and disrupted materials can have different parameters, falling material won't leave “disrupted trails”. And as a bonus, transitions between disrupted and non-disrupted states can be non-symmetrical. For example, compost now turns into lush grass, but lush grass turns into normal dirt when vehicles go over it.
The disadvantage of the new approach is that the number of layers on terrain is higher and operations are more complex. For example, a vehicle going over grass was previously just incrementing the disruption amount on each tile it touched while traveling. Now it has to do more complex layers conversions.
Fortunately, thanks to the new terrain representation and many other optimizations, the new more complex system is even faster than the old one! A test was performed where 127 vehicles drove over grass, disrupting it into dirt in the process. Simulation time per tick went from 1.4 ms to 0.8 ms, nearly 2x faster!
Terrain physics improvements
Apart from speedup thanks to the terrain representation, we have improved terrain physics to fall in a more natural way. Before, terrain was only allowed to fall in 4 directions aligned with the X and Y axes. This meant that terrain was preferably forming pyramid shapes when collapsing. This did not look great especially when the new stacker was dumping material.
We have improved this to use an 8-direction neighborhood when simulating terrain collapse. This is more expensive but produces much more natural circular patterns as you would expect.
Regarding performance, this is another success story. Despite more complex computations due to the 8-way neighborhood, terrain physics got 4x faster! To prevent lags, we had a limit of a maximum of 250 processed tiles per simulation tick so we increased it 4x to 1000 tiles per tick so that the maximum per-tick time budget is the same. This made large landslides so much more exciting!
Another improvement to terrain physics is a special treatment for thin material layers. We noticed that materials that collapse easily (such as sand or waste) slide down steep slopes way too easily, like on a water slide. This didn’t look realistic. We have improved this by biasing the layer collapse angle based on its thickness.
Terrain rendering optimizations
Terrain rendering was also significantly optimized not only yielding more FPS but also saving a lot of memory! Terrain is still rendered in chunks but thanks to the fixed terrain size we were able to do a lot more optimizations.
The biggest optimization was to completely eliminate meshes representing the terrain surface. Before, each chunk had a mesh with a grid of triangles. The issue is that a mesh requires a lot of memory and is expensive to update. Instead of meshes, we save all terrain properties such as height or material type to one large texture and all the fancy vertex displacement and coloring is done on GPU.
We also implemented dynamic level of details (LOD) so that further terrain is rendered using lower resolution meshes.
Finally, we use GPU instancing (discussed in CD#15, CD#29. and CD#31) so that the entire terrain including all LOD variants is rendered in a single draw-call, eliminating most of the GPU communication overhead.
To test the performance we created a gigantic map as a grid of 5 x 5 New Haven maps next to each other (area of approx 50 million tiles, or 200 km^2, or 124 mi^2).
The results speak for themselves: 9x faster rendering and 4x less memory consumed (8x less GPU dedicated memory).
New terrain size limits
Until now the primary limitation of the terrain size was performance. As we saw in the previous benchmark, maps beyond a few million tiles squared were unplayable. Now that the performance is longer the limiting factor, how large can maps be?
Turns out that Unity won’t allow us to allocate larger arrays than 2 GB of data. This means that we had to put a cap on the maximum terrain area of 65 million tiles, that’s 262 km^2 or 163 mi^2, roughly the size of the Cayman Islands. For comparison, our largest map is currently the Insula Mortis with an area of nearly 4 millions tiles.
We won’t be releasing such large maps yet since there might be other issues like pathfinding, but the potential is there!
Given the max terrain size, we have done another interesting optimization. We have reduced the size of all integer coordinates from 4 bytes to 2 bytes. This saves 50% of memory for all coordinates in memory, neat!
Improved terrain visuals
We are also working on a new terrain look and feel, including new textures and various props. This is still in progress so we cannot show you the final result but we have some cool things to show you.
As mentioned in CD#32, we have improved terrain rendering to handle 255 different textures, up from just 24! That’s a huge improvement and allows us to have unique textures for all dumpable products and still leave plenty of room for more new materials in the future as well as for mods.
We have also implemented what’s called “triplanar texture mapping”, significantly improving the quality of textures on the terrain, reducing stretched textures on steeper slopes. As the name suggests, this technique maps the textures from 3 different directions, depending on the normal of the terrain.
We are also considering having “weathered” texture variants for rocks to distinguish between freshly mined surfaces and old weathered rock faces.
Terrain revamp conclusion
This terrain representation change and all the improvements and optimizations were a massive undertaking. We could not possibly cover all the details and improvements that went into it but the results speak for themselves.
Unfortunately, due to the time investment that went into this, we have to postpone the terrain editor for the Update 2. We know that many of you were eagerly waiting for the terrain editor and it is still coming, but unfortunately not just yet. Sorry!
Ocean representation was improved in a similar manner to the terrain and it is now much more efficient to simulate and render, including LODs based on the camera distance.
However, the most exciting news is that we have also completely rewritten the ocean surface waves that are now simulated in real-time! Ocean is now composed of thousands of sine waves, making it look much more realistic and the waves even change based on the weather.
I won’t go too much into details of how this was done, but just to mention some key points, we use inverse fast-fourier transform (IFFT) to efficiently compute a sum of thousands of sine waves. This is done using compute shaders on the GPU. We compute 3D displacement as well as a jacobian (derivatives with respect to x, y, z) and then mix two scales of the waves into one ocean surface. If you’d like to learn more about how it’s done, here is a fantastic video called Ocean waves simulation with Fast Fourier transform detailing the motivations and all the steps.
Here are short animated gifs showing old and new ocean surface. Can you tell the difference? :)
Changes in landfill
In the new version, there will be a pollution penalty for dumping waste on terrain. Freshly dumped waste will turn into “settled waste” over a few game years and during that process, it will emit pollution. This is possible due to the changes in the terrain that we mentioned above, as we are now able to track waste conversion without any performance penalty.
Another change we are doing for Update 1 is that we will no longer allow building structures directly on waste to provide you with better immersion. Also, it was heartbreaking having to watch some of you building settlements on top of a landfill :)
However, we are obsessed with waste, so we couldn’t just stop there. We know that solid burners were not the most elegant solution to the waste problem. And so we are introducing an incineration plant. The incineration plant burns large volumes of waste and produces steam to run your power plant. That’s a win-win as long as you are so heartless about burning someone’s thrown-away teddy bear for power.
You might say, that waste got enough attention and we should move to another topic. Not yet. We are adding a waste compactor for better compression and transportation. None of you ever asked for it, but we still delivered and that’s called exceeding expectations all the way! The compactor will be able to also press recyclables and individual scrap products.
But for action, there must be a reaction. And for a compactor, there must be a shredder. That’s not something we made up, these are laws of physics. And so due to that, we had to add a shredder as well. The shredder is used to shred stuff that the compactor compacted. And you might think we are going crazy at this point, but the shredder will play its role in providing some new recipes. Such as the production of wood chips or handling late-game retired radioactive waste. That will be covered in our next blog post, in which we dive more into changes in power production.
Mixed products cargo
Some of you might remember that mining trucks were often driving with low volumes of cargo. This was especially noticeable for large haul trucks. This was something that puzzled us for some time. Initially, we tried a few tricks, such as that trucks will hang around the excavators until some excavator needs them again. That was usable only if there were plenty of trucks which was usually not the case. It also introduced an entirely new set of issues, such as that trucks were hanging around for too long.
The ultimate solution we came up with at the end is mixed cargo. Excavators now load multiple products at once, meaning they mine faster. And mining trucks that can carry multiple products at once as well. We measure that on early game mine, and it made mining vastly faster. Note that mixed cargo for trucks is supported only when mining.
You may be curious about how we handle mixed cargo once it's in the trucks. We had two options: introduce a sorting facility or have the trucks sort the cargo themselves by delivering it to individual silos. While some may prefer the immersive experience of a sorting facility, we ultimately decided to have the trucks handle the sorting automatically. This is because introducing a new sorting facility concept in the early game could overwhelm new players. Another option was to enable mixed mining later on through a toggle, but that seemed needlessly complex. Of course, it's also possible that we're just saying all of this to cover up the fact that we spent our entire budget on the waste compactor!
Trees planting & harvesting
One of the big features many of you are looking forward to is tree planting. We had this idea planted in our head and roadmap for some time but it really required some planning and preparations before we could make it happen.
We have put a lot of effort into making sure that planting & harvesting works in an automated fashion. And we also wanted to make sure we can keep using our tree harvester. It all came together and we are introducing a forestry tower. The forestry tower allows you to place and manage planting designations. We are adding a new vehicle - a tree planter that will plant tree saplings across designations managed by your forestry towers. Once trees grow enough, an assigned tree harvester will automatically chop them down. The tree planter will then plant new trees at free spots and the cycle repeats again. You can also select at what percentage of growth you want the trees to be cut. Tree saplings can be obtained by growing them on your regular farm.
Trees can be also placed manually to provide you with decoration options.
We have also removed the lumber mill from the world map that served as an infinite source of wood. As we mentioned initially, it was just a temporary remedy until we provide trees planting feature.
Another change related to trees is that we have solved the issue of trees traveling up & down with terrain becoming inaccessible. Now they just fall down and disappear.
Also, to compensate for the fact that you now need to grow saplings and block some area for forests, we made all the food in the game to be +20% more nutritious, meaning that you need 20% less farm space.
Also, we are adding a new recipe to shred wood into wood chips which can go directly to a boiler or into paper production, where paper will go to research.
We hope that some of the stuff arriving in the Update 1 caught your eye! Please keep in mind that this is just a smaller portion of the changes that are coming in the Update 1. We will try to squeeze in one more blog post before we go live with the update.