You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ImageOverlayPlugin: errors and black tiles when an overlay is swapped while tiles load #1769
When I swap an overlay while tiles load, ImageOverlayPlugin throws three errors.
I swap it like this: await next.init(), then deleteOverlay( old ), then addOverlay( next ).
Cannot read properties of undefined (reading 'signal'): unhandled promise rejection.
Cannot read properties of null (reading 'updateMatrixWorld'): unhandled promise rejection.
Cannot read properties of undefined (reading 'color'): thrown by renderer.render(), on every frame.
What you see: some tiles never get the new overlay, and the render stops at those tiles.
So part of the map stays black until those tiles load again.
I think there are three small causes, all in src/three/plugins/images/ImageOverlayPlugin.js:
reading 'signal': a tile joins processedTiles on tile-download-start, but it gets its tileControllers entry only later, in _processTileModel. _initOverlay goes over all processedTiles, so _initTileSceneOverlayInfo reads tileController.signal of a tile that has no controller yet.
The comment above that line says a missing controller means the tile was disposed, but the code does not check for it.
reading 'updateMatrixWorld': while a tile is in _processTileModel, its scene is only in pendingTiles, and tile.engineData.scene is still null. _initOverlay reads only tile.engineData.scene, so _initTileSceneOverlayInfo gets null.
That tile then never gets the new overlay.
reading 'color': after (2), the new overlay has no meshInfo for that tile. _updateLayers still sets LAYER_COUNT to overlays.length (from the meshes of the other overlays), but layerInfo.value[ i ] stays empty for the new overlay.
The uniform upload then reads .color of undefined.
Side note: params.layerMaps.length = overlays.length sets a property on the uniform object, not on its value array.
I have a fix for all three.
With it, the same steps give no errors, and every tile shows the overlay:
_initTileSceneOverlayInfo: also return early when tileController is missing.
_initOverlay: use tile.engineData.scene || pendingTiles.get( tile ), and skip a tile that has no scene yet (_processTileModel adds every overlay to it later).
_updateLayers: before the layers are filled, give every mesh of the tile one slot per overlay (layerMaps.value[ i ] = null, layerInfo.value[ i ] = overlay), and set the length on the value arrays.
I can open a pull request with these changes if this approach looks right to you.
Reproduction steps
Open the live example. It uses GeneratedSurfacePlugin with OpenStreetMap imagery (XYZTilesOverlay), and a red grid as a GeoJSONOverlay.
The page shows the area with the grid, and waits until all tiles are loaded. For the first few seconds the view is all red: on the first, coarse tiles the grid lines blur together (this also happens with the fix).
The camera moves closer. As soon as the new tiles are being processed, the page swaps the grid overlay once.
At that swap, the console shows the signal and updateMatrixWorld errors.
Right after it, some tiles show no grid, that part of the map turns black, and reading 'color' is logged on every frame.
Code
// a generated globe with OpenStreetMap imageryconstimagery=newXYZTilesOverlay({url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',levels: 19});constplugin=newImageOverlayPlugin({overlays: [imagery], renderer });consttiles=newTilesRenderer();tiles.registerPlugin(newGeneratedSurfacePlugin({overlay: imagery}));tiles.registerPlugin(plugin);// swap a GeoJSON overlay while tiles load: wait until the new one is ready, remove the old one, add the new oneletcurrent=null;asyncfunctionswap(){constnext=newGeoJSONOverlay({ geojson,strokeStyle: '#ff2d2d',strokeWidth: 2});awaitnext.init();if(current)plugin.deleteOverlay(current);plugin.addOverlay(next);current=next;}
TypeError: Cannot read properties of undefined (reading 'signal')
at pn._initTileSceneOverlayInfo (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1889:63)
at 3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1846:17
at Set.forEach (<anonymous>)
at pn._initOverlay (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1843:5)
at pn.addOverlay (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1819:26)
at swap (index.html:52:9)
TypeError: Cannot read properties of null (reading 'updateMatrixWorld')
at pn._initTileSceneOverlayInfo (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1891:5)
at 3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1846:17
at Set.forEach (<anonymous>)
at pn._initOverlay (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1843:5)
at pn.addOverlay (3d-tiles-renderer@0.5.3/build/plugins-DXnt62ch.js:1819:26)
at swap (index.html:52:9)
TypeError: Cannot read properties of undefined (reading 'color')
at StructuredUniform.setValue (three@0.186.1/build/three.module.js:5979:25)
at StructuredUniform.setValue (three@0.186.1/build/three.module.js:5979:6)
at WebGLUniforms.upload (three@0.186.1/build/three.module.js:6133:7)
at setProgram (three@0.186.1/build/three.module.js:18828:19)
at WebGLRenderer.renderBufferDirect (three@0.186.1/build/three.module.js:17239:20)
at renderObject (three@0.186.1/build/three.module.js:18216:11)
Activity
mikemikimike commentedon Oct 14, 2026