Bow shock - A summary of work done so far

Published October 24, 2011
Advertisement
This is my first journal entry so I'll start with a few words about myself. My name is Silviu Andrei, I'm a software developer from Romania, currently living in the United States. I've been working as a software developer for about 8 years now. As I am also a passionate gamer and Sci-Fi fan I always dreamed of an open world space sim where you would be able to explore an entire galaxy and seamlessly land on planets, asteroids, comets etc. So, about one year ago I decided to start developing a planet renderer that allows you to transit from space to ground in a seamless way and about one month ago I decided to try to make a game out of my planet renderer. Enough about me and my boring history, let's get down to business.

I will briefly present here all major aspects of what I have done so far.

Technology



As my natural tendencies drove me, I started to develop the planet renderer in C# using XNA. As I am frustrated by the fact that XNA is limited to DirectX9 at the moment I decided to stop the development on the current version and port all the code to a different platform. I have still not decided whether I should stick to C# and use SlimDX or whether I should just go with C++ and DX11. C# has one big advantage, the development speed is much higher IMHO. I am also a bit rusty on the C++ side, it might take me a while to get warmed up with it. On the other hand, C++ is faster, not a lot faster but faster. I wrote an app last year to test this out. It consisted of a perlin noise generator and some single&double precision floating point calculations both in C++ and C#. It turned out that C++ was overall about 1.2x faster than C#. (I'll try to find that benchmarking app and publish the source code in my next entry if anyone is interested). This was not enough to convince me at that time to turn to C++. However, I would like to grease up my C++ skills a bit so I'm tending towards C++ right now. I just hope it's not going to stall my progress on Bow shock.

Terrain LOD



I spent a lot of time on the terrain LOD last year, experimenting with a lot of techniques. I started out with ROAM (which is not efficient on current hardware anymore), then moved to SOAR (not a big change there) and finally ended up using a version of geomipmaps. I basically have a quadtree where the basic shape is a cube with 6 nodes (one for each face of the cube). Each node has about 29x29 vertices and is split based on a split-priority (I keep a sorted split-priority queue for all the nodes). The split priority is calculated based on the node's sea-level radius and its distance from the camera. The sea-level radius is the distance from the center vertex to the farthest vertex (all at sea-level without any height displacement). Initially I used instead of the sea-level radius the LOD of the node but soon I noticed that nodes closer to the main cube's edges tend to have smaller radius and nodes closer to the center of the cube's faces have larger radius due tothe cube-to-sphere transformation. Therefore nodes closer to the cube's edges got split sooner than nodes closer to the center of the cube's faces. The distance from the camera is calculated using the center vertex of the node (displaced by the heightfield)

Terrain Heightmap



For a planet of this scale it's not feasible to generate the terrain offline and store it to disk, it would take too much space and it wouldn't be possible to land on a planet without "loading" stops. Therefore it must be done procedurally in real-time each time a node is split. The terrain, along with atmospheric scattering (which I will describe below) is the part that required most of my time. The problem with heightmap generation is that variable tweaking to code implementation ratio is very high. I think I spent most of the time tweaking variables, recompiling, waiting for the result and going back to tweaking. Maybe it would have saved a lot of time if I had built a terrain editor of some sort where I could tweak parameters in real-time. I didn't do it because I always felt like the tweaking is almost done and that I would waste too much time writing one. Also the compile time of HLSL effects with deeply nested loops in XNA is horrible, I had a shader that took up to 20 minutes to compile, after tweaking I got it to 30 seconds but that is still a lot of time when you do constant tweaking on the shader code. For the terrain generation I use perlin noise, multifractal noise and cell noise. I use 3 main levels of noise, the first level defines the shape of the continents, the second one defines the mountain map (the areas that contain mountains) and the third one defines the mountains themselves.

The mountain map is basically 2 octaves of F1 Voronoi noise displaced by some FBM and modulated by the continent map so that mountains are most likely to exist on the continents but to allow for a small chance for them to extend outside the continent shore.

The mountain generation noise required most of the tweaking. It contains 2 octaves of F2-F1 Voronoi noise that define the basic shape of the mountains followed by a multifractal of 10 octaves of F1 Voronoi noise for the mountain details and an overall terraced effect modulated by 3 octaves of perlin noise (FBM) so that only some of the mountains become terraced.

I use a heightmap of 160x160 for each node and I compute a finite difference object-space normalmap in a second pass. I also render a 29x29 heightmap to get the height for the node vertices on the CPU side. I do this because I will need the height of the vertices for collision detection on the CPU side.


Atmospheric scattering



