Add existing to tracked
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
import PrimitivePipeline from "../Scene/PrimitivePipeline.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
function combineGeometry(packedParameters, transferableObjects) {
|
||||
const parameters =
|
||||
PrimitivePipeline.unpackCombineGeometryParameters(packedParameters);
|
||||
const results = PrimitivePipeline.combineGeometry(parameters);
|
||||
return PrimitivePipeline.packCombineGeometryResults(
|
||||
results,
|
||||
transferableObjects,
|
||||
);
|
||||
}
|
||||
export default createTaskProcessorWorker(combineGeometry);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import BoxGeometry from "../Core/BoxGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
function createBoxGeometry(boxGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
boxGeometry = BoxGeometry.unpack(boxGeometry, offset);
|
||||
}
|
||||
return BoxGeometry.createGeometry(boxGeometry);
|
||||
}
|
||||
export default createBoxGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import BoxOutlineGeometry from "../Core/BoxOutlineGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
function createBoxOutlineGeometry(boxGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
boxGeometry = BoxOutlineGeometry.unpack(boxGeometry, offset);
|
||||
}
|
||||
return BoxOutlineGeometry.createGeometry(boxGeometry);
|
||||
}
|
||||
export default createBoxOutlineGeometry;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import CircleGeometry from "../Core/CircleGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createCircleGeometry(circleGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
circleGeometry = CircleGeometry.unpack(circleGeometry, offset);
|
||||
}
|
||||
circleGeometry._ellipseGeometry._center = Cartesian3.clone(
|
||||
circleGeometry._ellipseGeometry._center,
|
||||
);
|
||||
circleGeometry._ellipseGeometry._ellipsoid = Ellipsoid.clone(
|
||||
circleGeometry._ellipseGeometry._ellipsoid,
|
||||
);
|
||||
return CircleGeometry.createGeometry(circleGeometry);
|
||||
}
|
||||
export default createCircleGeometry;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import CircleOutlineGeometry from "../Core/CircleOutlineGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createCircleOutlineGeometry(circleGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
circleGeometry = CircleOutlineGeometry.unpack(circleGeometry, offset);
|
||||
}
|
||||
circleGeometry._ellipseGeometry._center = Cartesian3.clone(
|
||||
circleGeometry._ellipseGeometry._center,
|
||||
);
|
||||
circleGeometry._ellipseGeometry._ellipsoid = Ellipsoid.clone(
|
||||
circleGeometry._ellipseGeometry._ellipsoid,
|
||||
);
|
||||
return CircleOutlineGeometry.createGeometry(circleGeometry);
|
||||
}
|
||||
export default createCircleOutlineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import CoplanarPolygonGeometry from "../Core/CoplanarPolygonGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
function createCoplanarPolygonGeometry(polygonGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polygonGeometry = CoplanarPolygonGeometry.unpack(polygonGeometry, offset);
|
||||
}
|
||||
return CoplanarPolygonGeometry.createGeometry(polygonGeometry);
|
||||
}
|
||||
export default createCoplanarPolygonGeometry;
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
import CoplanarPolygonOutlineGeometry from "../Core/CoplanarPolygonOutlineGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createCoplanarPolygonOutlineGeometry(polygonGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polygonGeometry = CoplanarPolygonOutlineGeometry.unpack(
|
||||
polygonGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
polygonGeometry._ellipsoid = Ellipsoid.clone(polygonGeometry._ellipsoid);
|
||||
return CoplanarPolygonOutlineGeometry.createGeometry(polygonGeometry);
|
||||
}
|
||||
export default createCoplanarPolygonOutlineGeometry;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import CorridorGeometry from "../Core/CorridorGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createCorridorGeometry(corridorGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
corridorGeometry = CorridorGeometry.unpack(corridorGeometry, offset);
|
||||
}
|
||||
corridorGeometry._ellipsoid = Ellipsoid.clone(corridorGeometry._ellipsoid);
|
||||
return CorridorGeometry.createGeometry(corridorGeometry);
|
||||
}
|
||||
export default createCorridorGeometry;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import CorridorOutlineGeometry from "../Core/CorridorOutlineGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createCorridorOutlineGeometry(corridorOutlineGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
corridorOutlineGeometry = CorridorOutlineGeometry.unpack(
|
||||
corridorOutlineGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
corridorOutlineGeometry._ellipsoid = Ellipsoid.clone(
|
||||
corridorOutlineGeometry._ellipsoid,
|
||||
);
|
||||
return CorridorOutlineGeometry.createGeometry(corridorOutlineGeometry);
|
||||
}
|
||||
export default createCorridorOutlineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import CylinderGeometry from "../Core/CylinderGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
function createCylinderGeometry(cylinderGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
cylinderGeometry = CylinderGeometry.unpack(cylinderGeometry, offset);
|
||||
}
|
||||
return CylinderGeometry.createGeometry(cylinderGeometry);
|
||||
}
|
||||
export default createCylinderGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import CylinderOutlineGeometry from "../Core/CylinderOutlineGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
function createCylinderOutlineGeometry(cylinderGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
cylinderGeometry = CylinderOutlineGeometry.unpack(cylinderGeometry, offset);
|
||||
}
|
||||
return CylinderOutlineGeometry.createGeometry(cylinderGeometry);
|
||||
}
|
||||
export default createCylinderOutlineGeometry;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import EllipseGeometry from "../Core/EllipseGeometry.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createEllipseGeometry(ellipseGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
ellipseGeometry = EllipseGeometry.unpack(ellipseGeometry, offset);
|
||||
}
|
||||
ellipseGeometry._center = Cartesian3.clone(ellipseGeometry._center);
|
||||
ellipseGeometry._ellipsoid = Ellipsoid.clone(ellipseGeometry._ellipsoid);
|
||||
return EllipseGeometry.createGeometry(ellipseGeometry);
|
||||
}
|
||||
export default createEllipseGeometry;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import EllipseOutlineGeometry from "../Core/EllipseOutlineGeometry.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
|
||||
function createEllipseOutlineGeometry(ellipseGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
ellipseGeometry = EllipseOutlineGeometry.unpack(ellipseGeometry, offset);
|
||||
}
|
||||
ellipseGeometry._center = Cartesian3.clone(ellipseGeometry._center);
|
||||
ellipseGeometry._ellipsoid = Ellipsoid.clone(ellipseGeometry._ellipsoid);
|
||||
return EllipseOutlineGeometry.createGeometry(ellipseGeometry);
|
||||
}
|
||||
export default createEllipseOutlineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import EllipsoidGeometry from "../Core/EllipsoidGeometry.js";
|
||||
|
||||
function createEllipsoidGeometry(ellipsoidGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
ellipsoidGeometry = EllipsoidGeometry.unpack(ellipsoidGeometry, offset);
|
||||
}
|
||||
return EllipsoidGeometry.createGeometry(ellipsoidGeometry);
|
||||
}
|
||||
export default createEllipsoidGeometry;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import EllipsoidOutlineGeometry from "../Core/EllipsoidOutlineGeometry.js";
|
||||
|
||||
function createEllipsoidOutlineGeometry(ellipsoidGeometry, offset) {
|
||||
if (defined(ellipsoidGeometry.buffer, offset)) {
|
||||
ellipsoidGeometry = EllipsoidOutlineGeometry.unpack(
|
||||
ellipsoidGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
return EllipsoidOutlineGeometry.createGeometry(ellipsoidGeometry);
|
||||
}
|
||||
export default createEllipsoidOutlineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import FrustumGeometry from "../Core/FrustumGeometry.js";
|
||||
|
||||
function createFrustumGeometry(frustumGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
frustumGeometry = FrustumGeometry.unpack(frustumGeometry, offset);
|
||||
}
|
||||
return FrustumGeometry.createGeometry(frustumGeometry);
|
||||
}
|
||||
export default createFrustumGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import FrustumOutlineGeometry from "../Core/FrustumOutlineGeometry.js";
|
||||
|
||||
function createFrustumOutlineGeometry(frustumGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
frustumGeometry = FrustumOutlineGeometry.unpack(frustumGeometry, offset);
|
||||
}
|
||||
return FrustumOutlineGeometry.createGeometry(frustumGeometry);
|
||||
}
|
||||
export default createFrustumOutlineGeometry;
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import DeveloperError from "../Core/DeveloperError.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import PrimitivePipeline from "../Scene/PrimitivePipeline.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
/* global require */
|
||||
|
||||
const moduleCache = {};
|
||||
|
||||
async function getModule(moduleName, modulePath) {
|
||||
let module = moduleCache[modulePath] ?? moduleCache[moduleName];
|
||||
|
||||
if (defined(module)) {
|
||||
return module;
|
||||
}
|
||||
|
||||
if (defined(modulePath)) {
|
||||
// ignore moduleName and use the path to import
|
||||
if (typeof exports === "object") {
|
||||
// Use CommonJS-style require.
|
||||
module = require(modulePath);
|
||||
} else {
|
||||
// Use ESM-style dynamic import
|
||||
const result = await import(modulePath);
|
||||
module = result.default;
|
||||
}
|
||||
|
||||
moduleCache[modulePath] = module;
|
||||
return module;
|
||||
}
|
||||
|
||||
if (typeof exports === "object") {
|
||||
// Use CommonJS-style require.
|
||||
module = require(`Workers/${moduleName}`);
|
||||
} else {
|
||||
// Use ESM-style dynamic import
|
||||
const result = defined(modulePath)
|
||||
? await import(modulePath)
|
||||
: await import(`./${moduleName}.js`);
|
||||
module = result.default;
|
||||
}
|
||||
|
||||
moduleCache[moduleName] = module;
|
||||
return module;
|
||||
}
|
||||
|
||||
async function createGeometry(parameters, transferableObjects) {
|
||||
const subTasks = parameters.subTasks;
|
||||
const length = subTasks.length;
|
||||
const resultsOrPromises = new Array(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const task = subTasks[i];
|
||||
const geometry = task.geometry;
|
||||
const moduleName = task.moduleName;
|
||||
const modulePath = task.modulePath;
|
||||
|
||||
if (defined(moduleName) && defined(modulePath)) {
|
||||
throw new DeveloperError("Must only set moduleName or modulePath");
|
||||
}
|
||||
|
||||
if (defined(moduleName) || defined(modulePath)) {
|
||||
resultsOrPromises[i] = getModule(moduleName, modulePath).then(
|
||||
(createFunction) => createFunction(geometry, task.offset),
|
||||
);
|
||||
} else {
|
||||
// Already created geometry
|
||||
resultsOrPromises[i] = geometry;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(resultsOrPromises).then(function (results) {
|
||||
return PrimitivePipeline.packCreateGeometryResults(
|
||||
results,
|
||||
transferableObjects,
|
||||
);
|
||||
});
|
||||
}
|
||||
export default createTaskProcessorWorker(createGeometry);
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import GroundPolylineGeometry from "../Core/GroundPolylineGeometry.js";
|
||||
|
||||
function createGroundPolylineGeometry(groundPolylineGeometry, offset) {
|
||||
return ApproximateTerrainHeights.initialize().then(function () {
|
||||
if (defined(offset)) {
|
||||
groundPolylineGeometry = GroundPolylineGeometry.unpack(
|
||||
groundPolylineGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
return GroundPolylineGeometry.createGeometry(groundPolylineGeometry);
|
||||
});
|
||||
}
|
||||
export default createGroundPolylineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import PlaneGeometry from "../Core/PlaneGeometry.js";
|
||||
|
||||
function createPlaneGeometry(planeGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
planeGeometry = PlaneGeometry.unpack(planeGeometry, offset);
|
||||
}
|
||||
return PlaneGeometry.createGeometry(planeGeometry);
|
||||
}
|
||||
export default createPlaneGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import PlaneOutlineGeometry from "../Core/PlaneOutlineGeometry.js";
|
||||
|
||||
function createPlaneOutlineGeometry(planeGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
planeGeometry = PlaneOutlineGeometry.unpack(planeGeometry, offset);
|
||||
}
|
||||
return PlaneOutlineGeometry.createGeometry(planeGeometry);
|
||||
}
|
||||
export default createPlaneOutlineGeometry;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import PolygonGeometry from "../Core/PolygonGeometry.js";
|
||||
|
||||
function createPolygonGeometry(polygonGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polygonGeometry = PolygonGeometry.unpack(polygonGeometry, offset);
|
||||
}
|
||||
polygonGeometry._ellipsoid = Ellipsoid.clone(polygonGeometry._ellipsoid);
|
||||
return PolygonGeometry.createGeometry(polygonGeometry);
|
||||
}
|
||||
export default createPolygonGeometry;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import PolygonOutlineGeometry from "../Core/PolygonOutlineGeometry.js";
|
||||
|
||||
function createPolygonOutlineGeometry(polygonGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polygonGeometry = PolygonOutlineGeometry.unpack(polygonGeometry, offset);
|
||||
}
|
||||
polygonGeometry._ellipsoid = Ellipsoid.clone(polygonGeometry._ellipsoid);
|
||||
return PolygonOutlineGeometry.createGeometry(polygonGeometry);
|
||||
}
|
||||
export default createPolygonOutlineGeometry;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import PolylineGeometry from "../Core/PolylineGeometry.js";
|
||||
|
||||
function createPolylineGeometry(polylineGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polylineGeometry = PolylineGeometry.unpack(polylineGeometry, offset);
|
||||
}
|
||||
polylineGeometry._ellipsoid = Ellipsoid.clone(polylineGeometry._ellipsoid);
|
||||
return PolylineGeometry.createGeometry(polylineGeometry);
|
||||
}
|
||||
export default createPolylineGeometry;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import PolylineVolumeGeometry from "../Core/PolylineVolumeGeometry.js";
|
||||
|
||||
function createPolylineVolumeGeometry(polylineVolumeGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
polylineVolumeGeometry = PolylineVolumeGeometry.unpack(
|
||||
polylineVolumeGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
polylineVolumeGeometry._ellipsoid = Ellipsoid.clone(
|
||||
polylineVolumeGeometry._ellipsoid,
|
||||
);
|
||||
return PolylineVolumeGeometry.createGeometry(polylineVolumeGeometry);
|
||||
}
|
||||
export default createPolylineVolumeGeometry;
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import PolylineVolumeOutlineGeometry from "../Core/PolylineVolumeOutlineGeometry.js";
|
||||
|
||||
function createPolylineVolumeOutlineGeometry(
|
||||
polylineVolumeOutlineGeometry,
|
||||
offset,
|
||||
) {
|
||||
if (defined(offset)) {
|
||||
polylineVolumeOutlineGeometry = PolylineVolumeOutlineGeometry.unpack(
|
||||
polylineVolumeOutlineGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
polylineVolumeOutlineGeometry._ellipsoid = Ellipsoid.clone(
|
||||
polylineVolumeOutlineGeometry._ellipsoid,
|
||||
);
|
||||
return PolylineVolumeOutlineGeometry.createGeometry(
|
||||
polylineVolumeOutlineGeometry,
|
||||
);
|
||||
}
|
||||
export default createPolylineVolumeOutlineGeometry;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import RectangleGeometry from "../Core/RectangleGeometry.js";
|
||||
|
||||
function createRectangleGeometry(rectangleGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
rectangleGeometry = RectangleGeometry.unpack(rectangleGeometry, offset);
|
||||
}
|
||||
rectangleGeometry._ellipsoid = Ellipsoid.clone(rectangleGeometry._ellipsoid);
|
||||
rectangleGeometry._rectangle = Rectangle.clone(rectangleGeometry._rectangle);
|
||||
return RectangleGeometry.createGeometry(rectangleGeometry);
|
||||
}
|
||||
export default createRectangleGeometry;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import RectangleOutlineGeometry from "../Core/RectangleOutlineGeometry.js";
|
||||
|
||||
function createRectangleOutlineGeometry(rectangleGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
rectangleGeometry = RectangleOutlineGeometry.unpack(
|
||||
rectangleGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
rectangleGeometry._ellipsoid = Ellipsoid.clone(rectangleGeometry._ellipsoid);
|
||||
rectangleGeometry._rectangle = Rectangle.clone(rectangleGeometry._rectangle);
|
||||
return RectangleOutlineGeometry.createGeometry(rectangleGeometry);
|
||||
}
|
||||
export default createRectangleOutlineGeometry;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import SimplePolylineGeometry from "../Core/SimplePolylineGeometry.js";
|
||||
|
||||
function createSimplePolylineGeometry(simplePolylineGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
simplePolylineGeometry = SimplePolylineGeometry.unpack(
|
||||
simplePolylineGeometry,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
simplePolylineGeometry._ellipsoid = Ellipsoid.clone(
|
||||
simplePolylineGeometry._ellipsoid,
|
||||
);
|
||||
return SimplePolylineGeometry.createGeometry(simplePolylineGeometry);
|
||||
}
|
||||
export default createSimplePolylineGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import SphereGeometry from "../Core/SphereGeometry.js";
|
||||
|
||||
function createSphereGeometry(sphereGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
sphereGeometry = SphereGeometry.unpack(sphereGeometry, offset);
|
||||
}
|
||||
return SphereGeometry.createGeometry(sphereGeometry);
|
||||
}
|
||||
export default createSphereGeometry;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import SphereOutlineGeometry from "../Core/SphereOutlineGeometry.js";
|
||||
|
||||
function createSphereOutlineGeometry(sphereGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
sphereGeometry = SphereOutlineGeometry.unpack(sphereGeometry, offset);
|
||||
}
|
||||
return SphereOutlineGeometry.createGeometry(sphereGeometry);
|
||||
}
|
||||
export default createSphereOutlineGeometry;
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
import formatError from "../Core/formatError.js";
|
||||
|
||||
/**
|
||||
* Creates an adapter function to allow a calculation function to operate as a Web Worker,
|
||||
* paired with TaskProcessor, to receive tasks and return results.
|
||||
*
|
||||
* @function createTaskProcessorWorker
|
||||
*
|
||||
* @param {createTaskProcessorWorker.WorkerFunction} workerFunction The calculation function,
|
||||
* which takes parameters and returns a result.
|
||||
* @returns {createTaskProcessorWorker.TaskProcessorWorkerFunction} A function that adapts the
|
||||
* calculation function to work as a Web Worker onmessage listener with TaskProcessor.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* function doCalculation(parameters, transferableObjects) {
|
||||
* // calculate some result using the inputs in parameters
|
||||
* return result;
|
||||
* }
|
||||
*
|
||||
* return Cesium.createTaskProcessorWorker(doCalculation);
|
||||
* // the resulting function is compatible with TaskProcessor
|
||||
*
|
||||
* @see TaskProcessor
|
||||
* @see {@link http://www.w3.org/TR/workers/|Web Workers}
|
||||
* @see {@link http://www.w3.org/TR/html5/common-dom-interfaces.html#transferable-objects|Transferable objects}
|
||||
*/
|
||||
function createTaskProcessorWorker(workerFunction) {
|
||||
async function onMessageHandler({ data }) {
|
||||
const transferableObjects = [];
|
||||
const responseMessage = {
|
||||
id: data.id,
|
||||
result: undefined,
|
||||
error: undefined,
|
||||
};
|
||||
|
||||
self.CESIUM_BASE_URL = data.baseUrl;
|
||||
|
||||
try {
|
||||
const result = await workerFunction(data.parameters, transferableObjects);
|
||||
responseMessage.result = result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
responseMessage.error = {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
};
|
||||
} else {
|
||||
responseMessage.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.canTransferArrayBuffer) {
|
||||
transferableObjects.length = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
postMessage(responseMessage, transferableObjects);
|
||||
} catch (error) {
|
||||
// something went wrong trying to post the message, post a simpler
|
||||
// error that we can be sure will be cloneable
|
||||
responseMessage.result = undefined;
|
||||
responseMessage.error = `postMessage failed with error: ${formatError(
|
||||
error,
|
||||
)}\n with responseMessage: ${JSON.stringify(responseMessage)}`;
|
||||
postMessage(responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function onMessageErrorHandler(event) {
|
||||
postMessage({
|
||||
id: event.data?.id,
|
||||
error: `postMessage failed with error: ${JSON.stringify(event)}`,
|
||||
});
|
||||
}
|
||||
|
||||
self.onmessage = onMessageHandler;
|
||||
self.onmessageerror = onMessageErrorHandler;
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that performs a calculation in a Web Worker.
|
||||
* @callback createTaskProcessorWorker.WorkerFunction
|
||||
*
|
||||
* @param {object} parameters Parameters to the calculation.
|
||||
* @param {Array} transferableObjects An array that should be filled with references to objects inside
|
||||
* the result that should be transferred back to the main document instead of copied.
|
||||
* @returns {object} The result of the calculation.
|
||||
*
|
||||
* @example
|
||||
* function calculate(parameters, transferableObjects) {
|
||||
* // perform whatever calculation is necessary.
|
||||
* const typedArray = new Float32Array(0);
|
||||
*
|
||||
* // typed arrays are transferable
|
||||
* transferableObjects.push(typedArray)
|
||||
*
|
||||
* return {
|
||||
* typedArray : typedArray
|
||||
* };
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Web Worker message event handler function that handles the interaction with TaskProcessor,
|
||||
* specifically, task ID management and posting a response message containing the result.
|
||||
* @callback createTaskProcessorWorker.TaskProcessorWorkerFunction
|
||||
*
|
||||
* @param {object} event The onmessage event object.
|
||||
*/
|
||||
export default createTaskProcessorWorker;
|
||||
+541
@@ -0,0 +1,541 @@
|
||||
import AttributeCompression from "../Core/AttributeCompression.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import combine from "../Core/combine.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const MAX_SHORT = 32767;
|
||||
const MITER_BREAK = Math.cos(CesiumMath.toRadians(150.0));
|
||||
|
||||
const scratchBVCartographic = new Cartographic();
|
||||
const scratchEncodedPosition = new Cartesian3();
|
||||
|
||||
function decodePositions(
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
rectangle,
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
ellipsoid,
|
||||
) {
|
||||
const positionsLength = uBuffer.length;
|
||||
const decodedPositions = new Float64Array(positionsLength * 3);
|
||||
for (let i = 0; i < positionsLength; ++i) {
|
||||
const u = uBuffer[i];
|
||||
const v = vBuffer[i];
|
||||
const h = heightBuffer[i];
|
||||
|
||||
const lon = CesiumMath.lerp(rectangle.west, rectangle.east, u / MAX_SHORT);
|
||||
const lat = CesiumMath.lerp(
|
||||
rectangle.south,
|
||||
rectangle.north,
|
||||
v / MAX_SHORT,
|
||||
);
|
||||
const alt = CesiumMath.lerp(minimumHeight, maximumHeight, h / MAX_SHORT);
|
||||
|
||||
const cartographic = Cartographic.fromRadians(
|
||||
lon,
|
||||
lat,
|
||||
alt,
|
||||
scratchBVCartographic,
|
||||
);
|
||||
const decodedPosition = ellipsoid.cartographicToCartesian(
|
||||
cartographic,
|
||||
scratchEncodedPosition,
|
||||
);
|
||||
Cartesian3.pack(decodedPosition, decodedPositions, i * 3);
|
||||
}
|
||||
return decodedPositions;
|
||||
}
|
||||
|
||||
function getPositionOffsets(counts) {
|
||||
const countsLength = counts.length;
|
||||
const positionOffsets = new Uint32Array(countsLength + 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < countsLength; ++i) {
|
||||
positionOffsets[i] = offset;
|
||||
offset += counts[i];
|
||||
}
|
||||
positionOffsets[countsLength] = offset;
|
||||
return positionOffsets;
|
||||
}
|
||||
|
||||
const previousCompressedCartographicScratch = new Cartographic();
|
||||
const currentCompressedCartographicScratch = new Cartographic();
|
||||
function removeDuplicates(uBuffer, vBuffer, heightBuffer, counts) {
|
||||
const countsLength = counts.length;
|
||||
const positionsLength = uBuffer.length;
|
||||
const markRemoval = new Uint8Array(positionsLength);
|
||||
const previous = previousCompressedCartographicScratch;
|
||||
const current = currentCompressedCartographicScratch;
|
||||
let offset = 0;
|
||||
for (let i = 0; i < countsLength; i++) {
|
||||
const count = counts[i];
|
||||
let updatedCount = count;
|
||||
for (let j = 1; j < count; j++) {
|
||||
const index = offset + j;
|
||||
const previousIndex = index - 1;
|
||||
current.longitude = uBuffer[index];
|
||||
current.latitude = vBuffer[index];
|
||||
previous.longitude = uBuffer[previousIndex];
|
||||
previous.latitude = vBuffer[previousIndex];
|
||||
|
||||
if (Cartographic.equals(current, previous)) {
|
||||
updatedCount--;
|
||||
markRemoval[previousIndex] = 1;
|
||||
}
|
||||
}
|
||||
counts[i] = updatedCount;
|
||||
offset += count;
|
||||
}
|
||||
|
||||
let nextAvailableIndex = 0;
|
||||
for (let k = 0; k < positionsLength; k++) {
|
||||
if (markRemoval[k] !== 1) {
|
||||
uBuffer[nextAvailableIndex] = uBuffer[k];
|
||||
vBuffer[nextAvailableIndex] = vBuffer[k];
|
||||
heightBuffer[nextAvailableIndex] = heightBuffer[k];
|
||||
nextAvailableIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function VertexAttributesAndIndices(volumesCount) {
|
||||
const vertexCount = volumesCount * 8;
|
||||
const vec3Floats = vertexCount * 3;
|
||||
const vec4Floats = vertexCount * 4;
|
||||
this.startEllipsoidNormals = new Float32Array(vec3Floats);
|
||||
this.endEllipsoidNormals = new Float32Array(vec3Floats);
|
||||
this.startPositionAndHeights = new Float32Array(vec4Floats);
|
||||
this.startFaceNormalAndVertexCornerIds = new Float32Array(vec4Floats);
|
||||
this.endPositionAndHeights = new Float32Array(vec4Floats);
|
||||
this.endFaceNormalAndHalfWidths = new Float32Array(vec4Floats);
|
||||
this.vertexBatchIds = new Uint16Array(vertexCount);
|
||||
|
||||
this.indices = IndexDatatype.createTypedArray(vertexCount, 36 * volumesCount);
|
||||
|
||||
this.vec3Offset = 0;
|
||||
this.vec4Offset = 0;
|
||||
this.batchIdOffset = 0;
|
||||
this.indexOffset = 0;
|
||||
|
||||
this.volumeStartIndex = 0;
|
||||
}
|
||||
|
||||
const towardCurrScratch = new Cartesian3();
|
||||
const towardNextScratch = new Cartesian3();
|
||||
function computeMiteredNormal(
|
||||
previousPosition,
|
||||
position,
|
||||
nextPosition,
|
||||
ellipsoidSurfaceNormal,
|
||||
result,
|
||||
) {
|
||||
const towardNext = Cartesian3.subtract(
|
||||
nextPosition,
|
||||
position,
|
||||
towardNextScratch,
|
||||
);
|
||||
let towardCurr = Cartesian3.subtract(
|
||||
position,
|
||||
previousPosition,
|
||||
towardCurrScratch,
|
||||
);
|
||||
Cartesian3.normalize(towardNext, towardNext);
|
||||
Cartesian3.normalize(towardCurr, towardCurr);
|
||||
|
||||
if (Cartesian3.dot(towardNext, towardCurr) < MITER_BREAK) {
|
||||
towardCurr = Cartesian3.multiplyByScalar(
|
||||
towardCurr,
|
||||
-1.0,
|
||||
towardCurrScratch,
|
||||
);
|
||||
}
|
||||
|
||||
Cartesian3.add(towardNext, towardCurr, result);
|
||||
if (Cartesian3.equals(result, Cartesian3.ZERO)) {
|
||||
result = Cartesian3.subtract(previousPosition, position);
|
||||
}
|
||||
|
||||
// Make sure the normal is orthogonal to the ellipsoid surface normal
|
||||
Cartesian3.cross(result, ellipsoidSurfaceNormal, result);
|
||||
Cartesian3.cross(ellipsoidSurfaceNormal, result, result);
|
||||
Cartesian3.normalize(result, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Winding order is reversed so each segment's volume is inside-out
|
||||
// 3-----------7
|
||||
// /| left /|
|
||||
// / | 1 / |
|
||||
// 2-----------6 5 end
|
||||
// | / | /
|
||||
// start |/ right |/
|
||||
// 0-----------4
|
||||
//
|
||||
const REFERENCE_INDICES = [
|
||||
0,
|
||||
2,
|
||||
6,
|
||||
0,
|
||||
6,
|
||||
4, // right
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
0,
|
||||
3,
|
||||
2, // start face
|
||||
0,
|
||||
4,
|
||||
5,
|
||||
0,
|
||||
5,
|
||||
1, // bottom
|
||||
5,
|
||||
3,
|
||||
1,
|
||||
5,
|
||||
7,
|
||||
3, // left
|
||||
7,
|
||||
5,
|
||||
4,
|
||||
7,
|
||||
4,
|
||||
6, // end face
|
||||
7,
|
||||
6,
|
||||
2,
|
||||
7,
|
||||
2,
|
||||
3, // top
|
||||
];
|
||||
const REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length;
|
||||
|
||||
const positionScratch = new Cartesian3();
|
||||
const scratchStartEllipsoidNormal = new Cartesian3();
|
||||
const scratchStartFaceNormal = new Cartesian3();
|
||||
const scratchEndEllipsoidNormal = new Cartesian3();
|
||||
const scratchEndFaceNormal = new Cartesian3();
|
||||
VertexAttributesAndIndices.prototype.addVolume = function (
|
||||
preStartRTC,
|
||||
startRTC,
|
||||
endRTC,
|
||||
postEndRTC,
|
||||
startHeight,
|
||||
endHeight,
|
||||
halfWidth,
|
||||
batchId,
|
||||
center,
|
||||
ellipsoid,
|
||||
) {
|
||||
let position = Cartesian3.add(startRTC, center, positionScratch);
|
||||
const startEllipsoidNormal = ellipsoid.geodeticSurfaceNormal(
|
||||
position,
|
||||
scratchStartEllipsoidNormal,
|
||||
);
|
||||
position = Cartesian3.add(endRTC, center, positionScratch);
|
||||
const endEllipsoidNormal = ellipsoid.geodeticSurfaceNormal(
|
||||
position,
|
||||
scratchEndEllipsoidNormal,
|
||||
);
|
||||
|
||||
const startFaceNormal = computeMiteredNormal(
|
||||
preStartRTC,
|
||||
startRTC,
|
||||
endRTC,
|
||||
startEllipsoidNormal,
|
||||
scratchStartFaceNormal,
|
||||
);
|
||||
const endFaceNormal = computeMiteredNormal(
|
||||
postEndRTC,
|
||||
endRTC,
|
||||
startRTC,
|
||||
endEllipsoidNormal,
|
||||
scratchEndFaceNormal,
|
||||
);
|
||||
|
||||
const startEllipsoidNormals = this.startEllipsoidNormals;
|
||||
const endEllipsoidNormals = this.endEllipsoidNormals;
|
||||
const startPositionAndHeights = this.startPositionAndHeights;
|
||||
const startFaceNormalAndVertexCornerIds =
|
||||
this.startFaceNormalAndVertexCornerIds;
|
||||
const endPositionAndHeights = this.endPositionAndHeights;
|
||||
const endFaceNormalAndHalfWidths = this.endFaceNormalAndHalfWidths;
|
||||
const vertexBatchIds = this.vertexBatchIds;
|
||||
|
||||
let batchIdOffset = this.batchIdOffset;
|
||||
let vec3Offset = this.vec3Offset;
|
||||
let vec4Offset = this.vec4Offset;
|
||||
|
||||
let i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
Cartesian3.pack(startEllipsoidNormal, startEllipsoidNormals, vec3Offset);
|
||||
Cartesian3.pack(endEllipsoidNormal, endEllipsoidNormals, vec3Offset);
|
||||
|
||||
Cartesian3.pack(startRTC, startPositionAndHeights, vec4Offset);
|
||||
startPositionAndHeights[vec4Offset + 3] = startHeight;
|
||||
|
||||
Cartesian3.pack(endRTC, endPositionAndHeights, vec4Offset);
|
||||
endPositionAndHeights[vec4Offset + 3] = endHeight;
|
||||
|
||||
Cartesian3.pack(
|
||||
startFaceNormal,
|
||||
startFaceNormalAndVertexCornerIds,
|
||||
vec4Offset,
|
||||
);
|
||||
startFaceNormalAndVertexCornerIds[vec4Offset + 3] = i;
|
||||
|
||||
Cartesian3.pack(endFaceNormal, endFaceNormalAndHalfWidths, vec4Offset);
|
||||
endFaceNormalAndHalfWidths[vec4Offset + 3] = halfWidth;
|
||||
|
||||
vertexBatchIds[batchIdOffset++] = batchId;
|
||||
|
||||
vec3Offset += 3;
|
||||
vec4Offset += 4;
|
||||
}
|
||||
|
||||
this.batchIdOffset = batchIdOffset;
|
||||
this.vec3Offset = vec3Offset;
|
||||
this.vec4Offset = vec4Offset;
|
||||
const indices = this.indices;
|
||||
const volumeStartIndex = this.volumeStartIndex;
|
||||
|
||||
const indexOffset = this.indexOffset;
|
||||
for (i = 0; i < REFERENCE_INDICES_LENGTH; i++) {
|
||||
indices[indexOffset + i] = REFERENCE_INDICES[i] + volumeStartIndex;
|
||||
}
|
||||
|
||||
this.volumeStartIndex += 8;
|
||||
this.indexOffset += REFERENCE_INDICES_LENGTH;
|
||||
};
|
||||
|
||||
const scratchRectangle = new Rectangle();
|
||||
const scratchEllipsoid = new Ellipsoid();
|
||||
const scratchCenter = new Cartesian3();
|
||||
|
||||
const scratchPrev = new Cartesian3();
|
||||
const scratchP0 = new Cartesian3();
|
||||
const scratchP1 = new Cartesian3();
|
||||
const scratchNext = new Cartesian3();
|
||||
function createVectorTileClampedPolylines(parameters, transferableObjects) {
|
||||
const encodedPositions = new Uint16Array(parameters.positions);
|
||||
const widths = new Uint16Array(parameters.widths);
|
||||
const counts = new Uint32Array(parameters.counts);
|
||||
const batchIds = new Uint16Array(parameters.batchIds);
|
||||
|
||||
// Unpack tile decoding parameters
|
||||
const rectangle = scratchRectangle;
|
||||
const ellipsoid = scratchEllipsoid;
|
||||
const center = scratchCenter;
|
||||
const packedBuffer = new Float64Array(parameters.packedBuffer);
|
||||
|
||||
let offset = 0;
|
||||
const minimumHeight = packedBuffer[offset++];
|
||||
const maximumHeight = packedBuffer[offset++];
|
||||
|
||||
Rectangle.unpack(packedBuffer, offset, rectangle);
|
||||
offset += Rectangle.packedLength;
|
||||
|
||||
Ellipsoid.unpack(packedBuffer, offset, ellipsoid);
|
||||
offset += Ellipsoid.packedLength;
|
||||
|
||||
Cartesian3.unpack(packedBuffer, offset, center);
|
||||
|
||||
let i;
|
||||
|
||||
// Unpack positions and generate volumes
|
||||
let positionsLength = encodedPositions.length / 3;
|
||||
const uBuffer = encodedPositions.subarray(0, positionsLength);
|
||||
const vBuffer = encodedPositions.subarray(
|
||||
positionsLength,
|
||||
2 * positionsLength,
|
||||
);
|
||||
const heightBuffer = encodedPositions.subarray(
|
||||
2 * positionsLength,
|
||||
3 * positionsLength,
|
||||
);
|
||||
AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
|
||||
|
||||
removeDuplicates(uBuffer, vBuffer, heightBuffer, counts);
|
||||
|
||||
// Figure out how many volumes and how many vertices there will be.
|
||||
const countsLength = counts.length;
|
||||
let volumesCount = 0;
|
||||
for (i = 0; i < countsLength; i++) {
|
||||
const polylinePositionCount = counts[i];
|
||||
volumesCount += polylinePositionCount - 1;
|
||||
}
|
||||
|
||||
const attribsAndIndices = new VertexAttributesAndIndices(volumesCount);
|
||||
|
||||
const positions = decodePositions(
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
rectangle,
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
ellipsoid,
|
||||
center,
|
||||
);
|
||||
|
||||
positionsLength = uBuffer.length;
|
||||
const positionsRTC = new Float32Array(positionsLength * 3);
|
||||
for (i = 0; i < positionsLength; ++i) {
|
||||
positionsRTC[i * 3] = positions[i * 3] - center.x;
|
||||
positionsRTC[i * 3 + 1] = positions[i * 3 + 1] - center.y;
|
||||
positionsRTC[i * 3 + 2] = positions[i * 3 + 2] - center.z;
|
||||
}
|
||||
|
||||
let currentPositionIndex = 0;
|
||||
let currentHeightIndex = 0;
|
||||
for (i = 0; i < countsLength; i++) {
|
||||
const polylineVolumeCount = counts[i] - 1;
|
||||
const halfWidth = widths[i] * 0.5;
|
||||
const batchId = batchIds[i];
|
||||
const volumeFirstPositionIndex = currentPositionIndex;
|
||||
for (let j = 0; j < polylineVolumeCount; j++) {
|
||||
const volumeStart = Cartesian3.unpack(
|
||||
positionsRTC,
|
||||
currentPositionIndex,
|
||||
scratchP0,
|
||||
);
|
||||
const volumeEnd = Cartesian3.unpack(
|
||||
positionsRTC,
|
||||
currentPositionIndex + 3,
|
||||
scratchP1,
|
||||
);
|
||||
|
||||
let startHeight = heightBuffer[currentHeightIndex];
|
||||
let endHeight = heightBuffer[currentHeightIndex + 1];
|
||||
startHeight = CesiumMath.lerp(
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
startHeight / MAX_SHORT,
|
||||
);
|
||||
endHeight = CesiumMath.lerp(
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
endHeight / MAX_SHORT,
|
||||
);
|
||||
|
||||
currentHeightIndex++;
|
||||
|
||||
let preStart = scratchPrev;
|
||||
let postEnd = scratchNext;
|
||||
if (j === 0) {
|
||||
// Check if this volume is like a loop
|
||||
const finalPositionIndex =
|
||||
volumeFirstPositionIndex + polylineVolumeCount * 3;
|
||||
const finalPosition = Cartesian3.unpack(
|
||||
positionsRTC,
|
||||
finalPositionIndex,
|
||||
scratchPrev,
|
||||
);
|
||||
if (Cartesian3.equals(finalPosition, volumeStart)) {
|
||||
Cartesian3.unpack(positionsRTC, finalPositionIndex - 3, preStart);
|
||||
} else {
|
||||
const offsetPastStart = Cartesian3.subtract(
|
||||
volumeStart,
|
||||
volumeEnd,
|
||||
scratchPrev,
|
||||
);
|
||||
preStart = Cartesian3.add(offsetPastStart, volumeStart, scratchPrev);
|
||||
}
|
||||
} else {
|
||||
Cartesian3.unpack(positionsRTC, currentPositionIndex - 3, preStart);
|
||||
}
|
||||
|
||||
if (j === polylineVolumeCount - 1) {
|
||||
// Check if this volume is like a loop
|
||||
const firstPosition = Cartesian3.unpack(
|
||||
positionsRTC,
|
||||
volumeFirstPositionIndex,
|
||||
scratchNext,
|
||||
);
|
||||
if (Cartesian3.equals(firstPosition, volumeEnd)) {
|
||||
Cartesian3.unpack(
|
||||
positionsRTC,
|
||||
volumeFirstPositionIndex + 3,
|
||||
postEnd,
|
||||
);
|
||||
} else {
|
||||
const offsetPastEnd = Cartesian3.subtract(
|
||||
volumeEnd,
|
||||
volumeStart,
|
||||
scratchNext,
|
||||
);
|
||||
postEnd = Cartesian3.add(offsetPastEnd, volumeEnd, scratchNext);
|
||||
}
|
||||
} else {
|
||||
Cartesian3.unpack(positionsRTC, currentPositionIndex + 6, postEnd);
|
||||
}
|
||||
|
||||
attribsAndIndices.addVolume(
|
||||
preStart,
|
||||
volumeStart,
|
||||
volumeEnd,
|
||||
postEnd,
|
||||
startHeight,
|
||||
endHeight,
|
||||
halfWidth,
|
||||
batchId,
|
||||
center,
|
||||
ellipsoid,
|
||||
);
|
||||
|
||||
currentPositionIndex += 3;
|
||||
}
|
||||
currentPositionIndex += 3;
|
||||
currentHeightIndex++;
|
||||
}
|
||||
|
||||
const indices = attribsAndIndices.indices;
|
||||
|
||||
transferableObjects.push(attribsAndIndices.startEllipsoidNormals.buffer);
|
||||
transferableObjects.push(attribsAndIndices.endEllipsoidNormals.buffer);
|
||||
transferableObjects.push(attribsAndIndices.startPositionAndHeights.buffer);
|
||||
transferableObjects.push(
|
||||
attribsAndIndices.startFaceNormalAndVertexCornerIds.buffer,
|
||||
);
|
||||
transferableObjects.push(attribsAndIndices.endPositionAndHeights.buffer);
|
||||
transferableObjects.push(attribsAndIndices.endFaceNormalAndHalfWidths.buffer);
|
||||
transferableObjects.push(attribsAndIndices.vertexBatchIds.buffer);
|
||||
transferableObjects.push(indices.buffer);
|
||||
|
||||
let results = {
|
||||
indexDatatype:
|
||||
indices.BYTES_PER_ELEMENT === 2
|
||||
? IndexDatatype.UNSIGNED_SHORT
|
||||
: IndexDatatype.UNSIGNED_INT,
|
||||
startEllipsoidNormals: attribsAndIndices.startEllipsoidNormals.buffer,
|
||||
endEllipsoidNormals: attribsAndIndices.endEllipsoidNormals.buffer,
|
||||
startPositionAndHeights: attribsAndIndices.startPositionAndHeights.buffer,
|
||||
startFaceNormalAndVertexCornerIds:
|
||||
attribsAndIndices.startFaceNormalAndVertexCornerIds.buffer,
|
||||
endPositionAndHeights: attribsAndIndices.endPositionAndHeights.buffer,
|
||||
endFaceNormalAndHalfWidths:
|
||||
attribsAndIndices.endFaceNormalAndHalfWidths.buffer,
|
||||
vertexBatchIds: attribsAndIndices.vertexBatchIds.buffer,
|
||||
indices: indices.buffer,
|
||||
};
|
||||
|
||||
if (parameters.keepDecodedPositions) {
|
||||
const positionOffsets = getPositionOffsets(counts);
|
||||
transferableObjects.push(positions.buffer, positionOffsets.buffer);
|
||||
results = combine(results, {
|
||||
decodedPositions: positions.buffer,
|
||||
decodedPositionOffsets: positionOffsets.buffer,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
export default createTaskProcessorWorker(createVectorTileClampedPolylines);
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
import BoundingSphere from "../Core/BoundingSphere.js";
|
||||
import BoxGeometry from "../Core/BoxGeometry.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Color from "../Core/Color.js";
|
||||
import CylinderGeometry from "../Core/CylinderGeometry.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import EllipsoidGeometry from "../Core/EllipsoidGeometry.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import Matrix4 from "../Core/Matrix4.js";
|
||||
import Vector3DTileBatch from "../Scene/Vector3DTileBatch.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const scratchCartesian = new Cartesian3();
|
||||
|
||||
const packedBoxLength = Matrix4.packedLength + Cartesian3.packedLength;
|
||||
const packedCylinderLength = Matrix4.packedLength + 2;
|
||||
const packedEllipsoidLength = Matrix4.packedLength + Cartesian3.packedLength;
|
||||
const packedSphereLength = Cartesian3.packedLength + 1;
|
||||
|
||||
const scratchModelMatrixAndBV = {
|
||||
modelMatrix: new Matrix4(),
|
||||
boundingVolume: new BoundingSphere(),
|
||||
};
|
||||
|
||||
function boxModelMatrixAndBoundingVolume(boxes, index) {
|
||||
let boxIndex = index * packedBoxLength;
|
||||
|
||||
const dimensions = Cartesian3.unpack(boxes, boxIndex, scratchCartesian);
|
||||
boxIndex += Cartesian3.packedLength;
|
||||
|
||||
const boxModelMatrix = Matrix4.unpack(
|
||||
boxes,
|
||||
boxIndex,
|
||||
scratchModelMatrixAndBV.modelMatrix,
|
||||
);
|
||||
Matrix4.multiplyByScale(boxModelMatrix, dimensions, boxModelMatrix);
|
||||
|
||||
const boundingVolume = scratchModelMatrixAndBV.boundingVolume;
|
||||
Cartesian3.clone(Cartesian3.ZERO, boundingVolume.center);
|
||||
boundingVolume.radius = Math.sqrt(3.0);
|
||||
|
||||
return scratchModelMatrixAndBV;
|
||||
}
|
||||
|
||||
function cylinderModelMatrixAndBoundingVolume(cylinders, index) {
|
||||
let cylinderIndex = index * packedCylinderLength;
|
||||
|
||||
const cylinderRadius = cylinders[cylinderIndex++];
|
||||
const length = cylinders[cylinderIndex++];
|
||||
const scale = Cartesian3.fromElements(
|
||||
cylinderRadius,
|
||||
cylinderRadius,
|
||||
length,
|
||||
scratchCartesian,
|
||||
);
|
||||
|
||||
const cylinderModelMatrix = Matrix4.unpack(
|
||||
cylinders,
|
||||
cylinderIndex,
|
||||
scratchModelMatrixAndBV.modelMatrix,
|
||||
);
|
||||
Matrix4.multiplyByScale(cylinderModelMatrix, scale, cylinderModelMatrix);
|
||||
|
||||
const boundingVolume = scratchModelMatrixAndBV.boundingVolume;
|
||||
Cartesian3.clone(Cartesian3.ZERO, boundingVolume.center);
|
||||
boundingVolume.radius = Math.sqrt(2.0);
|
||||
|
||||
return scratchModelMatrixAndBV;
|
||||
}
|
||||
|
||||
function ellipsoidModelMatrixAndBoundingVolume(ellipsoids, index) {
|
||||
let ellipsoidIndex = index * packedEllipsoidLength;
|
||||
|
||||
const radii = Cartesian3.unpack(ellipsoids, ellipsoidIndex, scratchCartesian);
|
||||
ellipsoidIndex += Cartesian3.packedLength;
|
||||
|
||||
const ellipsoidModelMatrix = Matrix4.unpack(
|
||||
ellipsoids,
|
||||
ellipsoidIndex,
|
||||
scratchModelMatrixAndBV.modelMatrix,
|
||||
);
|
||||
Matrix4.multiplyByScale(ellipsoidModelMatrix, radii, ellipsoidModelMatrix);
|
||||
|
||||
const boundingVolume = scratchModelMatrixAndBV.boundingVolume;
|
||||
Cartesian3.clone(Cartesian3.ZERO, boundingVolume.center);
|
||||
boundingVolume.radius = 1.0;
|
||||
|
||||
return scratchModelMatrixAndBV;
|
||||
}
|
||||
|
||||
function sphereModelMatrixAndBoundingVolume(spheres, index) {
|
||||
let sphereIndex = index * packedSphereLength;
|
||||
|
||||
const sphereRadius = spheres[sphereIndex++];
|
||||
|
||||
const sphereTranslation = Cartesian3.unpack(
|
||||
spheres,
|
||||
sphereIndex,
|
||||
scratchCartesian,
|
||||
);
|
||||
const sphereModelMatrix = Matrix4.fromTranslation(
|
||||
sphereTranslation,
|
||||
scratchModelMatrixAndBV.modelMatrix,
|
||||
);
|
||||
Matrix4.multiplyByUniformScale(
|
||||
sphereModelMatrix,
|
||||
sphereRadius,
|
||||
sphereModelMatrix,
|
||||
);
|
||||
|
||||
const boundingVolume = scratchModelMatrixAndBV.boundingVolume;
|
||||
Cartesian3.clone(Cartesian3.ZERO, boundingVolume.center);
|
||||
boundingVolume.radius = 1.0;
|
||||
|
||||
return scratchModelMatrixAndBV;
|
||||
}
|
||||
|
||||
const scratchPosition = new Cartesian3();
|
||||
|
||||
function createPrimitive(
|
||||
options,
|
||||
primitive,
|
||||
primitiveBatchIds,
|
||||
geometry,
|
||||
getModelMatrixAndBoundingVolume,
|
||||
) {
|
||||
if (!defined(primitive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numberOfPrimitives = primitiveBatchIds.length;
|
||||
const geometryPositions = geometry.attributes.position.values;
|
||||
const geometryIndices = geometry.indices;
|
||||
|
||||
const positions = options.positions;
|
||||
const vertexBatchIds = options.vertexBatchIds;
|
||||
const indices = options.indices;
|
||||
|
||||
const batchIds = options.batchIds;
|
||||
const batchTableColors = options.batchTableColors;
|
||||
const batchedIndices = options.batchedIndices;
|
||||
const indexOffsets = options.indexOffsets;
|
||||
const indexCounts = options.indexCounts;
|
||||
const boundingVolumes = options.boundingVolumes;
|
||||
|
||||
const modelMatrix = options.modelMatrix;
|
||||
const center = options.center;
|
||||
|
||||
let positionOffset = options.positionOffset;
|
||||
let batchIdIndex = options.batchIdIndex;
|
||||
let indexOffset = options.indexOffset;
|
||||
const batchedIndicesOffset = options.batchedIndicesOffset;
|
||||
|
||||
for (let i = 0; i < numberOfPrimitives; ++i) {
|
||||
const primitiveModelMatrixAndBV = getModelMatrixAndBoundingVolume(
|
||||
primitive,
|
||||
i,
|
||||
);
|
||||
const primitiveModelMatrix = primitiveModelMatrixAndBV.modelMatrix;
|
||||
Matrix4.multiply(modelMatrix, primitiveModelMatrix, primitiveModelMatrix);
|
||||
|
||||
const batchId = primitiveBatchIds[i];
|
||||
|
||||
const positionsLength = geometryPositions.length;
|
||||
for (let j = 0; j < positionsLength; j += 3) {
|
||||
const position = Cartesian3.unpack(geometryPositions, j, scratchPosition);
|
||||
Matrix4.multiplyByPoint(primitiveModelMatrix, position, position);
|
||||
Cartesian3.subtract(position, center, position);
|
||||
|
||||
Cartesian3.pack(position, positions, positionOffset * 3 + j);
|
||||
vertexBatchIds[batchIdIndex++] = batchId;
|
||||
}
|
||||
|
||||
const indicesLength = geometryIndices.length;
|
||||
for (let k = 0; k < indicesLength; ++k) {
|
||||
indices[indexOffset + k] = geometryIndices[k] + positionOffset;
|
||||
}
|
||||
|
||||
const offset = i + batchedIndicesOffset;
|
||||
batchedIndices[offset] = new Vector3DTileBatch({
|
||||
offset: indexOffset,
|
||||
count: indicesLength,
|
||||
color: Color.fromRgba(batchTableColors[batchId]),
|
||||
batchIds: [batchId],
|
||||
});
|
||||
batchIds[offset] = batchId;
|
||||
indexOffsets[offset] = indexOffset;
|
||||
indexCounts[offset] = indicesLength;
|
||||
boundingVolumes[offset] = BoundingSphere.transform(
|
||||
primitiveModelMatrixAndBV.boundingVolume,
|
||||
primitiveModelMatrix,
|
||||
);
|
||||
|
||||
positionOffset += positionsLength / 3;
|
||||
indexOffset += indicesLength;
|
||||
}
|
||||
|
||||
options.positionOffset = positionOffset;
|
||||
options.batchIdIndex = batchIdIndex;
|
||||
options.indexOffset = indexOffset;
|
||||
options.batchedIndicesOffset += numberOfPrimitives;
|
||||
}
|
||||
|
||||
const scratchCenter = new Cartesian3();
|
||||
const scratchMatrix4 = new Matrix4();
|
||||
|
||||
function unpackBuffer(buffer) {
|
||||
const packedBuffer = new Float64Array(buffer);
|
||||
|
||||
let offset = 0;
|
||||
Cartesian3.unpack(packedBuffer, offset, scratchCenter);
|
||||
offset += Cartesian3.packedLength;
|
||||
|
||||
Matrix4.unpack(packedBuffer, offset, scratchMatrix4);
|
||||
}
|
||||
|
||||
function packedBatchedIndicesLength(batchedIndices) {
|
||||
const length = batchedIndices.length;
|
||||
let count = 0;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
count += Color.packedLength + 3 + batchedIndices[i].batchIds.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function packBuffer(indicesBytesPerElement, batchedIndices, boundingVolumes) {
|
||||
const numBVs = boundingVolumes.length;
|
||||
const length =
|
||||
1 +
|
||||
1 +
|
||||
numBVs * BoundingSphere.packedLength +
|
||||
1 +
|
||||
packedBatchedIndicesLength(batchedIndices);
|
||||
|
||||
const packedBuffer = new Float64Array(length);
|
||||
|
||||
let offset = 0;
|
||||
packedBuffer[offset++] = indicesBytesPerElement;
|
||||
packedBuffer[offset++] = numBVs;
|
||||
|
||||
for (let i = 0; i < numBVs; ++i) {
|
||||
BoundingSphere.pack(boundingVolumes[i], packedBuffer, offset);
|
||||
offset += BoundingSphere.packedLength;
|
||||
}
|
||||
|
||||
const indicesLength = batchedIndices.length;
|
||||
packedBuffer[offset++] = indicesLength;
|
||||
|
||||
for (let j = 0; j < indicesLength; ++j) {
|
||||
const batchedIndex = batchedIndices[j];
|
||||
|
||||
Color.pack(batchedIndex.color, packedBuffer, offset);
|
||||
offset += Color.packedLength;
|
||||
|
||||
packedBuffer[offset++] = batchedIndex.offset;
|
||||
packedBuffer[offset++] = batchedIndex.count;
|
||||
|
||||
const batchIds = batchedIndex.batchIds;
|
||||
const batchIdsLength = batchIds.length;
|
||||
packedBuffer[offset++] = batchIdsLength;
|
||||
|
||||
for (let k = 0; k < batchIdsLength; ++k) {
|
||||
packedBuffer[offset++] = batchIds[k];
|
||||
}
|
||||
}
|
||||
|
||||
return packedBuffer;
|
||||
}
|
||||
|
||||
function createVectorTileGeometries(parameters, transferableObjects) {
|
||||
const boxes = defined(parameters.boxes)
|
||||
? new Float32Array(parameters.boxes)
|
||||
: undefined;
|
||||
const boxBatchIds = defined(parameters.boxBatchIds)
|
||||
? new Uint16Array(parameters.boxBatchIds)
|
||||
: undefined;
|
||||
const cylinders = defined(parameters.cylinders)
|
||||
? new Float32Array(parameters.cylinders)
|
||||
: undefined;
|
||||
const cylinderBatchIds = defined(parameters.cylinderBatchIds)
|
||||
? new Uint16Array(parameters.cylinderBatchIds)
|
||||
: undefined;
|
||||
const ellipsoids = defined(parameters.ellipsoids)
|
||||
? new Float32Array(parameters.ellipsoids)
|
||||
: undefined;
|
||||
const ellipsoidBatchIds = defined(parameters.ellipsoidBatchIds)
|
||||
? new Uint16Array(parameters.ellipsoidBatchIds)
|
||||
: undefined;
|
||||
const spheres = defined(parameters.spheres)
|
||||
? new Float32Array(parameters.spheres)
|
||||
: undefined;
|
||||
const sphereBatchIds = defined(parameters.sphereBatchIds)
|
||||
? new Uint16Array(parameters.sphereBatchIds)
|
||||
: undefined;
|
||||
|
||||
const numberOfBoxes = defined(boxes) ? boxBatchIds.length : 0;
|
||||
const numberOfCylinders = defined(cylinders) ? cylinderBatchIds.length : 0;
|
||||
const numberOfEllipsoids = defined(ellipsoids) ? ellipsoidBatchIds.length : 0;
|
||||
const numberOfSpheres = defined(spheres) ? sphereBatchIds.length : 0;
|
||||
|
||||
const boxGeometry = BoxGeometry.getUnitBox();
|
||||
const cylinderGeometry = CylinderGeometry.getUnitCylinder();
|
||||
const ellipsoidGeometry = EllipsoidGeometry.getUnitEllipsoid();
|
||||
|
||||
const boxPositions = boxGeometry.attributes.position.values;
|
||||
const cylinderPositions = cylinderGeometry.attributes.position.values;
|
||||
const ellipsoidPositions = ellipsoidGeometry.attributes.position.values;
|
||||
|
||||
let numberOfPositions = boxPositions.length * numberOfBoxes;
|
||||
numberOfPositions += cylinderPositions.length * numberOfCylinders;
|
||||
numberOfPositions +=
|
||||
ellipsoidPositions.length * (numberOfEllipsoids + numberOfSpheres);
|
||||
|
||||
const boxIndices = boxGeometry.indices;
|
||||
const cylinderIndices = cylinderGeometry.indices;
|
||||
const ellipsoidIndices = ellipsoidGeometry.indices;
|
||||
|
||||
let numberOfIndices = boxIndices.length * numberOfBoxes;
|
||||
numberOfIndices += cylinderIndices.length * numberOfCylinders;
|
||||
numberOfIndices +=
|
||||
ellipsoidIndices.length * (numberOfEllipsoids + numberOfSpheres);
|
||||
|
||||
const positions = new Float32Array(numberOfPositions);
|
||||
const vertexBatchIds = new Uint16Array(numberOfPositions / 3);
|
||||
const indices = IndexDatatype.createTypedArray(
|
||||
numberOfPositions / 3,
|
||||
numberOfIndices,
|
||||
);
|
||||
|
||||
const numberOfGeometries =
|
||||
numberOfBoxes + numberOfCylinders + numberOfEllipsoids + numberOfSpheres;
|
||||
const batchIds = new Uint16Array(numberOfGeometries);
|
||||
const batchedIndices = new Array(numberOfGeometries);
|
||||
const indexOffsets = new Uint32Array(numberOfGeometries);
|
||||
const indexCounts = new Uint32Array(numberOfGeometries);
|
||||
const boundingVolumes = new Array(numberOfGeometries);
|
||||
|
||||
unpackBuffer(parameters.packedBuffer);
|
||||
|
||||
const options = {
|
||||
batchTableColors: new Uint32Array(parameters.batchTableColors),
|
||||
positions: positions,
|
||||
vertexBatchIds: vertexBatchIds,
|
||||
indices: indices,
|
||||
batchIds: batchIds,
|
||||
batchedIndices: batchedIndices,
|
||||
indexOffsets: indexOffsets,
|
||||
indexCounts: indexCounts,
|
||||
boundingVolumes: boundingVolumes,
|
||||
positionOffset: 0,
|
||||
batchIdIndex: 0,
|
||||
indexOffset: 0,
|
||||
batchedIndicesOffset: 0,
|
||||
modelMatrix: scratchMatrix4,
|
||||
center: scratchCenter,
|
||||
};
|
||||
|
||||
createPrimitive(
|
||||
options,
|
||||
boxes,
|
||||
boxBatchIds,
|
||||
boxGeometry,
|
||||
boxModelMatrixAndBoundingVolume,
|
||||
);
|
||||
createPrimitive(
|
||||
options,
|
||||
cylinders,
|
||||
cylinderBatchIds,
|
||||
cylinderGeometry,
|
||||
cylinderModelMatrixAndBoundingVolume,
|
||||
);
|
||||
createPrimitive(
|
||||
options,
|
||||
ellipsoids,
|
||||
ellipsoidBatchIds,
|
||||
ellipsoidGeometry,
|
||||
ellipsoidModelMatrixAndBoundingVolume,
|
||||
);
|
||||
createPrimitive(
|
||||
options,
|
||||
spheres,
|
||||
sphereBatchIds,
|
||||
ellipsoidGeometry,
|
||||
sphereModelMatrixAndBoundingVolume,
|
||||
);
|
||||
|
||||
const packedBuffer = packBuffer(
|
||||
indices.BYTES_PER_ELEMENT,
|
||||
batchedIndices,
|
||||
boundingVolumes,
|
||||
);
|
||||
transferableObjects.push(
|
||||
positions.buffer,
|
||||
vertexBatchIds.buffer,
|
||||
indices.buffer,
|
||||
);
|
||||
transferableObjects.push(
|
||||
batchIds.buffer,
|
||||
indexOffsets.buffer,
|
||||
indexCounts.buffer,
|
||||
);
|
||||
transferableObjects.push(packedBuffer.buffer);
|
||||
|
||||
return {
|
||||
positions: positions.buffer,
|
||||
vertexBatchIds: vertexBatchIds.buffer,
|
||||
indices: indices.buffer,
|
||||
indexOffsets: indexOffsets.buffer,
|
||||
indexCounts: indexCounts.buffer,
|
||||
batchIds: batchIds.buffer,
|
||||
packedBuffer: packedBuffer.buffer,
|
||||
};
|
||||
}
|
||||
export default createTaskProcessorWorker(createVectorTileGeometries);
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import AttributeCompression from "../Core/AttributeCompression.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const maxShort = 32767;
|
||||
|
||||
const scratchBVCartographic = new Cartographic();
|
||||
const scratchEncodedPosition = new Cartesian3();
|
||||
|
||||
const scratchRectangle = new Rectangle();
|
||||
const scratchEllipsoid = new Ellipsoid();
|
||||
const scratchMinMaxHeights = {
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
};
|
||||
|
||||
function unpackBuffer(packedBuffer) {
|
||||
packedBuffer = new Float64Array(packedBuffer);
|
||||
|
||||
let offset = 0;
|
||||
scratchMinMaxHeights.min = packedBuffer[offset++];
|
||||
scratchMinMaxHeights.max = packedBuffer[offset++];
|
||||
|
||||
Rectangle.unpack(packedBuffer, offset, scratchRectangle);
|
||||
offset += Rectangle.packedLength;
|
||||
|
||||
Ellipsoid.unpack(packedBuffer, offset, scratchEllipsoid);
|
||||
}
|
||||
|
||||
function createVectorTilePoints(parameters, transferableObjects) {
|
||||
const positions = new Uint16Array(parameters.positions);
|
||||
|
||||
unpackBuffer(parameters.packedBuffer);
|
||||
const rectangle = scratchRectangle;
|
||||
const ellipsoid = scratchEllipsoid;
|
||||
const minimumHeight = scratchMinMaxHeights.min;
|
||||
const maximumHeight = scratchMinMaxHeights.max;
|
||||
|
||||
const positionsLength = positions.length / 3;
|
||||
const uBuffer = positions.subarray(0, positionsLength);
|
||||
const vBuffer = positions.subarray(positionsLength, 2 * positionsLength);
|
||||
const heightBuffer = positions.subarray(
|
||||
2 * positionsLength,
|
||||
3 * positionsLength,
|
||||
);
|
||||
AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
|
||||
|
||||
const decoded = new Float64Array(positions.length);
|
||||
for (let i = 0; i < positionsLength; ++i) {
|
||||
const u = uBuffer[i];
|
||||
const v = vBuffer[i];
|
||||
const h = heightBuffer[i];
|
||||
|
||||
const lon = CesiumMath.lerp(rectangle.west, rectangle.east, u / maxShort);
|
||||
const lat = CesiumMath.lerp(rectangle.south, rectangle.north, v / maxShort);
|
||||
const alt = CesiumMath.lerp(minimumHeight, maximumHeight, h / maxShort);
|
||||
|
||||
const cartographic = Cartographic.fromRadians(
|
||||
lon,
|
||||
lat,
|
||||
alt,
|
||||
scratchBVCartographic,
|
||||
);
|
||||
const decodedPosition = ellipsoid.cartographicToCartesian(
|
||||
cartographic,
|
||||
scratchEncodedPosition,
|
||||
);
|
||||
Cartesian3.pack(decodedPosition, decoded, i * 3);
|
||||
}
|
||||
|
||||
transferableObjects.push(decoded.buffer);
|
||||
|
||||
return {
|
||||
positions: decoded.buffer,
|
||||
};
|
||||
}
|
||||
export default createTaskProcessorWorker(createVectorTilePoints);
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
import AttributeCompression from "../Core/AttributeCompression.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import Color from "../Core/Color.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const scratchCenter = new Cartesian3();
|
||||
const scratchEllipsoid = new Ellipsoid();
|
||||
const scratchRectangle = new Rectangle();
|
||||
const scratchScalars = {
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
indexBytesPerElement: undefined,
|
||||
};
|
||||
|
||||
function unpackBuffer(buffer) {
|
||||
const packedBuffer = new Float64Array(buffer);
|
||||
|
||||
let offset = 0;
|
||||
scratchScalars.indexBytesPerElement = packedBuffer[offset++];
|
||||
|
||||
scratchScalars.min = packedBuffer[offset++];
|
||||
scratchScalars.max = packedBuffer[offset++];
|
||||
|
||||
Cartesian3.unpack(packedBuffer, offset, scratchCenter);
|
||||
offset += Cartesian3.packedLength;
|
||||
|
||||
Ellipsoid.unpack(packedBuffer, offset, scratchEllipsoid);
|
||||
offset += Ellipsoid.packedLength;
|
||||
|
||||
Rectangle.unpack(packedBuffer, offset, scratchRectangle);
|
||||
}
|
||||
|
||||
function packedBatchedIndicesLength(batchedIndices) {
|
||||
const length = batchedIndices.length;
|
||||
let count = 0;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
count += Color.packedLength + 3 + batchedIndices[i].batchIds.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function packBuffer(indexDatatype, boundingVolumes, batchedIndices) {
|
||||
const numBVs = boundingVolumes.length;
|
||||
const length =
|
||||
1 +
|
||||
1 +
|
||||
numBVs * OrientedBoundingBox.packedLength +
|
||||
1 +
|
||||
packedBatchedIndicesLength(batchedIndices);
|
||||
|
||||
const packedBuffer = new Float64Array(length);
|
||||
|
||||
let offset = 0;
|
||||
packedBuffer[offset++] = indexDatatype;
|
||||
packedBuffer[offset++] = numBVs;
|
||||
|
||||
for (let i = 0; i < numBVs; ++i) {
|
||||
OrientedBoundingBox.pack(boundingVolumes[i], packedBuffer, offset);
|
||||
offset += OrientedBoundingBox.packedLength;
|
||||
}
|
||||
|
||||
const indicesLength = batchedIndices.length;
|
||||
packedBuffer[offset++] = indicesLength;
|
||||
|
||||
for (let j = 0; j < indicesLength; ++j) {
|
||||
const batchedIndex = batchedIndices[j];
|
||||
|
||||
Color.pack(batchedIndex.color, packedBuffer, offset);
|
||||
offset += Color.packedLength;
|
||||
|
||||
packedBuffer[offset++] = batchedIndex.offset;
|
||||
packedBuffer[offset++] = batchedIndex.count;
|
||||
|
||||
const batchIds = batchedIndex.batchIds;
|
||||
const batchIdsLength = batchIds.length;
|
||||
packedBuffer[offset++] = batchIdsLength;
|
||||
|
||||
for (let k = 0; k < batchIdsLength; ++k) {
|
||||
packedBuffer[offset++] = batchIds[k];
|
||||
}
|
||||
}
|
||||
|
||||
return packedBuffer;
|
||||
}
|
||||
|
||||
const maxShort = 32767;
|
||||
|
||||
const scratchEncodedPosition = new Cartesian3();
|
||||
const scratchNormal = new Cartesian3();
|
||||
const scratchScaledNormal = new Cartesian3();
|
||||
const scratchMinHeightPosition = new Cartesian3();
|
||||
const scratchMaxHeightPosition = new Cartesian3();
|
||||
const scratchBVCartographic = new Cartographic();
|
||||
const scratchBVRectangle = new Rectangle();
|
||||
|
||||
function createVectorTilePolygons(parameters, transferableObjects) {
|
||||
unpackBuffer(parameters.packedBuffer);
|
||||
|
||||
let indices;
|
||||
const indexBytesPerElement = scratchScalars.indexBytesPerElement;
|
||||
if (indexBytesPerElement === 2) {
|
||||
indices = new Uint16Array(parameters.indices);
|
||||
} else {
|
||||
indices = new Uint32Array(parameters.indices);
|
||||
}
|
||||
|
||||
const positions = new Uint16Array(parameters.positions);
|
||||
const counts = new Uint32Array(parameters.counts);
|
||||
const indexCounts = new Uint32Array(parameters.indexCounts);
|
||||
const batchIds = new Uint32Array(parameters.batchIds);
|
||||
const batchTableColors = new Uint32Array(parameters.batchTableColors);
|
||||
|
||||
const boundingVolumes = new Array(counts.length);
|
||||
|
||||
const center = scratchCenter;
|
||||
const ellipsoid = scratchEllipsoid;
|
||||
let rectangle = scratchRectangle;
|
||||
const minHeight = scratchScalars.min;
|
||||
const maxHeight = scratchScalars.max;
|
||||
|
||||
let minimumHeights = parameters.minimumHeights;
|
||||
let maximumHeights = parameters.maximumHeights;
|
||||
if (defined(minimumHeights) && defined(maximumHeights)) {
|
||||
minimumHeights = new Float32Array(minimumHeights);
|
||||
maximumHeights = new Float32Array(maximumHeights);
|
||||
}
|
||||
|
||||
let i;
|
||||
let j;
|
||||
let rgba;
|
||||
|
||||
const positionsLength = positions.length / 2;
|
||||
const uBuffer = positions.subarray(0, positionsLength);
|
||||
const vBuffer = positions.subarray(positionsLength, 2 * positionsLength);
|
||||
AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer);
|
||||
|
||||
const decodedPositions = new Float64Array(positionsLength * 3);
|
||||
for (i = 0; i < positionsLength; ++i) {
|
||||
const u = uBuffer[i];
|
||||
const v = vBuffer[i];
|
||||
|
||||
const x = CesiumMath.lerp(rectangle.west, rectangle.east, u / maxShort);
|
||||
const y = CesiumMath.lerp(rectangle.south, rectangle.north, v / maxShort);
|
||||
|
||||
const cart = Cartographic.fromRadians(x, y, 0.0, scratchBVCartographic);
|
||||
const decodedPosition = ellipsoid.cartographicToCartesian(
|
||||
cart,
|
||||
scratchEncodedPosition,
|
||||
);
|
||||
Cartesian3.pack(decodedPosition, decodedPositions, i * 3);
|
||||
}
|
||||
|
||||
const countsLength = counts.length;
|
||||
const offsets = new Array(countsLength);
|
||||
const indexOffsets = new Array(countsLength);
|
||||
let currentOffset = 0;
|
||||
let currentIndexOffset = 0;
|
||||
for (i = 0; i < countsLength; ++i) {
|
||||
offsets[i] = currentOffset;
|
||||
indexOffsets[i] = currentIndexOffset;
|
||||
|
||||
currentOffset += counts[i];
|
||||
currentIndexOffset += indexCounts[i];
|
||||
}
|
||||
|
||||
const batchedPositions = new Float32Array(positionsLength * 3 * 2);
|
||||
const batchedIds = new Uint16Array(positionsLength * 2);
|
||||
const batchedIndexOffsets = new Uint32Array(indexOffsets.length);
|
||||
const batchedIndexCounts = new Uint32Array(indexCounts.length);
|
||||
let batchedIndices = [];
|
||||
|
||||
const colorToBuffers = {};
|
||||
for (i = 0; i < countsLength; ++i) {
|
||||
rgba = batchTableColors[i];
|
||||
if (!defined(colorToBuffers[rgba])) {
|
||||
colorToBuffers[rgba] = {
|
||||
positionLength: counts[i],
|
||||
indexLength: indexCounts[i],
|
||||
offset: 0,
|
||||
indexOffset: 0,
|
||||
batchIds: [i],
|
||||
};
|
||||
} else {
|
||||
colorToBuffers[rgba].positionLength += counts[i];
|
||||
colorToBuffers[rgba].indexLength += indexCounts[i];
|
||||
colorToBuffers[rgba].batchIds.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// get the offsets and counts for the positions and indices of each primitive
|
||||
let buffer;
|
||||
let byColorPositionOffset = 0;
|
||||
let byColorIndexOffset = 0;
|
||||
for (rgba in colorToBuffers) {
|
||||
if (colorToBuffers.hasOwnProperty(rgba)) {
|
||||
buffer = colorToBuffers[rgba];
|
||||
buffer.offset = byColorPositionOffset;
|
||||
buffer.indexOffset = byColorIndexOffset;
|
||||
|
||||
const positionLength = buffer.positionLength * 2;
|
||||
const indexLength = buffer.indexLength * 2 + buffer.positionLength * 6;
|
||||
|
||||
byColorPositionOffset += positionLength;
|
||||
byColorIndexOffset += indexLength;
|
||||
|
||||
buffer.indexLength = indexLength;
|
||||
}
|
||||
}
|
||||
|
||||
const batchedDrawCalls = [];
|
||||
|
||||
for (rgba in colorToBuffers) {
|
||||
if (colorToBuffers.hasOwnProperty(rgba)) {
|
||||
buffer = colorToBuffers[rgba];
|
||||
|
||||
batchedDrawCalls.push({
|
||||
color: Color.fromRgba(parseInt(rgba)),
|
||||
offset: buffer.indexOffset,
|
||||
count: buffer.indexLength,
|
||||
batchIds: buffer.batchIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < countsLength; ++i) {
|
||||
rgba = batchTableColors[i];
|
||||
|
||||
buffer = colorToBuffers[rgba];
|
||||
const positionOffset = buffer.offset;
|
||||
let positionIndex = positionOffset * 3;
|
||||
let batchIdIndex = positionOffset;
|
||||
|
||||
const polygonOffset = offsets[i];
|
||||
const polygonCount = counts[i];
|
||||
const batchId = batchIds[i];
|
||||
|
||||
let polygonMinimumHeight = minHeight;
|
||||
let polygonMaximumHeight = maxHeight;
|
||||
if (defined(minimumHeights) && defined(maximumHeights)) {
|
||||
polygonMinimumHeight = minimumHeights[i];
|
||||
polygonMaximumHeight = maximumHeights[i];
|
||||
}
|
||||
|
||||
let minLat = Number.POSITIVE_INFINITY;
|
||||
let maxLat = Number.NEGATIVE_INFINITY;
|
||||
let minLon = Number.POSITIVE_INFINITY;
|
||||
let maxLon = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (j = 0; j < polygonCount; ++j) {
|
||||
const position = Cartesian3.unpack(
|
||||
decodedPositions,
|
||||
polygonOffset * 3 + j * 3,
|
||||
scratchEncodedPosition,
|
||||
);
|
||||
ellipsoid.scaleToGeodeticSurface(position, position);
|
||||
|
||||
const carto = ellipsoid.cartesianToCartographic(
|
||||
position,
|
||||
scratchBVCartographic,
|
||||
);
|
||||
const lat = carto.latitude;
|
||||
const lon = carto.longitude;
|
||||
|
||||
minLat = Math.min(lat, minLat);
|
||||
maxLat = Math.max(lat, maxLat);
|
||||
minLon = Math.min(lon, minLon);
|
||||
maxLon = Math.max(lon, maxLon);
|
||||
|
||||
const normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
|
||||
let scaledNormal = Cartesian3.multiplyByScalar(
|
||||
normal,
|
||||
polygonMinimumHeight,
|
||||
scratchScaledNormal,
|
||||
);
|
||||
const minHeightPosition = Cartesian3.add(
|
||||
position,
|
||||
scaledNormal,
|
||||
scratchMinHeightPosition,
|
||||
);
|
||||
|
||||
scaledNormal = Cartesian3.multiplyByScalar(
|
||||
normal,
|
||||
polygonMaximumHeight,
|
||||
scaledNormal,
|
||||
);
|
||||
const maxHeightPosition = Cartesian3.add(
|
||||
position,
|
||||
scaledNormal,
|
||||
scratchMaxHeightPosition,
|
||||
);
|
||||
|
||||
Cartesian3.subtract(maxHeightPosition, center, maxHeightPosition);
|
||||
Cartesian3.subtract(minHeightPosition, center, minHeightPosition);
|
||||
|
||||
Cartesian3.pack(maxHeightPosition, batchedPositions, positionIndex);
|
||||
Cartesian3.pack(minHeightPosition, batchedPositions, positionIndex + 3);
|
||||
|
||||
batchedIds[batchIdIndex] = batchId;
|
||||
batchedIds[batchIdIndex + 1] = batchId;
|
||||
|
||||
positionIndex += 6;
|
||||
batchIdIndex += 2;
|
||||
}
|
||||
|
||||
rectangle = scratchBVRectangle;
|
||||
rectangle.west = minLon;
|
||||
rectangle.east = maxLon;
|
||||
rectangle.south = minLat;
|
||||
rectangle.north = maxLat;
|
||||
|
||||
boundingVolumes[i] = OrientedBoundingBox.fromRectangle(
|
||||
rectangle,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
ellipsoid,
|
||||
);
|
||||
|
||||
let indicesIndex = buffer.indexOffset;
|
||||
|
||||
const indexOffset = indexOffsets[i];
|
||||
const indexCount = indexCounts[i];
|
||||
|
||||
batchedIndexOffsets[i] = indicesIndex;
|
||||
|
||||
for (j = 0; j < indexCount; j += 3) {
|
||||
const i0 = indices[indexOffset + j] - polygonOffset;
|
||||
const i1 = indices[indexOffset + j + 1] - polygonOffset;
|
||||
const i2 = indices[indexOffset + j + 2] - polygonOffset;
|
||||
|
||||
// triangle on the top of the extruded polygon
|
||||
batchedIndices[indicesIndex++] = i0 * 2 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = i1 * 2 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = i2 * 2 + positionOffset;
|
||||
|
||||
// triangle on the bottom of the extruded polygon
|
||||
batchedIndices[indicesIndex++] = i2 * 2 + 1 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = i1 * 2 + 1 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = i0 * 2 + 1 + positionOffset;
|
||||
}
|
||||
|
||||
// indices for the walls of the extruded polygon
|
||||
for (j = 0; j < polygonCount; ++j) {
|
||||
const v0 = j;
|
||||
const v1 = (j + 1) % polygonCount;
|
||||
|
||||
batchedIndices[indicesIndex++] = v0 * 2 + 1 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = v1 * 2 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = v0 * 2 + positionOffset;
|
||||
|
||||
batchedIndices[indicesIndex++] = v0 * 2 + 1 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = v1 * 2 + 1 + positionOffset;
|
||||
batchedIndices[indicesIndex++] = v1 * 2 + positionOffset;
|
||||
}
|
||||
|
||||
buffer.offset += polygonCount * 2;
|
||||
buffer.indexOffset = indicesIndex;
|
||||
|
||||
batchedIndexCounts[i] = indicesIndex - batchedIndexOffsets[i];
|
||||
}
|
||||
|
||||
batchedIndices = IndexDatatype.createTypedArray(
|
||||
batchedPositions.length / 3,
|
||||
batchedIndices,
|
||||
);
|
||||
|
||||
const batchedIndicesLength = batchedDrawCalls.length;
|
||||
for (let m = 0; m < batchedIndicesLength; ++m) {
|
||||
const tempIds = batchedDrawCalls[m].batchIds;
|
||||
let count = 0;
|
||||
const tempIdsLength = tempIds.length;
|
||||
for (let n = 0; n < tempIdsLength; ++n) {
|
||||
count += batchedIndexCounts[tempIds[n]];
|
||||
}
|
||||
batchedDrawCalls[m].count = count;
|
||||
}
|
||||
|
||||
const indexDatatype =
|
||||
batchedIndices.BYTES_PER_ELEMENT === 2
|
||||
? IndexDatatype.UNSIGNED_SHORT
|
||||
: IndexDatatype.UNSIGNED_INT;
|
||||
const packedBuffer = packBuffer(
|
||||
indexDatatype,
|
||||
boundingVolumes,
|
||||
batchedDrawCalls,
|
||||
);
|
||||
|
||||
transferableObjects.push(
|
||||
batchedPositions.buffer,
|
||||
batchedIndices.buffer,
|
||||
batchedIndexOffsets.buffer,
|
||||
batchedIndexCounts.buffer,
|
||||
batchedIds.buffer,
|
||||
packedBuffer.buffer,
|
||||
);
|
||||
|
||||
return {
|
||||
positions: batchedPositions.buffer,
|
||||
indices: batchedIndices.buffer,
|
||||
indexOffsets: batchedIndexOffsets.buffer,
|
||||
indexCounts: batchedIndexCounts.buffer,
|
||||
batchIds: batchedIds.buffer,
|
||||
packedBuffer: packedBuffer.buffer,
|
||||
};
|
||||
}
|
||||
export default createTaskProcessorWorker(createVectorTilePolygons);
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import combine from "../Core/combine.js";
|
||||
import decodeVectorPolylinePositions from "../Core/decodeVectorPolylinePositions.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const scratchRectangle = new Rectangle();
|
||||
const scratchEllipsoid = new Ellipsoid();
|
||||
const scratchCenter = new Cartesian3();
|
||||
const scratchMinMaxHeights = {
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
};
|
||||
|
||||
function unpackBuffer(packedBuffer) {
|
||||
packedBuffer = new Float64Array(packedBuffer);
|
||||
|
||||
let offset = 0;
|
||||
scratchMinMaxHeights.min = packedBuffer[offset++];
|
||||
scratchMinMaxHeights.max = packedBuffer[offset++];
|
||||
|
||||
Rectangle.unpack(packedBuffer, offset, scratchRectangle);
|
||||
offset += Rectangle.packedLength;
|
||||
|
||||
Ellipsoid.unpack(packedBuffer, offset, scratchEllipsoid);
|
||||
offset += Ellipsoid.packedLength;
|
||||
|
||||
Cartesian3.unpack(packedBuffer, offset, scratchCenter);
|
||||
}
|
||||
|
||||
function getPositionOffsets(counts) {
|
||||
const countsLength = counts.length;
|
||||
const positionOffsets = new Uint32Array(countsLength + 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < countsLength; ++i) {
|
||||
positionOffsets[i] = offset;
|
||||
offset += counts[i];
|
||||
}
|
||||
positionOffsets[countsLength] = offset;
|
||||
return positionOffsets;
|
||||
}
|
||||
|
||||
const scratchP0 = new Cartesian3();
|
||||
const scratchP1 = new Cartesian3();
|
||||
const scratchPrev = new Cartesian3();
|
||||
const scratchCur = new Cartesian3();
|
||||
const scratchNext = new Cartesian3();
|
||||
|
||||
function createVectorTilePolylines(parameters, transferableObjects) {
|
||||
const encodedPositions = new Uint16Array(parameters.positions);
|
||||
const widths = new Uint16Array(parameters.widths);
|
||||
const counts = new Uint32Array(parameters.counts);
|
||||
const batchIds = new Uint16Array(parameters.batchIds);
|
||||
|
||||
unpackBuffer(parameters.packedBuffer);
|
||||
const rectangle = scratchRectangle;
|
||||
const ellipsoid = scratchEllipsoid;
|
||||
const center = scratchCenter;
|
||||
const minimumHeight = scratchMinMaxHeights.min;
|
||||
const maximumHeight = scratchMinMaxHeights.max;
|
||||
|
||||
const positions = decodeVectorPolylinePositions(
|
||||
encodedPositions,
|
||||
rectangle,
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
ellipsoid,
|
||||
);
|
||||
|
||||
const positionsLength = positions.length / 3;
|
||||
const size = positionsLength * 4 - 4;
|
||||
|
||||
const curPositions = new Float32Array(size * 3);
|
||||
const prevPositions = new Float32Array(size * 3);
|
||||
const nextPositions = new Float32Array(size * 3);
|
||||
const expandAndWidth = new Float32Array(size * 2);
|
||||
const vertexBatchIds = new Uint16Array(size);
|
||||
|
||||
let positionIndex = 0;
|
||||
let expandAndWidthIndex = 0;
|
||||
let batchIdIndex = 0;
|
||||
|
||||
let i;
|
||||
let offset = 0;
|
||||
let length = counts.length;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
const count = counts[i];
|
||||
const width = widths[i];
|
||||
const batchId = batchIds[i];
|
||||
|
||||
for (let j = 0; j < count; ++j) {
|
||||
let previous;
|
||||
if (j === 0) {
|
||||
const p0 = Cartesian3.unpack(positions, offset * 3, scratchP0);
|
||||
const p1 = Cartesian3.unpack(positions, (offset + 1) * 3, scratchP1);
|
||||
|
||||
previous = Cartesian3.subtract(p0, p1, scratchPrev);
|
||||
Cartesian3.add(p0, previous, previous);
|
||||
} else {
|
||||
previous = Cartesian3.unpack(
|
||||
positions,
|
||||
(offset + j - 1) * 3,
|
||||
scratchPrev,
|
||||
);
|
||||
}
|
||||
|
||||
const current = Cartesian3.unpack(
|
||||
positions,
|
||||
(offset + j) * 3,
|
||||
scratchCur,
|
||||
);
|
||||
|
||||
let next;
|
||||
if (j === count - 1) {
|
||||
const p2 = Cartesian3.unpack(
|
||||
positions,
|
||||
(offset + count - 1) * 3,
|
||||
scratchP0,
|
||||
);
|
||||
const p3 = Cartesian3.unpack(
|
||||
positions,
|
||||
(offset + count - 2) * 3,
|
||||
scratchP1,
|
||||
);
|
||||
|
||||
next = Cartesian3.subtract(p2, p3, scratchNext);
|
||||
Cartesian3.add(p2, next, next);
|
||||
} else {
|
||||
next = Cartesian3.unpack(positions, (offset + j + 1) * 3, scratchNext);
|
||||
}
|
||||
|
||||
Cartesian3.subtract(previous, center, previous);
|
||||
Cartesian3.subtract(current, center, current);
|
||||
Cartesian3.subtract(next, center, next);
|
||||
|
||||
const startK = j === 0 ? 2 : 0;
|
||||
const endK = j === count - 1 ? 2 : 4;
|
||||
|
||||
for (let k = startK; k < endK; ++k) {
|
||||
Cartesian3.pack(current, curPositions, positionIndex);
|
||||
Cartesian3.pack(previous, prevPositions, positionIndex);
|
||||
Cartesian3.pack(next, nextPositions, positionIndex);
|
||||
positionIndex += 3;
|
||||
|
||||
const direction = k - 2 < 0 ? -1.0 : 1.0;
|
||||
expandAndWidth[expandAndWidthIndex++] = 2 * (k % 2) - 1;
|
||||
expandAndWidth[expandAndWidthIndex++] = direction * width;
|
||||
|
||||
vertexBatchIds[batchIdIndex++] = batchId;
|
||||
}
|
||||
}
|
||||
|
||||
offset += count;
|
||||
}
|
||||
|
||||
const indices = IndexDatatype.createTypedArray(size, positionsLength * 6 - 6);
|
||||
let index = 0;
|
||||
let indicesIndex = 0;
|
||||
length = positionsLength - 1;
|
||||
for (i = 0; i < length; ++i) {
|
||||
indices[indicesIndex++] = index;
|
||||
indices[indicesIndex++] = index + 2;
|
||||
indices[indicesIndex++] = index + 1;
|
||||
|
||||
indices[indicesIndex++] = index + 1;
|
||||
indices[indicesIndex++] = index + 2;
|
||||
indices[indicesIndex++] = index + 3;
|
||||
|
||||
index += 4;
|
||||
}
|
||||
|
||||
transferableObjects.push(
|
||||
curPositions.buffer,
|
||||
prevPositions.buffer,
|
||||
nextPositions.buffer,
|
||||
);
|
||||
transferableObjects.push(
|
||||
expandAndWidth.buffer,
|
||||
vertexBatchIds.buffer,
|
||||
indices.buffer,
|
||||
);
|
||||
|
||||
let results = {
|
||||
indexDatatype:
|
||||
indices.BYTES_PER_ELEMENT === 2
|
||||
? IndexDatatype.UNSIGNED_SHORT
|
||||
: IndexDatatype.UNSIGNED_INT,
|
||||
currentPositions: curPositions.buffer,
|
||||
previousPositions: prevPositions.buffer,
|
||||
nextPositions: nextPositions.buffer,
|
||||
expandAndWidth: expandAndWidth.buffer,
|
||||
batchIds: vertexBatchIds.buffer,
|
||||
indices: indices.buffer,
|
||||
};
|
||||
|
||||
if (parameters.keepDecodedPositions) {
|
||||
const positionOffsets = getPositionOffsets(counts);
|
||||
transferableObjects.push(positions.buffer, positionOffsets.buffer);
|
||||
results = combine(results, {
|
||||
decodedPositions: positions.buffer,
|
||||
decodedPositionOffsets: positionOffsets.buffer,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
export default createTaskProcessorWorker(createVectorTilePolylines);
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
import Cesium3DTilesTerrainGeometryProcessor from "../Core/Cesium3DTilesTerrainGeometryProcessor.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {Cesium3DTilesTerrainGeometryProcessor.CreateMeshOptions} options An object describing options for mesh creation.
|
||||
* @param {ArrayBuffer[]} transferableObjects An array of buffers that can be transferred back to the main thread.
|
||||
* @returns {Promise<object>} A promise that resolves to an object containing selected info from the created TerrainMesh.
|
||||
*/
|
||||
function createVerticesFromCesium3DTilesTerrain(options, transferableObjects) {
|
||||
const meshPromise = Cesium3DTilesTerrainGeometryProcessor.createMesh(options);
|
||||
return meshPromise.then(function (mesh) {
|
||||
const verticesBuffer = mesh.vertices.buffer;
|
||||
const indicesBuffer = mesh.indices.buffer;
|
||||
const westIndicesBuffer = mesh.westIndicesSouthToNorth.buffer;
|
||||
const southIndicesBuffer = mesh.southIndicesEastToWest.buffer;
|
||||
const eastIndicesBuffer = mesh.eastIndicesNorthToSouth.buffer;
|
||||
const northIndicesBuffer = mesh.northIndicesWestToEast.buffer;
|
||||
|
||||
transferableObjects.push(
|
||||
verticesBuffer,
|
||||
indicesBuffer,
|
||||
westIndicesBuffer,
|
||||
southIndicesBuffer,
|
||||
eastIndicesBuffer,
|
||||
northIndicesBuffer,
|
||||
);
|
||||
|
||||
return {
|
||||
verticesBuffer: verticesBuffer,
|
||||
indicesBuffer: indicesBuffer,
|
||||
vertexCountWithoutSkirts: mesh.vertexCountWithoutSkirts,
|
||||
indexCountWithoutSkirts: mesh.indexCountWithoutSkirts,
|
||||
encoding: mesh.encoding,
|
||||
westIndicesBuffer: westIndicesBuffer,
|
||||
southIndicesBuffer: southIndicesBuffer,
|
||||
eastIndicesBuffer: eastIndicesBuffer,
|
||||
northIndicesBuffer: northIndicesBuffer,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(
|
||||
createVerticesFromCesium3DTilesTerrain,
|
||||
);
|
||||
Generated
Vendored
+651
@@ -0,0 +1,651 @@
|
||||
import AxisAlignedBoundingBox from "../Core/AxisAlignedBoundingBox.js";
|
||||
import BoundingSphere from "../Core/BoundingSphere.js";
|
||||
import Cartesian2 from "../Core/Cartesian2.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import Matrix4 from "../Core/Matrix4.js";
|
||||
import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import TerrainEncoding from "../Core/TerrainEncoding.js";
|
||||
import Transforms from "../Core/Transforms.js";
|
||||
import WebMercatorProjection from "../Core/WebMercatorProjection.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfFloat = Float32Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfDouble = Float64Array.BYTES_PER_ELEMENT;
|
||||
|
||||
function indexOfEpsilon(arr, elem, elemType) {
|
||||
elemType = elemType ?? CesiumMath;
|
||||
const count = arr.length;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
if (elemType.equalsEpsilon(arr[i], elem, CesiumMath.EPSILON12)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function createVerticesFromGoogleEarthEnterpriseBuffer(
|
||||
parameters,
|
||||
transferableObjects,
|
||||
) {
|
||||
parameters.ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
|
||||
parameters.rectangle = Rectangle.clone(parameters.rectangle);
|
||||
|
||||
const statistics = processBuffer(
|
||||
parameters.buffer,
|
||||
parameters.relativeToCenter,
|
||||
parameters.ellipsoid,
|
||||
parameters.rectangle,
|
||||
parameters.nativeRectangle,
|
||||
parameters.exaggeration,
|
||||
parameters.exaggerationRelativeHeight,
|
||||
parameters.skirtHeight,
|
||||
parameters.includeWebMercatorT,
|
||||
parameters.negativeAltitudeExponentBias,
|
||||
parameters.negativeElevationThreshold,
|
||||
);
|
||||
const vertices = statistics.vertices;
|
||||
transferableObjects.push(vertices.buffer);
|
||||
const indices = statistics.indices;
|
||||
transferableObjects.push(indices.buffer);
|
||||
|
||||
return {
|
||||
vertices: vertices.buffer,
|
||||
indices: indices.buffer,
|
||||
numberOfAttributes: statistics.encoding.stride,
|
||||
minimumHeight: statistics.minimumHeight,
|
||||
maximumHeight: statistics.maximumHeight,
|
||||
boundingSphere3D: statistics.boundingSphere3D,
|
||||
orientedBoundingBox: statistics.orientedBoundingBox,
|
||||
occludeePointInScaledSpace: statistics.occludeePointInScaledSpace,
|
||||
encoding: statistics.encoding,
|
||||
vertexCountWithoutSkirts: statistics.vertexCountWithoutSkirts,
|
||||
indexCountWithoutSkirts: statistics.indexCountWithoutSkirts,
|
||||
westIndicesSouthToNorth: statistics.westIndicesSouthToNorth,
|
||||
southIndicesEastToWest: statistics.southIndicesEastToWest,
|
||||
eastIndicesNorthToSouth: statistics.eastIndicesNorthToSouth,
|
||||
northIndicesWestToEast: statistics.northIndicesWestToEast,
|
||||
};
|
||||
}
|
||||
|
||||
const scratchCartographic = new Cartographic();
|
||||
const scratchCartesian = new Cartesian3();
|
||||
const minimumScratch = new Cartesian3();
|
||||
const maximumScratch = new Cartesian3();
|
||||
const matrix4Scratch = new Matrix4();
|
||||
|
||||
function processBuffer(
|
||||
buffer,
|
||||
relativeToCenter,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
nativeRectangle,
|
||||
exaggeration,
|
||||
exaggerationRelativeHeight,
|
||||
skirtHeight,
|
||||
includeWebMercatorT,
|
||||
negativeAltitudeExponentBias,
|
||||
negativeElevationThreshold,
|
||||
) {
|
||||
let geographicWest;
|
||||
let geographicSouth;
|
||||
let geographicEast;
|
||||
let geographicNorth;
|
||||
let rectangleWidth, rectangleHeight;
|
||||
|
||||
if (!defined(rectangle)) {
|
||||
geographicWest = CesiumMath.toRadians(nativeRectangle.west);
|
||||
geographicSouth = CesiumMath.toRadians(nativeRectangle.south);
|
||||
geographicEast = CesiumMath.toRadians(nativeRectangle.east);
|
||||
geographicNorth = CesiumMath.toRadians(nativeRectangle.north);
|
||||
rectangleWidth = CesiumMath.toRadians(rectangle.width);
|
||||
rectangleHeight = CesiumMath.toRadians(rectangle.height);
|
||||
} else {
|
||||
geographicWest = rectangle.west;
|
||||
geographicSouth = rectangle.south;
|
||||
geographicEast = rectangle.east;
|
||||
geographicNorth = rectangle.north;
|
||||
rectangleWidth = rectangle.width;
|
||||
rectangleHeight = rectangle.height;
|
||||
}
|
||||
|
||||
// Keep track of quad borders so we can remove duplicates around the borders
|
||||
const quadBorderLatitudes = [geographicSouth, geographicNorth];
|
||||
const quadBorderLongitudes = [geographicWest, geographicEast];
|
||||
|
||||
const fromENU = Transforms.eastNorthUpToFixedFrame(
|
||||
relativeToCenter,
|
||||
ellipsoid,
|
||||
);
|
||||
const toENU = Matrix4.inverseTransformation(fromENU, matrix4Scratch);
|
||||
|
||||
let southMercatorY;
|
||||
let oneOverMercatorHeight;
|
||||
if (includeWebMercatorT) {
|
||||
southMercatorY =
|
||||
WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicSouth);
|
||||
oneOverMercatorHeight =
|
||||
1.0 /
|
||||
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicNorth) -
|
||||
southMercatorY);
|
||||
}
|
||||
|
||||
const hasExaggeration = exaggeration !== 1.0;
|
||||
const includeGeodeticSurfaceNormals = hasExaggeration;
|
||||
|
||||
const dv = new DataView(buffer);
|
||||
|
||||
let minHeight = Number.POSITIVE_INFINITY;
|
||||
let maxHeight = Number.NEGATIVE_INFINITY;
|
||||
|
||||
const minimum = minimumScratch;
|
||||
minimum.x = Number.POSITIVE_INFINITY;
|
||||
minimum.y = Number.POSITIVE_INFINITY;
|
||||
minimum.z = Number.POSITIVE_INFINITY;
|
||||
|
||||
const maximum = maximumScratch;
|
||||
maximum.x = Number.NEGATIVE_INFINITY;
|
||||
maximum.y = Number.NEGATIVE_INFINITY;
|
||||
maximum.z = Number.NEGATIVE_INFINITY;
|
||||
|
||||
// Compute sizes
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
let indicesSize = 0;
|
||||
let quadSize;
|
||||
let quad;
|
||||
for (quad = 0; quad < 4; ++quad) {
|
||||
let o = offset;
|
||||
quadSize = dv.getUint32(o, true);
|
||||
o += sizeOfUint32;
|
||||
|
||||
const x = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0);
|
||||
o += sizeOfDouble;
|
||||
if (indexOfEpsilon(quadBorderLongitudes, x) === -1) {
|
||||
quadBorderLongitudes.push(x);
|
||||
}
|
||||
|
||||
const y = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0);
|
||||
o += sizeOfDouble;
|
||||
if (indexOfEpsilon(quadBorderLatitudes, y) === -1) {
|
||||
quadBorderLatitudes.push(y);
|
||||
}
|
||||
|
||||
o += 2 * sizeOfDouble; // stepX + stepY
|
||||
|
||||
let c = dv.getInt32(o, true); // Read point count
|
||||
o += sizeOfInt32;
|
||||
size += c;
|
||||
|
||||
c = dv.getInt32(o, true); // Read index count
|
||||
indicesSize += c * 3;
|
||||
|
||||
offset += quadSize + sizeOfUint32; // Jump to next quad
|
||||
}
|
||||
|
||||
// Quad Border points to remove duplicates
|
||||
const quadBorderPoints = [];
|
||||
const quadBorderIndices = [];
|
||||
|
||||
// Create arrays
|
||||
const positions = new Array(size);
|
||||
const uvs = new Array(size);
|
||||
const heights = new Array(size);
|
||||
const webMercatorTs = includeWebMercatorT ? new Array(size) : [];
|
||||
const geodeticSurfaceNormals = includeGeodeticSurfaceNormals
|
||||
? new Array(size)
|
||||
: [];
|
||||
const indices = new Array(indicesSize);
|
||||
|
||||
// Points are laid out in rows starting at SW, so storing border points as we
|
||||
// come across them all points will be adjacent.
|
||||
const westBorder = [];
|
||||
const southBorder = [];
|
||||
const eastBorder = [];
|
||||
const northBorder = [];
|
||||
|
||||
// Each tile is split into 4 parts
|
||||
let pointOffset = 0;
|
||||
let indicesOffset = 0;
|
||||
offset = 0;
|
||||
for (quad = 0; quad < 4; ++quad) {
|
||||
quadSize = dv.getUint32(offset, true);
|
||||
offset += sizeOfUint32;
|
||||
const startQuad = offset;
|
||||
|
||||
const originX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
|
||||
offset += sizeOfDouble;
|
||||
|
||||
const originY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
|
||||
offset += sizeOfDouble;
|
||||
|
||||
const stepX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
|
||||
const halfStepX = stepX * 0.5;
|
||||
offset += sizeOfDouble;
|
||||
|
||||
const stepY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0);
|
||||
const halfStepY = stepY * 0.5;
|
||||
offset += sizeOfDouble;
|
||||
|
||||
const numPoints = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
const numFaces = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
//const level = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
// Keep track of quad indices to overall tile indices
|
||||
const indicesMapping = new Array(numPoints);
|
||||
for (let i = 0; i < numPoints; ++i) {
|
||||
const longitude = originX + dv.getUint8(offset++) * stepX;
|
||||
scratchCartographic.longitude = longitude;
|
||||
const latitude = originY + dv.getUint8(offset++) * stepY;
|
||||
scratchCartographic.latitude = latitude;
|
||||
|
||||
let height = dv.getFloat32(offset, true);
|
||||
offset += sizeOfFloat;
|
||||
|
||||
// In order to support old clients, negative altitude values are stored as
|
||||
// height/-2^32. Old clients see the value as really close to 0 but new clients multiply
|
||||
// by -2^32 to get the real negative altitude value.
|
||||
if (height !== 0 && height < negativeElevationThreshold) {
|
||||
height *= -Math.pow(2, negativeAltitudeExponentBias);
|
||||
}
|
||||
|
||||
// Height is stored in units of (1/EarthRadius) or (1/6371010.0)
|
||||
height *= 6371010.0;
|
||||
|
||||
scratchCartographic.height = height;
|
||||
|
||||
// Is it along a quad border - if so check if already exists and use that index
|
||||
if (
|
||||
indexOfEpsilon(quadBorderLongitudes, longitude) !== -1 ||
|
||||
indexOfEpsilon(quadBorderLatitudes, latitude) !== -1
|
||||
) {
|
||||
const index = indexOfEpsilon(
|
||||
quadBorderPoints,
|
||||
scratchCartographic,
|
||||
Cartographic,
|
||||
);
|
||||
if (index === -1) {
|
||||
quadBorderPoints.push(Cartographic.clone(scratchCartographic));
|
||||
quadBorderIndices.push(pointOffset);
|
||||
} else {
|
||||
indicesMapping[i] = quadBorderIndices[index];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
indicesMapping[i] = pointOffset;
|
||||
|
||||
if (Math.abs(longitude - geographicWest) < halfStepX) {
|
||||
westBorder.push({
|
||||
index: pointOffset,
|
||||
cartographic: Cartographic.clone(scratchCartographic),
|
||||
});
|
||||
} else if (Math.abs(longitude - geographicEast) < halfStepX) {
|
||||
eastBorder.push({
|
||||
index: pointOffset,
|
||||
cartographic: Cartographic.clone(scratchCartographic),
|
||||
});
|
||||
} else if (Math.abs(latitude - geographicSouth) < halfStepY) {
|
||||
southBorder.push({
|
||||
index: pointOffset,
|
||||
cartographic: Cartographic.clone(scratchCartographic),
|
||||
});
|
||||
} else if (Math.abs(latitude - geographicNorth) < halfStepY) {
|
||||
northBorder.push({
|
||||
index: pointOffset,
|
||||
cartographic: Cartographic.clone(scratchCartographic),
|
||||
});
|
||||
}
|
||||
|
||||
minHeight = Math.min(height, minHeight);
|
||||
maxHeight = Math.max(height, maxHeight);
|
||||
heights[pointOffset] = height;
|
||||
|
||||
const pos = ellipsoid.cartographicToCartesian(scratchCartographic);
|
||||
positions[pointOffset] = pos;
|
||||
|
||||
if (includeWebMercatorT) {
|
||||
webMercatorTs[pointOffset] =
|
||||
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) -
|
||||
southMercatorY) *
|
||||
oneOverMercatorHeight;
|
||||
}
|
||||
|
||||
if (includeGeodeticSurfaceNormals) {
|
||||
const normal = ellipsoid.geodeticSurfaceNormal(pos);
|
||||
geodeticSurfaceNormals[pointOffset] = normal;
|
||||
}
|
||||
|
||||
Matrix4.multiplyByPoint(toENU, pos, scratchCartesian);
|
||||
|
||||
Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum);
|
||||
Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum);
|
||||
|
||||
let u = (longitude - geographicWest) / (geographicEast - geographicWest);
|
||||
u = CesiumMath.clamp(u, 0.0, 1.0);
|
||||
let v =
|
||||
(latitude - geographicSouth) / (geographicNorth - geographicSouth);
|
||||
v = CesiumMath.clamp(v, 0.0, 1.0);
|
||||
|
||||
uvs[pointOffset] = new Cartesian2(u, v);
|
||||
++pointOffset;
|
||||
}
|
||||
|
||||
const facesElementCount = numFaces * 3;
|
||||
for (let j = 0; j < facesElementCount; ++j, ++indicesOffset) {
|
||||
indices[indicesOffset] = indicesMapping[dv.getUint16(offset, true)];
|
||||
offset += sizeOfUint16;
|
||||
}
|
||||
|
||||
if (quadSize !== offset - startQuad) {
|
||||
throw new RuntimeError("Invalid terrain tile.");
|
||||
}
|
||||
}
|
||||
|
||||
positions.length = pointOffset;
|
||||
uvs.length = pointOffset;
|
||||
heights.length = pointOffset;
|
||||
if (includeWebMercatorT) {
|
||||
webMercatorTs.length = pointOffset;
|
||||
}
|
||||
if (includeGeodeticSurfaceNormals) {
|
||||
geodeticSurfaceNormals.length = pointOffset;
|
||||
}
|
||||
|
||||
const vertexCountWithoutSkirts = pointOffset;
|
||||
const indexCountWithoutSkirts = indicesOffset;
|
||||
|
||||
// Add skirt points
|
||||
const skirtOptions = {
|
||||
hMin: minHeight,
|
||||
lastBorderPoint: undefined,
|
||||
skirtHeight: skirtHeight,
|
||||
toENU: toENU,
|
||||
ellipsoid: ellipsoid,
|
||||
minimum: minimum,
|
||||
maximum: maximum,
|
||||
};
|
||||
|
||||
// Sort counter clockwise from NW corner
|
||||
// Corner points are in the east/west arrays
|
||||
westBorder.sort(function (a, b) {
|
||||
return b.cartographic.latitude - a.cartographic.latitude;
|
||||
});
|
||||
southBorder.sort(function (a, b) {
|
||||
return a.cartographic.longitude - b.cartographic.longitude;
|
||||
});
|
||||
eastBorder.sort(function (a, b) {
|
||||
return a.cartographic.latitude - b.cartographic.latitude;
|
||||
});
|
||||
northBorder.sort(function (a, b) {
|
||||
return b.cartographic.longitude - a.cartographic.longitude;
|
||||
});
|
||||
|
||||
const percentage = 0.00001;
|
||||
addSkirt(
|
||||
positions,
|
||||
heights,
|
||||
uvs,
|
||||
webMercatorTs,
|
||||
geodeticSurfaceNormals,
|
||||
indices,
|
||||
skirtOptions,
|
||||
westBorder,
|
||||
-percentage * rectangleWidth,
|
||||
true,
|
||||
-percentage * rectangleHeight,
|
||||
);
|
||||
addSkirt(
|
||||
positions,
|
||||
heights,
|
||||
uvs,
|
||||
webMercatorTs,
|
||||
geodeticSurfaceNormals,
|
||||
indices,
|
||||
skirtOptions,
|
||||
southBorder,
|
||||
-percentage * rectangleHeight,
|
||||
false,
|
||||
);
|
||||
addSkirt(
|
||||
positions,
|
||||
heights,
|
||||
uvs,
|
||||
webMercatorTs,
|
||||
geodeticSurfaceNormals,
|
||||
indices,
|
||||
skirtOptions,
|
||||
eastBorder,
|
||||
percentage * rectangleWidth,
|
||||
true,
|
||||
percentage * rectangleHeight,
|
||||
);
|
||||
addSkirt(
|
||||
positions,
|
||||
heights,
|
||||
uvs,
|
||||
webMercatorTs,
|
||||
geodeticSurfaceNormals,
|
||||
indices,
|
||||
skirtOptions,
|
||||
northBorder,
|
||||
percentage * rectangleHeight,
|
||||
false,
|
||||
);
|
||||
|
||||
// Since the corner between the north and west sides is in the west array, generate the last
|
||||
// two triangles between the last north vertex and the first west vertex
|
||||
if (westBorder.length > 0 && northBorder.length > 0) {
|
||||
const firstBorderIndex = westBorder[0].index;
|
||||
const firstSkirtIndex = vertexCountWithoutSkirts;
|
||||
const lastBorderIndex = northBorder[northBorder.length - 1].index;
|
||||
const lastSkirtIndex = positions.length - 1;
|
||||
|
||||
indices.push(
|
||||
lastBorderIndex,
|
||||
lastSkirtIndex,
|
||||
firstSkirtIndex,
|
||||
firstSkirtIndex,
|
||||
firstBorderIndex,
|
||||
lastBorderIndex,
|
||||
);
|
||||
}
|
||||
|
||||
size = positions.length; // Get new size with skirt vertices
|
||||
|
||||
const boundingSphere3D = BoundingSphere.fromPoints(positions);
|
||||
let orientedBoundingBox;
|
||||
if (defined(rectangle)) {
|
||||
orientedBoundingBox = OrientedBoundingBox.fromRectangle(
|
||||
rectangle,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
ellipsoid,
|
||||
);
|
||||
}
|
||||
|
||||
const occluder = new EllipsoidalOccluder(ellipsoid);
|
||||
const occludeePointInScaledSpace =
|
||||
occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(
|
||||
relativeToCenter,
|
||||
positions,
|
||||
minHeight,
|
||||
);
|
||||
|
||||
const aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);
|
||||
const encoding = new TerrainEncoding(
|
||||
relativeToCenter,
|
||||
aaBox,
|
||||
skirtOptions.hMin,
|
||||
maxHeight,
|
||||
fromENU,
|
||||
false,
|
||||
includeWebMercatorT,
|
||||
includeGeodeticSurfaceNormals,
|
||||
exaggeration,
|
||||
exaggerationRelativeHeight,
|
||||
);
|
||||
const vertices = new Float32Array(size * encoding.stride);
|
||||
|
||||
let bufferIndex = 0;
|
||||
for (let k = 0; k < size; ++k) {
|
||||
bufferIndex = encoding.encode(
|
||||
vertices,
|
||||
bufferIndex,
|
||||
positions[k],
|
||||
uvs[k],
|
||||
heights[k],
|
||||
undefined,
|
||||
webMercatorTs[k],
|
||||
geodeticSurfaceNormals[k],
|
||||
);
|
||||
}
|
||||
|
||||
const westIndicesSouthToNorth = westBorder
|
||||
.map(function (vertex) {
|
||||
return vertex.index;
|
||||
})
|
||||
.reverse();
|
||||
const southIndicesEastToWest = southBorder
|
||||
.map(function (vertex) {
|
||||
return vertex.index;
|
||||
})
|
||||
.reverse();
|
||||
const eastIndicesNorthToSouth = eastBorder
|
||||
.map(function (vertex) {
|
||||
return vertex.index;
|
||||
})
|
||||
.reverse();
|
||||
const northIndicesWestToEast = northBorder
|
||||
.map(function (vertex) {
|
||||
return vertex.index;
|
||||
})
|
||||
.reverse();
|
||||
|
||||
southIndicesEastToWest.unshift(
|
||||
eastIndicesNorthToSouth[eastIndicesNorthToSouth.length - 1],
|
||||
);
|
||||
southIndicesEastToWest.push(westIndicesSouthToNorth[0]);
|
||||
|
||||
northIndicesWestToEast.unshift(
|
||||
westIndicesSouthToNorth[westIndicesSouthToNorth.length - 1],
|
||||
);
|
||||
northIndicesWestToEast.push(eastIndicesNorthToSouth[0]);
|
||||
|
||||
return {
|
||||
vertices: vertices,
|
||||
indices: new Uint16Array(indices),
|
||||
maximumHeight: maxHeight,
|
||||
minimumHeight: minHeight,
|
||||
encoding: encoding,
|
||||
boundingSphere3D: boundingSphere3D,
|
||||
orientedBoundingBox: orientedBoundingBox,
|
||||
occludeePointInScaledSpace: occludeePointInScaledSpace,
|
||||
vertexCountWithoutSkirts: vertexCountWithoutSkirts,
|
||||
indexCountWithoutSkirts: indexCountWithoutSkirts,
|
||||
westIndicesSouthToNorth: westIndicesSouthToNorth,
|
||||
southIndicesEastToWest: southIndicesEastToWest,
|
||||
eastIndicesNorthToSouth: eastIndicesNorthToSouth,
|
||||
northIndicesWestToEast: northIndicesWestToEast,
|
||||
};
|
||||
}
|
||||
|
||||
function addSkirt(
|
||||
positions,
|
||||
heights,
|
||||
uvs,
|
||||
webMercatorTs,
|
||||
geodeticSurfaceNormals,
|
||||
indices,
|
||||
skirtOptions,
|
||||
borderPoints,
|
||||
fudgeFactor,
|
||||
eastOrWest,
|
||||
cornerFudge,
|
||||
) {
|
||||
const count = borderPoints.length;
|
||||
for (let j = 0; j < count; ++j) {
|
||||
const borderPoint = borderPoints[j];
|
||||
const borderCartographic = borderPoint.cartographic;
|
||||
const borderIndex = borderPoint.index;
|
||||
const currentIndex = positions.length;
|
||||
|
||||
const longitude = borderCartographic.longitude;
|
||||
let latitude = borderCartographic.latitude;
|
||||
latitude = CesiumMath.clamp(
|
||||
latitude,
|
||||
-CesiumMath.PI_OVER_TWO,
|
||||
CesiumMath.PI_OVER_TWO,
|
||||
); // Don't go over the poles
|
||||
const height = borderCartographic.height - skirtOptions.skirtHeight;
|
||||
skirtOptions.hMin = Math.min(skirtOptions.hMin, height);
|
||||
|
||||
Cartographic.fromRadians(longitude, latitude, height, scratchCartographic);
|
||||
|
||||
// Adjust sides to angle out
|
||||
if (eastOrWest) {
|
||||
scratchCartographic.longitude += fudgeFactor;
|
||||
}
|
||||
|
||||
// Adjust top or bottom to angle out
|
||||
// Since corners are in the east/west arrays angle the first and last points as well
|
||||
if (!eastOrWest) {
|
||||
scratchCartographic.latitude += fudgeFactor;
|
||||
} else if (j === count - 1) {
|
||||
scratchCartographic.latitude += cornerFudge;
|
||||
} else if (j === 0) {
|
||||
scratchCartographic.latitude -= cornerFudge;
|
||||
}
|
||||
|
||||
const pos =
|
||||
skirtOptions.ellipsoid.cartographicToCartesian(scratchCartographic);
|
||||
positions.push(pos);
|
||||
heights.push(height);
|
||||
uvs.push(Cartesian2.clone(uvs[borderIndex])); // Copy UVs from border point
|
||||
if (webMercatorTs.length > 0) {
|
||||
webMercatorTs.push(webMercatorTs[borderIndex]);
|
||||
}
|
||||
if (geodeticSurfaceNormals.length > 0) {
|
||||
geodeticSurfaceNormals.push(geodeticSurfaceNormals[borderIndex]);
|
||||
}
|
||||
|
||||
Matrix4.multiplyByPoint(skirtOptions.toENU, pos, scratchCartesian);
|
||||
|
||||
const minimum = skirtOptions.minimum;
|
||||
const maximum = skirtOptions.maximum;
|
||||
Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum);
|
||||
Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum);
|
||||
|
||||
const lastBorderPoint = skirtOptions.lastBorderPoint;
|
||||
if (defined(lastBorderPoint)) {
|
||||
const lastBorderIndex = lastBorderPoint.index;
|
||||
indices.push(
|
||||
lastBorderIndex,
|
||||
currentIndex - 1,
|
||||
currentIndex,
|
||||
currentIndex,
|
||||
borderIndex,
|
||||
lastBorderIndex,
|
||||
);
|
||||
}
|
||||
|
||||
skirtOptions.lastBorderPoint = borderPoint;
|
||||
}
|
||||
}
|
||||
export default createTaskProcessorWorker(
|
||||
createVerticesFromGoogleEarthEnterpriseBuffer,
|
||||
);
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import HeightmapEncoding from "../Core/HeightmapEncoding.js";
|
||||
import HeightmapTessellator from "../Core/HeightmapTessellator.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import Lerc from "lerc";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
function createVerticesFromHeightmap(parameters, transferableObjects) {
|
||||
// LERC encoded buffers must be decoded, then we can process them like normal
|
||||
if (parameters.encoding === HeightmapEncoding.LERC) {
|
||||
let result;
|
||||
try {
|
||||
result = Lerc.decode(parameters.heightmap);
|
||||
} catch (error) {
|
||||
throw new RuntimeError(error);
|
||||
}
|
||||
|
||||
const lercStatistics = result.statistics[0];
|
||||
if (lercStatistics.minValue === Number.MAX_VALUE) {
|
||||
throw new RuntimeError("Invalid tile data");
|
||||
}
|
||||
|
||||
parameters.heightmap = result.pixels[0];
|
||||
parameters.width = result.width;
|
||||
parameters.height = result.height;
|
||||
}
|
||||
|
||||
parameters.ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
|
||||
parameters.rectangle = Rectangle.clone(parameters.rectangle);
|
||||
|
||||
const statistics = HeightmapTessellator.computeVertices(parameters);
|
||||
const vertices = statistics.vertices;
|
||||
transferableObjects.push(vertices.buffer);
|
||||
|
||||
return {
|
||||
vertices: vertices.buffer,
|
||||
numberOfAttributes: statistics.encoding.stride,
|
||||
minimumHeight: statistics.minimumHeight,
|
||||
maximumHeight: statistics.maximumHeight,
|
||||
gridWidth: parameters.width,
|
||||
gridHeight: parameters.height,
|
||||
boundingSphere3D: statistics.boundingSphere3D,
|
||||
orientedBoundingBox: statistics.orientedBoundingBox,
|
||||
occludeePointInScaledSpace: statistics.occludeePointInScaledSpace,
|
||||
encoding: statistics.encoding,
|
||||
westIndicesSouthToNorth: statistics.westIndicesSouthToNorth,
|
||||
southIndicesEastToWest: statistics.southIndicesEastToWest,
|
||||
eastIndicesNorthToSouth: statistics.eastIndicesNorthToSouth,
|
||||
northIndicesWestToEast: statistics.northIndicesWestToEast,
|
||||
};
|
||||
}
|
||||
export default createTaskProcessorWorker(createVerticesFromHeightmap);
|
||||
Generated
Vendored
+549
@@ -0,0 +1,549 @@
|
||||
import AxisAlignedBoundingBox from "../Core/AxisAlignedBoundingBox.js";
|
||||
import Cartesian2 from "../Core/Cartesian2.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import Matrix4 from "../Core/Matrix4.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import TerrainEncoding from "../Core/TerrainEncoding.js";
|
||||
import TerrainProvider from "../Core/TerrainProvider.js";
|
||||
import Transforms from "../Core/Transforms.js";
|
||||
import WebMercatorProjection from "../Core/WebMercatorProjection.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const maxShort = 32767;
|
||||
|
||||
const cartesian3Scratch = new Cartesian3();
|
||||
const scratchMinimum = new Cartesian3();
|
||||
const scratchMaximum = new Cartesian3();
|
||||
const cartographicScratch = new Cartographic();
|
||||
const toPack = new Cartesian2();
|
||||
|
||||
function createVerticesFromQuantizedTerrainMesh(
|
||||
parameters,
|
||||
transferableObjects,
|
||||
) {
|
||||
const quantizedVertices = parameters.quantizedVertices;
|
||||
const quantizedVertexCount = quantizedVertices.length / 3;
|
||||
const octEncodedNormals = parameters.octEncodedNormals;
|
||||
const edgeVertexCount =
|
||||
parameters.westIndices.length +
|
||||
parameters.eastIndices.length +
|
||||
parameters.southIndices.length +
|
||||
parameters.northIndices.length;
|
||||
const includeWebMercatorT = parameters.includeWebMercatorT;
|
||||
|
||||
const exaggeration = parameters.exaggeration;
|
||||
const exaggerationRelativeHeight = parameters.exaggerationRelativeHeight;
|
||||
const hasExaggeration = exaggeration !== 1.0;
|
||||
const includeGeodeticSurfaceNormals = hasExaggeration;
|
||||
|
||||
const rectangle = Rectangle.clone(parameters.rectangle);
|
||||
const west = rectangle.west;
|
||||
const south = rectangle.south;
|
||||
const east = rectangle.east;
|
||||
const north = rectangle.north;
|
||||
|
||||
const ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
|
||||
|
||||
const minimumHeight = parameters.minimumHeight;
|
||||
const maximumHeight = parameters.maximumHeight;
|
||||
|
||||
const center = parameters.relativeToCenter;
|
||||
const fromENU = Transforms.eastNorthUpToFixedFrame(center, ellipsoid);
|
||||
const toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());
|
||||
|
||||
let southMercatorY;
|
||||
let oneOverMercatorHeight;
|
||||
if (includeWebMercatorT) {
|
||||
southMercatorY =
|
||||
WebMercatorProjection.geodeticLatitudeToMercatorAngle(south);
|
||||
oneOverMercatorHeight =
|
||||
1.0 /
|
||||
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(north) -
|
||||
southMercatorY);
|
||||
}
|
||||
|
||||
const uBuffer = quantizedVertices.subarray(0, quantizedVertexCount);
|
||||
const vBuffer = quantizedVertices.subarray(
|
||||
quantizedVertexCount,
|
||||
2 * quantizedVertexCount,
|
||||
);
|
||||
const heightBuffer = quantizedVertices.subarray(
|
||||
quantizedVertexCount * 2,
|
||||
3 * quantizedVertexCount,
|
||||
);
|
||||
const hasVertexNormals = defined(octEncodedNormals);
|
||||
|
||||
const uvs = new Array(quantizedVertexCount);
|
||||
const heights = new Array(quantizedVertexCount);
|
||||
const positions = new Array(quantizedVertexCount);
|
||||
const webMercatorTs = includeWebMercatorT
|
||||
? new Array(quantizedVertexCount)
|
||||
: [];
|
||||
const geodeticSurfaceNormals = includeGeodeticSurfaceNormals
|
||||
? new Array(quantizedVertexCount)
|
||||
: [];
|
||||
|
||||
const minimum = scratchMinimum;
|
||||
minimum.x = Number.POSITIVE_INFINITY;
|
||||
minimum.y = Number.POSITIVE_INFINITY;
|
||||
minimum.z = Number.POSITIVE_INFINITY;
|
||||
|
||||
const maximum = scratchMaximum;
|
||||
maximum.x = Number.NEGATIVE_INFINITY;
|
||||
maximum.y = Number.NEGATIVE_INFINITY;
|
||||
maximum.z = Number.NEGATIVE_INFINITY;
|
||||
|
||||
let minLongitude = Number.POSITIVE_INFINITY;
|
||||
let maxLongitude = Number.NEGATIVE_INFINITY;
|
||||
let minLatitude = Number.POSITIVE_INFINITY;
|
||||
let maxLatitude = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < quantizedVertexCount; ++i) {
|
||||
const rawU = uBuffer[i];
|
||||
const rawV = vBuffer[i];
|
||||
|
||||
const u = rawU / maxShort;
|
||||
const v = rawV / maxShort;
|
||||
const height = CesiumMath.lerp(
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
heightBuffer[i] / maxShort,
|
||||
);
|
||||
|
||||
cartographicScratch.longitude = CesiumMath.lerp(west, east, u);
|
||||
cartographicScratch.latitude = CesiumMath.lerp(south, north, v);
|
||||
cartographicScratch.height = height;
|
||||
|
||||
minLongitude = Math.min(cartographicScratch.longitude, minLongitude);
|
||||
maxLongitude = Math.max(cartographicScratch.longitude, maxLongitude);
|
||||
minLatitude = Math.min(cartographicScratch.latitude, minLatitude);
|
||||
maxLatitude = Math.max(cartographicScratch.latitude, maxLatitude);
|
||||
|
||||
const position = ellipsoid.cartographicToCartesian(cartographicScratch);
|
||||
|
||||
uvs[i] = new Cartesian2(u, v);
|
||||
heights[i] = height;
|
||||
positions[i] = position;
|
||||
|
||||
if (includeWebMercatorT) {
|
||||
webMercatorTs[i] =
|
||||
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(
|
||||
cartographicScratch.latitude,
|
||||
) -
|
||||
southMercatorY) *
|
||||
oneOverMercatorHeight;
|
||||
}
|
||||
|
||||
if (includeGeodeticSurfaceNormals) {
|
||||
geodeticSurfaceNormals[i] = ellipsoid.geodeticSurfaceNormal(position);
|
||||
}
|
||||
|
||||
Matrix4.multiplyByPoint(toENU, position, cartesian3Scratch);
|
||||
|
||||
Cartesian3.minimumByComponent(cartesian3Scratch, minimum, minimum);
|
||||
Cartesian3.maximumByComponent(cartesian3Scratch, maximum, maximum);
|
||||
}
|
||||
|
||||
const westIndicesSouthToNorth = copyAndSort(
|
||||
parameters.westIndices,
|
||||
function (a, b) {
|
||||
return uvs[a].y - uvs[b].y;
|
||||
},
|
||||
);
|
||||
const eastIndicesNorthToSouth = copyAndSort(
|
||||
parameters.eastIndices,
|
||||
function (a, b) {
|
||||
return uvs[b].y - uvs[a].y;
|
||||
},
|
||||
);
|
||||
const southIndicesEastToWest = copyAndSort(
|
||||
parameters.southIndices,
|
||||
function (a, b) {
|
||||
return uvs[b].x - uvs[a].x;
|
||||
},
|
||||
);
|
||||
const northIndicesWestToEast = copyAndSort(
|
||||
parameters.northIndices,
|
||||
function (a, b) {
|
||||
return uvs[a].x - uvs[b].x;
|
||||
},
|
||||
);
|
||||
|
||||
let occludeePointInScaledSpace;
|
||||
if (minimumHeight < 0.0) {
|
||||
// Horizon culling point needs to be recomputed since the tile is at least partly under the ellipsoid.
|
||||
const occluder = new EllipsoidalOccluder(ellipsoid);
|
||||
occludeePointInScaledSpace =
|
||||
occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(
|
||||
center,
|
||||
positions,
|
||||
minimumHeight,
|
||||
);
|
||||
}
|
||||
|
||||
let hMin = minimumHeight;
|
||||
hMin = Math.min(
|
||||
hMin,
|
||||
findMinMaxSkirts(
|
||||
parameters.westIndices,
|
||||
parameters.westSkirtHeight,
|
||||
heights,
|
||||
uvs,
|
||||
rectangle,
|
||||
ellipsoid,
|
||||
toENU,
|
||||
minimum,
|
||||
maximum,
|
||||
),
|
||||
);
|
||||
hMin = Math.min(
|
||||
hMin,
|
||||
findMinMaxSkirts(
|
||||
parameters.southIndices,
|
||||
parameters.southSkirtHeight,
|
||||
heights,
|
||||
uvs,
|
||||
rectangle,
|
||||
ellipsoid,
|
||||
toENU,
|
||||
minimum,
|
||||
maximum,
|
||||
),
|
||||
);
|
||||
hMin = Math.min(
|
||||
hMin,
|
||||
findMinMaxSkirts(
|
||||
parameters.eastIndices,
|
||||
parameters.eastSkirtHeight,
|
||||
heights,
|
||||
uvs,
|
||||
rectangle,
|
||||
ellipsoid,
|
||||
toENU,
|
||||
minimum,
|
||||
maximum,
|
||||
),
|
||||
);
|
||||
hMin = Math.min(
|
||||
hMin,
|
||||
findMinMaxSkirts(
|
||||
parameters.northIndices,
|
||||
parameters.northSkirtHeight,
|
||||
heights,
|
||||
uvs,
|
||||
rectangle,
|
||||
ellipsoid,
|
||||
toENU,
|
||||
minimum,
|
||||
maximum,
|
||||
),
|
||||
);
|
||||
|
||||
const aaBox = new AxisAlignedBoundingBox(minimum, maximum, center);
|
||||
const encoding = new TerrainEncoding(
|
||||
center,
|
||||
aaBox,
|
||||
hMin,
|
||||
maximumHeight,
|
||||
fromENU,
|
||||
hasVertexNormals,
|
||||
includeWebMercatorT,
|
||||
includeGeodeticSurfaceNormals,
|
||||
exaggeration,
|
||||
exaggerationRelativeHeight,
|
||||
);
|
||||
const vertexStride = encoding.stride;
|
||||
const size =
|
||||
quantizedVertexCount * vertexStride + edgeVertexCount * vertexStride;
|
||||
const vertexBuffer = new Float32Array(size);
|
||||
|
||||
let bufferIndex = 0;
|
||||
for (let j = 0; j < quantizedVertexCount; ++j) {
|
||||
if (hasVertexNormals) {
|
||||
const n = j * 2.0;
|
||||
toPack.x = octEncodedNormals[n];
|
||||
toPack.y = octEncodedNormals[n + 1];
|
||||
}
|
||||
|
||||
bufferIndex = encoding.encode(
|
||||
vertexBuffer,
|
||||
bufferIndex,
|
||||
positions[j],
|
||||
uvs[j],
|
||||
heights[j],
|
||||
toPack,
|
||||
webMercatorTs[j],
|
||||
geodeticSurfaceNormals[j],
|
||||
);
|
||||
}
|
||||
|
||||
const edgeTriangleCount = Math.max(0, (edgeVertexCount - 4) * 2);
|
||||
const indexBufferLength = parameters.indices.length + edgeTriangleCount * 3;
|
||||
const indexBuffer = IndexDatatype.createTypedArray(
|
||||
quantizedVertexCount + edgeVertexCount,
|
||||
indexBufferLength,
|
||||
);
|
||||
indexBuffer.set(parameters.indices, 0);
|
||||
|
||||
const percentage = 0.0001;
|
||||
const lonOffset = (maxLongitude - minLongitude) * percentage;
|
||||
const latOffset = (maxLatitude - minLatitude) * percentage;
|
||||
const westLongitudeOffset = -lonOffset;
|
||||
const westLatitudeOffset = 0.0;
|
||||
const eastLongitudeOffset = lonOffset;
|
||||
const eastLatitudeOffset = 0.0;
|
||||
const northLongitudeOffset = 0.0;
|
||||
const northLatitudeOffset = latOffset;
|
||||
const southLongitudeOffset = 0.0;
|
||||
const southLatitudeOffset = -latOffset;
|
||||
|
||||
// Add skirts.
|
||||
let vertexBufferIndex = quantizedVertexCount * vertexStride;
|
||||
addSkirt(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
westIndicesSouthToNorth,
|
||||
encoding,
|
||||
heights,
|
||||
uvs,
|
||||
octEncodedNormals,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
parameters.westSkirtHeight,
|
||||
southMercatorY,
|
||||
oneOverMercatorHeight,
|
||||
westLongitudeOffset,
|
||||
westLatitudeOffset,
|
||||
);
|
||||
vertexBufferIndex += parameters.westIndices.length * vertexStride;
|
||||
addSkirt(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
southIndicesEastToWest,
|
||||
encoding,
|
||||
heights,
|
||||
uvs,
|
||||
octEncodedNormals,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
parameters.southSkirtHeight,
|
||||
southMercatorY,
|
||||
oneOverMercatorHeight,
|
||||
southLongitudeOffset,
|
||||
southLatitudeOffset,
|
||||
);
|
||||
vertexBufferIndex += parameters.southIndices.length * vertexStride;
|
||||
addSkirt(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
eastIndicesNorthToSouth,
|
||||
encoding,
|
||||
heights,
|
||||
uvs,
|
||||
octEncodedNormals,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
parameters.eastSkirtHeight,
|
||||
southMercatorY,
|
||||
oneOverMercatorHeight,
|
||||
eastLongitudeOffset,
|
||||
eastLatitudeOffset,
|
||||
);
|
||||
vertexBufferIndex += parameters.eastIndices.length * vertexStride;
|
||||
addSkirt(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
northIndicesWestToEast,
|
||||
encoding,
|
||||
heights,
|
||||
uvs,
|
||||
octEncodedNormals,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
parameters.northSkirtHeight,
|
||||
southMercatorY,
|
||||
oneOverMercatorHeight,
|
||||
northLongitudeOffset,
|
||||
northLatitudeOffset,
|
||||
);
|
||||
|
||||
TerrainProvider.addSkirtIndices(
|
||||
westIndicesSouthToNorth,
|
||||
southIndicesEastToWest,
|
||||
eastIndicesNorthToSouth,
|
||||
northIndicesWestToEast,
|
||||
quantizedVertexCount,
|
||||
indexBuffer,
|
||||
parameters.indices.length,
|
||||
);
|
||||
|
||||
transferableObjects.push(vertexBuffer.buffer, indexBuffer.buffer);
|
||||
|
||||
return {
|
||||
vertices: vertexBuffer.buffer,
|
||||
indices: indexBuffer.buffer,
|
||||
westIndicesSouthToNorth: westIndicesSouthToNorth,
|
||||
southIndicesEastToWest: southIndicesEastToWest,
|
||||
eastIndicesNorthToSouth: eastIndicesNorthToSouth,
|
||||
northIndicesWestToEast: northIndicesWestToEast,
|
||||
vertexStride: vertexStride,
|
||||
center: center,
|
||||
minimumHeight: minimumHeight,
|
||||
maximumHeight: maximumHeight,
|
||||
occludeePointInScaledSpace: occludeePointInScaledSpace,
|
||||
encoding: encoding,
|
||||
indexCountWithoutSkirts: parameters.indices.length,
|
||||
};
|
||||
}
|
||||
|
||||
function findMinMaxSkirts(
|
||||
edgeIndices,
|
||||
edgeHeight,
|
||||
heights,
|
||||
uvs,
|
||||
rectangle,
|
||||
ellipsoid,
|
||||
toENU,
|
||||
minimum,
|
||||
maximum,
|
||||
) {
|
||||
let hMin = Number.POSITIVE_INFINITY;
|
||||
|
||||
const north = rectangle.north;
|
||||
const south = rectangle.south;
|
||||
let east = rectangle.east;
|
||||
const west = rectangle.west;
|
||||
|
||||
if (east < west) {
|
||||
east += CesiumMath.TWO_PI;
|
||||
}
|
||||
|
||||
const length = edgeIndices.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const index = edgeIndices[i];
|
||||
const h = heights[index];
|
||||
const uv = uvs[index];
|
||||
|
||||
cartographicScratch.longitude = CesiumMath.lerp(west, east, uv.x);
|
||||
cartographicScratch.latitude = CesiumMath.lerp(south, north, uv.y);
|
||||
cartographicScratch.height = h - edgeHeight;
|
||||
|
||||
const position = ellipsoid.cartographicToCartesian(
|
||||
cartographicScratch,
|
||||
cartesian3Scratch,
|
||||
);
|
||||
Matrix4.multiplyByPoint(toENU, position, position);
|
||||
|
||||
Cartesian3.minimumByComponent(position, minimum, minimum);
|
||||
Cartesian3.maximumByComponent(position, maximum, maximum);
|
||||
|
||||
hMin = Math.min(hMin, cartographicScratch.height);
|
||||
}
|
||||
return hMin;
|
||||
}
|
||||
|
||||
function addSkirt(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
edgeVertices,
|
||||
encoding,
|
||||
heights,
|
||||
uvs,
|
||||
octEncodedNormals,
|
||||
ellipsoid,
|
||||
rectangle,
|
||||
skirtLength,
|
||||
southMercatorY,
|
||||
oneOverMercatorHeight,
|
||||
longitudeOffset,
|
||||
latitudeOffset,
|
||||
) {
|
||||
const hasVertexNormals = defined(octEncodedNormals);
|
||||
|
||||
const north = rectangle.north;
|
||||
const south = rectangle.south;
|
||||
let east = rectangle.east;
|
||||
const west = rectangle.west;
|
||||
|
||||
if (east < west) {
|
||||
east += CesiumMath.TWO_PI;
|
||||
}
|
||||
|
||||
const length = edgeVertices.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const index = edgeVertices[i];
|
||||
const h = heights[index];
|
||||
const uv = uvs[index];
|
||||
|
||||
cartographicScratch.longitude =
|
||||
CesiumMath.lerp(west, east, uv.x) + longitudeOffset;
|
||||
cartographicScratch.latitude =
|
||||
CesiumMath.lerp(south, north, uv.y) + latitudeOffset;
|
||||
cartographicScratch.height = h - skirtLength;
|
||||
|
||||
const position = ellipsoid.cartographicToCartesian(
|
||||
cartographicScratch,
|
||||
cartesian3Scratch,
|
||||
);
|
||||
|
||||
if (hasVertexNormals) {
|
||||
const n = index * 2.0;
|
||||
toPack.x = octEncodedNormals[n];
|
||||
toPack.y = octEncodedNormals[n + 1];
|
||||
}
|
||||
|
||||
let webMercatorT;
|
||||
if (encoding.hasWebMercatorT) {
|
||||
webMercatorT =
|
||||
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(
|
||||
cartographicScratch.latitude,
|
||||
) -
|
||||
southMercatorY) *
|
||||
oneOverMercatorHeight;
|
||||
}
|
||||
|
||||
let geodeticSurfaceNormal;
|
||||
if (encoding.hasGeodeticSurfaceNormals) {
|
||||
geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(position);
|
||||
}
|
||||
|
||||
vertexBufferIndex = encoding.encode(
|
||||
vertexBuffer,
|
||||
vertexBufferIndex,
|
||||
position,
|
||||
uv,
|
||||
cartographicScratch.height,
|
||||
toPack,
|
||||
webMercatorT,
|
||||
geodeticSurfaceNormal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function copyAndSort(typedArray, comparator) {
|
||||
let copy;
|
||||
if (typeof typedArray.slice === "function") {
|
||||
copy = typedArray.slice();
|
||||
if (typeof copy.sort !== "function") {
|
||||
// Sliced typed array isn't sortable, so we can't use it.
|
||||
copy = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined(copy)) {
|
||||
copy = Array.prototype.slice.call(typedArray);
|
||||
}
|
||||
|
||||
copy.sort(comparator);
|
||||
|
||||
return copy;
|
||||
}
|
||||
export default createTaskProcessorWorker(
|
||||
createVerticesFromQuantizedTerrainMesh,
|
||||
);
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import WallGeometry from "../Core/WallGeometry.js";
|
||||
|
||||
function createWallGeometry(wallGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
wallGeometry = WallGeometry.unpack(wallGeometry, offset);
|
||||
}
|
||||
wallGeometry._ellipsoid = Ellipsoid.clone(wallGeometry._ellipsoid);
|
||||
return WallGeometry.createGeometry(wallGeometry);
|
||||
}
|
||||
export default createWallGeometry;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import WallOutlineGeometry from "../Core/WallOutlineGeometry.js";
|
||||
|
||||
function createWallOutlineGeometry(wallGeometry, offset) {
|
||||
if (defined(offset)) {
|
||||
wallGeometry = WallOutlineGeometry.unpack(wallGeometry, offset);
|
||||
}
|
||||
wallGeometry._ellipsoid = Ellipsoid.clone(wallGeometry._ellipsoid);
|
||||
return WallOutlineGeometry.createGeometry(wallGeometry);
|
||||
}
|
||||
export default createWallOutlineGeometry;
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
// Draco API uses many capitalized non-constructor methods.
|
||||
/* eslint-disable new-cap */
|
||||
|
||||
import ComponentDatatype from "../Core/ComponentDatatype.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
import dracoModule from "draco3d/draco_decoder_nodejs.js";
|
||||
|
||||
let draco;
|
||||
|
||||
function decodeIndexArray(dracoGeometry, dracoDecoder) {
|
||||
const numPoints = dracoGeometry.num_points();
|
||||
const numFaces = dracoGeometry.num_faces();
|
||||
const faceIndices = new draco.DracoInt32Array();
|
||||
const numIndices = numFaces * 3;
|
||||
const indexArray = IndexDatatype.createTypedArray(numPoints, numIndices);
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0; i < numFaces; ++i) {
|
||||
dracoDecoder.GetFaceFromMesh(dracoGeometry, i, faceIndices);
|
||||
|
||||
indexArray[offset + 0] = faceIndices.GetValue(0);
|
||||
indexArray[offset + 1] = faceIndices.GetValue(1);
|
||||
indexArray[offset + 2] = faceIndices.GetValue(2);
|
||||
offset += 3;
|
||||
}
|
||||
|
||||
draco.destroy(faceIndices);
|
||||
|
||||
return {
|
||||
typedArray: indexArray,
|
||||
numberOfIndices: numIndices,
|
||||
};
|
||||
}
|
||||
|
||||
function decodeQuantizedDracoTypedArray(
|
||||
dracoGeometry,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
quantization,
|
||||
vertexArrayLength,
|
||||
) {
|
||||
let vertexArray;
|
||||
let attributeData;
|
||||
if (quantization.quantizationBits <= 8) {
|
||||
attributeData = new draco.DracoUInt8Array();
|
||||
vertexArray = new Uint8Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeUInt8ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
} else if (quantization.quantizationBits <= 16) {
|
||||
attributeData = new draco.DracoUInt16Array();
|
||||
vertexArray = new Uint16Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeUInt16ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
} else {
|
||||
attributeData = new draco.DracoFloat32Array();
|
||||
vertexArray = new Float32Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeFloatForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < vertexArrayLength; ++i) {
|
||||
vertexArray[i] = attributeData.GetValue(i);
|
||||
}
|
||||
|
||||
draco.destroy(attributeData);
|
||||
return vertexArray;
|
||||
}
|
||||
|
||||
function decodeDracoTypedArray(
|
||||
dracoGeometry,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
vertexArrayLength,
|
||||
) {
|
||||
let vertexArray;
|
||||
let attributeData;
|
||||
|
||||
// Some attribute types are casted down to 32 bit since Draco only returns 32 bit values
|
||||
switch (dracoAttribute.data_type()) {
|
||||
case 1:
|
||||
case 11: // DT_INT8 or DT_BOOL
|
||||
attributeData = new draco.DracoInt8Array();
|
||||
vertexArray = new Int8Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeInt8ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 2: // DT_UINT8
|
||||
attributeData = new draco.DracoUInt8Array();
|
||||
vertexArray = new Uint8Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeUInt8ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 3: // DT_INT16
|
||||
attributeData = new draco.DracoInt16Array();
|
||||
vertexArray = new Int16Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeInt16ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 4: // DT_UINT16
|
||||
attributeData = new draco.DracoUInt16Array();
|
||||
vertexArray = new Uint16Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeUInt16ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 5:
|
||||
case 7: // DT_INT32 or DT_INT64
|
||||
attributeData = new draco.DracoInt32Array();
|
||||
vertexArray = new Int32Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeInt32ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 6:
|
||||
case 8: // DT_UINT32 or DT_UINT64
|
||||
attributeData = new draco.DracoUInt32Array();
|
||||
vertexArray = new Uint32Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeUInt32ForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
case 9:
|
||||
case 10: // DT_FLOAT32 or DT_FLOAT64
|
||||
attributeData = new draco.DracoFloat32Array();
|
||||
vertexArray = new Float32Array(vertexArrayLength);
|
||||
dracoDecoder.GetAttributeFloatForAllPoints(
|
||||
dracoGeometry,
|
||||
dracoAttribute,
|
||||
attributeData,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
for (let i = 0; i < vertexArrayLength; ++i) {
|
||||
vertexArray[i] = attributeData.GetValue(i);
|
||||
}
|
||||
|
||||
draco.destroy(attributeData);
|
||||
return vertexArray;
|
||||
}
|
||||
|
||||
function decodeAttribute(dracoGeometry, dracoDecoder, dracoAttribute) {
|
||||
const numPoints = dracoGeometry.num_points();
|
||||
const numComponents = dracoAttribute.num_components();
|
||||
|
||||
let quantization;
|
||||
let transform = new draco.AttributeQuantizationTransform();
|
||||
if (transform.InitFromAttribute(dracoAttribute)) {
|
||||
const minValues = new Array(numComponents);
|
||||
for (let i = 0; i < numComponents; ++i) {
|
||||
minValues[i] = transform.min_value(i);
|
||||
}
|
||||
quantization = {
|
||||
quantizationBits: transform.quantization_bits(),
|
||||
minValues: minValues,
|
||||
range: transform.range(),
|
||||
octEncoded: false,
|
||||
};
|
||||
}
|
||||
draco.destroy(transform);
|
||||
|
||||
transform = new draco.AttributeOctahedronTransform();
|
||||
if (transform.InitFromAttribute(dracoAttribute)) {
|
||||
quantization = {
|
||||
quantizationBits: transform.quantization_bits(),
|
||||
octEncoded: true,
|
||||
};
|
||||
}
|
||||
draco.destroy(transform);
|
||||
|
||||
const vertexArrayLength = numPoints * numComponents;
|
||||
let vertexArray;
|
||||
if (defined(quantization)) {
|
||||
vertexArray = decodeQuantizedDracoTypedArray(
|
||||
dracoGeometry,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
quantization,
|
||||
vertexArrayLength,
|
||||
);
|
||||
} else {
|
||||
vertexArray = decodeDracoTypedArray(
|
||||
dracoGeometry,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
vertexArrayLength,
|
||||
);
|
||||
}
|
||||
|
||||
const componentDatatype = ComponentDatatype.fromTypedArray(vertexArray);
|
||||
|
||||
return {
|
||||
array: vertexArray,
|
||||
data: {
|
||||
componentsPerAttribute: numComponents,
|
||||
componentDatatype: componentDatatype,
|
||||
byteOffset: dracoAttribute.byte_offset(),
|
||||
byteStride:
|
||||
ComponentDatatype.getSizeInBytes(componentDatatype) * numComponents,
|
||||
normalized: dracoAttribute.normalized(),
|
||||
quantization: quantization,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function decodePointCloud(parameters) {
|
||||
const dracoDecoder = new draco.Decoder();
|
||||
|
||||
if (parameters.dequantizeInShader) {
|
||||
dracoDecoder.SkipAttributeTransform(draco.POSITION);
|
||||
dracoDecoder.SkipAttributeTransform(draco.NORMAL);
|
||||
}
|
||||
|
||||
const buffer = new draco.DecoderBuffer();
|
||||
buffer.Init(parameters.buffer, parameters.buffer.length);
|
||||
|
||||
const geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
|
||||
if (geometryType !== draco.POINT_CLOUD) {
|
||||
throw new RuntimeError("Draco geometry type must be POINT_CLOUD.");
|
||||
}
|
||||
|
||||
const dracoPointCloud = new draco.PointCloud();
|
||||
const decodingStatus = dracoDecoder.DecodeBufferToPointCloud(
|
||||
buffer,
|
||||
dracoPointCloud,
|
||||
);
|
||||
if (!decodingStatus.ok() || dracoPointCloud.ptr === 0) {
|
||||
throw new RuntimeError(
|
||||
`Error decoding draco point cloud: ${decodingStatus.error_msg()}`,
|
||||
);
|
||||
}
|
||||
|
||||
draco.destroy(buffer);
|
||||
|
||||
const result = {};
|
||||
|
||||
const properties = parameters.properties;
|
||||
for (const propertyName in properties) {
|
||||
if (properties.hasOwnProperty(propertyName)) {
|
||||
let dracoAttribute;
|
||||
if (propertyName === "POSITION" || propertyName === "NORMAL") {
|
||||
const dracoAttributeId = dracoDecoder.GetAttributeId(
|
||||
dracoPointCloud,
|
||||
draco[propertyName],
|
||||
);
|
||||
dracoAttribute = dracoDecoder.GetAttribute(
|
||||
dracoPointCloud,
|
||||
dracoAttributeId,
|
||||
);
|
||||
} else {
|
||||
const attributeId = properties[propertyName];
|
||||
dracoAttribute = dracoDecoder.GetAttributeByUniqueId(
|
||||
dracoPointCloud,
|
||||
attributeId,
|
||||
);
|
||||
}
|
||||
result[propertyName] = decodeAttribute(
|
||||
dracoPointCloud,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
draco.destroy(dracoPointCloud);
|
||||
draco.destroy(dracoDecoder);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function decodePrimitive(parameters) {
|
||||
const dracoDecoder = new draco.Decoder();
|
||||
|
||||
if (parameters.dequantizeInShader) {
|
||||
for (let i = 0; i < parameters.attributesToSkipTransform.length; ++i) {
|
||||
dracoDecoder.SkipAttributeTransform(
|
||||
draco[parameters.attributesToSkipTransform[i]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const bufferView = parameters.bufferView;
|
||||
const buffer = new draco.DecoderBuffer();
|
||||
buffer.Init(parameters.array, bufferView.byteLength);
|
||||
|
||||
const geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
|
||||
if (geometryType !== draco.TRIANGULAR_MESH) {
|
||||
throw new RuntimeError("Unsupported draco mesh geometry type.");
|
||||
}
|
||||
|
||||
const dracoGeometry = new draco.Mesh();
|
||||
const decodingStatus = dracoDecoder.DecodeBufferToMesh(buffer, dracoGeometry);
|
||||
if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
|
||||
throw new RuntimeError(
|
||||
`Error decoding draco mesh geometry: ${decodingStatus.error_msg()}`,
|
||||
);
|
||||
}
|
||||
|
||||
draco.destroy(buffer);
|
||||
|
||||
const attributeData = {};
|
||||
|
||||
const compressedAttributes = parameters.compressedAttributes;
|
||||
for (const attributeName in compressedAttributes) {
|
||||
if (compressedAttributes.hasOwnProperty(attributeName)) {
|
||||
const compressedAttribute = compressedAttributes[attributeName];
|
||||
const dracoAttribute = dracoDecoder.GetAttributeByUniqueId(
|
||||
dracoGeometry,
|
||||
compressedAttribute,
|
||||
);
|
||||
attributeData[attributeName] = decodeAttribute(
|
||||
dracoGeometry,
|
||||
dracoDecoder,
|
||||
dracoAttribute,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
indexArray: decodeIndexArray(dracoGeometry, dracoDecoder),
|
||||
attributeData: attributeData,
|
||||
};
|
||||
|
||||
draco.destroy(dracoGeometry);
|
||||
draco.destroy(dracoDecoder);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function decode(parameters, transferableObjects) {
|
||||
if (defined(parameters.bufferView)) {
|
||||
return decodePrimitive(parameters);
|
||||
}
|
||||
return decodePointCloud(parameters);
|
||||
}
|
||||
|
||||
async function initWorker(parameters, transferableObjects) {
|
||||
// Require and compile WebAssembly module, or use fallback if not supported
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig) && defined(wasmConfig.wasmBinaryFile)) {
|
||||
draco = await dracoModule(wasmConfig);
|
||||
} else {
|
||||
draco = await dracoModule();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function decodeDraco(parameters, transferableObjects) {
|
||||
// Expect the first message to be to load a web assembly module
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig)) {
|
||||
return initWorker(parameters, transferableObjects);
|
||||
}
|
||||
|
||||
return decode(parameters, transferableObjects);
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(decodeDraco);
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
import decodeGoogleEarthEnterpriseData from "../Core/decodeGoogleEarthEnterpriseData.js";
|
||||
import GoogleEarthEnterpriseTileInformation from "../Core/GoogleEarthEnterpriseTileInformation.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import { inflate } from "pako/browser/inflate";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
// Datatype sizes
|
||||
const sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
|
||||
const sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
|
||||
|
||||
const Types = {
|
||||
METADATA: 0,
|
||||
TERRAIN: 1,
|
||||
DBROOT: 2,
|
||||
};
|
||||
|
||||
Types.fromString = function (s) {
|
||||
if (s === "Metadata") {
|
||||
return Types.METADATA;
|
||||
} else if (s === "Terrain") {
|
||||
return Types.TERRAIN;
|
||||
} else if (s === "DbRoot") {
|
||||
return Types.DBROOT;
|
||||
}
|
||||
};
|
||||
|
||||
function decodeGoogleEarthEnterprisePacket(parameters, transferableObjects) {
|
||||
const type = Types.fromString(parameters.type);
|
||||
let buffer = parameters.buffer;
|
||||
decodeGoogleEarthEnterpriseData(parameters.key, buffer);
|
||||
|
||||
const uncompressedTerrain = uncompressPacket(buffer);
|
||||
buffer = uncompressedTerrain.buffer;
|
||||
const length = uncompressedTerrain.length;
|
||||
|
||||
switch (type) {
|
||||
case Types.METADATA:
|
||||
return processMetadata(buffer, length, parameters.quadKey);
|
||||
case Types.TERRAIN:
|
||||
return processTerrain(buffer, length, transferableObjects);
|
||||
case Types.DBROOT:
|
||||
transferableObjects.push(buffer);
|
||||
return {
|
||||
buffer: buffer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const qtMagic = 32301;
|
||||
|
||||
function processMetadata(buffer, totalSize, quadKey) {
|
||||
const dv = new DataView(buffer);
|
||||
let offset = 0;
|
||||
const magic = dv.getUint32(offset, true);
|
||||
offset += sizeOfUint32;
|
||||
if (magic !== qtMagic) {
|
||||
throw new RuntimeError("Invalid magic");
|
||||
}
|
||||
|
||||
const dataTypeId = dv.getUint32(offset, true);
|
||||
offset += sizeOfUint32;
|
||||
if (dataTypeId !== 1) {
|
||||
throw new RuntimeError("Invalid data type. Must be 1 for QuadTreePacket");
|
||||
}
|
||||
|
||||
// Tile format version
|
||||
const quadVersion = dv.getUint32(offset, true);
|
||||
offset += sizeOfUint32;
|
||||
if (quadVersion !== 2) {
|
||||
throw new RuntimeError(
|
||||
"Invalid QuadTreePacket version. Only version 2 is supported.",
|
||||
);
|
||||
}
|
||||
|
||||
const numInstances = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
const dataInstanceSize = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
if (dataInstanceSize !== 32) {
|
||||
throw new RuntimeError("Invalid instance size.");
|
||||
}
|
||||
|
||||
const dataBufferOffset = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
const dataBufferSize = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
const metaBufferSize = dv.getInt32(offset, true);
|
||||
offset += sizeOfInt32;
|
||||
|
||||
// Offset from beginning of packet (instances + current offset)
|
||||
if (dataBufferOffset !== numInstances * dataInstanceSize + offset) {
|
||||
throw new RuntimeError("Invalid dataBufferOffset");
|
||||
}
|
||||
|
||||
// Verify the packets is all there header + instances + dataBuffer + metaBuffer
|
||||
if (dataBufferOffset + dataBufferSize + metaBufferSize !== totalSize) {
|
||||
throw new RuntimeError("Invalid packet offsets");
|
||||
}
|
||||
|
||||
// Read all the instances
|
||||
const instances = [];
|
||||
for (let i = 0; i < numInstances; ++i) {
|
||||
const bitfield = dv.getUint8(offset);
|
||||
++offset;
|
||||
|
||||
++offset; // 2 byte align
|
||||
|
||||
const cnodeVersion = dv.getUint16(offset, true);
|
||||
offset += sizeOfUint16;
|
||||
|
||||
const imageVersion = dv.getUint16(offset, true);
|
||||
offset += sizeOfUint16;
|
||||
|
||||
const terrainVersion = dv.getUint16(offset, true);
|
||||
offset += sizeOfUint16;
|
||||
|
||||
// Number of channels stored in the dataBuffer
|
||||
offset += sizeOfUint16;
|
||||
|
||||
offset += sizeOfUint16; // 4 byte align
|
||||
|
||||
// Channel type offset into dataBuffer
|
||||
offset += sizeOfInt32;
|
||||
|
||||
// Channel version offset into dataBuffer
|
||||
offset += sizeOfInt32;
|
||||
|
||||
offset += 8; // Ignore image neighbors for now
|
||||
|
||||
// Data providers
|
||||
const imageProvider = dv.getUint8(offset++);
|
||||
const terrainProvider = dv.getUint8(offset++);
|
||||
offset += sizeOfUint16; // 4 byte align
|
||||
|
||||
instances.push(
|
||||
new GoogleEarthEnterpriseTileInformation(
|
||||
bitfield,
|
||||
cnodeVersion,
|
||||
imageVersion,
|
||||
terrainVersion,
|
||||
imageProvider,
|
||||
terrainProvider,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const tileInfo = [];
|
||||
let index = 0;
|
||||
|
||||
function populateTiles(parentKey, parent, level) {
|
||||
let isLeaf = false;
|
||||
if (level === 4) {
|
||||
if (parent.hasSubtree()) {
|
||||
return; // We have a subtree, so just return
|
||||
}
|
||||
|
||||
isLeaf = true; // No subtree, so set all children to null
|
||||
}
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
const childKey = parentKey + i.toString();
|
||||
if (isLeaf) {
|
||||
// No subtree so set all children to null
|
||||
tileInfo[childKey] = null;
|
||||
} else if (level < 4) {
|
||||
// We are still in the middle of the subtree, so add child
|
||||
// only if their bits are set, otherwise set child to null.
|
||||
if (!parent.hasChild(i)) {
|
||||
tileInfo[childKey] = null;
|
||||
} else {
|
||||
if (index === numInstances) {
|
||||
console.log("Incorrect number of instances");
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = instances[index++];
|
||||
tileInfo[childKey] = instance;
|
||||
populateTiles(childKey, instance, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let level = 0;
|
||||
const root = instances[index++];
|
||||
if (quadKey === "") {
|
||||
// Root tile has data at its root and one less level
|
||||
++level;
|
||||
} else {
|
||||
tileInfo[quadKey] = root; // This will only contain the child bitmask
|
||||
}
|
||||
|
||||
populateTiles(quadKey, root, level);
|
||||
|
||||
return tileInfo;
|
||||
}
|
||||
|
||||
const numMeshesPerPacket = 5;
|
||||
const numSubMeshesPerMesh = 4;
|
||||
|
||||
// Each terrain packet will have 5 meshes - each contain 4 sub-meshes:
|
||||
// 1 even level mesh and its 4 odd level children.
|
||||
// Any remaining bytes after the 20 sub-meshes contains water surface meshes,
|
||||
// which are ignored.
|
||||
function processTerrain(buffer, totalSize, transferableObjects) {
|
||||
const dv = new DataView(buffer);
|
||||
|
||||
// Find the sub-meshes.
|
||||
const advanceMesh = function (pos) {
|
||||
for (let sub = 0; sub < numSubMeshesPerMesh; ++sub) {
|
||||
const size = dv.getUint32(pos, true);
|
||||
pos += sizeOfUint32;
|
||||
pos += size;
|
||||
if (pos > totalSize) {
|
||||
throw new RuntimeError("Malformed terrain packet found.");
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
||||
let offset = 0;
|
||||
const terrainMeshes = [];
|
||||
while (terrainMeshes.length < numMeshesPerPacket) {
|
||||
const start = offset;
|
||||
offset = advanceMesh(offset);
|
||||
const mesh = buffer.slice(start, offset);
|
||||
transferableObjects.push(mesh);
|
||||
terrainMeshes.push(mesh);
|
||||
}
|
||||
|
||||
return terrainMeshes;
|
||||
}
|
||||
|
||||
const compressedMagic = 0x7468dead;
|
||||
const compressedMagicSwap = 0xadde6874;
|
||||
|
||||
function uncompressPacket(data) {
|
||||
// The layout of this decoded data is
|
||||
// Magic Uint32
|
||||
// Size Uint32
|
||||
// [GZipped chunk of Size bytes]
|
||||
|
||||
// Pullout magic and verify we have the correct data
|
||||
const dv = new DataView(data);
|
||||
let offset = 0;
|
||||
const magic = dv.getUint32(offset, true);
|
||||
offset += sizeOfUint32;
|
||||
if (magic !== compressedMagic && magic !== compressedMagicSwap) {
|
||||
throw new RuntimeError("Invalid magic");
|
||||
}
|
||||
|
||||
// Get the size of the compressed buffer - the endianness depends on which magic was used
|
||||
const size = dv.getUint32(offset, magic === compressedMagic);
|
||||
offset += sizeOfUint32;
|
||||
|
||||
const compressedPacket = new Uint8Array(data, offset);
|
||||
const uncompressedPacket = inflate(compressedPacket);
|
||||
|
||||
if (uncompressedPacket.length !== size) {
|
||||
throw new RuntimeError("Size of packet doesn't match header");
|
||||
}
|
||||
|
||||
return uncompressedPacket;
|
||||
}
|
||||
export default createTaskProcessorWorker(decodeGoogleEarthEnterprisePacket);
|
||||
+1655
File diff suppressed because it is too large
Load Diff
+34
@@ -0,0 +1,34 @@
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
import { initSync, radix_sort_gaussians_indexes } from "@cesium/wasm-splats";
|
||||
|
||||
//load built wasm modules for sorting. Ensure we can load webassembly and we support SIMD.
|
||||
async function initWorker(parameters, transferableObjects) {
|
||||
// Require and compile WebAssembly module, or use fallback if not supported
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig) && defined(wasmConfig.wasmBinary)) {
|
||||
initSync({ module: wasmConfig.wasmBinary });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function generateGaussianSortWorker(parameters, transferableObjects) {
|
||||
// Handle initialization
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig)) {
|
||||
return initWorker(parameters, transferableObjects);
|
||||
}
|
||||
|
||||
const { primitive, sortType } = parameters;
|
||||
|
||||
if (sortType === "Index") {
|
||||
return radix_sort_gaussians_indexes(
|
||||
primitive.positions,
|
||||
primitive.modelView,
|
||||
primitive.count,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(generateGaussianSortWorker);
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
import defined from "../Core/defined.js";
|
||||
|
||||
import { initSync, generate_splat_texture } from "@cesium/wasm-splats";
|
||||
|
||||
//load built wasm modules for sorting. Ensure we can load webassembly and we support SIMD.
|
||||
async function initWorker(parameters, transferableObjects) {
|
||||
// Require and compile WebAssembly module, or use fallback if not supported
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig) && defined(wasmConfig.wasmBinary)) {
|
||||
initSync({ module: wasmConfig.wasmBinary });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function generateSplatTextureWorker(parameters, transferableObjects) {
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig)) {
|
||||
return initWorker(parameters, transferableObjects);
|
||||
}
|
||||
|
||||
const { attributes, count } = parameters;
|
||||
const result = generate_splat_texture(
|
||||
attributes.positions,
|
||||
attributes.scales,
|
||||
attributes.rotations,
|
||||
attributes.colors,
|
||||
count,
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
};
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(generateSplatTextureWorker);
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
import Matrix4 from "../Core/Matrix4.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import AxisAlignedBoundingBox from "../Core/AxisAlignedBoundingBox.js";
|
||||
|
||||
const scratchAABBCornerMin = new Cartesian3();
|
||||
const scratchAABBCornerMax = new Cartesian3();
|
||||
const scratchTrianglePoints = [
|
||||
new Cartesian3(),
|
||||
new Cartesian3(),
|
||||
new Cartesian3(),
|
||||
];
|
||||
const scratchTriangleAABB = new AxisAlignedBoundingBox();
|
||||
|
||||
const TILE_AABB_MAX = new Cartesian3(0.5, 0.5, 0.5);
|
||||
const TILE_AABB_MIN = new Cartesian3(-0.5, -0.5, -0.5);
|
||||
|
||||
/**
|
||||
* Builds the next layer of the terrain picker's quadtree by determining which triangles intersect
|
||||
* each of the four child nodes. (Essentially distributing the parent's triangles to its children.)
|
||||
*
|
||||
* Takes in the AABBs of the four child nodes in the tree's local space, an inverse transform
|
||||
* to convert triangle positions to the tree's local space, and the parent node's triangle indices and positions.
|
||||
*
|
||||
* Returns an four arrays - one for each child node - containing the indices of the triangles that intersect each node.
|
||||
* @ignore
|
||||
*/
|
||||
function incrementallyBuildTerrainPicker(parameters, transferableObjects) {
|
||||
// Rehydrate worker inputs
|
||||
const aabbs = new Float64Array(parameters.aabbs);
|
||||
const nodeAABBs = Array.from({ length: 4 }, (_, i) => {
|
||||
const min = Cartesian3.unpack(aabbs, i * 6, scratchAABBCornerMin);
|
||||
const max = Cartesian3.unpack(aabbs, i * 6 + 3, scratchAABBCornerMax);
|
||||
return AxisAlignedBoundingBox.fromCorners(
|
||||
min,
|
||||
max,
|
||||
new AxisAlignedBoundingBox(),
|
||||
);
|
||||
});
|
||||
|
||||
const inverseTransformArray = new Float64Array(parameters.inverseTransform);
|
||||
const inverseTransform = Matrix4.unpack(
|
||||
inverseTransformArray,
|
||||
0,
|
||||
new Matrix4(),
|
||||
);
|
||||
|
||||
const triangleIndices = new Uint32Array(parameters.triangleIndices);
|
||||
const trianglePositions = new Float64Array(parameters.trianglePositions);
|
||||
const intersectingTrianglesArrays = Array.from({ length: 4 }, () => []);
|
||||
|
||||
for (let j = 0; j < triangleIndices.length; j++) {
|
||||
Cartesian3.unpack(trianglePositions, j * 9, scratchTrianglePoints[0]);
|
||||
Cartesian3.unpack(trianglePositions, j * 9 + 3, scratchTrianglePoints[1]);
|
||||
Cartesian3.unpack(trianglePositions, j * 9 + 6, scratchTrianglePoints[2]);
|
||||
|
||||
const triangleAABB = createAABBFromTriangle(
|
||||
inverseTransform,
|
||||
scratchTrianglePoints,
|
||||
);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const aabbsIntersect =
|
||||
nodeAABBs[i].intersectAxisAlignedBoundingBox(triangleAABB);
|
||||
if (!aabbsIntersect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
intersectingTrianglesArrays[i].push(triangleIndices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
const intersectingTrianglesTypedArrays = intersectingTrianglesArrays.map(
|
||||
(array) => {
|
||||
const uintArray = new Uint32Array(array);
|
||||
transferableObjects.push(uintArray.buffer);
|
||||
return uintArray.buffer;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
intersectingTrianglesArrays: intersectingTrianglesTypedArrays,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tree-space axis-aligned bounding box from the given triangle points and inverse transform (from world to tree space).
|
||||
* @param {Matrix4} inverseTransform transform from world space to tree local space
|
||||
* @param {Cartesian3[]} trianglePoints array of 3 Cartesian3 points representing the triangle
|
||||
* @returns {AxisAlignedBoundingBox} the axis-aligned bounding box enclosing the triangle in tree local space
|
||||
* @ignore
|
||||
*/
|
||||
function createAABBFromTriangle(inverseTransform, trianglePoints) {
|
||||
Matrix4.multiplyByPoint(
|
||||
inverseTransform,
|
||||
trianglePoints[0],
|
||||
trianglePoints[0],
|
||||
);
|
||||
Matrix4.multiplyByPoint(
|
||||
inverseTransform,
|
||||
trianglePoints[1],
|
||||
trianglePoints[1],
|
||||
);
|
||||
Matrix4.multiplyByPoint(
|
||||
inverseTransform,
|
||||
trianglePoints[2],
|
||||
trianglePoints[2],
|
||||
);
|
||||
|
||||
const aabb = AxisAlignedBoundingBox.fromPoints(
|
||||
trianglePoints,
|
||||
scratchTriangleAABB,
|
||||
);
|
||||
|
||||
// In 2D mode, sometimes the height-scale of a tile is 0. See {@link TerrainMesh#computeTransform2D}.
|
||||
// This makes the inverseTransform degenerate, so we set the height-scale to 1 to be prevent that. However, this is artificial and
|
||||
// can lead to the triangle's AABB extending beyond the (height) bounds of the tile's AABB.
|
||||
// Thus, we clamp the triangle's AABB to the tile's local-space AABB.
|
||||
Cartesian3.clamp(aabb.minimum, TILE_AABB_MIN, TILE_AABB_MAX, aabb.minimum);
|
||||
Cartesian3.clamp(aabb.maximum, TILE_AABB_MIN, TILE_AABB_MAX, aabb.maximum);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(incrementallyBuildTerrainPicker);
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Check from "../Core/Check.js";
|
||||
import PixelFormat from "../Core/PixelFormat.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import VulkanConstants from "../Core//VulkanConstants.js";
|
||||
import PixelDatatype from "../Renderer/PixelDatatype.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
import { read } from "ktx-parse";
|
||||
import basis from "../ThirdParty/Workers/basis_transcoder.js";
|
||||
|
||||
const faceOrder = [
|
||||
"positiveX",
|
||||
"negativeX",
|
||||
"positiveY",
|
||||
"negativeY",
|
||||
"positiveZ",
|
||||
"negativeZ",
|
||||
];
|
||||
|
||||
// Flags
|
||||
const colorModelETC1S = 163;
|
||||
const colorModelUASTC = 166;
|
||||
|
||||
let transcoderModule;
|
||||
function transcode(parameters, transferableObjects) {
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
Check.typeOf.object("transcoderModule", transcoderModule);
|
||||
//>>includeEnd('debug');
|
||||
|
||||
const data = parameters.ktx2Buffer;
|
||||
const supportedTargetFormats = parameters.supportedTargetFormats;
|
||||
let header;
|
||||
try {
|
||||
header = read(data);
|
||||
} catch (e) {
|
||||
throw new RuntimeError("Invalid KTX2 file.");
|
||||
}
|
||||
|
||||
if (header.layerCount !== 0) {
|
||||
throw new RuntimeError("KTX2 texture arrays are not supported.");
|
||||
}
|
||||
|
||||
if (header.pixelDepth !== 0) {
|
||||
throw new RuntimeError("KTX2 3D textures are unsupported.");
|
||||
}
|
||||
|
||||
const dfd = header.dataFormatDescriptor[0];
|
||||
const result = new Array(header.levelCount);
|
||||
|
||||
if (
|
||||
header.vkFormat === 0x0 &&
|
||||
(dfd.colorModel === colorModelETC1S || dfd.colorModel === colorModelUASTC)
|
||||
) {
|
||||
// Compressed, initialize transcoder module
|
||||
transcodeCompressed(
|
||||
data,
|
||||
header,
|
||||
supportedTargetFormats,
|
||||
transcoderModule,
|
||||
transferableObjects,
|
||||
result,
|
||||
);
|
||||
} else {
|
||||
transferableObjects.push(data.buffer);
|
||||
parseUncompressed(header, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parser for uncompressed
|
||||
function parseUncompressed(header, result) {
|
||||
const internalFormat =
|
||||
header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8_SRGB
|
||||
? PixelFormat.RGB
|
||||
: PixelFormat.RGBA;
|
||||
let datatype;
|
||||
if (header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8A8_UNORM) {
|
||||
datatype = PixelDatatype.UNSIGNED_BYTE;
|
||||
} else if (
|
||||
header.vkFormat === VulkanConstants.VK_FORMAT_R16G16B16A16_SFLOAT
|
||||
) {
|
||||
datatype = PixelDatatype.HALF_FLOAT;
|
||||
} else if (
|
||||
header.vkFormat === VulkanConstants.VK_FORMAT_R32G32B32A32_SFLOAT
|
||||
) {
|
||||
datatype = PixelDatatype.FLOAT;
|
||||
}
|
||||
|
||||
for (let i = 0; i < header.levels.length; ++i) {
|
||||
const level = {};
|
||||
result[i] = level;
|
||||
const levelBuffer = header.levels[i].levelData;
|
||||
|
||||
const width = header.pixelWidth >> i;
|
||||
const height = header.pixelHeight >> i;
|
||||
const faceLength =
|
||||
width * height * PixelFormat.componentsLength(internalFormat);
|
||||
|
||||
for (let j = 0; j < header.faceCount; ++j) {
|
||||
// multiply levelBuffer.byteOffset by the size in bytes of the pixel data type
|
||||
const faceByteOffset =
|
||||
levelBuffer.byteOffset + faceLength * header.typeSize * j;
|
||||
let faceView;
|
||||
if (!defined(datatype) || PixelDatatype.sizeInBytes(datatype) === 1) {
|
||||
faceView = new Uint8Array(
|
||||
levelBuffer.buffer,
|
||||
faceByteOffset,
|
||||
faceLength,
|
||||
);
|
||||
} else if (PixelDatatype.sizeInBytes(datatype) === 2) {
|
||||
faceView = new Uint16Array(
|
||||
levelBuffer.buffer,
|
||||
faceByteOffset,
|
||||
faceLength,
|
||||
);
|
||||
} else {
|
||||
faceView = new Float32Array(
|
||||
levelBuffer.buffer,
|
||||
faceByteOffset,
|
||||
faceLength,
|
||||
);
|
||||
}
|
||||
|
||||
level[faceOrder[j]] = {
|
||||
internalFormat: internalFormat,
|
||||
datatype: datatype,
|
||||
width: width,
|
||||
height: height,
|
||||
levelBuffer: faceView,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transcodeCompressed(
|
||||
data,
|
||||
header,
|
||||
supportedTargetFormats,
|
||||
transcoderModule,
|
||||
transferableObjects,
|
||||
result,
|
||||
) {
|
||||
const ktx2File = new transcoderModule.KTX2File(data);
|
||||
let width = ktx2File.getWidth();
|
||||
let height = ktx2File.getHeight();
|
||||
const levels = ktx2File.getLevels();
|
||||
const hasAlpha = ktx2File.getHasAlpha();
|
||||
|
||||
if (!(width > 0) || !(height > 0) || !(levels > 0)) {
|
||||
ktx2File.close();
|
||||
ktx2File.delete();
|
||||
throw new RuntimeError("Invalid KTX2 file");
|
||||
}
|
||||
|
||||
let internalFormat, transcoderFormat;
|
||||
const dfd = header.dataFormatDescriptor[0];
|
||||
const BasisFormat = transcoderModule.transcoder_texture_format;
|
||||
|
||||
// Determine target format based on platform support
|
||||
if (dfd.colorModel === colorModelETC1S) {
|
||||
if (supportedTargetFormats.etc) {
|
||||
internalFormat = hasAlpha
|
||||
? PixelFormat.RGBA8_ETC2_EAC
|
||||
: PixelFormat.RGB8_ETC2;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFETC2_RGBA
|
||||
: BasisFormat.cTFETC1_RGB;
|
||||
} else if (supportedTargetFormats.etc1 && !hasAlpha) {
|
||||
internalFormat = PixelFormat.RGB_ETC1;
|
||||
transcoderFormat = BasisFormat.cTFETC1_RGB;
|
||||
} else if (supportedTargetFormats.s3tc) {
|
||||
internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFBC3_RGBA
|
||||
: BasisFormat.cTFBC1_RGB;
|
||||
} else if (supportedTargetFormats.pvrtc) {
|
||||
internalFormat = hasAlpha
|
||||
? PixelFormat.RGBA_PVRTC_4BPPV1
|
||||
: PixelFormat.RGB_PVRTC_4BPPV1;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFPVRTC1_4_RGBA
|
||||
: BasisFormat.cTFPVRTC1_4_RGB;
|
||||
} else if (supportedTargetFormats.astc) {
|
||||
internalFormat = PixelFormat.RGBA_ASTC;
|
||||
transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;
|
||||
} else if (supportedTargetFormats.bc7) {
|
||||
internalFormat = PixelFormat.RGBA_BC7;
|
||||
transcoderFormat = BasisFormat.cTFBC7_RGBA;
|
||||
} else {
|
||||
throw new RuntimeError(
|
||||
"No transcoding format target available for ETC1S compressed ktx2.",
|
||||
);
|
||||
}
|
||||
} else if (dfd.colorModel === colorModelUASTC) {
|
||||
if (supportedTargetFormats.astc) {
|
||||
internalFormat = PixelFormat.RGBA_ASTC;
|
||||
transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;
|
||||
} else if (supportedTargetFormats.bc7) {
|
||||
internalFormat = PixelFormat.RGBA_BC7;
|
||||
transcoderFormat = BasisFormat.cTFBC7_RGBA;
|
||||
} else if (supportedTargetFormats.s3tc) {
|
||||
internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFBC3_RGBA
|
||||
: BasisFormat.cTFBC1_RGB;
|
||||
} else if (supportedTargetFormats.etc) {
|
||||
internalFormat = hasAlpha
|
||||
? PixelFormat.RGBA8_ETC2_EAC
|
||||
: PixelFormat.RGB8_ETC2;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFETC2_RGBA
|
||||
: BasisFormat.cTFETC1_RGB;
|
||||
} else if (supportedTargetFormats.etc1 && !hasAlpha) {
|
||||
internalFormat = PixelFormat.RGB_ETC1;
|
||||
transcoderFormat = BasisFormat.cTFETC1_RGB;
|
||||
} else if (supportedTargetFormats.pvrtc) {
|
||||
internalFormat = hasAlpha
|
||||
? PixelFormat.RGBA_PVRTC_4BPPV1
|
||||
: PixelFormat.RGB_PVRTC_4BPPV1;
|
||||
transcoderFormat = hasAlpha
|
||||
? BasisFormat.cTFPVRTC1_4_RGBA
|
||||
: BasisFormat.cTFPVRTC1_4_RGB;
|
||||
} else {
|
||||
throw new RuntimeError(
|
||||
"No transcoding format target available for UASTC compressed ktx2.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ktx2File.startTranscoding()) {
|
||||
ktx2File.close();
|
||||
ktx2File.delete();
|
||||
throw new RuntimeError("startTranscoding() failed");
|
||||
}
|
||||
|
||||
for (let i = 0; i < header.levels.length; ++i) {
|
||||
const level = {};
|
||||
result[i] = level;
|
||||
width = header.pixelWidth >> i;
|
||||
height = header.pixelHeight >> i;
|
||||
|
||||
// Since supercompressed cubemaps are unsupported, this function
|
||||
// does not iterate over KTX2 faces and assumes faceCount = 1.
|
||||
|
||||
const dstSize = ktx2File.getImageTranscodedSizeInBytes(
|
||||
i, // level index
|
||||
0, // layer index
|
||||
0, // face index
|
||||
transcoderFormat.value,
|
||||
);
|
||||
const dst = new Uint8Array(dstSize);
|
||||
|
||||
const transcoded = ktx2File.transcodeImage(
|
||||
dst,
|
||||
i, // level index
|
||||
0, // layer index
|
||||
0, // face index
|
||||
transcoderFormat.value,
|
||||
0, // get_alpha_for_opaque_formats
|
||||
-1, // channel0
|
||||
-1, // channel1
|
||||
);
|
||||
|
||||
if (!defined(transcoded)) {
|
||||
throw new RuntimeError("transcodeImage() failed.");
|
||||
}
|
||||
|
||||
transferableObjects.push(dst.buffer);
|
||||
|
||||
level[faceOrder[0]] = {
|
||||
internalFormat: internalFormat,
|
||||
width: width,
|
||||
height: height,
|
||||
levelBuffer: dst,
|
||||
};
|
||||
}
|
||||
|
||||
ktx2File.close();
|
||||
ktx2File.delete();
|
||||
return result;
|
||||
}
|
||||
|
||||
async function initWorker(parameters, transferableObjects) {
|
||||
// Require and compile WebAssembly module, or use fallback if not supported
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
const basisTranscoder = basis ?? self.BASIS;
|
||||
if (defined(wasmConfig.wasmBinaryFile)) {
|
||||
transcoderModule = await basisTranscoder(wasmConfig);
|
||||
} else {
|
||||
transcoderModule = await basisTranscoder();
|
||||
}
|
||||
|
||||
transcoderModule.initializeBasis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transcodeKTX2(parameters, transferableObjects) {
|
||||
// Expect the first message to be to load a web assembly module
|
||||
const wasmConfig = parameters.webAssemblyConfig;
|
||||
if (defined(wasmConfig)) {
|
||||
return initWorker(parameters, transferableObjects);
|
||||
}
|
||||
|
||||
return transcode(parameters, transferableObjects);
|
||||
}
|
||||
export default createTaskProcessorWorker(transcodeKTX2);
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
self.onmessage = function (event) {
|
||||
const array = event.data.array;
|
||||
const postMessage = self.webkitPostMessage || self.postMessage;
|
||||
|
||||
try {
|
||||
// transfer the test array back to the caller
|
||||
postMessage(
|
||||
{
|
||||
array: array,
|
||||
},
|
||||
[array.buffer],
|
||||
);
|
||||
} catch (e) {
|
||||
postMessage({});
|
||||
}
|
||||
};
|
||||
+681
@@ -0,0 +1,681 @@
|
||||
import AttributeCompression from "../Core/AttributeCompression.js";
|
||||
import BoundingSphere from "../Core/BoundingSphere.js";
|
||||
import Cartesian2 from "../Core/Cartesian2.js";
|
||||
import Cartesian3 from "../Core/Cartesian3.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import defined from "../Core/defined.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import EllipsoidalOccluder from "../Core/EllipsoidalOccluder.js";
|
||||
import IndexDatatype from "../Core/IndexDatatype.js";
|
||||
import Intersections2D from "../Core/Intersections2D.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import TerrainEncoding from "../Core/TerrainEncoding.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
const maxShort = 32767;
|
||||
const halfMaxShort = (maxShort / 2) | 0;
|
||||
|
||||
const clipScratch = [];
|
||||
const clipScratch2 = [];
|
||||
const verticesScratch = [];
|
||||
const cartographicScratch = new Cartographic();
|
||||
let cartesian3Scratch = new Cartesian3();
|
||||
const uScratch = [];
|
||||
const vScratch = [];
|
||||
const heightScratch = [];
|
||||
const indicesScratch = [];
|
||||
const normalsScratch = [];
|
||||
const horizonOcclusionPointScratch = new Cartesian3();
|
||||
const boundingSphereScratch = new BoundingSphere();
|
||||
const orientedBoundingBoxScratch = new OrientedBoundingBox();
|
||||
const decodeTexCoordsScratch = new Cartesian2();
|
||||
const octEncodedNormalScratch = new Cartesian3();
|
||||
|
||||
function upsampleQuantizedTerrainMesh(parameters, transferableObjects) {
|
||||
const isEastChild = parameters.isEastChild;
|
||||
const isNorthChild = parameters.isNorthChild;
|
||||
|
||||
const minU = isEastChild ? halfMaxShort : 0;
|
||||
const maxU = isEastChild ? maxShort : halfMaxShort;
|
||||
const minV = isNorthChild ? halfMaxShort : 0;
|
||||
const maxV = isNorthChild ? maxShort : halfMaxShort;
|
||||
|
||||
const uBuffer = uScratch;
|
||||
const vBuffer = vScratch;
|
||||
const heightBuffer = heightScratch;
|
||||
const normalBuffer = normalsScratch;
|
||||
|
||||
uBuffer.length = 0;
|
||||
vBuffer.length = 0;
|
||||
heightBuffer.length = 0;
|
||||
normalBuffer.length = 0;
|
||||
|
||||
const indices = indicesScratch;
|
||||
indices.length = 0;
|
||||
|
||||
const vertexMap = {};
|
||||
|
||||
const parentVertices = parameters.vertices;
|
||||
let parentIndices = parameters.indices;
|
||||
parentIndices = parentIndices.subarray(0, parameters.indexCountWithoutSkirts);
|
||||
|
||||
const encoding = TerrainEncoding.clone(parameters.encoding);
|
||||
const hasVertexNormals = encoding.hasVertexNormals;
|
||||
|
||||
let vertexCount = 0;
|
||||
const quantizedVertexCount = parameters.vertexCountWithoutSkirts;
|
||||
|
||||
const parentMinimumHeight = parameters.minimumHeight;
|
||||
const parentMaximumHeight = parameters.maximumHeight;
|
||||
|
||||
const parentUBuffer = new Array(quantizedVertexCount);
|
||||
const parentVBuffer = new Array(quantizedVertexCount);
|
||||
const parentHeightBuffer = new Array(quantizedVertexCount);
|
||||
const parentNormalBuffer = hasVertexNormals
|
||||
? new Array(quantizedVertexCount * 2)
|
||||
: undefined;
|
||||
|
||||
const threshold = 20;
|
||||
let height;
|
||||
|
||||
let i, n;
|
||||
let u, v;
|
||||
for (i = 0, n = 0; i < quantizedVertexCount; ++i, n += 2) {
|
||||
const texCoords = encoding.decodeTextureCoordinates(
|
||||
parentVertices,
|
||||
i,
|
||||
decodeTexCoordsScratch,
|
||||
);
|
||||
height = encoding.decodeHeight(parentVertices, i);
|
||||
|
||||
u = CesiumMath.clamp((texCoords.x * maxShort) | 0, 0, maxShort);
|
||||
v = CesiumMath.clamp((texCoords.y * maxShort) | 0, 0, maxShort);
|
||||
parentHeightBuffer[i] = CesiumMath.clamp(
|
||||
(((height - parentMinimumHeight) /
|
||||
(parentMaximumHeight - parentMinimumHeight)) *
|
||||
maxShort) |
|
||||
0,
|
||||
0,
|
||||
maxShort,
|
||||
);
|
||||
|
||||
if (u < threshold) {
|
||||
u = 0;
|
||||
}
|
||||
|
||||
if (v < threshold) {
|
||||
v = 0;
|
||||
}
|
||||
|
||||
if (maxShort - u < threshold) {
|
||||
u = maxShort;
|
||||
}
|
||||
|
||||
if (maxShort - v < threshold) {
|
||||
v = maxShort;
|
||||
}
|
||||
|
||||
parentUBuffer[i] = u;
|
||||
parentVBuffer[i] = v;
|
||||
|
||||
if (hasVertexNormals) {
|
||||
const encodedNormal = encoding.getOctEncodedNormal(
|
||||
parentVertices,
|
||||
i,
|
||||
octEncodedNormalScratch,
|
||||
);
|
||||
parentNormalBuffer[n] = encodedNormal.x;
|
||||
parentNormalBuffer[n + 1] = encodedNormal.y;
|
||||
}
|
||||
|
||||
if (
|
||||
((isEastChild && u >= halfMaxShort) ||
|
||||
(!isEastChild && u <= halfMaxShort)) &&
|
||||
((isNorthChild && v >= halfMaxShort) ||
|
||||
(!isNorthChild && v <= halfMaxShort))
|
||||
) {
|
||||
vertexMap[i] = vertexCount;
|
||||
uBuffer.push(u);
|
||||
vBuffer.push(v);
|
||||
heightBuffer.push(parentHeightBuffer[i]);
|
||||
if (hasVertexNormals) {
|
||||
normalBuffer.push(parentNormalBuffer[n]);
|
||||
normalBuffer.push(parentNormalBuffer[n + 1]);
|
||||
}
|
||||
|
||||
++vertexCount;
|
||||
}
|
||||
}
|
||||
|
||||
const triangleVertices = [];
|
||||
triangleVertices.push(new Vertex());
|
||||
triangleVertices.push(new Vertex());
|
||||
triangleVertices.push(new Vertex());
|
||||
|
||||
const clippedTriangleVertices = [];
|
||||
clippedTriangleVertices.push(new Vertex());
|
||||
clippedTriangleVertices.push(new Vertex());
|
||||
clippedTriangleVertices.push(new Vertex());
|
||||
|
||||
let clippedIndex;
|
||||
let clipped2;
|
||||
|
||||
for (i = 0; i < parentIndices.length; i += 3) {
|
||||
const i0 = parentIndices[i];
|
||||
const i1 = parentIndices[i + 1];
|
||||
const i2 = parentIndices[i + 2];
|
||||
|
||||
const u0 = parentUBuffer[i0];
|
||||
const u1 = parentUBuffer[i1];
|
||||
const u2 = parentUBuffer[i2];
|
||||
|
||||
triangleVertices[0].initializeIndexed(
|
||||
parentUBuffer,
|
||||
parentVBuffer,
|
||||
parentHeightBuffer,
|
||||
parentNormalBuffer,
|
||||
i0,
|
||||
);
|
||||
triangleVertices[1].initializeIndexed(
|
||||
parentUBuffer,
|
||||
parentVBuffer,
|
||||
parentHeightBuffer,
|
||||
parentNormalBuffer,
|
||||
i1,
|
||||
);
|
||||
triangleVertices[2].initializeIndexed(
|
||||
parentUBuffer,
|
||||
parentVBuffer,
|
||||
parentHeightBuffer,
|
||||
parentNormalBuffer,
|
||||
i2,
|
||||
);
|
||||
|
||||
// Clip triangle on the east-west boundary.
|
||||
const clipped = Intersections2D.clipTriangleAtAxisAlignedThreshold(
|
||||
halfMaxShort,
|
||||
isEastChild,
|
||||
u0,
|
||||
u1,
|
||||
u2,
|
||||
clipScratch,
|
||||
);
|
||||
|
||||
// Get the first clipped triangle, if any.
|
||||
clippedIndex = 0;
|
||||
|
||||
if (clippedIndex >= clipped.length) {
|
||||
continue;
|
||||
}
|
||||
clippedIndex = clippedTriangleVertices[0].initializeFromClipResult(
|
||||
clipped,
|
||||
clippedIndex,
|
||||
triangleVertices,
|
||||
);
|
||||
|
||||
if (clippedIndex >= clipped.length) {
|
||||
continue;
|
||||
}
|
||||
clippedIndex = clippedTriangleVertices[1].initializeFromClipResult(
|
||||
clipped,
|
||||
clippedIndex,
|
||||
triangleVertices,
|
||||
);
|
||||
|
||||
if (clippedIndex >= clipped.length) {
|
||||
continue;
|
||||
}
|
||||
clippedIndex = clippedTriangleVertices[2].initializeFromClipResult(
|
||||
clipped,
|
||||
clippedIndex,
|
||||
triangleVertices,
|
||||
);
|
||||
|
||||
// Clip the triangle against the North-south boundary.
|
||||
clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(
|
||||
halfMaxShort,
|
||||
isNorthChild,
|
||||
clippedTriangleVertices[0].getV(),
|
||||
clippedTriangleVertices[1].getV(),
|
||||
clippedTriangleVertices[2].getV(),
|
||||
clipScratch2,
|
||||
);
|
||||
addClippedPolygon(
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
normalBuffer,
|
||||
indices,
|
||||
vertexMap,
|
||||
clipped2,
|
||||
clippedTriangleVertices,
|
||||
hasVertexNormals,
|
||||
);
|
||||
|
||||
// If there's another vertex in the original clipped result,
|
||||
// it forms a second triangle. Clip it as well.
|
||||
if (clippedIndex < clipped.length) {
|
||||
clippedTriangleVertices[2].clone(clippedTriangleVertices[1]);
|
||||
clippedTriangleVertices[2].initializeFromClipResult(
|
||||
clipped,
|
||||
clippedIndex,
|
||||
triangleVertices,
|
||||
);
|
||||
|
||||
clipped2 = Intersections2D.clipTriangleAtAxisAlignedThreshold(
|
||||
halfMaxShort,
|
||||
isNorthChild,
|
||||
clippedTriangleVertices[0].getV(),
|
||||
clippedTriangleVertices[1].getV(),
|
||||
clippedTriangleVertices[2].getV(),
|
||||
clipScratch2,
|
||||
);
|
||||
addClippedPolygon(
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
normalBuffer,
|
||||
indices,
|
||||
vertexMap,
|
||||
clipped2,
|
||||
clippedTriangleVertices,
|
||||
hasVertexNormals,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const uOffset = isEastChild ? -maxShort : 0;
|
||||
const vOffset = isNorthChild ? -maxShort : 0;
|
||||
|
||||
const westIndices = [];
|
||||
const southIndices = [];
|
||||
const eastIndices = [];
|
||||
const northIndices = [];
|
||||
|
||||
let minimumHeight = Number.MAX_VALUE;
|
||||
let maximumHeight = -minimumHeight;
|
||||
|
||||
const cartesianVertices = verticesScratch;
|
||||
cartesianVertices.length = 0;
|
||||
|
||||
const ellipsoid = Ellipsoid.clone(parameters.ellipsoid);
|
||||
const rectangle = Rectangle.clone(parameters.childRectangle);
|
||||
|
||||
const north = rectangle.north;
|
||||
const south = rectangle.south;
|
||||
let east = rectangle.east;
|
||||
const west = rectangle.west;
|
||||
|
||||
if (east < west) {
|
||||
east += CesiumMath.TWO_PI;
|
||||
}
|
||||
|
||||
for (i = 0; i < uBuffer.length; ++i) {
|
||||
u = Math.round(uBuffer[i]);
|
||||
if (u <= minU) {
|
||||
westIndices.push(i);
|
||||
u = 0;
|
||||
} else if (u >= maxU) {
|
||||
eastIndices.push(i);
|
||||
u = maxShort;
|
||||
} else {
|
||||
u = u * 2 + uOffset;
|
||||
}
|
||||
|
||||
uBuffer[i] = u;
|
||||
|
||||
v = Math.round(vBuffer[i]);
|
||||
if (v <= minV) {
|
||||
southIndices.push(i);
|
||||
v = 0;
|
||||
} else if (v >= maxV) {
|
||||
northIndices.push(i);
|
||||
v = maxShort;
|
||||
} else {
|
||||
v = v * 2 + vOffset;
|
||||
}
|
||||
|
||||
vBuffer[i] = v;
|
||||
|
||||
height = CesiumMath.lerp(
|
||||
parentMinimumHeight,
|
||||
parentMaximumHeight,
|
||||
heightBuffer[i] / maxShort,
|
||||
);
|
||||
if (height < minimumHeight) {
|
||||
minimumHeight = height;
|
||||
}
|
||||
if (height > maximumHeight) {
|
||||
maximumHeight = height;
|
||||
}
|
||||
|
||||
heightBuffer[i] = height;
|
||||
|
||||
cartographicScratch.longitude = CesiumMath.lerp(west, east, u / maxShort);
|
||||
cartographicScratch.latitude = CesiumMath.lerp(south, north, v / maxShort);
|
||||
cartographicScratch.height = height;
|
||||
|
||||
ellipsoid.cartographicToCartesian(cartographicScratch, cartesian3Scratch);
|
||||
|
||||
cartesianVertices.push(cartesian3Scratch.x);
|
||||
cartesianVertices.push(cartesian3Scratch.y);
|
||||
cartesianVertices.push(cartesian3Scratch.z);
|
||||
}
|
||||
|
||||
const boundingSphere = BoundingSphere.fromVertices(
|
||||
cartesianVertices,
|
||||
Cartesian3.ZERO,
|
||||
3,
|
||||
boundingSphereScratch,
|
||||
);
|
||||
const orientedBoundingBox = OrientedBoundingBox.fromRectangle(
|
||||
rectangle,
|
||||
minimumHeight,
|
||||
maximumHeight,
|
||||
ellipsoid,
|
||||
orientedBoundingBoxScratch,
|
||||
);
|
||||
|
||||
const occluder = new EllipsoidalOccluder(ellipsoid);
|
||||
const horizonOcclusionPoint =
|
||||
occluder.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid(
|
||||
boundingSphere.center,
|
||||
cartesianVertices,
|
||||
3,
|
||||
boundingSphere.center,
|
||||
minimumHeight,
|
||||
horizonOcclusionPointScratch,
|
||||
);
|
||||
|
||||
const heightRange = maximumHeight - minimumHeight;
|
||||
|
||||
const vertices = new Uint16Array(
|
||||
uBuffer.length + vBuffer.length + heightBuffer.length,
|
||||
);
|
||||
|
||||
for (i = 0; i < uBuffer.length; ++i) {
|
||||
vertices[i] = uBuffer[i];
|
||||
}
|
||||
|
||||
let start = uBuffer.length;
|
||||
|
||||
for (i = 0; i < vBuffer.length; ++i) {
|
||||
vertices[start + i] = vBuffer[i];
|
||||
}
|
||||
|
||||
start += vBuffer.length;
|
||||
|
||||
for (i = 0; i < heightBuffer.length; ++i) {
|
||||
vertices[start + i] =
|
||||
(maxShort * (heightBuffer[i] - minimumHeight)) / heightRange;
|
||||
}
|
||||
|
||||
const indicesTypedArray = IndexDatatype.createTypedArray(
|
||||
uBuffer.length,
|
||||
indices,
|
||||
);
|
||||
|
||||
let encodedNormals;
|
||||
if (hasVertexNormals) {
|
||||
const normalArray = new Uint8Array(normalBuffer);
|
||||
transferableObjects.push(
|
||||
vertices.buffer,
|
||||
indicesTypedArray.buffer,
|
||||
normalArray.buffer,
|
||||
);
|
||||
encodedNormals = normalArray.buffer;
|
||||
} else {
|
||||
transferableObjects.push(vertices.buffer, indicesTypedArray.buffer);
|
||||
}
|
||||
|
||||
return {
|
||||
vertices: vertices.buffer,
|
||||
encodedNormals: encodedNormals,
|
||||
indices: indicesTypedArray.buffer,
|
||||
minimumHeight: minimumHeight,
|
||||
maximumHeight: maximumHeight,
|
||||
westIndices: westIndices,
|
||||
southIndices: southIndices,
|
||||
eastIndices: eastIndices,
|
||||
northIndices: northIndices,
|
||||
boundingSphere: boundingSphere,
|
||||
orientedBoundingBox: orientedBoundingBox,
|
||||
horizonOcclusionPoint: horizonOcclusionPoint,
|
||||
};
|
||||
}
|
||||
|
||||
function Vertex() {
|
||||
this.vertexBuffer = undefined;
|
||||
this.index = undefined;
|
||||
this.first = undefined;
|
||||
this.second = undefined;
|
||||
this.ratio = undefined;
|
||||
}
|
||||
|
||||
Vertex.prototype.clone = function (result) {
|
||||
if (!defined(result)) {
|
||||
result = new Vertex();
|
||||
}
|
||||
|
||||
result.uBuffer = this.uBuffer;
|
||||
result.vBuffer = this.vBuffer;
|
||||
result.heightBuffer = this.heightBuffer;
|
||||
result.normalBuffer = this.normalBuffer;
|
||||
result.index = this.index;
|
||||
result.first = this.first;
|
||||
result.second = this.second;
|
||||
result.ratio = this.ratio;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Vertex.prototype.initializeIndexed = function (
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
normalBuffer,
|
||||
index,
|
||||
) {
|
||||
this.uBuffer = uBuffer;
|
||||
this.vBuffer = vBuffer;
|
||||
this.heightBuffer = heightBuffer;
|
||||
this.normalBuffer = normalBuffer;
|
||||
this.index = index;
|
||||
this.first = undefined;
|
||||
this.second = undefined;
|
||||
this.ratio = undefined;
|
||||
};
|
||||
|
||||
Vertex.prototype.initializeFromClipResult = function (
|
||||
clipResult,
|
||||
index,
|
||||
vertices,
|
||||
) {
|
||||
let nextIndex = index + 1;
|
||||
|
||||
if (clipResult[index] !== -1) {
|
||||
vertices[clipResult[index]].clone(this);
|
||||
} else {
|
||||
this.vertexBuffer = undefined;
|
||||
this.index = undefined;
|
||||
this.first = vertices[clipResult[nextIndex]];
|
||||
++nextIndex;
|
||||
this.second = vertices[clipResult[nextIndex]];
|
||||
++nextIndex;
|
||||
this.ratio = clipResult[nextIndex];
|
||||
++nextIndex;
|
||||
}
|
||||
|
||||
return nextIndex;
|
||||
};
|
||||
|
||||
Vertex.prototype.getKey = function () {
|
||||
if (this.isIndexed()) {
|
||||
return this.index;
|
||||
}
|
||||
return JSON.stringify({
|
||||
first: this.first.getKey(),
|
||||
second: this.second.getKey(),
|
||||
ratio: this.ratio,
|
||||
});
|
||||
};
|
||||
|
||||
Vertex.prototype.isIndexed = function () {
|
||||
return defined(this.index);
|
||||
};
|
||||
|
||||
Vertex.prototype.getH = function () {
|
||||
if (defined(this.index)) {
|
||||
return this.heightBuffer[this.index];
|
||||
}
|
||||
return CesiumMath.lerp(this.first.getH(), this.second.getH(), this.ratio);
|
||||
};
|
||||
|
||||
Vertex.prototype.getU = function () {
|
||||
if (defined(this.index)) {
|
||||
return this.uBuffer[this.index];
|
||||
}
|
||||
return CesiumMath.lerp(this.first.getU(), this.second.getU(), this.ratio);
|
||||
};
|
||||
|
||||
Vertex.prototype.getV = function () {
|
||||
if (defined(this.index)) {
|
||||
return this.vBuffer[this.index];
|
||||
}
|
||||
return CesiumMath.lerp(this.first.getV(), this.second.getV(), this.ratio);
|
||||
};
|
||||
|
||||
let encodedScratch = new Cartesian2();
|
||||
// An upsampled triangle may be clipped twice before it is assigned an index
|
||||
// In this case, we need a buffer to handle the recursion of getNormalX() and getNormalY().
|
||||
let depth = -1;
|
||||
const cartesianScratch1 = [new Cartesian3(), new Cartesian3()];
|
||||
const cartesianScratch2 = [new Cartesian3(), new Cartesian3()];
|
||||
function lerpOctEncodedNormal(vertex, result) {
|
||||
++depth;
|
||||
|
||||
let first = cartesianScratch1[depth];
|
||||
let second = cartesianScratch2[depth];
|
||||
|
||||
first = AttributeCompression.octDecode(
|
||||
vertex.first.getNormalX(),
|
||||
vertex.first.getNormalY(),
|
||||
first,
|
||||
);
|
||||
second = AttributeCompression.octDecode(
|
||||
vertex.second.getNormalX(),
|
||||
vertex.second.getNormalY(),
|
||||
second,
|
||||
);
|
||||
cartesian3Scratch = Cartesian3.lerp(
|
||||
first,
|
||||
second,
|
||||
vertex.ratio,
|
||||
cartesian3Scratch,
|
||||
);
|
||||
Cartesian3.normalize(cartesian3Scratch, cartesian3Scratch);
|
||||
|
||||
AttributeCompression.octEncode(cartesian3Scratch, result);
|
||||
|
||||
--depth;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vertex.prototype.getNormalX = function () {
|
||||
if (defined(this.index)) {
|
||||
return this.normalBuffer[this.index * 2];
|
||||
}
|
||||
|
||||
encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
|
||||
return encodedScratch.x;
|
||||
};
|
||||
|
||||
Vertex.prototype.getNormalY = function () {
|
||||
if (defined(this.index)) {
|
||||
return this.normalBuffer[this.index * 2 + 1];
|
||||
}
|
||||
|
||||
encodedScratch = lerpOctEncodedNormal(this, encodedScratch);
|
||||
return encodedScratch.y;
|
||||
};
|
||||
|
||||
const polygonVertices = [];
|
||||
polygonVertices.push(new Vertex());
|
||||
polygonVertices.push(new Vertex());
|
||||
polygonVertices.push(new Vertex());
|
||||
polygonVertices.push(new Vertex());
|
||||
|
||||
function addClippedPolygon(
|
||||
uBuffer,
|
||||
vBuffer,
|
||||
heightBuffer,
|
||||
normalBuffer,
|
||||
indices,
|
||||
vertexMap,
|
||||
clipped,
|
||||
triangleVertices,
|
||||
hasVertexNormals,
|
||||
) {
|
||||
if (clipped.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let numVertices = 0;
|
||||
let clippedIndex = 0;
|
||||
while (clippedIndex < clipped.length) {
|
||||
clippedIndex = polygonVertices[numVertices++].initializeFromClipResult(
|
||||
clipped,
|
||||
clippedIndex,
|
||||
triangleVertices,
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < numVertices; ++i) {
|
||||
const polygonVertex = polygonVertices[i];
|
||||
if (!polygonVertex.isIndexed()) {
|
||||
const key = polygonVertex.getKey();
|
||||
if (defined(vertexMap[key])) {
|
||||
polygonVertex.newIndex = vertexMap[key];
|
||||
} else {
|
||||
const newIndex = uBuffer.length;
|
||||
uBuffer.push(polygonVertex.getU());
|
||||
vBuffer.push(polygonVertex.getV());
|
||||
heightBuffer.push(polygonVertex.getH());
|
||||
if (hasVertexNormals) {
|
||||
normalBuffer.push(polygonVertex.getNormalX());
|
||||
normalBuffer.push(polygonVertex.getNormalY());
|
||||
}
|
||||
polygonVertex.newIndex = newIndex;
|
||||
vertexMap[key] = newIndex;
|
||||
}
|
||||
} else {
|
||||
polygonVertex.newIndex = vertexMap[polygonVertex.index];
|
||||
polygonVertex.uBuffer = uBuffer;
|
||||
polygonVertex.vBuffer = vBuffer;
|
||||
polygonVertex.heightBuffer = heightBuffer;
|
||||
if (hasVertexNormals) {
|
||||
polygonVertex.normalBuffer = normalBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numVertices === 3) {
|
||||
// A triangle.
|
||||
indices.push(polygonVertices[0].newIndex);
|
||||
indices.push(polygonVertices[1].newIndex);
|
||||
indices.push(polygonVertices[2].newIndex);
|
||||
} else if (numVertices === 4) {
|
||||
// A quad - two triangles.
|
||||
indices.push(polygonVertices[0].newIndex);
|
||||
indices.push(polygonVertices[1].newIndex);
|
||||
indices.push(polygonVertices[2].newIndex);
|
||||
|
||||
indices.push(polygonVertices[0].newIndex);
|
||||
indices.push(polygonVertices[2].newIndex);
|
||||
indices.push(polygonVertices[3].newIndex);
|
||||
}
|
||||
}
|
||||
export default createTaskProcessorWorker(upsampleQuantizedTerrainMesh);
|
||||
Generated
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
import Cesium3DTilesTerrainGeometryProcessor from "../Core/Cesium3DTilesTerrainGeometryProcessor.js";
|
||||
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Cesium3DTilesTerrainGeometryProcessor.UpsampleMeshOptions} options An object describing options for mesh upsampling.
|
||||
* @param {ArrayBuffer[]} transferableObjects An array of buffers that can be transferred back to the main thread.
|
||||
* @returns {TerrainMeshProxy} An object containing selected info from the upsampled TerrainMesh.
|
||||
*/
|
||||
|
||||
function upsampleVerticesFromCesium3DTilesTerrain(
|
||||
options,
|
||||
transferableObjects,
|
||||
) {
|
||||
const mesh = Cesium3DTilesTerrainGeometryProcessor.upsampleMesh(options);
|
||||
|
||||
const verticesBuffer = mesh.vertices.buffer;
|
||||
const indicesBuffer = mesh.indices.buffer;
|
||||
const westIndicesBuffer = mesh.westIndicesSouthToNorth.buffer;
|
||||
const southIndicesBuffer = mesh.southIndicesEastToWest.buffer;
|
||||
const eastIndicesBuffer = mesh.eastIndicesNorthToSouth.buffer;
|
||||
const northIndicesBuffer = mesh.northIndicesWestToEast.buffer;
|
||||
|
||||
transferableObjects.push(
|
||||
verticesBuffer,
|
||||
indicesBuffer,
|
||||
westIndicesBuffer,
|
||||
southIndicesBuffer,
|
||||
eastIndicesBuffer,
|
||||
northIndicesBuffer,
|
||||
);
|
||||
|
||||
/** @type {TerrainMeshProxy} */
|
||||
const result = {
|
||||
verticesBuffer: verticesBuffer,
|
||||
indicesBuffer: indicesBuffer,
|
||||
vertexCountWithoutSkirts: mesh.vertexCountWithoutSkirts,
|
||||
indexCountWithoutSkirts: mesh.indexCountWithoutSkirts,
|
||||
encoding: mesh.encoding,
|
||||
westIndicesBuffer: westIndicesBuffer,
|
||||
southIndicesBuffer: southIndicesBuffer,
|
||||
eastIndicesBuffer: eastIndicesBuffer,
|
||||
northIndicesBuffer: northIndicesBuffer,
|
||||
minimumHeight: mesh.minimumHeight,
|
||||
maximumHeight: mesh.maximumHeight,
|
||||
boundingSphere: mesh.boundingSphere3D,
|
||||
orientedBoundingBox: mesh.orientedBoundingBox,
|
||||
horizonOcclusionPoint: mesh.horizonOcclusionPoint,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export default createTaskProcessorWorker(
|
||||
upsampleVerticesFromCesium3DTilesTerrain,
|
||||
);
|
||||
Reference in New Issue
Block a user