diff --git a/CHANGES.md b/CHANGES.md index c6feeed64477..18366f3727fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,8 @@ ##### Additions :tada: - Added `Scene.pickVoxel` to pick individual cells from a `VoxelPrimitive`, and `VoxelCell` to report information about the picked cell. [#11828](https://github.com/CesiumGS/cesium/pull/11828) +- Added `Scene.defaultLogDepthbuffer` to allow changing the default behavior of the `logDepthBuffer` for newly created `Scene` instances. [#11859](https://github.com/CesiumGS/cesium/pull/11859) +- Added `SensorVolumePortionToDisplay` to assist `CzmlDataSource` in parsing CZML. [#11859](https://github.com/CesiumGS/cesium/pull/11859) - Added support for I3S Building Scene Layer. [#11678](https://github.com/CesiumGS/cesium/pull/11678) ##### Deprecated :hourglass_flowing_sand: diff --git a/packages/engine/Source/Core/IntersectionTests.js b/packages/engine/Source/Core/IntersectionTests.js index c8cbcbddebea..295b467bdb45 100644 --- a/packages/engine/Source/Core/IntersectionTests.js +++ b/packages/engine/Source/Core/IntersectionTests.js @@ -508,7 +508,10 @@ function addWithCancellationCheck(left, right, tolerance) { return difference; } -function quadraticVectorExpression(A, b, c, x, w) { +/** + * @private + */ +IntersectionTests.quadraticVectorExpression = function (A, b, c, x, w) { const xSquared = x * x; const wSquared = w * w; @@ -634,7 +637,7 @@ function quadraticVectorExpression(A, b, c, x, w) { } return solutions; -} +}; const firstAxisScratch = new Cartesian3(); const secondAxisScratch = new Cartesian3(); @@ -740,7 +743,7 @@ IntersectionTests.grazingAltitudeLocation = function (ray, ellipsoid) { const b = Matrix3.multiplyByVector(temp, position, bCart); // Solve for the solutions to the expression in standard form: - const solutions = quadraticVectorExpression( + const solutions = IntersectionTests.quadraticVectorExpression( A, Cartesian3.negate(b, firstAxisScratch), 0.0, diff --git a/packages/engine/Source/DataSources/CzmlDataSource.js b/packages/engine/Source/DataSources/CzmlDataSource.js index fed70f81705b..6bcf7006b3ee 100644 --- a/packages/engine/Source/DataSources/CzmlDataSource.js +++ b/packages/engine/Source/DataSources/CzmlDataSource.js @@ -90,6 +90,7 @@ import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; import VelocityVectorProperty from "./VelocityVectorProperty.js"; import WallGraphics from "./WallGraphics.js"; import Cesium3DTilesetGraphics from "./Cesium3DTilesetGraphics.js"; +import SensorVolumePortionToDisplay from "../Scene/SensorVolumePortionToDisplay.js"; // A marker type to distinguish CZML properties where we need to end up with a unit vector. // The data is still loaded into Cartesian3 objects but they are normalized. @@ -576,6 +577,10 @@ function unwrapInterval(type, czmlInterval, sourceUri) { return unwrapQuaternionInterval(czmlInterval); case Rotation: return defaultValue(czmlInterval.number, czmlInterval); + case SensorVolumePortionToDisplay: + return SensorVolumePortionToDisplay[ + defaultValue(czmlInterval.portionToDisplay, czmlInterval) + ]; case ShadowMode: return ShadowMode[ defaultValue( @@ -598,7 +603,7 @@ function unwrapInterval(type, czmlInterval, sourceUri) { defaultValue(czmlInterval.verticalOrigin, czmlInterval) ]; default: - throw new RuntimeError(type); + throw new RuntimeError(`Unknown CzmlDataSource interval type: ${type}`); } } @@ -5006,31 +5011,54 @@ Object.defineProperties(CzmlDataSource.prototype, { * @type {CzmlDataSource.UpdaterFunction[]} */ CzmlDataSource.updaters = [ - processBillboard, // - processBox, // - processCorridor, // - processCylinder, // - processEllipse, // - processEllipsoid, // - processLabel, // - processModel, // - processName, // - processDescription, // - processPath, // - processPoint, // - processPolygon, // - processPolyline, // - processPolylineVolume, // - processProperties, // - processRectangle, // - processPosition, // - processTileset, // - processViewFrom, // - processWall, // - processOrientation, // + processBillboard, + processBox, + processCorridor, + processCylinder, + processEllipse, + processEllipsoid, + processLabel, + processModel, + processName, + processDescription, + processPath, + processPoint, + processPolygon, + processPolyline, + processPolylineVolume, + processProperties, + processRectangle, + processPosition, + processTileset, + processViewFrom, + processWall, + processOrientation, processAvailability, ]; +/** + * Add the provided updater to the list of updaters if not already included + * @private + * @param {CzmlDataSource.UpdaterFunction} updater + */ +CzmlDataSource.registerUpdater = function (updater) { + if (!CzmlDataSource.updaters.includes(updater)) { + CzmlDataSource.updaters.push(updater); + } +}; + +/** + * Remove the provided updater from the list of updaters if already included + * @private + * @param {CzmlDataSource.UpdaterFunction} updater + */ +CzmlDataSource.unregisterUpdater = function (updater) { + if (CzmlDataSource.updaters.includes(updater)) { + const index = CzmlDataSource.updaters.indexOf(updater); + CzmlDataSource.updaters.splice(index, 1); + } +}; + /** * Processes the provided url or CZML object without clearing any existing data. * diff --git a/packages/engine/Source/DataSources/DataSourceDisplay.js b/packages/engine/Source/DataSources/DataSourceDisplay.js index 6ea22ddf41ba..eac541551705 100644 --- a/packages/engine/Source/DataSources/DataSourceDisplay.js +++ b/packages/engine/Source/DataSources/DataSourceDisplay.js @@ -120,6 +120,30 @@ function DataSourceDisplay(options) { this._ready = false; } +const ExtraVisualizers = []; +/** + * Add the provided Visualizer to the default visualizers callback if not already included + * @private + * @param {Visualizer} visualizer Visualizer class to add + */ +DataSourceDisplay.registerVisualizer = function (visualizer) { + if (!ExtraVisualizers.includes(visualizer)) { + ExtraVisualizers.push(visualizer); + } +}; + +/** + * Remove the provided Visualizer from the default visualizers callback if it's already included + * @private + * @param {Visualizer} visualizer Visualizer class to remove + */ +DataSourceDisplay.unregisterVisualizer = function (visualizer) { + if (ExtraVisualizers.includes(visualizer)) { + const index = ExtraVisualizers.indexOf(visualizer); + ExtraVisualizers.splice(index, 1); + } +}; + /** * Gets or sets the default function which creates an array of visualizers used for visualization. * By default, this function uses all standard visualizers. @@ -151,6 +175,9 @@ DataSourceDisplay.defaultVisualizersCallback = function ( dataSource._primitives, dataSource._groundPrimitives ), + ...ExtraVisualizers.map( + (VisualizerClass) => new VisualizerClass(scene, entities) + ), ]; }; diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index c7d20e8c0a3f..998108671b35 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -41,6 +41,8 @@ import WallGraphics from "./WallGraphics.js"; const cartoScratch = new Cartographic(); +const ExtraPropertyNames = []; + function createConstantPositionProperty(value) { return new ConstantPositionProperty(value); } @@ -127,7 +129,7 @@ function Entity(options) { "corridor", "cylinder", "description", - "ellipse", // + "ellipse", "ellipsoid", "label", "model", @@ -136,7 +138,7 @@ function Entity(options) { "path", "plane", "point", - "polygon", // + "polygon", "polyline", "polylineVolume", "position", @@ -144,6 +146,7 @@ function Entity(options) { "rectangle", "viewFrom", "wall", + ...ExtraPropertyNames, ]; this._billboard = undefined; @@ -494,6 +497,21 @@ Object.defineProperties(Entity.prototype, { wall: createPropertyTypeDescriptor("wall", WallGraphics), }); +/** + * Add the specified type and construct the properties for it in the Entity class + * @private + * @param {string} propertyName name of the property that controls/accesses this entity type + * @param {{ constructor: function }} Type The Graphics class to associate with this entity type + */ +Entity.registerEntityType = function (propertyName, Type) { + Object.defineProperties(Entity.prototype, { + [propertyName]: createPropertyTypeDescriptor(propertyName, Type), + }); + if (!ExtraPropertyNames.includes(propertyName)) { + ExtraPropertyNames.push(propertyName); + } +}; + /** * Given a time, returns true if this object should have data during that time. * diff --git a/packages/engine/Source/DataSources/GeometryUpdaterSet.js b/packages/engine/Source/DataSources/GeometryUpdaterSet.js new file mode 100644 index 000000000000..6dd0ba4ec90e --- /dev/null +++ b/packages/engine/Source/DataSources/GeometryUpdaterSet.js @@ -0,0 +1,114 @@ +import destroyObject from "../Core/destroyObject.js"; +import Event from "../Core/Event.js"; +import EventHelper from "../Core/EventHelper.js"; +import BoxGeometryUpdater from "./BoxGeometryUpdater.js"; +import CorridorGeometryUpdater from "./CorridorGeometryUpdater.js"; +import CylinderGeometryUpdater from "./CylinderGeometryUpdater.js"; +import EllipseGeometryUpdater from "./EllipseGeometryUpdater.js"; +import EllipsoidGeometryUpdater from "./EllipsoidGeometryUpdater.js"; +import PlaneGeometryUpdater from "./PlaneGeometryUpdater.js"; +import PolygonGeometryUpdater from "./PolygonGeometryUpdater.js"; +import PolylineVolumeGeometryUpdater from "./PolylineVolumeGeometryUpdater.js"; +import RectangleGeometryUpdater from "./RectangleGeometryUpdater.js"; +import WallGeometryUpdater from "./WallGeometryUpdater.js"; + +/** @type {GeometryUpdater[]} */ +const geometryUpdaters = [ + BoxGeometryUpdater, + CylinderGeometryUpdater, + CorridorGeometryUpdater, + EllipseGeometryUpdater, + EllipsoidGeometryUpdater, + PlaneGeometryUpdater, + PolygonGeometryUpdater, + PolylineVolumeGeometryUpdater, + RectangleGeometryUpdater, + WallGeometryUpdater, +]; + +/** + * Manages a set of "updater" classes for the {@link GeometryVisualizer} for each entity + * + * @private + * @param {Entity} entity + * @param {Scene} scene + */ +function GeometryUpdaterSet(entity, scene) { + this.entity = entity; + this.scene = scene; + const updaters = new Array(geometryUpdaters.length); + const geometryChanged = new Event(); + const eventHelper = new EventHelper(); + for (let i = 0; i < updaters.length; i++) { + const updater = new geometryUpdaters[i](entity, scene); + eventHelper.add(updater.geometryChanged, (geometry) => { + geometryChanged.raiseEvent(geometry); + }); + updaters[i] = updater; + } + this.updaters = updaters; + this.geometryChanged = geometryChanged; + this.eventHelper = eventHelper; + + this._removeEntitySubscription = entity.definitionChanged.addEventListener( + GeometryUpdaterSet.prototype._onEntityPropertyChanged, + this + ); +} + +GeometryUpdaterSet.prototype._onEntityPropertyChanged = function ( + entity, + propertyName, + newValue, + oldValue +) { + const updaters = this.updaters; + for (let i = 0; i < updaters.length; i++) { + updaters[i]._onEntityPropertyChanged( + entity, + propertyName, + newValue, + oldValue + ); + } +}; + +GeometryUpdaterSet.prototype.forEach = function (callback) { + const updaters = this.updaters; + for (let i = 0; i < updaters.length; i++) { + callback(updaters[i]); + } +}; + +GeometryUpdaterSet.prototype.destroy = function () { + this.eventHelper.removeAll(); + const updaters = this.updaters; + for (let i = 0; i < updaters.length; i++) { + updaters[i].destroy(); + } + this._removeEntitySubscription(); + destroyObject(this); +}; + +/** + * Add the provided updater to the default list of updaters if not already included + * @param {GeometryUpdater} updater + */ +GeometryUpdaterSet.registerUpdater = function (updater) { + if (!geometryUpdaters.includes(updater)) { + geometryUpdaters.push(updater); + } +}; + +/** + * Remove the provided updater from the default list of updaters if included + * @param {GeometryUpdater} updater + */ +GeometryUpdaterSet.unregisterUpdater = function (updater) { + if (geometryUpdaters.includes(updater)) { + const index = geometryUpdaters.indexOf(updater); + geometryUpdaters.splice(index, 1); + } +}; + +export default GeometryUpdaterSet; diff --git a/packages/engine/Source/DataSources/GeometryVisualizer.js b/packages/engine/Source/DataSources/GeometryVisualizer.js index 710478944371..e2d1ed306a0f 100644 --- a/packages/engine/Source/DataSources/GeometryVisualizer.js +++ b/packages/engine/Source/DataSources/GeometryVisualizer.js @@ -4,105 +4,23 @@ import Check from "../Core/Check.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; -import Event from "../Core/Event.js"; -import EventHelper from "../Core/EventHelper.js"; import ClassificationType from "../Scene/ClassificationType.js"; import MaterialAppearance from "../Scene/MaterialAppearance.js"; import PerInstanceColorAppearance from "../Scene/PerInstanceColorAppearance.js"; import ShadowMode from "../Scene/ShadowMode.js"; import BoundingSphereState from "./BoundingSphereState.js"; -import BoxGeometryUpdater from "./BoxGeometryUpdater.js"; import ColorMaterialProperty from "./ColorMaterialProperty.js"; -import CorridorGeometryUpdater from "./CorridorGeometryUpdater.js"; -import CylinderGeometryUpdater from "./CylinderGeometryUpdater.js"; import DynamicGeometryBatch from "./DynamicGeometryBatch.js"; -import EllipseGeometryUpdater from "./EllipseGeometryUpdater.js"; -import EllipsoidGeometryUpdater from "./EllipsoidGeometryUpdater.js"; import Entity from "./Entity.js"; -import PlaneGeometryUpdater from "./PlaneGeometryUpdater.js"; -import PolygonGeometryUpdater from "./PolygonGeometryUpdater.js"; -import PolylineVolumeGeometryUpdater from "./PolylineVolumeGeometryUpdater.js"; -import RectangleGeometryUpdater from "./RectangleGeometryUpdater.js"; +import GeometryUpdaterSet from "./GeometryUpdaterSet.js"; import StaticGeometryColorBatch from "./StaticGeometryColorBatch.js"; import StaticGeometryPerMaterialBatch from "./StaticGeometryPerMaterialBatch.js"; import StaticGroundGeometryColorBatch from "./StaticGroundGeometryColorBatch.js"; import StaticGroundGeometryPerMaterialBatch from "./StaticGroundGeometryPerMaterialBatch.js"; import StaticOutlineGeometryBatch from "./StaticOutlineGeometryBatch.js"; -import WallGeometryUpdater from "./WallGeometryUpdater.js"; const emptyArray = []; -const geometryUpdaters = [ - BoxGeometryUpdater, - CylinderGeometryUpdater, - CorridorGeometryUpdater, - EllipseGeometryUpdater, - EllipsoidGeometryUpdater, - PlaneGeometryUpdater, - PolygonGeometryUpdater, - PolylineVolumeGeometryUpdater, - RectangleGeometryUpdater, - WallGeometryUpdater, -]; - -function GeometryUpdaterSet(entity, scene) { - this.entity = entity; - this.scene = scene; - const updaters = new Array(geometryUpdaters.length); - const geometryChanged = new Event(); - function raiseEvent(geometry) { - geometryChanged.raiseEvent(geometry); - } - const eventHelper = new EventHelper(); - for (let i = 0; i < updaters.length; i++) { - const updater = new geometryUpdaters[i](entity, scene); - eventHelper.add(updater.geometryChanged, raiseEvent); - updaters[i] = updater; - } - this.updaters = updaters; - this.geometryChanged = geometryChanged; - this.eventHelper = eventHelper; - - this._removeEntitySubscription = entity.definitionChanged.addEventListener( - GeometryUpdaterSet.prototype._onEntityPropertyChanged, - this - ); -} - -GeometryUpdaterSet.prototype._onEntityPropertyChanged = function ( - entity, - propertyName, - newValue, - oldValue -) { - const updaters = this.updaters; - for (let i = 0; i < updaters.length; i++) { - updaters[i]._onEntityPropertyChanged( - entity, - propertyName, - newValue, - oldValue - ); - } -}; - -GeometryUpdaterSet.prototype.forEach = function (callback) { - const updaters = this.updaters; - for (let i = 0; i < updaters.length; i++) { - callback(updaters[i]); - } -}; - -GeometryUpdaterSet.prototype.destroy = function () { - this.eventHelper.removeAll(); - const updaters = this.updaters; - for (let i = 0; i < updaters.length; i++) { - updaters[i].destroy(); - } - this._removeEntitySubscription(); - destroyObject(this); -}; - /** * A general purpose visualizer for geometry represented by {@link Primitive} instances. * @alias GeometryVisualizer @@ -293,6 +211,24 @@ function GeometryVisualizer( ); } +/** + * Add the provided updater to the default list of updaters if not already included + * @private + * @param {GeometryUpdater} updater + */ +GeometryVisualizer.registerUpdater = function (updater) { + GeometryUpdaterSet.registerUpdater(updater); +}; + +/** + * Remove the provided updater from the default list of updaters if included + * @private + * @param {GeometryUpdater} updater + */ +GeometryVisualizer.unregisterUpdater = function (updater) { + GeometryUpdaterSet.unregisterUpdater(updater); +}; + /** * Updates all of the primitives created by this visualizer to match their * Entity counterpart at the given time. diff --git a/packages/engine/Source/Renderer/ContextLimits.js b/packages/engine/Source/Renderer/ContextLimits.js index 6bdb837cf60e..b6c5e5731f13 100644 --- a/packages/engine/Source/Renderer/ContextLimits.js +++ b/packages/engine/Source/Renderer/ContextLimits.js @@ -1,4 +1,6 @@ /** + * These are set in the constructor for {@link Context} + * * @private */ const ContextLimits = { diff --git a/packages/engine/Source/Scene/Primitive.js b/packages/engine/Source/Scene/Primitive.js index 12870094fdcb..970a719a3d80 100644 --- a/packages/engine/Source/Scene/Primitive.js +++ b/packages/engine/Source/Scene/Primitive.js @@ -1174,15 +1174,19 @@ function loadAsynchronous(primitive, frameState) { instanceIds.push(instances[i].id); //>>includeStart('debug', pragmas.debug); - if (!defined(geometry._workerName)) { + if ( + (defined(geometry._workerName) && defined(geometry._workerPath)) || + (!defined(geometry._workerName) && !defined(geometry._workerPath)) + ) { throw new DeveloperError( - "_workerName must be defined for asynchronous geometry." + "Must define either _workerName or _workerPath for asynchronous geometry." ); } //>>includeEnd('debug'); subTasks.push({ moduleName: geometry._workerName, + modulePath: geometry._workerPath, geometry: geometry, }); } diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 1d49771e5d3d..36a8193ce61b 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -172,7 +172,7 @@ function Scene(options) { this._globeHeightDirty = undefined; this._cameraUnderground = false; - this._logDepthBuffer = context.fragmentDepth; + this._logDepthBuffer = Scene.defaultLogDepthBuffer && context.fragmentDepth; this._logDepthBufferDirty = true; this._tweens = new TweenCollection(); @@ -734,6 +734,12 @@ function Scene(options) { this.initializeFrame(); } +/** + * Use this to set the default value for {@link Scene#logarithmicDepthBuffer} in newly constructed Scenes + * This property relies on fragmentDepth being supported. + */ +Scene.defaultLogDepthBuffer = true; + function updateGlobeListeners(scene, globe) { for (let i = 0; i < scene._removeGlobeCallbacks.length; ++i) { scene._removeGlobeCallbacks[i](); diff --git a/packages/engine/Source/Scene/SensorVolumePortionToDisplay.js b/packages/engine/Source/Scene/SensorVolumePortionToDisplay.js new file mode 100644 index 000000000000..5fafdcb63bdc --- /dev/null +++ b/packages/engine/Source/Scene/SensorVolumePortionToDisplay.js @@ -0,0 +1,69 @@ +import { DeveloperError } from "@cesium/engine"; + +/** + * Constants used to indicated what part of the sensor volume to display. + * + * @enum {Number} + */ +const SensorVolumePortionToDisplay = { + /** + * 0x0000. Display the complete sensor volume. + * + * @type {Number} + * @constant + */ + COMPLETE: 0x0000, + /** + * 0x0001. Display the portion of the sensor volume that lies below the true horizon of the ellipsoid. + * + * @type {Number} + * @constant + */ + BELOW_ELLIPSOID_HORIZON: 0x0001, + /** + * 0x0002. Display the portion of the sensor volume that lies above the true horizon of the ellipsoid. + * + * @type {Number} + * @constant + */ + ABOVE_ELLIPSOID_HORIZON: 0x0002, +}; + +/** + * Validates that the provided value is a valid {@link SensorVolumePortionToDisplay} enumeration value. + * + * @param {SensorVolumePortionToDisplay} portionToDisplay The value to validate. + * + * @returns {Boolean} true if the provided value is a valid enumeration value; otherwise, false. + */ +SensorVolumePortionToDisplay.validate = function (portionToDisplay) { + return ( + portionToDisplay === SensorVolumePortionToDisplay.COMPLETE || + portionToDisplay === SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON || + portionToDisplay === SensorVolumePortionToDisplay.ABOVE_ELLIPSOID_HORIZON + ); +}; + +/** + * Converts the provided value to its corresponding enumeration string. + * + * @param {SensorVolumePortionToDisplay} portionToDisplay The value to be converted to its corresponding enumeration string. + * + * @returns {String} The enumeration string corresponding to the value. + */ +SensorVolumePortionToDisplay.toString = function (portionToDisplay) { + switch (portionToDisplay) { + case SensorVolumePortionToDisplay.COMPLETE: + return "COMPLETE"; + case SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON: + return "BELOW_ELLIPSOID_HORIZON"; + case SensorVolumePortionToDisplay.ABOVE_ELLIPSOID_HORIZON: + return "ABOVE_ELLIPSOID_HORIZON"; + default: + throw new DeveloperError( + "SensorVolumePortionToDisplay value is not valid and cannot be converted to a String." + ); + } +}; + +export default SensorVolumePortionToDisplay; diff --git a/packages/engine/Source/Workers/createGeometry.js b/packages/engine/Source/Workers/createGeometry.js index 063453e50e56..e6b63094a2bd 100644 --- a/packages/engine/Source/Workers/createGeometry.js +++ b/packages/engine/Source/Workers/createGeometry.js @@ -1,23 +1,46 @@ +import DeveloperError from "../Core/DeveloperError.js"; +import defaultValue from "../Core/defaultValue.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) { - let module = moduleCache[moduleName]; - if (!defined(module)) { +async function getModule(moduleName, modulePath) { + let module = defaultValue(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. - /* global require */ - moduleCache[module] = module = require(`Workers/${moduleName}`); + module = require(modulePath); } else { // Use ESM-style dynamic import - const result = await import(`./${moduleName}.js`); + const result = await import(modulePath); module = result.default; - moduleCache[module] = module; } + + 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; } @@ -30,11 +53,17 @@ async function createGeometry(parameters, transferableObjects) { 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)) { - resultsOrPromises[i] = getModule(moduleName).then((createFunction) => - createFunction(geometry, task.offset) - ); + if (defined(moduleName) || defined(modulePath)) { + resultsOrPromises[i] = getModule( + moduleName, + modulePath + ).then((createFunction) => createFunction(geometry, task.offset)); } else { // Already created geometry resultsOrPromises[i] = geometry; diff --git a/packages/engine/Specs/DataSources/CzmlDataSourceSpec.js b/packages/engine/Specs/DataSources/CzmlDataSourceSpec.js index cf46759076af..7db764e5982d 100644 --- a/packages/engine/Specs/DataSources/CzmlDataSourceSpec.js +++ b/packages/engine/Specs/DataSources/CzmlDataSourceSpec.js @@ -10123,4 +10123,24 @@ describe("DataSources/CzmlDataSource", function () { expect(e.properties.custom_wsenDegrees.getValue(documentStopDate)).toEqual(Rectangle.fromDegrees(37, 16, 25, 23)); }); }); + + it("registers custom updaters", () => { + function processFakeEntity() {} + + expect(CzmlDataSource.updaters.length) + .withContext("length before register") + .toEqual(23); + + CzmlDataSource.registerUpdater(processFakeEntity); + + expect(CzmlDataSource.updaters.length) + .withContext("length after register") + .toEqual(24); + expect(CzmlDataSource.updaters[23]).toEqual(processFakeEntity); + + CzmlDataSource.unregisterUpdater(processFakeEntity); + expect(CzmlDataSource.updaters.length) + .withContext("length after unregister") + .toEqual(23); + }); }); diff --git a/packages/engine/Specs/DataSources/DataSourceDisplaySpec.js b/packages/engine/Specs/DataSources/DataSourceDisplaySpec.js index a37466c6be9f..f0209dfe10be 100644 --- a/packages/engine/Specs/DataSources/DataSourceDisplaySpec.js +++ b/packages/engine/Specs/DataSources/DataSourceDisplaySpec.js @@ -9,6 +9,15 @@ import { Entity, GroundPolylinePrimitive, GroundPrimitive, + defined, + BillboardVisualizer, + GeometryVisualizer, + LabelVisualizer, + ModelVisualizer, + Cesium3DTilesetVisualizer, + PointVisualizer, + PathVisualizer, + PolylineVisualizer, } from "../../index.js"; import createScene from "../../../../Specs/createScene.js"; @@ -39,7 +48,7 @@ describe( }); afterEach(function () { - if (!display.isDestroyed()) { + if (defined(display) && !display.isDestroyed()) { display.destroy(); } dataSourceCollection.removeAll(); @@ -597,6 +606,57 @@ describe( true ); }); + + it("has expected default visualizers", () => { + const dataSource = new MockDataSource(); + const entityCluster = dataSource.clustering; + const callback = DataSourceDisplay.defaultVisualizersCallback( + scene, + entityCluster, + dataSource + ); + expect(callback.length).toEqual(8); + expect(callback[0]).toBeInstanceOf(BillboardVisualizer); + expect(callback[1]).toBeInstanceOf(GeometryVisualizer); + expect(callback[2]).toBeInstanceOf(LabelVisualizer); + expect(callback[3]).toBeInstanceOf(ModelVisualizer); + expect(callback[4]).toBeInstanceOf(Cesium3DTilesetVisualizer); + expect(callback[5]).toBeInstanceOf(PointVisualizer); + expect(callback[6]).toBeInstanceOf(PathVisualizer); + expect(callback[7]).toBeInstanceOf(PolylineVisualizer); + }); + + it("registers extra visualizers", () => { + function FakeVisualizer() {} + const dataSource = new MockDataSource(); + const entityCluster = dataSource.clustering; + + const callback = DataSourceDisplay.defaultVisualizersCallback( + scene, + entityCluster, + dataSource + ); + expect(callback.length).withContext("length before register").toEqual(8); + + DataSourceDisplay.registerVisualizer(FakeVisualizer); + const callback2 = DataSourceDisplay.defaultVisualizersCallback( + scene, + entityCluster, + dataSource + ); + expect(callback2.length).withContext("length after register").toEqual(9); + expect(callback2[8]).toBeInstanceOf(FakeVisualizer); + + DataSourceDisplay.unregisterVisualizer(FakeVisualizer); + const callback3 = DataSourceDisplay.defaultVisualizersCallback( + scene, + entityCluster, + dataSource + ); + expect(callback3.length) + .withContext("length after unregister") + .toEqual(8); + }); }, "WebGL" ); diff --git a/packages/engine/Specs/DataSources/GeometryUpdaterSetSpec.js b/packages/engine/Specs/DataSources/GeometryUpdaterSetSpec.js new file mode 100644 index 000000000000..79da7d2f5a34 --- /dev/null +++ b/packages/engine/Specs/DataSources/GeometryUpdaterSetSpec.js @@ -0,0 +1,64 @@ +import createScene from "../../../../Specs/createScene.js"; +import { + BoxGeometryUpdater, + CorridorGeometryUpdater, + CylinderGeometryUpdater, + EllipseGeometryUpdater, + EllipsoidGeometryUpdater, + Entity, + Event, + GeometryUpdaterSet, + PlaneGeometryUpdater, + PolygonGeometryUpdater, + PolylineVolumeGeometryUpdater, + RectangleGeometryUpdater, + WallGeometryUpdater, +} from "../../index.js"; + +describe("GeometryUpdaterSet", () => { + let scene; + beforeAll(function () { + scene = createScene(); + }); + + afterAll(function () { + scene.destroyForSpecs(); + }); + + it("has expected defaults", () => { + const updaterSet = new GeometryUpdaterSet(new Entity(), scene); + + expect(updaterSet.updaters.length).toEqual(10); + expect(updaterSet.updaters[0]).toBeInstanceOf(BoxGeometryUpdater); + expect(updaterSet.updaters[1]).toBeInstanceOf(CylinderGeometryUpdater); + expect(updaterSet.updaters[2]).toBeInstanceOf(CorridorGeometryUpdater); + expect(updaterSet.updaters[3]).toBeInstanceOf(EllipseGeometryUpdater); + expect(updaterSet.updaters[4]).toBeInstanceOf(EllipsoidGeometryUpdater); + expect(updaterSet.updaters[5]).toBeInstanceOf(PlaneGeometryUpdater); + expect(updaterSet.updaters[6]).toBeInstanceOf(PolygonGeometryUpdater); + expect(updaterSet.updaters[7]).toBeInstanceOf( + PolylineVolumeGeometryUpdater + ); + expect(updaterSet.updaters[8]).toBeInstanceOf(RectangleGeometryUpdater); + expect(updaterSet.updaters[9]).toBeInstanceOf(WallGeometryUpdater); + }); + + it("registers new updater", () => { + function FakeUpdater() {} + FakeUpdater.prototype.geometryChanged = new Event(); + + GeometryUpdaterSet.registerUpdater(FakeUpdater); + + const updaterSet = new GeometryUpdaterSet(new Entity(), scene); + + expect(updaterSet.updaters.length).toEqual(11); + expect(updaterSet.updaters[10]).toBeInstanceOf(FakeUpdater); + + GeometryUpdaterSet.unregisterUpdater(FakeUpdater); + const updaterSet2 = new GeometryUpdaterSet(new Entity(), scene); + + expect(updaterSet2.updaters.length) + .withContext("length after unregister") + .toEqual(10); + }); +}); diff --git a/packages/engine/Specs/DataSources/GeometryVisualizerSpec.js b/packages/engine/Specs/DataSources/GeometryVisualizerSpec.js index 329334097cfa..8b8aae5048a2 100644 --- a/packages/engine/Specs/DataSources/GeometryVisualizerSpec.js +++ b/packages/engine/Specs/DataSources/GeometryVisualizerSpec.js @@ -21,6 +21,8 @@ import { MaterialAppearance, PerInstanceColorAppearance, ShadowMode, + Event, + GeometryUpdaterSet, } from "../../index.js"; import createDynamicProperty from "../../../../Specs/createDynamicProperty.js"; @@ -1014,6 +1016,24 @@ describe( visualizer.destroy(); }); }); + + it("registers new updater", () => { + function FakeUpdater() {} + FakeUpdater.prototype.geometryChanged = new Event(); + const spy = spyOn(GeometryUpdaterSet, "registerUpdater"); + + GeometryUpdaterSet.registerUpdater(FakeUpdater); + expect(spy).toHaveBeenCalledOnceWith(FakeUpdater); + }); + + it("unregisters custom updaters", () => { + function FakeUpdater() {} + FakeUpdater.prototype.geometryChanged = new Event(); + const spy = spyOn(GeometryUpdaterSet, "unregisterUpdater"); + + GeometryUpdaterSet.unregisterUpdater(FakeUpdater); + expect(spy).toHaveBeenCalledOnceWith(FakeUpdater); + }); }, "WebGL" ); diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js index 1da9e3a50f34..7e9bacb2efed 100644 --- a/packages/engine/Specs/Scene/Model/ModelSpec.js +++ b/packages/engine/Specs/Scene/Model/ModelSpec.js @@ -1282,6 +1282,7 @@ describe( // Renders without style. let original; + verifyRender(model, true); expect(renderOptions).toRenderAndCall(function (rgba) { original = rgba; }); @@ -1292,6 +1293,7 @@ describe( }); model.style = style; + verifyRender(model, true); expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba[0]).toEqual(original[0]); expect(rgba[1]).toBeLessThan(original[1]); @@ -1305,6 +1307,7 @@ describe( }); model.style = style; + verifyRender(model, true); expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba[0]).toBeLessThan(original[0]); expect(rgba[1]).toBeLessThan(original[1]); diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index ce22169ec504..082c3e1dce7e 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -127,79 +127,93 @@ describe( }); } - it("constructor has expected defaults", function () { - expect(scene.canvas).toBeInstanceOf(HTMLCanvasElement); - expect(scene.primitives).toBeInstanceOf(PrimitiveCollection); - expect(scene.camera).toBeInstanceOf(Camera); - expect(scene.screenSpaceCameraController).toBeInstanceOf( - ScreenSpaceCameraController - ); - expect(scene.mapProjection).toBeInstanceOf(GeographicProjection); - expect(scene.frameState).toBeInstanceOf(FrameState); - expect(scene.tweens).toBeInstanceOf(TweenCollection); - - const contextAttributes = scene.context._gl.getContextAttributes(); - // Do not check depth and antialias since they are requests not requirements - expect(contextAttributes.alpha).toEqual(false); - expect(contextAttributes.stencil).toEqual(true); - expect(contextAttributes.premultipliedAlpha).toEqual(true); - expect(contextAttributes.preserveDrawingBuffer).toEqual(false); - expect(scene._depthPlane._ellipsoidOffset).toEqual(0); - }); - - it("constructor sets options", function () { - const webglOptions = { - alpha: true, - depth: false, - stencil: true, - antialias: false, - premultipliedAlpha: false, - preserveDrawingBuffer: true, - }; - const mapProjection = new WebMercatorProjection(); + describe("constructor", () => { + it("has expected defaults", function () { + expect(scene.canvas).toBeInstanceOf(HTMLCanvasElement); + expect(scene.primitives).toBeInstanceOf(PrimitiveCollection); + expect(scene.camera).toBeInstanceOf(Camera); + expect(scene.screenSpaceCameraController).toBeInstanceOf( + ScreenSpaceCameraController + ); + expect(scene.mapProjection).toBeInstanceOf(GeographicProjection); + expect(scene.frameState).toBeInstanceOf(FrameState); + expect(scene.tweens).toBeInstanceOf(TweenCollection); - const s = createScene({ - contextOptions: { - webgl: webglOptions, - }, - mapProjection: mapProjection, - depthPlaneEllipsoidOffset: Number.POSITIVE_INFINITY, + const contextAttributes = scene.context._gl.getContextAttributes(); + // Do not check depth and antialias since they are requests not requirements + expect(contextAttributes.alpha).toEqual(false); + expect(contextAttributes.stencil).toEqual(true); + expect(contextAttributes.premultipliedAlpha).toEqual(true); + expect(contextAttributes.preserveDrawingBuffer).toEqual(false); + expect(scene._depthPlane._ellipsoidOffset).toEqual(0); }); - const contextAttributes = s.context._gl.getContextAttributes(); - expect(contextAttributes.alpha).toEqual(webglOptions.alpha); - expect(contextAttributes.depth).toEqual(webglOptions.depth); - expect(contextAttributes.stencil).toEqual(webglOptions.stencil); - expect(contextAttributes.antialias).toEqual(webglOptions.antialias); - expect(contextAttributes.premultipliedAlpha).toEqual( - webglOptions.premultipliedAlpha - ); - expect(contextAttributes.preserveDrawingBuffer).toEqual( - webglOptions.preserveDrawingBuffer - ); - expect(s.mapProjection).toEqual(mapProjection); - expect(s._depthPlane._ellipsoidOffset).toEqual(Number.POSITIVE_INFINITY); + it("respects default log depth buffer override", () => { + const previous = Scene.defaultLogDepthBuffer; - s.destroyForSpecs(); - }); + Scene.defaultLogDepthBuffer = false; + const newScene = createScene(); + expect(newScene._logDepthBuffer).toEqual(false); - it("constructor throws without options", function () { - expect(function () { - return new Scene(); - }).toThrowDeveloperError(); - }); + Scene.defaultLogDepthBuffer = previous; + }); - it("constructor throws without options.canvas", function () { - expect(function () { - return new Scene({}); - }).toThrowDeveloperError(); - }); + it("sets options", function () { + const webglOptions = { + alpha: true, + depth: false, + stencil: true, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: true, + }; + const mapProjection = new WebMercatorProjection(); + + const s = createScene({ + contextOptions: { + webgl: webglOptions, + }, + mapProjection: mapProjection, + depthPlaneEllipsoidOffset: Number.POSITIVE_INFINITY, + }); - it("draws background color", function () { - expect(scene).toRender([0, 0, 0, 255]); + const contextAttributes = s.context._gl.getContextAttributes(); + expect(contextAttributes.alpha).toEqual(webglOptions.alpha); + expect(contextAttributes.depth).toEqual(webglOptions.depth); + expect(contextAttributes.stencil).toEqual(webglOptions.stencil); + expect(contextAttributes.antialias).toEqual(webglOptions.antialias); + expect(contextAttributes.premultipliedAlpha).toEqual( + webglOptions.premultipliedAlpha + ); + expect(contextAttributes.preserveDrawingBuffer).toEqual( + webglOptions.preserveDrawingBuffer + ); + expect(s.mapProjection).toEqual(mapProjection); + expect(s._depthPlane._ellipsoidOffset).toEqual( + Number.POSITIVE_INFINITY + ); - scene.backgroundColor = Color.BLUE; - expect(scene).toRender([0, 0, 255, 255]); + s.destroyForSpecs(); + }); + + it("throws without options", function () { + expect(function () { + return new Scene(); + }).toThrowDeveloperError(); + }); + + it("throws without options.canvas", function () { + expect(function () { + return new Scene({}); + }).toThrowDeveloperError(); + }); + + it("draws background color", function () { + expect(scene).toRender([0, 0, 0, 255]); + + scene.backgroundColor = Color.BLUE; + expect(scene).toRender([0, 0, 255, 255]); + }); }); it("calls afterRender functions", function () {