In what sense is RTC_CENTER single-precision, and does cesium viewer handle it correctly?

Hi all, I have some questions about the RTC_CENTER property
that may be optionally included in .b3dm files and similar.

My understanding of RTC_CENTER

If I understand correctly, RTC_CENTER is supposed to be a high-precision
model-space-to-worldspace transform (aka modeling matrix), that the viewer
program should multiply by a high-precision worldspace-to-eyespace
transform (aka view matrix) in high precision on the CPU,
so that matching large translations can cancel out in high precision
and the product modelspace-to-eyespace transform (aka modelview matrix)
can be safely rounded to single precision and used on the GPU.

Given that, I expect RTC_CENTER to be expressed and used
as double-precision floating point numbers (aka doubles, aka float64’s).
The tileset expresses it as simply numbers in JSON
(in the feature table in the .b3dm file or similar), so no problem there.

Conflicting indicators in the docs

However, there seem to be conflicting indicators in the 3D Tiles documentation.

For example, the following docs say that RTC_CENTER is of type float32[3], which is surprising to me:

But, in this other place in the same spec, it says it’s
a number[3] or GlobalPropertyCartesian3 object, instead,
i.e. effectively float64[3], which would make more sense to me:

So, which is it?

If it’s indeed float32[3], what does that mean?
Does it mean the viewer program is supposed to
read the numbers from the JSON and round them to the nearest float32's?
Taking this example from the pointcloud part of the spec:

  var featureTableJSON = {
      ...
      RTC_CENTER : [1215013.8, -4736316.7, 4081608.4],
      ...
  };

When rounded to nearest float32s, those numbers are exactly:

  1215013.75, -4736316.5, 4081608.5

(if I did the math right).
So such rounding would move this point by more than 0.2 meters, so whether it’s
done or not certainly matters.

Conflicting indicators in the cesium source code

To try to see what the cesiumjs viewer does,
I searched for “RTC_CENTER” in the cesium source code,
and I found 4 occurrences. In at least one of those places,
(that is, Source/Scene/Geometry3DTileContent.js)
it looks to me like it’s using the values directly out of the JSON
without rounding to float32s, and putting them directly into the modeling
matrix in high precision:

    var center;
    if (defined(featureTableJson.RTC_CENTER)) {
      center = Cartesian3.unpack(featureTableJson.RTC_CENTER);
      Matrix4.multiplyByPoint(modelMatrix, center, center);
    }
    ... (and then doing more stuff with center down below) ...

But in the other 3 places in the cesium source code, it looks like it’s
converting to float32s. For example, in Source/Scene/Batched3DModel3DTileContent.js, there is this:

  var rtcCenter = featureTable.getGlobalProperty(
    "RTC_CENTER",
    ComponentDatatype.FLOAT,
    3
  );
  if (defined(rtcCenter)) {
    content._rtcCenterTransform = Matrix4.fromTranslation(
      Cartesian3.fromArray(rtcCenter)
    );
  }

(Note that ComponentDatatype.FLOAT means float32.)

Safe strategy for tileset creator?

As a tileset creator, I’m taking a strategy that seems safe,
regardless of what various consuming code might do with it:

  1. I always choose an RTC_CENTER that is exactly representable in float32, and
  2. When expressing that RTC_CENTER in JSON, I always print it
    with enough digits so it will come out exactly for all consumers,
    regardless of whether they round to float or not.
    (That is: print the numbers exactly, or at least enough digits to
    unambiguously represent double precision, i.e. %.17g;
    %.9g does not suffice, although it would suffice
    if I knew all consumers rounded and stored the values as float32).
    E.g. I would have to rewrite the example above as:
     RTC_CENTER : [1215013.75, -4736316.5, 4081608.5],
    
    if that’s the value I chose.

Summary of questions

So my questions are:

  1. Is my characterization of the situation correct?
  2. Do any of the references to which I’ve linked above point out bugs
    in the spec or viewer programs, that need to be fixed?
  3. Is my “safe” strategy as a tileset creator a good one?

@donhatch

Thank you for your detailed community forum post! I read through your questions and unfortunately, I do not have the expertise on this topic to provide a robust anwser. I shared this thread with our CesiumJS squad - they should get back to you shortly.

In the meantime, community input would be particularly welcomed. Let me know if you have any additional questions or concerns. If you have any updates regarding your questions, please share them as well.

-Sam