For games that deal with large scales, double-precision (64-bit) float support is very nice to have.
Many large-scale games on custom engines use double, such as Minecraft, Star Citizen, Elite: Dangerous, Space Engineers, Grand Theft Auto, the Unigine game engine, and likely many many more.
Few game engines currently support double for legacy reasons back when CPUs weren't able to calculate double quickly, however, modern FPUs and SIMDs can handle double-precision without issues. Some can even use quadruple-precision at high speeds.
Some large scale games, like Kerbal Space Program, use double for math and convert back to float for local positioning of objects, because it uses the Unity game engine which uses single-precision floats. This isn't efficient or ideal, is difficult to program, and ends up being buggy in the end. It isn't too practical for multiplayer, either.
One way of doing this is by creating an abstract type real_t instead of float which can be typedef to either float or double. This is ideal because it would allow MonoGame to compile with either float or double support with minimal effort. This is how Godot is implementing it: https://github.com/godotengine/godot/issues/288
It may also be worthwhile to create structs like Vector3f and Vector3d which are always float or double respectively, so that creators of games can choose a fixed type to use with their scripting.
One way of doing this is by creating an abstract type real_t instead of float which can be typedef to either float or double
You can't do this in C#.
I do not like this idea. Games rarely need double precision. It's very annoying to maintain parallel implementations and there's no way to do it in C# that would not make some aspect of maintaining harder. Also, parallel API's may confuse beginners.
Are you developing a game that needs double precision or is this just a general suggestion?
Perhaps there are other ways in C# to accomplish the same thing as typedef? StackOverflow
I am currently developing a game that needs double precision, and I am most familiar with C#. Game engines like Unity use single precision, so that's not an easy option, though it does let me get a prototype working. I am considering switching to Godot, but I would prefer to have more than one option.
Its achievable by doing something like the code bellow in each class, but I don't think its such a good idea to do it this way.
Code:
```c#
using dd = System.Double;
using dd = System.Single;
```
Another idea, exclusively use double-precision floats. This would be easy to maintain and simple (once implemented - I'm aware that converting everything to double-precision may not be easy). Performance shouldn't be a big issue, either. On modern processors, the performance difference in actual games would definitely be a small amount, likely only a few microseconds per frame.
That would be a huge breaking change for all users though.
@aaronfranke there's nothing preventing you to create your game logic using double maths.
Only when rendering with monogame, you would need to convert your absolute space locations to "relative to camera" locations, which would happen to be floats, not doubles.
This is what games like Elite Dangerous and other large scale games actually do: Internally they work with large types, and at rendering time, they resolve to floats, since floats it's the only type GPU understand.
Actually, games like Elite Dangerous most probably don't use doubles, but large integers like BigNumber or some kind of custom fixed point arithmetic type.
Godot is not implementing it, they're discussing it, and reaching the same conclussions than us. I understand you want an off-the-shelf solution for your game idea, unfortunately, universe-like games are not very common and usually require in-house solutions. I have the same kind of game projects in mind, and I would never expect a game engine to provide an off-the-shelf solution for this kind of games.
I actually did the maths a while ago... even with using doubles, if 1 unit = 1 metre, and given the center of the solar system is located at the center of the Sun, when you're around the orbit of Neptune, even with double precission, the loss of precission would be large enough to begin affecting the precission of the physics engine. I remember playing Elite Frontier (which used Doubles) and having a hard time trying to dock into a space station located in a far away planet, just because the movement of the ship was noticeably jerky due to the loss of precission. Outer Space is that big.
The only good solution for universe-scale games is to have a fixed point precission type, that has the same precission at any point in space.
Another idea, exclusively use double-precision floats.
That's fine for your own code. You can use double-precision as much as you like. If it is a requirement of your application, go ahead. But you must realise that it cannot be exclusively double-precision across the entire application. It doesn't work for graphics APIs which are exclusively single-precision, so anything that you do which is required to be passed to any graphics-related API would need to be converted to single-precision.
Another other aspect is storage. You instantly double the storage required, which is still a factor when it comes to large amounts of data. This affects storage in files (on disk or transmitted over a network) and in the amount of data that needs to be copied around in memory.
The final consideration (that I can immediately think of, there may be others) is operating with third-party APIs. Unless you're going to be using no other code, the libraries you are using will almost certainly be using single-precision, requiring you to convert to single-precision and back again.
There is no equivalent in C# to a global typedef as there is in C++ since C# has no preprocessor pass.
Godot is a game engine that is again single-precision in all APIs that are graphics API-related. There is no relationship between requiring to use double-precision and switching to Godot.
There is no equivalent in C# to a global typedef as there is in C++ since C# has no preprocessor pass.
@KonajuGames look at my comment above, we could do it if we wanted to.
look at my comment above, we could do it if we wanted to.
I did see that. That's not going to happen.
I don't like it either, I'm just correcting here :P
I should have responded in a typical Aussie way. Yeah, nah.
lol XD
@aaronfranke don't move the camera, instead move the world around the camera. Camera is fixed at 0,0,0 location. Single-precision float is plenty for rendering.
As vpenades said, you can use double for all your coordinates, you just need to convert positions of game objects from (double precision) world space to (single-precision) camera space before rendering.
Doing math with doubles and moving stuff relative to a camera at the origin can work, not as easily, but then my math functions need to be able to use double precision. This would require me to make my own Vector3 etc for my game. At the very least, make things like Vector3d etc which use double. It would make sense to add this to MonoGame rather than requiring each developer to re-implement.
@aaronfranke What types would you expect to have a double precision variant exactly? Just the math types?
Primarily, yes. All Vectors for sure, maybe quaternion, plane, ray, math helper. More I'm not thinking of.
As MathHelper isn't an object, we could just add methods with different names that return double, and it looks like many methods here actually use double internally, so we could even just call the double method + a cast to float from within each float-returning method, to simplify code.
@aaronfranke If what you want is to really simulate a Solar System scale space, you would need to define object locations like this:
public struct BigVector3
{
const float SCALE = 0.0001; // this controls the fixed point precission.
public System.Numerics.BigInteger RawX;
public System.Numerics.BigInteger RawY;
public System.Numerics.BigInteger RawZ;
public System.Numerics.Vector3 GetPositionRelativeTo(BigSpaceTransform other)
{
var x = (long)(this.RawX - other.RawX);
var y = (long)(this.RawY - other.RawY);
var z = (long)(this.RawZ - other.RawZ);
return new System.Numerics.Vector3( (float)x * SCALE,(float)y * SCALE,(float)z * SCALE);
}
}
As you can see, I defined my custom fixed floating point, safe, positioning system, and I didn't need to use doubles anywhere. for what you want to do, the right type to use is BigInteger, not Double.
Considering that most major large-scale games do use double, I'd have to disagree that this is the one "right" approach. Double is a lot more comparable to float.
@aaronfranke from where do you get that most large scale games use double? you state that games like Elite and others use doubles, are you sure?
I've been playing large scale games for a long time, and at least TWICE I had issues:
In Elite Frontier, when I tried to dock a large ship in a space station located in a far away planet, I always ended crashing, because even if it was already using doubles, the loss of precission was large enough to cause a jerky movement of the ship, leading to collision detection problems. I'm sure in Elite Dangerous they have corrected the problem by not using doubles and something larger.
In "Independence War", second mission, you had to dock with a long range relay comm satellite, Again, same problem, you ended crashing against the satellite due to precission issues.
And that's talking about 90's games, I'm sure they've moved past doubles a long time ago.
@aaronfranke any source to backup that claim? PUBG for example is using origin-shifting rather than opting for changing source to doubles:
“We had to write in our own origin shifting that basically shifts the whole world around the player, every 600 or so metres,” Greene says. “It’s dynamic, and we still have to improve it quite a bit, but we’ve got it down to about 100 milliseconds for the actual shift to happen. It’s all fixed, and whatever distance you are from the origin it’s always very smooth.”
source: https://www.pcgamesn.com/crossout/crossout-guide-combat-weapons-items
You can copy MG's Vector3 source code and replace all floats with doubles to create your own Vector3d type. The only custom functions that you would need to write are helper functions for conversion between these two types. I think MonoGame.Extended would be a better place for this.
I've been making large commercial games for many years and have never used
doubles in any reasonable amount, mostly for all the reasons I outlined
above. Origin shifting is quite common and is something I have used before.
No Man's Sky has a local origin for every planet, and possibly even more
granular than that. There's a GDC talk or two on it on YouTube. I remember
first reading about it in an article about Dungeon Keeper from the mid 90's.
Galaxia uses 64bit Integer with a fixed scaling of 10000
A Matter of Precission proposes fixed point arithmetics.
Godot also proposes origin shifting for large scale worlds
Kerbal's lead developer explaining what they used (they use double precission maths, but only in very specific places, not engine wide)
And that's for a 5 minutes google search...
Star Citizen: https://www.youtube.com/watch?v=OB_AI9ukSp8
Space Engineers: http://blog.marekrosa.org/2014/12/space-engineers-super-large-worlds_17.html
What Minecraft was like before double-precision: https://minecraft.gamepedia.com/Far_Lands
@vpenades The Godot issue you linked https://github.com/godotengine/godot/issues/6737 was closed in favor of double-precision https://github.com/godotengine/godot/issues/288
Okey, I didn't pretend to start a flame war about how many games are using doubles or not.
The whole point of what we've been trying to explain is that there's more than one way of doing what you want to achieve, and we've provided you with acceptable solutions on how to do it, without requiring the complete redesign of a game engine.
Why not do like sfml
Vector2i (int)
Vector2f (float)
so then you can create something like that:
Vector2d (double)
Yeah, parallel implementations is probably the best way to do it in C#. I'm still gonna vote no on this feature because
Just playing my small part in this, from:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb509646(v=vs.85).aspx
double - 64-bit floating point value. You cannot use double precision values as inputs and outputs for a stream. To pass double precision values between shaders, declare each double as a pair of uint data types. Then, use the asdouble function to pack each double into the pair of uints and the asuint function to unpack the pair of uints back into the double.
Is double often used in shaders ?
As a Star Point Gemini 2 player, which is a large scale game too, and having access to the hlsl files (they are available in the game's data folders), I did not see a single shader using something needing more precision than a float.
@MarcVador I've heard that you can use double with Vulkan, and OpenGL >= 4.0. If Vulkan support is added then double would be useful for rendering and global coordinates. But may still need to be cast down to float for rendering on older OpenGL versions. I have no idea of DirectX's support for double.
@aaronfranke, I guess what you're looking for is to have a pipeline that is able to render both small objects and entire planets in the same rendering context. For that, you not only needed double precision at shader level, but also you need it at depth buffer level, which I think is not supported by most hardware devices.
Games with real sized planetary simulations usually do the rendering in layers at different scales.
For example:
Having multiple layers at their optimal scale allows efficient use of single precission depth buffer and also allows keeping cascade shadow mapping at manageable levels, you could have satellites casting eclipse shadows over planets, and local, good quality shadows inside the cockpit
Closing this because:
Most helpful comment
I should have responded in a typical Aussie way. Yeah, nah.