diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000000..85fb445c2db3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceRoot}\\server.js", + "cwd": "${workspaceRoot}" + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Process", + "port": 5858 + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..f04ffafe314e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,30 @@ +// Cesium project settings for VSCode. +{ + "files.exclude": { + "**/.git": true, + ".idea": true, + ".metadata": true, + "Build": true, + "Instrumented": true, + "**/Cesium-*.zip": true, + "**/cesium-*.tgz": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + + "Apps/Sandcastle/jsHintOption?.js": true, + "Apps/Sandcastle/gallery/gallery-inde?.js": true, + + "Source/Cesiu?.js": true, + "Source/Shaders/**/*.js": true, + + "Specs/SpecLis?.js": true, + "node_modules": true, + "npm-debu?.log": true + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "editor.insertSpaces": true, + "editor.tabSize": 4, + "editor.detectIndentation": false, + "jshint.enable": true +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000000..4befb021f0ca --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,32 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "gulp", + "isShellCommand": true, + "args": [ + "--no-color" + ], + "tasks": [ + { + "taskName": "build", + "args": [], + "isBuildCommand": true, + "isBackground": false, + "problemMatcher": [ + "$lessCompile", + "$tsc", + "$jshint" + ] + }, + { + "taskName": "test", + "args": [], + "isTestCommand": true + }, + { + "taskName": "build-watch", + "isBackground": true + } + ] +} diff --git a/Apps/Sandcastle/gallery/Labels.html b/Apps/Sandcastle/gallery/Labels.html index 2c4b4b4b7f96..aaf32e0826d2 100644 --- a/Apps/Sandcastle/gallery/Labels.html +++ b/Apps/Sandcastle/gallery/Labels.html @@ -109,6 +109,18 @@ }); } +function scaleByDistance() { + Sandcastle.declare(scaleByDistance); + + viewer.entities.add({ + position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222), + label : { + text : 'Philadelphia', + scaleByDistance : new Cesium.NearFarScalar(1.5e2, 2.0, 1.5e7, 0.5) + } + }); +} + Sandcastle.addToolbarMenu([{ text : 'Add label', onselect : function() { @@ -139,6 +151,12 @@ fadeByDistance(); Sandcastle.highlight(fadeByDistance); } +}, { + text : 'Scale label by distance', + onselect : function() { + scaleByDistance(); + Sandcastle.highlight(scaleByDistance); + } }]); Sandcastle.reset = function() { diff --git a/Apps/Sandcastle/gallery/Picking.html b/Apps/Sandcastle/gallery/Picking.html index 00e82620e44f..a976f27363ac 100644 --- a/Apps/Sandcastle/gallery/Picking.html +++ b/Apps/Sandcastle/gallery/Picking.html @@ -174,17 +174,16 @@ } }); - var sceneModeWarningPosted = false; - // Mouse over the globe to see the cartographic position handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); handler.setInputAction(function(movement) { + var foundPosition = false; var scene = viewer.scene; - var pickedObject = scene.pick(movement.endPosition); - if (scene.pickPositionSupported && Cesium.defined(pickedObject) && pickedObject.id === modelEntity) { - if (scene.mode === Cesium.SceneMode.SCENE3D) { + if (scene.mode !== Cesium.SceneMode.MORPHING) { + var pickedObject = scene.pick(movement.endPosition); + if (scene.pickPositionSupported && Cesium.defined(pickedObject) && pickedObject.id === modelEntity) { var cartesian = viewer.scene.pickPosition(movement.endPosition); if (Cesium.defined(cartesian)) { @@ -200,14 +199,10 @@ '\nLat: ' + (' ' + latitudeString).slice(-7) + '\u00B0' + '\nAlt: ' + (' ' + heightString).slice(-7) + 'm'; - var camera = scene.camera; - labelEntity.label.eyeOffset = new Cesium.Cartesian3(0.0, 0.0, camera.frustum.near * 1.5 - Cesium.Cartesian3.distance(cartesian, camera.position)); + labelEntity.label.eyeOffset = new Cesium.Cartesian3(0.0, 0.0, -cartographic.height * (scene.mode === Cesium.SceneMode.SCENE2D ? 1.5 : 1.0)); foundPosition = true; } - } else if (!sceneModeWarningPosted) { - sceneModeWarningPosted = true; - console.log("pickPosition is currently only supported in 3D mode."); } } diff --git a/CHANGES.md b/CHANGES.md index eed35940b2bb..b5e58bc021d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,28 +12,41 @@ Change Log * `Cesium3DTilesInspector` * `Cesium3DTilesInspectorViewModel` +### 1.32 - 2017-04-03 + +* Added the event `Viewer.trackedEntityChanged`, which is raised when the value of `viewer.trackedEntity` changes. [#5060](https://github.com/AnalyticalGraphicsInc/cesium/pull/5060) +* Fix crunch compressed textures in IE11. [#5057](https://github.com/AnalyticalGraphicsInc/cesium/pull/5057) + ### 1.31 - 2017-03-01 * Deprecated - * The function `Quaternion.fromHeadingPitchRoll(heading, pitch, roll,result)` will be removed in 1.33. Use `Quaternion.fromHeadingPitchRoll(hpr,result)` instead where hpr is a HeadingPitchRoll. - * The function `Transforms.headingPitchRollToFixedFrame(origin, headingPitchRoll, ellipsoid, result)` will be removed in 1.33. Use `Transforms.headingPitchRollToFixedFrame(origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result)` instead where fixedFrameTransform is a a 4x4 transformation matrix (see Transforms.localFrameToFixedFrameGenerator). - * The function `Transforms.headingPitchRollQuaternion(origin, headingPitchRoll, ellipsoid, result)` will be removed in 1.33. Use `Transforms.headingPitchRollQuaternion(origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result)` instead where fixedFrameTransform is a a 4x4 transformation matrix (see Transforms.localFrameToFixedFrameGenerator). - * `ArcGisImageServerTerrainProvider` will be removed in 1.32 due to missing TIFF support in web browsers. + * The function `Quaternion.fromHeadingPitchRoll(heading, pitch, roll, result)` will be removed in 1.33. Use `Quaternion.fromHeadingPitchRoll(hpr, result)` instead where `hpr` is a `HeadingPitchRoll`. [#4896](https://github.com/AnalyticalGraphicsInc/cesium/pull/4896) + * The function `Transforms.headingPitchRollToFixedFrame(origin, headingPitchRoll, ellipsoid, result)` will be removed in 1.33. Use `Transforms.headingPitchRollToFixedFrame(origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result)` instead where `fixedFrameTransform` is a a 4x4 transformation matrix (see `Transforms.localFrameToFixedFrameGenerator`). [#4896](https://github.com/AnalyticalGraphicsInc/cesium/pull/4896) + * The function `Transforms.headingPitchRollQuaternion(origin, headingPitchRoll, ellipsoid, result)` will be removed in 1.33. Use `Transforms.headingPitchRollQuaternion(origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result)` instead where `fixedFrameTransform` is a a 4x4 transformation matrix (see `Transforms.localFrameToFixedFrameGenerator`). [#4896](https://github.com/AnalyticalGraphicsInc/cesium/pull/4896) + * `ArcGisImageServerTerrainProvider` will be removed in 1.32 due to missing TIFF support in web browsers. [#4981](https://github.com/AnalyticalGraphicsInc/cesium/pull/4981) * Breaking changes * Corrected spelling of `Color.FUCHSIA` from `Color.FUSCHIA`. [#4977](https://github.com/AnalyticalGraphicsInc/cesium/pull/4977) -* Added support to `DebugCameraPrimitive` to draw multifrustum planes. The attribute `debugShowFrustumPlanes` of `Scene` and `frustumPlanes` of `CesiumInspector` toggles this. `FrameState` has been augmented to include `frustumSplits` which is a `Number[]` of the near/far planes of the camera frustums. -* Enable rendering `GroundPrimitives` on hardware without the `EXT_frag_depth` extension; however, this could cause artifacts for certain viewing angles. -* Always outline KML line extrusions so that they show up properly in 2D and other straight down views. + * The enums `MIDDLE_DOUBLE_CLICK` and `RIGHT_DOUBLE_CLICK` from `ScreenSpaceEventType` have been removed. [#5052](https://github.com/AnalyticalGraphicsInc/cesium/pull/5052) + * Removed the function `GeometryPipeline.computeBinormalAndTangent`. Use `GeometryPipeline.computeTangentAndBitangent` instead. [#5053](https://github.com/AnalyticalGraphicsInc/cesium/pull/5053) + * Removed the `url` and `key` properties from `GeocoderViewModel`. [#5056](https://github.com/AnalyticalGraphicsInc/cesium/pull/5056) + * `BingMapsGeocoderServices` now requires `options.scene`. [#5056](https://github.com/AnalyticalGraphicsInc/cesium/pull/5056) * Added compressed texture support. [#4758](https://github.com/AnalyticalGraphicsInc/cesium/pull/4758) * glTF models and imagery layers can now reference [KTX](https://www.khronos.org/opengles/sdk/tools/KTX/) textures and textures compressed with [crunch](https://github.com/BinomialLLC/crunch). * Added `loadKTX`, to load KTX textures, and `loadCRN` to load crunch compressed textures. * Added new `PixelFormat` and `WebGLConstants` enums from WebGL extensions `WEBGL_compressed_s3tc`, `WEBGL_compressed_texture_pvrtc`, and `WEBGL_compressed_texture_etc1`. * Added `CompressedTextureBuffer`. -* Improved `RectangleGeometry` by skipping unecessary logic in the code [#4948](https://github.com/AnalyticalGraphicsInc/cesium/pull/4948) -* Added `Transforms.localFrameToFixedFrameGenerator` to generate a function that computes a 4x4 transformation matrix from a local reference frame to fixed reference frame. +* Added support for `Scene.pickPosition` in Columbus view and 2D. [#4990](https://github.com/AnalyticalGraphicsInc/cesium/pull/4990) +* Added support for depth picking translucent primitives when `Scene.pickTranslucentDepth` is `true`. [#4979](https://github.com/AnalyticalGraphicsInc/cesium/pull/4979) * Fixed an issue where the camera would zoom past an object and flip to the other side of the globe. [#4967](https://github.com/AnalyticalGraphicsInc/cesium/pull/4967) and [#4982](https://github.com/AnalyticalGraphicsInc/cesium/pull/4982) -* Fixed exception in 2D in certain cases with polylines when rotating the map. [#4619](https://github.com/AnalyticalGraphicsInc/cesium/issues/4619) +* Enable rendering `GroundPrimitives` on hardware without the `EXT_frag_depth` extension; however, this could cause artifacts for certain viewing angles. [#4930](https://github.com/AnalyticalGraphicsInc/cesium/pull/4930) +* Added `Transforms.localFrameToFixedFrameGenerator` to generate a function that computes a 4x4 transformation matrix from a local reference frame to fixed reference frame. [#4896](https://github.com/AnalyticalGraphicsInc/cesium/pull/4896) +* Added `Label.scaleByDistance` to control minimum/maximum label size based on distance from the camera. [#5019](https://github.com/AnalyticalGraphicsInc/cesium/pull/5019) +* Added support to `DebugCameraPrimitive` to draw multifrustum planes. The attribute `debugShowFrustumPlanes` of `Scene` and `frustumPlanes` of `CesiumInspector` toggle this. [#4932](https://github.com/AnalyticalGraphicsInc/cesium/pull/4932) +* Added fix to always outline KML line extrusions so that they show up properly in 2D and other straight down views. [#4961](https://github.com/AnalyticalGraphicsInc/cesium/pull/4961) +* Improved `RectangleGeometry` by skipping unnecessary logic in the code. [#4948](https://github.com/AnalyticalGraphicsInc/cesium/pull/4948) +* Fixed exception for polylines in 2D when rotating the map. [#4619](https://github.com/AnalyticalGraphicsInc/cesium/issues/4619) * Fixed an issue with constant `VertexArray` attributes not being set correctly. [#4995](https://github.com/AnalyticalGraphicsInc/cesium/pull/4995) +* Added the event `Viewer.selectedEntityChanged`, which is raised when the value of `viewer.selectedEntity` changes. [#5043](https://github.com/AnalyticalGraphicsInc/cesium/pull/5043) ### 1.30 - 2017-02-01 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 681280e6a60c..a6503618995e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -132,3 +132,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Jane Minghui Guo](https://github.com/Jane-Of-Art) * [Prasanna Natarajan](https://github.com/PrasannaNatarajan) * [Joseph Klinger](https://github.com/klingerj) +* [Grace Lee](https://github.com/glee2244) diff --git a/Documentation/Contributors/README.md b/Documentation/Contributors/README.md index 11ba10f61036..d783cee68e4a 100644 --- a/Documentation/Contributors/README.md +++ b/Documentation/Contributors/README.md @@ -5,6 +5,7 @@ * **IDEs** - use any IDE you want for Cesium development. Most contributors use WebStorm (commercial) or Eclipse (open source). * [WebStorm Guide](WebStormGuide/README.md) - How to set up WebStorm. * [Eclipse Guide](EclipseGuide/README.md) - How to set up Eclipse. + * [VSCode Guide](VSCodeGuide/README.md) - How to set up VSCode. * [Coding Guide](CodingGuide/README.md) - JavaScript and GLSL coding conventions and best practices for design, maintainability, and performance. * [Testing Guide](TestingGuide/README.md) - How to run the Cesium tests and write awesome tests. * [Documentation Guide](DocumentationGuide/README.md) - How to write great reference documentation. diff --git a/Documentation/Contributors/VSCodeGuide/README.md b/Documentation/Contributors/VSCodeGuide/README.md new file mode 100644 index 000000000000..61cedd85f973 --- /dev/null +++ b/Documentation/Contributors/VSCodeGuide/README.md @@ -0,0 +1,76 @@ +# VSCode Guide + +1. Install [VSCode](https://code.visualstudio.com/). + +2. If you haven't already, install `gulp-cli` globally, with +`npm install -g gulp-cli` from a bash prompt. This does not require +administrative rights, and places a `gulp` shim into your path that will +invoke a copy of gulp from the local project folder. This is needed for +VSCode's build tasks to work with Cesium. + +3. Click `File -> Open Folder...` and open the Cesium root folder. + +## Shell Integration (optional) + +VSCode has an integrated shell, exposed on Windows by pressing CTRL-\` (CTRL-backtick). +You may want to switch this to be a git bash shell by default. If so, click +File -> Preferences -> Settings... and enter `integrated.shell` into the search +box. Choose the appropriate key for your operating system, for example +`terminal.integrated.shell.windows` for Windows, and click the edit icon. +The default setting will be copied to your user settings. The default for +Windows is `"C:\\Windows\\system32\\cmd.exe"`. Change this to point to your +git bash install. For example: + +``` +{ + "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe" +} +``` + +Note that on Windows, the git bash desktop icon points at a different exe file, +one that forces a separate (non-integrated) window to pop open outside of VSCode. +Make sure you are pointed at the correct exe as shown above, with Git 2.0.0 or +higher installed, to get the correct integrated shell behavior. + +## VSCode Plugins (mostly optional) + +Click on the extensions icon, or press CTRL-SHIFT-X to see the list of installed +VSCode extensions. While we don't officially endorse any particular 3rd-party +plugin, there are some that appear to be quite useful to Cesium. Just enter +the desired plugin name in the search box and click install. You will need to +restart VSCode after you are done installing plugins. + +* **jshint** by Dirk Baeumer -- This plugin picks up on Cesium's own jsHint settings, +and will warn of any violations. The Cesium main repository should pass jsHint +using the Cesium jsHint settings with no warnings and no errors. Proposed +contributions to Cesium that introduce jsHint warnings will need to be corrected +before they are accepted. + +* **Shader languages support for VS Code** by slevesque -- This plugin provides +syntax highlighting for Cesium's shader code. + +* **Prettify JSON** by Mohsen Azimi -- This seems generally useful. + +## VSCode Tasks and Files + +You can launch any of Cesium's npm tasks from within VSCode by pressing +CTRL-P and typing `task ` (with a trailing space). Autocomplete will +offer the list of npm tasks for you to run. The first time you do this, +allow a moment for it to read the available tasks from the gulpfile. + +You can also jump to any source file with the same CTRL-P keypress +followed by the name of the file. + +## Building Cesium + +Cesium has a number of GLSL shaders and auto-generated files that must be +built before Cesium can be used. The simplest way in VSCode is to type +`CTRL-P` `task build` to trigger a single build. + +You can also start the build watcher with `CTRL-P` `task build-watch`. This +leaves a watcher running until you exit VSCode, that will automatically +update any shaders or auto-generated files to reflect changes to Cesium as +you save them. + +Keep in mind that `build-watch` will quietly terminate when +you quit VSCode, so should be restarted manually on next launch. diff --git a/README.md b/README.md index d75994100c97..7b1531411335 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ We appreciate attribution by including the Cesium logo and link in your app. ### Demos ###