I get chills up my spine whenever I think of atmospheric scattering. I spent so much time tweaking, retweaking, dropping everything and starting from scratch that I had enough of it. First I implemented Nishita'smethod from his original paper, then moved to Oneil's implementation and finally I dropped everything and settled to Bruneton's method for multiple scattering which also took some code-porting, tweaking and bugfixing. I am pleased with this final method and I hope I will never have to touch that part of code again. There is a lot of math involved here which I will not go into. It's allexplained in his paper "Eric Bruneton and Fabrice Neyret - Precomputed Atmospheric Scattering" which is freely available on the internet if you are interested.

Ocean



For the ocean I also experimented with a few methods. First I started with Gerstner waves computed directly in the vertex shader and pixelshader (for normal mapping). After hitting some aliasing and performance problems I implemented Bruneton's ocean model with a projected grid which instead of using a fixed object-space mesh for the ocean, it uses a screen-space mesh. Basically you have a grid of NxM equally distanced vertices on the screen which you project, in the vertex shader, onto the surface of the ocean, displace them by the wave function and then project them back to screen. This way you will have the same amount of vertices no matter how close you are to the surface of the ocean. The performance increased a lot but aliasing problems are only less obvious but they are still present and it looks bad at the shore because thegrid vertices sample the ocean at different locations along the shore and waves seem to pop out of the ground as you move the camera. Finally I implemented a FFT ocean model inspired from the Tessendorf paper and the Bruneton ocean code. Basically I generate a 256x256 wave heightmap and normalmap using a Fast Fourier Transform implemented in the pixel shader which I then tile over the entire ocean surface.


Ship controls



I spent the last couple of weeks working on the ship, finding a free model on turbosquid that also looks good. Importing it into the engine, adding thruster effect and adding navigation controls. The ship had to have some sort of wings because I wanted it to behave in the atmosphere like anairplane. The ship is also able to fly out of the atmosphere into space were it uses the same main thruster for gaining linear speed but uses nozzles for rotating since the wings do not generate any lift in space. For atmospheric flight, I implemented the drag and lift force equations based on speed, airdensity, wing surface, angle of attack etc. When the airspeed (linear speed^2 times the air density) is below a certain threshold I enable the nozzles to control the ship. I also simulated an autopilot that tries to stabilize the ship if it starts to rotate. In space the autopilot calculates which nozzles to activate in order to stop the ship from rotating and inside the atmosphere it adjusts the ailerons of the ship in order to control the roll. The roll is the only rotation that can go out of control at high speed since the pitch is implicitly stabilized by the tail and the bank by the ruder. The autopilot also tries to stop the ship from nose diving as it loses speed, by adjusting the main wing's flaps. I implemented all the auto-pilot adjustments by constantly solving all the aileron/nozzle equations for aileron angle of attack / nozzle intensity.

