itowns 2.44.3-next.21 → 2.44.3-next.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/debug.js +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/itowns.js +1 -1
- package/dist/itowns.js.map +1 -1
- package/examples/layers/JSONLayers/GeoidMNT.json +3 -1
- package/examples/source_file_geojson_3d.html +0 -1
- package/examples/source_stream_wfs_raster.html +0 -7
- package/lib/Converter/Feature2Mesh.js +1 -2
- package/lib/Converter/Feature2Texture.js +3 -1
- package/lib/Converter/convertToTile.js +3 -3
- package/lib/Converter/textureConverter.js +1 -2
- package/lib/Core/Feature.js +1 -2
- package/lib/Core/Geographic/Coordinates.js +1 -1
- package/lib/Core/Geographic/Crs.js +114 -144
- package/lib/Core/Geographic/Extent.js +2 -5
- package/lib/Core/Geographic/GeoidGrid.js +1 -1
- package/lib/Core/Prefab/Globe/Atmosphere.js +4 -2
- package/lib/Core/Prefab/Globe/GlobeLayer.js +21 -14
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +111 -0
- package/lib/Core/Prefab/GlobeView.js +2 -3
- package/lib/Core/Prefab/Planar/PlanarLayer.js +16 -10
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +43 -43
- package/lib/Core/Prefab/TileBuilder.js +27 -32
- package/lib/Core/Prefab/computeBufferTileGeometry.js +189 -130
- package/lib/Core/Tile/Tile.js +4 -4
- package/lib/Core/Tile/TileGrid.js +7 -10
- package/lib/Core/TileGeometry.js +112 -28
- package/lib/Core/TileMesh.js +1 -2
- package/lib/Core/View.js +2 -2
- package/lib/Layer/C3DTilesLayer.js +7 -4
- package/lib/Layer/ColorLayer.js +35 -9
- package/lib/Layer/CopcLayer.js +5 -0
- package/lib/Layer/ElevationLayer.js +39 -7
- package/lib/Layer/EntwinePointTileLayer.js +12 -5
- package/lib/Layer/FeatureGeometryLayer.js +20 -6
- package/lib/Layer/GeometryLayer.js +42 -11
- package/lib/Layer/LabelLayer.js +11 -5
- package/lib/Layer/Layer.js +83 -57
- package/lib/Layer/OGC3DTilesLayer.js +3 -2
- package/lib/Layer/OrientedImageLayer.js +12 -4
- package/lib/Layer/PointCloudLayer.js +69 -23
- package/lib/Layer/Potree2Layer.js +7 -2
- package/lib/Layer/PotreeLayer.js +8 -3
- package/lib/Layer/RasterLayer.js +12 -2
- package/lib/Layer/TiledGeometryLayer.js +69 -13
- package/lib/Main.js +1 -1
- package/lib/Provider/Fetcher.js +5 -1
- package/lib/Renderer/OBB.js +11 -13
- package/lib/Renderer/RasterTile.js +1 -2
- package/lib/Source/FileSource.js +1 -2
- package/lib/Source/Source.js +1 -1
- package/lib/Source/TMSSource.js +2 -3
- package/lib/Source/WFSSource.js +1 -2
- package/package.json +3 -3
- package/lib/Core/Prefab/Globe/BuilderEllipsoidTile.js +0 -110
|
@@ -5,7 +5,6 @@ import Coordinates from "../Geographic/Coordinates.js";
|
|
|
5
5
|
import GlobeLayer from "./Globe/GlobeLayer.js";
|
|
6
6
|
import Atmosphere from "./Globe/Atmosphere.js";
|
|
7
7
|
import CameraUtils from "../../Utils/CameraUtils.js";
|
|
8
|
-
import CRS from "../Geographic/Crs.js";
|
|
9
8
|
import { ellipsoidSizes } from "../Math/Ellipsoid.js";
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -129,11 +128,11 @@ class GlobeView extends View {
|
|
|
129
128
|
return Promise.reject(new Error('Add Layer type object'));
|
|
130
129
|
}
|
|
131
130
|
if (layer.isColorLayer) {
|
|
132
|
-
if (!this.tileLayer.tileMatrixSets.includes(
|
|
131
|
+
if (!this.tileLayer.tileMatrixSets.includes(layer.source.crs)) {
|
|
133
132
|
return layer._reject(`Only ${this.tileLayer.tileMatrixSets} tileMatrixSet are currently supported for color layers`);
|
|
134
133
|
}
|
|
135
134
|
} else if (layer.isElevationLayer) {
|
|
136
|
-
if (
|
|
135
|
+
if (layer.source.crs !== this.tileLayer.tileMatrixSets[0]) {
|
|
137
136
|
return layer._reject(`Only ${this.tileLayer.tileMatrixSets[0]} tileMatrixSet is currently supported for elevation layers`);
|
|
138
137
|
}
|
|
139
138
|
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import TiledGeometryLayer from "../../../Layer/TiledGeometryLayer.js";
|
|
3
3
|
import { globalExtentTMS } from "../../Tile/TileGrid.js";
|
|
4
|
-
import
|
|
5
|
-
import PlanarTileBuilder from "./PlanarTileBuilder.js";
|
|
4
|
+
import { PlanarTileBuilder } from "./PlanarTileBuilder.js";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* @property {boolean} isPlanarLayer - Used to checkout whether this layer is a
|
|
9
8
|
* PlanarLayer. Default is true. You should not change this, as it is used
|
|
10
9
|
* internally for optimisation.
|
|
10
|
+
* @extends TiledGeometryLayer
|
|
11
11
|
*/
|
|
12
12
|
class PlanarLayer extends TiledGeometryLayer {
|
|
13
13
|
/**
|
|
14
14
|
* A {@link TiledGeometryLayer} to use with a {@link PlanarView}. It has
|
|
15
15
|
* specific method for updating and subdivising its grid.
|
|
16
16
|
*
|
|
17
|
-
* @extends TiledGeometryLayer
|
|
18
|
-
*
|
|
19
17
|
* @param {string} id - The id of the layer, that should be unique. It is
|
|
20
18
|
* not mandatory, but an error will be emitted if this layer is added a
|
|
21
19
|
* {@link View} that already has a layer going by that id.
|
|
@@ -35,19 +33,27 @@ class PlanarLayer extends TiledGeometryLayer {
|
|
|
35
33
|
*/
|
|
36
34
|
constructor(id, extent, object3d) {
|
|
37
35
|
let config = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
38
|
-
const
|
|
36
|
+
const {
|
|
37
|
+
minSubdivisionLevel = 0,
|
|
38
|
+
maxSubdivisionLevel = 5,
|
|
39
|
+
...tiledConfig
|
|
40
|
+
} = config;
|
|
41
|
+
const tileMatrixSets = [extent.crs];
|
|
39
42
|
if (!globalExtentTMS.get(extent.crs)) {
|
|
40
43
|
// Add new global extent for this new crs projection.
|
|
41
44
|
globalExtentTMS.set(extent.crs, extent);
|
|
42
45
|
}
|
|
43
|
-
|
|
44
|
-
super(id, object3d || new THREE.Group(), [extent], new PlanarTileBuilder({
|
|
46
|
+
const builder = new PlanarTileBuilder({
|
|
45
47
|
crs: extent.crs
|
|
46
|
-
})
|
|
48
|
+
});
|
|
49
|
+
super(id, object3d || new THREE.Group(), [extent], builder, {
|
|
50
|
+
tileMatrixSets,
|
|
51
|
+
...tiledConfig
|
|
52
|
+
});
|
|
47
53
|
this.isPlanarLayer = true;
|
|
48
54
|
this.extent = extent;
|
|
49
|
-
this.minSubdivisionLevel =
|
|
50
|
-
this.maxSubdivisionLevel =
|
|
55
|
+
this.minSubdivisionLevel = minSubdivisionLevel;
|
|
56
|
+
this.maxSubdivisionLevel = maxSubdivisionLevel;
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
export default PlanarLayer;
|
|
@@ -3,70 +3,70 @@ import Coordinates from "../../Geographic/Coordinates.js";
|
|
|
3
3
|
import Extent from "../../Geographic/Extent.js";
|
|
4
4
|
const quaternion = new THREE.Quaternion();
|
|
5
5
|
const center = new THREE.Vector3();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
|
|
7
|
+
/** Specialized parameters for the [PlanarTileBuilder]. */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* TileBuilder implementation for the purpose of generating planar
|
|
11
|
+
* tile arrangements.
|
|
12
|
+
*/
|
|
13
|
+
export class PlanarTileBuilder {
|
|
14
|
+
constructor(options) {
|
|
10
15
|
if (options.projection) {
|
|
11
|
-
console.warn('PlanarTileBuilder projection parameter is deprecated, use crs instead.');
|
|
12
|
-
options.crs
|
|
13
|
-
}
|
|
14
|
-
if (options.crs) {
|
|
15
|
-
this.crs = options.crs;
|
|
16
|
-
} else {
|
|
17
|
-
throw new Error('options.crs is mandatory for PlanarTileBuilder');
|
|
16
|
+
console.warn('PlanarTileBuilder projection parameter is deprecated,' + ' use crs instead.');
|
|
17
|
+
options.crs ??= options.projection;
|
|
18
18
|
}
|
|
19
|
-
this.
|
|
19
|
+
this._crs = options.crs;
|
|
20
|
+
this._transform = {
|
|
20
21
|
coords: new Coordinates('EPSG:4326', 0, 0),
|
|
21
22
|
position: new THREE.Vector3(),
|
|
22
23
|
normal: new THREE.Vector3(0, 0, 1)
|
|
23
24
|
};
|
|
24
|
-
this.
|
|
25
|
+
this._uvCount = options.uvCount ?? 1;
|
|
26
|
+
}
|
|
27
|
+
get uvCount() {
|
|
28
|
+
return this._uvCount;
|
|
29
|
+
}
|
|
30
|
+
get crs() {
|
|
31
|
+
return this._crs;
|
|
25
32
|
}
|
|
26
|
-
// prepare params
|
|
27
|
-
// init projected object -> params.projected
|
|
28
33
|
prepare(params) {
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
const newParams = params;
|
|
35
|
+
newParams.nbRow = 2 ** (params.level + 1.0);
|
|
36
|
+
newParams.coordinates = new Coordinates(this.crs);
|
|
37
|
+
return newParams;
|
|
31
38
|
}
|
|
32
|
-
|
|
33
|
-
// get center tile in cartesian 3D
|
|
34
39
|
center(extent) {
|
|
35
|
-
extent.center(this.
|
|
36
|
-
center.set(this.
|
|
40
|
+
extent.center(this._transform.coords);
|
|
41
|
+
center.set(this._transform.coords.x, this._transform.coords.y, 0);
|
|
37
42
|
return center;
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.tmp.position.set(params.projected.x, params.projected.y, 0);
|
|
43
|
-
return this.tmp.position;
|
|
44
|
+
vertexPosition(coordinates) {
|
|
45
|
+
this._transform.position.set(coordinates.x, coordinates.y, 0);
|
|
46
|
+
return this._transform.position;
|
|
44
47
|
}
|
|
45
|
-
|
|
46
|
-
// get normal for last vertex
|
|
47
48
|
vertexNormal() {
|
|
48
|
-
return this.
|
|
49
|
+
return this._transform.normal;
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
uProjecte(u, params) {
|
|
53
|
-
params.projected.x = params.extent.west + u * (params.extent.east - params.extent.west);
|
|
51
|
+
uProject(u, extent) {
|
|
52
|
+
return extent.west + u * (extent.east - extent.west);
|
|
54
53
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
vProjecte(v, params) {
|
|
58
|
-
params.projected.y = params.extent.south + v * (params.extent.north - params.extent.south);
|
|
54
|
+
vProject(v, extent) {
|
|
55
|
+
return extent.south + v * (extent.north - extent.south);
|
|
59
56
|
}
|
|
60
|
-
|
|
61
|
-
// compute
|
|
57
|
+
computeShareableExtent(extent) {
|
|
58
|
+
// compute shareable extent to pool the geometries
|
|
62
59
|
// the geometry in common extent is identical to the existing input
|
|
63
60
|
// with a translation
|
|
64
|
-
const sharableExtent = new Extent(extent.crs, 0, Math.abs(extent.west - extent.east), 0, Math.abs(extent.north - extent.south));
|
|
65
61
|
return {
|
|
66
|
-
|
|
62
|
+
shareableExtent: new Extent(extent.crs, {
|
|
63
|
+
west: 0,
|
|
64
|
+
east: Math.abs(extent.west - extent.east),
|
|
65
|
+
south: 0,
|
|
66
|
+
north: Math.abs(extent.north - extent.south)
|
|
67
|
+
}),
|
|
67
68
|
quaternion,
|
|
68
69
|
position: this.center(extent).clone()
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
|
-
}
|
|
72
|
-
export default PlanarTileBuilder;
|
|
72
|
+
}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
|
-
import TileGeometry from "../TileGeometry.js";
|
|
2
|
+
import { TileGeometry } from "../TileGeometry.js";
|
|
3
3
|
import Cache from "../Scheduler/Cache.js";
|
|
4
|
-
import computeBuffers from "./computeBufferTileGeometry.js";
|
|
4
|
+
import { computeBuffers } from "./computeBufferTileGeometry.js";
|
|
5
5
|
import OBB from "../../Renderer/OBB.js";
|
|
6
6
|
const cacheBuffer = new Map();
|
|
7
7
|
const cacheTile = new Cache();
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reference to a tile's extent with rigid transformations.
|
|
11
|
+
* Enables reuse of geometry, saving a bit of memory.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export function newTileGeometry(builder, params) {
|
|
9
15
|
const {
|
|
10
|
-
|
|
16
|
+
shareableExtent,
|
|
11
17
|
quaternion,
|
|
12
18
|
position
|
|
13
|
-
} = builder.
|
|
14
|
-
const south =
|
|
19
|
+
} = builder.computeShareableExtent(params.extent);
|
|
20
|
+
const south = shareableExtent.south.toFixed(6);
|
|
15
21
|
const bufferKey = `${builder.crs}_${params.disableSkirt ? 0 : 1}_${params.segments}`;
|
|
16
22
|
let promiseGeometry = cacheTile.get(south, params.level, bufferKey);
|
|
17
23
|
|
|
@@ -22,49 +28,38 @@ export default function newTileGeometry(builder, params) {
|
|
|
22
28
|
resolve = r;
|
|
23
29
|
});
|
|
24
30
|
cacheTile.set(promiseGeometry, south, params.level, bufferKey);
|
|
25
|
-
params.extent =
|
|
31
|
+
params.extent = shareableExtent;
|
|
26
32
|
params.center = builder.center(params.extent).clone();
|
|
27
|
-
// Read previously cached values (index and uv.wgs84 only
|
|
33
|
+
// Read previously cached values (index and uv.wgs84 only
|
|
34
|
+
// depend on the # of triangles)
|
|
28
35
|
let cachedBuffers = cacheBuffer.get(bufferKey);
|
|
29
36
|
params.buildIndexAndUv_0 = !cachedBuffers;
|
|
30
|
-
params.builder = builder;
|
|
31
37
|
let buffers;
|
|
32
38
|
try {
|
|
33
|
-
buffers = computeBuffers(params);
|
|
39
|
+
buffers = computeBuffers(builder, params);
|
|
34
40
|
} catch (e) {
|
|
35
41
|
return Promise.reject(e);
|
|
36
42
|
}
|
|
37
43
|
if (!cachedBuffers) {
|
|
38
44
|
cachedBuffers = {};
|
|
45
|
+
// We know the fields will exist due to the condition
|
|
46
|
+
// matching with the one for buildIndexAndUv_0.
|
|
47
|
+
// TODO: Make this brain-based check compiler-based.
|
|
39
48
|
cachedBuffers.index = new THREE.BufferAttribute(buffers.index, 1);
|
|
40
49
|
cachedBuffers.uv = new THREE.BufferAttribute(buffers.uvs[0], 2);
|
|
41
50
|
|
|
42
51
|
// Update cacheBuffer
|
|
43
52
|
cacheBuffer.set(bufferKey, cachedBuffers);
|
|
44
53
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
buffers.uvs[1] = new THREE.BufferAttribute(buffers.uvs[1], 1);
|
|
51
|
-
}
|
|
52
|
-
const geometry = new TileGeometry(params, buffers);
|
|
53
|
-
geometry.OBB = new OBB(geometry.boundingBox.min, geometry.boundingBox.max);
|
|
54
|
-
geometry._count = 0;
|
|
55
|
-
geometry.dispose = () => {
|
|
56
|
-
geometry._count--;
|
|
57
|
-
if (geometry._count <= 0) {
|
|
58
|
-
// To avoid remove index buffer and attribute buffer uv
|
|
59
|
-
// error un-bound buffer in webgl with VAO rendering.
|
|
60
|
-
// Could be removed if the attribute buffer deleting is
|
|
61
|
-
// taken into account in the buffer binding state (in THREE.WebGLBindingStates code).
|
|
62
|
-
geometry.index = null;
|
|
63
|
-
delete geometry.attributes.uv;
|
|
64
|
-
THREE.BufferGeometry.prototype.dispose.call(geometry);
|
|
65
|
-
cacheTile.delete(south, params.level, bufferKey);
|
|
66
|
-
}
|
|
54
|
+
const gpuBuffers = {
|
|
55
|
+
index: cachedBuffers.index,
|
|
56
|
+
uvs: [cachedBuffers.uv, ...(buffers.uvs[1] !== undefined ? [new THREE.BufferAttribute(buffers.uvs[1], 1)] : [])],
|
|
57
|
+
position: new THREE.BufferAttribute(buffers.position, 3),
|
|
58
|
+
normal: new THREE.BufferAttribute(buffers.normal, 3)
|
|
67
59
|
};
|
|
60
|
+
const geometry = new TileGeometry(builder, params, gpuBuffers);
|
|
61
|
+
geometry.OBB = new OBB(geometry.boundingBox.min, geometry.boundingBox.max);
|
|
62
|
+
geometry.initRefCount(cacheTile, [south, params.level, bufferKey]);
|
|
68
63
|
resolve(geometry);
|
|
69
64
|
return Promise.resolve({
|
|
70
65
|
geometry,
|
|
@@ -3,14 +3,52 @@ export function getBufferIndexSize(segments, noSkirt) {
|
|
|
3
3
|
const triangles = segments * segments * 2 + (noSkirt ? 0 : 4 * segments * 2);
|
|
4
4
|
return triangles * 3;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
function getUintArrayConstructor(highestValue) {
|
|
7
|
+
let picked = null;
|
|
8
|
+
if (highestValue < 2 ** 8) {
|
|
9
|
+
picked = Uint8Array;
|
|
10
|
+
} else if (highestValue < 2 ** 16) {
|
|
11
|
+
picked = Uint16Array;
|
|
12
|
+
} else if (highestValue < 2 ** 32) {
|
|
13
|
+
picked = Uint32Array;
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error('Value is too high');
|
|
16
|
+
}
|
|
17
|
+
return picked;
|
|
18
|
+
}
|
|
19
|
+
function allocateIndexBuffer(nVertex, nSeg, params) {
|
|
20
|
+
if (!params.buildIndexAndUv_0) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const indexBufferSize = getBufferIndexSize(nSeg, params.disableSkirt);
|
|
24
|
+
const indexConstructor = getUintArrayConstructor(nVertex);
|
|
25
|
+
const tileLen = indexBufferSize;
|
|
26
|
+
const skirtLen = 4 * nSeg;
|
|
27
|
+
const indexBuffer = new ArrayBuffer((
|
|
28
|
+
// Tile
|
|
29
|
+
tileLen
|
|
30
|
+
// Skirt
|
|
31
|
+
+ (params.disableSkirt ? 0 : skirtLen)) * indexConstructor.BYTES_PER_ELEMENT);
|
|
32
|
+
const index = new indexConstructor(indexBuffer);
|
|
33
|
+
const skirt = !params.disableSkirt ? index.subarray(tileLen, tileLen + skirtLen) : undefined;
|
|
34
|
+
return {
|
|
35
|
+
index,
|
|
36
|
+
skirt
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function allocateBuffers(nVertex, nSeg, builder, params) {
|
|
40
|
+
const {
|
|
41
|
+
index,
|
|
42
|
+
skirt
|
|
43
|
+
} = allocateIndexBuffer(nVertex, nSeg, params) ?? {};
|
|
44
|
+
return {
|
|
45
|
+
index,
|
|
46
|
+
skirt,
|
|
47
|
+
position: new Float32Array(nVertex * 3),
|
|
48
|
+
normal: new Float32Array(nVertex * 3),
|
|
49
|
+
// 2 UV set per tile: wgs84 (uv[0]) and pseudo-mercator (pm, uv[1])
|
|
50
|
+
// - wgs84: 1 texture per tile because tiles are using wgs84
|
|
51
|
+
// projection
|
|
14
52
|
// - pm: use multiple textures per tile.
|
|
15
53
|
// +-------------------------+
|
|
16
54
|
// | |
|
|
@@ -24,142 +62,157 @@ export default function computeBuffers(params) {
|
|
|
24
62
|
// +-------------------------+
|
|
25
63
|
// * u = wgs84.u
|
|
26
64
|
// * v = textureid + v in builder texture
|
|
27
|
-
uvs: []
|
|
65
|
+
uvs: [params.buildIndexAndUv_0 ? new Float32Array(nVertex * 2) : undefined, builder.computeExtraOffset !== undefined ? new Float32Array(nVertex) : undefined]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function computeUv0(uv, id, u, v) {
|
|
69
|
+
uv[id * 2 + 0] = u;
|
|
70
|
+
uv[id * 2 + 1] = v;
|
|
71
|
+
}
|
|
72
|
+
function initComputeUv1(value) {
|
|
73
|
+
return (uv, id) => {
|
|
74
|
+
uv[id] = value;
|
|
28
75
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
}
|
|
77
|
+
/** Compute buffers describing a tile according to a builder and its params. */
|
|
78
|
+
// TODO: Split this even further into subfunctions
|
|
79
|
+
export function computeBuffers(builder, params) {
|
|
80
|
+
// n seg, n+1 vert + <- skirt, n verts per side
|
|
81
|
+
// <---------------> / |
|
|
82
|
+
// +---+---+---+---+ |
|
|
83
|
+
// | / | / | / | / | | Vertices:
|
|
84
|
+
// +---+---+---+---+ - + tile = (n + 1)^2
|
|
85
|
+
// | / | / | / | / | | skirt = 4n
|
|
86
|
+
// +---+---+---+---+ - +
|
|
87
|
+
// | / | / | / | / | | Segments:
|
|
88
|
+
// +---+---+---+---+ - + tile = 2 * n * (n + 1) + n^2
|
|
89
|
+
// | / | / | / | / | | skirt = 2n * 4
|
|
90
|
+
// +---+---+---+---+ |
|
|
91
|
+
const nSeg = Math.max(2, params.segments);
|
|
92
|
+
const nVertex = nSeg + 1;
|
|
93
|
+
const nTileVertex = nVertex ** 2;
|
|
94
|
+
const nSkirtVertex = params.disableSkirt ? 0 : 4 * nSeg;
|
|
95
|
+
const nTotalVertex = nTileVertex + nSkirtVertex;
|
|
96
|
+
|
|
97
|
+
// Computer should combust before this happens
|
|
98
|
+
if (nTotalVertex > 2 ** 32) {
|
|
37
99
|
throw new Error('Tile segments count is too big');
|
|
38
100
|
}
|
|
39
|
-
outBuffers
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (params.buildIndexAndUv_0) {
|
|
48
|
-
if (nVertex < 2 ** 8) {
|
|
49
|
-
outBuffers.index = new Uint8Array(bufferIndexSize);
|
|
50
|
-
} else if (nVertex < 2 ** 16) {
|
|
51
|
-
outBuffers.index = new Uint16Array(bufferIndexSize);
|
|
52
|
-
} else if (nVertex < 2 ** 32) {
|
|
53
|
-
outBuffers.index = new Uint32Array(bufferIndexSize);
|
|
101
|
+
const outBuffers = allocateBuffers(nTotalVertex, nSeg, builder, params);
|
|
102
|
+
const computeUvs = [params.buildIndexAndUv_0 ? computeUv0 : () => {}];
|
|
103
|
+
params = builder.prepare(params);
|
|
104
|
+
for (let y = 0; y <= nSeg; y++) {
|
|
105
|
+
const v = y / nSeg;
|
|
106
|
+
params.coordinates.y = builder.vProject(v, params.extent);
|
|
107
|
+
if (builder.computeExtraOffset !== undefined) {
|
|
108
|
+
computeUvs[1] = initComputeUv1(builder.computeExtraOffset(params));
|
|
54
109
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const widthSegments = Math.max(2, Math.floor(nSeg) || 2);
|
|
62
|
-
const heightSegments = Math.max(2, Math.floor(nSeg) || 2);
|
|
63
|
-
let idVertex = 0;
|
|
64
|
-
const vertices = [];
|
|
65
|
-
let skirt = [];
|
|
66
|
-
const skirtEnd = [];
|
|
67
|
-
builder.prepare(params);
|
|
68
|
-
for (let y = 0; y <= heightSegments; y++) {
|
|
69
|
-
const verticesRow = [];
|
|
70
|
-
const v = y / heightSegments;
|
|
71
|
-
builder.vProjecte(v, params);
|
|
72
|
-
if (uvCount > 1) {
|
|
73
|
-
const u = builder.computeUvs[1](params);
|
|
74
|
-
computeUvs[1] = id => {
|
|
75
|
-
outBuffers.uvs[1][id] = u;
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
for (let x = 0; x <= widthSegments; x++) {
|
|
79
|
-
const u = x / widthSegments;
|
|
80
|
-
const id_m3 = idVertex * 3;
|
|
81
|
-
builder.uProjecte(u, params);
|
|
82
|
-
const vertex = builder.vertexPosition(params, params.projected);
|
|
83
|
-
const normal = builder.vertexNormal(params);
|
|
110
|
+
for (let x = 0; x <= nSeg; x++) {
|
|
111
|
+
const u = x / nSeg;
|
|
112
|
+
const id_m3 = (y * nVertex + x) * 3;
|
|
113
|
+
params.coordinates.x = builder.uProject(u, params.extent);
|
|
114
|
+
const vertex = builder.vertexPosition(params.coordinates);
|
|
115
|
+
const normal = builder.vertexNormal();
|
|
84
116
|
|
|
85
117
|
// move geometry to center world
|
|
86
118
|
vertex.sub(params.center);
|
|
87
119
|
|
|
88
120
|
// align normal to z axis
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
// HACK: this check style is not great
|
|
122
|
+
if ('quatNormalToZ' in params) {
|
|
123
|
+
const quat = params.quatNormalToZ;
|
|
124
|
+
vertex.applyQuaternion(quat);
|
|
125
|
+
normal.applyQuaternion(quat);
|
|
92
126
|
}
|
|
93
127
|
vertex.toArray(outBuffers.position, id_m3);
|
|
94
128
|
normal.toArray(outBuffers.normal, id_m3);
|
|
95
|
-
for (const computeUv of computeUvs) {
|
|
96
|
-
computeUv
|
|
97
|
-
|
|
98
|
-
if (!params.disableSkirt) {
|
|
99
|
-
if (y !== 0 && y !== heightSegments) {
|
|
100
|
-
if (x === widthSegments) {
|
|
101
|
-
skirt.push(idVertex);
|
|
102
|
-
} else if (x === 0) {
|
|
103
|
-
skirtEnd.push(idVertex);
|
|
104
|
-
}
|
|
129
|
+
for (const [index, computeUv] of computeUvs.entries()) {
|
|
130
|
+
if (computeUv !== undefined) {
|
|
131
|
+
computeUv(outBuffers.uvs[index], y * nVertex + x, u, v);
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
|
-
verticesRow.push(idVertex);
|
|
108
|
-
idVertex++;
|
|
109
|
-
}
|
|
110
|
-
vertices.push(verticesRow);
|
|
111
|
-
if (y === 0) {
|
|
112
|
-
skirt = skirt.concat(verticesRow);
|
|
113
|
-
} else if (y === heightSegments) {
|
|
114
|
-
skirt = skirt.concat(verticesRow.slice().reverse());
|
|
115
134
|
}
|
|
116
135
|
}
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
|
|
137
|
+
// Fill skirt index buffer
|
|
138
|
+
if (params.buildIndexAndUv_0 && !params.disableSkirt) {
|
|
139
|
+
for (let x = 0; x < nVertex; x++) {
|
|
140
|
+
// -------->
|
|
141
|
+
// 0---1---2
|
|
142
|
+
// | / | / | [0-9] = assign order
|
|
143
|
+
// +---+---+
|
|
144
|
+
// | / | / |
|
|
145
|
+
// +---+---+
|
|
146
|
+
outBuffers.skirt[x] = x;
|
|
147
|
+
// +---+---+
|
|
148
|
+
// | / | / | [0-9] = assign order
|
|
149
|
+
// +---+---x x = skipped for now
|
|
150
|
+
// | / | / |
|
|
151
|
+
// 0---1---2
|
|
152
|
+
// <--------
|
|
153
|
+
outBuffers.skirt[2 * nVertex - 2 + x] = nVertex ** 2 - (x + 1);
|
|
154
|
+
}
|
|
155
|
+
for (let y = 1; y < nVertex - 1; y++) {
|
|
156
|
+
// +---+---s |
|
|
157
|
+
// | / | / | | o = stored vertices
|
|
158
|
+
// +---+---o | s = already stored
|
|
159
|
+
// | / | / | |
|
|
160
|
+
// +---+---s v
|
|
161
|
+
outBuffers.skirt[nVertex - 1 + y] = y * nVertex + (nVertex - 1);
|
|
162
|
+
// ^ s---+---+
|
|
163
|
+
// | | / | / | o = stored vertices
|
|
164
|
+
// | o---+---+ s = already stored
|
|
165
|
+
// | | / | / |
|
|
166
|
+
// | s---+---+
|
|
167
|
+
outBuffers.skirt[3 * nVertex - 3 + y] = nVertex * (nVertex - 1 - y);
|
|
168
|
+
}
|
|
119
169
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
outBuffers.index[
|
|
124
|
-
|
|
170
|
+
|
|
171
|
+
/** Copy passed indices at the desired index of the output index buffer. */
|
|
172
|
+
function bufferizeTri(id, va, vb, vc) {
|
|
173
|
+
outBuffers.index[id + 0] = va;
|
|
174
|
+
outBuffers.index[id + 1] = vb;
|
|
175
|
+
outBuffers.index[id + 2] = vc;
|
|
125
176
|
}
|
|
126
|
-
let idVertex2 = 0;
|
|
127
177
|
if (params.buildIndexAndUv_0) {
|
|
128
|
-
for (let y = 0; y <
|
|
129
|
-
for (let x = 0; x <
|
|
130
|
-
const v1 =
|
|
131
|
-
const v2 =
|
|
132
|
-
const v3 =
|
|
133
|
-
const v4 =
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
for (let y = 0; y < nSeg; y++) {
|
|
179
|
+
for (let x = 0; x < nSeg; x++) {
|
|
180
|
+
const v1 = y * nVertex + (x + 1);
|
|
181
|
+
const v2 = y * nVertex + x;
|
|
182
|
+
const v3 = (y + 1) * nVertex + x;
|
|
183
|
+
const v4 = (y + 1) * nVertex + (x + 1);
|
|
184
|
+
const id = (y * nSeg + x) * 6;
|
|
185
|
+
bufferizeTri(id, /**/v4, v2, v1);
|
|
186
|
+
bufferizeTri(id + 3, v4, v3, v2);
|
|
136
187
|
}
|
|
137
188
|
}
|
|
138
189
|
}
|
|
139
|
-
const iStart = idVertex;
|
|
140
190
|
|
|
141
|
-
//
|
|
142
|
-
// The size of the skirt is now a ratio of the size of the tile.
|
|
143
|
-
// To be perfect it should depend on the real elevation delta but too heavy
|
|
144
|
-
|
|
145
|
-
|
|
191
|
+
// PERF: Beware skirt's size influences performance
|
|
192
|
+
// INFO: The size of the skirt is now a ratio of the size of the tile.
|
|
193
|
+
// To be perfect it should depend on the real elevation delta but too heavy
|
|
194
|
+
// to compute
|
|
195
|
+
if (params.buildIndexAndUv_0 && !params.disableSkirt) {
|
|
196
|
+
// We compute the actual size of tile segment to use later for
|
|
197
|
+
// the skirt.
|
|
146
198
|
const segmentSize = new THREE.Vector3().fromArray(outBuffers.position).distanceTo(new THREE.Vector3().fromArray(outBuffers.position, 3));
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
id
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
199
|
+
const buildSkirt = {
|
|
200
|
+
index: (id, v1, v2, v3, v4) => {
|
|
201
|
+
bufferizeTri(id, v1, v2, v3);
|
|
202
|
+
bufferizeTri(id + 3, v1, v3, v4);
|
|
203
|
+
return id + 6;
|
|
204
|
+
},
|
|
205
|
+
uv: (buf, idTo, idFrom) => {
|
|
206
|
+
buf[idTo * 2 + 0] = buf[idFrom * 2 + 0];
|
|
207
|
+
buf[idTo * 2 + 1] = buf[idFrom * 2 + 1];
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Alias for readability
|
|
212
|
+
const start = nTileVertex;
|
|
213
|
+
for (let i = 0; i < outBuffers.skirt.length; i++) {
|
|
214
|
+
const id = outBuffers.skirt[i];
|
|
215
|
+
const id_m3 = (start + i) * 3;
|
|
163
216
|
const id2_m3 = id * 3;
|
|
164
217
|
outBuffers.position[id_m3 + 0] = outBuffers.position[id2_m3 + 0] - outBuffers.normal[id2_m3 + 0] * segmentSize;
|
|
165
218
|
outBuffers.position[id_m3 + 1] = outBuffers.position[id2_m3 + 1] - outBuffers.normal[id2_m3 + 1] * segmentSize;
|
|
@@ -167,17 +220,23 @@ export default function computeBuffers(params) {
|
|
|
167
220
|
outBuffers.normal[id_m3 + 0] = outBuffers.normal[id2_m3 + 0];
|
|
168
221
|
outBuffers.normal[id_m3 + 1] = outBuffers.normal[id2_m3 + 1];
|
|
169
222
|
outBuffers.normal[id_m3 + 2] = outBuffers.normal[id2_m3 + 2];
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
outBuffers.uvs[1][
|
|
223
|
+
buildSkirt.uv(outBuffers.uvs[0], start + i, id);
|
|
224
|
+
if (outBuffers.uvs[1] !== undefined) {
|
|
225
|
+
outBuffers.uvs[1][start + i] = outBuffers.uvs[1][id];
|
|
173
226
|
}
|
|
174
|
-
const idf = (i + 1) % skirt.length;
|
|
175
|
-
const v2 =
|
|
176
|
-
const v3 = idf === 0 ?
|
|
177
|
-
const v4 = skirt[idf];
|
|
178
|
-
|
|
179
|
-
idVertex++;
|
|
227
|
+
const idf = (i + 1) % outBuffers.skirt.length;
|
|
228
|
+
const v2 = start + i;
|
|
229
|
+
const v3 = idf === 0 ? start : start + i + 1;
|
|
230
|
+
const v4 = outBuffers.skirt[idf];
|
|
231
|
+
buildSkirt.index(6 * nSeg ** 2 + i * 6, id, v2, v3, v4);
|
|
180
232
|
}
|
|
181
233
|
}
|
|
182
|
-
|
|
234
|
+
|
|
235
|
+
// Dropping skirt view
|
|
236
|
+
return {
|
|
237
|
+
index: outBuffers.index,
|
|
238
|
+
position: outBuffers.position,
|
|
239
|
+
uvs: outBuffers.uvs,
|
|
240
|
+
normal: outBuffers.normal
|
|
241
|
+
};
|
|
183
242
|
}
|