Skip to content

Commit

Permalink
fix: apply some of the suggested changes
Browse files Browse the repository at this point in the history
feat(tile): add doc comments, rm unused tileCenter
  • Loading branch information
HoloTheDrunk committed Dec 9, 2024
1 parent 684bb92 commit 4fc027e
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 94 deletions.
2 changes: 1 addition & 1 deletion src/Core/Prefab/Globe/GlobeLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class GlobeLayer extends TiledGeometryLayer {
];

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

super(id, object3d || new THREE.Group(), schemeTile, builder, {
tileMatrixSets,
Expand Down
78 changes: 40 additions & 38 deletions src/Core/Prefab/Globe/GlobeTileBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as THREE from 'three';
import Coordinates from 'Core/Geographic/Coordinates';
import Extent from 'Core/Geographic/Extent';
import {
Projected,
ShareableExtent,
TileBuilder,
TileBuilderParams,
Expand All @@ -16,35 +15,56 @@ const quatToAlignLongitude = new THREE.Quaternion();
const quatToAlignLatitude = new THREE.Quaternion();
const quatNormalToZ = new THREE.Quaternion();

function WGS84ToOneSubY(latitude: number) {
/** 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 _crs: string;
private _transform: {
coords: Coordinates[];
position: THREE.Vector3;
dimension: THREE.Vector2;
};
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 this._crs;
return GlobeTileBuilder._crs;
}

public constructor(options: {
crs: string,
/** Number of unaligned texture sets. */
uvCount: number,
}) {
this._transform = {
Expand All @@ -56,9 +76,6 @@ implements TileBuilder<GlobeTileBuilderParams> {
dimension: new THREE.Vector2(),
};

this._crs = options.crs;
// Order crs projection on tiles

// UV: Normalized coordinates (from degree) on the entire tile
// EPSG:4326
// Offset: Float row coordinate from Pseudo mercator coordinates
Expand All @@ -68,13 +85,6 @@ implements TileBuilder<GlobeTileBuilderParams> {
}
}

private static _computeExtraOffset(params: GlobeTileBuilderParams): number {
const t = WGS84ToOneSubY(params.projected.latitude) * params.nbRow;
return (!isFinite(t) ? 0 : t) - params.deltaUV1;
}

// prepare params
// init projected object -> params.projected
public prepare(params: TileBuilderParams): GlobeTileBuilderParams {
const nbRow = 2 ** (params.level + 1.0);
let st1 = WGS84ToOneSubY(params.extent.south);
Expand All @@ -95,62 +105,54 @@ implements TileBuilder<GlobeTileBuilderParams> {
params.extent.center().latitude,
))),
// let's avoid building too much temp objects
projected: new Projected(),
coordinates: new Coordinates(this.crs),
};

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

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

// get center tile in cartesian 3D
public center(extent: Extent) {
return extent.center(this._transform.coords[0])
.as(this.crs, this._transform.coords[1])
.toVector3();
}

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

// get normal for last vertex
public vertexNormal() {
return this._transform.coords[1].geodesicNormal;
}

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

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

public computeShareableExtent(extent: Extent): ShareableExtent {
// Compute shareable extent to pool the geometries
// the geometry in common extent is identical to the existing input
// with a transformation (translation, rotation)

// TODO: It should be possible to use equatorial plan symetrie,
// but we should be reverse UV on tile
// Common geometry is looking for only on longitude
// 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 tile to position on ellipsoid
// this transformation takes into account the transformation of the
// parents
// 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,
);
Expand Down
20 changes: 9 additions & 11 deletions src/Core/Prefab/Planar/PlanarTileBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as THREE from 'three';
import Coordinates from 'Core/Geographic/Coordinates';
import Extent from 'Core/Geographic/Extent';
import {
Projected,
ShareableExtent,
TileBuilder,
TileBuilderParams,
Expand All @@ -17,12 +16,17 @@ type Transform = {
normal: THREE.Vector3,
};

/** Specialized parameters for the [PlanarTileBuilder]. */
export interface PlanarTileBuilderParams extends TileBuilderParams {
crs: string;
uvCount?: number;
nbRow: number;
}

/**
* TileBuilder implementation for the purpose of generating planar
* tile arrangements.
*/
export class PlanarTileBuilder implements TileBuilder<PlanarTileBuilderParams> {
private _uvCount: number;
private _transform: Transform;
Expand Down Expand Up @@ -58,12 +62,10 @@ export class PlanarTileBuilder implements TileBuilder<PlanarTileBuilderParams> {
return this._crs;
}

// prepare params
// init projected object -> params.projected
public prepare(params: TileBuilderParams): PlanarTileBuilderParams {
const newParams = params as PlanarTileBuilderParams;
newParams.nbRow = 2 ** (params.zoom + 1.0);
newParams.projected = new Projected();
newParams.nbRow = 2 ** (params.level + 1.0);
newParams.coordinates = new Coordinates(this.crs);
return newParams;
}

Expand All @@ -73,23 +75,19 @@ export class PlanarTileBuilder implements TileBuilder<PlanarTileBuilderParams> {
return center;
}

// set position 3D cartesian
public vertexPosition(position: THREE.Vector2): THREE.Vector3 {
this._transform.position.set(position.x, position.y, 0);
public vertexPosition(coordinates: Coordinates): THREE.Vector3 {
this._transform.position.set(coordinates.x, coordinates.y, 0);
return this._transform.position;
}

// get normal for last vertex
public vertexNormal(): THREE.Vector3 {
return this._transform.normal;
}

// coord u tile to projected
public uProject(u: number, extent: Extent): number {
return extent.west + u * (extent.east - extent.west);
}

// coord v tile to projected
public vProject(v: number, extent: Extent): number {
return extent.south + v * (extent.north - extent.south);
}
Expand Down
52 changes: 26 additions & 26 deletions src/Core/Prefab/TileBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Cache from 'Core/Scheduler/Cache';
import { computeBuffers } from 'Core/Prefab/computeBufferTileGeometry';
import OBB from 'Renderer/OBB';
import type Extent from 'Core/Geographic/Extent';
import Coordinates from 'Core/Geographic/Coordinates';

const cacheBuffer = new Map<string, { [buf: string]: THREE.BufferAttribute }>();
const cacheTile = new Cache();
Expand All @@ -15,46 +16,33 @@ export type GpuBufferAttributes = {
uvs: THREE.BufferAttribute[];
};

/**
* Reference to a tile's extent with rigid transformations.
* Enables reuse of geometry, saving a bit of memory.
*/
export type ShareableExtent = {
shareableExtent: Extent;
quaternion: THREE.Quaternion;
position: THREE.Vector3;
};

// TODO: Check if this order is right
// Ideally we split this into Vec2 and a simpler LatLon type
// Somewhat equivalent to a light Coordinates class
export class Projected extends THREE.Vector2 {
public get longitude(): number {
return this.x;
}

public set longitude(longitude: number) {
this.x = longitude;
}

public get latitude(): number {
return this.y;
}

public set latitude(latitude: number) {
this.y = latitude;
}
}

export interface TileBuilderParams {
/** Whether to build the skirt. */
disableSkirt: boolean;
/** Whether to render the skirt. */
hideSkirt: boolean;
/**
* Cache-related.
* Tells the function whether to build or skip the index and uv buffers.
*/
buildIndexAndUv_0: boolean;
/** Number of segments (edge loops) inside tiles. */
segments: number;
// TODO: Move this out of the interface
/** Buffer for projected points. */
projected: Projected;
coordinates: Coordinates;
extent: Extent;
level: number;
zoom: number;
center: THREE.Vector3;
}

Expand All @@ -63,13 +51,26 @@ export interface TileBuilder<SpecializedParams extends TileBuilderParams> {

/** Convert builder-agnostic params to specialized ones. */
prepare(params: TileBuilderParams): SpecializedParams;
/**
* Computes final offset of the second texture set.
* Only relevant in the case of more than one texture sets.
*/
computeExtraOffset?: (params: SpecializedParams) => number;
/** Get the center of the tile in 3D cartesian coordinates. */
/** Get the center of the current tile as a 3D vector. */
center(extent: Extent): THREE.Vector3;
vertexPosition(position: THREE.Vector2): THREE.Vector3;
/** Converts an x/y tile-space position to its equivalent in 3D space. */
vertexPosition(coordinates: Coordinates): THREE.Vector3;
/** Gets the geodesic normal of the last processed vertex. */
vertexNormal(): THREE.Vector3;
/** Project horizontal texture coordinate to world space. */
uProject(u: number, extent: Extent): number;
/** Project vertical texture coordinate to world space. */
vProject(v: number, extent: Extent): number;
/**
* Compute shareable extent to pool geometries together.
* The geometry of tiles on the same latitude is the same with an added
* rigid transform.
*/
computeShareableExtent(extent: Extent): ShareableExtent;
}

Expand All @@ -86,7 +87,6 @@ export function newTileGeometry(
`${builder.crs}_${params.disableSkirt ? 0 : 1}_${params.segments}`;

let promiseGeometry = cacheTile.get(south, params.level, bufferKey);
// let promiseGeometry;

// build geometry if doesn't exist
if (!promiseGeometry) {
Expand Down
Loading

0 comments on commit 4fc027e

Please sign in to comment.