informa
/
Programming
Featured Blog

Aerodynamics of Just Cause 4

This article delves into the techniques used to simulate aerodynamics and extreme weather events in Just Cause 4

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 simple idea of how objects should evolve around the tornado but we didn't necessarily agree about the distribution of wind speeds around it to get to that result. Instead of discussing endlessly, we made different components of the wind field tunable at runtime so Hamish could experiment and quickly find what was working. The tornado is really a stack of horizontal cylinders which centers are located on the center spline, which deforms over time. Within those horizontal cylinders, we decomposed the tornado wind field in cylindrical coordinates, with a tangential component, a radial component and a vertical component. Each component was influenced by tunable curves.

 

Figure 2 - Our gorgeous tornado - Shader work by Gabriel Sassone, Engin Cilasun, Stephen Yuen, Eddie Gong and Hamish Young who all contributed at different stages of development. Time is accelerated non uniformly here to showcase some nice looking times of day and skip faster over night time.

 

As in many other features in the past and as is common in the industry, Hamish Young and I went back and forth on the tuning and the code until the result was satisfactory. Hamish tuned the curves to his liking, dropping dozens of 40 ft (~12m) shipping container around the tornado to see how they behaved, and using the highest recorded wind speed (~230 mph) as the max speed around the tornado. After a while, Hamish found that his tuning was made difficult by the need to make the wind everywhere consistent with a simple rotating motion, but pull towards the center at the same time. It was already the case that we had angular velocity as part of the field, to spin objects in the tornado in addition to pushing them. This would guarantee for instance that objects stuck in the center would rotate in place, as we wanted. So Hamish asked to start with a wind everywhere consistent with this angular velocity at the center, meaning to start with a velocity field of a perfectly rotating field, which the tunable wind field would add itself to. With this last adjustment, the curves ended up being:

  • \(\mathcal{R}(d)\) - radial component multiplier (radial progression)
  • \(\mathcal{T}(d)\) - tangential component multiplier (radial progression)
  • \(\mathcal{V}(d)\) - vertical component multiplier (radial progression)
  • \(\mathcal{H}(h)\) - horizontal component multiplier (vertical progression)
  • \(\Phi(h)\) - vertical component multiplier (vertical progression)

Where \(h\) is the height above the bottom of the tornado and \(d\) the distance from the center. The tornado wind velocity is given by: $$\vec{V}(d, h) = [V_t \mathcal{T}(d) \mathcal{H}(h) + \Omega d]\hat{t} + V_r \mathcal{R}(d) \mathcal{H}(h) \hat{r} + V_u \mathcal{V}(d) \Phi(h) \hat{up}$$

When objects have accelerated to the tangential speed of the tornado at a given distance, the tangential component of the air drag forces becomes essentially zero since both air and object are tangentially moving in unison. The remaining part of the wind velocity pulls the object in, just as a planet would pull a satellite, to bend its trajectory so it remains an orbit, and the objects are pushed up, to reach higher altitudes. Pushing objects up is a crucial performance optimization, preventing expensive computation of contacts between hundreds of objects and the ground. That the tornado expands in radius with altitude also helps, spreading out objects spatially so they are less likely to touch and lead to contact computations. A real tornado is formed by an exchange of air when a layer of cold air sits on top of a layer of warm air. The warm air ascends in a swirling fashion around the center of the tornado which is the cold air descending. So in principle, at the center of the funnel the wind speed should be strongly directed downwards. As you can see we have no such thing to avoid the chance of objects being pushed down to the ground and being dragged along with the tornado, incurring an expensive collision detection cost.

 

Figure 3 - Tornado wind field in horizontal plane cuts (above, zoomed in), then in vertical plane cuts. The 2 cuts on the left leave out the \(\Omega d\) term of the purely rotating wind field from which we start, to see better the additional field necessary to make the tornado work. The version on the right includes the rotating field. At distance we have a very fast wind at distance that is roughly at 45 degrees from the radius to any point. The vertical cut on the left shows how much the tornado has to pull to the center. Then there is a transition range within which there is very little wind, followed by a strongly rotating field in the center. The intensity of the wind field is the color, the redder the stronger.

 

