Aerodynamics of Just Cause 4

Feb. 22, 2019
protect

Jacques Kerner is a senior software engineer at Avalanche Studios.

As if it wasn't crazy enough before

Introduction

The Just Cause series and Avalanche Studios are known for their open world technology and offer one of the most varied and engaging open world experience in video games. The latest iteration, Just Cause 4, adds wind and extreme weather as notable new comers in the array of technologies that deepens the gameplay experience. But extreme weather, from its original design, was more than just a way to simulate a more believable world. It is the fury of nature controlled by the forces of evil cast against Rico Rodriguez. The design intent was also to make wind more present throughout the world so extreme weather wouldn't feel like an abrupt event not belonging to that world. This article presents the techniques we developed to realize wind in all its manifestations from a physical standpoint and how all objects were made to react to it.

JC4 Tropical storm - early concept art (Volta)

An offer we couldn't refuse

As Just Cause 3 was coming to an end, a big part of the development team rolled off to the pre-production of Just Cause 4, while a small nucleus remained to work on patching JC3 and working on downloadable content ("DLC"). Hamish Young and myself, lead vehicle designer and lead vehicle programmer at the time, focused on the Mech Land Assault DLC. We were to become the lead physics (and player mechanics) designer and lead physics programmer of JC4, respectively, but were completely engrossed in our DLC, while an entirely new iteration of the franchise was being designed, its unique selling points and back of the box features determined. At the forefront: extreme weather, in all its manifestations, playing a central part in the game. By this time, extensive prototyping had been done, proving out new core mechanics and how Rico would react to wind. Part of the back story was drafted, the early design direction approved by the publisher, Square Enix. The only thing left to do was to embrace the concept. But how to do it? Especially in a way that wouldn't jeopardize performance. As soon as we joined the project, we attacked the problem on two fronts: 1. institute some broad restrictions right away to avoid the worst case scenarios (spoiler, we failed) 2. understand the different manifestations of extreme weather, and extreme wind in particular, to design a system giving a realistic behavior but scaling well with the desired number and density of objects.

JC4 Sand storm - early concept art (Volta)

Damage control

A performance bottle neck for real time simulation, and open world games in particular, is the number of collidable physical bodies. The main cost comes from computing collision of the many moving bodies among themselves and between them and the static scenery (terrain, buildings). This is why physics engines such as Havok distinguish between active and inactive bodies. Active bodies are checked for collision against other bodies and incur a full cost. When an active body hasn't moved for a few frames the physics engine marks it as inactive and from then on it can be completely ignored until it gets woken up by an active body coming into proximity. Those inactive bodies are usually resting on ground and the collision check between them and the ground is no longer calculated. The presence of wind everywhere in the open world was an obvious threat to this and it was important to make it very clear that the wind needed to be either moderate and purely cosmetic in which case it could occur in large areas, or strong and physically activating, in which case it needed to be constrained to smaller volumes and found in locations where the number of potentially active objects was not too large. I communicated these restrictions to our design team early on to be sure we wouldn't put ourselves in a tricky situation. At first the design team seemed to be listening and decided to restrain the trajectories of extreme weather events to predefined paths of destruction within which more restrictive building rules would be followed, and some of these restrictions were indeed respected. But the temptation was so great, the peer pressure to blow everything up in a Just Cause game too irresistible. What was I thinking? The very next thing to happen was the design of a kilometers high tornado passing right through the capital city, the densest populated part of the island. This tornado sucked in not only half of that capital city but also a few unsuspecting programmers not firmly committed to another core feature.

JC4 Blizzard - early concept art (Volta)

The overall approach

Ultimately, the physics of extreme weather of JC4 required the following ingredients:

  • An aerodynamic drag model applying to all dynamic objects in the world taking into account their shape and dimension

  • Sources of wind, matching the desired shape and wind distribution of extreme weather events (a storm, a tornado) but also for authoring wind patterns around the world

  • Optimization of the above, to remain within budget

These three problems needed to be solved almost simultaneously, and as early as possible. A lot of the design decisions depended on being able to deliver on all three, and in the first months you could feel the tension from designers patiently waiting to be able to play with any of it on a large scale.

