Skip to content

Commit 99993b7

Browse files
authored
fix(tiles): Extract rootNode.matrix into cartesianModelMatrix (#3314)
1 parent d854917 commit 99993b7

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

modules/tiles/src/tileset/helpers/transform-utils.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {Ellipsoid} from '@math.gl/geospatial';
66
import {Matrix4, Vector3} from '@math.gl/core';
77
import {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

Comments
 (0)