This repository has been archived by the owner on Mar 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 197
HARP-13329: Fix antimeridian cracks on ground planes. #2018
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
fd3584c
HARP-13329: Add IBCT tests.
atomicsulfate e3ee466
HARP-13329: Fix antimeridian cracks on ground planes for planar.
atomicsulfate bcd6454
HARP-13329: Fix antimeridian cracks on ground planes for sphere.
atomicsulfate dfe8b15
HARP-13329: Move projectTilePlaneCorners out of TileGeometryCreator.
atomicsulfate 1d92354
HARP-13329: Move ground plane generation out of TileGeometryCreator.
atomicsulfate File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* | ||
* Copyright (C) 2017-2020 HERE Europe B.V. | ||
* Licensed under Apache 2.0, see full license in LICENSE | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { GeometryKind } from "@here/harp-datasource-protocol"; | ||
import { | ||
EdgeLengthGeometrySubdivisionModifier, | ||
SubdivisionMode | ||
} from "@here/harp-geometry/lib/EdgeLengthGeometrySubdivisionModifier"; | ||
import { SphericalGeometrySubdivisionModifier } from "@here/harp-geometry/lib/SphericalGeometrySubdivisionModifier"; | ||
import { Projection, ProjectionType } from "@here/harp-geoutils"; | ||
import { MapMeshBasicMaterial, MapMeshStandardMaterial } from "@here/harp-materials"; | ||
import * as THREE from "three"; | ||
|
||
import { Tile } from "../Tile"; | ||
import { LodMesh } from "./LodMesh"; | ||
import { projectTilePlaneCorners } from "./ProjectTilePlaneCorners"; | ||
import { registerTileObject } from "./RegisterTileObject"; | ||
|
||
const tmpV = new THREE.Vector3(); | ||
|
||
/** | ||
* Creates and adds a background plane mesh for the tile. | ||
* @param tile - The tile to which the ground plane belongs. | ||
* @param renderOrder - The plane render order. | ||
* @param materialOrColor - The plane material or a color for a default material. | ||
* @param createTexCoords - Whether to create texture coordinates. | ||
* @param receiveShadow - Whether the plane should receive shadows. | ||
* @param createMultiLod - Whether to generate multiple LODs for sphere projection. | ||
* @internal | ||
*/ | ||
export function addGroundPlane( | ||
tile: Tile, | ||
renderOrder: number, | ||
materialOrColor: THREE.Material | THREE.Material[] | number = tile.mapView.clearColor, | ||
createTexCoords: boolean = false, | ||
receiveShadow: boolean = tile.mapView.shadowsEnabled, | ||
createMultiLod: boolean = tile.mapView.enableMixedLod !== false | ||
) { | ||
const mesh = createGroundPlane( | ||
tile, | ||
createTexCoords, | ||
receiveShadow, | ||
materialOrColor, | ||
createMultiLod | ||
); | ||
mesh.receiveShadow = receiveShadow; | ||
mesh.renderOrder = renderOrder; | ||
registerTileObject(tile, mesh, GeometryKind.Background, { pickable: false }); | ||
tile.objects.push(mesh); | ||
} | ||
|
||
function createGroundPlane( | ||
tile: Tile, | ||
createTexCoords: boolean, | ||
receiveShadow: boolean, | ||
materialOrColor: THREE.Material | THREE.Material[] | number, | ||
createMultiLod: boolean | ||
): THREE.Mesh { | ||
const { dataSource, projection } = tile; | ||
const sourceProjection = dataSource.getTilingScheme().projection; | ||
const shouldSubdivide = projection.type === ProjectionType.Spherical; | ||
const useLocalTargetCoords = !shouldSubdivide; | ||
|
||
const material = | ||
typeof materialOrColor === "number" | ||
? createGroundPlaneMaterial( | ||
new THREE.Color(materialOrColor), | ||
receiveShadow, | ||
projection.type === ProjectionType.Spherical | ||
) | ||
: materialOrColor; | ||
|
||
const geometry = createGroundPlaneGeometry( | ||
tile, | ||
useLocalTargetCoords, | ||
createTexCoords, | ||
receiveShadow | ||
); | ||
|
||
if (!shouldSubdivide) { | ||
return new THREE.Mesh(geometry, material); | ||
} | ||
|
||
const geometries: THREE.BufferGeometry[] = []; | ||
const sphericalModifier = new SphericalGeometrySubdivisionModifier( | ||
THREE.MathUtils.degToRad(10), | ||
sourceProjection | ||
); | ||
|
||
if (!createMultiLod) { | ||
sphericalModifier.modify(geometry); | ||
toLocalTargetCoords(geometry, sourceProjection, tile); | ||
|
||
return new THREE.Mesh(geometry, material); | ||
} | ||
|
||
// Use a [[LodMesh]] to adapt tesselation of tile depending on zoom level | ||
for (let zoomLevelOffset = 0; zoomLevelOffset < 4; ++zoomLevelOffset) { | ||
const subdivision = Math.pow(2, zoomLevelOffset); | ||
const zoomLevelGeometry = geometry.clone(); | ||
if (subdivision > 1) { | ||
const edgeModifier = new EdgeLengthGeometrySubdivisionModifier( | ||
subdivision, | ||
tile.geoBox, | ||
SubdivisionMode.All, | ||
sourceProjection | ||
); | ||
edgeModifier.modify(zoomLevelGeometry); | ||
} | ||
sphericalModifier.modify(zoomLevelGeometry); | ||
toLocalTargetCoords(zoomLevelGeometry, sourceProjection, tile); | ||
geometries.push(zoomLevelGeometry); | ||
} | ||
return new LodMesh(geometries, material); | ||
} | ||
|
||
function toLocalTargetCoords(geom: THREE.BufferGeometry, srcProjection: Projection, tile: Tile) { | ||
const attr = geom.getAttribute("position") as THREE.BufferAttribute; | ||
const oldArray = attr.array as Float64Array; | ||
// Convert to single precision before rendering (WebGL does not support double | ||
// precision). | ||
const newArray = new Float32Array(oldArray.length); | ||
for (let i = 0; i < attr.array.length; i += 1) { | ||
tmpV.fromBufferAttribute(attr, i); | ||
tile.projection.reprojectPoint(srcProjection, tmpV, tmpV).sub(tile.center); | ||
tmpV.toArray(newArray, i * 3); | ||
} | ||
attr.array = newArray; | ||
attr.needsUpdate = true; | ||
} | ||
|
||
function createGroundPlaneGeometry( | ||
tile: Tile, | ||
useLocalTargetCoords: boolean, | ||
createTexCoords: boolean, | ||
receiveShadow: boolean | ||
): THREE.BufferGeometry { | ||
const { dataSource, projection } = tile; | ||
const sourceProjection = dataSource.getTilingScheme().projection; | ||
const tmpV = new THREE.Vector3(); | ||
|
||
const geometry = new THREE.BufferGeometry(); | ||
const tileCorners = projectTilePlaneCorners(tile, sourceProjection); | ||
const cornersArray = [tileCorners.sw, tileCorners.se, tileCorners.nw, tileCorners.ne]; | ||
if (useLocalTargetCoords) { | ||
for (const corner of cornersArray) { | ||
projection.reprojectPoint(sourceProjection, corner, corner).sub(tile.center); | ||
} | ||
} | ||
// Use 64bits floats for world coordinates to avoid precision issues on coordinate | ||
// tranformations. The array must be converted to single precision before rendering. | ||
const bufferArray = useLocalTargetCoords ? new Float32Array(12) : new Float64Array(12); | ||
const posAttr = new THREE.BufferAttribute(bufferArray, 3).copyVector3sArray(cornersArray); | ||
geometry.setAttribute("position", posAttr); | ||
|
||
if (receiveShadow) { | ||
// Webmercator needs to have it negated to work correctly. | ||
sourceProjection.surfaceNormal(tileCorners.sw, tmpV).negate(); | ||
const normAttr = new THREE.BufferAttribute(new Float32Array(12), 3).copyVector3sArray( | ||
Array(4).fill(tmpV) | ||
); | ||
geometry.setAttribute("normal", normAttr); | ||
} | ||
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array([0, 1, 2, 2, 1, 3]), 1)); | ||
|
||
if (createTexCoords) { | ||
const uvAttr = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), 2); | ||
geometry.setAttribute("uv", uvAttr); | ||
} | ||
|
||
return geometry; | ||
} | ||
|
||
function createGroundPlaneMaterial( | ||
color: THREE.Color, | ||
receiveShadow: boolean, | ||
depthWrite: boolean | ||
): THREE.Material { | ||
if (receiveShadow) { | ||
return new MapMeshStandardMaterial({ | ||
color, | ||
visible: true, | ||
depthWrite, | ||
removeDiffuseLight: true | ||
}); | ||
} else { | ||
return new MapMeshBasicMaterial({ | ||
color, | ||
visible: true, | ||
depthWrite | ||
}); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
@here/harp-mapview/lib/geometry/ProjectTilePlaneCorners.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright (C) 2017-2020 HERE Europe B.V. | ||
* Licensed under Apache 2.0, see full license in LICENSE | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { GeoCoordinates, Projection } from "@here/harp-geoutils"; | ||
import * as THREE from "three"; | ||
|
||
import { Tile } from "../Tile"; | ||
|
||
/** | ||
* Coordinates of a tile's corners. | ||
* @internal | ||
* @hidden | ||
*/ | ||
export interface TileCorners { | ||
se: THREE.Vector3; | ||
sw: THREE.Vector3; | ||
ne: THREE.Vector3; | ||
nw: THREE.Vector3; | ||
} | ||
|
||
/** | ||
* Returns the corners of the tile's geo bounding box projected using a given projection. | ||
* @param tile - The tile whose corners will be projected. | ||
* @param projection - The projection to be used. | ||
* @returns The projected tile corners. | ||
* @internal | ||
* @hidden | ||
*/ | ||
export function projectTilePlaneCorners(tile: Tile, projection: Projection): TileCorners { | ||
const { east, west, north, south } = tile.geoBox; | ||
const sw = projection.projectPoint(new GeoCoordinates(south, west), new THREE.Vector3()); | ||
const se = projection.projectPoint(new GeoCoordinates(south, east), new THREE.Vector3()); | ||
const nw = projection.projectPoint(new GeoCoordinates(north, west), new THREE.Vector3()); | ||
const ne = projection.projectPoint(new GeoCoordinates(north, east), new THREE.Vector3()); | ||
return { sw, se, nw, ne }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright (C) 2017-2020 HERE Europe B.V. | ||
* Licensed under Apache 2.0, see full license in LICENSE | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { GeometryKind, GeometryKindSet } from "@here/harp-datasource-protocol"; | ||
import * as THREE from "three"; | ||
|
||
import { MapObjectAdapter, MapObjectAdapterParams } from "../MapObjectAdapter"; | ||
import { Tile } from "../Tile"; | ||
|
||
/** | ||
* Adds a THREE object to the root of the tile and register [[MapObjectAdapter]]. | ||
* | ||
* Sets the owning tiles datasource.name and the `tileKey` in the `userData` property of the | ||
* object, such that the tile it belongs to can be identified during picking. | ||
* | ||
* @param tile - The {@link Tile} to add the object to. | ||
* @param object - The object to add to the root of the tile. | ||
* @param geometryKind - The kind of object. Can be used for filtering. | ||
* @param mapAdapterParams - additional parameters for [[MapObjectAdapter]] | ||
*/ | ||
export function registerTileObject( | ||
tile: Tile, | ||
object: THREE.Object3D, | ||
geometryKind: GeometryKind | GeometryKindSet | undefined, | ||
mapAdapterParams?: MapObjectAdapterParams | ||
) { | ||
const kind = | ||
geometryKind instanceof Set | ||
? Array.from((geometryKind as GeometryKindSet).values()) | ||
: Array.isArray(geometryKind) | ||
? geometryKind | ||
: [geometryKind]; | ||
|
||
MapObjectAdapter.create(object, { | ||
dataSource: tile.dataSource, | ||
kind, | ||
level: tile.tileKey.level, | ||
...mapAdapterParams | ||
}); | ||
|
||
// TODO legacy fields, encoded directly in `userData to be removed | ||
if (object.userData === undefined) { | ||
object.userData = {}; | ||
} | ||
|
||
const userData = object.userData; | ||
userData.tileKey = tile.tileKey; | ||
userData.dataSource = tile.dataSource.name; | ||
|
||
userData.kind = kind; | ||
|
||
// Force a visibility check of all objects. | ||
tile.resetVisibilityCounter(); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to convert the Float64Array back to Float32Array also in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If !shouldSubdivide, then useLocalTargetCoords == true. You can see in createPlaneGeometry() that in that case whe use FLoat32Array:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not such a fan of having the useLocalTargetCoords control the transformation to local coordinates in the
createPlaneGeometry
function, I think it makes it easier to read when this transformation is explicitly outside of the function, like it was previously withmoveTileCenter
, so that it isn't responsible for too many things. But because @FraukeF already approve, I won't block it.