Saturday, September 1, 2018

Solar System with Elliptical Planet and Moon Orbits

I'm taking a break from procedural city work to fix up some unrelated features of 3DWorld. A few weeks ago, I came across a Reddit post of someone working on planet orbits, where the author stated that real elliptical orbits were used. 3DWorld was initially using simple circular orbits, so I decided to implement more realistic elliptical orbits. I had actually started writing elliptical orbit physics for planets, but it was incomplete and unused. I also had to fix the orbits for asteroid belts to make them match.

3DWorld orbits are represented by an axis of rotation and two orthogonal vectors of different lengths defining the shape of the ellipse. There were several requirements that made this effort nontrivial. First of all, the system needed to be stable over time. I wanted the planets and moons to orbit for hours while the player explored the universe and participated in ship battles. I didn't want to see planets showing jittery movement after that time. My first attempts at the math worked by iteratively computing the new position of each planet and moon every frame from the previous frame's position and orbital parameters. After a while the system would accumulate too much floating-point error and the planets would drift away from the sun, out of their intended orbits. Moving from floats to doubles extended the period of stability but didn't entirely solve the problem.

A second problem is that there are a lot of planets, and the orbital physics involves some expensive math including trig functions. It took too much CPU time to update every planet and moon in the current galaxy (~400 stars) every frame. I wanted to update more distant bodies at lower intervals, and very distant bodies only when the player flew close to them. This means that the update rate may be only once every few minutes, which is too long to perform an accurate incremental update.

The solution to both problems was to compute the position of the planet or moon from scratch given the starting position, orbit, and current elapsed time. This was applied as a fix-up to the incrementally updated position. It allowed for high accuracy, even with low update frequency. However, there was still some accumulated error in the time variable. What I ended up experimenting with was resetting time back to zero ever few tens of minutes. This can be done when the player isn't nearby or isn't looking at the target. Alternatively, the starting position can be updated to be equal to the current position when the time is reset. The effect is unnoticeable by the user and makes the system stable over hours.

Planets and asteroid belt with elliptical orbits around the star.

All that's really necessary for first order stability is placing the body at the correct distance (radius) based on the orbit for planet <=> star and moon <=> planet. This is trivial when using a circular orbit with constant radius. Elliptical orbits are more complex as the distance depends on the position along the elliptical path. I had to look up the math for this one. I decided to use the current rotation angle to compute the distance using the following online post for reference. My code can be found in the 3DWorld GitHub src directory, function get_elliptical_orbit_radius() in Universe.cpp. This function is used in urev_body::do_update() for planets and moons, and in uasteroid::apply_belt_physics() for asteroids (in asteroid.cpp).

Another problem I encountered was limited floating-point precision in the position update math. This caused planet movement to appear jittery. This was due to the large difference in magnitude between the planet's position in the galaxy compared to the size of the current frame's delta position update. If the position update was less than one mantissa bit, it would only move every few frames. The fix was to make planet motion math relative to the star it was orbiting around, and moon math relative to its parent planet. This kept the magnitude of the numbers smaller, improving the precision of position updates. Note that I actually fixed this problem some time ago.

Here is a video of planets and moons in motion with my attempt at true planetary physics. Keep an eye on the shadows, they look pretty good. I've added a config file variable that I used to speed up time by a factor of 400 over the default timestep values. This makes the planets orbit the sun in a matter of tens of seconds rather than hours. It makes planet and moon orbits much easier (and faster) to debug.

Yes, I'm aware that the planet I'm following is inside the asteroid belt. I'm not trying to fix this right now. The planet-asteroid belt intersection code doesn't handle elliptical orbits.

It took me quite a few attempts to get this right. I should have kept some of the crazy broken videos, but I didn't think of it at the time. I had some where there was an error in the math and the system quickly went out of control, throwing all of the bodies off into space. I had another bug where the orbits were wrong and formed a figure 8 through the sun. And I saw yet another problem that made planets do little loopy dances across the screen. Oh, and I also had one problem where the planet atmospheres trailed behind the planets by a frame. But in the end, I was able to get everything working correctly, stably, and efficiently.

I added another config file option to control elliptical orbits. I'm using circles for the default scene though, so that the changes in orbits don't break my current ship and colony placements.

No comments:

Post a Comment