diff --git a/packages/engine/Source/Core/EllipseGeometry.js b/packages/engine/Source/Core/EllipseGeometry.js index c141862112f3..774d204393a9 100644 --- a/packages/engine/Source/Core/EllipseGeometry.js +++ b/packages/engine/Source/Core/EllipseGeometry.js @@ -75,10 +75,13 @@ function computeTopBottomAttributes(positions, options, extrude) { let bitangent = scratchBitangent; const projection = new GeographicProjection(ellipsoid); - const projectedCenter = projection.project( - ellipsoid.cartesianToCartographic(center, scratchCartographic), - projectedCenterScratch, + const cartographic = ellipsoid.cartesianToCartographic( + center, + scratchCartographic, ); + const projectedCenter = defined(cartographic) + ? projection.project(cartographic, projectedCenterScratch) + : Cartesian3.clone(Cartesian3.ZERO, projectedCenterScratch); const geodeticNormal = ellipsoid.scaleToGeodeticSurface( center, @@ -132,10 +135,13 @@ function computeTopBottomAttributes(positions, options, extrude) { position, scratchCartesian2, ); - const projectedPoint = projection.project( - ellipsoid.cartesianToCartographic(rotatedPoint, scratchCartographic), - scratchCartesian3, + const cartographic = ellipsoid.cartesianToCartographic( + rotatedPoint, + scratchCartographic, ); + const projectedPoint = defined(cartographic) + ? projection.project(cartographic, scratchCartesian3) + : Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3); Cartesian3.subtract(projectedPoint, projectedCenter, projectedPoint); texCoordScratch.x = @@ -482,10 +488,13 @@ function computeWallAttributes(positions, options) { let bitangent = scratchBitangent; const projection = new GeographicProjection(ellipsoid); - const projectedCenter = projection.project( - ellipsoid.cartesianToCartographic(center, scratchCartographic), - projectedCenterScratch, + const cartographic = ellipsoid.cartesianToCartographic( + center, + scratchCartographic, ); + const projectedCenter = defined(cartographic) + ? projection.project(cartographic, projectedCenterScratch) + : Cartesian3.clone(Cartesian3.ZERO, projectedCenterScratch); const geodeticNormal = ellipsoid.scaleToGeodeticSurface( center, @@ -524,10 +533,13 @@ function computeWallAttributes(positions, options) { position, scratchCartesian2, ); - const projectedPoint = projection.project( - ellipsoid.cartesianToCartographic(rotatedPoint, scratchCartographic), - scratchCartesian3, + const cartographic = ellipsoid.cartesianToCartographic( + rotatedPoint, + scratchCartographic, ); + const projectedPoint = defined(cartographic) + ? projection.project(cartographic, scratchCartesian3) + : Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3); Cartesian3.subtract(projectedPoint, projectedCenter, projectedPoint); texCoordScratch.x = diff --git a/packages/engine/Source/Core/GeographicProjection.js b/packages/engine/Source/Core/GeographicProjection.js index 2246276c1b2d..1e86f1a16b65 100644 --- a/packages/engine/Source/Core/GeographicProjection.js +++ b/packages/engine/Source/Core/GeographicProjection.js @@ -3,6 +3,7 @@ import Cartographic from "./Cartographic.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import Ellipsoid from "./Ellipsoid.js"; +import Check from "./Check.js"; /** * A simple map projection where longitude and latitude are linearly mapped to X and Y by multiplying @@ -52,6 +53,10 @@ Object.defineProperties(GeographicProjection.prototype, { * created and returned. */ GeographicProjection.prototype.project = function (cartographic, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined("cartographic", cartographic); + //>>includeEnd('debug'); + // Actually this is the special case of equidistant cylindrical called the plate carree const semimajorAxis = this._semimajorAxis; const x = cartographic.longitude * semimajorAxis; diff --git a/packages/engine/Source/Core/GroundPolylineGeometry.js b/packages/engine/Source/Core/GroundPolylineGeometry.js index c2ffc7a1ae91..dcb6375fc843 100644 --- a/packages/engine/Source/Core/GroundPolylineGeometry.js +++ b/packages/engine/Source/Core/GroundPolylineGeometry.js @@ -872,11 +872,11 @@ function projectNormal( ); } - normalEndpointCartographic.height = 0.0; - const normalEndpointProjected = projection.project( - normalEndpointCartographic, - result, - ); + const normalEndpointProjected = defined(normalEndpointCartographic) + ? projection.project(normalEndpointCartographic, result) + : Cartesian3.clone(Cartesian3.ZERO, result); + normalEndpointProjected.height = 0.0; + result = Cartesian3.subtract( normalEndpointProjected, projectedPosition, diff --git a/packages/engine/Source/Core/Transforms.js b/packages/engine/Source/Core/Transforms.js index 282726bec75b..c1cc18216f4d 100644 --- a/packages/engine/Source/Core/Transforms.js +++ b/packages/engine/Source/Core/Transforms.js @@ -1189,10 +1189,10 @@ Transforms.basisTo2D = function (projection, matrix, result) { scratchCartographic, ); - projectedPosition = projection.project( - cartographic, - scratchCartesian3Projection, - ); + projectedPosition = defined(cartographic) + ? projection.project(cartographic, scratchCartesian3Projection) + : Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3Projection); + Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, @@ -1245,10 +1245,10 @@ Transforms.ellipsoidTo2DModelMatrix = function (projection, center, result) { center, scratchCartographic, ); - const projectedPosition = projection.project( - cartographic, - scratchCartesian3Projection, - ); + const projectedPosition = defined(cartographic) + ? projection.project(cartographic, scratchCartesian3Projection) + : Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3Projection); + Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, diff --git a/packages/engine/Source/Core/WebMercatorProjection.js b/packages/engine/Source/Core/WebMercatorProjection.js index 866bdf07e3ca..1900de918ccf 100644 --- a/packages/engine/Source/Core/WebMercatorProjection.js +++ b/packages/engine/Source/Core/WebMercatorProjection.js @@ -4,6 +4,7 @@ import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import Ellipsoid from "./Ellipsoid.js"; import CesiumMath from "./Math.js"; +import Check from "./Check.js"; /** * The map projection used by Google Maps, Bing Maps, and most of ArcGIS Online, EPSG:3857. This @@ -98,6 +99,10 @@ WebMercatorProjection.MaximumLatitude = * @returns {Cartesian3} The equivalent web mercator X, Y, Z coordinates, in meters. */ WebMercatorProjection.prototype.project = function (cartographic, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined("cartographic", cartographic); + //>>includeEnd('debug'); + const semimajorAxis = this._semimajorAxis; const x = cartographic.longitude * semimajorAxis; const y = diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js index 6a72cc180c99..0f47e419ad4f 100644 --- a/packages/engine/Source/Renderer/UniformState.js +++ b/packages/engine/Source/Renderer/UniformState.js @@ -1344,7 +1344,10 @@ function setSunAndMoonDirections(uniformState, frameState) { uniformState._sunPositionWC, sunCartographicScratch, ); - projection.project(sunCartographic, uniformState._sunPositionColumbusView); + + uniformState._sunPositionColumbusView = defined(sunCartographic) + ? projection.project(sunCartographic, uniformState._sunPositionColumbusView) + : Cartesian3.clone(Cartesian3.ZERO, uniformState._sunPositionColumbusView); } /** diff --git a/packages/engine/Source/Scene/Camera.js b/packages/engine/Source/Scene/Camera.js index 0cd1235f7c69..c70ecde615d4 100644 --- a/packages/engine/Source/Scene/Camera.js +++ b/packages/engine/Source/Scene/Camera.js @@ -562,10 +562,10 @@ function convertTransformFor2D(camera) { scratchCartographic, ); - const projectedPosition = projection.project( - cartographic, - scratchCartesian3Projection, - ); + let projectedPosition = defined(cartographic) + ? projection.project(cartographic, scratchCartesian3Projection) + : Cartesian3.clone(Cartesian3.ZERO, scratchCartesian3Projection); + const newOrigin = scratchCartesian4NewOrigin; newOrigin.x = projectedPosition.z; newOrigin.y = projectedPosition.x; @@ -584,7 +584,10 @@ function convertTransformFor2D(camera) { ); ellipsoid.cartesianToCartographic(xAxis, cartographic); - projection.project(cartographic, projectedPosition); + projectedPosition = defined(cartographic) + ? projection.project(cartographic, projectedPosition) + : Cartesian3.clone(Cartesian3.ZERO, projectedPosition); + const newXAxis = scratchCartesian4NewXAxis; newXAxis.x = projectedPosition.z; newXAxis.y = projectedPosition.x; @@ -605,7 +608,10 @@ function convertTransformFor2D(camera) { ); ellipsoid.cartesianToCartographic(yAxis, cartographic); - projection.project(cartographic, projectedPosition); + projectedPosition = defined(cartographic) + ? projection.project(cartographic, projectedPosition) + : Cartesian3.clone(Cartesian3.ZERO, projectedPosition); + newYAxis.x = projectedPosition.z; newYAxis.y = projectedPosition.x; newYAxis.z = projectedPosition.y; @@ -1313,7 +1319,9 @@ function setViewCV(camera, position, hpr, convert) { position, scratchSetViewCartographic, ); - position = projection.project(cartographic, scratchSetViewCartesian); + position = defined(cartographic) + ? projection.project(cartographic, scratchSetViewCartesian) + : Cartesian3.clone(Cartesian3.ZERO, scratchSetViewCartesian); } Cartesian3.clone(position, camera.position); } @@ -1348,7 +1356,9 @@ function setView2D(camera, position, hpr, convert) { position, scratchSetViewCartographic, ); - position = projection.project(cartographic, scratchSetViewCartesian); + position = defined(cartographic) + ? projection.project(cartographic, scratchSetViewCartesian) + : Cartesian3.clone(Cartesian3.ZERO, scratchSetViewCartesian); } Cartesian2.clone(position, camera.position); diff --git a/packages/engine/Source/Scene/GlobeSurfaceTile.js b/packages/engine/Source/Scene/GlobeSurfaceTile.js index 0571b9e363ee..756d7ae6c599 100644 --- a/packages/engine/Source/Scene/GlobeSurfaceTile.js +++ b/packages/engine/Source/Scene/GlobeSurfaceTile.js @@ -137,7 +137,10 @@ function getPosition(encoding, mode, projection, vertices, index, result) { position, scratchCartographic, ); - position = projection.project(positionCartographic, result); + position = defined(positionCartographic) + ? projection.project(positionCartographic, result) + : Cartesian3.clone(Cartesian3.ZERO, result); + position = Cartesian3.fromElements( position.z, position.x, diff --git a/packages/engine/Source/Scene/PolylineCollection.js b/packages/engine/Source/Scene/PolylineCollection.js index c216bbe30cef..61b3fbab7b5e 100644 --- a/packages/engine/Source/Scene/PolylineCollection.js +++ b/packages/engine/Source/Scene/PolylineCollection.js @@ -1778,10 +1778,14 @@ PolylineBucket.prototype.getSegments = function (polyline, projection) { for (let n = 0; n < length; ++n) { position = positions[n]; p = Matrix4.multiplyByPoint(modelMatrix, position, p); + const cartographic = ellipsoid.cartesianToCartographic( + p, + scratchCartographic, + ); newPositions.push( - projection.project( - ellipsoid.cartesianToCartographic(p, scratchCartographic), - ), + defined(cartographic) + ? projection.project(cartographic) + : Cartesian3.clone(Cartesian3.ZERO), ); } diff --git a/packages/engine/Source/Scene/Primitive.js b/packages/engine/Source/Scene/Primitive.js index 8a09a483bcad..eee2b7a9b2a4 100644 --- a/packages/engine/Source/Scene/Primitive.js +++ b/packages/engine/Source/Scene/Primitive.js @@ -1521,10 +1521,11 @@ function updateBatchTableBoundingSpheres(primitive, frameState) { center, scratchBoundingSphereCartographic, ); - const center2D = projection.project( - cartographic, - scratchBoundingSphereCenter2D, - ); + const center2D = defined(cartographic) + ? projection.project(cartographic, scratchBoundingSphereCenter2D) + : Cartesian3.clone( + Cartesian3.clone(Cartesian3.ZERO, scratchBoundingSphereCenter2D), + ); encodedCenter = EncodedCartesian3.fromCartesian( center2D, scratchBoundingSphereCenterEncoded, @@ -1589,18 +1590,17 @@ function updateBatchTableOffsets(primitive, frameState) { center, scratchBoundingSphereCartographic, ); - const center2D = projection.project( - cartographic, - scratchBoundingSphereCenter2D, - ); + + const center2D = defined(cartographic) + ? projection.project(cartographic, scratchBoundingSphereCenter2D) + : Cartesian3.clone(Cartesian3.ZERO, scratchBoundingSphereCenter2D); const newPoint = Cartesian3.add(offset, center, offsetScratchCartesian); cartographic = ellipsoid.cartesianToCartographic(newPoint, cartographic); - const newPointProjected = projection.project( - cartographic, - offsetScratchCartesian, - ); + const newPointProjected = defined(cartographic) + ? projection.project(cartographic, offsetScratchCartesian) + : Cartesian3.clone(Cartesian3.ZERO, scratchBoundingSphereCenter2D); const newVector = Cartesian3.subtract( newPointProjected, diff --git a/packages/engine/Source/Scene/SceneTransitioner.js b/packages/engine/Source/Scene/SceneTransitioner.js index 56e177cc08b1..c7d5c461ea15 100644 --- a/packages/engine/Source/Scene/SceneTransitioner.js +++ b/packages/engine/Source/Scene/SceneTransitioner.js @@ -161,10 +161,14 @@ SceneTransitioner.prototype.morphToColumbusView = function ( ); Matrix4.inverseTransformation(toENU, toENU); - scene.mapProjection.project( - ellipsoid.cartesianToCartographic(position, scratchToCVCartographic), + const cartographic = ellipsoid.cartesianToCartographic( position, + scratchToCVCartographic, ); + position = defined(cartographic) + ? scene.mapProjection.project(cartographic, position) + : Cartesian3.clone(Cartesian3.ZERO, position); + Matrix4.multiplyByPointAsVector(toENU, direction, direction); Matrix4.multiplyByPointAsVector(toENU, up, up); } diff --git a/packages/engine/Specs/Scene/PrimitiveSpec.js b/packages/engine/Specs/Scene/PrimitiveSpec.js index 010108e6905d..ddf47f0c6f30 100644 --- a/packages/engine/Specs/Scene/PrimitiveSpec.js +++ b/packages/engine/Specs/Scene/PrimitiveSpec.js @@ -1367,6 +1367,40 @@ describe( primitive.destroy(); expect(primitive.isDestroyed()).toEqual(true); }); + + it("does not throw an error when rendering a primitive at the origin", function () { + const modelMat = Matrix4.fromTranslation(Cartesian3.ZERO); + + const boxInstance = new GeometryInstance({ + geometry: BoxGeometry.fromDimensions({ + vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, + dimensions: new Cartesian3(500000.0, 500000.0, 500000.0), + }), + id: "box", + attributes: { + color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 0.5), + // This triggers batching to run, which is the code path where an origin-centered object needs special treatment. + distanceDisplayCondition: + new DistanceDisplayConditionGeometryInstanceAttribute( + 0, + 10000000.0, + ), + }, + modelMatrix: modelMat, + }); + + primitive = new Primitive({ + geometryInstances: boxInstance, + appearance: new PerInstanceColorAppearance({ + closed: true, + }), + asynchronous: false, + }); + + expect(function () { + primitive.update(frameState); + }).not.toThrow(); + }); }, "WebGL", );