+  +  +  +        @@ -94,7 +98,6 @@ We appreciate attribution by including the Cesium logo and link in your app.       -        @@ -134,7 +137,6 @@ We appreciate attribution by including the Cesium logo and link in your app.       -        @@ -143,7 +145,6 @@ We appreciate attribution by including the Cesium logo and link in your app.       -        diff --git a/Source/Core/BingMapsGeocoderService.js b/Source/Core/BingMapsGeocoderService.js index 7f9c4fc4ced8..e4130d8ed61a 100644 --- a/Source/Core/BingMapsGeocoderService.js +++ b/Source/Core/BingMapsGeocoderService.js @@ -17,7 +17,7 @@ define([ Rectangle) { 'use strict'; - var url = 'https://dev.virtualearth.net/REST/v1/Locations'; + var url = 'https://dev.virtualearth.net/REST/v1/Locations'; /** * Provides geocoding through Bing Maps. @@ -25,12 +25,26 @@ define([ * @constructor * * @param {Object} options Object with the following properties: + * @param {String} options.scene The scene * @param {String} [options.key] A key to use with the Bing Maps geocoding service */ function BingMapsGeocoderService(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - this._url = 'https://dev.virtualearth.net/REST/v1/Locations'; - this._key = BingMapsApi.getKey(options.key); + //>>includeStart('debug', pragmas.debug); + if (!defined(options.scene)) { + throw new DeveloperError('options.scene is required.'); + } + //>>includeEnd('debug'); + + var key = options.key; + this._key = BingMapsApi.getKey(key); + + if (defined(key)) { + var errorCredit = BingMapsApi.getErrorCredit(key); + if (defined(errorCredit)) { + options.scene._frameState.creditDisplay.addDefaultCredit(errorCredit); + } + } } defineProperties(BingMapsGeocoderService.prototype, { @@ -42,7 +56,7 @@ define([ */ url : { get : function () { - return this._url; + return url; } }, diff --git a/Source/Core/CesiumTerrainProvider.js b/Source/Core/CesiumTerrainProvider.js index 90e78b5370bc..6f19508b7929 100644 --- a/Source/Core/CesiumTerrainProvider.js +++ b/Source/Core/CesiumTerrainProvider.js @@ -19,11 +19,9 @@ define([ './Math', './OrientedBoundingBox', './QuantizedMeshTerrainData', - './Rectangle', './Request', './RequestScheduler', './RequestType', - './RuntimeError', './TerrainProvider', './TileAvailability', './TileProviderError' @@ -47,11 +45,9 @@ define([ CesiumMath, OrientedBoundingBox, QuantizedMeshTerrainData, - Rectangle, Request, RequestScheduler, RequestType, - RuntimeError, TerrainProvider, TileAvailability, TileProviderError) { diff --git a/Source/Core/CircleGeometry.js b/Source/Core/CircleGeometry.js index ddcf17d57532..4a3e87dfdaf6 100644 --- a/Source/Core/CircleGeometry.js +++ b/Source/Core/CircleGeometry.js @@ -5,7 +5,6 @@ define([ './defaultValue', './defined', './defineProperties', - './DeveloperError', './EllipseGeometry', './Ellipsoid', './VertexFormat' @@ -15,7 +14,6 @@ define([ defaultValue, defined, defineProperties, - DeveloperError, EllipseGeometry, Ellipsoid, VertexFormat) { diff --git a/Source/Core/CircleOutlineGeometry.js b/Source/Core/CircleOutlineGeometry.js index 9a6aab62dc12..fa5e78ade493 100644 --- a/Source/Core/CircleOutlineGeometry.js +++ b/Source/Core/CircleOutlineGeometry.js @@ -4,7 +4,6 @@ define([ './Check', './defaultValue', './defined', - './DeveloperError', './EllipseOutlineGeometry', './Ellipsoid' ], function( @@ -12,7 +11,6 @@ define([ Check, defaultValue, defined, - DeveloperError, EllipseOutlineGeometry, Ellipsoid) { 'use strict'; diff --git a/Source/Core/GeocoderService.js b/Source/Core/GeocoderService.js index c15982213a0f..08bbdb3f6d66 100644 --- a/Source/Core/GeocoderService.js +++ b/Source/Core/GeocoderService.js @@ -1,9 +1,7 @@ /*global define*/ define([ - './defineProperties', './DeveloperError' ], function( - defineProperties, DeveloperError) { 'use strict'; diff --git a/Source/Core/GeometryPipeline.js b/Source/Core/GeometryPipeline.js index ed8427ec4602..b689f6e8ecc4 100644 --- a/Source/Core/GeometryPipeline.js +++ b/Source/Core/GeometryPipeline.js @@ -10,7 +10,6 @@ define([ './ComponentDatatype', './defaultValue', './defined', - './deprecationWarning', './DeveloperError', './EncodedCartesian3', './GeographicProjection', @@ -37,7 +36,6 @@ define([ ComponentDatatype, defaultValue, defined, - deprecationWarning, DeveloperError, EncodedCartesian3, GeographicProjection, @@ -1173,20 +1171,28 @@ define([ for (i = 0; i < numVertices; i++) { var i3 = i * 3; vertexNormalData = normalsPerVertex[i]; + Cartesian3.clone(Cartesian3.ZERO, normal); if (vertexNormalData.count > 0) { - Cartesian3.clone(Cartesian3.ZERO, normal); for (j = 0; j < vertexNormalData.count; j++) { Cartesian3.add(normal, normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal); } - Cartesian3.normalize(normal, normal); - normalValues[i3] = normal.x; - normalValues[i3 + 1] = normal.y; - normalValues[i3 + 2] = normal.z; - } else { - normalValues[i3] = 0.0; - normalValues[i3 + 1] = 0.0; - normalValues[i3 + 2] = 1.0; + + // We can run into an issue where a vertex is used with 2 primitives that have opposite winding order. + if (Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10)) { + Cartesian3.clone(normalsPerTriangle[normalIndices[vertexNormalData.indexOffset]], normal); + } } + + // We end up with a zero vector probably because of a degenerate triangle + if (Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10)) { + // Default to (0,0,1) + normal.z = 1.0; + } + + Cartesian3.normalize(normal, normal); + normalValues[i3] = normal.x; + normalValues[i3 + 1] = normal.y; + normalValues[i3 + 2] = normal.z; } geometry.attributes.normal = new GeometryAttribute({ @@ -1344,34 +1350,6 @@ define([ return geometry; }; - /** - * Computes per-vertex binormal and tangents for a geometry containing TRIANGLES. - * The result is new binormal and tangent attributes added to the geometry. - * This assumes a counter-clockwise winding order. - *

- * Based on Computing Tangent Space Basis Vectors - * for an Arbitrary Mesh by Eric Lengyel. - *

- * - * @param {Geometry} geometry The geometry to modify. - * @returns {Geometry} The modified geometry argument with the computed binormal and tangent attributes. - * - * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3. - * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}. - * - * @example - * Cesium.GeometryPipeline.computeBinormalAndTangent(geometry); - * - * @see GeometryPipeline.computeTangentAndBitangent - */ - GeometryPipeline.computeBinormalAndTangent = function(geometry) { - deprecationWarning('computeBinormalAndTangent', 'computeBinormalAndTangent was deprecated in 1.30. It will be removed in 1.31. Use a computeTangentAndBitangent.'); - GeometryPipeline.computeTangentAndBitangent(geometry); - geometry.attributes.binormal = geometry.attributes.bitangent; - - return geometry; - }; - var scratchCartesian2 = new Cartesian2(); var toEncode1 = new Cartesian3(); var toEncode2 = new Cartesian3(); diff --git a/Source/Core/Matrix2.js b/Source/Core/Matrix2.js index 86f0e3597a9d..b7e717685487 100644 --- a/Source/Core/Matrix2.js +++ b/Source/Core/Matrix2.js @@ -5,7 +5,6 @@ define([ './defaultValue', './defined', './defineProperties', - './DeveloperError', './freezeObject' ], function( Cartesian2, @@ -13,7 +12,6 @@ define([ defaultValue, defined, defineProperties, - DeveloperError, freezeObject) { 'use strict'; diff --git a/Source/Core/Matrix4.js b/Source/Core/Matrix4.js index 67de77b5a76b..13e4ba5d9c69 100644 --- a/Source/Core/Matrix4.js +++ b/Source/Core/Matrix4.js @@ -6,7 +6,6 @@ define([ './defaultValue', './defined', './defineProperties', - './DeveloperError', './freezeObject', './Math', './Matrix3', @@ -18,7 +17,6 @@ define([ defaultValue, defined, defineProperties, - DeveloperError, freezeObject, CesiumMath, Matrix3, diff --git a/Source/Core/Quaternion.js b/Source/Core/Quaternion.js index cd19446e2fc8..8eac4c7de49c 100644 --- a/Source/Core/Quaternion.js +++ b/Source/Core/Quaternion.js @@ -5,7 +5,6 @@ define([ './defaultValue', './defined', './deprecationWarning', - './DeveloperError', './FeatureDetection', './freezeObject', './HeadingPitchRoll', @@ -17,7 +16,6 @@ define([ defaultValue, defined, deprecationWarning, - DeveloperError, FeatureDetection, freezeObject, HeadingPitchRoll, diff --git a/Source/Core/Rectangle.js b/Source/Core/Rectangle.js index 24d3efaf2c40..0f451aae3d2e 100644 --- a/Source/Core/Rectangle.js +++ b/Source/Core/Rectangle.js @@ -5,7 +5,6 @@ define([ './defaultValue', './defined', './defineProperties', - './DeveloperError', './Ellipsoid', './freezeObject', './Math' @@ -15,7 +14,6 @@ define([ defaultValue, defined, defineProperties, - DeveloperError, Ellipsoid, freezeObject, CesiumMath) { diff --git a/Source/Core/RectangleGeometry.js b/Source/Core/RectangleGeometry.js index 58738ccf7c7e..034e8d4fa45d 100644 --- a/Source/Core/RectangleGeometry.js +++ b/Source/Core/RectangleGeometry.js @@ -18,7 +18,6 @@ define([ './GeometryPipeline', './IndexDatatype', './Math', - './Matrix2', './Matrix3', './PolygonPipeline', './PrimitiveType', @@ -45,7 +44,6 @@ define([ GeometryPipeline, IndexDatatype, CesiumMath, - Matrix2, Matrix3, PolygonPipeline, PrimitiveType, diff --git a/Source/Core/ScreenSpaceEventHandler.js b/Source/Core/ScreenSpaceEventHandler.js index a22670eb7688..ba63e2e20f02 100644 --- a/Source/Core/ScreenSpaceEventHandler.js +++ b/Source/Core/ScreenSpaceEventHandler.js @@ -4,7 +4,6 @@ define([ './Cartesian2', './defaultValue', './defined', - './deprecationWarning', './destroyObject', './DeveloperError', './FeatureDetection', @@ -16,7 +15,6 @@ define([ Cartesian2, defaultValue, defined, - deprecationWarning, destroyObject, DeveloperError, FeatureDetection, @@ -272,10 +270,6 @@ define([ var screenSpaceEventType; if (button === MouseButton.LEFT) { screenSpaceEventType = ScreenSpaceEventType.LEFT_DOUBLE_CLICK; - } else if (button === MouseButton.MIDDLE) { - screenSpaceEventType = ScreenSpaceEventType.MIDDLE_DOUBLE_CLICK; - } else if (button === MouseButton.RIGHT) { - screenSpaceEventType = ScreenSpaceEventType.RIGHT_DOUBLE_CLICK; } else { return; } @@ -686,14 +680,6 @@ define([ registerListeners(this); } - function checkForDoubleClick(type) { - if (type === ScreenSpaceEventType.MIDDLE_DOUBLE_CLICK) { - deprecationWarning('MIDDLE_DOUBLE_CLICK', 'ScreenSpaceEventType.MIDDLE_DOUBLE_CLICK was deprecated in Cesium 1.30. It will be removed in 1.31.'); - } else if (type === ScreenSpaceEventType.RIGHT_DOUBLE_CLICK) { - deprecationWarning('RIGHT_DOUBLE_CLICK', 'ScreenSpaceEventType.RIGHT_DOUBLE_CLICK was deprecated in Cesium 1.30. It will be removed in 1.31.'); - } - } - /** * Set a function to be executed on an input event. * @@ -715,8 +701,6 @@ define([ } //>>includeEnd('debug'); - checkForDoubleClick(type); - var key = getInputEventKey(type, modifier); this._inputEvents[key] = action; }; @@ -738,8 +722,6 @@ define([ } //>>includeEnd('debug'); - checkForDoubleClick(type); - var key = getInputEventKey(type, modifier); return this._inputEvents[key]; }; @@ -761,8 +743,6 @@ define([ } //>>includeEnd('debug'); - checkForDoubleClick(type); - var key = getInputEventKey(type, modifier); delete this._inputEvents[key]; }; diff --git a/Source/Core/ScreenSpaceEventType.js b/Source/Core/ScreenSpaceEventType.js index 9cc2428da497..3c4388123f91 100644 --- a/Source/Core/ScreenSpaceEventType.js +++ b/Source/Core/ScreenSpaceEventType.js @@ -67,16 +67,6 @@ define([ */ RIGHT_CLICK : 7, - /** - * Represents a mouse right double click event. - * - * @type {Number} - * @constant - * - * @deprecated - */ - RIGHT_DOUBLE_CLICK : 8, - /** * Represents a mouse middle button down event. * @@ -101,16 +91,6 @@ define([ */ MIDDLE_CLICK : 12, - /** - * Represents a mouse middle double click event. - * - * @type {Number} - * @constant - * - * @deprecated - */ - MIDDLE_DOUBLE_CLICK : 13, - /** * Represents a mouse move event. * diff --git a/Source/Core/SphereGeometry.js b/Source/Core/SphereGeometry.js index a30873a61512..18d9b74d56ef 100644 --- a/Source/Core/SphereGeometry.js +++ b/Source/Core/SphereGeometry.js @@ -4,7 +4,6 @@ define([ './Check', './defaultValue', './defined', - './DeveloperError', './EllipsoidGeometry', './VertexFormat' ], function( @@ -12,7 +11,6 @@ define([ Check, defaultValue, defined, - DeveloperError, EllipsoidGeometry, VertexFormat) { 'use strict'; diff --git a/Source/Core/SphereOutlineGeometry.js b/Source/Core/SphereOutlineGeometry.js index fa025b9c99f5..0dbff9a8017a 100644 --- a/Source/Core/SphereOutlineGeometry.js +++ b/Source/Core/SphereOutlineGeometry.js @@ -4,14 +4,12 @@ define([ './Check', './defaultValue', './defined', - './DeveloperError', './EllipsoidOutlineGeometry' ], function( Cartesian3, Check, defaultValue, defined, - DeveloperError, EllipsoidOutlineGeometry) { 'use strict'; diff --git a/Source/Core/Spherical.js b/Source/Core/Spherical.js index 4f79d5871e9e..5eecf7e6b1a4 100644 --- a/Source/Core/Spherical.js +++ b/Source/Core/Spherical.js @@ -2,13 +2,11 @@ define([ './Check', './defaultValue', - './defined', - './DeveloperError' + './defined' ], function( Check, defaultValue, - defined, - DeveloperError) { + defined) { 'use strict'; /** diff --git a/Source/Core/TileAvailability.js b/Source/Core/TileAvailability.js index 22f6853716da..066ee2639b31 100644 --- a/Source/Core/TileAvailability.js +++ b/Source/Core/TileAvailability.js @@ -1,20 +1,16 @@ /*global define*/ define([ - '../ThirdParty/when', './binarySearch', './Cartographic', './defined', './defineProperties', - './sampleTerrain', './DeveloperError', './Rectangle' ], function( - when, binarySearch, Cartographic, defined, defineProperties, - sampleTerrain, DeveloperError, Rectangle) { "use strict"; diff --git a/Source/Core/Transforms.js b/Source/Core/Transforms.js index 41483f28b6e1..27edaabc93ec 100644 --- a/Source/Core/Transforms.js +++ b/Source/Core/Transforms.js @@ -13,7 +13,6 @@ define([ './EarthOrientationParameters', './EarthOrientationParametersSample', './Ellipsoid', - './HeadingPitchRoll', './Iau2006XysData', './Iau2006XysSample', './JulianDate', @@ -36,7 +35,6 @@ define([ EarthOrientationParameters, EarthOrientationParametersSample, Ellipsoid, - HeadingPitchRoll, Iau2006XysData, Iau2006XysSample, JulianDate, diff --git a/Source/Core/sampleTerrainMostDetailed.js b/Source/Core/sampleTerrainMostDetailed.js index 1416474cefb3..027fd8de22c8 100644 --- a/Source/Core/sampleTerrainMostDetailed.js +++ b/Source/Core/sampleTerrainMostDetailed.js @@ -3,14 +3,12 @@ define([ '../ThirdParty/when', './defined', './sampleTerrain', - './DeveloperError', - './Rectangle' + './DeveloperError' ], function( when, defined, sampleTerrain, - DeveloperError, - Rectangle) { + DeveloperError) { "use strict"; /** diff --git a/Source/DataSources/KmlDataSource.js b/Source/DataSources/KmlDataSource.js index f5be3bdd5baf..1f84db071fbf 100644 --- a/Source/DataSources/KmlDataSource.js +++ b/Source/DataSources/KmlDataSource.js @@ -2259,11 +2259,12 @@ define([ * * @example * var viewer = new Cesium.Viewer('cesiumContainer'); - * viewer.dataSources.add(Cesium.KmlDataSource.load('../../SampleData/facilities.kmz'), + * viewer.dataSources.add(Cesium.KmlDataSource.load('../../SampleData/facilities.kmz', * { - * camera: viewer.scene.camera, - * canvas: viewer.scene.canvas - * }); + * camera: viewer.scene.camera, + * canvas: viewer.scene.canvas + * }) + * ); */ function KmlDataSource(options) { options = defaultValue(options, {}); diff --git a/Source/DataSources/LabelGraphics.js b/Source/DataSources/LabelGraphics.js index 3377176256cf..bb7b8b004444 100644 --- a/Source/DataSources/LabelGraphics.js +++ b/Source/DataSources/LabelGraphics.js @@ -45,6 +45,7 @@ define([ * @param {Property} [options.pixelOffset=Cartesian2.ZERO] A {@link Cartesian2} Property specifying the pixel offset. * @param {Property} [options.translucencyByDistance] A {@link NearFarScalar} Property used to set translucency based on distance from the camera. * @param {Property} [options.pixelOffsetScaleByDistance] A {@link NearFarScalar} Property used to set pixelOffset based on distance from the camera. + * @param {Property} [options.scaleByDistance] A {@link NearFarScalar} Property used to set scale based on distance from the camera. * @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @param {Property} [options.distanceDisplayCondition] A Property specifying at what distance from the camera that this label will be displayed. * @@ -87,6 +88,8 @@ define([ this._translucencyByDistanceSubscription = undefined; this._pixelOffsetScaleByDistance = undefined; this._pixelOffsetScaleByDistanceSubscription = undefined; + this._scaleByDistance = undefined; + this._scaleByDistanceSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._definitionChanged = new Event(); @@ -289,6 +292,18 @@ define([ */ pixelOffsetScaleByDistance : createPropertyDescriptor('pixelOffsetScaleByDistance'), + /** + * Gets or sets near and far scaling properties of a Label based on the label's distance from the camera. + * A label's scale will interpolate between the {@link NearFarScalar#nearValue} and + * {@link NearFarScalar#farValue} while the camera distance falls within the upper and lower bounds + * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. + * Outside of these ranges the label's scale remains clamped to the nearest bound. If undefined, + * scaleByDistance will be disabled. + * @memberof LabelGraphics.prototype + * @type {Property} + */ + scaleByDistance : createPropertyDescriptor('scaleByDistance'), + /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this label will be displayed. * @memberof LabelGraphics.prototype @@ -325,6 +340,7 @@ define([ result.pixelOffset = this.pixelOffset; result.translucencyByDistance = this.translucencyByDistance; result.pixelOffsetScaleByDistance = this.pixelOffsetScaleByDistance; + result.scaleByDistance = this.scaleByDistance; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; @@ -360,6 +376,7 @@ define([ this.pixelOffset = defaultValue(this.pixelOffset, source.pixelOffset); this.translucencyByDistance = defaultValue(this._translucencyByDistance, source.translucencyByDistance); this.pixelOffsetScaleByDistance = defaultValue(this._pixelOffsetScaleByDistance, source.pixelOffsetScaleByDistance); + this.scaleByDistance = defaultValue(this._scaleByDistance, source.scaleByDistance); this.distanceDisplayCondition = defaultValue(this.distanceDisplayCondition, source.distanceDisplayCondition); }; diff --git a/Source/DataSources/LabelVisualizer.js b/Source/DataSources/LabelVisualizer.js index 019e8527fc77..3ec353a13fc9 100644 --- a/Source/DataSources/LabelVisualizer.js +++ b/Source/DataSources/LabelVisualizer.js @@ -59,6 +59,7 @@ define([ var pixelOffset = new Cartesian2(); var translucencyByDistance = new NearFarScalar(); var pixelOffsetScaleByDistance = new NearFarScalar(); + var scaleByDistance = new NearFarScalar(); var distanceDisplayCondition = new DistanceDisplayCondition(); function EntityData(entity) { @@ -161,6 +162,7 @@ define([ label.verticalOrigin = Property.getValueOrDefault(labelGraphics._verticalOrigin, time, defaultVerticalOrigin); label.translucencyByDistance = Property.getValueOrUndefined(labelGraphics._translucencyByDistance, time, translucencyByDistance); label.pixelOffsetScaleByDistance = Property.getValueOrUndefined(labelGraphics._pixelOffsetScaleByDistance, time, pixelOffsetScaleByDistance); + label.scaleByDistance = Property.getValueOrUndefined(labelGraphics._scaleByDistance, time, scaleByDistance); label.distanceDisplayCondition = Property.getValueOrUndefined(labelGraphics._distanceDisplayCondition, time, distanceDisplayCondition); } return true; diff --git a/Source/DataSources/dynamicGeometryGetBoundingSphere.js b/Source/DataSources/dynamicGeometryGetBoundingSphere.js index 09e277d811c3..d79b6b3fd6a3 100644 --- a/Source/DataSources/dynamicGeometryGetBoundingSphere.js +++ b/Source/DataSources/dynamicGeometryGetBoundingSphere.js @@ -1,17 +1,13 @@ /*global define*/ define([ '../Core/BoundingSphere', - '../Core/defaultValue', '../Core/defined', '../Core/DeveloperError', - '../Core/Matrix4', './BoundingSphereState' ], function( BoundingSphere, - defaultValue, defined, DeveloperError, - Matrix4, BoundingSphereState) { 'use strict'; diff --git a/Source/Renderer/ShaderCache.js b/Source/Renderer/ShaderCache.js index d4ed90bd9f7d..d02724bdfedc 100644 --- a/Source/Renderer/ShaderCache.js +++ b/Source/Renderer/ShaderCache.js @@ -55,7 +55,7 @@ define([ * fragmentShaderSource : fs, * attributeLocations : attributeLocations * }); - * + * * @see ShaderCache#getShaderProgram */ ShaderCache.prototype.replaceShaderProgram = function(options) { @@ -103,7 +103,7 @@ define([ var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations); var cachedShader; - if (this._shaders[keyword]) { + if (defined(this._shaders[keyword])) { cachedShader = this._shaders[keyword]; // No longer want to release this if it was previously released. @@ -125,6 +125,7 @@ define([ cache : this, shaderProgram : shaderProgram, keyword : keyword, + derivedKeywords : [], count : 0 }; @@ -138,14 +139,86 @@ define([ return cachedShader.shaderProgram; }; + ShaderCache.prototype.getDerivedShaderProgram = function(shaderProgram, keyword) { + var cachedShader = shaderProgram._cachedShader; + var derivedKeyword = keyword + cachedShader.keyword; + var cachedDerivedShader = this._shaders[derivedKeyword]; + if (!defined(cachedDerivedShader)) { + return undefined; + } + + return cachedDerivedShader.shaderProgram; + }; + + ShaderCache.prototype.createDerivedShaderProgram = function(shaderProgram, keyword, options) { + var cachedShader = shaderProgram._cachedShader; + var derivedKeyword = keyword + cachedShader.keyword; + + var vertexShaderSource = options.vertexShaderSource; + var fragmentShaderSource = options.fragmentShaderSource; + var attributeLocations = options.attributeLocations; + + if (typeof vertexShaderSource === 'string') { + vertexShaderSource = new ShaderSource({ + sources : [vertexShaderSource] + }); + } + + if (typeof fragmentShaderSource === 'string') { + fragmentShaderSource = new ShaderSource({ + sources : [fragmentShaderSource] + }); + } + + var vertexShaderText = vertexShaderSource.createCombinedVertexShader(); + var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(); + + var context = this._context; + var derivedShaderProgram = new ShaderProgram({ + gl : context._gl, + logShaderCompilation : context.logShaderCompilation, + debugShaders : context.debugShaders, + vertexShaderSource : vertexShaderSource, + vertexShaderText : vertexShaderText, + fragmentShaderSource : fragmentShaderSource, + fragmentShaderText : fragmentShaderText, + attributeLocations : attributeLocations + }); + + var derivedCachedShader = { + cache : this, + shaderProgram : derivedShaderProgram, + keyword : derivedKeyword, + derivedKeywords : [], + count : 0 + }; + + cachedShader.derivedKeywords.push(keyword); + derivedShaderProgram._cachedShader = derivedCachedShader; + this._shaders[derivedKeyword] = derivedCachedShader; + return derivedShaderProgram; + }; + + function destroyShader(cache, cachedShader) { + var derivedKeywords = cachedShader.derivedKeywords; + var length = derivedKeywords.length; + for (var i = 0; i < length; ++i) { + var keyword = derivedKeywords[i] + cachedShader.keyword; + var derivedCachedShader = cache._shaders[keyword]; + destroyShader(cache, derivedCachedShader); + } + + delete cache._shaders[cachedShader.keyword]; + cachedShader.shaderProgram.finalDestroy(); + } + ShaderCache.prototype.destroyReleasedShaderPrograms = function() { var shadersToRelease = this._shadersToRelease; for ( var keyword in shadersToRelease) { if (shadersToRelease.hasOwnProperty(keyword)) { var cachedShader = shadersToRelease[keyword]; - delete this._shaders[cachedShader.keyword]; - cachedShader.shaderProgram.finalDestroy(); + destroyShader(this, cachedShader); --this._numberOfShaders; } } @@ -168,13 +241,11 @@ define([ ShaderCache.prototype.destroy = function() { var shaders = this._shaders; - - for ( var keyword in shaders) { + for (var keyword in shaders) { if (shaders.hasOwnProperty(keyword)) { shaders[keyword].shaderProgram.finalDestroy(); } } - return destroyObject(this); }; diff --git a/Source/Scene/BatchTable.js b/Source/Scene/BatchTable.js index 71b31e3d6ce0..29a0542ce97b 100644 --- a/Source/Scene/BatchTable.js +++ b/Source/Scene/BatchTable.js @@ -12,7 +12,6 @@ define([ '../Core/FeatureDetection', '../Core/Math', '../Core/PixelFormat', - '../Core/RuntimeError', '../Renderer/ContextLimits', '../Renderer/PixelDatatype', '../Renderer/Sampler', @@ -32,7 +31,6 @@ define([ FeatureDetection, CesiumMath, PixelFormat, - RuntimeError, ContextLimits, PixelDatatype, Sampler, diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index a245d2337a85..bf112209c694 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -85,6 +85,15 @@ define([ } }, + /** + * Part of the {@link Cesium3DTileContent} interface. + */ + pointsLength : { + get : function() { + return 0; + } + }, + /** * Part of the {@link Cesium3DTileContent} interface. */ @@ -307,12 +316,13 @@ define([ gltf : gltfView, cull : false, // The model is already culled by the 3D tiles releaseGltfJson : true, // Models are unique and will not benefit from caching so save memory - basePath : getBaseUri(this._url), + basePath : getBaseUri(this._url, true), modelMatrix : this._tile.computedTransform, upAxis : this._tileset._gltfUpAxis, shadows: this._tileset.shadows, debugWireframe: this._tileset.debugWireframe, incrementallyLoadTextures : false, + pickPrimitive : this._tileset, vertexShaderLoaded : getVertexShaderCallback(this), fragmentShaderLoaded : getFragmentShaderCallback(this), uniformMapLoaded : batchTable.getUniformMapCallback(), diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index dd2e7436aa94..5f6b9441f3f6 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -5,6 +5,7 @@ define([ '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', + '../Core/clone', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -22,8 +23,11 @@ define([ '../Core/RequestScheduler', '../Core/SphereOutlineGeometry', '../ThirdParty/Uri', + './Cesium3DTileChildrenVisibility', './Cesium3DTileContentFactory', './Cesium3DTileContentState', + './Cesium3DTileOptimizations', + './Cesium3DTileOptimizationHint', './Cesium3DTileRefine', './Empty3DTileContent', './PerInstanceColorAppearance', @@ -38,6 +42,7 @@ define([ Cartesian3, Color, ColorGeometryInstanceAttribute, + clone, defaultValue, defined, defineProperties, @@ -55,8 +60,11 @@ define([ RequestScheduler, SphereOutlineGeometry, Uri, + Cesium3DTileChildrenVisibility, Cesium3DTileContentFactory, Cesium3DTileContentState, + Cesium3DTileOptimizations, + Cesium3DTileOptimizationHint, Cesium3DTileRefine, Empty3DTileContent, PerInstanceColorAppearance, @@ -305,6 +313,15 @@ define([ * @private */ this.visibilityPlaneMask = true; + + /** + * Flag to mark children visibility + * + * @type {Cesium3DTileChildrenVisibility} + * + * @private + */ + this.childrenVisibility = Cesium3DTileChildrenVisibility.VISIBLE; /** * The last frame number the tile was selected in. @@ -329,6 +346,13 @@ define([ this._debugViewerRequestVolume = undefined; this._debugColor = new Color.fromRandom({ alpha : 1.0 }); this._debugColorizeTiles = false; + + /** + * Marks whether the tile's children bounds are fully contained within the tile's bounds + * + * @type {Cesium3DTileOptimizationHint} + */ + this._optimChildrenWithinParent = Cesium3DTileOptimizationHint.NOT_COMPUTED; } defineProperties(Cesium3DTile.prototype, { diff --git a/Source/Scene/Cesium3DTileChildrenVisibility.js b/Source/Scene/Cesium3DTileChildrenVisibility.js new file mode 100644 index 000000000000..5715f0375818 --- /dev/null +++ b/Source/Scene/Cesium3DTileChildrenVisibility.js @@ -0,0 +1,19 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * @private + */ + var Cesium3DTileChildrenVisibility = { + NONE : 0, // No children visbile + VISIBLE : 1, // At least one child visible + IN_REQUEST_VOLUME : 2, // At least one child in viewer request volume + VISIBLE_IN_REQUEST_VOLUME : 4 // At least one child both visible and in viewer request volume + }; + + return freezeObject(Cesium3DTileChildrenVisibility); +}); diff --git a/Source/Scene/Cesium3DTileContent.js b/Source/Scene/Cesium3DTileContent.js index e63d77075858..7db1cffd2afe 100644 --- a/Source/Scene/Cesium3DTileContent.js +++ b/Source/Scene/Cesium3DTileContent.js @@ -78,6 +78,20 @@ define([ } }, + /** + * Gets the number of points in the tile. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + pointsLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + /** * Gets the array of {@link Cesium3DTileContent} objects that represent the * content a composite's inner tiles, which can also be composites. diff --git a/Source/Scene/Cesium3DTileOptimizationHint.js b/Source/Scene/Cesium3DTileOptimizationHint.js new file mode 100644 index 000000000000..9665c56a7da0 --- /dev/null +++ b/Source/Scene/Cesium3DTileOptimizationHint.js @@ -0,0 +1,22 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * @private + * + * @exports Cesium3DTileOptimizationHint + * + * Hint defining optimization support for a 3D tile + */ + var Cesium3DTileOptimizationHint = { + NOT_COMPUTED: -1, + USE_OPTIMIZATION: 1, + SKIP_OPTIMIZATION: 0 + }; + + return freezeObject(Cesium3DTileOptimizationHint); +}); diff --git a/Source/Scene/Cesium3DTileOptimizations.js b/Source/Scene/Cesium3DTileOptimizations.js new file mode 100644 index 000000000000..89515676e088 --- /dev/null +++ b/Source/Scene/Cesium3DTileOptimizations.js @@ -0,0 +1,116 @@ +/*global define*/ +define([ + '../Core/Cartesian3', + '../Core/Check', + '../Core/defaultValue', + '../Core/freezeObject', + './Cesium3DTileOptimizationHint', + './TileBoundingRegion', + './TileOrientedBoundingBox' + ], function( + Cartesian3, + Check, + defaultValue, + freezeObject, + Cesium3DTileOptimizationHint, + TileBoundingRegion, + TileOrientedBoundingBox) { + 'use strict'; + + /** + * Utility functions for computing optimization hints for a {@link Cesium3DTileset}. + * + * @private + * + * @exports Cesium3DTileOptimizations + */ + var Cesium3DTileOptimizations = {}; + + var scratchAxis = new Cartesian3(); + + /** + * Checks and optionally evaluates support for the childrenWithinParent optimization. This is used to more tightly cull tilesets if children bounds are + * fully contained within the parent. Currently, support for the optimization only works for oriented bounding boxes, so both the child and parent tile + * must be either a {@link TileOrientedBoundingBox} or {@link TileBoundingRegion}. The purpose of this check is to prevent use of a culling optimization + * when the child bounds exceed those of the parent. If the child bounds are greater, it is more likely that the optimization will waste CPU cycles. Bounding + * spheres are not supported for the reason that the child bounds can very often be partially outside of the parent bounds. + * + * @param {Cesium3DTile} tile The tile to check. + * @param {Boolean} [evaluate=false] Whether to evaluate support if support for the childrenWithinParent optimization is Cesium3DTileOptimizationHints.NOT_COMPUTED + * @param {Boolean} [force=false] Whether to always evaluate support for the childrenWithinParent optimization + * @returns {Boolean} Whether the childrenWithinParent optimization is supported. + */ + Cesium3DTileOptimizations.checkChildrenWithinParent = function(tile, evaluate, force) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('tile', tile); + //>>includeEnd('debug'); + + evaluate = defaultValue(evaluate, false); + force = defaultValue(force, false); + + if ((tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint.NOT_COMPUTED && evaluate) || force) { + var children = tile.children; + var length = children.length; + + // Check if the parent has an oriented bounding box. + var boundingVolume = tile._boundingVolume; + if (boundingVolume instanceof TileOrientedBoundingBox || boundingVolume instanceof TileBoundingRegion) { + var orientedBoundingBox = boundingVolume._orientedBoundingBox; + tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint.USE_OPTIMIZATION; + for (var i = 0; i < length; ++i) { + var child = children[i]; + + // Check if the child has an oriented bounding box. + var childBoundingVolume = child._boundingVolume; + if (childBoundingVolume instanceof TileOrientedBoundingBox || childBoundingVolume instanceof TileBoundingRegion) { + var childOrientedBoundingBox = childBoundingVolume._orientedBoundingBox; + + // Compute the axis from the parent to the child. + var axis = Cartesian3.subtract(childOrientedBoundingBox.center, orientedBoundingBox.center, scratchAxis); + var axisLength = Cartesian3.magnitude(axis); + Cartesian3.divideByScalar(axis, axisLength, axis); + + // Project the bounding box of the parent onto the axis. Because the axis is a ray from the parent to the child, + // the projection parameterized along the ray will be (+/- proj1). + var proj1 = Math.abs(orientedBoundingBox.halfAxes[0] * axis.x) + + Math.abs(orientedBoundingBox.halfAxes[1] * axis.y) + + Math.abs(orientedBoundingBox.halfAxes[2] * axis.z) + + Math.abs(orientedBoundingBox.halfAxes[3] * axis.x) + + Math.abs(orientedBoundingBox.halfAxes[4] * axis.y) + + Math.abs(orientedBoundingBox.halfAxes[5] * axis.z) + + Math.abs(orientedBoundingBox.halfAxes[6] * axis.x) + + Math.abs(orientedBoundingBox.halfAxes[7] * axis.y) + + Math.abs(orientedBoundingBox.halfAxes[8] * axis.z); + + // Project the bounding box of the child onto the axis. Because the axis is a ray from the parent to the child, + // the projection parameterized along the ray will be (+/- proj2) + axis.length. + var proj2 = Math.abs(childOrientedBoundingBox.halfAxes[0] * axis.x) + + Math.abs(childOrientedBoundingBox.halfAxes[1] * axis.y) + + Math.abs(childOrientedBoundingBox.halfAxes[2] * axis.z) + + Math.abs(childOrientedBoundingBox.halfAxes[3] * axis.x) + + Math.abs(childOrientedBoundingBox.halfAxes[4] * axis.y) + + Math.abs(childOrientedBoundingBox.halfAxes[5] * axis.z) + + Math.abs(childOrientedBoundingBox.halfAxes[6] * axis.x) + + Math.abs(childOrientedBoundingBox.halfAxes[7] * axis.y) + + Math.abs(childOrientedBoundingBox.halfAxes[8] * axis.z); + + // If the child extends the parent's bounds, the optimization is not valid and we skip it. + if (proj1 <= proj2 + axisLength) { + tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint.SKIP_OPTIMIZATION; + break; + } + + } else { + // Do not support if the parent and child both do not have oriented bounding boxes. + tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint.SKIP_OPTIMIZATION; + break; + } + } + } + } + + return tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint.USE_OPTIMIZATION; + }; + + return Cesium3DTileOptimizations; +}); diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index d352f5d15191..c6378d9cd5a3 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -3,6 +3,7 @@ define([ '../Core/Cartesian3', '../Core/Cartographic', '../Core/Color', + '../Core/clone', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -26,11 +27,15 @@ define([ '../ThirdParty/when', './Axis', './Cesium3DTile', + './Cesium3DTileChildrenVisibility', './Cesium3DTileColorBlendMode', + './Cesium3DTileOptimizations', + './Cesium3DTileOptimizationHint', './Cesium3DTileRefine', './Cesium3DTileStyleEngine', './CullingVolume', './DebugCameraPrimitive', + './LabelCollection', './SceneMode', './ShadowMode', './TileBoundingRegion', @@ -40,6 +45,7 @@ define([ Cartesian3, Cartographic, Color, + clone, defaultValue, defined, defineProperties, @@ -63,11 +69,15 @@ define([ when, Axis, Cesium3DTile, + Cesium3DTileChildrenVisibility, Cesium3DTileColorBlendMode, + Cesium3DTileOptimizations, + Cesium3DTileOptimizationHint, Cesium3DTileRefine, Cesium3DTileStyleEngine, CullingVolume, DebugCameraPrimitive, + LabelCollection, SceneMode, ShadowMode, TileBoundingRegion, @@ -88,6 +98,7 @@ define([ * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the tileset's root tile. * @param {Number} [options.maximumScreenSpaceError=16] The maximum screen-space error used to drive level-of-detail refinement. * @param {Boolean} [options.refineToVisible=false] Whether replacement refinement should refine when all visible children are ready. An experimental optimization. + * @param {Boolean} [options.cullWithChildrenBounds=true] Whether to cull tiles using the union of their children bounding volumes. * @param {Boolean} [options.dynamicScreenSpaceError=false] Reduce the screen space error for tiles that are further away from the camera. * @param {Number} [options.dynamicScreenSpaceErrorDensity=0.00278] Density used to adjust the dynamic screen space error, similar to fog density. * @param {Number} [options.dynamicScreenSpaceErrorFactor=4.0] A factor used to increase the computed dynamic screen space error. @@ -98,6 +109,7 @@ define([ * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile. * @param {Boolean} [options.debugShowContentBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile's content. * @param {Boolean} [options.debugShowViewerRequestVolume=false] For debugging only. When true, renders the viewer request volume for each tile. + * @param {Boolean} [options.debugShowGeometricError=false] For debugging only. When true, draws labels to indicate the geometric error of each tile * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source. * * @example @@ -123,7 +135,7 @@ define([ if (getExtensionFromUri(url) === 'json') { tilesetUrl = url; - baseUrl = getBaseUri(url); + baseUrl = getBaseUri(url, true); } else if (isDataUri(url)) { tilesetUrl = url; baseUrl = ''; @@ -156,6 +168,8 @@ define([ this._refineToVisible = defaultValue(options.refineToVisible, false); + this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true); + /** * Whether the tileset should should refine based on a dynamic screen space error. Tiles that are further * away will be rendered with lower detail than closer tiles. This improves performance by rendering fewer @@ -266,9 +280,16 @@ define([ numberProcessing : 0, numberContentReady : 0, // Number of tiles with content loaded, does not include empty tiles numberTotal : 0, // Number of tiles in tileset.json (and other tileset.json files as they are loaded) + // Features stats + numberOfFeaturesSelected : 0, // number of features rendered + numberOfFeaturesLoaded : 0, // number of features in memory + numberOfPointsSelected: 0, + numberOfPointsLoaded: 0, // Styling stats numberOfTilesStyled : 0, numberOfFeaturesStyled : 0, + // Optimization stats + numberOfTilesCulledWithChildrenUnion : 0, lastColor : new Cesium3DTilesetStatistics(), lastPick : new Cesium3DTilesetStatistics() @@ -348,6 +369,18 @@ define([ */ this.debugShowViewerRequestVolume = defaultValue(options.debugShowViewerRequestVolume, false); + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, draws labels to indicate the geometric error of each tile. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowGeometricError = defaultValue(options.debugShowGeometricError, false); + this._geometricErrorLabels = undefined; + /** * The event fired to indicate progress of loading new tiles. This event is fired when a new tile * is requested, when a requested tile is finished downloading, and when a downloaded tile has been @@ -468,8 +501,13 @@ define([ this.numberProcessing = 0; this.numberContentReady = 0; this.numberTotal = 0; + this.numberOfFeaturesSelected = 0; + this.numberOfFeaturesLoaded = 0; + this.numberOfPointsSelected = 0; + this.numberOfPointsLoaded = 0; this.numberOfTilesStyled = 0; this.numberOfFeaturesStyled = 0; + this.numberOfTilesCulledWithChildrenUnion = 0; } defineProperties(Cesium3DTileset.prototype, { @@ -860,13 +898,16 @@ define([ var stats = that._statistics; // Append the version to the baseUrl - var versionQuery = '?v=' + defaultValue(tilesetJson.asset.tilesetVersion, '0.0'); - that._baseUrl = joinUrls(that._baseUrl, versionQuery); + var hasVersionQuery = /[?&]v=/.test(tilesetUrl); + if (!hasVersionQuery) { + var versionQuery = '?v=' + defaultValue(tilesetJson.asset.tilesetVersion, '0.0'); + that._baseUrl = joinUrls(that._baseUrl, versionQuery); + tilesetUrl = joinUrls(tilesetUrl, versionQuery, false); + } // A tileset.json referenced from a tile may exist in a different directory than the root tileset. // Get the baseUrl relative to the external tileset. var baseUrl = getBaseUri(tilesetUrl, true); - baseUrl = joinUrls(baseUrl, versionQuery); var rootTile = new Cesium3DTile(that, baseUrl, tilesetJson.root, parentTile); var refiningTiles = []; @@ -913,6 +954,7 @@ define([ } } } + Cesium3DTileOptimizations.checkChildrenWithinParent(tile3D, true); if (tile3D.hasContent && hasEmptyChild && (tile3D.refine === Cesium3DTileRefine.REPLACE)) { // Tiles that use replacement refinement and have empty child tiles need to keep track of // descendants with content in order to refine correctly. @@ -1159,6 +1201,39 @@ define([ var scratchStack = []; var scratchRefiningTiles = []; + function computeChildrenVisibility(tile, frameState, checkViewerRequestVolume) { + var flag = Cesium3DTileChildrenVisibility.NONE; + var children = tile.children; + var childrenLength = children.length; + var visibilityPlaneMask = tile.visibilityPlaneMask; + for (var k = 0; k < childrenLength; ++k) { + var child = children[k]; + + var visibilityMask = child.visibility(frameState, visibilityPlaneMask); + + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE; + } + + if (checkViewerRequestVolume) { + if (!child.insideViewerRequestVolume(frameState)) { + visibilityMask = CullingVolume.MASK_OUTSIDE; + } else { + flag |= Cesium3DTileChildrenVisibility.IN_REQUEST_VOLUME; + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME; + } + } + } + + child.visibilityPlaneMask = visibilityMask; + } + + tile.childrenVisibility = flag; + + return flag; + } + function selectTiles(tileset, frameState, outOfCore) { if (tileset.debugFreezeFrame) { return; @@ -1291,10 +1366,30 @@ define([ // is not sufficient, its children (or ancestors) are // rendered instead - if ((sse <= maximumScreenSpaceError) || (childrenLength === 0)) { - // This tile meets the SSE so add its commands. + // This optimization may cause issues if the parent's content exceeds the bounds of the childrens` content. + // In these case nothing will be selected to fill the empty space. This is possible if occlusion-preserving + // decimation is not used, but it is arguably better to cull in this way because in the cases where we cull + // when the parent content is visible, if the object were to be drawn at full resolution, the geometry would + // not be visible. + var useChildrenBoundUnion = tileset._cullWithChildrenBounds && t._optimChildrenWithinParent === Cesium3DTileOptimizationHint.USE_OPTIMIZATION; + + var childrenVisibility; + + if (childrenLength === 0) { // Select tile if it's a leaf (childrenLength === 0) selectTile(tileset, t, fullyVisible, frameState); + } else if (sse <= maximumScreenSpaceError) { + // This tile meets the SSE so add its commands. + if (useChildrenBoundUnion) { + childrenVisibility = computeChildrenVisibility(t, frameState, false); + if (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE) { + selectTile(tileset, t, fullyVisible, frameState); + } else { + ++stats.numberOfTilesCulledWithChildrenUnion; + } + } else { + selectTile(tileset, t, fullyVisible, frameState); + } } else if (!tileset._refineToVisible) { // Tile does not meet SSE. // Refine when all children (visible or not) are loaded. @@ -1316,37 +1411,38 @@ define([ } if (!allChildrenLoaded) { - // Tile does not meet SSE. Add its commands since it is the best we have and request its children. - selectTile(tileset, t, fullyVisible, frameState); + if (useChildrenBoundUnion) { + childrenVisibility = computeChildrenVisibility(t, frameState, false); + } + if (!useChildrenBoundUnion || (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE)) { + // Tile does not meet SSE. Add its commands since it is the best we have and request its children. + selectTile(tileset, t, fullyVisible, frameState); - if (outOfCore) { - for (k = 0; (k < childrenLength) && t.canRequestContent(); ++k) { - child = children[k]; - // PERFORMANCE_IDEA: we could spin a bit less CPU here by keeping separate lists for unloaded/ready children. - if (child.contentUnloaded) { - requestContent(tileset, child, outOfCore); - } else { - // Touch loaded child even though it is not selected this frame since - // we want to keep it in the cache for when all children are loaded - // and this tile can refine to them. - touch(tileset, child, outOfCore); + if (outOfCore) { + for (k = 0; k < childrenLength; ++k) { + child = children[k]; + // PERFORMANCE_IDEA: we could spin a bit less CPU here by keeping separate lists for unloaded/ready children. + if (child.contentUnloaded) { + requestContent(tileset, child, outOfCore); + } else { + // Touch loaded child even though it is not selected this frame since + // we want to keep it in the cache for when all children are loaded + // and this tile can refine to them. + touch(tileset, child, outOfCore); + } } } + } else if (useChildrenBoundUnion) { + ++stats.numberOfTilesCulledWithChildrenUnion; } } else { // Tile does not meet SSE and its children are loaded. Refine to them in front-to-back order. - var anyChildrenVisible = false; + childrenVisibility = computeChildrenVisibility(t, frameState, true); for (k = 0; k < childrenLength; ++k) { child = children[k]; - if (child.insideViewerRequestVolume(frameState)) { - child.visibilityPlaneMask = child.visibility(frameState, visibilityPlaneMask); - } else { - child.visibilityPlaneMask = CullingVolume.MASK_OUTSIDE; - } if (isVisible(child.visibilityPlaneMask)) { stack.push(child); - anyChildrenVisible = true; } else { // Touch the child tile even if it is not visible. Since replacement refinement // requires all child tiles to be loaded to refine to them, we want to keep it in the cache. @@ -1354,15 +1450,17 @@ define([ } } - if (anyChildrenVisible) { + if (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) { t.replaced = true; if (defined(t.descendantsWithContent)) { scratchRefiningTiles.push(t); } - } else { + } else if (!useChildrenBoundUnion || (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE)) { // Even though the children are all loaded they may not be visible if the camera // is not inside their request volumes. selectTile(tileset, t, fullyVisible, frameState); + } else if (useChildrenBoundUnion) { + ++stats.numberOfTilesCulledWithChildrenUnion; } } } else { @@ -1371,16 +1469,13 @@ define([ // Get visibility for all children. Check if any visible children are not loaded. // PERFORMANCE_IDEA: exploit temporal coherence to avoid checking visibility every frame + updateTransforms(children, t.computedTransform); + childrenVisibility = computeChildrenVisibility(t, frameState, true); + var allVisibleChildrenLoaded = true; var someVisibleChildrenLoaded = false; for (k = 0; k < childrenLength; ++k) { child = children[k]; - child.updateTransform(t.computedTransform); - if (child.insideViewerRequestVolume(frameState)) { - child.visibilityPlaneMask = child.visibility(frameState, visibilityPlaneMask); - } else { - child.visibilityPlaneMask = CullingVolume.MASK_OUTSIDE; - } if (isVisible(child.visibilityPlaneMask)) { if (child.contentReady) { someVisibleChildrenLoaded = true; @@ -1390,6 +1485,13 @@ define([ } } + if (useChildrenBoundUnion && childrenVisibility === Cesium3DTileChildrenVisibility.NONE) { + if (allVisibleChildrenLoaded && !someVisibleChildrenLoaded) { + ++stats.numberOfTilesCulledWithChildrenUnion; + } + continue; + } + if (allVisibleChildrenLoaded && !someVisibleChildrenLoaded) { // No children are visible, select this tile selectTile(tileset, t, fullyVisible, frameState); @@ -1466,6 +1568,7 @@ define([ for (j = 0; j < descendantsLength; ++j) { descendant = refiningTile.descendantsWithContent[j]; if (!descendant.selected && !descendant.replaced && + ((descendant.childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE) || descendant.children.length === 0) && (frameState.cullingVolume.computeVisibility(descendant.contentBoundingVolume) !== Intersect.OUTSIDE)) { refinable = false; break; @@ -1503,6 +1606,7 @@ define([ // RESEARCH_IDEA: ability to unload tiles (without content) for an // external tileset when all the tiles are unloaded. ++tileset._statistics.numberContentReady; + incrementPointAndFeatureLoadCounts(tileset, tile.content); tile.replacementNode = tileset._replacementList.add(tile); } } else { @@ -1533,6 +1637,9 @@ define([ stats.numberOfAttemptedRequests = 0; stats.numberOfTilesStyled = 0; stats.numberOfFeaturesStyled = 0; + stats.numberOfTilesCulledWithChildrenUnion = 0; + stats.numberOfFeaturesSelected = 0; + stats.numberOfPointsSelected = 0; } function updateLastStats(tileset, isPick) { @@ -1547,8 +1654,79 @@ define([ last.numberProcessing = stats.numberProcessing; last.numberContentReady = stats.numberContentReady; last.numberTotal = stats.numberTotal; + last.numberOfFeaturesSelected = stats.numberOfFeaturesSelected; + last.numberOfFeaturesLoaded = stats.numberOfFeaturesLoaded; + last.numberOfPointsSelected = stats.numberOfPointsSelected; + last.numberOfPointsLoaded = stats.numberOfPointsLoaded; last.numberOfTilesStyled = stats.numberOfTilesStyled; last.numberOfFeaturesStyled = stats.numberOfFeaturesStyled; + last.numberOfTilesCulledWithChildrenUnion = stats.numberOfTilesCulledWithChildrenUnion; + } + + function updatePointAndFeatureCounts(tileset, content, decrement, load) { + var stats = tileset._statistics; + var contents = content.innerContents; + var pointsLength = content.pointsLength; + var featuresLength = content.featuresLength; + + if (load) { + stats.numberOfFeaturesLoaded += decrement ? -featuresLength : featuresLength; + stats.numberOfPointsLoaded += decrement ? -pointsLength : pointsLength; + } else { + stats.numberOfFeaturesSelected += decrement ? -featuresLength : featuresLength; + stats.numberOfPointsSelected += decrement ? -pointsLength : pointsLength; + } + + if (defined(contents)) { + var length = contents.length; + for (var i = 0; i < length; ++i) { + updatePointAndFeatureCounts(tileset, contents[i], decrement, load); + } + } + } + + function incrementPointAndFeatureSelectionCounts(tileset, content) { + updatePointAndFeatureCounts(tileset, content, false, false); + } + + function incrementPointAndFeatureLoadCounts(tileset, content) { + updatePointAndFeatureCounts(tileset, content, false, true); + } + + function decrementPointAndFeatureLoadCounts(tileset, content) { + updatePointAndFeatureCounts(tileset, content, true, true); + } + + var scratchCartesian = new Cartesian3(); + + function updateGeometricErrorLabels(tileset, frameState) { + var selectedTiles = tileset._selectedTiles; + var length = selectedTiles.length; + tileset._geometricErrorLabels.removeAll(); + for (var i = 0; i < length; ++i) { + var tile = selectedTiles[i]; + if (tile.selected) { + var boundingVolume = tile._boundingVolume.boundingVolume; + var halfAxes = boundingVolume.halfAxes; + var radius = boundingVolume.radius; + + var position = Cartesian3.clone(boundingVolume.center, scratchCartesian); + if (defined(halfAxes)) { + position.x += 0.75 * (halfAxes[0] + halfAxes[3] + halfAxes[6]); + position.y += 0.75 * (halfAxes[1] + halfAxes[4] + halfAxes[7]); + position.z += 0.75 * (halfAxes[2] + halfAxes[5] + halfAxes[8]); + } else if (defined(radius)) { + var normal = Cartesian3.normalize(boundingVolume.center, scratchCartesian); + normal = Cartesian3.multiplyByScalar(normal, 0.75 * radius, scratchCartesian); + position = Cartesian3.add(normal, boundingVolume.center, scratchCartesian); + } + tileset._geometricErrorLabels.add({ + text: tile.geometricError.toString(), + position: position + }); + } + } + tileset._geometricErrorLabels.update(frameState); } function updateTiles(tileset, frameState) { @@ -1566,11 +1744,21 @@ define([ // makes changes that update needs to apply to WebGL resources tileVisible.raiseEvent(tile); tile.update(tileset, frameState); + incrementPointAndFeatureSelectionCounts(tileset, tile.content); } } // Number of commands added by each update above tileset._statistics.numberOfCommands = (commandList.length - numberOfInitialCommands); + + if (tileset.debugShowGeometricError) { + if (!defined(tileset._geometricErrorLabels)) { + tileset._geometricErrorLabels = new LabelCollection(); + } + updateGeometricErrorLabels(tileset, frameState); + } else { + tileset._geometricErrorLabels = tileset._geometricErrorLabels && tileset._geometricErrorLabels.destroy(); + } } function unloadTiles(tileset, frameState) { @@ -1591,6 +1779,7 @@ define([ while ((node !== sentinel) && ((replacementList.length > maximumNumberOfLoadedTiles) || trimTiles)) { var tile = node.item; + decrementPointAndFeatureLoadCounts(tileset, tile.content); tileUnload.raiseEvent(tile); tile.unloadContent(); @@ -1747,7 +1936,6 @@ define([ } this._root = undefined; - this._debugCameraFrustum = this._debugCameraFrustum && this._debugCameraFrustum.destroy(); return destroyObject(this); }; diff --git a/Source/Scene/Composite3DTileContent.js b/Source/Scene/Composite3DTileContent.js index 6dd61495b387..0d16968063f4 100644 --- a/Source/Scene/Composite3DTileContent.js +++ b/Source/Scene/Composite3DTileContent.js @@ -89,6 +89,16 @@ define([ } }, + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call pointsLength for a tile in the composite. + */ + pointsLength : { + get : function() { + return 0; + } + }, + /** * Gets the array of {@link Cesium3DTileContent} objects that represent the * content of the composite's inner tiles, which can also be composites. diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index 82644c105d9e..e362ccd26b35 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -133,7 +133,14 @@ define([ * @type {Boolean} * @default false */ - pick : false + pick : false, + + /** + * true if the primitive should update for a depth only pass, false otherwise. + * @type {Boolean} + * @default false + */ + depth : false }; /** diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index bb92954ec673..f947b21caaf5 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -14,7 +14,6 @@ define([ '../Core/Event', '../Core/IntersectionTests', '../Core/loadImage', - '../Core/Math', '../Core/Ray', '../Core/Rectangle', '../Renderer/ShaderSource', @@ -44,7 +43,6 @@ define([ Event, IntersectionTests, loadImage, - CesiumMath, Ray, Rectangle, ShaderSource, diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index 4818e1e11bc0..e3d239ef9320 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -5,8 +5,6 @@ define([ '../Core/Cartesian2', '../Core/Cartesian3', '../Core/Cartographic', - '../Core/clone', - '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defaultValue', '../Core/defined', @@ -41,8 +39,6 @@ define([ Cartesian2, Cartesian3, Cartographic, - clone, - Color, ColorGeometryInstanceAttribute, defaultValue, defined, diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js index 2c42fe564875..d3542eabae66 100644 --- a/Source/Scene/Instanced3DModel3DTileContent.js +++ b/Source/Scene/Instanced3DModel3DTileContent.js @@ -101,7 +101,20 @@ define([ */ featuresLength : { get : function() { - return this._modelInstanceCollection.length; + if (defined(this._modelInstanceCollection)) { + return this._modelInstanceCollection.length; + } else { + return 0; + } + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. + */ + pointsLength : { + get : function() { + return 0; } }, @@ -311,11 +324,10 @@ define([ if (gltfFormat === 0) { var gltfUrl = getStringFromTypedArray(gltfView); - var baseUrl = defaultValue(this._tileset.baseUrl, ''); - collectionOptions.url = joinUrls(baseUrl, gltfUrl); + collectionOptions.url = joinUrls(getBaseUri(this._url, true), gltfUrl); } else { collectionOptions.gltf = gltfView; - collectionOptions.basePath = getBaseUri(this._url); + collectionOptions.basePath = getBaseUri(this._url, true); } var eastNorthUp = featureTable.getGlobalProperty('EAST_NORTH_UP'); diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 67502ac5d74c..3fe84a1b5057 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -75,6 +75,9 @@ define([ if (defined(options.pixelOffsetScaleByDistance) && options.pixelOffsetScaleByDistance.far <= options.pixelOffsetScaleByDistance.near) { throw new DeveloperError('pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near.'); } + if (defined(options.scaleByDistance) && options.scaleByDistance.far <= options.scaleByDistance.near) { + throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.'); + } if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) { throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near'); } @@ -99,6 +102,7 @@ define([ this._id = options.id; this._translucencyByDistance = options.translucencyByDistance; this._pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance; + this._scaleByDistance = options.scaleByDistance; this._heightReference = defaultValue(options.heightReference, HeightReference.NONE); this._distanceDisplayCondition = options.distanceDisplayCondition; @@ -613,6 +617,58 @@ define([ } }, + /** + * Gets or sets near and far scaling properties of a Label based on the label's distance from the camera. + * A label's scale will interpolate between the {@link NearFarScalar#nearValue} and + * {@link NearFarScalar#farValue} while the camera distance falls within the upper and lower bounds + * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. + * Outside of these ranges the label's scale remains clamped to the nearest bound. If undefined, + * scaleByDistance will be disabled. + * @memberof Label.prototype + * @type {NearFarScalar} + * + * @example + * // Example 1. + * // Set a label's scaleByDistance to scale by 1.5 when the + * // camera is 1500 meters from the label and disappear as + * // the camera distance approaches 8.0e6 meters. + * label.scaleByDistance = new Cesium.NearFarScalar(1.5e2, 1.5, 8.0e6, 0.0); + * + * @example + * // Example 2. + * // disable scaling by distance + * label.scaleByDistance = undefined; + */ + scaleByDistance : { + get : function() { + return this._scaleByDistance; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + if (defined(value) && value.far <= value.near) { + throw new DeveloperError('far distance must be greater than near distance.'); + } + //>>includeEnd('debug'); + + var scaleByDistance = this._scaleByDistance; + if (!NearFarScalar.equals(scaleByDistance, value)) { + this._scaleByDistance = NearFarScalar.clone(value, scaleByDistance); + + var glyphs = this._glyphs; + for (var i = 0, len = glyphs.length; i < len; i++) { + var glyph = glyphs[i]; + if (defined(glyph.billboard)) { + glyph.billboard.scaleByDistance = value; + } + } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.scaleByDistance = value; + } + } + } + }, + /** * Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. Eye coordinates is a left-handed * coordinate system, where x points towards the viewer's right, y points up, and @@ -1066,6 +1122,7 @@ define([ Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && NearFarScalar.equals(this._pixelOffsetScaleByDistance, other._pixelOffsetScaleByDistance) && + NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition) && this._id === other._id; }; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 1d4665ef4a32..9f3763f94586 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -2,7 +2,6 @@ define([ '../Core/BoundingRectangle', '../Core/Cartesian2', - '../Core/Color', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -20,7 +19,6 @@ define([ ], function( BoundingRectangle, Cartesian2, - Color, defaultValue, defined, defineProperties, @@ -171,6 +169,7 @@ define([ backgroundBillboard.id = label._id; backgroundBillboard.translucencyByDistance = label._translucencyByDistance; backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; + backgroundBillboard.scaleByDistance = label._scaleByDistance; backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; } @@ -263,6 +262,7 @@ define([ billboard.image = id; billboard.translucencyByDistance = label._translucencyByDistance; billboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; + billboard.scaleByDistance = label._scaleByDistance; billboard.distanceDisplayCondition = label._distanceDisplayCondition; } } diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 263a151e827d..95b696603681 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1549,13 +1549,15 @@ define([ ++model._loadResources.pendingTextureLoads; var imagePath = joinUrls(model._baseUri, gltfImage.uri); + var promise; if (ktxRegex.test(imagePath)) { - loadKTX(imagePath).then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = RequestScheduler.request(imagePath, loadKTX, undefined, model._requestType); } else if (crnRegex.test(imagePath)) { - loadCRN(imagePath).then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = RequestScheduler.request(imagePath, loadCRN, undefined, model._requestType); } else { - loadImage(imagePath).then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = RequestScheduler.request(imagePath, loadImage, undefined, model._requestType); } + promise.then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); } } } @@ -1848,7 +1850,7 @@ define([ // the normal attribute. for (i = 1; i < length; ++i) { var attribute = attributes[i]; - if (/position/i.test(attribute)) { + if (/pos/i.test(attribute)) { attributes[i] = attributes[0]; attributes[0] = attribute; break; @@ -2332,6 +2334,7 @@ define([ // Retrieve the compiled shader program to assign index values to attributes var attributeLocations = {}; + var location; var index; var technique = techniques[materials[primitive.material].technique]; var parameters = technique.parameters; @@ -2340,23 +2343,27 @@ define([ var programVertexAttributes = program.vertexAttributes; var programAttributeLocations = program._attributeLocations; - // Note: WebGL shader compiler may have optimized and removed some attributes from programAttributeLocations - for (var location in programAttributeLocations){ - if (programAttributeLocations.hasOwnProperty(location)) { + // Note: WebGL shader compiler may have optimized and removed some attributes from programVertexAttributes + for (location in programVertexAttributes){ + if (programVertexAttributes.hasOwnProperty(location)) { var attribute = attributes[location]; + index = programVertexAttributes[location].index; if (defined(attribute)) { - var vertexAttribute = programVertexAttributes[location]; - if (defined(vertexAttribute)) { - index = vertexAttribute.index; - var parameter = parameters[attribute]; - attributeLocations[parameter.semantic] = index; - } - } else { - // Pre-created attributes. - // Some pre-created attributes, like per-instance pickIds, may be compiled out of the draw program - // but should be included in the list of attribute locations for the pick program. - // This is safe to do since programVertexAttributes and programAttributeLocations are equivalent except - // that programVertexAttributes optimizes out unused attributes. + var parameter = parameters[attribute]; + attributeLocations[parameter.semantic] = index; + } + } + } + + // Always add pre-created attributes. + // Some pre-created attributes, like per-instance pickIds, may be compiled out of the draw program + // but should be included in the list of attribute locations for the pick program. + // This is safe to do since programVertexAttributes and programAttributeLocations are equivalent except + // that programVertexAttributes optimizes out unused attributes. + var precreatedAttributes = model._precreatedAttributes; + if (defined(precreatedAttributes)) { + for (location in precreatedAttributes) { + if (precreatedAttributes.hasOwnProperty(location)) { index = programAttributeLocations[location]; attributeLocations[location] = index; } diff --git a/Source/Scene/ModelInstanceCollection.js b/Source/Scene/ModelInstanceCollection.js index c11760ee9fc0..5935432bc27d 100644 --- a/Source/Scene/ModelInstanceCollection.js +++ b/Source/Scene/ModelInstanceCollection.js @@ -789,8 +789,8 @@ define([ for (var i = 0; i < commandsLength; ++i) { for (var j = 0; j < instancesLength; ++j) { var drawCommand = DrawCommand.shallowClone(drawCommands[i]); - drawCommand.modelMatrix = new Matrix4(); // Updated in updateNonInstancedCommands - drawCommand.boundingVolume = new BoundingSphere(); // Updated in updateNonInstancedCommands + drawCommand.modelMatrix = new Matrix4(); // Updated in updateCommandsNonInstanced + drawCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced drawCommand.cull = cull; drawCommand.uniformMap = clone(drawCommand.uniformMap); if (usesBatchTable) { @@ -800,8 +800,8 @@ define([ if (allowPicking) { var pickCommand = DrawCommand.shallowClone(pickCommands[i]); - pickCommand.modelMatrix = new Matrix4(); // Updated in updateNonInstancedCommands - pickCommand.boundingVolume = new BoundingSphere(); // Updated in updateNonInstancedCommands + pickCommand.modelMatrix = new Matrix4(); // Updated in updateCommandsNonInstanced + pickCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced pickCommand.cull = cull; pickCommand.uniformMap = clone(pickCommand.uniformMap); if (usesBatchTable) { @@ -919,8 +919,9 @@ define([ this._state = LoadState.LOADED; this._ready = true; - // Expand bounding volume to fit the radius of the loaded model - this._boundingSphere.radius += model.boundingSphere.radius; + // Expand bounding volume to fit the radius of the loaded model including the model's offset from the center + var modelRadius = model.boundingSphere.radius + Cartesian3.magnitude(model.boundingSphere.center); + this._boundingSphere.radius += modelRadius; var modelCommands = getModelCommands(model); this._modelCommands = modelCommands.draw; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index eba923512306..c74b7e945611 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -11,7 +11,6 @@ define([ '../Renderer/Framebuffer', '../Renderer/PixelDatatype', '../Renderer/RenderState', - '../Renderer/ShaderProgram', '../Renderer/ShaderSource', '../Renderer/Texture', '../Shaders/AdjustTranslucentFS', @@ -30,7 +29,6 @@ define([ Framebuffer, PixelDatatype, RenderState, - ShaderProgram, ShaderSource, Texture, AdjustTranslucentFS, @@ -82,8 +80,6 @@ define([ this._translucentRenderStateCache = {}; this._alphaRenderStateCache = {}; - this._translucentShaderCache = {}; - this._alphaShaderCache = {}; this._compositeCommand = undefined; this._adjustTranslucentCommand = undefined; @@ -411,9 +407,8 @@ define([ ' float ai = czm_gl_FragColor.a;\n' + ' gl_FragColor = vec4(ai);\n'; - function getTranslucentShaderProgram(context, shaderProgram, cache, source) { - var id = shaderProgram.id; - var shader = cache[id]; + function getTranslucentShaderProgram(context, shaderProgram, keyword, source) { + var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, keyword); if (!defined(shader)) { var attributeLocations = shaderProgram._attributeLocations; @@ -446,29 +441,26 @@ define([ source + '}\n'); - shader = ShaderProgram.fromCache({ - context : context, + shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, { vertexShaderSource : shaderProgram.vertexShaderSource, fragmentShaderSource : fs, attributeLocations : attributeLocations }); - - cache[id] = shader; } return shader; } - function getTranslucentMRTShaderProgram(oit, context, shaderProgram) { - return getTranslucentShaderProgram(context, shaderProgram, oit._translucentShaderCache, mrtShaderSource); + function getTranslucentMRTShaderProgram(context, shaderProgram) { + return getTranslucentShaderProgram(context, shaderProgram, 'translucentMRT', mrtShaderSource); } - function getTranslucentColorShaderProgram(oit, context, shaderProgram) { - return getTranslucentShaderProgram(context, shaderProgram, oit._translucentShaderCache, colorShaderSource); + function getTranslucentColorShaderProgram(context, shaderProgram) { + return getTranslucentShaderProgram(context, shaderProgram, 'translucentMultipass', colorShaderSource); } - function getTranslucentAlphaShaderProgram(oit, context, shaderProgram) { - return getTranslucentShaderProgram(context, shaderProgram, oit._alphaShaderCache, alphaShaderSource); + function getTranslucentAlphaShaderProgram(context, shaderProgram) { + return getTranslucentShaderProgram(context, shaderProgram, 'alphaMultipass', alphaShaderSource); } OIT.prototype.createDerivedCommands = function(command, context, result) { @@ -487,7 +479,7 @@ define([ result.translucentCommand = DrawCommand.shallowClone(command, result.translucentCommand); if (!defined(translucentShader) || result.shaderProgramId !== command.shaderProgram.id) { - result.translucentCommand.shaderProgram = getTranslucentMRTShaderProgram(this, context, command.shaderProgram); + result.translucentCommand.shaderProgram = getTranslucentMRTShaderProgram(context, command.shaderProgram); result.translucentCommand.renderState = getTranslucentMRTRenderState(this, context, command.renderState); result.shaderProgramId = command.shaderProgram.id; } else { @@ -510,9 +502,9 @@ define([ result.alphaCommand = DrawCommand.shallowClone(command, result.alphaCommand); if (!defined(colorShader) || result.shaderProgramId !== command.shaderProgram.id) { - result.translucentCommand.shaderProgram = getTranslucentColorShaderProgram(this, context, command.shaderProgram); + result.translucentCommand.shaderProgram = getTranslucentColorShaderProgram(context, command.shaderProgram); result.translucentCommand.renderState = getTranslucentColorRenderState(this, context, command.renderState); - result.alphaCommand.shaderProgram = getTranslucentAlphaShaderProgram(this, context, command.shaderProgram); + result.alphaCommand.shaderProgram = getTranslucentAlphaShaderProgram(context, command.shaderProgram); result.alphaCommand.renderState = getTranslucentAlphaRenderState(this, context, command.renderState); result.shaderProgramId = command.shaderProgram.id; } else { @@ -639,23 +631,6 @@ define([ this._adjustAlphaCommand.shaderProgram = this._adjustAlphaCommand.shaderProgram && this._adjustAlphaCommand.shaderProgram.destroy(); } - var name; - var cache = this._translucentShaderCache; - for (name in cache) { - if (cache.hasOwnProperty(name) && defined(cache[name])) { - cache[name].destroy(); - } - } - this._translucentShaderCache = {}; - - cache = this._alphaShaderCache; - for (name in cache) { - if (cache.hasOwnProperty(name) && defined(cache[name])) { - cache[name].destroy(); - } - } - this._alphaShaderCache = {}; - return destroyObject(this); }; diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index 91749ec79b18..5b47a0a09a2d 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -139,6 +139,7 @@ define([ this._contentReadyToProcessPromise = when.defer(); this._readyPromise = when.defer(); this._features = undefined; + this._pointsLength = 0; } defineProperties(PointCloud3DTileContent.prototype, { @@ -154,6 +155,15 @@ define([ } }, + /** + * Part of the {@link Cesium3DTileContent} interface. + */ + pointsLength : { + get : function() { + return this._pointsLength; + } + }, + /** * Part of the {@link Cesium3DTileContent} interface. */ @@ -459,13 +469,13 @@ define([ } this._parsedContent = { - pointsLength : pointsLength, positions : positions, colors : colors, normals : normals, batchIds : batchIds, styleableProperties : styleableProperties }; + this._pointsLength = pointsLength; this._isQuantized = isQuantized; this._isOctEncoded16P = isOctEncoded16P; @@ -487,7 +497,7 @@ define([ function createResources(content, frameState) { var context = frameState.context; var parsedContent = content._parsedContent; - var pointsLength = parsedContent.pointsLength; + var pointsLength = content._pointsLength; var positions = parsedContent.positions; var colors = parsedContent.colors; var normals = parsedContent.normals; diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 9149b60359e4..78c09c7be8a4 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -9,7 +9,6 @@ define([ '../Core/Event', '../Core/getTimestamp', '../Core/Math', - '../Core/Queue', '../Core/Ray', '../Core/Rectangle', '../Core/Visibility', @@ -28,7 +27,6 @@ define([ Event, getTimestamp, CesiumMath, - Queue, Ray, Rectangle, Visibility, diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c46d0f8fd57c..33433a159671 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -28,6 +28,7 @@ define([ '../Core/Matrix4', '../Core/mergeSort', '../Core/Occluder', + '../Core/PixelFormat', '../Core/RequestScheduler', '../Core/ShowGeometryInstanceAttribute', '../Core/Transforms', @@ -36,10 +37,14 @@ define([ '../Renderer/Context', '../Renderer/ContextLimits', '../Renderer/DrawCommand', + '../Renderer/Framebuffer', '../Renderer/Pass', '../Renderer/PassState', + '../Renderer/PixelDatatype', + '../Renderer/RenderState', '../Renderer/ShaderProgram', '../Renderer/ShaderSource', + '../Renderer/Texture', './Camera', './CreditDisplay', './CullingVolume', @@ -98,6 +103,7 @@ define([ Matrix4, mergeSort, Occluder, + PixelFormat, RequestScheduler, ShowGeometryInstanceAttribute, Transforms, @@ -106,10 +112,14 @@ define([ Context, ContextLimits, DrawCommand, + Framebuffer, Pass, PassState, + PixelDatatype, + RenderState, ShaderProgram, ShaderSource, + Texture, Camera, CreditDisplay, CullingVolume, @@ -299,6 +309,12 @@ define([ this._pickDepths = []; this._debugGlobeDepths = []; + this._pickDepthPassState = undefined; + this._pickDepthFramebuffer = undefined; + this._pickDepthFramebufferWidth = undefined; + this._pickDepthFramebufferHeight = undefined; + this._depthOnlyRenderStateCache = {}; + this._transitioner = new SceneTransitioner(this); this._renderError = new Event(); @@ -553,6 +569,18 @@ define([ */ this.useDepthPicking = true; + /** + * When true, enables picking translucent geometry using the depth buffer. + * {@link Scene#useDepthPicking} must also be true to enable picking the depth buffer. + *

+ * There is a decrease in performance when enabled. There are extra draw calls to write depth for + * translucent geometry. + *

+ * @type {Boolean} + * @default false + */ + this.pickTranslucentDepth = false; + /** * The time in milliseconds to wait before checking if the camera has not moved and fire the cameraMoveEnd event. * @type {Number} @@ -1167,11 +1195,10 @@ define([ shadowsDirty = true; } - if (command.dirty) { + var derivedCommands = command.derivedCommands; + if (command.dirty && defined(derivedCommands)) { command.dirty = false; - var derivedCommands = command.derivedCommands; - if (shadowsEnabled && (command.receiveShadows || command.castShadows)) { derivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, lightShadowMaps, command, shadowsDirty, context, derivedCommands.shadows); } @@ -1185,6 +1212,8 @@ define([ derivedCommands.oit = oit.createDerivedCommands(command, context, derivedCommands.oit); } } + + derivedCommands.depth = createDepthOnlyDerivedCommand(scene, command, context, derivedCommands.depth); } } @@ -1208,6 +1237,7 @@ define([ function clearPasses(passes) { passes.render = false; passes.pick = false; + passes.depth = false; } function updateFrameState(scene, frameNumber, time) { @@ -1582,6 +1612,8 @@ define([ // Some commands, such as OIT derived commands, do not have derived shadow commands themselves // and instead shadowing is built-in. In this case execute the command regularly below. command.derivedCommands.shadows.receiveCommand.execute(context, passState); + } else if (scene.frameState.passes.depth && defined(command.derivedCommands.depth)) { + command.derivedCommands.depth.depthOnlyCommand.execute(context, passState); } else { command.execute(context, passState); } @@ -1709,7 +1741,7 @@ define([ var scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum(); var scratchOrthographicFrustum = new OrthographicFrustum(); - function executeCommands(scene, passState, picking) { + function executeCommands(scene, passState) { var camera = scene._camera; var context = scene.context; var us = context.uniformState; @@ -1734,6 +1766,9 @@ define([ us.updatePass(Pass.ENVIRONMENT); var useWebVR = scene._useWebVR && scene.mode !== SceneMode.SCENE2D; + var passes = scene._frameState.passes; + var picking = passes.pick; + var depthOnly = passes.depth; var environmentState = scene._environmentState; // Do not render environment primitives during a pick pass since they do not generate picking commands. @@ -1882,10 +1917,11 @@ define([ commands.length = frustumCommands.indices[Pass.TRANSLUCENT]; executeTranslucentCommands(scene, executeCommand, passState, commands); - if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer && scene.useDepthPicking) { + if (defined(globeDepth) && (environmentState.useGlobeDepthFramebuffer || depthOnly) && scene.useDepthPicking) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. + var depthStencilTexture = depthOnly ? passState.framebuffer.depthStencilTexture : globeDepth.framebuffer.depthStencilTexture; var pickDepth = getPickDepth(scene, index); - pickDepth.update(context, globeDepth.framebuffer.depthStencilTexture); + pickDepth.update(context, depthStencilTexture); pickDepth.executeCopyDepth(context, passState); } } @@ -2004,7 +2040,7 @@ define([ } } - function updateAndExecuteCommands(scene, passState, backgroundColor, picking) { + function updateAndExecuteCommands(scene, passState, backgroundColor) { var context = scene._context; var viewport = passState.viewport; @@ -2012,13 +2048,20 @@ define([ var frameState = scene._frameState; var camera = frameState.camera; var mode = frameState.mode; + var depthOnly = frameState.passes.depth; if (scene._useWebVR && mode !== SceneMode.SCENE2D) { - updatePrimitives(scene); + if (!depthOnly) { + updatePrimitives(scene); + } + createPotentiallyVisibleSet(scene); - updateAndClearFramebuffers(scene, passState, backgroundColor, picking); - executeComputeCommands(scene); - executeShadowMapCastCommands(scene); + updateAndClearFramebuffers(scene, passState, backgroundColor); + + if (!depthOnly) { + executeComputeCommands(scene); + executeShadowMapCastCommands(scene); + } // Based on Calculating Stereo pairs by Paul Bourke // http://paulbourke.net/stereographics/stereorender/ @@ -2042,14 +2085,14 @@ define([ Cartesian3.add(savedCamera.position, eyeTranslation, camera.position); camera.frustum.xOffset = offset; - executeCommands(scene, passState, picking); + executeCommands(scene, passState); viewport.x = passState.viewport.width; Cartesian3.subtract(savedCamera.position, eyeTranslation, camera.position); camera.frustum.xOffset = -offset; - executeCommands(scene, passState, picking); + executeCommands(scene, passState); Camera.clone(savedCamera, camera); } else { @@ -2059,9 +2102,9 @@ define([ viewport.height = context.drawingBufferHeight; if (mode !== SceneMode.SCENE2D || scene._mapMode2D === MapMode2D.ROTATE) { - executeCommandsInViewport(true, scene, passState, backgroundColor, picking); + executeCommandsInViewport(true, scene, passState, backgroundColor); } else { - execute2DViewportCommands(scene, passState, backgroundColor, picking); + execute2DViewportCommands(scene, passState, backgroundColor); } } } @@ -2074,7 +2117,7 @@ define([ var scratch2DViewportEyePoint = new Cartesian3(); var scratch2DViewportWindowCoords = new Cartesian3(); - function execute2DViewportCommands(scene, passState, backgroundColor, picking) { + function execute2DViewportCommands(scene, passState, backgroundColor) { var context = scene.context; var frameState = scene.frameState; var camera = scene.camera; @@ -2105,7 +2148,7 @@ define([ var viewportWidth = viewport.width; if (x === 0.0 || windowCoordinates.x <= 0.0 || windowCoordinates.x >= context.drawingBufferWidth) { - executeCommandsInViewport(true, scene, passState, backgroundColor, picking); + executeCommandsInViewport(true, scene, passState, backgroundColor); } else if (Math.abs(context.drawingBufferWidth * 0.5 - windowCoordinates.x) < 1.0) { viewport.width = windowCoordinates.x; @@ -2116,7 +2159,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor, picking); + executeCommandsInViewport(true, scene, passState, backgroundColor); viewport.x = viewport.width; @@ -2128,7 +2171,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor, picking); + executeCommandsInViewport(false, scene, passState, backgroundColor); } else if (windowCoordinates.x > context.drawingBufferWidth * 0.5) { viewport.width = windowCoordinates.x; @@ -2138,7 +2181,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor, picking); + executeCommandsInViewport(true, scene, passState, backgroundColor); viewport.x += windowCoordinates.x; viewport.width = context.drawingBufferWidth - windowCoordinates.x; @@ -2151,7 +2194,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor, picking); + executeCommandsInViewport(false, scene, passState, backgroundColor); } else { viewport.x = windowCoordinates.x; viewport.width = context.drawingBufferWidth - windowCoordinates.x; @@ -2162,7 +2205,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor, picking); + executeCommandsInViewport(true, scene, passState, backgroundColor); viewport.x = 0; viewport.width = windowCoordinates.x; @@ -2175,7 +2218,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor, picking); + executeCommandsInViewport(false, scene, passState, backgroundColor); } camera._setTransform(transform); @@ -2186,21 +2229,28 @@ define([ viewport.width = viewportWidth; } - function executeCommandsInViewport(firstViewport, scene, passState, backgroundColor, picking) { - if (!firstViewport) { + function executeCommandsInViewport(firstViewport, scene, passState, backgroundColor) { + var depthOnly = scene.frameState.passes.depth; + + if (!firstViewport && !depthOnly) { scene.frameState.commandList.length = 0; } - updatePrimitives(scene); + if (!depthOnly) { + updatePrimitives(scene); + } + createPotentiallyVisibleSet(scene); if (firstViewport) { - updateAndClearFramebuffers(scene, passState, backgroundColor, picking); - executeComputeCommands(scene); - executeShadowMapCastCommands(scene); + updateAndClearFramebuffers(scene, passState, backgroundColor); + if (!depthOnly) { + executeComputeCommands(scene); + executeShadowMapCastCommands(scene); + } } - executeCommands(scene, passState, picking); + executeCommands(scene, passState); } function updateEnvironment(scene) { @@ -2310,10 +2360,12 @@ define([ } } - function updateAndClearFramebuffers(scene, passState, clearColor, picking) { + function updateAndClearFramebuffers(scene, passState, clearColor) { var context = scene._context; var environmentState = scene._environmentState; + var passes = scene._frameState.passes; + var picking = passes.pick; var useWebVR = scene._useWebVR && scene.mode !== SceneMode.SCENE2D; // Preserve the reference to the original framebuffer. @@ -2696,7 +2748,7 @@ define([ var passState = this._pickFramebuffer.begin(scratchRectangle); - updateAndExecuteCommands(this, passState, scratchColorZero, true); + updateAndExecuteCommands(this, passState, scratchColorZero); resolveFramebuffers(this, passState); var object = this._pickFramebuffer.end(scratchRectangle); @@ -2705,20 +2757,170 @@ define([ return object; }; + var fragDepthRegex = /\bgl_FragDepthEXT\b/; + var discardRegex = /\bdiscard\b/; + + function getDepthOnlyShaderProgram(context, shaderProgram) { + var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'depthOnly'); + if (!defined(shader)) { + var attributeLocations = shaderProgram._attributeLocations; + var fs = shaderProgram.fragmentShaderSource; + + var writesDepthOrDiscards = false; + var sources = fs.sources; + var length = sources.length; + for (var i = 0; i < length; ++i) { + if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) { + writesDepthOrDiscards = true; + break; + } + } + + if (!writesDepthOrDiscards) { + fs = new ShaderSource({ + sources : ['void main() { gl_FragColor = vec4(1.0); }'] + }); + } + + shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, 'depthOnly', { + vertexShaderSource : shaderProgram.vertexShaderSource, + fragmentShaderSource : fs, + attributeLocations : attributeLocations + }); + } + + return shader; + } + + function getDepthOnlyRenderState(scene, renderState) { + var cache = scene._depthOnlyRenderStateCache; + var depthOnlyState = cache[renderState.id]; + if (!defined(depthOnlyState)) { + var rs = RenderState.getState(renderState); + rs.depthMask = true; + rs.colorMask = { + red : false, + green : false, + blue : false, + alpha : false + }; + + depthOnlyState = RenderState.fromCache(rs); + cache[renderState.id] = depthOnlyState; + } + + return depthOnlyState; + } + + function createDepthOnlyDerivedCommand(scene, command, context, result) { + // For a depth only pass, we bind a framebuffer with only a depth attachment (no color attachments), + // do not write color, and write depth. If the fragment shader doesn't modify the fragment depth + // or discard, the driver can replace the fragment shader with a pass-through shader. We're unsure if this + // actually happens so we modify the shader to use a pass-through fragment shader. + + if (!defined(result)) { + result = {}; + } + + var shader; + var renderState; + if (defined(result.depthOnlyCommand)) { + shader = result.depthOnlyCommand.shaderProgram; + renderState = result.depthOnlyCommand.renderState; + } + + result.depthOnlyCommand = DrawCommand.shallowClone(command, result.depthOnlyCommand); + + if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) { + result.depthOnlyCommand.shaderProgram = getDepthOnlyShaderProgram(context, command.shaderProgram); + result.depthOnlyCommand.renderState = getDepthOnlyRenderState(scene, command.renderState); + result.shaderProgramId = command.shaderProgram.id; + } else { + result.depthOnlyCommand.shaderProgram = shader; + result.depthOnlyCommand.renderState = renderState; + } + + return result; + } + + function renderTranslucentDepthForPick(scene, drawingBufferPosition) { + // PERFORMANCE_IDEA: render translucent only and merge with the previous frame + var context = scene._context; + var frameState = scene._frameState; + + clearPasses(frameState.passes); + frameState.passes.pick = true; + frameState.passes.depth = true; + frameState.cullingVolume = getPickCullingVolume(scene, drawingBufferPosition, 1, 1); + + var passState = scene._pickDepthPassState; + if (!defined(passState)) { + passState = scene._pickDepthPassState = new PassState(context); + passState.scissorTest = { + enabled : true, + rectangle : new BoundingRectangle() + }; + passState.viewport = new BoundingRectangle(); + } + + var width = context.drawingBufferWidth; + var height = context.drawingBufferHeight; + + var framebuffer = scene._pickDepthFramebuffer; + var pickDepthFBWidth = scene._pickDepthFramebufferWidth; + var pickDepthFBHeight = scene._pickDepthFramebufferHeight; + if (!defined(framebuffer) || pickDepthFBWidth !== width || pickDepthFBHeight !== height) { + scene._pickDepthFramebuffer = scene._pickDepthFramebuffer && scene._pickDepthFramebuffer.destroy(); + framebuffer = scene._pickDepthFramebuffer = new Framebuffer({ + context : context, + depthStencilTexture : new Texture({ + context : context, + width : width, + height : height, + pixelFormat : PixelFormat.DEPTH_STENCIL, + pixelDatatype : PixelDatatype.UNSIGNED_INT_24_8 + }) + }); + + scene._pickDepthFramebufferWidth = width; + scene._pickDepthFramebufferHeight = height; + } + + passState.framebuffer = framebuffer; + passState.viewport.width = width; + passState.viewport.height = height; + passState.scissorTest.rectangle.x = drawingBufferPosition.x; + passState.scissorTest.rectangle.y = height - drawingBufferPosition.y; + passState.scissorTest.rectangle.width = 1; + passState.scissorTest.rectangle.height = 1; + + updateAndExecuteCommands(scene, passState, scratchColorZero); + resolveFramebuffers(scene, passState); + + context.endFrame(); + } + var scratchPackedDepth = new Cartesian4(); var packedDepthScale = new Cartesian4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0); /** * Returns the cartesian position reconstructed from the depth buffer and window position. + * The returned position is in world coordinates. Used internally by camera functions to + * prevent conversion to projected 2D coordinates and then back. + *

+ * Set {@link Scene#pickTranslucentDepth} to true to include the depth of + * translucent primitives; otherwise, this essentially picks through translucent primitives. + *

+ * + * @private * * @param {Cartesian2} windowPosition Window coordinates to perform picking on. * @param {Cartesian3} [result] The object on which to restore the result. - * @returns {Cartesian3} The cartesian position. + * @returns {Cartesian3} The cartesian position in world coordinates. * * @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported. - * @exception {DeveloperError} 2D is not supported. An orthographic projection matrix is not invertible. */ - Scene.prototype.pickPosition = function(windowPosition, result) { + Scene.prototype.pickPositionWorldCoordinates = function(windowPosition, result) { if (!this.useDepthPicking) { return undefined; } @@ -2736,6 +2938,9 @@ define([ var uniformState = context.uniformState; var drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(this, windowPosition, scratchPosition); + if (this.pickTranslucentDepth) { + renderTranslucentDepthForPick(this, drawingBufferPosition); + } drawingBufferPosition.y = this.drawingBufferHeight - drawingBufferPosition.y; var camera = this._camera; @@ -2747,9 +2952,7 @@ define([ } else if (defined(camera.frustum.infiniteProjectionMatrix)){ frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum); } else { - //>>includeStart('debug', pragmas.debug); - throw new DeveloperError('2D is not supported. An orthographic projection matrix is not invertible.'); - //>>includeEnd('debug'); + frustum = camera.frustum.clone(scratchOrthographicFrustum); } var numFrustums = this.numberOfFrustums; @@ -2769,17 +2972,68 @@ define([ if (depth > 0.0 && depth < 1.0) { var renderedFrustum = this._frustumCommandsList[i]; - frustum.near = renderedFrustum.near * (i !== 0 ? OPAQUE_FRUSTUM_NEAR_OFFSET : 1.0); - frustum.far = renderedFrustum.far; - uniformState.updateFrustum(frustum); + var height2D; + if (this.mode === SceneMode.SCENE2D) { + height2D = camera.position.z; + camera.position.z = height2D - renderedFrustum.near + 1.0; + frustum.far = Math.max(1.0, renderedFrustum.far - renderedFrustum.near); + frustum.near = 1.0; + uniformState.update(this.frameState); + uniformState.updateFrustum(frustum); + } else { + frustum.near = renderedFrustum.near * (i !== 0 ? OPAQUE_FRUSTUM_NEAR_OFFSET : 1.0); + frustum.far = renderedFrustum.far; + uniformState.updateFrustum(frustum); + } - return SceneTransforms.drawingBufferToWgs84Coordinates(this, drawingBufferPosition, depth, result); + result = SceneTransforms.drawingBufferToWgs84Coordinates(this, drawingBufferPosition, depth, result); + + if (this.mode === SceneMode.SCENE2D) { + camera.position.z = height2D; + uniformState.update(this.frameState); + } + + return result; } } return undefined; }; + var scratchPickPositionCartographic = new Cartographic(); + + /** + * Returns the cartesian position reconstructed from the depth buffer and window position. + *

+ * The position reconstructed from the depth buffer in 2D may be slightly different from those + * reconstructed in 3D and Columbus view. This is caused by the difference in the distribution + * of depth values of perspective and orthographic projection. + *

+ *

+ * Set {@link Scene#pickTranslucentDepth} to true to include the depth of + * translucent primitives; otherwise, this essentially picks through translucent primitives. + *

+ * + * @param {Cartesian2} windowPosition Window coordinates to perform picking on. + * @param {Cartesian3} [result] The object on which to restore the result. + * @returns {Cartesian3} The cartesian position. + * + * @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported. + */ + Scene.prototype.pickPosition = function(windowPosition, result) { + result = this.pickPositionWorldCoordinates(windowPosition, result); + if (defined(result) && this.mode !== SceneMode.SCENE3D) { + Cartesian3.fromElements(result.y, result.z, result.x, result); + + var projection = this.mapProjection; + var ellipsoid = projection.ellipsoid; + + var cart = projection.unproject(result, scratchPickPositionCartographic); + ellipsoid.cartographicToCartesian(cart, result); + } + return result; + }; + /** * Returns a list of objects, each containing a `primitive` property, for all primitives at * a particular window coordinate position. Other properties may also be set depending on the @@ -2952,6 +3206,7 @@ define([ this._screenSpaceCameraController = this._screenSpaceCameraController && this._screenSpaceCameraController.destroy(); this._deviceOrientationCameraController = this._deviceOrientationCameraController && !this._deviceOrientationCameraController.isDestroyed() && this._deviceOrientationCameraController.destroy(); this._pickFramebuffer = this._pickFramebuffer && this._pickFramebuffer.destroy(); + this._pickDepthFramebuffer = this._pickDepthFramebuffer && this._pickDepthFramebuffer.destroy(); this._primitives = this._primitives && this._primitives.destroy(); this._groundPrimitives = this._groundPrimitives && this._groundPrimitives.destroy(); this._globe = this._globe && this._globe.destroy(); diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index 8da7f9fbb523..afe452a05399 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -320,11 +320,24 @@ define([ ndc.z = (depth * 2.0) - 1.0; ndc.w = 1.0; - var worldCoords = Matrix4.multiplyByVector(uniformState.inverseViewProjection, ndc, scratchWorldCoords); - - // Reverse perspective divide - var w = 1.0 / worldCoords.w; - Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); + var worldCoords; + var frustum = scene.camera.frustum; + if (!defined(frustum.fovy)) { + var currentFrustum = uniformState.currentFrustum; + worldCoords = scratchWorldCoords; + worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5; + worldCoords.y = (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5; + worldCoords.z = (ndc.z * (currentFrustum.x - currentFrustum.y) - currentFrustum.x - currentFrustum.y) * 0.5; + worldCoords.w = 1.0; + + worldCoords = Matrix4.multiplyByVector(uniformState.inverseView, worldCoords, worldCoords); + } else { + worldCoords = Matrix4.multiplyByVector(uniformState.inverseViewProjection, ndc, scratchWorldCoords); + + // Reverse perspective divide + var w = 1.0 / worldCoords.w; + Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); + } return Cartesian3.fromCartesian4(worldCoords, result); }; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 3e3a7346103e..3d6e974b3ebc 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -806,7 +806,7 @@ define([ var depthIntersection; if (scene.pickPositionSupported) { - depthIntersection = scene.pickPosition(mousePosition, scratchDepthIntersection); + depthIntersection = scene.pickPositionWorldCoordinates(mousePosition, scratchDepthIntersection); } var ray = camera.getPickRay(mousePosition, pickGlobeScratchRay); diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js index cbb634f9e011..171b6a9bb7a6 100644 --- a/Source/Scene/ShadowMap.js +++ b/Source/Scene/ShadowMap.js @@ -37,7 +37,6 @@ define([ '../Renderer/RenderbufferFormat', '../Renderer/RenderState', '../Renderer/Sampler', - '../Renderer/ShaderProgram', '../Renderer/Texture', '../Renderer/TextureMagnificationFilter', '../Renderer/TextureMinificationFilter', @@ -89,7 +88,6 @@ define([ RenderbufferFormat, RenderState, Sampler, - ShaderProgram, Texture, TextureMagnificationFilter, TextureMinificationFilter, @@ -1481,28 +1479,28 @@ define([ result.receiveShadows = false; if (!defined(castShader) || oldShaderId !== command.shaderProgram.id || shadowsDirty) { - if (defined(castShader)) { - castShader.destroy(); - } - var shaderProgram = command.shaderProgram; - var vertexShaderSource = shaderProgram.vertexShaderSource; - var fragmentShaderSource = shaderProgram.fragmentShaderSource; var isTerrain = command.pass === Pass.GLOBE; var isOpaque = command.pass !== Pass.TRANSLUCENT; var isPointLight = shadowMap._isPointLight; var usesDepthTexture= shadowMap._usesDepthTexture; - var castVS = ShadowMapShader.createShadowCastVertexShader(vertexShaderSource, isPointLight, isTerrain); - var castFS = ShadowMapShader.createShadowCastFragmentShader(fragmentShaderSource, isPointLight, usesDepthTexture, isOpaque); + var keyword = ShadowMapShader.getShadowCastShaderKeyword(isPointLight, isTerrain, usesDepthTexture, isOpaque); + castShader = context.shaderCache.getDerivedShaderProgram(shaderProgram, keyword); + if (!defined(castShader)) { + var vertexShaderSource = shaderProgram.vertexShaderSource; + var fragmentShaderSource = shaderProgram.fragmentShaderSource; - castShader = ShaderProgram.fromCache({ - context : context, - vertexShaderSource : castVS, - fragmentShaderSource : castFS, - attributeLocations : shaderProgram._attributeLocations - }); + var castVS = ShadowMapShader.createShadowCastVertexShader(vertexShaderSource, isPointLight, isTerrain); + var castFS = ShadowMapShader.createShadowCastFragmentShader(fragmentShaderSource, isPointLight, usesDepthTexture, isOpaque); + + castShader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, { + vertexShaderSource : castVS, + fragmentShaderSource : castFS, + attributeLocations : shaderProgram._attributeLocations + }); + } castRenderState = shadowMap._primitiveRenderState; if (isPointLight) { @@ -1582,20 +1580,19 @@ define([ var shaderDirty = result.receiveShaderProgramId !== command.shaderProgram.id; if (!defined(receiveShader) || shaderDirty || shadowsDirty || castShadowsDirty) { - if (defined(receiveShader)) { - receiveShader.destroy(); + var keyword = ShadowMapShader.getShadowReceiveShaderKeyword(lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal); + receiveShader = context.shaderCache.getDerivedShaderProgram(shaderProgram, keyword); + if (!defined(receiveShader)) { + var receiveVS = ShadowMapShader.createShadowReceiveVertexShader(vertexShaderSource, isTerrain, hasTerrainNormal); + var receiveFS = ShadowMapShader.createShadowReceiveFragmentShader(fragmentShaderSource, lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal); + + receiveShader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, { + vertexShaderSource : receiveVS, + fragmentShaderSource : receiveFS, + attributeLocations : shaderProgram._attributeLocations + }); } - var receiveVS = ShadowMapShader.createShadowReceiveVertexShader(vertexShaderSource, isTerrain, hasTerrainNormal); - var receiveFS = ShadowMapShader.createShadowReceiveFragmentShader(fragmentShaderSource, lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal); - - receiveShader = ShaderProgram.fromCache({ - context : context, - vertexShaderSource : receiveVS, - fragmentShaderSource : receiveFS, - attributeLocations : shaderProgram._attributeLocations - }); - receiveUniformMap = combineUniforms(lightShadowMaps[0], command.uniformMap, isTerrain); } diff --git a/Source/Scene/ShadowMapShader.js b/Source/Scene/ShadowMapShader.js index 555b61b8e0ad..41b2e00966b7 100644 --- a/Source/Scene/ShadowMapShader.js +++ b/Source/Scene/ShadowMapShader.js @@ -13,6 +13,10 @@ define([ function ShadowMapShader() { } + ShadowMapShader.getShadowCastShaderKeyword = function(isPointLight, isTerrain, usesDepthTexture, isOpaque) { + return 'castShadow ' + isPointLight + ' ' + isTerrain + ' ' + usesDepthTexture + ' ' + isOpaque; + }; + ShadowMapShader.createShadowCastVertexShader = function(vs, isPointLight, isTerrain) { var defines = vs.defines.slice(0); var sources = vs.sources.slice(0); @@ -106,6 +110,19 @@ define([ }); }; + ShadowMapShader.getShadowReceiveShaderKeyword = function(shadowMap, castShadows, isTerrain, hasTerrainNormal) { + var usesDepthTexture = shadowMap._usesDepthTexture; + var polygonOffsetSupported = shadowMap._polygonOffsetSupported; + var isPointLight = shadowMap._isPointLight; + var isSpotLight = shadowMap._isSpotLight; + var hasCascades = shadowMap._numberOfCascades > 1; + var debugCascadeColors = shadowMap.debugCascadeColors; + var softShadows = shadowMap.softShadows; + + return 'receiveShadow ' + usesDepthTexture + polygonOffsetSupported + isPointLight + isSpotLight + + hasCascades + debugCascadeColors + softShadows + castShadows + isTerrain + hasTerrainNormal; + }; + ShadowMapShader.createShadowReceiveVertexShader = function(vs, isTerrain, hasTerrainNormal) { var defines = vs.defines.slice(0); var sources = vs.sources.slice(0); diff --git a/Source/Scene/Tileset3DTileContent.js b/Source/Scene/Tileset3DTileContent.js index 49a7daba114a..0b8806b3680b 100644 --- a/Source/Scene/Tileset3DTileContent.js +++ b/Source/Scene/Tileset3DTileContent.js @@ -49,6 +49,15 @@ define([ } }, + /** + * Part of the {@link Cesium3DTileContent} interface. + */ + pointsLength : { + get : function() { + return 0; + } + }, + /** * Part of the {@link Cesium3DTileContent} interface. */ diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index c56ebd30516c..e9448a6cdf69 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -2,7 +2,7 @@ attribute vec2 direction; #endif attribute vec4 positionHighAndScale; -attribute vec4 positionLowAndRotation; +attribute vec4 positionLowAndRotation; attribute vec4 compressedAttribute0; // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates (texture offset) attribute vec4 compressedAttribute1; // aligned axis, translucency by distance, image width attribute vec4 compressedAttribute2; // image height, color, pick color, size in meters, valid aligned axis, 13 bits free @@ -44,7 +44,7 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca halfSize *= ((direction * 2.0) - 1.0); vec2 originTranslate = origin * abs(halfSize); - + #if defined(ROTATION) || defined(ALIGNED_AXIS) if (validAlignedAxis || rotation != 0.0) { @@ -57,7 +57,7 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca tangent = czm_modelViewProjection * tangent; angle += sign(-tangent.x) * acos(tangent.y / length(tangent.xy)); } - + float cosTheta = cos(angle); float sinTheta = sin(angle); mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta); @@ -82,22 +82,22 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca { positionWC.xy += halfSize; } - + positionWC.xy += translate; positionWC.xy += (pixelOffset * czm_resolutionScale); - + return positionWC; } -void main() +void main() { // Modifying this shader may also require modifications to Billboard._computeScreenSpacePosition - + // unpack attributes vec3 positionHigh = positionHighAndScale.xyz; vec3 positionLow = positionLowAndRotation.xyz; float scale = positionHighAndScale.w; - + #if defined(ROTATION) || defined(ALIGNED_AXIS) float rotation = positionLowAndRotation.w; #else @@ -105,21 +105,21 @@ void main() #endif float compressed = compressedAttribute0.x; - + vec2 pixelOffset; pixelOffset.x = floor(compressed * SHIFT_RIGHT7); compressed -= pixelOffset.x * SHIFT_LEFT7; pixelOffset.x -= UPPER_BOUND; - + vec2 origin; origin.x = floor(compressed * SHIFT_RIGHT5); compressed -= origin.x * SHIFT_LEFT5; - + origin.y = floor(compressed * SHIFT_RIGHT3); compressed -= origin.y * SHIFT_LEFT3; - + origin -= vec2(1.0); - + float show = floor(compressed * SHIFT_RIGHT2); compressed -= show * SHIFT_LEFT2; @@ -134,30 +134,30 @@ void main() vec2 textureCoordinates = czm_decompressTextureCoordinates(compressedAttribute0.w); #endif - + float temp = compressedAttribute0.y * SHIFT_RIGHT8; pixelOffset.y = -(floor(temp) - UPPER_BOUND); - + vec2 translate; translate.y = (temp - floor(temp)) * SHIFT_LEFT16; - + temp = compressedAttribute0.z * SHIFT_RIGHT8; translate.x = floor(temp) - UPPER_BOUND; - + translate.y += (temp - floor(temp)) * SHIFT_LEFT8; translate.y -= UPPER_BOUND; temp = compressedAttribute1.x * SHIFT_RIGHT8; - + vec2 imageSize = vec2(floor(temp), compressedAttribute2.w); - + #ifdef EYE_DISTANCE_TRANSLUCENCY vec4 translucencyByDistance; translucencyByDistance.x = compressedAttribute1.z; translucencyByDistance.z = compressedAttribute1.w; - + translucencyByDistance.y = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0; - + temp = compressedAttribute1.y * SHIFT_RIGHT8; translucencyByDistance.w = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0; #endif @@ -170,7 +170,7 @@ void main() vec3 alignedAxis = vec3(0.0); bool validAlignedAxis = false; #endif - + #ifdef RENDER_FOR_PICK temp = compressedAttribute2.y; #else @@ -183,11 +183,11 @@ void main() temp = floor(temp) * SHIFT_RIGHT8; color.g = (temp - floor(temp)) * SHIFT_LEFT8; color.r = floor(temp); - + temp = compressedAttribute2.z * SHIFT_RIGHT8; bool sizeInMeters = floor((temp - floor(temp)) * SHIFT_LEFT7) > 0.0; temp = floor(temp) * SHIFT_RIGHT8; - + #ifdef RENDER_FOR_PICK color.a = (temp - floor(temp)) * SHIFT_LEFT8; vec4 pickColor = color / 255.0; @@ -195,15 +195,15 @@ void main() color.a = floor(temp); color /= 255.0; #endif - + /////////////////////////////////////////////////////////////////////////// - + vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); vec4 positionEC = czm_modelViewRelativeToEye * p; positionEC = czm_eyeOffset(positionEC, eyeOffset.xyz); positionEC.xyz *= show; - - /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// #if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(EYE_DISTANCE_PIXEL_OFFSET) || defined(DISTANCE_DISPLAY_CONDITION) float lengthSq; @@ -220,7 +220,9 @@ void main() #endif #ifdef EYE_DISTANCE_SCALING - scale *= czm_nearFarScalar(scaleByDistance, lengthSq); + float distanceScale = czm_nearFarScalar(scaleByDistance, lengthSq); + scale *= distanceScale; + translate *= distanceScale; // push vertex behind near plane for clipping if (scale == 0.0) { diff --git a/Source/Widgets/CesiumInspector/Cesium3DTilesInspector.js b/Source/Widgets/CesiumInspector/Cesium3DTilesInspector.js index 187f03d3dbc6..f9f93ddb1ed5 100644 --- a/Source/Widgets/CesiumInspector/Cesium3DTilesInspector.js +++ b/Source/Widgets/CesiumInspector/Cesium3DTilesInspector.js @@ -113,6 +113,7 @@ define([ displayPanel.contents.appendChild(makeCheckbox('showBoundingVolumes', 'Bounding Volumes')); displayPanel.contents.appendChild(makeCheckbox('showContentBoundingVolumes', 'Content Volumes')); displayPanel.contents.appendChild(makeCheckbox('showRequestVolumes', 'Request Volumes')); + displayPanel.contents.appendChild(makeCheckbox('showGeometricError', 'Geometric Error')); knockout.applyBindings(viewModel, displayPanel.contents); updatePanel.contents.appendChild(makeCheckbox('freezeFrame', 'Freeze Frame')); diff --git a/Source/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModel.js b/Source/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModel.js index 90017a1089eb..56a7b6edc365 100644 --- a/Source/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModel.js @@ -2,6 +2,7 @@ define([ '../../Core/Cartesian3', '../../Core/Cartographic', + '../../Scene/Cesium3DTileFeature', '../../Scene/Cesium3DTileset', '../../Scene/Cesium3DTileStyle', '../../Scene/Cesium3DTileColorBlendMode', @@ -19,6 +20,7 @@ define([ ], function( Cartesian3, Cartographic, + Cesium3DTileFeature, Cesium3DTileset, Cesium3DTileStyle, Cesium3DTileColorBlendMode, @@ -153,8 +155,11 @@ define([ return function(val) { if (val) { that._eventHandler.setInputAction(function(e) { - that._feature = scene.pick(e.endPosition); - that._updateStats(true); + var picked = scene.pick(e.endPosition); + if (picked instanceof Cesium3DTileFeature) { + that._feature = picked; + that._updateStats(true); + } }, ScreenSpaceEventType.MOUSE_MOVE); } else { that._feature = undefined; @@ -257,6 +262,21 @@ define([ } } }, + /** + * Gets or sets the flag to show tile geometric error. This property is observable. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * + * @type {Boolean} + * @default false + */ + showGeometricError: { + default: false, + subscribe: function(val) { + if (that._tileset) { + that._tileset.debugShowGeometricError = val; + } + } + }, /** * Gets or sets the maximum screen space error. This property is observable. * @memberof Cesium3DTilesInspectorViewModel.prototype @@ -401,6 +421,7 @@ define([ 'showBoundingVolumes', 'showContentBoundingVolumes', 'showRequestVolumes', + 'showGeometricError', 'freezeFrame']; var length = settings.length; for (var i = 0; i < length; ++i) { @@ -422,7 +443,6 @@ define([ _feature: { default: undefined, subscribe: (function() { - var current; var scratchColor = new Color(); var oldColor = new Color(); @@ -611,31 +631,44 @@ define([ var s = ''; s += ''; + s += ''; s += ''; + s += ''; } diff --git a/Source/Widgets/Geocoder/Geocoder.js b/Source/Widgets/Geocoder/Geocoder.js index 2b42e86a2ccb..3d21be2dd4fd 100644 --- a/Source/Widgets/Geocoder/Geocoder.js +++ b/Source/Widgets/Geocoder/Geocoder.js @@ -34,14 +34,6 @@ define([ * @param {Scene} options.scene The Scene instance to use. * @param {GeocoderService[]} [options.geocoderServices] The geocoder services to be used * @param {Boolean} [options.autoComplete = true] True if the geocoder should query as the user types to autocomplete - * @param {String} [options.url='https://dev.virtualearth.net'] The base URL of the Bing Maps API. - * @param {String} [options.key] The Bing Maps key for your application, which can be - * created at {@link https://www.bingmapsportal.com}. - * If this parameter is not provided, {@link BingMapsApi.defaultKey} is used. - * If {@link BingMapsApi.defaultKey} is undefined as well, a message is - * written to the console reminding you that you must create and supply a Bing Maps - * key as soon as possible. Please do not deploy an application that uses - * this widget without creating a separate key for your application. * @param {Number} [options.flightDuration=1.5] The duration of the camera flight to an entered location, in seconds. */ function Geocoder(options) { diff --git a/Source/Widgets/Geocoder/GeocoderViewModel.js b/Source/Widgets/Geocoder/GeocoderViewModel.js index b5072cdeade9..c12e42e7a8a9 100644 --- a/Source/Widgets/Geocoder/GeocoderViewModel.js +++ b/Source/Widgets/Geocoder/GeocoderViewModel.js @@ -1,36 +1,26 @@ /*global define*/ define([ - '../../Core/BingMapsApi', '../../Core/BingMapsGeocoderService', - '../../Core/Cartesian3', '../../Core/defaultValue', '../../Core/defined', '../../Core/defineProperties', - '../../Core/deprecationWarning', '../../Core/DeveloperError', '../../Core/Event', '../../Core/CartographicGeocoderService', '../../Core/Matrix4', - '../../Core/Rectangle', - '../../Core/RequestScheduler', '../../ThirdParty/knockout', '../../ThirdParty/when', '../createCommand', '../getElement' ], function( - BingMapsApi, BingMapsGeocoderService, - Cartesian3, defaultValue, defined, defineProperties, - deprecationWarning, DeveloperError, Event, CartographicGeocoderService, Matrix4, - Rectangle, - RequestScheduler, knockout, when, createCommand, @@ -47,14 +37,6 @@ define([ * @param {GeocoderService[]} [options.geocoderServices] Geocoder services to use for geocoding queries. * If more than one are supplied, suggestions will be gathered for the geocoders that support it, * and if no suggestion is selected the result from the first geocoder service wil be used. - * @param {String} [options.url='https://dev.virtualearth.net'] The base URL of the Bing Maps API. - * @param {String} [options.key] The Bing Maps key for your application, which can be - * created at {@link https://www.bingmapsportal.com}. - * If this parameter is not provided, {@link BingMapsApi.defaultKey} is used. - * If {@link BingMapsApi.defaultKey} is undefined as well, a message is - * written to the console reminding you that you must create and supply a Bing Maps - * key as soon as possible. Please do not deploy an application that uses - * this widget without creating a separate key for your application. * @param {Number} [options.flightDuration] The duration of the camera flight to an entered location, in seconds. */ function GeocoderViewModel(options) { @@ -69,29 +51,10 @@ define([ } else { this._geocoderServices = [ new CartographicGeocoderService(), - new BingMapsGeocoderService() + new BingMapsGeocoderService({scene: options.scene}) ]; } - var errorCredit; - this._url = defaultValue(options.url, 'https://dev.virtualearth.net/'); - if (this._url.length > 0 && this._url[this._url.length - 1] !== '/') { - this._url += '/'; - } - - this._key = BingMapsApi.getKey(options.key); - this._defaultGeocoderOptions = { - url: this._url, - key: this._key - }; - - if (defined(options.key)) { - errorCredit = BingMapsApi.getErrorCredit(options.key); - } - if (defined(errorCredit)) { - options.scene._frameState.creditDisplay.addDefaultCredit(errorCredit); - } - this._viewContainer = options.container; this._scene = options.scene; this._flightDuration = options.flightDuration; @@ -269,34 +232,6 @@ define([ } defineProperties(GeocoderViewModel.prototype, { - /** - * Gets the Bing maps url. - * @deprecated - * @memberof GeocoderViewModel.prototype - * - * @type {String} - */ - url : { - get : function() { - deprecationWarning('url is deprecated', 'The url property was deprecated in Cesium 1.30 and will be removed in version 1.31.'); - return this._url; - } - }, - - /** - * Gets the Bing maps key. - * @deprecated - * @memberof GeocoderViewModel.prototype - * - * @type {String} - */ - key : { - get : function() { - deprecationWarning('key is deprecated', 'The key property was deprecated in Cesium 1.30 and will be removed in version 1.31.'); - return this._key; - } - }, - /** * Gets the event triggered on flight completion. * @memberof GeocoderViewModel.prototype diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 63a858b38c9b..3fd5049fe410 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -7,6 +7,7 @@ define([ '../../Core/defineProperties', '../../Core/destroyObject', '../../Core/DeveloperError', + '../../Core/Event', '../../Core/EventHelper', '../../Core/isArray', '../../Core/Matrix4', @@ -49,6 +50,7 @@ define([ defineProperties, destroyObject, DeveloperError, + Event, EventHelper, isArray, Matrix4, @@ -661,6 +663,8 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._zoomTarget = undefined; this._zoomPromise = undefined; this._zoomOptions = undefined; + this._selectedEntityChanged = new Event(); + this._trackedEntityChanged = new Event(); knockout.track(this, ['_trackedEntity', '_selectedEntity', '_clockTrackedDataSource']); @@ -1168,12 +1172,13 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._entityView = undefined; this.camera.lookAtTransform(Matrix4.IDENTITY); - return; + } else { + //We can't start tracking immediately, so we set a flag and start tracking + //when the bounding sphere is ready (most likely next frame). + this._needTrackedEntityUpdate = true; } - //We can't start tracking immediately, so we set a flag and start tracking - //when the bounding sphere is ready (most likely next frame). - this._needTrackedEntityUpdate = true; + this._trackedEntityChanged.raiseEvent(value); } } }, @@ -1200,9 +1205,32 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to selectionIndicatorViewModel.animateDepart(); } } + this._selectedEntityChanged.raiseEvent(value); } } }, + /** + * Gets the event that is raised when the selected entity chages + * @memberof Viewer.prototype + * @type {Event} + * @readonly + */ + selectedEntityChanged : { + get : function() { + return this._selectedEntityChanged; + } + }, + /** + * Gets the event that is raised when the tracked entity chages + * @memberof Viewer.prototype + * @type {Event} + * @readonly + */ + trackedEntityChanged : { + get : function() { + return this._trackedEntityChanged; + } + }, /** * Gets or sets the data source to track with the viewer's clock. * @memberof Viewer.prototype diff --git a/Source/Workers/transcodeCRNToDXT.js b/Source/Workers/transcodeCRNToDXT.js index c1f57876e457..5f781025bc68 100644 --- a/Source/Workers/transcodeCRNToDXT.js +++ b/Source/Workers/transcodeCRNToDXT.js @@ -133,7 +133,10 @@ define([ // Mipmaps are unsupported, so copy the level 0 texture // When mipmaps are supported, a copy will still be necessary as dxtData is a view on the heap. - var level0DXTData = dxtData.slice(0, PixelFormat.compressedTextureSize(format, width, height)); + var length = PixelFormat.compressedTextureSize(format, width, height); + var level0DXTData = new Uint8Array(length); + level0DXTData.set(dxtData, 0); + transferableObjects.push(level0DXTData.buffer); return new CompressedTextureBuffer(format, width, height, level0DXTData); } diff --git a/Specs/Core/BingMapsGeocoderServiceSpec.js b/Specs/Core/BingMapsGeocoderServiceSpec.js index 5c68a7804d1c..7543deb78d4c 100644 --- a/Specs/Core/BingMapsGeocoderServiceSpec.js +++ b/Specs/Core/BingMapsGeocoderServiceSpec.js @@ -1,17 +1,32 @@ /*global defineSuite*/ defineSuite([ 'Core/BingMapsGeocoderService', - 'Core/Cartesian3', 'Core/loadJsonp', - 'Core/Rectangle' + 'Core/Rectangle', + 'Specs/createScene' ], function( BingMapsGeocoderService, - Cartesian3, loadJsonp, - Rectangle) { + Rectangle, + createScene) { 'use strict'; - var service = new BingMapsGeocoderService(); + var service; + var scene; + beforeEach(function() { + scene = createScene(); + service = new BingMapsGeocoderService({scene: scene}); + }); + + afterEach(function() { + scene.destroyForSpecs(); + }); + + it('constructor throws without scene', function() { + expect(function() { + return new BingMapsGeocoderService(); + }).toThrowDeveloperError(); + }); it('returns geocoder results', function (done) { var query = 'some query'; diff --git a/Specs/Core/GeometryPipelineSpec.js b/Specs/Core/GeometryPipelineSpec.js index 31005ab93ec2..71470fd08ffe 100644 --- a/Specs/Core/GeometryPipelineSpec.js +++ b/Specs/Core/GeometryPipelineSpec.js @@ -1410,6 +1410,50 @@ defineSuite([ expect(Cartesian3.fromArray(normals, 18)).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON7); }); + it('computeNormal computes normal of (0,0,1) for a degenerate triangle', function() { + var geometry = new Geometry({ + attributes: { + position: new GeometryAttribute({ + values: [0, 0, 0, 1, 0, 0], + componentsPerAttribute: 3, + componentDatatype : ComponentDatatype.FLOAT + }) + }, + indices : [0, 1, 0], + primitiveType: PrimitiveType.TRIANGLES + }); + + geometry = GeometryPipeline.computeNormal(geometry); + + expect(geometry.attributes.normal.values.length).toEqual(2*3); + expect(geometry.attributes.normal.values).toEqual([0, 0, 1, 0, 0, 1]); + }); + + it('computeNormal takes first normal for two coplanar triangles with opposite winding orders', function() { + var geometry = new Geometry({ + attributes: { + position: new GeometryAttribute({ + values: [0, 0, 0, 1, 0, 1, 1, 1, 1], + componentsPerAttribute: 3, + componentDatatype : ComponentDatatype.FLOAT + }) + }, + indices : [0, 1, 2, 2, 1, 0], + primitiveType: PrimitiveType.TRIANGLES + }); + + geometry = GeometryPipeline.computeNormal(geometry); + + var normals = geometry.attributes.normal.values; + expect(normals.length).toEqual(3*3); + + var a = Cartesian3.normalize(new Cartesian3(-1, 0, 1), new Cartesian3()); + + expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(a, CesiumMath.EPSILON7); + expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(a, CesiumMath.EPSILON7); + expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(a, CesiumMath.EPSILON7); + }); + it('computeTangentAndBitangent throws when geometry is undefined', function() { expect(function() { GeometryPipeline.computeTangentAndBitangent(); @@ -1668,45 +1712,6 @@ defineSuite([ } }); - it ('computeBinormalAndTangent computes tangent and binormal for BoxGeometry', function() { - // This test is for the deprecated computeBinormalAndTangent API - // It tests to assert that the binormal attribute is set correctly and - // is a copy of the bitangent attribute - var geometry = BoxGeometry.createGeometry(new BoxGeometry({ - vertexFormat : new VertexFormat({ - position : true, - normal : true, - st : true - }), - maximum : new Cartesian3(250000.0, 250000.0, 250000.0), - minimum : new Cartesian3(-250000.0, -250000.0, -250000.0) - })); - geometry = GeometryPipeline.computeBinormalAndTangent(geometry); - var actualTangents = geometry.attributes.tangent.values; - var actualBinormals = geometry.attributes.binormal.values; - - var expectedGeometry = BoxGeometry.createGeometry(new BoxGeometry({ - vertexFormat: VertexFormat.ALL, - maximum : new Cartesian3(250000.0, 250000.0, 250000.0), - minimum : new Cartesian3(-250000.0, -250000.0, -250000.0) - })); - var expectedTangents = expectedGeometry.attributes.tangent.values; - var expectedBitangents = expectedGeometry.attributes.bitangent.values; - - expect(actualTangents.length).toEqual(expectedTangents.length); - expect(actualBinormals.length).toEqual(expectedBitangents.length); - - for (var i = 0; i < actualTangents.length; i += 3) { - var actual = Cartesian3.fromArray(actualTangents, i); - var expected = Cartesian3.fromArray(expectedTangents, i); - expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1); - - actual = Cartesian3.fromArray(actualBinormals, i); - expected = Cartesian3.fromArray(expectedBitangents, i); - expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1); - } - }); - it('compressVertices throws without geometry', function() { expect(function() { return GeometryPipeline.compressVertices(); diff --git a/Specs/Core/ScreenSpaceEventHandlerSpec.js b/Specs/Core/ScreenSpaceEventHandlerSpec.js index 3ec9a6dba305..440a0f0f2104 100644 --- a/Specs/Core/ScreenSpaceEventHandlerSpec.js +++ b/Specs/Core/ScreenSpaceEventHandlerSpec.js @@ -387,9 +387,9 @@ defineSuite([ expect(action).not.toHaveBeenCalled(); } - var possibleButtons = [MouseButton.LEFT, MouseButton.MIDDLE, MouseButton.RIGHT]; + var possibleButtons = [MouseButton.LEFT]; var possibleModifiers = [undefined, KeyboardEventModifier.SHIFT, KeyboardEventModifier.CTRL, KeyboardEventModifier.ALT]; - var possibleEventTypes = [ScreenSpaceEventType.LEFT_DOUBLE_CLICK, ScreenSpaceEventType.MIDDLE_DOUBLE_CLICK, ScreenSpaceEventType.RIGHT_DOUBLE_CLICK]; + var possibleEventTypes = [ScreenSpaceEventType.LEFT_DOUBLE_CLICK]; createAllMouseSpecCombinations(testMouseDoubleClickEvent, possibleButtons, possibleModifiers, possibleEventTypes); }); diff --git a/Specs/Core/TileAvailabilitySpec.js b/Specs/Core/TileAvailabilitySpec.js index 1d20e98a46c2..d1bb25785fe9 100644 --- a/Specs/Core/TileAvailabilitySpec.js +++ b/Specs/Core/TileAvailabilitySpec.js @@ -2,14 +2,12 @@ defineSuite([ 'Core/TileAvailability', 'Core/Cartographic', - 'Core/CesiumTerrainProvider', 'Core/GeographicTilingScheme', 'Core/Rectangle', 'Core/WebMercatorTilingScheme' ], function( TileAvailability, Cartographic, - CesiumTerrainProvider, GeographicTilingScheme, Rectangle, WebMercatorTilingScheme) { @@ -123,4 +121,4 @@ defineSuite([ expect(availability.computeMaximumLevelAtPosition(new Cartographic(-Math.PI / 2.0, 0.0))).toBe(1); }); }); -}); \ No newline at end of file +}); diff --git a/Specs/Core/loadCRNSpec.js b/Specs/Core/loadCRNSpec.js index 40164fe346aa..7377bb260ba0 100644 --- a/Specs/Core/loadCRNSpec.js +++ b/Specs/Core/loadCRNSpec.js @@ -2,13 +2,11 @@ defineSuite([ 'Core/loadCRN', 'Core/PixelFormat', - 'Core/RequestErrorEvent', - 'Core/RuntimeError' + 'Core/RequestErrorEvent' ], function( loadCRN, PixelFormat, - RequestErrorEvent, - RuntimeError) { + RequestErrorEvent) { 'use strict'; var validCompressed = new Uint8Array([72, 120, 0, 74, 227, 123, 0, 0, 0, 138, 92, 167, 0, 4, 0, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 22, 0, 1, 0, 0, 96, 0, 0, 12, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 108, 0, 0, 0, 137, 0, 10, 96, 0, 0, 0, 0, 0, 0, 16, 4, 9, 130, 0, 0, 0, 0, 0, 0, 109, 4, 0, 0, 198, 96, 128, 0, 0, 0, 0, 0, 26, 80, 0, 0, 6, 96, 0, 0, 0, 0, 0, 0, 16, 0, 51, 0, 0, 0, 0, 0, 0, 0, 128, 1, 152, 0, 0, 0, 0, 0, 0, 4, 0]); diff --git a/Specs/Core/sampleTerrainMostDetailedSpec.js b/Specs/Core/sampleTerrainMostDetailedSpec.js index 470c92687131..bdb7ea157b8b 100644 --- a/Specs/Core/sampleTerrainMostDetailedSpec.js +++ b/Specs/Core/sampleTerrainMostDetailedSpec.js @@ -2,17 +2,11 @@ defineSuite([ 'Core/sampleTerrainMostDetailed', 'Core/Cartographic', - 'Core/CesiumTerrainProvider', - 'Core/GeographicTilingScheme', - 'Core/Rectangle', - 'ThirdParty/when' + 'Core/CesiumTerrainProvider' ], function( sampleTerrainMostDetailed, Cartographic, - CesiumTerrainProvider, - GeographicTilingScheme, - Rectangle, - when) { + CesiumTerrainProvider) { "use strict"; var terrainProvider = new CesiumTerrainProvider({ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ll.b3dm new file mode 100644 index 000000000000..6e5e1599da0f Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/lr.b3dm new file mode 100644 index 000000000000..c6c194e74e94 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/parent.b3dm new file mode 100644 index 000000000000..e5721889b1ca Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/tileset.json new file mode 100644 index 000000000000..6d557fdece67 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/tileset.json @@ -0,0 +1,174 @@ +{ + "asset": { + "version": "0.0", + "tilesetVersion": "1.2.3" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3197192952275933, + "maximum": -1.319644104024109 + }, + "Latitude": { + "minimum": 0.698848878034009, + "maximum": 0.6989046192460953 + }, + "Height": { + "minimum": 6.161747192963958, + "maximum": 84.83180232718587 + } + }, + "geometricError": 240, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 88 + ] + }, + "geometricError": 240, + "refine": "replace", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 88 + ] + }, + "geometricError": 70, + "refine": "replace", + "content": { + "url": "parent.b3dm", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + } + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "viewerRequestVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 50 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "viewerRequestVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 50 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "viewerRequestVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 50 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "viewerRequestVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 50 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ul.b3dm new file mode 100644 index 000000000000..4fd75a2453b6 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ur.b3dm new file mode 100644 index 000000000000..bd80912f82b7 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/embed.i3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/embed.i3dm new file mode 100644 index 000000000000..54c54da85479 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/embed.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.b3dm new file mode 100644 index 000000000000..31f8499e64f2 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.i3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.i3dm new file mode 100644 index 000000000000..5da1db5fe54d Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/external.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/Cesium_Logo_Flat.png b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/Cesium_Logo_Flat.png new file mode 100644 index 000000000000..3b8baee1bce0 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/Cesium_Logo_Flat.png differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/fragmentShader0.glsl b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/fragmentShader0.glsl new file mode 100644 index 000000000000..3b357000a18f --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/fragmentShader0.glsl @@ -0,0 +1,33 @@ +precision highp float; +uniform vec4 u_ambient; +uniform sampler2D u_diffuse; +uniform vec4 u_emission; +uniform vec4 u_specular; +uniform float u_shininess; +uniform float u_transparency; +varying vec3 v_positionEC; +varying vec3 v_normal; +varying vec2 v_texcoord_0; +void main(void) { + vec3 normal = normalize(v_normal); + vec4 diffuse = texture2D(u_diffuse, v_texcoord_0); + vec3 diffuseLight = vec3(0.0, 0.0, 0.0); + vec3 specular = u_specular.rgb; + vec3 specularLight = vec3(0.0, 0.0, 0.0); + vec3 emission = u_emission.rgb; + vec3 ambient = u_ambient.rgb; + vec3 viewDir = -normalize(v_positionEC); + vec3 ambientLight = vec3(0.0, 0.0, 0.0); + ambientLight += vec3(0.2, 0.2, 0.2); + vec3 l = vec3(0.0, 0.0, 1.0); + diffuseLight += vec3(1.0, 1.0, 1.0) * max(dot(normal,l), 0.); + vec3 reflectDir = reflect(-l, normal); + float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)); + specularLight += vec3(1.0, 1.0, 1.0) * specularIntensity; + vec3 color = vec3(0.0, 0.0, 0.0); + color += diffuse.rgb * diffuseLight; + color += specular * specularLight; + color += emission; + color += ambient * ambientLight; + gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency); +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/textured_box.glb b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/textured_box.glb new file mode 100644 index 000000000000..5f65357c9eee Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/textured_box.glb differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/vertexShader0.glsl b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/vertexShader0.glsl new file mode 100644 index 000000000000..c489a5595ff8 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/textured_box_separate/vertexShader0.glsl @@ -0,0 +1,17 @@ +precision highp float; +uniform mat4 u_modelViewMatrix; +uniform mat4 u_projectionMatrix; +uniform mat3 u_normalMatrix; +attribute vec3 a_position; +varying vec3 v_positionEC; +attribute vec3 a_normal; +varying vec3 v_normal; +attribute vec2 a_texcoord_0; +varying vec2 v_texcoord_0; +void main(void) { + vec4 pos = u_modelViewMatrix * vec4(a_position,1.0); + v_positionEC = pos.xyz; + gl_Position = u_projectionMatrix * pos; + v_normal = u_normalMatrix * a_normal; + v_texcoord_0 = a_texcoord_0; +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset.json new file mode 100644 index 000000000000..572a43b44cfa --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset.json @@ -0,0 +1,145 @@ +{ + "asset": { + "version": "0.0", + "tilesetVersion": "1.2.3" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "tileset2/tileset2.json" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "external.b3dm" + }, + "transform": [ + 0.9686325809759725, + 0.24849733011005554, + 0, + 0, + -0.15987226587942635, + 0.6231756512501834, + 0.7655670880409421, + 0, + 0.190241377398304, + -0.7415532243993574, + 0.6433560707015301, + 0, + 1215072.0326519238, + -4736301.828828896, + 4081608.4380407534, + 1 + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "external.i3dm" + }, + "transform": [ + 0.9686335987925251, + 0.24849336266838487, + 0, + 0, + -0.15986971340174239, + 0.6231763060686413, + 0.7655670880409422, + 0, + 0.1902383400555372, + -0.7415540036062118, + 0.6433560707015301, + 0, + 1215052.6331380012, + -4736306.80562453, + 4081608.4380407534, + 1 + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "embed.i3dm" + }, + "transform": [ + 0.9686346165928273, + 0.24848939522254557, + 0, + 0, + -0.15986716092137648, + 0.6231769608766445, + 0.7655670880409424, + 0, + 0.19023530270957903, + -0.7415547828006255, + 0.6433560707015301, + 0, + 1215033.2336036952, + -4736311.782340704, + 4081608.4380407534, + 1 + ] + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/embed.i3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/embed.i3dm new file mode 100644 index 000000000000..855968339418 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/embed.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.b3dm new file mode 100644 index 000000000000..83cc7bee91fa Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.i3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.i3dm new file mode 100644 index 000000000000..169a87d21900 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/external.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/tileset2.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/tileset2.json new file mode 100644 index 000000000000..8a51f4353085 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources/tileset2/tileset2.json @@ -0,0 +1,127 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "external.b3dm" + }, + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.1598646084383285, + 0.6231776156741929, + 0.7655670880409422, + 0, + 0.19023226536042928, + -0.7415555619825982, + 0.6433560707015301, + 0, + 1215013.8340490046, + -4736316.75897742, + 4081608.4380407534, + 1 + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "external.i3dm" + }, + "transform": [ + 0.9686366521446808, + 0.2484814603183605, + 0, + 0, + -0.1598620559525986, + 0.6231782704612867, + 0.7655670880409423, + 0, + 0.19022922800808817, + -0.7415563411521302, + 0.6433560707015301, + 0, + 1214994.4344739306, + -4736321.735534675, + 4081608.4380407534, + 1 + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "embed.i3dm" + }, + "transform": [ + 0.9686376698962317, + 0.24847749286001491, + 0, + 0, + -0.1598595034641867, + 0.6231789252379254, + 0.7655670880409422, + 0, + 0.19022619065255567, + -0.7415571203092216, + 0.6433560707015301, + 0, + 1214975.0348784733, + -4736326.712012473, + 4081608.4380407534, + 1 + ] + } + ] + } +} diff --git a/Specs/DataSources/LabelGraphicsSpec.js b/Specs/DataSources/LabelGraphicsSpec.js index 1575a6057bd2..d210b3035904 100644 --- a/Specs/DataSources/LabelGraphicsSpec.js +++ b/Specs/DataSources/LabelGraphicsSpec.js @@ -39,6 +39,7 @@ defineSuite([ show : true, translucencyByDistance : new NearFarScalar(9, 10, 11, 12), pixelOffsetScaleByDistance : new NearFarScalar(13, 14, 15, 16), + scaleByDistance : new NearFarScalar(17, 18, 19, 20), distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0) }; @@ -56,6 +57,7 @@ defineSuite([ expect(label.show).toBeInstanceOf(ConstantProperty); expect(label.translucencyByDistance).toBeInstanceOf(ConstantProperty); expect(label.pixelOffsetScaleByDistance).toBeInstanceOf(ConstantProperty); + expect(label.scaleByDistance).toBeInstanceOf(ConstantProperty); expect(label.distanceDisplayCondition).toBeInstanceOf(ConstantProperty); expect(label.text.getValue()).toEqual(options.text); @@ -71,6 +73,7 @@ defineSuite([ expect(label.show.getValue()).toEqual(options.show); expect(label.translucencyByDistance.getValue()).toEqual(options.translucencyByDistance); expect(label.pixelOffsetScaleByDistance.getValue()).toEqual(options.pixelOffsetScaleByDistance); + expect(label.scaleByDistance.getValue()).toEqual(options.scaleByDistance); expect(label.distanceDisplayCondition.getValue()).toEqual(options.distanceDisplayCondition); }); @@ -90,6 +93,7 @@ defineSuite([ source.show = new ConstantProperty(false); source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); + source.scaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); var target = new LabelGraphics(); @@ -109,6 +113,7 @@ defineSuite([ expect(target.show).toBe(source.show); expect(target.translucencyByDistance).toBe(source.translucencyByDistance); expect(target.pixelOffsetScaleByDistance).toBe(source.pixelOffsetScaleByDistance); + expect(target.scaleByDistance).toBe(source.scaleByDistance); expect(target.distanceDisplayCondition).toBe(source.distanceDisplayCondition); }); @@ -128,6 +133,7 @@ defineSuite([ source.show = new ConstantProperty(false); source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); + source.scaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); var text = new ConstantProperty('my text'); @@ -144,6 +150,7 @@ defineSuite([ var show = new ConstantProperty(true); var translucencyByDistance = new ConstantProperty(new NearFarScalar()); var pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar()); + var scaleByDistance = new ConstantProperty(new NearFarScalar()); var distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); var target = new LabelGraphics(); @@ -161,6 +168,7 @@ defineSuite([ target.show = show; target.translucencyByDistance = translucencyByDistance; target.pixelOffsetScaleByDistance = pixelOffsetScaleByDistance; + target.scaleByDistance = scaleByDistance; target.distanceDisplayCondition = distanceDisplayCondition; target.merge(source); @@ -179,6 +187,7 @@ defineSuite([ expect(target.show).toBe(show); expect(target.translucencyByDistance).toBe(translucencyByDistance); expect(target.pixelOffsetScaleByDistance).toBe(pixelOffsetScaleByDistance); + expect(target.scaleByDistance).toBe(scaleByDistance); expect(target.distanceDisplayCondition).toBe(distanceDisplayCondition); }); @@ -198,6 +207,7 @@ defineSuite([ source.show = new ConstantProperty(false); source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); + source.scaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); var result = source.clone(); @@ -215,6 +225,7 @@ defineSuite([ expect(result.show).toBe(source.show); expect(result.translucencyByDistance).toBe(source.translucencyByDistance); expect(result.pixelOffsetScaleByDistance).toBe(source.pixelOffsetScaleByDistance); + expect(result.scaleByDistance).toBe(source.scaleByDistance); expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition); }); diff --git a/Specs/DataSources/LabelVisualizerSpec.js b/Specs/DataSources/LabelVisualizerSpec.js index 55021d652b94..7805b4f18345 100644 --- a/Specs/DataSources/LabelVisualizerSpec.js +++ b/Specs/DataSources/LabelVisualizerSpec.js @@ -154,6 +154,7 @@ defineSuite([ label.show = new ConstantProperty(true); label.translucencyByDistance = new ConstantProperty(new NearFarScalar()); label.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar()); + label.scaleByDistance = new ConstantProperty(new NearFarScalar()); label.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); visualizer.update(time); @@ -179,6 +180,7 @@ defineSuite([ expect(l.show).toEqual(testObject.label.show.getValue(time)); expect(l.translucencyByDistance).toEqual(testObject.label.translucencyByDistance.getValue(time)); expect(l.pixelOffsetScaleByDistance).toEqual(testObject.label.pixelOffsetScaleByDistance.getValue(time)); + expect(l.scaleByDistance).toEqual(testObject.label.scaleByDistance.getValue(time)); expect(l.distanceDisplayCondition).toEqual(testObject.label.distanceDisplayCondition.getValue(time)); testObject.position = new ConstantProperty(new Cartesian3(5678, 1234, 1293434)); @@ -196,6 +198,7 @@ defineSuite([ label.show = new ConstantProperty(true); label.translucencyByDistance = new ConstantProperty(new NearFarScalar()); label.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar()); + label.scaleByDistance = new ConstantProperty(new NearFarScalar()); label.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); visualizer.update(time); @@ -214,6 +217,7 @@ defineSuite([ expect(l.show).toEqual(testObject.label.show.getValue(time)); expect(l.translucencyByDistance).toEqual(testObject.label.translucencyByDistance.getValue(time)); expect(l.pixelOffsetScaleByDistance).toEqual(testObject.label.pixelOffsetScaleByDistance.getValue(time)); + expect(l.scaleByDistance).toEqual(testObject.label.scaleByDistance.getValue(time)); expect(l.distanceDisplayCondition).toEqual(testObject.label.distanceDisplayCondition.getValue(time)); label.show = new ConstantProperty(false); diff --git a/Specs/Renderer/CubeMapSpec.js b/Specs/Renderer/CubeMapSpec.js index 6b4b7b6300d6..4962c6d4b222 100644 --- a/Specs/Renderer/CubeMapSpec.js +++ b/Specs/Renderer/CubeMapSpec.js @@ -5,20 +5,14 @@ defineSuite([ 'Core/Color', 'Core/loadImage', 'Core/PixelFormat', - 'Core/PrimitiveType', - 'Renderer/Buffer', - 'Renderer/BufferUsage', 'Renderer/ClearCommand', 'Renderer/ContextLimits', - 'Renderer/DrawCommand', 'Renderer/PixelDatatype', 'Renderer/Sampler', - 'Renderer/ShaderProgram', 'Renderer/Texture', 'Renderer/TextureMagnificationFilter', 'Renderer/TextureMinificationFilter', 'Renderer/TextureWrap', - 'Renderer/VertexArray', 'Specs/createContext', 'ThirdParty/when' ], function( @@ -27,20 +21,14 @@ defineSuite([ Color, loadImage, PixelFormat, - PrimitiveType, - Buffer, - BufferUsage, ClearCommand, ContextLimits, - DrawCommand, PixelDatatype, Sampler, - ShaderProgram, Texture, TextureMagnificationFilter, TextureMinificationFilter, TextureWrap, - VertexArray, createContext, when) { 'use strict'; diff --git a/Specs/Renderer/FramebufferSpec.js b/Specs/Renderer/FramebufferSpec.js index 88f31c03e0a6..d733d5899202 100644 --- a/Specs/Renderer/FramebufferSpec.js +++ b/Specs/Renderer/FramebufferSpec.js @@ -527,15 +527,22 @@ defineSuite([ return; } + var source = new Uint8Array(4); var colorTexture0 = new Texture({ context : context, - width : 1, - height : 1 + source : { + arrayBufferView : source, + width : 1, + height : 1 + } }); var colorTexture1 = new Texture({ context : context, - width : 1, - height : 1 + source : { + arrayBufferView : source, + width : 1, + height : 1 + } }); framebuffer = new Framebuffer({ context : context, diff --git a/Specs/Renderer/ShaderCacheSpec.js b/Specs/Renderer/ShaderCacheSpec.js index 953890a471d1..aa23202aa3b7 100644 --- a/Specs/Renderer/ShaderCacheSpec.js +++ b/Specs/Renderer/ShaderCacheSpec.js @@ -165,6 +165,69 @@ defineSuite([ cache.destroy(); }); + it('create derived shader program', function() { + var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; + var fs = 'void main() { gl_FragColor = vec4(1.0); }'; + + var cache = new ShaderCache(context); + var sp = cache.getShaderProgram({ + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : { + position : 0 + } + }); + + var keyword = 'derived'; + var spDerived = cache.getDerivedShaderProgram(sp, keyword); + expect(spDerived).not.toBeDefined(); + + var fsDerived = 'void main() { gl_FragColor = vec4(vec3(1.0), 0.5); }'; + spDerived = cache.createDerivedShaderProgram(sp, keyword, { + vertexShaderSource : vs, + fragmentShaderSource : fsDerived, + attributeLocations : { + position : 0 + } + }); + expect(spDerived).toBeDefined(); + + cache.destroy(); + }); + + it('destroying a shader program destroys its derived shaders', function() { + var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; + var fs = 'void main() { gl_FragColor = vec4(1.0); }'; + + var cache = new ShaderCache(context); + var sp = cache.getShaderProgram({ + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : { + position : 0 + } + }); + + var keyword = 'derived'; + var fsDerived = 'void main() { gl_FragColor = vec4(vec3(1.0), 0.5); }'; + var spDerived = cache.createDerivedShaderProgram(sp, keyword, { + vertexShaderSource : vs, + fragmentShaderSource : fsDerived, + attributeLocations : { + position : 0 + } + }); + expect(spDerived).toBeDefined(); + + sp.destroy(); + cache.destroyReleasedShaderPrograms(); + + expect(sp.isDestroyed()).toEqual(true); + expect(spDerived.isDestroyed()).toEqual(true); + + cache.destroy(); + }); + it('is destroyed', function() { var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; diff --git a/Specs/Renderer/ShaderProgramSpec.js b/Specs/Renderer/ShaderProgramSpec.js index fdb4e9e68f84..374460e21fd7 100644 --- a/Specs/Renderer/ShaderProgramSpec.js +++ b/Specs/Renderer/ShaderProgramSpec.js @@ -1,25 +1,13 @@ /*global defineSuite*/ defineSuite([ 'Renderer/ShaderProgram', - 'Core/PrimitiveType', - 'Renderer/Buffer', - 'Renderer/BufferUsage', - 'Renderer/ClearCommand', 'Renderer/ContextLimits', - 'Renderer/DrawCommand', 'Renderer/ShaderSource', - 'Renderer/VertexArray', 'Specs/createContext' ], function( ShaderProgram, - PrimitiveType, - Buffer, - BufferUsage, - ClearCommand, ContextLimits, - DrawCommand, ShaderSource, - VertexArray, createContext) { 'use strict'; diff --git a/Specs/Renderer/TextureSpec.js b/Specs/Renderer/TextureSpec.js index a6df8c418a05..3ecf5c20b21c 100644 --- a/Specs/Renderer/TextureSpec.js +++ b/Specs/Renderer/TextureSpec.js @@ -6,19 +6,13 @@ defineSuite([ 'Core/loadImage', 'Core/loadKTX', 'Core/PixelFormat', - 'Core/PrimitiveType', - 'Renderer/Buffer', - 'Renderer/BufferUsage', 'Renderer/ClearCommand', 'Renderer/ContextLimits', - 'Renderer/DrawCommand', 'Renderer/PixelDatatype', 'Renderer/Sampler', - 'Renderer/ShaderProgram', 'Renderer/TextureMagnificationFilter', 'Renderer/TextureMinificationFilter', 'Renderer/TextureWrap', - 'Renderer/VertexArray', 'Specs/createContext', 'ThirdParty/when' ], function( @@ -28,19 +22,13 @@ defineSuite([ loadImage, loadKTX, PixelFormat, - PrimitiveType, - Buffer, - BufferUsage, ClearCommand, ContextLimits, - DrawCommand, PixelDatatype, Sampler, - ShaderProgram, TextureMagnificationFilter, TextureMinificationFilter, TextureWrap, - VertexArray, createContext, when) { 'use strict'; diff --git a/Specs/Scene/Cesium3DTileBatchTableSpec.js b/Specs/Scene/Cesium3DTileBatchTableSpec.js index 8a4f71efac4a..a1100e13fe2b 100644 --- a/Specs/Scene/Cesium3DTileBatchTableSpec.js +++ b/Specs/Scene/Cesium3DTileBatchTableSpec.js @@ -37,7 +37,7 @@ defineSuite([ var withBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTable/'; var withoutBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/'; - var batchLengthZeroUrl = './Data/Cesium3DTiles/Batched/BatchedNoBatchIds/'; + var noBatchIdsUrl = './Data/Cesium3DTiles/Batched/BatchedNoBatchIds/'; var batchTableHierarchyUrl = './Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/'; var batchTableHierarchyBinaryUrl = './Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/'; var batchTableHierarchyMultipleParentsUrl = './Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/'; @@ -570,12 +570,12 @@ defineSuite([ }); it('renders with featuresLength of zero', function() { - return Cesium3DTilesTester.loadTileset(scene, batchLengthZeroUrl).then(function(tileset) { + return Cesium3DTilesTester.loadTileset(scene, noBatchIdsUrl).then(function(tileset) { Cesium3DTilesTester.expectRender(scene, tileset); expect(scene).toPickAndCall(function(result) { expect(result).toBeDefined(); - expect(result.primitive).toBe(tileset._root.content._model); + expect(result.primitive).toBe(tileset); }); }); }); diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index ae4323cc509e..35e5bf2dc55e 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -13,6 +13,7 @@ defineSuite([ 'Scene/Cesium3DTile', 'Scene/Cesium3DTileColorBlendMode', 'Scene/Cesium3DTileContentState', + 'Scene/Cesium3DTileOptimizations', 'Scene/Cesium3DTileRefine', 'Scene/Cesium3DTileStyle', 'Scene/CullingVolume', @@ -34,6 +35,7 @@ defineSuite([ Cesium3DTile, Cesium3DTileColorBlendMode, Cesium3DTileContentState, + Cesium3DTileOptimizations, Cesium3DTileRefine, Cesium3DTileStyle, CullingVolume, @@ -70,6 +72,7 @@ defineSuite([ var withoutBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/'; var withBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTable/'; + var noBatchIdsUrl = './Data/Cesium3DTiles/Batched/BatchedNoBatchIds/'; var withTransformBoxUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformBox/'; var withTransformSphereUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/'; @@ -97,8 +100,16 @@ defineSuite([ // Root tile with 4 b3dm children and 1 pnts child with a viewer request volume var tilesetWithViewerRequestVolumeUrl = './Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume'; + // Parent tile with content and four child tiles with content with viewer request volume for each child + var tilesetReplacementWithViewerRequestVolumeUrl = './Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume'; + + var tilesetWithExternalResources = './Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources'; + var styleUrl = './Data/Cesium3DTiles/Style/style.json'; + var pointCloudUrl = './Data/Cesium3DTiles/PointCloud/PointCloudRGB'; + var pointCloudBatchedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudBatched'; + var originalMaximumRequests; beforeAll(function() { @@ -182,7 +193,7 @@ defineSuite([ }); it('url and tilesetUrl set up correctly given tileset.json path', function() { - var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3.json'; + var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json'; var tileset = new Cesium3DTileset({ url : path }); @@ -252,6 +263,30 @@ defineSuite([ }); }); + it('passes version in query string to all external resources', function() { + //Spy on loadWithXhr so we can verify requested urls + spyOn(loadWithXhr, 'load').and.callThrough(); + + var queryParams = '?a=1&b=boy'; + var queryParamsWithVersion = '?a=1&b=boy&v=1.2.3'; + return Cesium3DTilesTester.loadTileset(scene, tilesetWithExternalResources + queryParams).then(function(tileset) { + var calls = loadWithXhr.load.calls.all(); + var callsLength = calls.length; + for (var i = 0; i < callsLength; ++i) { + var url = calls[0].args[0]; + if (url.indexOf(tilesetWithExternalResources) >= 0) { + var query = url.slice(url.indexOf('?')); + if (url.indexOf('tileset.json') >= 0) { + // The initial tileset.json does not have a tileset version parameter + expect(query).toBe(queryParams); + } else { + expect(query).toBe(queryParamsWithVersion); + } + } + } + }); + }); + it('throws when getting asset and tileset is not ready', function() { var tileset = new Cesium3DTileset({ url : tilesetUrl @@ -373,6 +408,100 @@ defineSuite([ }); }); + function checkPointAndFeatureCounts(tileset, features, points) { + var stats = tileset._statistics; + + expect(stats.numberOfFeaturesSelected).toEqual(0); + expect(stats.numberOfFeaturesLoaded).toEqual(0); + expect(stats.numberOfPointsSelected).toEqual(0); + expect(stats.numberOfPointsLoaded).toEqual(0); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + scene.renderForSpecs(); + expect(stats.numberOfFeaturesSelected).toEqual(features); + expect(stats.numberOfFeaturesLoaded).toEqual(features); + expect(stats.numberOfPointsSelected).toEqual(points); + expect(stats.numberOfPointsLoaded).toEqual(points); + + viewNothing(); + scene.renderForSpecs(); + + expect(stats.numberOfFeaturesSelected).toEqual(0); + expect(stats.numberOfFeaturesLoaded).toEqual(features); + expect(stats.numberOfPointsSelected).toEqual(0); + expect(stats.numberOfPointsLoaded).toEqual(points); + + tileset.trimLoadedTiles(); + + scene.renderForSpecs(); + + expect(stats.numberOfFeaturesSelected).toEqual(0); + expect(stats.numberOfFeaturesLoaded).toEqual(0); + expect(stats.numberOfPointsSelected).toEqual(0); + expect(stats.numberOfPointsLoaded).toEqual(0); + }); + } + + it('verify batched features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : withBatchTableUrl + })); + + return checkPointAndFeatureCounts(tileset, 10, 0); + }); + + it('verify no batch table features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : noBatchIdsUrl + })); + + return checkPointAndFeatureCounts(tileset, 0, 0); + }); + + it('verify instanced features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : instancedRedMaterialUrl + })); + + return checkPointAndFeatureCounts(tileset, 25, 0); + }); + + it('verify composite features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : compositeUrl + })); + + return checkPointAndFeatureCounts(tileset, 35, 0); + }); + + it('verify tileset of tilesets features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetOfTilesetsUrl + })); + + return checkPointAndFeatureCounts(tileset, 50, 0); + }); + + it('verify points statistics', function() { + scene.camera.lookAt(Cartesian3.fromRadians(-1.31968, 0.698874, 5.0), new HeadingPitchRange(0.0, -1.57, 5.0)); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : pointCloudUrl + })); + + return checkPointAndFeatureCounts(tileset, 0, 1000); + }); + + it('verify batched points statistics', function() { + scene.camera.lookAt(Cartesian3.fromRadians(-1.31968, 0.698874, 5.0), new HeadingPitchRange(0.0, -1.57, 5.0)); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : pointCloudBatchedUrl + })); + + return checkPointAndFeatureCounts(tileset, 8, 1000); + }); + it('does not process tileset when screen space error is not met', function() { return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { scene.renderForSpecs(); @@ -840,6 +969,158 @@ defineSuite([ }); }); + describe('children bound union optimization', function() { + it('does not select visible tiles with invisible children', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + scene.camera.setView({ + destination: Cartesian3.fromRadians(centerLongitude, centerLatitude, 22), + orientation: { + heading: 0, + pitch: 1.57, + roll: 0 + } + }); + + var root = tileset._root; + var childRoot = root.children[0]; + + scene.renderForSpecs(); + + expect(childRoot.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.children[0].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[1].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[2].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[3].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).toEqual(CullingVolume.MASK_OUTSIDE); + + expect(tileset._selectedTiles.length).toEqual(0); + expect(childRoot.selected).toBe(false); + }); + }); + + it('does not select visible tiles not meeting SSE with visible children', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + var root = tileset._root; + var childRoot = root.children[0]; + childRoot.geometricError = 240; + + scene.camera.setView({ + destination: Cartesian3.fromRadians(centerLongitude, centerLatitude, 49), + orientation: { + heading: 0, + pitch: -1.57, + roll: 0 + } + }); + + scene.renderForSpecs(); + + expect(childRoot.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.children[0].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[1].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[2].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[3].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.selected).toBe(false); + expect(childRoot.replaced).toBe(true); + }); + }); + + it ('does select visible tiles meeting SSE with visible children', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + var root = tileset._root; + var childRoot = root.children[0]; + + scene.camera.setView({ + destination: Cartesian3.fromRadians(centerLongitude, centerLatitude, 49), + orientation: { + heading: 0, + pitch: -1.57, + roll: 0 + } + }); + + childRoot.geometricError = 0; + scene.renderForSpecs(); + + expect(childRoot.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.children[0].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[1].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[2].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[3].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.selected).toBe(true); + expect(childRoot.replaced).toBe(false); + }); + }); + + it('does select visibile tiles with visible children failing request volumes', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + scene.camera.setView({ + destination: Cartesian3.fromRadians(centerLongitude, centerLatitude, 100), + orientation: { + heading: 0, + pitch: -1.57, + roll: 0 + } + }); + + var root = tileset._root; + var childRoot = root.children[0]; + childRoot.geometricError = 0; + + scene.renderForSpecs(); + + expect(childRoot.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.children[0].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[1].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[2].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[3].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(tileset._selectedTiles.length).toEqual(1); + expect(childRoot.selected).toBe(true); + }); + }); + + it('does select visibile tiles with visible children passing request volumes', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + scene.camera.setView({ + destination: Cartesian3.fromRadians(centerLongitude, centerLatitude, 40), + orientation: { + heading: 0, + pitch: -1.57, + roll: 0 + } + }); + + var root = tileset._root; + var childRoot = root.children[0]; + childRoot.geometricError = 0; + + scene.renderForSpecs(); + + expect(childRoot.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(childRoot.children[0].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[1].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[2].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(childRoot.children[3].visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + + expect(tileset._selectedTiles.length).toEqual(1); + expect(childRoot.selected).toBe(true); + + childRoot.geometricError = 200; + scene.renderForSpecs(); + expect(tileset._selectedTiles.length).toEqual(4); + expect(childRoot.selected).toBe(false); + expect(childRoot.replaced).toBe(true); + }); + }); + }); + it('loads tileset with external tileset.json', function() { // Set view so that no tiles are loaded initially viewNothing(); @@ -1000,6 +1281,61 @@ defineSuite([ }); }); + it('debugShowGeometricError with regions', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).toBeDefined(); + expect(tileset._geometricErrorLabels.length).toEqual(5); + expect(tileset._geometricErrorLabels._labels[0].text).toEqual('70'); + expect(tileset._geometricErrorLabels._labels[1].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[2].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[3].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[4].text).toEqual('0'); + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).not.toBeDefined(); + }); + }); + + it('debugShowGeometricError with boxes', function() { + // tilesetWithTransformsUrl has bounding boxes + return Cesium3DTilesTester.loadTileset(scene, tilesetWithTransformsUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).toBeDefined(); + expect(tileset._geometricErrorLabels.length).toEqual(2); + expect(tileset._geometricErrorLabels._labels[0].text).toEqual('70'); + expect(tileset._geometricErrorLabels._labels[1].text).toEqual('0'); + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).not.toBeDefined(); + }); + }); + + it('debugShowGeometricError with bounding spheres', function() { + // tilesetWithViewerRequestVolumeUrl has bounding sphere + return Cesium3DTilesTester.loadTileset(scene, tilesetWithViewerRequestVolumeUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).toBeDefined(); + expect(tileset._geometricErrorLabels.length).toEqual(6); + expect(tileset._geometricErrorLabels._labels[0].text).toEqual('70'); + expect(tileset._geometricErrorLabels._labels[1].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[2].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[3].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[4].text).toEqual('0'); + expect(tileset._geometricErrorLabels._labels[5].text).toEqual('0'); + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._geometricErrorLabels).not.toBeDefined(); + }); + }); + it('does not request tiles when picking', function() { viewNothing(); return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index 26219853c55b..6d4a67e36a6c 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -2011,11 +2011,11 @@ defineSuite([ it('throws if fract function takes an invalid number of arguments', function() { expect(function() { - return new Expression('log2()'); + return new Expression('fract()'); }).toThrowDeveloperError(); expect(function() { - return new Expression('log2(1, 2)'); + return new Expression('fract(1, 2)'); }).toThrowDeveloperError(); }); diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 694e42eaf079..5a0830cf6ef6 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -6,7 +6,6 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Color', - 'Core/defined', 'Core/DistanceDisplayCondition', 'Core/Math', 'Core/NearFarScalar', @@ -27,7 +26,6 @@ defineSuite([ Cartesian2, Cartesian3, Color, - defined, DistanceDisplayCondition, CesiumMath, NearFarScalar, @@ -100,6 +98,7 @@ defineSuite([ expect(label.id).not.toBeDefined(); expect(label.translucencyByDistance).not.toBeDefined(); expect(label.pixelOffsetScaleByDistance).not.toBeDefined(); + expect(label.scaleByDistance).not.toBeDefined(); expect(label.distanceDisplayCondition).not.toBeDefined(); }); @@ -133,6 +132,7 @@ defineSuite([ var backgroundPadding = new Cartesian2(11, 12); var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var pixelOffsetScale = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); + var scaleByDistance = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0); var label = labels.add({ show : show, @@ -154,6 +154,7 @@ defineSuite([ id : 'id', translucencyByDistance : translucency, pixelOffsetScaleByDistance : pixelOffsetScale, + scaleByDistance : scaleByDistance, distanceDisplayCondition : distanceDisplayCondition }); @@ -176,6 +177,7 @@ defineSuite([ expect(label.id).toEqual('id'); expect(label.translucencyByDistance).toEqual(translucency); expect(label.pixelOffsetScaleByDistance).toEqual(pixelOffsetScale); + expect(label.scaleByDistance).toEqual(scaleByDistance); expect(label.distanceDisplayCondition).toEqual(distanceDisplayCondition); }); @@ -617,6 +619,22 @@ defineSuite([ expect(scene).toRender([0, 0, 0, 255]); }); + it('renders label with scaleByDistance', function() { + labels.add({ + position : Cartesian3.ZERO, + text : solidBox, + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER, + scaleByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0) + }); + + camera.position = new Cartesian3(2.0, 0.0, 0.0); + expect(scene).toRender([255, 255, 255, 255]); + + camera.position = new Cartesian3(4.0, 0.0, 0.0); + expect(scene).toRender([0, 0, 0, 255]); + }); + it('renders label with distanceDisplayCondition', function() { labels.add({ position : Cartesian3.ZERO, @@ -886,6 +904,7 @@ defineSuite([ var scale = 2.0; var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var pixelOffsetScale = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); + var scaleByDistance = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); label.show = show; label.position = position; @@ -902,6 +921,7 @@ defineSuite([ label.scale = scale; label.translucencyByDistance = translucency; label.pixelOffsetScaleByDistance = pixelOffsetScale; + label.scaleByDistance = scaleByDistance; expect(label.show).toEqual(show); expect(label.position).toEqual(position); @@ -918,6 +938,7 @@ defineSuite([ expect(label.scale).toEqual(scale); expect(label.translucencyByDistance).toEqual(translucency); expect(label.pixelOffsetScaleByDistance).toEqual(pixelOffsetScale); + expect(label.scaleByDistance).toEqual(scaleByDistance); }); it('is destroyed after being removed', function() { @@ -1212,6 +1233,8 @@ defineSuite([ var translucency2 = new NearFarScalar(1.1e4, 1.2, 1.3e6, 4.0); var pixelOffsetScale1 = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var pixelOffsetScale2 = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0); + var scaleByDistance1 = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); + var scaleByDistance2 = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0); var label = labels.add({ position : position1, @@ -1223,6 +1246,7 @@ defineSuite([ id : id1, translucencyByDistance : translucency1, pixelOffsetScaleByDistance : pixelOffsetScale1, + scaleByDistance : scaleByDistance1, showBackground : true }); @@ -1237,6 +1261,7 @@ defineSuite([ label.id = id2; label.translucencyByDistance = translucency2; label.pixelOffsetScaleByDistance = pixelOffsetScale2; + label.scaleByDistance = scaleByDistance2; scene.renderForSpecs(); @@ -1253,6 +1278,7 @@ defineSuite([ expect(billboard.id).toEqual(label.id); expect(billboard.translucencyByDistance).toEqual(label.translucencyByDistance); expect(billboard.pixelOffsetScaleByDistance).toEqual(label.pixelOffsetScaleByDistance); + expect(billboard.scaleByDistance).toEqual(label.scaleByDistance); expect(billboard.pickPrimitive).toEqual(label); } @@ -1271,6 +1297,7 @@ defineSuite([ id : 'id1', translucencyByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0), pixelOffsetScaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0), + scaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0), showBackground : true }); scene.renderForSpecs(); @@ -1392,6 +1419,17 @@ defineSuite([ }); }); + it('scaleByDistance', function() { + var newValue = new NearFarScalar(1.5e4, 1.6, 1.7e6, 8.0); + expect(label.scaleByDistance).not.toEqual(newValue); + label.scaleByDistance = newValue; + scene.renderForSpecs(); + + getGlyphBillboards().forEach(function(billboard) { + expect(billboard.scaleByDistance).toEqual(label.scaleByDistance); + }); + }); + it('translucencyByDistance to undefined', function() { var newValue; expect(label.translucencyByDistance).not.toEqual(newValue); @@ -1414,6 +1452,17 @@ defineSuite([ }); }); + it('scaleByDistance to undefined', function() { + var newValue; + expect(label.scaleByDistance).not.toEqual(newValue); + label.scaleByDistance = newValue; + scene.renderForSpecs(); + + getGlyphBillboards().forEach(function(billboard) { + expect(billboard.scaleByDistance).toEqual(label.scaleByDistance); + }); + }); + it('clusterShow', function() { expect(label.clusterShow).toEqual(true); label.clusterShow = false; @@ -1954,6 +2003,14 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('Label.scaleByDistance throws with nearDistance === farDistance', function() { + var label = labels.add(); + var scaleByDistance = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + label.scaleByDistance = scaleByDistance; + }).toThrowDeveloperError(); + }); + it('new label throws with invalid translucencyByDistance (nearDistance === farDistance)', function() { var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { @@ -1972,6 +2029,15 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('new label throws with invalid scaleByDistance (nearDistance === farDistance)', function() { + var scaleByDistance = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + labels.add({ + scaleByDistance : scaleByDistance + }); + }).toThrowDeveloperError(); + }); + it('Label.translucencyByDistance throws with nearDistance > farDistance', function() { var label = labels.add(); var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); @@ -1988,6 +2054,14 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('Label.scaleByDistance throws with nearDistance > farDistance', function() { + var label = labels.add(); + var scaleByDistance = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); + expect(function() { + label.scaleByDistance = scaleByDistance; + }).toThrowDeveloperError(); + }); + it('destroys texture atlas when destroying', function() { labels.add({ text : 'a' diff --git a/Specs/Scene/MaterialSpec.js b/Specs/Scene/MaterialSpec.js index a38ca9122b8a..d6bfaf4f4008 100644 --- a/Specs/Scene/MaterialSpec.js +++ b/Specs/Scene/MaterialSpec.js @@ -7,10 +7,8 @@ defineSuite([ 'Core/defined', 'Core/Ellipsoid', 'Core/GeometryInstance', - 'Core/loadImage', 'Core/Rectangle', 'Core/RectangleGeometry', - 'Renderer/Texture', 'Scene/MaterialAppearance', 'Scene/PolylineCollection', 'Scene/Primitive', @@ -24,10 +22,8 @@ defineSuite([ defined, Ellipsoid, GeometryInstance, - loadImage, Rectangle, RectangleGeometry, - Texture, MaterialAppearance, PolylineCollection, Primitive, diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 45b4e9fa4777..58ff84eeb970 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -1,6 +1,5 @@ /*global defineSuite*/ defineSuite([ - 'Core/Cartesian2', 'Core/FeatureDetection', 'Core/GeometryInstance', 'Core/Math', @@ -13,7 +12,6 @@ defineSuite([ 'Scene/SceneMode', 'Specs/createScene' ], 'Scene/Pick', function( - Cartesian2, FeatureDetection, GeometryInstance, CesiumMath, diff --git a/Specs/Scene/SceneSpec.js b/Specs/Scene/SceneSpec.js index 5ae060f103aa..df8e6649e9d9 100644 --- a/Specs/Scene/SceneSpec.js +++ b/Specs/Scene/SceneSpec.js @@ -8,6 +8,7 @@ defineSuite([ 'Core/Ellipsoid', 'Core/GeographicProjection', 'Core/GeometryInstance', + 'Core/Math', 'Core/PixelFormat', 'Core/Rectangle', 'Core/RectangleGeometry', @@ -18,7 +19,9 @@ defineSuite([ 'Renderer/Framebuffer', 'Renderer/Pass', 'Renderer/PixelDatatype', + 'Renderer/RenderState', 'Renderer/ShaderProgram', + 'Renderer/ShaderSource', 'Renderer/Texture', 'Scene/Camera', 'Scene/EllipsoidSurfaceAppearance', @@ -43,6 +46,7 @@ defineSuite([ Ellipsoid, GeographicProjection, GeometryInstance, + CesiumMath, PixelFormat, Rectangle, RectangleGeometry, @@ -53,7 +57,9 @@ defineSuite([ Framebuffer, Pass, PixelDatatype, + RenderState, ShaderProgram, + ShaderSource, Texture, Camera, EllipsoidSurfaceAppearance, @@ -72,9 +78,21 @@ defineSuite([ 'use strict'; var scene; + var simpleShaderProgram; + var simpleRenderState; beforeAll(function() { scene = createScene(); + simpleShaderProgram = ShaderProgram.fromCache({ + context : scene.context, + vertexShaderSource : new ShaderSource({ + sources : ['void main() { gl_Position = vec4(1.0); }'] + }), + fragmentShaderSource : new ShaderSource({ + sources : ['void main() { gl_FragColor = vec4(1.0); }'] + }) + }); + simpleRenderState = new RenderState(); }); afterEach(function() { @@ -82,7 +100,7 @@ defineSuite([ scene.debugCommandFilter = undefined; scene.fxaa = false; scene.primitives.removeAll(); - scene.morphTo3D(); + scene.morphTo3D(0.0); }); afterAll(function() { @@ -197,6 +215,8 @@ defineSuite([ it('debugCommandFilter filters commands', function() { var c = new DrawCommand({ + shaderProgram : simpleShaderProgram, + renderState : simpleRenderState, pass : Pass.OPAQUE }); c.execute = function() {}; @@ -214,6 +234,8 @@ defineSuite([ it('debugCommandFilter does not filter commands', function() { var c = new DrawCommand({ + shaderProgram : simpleShaderProgram, + renderState : simpleRenderState, pass : Pass.OPAQUE }); c.execute = function() {}; @@ -231,6 +253,8 @@ defineSuite([ var center = Cartesian3.add(scene.camera.position, scene.camera.direction, new Cartesian3()); var c = new DrawCommand({ + shaderProgram : simpleShaderProgram, + renderState : simpleRenderState, pass : Pass.OPAQUE, debugShowBoundingVolume : true, boundingVolume : new BoundingSphere(center, radius) @@ -247,13 +271,9 @@ defineSuite([ it('debugShowCommands tints commands', function() { var c = new DrawCommand({ - pass : Pass.OPAQUE, - - shaderProgram : ShaderProgram.fromCache({ - context : scene.context, - vertexShaderSource : 'void main() { gl_Position = vec4(1.0); }', - fragmentShaderSource : 'void main() { gl_FragColor = vec4(1.0); }' - }) + shaderProgram : simpleShaderProgram, + renderState : simpleRenderState, + pass : Pass.OPAQUE }); c.execute = function() {}; @@ -628,6 +648,9 @@ defineSuite([ scene.destroyForSpecs(); }); + var pickedPosition3D = new Cartesian3(-455845.46867895435, -5210337.548977215, 3637549.8562320103); + var pickedPosition2D = new Cartesian3(-455861.7055871038, -5210523.137686572, 3637866.6638769475); + it('pickPosition', function() { if (!scene.pickPositionSupported) { return; @@ -652,7 +675,67 @@ defineSuite([ expect(scene).toRenderAndCall(function() { var position = scene.pickPosition(windowPosition); - expect(position).toBeDefined(); + expect(position).toEqualEpsilon(pickedPosition3D, CesiumMath.EPSILON6); + }); + }); + + it('pickPosition in CV', function() { + if (!scene.pickPositionSupported) { + return; + } + + scene.morphToColumbusView(0.0); + + var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + scene.camera.setView({ destination : rectangle }); + + var canvas = scene.canvas; + var windowPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); + + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).not.toBeDefined(); + + var rectanglePrimitive = createRectangle(rectangle); + rectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); + + var primitives = scene.primitives; + primitives.add(rectanglePrimitive); + }); + + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).toEqualEpsilon(pickedPosition2D, CesiumMath.EPSILON6); + }); + }); + + it('pickPosition in 2D', function() { + if (!scene.pickPositionSupported) { + return; + } + + scene.morphTo2D(0.0); + + var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + scene.camera.setView({ destination : rectangle }); + + var canvas = scene.canvas; + var windowPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); + + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).not.toBeDefined(); + + var rectanglePrimitive = createRectangle(rectangle); + rectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); + + var primitives = scene.primitives; + primitives.add(rectanglePrimitive); + }); + + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).toEqualEpsilon(pickedPosition2D, CesiumMath.EPSILON6); }); }); @@ -688,6 +771,66 @@ defineSuite([ }); }); + it('pickPosition picks translucent geometry when pickTranslucentDepth is true', function() { + if (!scene.pickPositionSupported) { + return; + } + + var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + scene.camera.setView({ + destination : rectangle + }); + + var canvas = scene.canvas; + var windowPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); + + var rectanglePrimitive = createRectangle(rectangle); + rectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 0.5); + + var primitives = scene.primitives; + primitives.add(rectanglePrimitive); + + scene.useDepthPicking = true; + scene.pickTranslucentDepth = false; + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).not.toBeDefined(); + }); + + scene.pickTranslucentDepth = true; + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).toBeDefined(); + }); + + var rectanglePrimitive2 = createRectangle(rectangle); + rectanglePrimitive2.appearance.material.uniforms.color = new Color(0.0, 1.0, 0.0, 0.5); + primitives.add(rectanglePrimitive2); + + expect(scene).toRenderAndCall(function() { + var position = scene.pickPosition(windowPosition); + expect(position).toBeDefined(); + + var commandList = scene.frameState.commandList; + expect(commandList.length).toEqual(2); + + var command1 = commandList[0]; + var command2 = commandList[1]; + + expect(command1.derivedCommands).toBeDefined(); + expect(command2.derivedCommands).toBeDefined(); + + expect(command1.derivedCommands.depth).toBeDefined(); + expect(command2.derivedCommands.depth).toBeDefined(); + + expect(command1.derivedCommands.depth.depthOnlyCommand).toBeDefined(); + expect(command2.derivedCommands.depth.depthOnlyCommand).toBeDefined(); + + expect(command1.derivedCommands.depth.depthOnlyCommand.shaderProgram).toEqual(command2.derivedCommands.depth.depthOnlyCommand.shaderProgram); + expect(command1.derivedCommands.depth.depthOnlyCommand.renderState).toEqual(command2.derivedCommands.depth.depthOnlyCommand.renderState); + }); + }); + it('pickPosition throws without windowPosition', function() { expect(function() { scene.pickPosition(); diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index b5c2dc127bd5..69c4e33af57f 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -7,7 +7,6 @@ defineSuite([ 'Core/Color', 'Core/ColorGeometryInstanceAttribute', 'Core/ComponentDatatype', - 'Core/defined', 'Core/EllipsoidTerrainProvider', 'Core/GeometryInstance', 'Core/HeadingPitchRange', @@ -40,7 +39,6 @@ defineSuite([ Color, ColorGeometryInstanceAttribute, ComponentDatatype, - defined, EllipsoidTerrainProvider, GeometryInstance, HeadingPitchRange, diff --git a/Specs/Scene/TextureAtlasSpec.js b/Specs/Scene/TextureAtlasSpec.js index 63a52fe448da..ca9b6102e2cb 100644 --- a/Specs/Scene/TextureAtlasSpec.js +++ b/Specs/Scene/TextureAtlasSpec.js @@ -6,13 +6,6 @@ defineSuite([ 'Core/loadImage', 'Core/Math', 'Core/PixelFormat', - 'Core/PrimitiveType', - 'Renderer/Buffer', - 'Renderer/BufferUsage', - 'Renderer/ClearCommand', - 'Renderer/DrawCommand', - 'Renderer/ShaderProgram', - 'Renderer/VertexArray', 'Specs/createScene', 'ThirdParty/when' ], function( @@ -22,13 +15,6 @@ defineSuite([ loadImage, CesiumMath, PixelFormat, - PrimitiveType, - Buffer, - BufferUsage, - ClearCommand, - DrawCommand, - ShaderProgram, - VertexArray, createScene, when) { 'use strict'; @@ -77,7 +63,7 @@ defineSuite([ function expectToRender(texture, textureCoordinates, expected) { var x = textureCoordinates.x + textureCoordinates.width / 2.0; var y = textureCoordinates.y + textureCoordinates.height / 2.0; - var fs = + var fs = 'uniform sampler2D u_texture;' + 'void main() {' + ' gl_FragColor = texture2D(u_texture, vec2(' + x + ', ' + y + '));' + diff --git a/Specs/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModelSpec.js b/Specs/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModelSpec.js index b700d31336c9..ec890f053dd2 100644 --- a/Specs/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModelSpec.js +++ b/Specs/Widgets/CesiumInspector/Cesium3DTilesInspectorViewModelSpec.js @@ -121,6 +121,13 @@ defineSuite([ viewModel.showRequestVolumes = false; expect(viewModel.tileset.debugShowViewerRequestVolume).toBe(false); }); + + it('showGeometricError', function() { + viewModel.showGeometricError = true; + expect(viewModel.tileset.debugShowGeometricError).toBe(true); + viewModel.showGeometricError = false; + expect(viewModel.tileset.debugShowGeometricError).toBe(false); + }); }); describe('update options', function() { diff --git a/Specs/Widgets/Geocoder/GeocoderSpec.js b/Specs/Widgets/Geocoder/GeocoderSpec.js index dc6bd8772caf..affff0d4fbf2 100644 --- a/Specs/Widgets/Geocoder/GeocoderSpec.js +++ b/Specs/Widgets/Geocoder/GeocoderSpec.js @@ -41,22 +41,16 @@ defineSuite([ it('constructor sets expected properties', function() { var flightDuration = 1234; - var url = 'bing.invalid/'; - var key = 'testKey'; var geocoder = new Geocoder({ container : document.body, scene : scene, - flightDuration : flightDuration, - url : url, - key : key + flightDuration : flightDuration }); var viewModel = geocoder.viewModel; expect(viewModel.scene).toBe(scene); expect(viewModel.flightDuration).toBe(flightDuration); - expect(viewModel.url).toBe(url); - expect(viewModel.key).toBe(key); geocoder.destroy(); }); diff --git a/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js b/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js index 442e7dd9ded2..833f977b4886 100644 --- a/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js +++ b/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js @@ -2,14 +2,12 @@ defineSuite([ 'Widgets/Geocoder/GeocoderViewModel', 'Core/Cartesian3', - 'Scene/Camera', 'Specs/createScene', 'Specs/pollToPromise', 'ThirdParty/when' ], function( GeocoderViewModel, Cartesian3, - Camera, createScene, pollToPromise, when) { @@ -63,20 +61,14 @@ defineSuite([ it('constructor sets expected properties', function() { var flightDuration = 1234; - var url = 'bing.invalid/'; - var key = 'testKey'; var viewModel = new GeocoderViewModel({ scene : scene, - flightDuration : flightDuration, - url : url, - key : key + flightDuration : flightDuration }); expect(viewModel.scene).toBe(scene); expect(viewModel.flightDuration).toBe(flightDuration); - expect(viewModel.url).toBe(url); - expect(viewModel.key).toBe(key); expect(viewModel.keepExpanded).toBe(false); }); diff --git a/Specs/Widgets/NavigationHelpButton/NavigationHelpButtonSpec.js b/Specs/Widgets/NavigationHelpButton/NavigationHelpButtonSpec.js index 8ebbcbfd166f..0b047588da40 100644 --- a/Specs/Widgets/NavigationHelpButton/NavigationHelpButtonSpec.js +++ b/Specs/Widgets/NavigationHelpButton/NavigationHelpButtonSpec.js @@ -1,12 +1,10 @@ /*global defineSuite*/ defineSuite([ 'Widgets/NavigationHelpButton/NavigationHelpButton', - 'Core/defined', 'Core/FeatureDetection', 'Specs/DomEventSimulator' ], function( NavigationHelpButton, - defined, FeatureDetection, DomEventSimulator) { 'use strict'; diff --git a/Specs/Widgets/SceneModePicker/SceneModePickerSpec.js b/Specs/Widgets/SceneModePicker/SceneModePickerSpec.js index 29e62e8350ad..66d5710ba3ae 100644 --- a/Specs/Widgets/SceneModePicker/SceneModePickerSpec.js +++ b/Specs/Widgets/SceneModePicker/SceneModePickerSpec.js @@ -1,13 +1,11 @@ /*global defineSuite*/ defineSuite([ 'Widgets/SceneModePicker/SceneModePicker', - 'Core/defined', 'Core/FeatureDetection', 'Specs/createScene', 'Specs/DomEventSimulator' ], function( SceneModePicker, - defined, FeatureDetection, createScene, DomEventSimulator) { diff --git a/Specs/Widgets/Viewer/ViewerSpec.js b/Specs/Widgets/Viewer/ViewerSpec.js index a5fbed70cfa6..9b91922c4b87 100644 --- a/Specs/Widgets/Viewer/ViewerSpec.js +++ b/Specs/Widgets/Viewer/ViewerSpec.js @@ -33,7 +33,7 @@ defineSuite([ 'Widgets/SceneModePicker/SceneModePicker', 'Widgets/SelectionIndicator/SelectionIndicator', 'Widgets/Timeline/Timeline' - ], function( + ], 'Widgets/Viewer/Viewer', function( Cartesian3, ClockRange, ClockStep, @@ -826,6 +826,54 @@ defineSuite([ viewer.destroy(); }); + it('raises an event when the selected entity changes', function() { + var viewer = createViewer(container); + + var dataSource = new MockDataSource(); + viewer.dataSources.add(dataSource); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(123456, 123456, 123456)); + + dataSource.entities.add(entity); + + var myEntity; + viewer.selectedEntityChanged.addEventListener(function(newSelection) { + myEntity = newSelection; + }); + viewer.selectedEntity = entity; + expect(myEntity).toBe(entity); + + viewer.selectedEntity = undefined; + expect(myEntity).toBeUndefined(); + + viewer.destroy(); + }); + + it('raises an event when the tracked entity changes', function() { + var viewer = createViewer(container); + + var dataSource = new MockDataSource(); + viewer.dataSources.add(dataSource); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(123456, 123456, 123456)); + + dataSource.entities.add(entity); + + var myEntity; + viewer.trackedEntityChanged.addEventListener(function(newValue) { + myEntity = newValue; + }); + viewer.trackedEntity = entity; + expect(myEntity).toBe(entity); + + viewer.trackedEntity = undefined; + expect(myEntity).toBeUndefined(); + + viewer.destroy(); + }); + it('selectedEntity sets InfoBox properties', function() { var viewer = createViewer(container); diff --git a/package.json b/package.json index fd02a0330478..546771c0a134 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "1.30.0", + "version": "1.31.0", "description": "Cesium is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "homepage": "http://cesiumjs.org", "license": "Apache-2.0", @@ -34,13 +34,13 @@ }, "devDependencies": { "almond": "^0.3.3", - "aws-sdk": "^2.7.9", + "aws-sdk": "^2.18.0", "bluebird": "^3.4.6", "compressible": "^2.0.9", "compression": "^1.6.2", - "electron": "^1.4.10", + "electron": "^1.6.1", "event-stream": "^3.3.4", - "express": "^4.14.0", + "express": "^4.15.0", "globby": "^6.1.0", "glsl-strip-comments": "^1.0.0", "gulp": "^3.9.1", @@ -64,15 +64,16 @@ "karma-jasmine": "^1.1.0", "karma-requirejs": "^1.1.0", "karma-safari-launcher": "^1.0.0", - "karma-spec-reporter": "^0.0.26", + "karma-spec-reporter": "^0.0.30", "mime": "^1.3.4", "mkdirp": "^0.5.1", "request": "^2.79.0", - "rimraf": "^2.5.4", + "rimraf": "^2.6.1", "yargs": "^6.4.0" }, "scripts": { "start": "node server.js", + "startPublic": "node server.js --public", "build": "gulp build", "build-watch": "gulp build-watch", "buildApps": "gulp buildApps",