You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

771 lines
18 KiB
JavaScript

import { Cartesian2 } from "../../Source/Cesium.js";
import { Cartesian3 } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { HeightmapTessellator } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { WebMercatorProjection } from "../../Source/Cesium.js";
describe("Scene/HeightmapTessellator", function () {
it("throws when heightmap is not provided", function () {
expect(function () {
HeightmapTessellator.computeVertices();
}).toThrowDeveloperError();
expect(function () {
HeightmapTessellator.computeVertices({
width: 2,
height: 2,
vertices: [],
nativeRectangle: {
west: 10.0,
south: 20.0,
east: 20.0,
north: 30.0,
},
skirtHeight: 10.0,
});
}).toThrowDeveloperError();
});
it("throws when width or height is not provided", function () {
expect(function () {
HeightmapTessellator.computeVertices({
heightmap: [1.0, 2.0, 3.0, 4.0],
height: 2,
vertices: [],
nativeRectangle: {
west: 10.0,
south: 20.0,
east: 20.0,
north: 30.0,
},
skirtHeight: 10.0,
});
}).toThrowDeveloperError();
expect(function () {
HeightmapTessellator.computeVertices({
heightmap: [1.0, 2.0, 3.0, 4.0],
width: 2,
vertices: [],
nativeRectangle: {
west: 10.0,
south: 20.0,
east: 20.0,
north: 30.0,
},
skirtHeight: 10.0,
});
}).toThrowDeveloperError();
});
it("throws when nativeRectangle is not provided", function () {
expect(function () {
HeightmapTessellator.computeVertices({
heightmap: [1.0, 2.0, 3.0, 4.0],
width: 2,
height: 2,
vertices: [],
skirtHeight: 10.0,
});
}).toThrowDeveloperError();
});
it("throws when skirtHeight is not provided", function () {
expect(function () {
HeightmapTessellator.computeVertices({
heightmap: [1.0, 2.0, 3.0, 4.0],
width: 2,
height: 2,
vertices: [],
nativeRectangle: {
west: 10.0,
south: 20.0,
east: 20.0,
north: 30.0,
},
});
}).toThrowDeveloperError();
});
function checkExpectedVertex(
nativeRectangle,
i,
j,
width,
height,
index,
isEdge,
vertices,
heightmap,
ellipsoid,
skirtHeight
) {
var latitude = CesiumMath.lerp(
nativeRectangle.north,
nativeRectangle.south,
j / (height - 1)
);
latitude = CesiumMath.toRadians(latitude);
var longitude = CesiumMath.lerp(
nativeRectangle.west,
nativeRectangle.east,
i / (width - 1)
);
longitude = CesiumMath.toRadians(longitude);
var heightSample = heightmap[j * width + i];
if (isEdge) {
heightSample -= skirtHeight;
}
var expectedVertexPosition = ellipsoid.cartographicToCartesian({
longitude: longitude,
latitude: latitude,
height: heightSample,
});
index = index * 6;
var vertexPosition = new Cartesian3(
vertices[index],
vertices[index + 1],
vertices[index + 2]
);
expect(vertexPosition).toEqualEpsilon(expectedVertexPosition, 1.0);
expect(vertices[index + 3]).toEqual(heightSample);
expect(vertices[index + 4]).toEqualEpsilon(
i / (width - 1),
CesiumMath.EPSILON7
);
expect(vertices[index + 5]).toEqualEpsilon(
1.0 - j / (height - 1),
CesiumMath.EPSILON7
);
}
function checkExpectedQuantizedVertex(
nativeRectangle,
i,
j,
width,
height,
index,
isEdge,
vertices,
heightmap,
ellipsoid,
skirtHeight,
encoding
) {
var latitude = CesiumMath.lerp(
nativeRectangle.north,
nativeRectangle.south,
j / (height - 1)
);
latitude = CesiumMath.toRadians(latitude);
var longitude = CesiumMath.lerp(
nativeRectangle.west,
nativeRectangle.east,
i / (width - 1)
);
longitude = CesiumMath.toRadians(longitude);
var heightSample = heightmap[j * width + i];
if (isEdge) {
heightSample -= skirtHeight;
}
var expectedVertexPosition = ellipsoid.cartographicToCartesian({
longitude: longitude,
latitude: latitude,
height: heightSample,
});
expect(encoding.decodePosition(vertices, index)).toEqualEpsilon(
expectedVertexPosition,
1.0
);
}
it("creates mesh without skirt", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
width: width,
height: height,
skirtHeight: 0.0,
nativeRectangle: {
west: 10.0,
south: 30.0,
east: 20.0,
north: 40.0,
},
rectangle: new Rectangle(
CesiumMath.toRadians(10.0),
CesiumMath.toRadians(30.0),
CesiumMath.toRadians(20.0),
CesiumMath.toRadians(40.0)
),
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var nativeRectangle = options.nativeRectangle;
var index = 0;
for (var j = 0; j < height; ++j) {
for (var i = 0; i < width; ++i) {
checkExpectedVertex(
nativeRectangle,
i,
j,
width,
height,
index++,
false,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
}
});
it("creates mesh with skirt", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
width: width,
height: height,
skirtHeight: 10.0,
nativeRectangle: {
west: 10.0,
east: 20.0,
south: 30.0,
north: 40.0,
},
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var nativeRectangle = options.nativeRectangle;
var i, j;
var index = 0;
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
checkExpectedVertex(
nativeRectangle,
i,
j,
width,
height,
index++,
false,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
}
// Heightmap is expected to be ordered from west to east and north to south,
// so flip i and j depending on how skirts are arranged.
for (j = 0; j < height; ++j) {
// West edge goes from south to north
checkExpectedVertex(
nativeRectangle,
0,
height - 1 - j,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
for (i = 0; i < height; ++i) {
// South edge goes from east to west
checkExpectedVertex(
nativeRectangle,
width - 1 - i,
height - 1,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
for (j = 0; j < height; ++j) {
// East edge goes from north to south
checkExpectedVertex(
nativeRectangle,
width - 1,
j,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
for (i = 0; i < height; ++i) {
// North edge goes from west to east
checkExpectedVertex(
nativeRectangle,
i,
0,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight
);
}
});
it("creates quantized mesh", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
width: width,
height: height,
skirtHeight: 10.0,
nativeRectangle: {
west: 0.01,
east: 0.02,
south: 0.01,
north: 0.02,
},
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var nativeRectangle = options.nativeRectangle;
var i, j;
var index = 0;
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
checkExpectedQuantizedVertex(
nativeRectangle,
i,
j,
width,
height,
index++,
false,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight,
results.encoding
);
}
}
// Heightmap is expected to be ordered from west to east and north to south,
// so flip i and j depending on how skirts are arranged.
for (j = 0; j < height; ++j) {
// West edge goes from south to north
checkExpectedQuantizedVertex(
nativeRectangle,
0,
height - 1 - j,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight,
results.encoding
);
}
for (i = 0; i < height; ++i) {
// South edge goes from east to west
checkExpectedQuantizedVertex(
nativeRectangle,
width - 1 - i,
height - 1,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight,
results.encoding
);
}
for (j = 0; j < height; ++j) {
// East edge goes from north to south
checkExpectedQuantizedVertex(
nativeRectangle,
width - 1,
j,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight,
results.encoding
);
}
for (i = 0; i < height; ++i) {
// North edge goes from west to east
checkExpectedQuantizedVertex(
nativeRectangle,
i,
0,
width,
height,
index++,
true,
vertices,
options.heightmap,
ellipsoid,
options.skirtHeight,
results.encoding
);
}
});
it("tessellates web mercator heightmaps", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
width: width,
height: height,
skirtHeight: 0.0,
nativeRectangle: {
west: 1000000.0,
east: 2000000.0,
south: 3000000.0,
north: 4000000.0,
},
isGeographic: false,
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var projection = new WebMercatorProjection(ellipsoid);
var nativeRectangle = options.nativeRectangle;
var geographicSouthwest = projection.unproject(
new Cartesian2(nativeRectangle.west, nativeRectangle.south)
);
var geographicNortheast = projection.unproject(
new Cartesian2(nativeRectangle.east, nativeRectangle.north)
);
for (var j = 0; j < height; ++j) {
var y = CesiumMath.lerp(
nativeRectangle.north,
nativeRectangle.south,
j / (height - 1)
);
for (var i = 0; i < width; ++i) {
var x = CesiumMath.lerp(
nativeRectangle.west,
nativeRectangle.east,
i / (width - 1)
);
var latLon = projection.unproject(new Cartesian2(x, y));
var longitude = latLon.longitude;
var latitude = latLon.latitude;
var heightSample = options.heightmap[j * width + i];
var expectedVertexPosition = ellipsoid.cartographicToCartesian({
longitude: longitude,
latitude: latitude,
height: heightSample,
});
var index = (j * width + i) * 6;
var vertexPosition = new Cartesian3(
vertices[index],
vertices[index + 1],
vertices[index + 2]
);
var expectedU =
(longitude - geographicSouthwest.longitude) /
(geographicNortheast.longitude - geographicSouthwest.longitude);
var expectedV =
(latitude - geographicSouthwest.latitude) /
(geographicNortheast.latitude - geographicSouthwest.latitude);
expect(vertexPosition).toEqualEpsilon(expectedVertexPosition, 1.0);
expect(vertices[index + 3]).toEqual(heightSample);
expect(vertices[index + 4]).toEqualEpsilon(
expectedU,
CesiumMath.EPSILON7
);
expect(vertices[index + 5]).toEqualEpsilon(
expectedV,
CesiumMath.EPSILON7
);
}
}
});
it("supports multi-element little endian heights", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [
1.0,
2.0,
100.0,
3.0,
4.0,
100.0,
5.0,
6.0,
100.0,
7.0,
8.0,
100.0,
9.0,
10.0,
100.0,
11.0,
12.0,
100.0,
13.0,
14.0,
100.0,
15.0,
16.0,
100.0,
17.0,
18.0,
100.0,
],
width: width,
height: height,
skirtHeight: 0.0,
nativeRectangle: {
west: 10.0,
south: 30.0,
east: 20.0,
north: 40.0,
},
rectangle: new Rectangle(
CesiumMath.toRadians(10.0),
CesiumMath.toRadians(30.0),
CesiumMath.toRadians(20.0),
CesiumMath.toRadians(40.0)
),
structure: {
stride: 3,
elementsPerHeight: 2,
elementMultiplier: 10,
},
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var nativeRectangle = options.nativeRectangle;
for (var j = 0; j < height; ++j) {
var latitude = CesiumMath.lerp(
nativeRectangle.north,
nativeRectangle.south,
j / (height - 1)
);
latitude = CesiumMath.toRadians(latitude);
for (var i = 0; i < width; ++i) {
var longitude = CesiumMath.lerp(
nativeRectangle.west,
nativeRectangle.east,
i / (width - 1)
);
longitude = CesiumMath.toRadians(longitude);
var heightSampleIndex = (j * width + i) * options.structure.stride;
var heightSample =
options.heightmap[heightSampleIndex] +
options.heightmap[heightSampleIndex + 1] * 10.0;
var expectedVertexPosition = ellipsoid.cartographicToCartesian({
longitude: longitude,
latitude: latitude,
height: heightSample,
});
var index = (j * width + i) * 6;
var vertexPosition = new Cartesian3(
vertices[index],
vertices[index + 1],
vertices[index + 2]
);
expect(vertexPosition).toEqualEpsilon(expectedVertexPosition, 1.0);
expect(vertices[index + 3]).toEqual(heightSample);
expect(vertices[index + 4]).toEqualEpsilon(
i / (width - 1),
CesiumMath.EPSILON7
);
expect(vertices[index + 5]).toEqualEpsilon(
1.0 - j / (height - 1),
CesiumMath.EPSILON7
);
}
}
});
it("supports multi-element big endian heights", function () {
var width = 3;
var height = 3;
var options = {
heightmap: [
1.0,
2.0,
100.0,
3.0,
4.0,
100.0,
5.0,
6.0,
100.0,
7.0,
8.0,
100.0,
9.0,
10.0,
100.0,
11.0,
12.0,
100.0,
13.0,
14.0,
100.0,
15.0,
16.0,
100.0,
17.0,
18.0,
100.0,
],
width: width,
height: height,
skirtHeight: 0.0,
nativeRectangle: {
west: 10.0,
south: 30.0,
east: 20.0,
north: 40.0,
},
rectangle: new Rectangle(
CesiumMath.toRadians(10.0),
CesiumMath.toRadians(30.0),
CesiumMath.toRadians(20.0),
CesiumMath.toRadians(40.0)
),
structure: {
stride: 3,
elementsPerHeight: 2,
elementMultiplier: 10,
isBigEndian: true,
},
};
var results = HeightmapTessellator.computeVertices(options);
var vertices = results.vertices;
var ellipsoid = Ellipsoid.WGS84;
var nativeRectangle = options.nativeRectangle;
for (var j = 0; j < height; ++j) {
var latitude = CesiumMath.lerp(
nativeRectangle.north,
nativeRectangle.south,
j / (height - 1)
);
latitude = CesiumMath.toRadians(latitude);
for (var i = 0; i < width; ++i) {
var longitude = CesiumMath.lerp(
nativeRectangle.west,
nativeRectangle.east,
i / (width - 1)
);
longitude = CesiumMath.toRadians(longitude);
var heightSampleIndex = (j * width + i) * options.structure.stride;
var heightSample =
options.heightmap[heightSampleIndex] * 10.0 +
options.heightmap[heightSampleIndex + 1];
var expectedVertexPosition = ellipsoid.cartographicToCartesian({
longitude: longitude,
latitude: latitude,
height: heightSample,
});
var index = (j * width + i) * 6;
var vertexPosition = new Cartesian3(
vertices[index],
vertices[index + 1],
vertices[index + 2]
);
expect(vertexPosition).toEqualEpsilon(expectedVertexPosition, 1.0);
expect(vertices[index + 3]).toEqual(heightSample);
expect(vertices[index + 4]).toEqualEpsilon(
i / (width - 1),
CesiumMath.EPSILON7
);
expect(vertices[index + 5]).toEqualEpsilon(
1.0 - j / (height - 1),
CesiumMath.EPSILON7
);
}
}
});
});