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
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
|
|
);
|
|
}
|
|
}
|
|
});
|
|
});
|