Expose transform controls (rotation & scale) for video-generated Tilesets in Cesium for Unity
Feature
I’m working with Tilesets generated from video and I find that, in the current Cesium for Unity API, there is no way to apply a custom rotation or non-uniform scale to the loaded tileset model itself. This limits my ability to align or adjust the model in the scene, especially when the source imagery is not perfectly aligned to axis, or when I want to locally tweak its orientation/size.
In the Cesium3DTileset documentation it is recommended that “the Transform of the GameObject that contains this component, and its ancestors, should be an identity transform: 0 position, 0 rotation, 1 scale” to avoid misalignment with other globe tilesets. However, this also means users are discouraged (or prevented) from applying arbitrary transforms to the tileset.
I’d also mention that applying a local transform change to a tileset fundamentally breaks the display of the tileset — tiles begin to misalign, disappear, or fail to load properly. This makes it impossible to use any Unity-native transform tools to rotate or scale the model, even for small local corrections.
To me, it would be very useful if the API allowed a controlled override or extension point to apply a model transform matrix (rotation, scale, translation) on top of the georeference mapping, or optionally switch the tileset into a “local space” mode where it can be manipulated like a normal 3D model.
Hi @galdalali, I'm surprised to hear that setting the Transform of a GameObject with a Cesium3DTileset isn't working for you. I just tried it myself, and it appears to work correctly. I'm able to translate and rotate tilesets. The already-loaded tiles are repositioned accordingly, and newly-loaded tiles appear in the correct place. Is there a chance you're using an older version of Cesium for Unity? Can you walk us step-by-step through what you're doing, and what you're seeing?
I just noticed that you mentioned non-uniform scales. I can confirm that non-uniform scales disrupt tile selection. Translation, rotation, and uniform scale work well, though.
Thanks for sending me a video of the problem. From your video, it looks like you're doing something (a custom material?) to clip the tileset to a rectangle. Do you have the same problem if you turn that off?
I can confirm that it happens regardless of the material. The actual transforms themselves appear to be moving on the Y axis. Our setup is a top level anchor that we move with the map, and below it are the loaded cesium tilesets. A couple of our tilesets where built from incorrect scans, so we wish to adjust them (For example, one of them has a completely incorrect scale, and is 100 times smaller). However, performing any of these adjustments breaks the scene, leading to the tilesets performing all sorts of unusual movements as the main anchor shifts.
Ok, I see the problem. I was testing in Editor mode, which works fine. But in Play mode, the CesiumOriginShift component causes problems. If you don't need to shift the origin for your application, you can disable it. Click the DynamicCamera and either delete the CesiumOriginShift component, or disable it by unticking the box. That should avoid the problem you're seeing.
Ok, so the purpose of the CesiumOriginShift is to keep the Unity world origin near the camera as the camera moves. This helps maintain coordinate precision, because Unity only supports single-precision coordinates. It does this by setting the camera's (new) position to (0,0,0), and then updating the position of all other objects so that they're at the same location as before relative to the updated camera position. That way the camera stays at the center of the world, but nothing appears to move.
But actually it doesn't move all objects. It only moves objects with a CesiumGlobeAnchor component. Any object that does not have a globe anchor component will stay at the same Unity world coordinates after an origin shift, which means it will appear to move. Or, put another way, it will appear to stay in the same place relative to the camera even as the camera moves.
Here's what that looks like with a simple cube without a globe anchor:
This is similar to what is happening with your tileset. When the camera moves, the CesiumOriginShift component moves the origin, which changes the Unity world coordinate system, and then the tileset's transformation is applied in the new frame. It's a little bit different in the Tileset case, though, because tilesets are globe aware and are update when the origin shifts. The Transform on the object itself, though, doesn't change, and is applied after the globe transform.
The problem is described in more detailed and technical terms in this issue: #132
While that looks similar to the issue we're having, we don't have any CesiumOriginShift components in our scene. Because of the way we handle our netcode, along with a few other limitations, we control tileset visibility and active state manually. As such, the only Cesium components in our scene are:
- a 'CesiumGeoreference' script attached to a master pin. This pin is always in the 'center' of our overlay map and is the logical center of the scene. When the map moves, it updates the CesiumGeoreference so that its position matches the maps position.
- A CesiumCameraManager to allow for some fine tooth control over a few visual effects we do with multi-cam rendering.
- A custom CesiumTileExcluder that we use to prune for these visual effects.
- several Cesium3DTileset gameobjects.
The only thing I can think that could even be tangentially related would be that the game object the CesiumGeoreference is on (our master pin) has some rotation applied to it so that its north matches our maps north. In addition, if we change the scaling of a tileset, it loses its 'anchor' in space, and begins to move as if the world is 'smaller' or 'larger' than it actually is. This means that the internal calculations for determining the transform matrix of a tileset appear to be using the worldSpace position of the masterpin, rather than its local position. Rotating the parent of the pin causes the same issues, which shouldn't happen if the origin is operating in local space.
When the map moves, it updates the CesiumGeoreference so that its position matches the maps position.****
This is exactly what the CesiumOriginShift does. So while you don't have that particular component, you have custom code that does the same thing.
I see, that seems to make sense, yeah. Im testing it out now by making a CesiumGlobeAnchor on some random test object. When I adjust its parents transform, I get the same behavior as the tilesets. Im unsure of how to proceed at this point. If the tileset object itself was the CesiumGlobalAnchor's reference point, I could solve this by editing the anchor directly. However every tile has its own anchor, thus I would have to edit each tile when it loads to give it the offset. Is there another way to deal with this issue?
In your own custom application, the easiest approach is probably to update the tileset's Transform whenever you change the CesiumGeorefence origin. The math isn't trivial, I'd have to think about it for a bit. But it's basically a change of basis, as described in #132.