UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

252 lines (164 loc) 5.95 kB
import { Ray, Sphere } from 'three'; import { OBB } from '3d-tiles-renderer/three'; /** * Plugin that restricts tile loading and traversal to one or more geometric regions * (`SphereRegion`, `RayRegion`, `OBBRegion`). Only tiles that intersect an active * region are loaded and refined. Regions marked as masks additionally prevent tiles * outside them from loading. * * @param {Object} [options] * @param {Array<BaseRegion>} [options.regions=[]] Initial set of regions to register. Equivalent to calling `addRegion` for each entry. */ export class LoadRegionPlugin { constructor( options = {} ) { const { regions = [], } = options; this.name = 'LOAD_REGION_PLUGIN'; this.regions = []; this.tiles = null; regions.forEach( region => this.addRegion( region ) ); } init( tiles ) { this.tiles = tiles; } addRegion( region ) { if ( this.regions.indexOf( region ) === - 1 ) { this.regions.push( region ); } } removeRegion( region ) { const index = this.regions.indexOf( region ); if ( index !== - 1 ) { this.regions.splice( index, 1 ); } } hasRegion( region ) { return this.regions.indexOf( region ) !== - 1; } clearRegions() { this.regions = []; } // Calculates shape intersections and associated error values to use. If "mask" shapes are present then // tiles are only loaded if they are within those shapes. calculateTileViewError( tile, target ) { const boundingVolume = tile.engineData.boundingVolume; const { regions, tiles } = this; // "inShape" indicates whether the tile intersects the given shape. // "inMask" indicates whether the tile is _within_ a mask if boolean, if // null then it is ignored. // "maxError" is the maximum error from the shapes. let inShape = false; let inMask = null; let maxError = 0; let minDistance = Infinity; for ( const region of regions ) { // TODO: we should only set the error if it is "intersected". // Check if the tile is intersecting the shape and calculate the // view and error values. const intersects = region.intersectsTile( boundingVolume, tile, tiles ); inShape = inShape || intersects; if ( intersects ) { maxError = Math.max( region.calculateError( tile, tiles ), maxError ); minDistance = Math.min( region.calculateDistance( boundingVolume, tile, tiles ), minDistance ); } // Store whether the tile is in a "mask" shape if they exist. If "inMask" is // still "null" by the end of the loop then there are no mask elements. if ( region.mask ) { inMask = inMask || intersects; } } // A tile should only be visible if it intersects a shape and a mask shape or there // are no masks. target.inView = inShape && inMask !== false; target.error = maxError; target.distance = minDistance; // Returning "false" indicates "no operation" and all results will be ignored. return target.inView || inMask !== null; } dispose() { this.regions = []; } } // Definitions of predefined regions /** * Abstract base class for `LoadRegionPlugin` regions. Subclass and override * `intersectsTile` to define custom load regions. * @param {Object} [options] * @param {number} [options.errorTarget=10] Geometric error target used when this region controls refinement. * @param {boolean} [options.mask=false] When `true`, tiles outside this region are suppressed (mask mode). */ export class BaseRegion { constructor( options = {} ) { const { errorTarget = 10, mask = false, } = options; this.errorTarget = errorTarget; this.mask = mask; } intersectsTile( boundingVolume, tile, tiles ) { return false; } calculateDistance( boundingVolume, tile, tiles ) { return Infinity; } calculateError( tile, tilesRenderer ) { return tile.geometricError - this.errorTarget + tilesRenderer.errorTarget; } } /** * A spherical load region. Only tiles that intersect `sphere` are loaded. * @extends BaseRegion * @param {Object} [options] * @param {Sphere} [options.sphere] The sphere volume; defaults to an empty sphere at the origin. * @param {number} [options.errorTarget=10] Geometric error target for tiles inside the region. * @param {boolean} [options.mask=false] Mask mode — suppresses tiles outside this region. */ export class SphereRegion extends BaseRegion { constructor( options = {} ) { const { sphere = new Sphere() } = options; super( options ); this.sphere = sphere.clone(); } intersectsTile( boundingVolume ) { return boundingVolume.intersectsSphere( this.sphere ); } } /** * A ray-based load region. Only tiles that intersect `ray` are loaded. * @extends BaseRegion * @param {Object} [options] * @param {Ray} [options.ray] The ray; defaults to a ray at the origin pointing in +Z. * @param {number} [options.errorTarget=10] Geometric error target for tiles inside the region. * @param {boolean} [options.mask=false] Mask mode — suppresses tiles outside this region. */ export class RayRegion extends BaseRegion { constructor( options = {} ) { const { ray = new Ray() } = options; super( options ); this.ray = ray.clone(); } intersectsTile( boundingVolume ) { return boundingVolume.intersectsRay( this.ray ); } } /** * An oriented bounding-box load region. Only tiles that intersect `obb` are loaded. * @extends BaseRegion * @param {Object} [options] * @param {OBB} [options.obb] The oriented bounding box; defaults to an empty OBB at the origin. * @param {number} [options.errorTarget=10] Geometric error target for tiles inside the region. * @param {boolean} [options.mask=false] Mask mode — suppresses tiles outside this region. */ export class OBBRegion extends BaseRegion { constructor( options = {} ) { const { obb = new OBB() } = options; super( options ); this.obb = obb.clone(); this.obb.update(); } intersectsTile( boundingVolume ) { return boundingVolume.intersectsOBB( this.obb ); } }