The aerodynamic drag model came first. After its initial submission, every object suddenly would react to wind in a somewhat realistic manner. For instance, when dropped from a cliff or falling back down from an explosion, all objects slowed down and rotated in air in a more realistic manner, which was worth it in itself. But with no source of wind to speak of it was hard to tell how well it was going to work in extreme wind. While we prided ourselves that experience and intuition helped quickly adopt an approach for the first two problems that would satisfy the performance requirements, we estimated ourselves pretty lucky when the first version of the tornado turned out to work exactly as expected, picking up all dynamic objects, swirling them around in a way that seemed realistic enough, without completely bringing the CPU to its knees. Francesco Antollini, Game Director of JC4, who had been asking at increasingly close intervals about the tornado, came by my desk almost immediately after its submission to express his deep relief: this was going to work - I guess I must have missed an opportunity to make him feel bad for ever doubting me or something along those lines; in reality I felt exactly the same.

JC4 had to push the envelope compared to JC3 in terms of physics: a denser, fuller, more lively and beautiful world, with more interesting destruction. All of this on exactly the same target platforms (XBox One and PlayStation 4). Add to this extreme weather and you had ... the perfect storm. It is no wonder that optimization work took a great part of our time. One advantage that Dave Barrett, our technical director, leveraged immediately was that we had a base line for performance with JC3 and Daniele Pieroni, lead of the engine team, was put in charge of determining what the performance and memory budgets should be for each technical discipline. Our budget for physics was a generous 8.5 ms on 4 threads out of 33 ms of CPU time available to simulate a frame, to reach 30 frames per second. The greatest part of this budget is reserved to Havok to detect possible collision between objects, compute contacts, solve constraints and "integrate" the motion of all active bodies to determine their position after 33 ms of simulated time. I estimated that our budget for all aerodynamic and wind related computation was therefore about 1 ms on 4 threads. We mostly kept within budget, although it's probably a good idea to not ask Daniele for his opinion on the matter. The truth is that the solution presented here is fast enough for real time that involves several hundreds of objects on modern CPUs. It could certainly be optimized much further by being adapted to run on GPUs, as many things are.

In the rest of this article I outline how we approached each problem, and confine the details or mathematical developments in appendices, for those interested.

JC4 Wind - early concept art (Ironklad Studios)

Drag model

Our goal for the drag model was to estimate the resistive forces and torques that vaguely resemble those that would be applied on a body of arbitrary shape in reality. Note how unambitious we are here, there is no claim to great realism. We did however have some requirements. First, the resistive forces should oppose the motion of the body. Second, the force should stem from the basic aerodynamic drag equation, i.e. scale with the surface area of the shape in contact with air and scale with the square of speed of motion through air. Finally, the shape and size of the body should come into play somehow, so that objects of the same surface area but very different shape behave differently. I was very tempted to just approximate every body's shape by its bounding box but I thought I needed to do better than that, as it was certain that the shape of a lot of objects would vary considerably from a box or even a small number of boxes. Previous experience with systems crudely approximating the volume of 3D objects with voxels or spheres pushed me to find a better solution. Some suggested we author coefficients of drag for each objects manually but I wanted to prevent adding yet another manual step to our already complicated pipeline. So I kept looking for an automated method. I'm glad I persevered.

The simplest method was to estimate a wind pressure on each face of the collision shape of our objects, due to incoming air, ignoring any sort of air circulation around the object or viscosity of the air surrounding it. As if the air drag simply consisted of pushing forward the mass of air in front of the object, or sucking it in on the receding side. An early prototype showed that this method was good enough for our purposes.

The "only" issue was that summing the contributions of thousands of triangles for every single shape at runtime was an order of magnitude too costly. We could easily reach more than 100,000 faces in heavy scenes. It would be great if we could precompute the forces beforehand. A first brute force method is to precompute the aerodynamic forces and torques due to the body translating at different speeds in stationary air in several directions and rotating at different angular speeds around different directions to build a look up table to be sampled from at runtime instead. Or you can imagine a virtual wind tunnel, operating at compilation time of our content, where we place each object in turn, and subject them to different translating or rotating air speeds, positioning each object at various angles and measuring the forces and torques each time.

What would this lookup table look like? At runtime, the known quantities are the linear and angular velocities of the body, so \(S=5\), only about 300 kB per object. It is still at least an order of magnitude over the memory consumption I could afford.

To reduce drastically the number of samples I used two techniques. Firstly, I linearized the computation into a sum of contributions based on precomputing the forces and torques on the purely translating body on one hand and the purely rotating body on the other. Secondly, I approximated it into an analytical formula for extrapolating the value at arbitrary speeds based on the precomputed response at a translating speed of 1 m/s and a rotating speed of 1 rad/s only. This reduced the precomputed table to a size of just 7 kB per object. Unfortunately this required making a few approximations. The issue stems from the fact that even with a simple model of drag we are facing the fact that the forces applied to a body that is both rotating and translating is not just the sum of the forces applied to it when just rotating and just translating. Even though I had to make some approximation, I managed to keep some of the terms due to the combination of rotating while translating motion, akin to a Coriolis term. You can refer to Appendix 1 for the derivation of the formulation. The end result is that instead of directly storing the forces and torques for each sample, we store a vector of terms \(\mathcal{A}(\hat{u})\) that appear in the linearized formula. Each sample corresponds to a unit linear velocity relative to air of 1 m/s or a unit angular velocity relative to air of 1 rad/s, in a certain direction. The components of this vector are used in the formula that also takes the actual linear and angular velocities of the object relative to air to compute the forces and torques to apply on that object. Appendix 1 explains exactly how.

To store the look up table we used a cube map, i.e. a cube which faces are divided into grids of, say, 3x3 cells, like a Rubik's cube. We store values at the corners of the cells so each face stores 4x4 values. At each cell corner we calculate the vector going from the center of the cube to the corner, and we normalize that vector to get a unit direction. We use this unit direction vector as \(\mathcal{A}(\hat{u})\) for the cell corner. We end up with 6 tables (one per face) of 16 entries with 19 floats each, just a little over 7 kB, very manageable. There are ways to reduce the number of samples even further, but this way makes it very easy to interpolate an arbitrary velocity direction: since it always falls on a cell, it is always possible to use the values at the four corners of that cell to do a bilinear interpolation for that particular direction. The cube map technique and bilinear interpolation are very widely used in rendering so there are ample resources and code available.


 

Figure 1 - Cube mapping of unit velocities relative to air. On this figure I have isolated in red a particular cell of the cube map to illustrate the principle. Given the linear velocity \(\hat{\omega}\) relative to air, we sample the same cube map again to find the blue cell and use the 4 coefficients stored at its corners. We then mix all the values into the formula of Appendix 1.

 

While this worked well in most cases, we encountered a few complications regarding changing the center of mass or scale of objects at runtime. Our solutions to these problems are found in Appendices 2 and 3.

While developing this model, I wasn't certain about the amount of sampling needed to get a good behavior of objects moving and rotating in air, so I wasn't sure I would have enough memory on disk or at runtime. We also started building more Havok destruction assets which have hundreds of fracture debris of various sizes, sometimes several hundred pieces for a single asset, and it dawned on me that a few of these assets would require as much memory as all other assets combined if every fragment was to come with its own cube map. The general model was good to capture some of the unique characteristics of complex objects but overkill for smaller debris. I also wasn't sure about the cost at runtime and having the option to fall back on a cheaper model was a good way to limit the risk ahead of me. So I found a very compact formulation (making generous approximations) solely based on the bounding box of objects, which you can find in Appendix 4.

Wind volumes

We introduced the concept of wind volume to cement the idea that strong, physically activating wind was only occurring in finite spaces, and we started classifying them by shape and wind distribution. We distinguished between the following main types of wind volumes:

  • Cylindrical wind volume used for the blizzard, sandstorm and tropical storms

  • The tornado wind volume, a vertical stack of cylinders centered around a vertical spline deforming over time

  • Wind tunnels composed of a series of connected capsuloids (capsules with different radii on either end) forming a sort of wormhole

This restriction to a finite volume came under attack a couple of times, and it came almost undone at the eleventh hour with "topographical wind". This wind, present everywhere, would correspond to the drift of clouds in the upper atmosphere and would provide the player with an updraft wind whenever it pushed against a rising incline of terrain. We did implement it but the lack of clear graphical representation of that wind forced us to cut the feature. The rendering team was already over tasked with implementing clouds, rivers, waterfalls, upgrading to DirectX 12 and so many other things.

In retrospect the storms were the simplest wind volumes, in terms of shape (cylindrical) and wind distribution (mostly unidirectional for the sandstorm, or rotating around a center axis for the blizzard). The tornado and wind tunnels on the other hand were more complicated and deserve a bit more explanation.

The tornado

We all had in our mind a very clear and sim

JikGuard.com, a high-tech security service provider focusing on game protection and anti-cheat, is committed to helping game companies solve the problem of cheats and hacks, and providing deeply integrated encryption protection solutions for games.

Read More>>