Figure 4 - Path lines around the tornado wind field - center spline straight.

A straight tornado is a boring tornado, that's what I always say. To deform it we used 2 cubic splines connected end to end, with 3 of the control points in orbit at different speeds around an imaginary vertical line. As a result the profile of the tornado changes over time so it slowly shifts from an S shape to a C shape and back. Appendix 5 explains exactly how we constructed the splines. Interestingly the tornado didn't work as well in deforming mode. Objects that had orbited in an orderly fashion around a straight tornado were now sling shot as soon as the center spline of the tornado was receding from them, and would sadly rain down to the ground. To fix this problem we included yet another term to the velocity, due to the deformation of the tornado itself. After all, that deformation must have originated from air motion in the first place and it seemed natural to include that component. This fixed the problem nicely, giving path lines very similar to the figure below.

 

Figure 5 - Path lines around the tornado wind field - center spline bending over time

 

Figure 6 - The tornado in game, destroying and sucking up everything on its path. This is Havok's visual debugger (VDB), an extremely useful tool that allows us to see the simulated elements of our simulation.

 

Wind Tunnels

Wind tunnels were developed to give control over the physical wind in certain sections of the world, such as canyons and caves but also over smoke stacks, in front of giant industrial fans or wind cannons. It can be used as a way to guide the player in certain spaces or missions. Our first prototypes used a traditional cylinder with a uniform vector field of constant velocity parallel to the cylinder's axis of revolution. Placing all those cylinders in the scene was cumbersome so we instead used a cubic Bézier spline around which a circle of varying radius is extruded. This shape is approximated by splitting it along the length of the central spline into capsuloids, i.e. capsules which radii at either end are different. The velocity field within it also went through several evolutions. At first, the wind in the tunnel was tangential to the central spline. We authored wind speed at each control point of the spline, and the speed was interpolated from one control point to the next along a spline segment. Soon, however, Joshua Espinosa, designer in charge of wind tunnels and their traversal by the player, found that he needed two more components. One component was an updraft, giving an additional artificial lift to the player, always pointed upwards. The other component we called "pull-in", which pushed the player towards the central spline, but was strongest out the outskirt of the tunnel, ramping down to zero at the center. Finally, a fall-off margin in the outer part of the tunnel ensured the transition from inside to outside the tunnel wasn't too abrupt. That margin thickness was authored as a percentage of radius, effective across the whole wind tunnel.

 

Figure 7 - Wind tunnel as split into a sequence of overlapping capsuloids. The splitting occurs when the direction of the spline changes, or when it is no longer possible to interpolate linearly the longitudinal change in radius (A) or wind speed (B). We computed the second derivative of those variables along the spline to determine when to cut.

 

Figure 8 - Wind tunnels as edited in the Apex Engine. The wind tunnel is broken into capsules as needed, depending on the spline, and the variations of radius and wind speed variables along it. As discussed in the next section you can also see the spatial partitioning which sorts the capsuloids into Morton ordered cells. This is all fast enough to update in real time and the wind tunnel can quickly be interacted with for testing. This authoring tool was scrapped together by re-purposing some parts of the river authoring tool under development and debug draws.

 

Figure 9 - An example of a long wind tunnel used to help the player travel upstream the river to the mountain. The river current pushes Rico towards the sea and the wind can bring him back quickly inland atop the same river. The player mechanics team found that rotating Rico by side wind was too jarring, so the main influence of wind is to accelerate him (with tail wind) or decelerate him (with head wind) and give him additional lift (updraft component).

 

Figure 10 - Rico wing suiting in that same wind tunnel. No direction changing input was given here. At first Rico loses altitude until he reaches the outer part of the wind tunnel which slo

Latest Jobs

Sucker Punch Productions

Bellevue, Washington
08.27.21
Combat Designer

Xbox Graphics

Redmond, Washington
08.27.21
Senior Software Engineer: GPU Compilers

Insomniac Games

Burbank, California
08.27.21
Systems Designer

Deep Silver Volition

Champaign, Illinois
08.27.21
Senior Environment Artist
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more