Customer Story: How Simplygon helped bringing Anno 1800 to PS5 and Xbox

Written by David Shelton, Senior Digital Artist, Ubisoft Mainz

Anno 1800 is a city-building game from Ubisoft Mainz that was released in 2019 for PC. It features a massive world with huge islands, dense forests and hundreds of unique and beautiful buildings that form anything from small villages to gigantic cities. There’s a wide range of assets, small fishing huts, noble four-storey-tall residences, breathtaking skyscrapers, factories, monuments and more.

Screenshot from Anno 1800.

Thanks to its great initial success and continuing support of the live-game with DLCs and cosmetic DLCs, the decision was made that the title should launch on console, too. This was a huge undertaking for all of us at Ubisoft Mainz, as we had never released on console before. The entire Anno series had been PC only, for all prior installments.

Screenshot from Anno 1800 showcasing residential quarter.

We wanted to see if Simplygon could help improve the general performance and memory usage of the game. Our RnD team was particularly interested in reducing the object count. Optimizing all assets of the game seemed like a daunting task. But we accepted the challenge and mastered it in the end.

Screenshot from Anno 1800 showcasing tourists in city.

Getting the assets into Simplygon

We use a proprietary engine with custom tools at Ubisoft Mainz, so the first step was to get the fully configured assets with materials to Simplygon. A typical Anno asset consists of model files for the main building using just a few materials and a lot of props like barrels, boxes and lamps. The assets can also be nested, with files inside files. This means that even a simple building can have a high number of objects associated with it. This wasn’t a big issue for PC but had to be solved for console.

Marked building from Anno 1800.

The first step was writing all meshes from an asset to disk, using one of the supported formats by Simplygon. Since all models already used 5 LODs, that were proven to work and had to be kept, we included all LOD levels within that file.

Prototyping in 3ds Max

The easiest and most straightforward method to test different pipelines was to load the model into 3ds Max and test all the options with the Simplygon plugin. After some research we decided to go with the aggregation component of Simplygon with chart-aggregation for textures. This meant, that we had to get all material information from our original models through Max to Simplygon. Telling Simplygon how to interpret our materials was done using the shadergraph. We were supported by the Simplygon Support on difficult topics like these. Here’s an entire blog-post about one of the topics:

This was our prototype pipeline: Game engine → 3ds Max → Simplygon → 3ds Max → game engine.

Running all objects through 3ds max would have had quite a few downsides, though:

  • A lot of imports and exports meant a lot of conversions of mesh formats and materials. Retaining all properties of a material from our engine was nearly impossible.
  • No multi-threading
  • Optimizing all assets would take days.

Using Simplygon Standalone with Python

It was clear that we needed to optimize our pipeline and build a slim and more effective version. We cut out 3ds Max and used Simplygon Standalone with its python interface instead.

Final pipeline: Game engine → Simplygon → game engine.

Although this was not effortless, it was straightforward, and meant a spectacular speed boost, which was highly appreciated. Optimizing a single asset was significantly faster than before (no need to start 3ds max), and optimizing multiple assets was mind-blowingly fast thanks to multi-threading. The entire game now took only around 3 hours to optimize. This meant we could optimize all assets in one go, look for any issues, and optimize all of them again. In the end we ran all assets countless times through Simplygon.

Here is a snippet from the code:

def start_batch_processing(batchfilepath, logfile, use_remeshing, casting):
    # cleanup()
    start_time = time.perf_counter()

    # get all paths from a text-file
    all_folders = parse_batch_file(batchfilepath)

    sg = simplygon_loader.init_simplygon()
    sg.SetGlobalDefaultTangentCalculatorTypeSetting(Simplygon.ETangentSpaceMethod_Autodesk3dsMax)

    USE_THREADING = True

    timings = []

    if USE_THREADING:

        with concurrent.futures.ThreadPoolExecutor() as executor:
            # optimize all assets
            results = [executor.submit(process_assets, sg, folder, logfile, use_remeshing, casting) for folder in all_folders]

            for f in concurrent.futures.as_completed(results):
                timings.append(f.result())


    else:  # no threading
        for folder in all_folders:
            res = process_assets(sg, folder, logfile, use_remeshing)
            timings.append(res)


    num_objs = 0
    for folder in all_folders:
        num_objs += len(glob.glob(folder + '*.obj'))
    print('Success!')
    print(f'{len(all_folders)} folders containing {num_objs} objs have been optimized')
    print('')
    print("Total time elapsed: {:.2f}s".format(time.perf_counter() - start_time))

    del sg
    # sg = None
    gc.collect()

The support of the team from Simplygon was really amazing. They basically guided us through all steps of the entire process and were able to solve all issues. And they were very proactive and suggested potential improvements.

Challenging the LOD-chain

Once we had a working pipeline we were able to perform any test we liked and get the results pretty fast. The most important question was of course: What would bring us the biggest gains?

Aggregation with geometry merging and reduction

One of the bottlenecks in our game was object count. Each object had it's transform going through the CPU. When all objects merged together this led to a significant reduction of draw calls, with almost no impact on storage space of the game. The triangle count was improved for most assets.

Aggregated asset with one object compared to original object with 43 objects.

Aggregation with texture aggregation and reduction

Even more significant reduction of draw calls, but also a lot of new textures. Surprisingly the general performance was not improved that much.

Several materials merged into one atlas.

Remeshing of the last LOD level

This was an idea to improve our performance by using a completely remeshed version of our assets. We loved how well this worked, however the performance impact was barely noticeable if applied to many assets. As Anno 1800 is a strategy game our LOD4 is usually very low poly already, with less than 1 000 triangles. The textures where also very low resolution as is. But it is nice to have this technique ready when we need it. Going from LOD0 to LOD4:

Original asset of 40k triangles and remeshed asset of 900 triangles.

In the end, we opted for the initial option, just aggregation with geometry merging and reduction in most cases, as it gave us the biggest bang for the buck. Since it didn’t require huge computational power, we could optimize all assets in about 30 minutes now.

Conclusion

We are very happy with the outcome of the research, and the final implementation. It’s great to have a Swiss army knife of optimization techniques at our disposal now. It’s a solid, versatile, fast and effective pipeline.

We can’t wait to use it for any upcoming projects, right from the start.

⇐ Back to all posts

Request 30-days free evaluation license

*
*
*
*
Industry
*

Request 30-days free evaluation license

*
*
*
*
Industry
*