This is my ShipWing class that does all the aerodynamics calculations.
public class ShipWing{ private float _shipOneOverMomentOfInertia; private float _shipMass; public ShipWing(float shipShipOneOverMomentOfInertia, float shipMass) { _shipOneOverMomentOfInertia = shipShipOneOverMomentOfInertia; _shipMass = shipMass; } public Vector3 CenterOfGravity { get; set; } public Vector2 MinMaxDrag { get; set; } public Vector2 MinMaxArea { get; set; } public Vector3 Normal { get; set; } public Vector3 BiNormal { get; set; } public float Flaps { get; set; } /// /// Returns the lift coefficient for the specified angle of attack and flaps angle /// /// Angle of attack in radians /// Flaps angle ranging from -1 to 1 representing about +- 28 degrees public float GetLiftCoefficient(float angleOfAttack, float flaps) { angleOfAttack += flaps * 0.5f; angleOfAttack /= (float)(Math.PI / 2d); if (angleOfAttack < -1) angleOfAttack += 2; if (angleOfAttack > 1) angleOfAttack -= 2; return (float)Math.Sin(angleOfAttack * (1 + (1 - Math.Abs(angleOfAttack))) * Math.PI) * 1.5f + (flaps * 0.9f); } /// /// Solves for flaps angle using a binary search /// /// The angle of attack of the wing /// The desired lift coefficient /// public float GetFlaps(float angleOfAttack, float liftCoeff) { float delta = 0.001f; float maxLc = 2.4f - delta * 2; float minFlaps = -1; float maxFlaps = 1; if (Math.Abs(liftCoeff) < delta) return 0; if (liftCoeff > maxLc) return maxFlaps; if (liftCoeff < -maxLc) return minFlaps; var flaps = 0f; var currLC = 0f; do { currLC = GetLiftCoefficient(angleOfAttack, flaps); if (Math.Abs(currLC - liftCoeff) > delta) { if (currLC > liftCoeff) { maxFlaps = flaps; flaps = flaps + (minFlaps - flaps) * 0.5f; } else { minFlaps = flaps; flaps = flaps + (maxFlaps - flaps) * 0.5f; } } } while (Math.Abs(currLC - liftCoeff) > delta && Math.Abs(1 - liftCoeff) > delta && Math.Abs(-1 - liftCoeff) > delta && minFlaps < maxFlaps); return flaps; } /// /// Solves the wing for drag and lift /// /// /// /// 0.5f * AirDensity * speedSquared /// /// public void SolveWing(Vector3 shipSpeed, Matrix shipWorld, float A, out Vector3 drag, out Vector3 lift) { lift = Vector3.Zero; var speedDir = Vector3.Normalize(shipSpeed); var worldNormal = Vector3.TransformNormal(Normal, shipWorld); var worldBiNormal = Vector3.TransformNormal(BiNormal, shipWorld); var angleOfAttack = MathUtils.GetUnitVectorsAngle(worldNormal, speedDir) - MathHelper.PiOver2; var angleOfAttackScaled = angleOfAttack / MathHelper.PiOver2; var angleOfAttackABS = Math.Abs(angleOfAttackScaled); var dragCoeff = MinMaxDrag.X + angleOfAttackABS * (MinMaxDrag.Y - MinMaxDrag.X); var dragArea = MinMaxArea.X + angleOfAttackABS * (MinMaxArea.Y - MinMaxArea.X); drag = -A * dragCoeff * dragArea * speedDir; if (float.IsNaN(shipSpeed.Length())) return; if (BiNormal.Length() > 0.5f) { var liftCoeff = GetLiftCoefficient(-angleOfAttack, Flaps); var liftDir = Vector3.Normalize(Vector3.Cross(speedDir, worldBiNormal)); if (Math.Abs(1f - liftDir.Length()) < 0.1f) lift = A * liftCoeff * MinMaxArea.Y * liftDir; if (float.IsNaN(shipSpeed.Length())) return; } } /// /// Solves for flaps angle in order to get the desired angular acceleration /// /// linear speed /// ship matrix /// 0.5f * AirDensity * speedSquared /// desired angular acceleration /// public float SolveFlapsForAngularAccel(Vector3 shipSpeed, Matrix shipWorld, float A, float desiredAcceleration) { var torque = desiredAcceleration / _shipOneOverMomentOfInertia; var force = (torque / CenterOfGravity.Length()); var liftCoeff = force / (A * MinMaxArea.Y); var speedDir = Vector3.Normalize(shipSpeed); var worldNormal = Vector3.TransformNormal(Normal, shipWorld); var angleOfAttack = -MathUtils.GetUnitVectorsAngle(worldNormal, speedDir) - MathUtils.PI2; return GetFlaps(angleOfAttack, liftCoeff); } /// /// Solves for flaps angle in order to get the desired linear acceleration /// /// linear speed /// ship matrix /// 0.5f * AirDensity * speedSquared /// desired linear acceleration /// public float SolveFlapsForLinearAccel(Vector3 shipSpeed, Matrix shipWorld, float A, float desiredAcceleration) { var force = desiredAcceleration * _shipMass; var liftCoeff = force / (A * MinMaxArea.Y); var speedDir = Vector3.Normalize(shipSpeed); var worldNormal = Vector3.TransformNormal(Normal, shipWorld); var angleOfAttack = -MathUtils.GetUnitVectorsAngle(worldNormal, speedDir) - MathUtils.PI2; return GetFlaps(angleOfAttack, liftCoeff); }}
And here are 2 videos:

[media]
[/media]

[media]
[/media]
3 likes 17 comments

Comments

NineYearCycle
This is really impressive work!
October 24, 2011 08:20 PM
nife87
Really nice work, especially with the water and the scattering, although I have yet to come across a pilot like yours who can endure more or less constant 10-50 G's within 8 minutes :lol:
And thumbs up for your detailed description of both working and tried algorithms.
October 25, 2011 05:41 AM
Silviu Andrei
[quote name='nife87' timestamp='1319521280']
Really nice work, especially with the water and the scattering, although I have yet to come across a pilot like yours who can endure more or less constant 10-50 G's within 8 minutes :lol:
And thumbs up for your detailed description of both working and tried algorithms.
[/quote]


Thanks. Let's just say it's a ship from "the future" where they can fly through the atmosphere at 20,000 km / h and pilots can endure extreme G's [img]http://public.gamedev.net/public/style_emoticons/default/biggrin.gif[/img]
October 25, 2011 06:50 AM
frednaar
Very nice work, have you considered porting your code to Unity ? I think it would be very appreciated by the community there (as freeware or payware...)

regarding C# vs C++ I recommend this post
[url="http://www.codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx"]http://www.codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx[/url]

So stick with C# and please update this blog, it is very interesting...

Fred
December 19, 2011 05:57 PM
Silviu Andrei
[quote name='frednaar' timestamp='1324317423']
Very nice work, have you considered porting your code to Unity ? I think it would be very appreciated by the community there (as freeware or payware...)

regarding C# vs C++ I recommend this post
[url="http://www.codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx"]http://www.codeproje...ppVsDotNet.aspx[/url]

So stick with C# and please update this blog, it is very interesting...

Fred
[/quote]

Thanks, I am sorry to disappoint but I already decided for C++ & DirectX11 and am almost finished with the porting. I did a lot of improvements using compute shaders and other DirectX11 features like support for integer math and double precision floating point numbers. I did briefly investigate Unity a few months back when I was trying to decide on the new platform but I was disappointed to see that it also lacks DirectX11 support.

The link is very interesting, that guy really did his homework. C++ definetly has it's pro's and con's. Lately I started to slowly become friends with C++ and I don't miss the comfort of C# so much anymore and I'm really glad about that.


I will continue posting on this blog as soon as I finish porting the code. I just moved to the United States at the request of my employer and had a lot of other things on my mind. Now I'm all settled and I just resumed my work on this project. So, a new post is on the way [img]http://public.gamedev.net/public/style_emoticons/default/smile.gif[/img]
December 19, 2011 09:21 PM
NineYearCycle
So, any update on that C++ DX11 port you were doing?
The videos from this post already look great so it'd be cool to see more.
January 02, 2012 11:27 AM
Nicol
Perfect work, very very good!
[color=#333333][font=arial, sans-serif][size=4]There is the possibility[/size][/font][/color][color=#333333][font=arial, sans-serif][size=4] [/size][/font][/color][color=#333333][font=arial, sans-serif][size=4]of having a[/size][/font][/color][color=#333333][font=arial, sans-serif][size=4] [/size][/font][/color][color=#333333][font=arial, sans-serif][size=4]tutorail[/size][/font][/color][color=#333333][font=arial, sans-serif][size=4] [/size][/font][/color][color=#333333][font=arial, sans-serif][size=4]or the source code[/size][/font][/color][color=#333333][font=arial, sans-serif][size=4]? Thank you![/size][/font][/color]
January 03, 2012 01:29 PM
Sergey Kislitsyn
awesome work! keep it updated!
February 05, 2012 05:52 PM
Alex Filipovici
Very nice work! Succes în continuare!
February 21, 2012 02:20 PM
cameni
[quote]I get chills up my spine whenever I think of atmospheric scattering. I spent so much time tweaking, retweaking, dropping everything and starting from scratch that I had enough of it. First I implemented Nishita’smethod from his original paper, then moved to Oneil’s implementation and finally I dropped everything and settled to Bruneton’s method for multiple scattering which also took some code-porting, tweaking and bugfixing. I am pleased with this final method and I hope I will never have to touch that part of code again.[/quote]
This is so true .. I laughed when I read this because it quite describes what I went through as well. I think Ysaneya mentioned the same thing about the atmospheric code ...

Otherwise, great work ^.^
March 29, 2012 08:19 AM
Silviu Andrei
[quote name='cameni' timestamp='1333009141']
This is so true .. I laughed when I read this because it quite describes what I went through as well. I think Ysaneya mentioned the same thing about the atmospheric code ...
[/quote]

Yes, he did. When I read it, I almost felt like he was reading my mind. I just had to re-mention it :)
April 27, 2012 04:59 PM
NineYearCycle
Are you still working on this? It'd be interesting to see how it's going.
April 27, 2012 05:24 PM
Silviu Andrei
[quote name='NineYearCycle' timestamp='1335547463']
Are you still working on this? It'd be interesting to see how it's going.
[/quote]
Yes, I am, although not as much as I used to because my free time has greatly been culled lately. I would love to be able to promise that I will post something new next week but I just can't right now although that is my intention.
April 27, 2012 05:55 PM
Shredder2500
"I started to develop the planet renderer in C# using XNA. As I am frustrated by the fact that XNA is limited to DirectX9"

why dont you use MonoGame witch is a mono version of XNA using OpenGL to make it work crossplatform?
April 30, 2012 08:04 PM
Armak
Verry nice work, i was trying to figure out how to render a planet in such a way for my own game concept, but I got a few questions. Did you overlay/underlay a sphear of water just smaller than a sphear of land and adjust the peramiters of the land to rise and fall into the water? If so whats kind of affects can you do by creating a variable magnetic node on say something like a moon to pull hight in the water bit map naturaly, efectivly creating high tides and low tides, based on the location of the moon and a real time location of the planet?
May 01, 2012 10:11 PM
evandaley

This is really amazing! Great work!

I would be extremely interested in seeing any C# code related to Terrain LOD and Terrain Height-mapping.

September 02, 2015 04:06 AM
gaiastellar

hi Silviu,

this is very impressive. you should seriously consider porting this to unity - you could easily sell this on the asset store, and possibly to UE as well - something like this is seriously lacking, and very much sought after - i would pay for this myself- !!!

 

August 28, 2017 07:18 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement