Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gltfpack: Double precision support for node translation values #583

Open
sweco-sekrsv opened this issue Jul 18, 2023 · 14 comments
Open

gltfpack: Double precision support for node translation values #583

sweco-sekrsv opened this issue Jul 18, 2023 · 14 comments

Comments

@sweco-sekrsv
Copy link

Hi!

I noticed that the translation values of my matrix lacks the original precision after gltfpack have been applied.
I'm using these options when compressing the asset (gltfpack 0.19): -noq -cc -kn

My original matrix is:

            "matrix": [
                1,
                0,
                0,
                0,
                0,
                0,
                -1,
                0,
                0,
                1,
                0,
                0,
                3133805.225747891,
                5447439.535584584,
                -992857.7233499398,
                1
            ]

The matrix after gltfpack:

      "matrix": [
        1,
        0,
        0,
        0,
        0,
        0,
        -1,
        0,
        0,
        1,
        0,
        0,
        3133805.25,
        5447439.5,
        -992857.75,
        1
      ]

Is this possibly a bug or can I solve this somehow with the settings? The asset that are being compressed is to be used as an asset in 3D Tiles and the translation values in the matrix is important to move the asset to the correct real world location.

I attach a example.
matrix_precision.zip

@zeux
Copy link
Owner

zeux commented Jul 19, 2023

Can you observe these differences during rendering?

gltfpack and cgltf, the library it uses, use single precision floating point numbers to represent everything internally. The values here require double precision to fully roundtrip.

The reason I ask if this affects rendering is that typically renderers would also end up truncating translations to single precision at some point during loading or transformation although it’s certainly possible to build a renderer that doesn’t.

@sweco-sekrsv
Copy link
Author

Yes, I think there will be problems when rendering.

In the 1.1 version of the 3d tiles specification
https://docs.ogc.org/cs/22-025r4/22-025r4.html

"The RTC_CENTER can be added to the translation component of the root node of the glTF asset."

So all viewers that intend to support loading of 3D tiles must support this way of offsetting to original coordinates.

I have only tested this in CesiumJS but it works fine there. No jittering which you would expect with large coordinates like these.

When producing a 3d tile, a common way is to translate the asset from the original world coordinates to origo. In the earlier versions of 3d tiles you would then store the offset to go back to the real world coordinates in CESIUM_RTC or RTC_CENTER. This have now been replaced with using the translation component of the root node instead.

So imagine having multiple tiles and they all have their own offset. Without the full precision there will be gaps between the tiles when they have been offsetted back to original coordinates.

@zeux zeux added the gltfpack label Jul 20, 2023
@zeux
Copy link
Owner

zeux commented Jul 20, 2023

It's a little odd that RTC_CENTER is specified as float32[3] as that implies that 32-bit floating point precision should be sufficient. I'm also curious if the tiles need to be quantized to a specific grid where the offsets can be represented without round-off error, or if the intent is really for the root node to have what looks like nanometer-level precision...

At any rate, I'd be interested in a full end-to-end example where multiple tiles get combined to see if this causes gaps or precision issues in final render in the viewer. It's not clear to me if the problem is limited to root node translation or if there are other components that would need higher precision during processing; changing this in cgltf is possible but it's not obvious how much to change, as changing all floats to doubles is probably not a great idea.

Also noting that you're using -noq - is there a reason for it?

@zeux zeux added enhancement and removed bug labels Jul 20, 2023
@zeux zeux changed the title Translation values of the matrix lacks the original precision gltfpack: Double precision support for node translation values Jul 20, 2023
@sweco-sekrsv
Copy link
Author

When testing I had -noq. My idea is that quantized files also would create small gaps between the tiles. But as you say, "quantized to a specific grid if it's possible" might fix it. I think someone from the 3D tiles team can/need to clarify that.

I have attached a small example using four tiles that is loaded into CesiumJS. Start a local webserver and compare the two examples. One example is showing the original tiles (without gaps) and the other example the glb-files compressed with gltfpack. As you can see there are gaps between the tiles after compressing.

See this image (I have used options -cc -kn for the compressed tiles)
gltfpack_gaps_between_tiles

precision_bug.zip

@richard
Copy link

richard commented Aug 10, 2023

@sweco-sekrsv If you are in control of the per-tile origin, you can make it float32-friendly early in your process using something like this. (Of course, the positions in your glTF should be relative to this new origin instead.)

origin = round(origin / 4.0) * 4.0;

I've taken this approach to accommodate tools in the ecosystem beyond my control. Even if the glTF specification gave clear guidance re: precision, you'll still find a mix of 32 and 64-bit matrix types in various glTF libraries/tools.

I don't mean to say what meshoptimizer should do here re: precision, just that I found it best to build data that works in as many tools as possible. Hope this helps.

@sweco-sekrsv
Copy link
Author

As @richard mentioned this can be made float32-friendly before running gltfpack. Thank you for you suggestion, it works fine!
Using flags: -cc -kn -noq, it looks good now.

However I can still see tiny gaps when using quantized files (removing the -noq option). Even if I choose -vp 16

I have attached a new example that shows this.

quant_gap.zip

@sweco-sekrsv
Copy link
Author

sweco-sekrsv commented Aug 17, 2023

Using -vpf seems to fix the issue so I still can benefit from quantization. So my flags for the working dataset is:
-cc -kn -vpf -vp 16

@FreakTheMighty
Copy link

@zeux this is something I've just bumped into. I have a file that is "centered" via a root node that is very far from the origin, for example -75235304.000 units on the X axis. Children are then positioned at 75235304.000 units on the X resulting in them being rendered very close to the origin.

The babylonjs viewer displays the original file correctly after this issue was resolved by using double precision for the matrices. However, after I process this file through gltfpack, element positions are noisy and result in the model appearing broken. I'm not sure there's anyway around needing the transforms to handled internally with double precision.

Thoughts?

@zeux
Copy link
Owner

zeux commented Oct 2, 2023

Does this happen when -kn is used as well? 75235304.000 should roundtrip exactly as a 32-bit float. Although maybe you mean that the children have offsets that are close to 75235304.000 but aren't exactly equal to this, in which case yeah you'd see the precision issues due to node deserialization. In either case, an example scene would be appreciated.

I haven't had time to look into the aforementioned quantization precision issues, but in general the first step here would be to somehow add double-precision support to transforms to https://github.com/jkuhlmann/cgltf without forcing the entire library to use double-precision.

@FreakTheMighty
Copy link

Thanks for the quick response @zeux . It does happen with -kn enabled. You're correct, the child nodes are close to that very large value, but they are all in slightly different location. I've attached a very simple example that I believe replicates the issue.

If you upload this gltf to the babylon viewer you should see two cubes perfectly aligned to one another. After processing them with gltfpack the cubes overlap.

floating-point-issue.gltf.zip

@zeux
Copy link
Owner

zeux commented Oct 2, 2023

Ok - yeah, the attached scene requires full double precision to support. I filed jkuhlmann/cgltf#228 for now. Short of the previously discussed workarounds in this thread where the objects are stored relative to a cleanly roundtrippable origin (e.g. if you want to keep objects around 7M meters away from the origin, parent them to a node that has a translation that is ~7M but representable as a 32-bit floating point number), the only route to support scenes like this without transform precision loss is to use double precision.

@FreakTheMighty
Copy link

@zeux thank you for creating that ticket.

I'm not sure we can make both the root and children all clean round-trippable floating point numbers. Aside from getting that fix into cgltf, I think our next best option would be to collapse the transforms ourselves before we get to gltfpack.

@FreakTheMighty
Copy link

@zeux my colleague made a pass at adding double support to cgltf jkuhlmann/cgltf#229, would be curious what you think next steps should be. We'd be happy to contribute a change to meshoptimizer, but I think we were a little less clear on the scope.

@matthargett
Copy link

I think I ran into the same problem when trying to optimize avatars from the 100avatars.com website, there were gaps and aliasing in some of the models after optimizing their meshes. I see there was an alternative proposed in jkuhlmann/cgltf#239 , but it’s not clear to me that a PR was ever merged based on that suggestion. Is that upstream package fix still a possibility?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants