Devlog #1 - Upgrading Brickadia to Unreal Engine 5.1

Starting off our new dev blog series with a jump to the future - Brickadia will be using Unreal Engine 5.1 in Early Access. Read on for what it took to get there.

Devlog #1 - Upgrading Brickadia to Unreal Engine 5.1

Welcome to the first post of our new blog series! Here, we'll be keeping you in the loop on all the exciting developments happening behind the scenes as we gear up for Early Access and beyond. We'll be posting every other Monday with updates on our progress and sharing some interesting technical tidbits along the way.

Looking back on last week's announcement

It's been just over a week since we released our first trailer and announced our Early Access release, and we've been blown away by the positive response from the public. With over 100,000 visits to our Steam page and hundreds of thousands of impressions on our social media channels, it's clear that our community is growing fast.

In light of this, we're now planning to have the game localized in multiple languages in time for the release. If you are interested in helping us translate the game in the near future, please feel free to contact us using a Modmail ticket in the Discord.

This is just the beginning, and we have even more exciting announcements coming in the lead up to our April release. Make sure to follow us to stay updated on all the latest news. We truly appreciate the enthusiasm of our community, and together we can make this game into everything we've dreamed it could be. Thank you for being a part of our journey.

Moving to the future: Unreal Engine 5.1

Over the past weeks, when not working at my day job or preparing the big announcement, I've been working on the monumental task of upgrading Brickadia to Unreal Engine 5.1.

Some of you might be wondering why we'd spend so much time on a major engine upgrade less than 6 months before the planned release date. Here's why this is really important:

  • In our previous Open Alpha, the world was made up of bricks on a single global grid. We were able to efficiently send millions of bricks to other clients using custom replication systems. However, in our upcoming Early Access release, we're introducing the ability to place interactive objects and have bricks in multiple grids that can move, like wheels on a car. Unreal Engine 5.1 has a new replication system called "Iris" that will help us handle significantly more interactive objects in the world without performance issues. We'll share more about this in a future blog post.
  • All of the other performance improvements, new features, and especially bug and security fixes Epic is moving into the engine from Fortnite will only arrive in Unreal 5.1 going forward. One day we can use Nanite to render bricks with less overhead in complex interior builds, and Virtual Shadow Maps to remove the constant overhead of rendering the entire build to shadow cascades every frame. Maybe one day we can use Lumen Global Illumination, though this is currently quite far fetched as Lumen handles neither millions of instances nor runtime merged meshes.

So how hard can it be?

Is what I asked myself as I started a few weeks ago on what would become the most difficult software upgrade I ever had to solve. The Open Alpha version of Brickadia ran on a heavily customized version of Unreal Engine 4.27 - we've made several hundred changes in nearly a thousand files ranging from fixing a typo to Quality of Life improvements in the editor to major performance improvements in the underlying physics engine. Nothing got spared from us thinking it should work differently in Brickadia and then making it so.

But at the same time, Epic has been doing the same to their version of the engine... oh no!

Combining Unreal Engine 5.1 and PhysX 5.1

In the Open Alpha, we had upgraded the physics engine in Unreal from PhysX 3.4 to PhysX 4.1 for significant performance improvements with brick collision. Our bricks are now also directly integrated with PhysX for collision detection rather than going through the slow abstraction layers in the engine. Did I mention that we like PhysX?

So what do we do about the fact that Epic deleted PhysX from the engine?

Yes, you read that right - a key component we've relied on simply disappeared from the engine. It was replaced by their custom "Chaos" physics engine, which at least in our tests, currently runs at significantly worse performance for the kind of simulations players will undoubtedly create in a physics based sandbox game.

Luckily, we're not bound by sanity as an indie developer, so the solution is obvious: We just have to put PhysX back in the engine! Oh, and they just released a new version, so let's upgrade it to PhysX 5.1 also while we're at it.

This marks the start of a rather painstaking several weeks of doing nothing but scouring the history for tens of thousands of lines of PhysX integration code that Epic removed, putting it back, then rewriting half of it to make it function properly again, and also hiding all the Chaos integration code.

It was all worth it in the end, because we now have a branch of Unreal Engine 5.1 that can switch between Chaos and PhysX 5.1 with a single build setting. The PhysX version runs several times faster in large simulations, and our brick collision code can now work again. The engine also compiles faster without the Chaos code in it, improving iteration times. As far as we know, we're currently the only ones with such a branch.

Putting it all back together

With the physics engine out of the way, we still just have a bare engine. What about the hundreds of changes we made to 4.27? We need those too, right? In theory, this should be simple - we only need to rebase them onto the new engine version.

In practice, getting those changes across took another entire week of doing not much other than solving several hundred merge conflicts in a row. It was at this point, that I could finally compile the engine and try to load our project again for the first time.

Only to then have to immediately fix hundreds of compile errors all across the actual project code. Did you know the Brickadia project also has more than 200,000 lines of C++ in more than 750 files? Just this weekend, I got to the point where I can make a packaged build again that does not crash when you load some bricks. Almost there... right?

Interlude: Christmas miracle

The first time I got to play the game on the new engine version without it crashing on opening any UI, which just happened to be on Christmas Eve, I was presented with these beautiful red/white colored items. I honestly can't imagine a more amazingly timed bug.

For those curious what actually happened - all items in Brickadia use the same shader to draw them, and we pass 8 colors to the GPU for each item. Since Unreal only allows us to store floating point values with each primitive on the GPU, we reinterpret the color bits as a float before uploading it and cast it back to a color in the shader.

Apparently, one of the APIs was changed to take double precision values before converting them back to a float, making the engine actually do some math with the values that are only pretending to be floats. When you cast some invalid floats like NaN values back to an 8 bit color, it likes to make white (near 0xffffffff) and red (near 0xff000000).

The incorrect vertex tangent saga

Brickadia does a lot of custom mesh processing for bricks, such as generating procedural bricks at runtime, or importing basic ones to our custom asset types. In Unreal Engine 5.1, Epic has done some large refactoring to the mesh data structures we use for this. Oh joy!

Upgrading our code to use the new API was luckily really easy, mostly just adapting to a few renamed types, but this still doesn't look quite right. What could have happened to the normals to make it look this bad?

At some point, I decided to try importing the original normals from the Blender file rather than recompute them. That looked fine, except it doesn't give us any tangent information. So we're now missing the stud on the side, because that's using a normal map, and you need tangent data to display normal maps.

Continuing to try various parameters of the tangent generator in the engine, I ended up getting a stud on a flat shaded turkey:

And a turkey that looks almost correct, but with a slightly bent stud:

The bending problem is more visible in the normal visualizer view, compared to a proper flat stud it has a gradient on the sides.

I've got to be honest with you, I still have no clue why it broke, if it was always broken, or why it worked in 4.27. After spending several hours trying to reverse engineer how the minor changes to this code could have ended up breaking like this and coming up with absolutely nothing, I deleted all of it and replaced it with a different code snippet found online, which worked instantly. Oh well.

Are we there yet?

There's still a lot to do with this upgrade. Many things are printing warnings or crashing outright. Totally expect us to have ironed out these issues over the next weeks though.

I will leave you with this image of Brickadia running inside the Unreal Engine 5.1 editor. Remember to subscribe or follow us so you won't miss the next blog!