Skip to content

Commit 78e7b87

Browse files
authored
Examples: Add load region to prevent low lod data in google photorealistic tiles (#1626)
* Add new region, remove deprecated logs * Get it all working * Updates * Clean up * Lint fix
1 parent 6ccdcd0 commit 78e7b87

4 files changed

Lines changed: 162 additions & 38 deletions

File tree

example/three/annotationsExample.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
MVTAnnotationsPlugin,
1414
UpdateOnChangePlugin,
1515
} from '3d-tiles-renderer/plugins';
16+
import { LoadRegionPlugin } from '3d-tiles-renderer/three/plugins';
17+
import { CameraCartographicRegion } from './src/plugins/CameraCartographicRegion.js';
1618
import {
1719
Scene,
1820
WebGLRenderer,
@@ -145,6 +147,18 @@ function reinstantiateTiles() {
145147
onAnnotationsUpdate: ( added, removed ) => annotationsPoints.update( added, removed ),
146148
} ) );
147149

150+
// use the camera cartographic region plugin to prevent particularly low-lod
151+
// tiles from loading beneath the camera, causing navigation issues.
152+
const cameraRegion = new CameraCartographicRegion( {
153+
camera,
154+
radius: 1500,
155+
errorTarget: 5000,
156+
} );
157+
158+
tiles.registerPlugin( new LoadRegionPlugin( {
159+
regions: [ cameraRegion ],
160+
} ) );
161+
148162
annotationsPoints = new AnnotationPoints( {
149163
size: 20,
150164
glyphSize: 2 * 20 * renderer.getPixelRatio(),

example/three/googleMapsExample.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
BatchedTilesPlugin,
1616
CesiumIonAuthPlugin,
1717
} from '3d-tiles-renderer/plugins';
18+
import { LoadRegionPlugin } from '3d-tiles-renderer/three/plugins';
19+
import { CameraCartographicRegion } from './src/plugins/CameraCartographicRegion.js';
1820
import {
1921
Scene,
2022
WebGLRenderer,
@@ -29,6 +31,7 @@ import { TopoLinesPlugin } from './src/plugins/topolines/TopoLinesPlugin.js';
2931

3032
let controls, scene, renderer, tiles, transition;
3133
let statsContainer, stats;
34+
let cameraRegion;
3235

3336
const params = {
3437

@@ -73,6 +76,18 @@ function reinstantiateTiles() {
7376
dracoLoader: new DRACOLoader().setDecoderPath( 'https://unpkg.com/three@0.153.0/examples/jsm/libs/draco/gltf/' )
7477
} ) );
7578

79+
// use the camera cartographic region plugin to prevent particularly low-lod
80+
// tiles from loading beneath the camera, causing navigation issues.
81+
cameraRegion = new CameraCartographicRegion( {
82+
camera: transition.perspectiveCamera,
83+
radius: 1500,
84+
errorTarget: 5000,
85+
} );
86+
87+
tiles.registerPlugin( new LoadRegionPlugin( {
88+
regions: [ cameraRegion ],
89+
} ) );
90+
7691
if ( params.useFadePlugin ) {
7792

7893
tiles.registerPlugin( new TilesFadePlugin() );
@@ -349,6 +364,9 @@ function animate() {
349364
plugin.topoOpacity = params.displayTopoLines ? 0.5 : 0;
350365
plugin.cartoOpacity = params.displayTopoLines ? 0.5 : 0;
351366

367+
// update camera region
368+
cameraRegion.camera = camera;
369+
352370
// update tiles
353371
camera.updateMatrixWorld();
354372
tiles.errorTarget = params.errorTarget;
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/** @import { Camera } from 'three' */
2+
import { Vector3, Matrix4, MathUtils } from 'three';
3+
import { OBB } from '3d-tiles-renderer/three';
4+
import { BaseRegion } from '3d-tiles-renderer/three/plugins';
5+
6+
const _cameraWorld = /* @__PURE__ */ new Vector3();
7+
const _cameraLocal = /* @__PURE__ */ new Vector3();
8+
const _cartographic = { lat: 0, lon: 0, height: 0 };
9+
const _east = /* @__PURE__ */ new Vector3();
10+
const _north = /* @__PURE__ */ new Vector3();
11+
const _up = /* @__PURE__ */ new Vector3();
12+
const _center = /* @__PURE__ */ new Vector3();
13+
14+
const PILLAR_HEIGHT = 3000;
15+
16+
/**
17+
* A load region shaped as a vertical pillar below the camera, aligned to the globe gravity
18+
* direction derived from the tile renderer's ellipsoid. Intended for use with LoadRegionPlugin
19+
* to force higher-quality tile loading directly below the camera, preventing coarse LoD tiles
20+
* from producing large height discontinuities under the camera (e.g. with Google Photorealistic
21+
* Tiles)
22+
*
23+
* @param {Object} [options]
24+
* @param {Camera} options.camera The camera whose position drives the pillar placement.
25+
* @param {number} [options.radius=1000] Half-width of the pillar in world units.
26+
* @param {number} [options.errorTarget=10] Geometric error target for tiles inside the region.
27+
*/
28+
export class CameraCartographicRegion extends BaseRegion {
29+
30+
constructor( options = {} ) {
31+
32+
const {
33+
camera,
34+
radius = 1000,
35+
...rest
36+
} = options;
37+
38+
super( rest );
39+
40+
this.camera = camera;
41+
this.radius = radius;
42+
43+
this.obb = new OBB();
44+
this._cameraMatrix = new Matrix4();
45+
this._localCameraPos = new Vector3();
46+
this._localUp = new Vector3();
47+
48+
}
49+
50+
calculateDistance( boundingVolume ) {
51+
52+
return boundingVolume.distanceToPoint( this._localCameraPos );
53+
54+
}
55+
56+
calculateError( tile, tilesRenderer ) {
57+
58+
// calculate the error so it drops off with distance to enable progressive loading
59+
this._updateOBB( tilesRenderer );
60+
61+
const boundingVolume = tile.engineData.boundingVolume;
62+
const dist = boundingVolume.distanceToPoint( this._localCameraPos );
63+
const t = Math.min( dist / this.radius, 1 );
64+
65+
const minTarget = tile.geometricError - this.errorTarget + tilesRenderer.errorTarget;
66+
const maxTarget = tile.geometricError - this.errorTarget * 5 + tilesRenderer.errorTarget;
67+
return MathUtils.lerp( minTarget, maxTarget, t );
68+
69+
}
70+
71+
intersectsTile( boundingVolume, tile, tilesRenderer ) {
72+
73+
// update the obb and check the intersection with the tile bounding volume
74+
this._updateOBB( tilesRenderer );
75+
76+
const { obb } = this;
77+
return boundingVolume.intersectsOBB( obb );
78+
79+
}
80+
81+
_updateOBB( tilesRenderer ) {
82+
83+
// update the OBB to live below the camera position toward the globe surface
84+
const { camera, radius, obb } = this;
85+
if ( ! camera.matrixWorld.equals( this._cameraMatrix ) ) {
86+
87+
const { ellipsoid, group } = tilesRenderer;
88+
89+
// Transform camera world position into the tile renderer's local (ellipsoid) space
90+
camera.getWorldPosition( _cameraWorld );
91+
_cameraLocal.copy( _cameraWorld ).applyMatrix4( group.matrixWorldInverse );
92+
this._localCameraPos.copy( _cameraLocal );
93+
94+
// Get the geodetic ENU axes at the camera's position on the ellipsoid
95+
ellipsoid.getPositionToCartographic( _cameraLocal, _cartographic );
96+
ellipsoid.getEastNorthUpAxes( _cartographic.lat, _cartographic.lon, _east, _north, _up );
97+
this._localUp.copy( _up );
98+
99+
// Pillar center: camera local position shifted half-depth downward (nadir = -up)
100+
_center.copy( _cameraLocal ).addScaledVector( _up, - PILLAR_HEIGHT / 2 );
101+
102+
// OBB transform: east/north/nadir as X/Y/Z column axes
103+
obb.transform.set(
104+
_east.x, _north.x, - _up.x, _center.x,
105+
_east.y, _north.y, - _up.y, _center.y,
106+
_east.z, _north.z, - _up.z, _center.z,
107+
0, 0, 0, 1,
108+
);
109+
110+
obb.box.min.set( - radius, - radius, - PILLAR_HEIGHT / 2 );
111+
obb.box.max.set( radius, radius, PILLAR_HEIGHT / 2 );
112+
113+
obb.update();
114+
this._cameraMatrix.copy( camera.matrixWorld );
115+
116+
}
117+
118+
}
119+
120+
}

src/three/plugins/LoadRegionPlugin.js

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,24 @@ import { OBB } from '3d-tiles-renderer/three';
66
* (`SphereRegion`, `RayRegion`, `OBBRegion`). Only tiles that intersect an active
77
* region are loaded and refined. Regions marked as masks additionally prevent tiles
88
* outside them from loading.
9+
*
10+
* @param {Object} [options]
11+
* @param {Array<BaseRegion>} [options.regions=[]] Initial set of regions to register. Equivalent to calling `addRegion` for each entry.
912
*/
1013
export class LoadRegionPlugin {
1114

12-
constructor() {
15+
constructor( options = {} ) {
16+
17+
const {
18+
regions = [],
19+
} = options;
1320

1421
this.name = 'LOAD_REGION_PLUGIN';
1522
this.regions = [];
1623
this.tiles = null;
1724

25+
regions.forEach( region => this.addRegion( region ) );
26+
1827
}
1928

2029
init( tiles ) {
@@ -129,13 +138,6 @@ export class BaseRegion {
129138

130139
constructor( options = {} ) {
131140

132-
if ( typeof options === 'number' ) {
133-
134-
console.warn( 'LoadRegionPlugin: Region constructor has been changed to take options as an object.' );
135-
options = { errorTarget: options };
136-
137-
}
138-
139141
const {
140142
errorTarget = 10,
141143
mask = false,
@@ -178,16 +180,6 @@ export class SphereRegion extends BaseRegion {
178180

179181
constructor( options = {} ) {
180182

181-
if ( typeof options === 'number' ) {
182-
183-
console.warn( 'SphereRegion: Region constructor has been changed to take options as an object.' );
184-
options = {
185-
errorTarget: arguments[ 0 ],
186-
sphere: arguments[ 1 ],
187-
};
188-
189-
}
190-
191183
const { sphere = new Sphere() } = options;
192184

193185
super( options );
@@ -215,16 +207,6 @@ export class RayRegion extends BaseRegion {
215207

216208
constructor( options = {} ) {
217209

218-
if ( typeof options === 'number' ) {
219-
220-
console.warn( 'RayRegion: Region constructor has been changed to take options as an object.' );
221-
options = {
222-
errorTarget: arguments[ 0 ],
223-
ray: arguments[ 1 ],
224-
};
225-
226-
}
227-
228210
const { ray = new Ray() } = options;
229211

230212
super( options );
@@ -252,16 +234,6 @@ export class OBBRegion extends BaseRegion {
252234

253235
constructor( options = {} ) {
254236

255-
if ( typeof options === 'number' ) {
256-
257-
console.warn( 'RayRegion: Region constructor has been changed to take options as an object.' );
258-
options = {
259-
errorTarget: arguments[ 0 ],
260-
obb: arguments[ 1 ],
261-
};
262-
263-
}
264-
265237
const { obb = new OBB() } = options;
266238

267239
super( options );

0 commit comments

Comments
 (0)