diff --git a/CHANGES.md b/CHANGES.md index 8c0610b23883..e18a0e2643a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ Change Log ========== ### 1.30 - 2017-02-01 + * Deprecated * The properties `url` and `key` will be removed from `GeocoderViewModel` in 1.31. These properties will be available on geocoder services that support them, like `BingMapsGeocoderService`. * The function `createBinormalAndBitangent` of `GeometryPipeline` will be removed in 1.31. Use the function `createTangentAndBitangent` instead. [#4856](https://github.com/AnalyticalGraphicsInc/cesium/pull/4856) @@ -31,8 +32,10 @@ Change Log * Added `Rectangle.fromRadians`. * `TerrainProvider` now optionally exposes an `availability` property that can be used to query the terrain level that is available at a location or in a rectangle. Currently only `CesiumTerrainProvider` exposes this property. * Added `sampleTerrainMostDetailed` to sample the height of an array of positions using the best available terrain data at each point. This requires a `TerrainProvider` with the `availability` property. +* Added 2D and Columbus View support for models using the RTC extension or whose vertices are in WGS84 coordinates. [#4922](https://github.com/AnalyticalGraphicsInc/cesium/pull/4922) ### 1.29 - 2017-01-02 + * Improved 3D Models * Added the ability to blend a `Model` with a color/translucency. Added `color`, `colorBlendMode`, and `colorBlendAmount` properties to `Model`, `ModelGraphics`, and CZML. Also added `ColorBlendMode` enum. [#4547](https://github.com/AnalyticalGraphicsInc/cesium/pull/4547) * Added the ability to render a `Model` with a silhouette. Added `silhouetteColor` and `silhouetteSize` properties to `Model`, `ModelGraphics`, and CZML. [#4314](https://github.com/AnalyticalGraphicsInc/cesium/pull/4314) @@ -69,6 +72,7 @@ Change Log * Fixed `Cartographic.fromCartesian` when the cartesian is not on the ellipsoid surface. [#4611](https://github.com/AnalyticalGraphicsInc/cesium/issues/4611) ### 1.27 - 2016-11-01 + * Deprecated * Individual heading, pitch, and roll options to `Transforms.headingPitchRollToFixedFrame` and `Transforms.headingPitchRollQuaternion` have been deprecated and will be removed in 1.30. Pass the new `HeadingPitchRoll` object instead. [#4498](https://github.com/AnalyticalGraphicsInc/cesium/pull/4498) * Breaking changes @@ -207,9 +211,11 @@ Change Log * Added `packArray` and `unpackArray` functions to `Cartesian2`, `Cartesian3`, and `Cartesian4`. ### 1.22.2 - 2016-06-14 + * This is an npm only release to fix the improperly published 1.22.1. There were no code changes. ### 1.22.1 - 2016-06-13 + * Fixed default Bing Key and added a watermark to notify users that they need to sign up for their own key. ### 1.22 - 2016-06-01 @@ -328,6 +334,7 @@ Change Log * Fixed hole that appeared in the top of in dynamic ellipsoids ### 1.18 - 2016-02-01 + * Breaking changes * Removed support for `CESIUM_binary_glTF`. Use `KHR_binary_glTF` instead, which is the default for the online [COLLADA-to-glTF converter](http://cesiumjs.org/convertmodel.html). * Deprecated diff --git a/Source/Core/Transforms.js b/Source/Core/Transforms.js index e29c010c275b..e6a561adf9a2 100644 --- a/Source/Core/Transforms.js +++ b/Source/Core/Transforms.js @@ -1001,5 +1001,47 @@ define([ return result; }; + var swizzleMatrix = new Matrix4( + 0.0, 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + + /** + * @private + */ + Transforms.wgs84To2DModelMatrix = function(projection, center, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(projection)) { + throw new DeveloperError('projection is required.'); + } + if (!defined(center)) { + throw new DeveloperError('center is required.'); + } + if (!defined(result)) { + throw new DeveloperError('result is required.'); + } + //>>includeEnd('debug'); + + var ellipsoid = projection.ellipsoid; + + var fromENU = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, scratchFromENU); + var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); + + var cartographic = ellipsoid.cartesianToCartographic(center, scratchCartographic); + var projectedPosition = projection.project(cartographic, scratchCartesian3Projection); + var newOrigin = scratchCartesian4NewOrigin; + newOrigin.x = projectedPosition.z; + newOrigin.y = projectedPosition.x; + newOrigin.z = projectedPosition.y; + newOrigin.w = 1.0; + + var translation = Matrix4.fromTranslation(newOrigin, scratchFromENU); + Matrix4.multiply(swizzleMatrix, toENU, result); + Matrix4.multiply(translation, result, result); + + return result; + }; + return Transforms; }); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 3d6cc75f72eb..ee4952c7fcc5 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -671,8 +671,10 @@ define([ this._pickIds = []; // CESIUM_RTC extension - this._rtcCenter = undefined; // in world coordinates + this._rtcCenter = undefined; // reference to either 3D or 2D this._rtcCenterEye = undefined; // in eye coordinates + this._rtcCenter3D = undefined; // in world coordinates + this._rtcCenter2D = undefined; // in projected world coordinates } defineProperties(Model.prototype, { @@ -2494,10 +2496,13 @@ define([ // CESIUM_RTC extension var mvRtc = new Matrix4(); return function() { - Matrix4.getTranslation(uniformState.model, scratchTranslationRtc); - Cartesian3.add(scratchTranslationRtc, model._rtcCenter, scratchTranslationRtc); - Matrix4.multiplyByPoint(uniformState.view, scratchTranslationRtc, scratchTranslationRtc); - return Matrix4.setTranslation(uniformState.modelView, scratchTranslationRtc, mvRtc); + if (defined(model._rtcCenter)) { + Matrix4.getTranslation(uniformState.model, scratchTranslationRtc); + Cartesian3.add(scratchTranslationRtc, model._rtcCenter, scratchTranslationRtc); + Matrix4.multiplyByPoint(uniformState.view, scratchTranslationRtc, scratchTranslationRtc); + return Matrix4.setTranslation(uniformState.modelView, scratchTranslationRtc, mvRtc); + } + return uniformState.modelView; }; }, MODELVIEWPROJECTION : function(uniformState, model) { @@ -3325,6 +3330,7 @@ define([ } var scratchNodeStack = []; + var scratchComputedTranslation = new Cartesian4(); var scratchComputedMatrixIn2D = new Matrix4(); function updateNodeHierarchyModelMatrix(model, modelTransformChanged, justLoaded, projection) { @@ -3338,7 +3344,20 @@ define([ var computedModelMatrix = model._computedModelMatrix; if (model._mode !== SceneMode.SCENE3D) { - computedModelMatrix = Transforms.basisTo2D(projection, computedModelMatrix, scratchComputedMatrixIn2D); + var translation = Matrix4.getColumn(computedModelMatrix, 3, scratchComputedTranslation); + if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { + computedModelMatrix = Transforms.basisTo2D(projection, computedModelMatrix, scratchComputedMatrixIn2D); + model._rtcCenter = model._rtcCenter3D; + } else { + var center = model.boundingSphere.center; + var to2D = Transforms.wgs84To2DModelMatrix(projection, center, scratchComputedMatrixIn2D); + computedModelMatrix = Matrix4.multiply(to2D, computedModelMatrix, scratchComputedMatrixIn2D); + + if (defined(model._rtcCenter)) { + Matrix4.setTranslation(computedModelMatrix, Cartesian4.UNIT_W, computedModelMatrix); + model._rtcCenter = model._rtcCenter2D; + } + } } for (var i = 0; i < length; ++i) { @@ -4091,8 +4110,20 @@ define([ if (this._state !== ModelState.FAILED) { var extensions = this.gltf.extensions; if (defined(extensions) && defined(extensions.CESIUM_RTC)) { - this._rtcCenter = Cartesian3.fromArray(extensions.CESIUM_RTC.center); - this._rtcCenterEye = new Cartesian3(); + var center = Cartesian3.fromArray(extensions.CESIUM_RTC.center); + if (!Cartesian3.equals(center, Cartesian3.ZERO)) { + this._rtcCenter3D = center; + + var projection = frameState.mapProjection; + var ellipsoid = projection.ellipsoid; + var cartographic = ellipsoid.cartesianToCartographic(this._rtcCenter3D); + var projectedCart = projection.project(cartographic); + Cartesian3.fromElements(projectedCart.z, projectedCart.x, projectedCart.y, projectedCart); + this._rtcCenter2D = projectedCart; + + this._rtcCenterEye = new Cartesian3(); + this._rtcCenter = this._rtcCenter3D; + } } this._loadResources = new LoadResources(); diff --git a/Specs/Core/TransformsSpec.js b/Specs/Core/TransformsSpec.js index 1d4682ca3b7a..da6f5de0943e 100644 --- a/Specs/Core/TransformsSpec.js +++ b/Specs/Core/TransformsSpec.js @@ -899,6 +899,34 @@ defineSuite([ expect(rotation2D).toEqualEpsilon(expected, CesiumMath.EPSILON3); }); + it('wgs84To2DModelMatrix creates a model matrix to transform vertices centered origin to 2D', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + var origin = Cartesian3.fromDegrees(-72.0, 40.0, 100.0, ellipsoid); + + var actual = Transforms.wgs84To2DModelMatrix(projection, origin, new Matrix4()); + var expected = Matrix4.fromTranslation(origin); + Transforms.basisTo2D(projection, expected, expected); + + var actualRotation = Matrix4.getRotation(actual, new Matrix3()); + var expectedRotation = Matrix4.getRotation(expected, new Matrix3()); + expect(actualRotation).toEqualEpsilon(expectedRotation, CesiumMath.EPSILON14); + + var fromENU = Transforms.eastNorthUpToFixedFrame(origin, ellipsoid, new Matrix4()); + var toENU = Matrix4.inverseTransformation(fromENU, new Matrix4()); + var toENUTranslation = Matrix4.getTranslation(toENU, new Cartesian4()); + var projectedTranslation = Matrix4.getTranslation(expected, new Cartesian4()); + + var expectedTranslation = new Cartesian4(); + expectedTranslation.x = projectedTranslation.x + toENUTranslation.z; + expectedTranslation.y = projectedTranslation.y + toENUTranslation.x; + expectedTranslation.z = projectedTranslation.z + toENUTranslation.y; + + var actualTranslation = Matrix4.getTranslation(actual, new Cartesian4()); + + expect(actualTranslation).toEqualEpsilon(expectedTranslation, CesiumMath.EPSILON14); + }); + it('eastNorthUpToFixedFrame throws without an origin', function() { expect(function() { Transforms.eastNorthUpToFixedFrame(undefined, Ellipsoid.WGS84); @@ -970,4 +998,22 @@ defineSuite([ Transforms.basisTo2D(new GeographicProjection(), Matrix4.IDENTITY, undefined); }).toThrowDeveloperError(); }); + + it ('wgs84To2DModelMatrix throws without projection', function() { + expect(function() { + Transforms.wgs84To2DModelMatrix(undefined, Cartesian3.UNIT_X, new Matrix4()); + }).toThrowDeveloperError(); + }); + + it ('wgs84To2DModelMatrix throws without center', function() { + expect(function() { + Transforms.wgs84To2DModelMatrix(new GeographicProjection(), undefined, new Matrix4()); + }).toThrowDeveloperError(); + }); + + it ('wgs84To2DModelMatrix throws without result', function() { + expect(function() { + Transforms.wgs84To2DModelMatrix(new GeographicProjection(), Cartesian3.UNIT_X, undefined); + }).toThrowDeveloperError(); + }); }); diff --git a/Specs/Data/Models/Boxes-ECEF/ecef.glb b/Specs/Data/Models/Boxes-ECEF/ecef.glb new file mode 100644 index 000000000000..5ce9b416c51e Binary files /dev/null and b/Specs/Data/Models/Boxes-ECEF/ecef.glb differ diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index c9908b0fa5c3..2a460f4b94cb 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -77,6 +77,7 @@ defineSuite([ var texturedBoxCustomUrl = './Data/Models/Box-Textured-Custom/CesiumTexturedBoxTest.gltf'; var texturedBoxKhrBinaryUrl = './Data/Models/Box-Textured-Binary/CesiumTexturedBoxTest.glb'; var boxRtcUrl = './Data/Models/Box-RTC/Box.gltf'; + var boxesEcefUrl = './Data/Models/Boxes-ECEF/ecef.glb'; var cesiumAirUrl = './Data/Models/CesiumAir/Cesium_Air.gltf'; var cesiumAir_0_8Url = './Data/Models/CesiumAir/Cesium_Air_0_8.gltf'; var animBoxesUrl = './Data/Models/anim-test-1-boxes/anim-test-1-boxes.gltf'; @@ -266,6 +267,50 @@ defineSuite([ }); }); + it('renders RTC in 2D', function() { + return loadModel(boxRtcUrl, { + modelMatrix : Matrix4.IDENTITY, + minimumPixelSize : 1 + }).then(function(m) { + scene.morphTo2D(0.0); + verifyRender(m); + primitives.remove(m); + }); + }); + + it('renders ECEF in 2D', function() { + return loadModel(boxesEcefUrl, { + modelMatrix : Matrix4.IDENTITY, + minimumPixelSize : undefined + }).then(function(m) { + scene.morphTo2D(0.0); + verifyRender(m); + primitives.remove(m); + }); + }); + + it('renders RTC in CV', function() { + return loadModel(boxRtcUrl, { + modelMatrix : Matrix4.IDENTITY, + minimumPixelSize : 1 + }).then(function(m) { + scene.morphToColumbusView(0.0); + verifyRender(m); + primitives.remove(m); + }); + }); + + it('renders ECEF in CV', function() { + return loadModel(boxesEcefUrl, { + modelMatrix : Matrix4.IDENTITY, + minimumPixelSize : undefined + }).then(function(m) { + scene.morphToColumbusView(0.0); + verifyRender(m); + primitives.remove(m); + }); + }); + it('resolves readyPromise', function() { return texturedBoxModel.readyPromise.then(function(model) { verifyRender(model);