jMonkeyEngine / jmonkeyengine

A complete 3-D game development suite written in Java.
http://jmonkeyengine.org
BSD 3-Clause "New" or "Revised" License
3.84k stars 1.13k forks source link

[Suggestion] 64-bit Vectors and Matrices for rendering and positioning #1384

Open CammiePone opened 4 years ago

CammiePone commented 4 years ago

As the title says, I think it would be great if we could optionally use vectors and matrices that use double instead of float for rendering, and vectors for positioning in the world, as it would allow for games with larger worlds if someone really needed all that space, while not sacrificing on precision. I'm not sure how difficult it would be to implement that, but it would be a nice to have thing, since a lot of game engines lack it.

stephengold commented 4 years ago

Thanks for the suggestion. I thought double-precision math was available as an add-on library for JME. @pspeed42 can you comment?

pspeed42 commented 4 years ago

Yes, sim-math is double precision Vec3d, Quatd, etc..

The problem with JME supporting it is that OpenGL is 32 bit floating point at a pretty fundamental level and that's plenty of precision for rendering. If JME was all double based then that would mean a 64 bit to 32 bit conversion for every vertex, draw call, transform, etc. all the time. A pretty HUGE cost overall.

Also, there are a 100 other problems with "big worlds that need double" that are not solved by just shoving all of your game objects into the scene graph without care. Once you work out the 100 other issues (paging, localized space, zones, etc.) then the floating point issue isn't an issue anymore.

If the basic problem is that you are treating Spatials as game objects then you are already on the failing path anyway.

CammiePone commented 4 years ago

I've not heard of this library, but it's good to know it exists, and the performance impact is something I'm aware of, hence why I said having it be optional instead of mandatory, so it's not a problem for other games that would rather performance over larger maps.

As for the other stuff mentioned, I'm well aware of how difficult it can be to get it to work, and all the other problems that occur when trying to do what I suggested, but if I were to create a world 20,000,000 units from the origin, short of making the world move around the camera (which is problematic for multi-player), there's no way to make it without huge amounts of stuttering, something moving to doubles solves, my examples being Minecraft Bedrock Edition and Minecraft Java Edition.

Bedrock edition uses floats for everything, as most game engines do, and around 250,000 units out, you start to encounter a noticeable stutter.

Java Edition on the other hand uses doubles, and has no problems going about 30,000,000 units out from the origin point, without needing to rely on anything too hacky, as far as I've seen anyways. They're not impossible to solve problems, as they've been solved already.

pspeed42 commented 4 years ago

Multiplayer very much REQUIRES that you move the world around the player. Game units are in double, networking is in zones, rendering is in float and relative to the player's position. This is so common.

That's why SimEthereal also uses double. All of your game logic should be in double if possible. Find a physics engine that uses double, etc.. But rendering necessarily needs to be in float because "everything is float" and there is no reason that you need to render the stuff around the camera in double precision.

Edit: at some point that minecraft code is converting back to float... probably relative to the camera position. In jMonkeyEngine, this is done when you position your spatials (views) of your game objects.

CammiePone commented 4 years ago

Huh, alright. Didn't know that. Every game I've made or modded always made it appear like the camera moved around the world for rendering purposes.

pspeed42 commented 4 years ago

For any world large enough to have to page in additional data, it's super common for rendering to happen around the player. LOD, clipping, etc. are all 100x easier.

Game data will certainly be in proper world coordinates, though.

CammiePone commented 4 years ago

Ahh, okay, that makes sense

riccardobl commented 4 years ago

OpenGL 4 supports double floating point precision. To implement this in a backward compatible manner we would need to make Vector3f extend a parent class Vector3 that defines all the abstract methods used by the core in addition to their double version.

The double methods can be then redirected to their float counterpart in Vector3f and implemented in Vector3df (double float). eg

In the same manner float methods should be implemented in Vector3df to cast the double values eg

In this way the renderer can still use Vector3df as if it was a Vector3f until (if) we decide we should support opengl4 double precision.

In addition to this we should deprecate the public fields x,y,z to discourage their use in favor of get/setX get/setY get/setZ

pspeed42 commented 4 years ago

In that case, it would be more efficient overall just to s/float/double in the whole engine and do the double->float thunking in the renderer. At least that conversion would only be done once per frame or so (per object) and avoids all of the extra virtual method overhead, duplication of code, etc..

It's just not particularly worth it. From someone who does ALL of their game objects+logic 100% in double and never has a problem with float at the engine level, it really is fine the way it is.

riccardobl commented 4 years ago

No, i disagree. The castings are there to provides a conversion path for existing code, in a pure double workflow they would be performed only by the renderer or not even there if we were to implement double precision. On the other hand replacing float with double on the whole engine would break all existing code.

pspeed42 commented 4 years ago

Your proposal breaks existing code in more subtle ways by making everything a little slower and potentially introducing some hidden issues. And for what real benefit? At best it's a partial patch for a mostly non-problem.

The conversions from float->double and double->float can not be waved around lightly and can cause subtle problems on their own... we'd have to be SUPER careful about this and even then users would probably have to be aware of the internals to some extent.

To properly support double without issue, the engine would need to be built from the ground up using double and then support 'float' as compatibility. (Though note that in pretty much all cases except nio Buffers and float[] arrays, for the user float -> double is automatic at the API level.)