TilesRenderer initialization appears synchronous but scene is not ready immediately
TilesRenderer initialization appears synchronous but scene is not ready immediately, causing transformLatLonHeightToOrigin to fail
Hi team,
When initializing the TilesRenderer and immediately calling reorientationPlugin.transformLatLonHeightToOrigin(lat, lon, height), the renderer does not properly render the scene. This happens because if no tiles are loaded yet, there is no event or Promise that signals readiness.
Versions:
"3d-tiles-renderer": "^0.4.14",
"react": "^18.2.0",
"three": "^0.176.0",
Steps to Reproduce:
- Call initTilesRendererManager() to create a TilesRenderer instance
- Register all necessary plugins, including ReorientationPlugin.
- Immediately call transformLatLonHeightToOrigin(...).
Problem:
The renderer does not render correctly if called immediately. Adding an arbitrary 2-second delay works as a workaround, but it is not async/await-friendly.
Example Initialization:
export async function initTilesRendererManager(): Promise<TilesRenderer> {
if (tilesRenderer) return tilesRenderer;
const apiKey = process.env.GOOGLE_MAPS_3D_TILES_API_KEY;
const rootURL = process.env.GOOGLE_MAPS_3D_TILES_URL;
tilesRenderer = new TilesRenderer(rootURL);
tilesRenderer.registerPlugin(new GoogleCloudAuthPlugin({ apiToken: apiKey }));
tilesRenderer.registerPlugin(new TileCompressionPlugin());
tilesRenderer.registerPlugin(new TilesFadePlugin());
const loadRegionPlugin = new LoadRegionPlugin();
tilesRenderer.registerPlugin(loadRegionPlugin);
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("/draco/");
tilesRenderer.registerPlugin(new GLTFExtensionsPlugin({ dracoLoader }));
reorientationPlugin = new ReorientationPlugin();
tilesRenderer.registerPlugin(reorientationPlugin);
return tilesRenderer;
}
Current workaround:
const tilesRenderer = await initTilesRendererManager();
tilesRenderer.setCamera(camera);
tilesRenderer.setResolutionFromRenderer(camera, renderer);
// Temporary wait to ensure the renderer is ready
await new Promise(resolve => setTimeout(resolve, 2000));
reorientationPlugin.transformLatLonHeightToOrigin(latRad, lonRad, height);
Impact:
- Cannot use a clean async/await flow.
- Forces using an arbitrary delay.
Suggested Improvement:
- Add a “renderer ready” Promise or event that resolves when it is safe to call transformLatLonHeightToOrigin etc.
- Document the proper way to call transformLatLonHeightToOrigin immediately after initialization.
Maybe you need "load-tile-set" event?
tilesRenderer.addEventListener( 'load-tile-set', () => {
reorientationPlugin.transformLatLonHeightToOrigin(latRad, lonRad, height);
});
Maybe you need "load-tile-set" event?
tilesRenderer.addEventListener( 'load-tile-set', () => { reorientationPlugin.transformLatLonHeightToOrigin(latRad, lonRad, height); });
Thanks for the suggestion!
You're right, attaching the reorientation logic directly to the load-tile-set event is a great and direct way to solve this timing issue with the transformation.
The main reason I've chosen for wrapping the event in a promise within a larger async initialize() function is for overall application state management. In my current use case, UI needs a definitive point in time to transition from a global "loading" state to a "ready" state (e.g., to hide a loading spinner and enable user controls).
The async/await approach would give me a clear, awaitable signal for when the renderer is fully operational.
Your suggestion is spot on for solving the direct problem, though. Thanks again for the feedback!
Thanks for the report - the "load-tile-set" event is likely the kind of callback you're looking for (it's whats used in the docs for this kind of "ready" state) though bear in mind that it is called every time a tile set json file is loaded, including hierarchy subtrees. It's possible that a "ready" or "root tile set loaded" event would be better.
One other possible solution is to adjust the ReorientationPlugin so that it keeps the new settings that a user wants to adjust the globe to so it automatically reorients the tile set when the tile set has loaded. It currently only respects the arguments passed into the construct or set as instance members. Then you wouldn't need to register a callback for this. Something like so:
transformLatLonHeightToOrigin( lat, lon, height = 0, azimuth = 0, elevation = 0, roll = 0 ) {
this.lat = lat;
this.lon = lon;
this.height = height;
this.azimuth = azimuth;
this.elevation = elevation;
this.roll = roll;
// ...
}
Feel free to submit a PR for either!
Thanks a lot for the reply and ideas, Mr. Johnson!
It's possible that a "ready" or "root tile set loaded" event would be better.
I think updating the ReorientationPlugin is a good approach, but a dedicated "root tileset loaded" -event could be more broadly useful across the library.
I’ll look into this further and prepare a PR.