Skip to content

Commit

Permalink
refactor(TileBuilder): migrate to TypeScript (#2440)
Browse files Browse the repository at this point in the history
Improve performance by using statically-sized ArrayBuffers.
Reorganize code to get rid of some of the params/builder mess.
Cleanup computeBuffers function.

Squashed commit history (oldest to youngest):
- fix: UV_1 generation
- refacto(wip): cleanup and optimize computeBuffers
- refacto(wip): improve index generation
- wip: correct offset, off-by-one still at large
- fix: change calls to allow camera debug
- fix: found the error, off by a power of 2 actually
- fix(uv): correct indices passed to UV buffering
- fix(index): only generate buffer when needed
- wip: enable cache
- fix(computeBuffers): group tile and skirt together
- fix(wip): squash rogue private field access
- refacto: convert TileGeometry to TypeScript
- style(builders): make method visibility explicit
- refactor(exports): remove default exports
- feat(TileGeometry): add OBB type
- refactor: rename BuilderEllipsoidTile, fix imports
- fix(tileGeometry): update tests to use public api
- fix(TileBuilder): remove dead code comments
- fix: apply some of the suggested changes
- feat(tile): add doc comments, rm unused tileCenter
  • Loading branch information
HoloTheDrunk authored Dec 10, 2024
1 parent dc347d1 commit 3207dcd
Show file tree
Hide file tree
Showing 16 changed files with 962 additions and 570 deletions.
6 changes: 3 additions & 3 deletions src/Converter/convertToTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as THREE from 'three';
import TileMesh from 'Core/TileMesh';
import LayeredMaterial from 'Renderer/LayeredMaterial';
import newTileGeometry from 'Core/Prefab/TileBuilder';
import { newTileGeometry } from 'Core/Prefab/TileBuilder';
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
import { geoidLayerIsVisible } from 'Layer/GeoidLayer';

Expand Down Expand Up @@ -52,7 +52,7 @@ export default {

return newTileGeometry(builder, paramsGeometry).then((result) => {
// build tile mesh
result.geometry._count++;
result.geometry.increaseRefCount();
const crsCount = layer.tileMatrixSets.length;
const material = new LayeredMaterial(layer.materialOptions, crsCount);
ReferLayerProperties(material, layer);
Expand All @@ -61,7 +61,7 @@ export default {

if (parent && parent.isTileMesh) {
// get parent extent transformation
const pTrans = builder.computeSharableExtent(parent.extent);
const pTrans = builder.computeShareableExtent(parent.extent);
// place relative to his parent
result.position.sub(pTrans.position).applyQuaternion(pTrans.quaternion.invert());
result.quaternion.premultiply(pTrans.quaternion);
Expand Down
126 changes: 0 additions & 126 deletions src/Core/Prefab/Globe/BuilderEllipsoidTile.js

This file was deleted.

5 changes: 3 additions & 2 deletions src/Core/Prefab/Globe/GlobeLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as THREE from 'three';
import TiledGeometryLayer from 'Layer/TiledGeometryLayer';
import { ellipsoidSizes } from 'Core/Math/Ellipsoid';
import { globalExtentTMS, schemeTiles } from 'Core/Tile/TileGrid';
import BuilderEllipsoidTile from 'Core/Prefab/Globe/BuilderEllipsoidTile';
import { GlobeTileBuilder } from 'Core/Prefab/Globe/GlobeTileBuilder';

// matrix to convert sphere to ellipsoid
const worldToScaledEllipsoid = new THREE.Matrix4();
Expand Down Expand Up @@ -61,8 +61,9 @@ class GlobeLayer extends TiledGeometryLayer {
'EPSG:4326',
'EPSG:3857',
];

const uvCount = tileMatrixSets.length;
const builder = new BuilderEllipsoidTile({ crs: 'EPSG:4978', uvCount });
const builder = new GlobeTileBuilder({ uvCount });

super(id, object3d || new THREE.Group(), schemeTile, builder, {
tileMatrixSets,
Expand Down
172 changes: 172 additions & 0 deletions src/Core/Prefab/Globe/GlobeTileBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import * as THREE from 'three';
import Coordinates from 'Core/Geographic/Coordinates';
import Extent from 'Core/Geographic/Extent';
import {
ShareableExtent,
TileBuilder,
TileBuilderParams,
} from '../TileBuilder';

const PI_OV_FOUR = Math.PI / 4;
const INV_TWO_PI = 1.0 / (Math.PI * 2);
const axisZ = new THREE.Vector3(0, 0, 1);
const axisY = new THREE.Vector3(0, 1, 0);
const quatToAlignLongitude = new THREE.Quaternion();
const quatToAlignLatitude = new THREE.Quaternion();
const quatNormalToZ = new THREE.Quaternion();

/** Transforms a WGS84 latitude into a usable texture offset. */
function WGS84ToOneSubY(latitude: number): number {
return 1.0 - (0.5 - Math.log(Math.tan(
PI_OV_FOUR + THREE.MathUtils.degToRad(latitude) * 0.5,
)) * INV_TWO_PI);
}

type Transform = {
/** Buffers for 2-part coordinate mapping operations. */
coords: [Coordinates, Coordinates];
position: THREE.Vector3;
dimension: THREE.Vector2;
};

/** Specialized parameters for the [GlobeTileBuilder]. */
export interface GlobeTileBuilderParams extends TileBuilderParams {
/** Number of rows of tiles, essentially the resolution of the globe. */
nbRow: number;
/** Offset of the second texture set. */
deltaUV1: number;
/** Transformation to align a tile's normal to the Z axis. */
quatNormalToZ: THREE.Quaternion;
}

/**
* TileBuilder implementation for the purpose of generating globe (or more
* precisely ellipsoidal) tile arrangements.
*/
export class GlobeTileBuilder
implements TileBuilder<GlobeTileBuilderParams> {
private static _crs: string = 'EPSG:4978';
private static _computeExtraOffset(params: GlobeTileBuilderParams): number {
const t = WGS84ToOneSubY(params.coordinates.latitude) * params.nbRow;
return (!isFinite(t) ? 0 : t) - params.deltaUV1;
}

/**
* Buffer holding information about the tile/vertex currently being
* processed.
*/
private _transform: Transform;

public computeExtraOffset?: (params: GlobeTileBuilderParams) => number;

public get crs(): string {
return GlobeTileBuilder._crs;
}

public constructor(options: {
/** Number of unaligned texture sets. */
uvCount: number,
}) {
this._transform = {
coords: [
new Coordinates('EPSG:4326', 0, 0),
new Coordinates('EPSG:4326', 0, 0),
],
position: new THREE.Vector3(),
dimension: new THREE.Vector2(),
};

// UV: Normalized coordinates (from degree) on the entire tile
// EPSG:4326
// Offset: Float row coordinate from Pseudo mercator coordinates
// EPSG:3857
if (options.uvCount > 1) {
this.computeExtraOffset = GlobeTileBuilder._computeExtraOffset;
}
}

public prepare(params: TileBuilderParams): GlobeTileBuilderParams {
const nbRow = 2 ** (params.level + 1.0);
let st1 = WGS84ToOneSubY(params.extent.south);

if (!isFinite(st1)) { st1 = 0; }

const sizeTexture = 1.0 / nbRow;

const start = (st1 % (sizeTexture));

const newParams = {
nbRow,
deltaUV1: (st1 - start) * nbRow,
// transformation to align tile's normal to z axis
quatNormalToZ: quatNormalToZ.setFromAxisAngle(
axisY,
-(Math.PI * 0.5 - THREE.MathUtils.degToRad(
params.extent.center().latitude,
))),
// let's avoid building too much temp objects
coordinates: new Coordinates(this.crs),
};

params.extent.planarDimensions(this._transform.dimension);

return { ...params, ...newParams };
}

public center(extent: Extent) {
return extent.center(this._transform.coords[0])
.as(this.crs, this._transform.coords[1])
.toVector3();
}

public vertexPosition(coordinates: Coordinates): THREE.Vector3 {
return this._transform.coords[0]
.setFromValues(coordinates.x, coordinates.y)
.as(this.crs, this._transform.coords[1])
.toVector3(this._transform.position);
}

public vertexNormal() {
return this._transform.coords[1].geodesicNormal;
}

public uProject(u: number, extent: Extent): number {
return extent.west + u * this._transform.dimension.x;
}

public vProject(v: number, extent: Extent): number {
return extent.south + v * this._transform.dimension.y;
}

public computeShareableExtent(extent: Extent): ShareableExtent {
// NOTE: It should be possible to take advantage of equatorial plane
// symmetry, for which we'd have to reverse the tile's UVs.
// This would halve the memory requirement when viewing a full globe,
// but that case is not that relevant for iTowns' usual use cases and
// the globe mesh memory usage is already inconsequential.
const sizeLongitude = Math.abs(extent.west - extent.east) / 2;
const shareableExtent = new Extent(
extent.crs,
-sizeLongitude, sizeLongitude,
extent.south, extent.north,
);

// Compute rotation to transform the tile to position on the ellispoid.
// This transformation takes the parents' transformation into account.
const rotLon = THREE.MathUtils.degToRad(
extent.west - shareableExtent.west,
);
const rotLat = THREE.MathUtils.degToRad(
90 - extent.center(this._transform.coords[0]).latitude,
);
quatToAlignLongitude.setFromAxisAngle(axisZ, rotLon);
quatToAlignLatitude.setFromAxisAngle(axisY, rotLat);
quatToAlignLongitude.multiply(quatToAlignLatitude);

return {
shareableExtent,
quaternion: quatToAlignLongitude.clone(),
position: this.center(extent),
};
}
}
2 changes: 1 addition & 1 deletion src/Core/Prefab/Planar/PlanarLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as THREE from 'three';

import TiledGeometryLayer from 'Layer/TiledGeometryLayer';
import { globalExtentTMS } from 'Core/Tile/TileGrid';
import PlanarTileBuilder from './PlanarTileBuilder';
import { PlanarTileBuilder } from './PlanarTileBuilder';

/**
* @property {boolean} isPlanarLayer - Used to checkout whether this layer is a
Expand Down
Loading

0 comments on commit 3207dcd

Please sign in to comment.