@@ -6,7 +6,9 @@ import {Ellipsoid} from '@math.gl/geospatial';
66import { Matrix4 , Vector3 } from '@math.gl/core' ;
77import { assert } from '@loaders.gl/loader-utils' ;
88
9- export function calculateTransformProps ( tileHeader , tile ) {
9+ import { Tile3D } from '../tile-3d' ;
10+
11+ export function calculateTransformProps ( tileHeader : Tile3D , tile : Tile3D [ 'content' ] ) {
1012 assert ( tileHeader ) ;
1113 assert ( tile ) ;
1214
@@ -62,8 +64,46 @@ export function calculateTransformProps(tileHeader, tile) {
6264 tile . cartographicModelMatrix = toFixedFrameMatrix . multiplyRight ( modelMatrix ) ;
6365 tile . cartographicOrigin = cartographicOrigin ;
6466
67+ // Absorb glTF root node matrix into model matrices for Float32 precision.
68+ // The glTF root node matrix (applied as sceneModelMatrix in the shader) may contain
69+ // ECEF-scale translations (~millions of meters). When both cartographicModelMatrix
70+ // and sceneModelMatrix are applied in the Float32 GPU shader, catastrophic cancellation
71+ // occurs causing visible seams between adjacent tiles. By combining them here in Float64,
72+ // the result has small ENU-scale values that preserve precision.
73+ const rootNode = _getRootNode ( tile ) ;
74+ if ( rootNode ) {
75+ tile . cartesianModelMatrix = new Matrix4 ( modelMatrix ) . multiplyRight ( rootNode . matrix ) ;
76+ tile . cartographicModelMatrix . multiplyRight ( rootNode . matrix ) ;
77+ rootNode . matrix = Matrix4 . IDENTITY ;
78+ }
79+
6580 // Deprecated, drop
6681 if ( ! tile . coordinateSystem ) {
6782 tile . modelMatrix = tile . cartographicModelMatrix ;
6883 }
6984}
85+
86+ const TRANSLATION_LIMIT_SQUARED = 10e5 ** 2 ; // 100km
87+
88+ /**
89+ * Returns the glTF root node if it has a matrix with earth-scale translations (> 100km).
90+ * These large translations cause Float32 precision issues when applied in the GPU shader.
91+ */
92+ function _getRootNode ( tile : Tile3D [ 'content' ] ) : { matrix : number [ ] } | null {
93+ const gltf = tile . gltf ;
94+ if ( ! gltf ) {
95+ return null ;
96+ }
97+
98+ const sceneIndex = typeof gltf . scene === 'number' ? gltf . scene : 0 ;
99+ const scene = gltf . scenes ?. [ sceneIndex ] ;
100+ const rootNode = scene ?. nodes ?. [ 0 ] ;
101+ if ( ! rootNode ?. matrix ) return null ;
102+
103+ // Extract translation and compare magnitude (meters) to limit
104+ const m = rootNode . matrix ;
105+ const translationMagnitude = m [ 12 ] * m [ 12 ] + m [ 13 ] * m [ 13 ] + m [ 14 ] * m [ 14 ] ;
106+ if ( translationMagnitude <= TRANSLATION_LIMIT_SQUARED ) return null ;
107+
108+ return rootNode ;
109+ }
0 commit comments