diff --git a/.editorconfig b/.editorconfig index 2536d66bf13a..1c96445761b3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,3 +10,6 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false + +[package.json] +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000000..4baf9b882b1c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,9 @@ +Apps/HelloWorld.html +Apps/Sandcastle/ThirdParty/** +Build/** +Documentation/** +Source/Shaders/** +Source/ThirdParty/** +Source/Workers/cesiumWorkerBootstrapper.js +ThirdParty/** +Tools/** diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000000..5559ce5b6e87 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "./Tools/eslint-config-cesium/browser.js", + "rules": { + "no-unused-vars": ["error", {"vars": "all", "args": "none"}] + } +} diff --git a/.gitignore b/.gitignore index 56f10b1afd70..3028e8e0b71a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /cesium-*.tgz .DS_Store Thumbs.db +.eslintcache /Apps/CesiumViewer/Gallery/gallery-index.js @@ -16,11 +17,13 @@ Thumbs.db /Source/Shaders/*.js /Source/Shaders/*/*.js /Source/Shaders/*/*/*.js +/Source/ThirdParty/Shaders/*.js /Specs/SpecList.js /node_modules npm-debug.log +npm-debug.log.* # WebStorm user-specific .idea/workspace.xml diff --git a/.idea/cesium.iml b/.idea/cesium.iml index 5ecdb1a3e790..075b2a75ef2b 100644 --- a/.idea/cesium.iml +++ b/.idea/cesium.iml @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index eff7139dc940..c6cc8c8196a2 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml index d45df048bf58..13a58bed92c6 100644 --- a/.idea/jsLibraryMappings.xml +++ b/.idea/jsLibraryMappings.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_tests.xml b/.idea/runConfigurations/Run_tests.xml index 4a52554ce6db..fb0f97e40d3d 100644 --- a/.idea/runConfigurations/Run_tests.xml +++ b/.idea/runConfigurations/Run_tests.xml @@ -7,4 +7,4 @@ - \ No newline at end of file + diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 9d360ee6532f..000000000000 --- a/.jshintrc +++ /dev/null @@ -1,62 +0,0 @@ -{ - "bitwise": false, - "camelcase": false, - "curly": true, - "eqeqeq": true, - "forin": true, - "freeze": true, - "immed": true, - "latedef": false, - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": false, - "undef": true, - "unused": "vars", - "strict": true, - "asi": false, - "boss": false, - "debug": false, - "eqnull": false, - "esnext": false, - "moz": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": false, - "multistr": true, - "noyield": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": false, - "supernew": false, - "validthis": false, - "browser": true, - "browserify": false, - "couch": false, - "devel": true, - "dojo": false, - "jasmine": true, - "jquery": false, - "mocha": true, - "mootools": false, - "node": false, - "nonstandard": false, - "prototypejs": false, - "qunit": false, - "rhino": false, - "shelljs": false, - "worker": false, - "wsh": false, - "yui": false -} \ No newline at end of file diff --git a/.npmignore b/.npmignore index d9e2e3bb2e65..5c1ffb023c05 100644 --- a/.npmignore +++ b/.npmignore @@ -7,6 +7,7 @@ /.project /.settings /.travis.yml +/.vscode /Apps /Build/minifyShaders.state /Build/Stubs diff --git a/.travis.yml b/.travis.yml index 1d33074b1e57..d77dad001831 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,11 +11,12 @@ script: - npm run deploy-status -- --status pending --message 'Waiting for build' - echo -en 'travis_fold:end:script.deployPending\\r' - - echo 'jsHint' && echo -en 'travis_fold:start:script.jsHint\\r' - - npm run jsHint -- --failTaskOnError - - echo -en 'travis_fold:end:script.jsHint\\r' + - echo 'eslint' && echo -en 'travis_fold:start:script.eslint\\r' + - npm run eslint + - echo -en 'travis_fold:end:script.eslint\\r' - echo 'test webgl-stub' && echo -en 'travis_fold:start:script.test\\r' + - npm run build - npm run test -- --browsers Electron --webgl-stub --failTaskOnError --suppressPassed - echo -en 'travis_fold:end:script.test\\r' diff --git a/.vscode/settings.json b/.vscode/settings.json index e3ecc5978bac..ef0adfd47a46 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,7 @@ "editor.insertSpaces": true, "editor.tabSize": 4, "editor.detectIndentation": false, - "jshint.enable": true, + "eslint.enable": true, "javascript.format.insertSpaceAfterCommaDelimiter": true, "javascript.format.insertSpaceAfterSemicolonInForStatements": true, "javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true, diff --git a/Apps/.jshintrc b/Apps/.jshintrc index 24762c7dc745..ad12e9a7e032 100644 --- a/Apps/.jshintrc +++ b/Apps/.jshintrc @@ -1,4 +1,62 @@ { - "extends": "../.jshintrc", - "jasmine": false + "bitwise": false, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "immed": true, + "latedef": false, + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": false, + "undef": true, + "unused": "vars", + "strict": true, + "asi": false, + "boss": false, + "debug": false, + "eqnull": false, + "esnext": false, + "moz": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": false, + "multistr": true, + "noyield": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": false, + "supernew": false, + "validthis": false, + "browser": true, + "browserify": false, + "couch": false, + "devel": true, + "dojo": false, + "jasmine": false, + "jquery": false, + "mocha": true, + "mootools": false, + "node": false, + "nonstandard": false, + "prototypejs": false, + "qunit": false, + "rhino": false, + "shelljs": false, + "worker": false, + "wsh": false, + "yui": false } diff --git a/Apps/CesiumViewer/CesiumViewer.js b/Apps/CesiumViewer/CesiumViewer.js index 9d0cbfeb9ff5..5e126db8c6e3 100644 --- a/Apps/CesiumViewer/CesiumViewer.js +++ b/Apps/CesiumViewer/CesiumViewer.js @@ -63,7 +63,7 @@ define([ var message = formatError(exception); console.error(message); if (!document.querySelector('.cesium-widget-errorPanel')) { - window.alert(message); + window.alert(message); //eslint-disable-line no-alert } return; } @@ -166,6 +166,7 @@ define([ } } + var camera = viewer.camera; function saveCamera() { var position = camera.positionCartographic; var hpr = ''; @@ -178,7 +179,6 @@ define([ var updateTimer; if (endUserOptions.saveCamera !== 'false') { - var camera = viewer.camera; camera.moveStart.addEventListener(function() { if (!defined(updateTimer)) { updateTimer = window.setInterval(saveCamera, 1000); diff --git a/Apps/CesiumViewer/CesiumViewerStartup.js b/Apps/CesiumViewer/CesiumViewerStartup.js index 0860c9af7dfa..f387d66ce375 100644 --- a/Apps/CesiumViewer/CesiumViewerStartup.js +++ b/Apps/CesiumViewer/CesiumViewerStartup.js @@ -1,4 +1,5 @@ /*global require*/ +/*eslint-disable strict*/ require({ baseUrl : '.', paths : { diff --git a/Apps/SampleData/kml/facilities/facilities.kml b/Apps/SampleData/kml/facilities/facilities.kml index 48c94c029f33..2e3a3c6aa6d9 100644 --- a/Apps/SampleData/kml/facilities/facilities.kml +++ b/Apps/SampleData/kml/facilities/facilities.kml @@ -1 +1 @@ -MACRES Ground Receiving Station#GroundStation102.339,3.463Ground StationActiveMalaysiaALCOR STDN KMRQ#RadarStation167.48284936,9.39859958Radar StationActiveMarshall IslandsGuiana Space Center - ZL3 (STDN KR3P)#LaunchPad-52.75178147,5.24036314Launch PadActiveFrench GuianaAsan Teleport#GroundStation127.018,36.741Ground StationActiveSouth KoreaPillar Point STDN PP2F#GroundStation-122.49668389,37.49685436Ground StationActiveUnited StatesDiego Garcia GEODSS Cam3#OpticalTrackingStation72.45237,-7.41186Optical Tracking StationActiveUnited KingdomPlesetsk Cosmodrome#LaunchSite40.6,62.9Launch SiteActiveRussiaPhillips Hill STDN WH9F#GroundStation-106.13210839,33.44521867Ground StationActiveUnited StatesSantiago Station AGO 6#GroundStation-70.6701611,-33.1506778Ground StationActiveChileTanegashima#LaunchSite130.976,30.401Launch SiteActiveJapanBaikonur Cosmodrome - LC 90-19#LaunchPad62.9144,46.0863Launch PadActiveKazakhstanMid-Atlantic Regional Spaceport#LaunchSite-75.49,37.832Launch SiteActiveUnited StatesDombarovsky Cosmodrome x44#LaunchPad59.911557,51.54739Launch PadActiveRussiaFort Bliss#LaunchSite-106.1526,32.0737Launch SiteActiveUnited StatesSemnan Launch Center - xx3#LaunchPad53.952,35.239Launch PadActiveIranEarth Observation Center#GroundStation139.348734,36.002563Ground StationActiveJapanFillmore Teleport#GroundStation-118.894041,34.405802Ground StationActiveUnited StatesDSS 23 Goldstone#GroundStation-116.87295,35.33951Ground StationInactiveUnited StatesGTSA#GroundStation144.8560742,13.6151942Ground StationActiveUnited StatesAntigua STDN AN8S#GroundStation-61.77428983,17.13662489Ground StationActiveAntigua and BarbudaLandsat Station STDN SF1S#GroundStation-96.62251461,43.73607264Ground StationActiveUnited StatesZhanyi Station#GroundStation103.82,25.6Ground StationActiveChinaWallops Island STDN WP3S#GroundStation-75.47418797,37.92836117Ground StationActiveUnited StatesRAL Station 2m#GroundStation-1.31419,51.57159Ground StationActiveUnited KingdomMaryland LSE Leolut#GroundStation-76.93,38.850333Ground StationActiveUnited StatesSemnan Launch Center xx2#LaunchPad53.921,35.2347Launch PadActiveIranBaikonur Cosmodrome LC 200-40#LaunchPad63.0379,46.0364Launch PadActiveKazakhstanNakhodka Leolut#GroundStation132.7907,42.858666Ground StationPlannedRussiaXichang Satellite Launch Center - LA 4#LaunchPad102.0232,28.2495Launch PadActiveChinaBarking Sands LC 14#LaunchPad-159.7788,22.058Launch PadActiveUnited StatesDombarovsky Cosmodrome - x24#LaunchPad59.59691,51.150886Launch PadActiveRussiaWhite Sands Space STDN WSSH#GroundStation-106.46472222,32.88027778Ground StationActiveUnited StatesFort Detrick#GroundStation-77.416,39.443Ground StationActiveUnited StatesBristow Station#GroundStation-77.5732,38.7837Ground StationActiveUnited StatesTromsoe Leolut#GroundStation18.9403,69.662333Ground StationActiveNorwayTonghae Satellite Launching Ground#LaunchSite129.665994,40.855652Launch SiteActiveNorth KoreaDombarovsky Cosmodrome - x29#LaunchPad60.765442,51.201923Launch PadActiveRussiaVandenberg AFB SLC 1E#LaunchPad-120.6263,34.7561Launch PadInactiveUnited StatesSevastopol Radar#RadarStation33.386,44.579Radar StationActiveUkraineWallops Flight Facility - LA 2#LaunchPad-75.4834,37.8376Launch PadActiveUnited StatesOkno Complex#OpticalTrackingStation69.224,38.282Optical Tracking StationActiveTajikistanKapustin Yar - xx7#LaunchPad45.716196,48.812023Launch PadActiveRussiaKwajalein STDN KMRT#GroundStation167.71852428,8.71954542Ground StationInactiveMarshall IslandsXTAR 6m 2#GroundStation-15.63057,27.76272Ground StationActiveSpainWallops Flight Facility LA 1#LaunchPad-75.4861,37.8352Launch PadActiveUnited StatesPoker Flat LEO-T STDN LE1S#GroundStation-147.462255,65.11685167Ground StationActiveUnited StatesVLBA North Liberty#GroundStation-91.574133,41.771425Ground StationActiveUnited StatesWhite Sands - SULF#LaunchPad-106.7364,33.7212Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 108#LaunchPad63.815,46.046Launch PadActiveKazakhstanBaikonur Cosmodrome LC 246#LaunchPad63.4236,45.7656Launch PadActiveKazakhstanMILA STDN MILA#GroundStation-80.69275272,28.50816025Ground StationInactiveUnited StatesMeck Island#LaunchSite167.727188,9.006637Launch SiteActiveMarshall IslandsVLBA Mauna Kea#GroundStation-155.455733,19.801415Ground StationActiveUnited StatesVandenberg STDN WULY#GroundStation-120.55598472,34.65541336Ground StationActiveUnited StatesCape Canaveral AFS SLC 17B STDN B17P#LaunchPad-80.56564944,28.44579042Launch PadActiveUnited StatesGRGT STDN GWMJ#GroundStation144.84080419,13.58675639Ground StationActiveUnited StatesJicamarca Radio Observatory#RadarStation-76.874,-11.951Radar StationActivePeruCarteret Station#GroundStation-74.2166,40.58Ground StationActiveUnited StatesSomis Station#GroundStation-118.996,34.325Ground StationActiveUnited StatesEchoStar New Braunfels Station#GroundStation-98.0635,29.7599Ground StationActiveUnited StatesKashima Space Research Center, 13m B#GroundStation140.66298,35.95631Ground StationActiveJapanOttawa 1 Geolut#GroundStation-75.674,45.329Ground StationActiveCanadaSvobodny Cosmodrome x10#LaunchPad128.3323,51.877Launch PadInactiveRussiaSvalbard STDN S22S#GroundStation15.381773,78.232908Ground StationActiveNorwayCamp Parks Communications Annex#GroundStation-121.881,37.7331Ground StationActiveUnited StatesKapustin Yar PL1#LaunchPad46.2621,48.4116Launch PadActiveRussiaBaikonur Cosmodrome - xx5#LaunchPad62.932319,46.081306Launch PadActiveKazakhstanMillstone Hill Steerable Antenna#RadarStation-71.4912,42.61959Radar StationActiveUnited StatesDombarovsky Cosmodrome - xx2#LaunchPad59.791895,50.681452Launch PadActiveRussiaKamisaibara Spaceguard Center#RadarStation133.9413,35.3125Radar StationActiveJapanMauritius#GroundStation57.52,-20.17Ground StationActiveMauritiusOrbcomm San Luis B#GroundStation-65.18199,-33.83795Ground StationActiveArgentinaDombarovsky Cosmodrome - x37#LaunchPad60.728749,51.301802Launch PadActiveRussiaOverberg Test Range#LaunchSite20.28,-34.61Launch SiteActiveSouth AfricaBermuda STDN BDAA#GroundStation-64.65878961,32.35115261Ground StationInactiveBermudaSemnan Launch Center xx4#LaunchPad53.955,35.258Launch PadActiveIranBaikonur Cosmodrome LC 172#LaunchPad63.092,46.065Launch PadActiveKazakhstanCape Canaveral AFS LC 3#LaunchPad-80.5363,28.4662Launch PadInactiveUnited StatesXichang LA 3#LaunchPad102.0271,28.2455Launch PadActiveChinaNatal Station STDN NATL#LaserStation-35.16455019,-5.92780617Laser StationInactiveBrazilBaikonur Cosmodrome - LC 200-39#LaunchPad63.032,46.0399Launch PadActiveKazakhstanWallops Island STDN HR1S#GroundStation-75.46113556,37.94549917Ground StationActiveUnited StatesWoomera Test Range LA 5B#LaunchPad136.4763,-30.9711Launch PadActiveAustraliaGreen Bank#GroundStation-79.83977,38.43297Ground StationActiveUnited StatesMukachevo Radar#RadarStation22.708,48.378Radar StationActiveUkraineHolloman SLED#LaunchPad-106.1484,32.9263Launch PadActiveUnited StatesNevada Test Site Area 26#LaunchSite-116.114,36.771Launch SiteActiveUnited StatesBaikonur Cosmodrome - LC 70#LaunchPad63.0963,46.0329Launch PadActiveKazakhstanEl Arenosillo#LaunchSite-6.733,37.098Launch SiteActiveSpainBaikonur Cosmodrome - LC 81-23#LaunchPad62.9785,46.074Launch PadActiveKazakhstanKeelung (1) Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanPushkino Phased-array Radar#RadarStation37.769,56.173Radar StationActiveRussiaAuScope VLBI Hobart#GroundStation147.4381,-42.80557Ground StationActiveAustraliaSvalsat SG 5#GroundStation15.389123,78.230127Ground StationActiveNorwayQueensland Transmitter#RadarStation144.1454,-23.658Radar StationActiveAustraliaAlice Springs BRT STDN AL2J#GroundStation133.8825985,-23.75881044Ground StationActiveAustraliaSGS Oakhanger Site Annex#GroundStation-0.899,51.1095Ground StationActiveUnited KingdomBaikonur Cosmodrome LC 110L#LaunchPad63.3049,45.9647Launch PadActiveKazakhstanBaikonur Cosmodrome LC 45-2#LaunchPad63.6532,45.9433Launch PadActiveKazakhstanJiuquan Satellite Launch Center - LA 3#LaunchPad100.78,41.0152Launch PadActiveChinaBaikonur Cosmodrome - LC 241#LaunchPad63.4558,45.8583Launch PadActiveKazakhstanGrand Bahama Island STDN GBIQ#GroundStation-78.34784006,26.615642Ground StationInactiveBahamasOrbcomm Kijal A#GroundStation103.47377,4.34888Ground StationActiveMalaysiaMisawa Air Base Security Hill#GroundStation141.3225,40.72Ground StationActiveJapanMoscow Leolut#GroundStation37.7227,55.743333Ground StationPlannedRussiaWallops Flight Facility - LA 3A#LaunchPad-75.4726,37.848Launch PadActiveUnited StatesPoker Flat STDN PFTS#GroundStation-147.46329083,65.11679308Ground StationActiveUnited StatesEtisalat Jebel Ali Teleport#GroundStation55.1247,25.0306Ground StationActiveUnited Arab EmiratesPlesetsk Cosmodrome - LC 133-1#LaunchPad40.8468,62.8868Launch PadActiveRussiaKapustin Yar xx6#LaunchPad45.8152,48.783061Launch PadActiveRussiaAnkara (2) Leolut#GroundStation32.9897,40.140666Ground StationActiveTurkeySvobodny Cosmodrome - xx7#LaunchPad128.301,51.823Launch PadInactiveRussiaOnizuka Control Node xx2#GroundStation-122.029075,37.403492Ground StationInactiveUnited StatesCSTARS_West_11m#GroundStation-80.38489,25.61403Ground StationActiveUnited StatesMaspalomas (1) Geolut#GroundStation-15.634,27.764Ground StationActiveSpainGalenki RT-70#GroundStation131.757,44.0161Ground StationActiveRussiaSugar Grove Station#GroundStation-79.28,38.516Ground StationActiveUnited StatesEnnylabegan#GroundStation167.618495,8.797797Ground StationActiveMarshall IslandsFinegayan SATCOM Site#GroundStation144.849827,13.583618Ground StationActiveUnited StatesHawley Earth Station#GroundStation-75.13,41.46417Ground StationActiveUnited StatesKapustin Yar - xx1#LaunchPad46.318006,48.484051Launch PadActiveRussiaEsrange Station KSX#GroundStation21.0556028,67.8887061Ground StationActiveSwedenVTS STDN VTSS#GroundStation-120.50184844,34.82261656Ground StationActiveUnited StatesEchoStar Gilbert Station#GroundStation-111.8142,33.3655Ground StationActiveUnited StatesTriunfo Pass Station#GroundStation-118.8964,34.0808Ground StationActiveUnited StatesSocorro GEODSS, xx2#OpticalTrackingStation-106.65962,33.81725Optical Tracking StationActiveUnited StatesSaskatoon#GroundStation-106.62587,52.139427Ground StationActiveCanadaBaikonur Cosmodrome - LC 60-7#LaunchPad64.0173,46.0181Launch PadActiveKazakhstanHolloman NATIV#LaunchPad-106.0753,32.8866Launch PadActiveUnited StatesAscension Island STDN ACN3#GroundStation-14.32710328,-7.95488247Ground StationActiveUnited KingdomDombarovsky Cosmodrome x24#LaunchPad59.59691,51.150886Launch PadActiveRussiaBaikonur Cosmodrome - LC 103#LaunchPad63.4448,45.9523Launch PadActiveKazakhstanGoonhilly Satellite Earth Station#GroundStation-5.181944,50.048056Ground StationInactiveUnited KingdomEffelsberg Radio Telescope#GroundStation6.883417,50.525Ground StationActiveGermanyWallops Flight Facility LA 5#LaunchPad-75.4681,37.8529Launch PadActiveUnited StatesSouth Point Station USHI01#GroundStation-155.6633318,19.0139525Ground StationActiveUnited StatesUchinoura Lambda Pad#LaunchPad131.079125,31.251908Launch PadActiveJapanKapustin Yar START Pioner#LaunchPad46.3009,48.6149Launch PadActiveRussiaMount Pleasant 14m#GroundStation147.44165,-42.8034Ground StationActiveAustraliaHartebeesthoek STDN HBK3#GroundStation27.7126,-25.88672778Ground StationActiveSouth AfricaLake Kickapoo Space Fence#RadarStation-98.7628,33.5464Radar StationActiveUnited StatesEsrange Rocket Range - N#LaunchPad21.1064,67.8933Launch PadActiveSwedenSantiago Station AGO 3 STDN AGU3#GroundStation-70.666403,-33.15110747Ground StationActiveChileDakar STDN DAKS#GroundStation-17.12876689,14.72476222Ground StationActiveSenegalHartebeesthoek 5m#GroundStation27.70665,-25.88649Ground StationActiveSouth AfricaPhilippine Space Comm Center#GroundStation121.294,14.585Ground StationActivePhilippinesWenchang#LaunchSite111.01,19.68Launch SitePlannedChinaMaui GEODSS, xx3#OpticalTrackingStation-156.2575,20.7081Optical Tracking StationActiveUnited StatesBochum Observatory#GroundStation7.19257,51.42697Ground StationActiveGermanyMaui GEODSS Cam1#OpticalTrackingStation-156.2578,20.7081Optical Tracking StationActiveUnited StatesOttawa 2 Geolut#GroundStation-75.674333,45.3438Ground StationActiveCanadaHartebeesthoek Ka 13m#GroundStation27.70746,-25.88813Ground StationActiveSouth AfricaSANAE IV#GroundStation-2.8403,-71.6725Ground StationActiveAntarcticaKwajalein GPS Station#GroundStation167.731327,8.723044Ground StationActiveMarshall IslandsTowi Alsaman#GroundStation55.832,25.251Ground StationActiveUnited Arab EmiratesWhite Sands STDN ST3K#GroundStation-106.61208889,32.542675Ground StationActiveUnited StatesAlcantara Launch Center#LaunchSite-44.367,-2.317Launch SiteActiveBrazilSouth Point Station USHI02 STDN U2HS#GroundStation-155.66294408,19.01379344Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 1E#LaunchPad-120.6263,34.7561Launch PadInactiveUnited StatesCape Canaveral Air Force Station - SLC 37A#LaunchPad-80.568,28.534Launch PadInactiveUnited StatesPlesetsk Cosmodrome LC 35#LaunchPad40.575422,62.927806Launch PadActiveRussiaSednaya#GroundStation36.3635,33.6676Ground StationActiveSyriaDSS 16 Goldstone STDN DS16#GroundStation-116.87364975,35.34153939Ground StationInactiveUnited StatesEagle Vision 6#GroundStation-86.66,34.64Ground StationActiveUnited StatesKapustin Yar LC 107 2d#LaunchPad46.2959,48.5695Launch PadActiveRussiaVentspils#GroundStation21.857778,57.558056Ground StationActiveLatviaVandenberg Air Force Base - SLC 2W (STDN WTRP)#LaunchPad-120.62237833,34.75563222Launch PadActiveUnited StatesBaikonur Cosmodrome LC 81-24#LaunchPad62.9848,46.0709Launch PadActiveKazakhstanCape Canaveral AFS#LaunchSite-80.57,28.5Launch SiteActiveUnited StatesGuam 1 GU1 Leolut#GroundStation144.939,13.578333Ground StationActiveUnited StatesESTEC#GroundStation4.41994,52.21838Ground StationActiveNetherlandsKapustin Yar LC 107 2b#LaunchPad46.2959,48.5609Launch PadActiveRussiaSvobodny Cosmodrome#LaunchSite128.4,51.8Launch SiteInactiveRussiaPik Terskol Observatory#OpticalTrackingStation42.49939,43.27631Optical Tracking StationActiveRussiaOrroral Valley STDN ORR3#GroundStation148.95702322,-35.62789289Ground StationRelocatedAustraliaKapustin Yar LC 107 1a#LaunchPad46.298,48.5992Launch PadActiveRussiaKashima Space Research Center 13m B#GroundStation140.66298,35.95631Ground StationActiveJapanUchinoura#LaunchSite131.08,31.251Launch SiteActiveJapanAnkara Geolut#GroundStation32.99,40.1403Ground StationActiveTurkeyPlesetsk Cosmodrome LC 133-3#LaunchPad40.8504,62.887Launch PadActiveRussiaKijal Earth Station#GroundStation103.475,4.439Ground StationActiveMalaysiaPlesetsk Cosmodrome LC 32-1#LaunchPad40.7872,62.9073Launch PadActiveRussiaSantiago Station AGO 3 (STDN AGU3)#GroundStation-70.666403,-33.15110747Ground StationActiveChileQabala Phased-array Radar#RadarStation47.7955,40.8679Radar StationActiveAzerbaijanGCSB Waihopai#GroundStation173.738889,-41.576389Ground StationActiveNew ZealandGRGT STDN GWMS#GroundStation144.8409295,13.58801786Ground StationActiveUnited StatesBarreira do Inferno Launch Center#LaunchSite-35.1613,-5.9236Launch SiteActiveBrazilDiego Garcia GEODSS, xx3#OpticalTrackingStation72.45237,-7.41186Optical Tracking StationActiveUnited KingdomMichelstadt Station#GroundStation8.971934,49.711812Ground StationInactiveGermanySHAR-1#GroundStation80.19631,13.67484Ground StationActiveIndiaMt Lemmon STDN MTLS#GroundStation-110.78945064,32.44213656Ground StationActiveUnited StatesPunta Arenas Station#GroundStation-70.857,-52.938Ground StationActiveChileBermuda STDN BDAL#LaserStation-64.65610619,32.35381689Laser StationInactiveBermudaSan Nicolas Island STDN SN2F#GroundStation-119.52074281,33.247685Ground StationActiveUnited StatesLackland Air Force Base#GroundStation-98.590371,29.363209Ground StationActiveUnited StatesBaikonur Cosmodrome#LaunchSite63.3,46Launch SiteActiveKazakhstanNorth Pole Station USAK01 (STDN USAS)#GroundStation-147.50021417,64.80424111Ground StationActiveUnited StatesOoty Radio Telescope#GroundStation76.666,11.384Ground StationActiveIndiaWeilheim STDN WU2S#GroundStation11.08361889,47.88119889Ground StationActiveGermanyBaikonur Cosmodrome - LC 243#LaunchPad63.737,45.8549Launch PadActiveKazakhstanLongyearbyen EISCAT Radar#RadarStation16.0289,78.1531Radar StationActiveNorwayKapustin Yar - LC 107 2b#LaunchPad46.2959,48.5609Launch PadActiveRussiaKerguelen Island STDN KERS#GroundStation70.257282,-49.352906Ground StationActiveFranceGrand Turk Island STDN GTKL#LaserStation-71.13194483,21.46052522Laser StationActiveUnited KingdomHartebeesthoek STDN HB33#GroundStation27.70744722,-25.88642778Ground StationActiveSouth AfricaIP-1 Tracking Station#GroundStation63.334,45.908Ground StationActiveKazakhstanCSTARS#GroundStation-80.3844,25.6138Ground StationActiveUnited StatesDombarovsky Cosmodrome - x11#LaunchPad59.567713,51.020939Launch PadActiveRussiaDombarovsky Cosmodrome x28#LaunchPad59.635083,51.193164Launch PadActiveRussiaEUMETSAT Maspalomas#GroundStation-15.63295,27.76264Ground StationActiveSpainDombarovsky Cosmodrome x43#LaunchPad59.801475,51.526422Launch PadActiveRussiaCarnarvon Tracking Station#GroundStation113.721,-24.905Ground StationDemolishedAustraliaAlgonquin#GroundStation-78.0727,45.9554Ground StationActiveCanadaDSS 28 Goldstone#GroundStation-116.778711,35.23831Ground StationInactiveUnited StatesArequipa STDN AREL#LaserStation-71.49305469,-16.46570239Laser StationActivePeruKapustin Yar xx5#LaunchPad45.781879,48.781927Launch PadActiveRussiaBarking Sands - LC 42#LaunchPad-159.7727,22.0682Launch PadActiveUnited StatesDombarovsky Cosmodrome - x31#LaunchPad60.606992,51.241105Launch PadActiveRussiaMSSC 15in Aux Telescope#OpticalTrackingStation-156.2576,20.70849Optical Tracking StationActiveUnited StatesSimferopol TNA-400#GroundStation33.890256,45.052703Ground StationActiveUkraineMalabar Test Facility#OpticalTrackingStation-80.684,28.025Optical Tracking StationActiveUnited StatesEsrange Rocket Range - S#LaunchPad21.1054,67.8932Launch PadActiveSwedenGuiana Space Center - ZL3#LaunchPad-52.7685,5.2393Launch PadActiveFrench GuianaDombarovsky Cosmodrome x21#LaunchPad59.576451,51.102299Launch PadActiveRussiaPCMC Radar#RadarStation80.19886,13.67944Radar StationActiveIndiaCape Canaveral Air Force Station - LC 26B#LaunchPad-80.5712,28.4433Launch PadInactiveUnited StatesCDAS#GroundStation116.27,40.05Ground StationActiveChinaDombarovsky Cosmodrome x30#LaunchPad59.85002,51.207054Launch PadActiveRussiaDombarovsky Cosmodrome xx6#LaunchPad60.52463,50.877274Launch PadActiveRussiaGuam STDN GWM3#GroundStation144.73681531,13.31068944Ground StationDemolishedUnited StatesRAL Station 4m#GroundStation-1.43673,51.14283Ground StationActiveUnited KingdomEsrange Station ETX STDN KU1S#GroundStation21.06565472,67.88955833Ground StationActiveSwedenUniversity of Hawaii STDN HAWQ#GroundStation-157.8864,21.3161Ground StationActiveUnited StatesOrbcomm Arcade B#GroundStation-78.382119,42.524096Ground StationActiveUnited StatesKiruna Station KIR-2 13m#GroundStation20.96688,67.858428Ground StationActiveSwedenEdwards AFB STDN EA2F#GroundStation-117.93056167,34.97045389Ground StationActiveUnited StatesPoker Flat LC 5#LaunchPad-147.4832,65.1292Launch PadActiveUnited StatesNy-Alesund#GroundStation11.870967,78.929071Ground StationActiveNorwayWashington International Teleport#GroundStation-77.164891,38.794257Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 8#LaunchPad-120.6324,34.5762Launch PadActiveUnited StatesMaspalomas (2) Geolut#GroundStation-15.634,27.764Ground StationActiveSpainAtlanta STDN ATDQ#GroundStation-84.10862994,33.93088417Ground StationActiveUnited StatesDongara Station AUWA01 STDN USDS#GroundStation115.348678,-29.045772Ground StationActiveAustraliaPoint Mugu LC 1#LaunchPad-119.1215,34.0996Launch PadActiveUnited StatesTromso Satellite Station#GroundStation18.9408,69.6625Ground StationActiveNorwayDombarovsky Cosmodrome x23#LaunchPad60.675969,51.146793Launch PadActiveRussiaMaui STDN MA2C#OpticalTrackingStation-156.2575,20.70057325Optical Tracking StationActiveUnited StatesWettzel STDN WETL#LaserStation12.87800803,49.14441731Laser StationActiveGermanyCape Canaveral Air Force Station#LaunchSite-80.57,28.5Launch SiteActiveUnited StatesClear AFS BMEWS#RadarStation-149.1897,64.3002Radar StationActiveUnited StatesMalaysia Space Centre#GroundStation101.508,2.784Ground StationActiveMalaysiaBaikonur Cosmodrome - LC 67-22#LaunchPad63.7073,45.9895Launch PadActiveKazakhstanIssus-Aussaguel Station#GroundStation1.497,43.429Ground StationActiveFranceWallops Flight Facility LA 4 MAST#LaunchPad-75.4702,37.8508Launch PadActiveUnited StatesPlesetsk Cosmodrome PU 11#LaunchPad40.4953,62.8978Launch PadActiveRussiaOrbcomm Albury A#GroundStation146.53351,-36.03145Ground StationActiveAustraliaBiscarosse BS#LaunchPad-1.2675,44.2882Launch PadInactiveFranceCape Canaveral Air Force Station - LC 18B#LaunchPad-80.562,28.449Launch PadInactiveUnited StatesGuiana Space Center ZL3#LaunchPad-52.7685,5.2393Launch PadActiveFrench GuianaAntigua Radar STDN ANTQ#RadarStation-61.7925125,17.14365111Radar StationActiveAntigua and BarbudaPoker Flat LC 2#LaunchPad-147.4857,65.1299Launch PadActiveUnited StatesTokai University#GroundStation130.87,32.835Ground StationActiveJapanKapustin Yar - xx3#LaunchPad46.300125,48.540529Launch PadActiveRussiaHong Kong (2) Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaPaliivka Station#GroundStation30.5069,46.6339Ground StationActiveUkraineKapustin Yar - SAM#LaunchPad45.7346,48.8055Launch PadActiveRussiaDSS 14 Goldstone STDN DS14#GroundStation-116.88953822,35.42590086Ground StationActiveUnited StatesVLBA St Croix#GroundStation-64.583631,17.756578Ground StationActiveUnited StatesPoker Flat - LC 4#LaunchPad-147.4827,65.1303Launch PadActiveUnited StatesVandenberg AFB SLC 2W STDN WTRP#LaunchPad-120.62237833,34.75563222Launch PadActiveUnited StatesOrbcomm Hartebeesthoek B#GroundStation27.70568,-25.88812Ground StationActiveSouth AfricaDombarovsky Cosmodrome x18#LaunchPad59.842142,51.094496Launch PadActiveRussiaVandenberg Air Force Base - SLC 3W (STDN WT3P)#LaunchPad-120.59285703,34.64367528Launch PadActiveUnited StatesBisei Spaceguard Center#OpticalTrackingStation133.545,34.672Optical Tracking StationActiveJapanGRGT STDN GW2K#GroundStation144.84087447,13.58758828Ground StationActiveUnited StatesFinegayan SATCOM Site#GroundStation144.849827,13.583618Ground StationActiveUnited StatesKodiak Launch Complex#LaunchSite-152.3393,57.4352Launch SiteActiveUnited StatesWhite Sands STDN STGS#GroundStation-106.61208894,32.54245167Ground StationActiveUnited StatesLucknow Leolut#GroundStation80.9573,26.913333Ground StationActiveIndiaBarking Sands#LaunchSite-159.78,22.06Launch SiteActiveUnited StatesWeipa STDN KRCS#GroundStation141.93066667,-12.694Ground StationActiveAustraliaDombarovsky Cosmodrome - x26#LaunchPad59.74824,51.154839Launch PadActiveRussiaDombarovsky Cosmodrome - x12#LaunchPad59.808673,51.022624Launch PadActiveRussiaVandenberg Air Force Base - SLC 3E (STDN WTEP)#LaunchPad-120.59,34.64Launch PadActiveUnited StatesGreen Bank 85-2 Telescope#GroundStation-79.8525,38.42577Ground StationActiveUnited StatesSubmarine Launch Platform - Barents Sea#LaunchSite34.2,69.5Launch SiteActiveRussiaWettzell#GroundStation12.8772,49.145Ground StationActiveGermanyBaikonur Cosmodrome xx4#LaunchPad62.935495,46.079747Launch PadActiveKazakhstanBaikonur Cosmodrome - LC 104#LaunchPad63.4197,45.9875Launch PadActiveKazakhstanVillafranca VIL-1#GroundStation-3.951582,40.442565Ground StationInactiveSpainGreen Bank Telescope 14m#GroundStation-79.8224,38.43326Ground StationActiveUnited StatesEsrange Station ESTC STDN KU2S#GroundStation21.06044833,67.8831825Ground StationActiveSwedenHerstmonceaux STDN HERL#LaserStation0.33612414,50.86738069Laser StationActiveUnited KingdomWallops Island STDN WL3F#GroundStation-75.51143536,37.85636525Ground StationActiveUnited StatesEsrange Station ESX (STDN KIXS)#GroundStation21.063398,67.878157Ground StationActiveSwedenMU Radar#RadarStation136.1055,34.854Radar StationActiveJapanBarking Sands LC 1#LaunchPad-159.777,22.058Launch PadActiveUnited StatesSouthbury Station#GroundStation-73.2889,41.4514Ground StationActiveUnited StatesSantiago Station AGO 4 (STDN AG33)#GroundStation-70.66830756,-33.15147853Ground StationActiveChilePlesetsk Cosmodrome - LC 32-2#LaunchPad40.7895,62.9057Launch PadActiveRussiaDSS 62 Cebreros#GroundStation-4.36788,40.45311Ground StationDemolishedSpainVandenberg Air Force Base - SLC 2E#LaunchPad-120.619201,34.751617Launch PadDemolishedUnited StatesKTS#GroundStation-152.178,57.5986Ground StationDemolishedUnited StatesDombarovsky Cosmodrome x40#LaunchPad60.372543,51.379286Launch PadActiveRussiaMaspalomas 2 Geolut#GroundStation-15.634,27.764Ground StationActiveSpainBaikonur Cosmodrome LC 244#LaunchPad63.6346,45.8403Launch PadActiveKazakhstanDombarovsky Cosmodrome - x13#LaunchPad59.689541,51.030416Launch PadActiveRussiaAdour 2 Radar STDN ADRQ#RadarStation-52.74840111,5.20912725Radar StationActiveFrench GuianaOrbcomm Albury B#GroundStation146.53351,-36.03223Ground StationActiveAustraliaVTSB#GroundStation-120.505403,34.8256235Ground StationActiveUnited StatesUchinoura Space Center, 20m#GroundStation131.080567,31.256784Ground StationActiveJapanBaikonur Cosmodrome - LC 81-24#LaunchPad62.9848,46.0709Launch PadActiveKazakhstanWallops Island STDN WD4F#GroundStation-75.511439,37.85636639Ground StationActiveUnited StatesBaikonur Cosmodrome LC 109#LaunchPad63.4452,45.9525Launch PadActiveKazakhstanKeelung (2) Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanTelkom SA Earth Station#GroundStation27.71,-25.91Ground StationActiveSouth AfricaVTSA#GroundStation-120.501852,34.8226094Ground StationActiveUnited StatesBaikonur Cosmodrome - xx3#LaunchPad63.481448,45.945516Launch PadActiveKazakhstanZimmerwald#GroundStation7.478169,46.886794Ground StationActiveSwitzerlandNHSB#GroundStation-71.630319,42.9447544Ground StationActiveUnited StatesElephant Butte Space Fence#RadarStation-106.9972,33.4431Radar StationActiveUnited StatesMid-Atlantic Regional Spaceport#LaunchSite-75.49,37.832Launch SiteActiveUnited StatesGrand Bahama Island STDN GBIY#GroundStation-78.29909119,26.62544656Ground StationActiveBahamasMoores Valley Station#GroundStation-123.2875,45.3425Ground StationDemolishedUnited StatesJiuquan xx2#LaunchPad100.305083,41.283173Launch PadActiveChinaSouth Uist Range Radar#GroundStation-7.446834,57.617444Ground StationActiveUnited KingdomKapustin Yar xx3#LaunchPad46.300125,48.540529Launch PadActiveRussiaIssus-Aussaguel Station#GroundStation1.497,43.429Ground StationActiveFranceAscension#LaunchSite-14.4147,-7.9748Launch SiteActiveUnited KingdomAyios Nikolaos Station#GroundStation33.888,35.09423Ground StationActiveCyprusAscension Island BRT STDN AC2J#GroundStation-14.39076792,-7.91791558Ground StationActiveUnited KingdomHammaguira Brigitte-A#LaunchPad-3.0081,30.8712Launch PadActiveAlgeriaHartebeesthoek STDN HBKS#GroundStation27.712,-25.887Ground StationActiveSouth AfricaGRAVES Receiver#RadarStation5.5346,44.0715Radar StationActiveFranceDSS 13 Goldstone STDN VEND#GroundStation-116.794459,35.24716425Ground StationActiveUnited StatesGalenki 32m#GroundStation131.7561,44.0204Ground StationActiveRussiaCape Canaveral AFS LC 18B#LaunchPad-80.562,28.449Launch PadInactiveUnited StatesSan Nicolas Island STDN SNIF#GroundStation-119.52009433,33.24697758Ground StationActiveUnited StatesHaystack Auxilliary Radar#RadarStation-71.4872,42.62283Radar StationActiveUnited StatesVLBA Brewster#GroundStation-119.683278,48.131228Ground StationActiveUnited StatesDombarovsky Cosmodrome - x42#LaunchPad59.972508,51.521988Launch PadActiveRussiaWallops Flight Facility LA 2 JUP#LaunchPad-75.482,37.8394Launch PadActiveUnited StatesMid-Atlantic Regional Spaceport - Launch Pad 0-A#LaunchPad-75.4882,37.8338Launch PadActiveUnited StatesGilmore Creek STDN ULA4#GroundStation-147.52128289,64.97659575Ground StationActiveUnited StatesKashima Space Research Center 26m#GroundStation140.6627,35.9541Ground StationDemolishedJapanUchinoura Space Center - Lambda Pad#LaunchPad131.079125,31.251908Launch PadActiveJapanMizusawa VLBI Observatory#GroundStation141.1326,39.1335Ground StationActiveJapanOmelek Island#LaunchSite167.743322,9.048224Launch SiteActiveMarshall IslandsBaikonur Cosmodrome LC 60-8#LaunchPad64.0183,46.0174Launch PadActiveKazakhstanHiroshima Institute of Technology#GroundStation132.3761,34.3678Ground StationActiveJapanTHEOS Control and Receiving Station#GroundStation100.932849,13.103529Ground StationActiveThailandZhongshan Station#GroundStation76.378,-69.379Ground StationActiveAntarcticaDSS 44 Honeysuckle Creek#GroundStation148.97784,-35.58319Ground StationRelocatedAustraliaDombarovsky Cosmodrome - x35#LaunchPad60.858975,51.26491Launch PadActiveRussiaDombarovsky Cosmodrome#LaunchSite60,51Launch SiteActiveRussiaBiscarosse BESA#LaunchPad-1.2335,44.3917Launch PadActiveFranceAllen Telescope Array#GroundStation-121.47,40.817Ground StationActiveUnited StatesDulles Station#GroundStation-77.42795,39.01449Ground StationActiveUnited StatesSvobodny Cosmodrome - xx1#LaunchPad128.3102,51.747Launch PadInactiveRussiaRAF Fylingdales BMEWS#RadarStation-0.6701,54.3618Radar StationActiveUnited KingdomWoomera Test Range - LA 4#LaunchPad136.5155,-30.9053Launch PadActiveAustraliaBaikonur Cosmodrome LC 163#LaunchPad63.172,46.002Launch PadActiveKazakhstanAndover Station#GroundStation-70.699,44.633Ground StationActiveUnited StatesThuraya Station#GroundStation55.8285,25.2433Ground StationActiveUnited Arab EmiratesGEROC Ikonos Station#GroundStation11.28006,48.08407Ground StationActiveGermanySuffa RT-70 Radio Telescope#GroundStation68.448,39.624Ground StationActiveUzbekistanSocorro GEODSS, xx3#OpticalTrackingStation-106.65968,33.81703Optical Tracking StationActiveUnited StatesBaikonur Cosmodrome xx5#LaunchPad62.932319,46.081306Launch PadActiveKazakhstanBiscarosse - BLB#LaunchPad-1.262,44.3659Launch PadActiveFranceAlaska 2 (AK2) Leolut#GroundStation-147.5177,64.9735Ground StationActiveUnited StatesOuargla Leolut#GroundStation5.49,31.88Ground StationActiveAlgeriaEsrange Station ESX STDN KIXS#GroundStation21.063398,67.878157Ground StationActiveSwedenMaryland 2 Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesBaikonur Cosmodrome LC 103#LaunchPad63.4448,45.9523Launch PadActiveKazakhstanWellington (1) Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandWallops Flight Facility - LA 4 ML#LaunchPad-75.4698,37.851Launch PadActiveUnited StatesJiuquan LA 3B#LaunchPad100.7803,41.1593Launch PadActiveChinaColomb-Bechar#LaunchSite-2.255,31.693Launch SiteActiveAlgeriaWallops Flight Facility LA 2 HAD#LaunchPad-75.4826,37.8383Launch PadActiveUnited StatesAnkara (1) Leolut#GroundStation32.9897,40.140833Ground StationActiveTurkeyTCSA#GroundStation-0.90636355,51.1167229Ground StationActiveUnited KingdomBiscarosse - BP#LaunchPad-1.2396,44.3755Launch PadActiveFranceNakhodka Station#GroundStation132.7906,42.8584Ground StationActiveRussiaPlesetsk Cosmodrome - LC 16-2#LaunchPad40.6834,62.96Launch PadActiveRussiaPlesetsk Cosmodrome - LC 35#LaunchPad40.575422,62.927806Launch PadActiveRussiaGilmore Creek STDN GILD#GroundStation-147.49800019,64.97850925Ground StationActiveUnited StatesKapustin Yar LC 86 4c#LaunchPad46.297,48.5481Launch PadActiveRussiaOrbcomm Maghreb A#GroundStation-7.64361,33.04828Ground StationActiveMoroccoTidbinbilla STDN RTKS#GroundStation148.98260486,-35.40474494Ground StationActiveAustraliaPrimrose Lake#LaunchSite-110.05,54.75Launch SiteActiveCanadaNetaji Station#GroundStation88.4276,22.946Ground StationActiveIndiaRobins AFB PAVE PAWS#RadarStation-83.56936,32.58115Radar StationInactiveUnited StatesSantiago Station AGO 4 STDN AG33#GroundStation-70.66830756,-33.15147853Ground StationActiveChileAscension Island STDN ASNS#GroundStation-14.33333333,-7.91666717Ground StationActiveUnited KingdomJiuquan Satellite Launch Center - xx2#LaunchPad100.305083,41.283173Launch PadActiveChinaBretagne 2 Radar STDN BREQ#RadarStation-52.30954428,4.94887892Radar StationActiveFrench GuianaVandenberg Air Force Base - SLC 10E#LaunchPad-120.6213,34.7626Launch PadInactiveUnited StatesTTSC#GroundStation-68.605031,76.5157036Ground StationActiveGreenlandWhite Sands LC 39#LaunchPad-106.2367,32.4161Launch PadActiveUnited StatesArecibo Observatory#RadarStation-66.753083,18.3435Radar StationActiveUnited StatesOrbcomm Wenatchee A#GroundStation-120.17509,47.551685Ground StationActiveUnited StatesWallops Flight Facility LA 4 HAD#LaunchPad-75.4696,37.8511Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 75#LaunchPad63.1983,45.9657Launch PadActiveKazakhstanDombarovsky Cosmodrome - x46#LaunchPad60.082438,51.588487Launch PadActiveRussiaDombarovsky Cosmodrome xx3#LaunchPad59.597872,50.756297Launch PadActiveRussiaEsrange Station ESX#GroundStation21.0634472,67.8781431Ground StationActiveSwedenKumsan Station#GroundStation127.4892,36.125Ground StationActiveSouth KoreaKennedy Space Center - LC 39A (STDN A39P)#LaunchPad-80.60411653,28.60827486Launch PadActiveUnited StatesScanEx Moscow Station#GroundStation37.58411,55.73606Ground StationActiveRussiaMaiquetia 2 Leolut#GroundStation-66.9865,10.599Ground StationPlannedVenezuelaOTC Carnarvon#GroundStation113.7049,-24.8691Ground StationActiveAustraliaVandenberg AFB SLC 5#LaunchPad-120.6247,34.608Launch PadInactiveUnited StatesEglin AFB STDN EG3F#GroundStation-86.798012,30.42166558Ground StationInactiveUnited StatesVLBA Pie Town#GroundStation-108.119183,34.301003Ground StationActiveUnited StatesDombarovsky Cosmodrome xx7#LaunchPad59.73193,50.883614Launch PadActiveRussiaBaikonur Cosmodrome LC 51#LaunchPad63.3409,45.9239Launch PadActiveKazakhstanSanta Paula Station#GroundStation-119.0734,34.4021Ground StationActiveUnited StatesWallops Island STDN WLPF#GroundStation-75.48508908,37.84133972Ground StationActiveUnited StatesEagle Vision 2#GroundStation-104.71,38.82Ground StationActiveUnited StatesLibreville Station STDN LBVS#GroundStation9.67530028,0.35462978Ground StationActiveGabonGreen Bank 85-3 Telescope#GroundStation-79.84341,38.42959Ground StationActiveUnited StatesHTS STDN HTSS#GroundStation-158.24208956,21.56227219Ground StationActiveUnited StatesHyderabad#GroundStation78.188333,17.028611Ground StationActiveIndiaLAPAN Station#GroundStation119.65,-3.978Ground StationActiveIndonesiaYellowknife Site#GroundStation-114.47924,62.48044Ground StationActiveCanadaBaikonur Cosmodrome LC 191-66#LaunchPad63.1966,45.9698Launch PadActiveKazakhstanPoker Flat LC 4#LaunchPad-147.4827,65.1303Launch PadActiveUnited StatesCape Canaveral AFS LC 1#LaunchPad-80.5375,28.465Launch PadInactiveUnited StatesKunming Station#GroundStation102.79583,25.02734Ground StationActiveChinaWake Island#LaunchSite166.618,19.29Launch SiteActiveUnited StatesChangchun Station#GroundStation125.324,43.817Ground StationActiveChinaBiak#GroundStation136.1074,-1.19Ground StationActiveIndonesiaMaryland 1 Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesBaikonur Cosmodrome LC 67-21#LaunchPad63.705,45.9893Launch PadActiveKazakhstanJiuquan LA 2A#LaunchPad100.3165,41.3088Launch PadActiveChinaRiverside Teleport#GroundStation-117.087843,33.795862Ground StationActiveUnited StatesIrkutsk Radar#RadarStation103.259,52.877Radar StationActiveRussiaRAL Station 12m STDN RALS#GroundStation-1.31146389,51.572026Ground StationActiveUnited KingdomTehran Station#GroundStation51.408,35.78Ground StationActiveIranUchinoura 10m#GroundStation131.084654,31.255675Ground StationActiveJapanOnizuka Control Node xx1#GroundStation-122.028987,37.403778Ground StationInactiveUnited StatesSubmarine Launch Platform Barents Sea#LaunchSite34.2,69.5Launch SiteActiveRussiaMaryland (1) Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesPoker Flat - LC 2#LaunchPad-147.4857,65.1299Launch PadActiveUnited StatesNy-Alesund Station#GroundStation11.883535,78.927718Ground StationActiveNorwayAlcantara Launch Center MRL Pad#LaunchPad-44.3671,-2.3167Launch PadActiveBrazilSocorro GEODSS Cam2#OpticalTrackingStation-106.65962,33.81725Optical Tracking StationActiveUnited StatesWoomera Test Range LA 6A#LaunchPad136.4394,-31.074Launch PadActiveAustraliaERIS#GroundStation-88.263568,18.544696Ground StationActiveMexicoWhite Sands STDN WH3K#GroundStation-106.60855158,32.50046228Ground StationActiveUnited StatesNevada Test Site Area 26#LaunchSite-116.114,36.771Launch SiteActiveUnited StatesKapustin Yar LC 107 1b#LaunchPad46.298,48.5969Launch PadActiveRussiaJiuquan SLS 2#LaunchPad100.2983,40.9607Launch PadActiveChinaEsrange Station TTC (STDN KICS)#GroundStation21.060769,67.884232Ground StationActiveSwedenShcholkovo#GroundStation37.9576,55.9506Ground StationActiveRussiaDombarovsky Cosmodrome xx9#LaunchPad59.551097,50.972694Launch PadActiveRussiaEsrange Rocket Range#LaunchSite21.105,67.893Launch SiteActiveSwedenAlcantara Launch Center VLS Pad#LaunchPad-44.3675,-2.3184Launch PadActiveBrazilCasey Station#GroundStation110.52871,-66.28125Ground StationActiveAntarcticaNegev#LaunchSite34.9728,31.02335Launch SiteActiveIsraelDombarovsky Cosmodrome x25#LaunchPad59.524663,51.153297Launch PadActiveRussiaLannion STDN LANS#GroundStation-3.47,48.75141536Ground StationActiveFranceCape Canaveral AFS LC 30#LaunchPad-80.5803,28.4393Launch PadInactiveUnited StatesByalalu 32m#GroundStation77.36898,12.90314Ground StationActiveIndiaSeoul#GroundStation126.9997,37.5664Ground StationActiveSouth KoreaQuantico#GroundStation-77.3198,38.4912Ground StationActiveUnited StatesConcepcion#GroundStation-73.025036,-36.842772Ground StationActiveChilePlesetsk Cosmodrome LC 41-1#LaunchPad40.529,62.9405Launch PadDemolishedRussiaCape Canaveral AFS LC 9#LaunchPad-80.5594,28.4522Launch PadInactiveUnited StatesEOC Antenna 4#GroundStation139.35,36.0026Ground StationActiveJapanKwajalein STDN KMQF#RadarStation167.726622,8.72163539Radar StationActiveMarshall IslandsLuxembourg Teleport#GroundStation6.114,49.579Ground StationActiveLuxembourgNCTS Bahrain#GroundStation50.61,26.2073Ground StationActiveBahrainKapustin Yar LC 107 2h#LaunchPad46.2949,48.5617Launch PadActiveRussiaWallops Flight Facility#LaunchSite-75.48,37.85Launch SiteActiveUnited StatesCape Canaveral AFS LC 26B#LaunchPad-80.5712,28.4433Launch PadInactiveUnited StatesMerritt Island STDN MIMF#GroundStation-80.68279381,28.62594319Ground StationActiveUnited StatesSantiago Geolut#GroundStation-70.7,-33.489Ground StationActiveChileAhemad Station#GroundStation78.1042,30.1778Ground StationActiveIndiaWhite Sands STDN WHSF#GroundStation-106.36980742,32.35804211Ground StationActiveUnited StatesJeddah (2) Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaDSS 65 Robledo STDN DS65#GroundStation-4.25069889,40.42720636Ground StationActiveSpainFirepond Laser#LaserStation-71.4923,42.61757Laser StationActiveUnited StatesPoker Flat Station PF1 STDN DX2S#GroundStation-147.4311625,65.11783389Ground StationActiveUnited StatesKapustin Yar - PL1#LaunchPad46.2621,48.4116Launch PadActiveRussiaAPL 10m#GroundStation-76.89825,39.169Ground StationActiveUnited StatesDSS 45 Tidbinbilla STDN DS45#GroundStation148.97768564,-35.39845769Ground StationActiveAustraliaKashi Ground Station#GroundStation75.929,39.505Ground StationActiveChinaCape Canaveral AFS LC 25#LaunchPad-80.575,28.431Launch PadInactiveUnited StatesCape Canaveral AFS SLC 36B STDN B36P#LaunchPad-80.54095408,28.46836775Launch PadDemolishedUnited StatesWallops Island STDN WL4F#GroundStation-75.511439,37.85636639Ground StationActiveUnited StatesGreen Bank Telescope 12m#GroundStation-79.83131,38.43724Ground StationActiveUnited StatesAlgiers Geolut#GroundStation3.381,36.7533Ground StationActiveAlgeriaNCTAMS PAC#GroundStation-157.995,21.52Ground StationActiveUnited StatesGlobus II#RadarStation31.1271,70.3671Radar StationActiveNorwayKapustin Yar xx1#LaunchPad46.318006,48.484051Launch PadActiveRussiaNenoksa Test Range#LaunchSite39.22,64.646Launch SiteActiveRussiaMSSC AEOS 3.7m#OpticalTrackingStation-156.2569,20.7081Optical Tracking StationActiveUnited StatesNanhai Station#GroundStation113.14,23.03Ground StationActiveChinaSilver Lake Space Fence#RadarStation-91.0211,33.145Radar StationActiveUnited StatesKourou Station STDN KRUP#GroundStation-52.80582497,5.25185486Ground StationActiveFrench GuianaSvobodny Cosmodrome xx9#LaunchPad128.3656,51.837Launch PadInactiveRussiaBaikonur Cosmodrome - LC 80-17#LaunchPad64.0198,46.0068Launch PadActiveKazakhstanOrbcomm St Johns A#GroundStation-109.554917,34.456282Ground StationActiveUnited StatesMSSC RAVEN#OpticalTrackingStation-156.25745,20.7085Optical Tracking StationActiveUnited StatesASF 10m#GroundStation-147.849467,64.859482Ground StationActiveUnited StatesSvobodny Cosmodrome - xx5#LaunchPad128.23218,51.80532Launch PadInactiveRussiaDombarovsky Cosmodrome - x30#LaunchPad59.85002,51.207054Launch PadActiveRussiaAtlanta STDN ATDS#GroundStation-84.10862994,33.93088417Ground StationActiveUnited StatesBaikonur Cosmodrome - xx2#LaunchPad63.462445,45.94176Launch PadActiveKazakhstanWallops Island (STDN WAPS)#GroundStation-75.4765225,37.92492556Ground StationActiveUnited StatesBlossom Point Tracking Facility#GroundStation-77.086,38.431Ground StationActiveUnited StatesCape Canaveral STDN CN2F#GroundStation-80.59056164,28.52887219Ground StationInactiveUnited StatesLackland AFB#GroundStation-98.590371,29.363209Ground StationActiveUnited StatesFranklin Station#GroundStation-74.5757,41.1167Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 195#LaunchPad63.2127,45.783Launch PadActiveKazakhstanWallops Flight Facility - LA 5#LaunchPad-75.4681,37.8529Launch PadActiveUnited StatesYevpatoria North Station#GroundStation33.169,45.221Ground StationActiveUkraineCyberjaya Station#GroundStation101.6583,2.9348Ground StationActiveMalaysiaDombarovsky Cosmodrome x27#LaunchPad59.967627,51.168438Launch PadActiveRussiaHumacao Station#GroundStation-65.788,18.1494Ground StationActiveUnited StatesCombe Martin Geolut#GroundStation-4.047166,51.1675Ground StationActiveUnited KingdomDombarovsky Cosmodrome - x38#LaunchPad60.163782,51.338805Launch PadActiveRussiaMullach Sgr Radar Station#GroundStation-8.580436,57.806607Ground StationActiveUnited KingdomAtlanta STDN ATLS#GroundStation-84.10836667,33.93086667Ground StationActiveUnited StatesWestern Australian Receiver#RadarStation122.008,-28.327Radar StationActiveAustraliaProspect Harbor Naval Satellite Operations Station#GroundStation-68.013,44.404Ground StationActiveUnited StatesCape Town Leolut#GroundStation18.5,-33.88Ground StationActiveSouth AfricaLarge Millimeter Telescope#RadarStation-97.3149,18.9858Radar StationActiveMexicoAerospace Data Facility Southwest#GroundStation-106.6,32.5Ground StationActiveUnited StatesIbaraki Satellite Control Center#GroundStation140.373,36.532Ground StationActiveJapanCobra Dane Radar#RadarStation174.0914,52.7373Radar StationActiveUnited StatesKrona 30J6 Complex#OpticalTrackingStation41.226213,43.718337Optical Tracking StationActiveRussiaBaikonur Cosmodrome - xx1#LaunchPad63.462718,45.939593Launch PadActiveKazakhstanFairbanks STDN UL23#GroundStation-147.51806567,64.97240711Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 90-20#LaunchPad62.9167,46.0855Launch PadActiveKazakhstanAPL 5m#GroundStation-76.89839,39.16821Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 193#LaunchPad63.389,45.9532Launch PadActiveKazakhstanEagle Vision 1#GroundStation7.6,49.44Ground StationActiveGermanyTilla#LaunchSite73.29608,33.3961Launch SiteActivePakistanVandenberg AFB SLC 4E#LaunchPad-120.61059,34.632067Launch PadInactiveUnited StatesSingapore Leolut#GroundStation103.988,1.352Ground StationActiveSingaporeWallops Flight Facility LA 4 ML#LaunchPad-75.4698,37.851Launch PadActiveUnited StatesYamaguchi Station#GroundStation131.557,34.217Ground StationActiveJapanWhite Sands BRT STDN WHSJ#GroundStation-106.61196575,32.50628147Ground StationActiveUnited StatesHawaii Station STDN HAWS#GroundStation-155.663,19.01358372Ground StationActiveUnited StatesOrbcomm Curacao#GroundStation-69.1538,12.3814Ground StationActiveNetherlandsKiruna Station KIR-1 15m STDN KI2S#GroundStation20.96434169,67.85712517Ground StationActiveSwedenWhite Sands LC 38#LaunchPad-106.2796,32.4179Launch PadActiveUnited StatesHolloman#LaunchSite-106.07,32.88Launch SiteActiveUnited StatesBaikonur Cosmodrome LC 60-6#LaunchPad64.0161,46.0188Launch PadActiveKazakhstanXichang#LaunchSite102.029,28.246Launch SiteActiveChinaWeilheim 11m#GroundStation11.08538,47.88118Ground StationActiveGermanySyowa Station STDN SYOQ#GroundStation39.59015389,-69.00609644Ground StationActiveAntarcticaKorou SIGINT Station#GroundStation-52.7007,5.1664Ground StationActiveFrench GuianaGreenbelt Test BRT STDN BLTJ#GroundStation-76.83792783,39.00270353Ground StationActiveUnited StatesXichang LA 1#LaunchPad102.0292,28.2474Launch PadActiveChinaMopra Observatory#GroundStation149.0996,-31.2678Ground StationActiveAustraliaWhite Sands LC 32#LaunchPad-106.4069,32.4068Launch PadActiveUnited StatesJiamusi Station#GroundStation130.32,46.8Ground StationActiveChinaOrbcomm Rio de Janiero A#GroundStation-42.87275,-22.69619Ground StationActiveBrazilTokyo Institute of Technology#GroundStation139.68495,35.60084Ground StationActiveJapanAlcantara Launch Center - VLS Pad#LaunchPad-44.3675,-2.3184Launch PadActiveBrazilWhite Sands LC 33#LaunchPad-106.443,32.3396Launch PadActiveUnited StatesTeide Observatory#GroundStation-16.51,28.3Ground StationActiveSpainWoomera Test Range LA 9#LaunchPad136.4871,-30.9031Launch PadActiveAustraliaDombarovsky Cosmodrome - x27#LaunchPad59.967627,51.168438Launch PadActiveRussiaHammaguira Bacchus#LaunchPad-3.1241,30.8565Launch PadActiveAlgeriaUssuriysk Observatory#OpticalTrackingStation132.16583,43.69917Optical Tracking StationActiveRussiaBaikonur Cosmodrome - LC 191-66#LaunchPad63.1966,45.9698Launch PadActiveKazakhstanTTS STDN TT2S#GroundStation-68.59885831,76.51536442Ground StationActiveGreenland5N24 Argun Radar#RadarStation73.5721,45.808Radar StationActiveKazakhstanLibya Station#GroundStation13.236,32.34Ground StationActiveLibyaCanadian Forces Station Leitrim#GroundStation-75.5867,45.3377Ground StationActiveCanadaSemnan Launch Center - xx2#LaunchPad53.921,35.2347Launch PadActiveIranWallops Island STDN WPSS#GroundStation-75.47630453,37.92658986Ground StationActiveUnited StatesBiscarosse BLB#LaunchPad-1.262,44.3659Launch PadActiveFranceTelegraph Hill#GroundStation-14.3915,-7.9743Ground StationActiveUnited KingdomPoker Flat - LC 5#LaunchPad-147.4832,65.1292Launch PadActiveUnited StatesNaval SATCOM Facility Northwest#GroundStation-76.267222,36.559444Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 4W#LaunchPad-120.6154,34.6331Launch PadInactiveUnited StatesKapustin Yar LC 107 2c#LaunchPad46.2949,48.5695Launch PadActiveRussiaCape Canaveral Air Force Station - SLC 36A (STDN A36P)#LaunchPad-80.53772211,28.47143831Launch PadDemolishedUnited StatesTelesat Calgary Teleport#GroundStation-114.016,51.053Ground StationActiveCanadaWallops Flight Facility - LA 2 HAD#LaunchPad-75.4826,37.8383Launch PadActiveUnited StatesEchoStar Orange Station#GroundStation-74.2203,40.784Ground StationActiveUnited StatesDSS 49 Parkes#GroundStation148.263524,-32.998278Ground StationActiveAustraliaPlesetsk Cosmodrome LC 133-1#LaunchPad40.8468,62.8868Launch PadActiveRussiaAPT Satellite Control Center#GroundStation114.188,22.453Ground StationActiveChinaTelesat Montreal Teleport#GroundStation-73.5504,45.5224Ground StationActiveCanadaWallops Flight Facility LA 2#LaunchPad-75.4834,37.8376Launch PadActiveUnited StatesMoron MOSS#OpticalTrackingStation-5.5884,37.1516Optical Tracking StationActiveSpainBaikonur Cosmodrome - LC 175-2#LaunchPad62.987,46.0512Launch PadActiveKazakhstanCape Canaveral AFS SLC 20#LaunchPad-80.5567,28.5122Launch PadActiveUnited StatesKiritimati Downrange Tracking Station#GroundStation-157.44821,2.04613Ground StationActiveKiribatiSentosa Station#GroundStation103.836,1.248Ground StationActiveSingaporePoint Mugu - LC 1#LaunchPad-119.1215,34.0996Launch PadActiveUnited StatesWhite Sands STDN WSGT#GroundStation-106.60854583,32.5013695Ground StationActiveUnited StatesKapustin Yar - xx6#LaunchPad45.8152,48.783061Launch PadActiveRussiaBrisbane Station#GroundStation153.1312,-27.5539Ground StationActiveAustraliaCape Canaveral Air Force Station - LC 14#LaunchPad-80.5471,28.4911Launch PadInactiveUnited StatesSouth Uist Missile Range#LaunchSite-7.4,57.36Launch SiteActiveUnited KingdomWallops Flight Facility LA 3B#LaunchPad-75.4725,37.8494Launch PadActiveUnited StatesBermuda STDN BDA3#GroundStation-64.65788833,32.35126753Ground StationInactiveBermudaEchoStar Monee Station#GroundStation-87.7764,41.4684Ground StationActiveUnited StatesPenteli Geolut#GroundStation23.883,38.0808Ground StationActiveGreeceDSS 43 Tidbinbilla STDN DS43#GroundStation148.98126731,-35.40242422Ground StationActiveAustraliaBaikonur Cosmodrome - LC 51#LaunchPad63.3409,45.9239Launch PadActiveKazakhstanSmolino A-35 Anti-missile Site#RadarStation36.482,55.35Radar StationActiveRussiaWallops Flight Facility - LA 1 AML#LaunchPad-75.4871,37.8352Launch PadActiveUnited StatesDombarovsky Cosmodrome x13#LaunchPad59.689541,51.030416Launch PadActiveRussiaBaikonur Cosmodrome LC 75#LaunchPad63.1983,45.9657Launch PadActiveKazakhstanGuiana Space Center - Diamant Pad#LaunchPad-52.7524,5.2325Launch PadInactiveFrench GuianaGilmore Creek STDN GLAS#GroundStation-147.51283867,64.97367197Ground StationActiveUnited StatesEik Station#GroundStation6.4675,58.5383Ground StationActiveNorwayGran Canaria Drop Zone#LaunchSite-15.3,27Launch SiteActiveSpainKashima Space Research Center 13m A#GroundStation140.66245,35.95629Ground StationActiveJapanAlaska 2 AK2 Leolut#GroundStation-147.5177,64.9735Ground StationActiveUnited StatesAnkara#GroundStation32.86,39.93Ground StationActiveTurkeyPlesetsk Cosmodrome - PU 11#LaunchPad40.4953,62.8978Launch PadActiveRussiaCamp Zama#GroundStation139.39804,35.50014Ground StationActiveJapanEagle Vision 4#GroundStation-80.8,33.92Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 164#LaunchPad63.064,45.9445Launch PadActiveKazakhstanSantiago Station AGO 5 (STDN AG23)#GroundStation-70.66731169,-33.15179414Ground StationActiveChileGrimstad Ground Station#GroundStation8.35,58.33Ground StationActiveNorwayEsrange Station ETX (STDN KU1S)#GroundStation21.06565472,67.88955833Ground StationActiveSwedenSt Thomas STDN ST1F#GroundStation-64.97321647,18.35725333Ground StationActiveUnited StatesGuiana Space Center ZLV#LaunchPad-52.775,5.236Launch PadActiveFrench GuianaLongovilo Station#GroundStation-71.401,-33.956Ground StationActiveChileEsrange Station ESC#GroundStation21.0599183,67.8843522Ground StationActiveSwedenLivermore Station#GroundStation-121.799,37.7605Ground StationActiveUnited StatesYevpatoria RT-70#GroundStation33.187068,45.189096Ground StationActiveUkraineSemnan Launch Center#LaunchSite53.92,35.25Launch SiteActiveIranOrbcomm Ocilla A#GroundStation-83.199591,31.50119Ground StationActiveUnited StatesGuiana Space Center ELS#LaunchPad-52.8345,5.3047Launch PadActiveFrench GuianaDombarovsky Cosmodrome xx4#LaunchPad59.77451,50.775737Launch PadActiveRussiaTranquillon Peak STDN CALF#GroundStation-120.56157233,34.58273856Ground StationActiveUnited StatesNaval Radio Transmitter Facility#GroundStation14.44,37.117Ground StationActiveItalyCape Canaveral Air Force Station - LC 25#LaunchPad-80.575,28.431Launch PadInactiveUnited StatesGuiana Space Center#LaunchSite-52.77,5.23Launch SiteActiveFrench GuianaGreenbelt STDN GGAL#LaserStation-76.82747947,39.02026828Laser StationActiveUnited StatesCombe Martin Leolut#GroundStation-4.051,51.17Ground StationActiveUnited KingdomDombarovsky Cosmodrome x12#LaunchPad59.808673,51.022624Launch PadActiveRussiaSydney Station#GroundStation151.2115,-33.7172Ground StationActiveAustraliaBaikonur Cosmodrome LC 243#LaunchPad63.737,45.8549Launch PadActiveKazakhstanJSC Test BRT STDN JSCJ#GroundStation-95.09,29.56168961Ground StationActiveUnited StatesVandenberg AFB SLC 2E#LaunchPad-120.619201,34.751617Launch PadDemolishedUnited StatesGissar#OpticalTrackingStation68.6818,38.491Optical Tracking StationActiveTajikistanBrewster Station#GroundStation-119.692,48.147Ground StationActiveUnited StatesNanning Station#GroundStation108.37,22.82Ground StationActiveChinaTattnall Space Fence#RadarStation-81.926,32.0437Radar StationActiveUnited StatesKaneohe Omega Station#GroundStation-157.8319,21.4047Ground StationInactiveUnited StatesPlesetsk Tracking Station#GroundStation40.558672,62.9001Ground StationActiveRussiaBTA-6#OpticalTrackingStation41.440447,43.646825Optical Tracking StationActiveRussiaPoker Flat Station PF2 STDN DXAS#GroundStation-147.43350389,65.11792972Ground StationActiveUnited StatesVandenberg AFB SLC 10E#LaunchPad-120.6213,34.7626Launch PadInactiveUnited StatesMakaha Ridge#GroundStation-159.723,22.13Ground StationActiveUnited StatesGTS STDN GTSS#GroundStation144.85605225,13.61518911Ground StationActiveUnited StatesRambouillet Teleport#GroundStation1.7826,48.5494Ground StationActiveFranceDombarovsky Cosmodrome - xx8#LaunchPad60.52694,50.959329Launch PadActiveRussiaDombarovsky Cosmodrome - x28#LaunchPad59.635083,51.193164Launch PadActiveRussiaSocorro GEODSS Cam3#OpticalTrackingStation-106.65968,33.81703Optical Tracking StationActiveUnited StatesYakima Training Center#GroundStation-120.356544,46.68209Ground StationActiveUnited StatesVLBA Los Alamos#GroundStation-106.245597,35.775125Ground StationActiveUnited StatesDombarovsky Cosmodrome xx2#LaunchPad59.791895,50.681452Launch PadActiveRussiaGreenbelt#GroundStation-76.8265,39.0219Ground StationActiveUnited StatesSUPARCO Satellite Ground Station#GroundStation73.17674,33.51787Ground StationActivePakistanDombarovsky Cosmodrome x20#LaunchPad60.087307,51.096909Launch PadActiveRussiaWoomera Test Range - LA 5A#LaunchPad136.474,-30.9716Launch PadActiveAustraliaWeinan Station#GroundStation109.51,34.5Ground StationActiveChinaBaikonur Cosmodrome xx3#LaunchPad63.481448,45.945516Launch PadActiveKazakhstanKapustin Yar S#LaunchPad46.3175,48.4763Launch PadActiveRussiaSan Diego Space Fence#RadarStation-116.973,32.5774Radar StationActiveUnited StatesKalyazin Radio Astronomy Observatory#GroundStation37.9,57.222Ground StationActiveRussiaGAVRT STDN DS12#GroundStation-116.80544339,35.29993942Ground StationActiveUnited StatesXichang Satellite Launch Center - LA 1#LaunchPad102.0292,28.2474Launch PadActiveChinaKennedy Space Center LC 39B STDN B39P#LaunchPad-80.62084967,28.62716064Launch PadActiveUnited StatesXian#GroundStation109.494,34.445Ground StationActiveChinaSvobodny Cosmodrome - x11#LaunchPad128.373907,51.879783Launch PadInactiveRussiaOrlando Station#GroundStation-81.121,28.4251Ground StationActiveUnited StatesDombarovsky Cosmodrome - x47#LaunchPad59.92692,51.600688Launch PadActiveRussiaDiego Garcia GEODSS Cam1#OpticalTrackingStation72.45203,-7.41162Optical Tracking StationActiveUnited KingdomWoomera Test Range#LaunchSite136.5,-30.95Launch SiteActiveAustraliaCape Canaveral Air Force Station - LC 15#LaunchPad-80.5494,28.4963Launch PadInactiveUnited StatesCape Canaveral STDN CN5F#GroundStation-80.56341989,28.51702439Ground StationActiveUnited StatesQuicksburg Station#GroundStation-78.658,38.73Ground StationActiveUnited StatesMid-Atlantic Regional Spaceport - Launch Pad 0-B#LaunchPad-75.4913,37.8312Launch PadActiveUnited StatesStarfire Optical Range#OpticalTrackingStation-106.4639,34.9642Optical Tracking StationActiveUnited StatesCape Canaveral Air Force Station - LC 18A#LaunchPad-80.5623,28.4506Launch PadInactiveUnited StatesPoker Flat LC 1#LaunchPad-147.4879,65.1295Launch PadActiveUnited StatesCape Canaveral AFS SLC 46 STDN A46P#LaunchPad-80.52840256,28.45849161Launch PadInactiveUnited StatesKapustin Yar - LC 107 1a#LaunchPad46.298,48.5992Launch PadActiveRussiaGrand Bahama Island STDN GB2Y#GroundStation-78.29852533,26.62547128Ground StationActiveBahamasSvobodny Cosmodrome x11#LaunchPad128.373907,51.879783Launch PadInactiveRussiaEsrange Rocket Range - L#LaunchPad21.1062,67.8943Launch PadActiveSwedenEsrange Station Idun#GroundStation21.038,67.879Ground StationActiveSwedenSES ASTRA#GroundStation6.330278,49.694167Ground StationActiveLuxembourgYacolt Station#GroundStation-122.396,45.863Ground StationActiveUnited StatesLondon Inmarsat HQ#GroundStation-0.0865,51.5254Ground StationActiveUnited KingdomKiruna EISCAT Radar#RadarStation20.43395,67.86068Radar StationActiveSwedenGoose Bay Leolut#GroundStation-60.466,53.312666Ground StationActiveCanadaEOC Antenna 2#GroundStation139.34814,36.00353Ground StationActiveJapanEchoStar Spokane Station#GroundStation-117.551,47.592Ground StationActiveUnited StatesBatam Island Station#GroundStation103.9506,1.1131Ground StationActiveIndonesiaBaikonur Cosmodrome LC 175-2#LaunchPad62.987,46.0512Launch PadActiveKazakhstanBaikonur Cosmodrome LC 81-23#LaunchPad62.9785,46.074Launch PadActiveKazakhstanVandenberg Air Force Base - SLC 576-E#LaunchPad-120.6191,34.7396Launch PadActiveUnited StatesWallops Island STDN WL2S#GroundStation-75.46205786,37.94642969Ground StationActiveUnited StatesNCTAMS LANT#GroundStation-76.3088,36.9505Ground StationActiveUnited StatesWhite Sands - LC 94#LaunchPad-106.4589,34.2049Launch PadActiveUnited StatesEsrange Station ESC#GroundStation21.0599183,67.8843522Ground StationActiveSwedenHelios Station#GroundStation-15.63071,27.76343Ground StationActiveSpainGreen River Station#GroundStation-109.352,41.537Ground StationActiveUnited StatesGalenki 25m#GroundStation131.7581,44.0204Ground StationActiveRussiaCape Canaveral AFS LC 15#LaunchPad-80.5494,28.4963Launch PadInactiveUnited StatesSpitsbergen Leolut#GroundStation15.396,78.229Ground StationActiveNorwayPatrick AFB STDN RAML#LaserStation-80.60574442,28.22799675Laser StationInactiveUnited StatesSatish Dhawan Pad 2#LaunchPad80.2304,13.7199Launch PadActiveIndiaDombarovsky Cosmodrome x41#LaunchPad60.178331,51.50362Launch PadActiveRussiaRoi-Namur Super RADOT#OpticalTrackingStation167.476576,9.393608Optical Tracking StationActiveMarshall IslandsSalto di Quirra#LaunchSite9.633,39.5273Launch SiteActiveItalyWhite Sands LC 36#LaunchPad-106.322,32.417Launch PadActiveUnited StatesZimmerwald Observatory#OpticalTrackingStation7.46522,46.877229Optical Tracking StationActiveSwitzerlandDSS 55 Robledo STDN DS55#GroundStation-4.25263331,40.42429592Ground StationActiveSpainSemnan Launch Center xx3#LaunchPad53.952,35.239Launch PadActiveIranDominion Observatory#GroundStation-119.6203,49.3209Ground StationActiveCanadaHawaii 1 HI1 Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesDongara Station AUWA01 STDN USPS#GroundStation115.34867764,-29.04577217Ground StationActiveAustraliaWoomera Test Range - LA 8#LaunchPad136.4629,-31.0328Launch PadActiveAustraliaYap Station#GroundStation138.0772,9.4909Ground StationActiveMicronesiaAscension Island STDN ASCQ#GroundStation-14.4025,-7.90663519Ground StationActiveUnited KingdomWhite Sands - LC 38#LaunchPad-106.2796,32.4179Launch PadActiveUnited StatesBangalore STDN BANF#GroundStation77.51,13.03Ground StationActiveIndiaAMISR Poker Flat#RadarStation-147.4707,65.1298Radar StationActiveUnited StatesWilkes-Barre Station#GroundStation-75.88268,41.24499Ground StationActiveUnited StatesBangkok 2 Leolut#GroundStation100.5432,13.717166Ground StationActiveThailandTangua Station#GroundStation-42.7837,-22.7468Ground StationActiveBrazilTanegashima Osaki Range#LaunchPad130.970274,30.39953Launch PadInactiveJapanAlcantara Launch Center - HAD Pad#LaunchPad-44.377,-2.3652Launch PadActiveBrazilHolloman A#LaunchPad-106.0714,32.8954Launch PadActiveUnited StatesKennedy Space Center LC 39A STDN A39P#LaunchPad-80.60411653,28.60827486Launch PadActiveUnited StatesKoganei xx2#GroundStation139.488122,35.710559Ground StationActiveJapanAlcantara Launch Center#LaunchSite-44.367,-2.317Launch SiteActiveBrazilOrbcomm Rio de Janiero B#GroundStation-42.87227,-22.69679Ground StationActiveBrazilUchinoura 20m#GroundStation131.080567,31.256784Ground StationActiveJapanChurchill Leolut#GroundStation-93.994,58.759Ground StationActiveCanadaPoker Flat - LC 3#LaunchPad-147.4851,65.1299Launch PadActiveUnited StatesTonghae Satellite Launching Ground#LaunchSite129.665994,40.855652Launch SiteActiveNorth KoreaCape Canaveral Air Force Station - LC 2#LaunchPad-80.5369,28.4657Launch PadInactiveUnited StatesDryden STDN DFRS#GroundStation-117.88739406,34.94979075Ground StationActiveUnited StatesMiyun Ground Station#GroundStation116.8589,40.4514Ground StationActiveChinaGRGT STDN GW3S#GroundStation144.84087494,13.58730961Ground StationActiveUnited StatesKapustin Yar - LC 107 2g#LaunchPad46.2958,48.5616Launch PadActiveRussiaMasuda USB F2#GroundStation131.01771,30.5555Ground StationActiveJapanBaikonur Cosmodrome LC 67-22#LaunchPad63.7073,45.9895Launch PadActiveKazakhstanUchinoura Space Center, 34m#GroundStation131.078495,31.254462Ground StationActiveJapanFort Buckner#GroundStation127.776,26.296Ground StationActiveJapanVandenberg Air Force Base - SLC 6 (STDN WT6P)#LaunchPad-120.62609183,34.58163228Launch PadActiveUnited StatesBear Lake RT-64#GroundStation37.951896,55.868035Ground StationActiveRussiaAberporth#LaunchSite-4.5566,52.1393Launch SiteActiveUnited KingdomMSSC 1.6m#OpticalTrackingStation-156.2574,20.70837Optical Tracking StationActiveUnited StatesWallops Island STDN WD3F#GroundStation-75.51143536,37.85636525Ground StationActiveUnited StatesKapustin Yar - LC 107 1b#LaunchPad46.298,48.5969Launch PadActiveRussiaDombarovsky Cosmodrome x11#LaunchPad59.567713,51.020939Launch PadActiveRussiaVandenberg STDN VDBF#GroundStation-120.53610972,34.77487769Ground StationActiveUnited StatesYatharagga Satellite Station#GroundStation115.3543,-29.0452Ground StationActiveAustraliaPlesetsk Cosmodrome - LC 133-3#LaunchPad40.8504,62.887Launch PadActiveRussiaPoker Flat - LC 1#LaunchPad-147.4879,65.1295Launch PadActiveUnited StatesRecife Geolut#GroundStation-34.925,-8.1383Ground StationActiveBrazilMSSC MOTIF#OpticalTrackingStation-156.2578,20.70852Optical Tracking StationActiveUnited StatesDombarovsky Cosmodrome - x43#LaunchPad59.801475,51.526422Launch PadActiveRussiaKeelung 2 Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanTCSB#GroundStation-0.90642519,51.1178738Ground StationActiveUnited KingdomLandstuhl SATCOM Terminal#GroundStation7.534,49.402Ground StationActiveGermanyHelsinki Teleport#GroundStation24.921667,60.206389Ground StationActiveFinlandWallops Flight Facility LA 2 RAG#LaunchPad-75.4823,37.8385Launch PadActiveUnited StatesSantiago Station AGO 6#GroundStation-70.6701611,-33.1506778Ground StationActiveChileDirecTV Los Angeles Broadcast Center (LABC)#GroundStation-118.425,33.9827Ground StationActiveUnited StatesSan Nicolas Island STDN SN3F#GroundStation-119.52139117,33.24839275Ground StationActiveUnited StatesDiego Garcia GEODSS Cam2#OpticalTrackingStation72.45244,-7.41163Optical Tracking StationActiveUnited KingdomSagamihara Campus#GroundStation139.395276,35.55831Ground StationActiveJapanEsrange Rocket Range - C#LaunchPad21.1029,67.8931Launch PadActiveSwedenHolloman AFB STDN HOLF#GroundStation-106.09916956,32.90146386Ground StationActiveUnited StatesBarreira do Inferno Launch Center#LaunchSite-35.1613,-5.9236Launch SiteActiveBrazilSocorro GEODSS Cam1#OpticalTrackingStation-106.66009,33.81727Optical Tracking StationActiveUnited StatesBiscarosse BP#LaunchPad-1.2396,44.3755Launch PadActiveFranceDongara Station AUWA02#GroundStation115.3494,-29.0454Ground StationActiveAustraliaMaiquetia (2) Leolut#GroundStation-66.9865,10.599Ground StationPlannedVenezuelaOrbcomm Kitaura#GroundStation140.31,36.05Ground StationActiveJapanHammaguira - Beatrice#LaunchPad-3.0851,30.8601Launch PadActiveAlgeriaArbuckle Station#GroundStation-122.1481,38.9383Ground StationActiveUnited StatesDombarovsky Cosmodrome - x17#LaunchPad59.748227,51.079713Launch PadActiveRussiaVandenberg STDN CALT#GroundStation-120.58144669,34.6658445Ground StationActiveUnited StatesAguimes#GroundStation-15.443,27.88Ground StationActiveSpainWallops Island LEO-T STDN LE2S#GroundStation-75.47613917,37.92352944Ground StationActiveUnited StatesCat House Phased-array Radar#RadarStation37.297,55.231Radar StationActiveRussiaEsrange Rocket Range N#LaunchPad21.1064,67.8933Launch PadActiveSwedenMasuda USB F1#GroundStation131.01494,30.55581Ground StationActiveJapanTarawa Station#GroundStation173.1558,1.3645Ground StationInactiveKiribatiDiyarbakir Radar#RadarStation39.9932,37.90476Radar StationInactiveTurkeyMalindi Station STDN KENS#GroundStation40.194505,-2.9955575Ground StationActiveKenyaBiscarosse#LaunchSite-1.25,44.32Launch SiteActiveFranceOrbcomm Kijal B#GroundStation103.47409,4.34818Ground StationActiveMalaysiaGreenbelt STDN BLTD#GroundStation-76.84276675,38.9984475Ground StationInactiveUnited StatesBaikonur Cosmodrome LC 106#LaunchPad63.4971,45.9511Launch PadActiveKazakhstanPune Earth Station#GroundStation73.8657,18.6041Ground StationActiveIndiaBaikonur Cosmodrome LC 175-59#LaunchPad62.9862,46.0525Launch PadActiveKazakhstanAflenz Teleport#GroundStation15.29175,47.55456Ground StationActiveAustriaVandenberg Air Force Base - SLC 4E#LaunchPad-120.61059,34.632067Launch PadInactiveUnited StatesWoomera Test Range LA 6B#LaunchPad136.445,-31.0792Launch PadActiveAustraliaYevpatoria South Station#GroundStation33.253128,45.170314Ground StationActiveUkraineESRIN Frascati#GroundStation12.67585,41.8275Ground StationActiveItalyKwajalein Drop Zone#LaunchSite167.7,7.65Launch SiteActiveMarshall IslandsKapustin Yar xx7#LaunchPad45.716196,48.812023Launch PadActiveRussiaCape Canaveral AFS LC 19#LaunchPad-80.5542,28.5068Launch PadInactiveUnited StatesVandenberg Air Force Base - SLC 10N#LaunchPad-120.6227,34.7663Launch PadInactiveUnited StatesAMISR Resolute Bay#RadarStation-94.90624,74.72941Radar StationActiveCanadaEuropean Direct Access Facility#GroundStation11.2789,48.0862Ground StationActiveGermanySwakopmund Station#GroundStation14.547462,-22.574087Ground StationActiveNamibiaGreen River#LaunchSite-110.076,38.942Launch SiteActiveUnited StatesEmeq Haela Station#GroundStation34.9942,31.6833Ground StationActiveIsraelBITF 11m#GroundStation166.1504,-78.1296Ground StationActiveAntarcticaSouth Point Station USHI01 STDN USHS#GroundStation-155.66330125,19.0139045Ground StationActiveUnited StatesHammaguira#LaunchSite-3.06,30.88Launch SiteActiveAlgeriaKapustin Yar - V2#LaunchPad45.9074,48.5709Launch PadActiveRussiaKingston#OpticalTrackingStation-76.4675,44.231Optical Tracking StationActiveCanadaDombarovsky Cosmodrome#LaunchSite60,51Launch SiteActiveRussiaKiruna Station 15m (STDN KI2S)#GroundStation20.96434169,67.85712517Ground StationActiveSwedenSanta Ynez Peak STDN SNYC#OpticalTrackingStation-119.98584869,34.53031086Optical Tracking StationActiveUnited StatesMumbai Station#GroundStation72.8306,18.9345Ground StationActiveIndiaDombarovsky Cosmodrome - x19#LaunchPad59.844333,51.093509Launch PadActiveRussiaBaikonur Cosmodrome - LC 200-40#LaunchPad63.0379,46.0364Launch PadActiveKazakhstanSatish Dhawan#LaunchSite80.23,13.73Launch SiteActiveIndiaDombarovsky Cosmodrome x17#LaunchPad59.748227,51.079713Launch PadActiveRussiaBarking Sands LC 10#LaunchPad-159.7816,22.0569Launch PadActiveUnited StatesDombarovsky Cosmodrome x22#LaunchPad59.634472,51.11475Launch PadActiveRussiaLakhadaria Station#GroundStation3.6057,36.564Ground StationActiveAlgeriaMaidanak#OpticalTrackingStation66.89641,38.67332Optical Tracking StationActiveUzbekistanNairobi#GroundStation36.82,-1.28Ground StationActiveKenyaCape Canaveral Air Force Station - SLC 17B (STDN B17P)#LaunchPad-80.56564944,28.44579042Launch PadActiveUnited StatesEagle River Earth Station#GroundStation-149.4475,61.2995Ground StationActiveUnited StatesOklahoma Spaceport#LaunchSite-99.2,35.34Launch SitePlannedUnited StatesOsan AB#GroundStation127.03166,37.09433Ground StationActiveSouth KoreaKauai STDN HAW3#GroundStation-159.66515503,22.12627256Ground StationActiveUnited StatesMagdalena Ridge Observatory#OpticalTrackingStation-107.1894,33.98486Optical Tracking StationActiveUnited StatesCape Canaveral AFS LC 5#LaunchPad-80.5733,28.4394Launch PadInactiveUnited StatesNaval Reserve Center Station#GroundStation-121.347,37.945Ground StationInactiveUnited StatesEsrange Rocket Range - A#LaunchPad21.1018,67.8931Launch PadActiveSwedenSemnan Launch Center#LaunchSite53.92,35.25Launch SiteActiveIranAdelaide Satellite Facility#GroundStation138.572,-34.8627Ground StationActiveAustraliaDombarovsky Cosmodrome - x45#LaunchPad59.956989,51.558712Launch PadActiveRussiaAlcantara Launch Center HAD Pad#LaunchPad-44.377,-2.3652Launch PadActiveBrazilKolonicke#OpticalTrackingStation22.273858,48.935001Optical Tracking StationActiveSlovakiaAndoya Rocket Range#LaunchSite16.021,69.294Launch SiteActiveNorwayEsrange Rocket Range - MRL#LaunchPad21.103,67.8934Launch PadActiveSwedenGiant Metrewave Radio Telescope#GroundStation74.05,19.09Ground StationActiveIndiaEsrange Station ESTC (STDN KU2S)#GroundStation21.06044833,67.8831825Ground StationActiveSwedenPenteli Leolut#GroundStation23.883,38.080833Ground StationActiveGreeceHeimenschwand#GroundStation7.716108,46.831336Ground StationActiveSwitzerlandWhite Sands LC 35#LaunchPad-106.3422,32.4041Launch PadActiveUnited StatesPlesetsk Cosmodrome LC 132-2#LaunchPad40.8722,62.8834Launch PadActiveRussiaIOS STDN SEYS#GroundStation55.47782053,-4.67174811Ground StationInactiveSeychellesDombarovsky Cosmodrome x38#LaunchPad60.163782,51.338805Launch PadActiveRussiaHartebeesthoek STDN HARL#LaserStation27.68617419,-25.88970925Laser StationActiveSouth AfricaGilmore Creek STDN GLCS#GroundStation-147.50477836,64.97314211Ground StationActiveUnited StatesWeilheim#GroundStation11.078325,47.881242Ground StationInactiveGermanyWhite Sands SULF#LaunchPad-106.7364,33.7212Launch PadActiveUnited StatesCape Canaveral AFS LC 2#LaunchPad-80.5369,28.4657Launch PadInactiveUnited StatesTaiyuan North Pad#LaunchPad111.608381,38.848552Launch PadActiveChinaKapustin Yar - START R12#LaunchPad46.298,48.5807Launch PadActiveRussiaHolloman - NATIV#LaunchPad-106.0753,32.8866Launch PadActiveUnited StatesMakassar Leolut#GroundStation119.55,-5.066666Ground StationPlannedIndonesiaWellington Leolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandHartRAO#GroundStation27.685393,-25.889752Ground StationActiveSouth AfricaAbu Dhabi Geolut#GroundStation54.448,24.4312Ground StationActiveUnited Arab EmiratesKapustin Yar - xx2#LaunchPad46.297833,48.539973Launch PadActiveRussiaKapustin Yar xx4#LaunchPad46.299018,48.615791Launch PadActiveRussiaAlice Springs Transmitter#RadarStation134.4479,-22.9676Radar StationActiveAustraliaBaikonur Cosmodrome - LC 45-1#LaunchPad63.655387,45.940067Launch PadActiveKazakhstanBaikonur Cosmodrome LC 241#LaunchPad63.4558,45.8583Launch PadActiveKazakhstanPillar Point STDN PPTQ#GroundStation-122.49971472,37.49781692Ground StationActiveUnited StatesGalliot Station STDN KRUS#GroundStation-52.639872,5.098848Ground StationActiveFrench GuianaCape Canaveral AFS SLC 37B STDN B37P#LaunchPad-80.56445806,28.53121944Launch PadActiveUnited StatesNERC Satellite Receiving Station#GroundStation-2.980112,56.458105Ground StationActiveUnited KingdomWoomera Test Range - MRL#LaunchPad136.5332,-30.9573Launch PadActiveAustraliaBeijing (1) Leolut#GroundStation116.42,39.908Ground StationActiveChinaSemnan Launch Center - xx1#LaunchPad53.896,35.222Launch PadActiveIranGuiana Space Center Diamant Pad#LaunchPad-52.7524,5.2325Launch PadInactiveFrench GuianaMcMurdo STDN MC1S#GroundStation166.66708233,-77.8391295Ground StationActiveAntarcticaBangkok (1) Leolut#GroundStation100.5433,13.717166Ground StationActiveThailandMid-Atlantic Regional Spaceport 0-A#LaunchPad-75.4882,37.8338Launch PadActiveUnited StatesWhite Sands - LC 35#LaunchPad-106.3422,32.4041Launch PadActiveUnited StatesByalalu 18m#GroundStation77.36882,12.90042Ground StationActiveIndiaNew Norcia DSA 1#GroundStation116.1915,-31.048225Ground StationActiveAustraliaSvobodny Cosmodrome xx2#LaunchPad128.4085,51.7734Launch PadInactiveRussiaDombarovsky Cosmodrome x46#LaunchPad60.082438,51.588487Launch PadActiveRussiaWoomera Test Range - LA 3#LaunchPad136.5191,-30.93Launch PadActiveAustraliaJiuquan Satellite Launch Center - SLS#LaunchPad100.2915,40.958Launch PadActiveChinaUchinoura Space Center - Mu Pad#LaunchPad131.08223,31.251028Launch PadActiveJapanUS Naval Observatory#GroundStation-77.067,38.921Ground StationActiveUnited StatesNenoksa Test Range#LaunchSite39.22,64.646Launch SiteActiveRussiaVandenberg AFB SLC 10N#LaunchPad-120.6227,34.7663Launch PadInactiveUnited StatesHawkinsville Space Fence#RadarStation-83.5361,32.2889Radar StationActiveUnited StatesPoint Mugu - LC 2#LaunchPad-119.121,34.0992Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 131#LaunchPad62.9562,46.0716Launch PadActiveKazakhstanDubai#GroundStation55.267,25.267Ground StationActiveUnited Arab EmiratesKarachi Station#GroundStation67.03,24.89Ground StationActivePakistanHartebeesthoek Europ Star#GroundStation27.70793,-25.88549Ground StationActiveSouth AfricaRiyadh SLR#LaserStation46.40037,24.91067Laser StationActiveSaudi ArabiaSvobodny Cosmodrome#LaunchSite128.4,51.8Launch SiteInactiveRussiaJeddah 2 Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaTanum Teleport#GroundStation11.377,58.704Ground StationInactiveSwedenWoomera Test Range LA 5A#LaunchPad136.474,-30.9716Launch PadActiveAustraliaMonument Peak STDN MNPL#LaserStation-116.42267108,32.89173831Laser StationActiveUnited StatesFort Greely#LaunchSite-145.73,63.95Launch SiteActiveUnited StatesMelbourne Station#GroundStation-80.63638,28.08647Ground StationActiveUnited StatesChantilly STDN CHAS#GroundStation-77.44216667,38.89033333Ground StationInactiveUnited StatesJAXA Maspalomas#GroundStation-15.6348,27.76505Ground StationActiveSpainRoi-Namur Island#LaunchSite167.4652,9.4012Launch SiteActiveMarshall IslandsMoscow Teleport#GroundStation37.626,55.843Ground StationActiveRussiaCape Canaveral Air Force Station - LC 19#LaunchPad-80.5542,28.5068Launch PadInactiveUnited StatesALMA#GroundStation-67.755,-23.028Ground StationPlannedChileCape Canaveral STDN CN4F#GroundStation-80.58311147,28.46316792Ground StationActiveUnited StatesVLBA Owens Valley#GroundStation-118.277047,37.231653Ground StationActiveUnited StatesTTS STDN TTSS#GroundStation-68.59997228,76.51593456Ground StationActiveGreenlandPerth International Telecom Centre#GroundStation115.88785,-31.80485Ground StationActiveAustraliaAlamo Peak STDN ALAY#GroundStation-105.81237947,32.87253311Ground StationActiveUnited StatesPunta Arenas Leolut#GroundStation-70.847,-53.006Ground StationActiveChileCape Canaveral Air Force Station - LC 16#LaunchPad-80.5518,28.5016Launch PadInactiveUnited StatesSvobodny Cosmodrome - xx8#LaunchPad128.2764,51.8357Launch PadInactiveRussiaNorman Station#GroundStation-97.5658,35.1798Ground StationActiveUnited StatesGreen River - Pad 2#LaunchPad-110.0753,38.9413Launch PadActiveUnited StatesEsrange Station SfinX#GroundStation21.0525247,67.8882281Ground StationActiveSwedenHTSB#GroundStation-158.262297,21.5689783Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 106#LaunchPad63.4971,45.9511Launch PadActiveKazakhstanGuam 2 GU2 Leolut#GroundStation144.9392,13.578333Ground StationActiveUnited StatesWallops Island STDN WPDA#GroundStation-75.47495275,37.92737297Ground StationActiveUnited StatesGuiana Space Center ZL3 STDN KR3P#LaunchPad-52.75178147,5.24036314Launch PadActiveFrench GuianaWallops Flight Facility LA 3#LaunchPad-75.4725,37.8506Launch PadActiveUnited StatesMt Hopkins STDN HOPL#LaserStation-110.87806108,31.68424706Laser StationInactiveUnited StatesDSS 24 Goldstone STDN DS24#GroundStation-116.87479442,35.33989283Ground StationActiveUnited StatesWellington 2 Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandStaten Island Teleport#GroundStation-74.1784,40.6028Ground StationActiveUnited StatesThornton Station#GroundStation-104.9839,39.9158Ground StationActiveUnited StatesToulouse MultiMission Station#GroundStation1.487456,43.554442Ground StationActiveFranceAustralia Telescope Compact Array#GroundStation149.565,-30.313Ground StationActiveAustraliaSatish Dhawan Space Centre - Pad 1#LaunchPad80.2346,13.7334Launch PadActiveIndiaUchinoura Space Center, 10m#GroundStation131.084654,31.255675Ground StationActiveJapanWallops Island STDN WL4S#GroundStation-75.46059947,37.94631331Ground StationActiveUnited StatesNanshan Station#GroundStation87.17837,43.47153Ground StationActiveChinaWhite Sands STDN WH6K#GroundStation-106.60923475,32.50146197Ground StationActiveUnited StatesTaiyuan Satellite Launch Center - North Pad#LaunchPad111.608381,38.848552Launch PadActiveChinaEareckson#LaunchSite174.07,52.72Launch SiteActiveUnited StatesHartebeesthoek 11m#GroundStation27.70692,-25.8855Ground StationActiveSouth AfricaSvobodny Cosmodrome xx5#LaunchPad128.23218,51.80532Launch PadInactiveRussiaPoker Flat STDN PFTQ#GroundStation-147.46329083,65.11679308Ground StationActiveUnited StatesStockholm Teleport#GroundStation18.083866,59.211779Ground StationActiveSwedenDombarovsky Cosmodrome - x39#LaunchPad60.472701,51.346817Launch PadActiveRussiaKashima Space Research Center 11m#GroundStation140.65745,35.95558Ground StationActiveJapanCape Canaveral Air Force Station - LC 26A#LaunchPad-80.5705,28.4446Launch PadInactiveUnited StatesSatish Dhawan Space Centre - Pad 2#LaunchPad80.2304,13.7199Launch PadActiveIndiaDombarovsky Cosmodrome - xx4#LaunchPad59.77451,50.775737Launch PadActiveRussiaWhite Sands - LC 37#LaunchPad-106.2911,32.4155Launch PadActiveUnited StatesSatish Dhawan Space Centre - Sounding Rocket Pad#LaunchPad80.2404,13.759Launch PadActiveIndiaBaikonur Cosmodrome LC 107#LaunchPad63.81,46.046Launch PadActiveKazakhstanBaikonur Cosmodrome LC 131#LaunchPad62.9562,46.0716Launch PadActiveKazakhstanDSS 46 Tidbinbilla STDN DS46#GroundStation148.98308169,-35.40501064Ground StationInactiveAustraliaHammaguira - Bacchus#LaunchPad-3.1241,30.8565Launch PadActiveAlgeriaVandenberg AFB SLC 8#LaunchPad-120.6324,34.5762Launch PadActiveUnited StatesHong Kong 1 Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaSheshan Station#GroundStation121.1995,31.0992Ground StationActiveChinaMaiquetia 1 Leolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaLatefia#GroundStation44.2079,33.7224Ground StationActiveIraqComcast Los Angeles Station#GroundStation-118.4566,34.0312Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 163#LaunchPad63.172,46.002Launch PadActiveKazakhstanEdwards AFB STDN EAFF#GroundStation-117.91155094,34.96065983Ground StationActiveUnited StatesDSS 26 Goldstone STDN D26D#GroundStation-116.8730165,35.33568922Ground StationActiveUnited StatesAscension Island STDN AS2Q#GroundStation-14.40095086,-7.97280625Ground StationActiveUnited KingdomWallops Flight Facility LA 2 MLS#LaunchPad-75.4835,37.8375Launch PadActiveUnited StatesFlorida 2 (FL2) Leolut#GroundStation-80.3838,25.616333Ground StationActiveUnited StatesDombarovsky Cosmodrome x26#LaunchPad59.74824,51.154839Launch PadActiveRussiaPrince Albert xx2#GroundStation-105.92615,53.21206Ground StationActiveCanadaWhite Sands BRT STDN WH2J#GroundStation-106.61196575,32.50628147Ground StationActiveUnited StatesMaryland (LSE) Leolut#GroundStation-76.93,38.850333Ground StationActiveUnited StatesMaiquetia Geolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaSpace Surveillance Telescope#OpticalTrackingStation-106.3644,33.7395Optical Tracking StationActiveUnited StatesSintra Station#GroundStation-9.2817,38.8692Ground StationActivePortugalKapustin Yar xx2#LaunchPad46.297833,48.539973Launch PadActiveRussiaERMEXS#GroundStation-99.12879,19.32263Ground StationActiveMexicoAerospace Data Facility East#GroundStation-77.1583,38.7361Ground StationActiveUnited StatesHammaguira Brigitte#LaunchPad-3.0357,30.8935Launch PadActiveAlgeriaSvobodny Cosmodrome - x12#LaunchPad128.252,51.8818Launch PadInactiveRussiaCalifornia 1 (CA1) Leolut#GroundStation-120.5515,34.6625Ground StationActiveUnited StatesOrbcomm Almaty B#GroundStation76.78226,44.49652Ground StationActiveKazakhstanAuckland Station#GroundStation174.6959,-36.7482Ground StationActiveNew ZealandTVB City#GroundStation114.2723,22.2778Ground StationActiveChinaTaejon#GroundStation127.4333,36.3269Ground StationActiveSouth KoreaWhite Sands LC 37#LaunchPad-106.2911,32.4155Launch PadActiveUnited StatesWhite Sands STDN WHSS#GroundStation-106.60855189,32.50026978Ground StationActiveUnited StatesSondrestrom Rocket Range#LaunchSite-50.6,67.024Launch SiteActiveGreenlandKapustin Yar SAM#LaunchPad45.7346,48.8055Launch PadActiveRussiaKapustin Yar - xx5#LaunchPad45.781879,48.781927Launch PadActiveRussiaWhite Sands STDN STSS#GroundStation-106.61209906,32.54171675Ground StationActiveUnited StatesBaikonur Cosmodrome LC 110R#LaunchPad63.3102,45.9622Launch PadActiveKazakhstanKitab#OpticalTrackingStation66.8863,39.1337Optical Tracking StationActiveUzbekistanAPL STDN MDLS#GroundStation-76.89877778,39.16736111Ground StationActiveUnited StatesBerkeley STDN BRKS#GroundStation-122.24278381,37.87937658Ground StationActiveUnited StatesTorrejon Air Base xx1#GroundStation-3.4469,40.4805Ground StationActiveSpainVandenberg Air Force Base - SLC 10W#LaunchPad-120.6244,34.7636Launch PadInactiveUnited StatesMaryland Point#GroundStation-77.23068,38.3742Ground StationActiveUnited StatesEsrange Station TTC STDN KICS#GroundStation21.060769,67.884232Ground StationActiveSwedenGreen River Pad 3#LaunchPad-110.0741,38.9426Launch PadActiveUnited StatesTanegashima Space Center - Yoshinobu LC, Pad 1#LaunchPad130.977582,30.400975Launch PadActiveJapanJPL#GroundStation-118.1726,34.2031Ground StationActiveUnited StatesBITF 7m#GroundStation166.1504,-78.1296Ground StationActiveAntarcticaGRGT STDN GWMK#GroundStation144.8409295,13.58801786Ground StationActiveUnited StatesCape Cod AFS PAVE PAWS#RadarStation-70.5386,41.7524Radar StationActiveUnited StatesKronogard#LaunchSite19.32,66.3922Launch SiteInactiveSwedenTTSA#GroundStation-68.5956028,76.5151944Ground StationActiveGreenlandEagle Vision 5#GroundStation-157.92,21.32Ground StationActiveUnited StatesAlcantara Ground Station#GroundStation-44.404167,-2.338333Ground StationActiveBrazilRAL Station 12m#GroundStation-1.31268,51.57215Ground StationActiveUnited KingdomOttawa Leolut#GroundStation-75.6745,45.328666Ground StationActiveCanadaCape Canaveral Air Force Station - SLC 36B (STDN B36P)#LaunchPad-80.54095408,28.46836775Launch PadDemolishedUnited StatesCocos Island STDN COCS#GroundStation96.85,-12.2Ground StationActiveAustraliaQingdao Station#GroundStation120.383,36.066Ground StationActiveChinaWoomera Test Range LA 8#LaunchPad136.4629,-31.0328Launch PadActiveAustraliaTutuila BRT STDN AM2J#GroundStation-170.71941667,-14.33144444Ground StationActiveUnited StatesAlcantara Launch Center - UL Pad#LaunchPad-44.367,-2.316Launch PadActiveBrazilOverberg Earth Station 10m#GroundStation20.226,-34.616Ground StationActiveSouth AfricaKapustin Yar - LC 107 2a#LaunchPad46.2949,48.5609Launch PadActiveRussiaBatelco Teleport#GroundStation50.6114,26.0714Ground StationActiveBahrainMcMurdo STDN MCMS#GroundStation166.4,-77.8Ground StationActiveAntarcticaBarking Sands - LC 14#LaunchPad-159.7788,22.058Launch PadActiveUnited StatesGreenbelt Geolut#GroundStation-76.840666,38.999Ground StationActiveUnited StatesEsrange Rocket Range A#LaunchPad21.1018,67.8931Launch PadActiveSwedenOrbcomm San Luis A#GroundStation-65.18131,-33.8374Ground StationActiveArgentinaStanley Earth Station#GroundStation114.221,22.197Ground StationActiveChinaPARTNeR STDN DS61#GroundStation-4.24892803,40.42874444Ground StationActiveSpainKennedy Space Center - LC 39B (STDN B39P)#LaunchPad-80.62084967,28.62716064Launch PadActiveUnited StatesDoral Station#GroundStation-80.3525,25.81Ground StationActiveUnited StatesCape D-Aguilar Station#GroundStation114.2492,22.2167Ground StationActiveChinaDombarovsky Cosmodrome x29#LaunchPad60.765442,51.201923Launch PadActiveRussiaChilbolton Observatory#GroundStation-1.43842,51.14502Ground StationActiveUnited KingdomMojave Air and Space Port#LaunchSite-118.15,35.06Launch SitePlannedUnited StatesEsrange Rocket Range MRL#LaunchPad21.103,67.8934Launch PadActiveSwedenCape Canaveral Air Force Station - LC 31#LaunchPad-80.5563,28.4519Launch PadInactiveUnited StatesWoomera Test Range LA 2#LaunchPad136.521,-30.9433Launch PadActiveAustraliaWoomera Test Range - HAD#LaunchPad136.5322,-30.9553Launch PadActiveAustraliaDSS 53 Robledo#GroundStation-4.24964,40.42744Ground StationInactiveSpainRio Grande Leolut#GroundStation-67.7053,-53.779166Ground StationActiveArgentinaKapustin Yar START R12#LaunchPad46.298,48.5807Launch PadActiveRussiaBaikonur Cosmodrome - LC 67-21#LaunchPad63.705,45.9893Launch PadActiveKazakhstanBundaberg Leolut#GroundStation152.4128,-24.758333Ground StationActiveAustraliaMiyun Deep Space Station#GroundStation116.976,40.558Ground StationActiveChinaSan Marco Launch Platform#LaunchSite40.2125,-2.9383Launch SiteInactiveKenyaBlack Island STDN BLKQ#GroundStation166.15043278,-78.12957889Ground StationActiveAntarcticaTDC Herstedvester Teleport#GroundStation12.355,55.68Ground StationActiveDenmarkSvalsat SG 3 STDN SG3S#GroundStation15.40809583,78.229735Ground StationActiveNorwayAlice Springs BRT STDN ALSJ#GroundStation133.8825985,-23.75881044Ground StationActiveAustraliaTai Po Earth Station#GroundStation114.19,22.453Ground StationActiveChinaKashima Space Research Center 34m#GroundStation140.66006,35.95589Ground StationActiveJapanWallops Flight Facility LA 2 AML-2#LaunchPad-75.483,37.8379Launch PadActiveUnited StatesXichang Satellite Launch Center - LA 2#LaunchPad102.0271,28.2455Launch PadActiveChinaKwajalein STDN KMPF#RadarStation167.72649006,8.72167692Radar StationActiveMarshall IslandsGuam 2 (GU2) Leolut#GroundStation144.9392,13.578333Ground StationActiveUnited StatesBaikonur Cosmodrome xx2#LaunchPad63.462445,45.94176Launch PadActiveKazakhstanDefford Site#GroundStation-2.147,52.097Ground StationActiveUnited KingdomWhite Sands - LC 33#LaunchPad-106.443,32.3396Launch PadActiveUnited StatesWallops Flight Facility LA 2 AML-1#LaunchPad-75.4828,37.8381Launch PadActiveUnited StatesSantiago Station AGO 3 STDN AGO3#GroundStation-70.666403,-33.15110747Ground StationActiveChileKapustin Yar - LC 107 2h#LaunchPad46.2949,48.5617Launch PadActiveRussiaWhite Sands STDN STGK#GroundStation-106.61208894,32.54327958Ground StationActiveUnited StatesBangalore#GroundStation77.5116,13.0344Ground StationActiveIndiaLasham Satellite Ground Station#GroundStation-1.044,51.184Ground StationDemolishedUnited KingdomOtay Mt STDN SNDL#LaserStation-116.84080406,32.60073169Laser StationInactiveUnited StatesBaikonur Cosmodrome - LC 172#LaunchPad63.092,46.065Launch PadActiveKazakhstanHolloman - SLED#LaunchPad-106.1484,32.9263Launch PadActiveUnited StatesVandenberg STDN VDB3#GroundStation-120.50161731,34.56562592Ground StationActiveUnited StatesPerth STDN PRTS#GroundStation115.885,-31.802Ground StationActiveAustraliaMadley Communications Centre#GroundStation-2.84049,52.03198Ground StationActiveUnited KingdomIllegini Island#LaunchSite167.4754,9.0856Launch SiteActiveMarshall IslandsEsrange Station SSC-CNES#GroundStation21.0309,67.88225Ground StationActiveSwedenAlcantara Launch Center - MRL Pad#LaunchPad-44.3671,-2.3167Launch PadActiveBrazilRed River Space Fence#RadarStation-93.5503,33.33Radar StationActiveUnited StatesRedu Station#GroundStation5.145344,50.000456Ground StationActiveBelgiumWallops Flight Facility - LA 2 ARC#LaunchPad-75.4841,37.838Launch PadActiveUnited StatesVandenberg STDN VD2F#GroundStation-120.62712139,34.75823183Ground StationActiveUnited StatesFort Wingate#LaunchSite-108.5994,35.44868Launch SiteActiveUnited StatesAnkara 2 Leolut#GroundStation32.9897,40.140666Ground StationActiveTurkeyBaikonur Cosmodrome LC 195#LaunchPad63.2127,45.783Launch PadActiveKazakhstanChilworth Station#GroundStation-1.4274,50.9608Ground StationActiveUnited KingdomSvobodny Cosmodrome - xx6#LaunchPad128.467,51.8054Launch PadInactiveRussiaBiscarosse CE#LaunchPad-1.2325,44.3917Launch PadActiveFranceRATAN-600 Radio Telescope#GroundStation41.587,43.826Ground StationActiveRussiaDSTO Adelaide#GroundStation138.65,-34.731Ground StationActiveAustraliaZhuklino A-35 Anti-missile Site#RadarStation38.579,56.244Radar StationActiveRussiaTanegashima Yoshinobu LC Pad 1#LaunchPad130.977582,30.400975Launch PadActiveJapanWhite Sands STDN WH7F#GroundStation-106.65901447,33.81307828Ground StationActiveUnited StatesJiuquan Satellite Launch Center - LA 2A#LaunchPad100.3165,41.3088Launch PadActiveChinaDombarovsky Cosmodrome xx1#LaunchPad59.655376,50.658373Launch PadActiveRussiaVandenberg Air Force Base - SLC 1W#LaunchPad-120.6303,34.7571Launch PadInactiveUnited StatesJiuquan Satellite Launch Center - xx1#LaunchPad100.304946,41.280432Launch PadActiveChinaDGSA#GroundStation72.37002305,-7.2700227Ground StationActiveUnited KingdomHetian Station#GroundStation79.922,37.114Ground StationActiveChinaElfordstown Station#GroundStation-8.1758,51.9533Ground StationActiveIrelandBaikonur Cosmodrome LC 164#LaunchPad63.064,45.9445Launch PadActiveKazakhstanEsrange Rocket Range L#LaunchPad21.1062,67.8943Launch PadActiveSwedenWoodbine Teleport#GroundStation-77.081,39.3763Ground StationActiveUnited StatesCape Canaveral AFS LC 11#LaunchPad-80.5395,28.4753Launch PadInactiveUnited StatesDSS 42 Tidbinbilla STDN DS42#GroundStation148.98124419,-35.40068386Ground StationDemolishedAustraliaEdmonton Leolut#GroundStation-113.3162,53.678166Ground StationActiveCanadaCarpentersville Station#GroundStation-75.1911,40.6444Ground StationActiveUnited StatesBaikonur Cosmodrome LC 160#LaunchPad62.9423,46.0783Launch PadActiveKazakhstanPoker Flat STDN WT1S#GroundStation-147.45906028,65.11723692Ground StationInactiveUnited StatesUchinoura 34m#GroundStation131.078495,31.254462Ground StationActiveJapanWoomera Test Range - LA 6B#LaunchPad136.445,-31.0792Launch PadActiveAustraliaEagle Vision 3#GroundStation-117.16202,32.83818Ground StationActiveUnited StatesEchoStar Cheyenne Station#GroundStation-104.736,41.132Ground StationActiveUnited StatesYarragadee STDN YARL#LaserStation115.34674628,-29.04649844Laser StationActiveAustraliaO-Higgins#GroundStation-57.901241,-63.321128Ground StationActiveAntarcticaOrbcomm Matera A#GroundStation16.70751,40.64922Ground StationActiveItalyEOC Antenna 3#GroundStation139.3478,36.00235Ground StationActiveJapanOverberg Test Range - xx1#LaunchPad20.30271,-34.60276Launch PadActiveSouth AfricaMatera STDN MROL#LaserStation16.70460944,40.64867006Laser StationActiveItalyDombarovsky Cosmodrome - x36#LaunchPad60.301504,51.270092Launch PadActiveRussiaGrasse STDN GRAL#LaserStation6.92157186,43.75463183Laser StationActiveFranceVillafranca VIL-2#GroundStation-3.95257,40.44558Ground StationActiveSpainDombarovsky Cosmodrome - xx9#LaunchPad59.551097,50.972694Launch PadActiveRussiaSGS Colerne Site#GroundStation-2.2771,51.444Ground StationActiveUnited KingdomXichang Satellite Launch Center - LA 3#LaunchPad102.0271,28.2455Launch PadActiveChinaAlbright Station#GroundStation-79.579,39.5686Ground StationActiveUnited StatesCape Canaveral AFS LC 22#LaunchPad-80.5398,28.461Launch PadInactiveUnited StatesGilmore Creek STDN GILE#GroundStation-147.49800047,64.97850711Ground StationActiveUnited StatesDSS 34 Tidbinbilla STDN DS34#GroundStation148.98196442,-35.39847883Ground StationActiveAustraliaFairbanks STDN ULA3#GroundStation-147.51338883,64.97214025Ground StationInactiveUnited StatesSvobodny Cosmodrome xx4#LaunchPad128.3386,51.7938Launch PadInactiveRussiaFGAN Radar#RadarStation7.129822,50.616569Radar StationActiveGermanyWallops Flight Facility - LA 4 HAD#LaunchPad-75.4696,37.8511Launch PadActiveUnited StatesAlaska 1 (AK1) Leolut#GroundStation-147.5173,64.973666Ground StationActiveUnited StatesKwajalein Phased Array Radar#RadarStation167.715317,8.725601Radar StationActiveMarshall IslandsBaikonur Cosmodrome LC 194#LaunchPad63.301,45.854Launch PadActiveKazakhstanLucknow#GroundStation80.957329,26.913182Ground StationActiveIndiaEOC Antenna 1#GroundStation139.3488,36.00386Ground StationActiveJapanCallao Leolut#GroundStation-77.1298,-12.030666Ground StationActivePeruTranquillon Peak STDN CALC#OpticalTrackingStation-120.56243417,34.58238614Optical Tracking StationActiveUnited StatesDSS 17 Goldstone STDN DS17#GroundStation-116.87345528,35.34222994Ground StationDemolishedUnited StatesKaena Point Radar STDN KPTQ#RadarStation-158.26658533,21.57211972Radar StationActiveUnited StatesSvalsat EUM 2#GroundStation15.3884,78.229Ground StationActiveNorwayMid-Atlantic Regional Spaceport 0-B#LaunchPad-75.4913,37.8312Launch PadActiveUnited StatesEsrange Rocket Range#LaunchSite21.105,67.893Launch SiteActiveSwedenTaiyuan#LaunchSite111.61,38.84Launch SiteActiveChinaBaikonur Cosmodrome LC 60-7#LaunchPad64.0173,46.0181Launch PadActiveKazakhstanTRADEX Radar STDN KMRF#RadarStation167.48214819,9.39874711Radar StationActiveMarshall IslandsOrbcomm Maghreb B#GroundStation-7.64393,33.04755Ground StationActiveMoroccoGuiana Space Center ZL2 STDN KR2P#LaunchPad-52.77567156,5.23240672Launch PadInactiveFrench GuianaTaeduk Radio Astronomy Observatory#GroundStation127.3752,36.3976Ground StationActiveSouth KoreaCar Nicobar Station#GroundStation92.82765,9.1548Ground StationActiveIndiaSatish Dhawan Sounding Rocket Pad#LaunchPad80.2404,13.759Launch PadActiveIndiaWallops Island STDN WAPS#GroundStation-75.4765225,37.92492556Ground StationActiveUnited StatesHai Phong Station#GroundStation106.7104,20.8005Ground StationActiveVietnamPlesetsk Cosmodrome - LC 41-1#LaunchPad40.529,62.9405Launch PadDemolishedRussiaPatrick AFB STDN PATQ#GroundStation-80.59927636,28.22640242Ground StationActiveUnited StatesAtom Peak STDN ATMY#GroundStation-106.36455683,33.73962981Ground StationActiveUnited StatesWoomera Test Range - LA 1#LaunchPad136.5037,-30.9587Launch PadActiveAustraliaWellington (2) Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandDSS 63 Robledo STDN DS63#GroundStation-4.24800856,40.43120975Ground StationActiveSpainEdwards AFB STDN FRCF#GroundStation-117.91149608,34.96080469Ground StationActiveUnited StatesBukit Timah Satellite Earth Station#GroundStation103.7911,1.3514Ground StationActiveSingaporeFort Meade SATCOM Terminal#GroundStation-76.757,39.104Ground StationActiveUnited StatesWhite Sands STDN ST2K#GroundStation-106.61208894,32.54297736Ground StationActiveUnited StatesAuScope VLBI Katherine#GroundStation132.1524,-14.3755Ground StationActiveAustraliaPlesetsk Cosmodrome LC 32-2#LaunchPad40.7895,62.9057Launch PadActiveRussiaCape Canaveral Air Force Station - LC 4#LaunchPad-80.5356,28.4669Launch PadInactiveUnited StatesWallops Island STDN WPSA#GroundStation-75.47498581,37.92727767Ground StationInactiveUnited StatesThule BMEWS#RadarStation-68.2992,76.5703Radar StationActiveGreenlandFauske Geolut#GroundStation15.302,67.237Ground StationActiveNorwayCape Canaveral Air Force Station - LC 6#LaunchPad-80.5726,28.4407Launch PadInactiveUnited StatesMILA Test BRT STDN MILJ#GroundStation-80.69301289,28.50598947Ground StationInactiveUnited StatesEsrange Station KSX#GroundStation21.0556028,67.8887061Ground StationActiveSwedenNeustrelitz STDN NSGS#GroundStation13.07,53.32972222Ground StationActiveGermanyQueensland Receiver#RadarStation143.1936,-24.2871Radar StationActiveAustraliaMcDonald Observatory STDN MLRL#LaserStation-104.01519731,30.68026717Laser StationActiveUnited StatesMidway Research Center#GroundStation-77.373,38.498Ground StationActiveUnited StatesAlaska 1 AK1 Leolut#GroundStation-147.5173,64.973666Ground StationActiveUnited StatesTromso EISCAT Dish Radar#RadarStation19.22637,69.58648Radar StationActiveNorwayOrbcomm St Johns B#GroundStation-109.554917,34.455501Ground StationActiveUnited StatesBretagne 1 Radar STDN KRUF#RadarStation-52.64498992,5.11400642Radar StationActiveFrench GuianaNauchny#OpticalTrackingStation34.01567,44.72785Optical Tracking StationActiveUkraineSocorro GEODSS, xx1#OpticalTrackingStation-106.66009,33.81727Optical Tracking StationActiveUnited StatesKuantan Station#GroundStation103.36,3.866Ground StationActiveMalaysiaOttawa (2) Geolut#GroundStation-75.674333,45.3438Ground StationActiveCanadaDombarovsky Cosmodrome x37#LaunchPad60.728749,51.301802Launch PadActiveRussiaNHS STDN NH2S#GroundStation-71.63032172,42.94474167Ground StationActiveUnited StatesVostochny Cosmodrome#LaunchSite128.25,51.817Launch SitePlannedRussiaPsary Station#GroundStation20.8542,50.9336Ground StationActivePolandCape Canaveral AFS SLC 17A STDN A17P#LaunchPad-80.56492617,28.44716106Launch PadActiveUnited StatesMiramar Station#GroundStation-80.2833,25.9756Ground StationActiveUnited StatesHolloman - A#LaunchPad-106.0714,32.8954Launch PadActiveUnited StatesCheia Station#GroundStation25.9465,45.4567Ground StationActiveRomaniaTularosa STDN TULF#GroundStation-106.15915417,33.09616153Ground StationActiveUnited StatesCSTARS_East_11m#GroundStation-80.3837,25.61336Ground StationActiveUnited StatesPillar Point STDN PPTY#GroundStation-122.49918072,37.49777411Ground StationActiveUnited StatesDongara Station AUWA01 STDN AUWS#GroundStation115.34866806,-29.04576808Ground StationActiveAustraliaGSE Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesOrbcomm Chong Ho Won#GroundStation127.66,37.14Ground StationActiveSouth KoreaWallops Flight Facility - LA 4#LaunchPad-75.4701,37.8508Launch PadActiveUnited StatesKapustin Yar - LC 86 4a#LaunchPad46.295,48.5508Launch PadActiveRussiaPoint Mugu LC 2#LaunchPad-119.121,34.0992Launch PadActiveUnited StatesGreenbelt STDN STAL#LaserStation-76.82777092,39.02029483Laser StationInactiveUnited StatesBeijing 2 Leolut#GroundStation116.42,39.908Ground StationActiveChinaEglin AFB AN-FPS 85 PAR STDN EG2F#RadarStation-86.21471742,30.57252794Radar StationActiveUnited StatesRas Al Khaimah Spaceport#LaunchSite55.941,25.617Launch SitePlannedUnited Arab EmiratesPatrick AFB STDN PA2Q#GroundStation-80.60609819,28.22732817Ground StationActiveUnited StatesSvobodny Cosmodrome xx1#LaunchPad128.3102,51.747Launch PadInactiveRussiaNOAA STDN SOCA#GroundStation-76.93222222,38.85002611Ground StationActiveUnited StatesSpaceport Singapore#LaunchSite103.99,1.36Launch SitePlannedSingaporeSpaceport America#LaunchSite-106.98,32.99Launch SitePlannedUnited StatesJiuquan#LaunchSite100.5,41.1Launch SiteActiveChinaJamesburg Earth Station#GroundStation-121.64704,36.40313Ground StationActiveUnited StatesWestford Radio Telescope#GroundStation-71.49377,42.61293Ground StationActiveUnited StatesAnkara 1 Leolut#GroundStation32.9897,40.140833Ground StationActiveTurkeyKapustin Yar - LC 107 2d#LaunchPad46.2959,48.5695Launch PadActiveRussiaBaikonur Cosmodrome - LC 45-2#LaunchPad63.6532,45.9433Launch PadActiveKazakhstanJiuquan SLS#LaunchPad100.2915,40.958Launch PadActiveChinaWhite Sands LC 50#LaunchPad-106.3488,32.4064Launch PadActiveUnited StatesSvobodny Cosmodrome xx3#LaunchPad128.185,51.792Launch PadInactiveRussiaVillafranca VIL-4#GroundStation-3.95173,40.44451Ground StationActiveSpainKwajalein BC-4 Cameras#OpticalTrackingStation167.719465,8.72304Optical Tracking StationActiveMarshall IslandsADSCGS#GroundStation114.842,-28.695Ground StationActiveAustraliaSvobodny Cosmodrome xx8#LaunchPad128.2764,51.8357Launch PadInactiveRussiaIncheon Leolut#GroundStation126.649,37.393Ground StationActiveSouth KoreaWoomera Test Range - LA 9#LaunchPad136.4871,-30.9031Launch PadActiveAustraliaWestern Australian Transmitter#RadarStation122.8435,-28.3174Radar StationActiveAustraliaWallops Island STDN WTDS#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesHauppauge Station#GroundStation-73.264,40.8202Ground StationActiveUnited StatesKeelung 1 Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanSPTR-2#GroundStation0,-90Ground StationActiveAntarcticaBaikonur Cosmodrome LC 105#LaunchPad63.4962,45.9503Launch PadActiveKazakhstanBaikonur Cosmodrome - LC 110R#LaunchPad63.3102,45.9622Launch PadActiveKazakhstanYarragedee STDN YARZ#GroundStation115.34666667,-29.04664553Ground StationActiveAustraliaResolute Bay#LaunchSite-94.8962,74.687Launch SiteInactiveCanadaMaui GEODSS Cam3#OpticalTrackingStation-156.2575,20.7085Optical Tracking StationActiveUnited StatesAntigua Island STDN ANRQ#GroundStation-61.77522336,17.13728994Ground StationActiveAntigua and BarbudaCape Canaveral AFS LC 34#LaunchPad-80.5611,28.5218Launch PadInactiveUnited StatesCebreros DSA 2#GroundStation-4.367549,40.45269Ground StationActiveSpainBaikonur Cosmodrome - LC 109#LaunchPad63.4452,45.9525Launch PadActiveKazakhstanEsrange Station ELS#GroundStation21.062325,67.8765153Ground StationActiveSwedenAntigua STDN AN3S#GroundStation-61.77435,17.13695083Ground StationActiveAntigua and BarbudaWallops Flight Facility - LA 4 MAST#LaunchPad-75.4702,37.8508Launch PadActiveUnited StatesJoint Defense Facility Pine Gap#GroundStation133.737,-23.799Ground StationActiveAustraliaKarachi Leolut#GroundStation67.136,24.946Ground StationActivePakistanDarwin Station#GroundStation130.9812,-12.4765Ground StationActiveAustraliaBaikonur Cosmodrome - LC 110L#LaunchPad63.3049,45.9647Launch PadActiveKazakhstanSvalsat EUM 1#GroundStation15.4014,78.2286Ground StationActiveNorwayTidbinbilla STDN RGTS#GroundStation148.98241831,-35.40453633Ground StationActiveAustraliaSvalsat SG 4 STDN SG4S#GroundStation15.4097095,78.22801361Ground StationActiveNorwayTianshan Station#GroundStation120.09,43.874Ground StationActiveChinaMMW Radar#RadarStation167.48123,9.39731Radar StationActiveMarshall IslandsCTS STDN CTSS#GroundStation-104.52846914,38.80598842Ground StationActiveUnited StatesTromso EISCAT Cylinder Radar#RadarStation19.2219,69.5867Radar StationActiveNorwayKodiak Launch Complex#LaunchSite-152.3393,57.4352Launch SiteActiveUnited StatesEchoStar Mt Jackson Station#GroundStation-78.667,38.723Ground StationActiveUnited StatesItapetinga Radio Observatory#GroundStation-46.55823,-23.18524Ground StationActiveBrazilVostochny Cosmodrome#LaunchSite128.25,51.817Launch SitePlannedRussiaWallops Island STDN WLPQ#GroundStation-75.50929556,37.86026147Ground StationActiveUnited StatesHawaii 1 (HI1) Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesKapustin Yar LC 107 2a#LaunchPad46.2949,48.5609Launch PadActiveRussiaDombarovsky Cosmodrome x36#LaunchPad60.301504,51.270092Launch PadActiveRussiaWallops Island STDN WL53#GroundStation-75.4601,37.9468Ground StationActiveUnited StatesSvobodny Cosmodrome xx6#LaunchPad128.467,51.8054Launch PadInactiveRussiaDehmandro Station#GroundStation67.0992,25.1933Ground StationActivePakistanXTAR 16m#GroundStation-15.63132,27.76271Ground StationActiveSpainBaikonur Cosmodrome LC 1#LaunchPad63.3422,45.9203Launch PadActiveKazakhstanMakassar Leolut#GroundStation119.55,-5.066666Ground StationPlannedIndonesiaVandenberg AFB SLC 10W#LaunchPad-120.6244,34.7636Launch PadInactiveUnited StatesBiscarosse - CE#LaunchPad-1.2325,44.3917Launch PadActiveFranceBaikonur Cosmodrome LC 104#LaunchPad63.4197,45.9875Launch PadActiveKazakhstanBaikonur Cosmodrome LC 161-35#LaunchPad63.063,46.0335Launch PadActiveKazakhstanWallops Island STDN WPS8#GroundStation-75.47583319,37.92735903Ground StationActiveUnited StatesAnderson Peak STDN ANPC#OpticalTrackingStation-121.64434508,36.18052364Optical Tracking StationActiveUnited StatesCape Canaveral Air Force Station - SLC 46 (STDN A46P)#LaunchPad-80.52840256,28.45849161Launch PadInactiveUnited StatesPoint Mugu#LaunchSite-119.121,34.099Launch SiteActiveUnited StatesChilca#LaunchSite-76.799,-12.505Launch SiteActivePeruKueijen#GroundStation120.28,22.97Ground StationActiveTaiwanZimmerwald STDN ZIML#LaserStation7.46521981,46.87722883Laser StationActiveSwitzerlandFresnedillas Monitoring Station#GroundStation-4.1697,40.4555Ground StationActiveSpainDombarovsky Cosmodrome - x23#LaunchPad60.675969,51.146793Launch PadActiveRussiaHagerstown Teleport#GroundStation-77.757011,39.599906Ground StationActiveUnited StatesNapa Teleport#GroundStation-122.279965,38.245535Ground StationActiveUnited StatesDombarovsky Cosmodrome xx5#LaunchPad59.595072,50.837656Launch PadActiveRussiaDSS 25 Goldstone STDN DS25#GroundStation-116.87536319,35.33761197Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 22#LaunchPad-80.5398,28.461Launch PadInactiveUnited StatesEglin AFB Launch Site#LaunchSite-85.34588,29.67729Launch SiteActiveUnited StatesHolloman ZEL#LaunchPad-106.06,32.88Launch PadActiveUnited StatesOgasawara Downrange Station#GroundStation142.215439,27.079017Ground StationActiveJapanInuvik Station INU#GroundStation-133.54391,68.31788Ground StationActiveCanadaToulouse 1 Leolut#GroundStation1.4808,43.560666Ground StationActiveFranceBaikonur Cosmodrome - LC 165#LaunchPad62.9185,45.9912Launch PadActiveKazakhstanJiuquan LA 2B#LaunchPad100.3132,41.3061Launch PadActiveChinaPort Hedland STDN SWNS#GroundStation118.635,-20.38Ground StationActiveAustraliaTsukuba Space Center#GroundStation140.126716,36.071299Ground StationActiveJapanDSS 27 Goldstone STDN D27D#GroundStation-116.77665044,35.23827178Ground StationActiveUnited StatesTrivandrum#GroundStation76.8731,8.5365Ground StationActiveIndiaMaryland (2) Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesEsrange Station SfinX#GroundStation21.0525247,67.8882281Ground StationActiveSwedenTranquillon Peak STDN CA2F#GroundStation-120.56111489,34.58302833Ground StationActiveUnited StatesWhite Sands STDN WH2K#GroundStation-106.60855172,32.50073719Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 161-35#LaunchPad63.063,46.0335Launch PadActiveKazakhstanBaikonur Cosmodrome LC 45-1#LaunchPad63.655387,45.940067Launch PadActiveKazakhstanASF 11m STDN ASFS#GroundStation-147.85817536,64.85880858Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-3#LaunchPad63.6598,45.9754Launch PadActiveKazakhstanDSS 17 Goldstone STDN GDSA#GroundStation-116.87300133,35.34154264Ground StationDemolishedUnited StatesKodiak Ranging Site#GroundStation-152.3737,57.4554Ground StationActiveUnited StatesToulouse (2) Leolut#GroundStation1.4808,43.5605Ground StationActiveFranceDSS 15 Goldstone STDN DS15#GroundStation-116.88719511,35.42185328Ground StationActiveUnited StatesDomme SIGINT Station#GroundStation1.2381,44.7864Ground StationActiveFranceMullach Mor Radar Station#GroundStation-8.584962,57.819175Ground StationActiveUnited KingdomTula Peak STDN TULS#GroundStation-106.139091,33.02694489Ground StationInactiveUnited StatesESA Space Debris Telescope#OpticalTrackingStation-16.51189,28.30095Optical Tracking StationActiveSpainBaikonur Cosmodrome - LC 175-59#LaunchPad62.9862,46.0525Launch PadActiveKazakhstanCTSA#GroundStation-104.52848,38.8059353Ground StationActiveUnited StatesPanamsat Long Beach Operations Center#GroundStation-118.2112,33.8292Ground StationActiveUnited StatesDombarovsky Cosmodrome x34#LaunchPad60.461524,51.259129Launch PadActiveRussiaESRIN Maspalomas#GroundStation-15.63377,27.76433Ground StationActiveSpainBaikonur Cosmodrome - LC 60-8#LaunchPad64.0183,46.0174Launch PadActiveKazakhstanSary Shagan#LaunchSite73.562,45.812Launch SiteActiveKazakhstanNiles Canyon Station#GroundStation-121.944,37.6Ground StationActiveUnited StatesComcast Titan Station#GroundStation-105.025,39.514Ground StationActiveUnited StatesPrince Albert xx1#GroundStation-105.93291,53.21266Ground StationActiveCanadaTidbinbilla STDN CANS#GroundStation148.983058,-35.40466667Ground StationActiveAustraliaNATO Missile Firing Installation#LaunchSite24.175,35.573Launch SiteActiveCreteEldorado AFS PAVE PAWS#RadarStation-100.5529,30.9783Radar StationInactiveUnited StatesSanta Maria Station#GroundStation-25.136103,36.99694Ground StationActivePortugalBaikonur Cosmodrome - LC 1#LaunchPad63.3422,45.9203Launch PadActiveKazakhstanWallops Flight Facility - LA 2 AML-2#LaunchPad-75.483,37.8379Launch PadActiveUnited StatesTokyo STDN KA2S#GroundStation139.49177778,35.70876186Ground StationActiveJapanWallops Island STDN WP2Y#GroundStation-75.47685742,37.92558517Ground StationActiveUnited StatesFort Huachuca STDN FT2F#GroundStation-110.43817333,31.55676686Ground StationActiveUnited StatesGatineau xx1#GroundStation-75.80933,45.5858Ground StationActiveCanadaWallops Island Drop Zone#LaunchSite-75,37.5Launch SiteActiveUnited StatesHAARP#RadarStation-145.151,62.393Radar StationActiveUnited StatesDombarovsky Cosmodrome x35#LaunchPad60.858975,51.26491Launch PadActiveRussiaNatal Station STDN NATC#OpticalTrackingStation-35.16434436,-5.92780506Optical Tracking StationActiveBrazilCape Canaveral AFS SLC 36A STDN A36P#LaunchPad-80.53772211,28.47143831Launch PadDemolishedUnited StatesTrollSat Ground Station#GroundStation2.53838,-72.0117Ground StationActiveAntarcticaMount Pleasant STDN TSMF#GroundStation147.439,-42.805Ground StationActiveAustraliaDGS STDN DGIS#GroundStation72.36999861,-7.27003056Ground StationActiveUnited KingdomVela Antenna#GroundStation147.44165,-42.8034Ground StationActiveAustraliaWhite Sands STDN STWS#GroundStation-106.60856383,32.49950678Ground StationActiveUnited StatesEsrange Station Balder#GroundStation21.038,67.879Ground StationActiveSwedenBaikonur Cosmodrome - LC 192#LaunchPad63.2995,46.0243Launch PadActiveKazakhstanMerritt Island STDN MLAQ#GroundStation-80.66438767,28.42471119Ground StationActiveUnited StatesNCTS Naples#GroundStation14.049,40.929Ground StationActiveItalyKennedy Space Center#LaunchSite-80.62,28.62Launch SiteActiveUnited StatesRAF Menwith Hill#GroundStation-1.689492,54.008692Ground StationActiveUnited KingdomVLBA Kitt Peak#GroundStation-111.611992,31.956316Ground StationActiveUnited StatesDiego Garcia GEODSS, xx2#OpticalTrackingStation72.45244,-7.41163Optical Tracking StationActiveUnited KingdomBaikonur Cosmodrome LC 41-3#LaunchPad63.6598,45.9754Launch PadActiveKazakhstanLustbuhel Observatory#LaserStation15.4934,47.06714Laser StationActiveAustriaWoomera Test Range MRL#LaunchPad136.5332,-30.9573Launch PadActiveAustraliaCape Canaveral Air Force Station - SLC 37B (STDN B37P)#LaunchPad-80.56445806,28.53121944Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx7#LaunchPad59.73193,50.883614Launch PadActiveRussiaKunia Station#GroundStation-158.055314,21.473136Ground StationActiveUnited StatesGRGT STDN GW2S#GroundStation144.84087447,13.58758828Ground StationActiveUnited StatesJohnston Island#LaunchSite-169.53,16.733Launch SiteActiveUnited StatesFort Churchill#LaunchSite-93.820278,58.734167Launch SiteActiveCanadaHolloman - ZEL#LaunchPad-106.06,32.88Launch PadActiveUnited StatesNHSA#GroundStation-71.626559,42.9478333Ground StationActiveUnited StatesSondrestrom Rocket Range#LaunchSite-50.6,67.024Launch SiteActiveGreenlandWoomera Test Range HAD#LaunchPad136.5322,-30.9553Launch PadActiveAustraliaOrbcomm Wenatchee B#GroundStation-120.175247,47.550924Ground StationActiveUnited StatesHong Kong (1) Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaCape Canaveral Air Force Station - LC 5#LaunchPad-80.5733,28.4394Launch PadInactiveUnited StatesTTSB#GroundStation-68.5988147,76.5153639Ground StationActiveGreenlandScanEx Magadan Station#GroundStation150.81308,59.55632Ground StationActiveRussiaNemea Station#GroundStation22.6222,37.8461Ground StationActiveGreeceRoaring Creek Station#GroundStation-76.4393,40.8935Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 244#LaunchPad63.6346,45.8403Launch PadActiveKazakhstanRas Al Khaimah Station#GroundStation56.0408,25.7927Ground StationActiveUnited Arab EmiratesNaro Space Center#LaunchSite127.53507,34.43187Launch SiteActiveSouth KoreaFlorida 2 FL2 Leolut#GroundStation-80.3838,25.616333Ground StationActiveUnited StatesCape Canaveral Air Force Station - SLC 41 (STDN A41P)#LaunchPad-80.58287267,28.58345786Launch PadActiveUnited StatesCachoeira Paulista#GroundStation-45.000214,-22.68211Ground StationActiveBrazilGreen River Pad 1#LaunchPad-110.0775,38.9416Launch PadActiveUnited StatesEsrange Station ESX#GroundStation21.0634472,67.8781431Ground StationActiveSwedenToulouse 2 Leolut#GroundStation1.4808,43.5605Ground StationActiveFranceWallops Island STDN WP2Z#GroundStation-75.47375114,37.92891233Ground StationActiveUnited StatesGellinam#GroundStation167.727855,9.099416Ground StationActiveMarshall IslandsGreat Wall Station#GroundStation-58.9626,-62.2166Ground StationActiveAntarcticaBarking Sands - LC 19#LaunchPad-159.7813,22.0619Launch PadActiveUnited StatesThule STDN TH2S#GroundStation-68.59881472,76.51536389Ground StationActiveGreenlandOrbcomm Ocilla B#GroundStation-83.199585,31.500413Ground StationActiveUnited StatesWallops Island STDN HR2S#GroundStation-75.46209,37.94542583Ground StationActiveUnited StatesOhr Station#GroundStation9.33,52.06Ground StationActiveGermanyRozhen#OpticalTrackingStation24.744,41.693Optical Tracking StationActiveBulgariaOrroral Valley#GroundStation148.95713,-35.62982Ground StationRelocatedAustraliaBaikonur Cosmodrome - LC 242#LaunchPad63.4626,45.9407Launch PadActiveKazakhstanCape Canaveral Air Force Station - LC 9#LaunchPad-80.5594,28.4522Launch PadInactiveUnited StatesGCHQ Bude#GroundStation-4.5537,50.8862Ground StationActiveUnited KingdomVandenberg AFB SLC 576-E#LaunchPad-120.6191,34.7396Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx3#LaunchPad59.597872,50.756297Launch PadActiveRussiaPomonkey STDN PMKS#GroundStation-77.05774122,38.55771053Ground StationInactiveUnited StatesEaster Island Leolut#GroundStation-109.437,-27.150166Ground StationActiveChilePoint Mugu STDN PM3F#GroundStation-119.15476742,34.12288772Ground StationActiveUnited StatesCape Canaveral AFS LC 16#LaunchPad-80.5518,28.5016Launch PadInactiveUnited StatesAmman#GroundStation35.837,32.0808Ground StationActiveJordanBaikonur Cosmodrome LC 90-19#LaunchPad62.9144,46.0863Launch PadActiveKazakhstanKapustin Yar V2#LaunchPad45.9074,48.5709Launch PadActiveRussiaBarking Sands LC 42#LaunchPad-159.7727,22.0682Launch PadActiveUnited StatesCape Canaveral AFS SLC 37A#LaunchPad-80.568,28.534Launch PadInactiveUnited StatesBermuda STDN BDDQ#GroundStation-64.65344947,32.34794319Ground StationInactiveBermudaSouth Uist Range Control Unit#GroundStation-7.355771,57.340814Ground StationActiveUnited KingdomSvobodny Cosmodrome - x10#LaunchPad128.3323,51.877Launch PadInactiveRussiaWallops Flight Facility LA 3A#LaunchPad-75.4726,37.848Launch PadActiveUnited StatesKapustin Yar - LC 107 2c#LaunchPad46.2949,48.5695Launch PadActiveRussiaWhite Sands STDN WHSX#GroundStation-106.60801708,32.50094139Ground StationActiveUnited StatesTonopah Test Range#LaunchSite-116.77,37.8Launch SiteActiveUnited StatesWhite Sands STDN WH4K#GroundStation-106.60855389,32.50138961Ground StationActiveUnited StatesVandenberg AFB#LaunchSite-120.6,34.7Launch SiteActiveUnited StatesKapustin Yar#LaunchSite46.1,48.5Launch SiteActiveRussiaEl Palomar Geolut#GroundStation-58.6,-34.6Ground StationActiveArgentinaBiscarosse - BS#LaunchPad-1.2675,44.2882Launch PadInactiveFranceBad Aibling Station#GroundStation11.984444,47.879444Ground StationInactiveGermanyPlesetsk Cosmodrome#LaunchSite40.6,62.9Launch SiteActiveRussiaSHAR-2#GroundStation80.19633,13.67394Ground StationActiveIndiaMayaguez#GroundStation-67.1368,18.2111Ground StationActiveUnited StatesWallops Flight Facility LA 1 AML#LaunchPad-75.4871,37.8352Launch PadActiveUnited StatesBaikonur Cosmodrome LC 162-36#LaunchPad63.0668,46.0323Launch PadActiveKazakhstanValcartier#OpticalTrackingStation-71.47075,46.87545Optical Tracking StationActiveCanadaGRAVES Transmitter#RadarStation5.515,47.348Radar StationActiveFranceGuam 1 (GU1) Leolut#GroundStation144.939,13.578333Ground StationActiveUnited StatesSpaceport Sweden#LaunchSite20.331,67.822Launch SitePlannedSwedenKapustin Yar - LC 86 4c#LaunchPad46.297,48.5481Launch PadActiveRussiaSan Clemente#LaunchSite-118.48698,32.91771Launch SiteActiveUnited StatesMayport Drop Zone#LaunchSite-78.5,29Launch SiteActiveUnited StatesYokohama Satellite Control Center#GroundStation139.5145,35.5055Ground StationActiveJapanEsrange Station SSC-CNES#GroundStation21.0309,67.88225Ground StationActiveSwedenWallops Island STDN WL2F#GroundStation-75.46422336,37.94409914Ground StationActiveUnited StatesSuffield#OpticalTrackingStation-111.10471,50.29301Optical Tracking StationActiveCanadaBarking Sands - LC 12#LaunchPad-159.7819,22.0593Launch PadActiveUnited StatesBarking Sands - Kokole Point#LaunchPad-159.7627,21.988Launch PadActiveUnited StatesMedicina Radio Observatory#GroundStation11.646944,44.520833Ground StationActiveItalyVandenberg STDN CALY#GroundStation-120.50101308,34.56562375Ground StationActiveUnited StatesSemnan Launch Center xx1#LaunchPad53.896,35.222Launch PadActiveIranWallops Island STDN WP3Z#GroundStation-75.47589722,37.92820833Ground StationActiveUnited StatesNATO Missile Firing Installation#LaunchSite24.175,35.573Launch SiteActiveCreteYorii#GroundStation139.2,36.12Ground StationActiveJapanUsuda Deep Space Center#GroundStation138.3627,36.1325Ground StationActiveJapanEsrange Station ELS#GroundStation21.062325,67.8765153Ground StationActiveSwedenCorn Ranch#LaunchSite-104.7589,31.4233Launch SiteActiveUnited StatesWallops Flight Facility LA 2 ARC#LaunchPad-75.4841,37.838Launch PadActiveUnited StatesPennant Point Station#GroundStation-63.6133,44.4648Ground StationActiveCanadaSodankyla EISCAT Radar#RadarStation26.63043,67.36383Radar StationActiveFinlandWallops Island STDN WT3S#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesOttawa Leolut#GroundStation-75.6745,45.328666Ground StationActiveCanadaBiscarosse SUD#LaunchPad-1.276,44.295Launch PadActiveFranceTorrejon Air Base xx2#GroundStation-3.44474,40.48276Ground StationActiveSpainSPTR-1#GroundStation0,-90Ground StationInactiveAntarcticaPlesetsk Cosmodrome - LC 132-1#LaunchPad40.8686,62.8833Launch PadActiveRussiaEsrange Station ESSEX#GroundStation21.0630978,67.8904339Ground StationActiveSwedenDirecTV LA Broadcast Center LABC#GroundStation-118.425,33.9827Ground StationActiveUnited StatesYatharagga STDN ATFS#GroundStation115.35126589,-29.04494003Ground StationActiveAustraliaWhite Sands - LC 32#LaunchPad-106.4069,32.4068Launch PadActiveUnited StatesFairbanks STDN UL33#GroundStation-147.51806617,64.97681389Ground StationActiveUnited StatesCalifornia 1 CA1 Leolut#GroundStation-120.5515,34.6625Ground StationActiveUnited StatesESRIN#GroundStation12.67585,41.8275Ground StationActiveItalyDSS 16 Goldstone STDN GD28#GroundStation-116.87360467,35.34154264Ground StationInactiveUnited StatesUchinoura Mu Pad#LaunchPad131.08223,31.251028Launch PadActiveJapanPort Blair#GroundStation92.71247,11.63727Ground StationActiveIndiaAndrushevka#OpticalTrackingStation28.997306,50.000556Optical Tracking StationActiveUkraineMatera Station#GroundStation16.704079,40.649536Ground StationActiveItalyCape Canaveral AFS SLC 40 STDN A40P#LaunchPad-80.57719364,28.56198939Launch PadActiveUnited StatesEllenwood Teleport#GroundStation-84.272016,33.664202Ground StationActiveUnited StatesEdmonton Geolut#GroundStation-113.316166,53.6782Ground StationActiveCanadaCalifornia 2 CA2 Leolut#GroundStation-120.5517,34.662333Ground StationActiveUnited StatesPalmachim AFB#LaunchSite34.69,31.89Launch SiteActiveIsraelDombarovsky Cosmodrome x31#LaunchPad60.606992,51.241105Launch PadActiveRussiaGuiana Space Center - ELS#LaunchPad-52.8345,5.3047Launch PadActiveFrench GuianaVLBA Hancock#GroundStation-71.986581,42.933608Ground StationActiveUnited StatesEsrange Station ELS (STDN KILS)#GroundStation21.062337,67.876532Ground StationActiveSwedenClewiston Station#GroundStation-81.049,26.747Ground StationActiveUnited StatesEutelsat Paris#GroundStation2.277808,48.840264Ground StationActiveFrancePalmachim AFB Shavit Pad#LaunchPad34.6802,31.8848Launch PadActiveIsraelWhite Sands STDN WS1S#GroundStation-106.61209953,32.54075494Ground StationActiveUnited StatesBrasilia Leolut#GroundStation-47.9027,-15.857166Ground StationActiveBrazilCape Canaveral Air Force Station - LC 13#LaunchPad-80.5446,28.4859Launch PadInactiveUnited StatesOttawa (1) Geolut#GroundStation-75.674,45.329Ground StationActiveCanadaEsrange Station Balder#GroundStation21.038,67.879Ground StationActiveSwedenHaiphong Leolut#GroundStation106.71,20.801166Ground StationActiveVietnamFucino#GroundStation13.6018,41.9793Ground StationActiveItalyFuchsstadt Station#GroundStation9.924176,50.118315Ground StationActiveGermanySouth Point Station USHI01 STDN HWIS#GroundStation-155.66330125,19.0139045Ground StationActiveUnited StatesWhite Sands STDN WH2S#GroundStation-106.60855389,32.50129656Ground StationActiveUnited StatesPulantat Station#GroundStation144.75,13.42Ground StationActiveUnited StatesDombarovsky Cosmodrome xx8#LaunchPad60.52694,50.959329Launch PadActiveRussiaRAF Feltwell#GroundStation0.5205,52.4806Ground StationActiveUnited KingdomMondy#OpticalTrackingStation100.918792,51.621694Optical Tracking StationActiveRussiaNeu Golm Station#GroundStation14.0867,52.3137Ground StationActiveGermanyCape Canaveral AFS LC 4#LaunchPad-80.5356,28.4669Launch PadInactiveUnited StatesMerritt Island STDN MMTF#GroundStation-80.67478864,28.47857681Ground StationActiveUnited StatesVandenberg AFB SLC 4W#LaunchPad-120.6154,34.6331Launch PadInactiveUnited StatesPongdong-ri#LaunchSite124.705265,39.659987Launch SiteActiveNorth KoreaDombarovsky Cosmodrome x10#LaunchPad60.649859,50.985518Launch PadActiveRussiaJiuquan LA 3#LaunchPad100.78,41.0152Launch PadActiveChinaCape Canaveral STDN CNVF#GroundStation-80.57650917,28.48160589Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-15#LaunchPad63.6687,45.9763Launch PadActiveKazakhstanKapustin Yar - S#LaunchPad46.3175,48.4763Launch PadActiveRussiaDombarovsky Cosmodrome x42#LaunchPad59.972508,51.521988Launch PadActiveRussiaPlesetsk Cosmodrome - LC 132-2#LaunchPad40.8722,62.8834Launch PadActiveRussiaMountain Horse Station#GroundStation-121.5933,37.7502Ground StationActiveUnited StatesBarking Sands LC 12#LaunchPad-159.7819,22.0593Launch PadActiveUnited StatesLethbridge Station#GroundStation-112.873188,49.681208Ground StationActiveCanadaBaikonur Cosmodrome LC 41-15#LaunchPad63.6687,45.9763Launch PadActiveKazakhstanCape Canaveral AFS LC 21#LaunchPad-80.5403,28.4606Launch PadInactiveUnited StatesVandenberg Air Force Base - SLC 5#LaunchPad-120.6247,34.608Launch PadInactiveUnited StatesCSTARS_20m#GroundStation-80.38477,25.61375Ground StationActiveUnited StatesAlice Springs Receiver#RadarStation133.6792,-23.5213Radar StationActiveAustraliaBarking Sands Kokole Point#LaunchPad-159.7627,21.988Launch PadActiveUnited StatesBaikonur Cosmodrome LC 200-39#LaunchPad63.032,46.0399Launch PadActiveKazakhstanCotopaxi Station#GroundStation-78.578,-0.623Ground StationActiveEcuadorBaikonur Cosmodrome LC 245#LaunchPad63.5271,45.8175Launch PadActiveKazakhstanOuargla Leolut#GroundStation5.49,31.88Ground StationActiveAlgeriaDombarovsky Cosmodrome x14#LaunchPad59.958431,51.036288Launch PadActiveRussiaAbu Dhabi Leolut#GroundStation54.4478,24.4315Ground StationActiveUnited Arab EmiratesCape Canaveral AFS SLC 41 STDN A41P#LaunchPad-80.58287267,28.58345786Launch PadActiveUnited StatesHammaguira - Brigitte-A#LaunchPad-3.0081,30.8712Launch PadActiveAlgeriaSemnan Launch Center - xx4#LaunchPad53.955,35.258Launch PadActiveIranSvobodny Cosmodrome - xx4#LaunchPad128.3386,51.7938Launch PadInactiveRussiaFlorida 1 (FL1) Leolut#GroundStation-80.3838,25.616Ground StationActiveUnited StatesDongfeng Station#GroundStation125.53,42.68Ground StationActiveChinaGreen River - Pad 3#LaunchPad-110.0741,38.9426Launch PadActiveUnited StatesBaikonur Cosmodrome LC 193#LaunchPad63.389,45.9532Launch PadActiveKazakhstanBeijing Station#GroundStation116.2275,40.1172Ground StationActiveChinaBaikonur Cosmodrome LC 41-4#LaunchPad63.6649,45.9759Launch PadActiveKazakhstanBrunei#GroundStation114.929,4.946Ground StationActiveBruneiMatagorda Island#LaunchSite-96.46,28.32Launch SiteActiveUnited StatesDombarovsky Cosmodrome - x41#LaunchPad60.178331,51.50362Launch PadActiveRussiaMaiquetia (1) Leolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaBaikonur Cosmodrome - LC 250#LaunchPad63.3049,46.0083Launch PadActiveKazakhstanProspect Harbor Naval Sat Station#GroundStation-68.013,44.404Ground StationActiveUnited StatesGISTDA Ground Receiving Station#GroundStation100.788982,13.730775Ground StationActiveThailandKoganei xx1#GroundStation139.488503,35.711393Ground StationActiveJapanTanegashima Space Center - Sounding Rocket Pad#LaunchPad130.962691,30.376847Launch PadActiveJapanGatineau xx2#GroundStation-75.80811,45.58435Ground StationActiveCanadaGreen Bank Telescope 20m#GroundStation-79.82551,38.43684Ground StationActiveUnited StatesWest Freugh Ground Station#GroundStation-4.946,54.846Ground StationActiveUnited KingdomCUHK Station#GroundStation114.206591,22.421278Ground StationActiveChinaBaikonur Cosmodrome - LC 196#LaunchPad63.1477,45.8283Launch PadActiveKazakhstanMaspalomas 1 Geolut#GroundStation-15.634,27.764Ground StationActiveSpainKatsuura USB F2#GroundStation140.29975,35.20592Ground StationActiveJapanOrroral Valley STDN ORRL#LaserStation148.95480464,-35.62492722Laser StationActiveAustraliaBiscarosse BE#LaunchPad-1.2727,44.2742Launch PadActiveFranceKapustin Yar LC 107 2g#LaunchPad46.2958,48.5616Launch PadActiveRussiaDombarovsky Cosmodrome - x32#LaunchPad60.607974,51.241789Launch PadActiveRussiaWallops Flight Facility - LA 2 RAG#LaunchPad-75.4823,37.8385Launch PadActiveUnited StatesNorth Pole Station USAK01 STDN USAS#GroundStation-147.50021417,64.80424111Ground StationActiveUnited StatesDkhila Mateur#GroundStation9.714,36.88Ground StationActiveTunisiaCavalier AFS PARCS#RadarStation-97.8998,48.7246Radar StationActiveUnited StatesGreen River Pad 2#LaunchPad-110.0753,38.9413Launch PadActiveUnited StatesPoker Flat Station PF1 (STDN DX2S)#GroundStation-147.4311625,65.11783389Ground StationActiveUnited StatesHammaguira - Brigitte#LaunchPad-3.0357,30.8935Launch PadActiveAlgeriaBangkok (2) Leolut#GroundStation100.5432,13.717166Ground StationActiveThailandEglin AFB AN-FPS 85 PAR (STDN EG2F)#RadarStation-86.21471742,30.57252794Radar StationActiveUnited StatesPalmer Station#GroundStation-64.05107,-64.77413Ground StationActiveAntarcticaBaikonur Cosmodrome - LC 160#LaunchPad62.9423,46.0783Launch PadActiveKazakhstanOverberg Test Range xx2#LaunchPad20.25879,-34.61745Launch PadActiveSouth AfricaBarking Sands - LC 1#LaunchPad-159.777,22.058Launch PadActiveUnited StatesPonce de Leon STDN PDLS#GroundStation-80.91302136,29.06664839Ground StationActiveUnited StatesJiuquan Satellite Launch Center - SLS 2#LaunchPad100.2983,40.9607Launch PadActiveChinaCape Canaveral AFS LC 6#LaunchPad-80.5726,28.4407Launch PadInactiveUnited StatesOlenegorsk Radar#RadarStation33.9107,68.1137Radar StationActiveRussiaNHS STDN NHSS#GroundStation-71.62656253,42.94782133Ground StationActiveUnited StatesFort Huachuca STDN FTHF#GroundStation-110.37079797,31.57102425Ground StationActiveUnited StatesSvobodny Cosmodrome x12#LaunchPad128.252,51.8818Launch PadInactiveRussiaMount Stromlo SLR Station STDN MTSL#LaserStation149.00987869,-35.31614697Laser StationActiveAustraliaEuropean Direct Access Facility#GroundStation11.2789,48.0862Ground StationActiveGermanyWhite Sands - LC 50#LaunchPad-106.3488,32.4064Launch PadActiveUnited StatesGreen River - Pad 1#LaunchPad-110.0775,38.9416Launch PadActiveUnited StatesBaikonur Cosmodrome LC 90-20#LaunchPad62.9167,46.0855Launch PadActiveKazakhstanOrbcomm Matera B#GroundStation16.70869,40.64935Ground StationActiveItalyPulkovo#OpticalTrackingStation30.32737,59.77186Optical Tracking StationActiveRussiaTERSS#GroundStation147.42215,-42.92417Ground StationActiveAustraliaRaisting Station#GroundStation11.1126,47.8978Ground StationActiveGermanyYevpatoria 32m#GroundStation33.25016,45.17031Ground StationActiveUkraineAlcantara Launch Center UL Pad#LaunchPad-44.367,-2.316Launch PadActiveBrazilDombarovsky Cosmodrome - x25#LaunchPad59.524663,51.153297Launch PadActiveRussiaSeaMobile Holmdel Teleport#GroundStation-74.1732,40.3945Ground StationActiveUnited StatesIssus-Aussaguel STDN AUSS#GroundStation1.499412,43.428696Ground StationActiveFranceAlbany Leolut#GroundStation117.899,-35.12Ground StationActiveAustraliaVLBA Fort Davis#GroundStation-103.944817,30.635031Ground StationActiveUnited StatesBiscarosse - BE#LaunchPad-1.2727,44.2742Launch PadActiveFranceKwajalein RADOT#OpticalTrackingStation167.719221,8.723176Optical Tracking StationActiveMarshall IslandsPoker Flat - LC 6#LaunchPad-147.4621,65.1169Launch PadActiveUnited StatesLeuk#GroundStation7.645278,46.318056Ground StationActiveSwitzerlandGilmore Creek STDN GLBS#GroundStation-147.50869853,64.97348203Ground StationActiveUnited StatesWhite Sands STDN WH6F#GroundStation-106.65901228,33.81386394Ground StationActiveUnited StatesManaus Leolut#GroundStation-60.054,-3.023166Ground StationActiveBrazilAuScope VLBI Yarragadee#GroundStation115.3456,-29.0471Ground StationActiveAustraliaWallops Island STDN WL6S#GroundStation-75.4611,37.9456Ground StationActiveUnited StatesCape Canaveral ROCC#GroundStation-80.5949,28.4311Ground StationActiveUnited StatesPoint Mugu STDN PM2F#GroundStation-119.15379856,34.12250292Ground StationActiveUnited StatesXichang LA 2#LaunchPad102.0271,28.2455Launch PadActiveChinaNorth Pole Station USAK02#GroundStation-147.5003,64.8048Ground StationActiveUnited StatesBiscarosse - SUD#LaunchPad-1.276,44.295Launch PadActiveFranceDombarovsky Cosmodrome x33#LaunchPad60.142215,51.248931Launch PadActiveRussiaCayenne Station#GroundStation-52.30968,4.94777Ground StationActiveFrench GuianaMillstone Hill Radar#RadarStation-71.491,42.6174Radar StationActiveUnited StatesTranquillon Peak STDN VD4F#GroundStation-120.56111494,34.58304294Ground StationActiveUnited StatesEtam Earth Station#GroundStation-79.736,39.281Ground StationActiveUnited StatesCalifornia 2 (CA2) Leolut#GroundStation-120.5517,34.662333Ground StationActiveUnited StatesFort Meade#GroundStation-76.765,39.102Ground StationActiveUnited StatesInmarsat Station Pune#GroundStation73.957,19.151Ground StationActiveIndiaXiamen Station#GroundStation118.09,24.48Ground StationActiveChinaPlesetsk Cosmodrome - LC 43-3#LaunchPad40.4501,62.9273Launch PadActiveRussiaKapustin Yar - START Pioner#LaunchPad46.3009,48.6149Launch PadActiveRussiaBaikonur Cosmodrome - LC 31#LaunchPad63.5643,45.9961Launch PadActiveKazakhstanKaneohe Omega Transmitter#GroundStation-157.8307,21.4048Ground StationInactiveUnited StatesMerritt Island STDN EULY#GroundStation-80.65301219,28.46356408Ground StationActiveUnited StatesMt Lemmon STDN MTLF#GroundStation-110.78880306,32.44166322Ground StationActiveUnited StatesNakhodka Krona Complex#RadarStation132.577073,42.935333Radar StationActiveRussiaVTS STDN VT2S#GroundStation-120.50539767,34.82564078Ground StationActiveUnited StatesThermopylae Station#GroundStation22.6866,38.8231Ground StationActiveGreeceGrand Turk Island STDN GTKQ#GroundStation-71.13208822,21.46263161Ground StationActiveUnited KingdomFort Monmouth#GroundStation-74.036,40.321Ground StationActiveUnited StatesJonathan Dickinson STDN JDIY#GroundStation-80.10874725,26.98380394Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-4#LaunchPad63.6649,45.9759Launch PadActiveKazakhstanOverberg Test Range - xx2#LaunchPad20.25879,-34.61745Launch PadActiveSouth AfricaDombarovsky Cosmodrome x45#LaunchPad59.956989,51.558712Launch PadActiveRussiaFairbanks STDN ULAE#GroundStation-147.51806617,64.97681389Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 3#LaunchPad-80.5363,28.4662Launch PadInactiveUnited StatesDombarovsky Cosmodrome - x34#LaunchPad60.461524,51.259129Launch PadActiveRussiaDombarovsky Cosmodrome - x40#LaunchPad60.372543,51.379286Launch PadActiveRussiaGunma Leolut#GroundStation138.955,36.426Ground StationActiveJapanOverberg Test Range xx1#LaunchPad20.30271,-34.60276Launch PadActiveSouth AfricaSatish Dhawan Pad 1#LaunchPad80.2346,13.7334Launch PadActiveIndiaCape Canaveral Air Force Station - LC 1#LaunchPad-80.5375,28.465Launch PadInactiveUnited StatesDSS 65 Robledo prior to 2005#GroundStation-4.25141796,40.42718512Ground StationRelocatedSpainSturup Station#GroundStation13.349467,55.541122Ground StationActiveSwedenBaikonur Cosmodrome LC 196#LaunchPad63.1477,45.8283Launch PadActiveKazakhstanMSSC Beam Director Tracker#OpticalTrackingStation-156.2576,20.70855Optical Tracking StationActiveUnited StatesPaumalu Teleport#GroundStation-158.034,21.67Ground StationActiveUnited StatesBaikonur Cosmodrome LC 242#LaunchPad63.4626,45.9407Launch PadActiveKazakhstanKerguelen Island STDN KGLQ#GroundStation70.25598389,-49.35191544Ground StationActiveFranceSantiago Leolut#GroundStation-70.7,-33.489Ground StationActiveChileDombarovsky Cosmodrome x16#LaunchPad59.484369,51.06922Launch PadActiveRussiaBaikonur Cosmodrome - LC 107#LaunchPad63.81,46.046Launch PadActiveKazakhstanPoint Mugu STDN PM4F#GroundStation-119.15283014,34.12211825Ground StationActiveUnited StatesVandenberg AFB SLC 1W#LaunchPad-120.6303,34.7571Launch PadInactiveUnited StatesSan Marco Launch Platform#LaunchSite40.2125,-2.9383Launch SiteInactiveKenyaEsrange Station ELS STDN KILS#GroundStation21.062337,67.876532Ground StationActiveSwedenGTS STDN GT2S#GroundStation144.85543775,13.61588064Ground StationActiveUnited StatesCold Lake#LaunchSite-110.28,54.41Launch SiteActiveCanadaSvobodny Cosmodrome - xx3#LaunchPad128.185,51.792Launch PadInactiveRussiaLabuan Teleport#GroundStation115.198261,5.33198Ground StationActiveMalaysiaFrance Sud#GroundStation2.12416,43.27977Ground StationActiveFranceBaikonur Cosmodrome#LaunchSite63.3,46Launch SiteActiveKazakhstanBaikonur Cosmodrome xx1#LaunchPad63.462718,45.939593Launch PadActiveKazakhstanCape Canaveral Air Force Station - LC 11#LaunchPad-80.5395,28.4753Launch PadInactiveUnited StatesWoomera Test Range#LaunchSite136.5,-30.95Launch SiteActiveAustraliaObninsk#GroundStation36.62,55.08Ground StationActiveRussiaPoker Flat#LaunchSite-147.485,65.129Launch SiteActiveUnited StatesHaystack Radar#RadarStation-71.4881,42.62329Radar StationActiveUnited StatesAlcantara Launch Center RAG Pad#LaunchPad-44.367,-2.3148Launch PadActiveBrazilMickelsen Safeguard Complex#RadarStation-98.3565,48.5895Radar StationInactiveUnited StatesXichang LA 4#LaunchPad102.0232,28.2495Launch PadActiveChinaBaikonur Cosmodrome LC 31#LaunchPad63.5643,45.9961Launch PadActiveKazakhstanFresnedillas STDN MAD8#GroundStation-4.16838497,40.45544939Ground StationRelocatedSpainCape Canaveral Air Force Station - LC 34#LaunchPad-80.5611,28.5218Launch PadInactiveUnited StatesKrona 20J6 Complex#RadarStation41.342745,43.825373Radar StationActiveRussiaBangalore Leolut#GroundStation77.5117,13.034833Ground StationActiveIndiaBaikonur Cosmodrome - LC 105#LaunchPad63.4962,45.9503Launch PadActiveKazakhstanGreenbelt STDN BLT3#GroundStation-76.84276675,38.9984475Ground StationInactiveUnited StatesRiyadh#GroundStation46.5865,24.4259Ground StationActiveSaudi ArabiaBaikonur Cosmodrome - LC 246#LaunchPad63.4236,45.7656Launch PadActiveKazakhstanAscent Media Northvale#GroundStation-73.9414,41.0161Ground StationActiveUnited StatesHTSA#GroundStation-158.242109,21.56228Ground StationActiveUnited StatesCordoba#GroundStation-64.4636,-31.5242Ground StationActiveArgentinaOnsala Observatory 20m#GroundStation11.92632,57.39582Ground StationActiveSwedenPoker Flat Station PF2 (STDN DXAS)#GroundStation-147.43350389,65.11792972Ground StationActiveUnited StatesGreen Bank 85-1 Telescope#GroundStation-79.82817,38.43586Ground StationActiveUnited StatesOkinawa USB F2#GroundStation127.90353,26.49976Ground StationActiveJapanCape Canaveral AFS LC 26A#LaunchPad-80.5705,28.4446Launch PadInactiveUnited StatesNoto Radio Observatory#GroundStation14.989031,36.87605Ground StationActiveItalyGTSB#GroundStation144.8554464,13.6158802Ground StationActiveUnited StatesPalmachim Air Force Base - Arrow II Launcher#LaunchPad34.7023,31.8859Launch PadActiveIsraelCamp Roberts#GroundStation-120.754,35.736Ground StationActiveUnited StatesWoomera Test Range - LA 6A#LaunchPad136.4394,-31.074Launch PadActiveAustraliaHartebeesthoek STDN HB4S#GroundStation27.71260331,-25.88672544Ground StationActiveSouth AfricaDSS 66 Robledo STDN DS66#GroundStation-4.25141764,40.42997486Ground StationActiveSpainMojave Air and Space Port#LaunchSite-118.15,35.06Launch SitePlannedUnited StatesOrbcomm Almaty A#GroundStation76.78235,44.49732Ground StationActiveKazakhstanArabsat Amman Station#GroundStation35.9315,31.9053Ground StationActiveJordanAGS STDN AG1S#GroundStation-147.46120417,65.11670028Ground StationActiveUnited StatesWallops Island STDN WTDQ#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesDSS 13 Goldstone legacy#GroundStation-116.79488,35.24773Ground StationInactiveUnited StatesDocklands Teleport#GroundStation0.058858,51.49939Ground StationActiveUnited KingdomJiuquan xx1#LaunchPad100.304946,41.280432Launch PadActiveChinaPoint Arguello Drop Zone#LaunchSite-123,36Launch SiteActiveUnited StatesKatsuura USB F1#GroundStation140.29886,35.21086Ground StationActiveJapanLandsat Station STDN SF2S#GroundStation-96.61943778,43.73428667Ground StationActiveUnited StatesCeduna Observatory#GroundStation133.80968,-31.86772Ground StationActiveAustraliaInuvik Station IVK#GroundStation-133.53816,68.31762Ground StationActiveCanadaAbuja Leolut#GroundStation7.493,9.076Ground StationActiveNigeriaTanegashima Space Center - Osaki Range#LaunchPad130.970274,30.39953Launch PadInactiveJapanJeddah (1) Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaWhite Sands LC 94#LaunchPad-106.4589,34.2049Launch PadActiveUnited StatesAerospace Data Facility Colorado#GroundStation-104.7765,39.7173Ground StationActiveUnited StatesMaspalomas STDN MPLS#GroundStation-15.6338,27.762892Ground StationActiveSpainAdelaide Satellite Facility#GroundStation138.572,-34.8627Ground StationActiveAustraliaGagan Super RADOT#OpticalTrackingStation167.5376,9.286663Optical Tracking StationActiveMarshall IslandsLasham Satellite Ground Station#GroundStation-1.044,51.184Ground StationDemolishedUnited KingdomDSS 11 Goldstone STDN PIOD#GroundStation-116.84937706,35.38951828Ground StationInactiveUnited StatesBaikonur Cosmodrome LC 70#LaunchPad63.0963,46.0329Launch PadActiveKazakhstanKapustin Yar LC 107 2f#LaunchPad46.2958,48.5688Launch PadActiveRussiaKootwijk STDN KOOL#LaserStation5.80976658,52.1783975Laser StationActiveNetherlandsOkinawa USB F1#GroundStation127.90179,26.49815Ground StationActiveJapanIP-5 Tracking Station#GroundStation63.34,45.704Ground StationActiveKazakhstanDombarovsky Cosmodrome x19#LaunchPad59.844333,51.093509Launch PadActiveRussiaTonopah Test Range#LaunchSite-116.77,37.8Launch SiteActiveUnited StatesOverberg Earth Station 4m#GroundStation20.2206,-34.6202Ground StationActiveSouth AfricaKapustin Yar LC 107 2e#LaunchPad46.2949,48.5688Launch PadActiveRussiaGila River Space Fence#RadarStation-112.031,33.113Radar StationActiveUnited StatesAtlantic Test Range#GroundStation-76.378,38.296Ground StationActiveUnited StatesCape Canaveral AFS LC 12#LaunchPad-80.5421,28.4806Launch PadInactiveUnited StatesTranquillon Peak STDN VD3F#GroundStation-120.56111494,34.58304294Ground StationActiveUnited StatesEdwards AFB STDN FR2F#GroundStation-117.91187492,34.95773825Ground StationActiveUnited StatesBigen Island#LaunchSite171.0428,8.3646Launch SiteActiveMarshall IslandsLovell Telescope#GroundStation-2.308724,53.236548Ground StationActiveUnited KingdomSonmiani#LaunchSite66.75,25.2Launch SiteActivePakistanSt Hubert#GroundStation-73.39675,45.518836Ground StationActiveCanadaHartebeesthoek, Europ Star#GroundStation27.70793,-25.88549Ground StationActiveSouth AfricaSanta Ana#OpticalTrackingStation-64.624,-21.5963Optical Tracking StationActiveBoliviaPlesetsk Cosmodrome - LC 32-1#LaunchPad40.7872,62.9073Launch PadActiveRussiaDombarovsky Cosmodrome - x10#LaunchPad60.649859,50.985518Launch PadActiveRussiaCuiaba Station#GroundStation-56.0734,-15.5525Ground StationActiveBrazilPlesetsk Cosmodrome LC 43-4#LaunchPad40.4572,62.9288Launch PadActiveRussiaKapustin Yar LC 86 4a#LaunchPad46.295,48.5508Launch PadActiveRussiaEVCF STDN EVCS#GroundStation-80.576,28.486Ground StationActiveUnited StatesPoker Flat LC 6#LaunchPad-147.4621,65.1169Launch PadActiveUnited StatesCape Canaveral AFS LC 32#LaunchPad-80.5556,28.4537Launch PadInactiveUnited StatesBarking Sands - LC 10#LaunchPad-159.7816,22.0569Launch PadActiveUnited StatesSvobodny Cosmodrome - xx2#LaunchPad128.4085,51.7734Launch PadInactiveRussiaBangkok 1 Leolut#GroundStation100.5433,13.717166Ground StationActiveThailandEdwards AFB STDN EA3F#GroundStation-118.09132969,34.93811433Ground StationActiveUnited StatesStellenbosch University#GroundStation18.86608,-33.92826Ground StationActiveSouth AfricaBeale AFB PAVE PAWS#RadarStation-121.3506,39.1361Radar StationActiveUnited StatesKourou Station#GroundStation-52.80466242,5.25143694Ground StationActiveFrench GuianaOnsala Observatory 25m#GroundStation11.9178,57.39307Ground StationActiveSwedenSvalsat SG 1 STDN SG1S#GroundStation15.38969589,78.23072231Ground StationActiveNorwayTVF1#GroundStation-80.575793,28.4858933Ground StationActiveUnited StatesWoomera Test Range LA 4#LaunchPad136.5155,-30.9053Launch PadActiveAustraliaHawaii 2 HI2 Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesUsingen Station#GroundStation8.48,50.333Ground StationActiveGermanyWallops Flight Facility - LA 2 MLS#LaunchPad-75.4835,37.8375Launch PadActiveUnited StatesLegan Super RADOT#OpticalTrackingStation167.578487,8.981922Optical Tracking StationActiveMarshall IslandsAscension AAF Radar#RadarStation-14.412,-7.951Radar StationActiveUnited KingdomDSS 41 Island Lagoon#GroundStation136.8875,-31.382Ground StationDemolishedAustraliaLushan Station#GroundStation116,29.7Ground StationActiveChinaEsrange Rocket Range C#LaunchPad21.1029,67.8931Launch PadActiveSwedenRota SATCOM Terminal#GroundStation-6.367,36.63747Ground StationActiveSpainWoomera Test Range LA 1#LaunchPad136.5037,-30.9587Launch PadActiveAustraliaWhite Sands STDN WH5K#GroundStation-106.60855361,32.50124183Ground StationActiveUnited StatesJordan Lake Space Fence#RadarStation-86.2636,32.6588Radar StationActiveUnited StatesTaiyuan Satellite Launch Center - South Pad#LaunchPad111.60666,38.83538Launch PadActiveChinaTanegashima Space Center - Yoshinobu LC, Pad 2#LaunchPad130.975497,30.400919Launch PadActiveJapanBaikonur Cosmodrome - LC 60-6#LaunchPad64.0161,46.0188Launch PadActiveKazakhstanTanegashima Sounding Rocket Pad#LaunchPad130.962691,30.376847Launch PadActiveJapanWallops Flight Facility - LA 3B#LaunchPad-75.4725,37.8494Launch PadActiveUnited StatesDombarovsky Cosmodrome - x15#LaunchPad60.492582,51.053644Launch PadActiveRussiaOverberg Test Range#LaunchSite20.28,-34.61Launch SiteActiveSouth AfricaDubna Teleport#GroundStation37.251,56.738Ground StationActiveRussiaDombarovsky Cosmodrome x32#LaunchPad60.607974,51.241789Launch PadActiveRussiaJiuquan Satellite Launch Center - LA 3B#LaunchPad100.7803,41.1593Launch PadActiveChinaTel Aviv#GroundStation34.9,32Ground StationActiveIsraelJoint Defense Facility Nurrungar#GroundStation136.776545,-31.323342Ground StationInactiveAustraliaCroughton Station#GroundStation-1.187,51.987Ground StationActiveUnited KingdomBermuda STDN BDAQ#GroundStation-64.65344947,32.34794319Ground StationInactiveBermudaOrbcomm Hartebeesthoek A#GroundStation27.70566,-25.88736Ground StationActiveSouth AfricaCape Canaveral Air Force Station - LC 30#LaunchPad-80.5803,28.4393Launch PadInactiveUnited StatesNobeyama Radio Observatory#GroundStation138.472609,35.944518Ground StationActiveJapanJeddah 1 Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaDombarovsky Cosmodrome - x18#LaunchPad59.842142,51.094496Launch PadActiveRussiaNorth Pole Station USAK02#GroundStation-147.5003,64.8048Ground StationActiveUnited StatesRecife Leolut#GroundStation-34.925,-8.138333Ground StationActiveBrazilWoomera Test Range LA 3#LaunchPad136.5191,-30.93Launch PadActiveAustraliaSea Launch Mobile Platform#LaunchSite-154,0Launch SiteActiveInternationalPillar Point STDN PPTF#GroundStation-122.49869897,37.49769667Ground StationActiveUnited StatesSea Launch Mobile Platform#LaunchSite-154,0Launch SiteActiveInternationalBaikonur Cosmodrome - LC 194#LaunchPad63.301,45.854Launch PadActiveKazakhstanBurum Station#GroundStation6.2143,53.2842Ground StationActiveNetherlandsBarking Sands LC 19#LaunchPad-159.7813,22.0619Launch PadActiveUnited StatesWallops Flight Facility#LaunchSite-75.48,37.85Launch SiteActiveUnited StatesVernon Valley Teleport#GroundStation-74.5269,41.2018Ground StationActiveUnited StatesWoomera Test Range - LA 5B#LaunchPad136.4763,-30.9711Launch PadActiveAustraliaPalmachim Air Force Base - Shavit Pad#LaunchPad34.6802,31.8848Launch PadActiveIsraelCape Canaveral AFS LC 18A#LaunchPad-80.5623,28.4506Launch PadInactiveUnited StatesSouth Uist Missile Range#LaunchSite-7.4,57.36Launch SiteActiveUnited KingdomBrasilia Geolut#GroundStation-47.902666,-15.8572Ground StationActiveBrazilToulouse (1) Leolut#GroundStation1.4808,43.560666Ground StationActiveFranceGreen Bank Telescope 43m#GroundStation-79.83585,38.43773Ground StationActiveUnited StatesIstanbul ITU Station#GroundStation29.02729,41.1015Ground StationActiveTurkeyNashville Station#GroundStation-86.756,36.235Ground StationActiveUnited StatesCastle Rock Teleport#GroundStation-104.807303,39.276772Ground StationActiveUnited StatesRas Al Khaimah Spaceport#LaunchSite55.941,25.617Launch SitePlannedUnited Arab EmiratesBaikonur Cosmodrome LC 165#LaunchPad62.9185,45.9912Launch PadActiveKazakhstanBaikonur Cosmodrome LC 80-17#LaunchPad64.0198,46.0068Launch PadActiveKazakhstanL-3 Integrated Systems Test Range#GroundStation-96.056,33.07Ground StationActiveUnited StatesVandenberg AFB SLC 3E STDN WTEP#LaunchPad-120.59,34.64Launch PadActiveUnited StatesAndoya Rocket Range#LaunchSite16.021,69.294Launch SiteActiveNorwayAbastumani#OpticalTrackingStation42.82311,41.75444Optical Tracking StationActiveGeorgiaDombarovsky Cosmodrome x47#LaunchPad59.92692,51.600688Launch PadActiveRussiaThule STDN THUS#GroundStation-68.59901778,76.51629306Ground StationActiveGreenlandGuiana Space Center - ZL2 (STDN KR2P)#LaunchPad-52.77567156,5.23240672Launch PadInactiveFrench GuianaGreenbelt STDN BLTA#GroundStation-76.84193894,38.9982475Ground StationActiveUnited StatesIstana Station#GroundStation114.9231,4.8717Ground StationActiveBruneiWallops Flight Facility - LA 1#LaunchPad-75.4861,37.8352Launch PadActiveUnited StatesCape Canaveral AFS LC 14#LaunchPad-80.5471,28.4911Launch PadInactiveUnited StatesSGS Oakhanger Site STDN OTSS#GroundStation-0.89489706,51.11411733Ground StationActiveUnited KingdomALTAIR STDN KM2F#RadarStation167.47928833,9.39542881Radar StationActiveMarshall IslandsBangalore Geolut#GroundStation77.511666,13.0348Ground StationActiveIndiaLaurentides Station#GroundStation-74.5333,45.9444Ground StationActiveCanadaReach Stanley Station#GroundStation114.2722,22.2778Ground StationActiveChinaGagan#GroundStation167.53753,9.28686Ground StationActiveMarshall IslandsKapustin Yar - LC 107 2e#LaunchPad46.2949,48.5688Launch PadActiveRussiaHammaguira Beatrice#LaunchPad-3.0851,30.8601Launch PadActiveAlgeriaWoomera Test Range - LA 2#LaunchPad136.521,-30.9433Launch PadActiveAustraliaTRY ADD Radar#RadarStation73.573,45.8108Radar StationActiveKazakhstanKapustin Yar - LC 86 4b#LaunchPad46.295,48.5487Launch PadActiveRussiaBaikonur Cosmodrome LC 192#LaunchPad63.2995,46.0243Launch PadActiveKazakhstanPalmachim AFB Arrow II Launcher#LaunchPad34.7023,31.8859Launch PadActiveIsraelPoker Flat STDN WT2S#GroundStation-147.46154869,65.11673325Ground StationInactiveUnited StatesMaui GEODSS Cam2#OpticalTrackingStation-156.2575,20.7081Optical Tracking StationActiveUnited StatesVandenberg AFB SLC 6 STDN WT6P#LaunchPad-120.62609183,34.58163228Launch PadActiveUnited StatesAlcantara Launch Center - RAG Pad#LaunchPad-44.367,-2.3148Launch PadActiveBrazilPlesetsk Cosmodrome - LC 43-4#LaunchPad40.4572,62.9288Launch PadActiveRussiaDombarovsky Cosmodrome - x20#LaunchPad60.087307,51.096909Launch PadActiveRussiaDongara Station AUWA03#GroundStation115.3493,-29.04607Ground StationActiveAustraliaWhite Sands - LC 39#LaunchPad-106.2367,32.4161Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx6#LaunchPad60.52463,50.877274Launch PadActiveRussiaSATCOM Ground Terminal Facility#GroundStation-75.890866,45.349824Ground StationActiveCanadaDombarovsky Cosmodrome - xx5#LaunchPad59.595072,50.837656Launch PadActiveRussiaBoa Vista Station#GroundStation-60.67,2.82Ground StationActiveBrazilMalargue DSA 3#GroundStation-69.3983,-35.776Ground StationPlannedArgentinaGuiana Space Center - ZLV#LaunchPad-52.775,5.236Launch PadActiveFrench GuianaBeijing (2) Leolut#GroundStation116.42,39.908Ground StationActiveChinaScanEx Irkutsk Station#GroundStation104.30864,52.275261Ground StationActiveRussiaPleumeur-Bodou Station#GroundStation-3.521,48.786Ground StationInactiveFranceSolna#GroundStation17.96916,59.35483Ground StationActiveSwedenCape Canaveral AFS LC 29#LaunchPad-80.5778,28.4285Launch PadInactiveUnited StatesMILA STDN MIL3#GroundStation-80.69339994,28.50812389Ground StationInactiveUnited StatesMaspalomas Leolut#GroundStation-15.634,27.764Ground StationActiveSpainWellington 1 Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandTanegashima Yoshinobu LC Pad 2#LaunchPad130.975497,30.400919Launch PadActiveJapanNARSS Aswan Station#GroundStation32.91,24.07Ground StationActiveEgyptMojave Apollo Station#GroundStation-116.88759,35.33163Ground StationActiveUnited StatesEsrange Station ESSEX#GroundStation21.0630978,67.8904339Ground StationActiveSwedenWallops Flight Facility - LA 2 AML-1#LaunchPad-75.4828,37.8381Launch PadActiveUnited StatesWallops Island STDN DS87#GroundStation-75.47630453,37.92658986Ground StationActiveUnited StatesHartebeesthoek STDN HB5S#GroundStation27.70667183,-25.8869335Ground StationActiveSouth AfricaRecife Station#GroundStation-34.9,-8.04Ground StationActiveBrazilVery Large Array#GroundStation-107.618283,34.078749Ground StationActiveUnited StatesClear AFS PAVE PAWS#RadarStation-149.189444,64.288611Radar StationActiveUnited StatesWallops Flight Facility - LA 2 JUP#LaunchPad-75.482,37.8394Launch PadActiveUnited StatesXTAR 6m 1#GroundStation-15.63075,27.76268Ground StationActiveSpainCape Canaveral AFS LC 13#LaunchPad-80.5446,28.4859Launch PadInactiveUnited StatesBari Leolut#GroundStation16.8477,41.137666Ground StationActiveItalyDombarovsky Cosmodrome x39#LaunchPad60.472701,51.346817Launch PadActiveRussiaPlesetsk Cosmodrome LC 16-2#LaunchPad40.6834,62.96Launch PadActiveRussiaEglin AFB Launch Site#LaunchSite-85.34588,29.67729Launch SiteActiveUnited StatesFlorida 1 FL1 Leolut#GroundStation-80.3838,25.616Ground StationActiveUnited StatesNudol Station#GroundStation36.5033,56.15Ground StationActiveRussiaGEROC Ikonos Station#GroundStation11.28006,48.08407Ground StationActiveGermanyBaikonur Cosmodrome - LC 245#LaunchPad63.5271,45.8175Launch PadActiveKazakhstanSwedish-ESO Submillimetre Telescope#GroundStation-70.73234,-29.26345Ground StationInactiveChileSantiago Station AGO 5 STDN AG23#GroundStation-70.66731169,-33.15179414Ground StationActiveChileSylmar Station#GroundStation-118.4875,34.3152Ground StationActiveUnited StatesCape Canaveral Air Force Station - SLC 40 (STDN A40P)#LaunchPad-80.57719364,28.56198939Launch PadActiveUnited StatesBaikonur Cosmodrome - xx4#LaunchPad62.935495,46.079747Launch PadActiveKazakhstanVikram Sarabhai#LaunchSite76.869836,8.530116Launch SiteActiveIndiaWallops Island STDN WP2S#GroundStation-75.47441625,37.92806697Ground StationActiveUnited StatesWhite Sands - LC 36#LaunchPad-106.322,32.417Launch PadActiveUnited StatesLake Cowichan Station#GroundStation-124.07536,48.843501Ground StationActiveCanadaTutuila BRT STDN AMSJ#GroundStation-170.71941667,-14.33144444Ground StationActiveUnited StatesOffutt AFB SATCOM Terminal#GroundStation-95.911,41.1086Ground StationActiveUnited StatesCape Canaveral AFS LC 31#LaunchPad-80.5563,28.4519Launch PadInactiveUnited StatesCape Canaveral Air Force Station - SLC 20#LaunchPad-80.5567,28.5122Launch PadActiveUnited StatesMaadi Earth Station#GroundStation31.275,29.967Ground StationActiveEgyptDombarovsky Cosmodrome - x44#LaunchPad59.911557,51.54739Launch PadActiveRussiaZenith Antenna#RadarStation-71.49122,42.619Radar StationActiveUnited StatesSaipan STDN SIPQ#GroundStation145.79621667,15.24914583Ground StationActiveUnited StatesDSS 54 Robledo STDN DS54#GroundStation-4.25409683,40.42562167Ground StationActiveSpainCape Canaveral Air Force Station - LC 32#LaunchPad-80.5556,28.4537Launch PadInactiveUnited StatesBaikonur Cosmodrome LC 108#LaunchPad63.815,46.046Launch PadActiveKazakhstanBACC#GroundStation116.257092,40.071989Ground StationActiveChinaWhite Sands STDN WHSK#GroundStation-106.60855172,32.50101206Ground StationActiveUnited StatesDombarovsky Cosmodrome x15#LaunchPad60.492582,51.053644Launch PadActiveRussiaPlesetsk Cosmodrome LC 132-1#LaunchPad40.8686,62.8833Launch PadActiveRussiaCape Canaveral Air Force Station - LC 21#LaunchPad-80.5403,28.4606Launch PadInactiveUnited StatesPoker Flat LC 3#LaunchPad-147.4851,65.1299Launch PadActiveUnited StatesPlesetsk Cosmodrome LC 43-3#LaunchPad40.4501,62.9273Launch PadActiveRussiaWeilheim STDN WU1S#GroundStation11.0853025,47.88006944Ground StationActiveGermanyKoganei STDN KOGL#LaserStation139.48827494,35.71027478Laser StationActiveJapanEsrange Rocket Range S#LaunchPad21.1054,67.8932Launch PadActiveSwedenSvalsat SG 2 STDN KLMS#GroundStation15.39813814,78.23022167Ground StationActiveNorwayNCTS Diego Garcia#GroundStation72.363,-7.266Ground StationActiveUnited KingdomNortheastern Space Radio Observatory#GroundStation-38.42599,-3.87812Ground StationActiveBrazilHTS STDN HT2S#GroundStation-158.26227883,21.56897181Ground StationActiveUnited StatesGreenbelt Leolut#GroundStation-76.841,38.998666Ground StationActiveUnited StatesDombarovsky Cosmodrome - x22#LaunchPad59.634472,51.11475Launch PadActiveRussiaWallops Island STDN WL3S#GroundStation-75.46043669,37.94579497Ground StationActiveUnited StatesWallops Flight Facility - LA 3#LaunchPad-75.4725,37.8506Launch PadActiveUnited StatesMartlesham Heath Station#GroundStation1.2874,52.0597Ground StationActiveUnited KingdomHawaii 2 (HI2) Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesMaui GEODSS, xx1#OpticalTrackingStation-156.2575,20.7085Optical Tracking StationActiveUnited StatesHartebeesthoek Ku 13m#GroundStation27.70745,-25.88547Ground StationActiveSouth AfricaDombarovsky Cosmodrome - xx1#LaunchPad59.655376,50.658373Launch PadActiveRussiaSanya Ground Station#GroundStation109.311,18.313Ground StationActiveChinaClarksburg Teleport#GroundStation-77.270401,39.218075Ground StationActiveUnited StatesToulouse Geolut#GroundStation1.480833,43.5587Ground StationActiveFranceSvobodny Cosmodrome - xx9#LaunchPad128.3656,51.837Launch PadInactiveRussiaDombarovsky Cosmodrome - x16#LaunchPad59.484369,51.06922Launch PadActiveRussiaBaikonur Cosmodrome - LC 162-36#LaunchPad63.0668,46.0323Launch PadActiveKazakhstanHong Kong 2 Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaDiego Garcia GEODSS, xx1#OpticalTrackingStation72.45203,-7.41162Optical Tracking StationActiveUnited KingdomBeijing 1 Leolut#GroundStation116.42,39.908Ground StationActiveChinaKapustin Yar - LC 107 2f#LaunchPad46.2958,48.5688Launch PadActiveRussiaFort Belvoir SATCOM Terminal#GroundStation-77.145,38.726Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 12#LaunchPad-80.5421,28.4806Launch PadInactiveUnited StatesBaikonur Cosmodrome LC 250#LaunchPad63.3049,46.0083Launch PadActiveKazakhstanDSS 33 Tidbinbilla#GroundStation148.98304,-35.400549Ground StationDemolishedAustraliaAscension Island BRT STDN ACNJ#GroundStation-14.39076792,-7.91791558Ground StationActiveUnited KingdomJonathan Dickinson STDN JDIQ#GroundStation-80.10820406,26.98299969Ground StationActiveUnited StatesKiruna Station 13m#GroundStation20.96688,67.858428Ground StationActiveSwedenKennedy Space Center#LaunchSite-80.62,28.62Launch SiteActiveUnited StatesJakarta Leolut#GroundStation106.656,-6.126166Ground StationActiveIndonesiaVandenberg AFB SLC 3W STDN WT3P#LaunchPad-120.59285703,34.64367528Launch PadActiveUnited StatesKapustin Yar LC 86 4b#LaunchPad46.295,48.5487Launch PadActiveRussiaWallops Flight Facility LA 4#LaunchPad-75.4701,37.8508Launch PadActiveUnited StatesSan Nicolas#LaunchSite-119.52208,33.27981Launch SiteActiveUnited StatesDombarovsky Cosmodrome - x14#LaunchPad59.958431,51.036288Launch PadActiveRussiaAnhueng#LaunchSite126.47,36.7Launch SiteActiveSouth KoreaBlaavand Station#GroundStation8.1137,55.5571Ground StationActiveDenmarkBari Geolut#GroundStation16.847,41.137Ground StationActiveItalyChung-Li Station#GroundStation121.18705,24.96783Ground StationActiveTaiwanCape Canaveral Air Force Station - LC 29#LaunchPad-80.5778,28.4285Launch PadInactiveUnited StatesDish Antenna Facility#GroundStation-122.179604,37.40854Ground StationActiveUnited StatesOrbcomm Arcade A#GroundStation-78.382119,42.524867Ground StationActiveUnited StatesGerman Space Operations Center#GroundStation11.28123,48.08716Ground StationActiveGermanyCRISP Station#GroundStation103.774,1.2977Ground StationActiveSingaporeDombarovsky Cosmodrome - x33#LaunchPad60.142215,51.248931Launch PadActiveRussiaKACST Station#GroundStation46.642,24.71Ground StationActiveSaudi ArabiaUsuda#GroundStation138.3627,36.1325Ground StationActiveJapanCape Canaveral Air Force Station - SLC 17A (STDN A17P)#LaunchPad-80.56492617,28.44716106Launch PadActiveUnited StatesJiuquan Satellite Launch Center - LA 2B#LaunchPad100.3132,41.3061Launch PadActiveChinaMaui GEODSS, xx2#OpticalTrackingStation-156.2578,20.7081Optical Tracking StationActiveUnited StatesComcast Denver Station#GroundStation-104.9386,39.5793Ground StationActiveUnited StatesSvobodny Cosmodrome xx7#LaunchPad128.301,51.823Launch PadInactiveRussiaDombarovsky Cosmodrome - x21#LaunchPad59.576451,51.102299Launch PadActiveRussiaTaiyuan South Pad#LaunchPad111.60666,38.83538Launch PadActiveChinaKapustin Yar - xx4#LaunchPad46.299018,48.615791Launch PadActiveRussiaSondrestrom Radar Facility#RadarStation-50.9459,66.9856Radar StationActiveGreenlandAlgiers Leolut#GroundStation3.381,36.753333Ground StationActiveAlgeriaBiscarosse - BESA#LaunchPad-1.2335,44.3917Launch PadActiveFranceSedlec-Prcice Station#GroundStation14.5247,49.5919Ground StationActiveCzech RepublicJonathan Dickinson STDN JD2Y#GroundStation-80.10756081,26.98222986Ground StationActiveUnited StatesSvalRak#LaunchSite11.850278,78.931389Launch SiteActiveNorwayWhite Sands#LaunchSite-106.3,32.4Launch SiteActiveUnited StatesSantiago Station AGO 3 (STDN AGO3)#GroundStation-70.666403,-33.15110747Ground StationActiveChileKwajalein X-band Radar#RadarStation167.733133,8.724319Radar StationActiveMarshall IslandsEl Palomar Leolut#GroundStation-58.6,-34.6Ground StationActiveArgentinaSary Shagan Radar#RadarStation74.52,46.62Radar StationActiveKazakhstan \ No newline at end of file +MACRES Ground Receiving Station#GroundStation102.339,3.463Ground StationActiveMalaysiaALCOR STDN KMRQ#RadarStation167.48284936,9.39859958Radar StationActiveMarshall IslandsGuiana Space Center - ZL3 (STDN KR3P)#LaunchPad-52.75178147,5.24036314Launch PadActiveFrench GuianaAsan Teleport#GroundStation127.018,36.741Ground StationActiveSouth KoreaPillar Point STDN PP2F#GroundStation-122.49668389,37.49685436Ground StationActiveUnited StatesDiego Garcia GEODSS Cam3#OpticalTrackingStation72.45237,-7.41186Optical Tracking StationActiveUnited KingdomPlesetsk Cosmodrome#LaunchSite40.6,62.9Launch SiteActiveRussiaPhillips Hill STDN WH9F#GroundStation-106.13210839,33.44521867Ground StationActiveUnited StatesSantiago Station AGO 6#GroundStation-70.6701611,-33.1506778Ground StationActiveChileTanegashima#LaunchSite130.976,30.401Launch SiteActiveJapanBaikonur Cosmodrome - LC 90-19#LaunchPad62.9144,46.0863Launch PadActiveKazakhstanMid-Atlantic Regional Spaceport#LaunchSite-75.49,37.832Launch SiteActiveUnited StatesDombarovsky Cosmodrome x44#LaunchPad59.911557,51.54739Launch PadActiveRussiaFort Bliss#LaunchSite-106.1526,32.0737Launch SiteActiveUnited StatesSemnan Launch Center - xx3#LaunchPad53.952,35.239Launch PadActiveIranEarth Observation Center#GroundStation139.348734,36.002563Ground StationActiveJapanFillmore Teleport#GroundStation-118.894041,34.405802Ground StationActiveUnited StatesDSS 23 Goldstone#GroundStation-116.87295,35.33951Ground StationInactiveUnited StatesGTSA#GroundStation144.8560742,13.6151942Ground StationActiveUnited StatesAntigua STDN AN8S#GroundStation-61.77428983,17.13662489Ground StationActiveAntigua and BarbudaLandsat Station STDN SF1S#GroundStation-96.62251461,43.73607264Ground StationActiveUnited StatesZhanyi Station#GroundStation103.82,25.6Ground StationActiveChinaWallops Island STDN WP3S#GroundStation-75.47418797,37.92836117Ground StationActiveUnited StatesRAL Station 2m#GroundStation-1.31419,51.57159Ground StationActiveUnited KingdomMaryland LSE Leolut#GroundStation-76.93,38.850333Ground StationActiveUnited StatesSemnan Launch Center xx2#LaunchPad53.921,35.2347Launch PadActiveIranBaikonur Cosmodrome LC 200-40#LaunchPad63.0379,46.0364Launch PadActiveKazakhstanNakhodka Leolut#GroundStation132.7907,42.858666Ground StationPlannedRussiaXichang Satellite Launch Center - LA 4#LaunchPad102.0232,28.2495Launch PadActiveChinaBarking Sands LC 14#LaunchPad-159.7788,22.058Launch PadActiveUnited StatesDombarovsky Cosmodrome - x24#LaunchPad59.59691,51.150886Launch PadActiveRussiaWhite Sands Space STDN WSSH#GroundStation-106.46472222,32.88027778Ground StationActiveUnited StatesFort Detrick#GroundStation-77.416,39.443Ground StationActiveUnited StatesBristow Station#GroundStation-77.5732,38.7837Ground StationActiveUnited StatesTromsoe Leolut#GroundStation18.9403,69.662333Ground StationActiveNorwayTonghae Satellite Launching Ground#LaunchSite129.665994,40.855652Launch SiteActiveNorth KoreaDombarovsky Cosmodrome - x29#LaunchPad60.765442,51.201923Launch PadActiveRussiaVandenberg AFB SLC 1E#LaunchPad-120.6263,34.7561Launch PadInactiveUnited StatesSevastopol Radar#RadarStation33.386,44.579Radar StationActiveUkraineWallops Flight Facility - LA 2#LaunchPad-75.4834,37.8376Launch PadActiveUnited StatesOkno Complex#OpticalTrackingStation69.224,38.282Optical Tracking StationActiveTajikistanKapustin Yar - xx7#LaunchPad45.716196,48.812023Launch PadActiveRussiaKwajalein STDN KMRT#GroundStation167.71852428,8.71954542Ground StationInactiveMarshall IslandsXTAR 6m 2#GroundStation-15.63057,27.76272Ground StationActiveSpainWallops Flight Facility LA 1#LaunchPad-75.4861,37.8352Launch PadActiveUnited StatesPoker Flat LEO-T STDN LE1S#GroundStation-147.462255,65.11685167Ground StationActiveUnited StatesVLBA North Liberty#GroundStation-91.574133,41.771425Ground StationActiveUnited StatesWhite Sands - SULF#LaunchPad-106.7364,33.7212Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 108#LaunchPad63.815,46.046Launch PadActiveKazakhstanBaikonur Cosmodrome LC 246#LaunchPad63.4236,45.7656Launch PadActiveKazakhstanMILA STDN MILA#GroundStation-80.69275272,28.50816025Ground StationInactiveUnited StatesMeck Island#LaunchSite167.727188,9.006637Launch SiteActiveMarshall IslandsVLBA Mauna Kea#GroundStation-155.455733,19.801415Ground StationActiveUnited StatesVandenberg STDN WULY#GroundStation-120.55598472,34.65541336Ground StationActiveUnited StatesCape Canaveral AFS SLC 17B STDN B17P#LaunchPad-80.56564944,28.44579042Launch PadActiveUnited StatesGRGT STDN GWMJ#GroundStation144.84080419,13.58675639Ground StationActiveUnited StatesJicamarca Radio Observatory#RadarStation-76.874,-11.951Radar StationActivePeruCarteret Station#GroundStation-74.2166,40.58Ground StationActiveUnited StatesSomis Station#GroundStation-118.996,34.325Ground StationActiveUnited StatesEchoStar New Braunfels Station#GroundStation-98.0635,29.7599Ground StationActiveUnited StatesKashima Space Research Center, 13m B#GroundStation140.66298,35.95631Ground StationActiveJapanOttawa 1 Geolut#GroundStation-75.674,45.329Ground StationActiveCanadaSvobodny Cosmodrome x10#LaunchPad128.3323,51.877Launch PadInactiveRussiaSvalbard STDN S22S#GroundStation15.381773,78.232908Ground StationActiveNorwayCamp Parks Communications Annex#GroundStation-121.881,37.7331Ground StationActiveUnited StatesKapustin Yar PL1#LaunchPad46.2621,48.4116Launch PadActiveRussiaBaikonur Cosmodrome - xx5#LaunchPad62.932319,46.081306Launch PadActiveKazakhstanMillstone Hill Steerable Antenna#RadarStation-71.4912,42.61959Radar StationActiveUnited StatesDombarovsky Cosmodrome - xx2#LaunchPad59.791895,50.681452Launch PadActiveRussiaKamisaibara Spaceguard Center#RadarStation133.9413,35.3125Radar StationActiveJapanMauritius#GroundStation57.52,-20.17Ground StationActiveMauritiusOrbcomm San Luis B#GroundStation-65.18199,-33.83795Ground StationActiveArgentinaDombarovsky Cosmodrome - x37#LaunchPad60.728749,51.301802Launch PadActiveRussiaOverberg Test Range#LaunchSite20.28,-34.61Launch SiteActiveSouth AfricaBermuda STDN BDAA#GroundStation-64.65878961,32.35115261Ground StationInactiveBermudaSemnan Launch Center xx4#LaunchPad53.955,35.258Launch PadActiveIranBaikonur Cosmodrome LC 172#LaunchPad63.092,46.065Launch PadActiveKazakhstanCape Canaveral AFS LC 3#LaunchPad-80.5363,28.4662Launch PadInactiveUnited StatesXichang LA 3#LaunchPad102.0271,28.2455Launch PadActiveChinaNatal Station STDN NATL#LaserStation-35.16455019,-5.92780617Laser StationInactiveBrazilBaikonur Cosmodrome - LC 200-39#LaunchPad63.032,46.0399Launch PadActiveKazakhstanWallops Island STDN HR1S#GroundStation-75.46113556,37.94549917Ground StationActiveUnited StatesWoomera Test Range LA 5B#LaunchPad136.4763,-30.9711Launch PadActiveAustraliaGreen Bank#GroundStation-79.83977,38.43297Ground StationActiveUnited StatesMukachevo Radar#RadarStation22.708,48.378Radar StationActiveUkraineHolloman SLED#LaunchPad-106.1484,32.9263Launch PadActiveUnited StatesNevada Test Site Area 26#LaunchSite-116.114,36.771Launch SiteActiveUnited StatesBaikonur Cosmodrome - LC 70#LaunchPad63.0963,46.0329Launch PadActiveKazakhstanEl Arenosillo#LaunchSite-6.733,37.098Launch SiteActiveSpainBaikonur Cosmodrome - LC 81-23#LaunchPad62.9785,46.074Launch PadActiveKazakhstanKeelung (1) Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanPushkino Phased-array Radar#RadarStation37.769,56.173Radar StationActiveRussiaAuScope VLBI Hobart#GroundStation147.4381,-42.80557Ground StationActiveAustraliaSvalsat SG 5#GroundStation15.389123,78.230127Ground StationActiveNorwayQueensland Transmitter#RadarStation144.1454,-23.658Radar StationActiveAustraliaAlice Springs BRT STDN AL2J#GroundStation133.8825985,-23.75881044Ground StationActiveAustraliaSGS Oakhanger Site Annex#GroundStation-0.899,51.1095Ground StationActiveUnited KingdomBaikonur Cosmodrome LC 110L#LaunchPad63.3049,45.9647Launch PadActiveKazakhstanBaikonur Cosmodrome LC 45-2#LaunchPad63.6532,45.9433Launch PadActiveKazakhstanJiuquan Satellite Launch Center - LA 3#LaunchPad100.78,41.0152Launch PadActiveChinaBaikonur Cosmodrome - LC 241#LaunchPad63.4558,45.8583Launch PadActiveKazakhstanGrand Bahama Island STDN GBIQ#GroundStation-78.34784006,26.615642Ground StationInactiveBahamasOrbcomm Kijal A#GroundStation103.47377,4.34888Ground StationActiveMalaysiaMisawa Air Base Security Hill#GroundStation141.3225,40.72Ground StationActiveJapanMoscow Leolut#GroundStation37.7227,55.743333Ground StationPlannedRussiaWallops Flight Facility - LA 3A#LaunchPad-75.4726,37.848Launch PadActiveUnited StatesPoker Flat STDN PFTS#GroundStation-147.46329083,65.11679308Ground StationActiveUnited StatesEtisalat Jebel Ali Teleport#GroundStation55.1247,25.0306Ground StationActiveUnited Arab EmiratesPlesetsk Cosmodrome - LC 133-1#LaunchPad40.8468,62.8868Launch PadActiveRussiaKapustin Yar xx6#LaunchPad45.8152,48.783061Launch PadActiveRussiaAnkara (2) Leolut#GroundStation32.9897,40.140666Ground StationActiveTurkeySvobodny Cosmodrome - xx7#LaunchPad128.301,51.823Launch PadInactiveRussiaOnizuka Control Node xx2#GroundStation-122.029075,37.403492Ground StationInactiveUnited StatesCSTARS_West_11m#GroundStation-80.38489,25.61403Ground StationActiveUnited StatesMaspalomas (1) Geolut#GroundStation-15.634,27.764Ground StationActiveSpainGalenki RT-70#GroundStation131.757,44.0161Ground StationActiveRussiaSugar Grove Station#GroundStation-79.28,38.516Ground StationActiveUnited StatesEnnylabegan#GroundStation167.618495,8.797797Ground StationActiveMarshall IslandsFinegayan SATCOM Site#GroundStation144.849827,13.583618Ground StationActiveUnited StatesHawley Earth Station#GroundStation-75.13,41.46417Ground StationActiveUnited StatesKapustin Yar - xx1#LaunchPad46.318006,48.484051Launch PadActiveRussiaEsrange Station KSX#GroundStation21.0556028,67.8887061Ground StationActiveSwedenVTS STDN VTSS#GroundStation-120.50184844,34.82261656Ground StationActiveUnited StatesEchoStar Gilbert Station#GroundStation-111.8142,33.3655Ground StationActiveUnited StatesTriunfo Pass Station#GroundStation-118.8964,34.0808Ground StationActiveUnited StatesSocorro GEODSS, xx2#OpticalTrackingStation-106.65962,33.81725Optical Tracking StationActiveUnited StatesSaskatoon#GroundStation-106.62587,52.139427Ground StationActiveCanadaBaikonur Cosmodrome - LC 60-7#LaunchPad64.0173,46.0181Launch PadActiveKazakhstanHolloman NATIV#LaunchPad-106.0753,32.8866Launch PadActiveUnited StatesAscension Island STDN ACN3#GroundStation-14.32710328,-7.95488247Ground StationActiveUnited KingdomDombarovsky Cosmodrome x24#LaunchPad59.59691,51.150886Launch PadActiveRussiaBaikonur Cosmodrome - LC 103#LaunchPad63.4448,45.9523Launch PadActiveKazakhstanGoonhilly Satellite Earth Station#GroundStation-5.181944,50.048056Ground StationInactiveUnited KingdomEffelsberg Radio Telescope#GroundStation6.883417,50.525Ground StationActiveGermanyWallops Flight Facility LA 5#LaunchPad-75.4681,37.8529Launch PadActiveUnited StatesSouth Point Station USHI01#GroundStation-155.6633318,19.0139525Ground StationActiveUnited StatesUchinoura Lambda Pad#LaunchPad131.079125,31.251908Launch PadActiveJapanKapustin Yar START Pioner#LaunchPad46.3009,48.6149Launch PadActiveRussiaMount Pleasant 14m#GroundStation147.44165,-42.8034Ground StationActiveAustraliaHartebeesthoek STDN HBK3#GroundStation27.7126,-25.88672778Ground StationActiveSouth AfricaLake Kickapoo Space Fence#RadarStation-98.7628,33.5464Radar StationActiveUnited StatesEsrange Rocket Range - N#LaunchPad21.1064,67.8933Launch PadActiveSwedenSantiago Station AGO 3 STDN AGU3#GroundStation-70.666403,-33.15110747Ground StationActiveChileDakar STDN DAKS#GroundStation-17.12876689,14.72476222Ground StationActiveSenegalHartebeesthoek 5m#GroundStation27.70665,-25.88649Ground StationActiveSouth AfricaPhilippine Space Comm Center#GroundStation121.294,14.585Ground StationActivePhilippinesWenchang#LaunchSite111.01,19.68Launch SitePlannedChinaMaui GEODSS, xx3#OpticalTrackingStation-156.2575,20.7081Optical Tracking StationActiveUnited StatesBochum Observatory#GroundStation7.19257,51.42697Ground StationActiveGermanyMaui GEODSS Cam1#OpticalTrackingStation-156.2578,20.7081Optical Tracking StationActiveUnited StatesOttawa 2 Geolut#GroundStation-75.674333,45.3438Ground StationActiveCanadaHartebeesthoek Ka 13m#GroundStation27.70746,-25.88813Ground StationActiveSouth AfricaSANAE IV#GroundStation-2.8403,-71.6725Ground StationActiveAntarcticaKwajalein GPS Station#GroundStation167.731327,8.723044Ground StationActiveMarshall IslandsTowi Alsaman#GroundStation55.832,25.251Ground StationActiveUnited Arab EmiratesWhite Sands STDN ST3K#GroundStation-106.61208889,32.542675Ground StationActiveUnited StatesAlcantara Launch Center#LaunchSite-44.367,-2.317Launch SiteActiveBrazilSouth Point Station USHI02 STDN U2HS#GroundStation-155.66294408,19.01379344Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 1E#LaunchPad-120.6263,34.7561Launch PadInactiveUnited StatesCape Canaveral Air Force Station - SLC 37A#LaunchPad-80.568,28.534Launch PadInactiveUnited StatesPlesetsk Cosmodrome LC 35#LaunchPad40.575422,62.927806Launch PadActiveRussiaSednaya#GroundStation36.3635,33.6676Ground StationActiveSyriaDSS 16 Goldstone STDN DS16#GroundStation-116.87364975,35.34153939Ground StationInactiveUnited StatesEagle Vision 6#GroundStation-86.66,34.64Ground StationActiveUnited StatesKapustin Yar LC 107 2d#LaunchPad46.2959,48.5695Launch PadActiveRussiaVentspils#GroundStation21.857778,57.558056Ground StationActiveLatviaVandenberg Air Force Base - SLC 2W (STDN WTRP)#LaunchPad-120.62237833,34.75563222Launch PadActiveUnited StatesBaikonur Cosmodrome LC 81-24#LaunchPad62.9848,46.0709Launch PadActiveKazakhstanCape Canaveral AFS#LaunchSite-80.57,28.5Launch SiteActiveUnited StatesGuam 1 GU1 Leolut#GroundStation144.939,13.578333Ground StationActiveUnited StatesESTEC#GroundStation4.41994,52.21838Ground StationActiveNetherlandsKapustin Yar LC 107 2b#LaunchPad46.2959,48.5609Launch PadActiveRussiaSvobodny Cosmodrome#LaunchSite128.4,51.8Launch SiteInactiveRussiaPik Terskol Observatory#OpticalTrackingStation42.49939,43.27631Optical Tracking StationActiveRussiaOrroral Valley STDN ORR3#GroundStation148.95702322,-35.62789289Ground StationRelocatedAustraliaKapustin Yar LC 107 1a#LaunchPad46.298,48.5992Launch PadActiveRussiaKashima Space Research Center 13m B#GroundStation140.66298,35.95631Ground StationActiveJapanUchinoura#LaunchSite131.08,31.251Launch SiteActiveJapanAnkara Geolut#GroundStation32.99,40.1403Ground StationActiveTurkeyPlesetsk Cosmodrome LC 133-3#LaunchPad40.8504,62.887Launch PadActiveRussiaKijal Earth Station#GroundStation103.475,4.439Ground StationActiveMalaysiaPlesetsk Cosmodrome LC 32-1#LaunchPad40.7872,62.9073Launch PadActiveRussiaSantiago Station AGO 3 (STDN AGU3)#GroundStation-70.666403,-33.15110747Ground StationActiveChileQabala Phased-array Radar#RadarStation47.7955,40.8679Radar StationActiveAzerbaijanGCSB Waihopai#GroundStation173.738889,-41.576389Ground StationActiveNew ZealandGRGT STDN GWMS#GroundStation144.8409295,13.58801786Ground StationActiveUnited StatesBarreira do Inferno Launch Center#LaunchSite-35.1613,-5.9236Launch SiteActiveBrazilDiego Garcia GEODSS, xx3#OpticalTrackingStation72.45237,-7.41186Optical Tracking StationActiveUnited KingdomMichelstadt Station#GroundStation8.971934,49.711812Ground StationInactiveGermanySHAR-1#GroundStation80.19631,13.67484Ground StationActiveIndiaMt Lemmon STDN MTLS#GroundStation-110.78945064,32.44213656Ground StationActiveUnited StatesPunta Arenas Station#GroundStation-70.857,-52.938Ground StationActiveChileBermuda STDN BDAL#LaserStation-64.65610619,32.35381689Laser StationInactiveBermudaSan Nicolas Island STDN SN2F#GroundStation-119.52074281,33.247685Ground StationActiveUnited StatesLackland Air Force Base#GroundStation-98.590371,29.363209Ground StationActiveUnited StatesBaikonur Cosmodrome#LaunchSite63.3,46Launch SiteActiveKazakhstanNorth Pole Station USAK01 (STDN USAS)#GroundStation-147.50021417,64.80424111Ground StationActiveUnited StatesOoty Radio Telescope#GroundStation76.666,11.384Ground StationActiveIndiaWeilheim STDN WU2S#GroundStation11.08361889,47.88119889Ground StationActiveGermanyBaikonur Cosmodrome - LC 243#LaunchPad63.737,45.8549Launch PadActiveKazakhstanLongyearbyen EISCAT Radar#RadarStation16.0289,78.1531Radar StationActiveNorwayKapustin Yar - LC 107 2b#LaunchPad46.2959,48.5609Launch PadActiveRussiaKerguelen Island STDN KERS#GroundStation70.257282,-49.352906Ground StationActiveFranceGrand Turk Island STDN GTKL#LaserStation-71.13194483,21.46052522Laser StationActiveUnited KingdomHartebeesthoek STDN HB33#GroundStation27.70744722,-25.88642778Ground StationActiveSouth AfricaIP-1 Tracking Station#GroundStation63.334,45.908Ground StationActiveKazakhstanCSTARS#GroundStation-80.3844,25.6138Ground StationActiveUnited StatesDombarovsky Cosmodrome - x11#LaunchPad59.567713,51.020939Launch PadActiveRussiaDombarovsky Cosmodrome x28#LaunchPad59.635083,51.193164Launch PadActiveRussiaEUMETSAT Maspalomas#GroundStation-15.63295,27.76264Ground StationActiveSpainDombarovsky Cosmodrome x43#LaunchPad59.801475,51.526422Launch PadActiveRussiaCarnarvon Tracking Station#GroundStation113.721,-24.905Ground StationDemolishedAustraliaAlgonquin#GroundStation-78.0727,45.9554Ground StationActiveCanadaDSS 28 Goldstone#GroundStation-116.778711,35.23831Ground StationInactiveUnited StatesArequipa STDN AREL#LaserStation-71.49305469,-16.46570239Laser StationActivePeruKapustin Yar xx5#LaunchPad45.781879,48.781927Launch PadActiveRussiaBarking Sands - LC 42#LaunchPad-159.7727,22.0682Launch PadActiveUnited StatesDombarovsky Cosmodrome - x31#LaunchPad60.606992,51.241105Launch PadActiveRussiaMSSC 15in Aux Telescope#OpticalTrackingStation-156.2576,20.70849Optical Tracking StationActiveUnited StatesSimferopol TNA-400#GroundStation33.890256,45.052703Ground StationActiveUkraineMalabar Test Facility#OpticalTrackingStation-80.684,28.025Optical Tracking StationActiveUnited StatesEsrange Rocket Range - S#LaunchPad21.1054,67.8932Launch PadActiveSwedenGuiana Space Center - ZL3#LaunchPad-52.7685,5.2393Launch PadActiveFrench GuianaDombarovsky Cosmodrome x21#LaunchPad59.576451,51.102299Launch PadActiveRussiaPCMC Radar#RadarStation80.19886,13.67944Radar StationActiveIndiaCape Canaveral Air Force Station - LC 26B#LaunchPad-80.5712,28.4433Launch PadInactiveUnited StatesCDAS#GroundStation116.27,40.05Ground StationActiveChinaDombarovsky Cosmodrome x30#LaunchPad59.85002,51.207054Launch PadActiveRussiaDombarovsky Cosmodrome xx6#LaunchPad60.52463,50.877274Launch PadActiveRussiaGuam STDN GWM3#GroundStation144.73681531,13.31068944Ground StationDemolishedUnited StatesRAL Station 4m#GroundStation-1.43673,51.14283Ground StationActiveUnited KingdomEsrange Station ETX STDN KU1S#GroundStation21.06565472,67.88955833Ground StationActiveSwedenUniversity of Hawaii STDN HAWQ#GroundStation-157.8864,21.3161Ground StationActiveUnited StatesOrbcomm Arcade B#GroundStation-78.382119,42.524096Ground StationActiveUnited StatesKiruna Station KIR-2 13m#GroundStation20.96688,67.858428Ground StationActiveSwedenEdwards AFB STDN EA2F#GroundStation-117.93056167,34.97045389Ground StationActiveUnited StatesPoker Flat LC 5#LaunchPad-147.4832,65.1292Launch PadActiveUnited StatesNy-Alesund#GroundStation11.870967,78.929071Ground StationActiveNorwayWashington International Teleport#GroundStation-77.164891,38.794257Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 8#LaunchPad-120.6324,34.5762Launch PadActiveUnited StatesMaspalomas (2) Geolut#GroundStation-15.634,27.764Ground StationActiveSpainAtlanta STDN ATDQ#GroundStation-84.10862994,33.93088417Ground StationActiveUnited StatesDongara Station AUWA01 STDN USDS#GroundStation115.348678,-29.045772Ground StationActiveAustraliaPoint Mugu LC 1#LaunchPad-119.1215,34.0996Launch PadActiveUnited StatesTromso Satellite Station#GroundStation18.9408,69.6625Ground StationActiveNorwayDombarovsky Cosmodrome x23#LaunchPad60.675969,51.146793Launch PadActiveRussiaMaui STDN MA2C#OpticalTrackingStation-156.2575,20.70057325Optical Tracking StationActiveUnited StatesWettzel STDN WETL#LaserStation12.87800803,49.14441731Laser StationActiveGermanyCape Canaveral Air Force Station#LaunchSite-80.57,28.5Launch SiteActiveUnited StatesClear AFS BMEWS#RadarStation-149.1897,64.3002Radar StationActiveUnited StatesMalaysia Space Centre#GroundStation101.508,2.784Ground StationActiveMalaysiaBaikonur Cosmodrome - LC 67-22#LaunchPad63.7073,45.9895Launch PadActiveKazakhstanIssus-Aussaguel Station#GroundStation1.497,43.429Ground StationActiveFranceWallops Flight Facility LA 4 MAST#LaunchPad-75.4702,37.8508Launch PadActiveUnited StatesPlesetsk Cosmodrome PU 11#LaunchPad40.4953,62.8978Launch PadActiveRussiaOrbcomm Albury A#GroundStation146.53351,-36.03145Ground StationActiveAustraliaBiscarosse BS#LaunchPad-1.2675,44.2882Launch PadInactiveFranceCape Canaveral Air Force Station - LC 18B#LaunchPad-80.562,28.449Launch PadInactiveUnited StatesGuiana Space Center ZL3#LaunchPad-52.7685,5.2393Launch PadActiveFrench GuianaAntigua Radar STDN ANTQ#RadarStation-61.7925125,17.14365111Radar StationActiveAntigua and BarbudaPoker Flat LC 2#LaunchPad-147.4857,65.1299Launch PadActiveUnited StatesTokai University#GroundStation130.87,32.835Ground StationActiveJapanKapustin Yar - xx3#LaunchPad46.300125,48.540529Launch PadActiveRussiaHong Kong (2) Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaPaliivka Station#GroundStation30.5069,46.6339Ground StationActiveUkraineKapustin Yar - SAM#LaunchPad45.7346,48.8055Launch PadActiveRussiaDSS 14 Goldstone STDN DS14#GroundStation-116.88953822,35.42590086Ground StationActiveUnited StatesVLBA St Croix#GroundStation-64.583631,17.756578Ground StationActiveUnited StatesPoker Flat - LC 4#LaunchPad-147.4827,65.1303Launch PadActiveUnited StatesVandenberg AFB SLC 2W STDN WTRP#LaunchPad-120.62237833,34.75563222Launch PadActiveUnited StatesOrbcomm Hartebeesthoek B#GroundStation27.70568,-25.88812Ground StationActiveSouth AfricaDombarovsky Cosmodrome x18#LaunchPad59.842142,51.094496Launch PadActiveRussiaVandenberg Air Force Base - SLC 3W (STDN WT3P)#LaunchPad-120.59285703,34.64367528Launch PadActiveUnited StatesBisei Spaceguard Center#OpticalTrackingStation133.545,34.672Optical Tracking StationActiveJapanGRGT STDN GW2K#GroundStation144.84087447,13.58758828Ground StationActiveUnited StatesFinegayan SATCOM Site#GroundStation144.849827,13.583618Ground StationActiveUnited StatesKodiak Launch Complex#LaunchSite-152.3393,57.4352Launch SiteActiveUnited StatesWhite Sands STDN STGS#GroundStation-106.61208894,32.54245167Ground StationActiveUnited StatesLucknow Leolut#GroundStation80.9573,26.913333Ground StationActiveIndiaBarking Sands#LaunchSite-159.78,22.06Launch SiteActiveUnited StatesWeipa STDN KRCS#GroundStation141.93066667,-12.694Ground StationActiveAustraliaDombarovsky Cosmodrome - x26#LaunchPad59.74824,51.154839Launch PadActiveRussiaDombarovsky Cosmodrome - x12#LaunchPad59.808673,51.022624Launch PadActiveRussiaVandenberg Air Force Base - SLC 3E (STDN WTEP)#LaunchPad-120.59,34.64Launch PadActiveUnited StatesGreen Bank 85-2 Telescope#GroundStation-79.8525,38.42577Ground StationActiveUnited StatesSubmarine Launch Platform - Barents Sea#LaunchSite34.2,69.5Launch SiteActiveRussiaWettzell#GroundStation12.8772,49.145Ground StationActiveGermanyBaikonur Cosmodrome xx4#LaunchPad62.935495,46.079747Launch PadActiveKazakhstanBaikonur Cosmodrome - LC 104#LaunchPad63.4197,45.9875Launch PadActiveKazakhstanVillafranca VIL-1#GroundStation-3.951582,40.442565Ground StationInactiveSpainGreen Bank Telescope 14m#GroundStation-79.8224,38.43326Ground StationActiveUnited StatesEsrange Station ESTC STDN KU2S#GroundStation21.06044833,67.8831825Ground StationActiveSwedenHerstmonceaux STDN HERL#LaserStation0.33612414,50.86738069Laser StationActiveUnited KingdomWallops Island STDN WL3F#GroundStation-75.51143536,37.85636525Ground StationActiveUnited StatesEsrange Station ESX (STDN KIXS)#GroundStation21.063398,67.878157Ground StationActiveSwedenMU Radar#RadarStation136.1055,34.854Radar StationActiveJapanBarking Sands LC 1#LaunchPad-159.777,22.058Launch PadActiveUnited StatesSouthbury Station#GroundStation-73.2889,41.4514Ground StationActiveUnited StatesSantiago Station AGO 4 (STDN AG33)#GroundStation-70.66830756,-33.15147853Ground StationActiveChilePlesetsk Cosmodrome - LC 32-2#LaunchPad40.7895,62.9057Launch PadActiveRussiaDSS 62 Cebreros#GroundStation-4.36788,40.45311Ground StationDemolishedSpainVandenberg Air Force Base - SLC 2E#LaunchPad-120.619201,34.751617Launch PadDemolishedUnited StatesKTS#GroundStation-152.178,57.5986Ground StationDemolishedUnited StatesDombarovsky Cosmodrome x40#LaunchPad60.372543,51.379286Launch PadActiveRussiaMaspalomas 2 Geolut#GroundStation-15.634,27.764Ground StationActiveSpainBaikonur Cosmodrome LC 244#LaunchPad63.6346,45.8403Launch PadActiveKazakhstanDombarovsky Cosmodrome - x13#LaunchPad59.689541,51.030416Launch PadActiveRussiaAdour 2 Radar STDN ADRQ#RadarStation-52.74840111,5.20912725Radar StationActiveFrench GuianaOrbcomm Albury B#GroundStation146.53351,-36.03223Ground StationActiveAustraliaVTSB#GroundStation-120.505403,34.8256235Ground StationActiveUnited StatesUchinoura Space Center, 20m#GroundStation131.080567,31.256784Ground StationActiveJapanBaikonur Cosmodrome - LC 81-24#LaunchPad62.9848,46.0709Launch PadActiveKazakhstanWallops Island STDN WD4F#GroundStation-75.511439,37.85636639Ground StationActiveUnited StatesBaikonur Cosmodrome LC 109#LaunchPad63.4452,45.9525Launch PadActiveKazakhstanKeelung (2) Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanTelkom SA Earth Station#GroundStation27.71,-25.91Ground StationActiveSouth AfricaVTSA#GroundStation-120.501852,34.8226094Ground StationActiveUnited StatesBaikonur Cosmodrome - xx3#LaunchPad63.481448,45.945516Launch PadActiveKazakhstanZimmerwald#GroundStation7.478169,46.886794Ground StationActiveSwitzerlandNHSB#GroundStation-71.630319,42.9447544Ground StationActiveUnited StatesElephant Butte Space Fence#RadarStation-106.9972,33.4431Radar StationActiveUnited StatesMid-Atlantic Regional Spaceport#LaunchSite-75.49,37.832Launch SiteActiveUnited StatesGrand Bahama Island STDN GBIY#GroundStation-78.29909119,26.62544656Ground StationActiveBahamasMoores Valley Station#GroundStation-123.2875,45.3425Ground StationDemolishedUnited StatesJiuquan xx2#LaunchPad100.305083,41.283173Launch PadActiveChinaSouth Uist Range Radar#GroundStation-7.446834,57.617444Ground StationActiveUnited KingdomKapustin Yar xx3#LaunchPad46.300125,48.540529Launch PadActiveRussiaIssus-Aussaguel Station#GroundStation1.497,43.429Ground StationActiveFranceAscension#LaunchSite-14.4147,-7.9748Launch SiteActiveUnited KingdomAyios Nikolaos Station#GroundStation33.888,35.09423Ground StationActiveCyprusAscension Island BRT STDN AC2J#GroundStation-14.39076792,-7.91791558Ground StationActiveUnited KingdomHammaguira Brigitte-A#LaunchPad-3.0081,30.8712Launch PadActiveAlgeriaHartebeesthoek STDN HBKS#GroundStation27.712,-25.887Ground StationActiveSouth AfricaGRAVES Receiver#RadarStation5.5346,44.0715Radar StationActiveFranceDSS 13 Goldstone STDN VEND#GroundStation-116.794459,35.24716425Ground StationActiveUnited StatesGalenki 32m#GroundStation131.7561,44.0204Ground StationActiveRussiaCape Canaveral AFS LC 18B#LaunchPad-80.562,28.449Launch PadInactiveUnited StatesSan Nicolas Island STDN SNIF#GroundStation-119.52009433,33.24697758Ground StationActiveUnited StatesHaystack Auxilliary Radar#RadarStation-71.4872,42.62283Radar StationActiveUnited StatesVLBA Brewster#GroundStation-119.683278,48.131228Ground StationActiveUnited StatesDombarovsky Cosmodrome - x42#LaunchPad59.972508,51.521988Launch PadActiveRussiaWallops Flight Facility LA 2 JUP#LaunchPad-75.482,37.8394Launch PadActiveUnited StatesMid-Atlantic Regional Spaceport - Launch Pad 0-A#LaunchPad-75.4882,37.8338Launch PadActiveUnited StatesGilmore Creek STDN ULA4#GroundStation-147.52128289,64.97659575Ground StationActiveUnited StatesKashima Space Research Center 26m#GroundStation140.6627,35.9541Ground StationDemolishedJapanUchinoura Space Center - Lambda Pad#LaunchPad131.079125,31.251908Launch PadActiveJapanMizusawa VLBI Observatory#GroundStation141.1326,39.1335Ground StationActiveJapanOmelek Island#LaunchSite167.743322,9.048224Launch SiteActiveMarshall IslandsBaikonur Cosmodrome LC 60-8#LaunchPad64.0183,46.0174Launch PadActiveKazakhstanHiroshima Institute of Technology#GroundStation132.3761,34.3678Ground StationActiveJapanTHEOS Control and Receiving Station#GroundStation100.932849,13.103529Ground StationActiveThailandZhongshan Station#GroundStation76.378,-69.379Ground StationActiveAntarcticaDSS 44 Honeysuckle Creek#GroundStation148.97784,-35.58319Ground StationRelocatedAustraliaDombarovsky Cosmodrome - x35#LaunchPad60.858975,51.26491Launch PadActiveRussiaDombarovsky Cosmodrome#LaunchSite60,51Launch SiteActiveRussiaBiscarosse BESA#LaunchPad-1.2335,44.3917Launch PadActiveFranceAllen Telescope Array#GroundStation-121.47,40.817Ground StationActiveUnited StatesDulles Station#GroundStation-77.42795,39.01449Ground StationActiveUnited StatesSvobodny Cosmodrome - xx1#LaunchPad128.3102,51.747Launch PadInactiveRussiaRAF Fylingdales BMEWS#RadarStation-0.6701,54.3618Radar StationActiveUnited KingdomWoomera Test Range - LA 4#LaunchPad136.5155,-30.9053Launch PadActiveAustraliaBaikonur Cosmodrome LC 163#LaunchPad63.172,46.002Launch PadActiveKazakhstanAndover Station#GroundStation-70.699,44.633Ground StationActiveUnited StatesThuraya Station#GroundStation55.8285,25.2433Ground StationActiveUnited Arab EmiratesGEROC Ikonos Station#GroundStation11.28006,48.08407Ground StationActiveGermanySuffa RT-70 Radio Telescope#GroundStation68.448,39.624Ground StationActiveUzbekistanSocorro GEODSS, xx3#OpticalTrackingStation-106.65968,33.81703Optical Tracking StationActiveUnited StatesBaikonur Cosmodrome xx5#LaunchPad62.932319,46.081306Launch PadActiveKazakhstanBiscarosse - BLB#LaunchPad-1.262,44.3659Launch PadActiveFranceAlaska 2 (AK2) Leolut#GroundStation-147.5177,64.9735Ground StationActiveUnited StatesOuargla Leolut#GroundStation5.49,31.88Ground StationActiveAlgeriaEsrange Station ESX STDN KIXS#GroundStation21.063398,67.878157Ground StationActiveSwedenMaryland 2 Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesBaikonur Cosmodrome LC 103#LaunchPad63.4448,45.9523Launch PadActiveKazakhstanWellington (1) Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandWallops Flight Facility - LA 4 ML#LaunchPad-75.4698,37.851Launch PadActiveUnited StatesJiuquan LA 3B#LaunchPad100.7803,41.1593Launch PadActiveChinaColomb-Bechar#LaunchSite-2.255,31.693Launch SiteActiveAlgeriaWallops Flight Facility LA 2 HAD#LaunchPad-75.4826,37.8383Launch PadActiveUnited StatesAnkara (1) Leolut#GroundStation32.9897,40.140833Ground StationActiveTurkeyTCSA#GroundStation-0.90636355,51.1167229Ground StationActiveUnited KingdomBiscarosse - BP#LaunchPad-1.2396,44.3755Launch PadActiveFranceNakhodka Station#GroundStation132.7906,42.8584Ground StationActiveRussiaPlesetsk Cosmodrome - LC 16-2#LaunchPad40.6834,62.96Launch PadActiveRussiaPlesetsk Cosmodrome - LC 35#LaunchPad40.575422,62.927806Launch PadActiveRussiaGilmore Creek STDN GILD#GroundStation-147.49800019,64.97850925Ground StationActiveUnited StatesKapustin Yar LC 86 4c#LaunchPad46.297,48.5481Launch PadActiveRussiaOrbcomm Maghreb A#GroundStation-7.64361,33.04828Ground StationActiveMoroccoTidbinbilla STDN RTKS#GroundStation148.98260486,-35.40474494Ground StationActiveAustraliaPrimrose Lake#LaunchSite-110.05,54.75Launch SiteActiveCanadaNetaji Station#GroundStation88.4276,22.946Ground StationActiveIndiaRobins AFB PAVE PAWS#RadarStation-83.56936,32.58115Radar StationInactiveUnited StatesSantiago Station AGO 4 STDN AG33#GroundStation-70.66830756,-33.15147853Ground StationActiveChileAscension Island STDN ASNS#GroundStation-14.33333333,-7.91666717Ground StationActiveUnited KingdomJiuquan Satellite Launch Center - xx2#LaunchPad100.305083,41.283173Launch PadActiveChinaBretagne 2 Radar STDN BREQ#RadarStation-52.30954428,4.94887892Radar StationActiveFrench GuianaVandenberg Air Force Base - SLC 10E#LaunchPad-120.6213,34.7626Launch PadInactiveUnited StatesTTSC#GroundStation-68.605031,76.5157036Ground StationActiveGreenlandWhite Sands LC 39#LaunchPad-106.2367,32.4161Launch PadActiveUnited StatesArecibo Observatory#RadarStation-66.753083,18.3435Radar StationActiveUnited StatesOrbcomm Wenatchee A#GroundStation-120.17509,47.551685Ground StationActiveUnited StatesWallops Flight Facility LA 4 HAD#LaunchPad-75.4696,37.8511Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 75#LaunchPad63.1983,45.9657Launch PadActiveKazakhstanDombarovsky Cosmodrome - x46#LaunchPad60.082438,51.588487Launch PadActiveRussiaDombarovsky Cosmodrome xx3#LaunchPad59.597872,50.756297Launch PadActiveRussiaEsrange Station ESX#GroundStation21.0634472,67.8781431Ground StationActiveSwedenKumsan Station#GroundStation127.4892,36.125Ground StationActiveSouth KoreaKennedy Space Center - LC 39A (STDN A39P)#LaunchPad-80.60411653,28.60827486Launch PadActiveUnited StatesScanEx Moscow Station#GroundStation37.58411,55.73606Ground StationActiveRussiaMaiquetia 2 Leolut#GroundStation-66.9865,10.599Ground StationPlannedVenezuelaOTC Carnarvon#GroundStation113.7049,-24.8691Ground StationActiveAustraliaVandenberg AFB SLC 5#LaunchPad-120.6247,34.608Launch PadInactiveUnited StatesEglin AFB STDN EG3F#GroundStation-86.798012,30.42166558Ground StationInactiveUnited StatesVLBA Pie Town#GroundStation-108.119183,34.301003Ground StationActiveUnited StatesDombarovsky Cosmodrome xx7#LaunchPad59.73193,50.883614Launch PadActiveRussiaBaikonur Cosmodrome LC 51#LaunchPad63.3409,45.9239Launch PadActiveKazakhstanSanta Paula Station#GroundStation-119.0734,34.4021Ground StationActiveUnited StatesWallops Island STDN WLPF#GroundStation-75.48508908,37.84133972Ground StationActiveUnited StatesEagle Vision 2#GroundStation-104.71,38.82Ground StationActiveUnited StatesLibreville Station STDN LBVS#GroundStation9.67530028,0.35462978Ground StationActiveGabonGreen Bank 85-3 Telescope#GroundStation-79.84341,38.42959Ground StationActiveUnited StatesHTS STDN HTSS#GroundStation-158.24208956,21.56227219Ground StationActiveUnited StatesHyderabad#GroundStation78.188333,17.028611Ground StationActiveIndiaLAPAN Station#GroundStation119.65,-3.978Ground StationActiveIndonesiaYellowknife Site#GroundStation-114.47924,62.48044Ground StationActiveCanadaBaikonur Cosmodrome LC 191-66#LaunchPad63.1966,45.9698Launch PadActiveKazakhstanPoker Flat LC 4#LaunchPad-147.4827,65.1303Launch PadActiveUnited StatesCape Canaveral AFS LC 1#LaunchPad-80.5375,28.465Launch PadInactiveUnited StatesKunming Station#GroundStation102.79583,25.02734Ground StationActiveChinaWake Island#LaunchSite166.618,19.29Launch SiteActiveUnited StatesChangchun Station#GroundStation125.324,43.817Ground StationActiveChinaBiak#GroundStation136.1074,-1.19Ground StationActiveIndonesiaMaryland 1 Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesBaikonur Cosmodrome LC 67-21#LaunchPad63.705,45.9893Launch PadActiveKazakhstanJiuquan LA 2A#LaunchPad100.3165,41.3088Launch PadActiveChinaRiverside Teleport#GroundStation-117.087843,33.795862Ground StationActiveUnited StatesIrkutsk Radar#RadarStation103.259,52.877Radar StationActiveRussiaRAL Station 12m STDN RALS#GroundStation-1.31146389,51.572026Ground StationActiveUnited KingdomTehran Station#GroundStation51.408,35.78Ground StationActiveIranUchinoura 10m#GroundStation131.084654,31.255675Ground StationActiveJapanOnizuka Control Node xx1#GroundStation-122.028987,37.403778Ground StationInactiveUnited StatesSubmarine Launch Platform Barents Sea#LaunchSite34.2,69.5Launch SiteActiveRussiaMaryland (1) Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesPoker Flat - LC 2#LaunchPad-147.4857,65.1299Launch PadActiveUnited StatesNy-Alesund Station#GroundStation11.883535,78.927718Ground StationActiveNorwayAlcantara Launch Center MRL Pad#LaunchPad-44.3671,-2.3167Launch PadActiveBrazilSocorro GEODSS Cam2#OpticalTrackingStation-106.65962,33.81725Optical Tracking StationActiveUnited StatesWoomera Test Range LA 6A#LaunchPad136.4394,-31.074Launch PadActiveAustraliaERIS#GroundStation-88.263568,18.544696Ground StationActiveMexicoWhite Sands STDN WH3K#GroundStation-106.60855158,32.50046228Ground StationActiveUnited StatesNevada Test Site Area 26#LaunchSite-116.114,36.771Launch SiteActiveUnited StatesKapustin Yar LC 107 1b#LaunchPad46.298,48.5969Launch PadActiveRussiaJiuquan SLS 2#LaunchPad100.2983,40.9607Launch PadActiveChinaEsrange Station TTC (STDN KICS)#GroundStation21.060769,67.884232Ground StationActiveSwedenShcholkovo#GroundStation37.9576,55.9506Ground StationActiveRussiaDombarovsky Cosmodrome xx9#LaunchPad59.551097,50.972694Launch PadActiveRussiaEsrange Rocket Range#LaunchSite21.105,67.893Launch SiteActiveSwedenAlcantara Launch Center VLS Pad#LaunchPad-44.3675,-2.3184Launch PadActiveBrazilCasey Station#GroundStation110.52871,-66.28125Ground StationActiveAntarcticaNegev#LaunchSite34.9728,31.02335Launch SiteActiveIsraelDombarovsky Cosmodrome x25#LaunchPad59.524663,51.153297Launch PadActiveRussiaLannion STDN LANS#GroundStation-3.47,48.75141536Ground StationActiveFranceCape Canaveral AFS LC 30#LaunchPad-80.5803,28.4393Launch PadInactiveUnited StatesByalalu 32m#GroundStation77.36898,12.90314Ground StationActiveIndiaSeoul#GroundStation126.9997,37.5664Ground StationActiveSouth KoreaQuantico#GroundStation-77.3198,38.4912Ground StationActiveUnited StatesConcepcion#GroundStation-73.025036,-36.842772Ground StationActiveChilePlesetsk Cosmodrome LC 41-1#LaunchPad40.529,62.9405Launch PadDemolishedRussiaCape Canaveral AFS LC 9#LaunchPad-80.5594,28.4522Launch PadInactiveUnited StatesEOC Antenna 4#GroundStation139.35,36.0026Ground StationActiveJapanKwajalein STDN KMQF#RadarStation167.726622,8.72163539Radar StationActiveMarshall IslandsLuxembourg Teleport#GroundStation6.114,49.579Ground StationActiveLuxembourgNCTS Bahrain#GroundStation50.61,26.2073Ground StationActiveBahrainKapustin Yar LC 107 2h#LaunchPad46.2949,48.5617Launch PadActiveRussiaWallops Flight Facility#LaunchSite-75.48,37.85Launch SiteActiveUnited StatesCape Canaveral AFS LC 26B#LaunchPad-80.5712,28.4433Launch PadInactiveUnited StatesMerritt Island STDN MIMF#GroundStation-80.68279381,28.62594319Ground StationActiveUnited StatesSantiago Geolut#GroundStation-70.7,-33.489Ground StationActiveChileAhemad Station#GroundStation78.1042,30.1778Ground StationActiveIndiaWhite Sands STDN WHSF#GroundStation-106.36980742,32.35804211Ground StationActiveUnited StatesJeddah (2) Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaDSS 65 Robledo STDN DS65#GroundStation-4.25069889,40.42720636Ground StationActiveSpainFirepond Laser#LaserStation-71.4923,42.61757Laser StationActiveUnited StatesPoker Flat Station PF1 STDN DX2S#GroundStation-147.4311625,65.11783389Ground StationActiveUnited StatesKapustin Yar - PL1#LaunchPad46.2621,48.4116Launch PadActiveRussiaAPL 10m#GroundStation-76.89825,39.169Ground StationActiveUnited StatesDSS 45 Tidbinbilla STDN DS45#GroundStation148.97768564,-35.39845769Ground StationActiveAustraliaKashi Ground Station#GroundStation75.929,39.505Ground StationActiveChinaCape Canaveral AFS LC 25#LaunchPad-80.575,28.431Launch PadInactiveUnited StatesCape Canaveral AFS SLC 36B STDN B36P#LaunchPad-80.54095408,28.46836775Launch PadDemolishedUnited StatesWallops Island STDN WL4F#GroundStation-75.511439,37.85636639Ground StationActiveUnited StatesGreen Bank Telescope 12m#GroundStation-79.83131,38.43724Ground StationActiveUnited StatesAlgiers Geolut#GroundStation3.381,36.7533Ground StationActiveAlgeriaNCTAMS PAC#GroundStation-157.995,21.52Ground StationActiveUnited StatesGlobus II#RadarStation31.1271,70.3671Radar StationActiveNorwayKapustin Yar xx1#LaunchPad46.318006,48.484051Launch PadActiveRussiaNenoksa Test Range#LaunchSite39.22,64.646Launch SiteActiveRussiaMSSC AEOS 3.7m#OpticalTrackingStation-156.2569,20.7081Optical Tracking StationActiveUnited StatesNanhai Station#GroundStation113.14,23.03Ground StationActiveChinaSilver Lake Space Fence#RadarStation-91.0211,33.145Radar StationActiveUnited StatesKourou Station STDN KRUP#GroundStation-52.80582497,5.25185486Ground StationActiveFrench GuianaSvobodny Cosmodrome xx9#LaunchPad128.3656,51.837Launch PadInactiveRussiaBaikonur Cosmodrome - LC 80-17#LaunchPad64.0198,46.0068Launch PadActiveKazakhstanOrbcomm St Johns A#GroundStation-109.554917,34.456282Ground StationActiveUnited StatesMSSC RAVEN#OpticalTrackingStation-156.25745,20.7085Optical Tracking StationActiveUnited StatesASF 10m#GroundStation-147.849467,64.859482Ground StationActiveUnited StatesSvobodny Cosmodrome - xx5#LaunchPad128.23218,51.80532Launch PadInactiveRussiaDombarovsky Cosmodrome - x30#LaunchPad59.85002,51.207054Launch PadActiveRussiaAtlanta STDN ATDS#GroundStation-84.10862994,33.93088417Ground StationActiveUnited StatesBaikonur Cosmodrome - xx2#LaunchPad63.462445,45.94176Launch PadActiveKazakhstanWallops Island (STDN WAPS)#GroundStation-75.4765225,37.92492556Ground StationActiveUnited StatesBlossom Point Tracking Facility#GroundStation-77.086,38.431Ground StationActiveUnited StatesCape Canaveral STDN CN2F#GroundStation-80.59056164,28.52887219Ground StationInactiveUnited StatesLackland AFB#GroundStation-98.590371,29.363209Ground StationActiveUnited StatesFranklin Station#GroundStation-74.5757,41.1167Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 195#LaunchPad63.2127,45.783Launch PadActiveKazakhstanWallops Flight Facility - LA 5#LaunchPad-75.4681,37.8529Launch PadActiveUnited StatesYevpatoria North Station#GroundStation33.169,45.221Ground StationActiveUkraineCyberjaya Station#GroundStation101.6583,2.9348Ground StationActiveMalaysiaDombarovsky Cosmodrome x27#LaunchPad59.967627,51.168438Launch PadActiveRussiaHumacao Station#GroundStation-65.788,18.1494Ground StationActiveUnited StatesCombe Martin Geolut#GroundStation-4.047166,51.1675Ground StationActiveUnited KingdomDombarovsky Cosmodrome - x38#LaunchPad60.163782,51.338805Launch PadActiveRussiaMullach Sgr Radar Station#GroundStation-8.580436,57.806607Ground StationActiveUnited KingdomAtlanta STDN ATLS#GroundStation-84.10836667,33.93086667Ground StationActiveUnited StatesWestern Australian Receiver#RadarStation122.008,-28.327Radar StationActiveAustraliaProspect Harbor Naval Satellite Operations Station#GroundStation-68.013,44.404Ground StationActiveUnited StatesCape Town Leolut#GroundStation18.5,-33.88Ground StationActiveSouth AfricaLarge Millimeter Telescope#RadarStation-97.3149,18.9858Radar StationActiveMexicoAerospace Data Facility Southwest#GroundStation-106.6,32.5Ground StationActiveUnited StatesIbaraki Satellite Control Center#GroundStation140.373,36.532Ground StationActiveJapanCobra Dane Radar#RadarStation174.0914,52.7373Radar StationActiveUnited StatesKrona 30J6 Complex#OpticalTrackingStation41.226213,43.718337Optical Tracking StationActiveRussiaBaikonur Cosmodrome - xx1#LaunchPad63.462718,45.939593Launch PadActiveKazakhstanFairbanks STDN UL23#GroundStation-147.51806567,64.97240711Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 90-20#LaunchPad62.9167,46.0855Launch PadActiveKazakhstanAPL 5m#GroundStation-76.89839,39.16821Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 193#LaunchPad63.389,45.9532Launch PadActiveKazakhstanEagle Vision 1#GroundStation7.6,49.44Ground StationActiveGermanyTilla#LaunchSite73.29608,33.3961Launch SiteActivePakistanVandenberg AFB SLC 4E#LaunchPad-120.61059,34.632067Launch PadInactiveUnited StatesSingapore Leolut#GroundStation103.988,1.352Ground StationActiveSingaporeWallops Flight Facility LA 4 ML#LaunchPad-75.4698,37.851Launch PadActiveUnited StatesYamaguchi Station#GroundStation131.557,34.217Ground StationActiveJapanWhite Sands BRT STDN WHSJ#GroundStation-106.61196575,32.50628147Ground StationActiveUnited StatesHawaii Station STDN HAWS#GroundStation-155.663,19.01358372Ground StationActiveUnited StatesOrbcomm Curacao#GroundStation-69.1538,12.3814Ground StationActiveNetherlandsKiruna Station KIR-1 15m STDN KI2S#GroundStation20.96434169,67.85712517Ground StationActiveSwedenWhite Sands LC 38#LaunchPad-106.2796,32.4179Launch PadActiveUnited StatesHolloman#LaunchSite-106.07,32.88Launch SiteActiveUnited StatesBaikonur Cosmodrome LC 60-6#LaunchPad64.0161,46.0188Launch PadActiveKazakhstanXichang#LaunchSite102.029,28.246Launch SiteActiveChinaWeilheim 11m#GroundStation11.08538,47.88118Ground StationActiveGermanySyowa Station STDN SYOQ#GroundStation39.59015389,-69.00609644Ground StationActiveAntarcticaKorou SIGINT Station#GroundStation-52.7007,5.1664Ground StationActiveFrench GuianaGreenbelt Test BRT STDN BLTJ#GroundStation-76.83792783,39.00270353Ground StationActiveUnited StatesXichang LA 1#LaunchPad102.0292,28.2474Launch PadActiveChinaMopra Observatory#GroundStation149.0996,-31.2678Ground StationActiveAustraliaWhite Sands LC 32#LaunchPad-106.4069,32.4068Launch PadActiveUnited StatesJiamusi Station#GroundStation130.32,46.8Ground StationActiveChinaOrbcomm Rio de Janiero A#GroundStation-42.87275,-22.69619Ground StationActiveBrazilTokyo Institute of Technology#GroundStation139.68495,35.60084Ground StationActiveJapanAlcantara Launch Center - VLS Pad#LaunchPad-44.3675,-2.3184Launch PadActiveBrazilWhite Sands LC 33#LaunchPad-106.443,32.3396Launch PadActiveUnited StatesTeide Observatory#GroundStation-16.51,28.3Ground StationActiveSpainWoomera Test Range LA 9#LaunchPad136.4871,-30.9031Launch PadActiveAustraliaDombarovsky Cosmodrome - x27#LaunchPad59.967627,51.168438Launch PadActiveRussiaHammaguira Bacchus#LaunchPad-3.1241,30.8565Launch PadActiveAlgeriaUssuriysk Observatory#OpticalTrackingStation132.16583,43.69917Optical Tracking StationActiveRussiaBaikonur Cosmodrome - LC 191-66#LaunchPad63.1966,45.9698Launch PadActiveKazakhstanTTS STDN TT2S#GroundStation-68.59885831,76.51536442Ground StationActiveGreenland5N24 Argun Radar#RadarStation73.5721,45.808Radar StationActiveKazakhstanLibya Station#GroundStation13.236,32.34Ground StationActiveLibyaCanadian Forces Station Leitrim#GroundStation-75.5867,45.3377Ground StationActiveCanadaSemnan Launch Center - xx2#LaunchPad53.921,35.2347Launch PadActiveIranWallops Island STDN WPSS#GroundStation-75.47630453,37.92658986Ground StationActiveUnited StatesBiscarosse BLB#LaunchPad-1.262,44.3659Launch PadActiveFranceTelegraph Hill#GroundStation-14.3915,-7.9743Ground StationActiveUnited KingdomPoker Flat - LC 5#LaunchPad-147.4832,65.1292Launch PadActiveUnited StatesNaval SATCOM Facility Northwest#GroundStation-76.267222,36.559444Ground StationActiveUnited StatesVandenberg Air Force Base - SLC 4W#LaunchPad-120.6154,34.6331Launch PadInactiveUnited StatesKapustin Yar LC 107 2c#LaunchPad46.2949,48.5695Launch PadActiveRussiaCape Canaveral Air Force Station - SLC 36A (STDN A36P)#LaunchPad-80.53772211,28.47143831Launch PadDemolishedUnited StatesTelesat Calgary Teleport#GroundStation-114.016,51.053Ground StationActiveCanadaWallops Flight Facility - LA 2 HAD#LaunchPad-75.4826,37.8383Launch PadActiveUnited StatesEchoStar Orange Station#GroundStation-74.2203,40.784Ground StationActiveUnited StatesDSS 49 Parkes#GroundStation148.263524,-32.998278Ground StationActiveAustraliaPlesetsk Cosmodrome LC 133-1#LaunchPad40.8468,62.8868Launch PadActiveRussiaAPT Satellite Control Center#GroundStation114.188,22.453Ground StationActiveChinaTelesat Montreal Teleport#GroundStation-73.5504,45.5224Ground StationActiveCanadaWallops Flight Facility LA 2#LaunchPad-75.4834,37.8376Launch PadActiveUnited StatesMoron MOSS#OpticalTrackingStation-5.5884,37.1516Optical Tracking StationActiveSpainBaikonur Cosmodrome - LC 175-2#LaunchPad62.987,46.0512Launch PadActiveKazakhstanCape Canaveral AFS SLC 20#LaunchPad-80.5567,28.5122Launch PadActiveUnited StatesKiritimati Downrange Tracking Station#GroundStation-157.44821,2.04613Ground StationActiveKiribatiSentosa Station#GroundStation103.836,1.248Ground StationActiveSingaporePoint Mugu - LC 1#LaunchPad-119.1215,34.0996Launch PadActiveUnited StatesWhite Sands STDN WSGT#GroundStation-106.60854583,32.5013695Ground StationActiveUnited StatesKapustin Yar - xx6#LaunchPad45.8152,48.783061Launch PadActiveRussiaBrisbane Station#GroundStation153.1312,-27.5539Ground StationActiveAustraliaCape Canaveral Air Force Station - LC 14#LaunchPad-80.5471,28.4911Launch PadInactiveUnited StatesSouth Uist Missile Range#LaunchSite-7.4,57.36Launch SiteActiveUnited KingdomWallops Flight Facility LA 3B#LaunchPad-75.4725,37.8494Launch PadActiveUnited StatesBermuda STDN BDA3#GroundStation-64.65788833,32.35126753Ground StationInactiveBermudaEchoStar Monee Station#GroundStation-87.7764,41.4684Ground StationActiveUnited StatesPenteli Geolut#GroundStation23.883,38.0808Ground StationActiveGreeceDSS 43 Tidbinbilla STDN DS43#GroundStation148.98126731,-35.40242422Ground StationActiveAustraliaBaikonur Cosmodrome - LC 51#LaunchPad63.3409,45.9239Launch PadActiveKazakhstanSmolino A-35 Anti-missile Site#RadarStation36.482,55.35Radar StationActiveRussiaWallops Flight Facility - LA 1 AML#LaunchPad-75.4871,37.8352Launch PadActiveUnited StatesDombarovsky Cosmodrome x13#LaunchPad59.689541,51.030416Launch PadActiveRussiaBaikonur Cosmodrome LC 75#LaunchPad63.1983,45.9657Launch PadActiveKazakhstanGuiana Space Center - Diamant Pad#LaunchPad-52.7524,5.2325Launch PadInactiveFrench GuianaGilmore Creek STDN GLAS#GroundStation-147.51283867,64.97367197Ground StationActiveUnited StatesEik Station#GroundStation6.4675,58.5383Ground StationActiveNorwayGran Canaria Drop Zone#LaunchSite-15.3,27Launch SiteActiveSpainKashima Space Research Center 13m A#GroundStation140.66245,35.95629Ground StationActiveJapanAlaska 2 AK2 Leolut#GroundStation-147.5177,64.9735Ground StationActiveUnited StatesAnkara#GroundStation32.86,39.93Ground StationActiveTurkeyPlesetsk Cosmodrome - PU 11#LaunchPad40.4953,62.8978Launch PadActiveRussiaCamp Zama#GroundStation139.39804,35.50014Ground StationActiveJapanEagle Vision 4#GroundStation-80.8,33.92Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 164#LaunchPad63.064,45.9445Launch PadActiveKazakhstanSantiago Station AGO 5 (STDN AG23)#GroundStation-70.66731169,-33.15179414Ground StationActiveChileGrimstad Ground Station#GroundStation8.35,58.33Ground StationActiveNorwayEsrange Station ETX (STDN KU1S)#GroundStation21.06565472,67.88955833Ground StationActiveSwedenSt Thomas STDN ST1F#GroundStation-64.97321647,18.35725333Ground StationActiveUnited StatesGuiana Space Center ZLV#LaunchPad-52.775,5.236Launch PadActiveFrench GuianaLongovilo Station#GroundStation-71.401,-33.956Ground StationActiveChileEsrange Station ESC#GroundStation21.0599183,67.8843522Ground StationActiveSwedenLivermore Station#GroundStation-121.799,37.7605Ground StationActiveUnited StatesYevpatoria RT-70#GroundStation33.187068,45.189096Ground StationActiveUkraineSemnan Launch Center#LaunchSite53.92,35.25Launch SiteActiveIranOrbcomm Ocilla A#GroundStation-83.199591,31.50119Ground StationActiveUnited StatesGuiana Space Center ELS#LaunchPad-52.8345,5.3047Launch PadActiveFrench GuianaDombarovsky Cosmodrome xx4#LaunchPad59.77451,50.775737Launch PadActiveRussiaTranquillon Peak STDN CALF#GroundStation-120.56157233,34.58273856Ground StationActiveUnited StatesNaval Radio Transmitter Facility#GroundStation14.44,37.117Ground StationActiveItalyCape Canaveral Air Force Station - LC 25#LaunchPad-80.575,28.431Launch PadInactiveUnited StatesGuiana Space Center#LaunchSite-52.77,5.23Launch SiteActiveFrench GuianaGreenbelt STDN GGAL#LaserStation-76.82747947,39.02026828Laser StationActiveUnited StatesCombe Martin Leolut#GroundStation-4.051,51.17Ground StationActiveUnited KingdomDombarovsky Cosmodrome x12#LaunchPad59.808673,51.022624Launch PadActiveRussiaSydney Station#GroundStation151.2115,-33.7172Ground StationActiveAustraliaBaikonur Cosmodrome LC 243#LaunchPad63.737,45.8549Launch PadActiveKazakhstanJSC Test BRT STDN JSCJ#GroundStation-95.09,29.56168961Ground StationActiveUnited StatesVandenberg AFB SLC 2E#LaunchPad-120.619201,34.751617Launch PadDemolishedUnited StatesGissar#OpticalTrackingStation68.6818,38.491Optical Tracking StationActiveTajikistanBrewster Station#GroundStation-119.692,48.147Ground StationActiveUnited StatesNanning Station#GroundStation108.37,22.82Ground StationActiveChinaTattnall Space Fence#RadarStation-81.926,32.0437Radar StationActiveUnited StatesKaneohe Omega Station#GroundStation-157.8319,21.4047Ground StationInactiveUnited StatesPlesetsk Tracking Station#GroundStation40.558672,62.9001Ground StationActiveRussiaBTA-6#OpticalTrackingStation41.440447,43.646825Optical Tracking StationActiveRussiaPoker Flat Station PF2 STDN DXAS#GroundStation-147.43350389,65.11792972Ground StationActiveUnited StatesVandenberg AFB SLC 10E#LaunchPad-120.6213,34.7626Launch PadInactiveUnited StatesMakaha Ridge#GroundStation-159.723,22.13Ground StationActiveUnited StatesGTS STDN GTSS#GroundStation144.85605225,13.61518911Ground StationActiveUnited StatesRambouillet Teleport#GroundStation1.7826,48.5494Ground StationActiveFranceDombarovsky Cosmodrome - xx8#LaunchPad60.52694,50.959329Launch PadActiveRussiaDombarovsky Cosmodrome - x28#LaunchPad59.635083,51.193164Launch PadActiveRussiaSocorro GEODSS Cam3#OpticalTrackingStation-106.65968,33.81703Optical Tracking StationActiveUnited StatesYakima Training Center#GroundStation-120.356544,46.68209Ground StationActiveUnited StatesVLBA Los Alamos#GroundStation-106.245597,35.775125Ground StationActiveUnited StatesDombarovsky Cosmodrome xx2#LaunchPad59.791895,50.681452Launch PadActiveRussiaGreenbelt#GroundStation-76.8265,39.0219Ground StationActiveUnited StatesSUPARCO Satellite Ground Station#GroundStation73.17674,33.51787Ground StationActivePakistanDombarovsky Cosmodrome x20#LaunchPad60.087307,51.096909Launch PadActiveRussiaWoomera Test Range - LA 5A#LaunchPad136.474,-30.9716Launch PadActiveAustraliaWeinan Station#GroundStation109.51,34.5Ground StationActiveChinaBaikonur Cosmodrome xx3#LaunchPad63.481448,45.945516Launch PadActiveKazakhstanKapustin Yar S#LaunchPad46.3175,48.4763Launch PadActiveRussiaSan Diego Space Fence#RadarStation-116.973,32.5774Radar StationActiveUnited StatesKalyazin Radio Astronomy Observatory#GroundStation37.9,57.222Ground StationActiveRussiaGAVRT STDN DS12#GroundStation-116.80544339,35.29993942Ground StationActiveUnited StatesXichang Satellite Launch Center - LA 1#LaunchPad102.0292,28.2474Launch PadActiveChinaKennedy Space Center LC 39B STDN B39P#LaunchPad-80.62084967,28.62716064Launch PadActiveUnited StatesXian#GroundStation109.494,34.445Ground StationActiveChinaSvobodny Cosmodrome - x11#LaunchPad128.373907,51.879783Launch PadInactiveRussiaOrlando Station#GroundStation-81.121,28.4251Ground StationActiveUnited StatesDombarovsky Cosmodrome - x47#LaunchPad59.92692,51.600688Launch PadActiveRussiaDiego Garcia GEODSS Cam1#OpticalTrackingStation72.45203,-7.41162Optical Tracking StationActiveUnited KingdomWoomera Test Range#LaunchSite136.5,-30.95Launch SiteActiveAustraliaCape Canaveral Air Force Station - LC 15#LaunchPad-80.5494,28.4963Launch PadInactiveUnited StatesCape Canaveral STDN CN5F#GroundStation-80.56341989,28.51702439Ground StationActiveUnited StatesQuicksburg Station#GroundStation-78.658,38.73Ground StationActiveUnited StatesMid-Atlantic Regional Spaceport - Launch Pad 0-B#LaunchPad-75.4913,37.8312Launch PadActiveUnited StatesStarfire Optical Range#OpticalTrackingStation-106.4639,34.9642Optical Tracking StationActiveUnited StatesCape Canaveral Air Force Station - LC 18A#LaunchPad-80.5623,28.4506Launch PadInactiveUnited StatesPoker Flat LC 1#LaunchPad-147.4879,65.1295Launch PadActiveUnited StatesCape Canaveral AFS SLC 46 STDN A46P#LaunchPad-80.52840256,28.45849161Launch PadInactiveUnited StatesKapustin Yar - LC 107 1a#LaunchPad46.298,48.5992Launch PadActiveRussiaGrand Bahama Island STDN GB2Y#GroundStation-78.29852533,26.62547128Ground StationActiveBahamasSvobodny Cosmodrome x11#LaunchPad128.373907,51.879783Launch PadInactiveRussiaEsrange Rocket Range - L#LaunchPad21.1062,67.8943Launch PadActiveSwedenEsrange Station Idun#GroundStation21.038,67.879Ground StationActiveSwedenSES ASTRA#GroundStation6.330278,49.694167Ground StationActiveLuxembourgYacolt Station#GroundStation-122.396,45.863Ground StationActiveUnited StatesLondon Inmarsat HQ#GroundStation-0.0865,51.5254Ground StationActiveUnited KingdomKiruna EISCAT Radar#RadarStation20.43395,67.86068Radar StationActiveSwedenGoose Bay Leolut#GroundStation-60.466,53.312666Ground StationActiveCanadaEOC Antenna 2#GroundStation139.34814,36.00353Ground StationActiveJapanEchoStar Spokane Station#GroundStation-117.551,47.592Ground StationActiveUnited StatesBatam Island Station#GroundStation103.9506,1.1131Ground StationActiveIndonesiaBaikonur Cosmodrome LC 175-2#LaunchPad62.987,46.0512Launch PadActiveKazakhstanBaikonur Cosmodrome LC 81-23#LaunchPad62.9785,46.074Launch PadActiveKazakhstanVandenberg Air Force Base - SLC 576-E#LaunchPad-120.6191,34.7396Launch PadActiveUnited StatesWallops Island STDN WL2S#GroundStation-75.46205786,37.94642969Ground StationActiveUnited StatesNCTAMS LANT#GroundStation-76.3088,36.9505Ground StationActiveUnited StatesWhite Sands - LC 94#LaunchPad-106.4589,34.2049Launch PadActiveUnited StatesEsrange Station ESC#GroundStation21.0599183,67.8843522Ground StationActiveSwedenHelios Station#GroundStation-15.63071,27.76343Ground StationActiveSpainGreen River Station#GroundStation-109.352,41.537Ground StationActiveUnited StatesGalenki 25m#GroundStation131.7581,44.0204Ground StationActiveRussiaCape Canaveral AFS LC 15#LaunchPad-80.5494,28.4963Launch PadInactiveUnited StatesSpitsbergen Leolut#GroundStation15.396,78.229Ground StationActiveNorwayPatrick AFB STDN RAML#LaserStation-80.60574442,28.22799675Laser StationInactiveUnited StatesSatish Dhawan Pad 2#LaunchPad80.2304,13.7199Launch PadActiveIndiaDombarovsky Cosmodrome x41#LaunchPad60.178331,51.50362Launch PadActiveRussiaRoi-Namur Super RADOT#OpticalTrackingStation167.476576,9.393608Optical Tracking StationActiveMarshall IslandsSalto di Quirra#LaunchSite9.633,39.5273Launch SiteActiveItalyWhite Sands LC 36#LaunchPad-106.322,32.417Launch PadActiveUnited StatesZimmerwald Observatory#OpticalTrackingStation7.46522,46.877229Optical Tracking StationActiveSwitzerlandDSS 55 Robledo STDN DS55#GroundStation-4.25263331,40.42429592Ground StationActiveSpainSemnan Launch Center xx3#LaunchPad53.952,35.239Launch PadActiveIranDominion Observatory#GroundStation-119.6203,49.3209Ground StationActiveCanadaHawaii 1 HI1 Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesDongara Station AUWA01 STDN USPS#GroundStation115.34867764,-29.04577217Ground StationActiveAustraliaWoomera Test Range - LA 8#LaunchPad136.4629,-31.0328Launch PadActiveAustraliaYap Station#GroundStation138.0772,9.4909Ground StationActiveMicronesiaAscension Island STDN ASCQ#GroundStation-14.4025,-7.90663519Ground StationActiveUnited KingdomWhite Sands - LC 38#LaunchPad-106.2796,32.4179Launch PadActiveUnited StatesBangalore STDN BANF#GroundStation77.51,13.03Ground StationActiveIndiaAMISR Poker Flat#RadarStation-147.4707,65.1298Radar StationActiveUnited StatesWilkes-Barre Station#GroundStation-75.88268,41.24499Ground StationActiveUnited StatesBangkok 2 Leolut#GroundStation100.5432,13.717166Ground StationActiveThailandTangua Station#GroundStation-42.7837,-22.7468Ground StationActiveBrazilTanegashima Osaki Range#LaunchPad130.970274,30.39953Launch PadInactiveJapanAlcantara Launch Center - HAD Pad#LaunchPad-44.377,-2.3652Launch PadActiveBrazilHolloman A#LaunchPad-106.0714,32.8954Launch PadActiveUnited StatesKennedy Space Center LC 39A STDN A39P#LaunchPad-80.60411653,28.60827486Launch PadActiveUnited StatesKoganei xx2#GroundStation139.488122,35.710559Ground StationActiveJapanAlcantara Launch Center#LaunchSite-44.367,-2.317Launch SiteActiveBrazilOrbcomm Rio de Janiero B#GroundStation-42.87227,-22.69679Ground StationActiveBrazilUchinoura 20m#GroundStation131.080567,31.256784Ground StationActiveJapanChurchill Leolut#GroundStation-93.994,58.759Ground StationActiveCanadaPoker Flat - LC 3#LaunchPad-147.4851,65.1299Launch PadActiveUnited StatesTonghae Satellite Launching Ground#LaunchSite129.665994,40.855652Launch SiteActiveNorth KoreaCape Canaveral Air Force Station - LC 2#LaunchPad-80.5369,28.4657Launch PadInactiveUnited StatesDryden STDN DFRS#GroundStation-117.88739406,34.94979075Ground StationActiveUnited StatesMiyun Ground Station#GroundStation116.8589,40.4514Ground StationActiveChinaGRGT STDN GW3S#GroundStation144.84087494,13.58730961Ground StationActiveUnited StatesKapustin Yar - LC 107 2g#LaunchPad46.2958,48.5616Launch PadActiveRussiaMasuda USB F2#GroundStation131.01771,30.5555Ground StationActiveJapanBaikonur Cosmodrome LC 67-22#LaunchPad63.7073,45.9895Launch PadActiveKazakhstanUchinoura Space Center, 34m#GroundStation131.078495,31.254462Ground StationActiveJapanFort Buckner#GroundStation127.776,26.296Ground StationActiveJapanVandenberg Air Force Base - SLC 6 (STDN WT6P)#LaunchPad-120.62609183,34.58163228Launch PadActiveUnited StatesBear Lake RT-64#GroundStation37.951896,55.868035Ground StationActiveRussiaAberporth#LaunchSite-4.5566,52.1393Launch SiteActiveUnited KingdomMSSC 1.6m#OpticalTrackingStation-156.2574,20.70837Optical Tracking StationActiveUnited StatesWallops Island STDN WD3F#GroundStation-75.51143536,37.85636525Ground StationActiveUnited StatesKapustin Yar - LC 107 1b#LaunchPad46.298,48.5969Launch PadActiveRussiaDombarovsky Cosmodrome x11#LaunchPad59.567713,51.020939Launch PadActiveRussiaVandenberg STDN VDBF#GroundStation-120.53610972,34.77487769Ground StationActiveUnited StatesYatharagga Satellite Station#GroundStation115.3543,-29.0452Ground StationActiveAustraliaPlesetsk Cosmodrome - LC 133-3#LaunchPad40.8504,62.887Launch PadActiveRussiaPoker Flat - LC 1#LaunchPad-147.4879,65.1295Launch PadActiveUnited StatesRecife Geolut#GroundStation-34.925,-8.1383Ground StationActiveBrazilMSSC MOTIF#OpticalTrackingStation-156.2578,20.70852Optical Tracking StationActiveUnited StatesDombarovsky Cosmodrome - x43#LaunchPad59.801475,51.526422Launch PadActiveRussiaKeelung 2 Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanTCSB#GroundStation-0.90642519,51.1178738Ground StationActiveUnited KingdomLandstuhl SATCOM Terminal#GroundStation7.534,49.402Ground StationActiveGermanyHelsinki Teleport#GroundStation24.921667,60.206389Ground StationActiveFinlandWallops Flight Facility LA 2 RAG#LaunchPad-75.4823,37.8385Launch PadActiveUnited StatesSantiago Station AGO 6#GroundStation-70.6701611,-33.1506778Ground StationActiveChileDirecTV Los Angeles Broadcast Center (LABC)#GroundStation-118.425,33.9827Ground StationActiveUnited StatesSan Nicolas Island STDN SN3F#GroundStation-119.52139117,33.24839275Ground StationActiveUnited StatesDiego Garcia GEODSS Cam2#OpticalTrackingStation72.45244,-7.41163Optical Tracking StationActiveUnited KingdomSagamihara Campus#GroundStation139.395276,35.55831Ground StationActiveJapanEsrange Rocket Range - C#LaunchPad21.1029,67.8931Launch PadActiveSwedenHolloman AFB STDN HOLF#GroundStation-106.09916956,32.90146386Ground StationActiveUnited StatesBarreira do Inferno Launch Center#LaunchSite-35.1613,-5.9236Launch SiteActiveBrazilSocorro GEODSS Cam1#OpticalTrackingStation-106.66009,33.81727Optical Tracking StationActiveUnited StatesBiscarosse BP#LaunchPad-1.2396,44.3755Launch PadActiveFranceDongara Station AUWA02#GroundStation115.3494,-29.0454Ground StationActiveAustraliaMaiquetia (2) Leolut#GroundStation-66.9865,10.599Ground StationPlannedVenezuelaOrbcomm Kitaura#GroundStation140.31,36.05Ground StationActiveJapanHammaguira - Beatrice#LaunchPad-3.0851,30.8601Launch PadActiveAlgeriaArbuckle Station#GroundStation-122.1481,38.9383Ground StationActiveUnited StatesDombarovsky Cosmodrome - x17#LaunchPad59.748227,51.079713Launch PadActiveRussiaVandenberg STDN CALT#GroundStation-120.58144669,34.6658445Ground StationActiveUnited StatesAguimes#GroundStation-15.443,27.88Ground StationActiveSpainWallops Island LEO-T STDN LE2S#GroundStation-75.47613917,37.92352944Ground StationActiveUnited StatesCat House Phased-array Radar#RadarStation37.297,55.231Radar StationActiveRussiaEsrange Rocket Range N#LaunchPad21.1064,67.8933Launch PadActiveSwedenMasuda USB F1#GroundStation131.01494,30.55581Ground StationActiveJapanTarawa Station#GroundStation173.1558,1.3645Ground StationInactiveKiribatiDiyarbakir Radar#RadarStation39.9932,37.90476Radar StationInactiveTurkeyMalindi Station STDN KENS#GroundStation40.194505,-2.9955575Ground StationActiveKenyaBiscarosse#LaunchSite-1.25,44.32Launch SiteActiveFranceOrbcomm Kijal B#GroundStation103.47409,4.34818Ground StationActiveMalaysiaGreenbelt STDN BLTD#GroundStation-76.84276675,38.9984475Ground StationInactiveUnited StatesBaikonur Cosmodrome LC 106#LaunchPad63.4971,45.9511Launch PadActiveKazakhstanPune Earth Station#GroundStation73.8657,18.6041Ground StationActiveIndiaBaikonur Cosmodrome LC 175-59#LaunchPad62.9862,46.0525Launch PadActiveKazakhstanAflenz Teleport#GroundStation15.29175,47.55456Ground StationActiveAustriaVandenberg Air Force Base - SLC 4E#LaunchPad-120.61059,34.632067Launch PadInactiveUnited StatesWoomera Test Range LA 6B#LaunchPad136.445,-31.0792Launch PadActiveAustraliaYevpatoria South Station#GroundStation33.253128,45.170314Ground StationActiveUkraineESRIN Frascati#GroundStation12.67585,41.8275Ground StationActiveItalyKwajalein Drop Zone#LaunchSite167.7,7.65Launch SiteActiveMarshall IslandsKapustin Yar xx7#LaunchPad45.716196,48.812023Launch PadActiveRussiaCape Canaveral AFS LC 19#LaunchPad-80.5542,28.5068Launch PadInactiveUnited StatesVandenberg Air Force Base - SLC 10N#LaunchPad-120.6227,34.7663Launch PadInactiveUnited StatesAMISR Resolute Bay#RadarStation-94.90624,74.72941Radar StationActiveCanadaEuropean Direct Access Facility#GroundStation11.2789,48.0862Ground StationActiveGermanySwakopmund Station#GroundStation14.547462,-22.574087Ground StationActiveNamibiaGreen River#LaunchSite-110.076,38.942Launch SiteActiveUnited StatesEmeq Haela Station#GroundStation34.9942,31.6833Ground StationActiveIsraelBITF 11m#GroundStation166.1504,-78.1296Ground StationActiveAntarcticaSouth Point Station USHI01 STDN USHS#GroundStation-155.66330125,19.0139045Ground StationActiveUnited StatesHammaguira#LaunchSite-3.06,30.88Launch SiteActiveAlgeriaKapustin Yar - V2#LaunchPad45.9074,48.5709Launch PadActiveRussiaKingston#OpticalTrackingStation-76.4675,44.231Optical Tracking StationActiveCanadaDombarovsky Cosmodrome#LaunchSite60,51Launch SiteActiveRussiaKiruna Station 15m (STDN KI2S)#GroundStation20.96434169,67.85712517Ground StationActiveSwedenSanta Ynez Peak STDN SNYC#OpticalTrackingStation-119.98584869,34.53031086Optical Tracking StationActiveUnited StatesMumbai Station#GroundStation72.8306,18.9345Ground StationActiveIndiaDombarovsky Cosmodrome - x19#LaunchPad59.844333,51.093509Launch PadActiveRussiaBaikonur Cosmodrome - LC 200-40#LaunchPad63.0379,46.0364Launch PadActiveKazakhstanSatish Dhawan#LaunchSite80.23,13.73Launch SiteActiveIndiaDombarovsky Cosmodrome x17#LaunchPad59.748227,51.079713Launch PadActiveRussiaBarking Sands LC 10#LaunchPad-159.7816,22.0569Launch PadActiveUnited StatesDombarovsky Cosmodrome x22#LaunchPad59.634472,51.11475Launch PadActiveRussiaLakhadaria Station#GroundStation3.6057,36.564Ground StationActiveAlgeriaMaidanak#OpticalTrackingStation66.89641,38.67332Optical Tracking StationActiveUzbekistanNairobi#GroundStation36.82,-1.28Ground StationActiveKenyaCape Canaveral Air Force Station - SLC 17B (STDN B17P)#LaunchPad-80.56564944,28.44579042Launch PadActiveUnited StatesEagle River Earth Station#GroundStation-149.4475,61.2995Ground StationActiveUnited StatesOklahoma Spaceport#LaunchSite-99.2,35.34Launch SitePlannedUnited StatesOsan AB#GroundStation127.03166,37.09433Ground StationActiveSouth KoreaKauai STDN HAW3#GroundStation-159.66515503,22.12627256Ground StationActiveUnited StatesMagdalena Ridge Observatory#OpticalTrackingStation-107.1894,33.98486Optical Tracking StationActiveUnited StatesCape Canaveral AFS LC 5#LaunchPad-80.5733,28.4394Launch PadInactiveUnited StatesNaval Reserve Center Station#GroundStation-121.347,37.945Ground StationInactiveUnited StatesEsrange Rocket Range - A#LaunchPad21.1018,67.8931Launch PadActiveSwedenSemnan Launch Center#LaunchSite53.92,35.25Launch SiteActiveIranAdelaide Satellite Facility#GroundStation138.572,-34.8627Ground StationActiveAustraliaDombarovsky Cosmodrome - x45#LaunchPad59.956989,51.558712Launch PadActiveRussiaAlcantara Launch Center HAD Pad#LaunchPad-44.377,-2.3652Launch PadActiveBrazilKolonicke#OpticalTrackingStation22.273858,48.935001Optical Tracking StationActiveSlovakiaAndoya Rocket Range#LaunchSite16.021,69.294Launch SiteActiveNorwayEsrange Rocket Range - MRL#LaunchPad21.103,67.8934Launch PadActiveSwedenGiant Metrewave Radio Telescope#GroundStation74.05,19.09Ground StationActiveIndiaEsrange Station ESTC (STDN KU2S)#GroundStation21.06044833,67.8831825Ground StationActiveSwedenPenteli Leolut#GroundStation23.883,38.080833Ground StationActiveGreeceHeimenschwand#GroundStation7.716108,46.831336Ground StationActiveSwitzerlandWhite Sands LC 35#LaunchPad-106.3422,32.4041Launch PadActiveUnited StatesPlesetsk Cosmodrome LC 132-2#LaunchPad40.8722,62.8834Launch PadActiveRussiaIOS STDN SEYS#GroundStation55.47782053,-4.67174811Ground StationInactiveSeychellesDombarovsky Cosmodrome x38#LaunchPad60.163782,51.338805Launch PadActiveRussiaHartebeesthoek STDN HARL#LaserStation27.68617419,-25.88970925Laser StationActiveSouth AfricaGilmore Creek STDN GLCS#GroundStation-147.50477836,64.97314211Ground StationActiveUnited StatesWeilheim#GroundStation11.078325,47.881242Ground StationInactiveGermanyWhite Sands SULF#LaunchPad-106.7364,33.7212Launch PadActiveUnited StatesCape Canaveral AFS LC 2#LaunchPad-80.5369,28.4657Launch PadInactiveUnited StatesTaiyuan North Pad#LaunchPad111.608381,38.848552Launch PadActiveChinaKapustin Yar - START R12#LaunchPad46.298,48.5807Launch PadActiveRussiaHolloman - NATIV#LaunchPad-106.0753,32.8866Launch PadActiveUnited StatesMakassar Leolut#GroundStation119.55,-5.066666Ground StationPlannedIndonesiaWellington Leolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandHartRAO#GroundStation27.685393,-25.889752Ground StationActiveSouth AfricaAbu Dhabi Geolut#GroundStation54.448,24.4312Ground StationActiveUnited Arab EmiratesKapustin Yar - xx2#LaunchPad46.297833,48.539973Launch PadActiveRussiaKapustin Yar xx4#LaunchPad46.299018,48.615791Launch PadActiveRussiaAlice Springs Transmitter#RadarStation134.4479,-22.9676Radar StationActiveAustraliaBaikonur Cosmodrome - LC 45-1#LaunchPad63.655387,45.940067Launch PadActiveKazakhstanBaikonur Cosmodrome LC 241#LaunchPad63.4558,45.8583Launch PadActiveKazakhstanPillar Point STDN PPTQ#GroundStation-122.49971472,37.49781692Ground StationActiveUnited StatesGalliot Station STDN KRUS#GroundStation-52.639872,5.098848Ground StationActiveFrench GuianaCape Canaveral AFS SLC 37B STDN B37P#LaunchPad-80.56445806,28.53121944Launch PadActiveUnited StatesNERC Satellite Receiving Station#GroundStation-2.980112,56.458105Ground StationActiveUnited KingdomWoomera Test Range - MRL#LaunchPad136.5332,-30.9573Launch PadActiveAustraliaBeijing (1) Leolut#GroundStation116.42,39.908Ground StationActiveChinaSemnan Launch Center - xx1#LaunchPad53.896,35.222Launch PadActiveIranGuiana Space Center Diamant Pad#LaunchPad-52.7524,5.2325Launch PadInactiveFrench GuianaMcMurdo STDN MC1S#GroundStation166.66708233,-77.8391295Ground StationActiveAntarcticaBangkok (1) Leolut#GroundStation100.5433,13.717166Ground StationActiveThailandMid-Atlantic Regional Spaceport 0-A#LaunchPad-75.4882,37.8338Launch PadActiveUnited StatesWhite Sands - LC 35#LaunchPad-106.3422,32.4041Launch PadActiveUnited StatesByalalu 18m#GroundStation77.36882,12.90042Ground StationActiveIndiaNew Norcia DSA 1#GroundStation116.1915,-31.048225Ground StationActiveAustraliaSvobodny Cosmodrome xx2#LaunchPad128.4085,51.7734Launch PadInactiveRussiaDombarovsky Cosmodrome x46#LaunchPad60.082438,51.588487Launch PadActiveRussiaWoomera Test Range - LA 3#LaunchPad136.5191,-30.93Launch PadActiveAustraliaJiuquan Satellite Launch Center - SLS#LaunchPad100.2915,40.958Launch PadActiveChinaUchinoura Space Center - Mu Pad#LaunchPad131.08223,31.251028Launch PadActiveJapanUS Naval Observatory#GroundStation-77.067,38.921Ground StationActiveUnited StatesNenoksa Test Range#LaunchSite39.22,64.646Launch SiteActiveRussiaVandenberg AFB SLC 10N#LaunchPad-120.6227,34.7663Launch PadInactiveUnited StatesHawkinsville Space Fence#RadarStation-83.5361,32.2889Radar StationActiveUnited StatesPoint Mugu - LC 2#LaunchPad-119.121,34.0992Launch PadActiveUnited StatesBaikonur Cosmodrome - LC 131#LaunchPad62.9562,46.0716Launch PadActiveKazakhstanDubai#GroundStation55.267,25.267Ground StationActiveUnited Arab EmiratesKarachi Station#GroundStation67.03,24.89Ground StationActivePakistanHartebeesthoek Europ Star#GroundStation27.70793,-25.88549Ground StationActiveSouth AfricaRiyadh SLR#LaserStation46.40037,24.91067Laser StationActiveSaudi ArabiaSvobodny Cosmodrome#LaunchSite128.4,51.8Launch SiteInactiveRussiaJeddah 2 Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaTanum Teleport#GroundStation11.377,58.704Ground StationInactiveSwedenWoomera Test Range LA 5A#LaunchPad136.474,-30.9716Launch PadActiveAustraliaMonument Peak STDN MNPL#LaserStation-116.42267108,32.89173831Laser StationActiveUnited StatesFort Greely#LaunchSite-145.73,63.95Launch SiteActiveUnited StatesMelbourne Station#GroundStation-80.63638,28.08647Ground StationActiveUnited StatesChantilly STDN CHAS#GroundStation-77.44216667,38.89033333Ground StationInactiveUnited StatesJAXA Maspalomas#GroundStation-15.6348,27.76505Ground StationActiveSpainRoi-Namur Island#LaunchSite167.4652,9.4012Launch SiteActiveMarshall IslandsMoscow Teleport#GroundStation37.626,55.843Ground StationActiveRussiaCape Canaveral Air Force Station - LC 19#LaunchPad-80.5542,28.5068Launch PadInactiveUnited StatesALMA#GroundStation-67.755,-23.028Ground StationPlannedChileCape Canaveral STDN CN4F#GroundStation-80.58311147,28.46316792Ground StationActiveUnited StatesVLBA Owens Valley#GroundStation-118.277047,37.231653Ground StationActiveUnited StatesTTS STDN TTSS#GroundStation-68.59997228,76.51593456Ground StationActiveGreenlandPerth International Telecom Centre#GroundStation115.88785,-31.80485Ground StationActiveAustraliaAlamo Peak STDN ALAY#GroundStation-105.81237947,32.87253311Ground StationActiveUnited StatesPunta Arenas Leolut#GroundStation-70.847,-53.006Ground StationActiveChileCape Canaveral Air Force Station - LC 16#LaunchPad-80.5518,28.5016Launch PadInactiveUnited StatesSvobodny Cosmodrome - xx8#LaunchPad128.2764,51.8357Launch PadInactiveRussiaNorman Station#GroundStation-97.5658,35.1798Ground StationActiveUnited StatesGreen River - Pad 2#LaunchPad-110.0753,38.9413Launch PadActiveUnited StatesEsrange Station SfinX#GroundStation21.0525247,67.8882281Ground StationActiveSwedenHTSB#GroundStation-158.262297,21.5689783Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 106#LaunchPad63.4971,45.9511Launch PadActiveKazakhstanGuam 2 GU2 Leolut#GroundStation144.9392,13.578333Ground StationActiveUnited StatesWallops Island STDN WPDA#GroundStation-75.47495275,37.92737297Ground StationActiveUnited StatesGuiana Space Center ZL3 STDN KR3P#LaunchPad-52.75178147,5.24036314Launch PadActiveFrench GuianaWallops Flight Facility LA 3#LaunchPad-75.4725,37.8506Launch PadActiveUnited StatesMt Hopkins STDN HOPL#LaserStation-110.87806108,31.68424706Laser StationInactiveUnited StatesDSS 24 Goldstone STDN DS24#GroundStation-116.87479442,35.33989283Ground StationActiveUnited StatesWellington 2 Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandStaten Island Teleport#GroundStation-74.1784,40.6028Ground StationActiveUnited StatesThornton Station#GroundStation-104.9839,39.9158Ground StationActiveUnited StatesToulouse MultiMission Station#GroundStation1.487456,43.554442Ground StationActiveFranceAustralia Telescope Compact Array#GroundStation149.565,-30.313Ground StationActiveAustraliaSatish Dhawan Space Centre - Pad 1#LaunchPad80.2346,13.7334Launch PadActiveIndiaUchinoura Space Center, 10m#GroundStation131.084654,31.255675Ground StationActiveJapanWallops Island STDN WL4S#GroundStation-75.46059947,37.94631331Ground StationActiveUnited StatesNanshan Station#GroundStation87.17837,43.47153Ground StationActiveChinaWhite Sands STDN WH6K#GroundStation-106.60923475,32.50146197Ground StationActiveUnited StatesTaiyuan Satellite Launch Center - North Pad#LaunchPad111.608381,38.848552Launch PadActiveChinaEareckson#LaunchSite174.07,52.72Launch SiteActiveUnited StatesHartebeesthoek 11m#GroundStation27.70692,-25.8855Ground StationActiveSouth AfricaSvobodny Cosmodrome xx5#LaunchPad128.23218,51.80532Launch PadInactiveRussiaPoker Flat STDN PFTQ#GroundStation-147.46329083,65.11679308Ground StationActiveUnited StatesStockholm Teleport#GroundStation18.083866,59.211779Ground StationActiveSwedenDombarovsky Cosmodrome - x39#LaunchPad60.472701,51.346817Launch PadActiveRussiaKashima Space Research Center 11m#GroundStation140.65745,35.95558Ground StationActiveJapanCape Canaveral Air Force Station - LC 26A#LaunchPad-80.5705,28.4446Launch PadInactiveUnited StatesSatish Dhawan Space Centre - Pad 2#LaunchPad80.2304,13.7199Launch PadActiveIndiaDombarovsky Cosmodrome - xx4#LaunchPad59.77451,50.775737Launch PadActiveRussiaWhite Sands - LC 37#LaunchPad-106.2911,32.4155Launch PadActiveUnited StatesSatish Dhawan Space Centre - Sounding Rocket Pad#LaunchPad80.2404,13.759Launch PadActiveIndiaBaikonur Cosmodrome LC 107#LaunchPad63.81,46.046Launch PadActiveKazakhstanBaikonur Cosmodrome LC 131#LaunchPad62.9562,46.0716Launch PadActiveKazakhstanDSS 46 Tidbinbilla STDN DS46#GroundStation148.98308169,-35.40501064Ground StationInactiveAustraliaHammaguira - Bacchus#LaunchPad-3.1241,30.8565Launch PadActiveAlgeriaVandenberg AFB SLC 8#LaunchPad-120.6324,34.5762Launch PadActiveUnited StatesHong Kong 1 Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaSheshan Station#GroundStation121.1995,31.0992Ground StationActiveChinaMaiquetia 1 Leolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaLatefia#GroundStation44.2079,33.7224Ground StationActiveIraqComcast Los Angeles Station#GroundStation-118.4566,34.0312Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 163#LaunchPad63.172,46.002Launch PadActiveKazakhstanEdwards AFB STDN EAFF#GroundStation-117.91155094,34.96065983Ground StationActiveUnited StatesDSS 26 Goldstone STDN D26D#GroundStation-116.8730165,35.33568922Ground StationActiveUnited StatesAscension Island STDN AS2Q#GroundStation-14.40095086,-7.97280625Ground StationActiveUnited KingdomWallops Flight Facility LA 2 MLS#LaunchPad-75.4835,37.8375Launch PadActiveUnited StatesFlorida 2 (FL2) Leolut#GroundStation-80.3838,25.616333Ground StationActiveUnited StatesDombarovsky Cosmodrome x26#LaunchPad59.74824,51.154839Launch PadActiveRussiaPrince Albert xx2#GroundStation-105.92615,53.21206Ground StationActiveCanadaWhite Sands BRT STDN WH2J#GroundStation-106.61196575,32.50628147Ground StationActiveUnited StatesMaryland (LSE) Leolut#GroundStation-76.93,38.850333Ground StationActiveUnited StatesMaiquetia Geolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaSpace Surveillance Telescope#OpticalTrackingStation-106.3644,33.7395Optical Tracking StationActiveUnited StatesSintra Station#GroundStation-9.2817,38.8692Ground StationActivePortugalKapustin Yar xx2#LaunchPad46.297833,48.539973Launch PadActiveRussiaERMEXS#GroundStation-99.12879,19.32263Ground StationActiveMexicoAerospace Data Facility East#GroundStation-77.1583,38.7361Ground StationActiveUnited StatesHammaguira Brigitte#LaunchPad-3.0357,30.8935Launch PadActiveAlgeriaSvobodny Cosmodrome - x12#LaunchPad128.252,51.8818Launch PadInactiveRussiaCalifornia 1 (CA1) Leolut#GroundStation-120.5515,34.6625Ground StationActiveUnited StatesOrbcomm Almaty B#GroundStation76.78226,44.49652Ground StationActiveKazakhstanAuckland Station#GroundStation174.6959,-36.7482Ground StationActiveNew ZealandTVB City#GroundStation114.2723,22.2778Ground StationActiveChinaTaejon#GroundStation127.4333,36.3269Ground StationActiveSouth KoreaWhite Sands LC 37#LaunchPad-106.2911,32.4155Launch PadActiveUnited StatesWhite Sands STDN WHSS#GroundStation-106.60855189,32.50026978Ground StationActiveUnited StatesSondrestrom Rocket Range#LaunchSite-50.6,67.024Launch SiteActiveGreenlandKapustin Yar SAM#LaunchPad45.7346,48.8055Launch PadActiveRussiaKapustin Yar - xx5#LaunchPad45.781879,48.781927Launch PadActiveRussiaWhite Sands STDN STSS#GroundStation-106.61209906,32.54171675Ground StationActiveUnited StatesBaikonur Cosmodrome LC 110R#LaunchPad63.3102,45.9622Launch PadActiveKazakhstanKitab#OpticalTrackingStation66.8863,39.1337Optical Tracking StationActiveUzbekistanAPL STDN MDLS#GroundStation-76.89877778,39.16736111Ground StationActiveUnited StatesBerkeley STDN BRKS#GroundStation-122.24278381,37.87937658Ground StationActiveUnited StatesTorrejon Air Base xx1#GroundStation-3.4469,40.4805Ground StationActiveSpainVandenberg Air Force Base - SLC 10W#LaunchPad-120.6244,34.7636Launch PadInactiveUnited StatesMaryland Point#GroundStation-77.23068,38.3742Ground StationActiveUnited StatesEsrange Station TTC STDN KICS#GroundStation21.060769,67.884232Ground StationActiveSwedenGreen River Pad 3#LaunchPad-110.0741,38.9426Launch PadActiveUnited StatesTanegashima Space Center - Yoshinobu LC, Pad 1#LaunchPad130.977582,30.400975Launch PadActiveJapanJPL#GroundStation-118.1726,34.2031Ground StationActiveUnited StatesBITF 7m#GroundStation166.1504,-78.1296Ground StationActiveAntarcticaGRGT STDN GWMK#GroundStation144.8409295,13.58801786Ground StationActiveUnited StatesCape Cod AFS PAVE PAWS#RadarStation-70.5386,41.7524Radar StationActiveUnited StatesKronogard#LaunchSite19.32,66.3922Launch SiteInactiveSwedenTTSA#GroundStation-68.5956028,76.5151944Ground StationActiveGreenlandEagle Vision 5#GroundStation-157.92,21.32Ground StationActiveUnited StatesAlcantara Ground Station#GroundStation-44.404167,-2.338333Ground StationActiveBrazilRAL Station 12m#GroundStation-1.31268,51.57215Ground StationActiveUnited KingdomOttawa Leolut#GroundStation-75.6745,45.328666Ground StationActiveCanadaCape Canaveral Air Force Station - SLC 36B (STDN B36P)#LaunchPad-80.54095408,28.46836775Launch PadDemolishedUnited StatesCocos Island STDN COCS#GroundStation96.85,-12.2Ground StationActiveAustraliaQingdao Station#GroundStation120.383,36.066Ground StationActiveChinaWoomera Test Range LA 8#LaunchPad136.4629,-31.0328Launch PadActiveAustraliaTutuila BRT STDN AM2J#GroundStation-170.71941667,-14.33144444Ground StationActiveUnited StatesAlcantara Launch Center - UL Pad#LaunchPad-44.367,-2.316Launch PadActiveBrazilOverberg Earth Station 10m#GroundStation20.226,-34.616Ground StationActiveSouth AfricaKapustin Yar - LC 107 2a#LaunchPad46.2949,48.5609Launch PadActiveRussiaBatelco Teleport#GroundStation50.6114,26.0714Ground StationActiveBahrainMcMurdo STDN MCMS#GroundStation166.4,-77.8Ground StationActiveAntarcticaBarking Sands - LC 14#LaunchPad-159.7788,22.058Launch PadActiveUnited StatesGreenbelt Geolut#GroundStation-76.840666,38.999Ground StationActiveUnited StatesEsrange Rocket Range A#LaunchPad21.1018,67.8931Launch PadActiveSwedenOrbcomm San Luis A#GroundStation-65.18131,-33.8374Ground StationActiveArgentinaStanley Earth Station#GroundStation114.221,22.197Ground StationActiveChinaPARTNeR STDN DS61#GroundStation-4.24892803,40.42874444Ground StationActiveSpainKennedy Space Center - LC 39B (STDN B39P)#LaunchPad-80.62084967,28.62716064Launch PadActiveUnited StatesDoral Station#GroundStation-80.3525,25.81Ground StationActiveUnited StatesCape D-Aguilar Station#GroundStation114.2492,22.2167Ground StationActiveChinaDombarovsky Cosmodrome x29#LaunchPad60.765442,51.201923Launch PadActiveRussiaChilbolton Observatory#GroundStation-1.43842,51.14502Ground StationActiveUnited KingdomMojave Air and Space Port#LaunchSite-118.15,35.06Launch SitePlannedUnited StatesEsrange Rocket Range MRL#LaunchPad21.103,67.8934Launch PadActiveSwedenCape Canaveral Air Force Station - LC 31#LaunchPad-80.5563,28.4519Launch PadInactiveUnited StatesWoomera Test Range LA 2#LaunchPad136.521,-30.9433Launch PadActiveAustraliaWoomera Test Range - HAD#LaunchPad136.5322,-30.9553Launch PadActiveAustraliaDSS 53 Robledo#GroundStation-4.24964,40.42744Ground StationInactiveSpainRio Grande Leolut#GroundStation-67.7053,-53.779166Ground StationActiveArgentinaKapustin Yar START R12#LaunchPad46.298,48.5807Launch PadActiveRussiaBaikonur Cosmodrome - LC 67-21#LaunchPad63.705,45.9893Launch PadActiveKazakhstanBundaberg Leolut#GroundStation152.4128,-24.758333Ground StationActiveAustraliaMiyun Deep Space Station#GroundStation116.976,40.558Ground StationActiveChinaSan Marco Launch Platform#LaunchSite40.2125,-2.9383Launch SiteInactiveKenyaBlack Island STDN BLKQ#GroundStation166.15043278,-78.12957889Ground StationActiveAntarcticaTDC Herstedvester Teleport#GroundStation12.355,55.68Ground StationActiveDenmarkSvalsat SG 3 STDN SG3S#GroundStation15.40809583,78.229735Ground StationActiveNorwayAlice Springs BRT STDN ALSJ#GroundStation133.8825985,-23.75881044Ground StationActiveAustraliaTai Po Earth Station#GroundStation114.19,22.453Ground StationActiveChinaKashima Space Research Center 34m#GroundStation140.66006,35.95589Ground StationActiveJapanWallops Flight Facility LA 2 AML-2#LaunchPad-75.483,37.8379Launch PadActiveUnited StatesXichang Satellite Launch Center - LA 2#LaunchPad102.0271,28.2455Launch PadActiveChinaKwajalein STDN KMPF#RadarStation167.72649006,8.72167692Radar StationActiveMarshall IslandsGuam 2 (GU2) Leolut#GroundStation144.9392,13.578333Ground StationActiveUnited StatesBaikonur Cosmodrome xx2#LaunchPad63.462445,45.94176Launch PadActiveKazakhstanDefford Site#GroundStation-2.147,52.097Ground StationActiveUnited KingdomWhite Sands - LC 33#LaunchPad-106.443,32.3396Launch PadActiveUnited StatesWallops Flight Facility LA 2 AML-1#LaunchPad-75.4828,37.8381Launch PadActiveUnited StatesSantiago Station AGO 3 STDN AGO3#GroundStation-70.666403,-33.15110747Ground StationActiveChileKapustin Yar - LC 107 2h#LaunchPad46.2949,48.5617Launch PadActiveRussiaWhite Sands STDN STGK#GroundStation-106.61208894,32.54327958Ground StationActiveUnited StatesBangalore#GroundStation77.5116,13.0344Ground StationActiveIndiaLasham Satellite Ground Station#GroundStation-1.044,51.184Ground StationDemolishedUnited KingdomOtay Mt STDN SNDL#LaserStation-116.84080406,32.60073169Laser StationInactiveUnited StatesBaikonur Cosmodrome - LC 172#LaunchPad63.092,46.065Launch PadActiveKazakhstanHolloman - SLED#LaunchPad-106.1484,32.9263Launch PadActiveUnited StatesVandenberg STDN VDB3#GroundStation-120.50161731,34.56562592Ground StationActiveUnited StatesPerth STDN PRTS#GroundStation115.885,-31.802Ground StationActiveAustraliaMadley Communications Centre#GroundStation-2.84049,52.03198Ground StationActiveUnited KingdomIllegini Island#LaunchSite167.4754,9.0856Launch SiteActiveMarshall IslandsEsrange Station SSC-CNES#GroundStation21.0309,67.88225Ground StationActiveSwedenAlcantara Launch Center - MRL Pad#LaunchPad-44.3671,-2.3167Launch PadActiveBrazilRed River Space Fence#RadarStation-93.5503,33.33Radar StationActiveUnited StatesRedu Station#GroundStation5.145344,50.000456Ground StationActiveBelgiumWallops Flight Facility - LA 2 ARC#LaunchPad-75.4841,37.838Launch PadActiveUnited StatesVandenberg STDN VD2F#GroundStation-120.62712139,34.75823183Ground StationActiveUnited StatesFort Wingate#LaunchSite-108.5994,35.44868Launch SiteActiveUnited StatesAnkara 2 Leolut#GroundStation32.9897,40.140666Ground StationActiveTurkeyBaikonur Cosmodrome LC 195#LaunchPad63.2127,45.783Launch PadActiveKazakhstanChilworth Station#GroundStation-1.4274,50.9608Ground StationActiveUnited KingdomSvobodny Cosmodrome - xx6#LaunchPad128.467,51.8054Launch PadInactiveRussiaBiscarosse CE#LaunchPad-1.2325,44.3917Launch PadActiveFranceRATAN-600 Radio Telescope#GroundStation41.587,43.826Ground StationActiveRussiaDSTO Adelaide#GroundStation138.65,-34.731Ground StationActiveAustraliaZhuklino A-35 Anti-missile Site#RadarStation38.579,56.244Radar StationActiveRussiaTanegashima Yoshinobu LC Pad 1#LaunchPad130.977582,30.400975Launch PadActiveJapanWhite Sands STDN WH7F#GroundStation-106.65901447,33.81307828Ground StationActiveUnited StatesJiuquan Satellite Launch Center - LA 2A#LaunchPad100.3165,41.3088Launch PadActiveChinaDombarovsky Cosmodrome xx1#LaunchPad59.655376,50.658373Launch PadActiveRussiaVandenberg Air Force Base - SLC 1W#LaunchPad-120.6303,34.7571Launch PadInactiveUnited StatesJiuquan Satellite Launch Center - xx1#LaunchPad100.304946,41.280432Launch PadActiveChinaDGSA#GroundStation72.37002305,-7.2700227Ground StationActiveUnited KingdomHetian Station#GroundStation79.922,37.114Ground StationActiveChinaElfordstown Station#GroundStation-8.1758,51.9533Ground StationActiveIrelandBaikonur Cosmodrome LC 164#LaunchPad63.064,45.9445Launch PadActiveKazakhstanEsrange Rocket Range L#LaunchPad21.1062,67.8943Launch PadActiveSwedenWoodbine Teleport#GroundStation-77.081,39.3763Ground StationActiveUnited StatesCape Canaveral AFS LC 11#LaunchPad-80.5395,28.4753Launch PadInactiveUnited StatesDSS 42 Tidbinbilla STDN DS42#GroundStation148.98124419,-35.40068386Ground StationDemolishedAustraliaEdmonton Leolut#GroundStation-113.3162,53.678166Ground StationActiveCanadaCarpentersville Station#GroundStation-75.1911,40.6444Ground StationActiveUnited StatesBaikonur Cosmodrome LC 160#LaunchPad62.9423,46.0783Launch PadActiveKazakhstanPoker Flat STDN WT1S#GroundStation-147.45906028,65.11723692Ground StationInactiveUnited StatesUchinoura 34m#GroundStation131.078495,31.254462Ground StationActiveJapanWoomera Test Range - LA 6B#LaunchPad136.445,-31.0792Launch PadActiveAustraliaEagle Vision 3#GroundStation-117.16202,32.83818Ground StationActiveUnited StatesEchoStar Cheyenne Station#GroundStation-104.736,41.132Ground StationActiveUnited StatesYarragadee STDN YARL#LaserStation115.34674628,-29.04649844Laser StationActiveAustraliaO-Higgins#GroundStation-57.901241,-63.321128Ground StationActiveAntarcticaOrbcomm Matera A#GroundStation16.70751,40.64922Ground StationActiveItalyEOC Antenna 3#GroundStation139.3478,36.00235Ground StationActiveJapanOverberg Test Range - xx1#LaunchPad20.30271,-34.60276Launch PadActiveSouth AfricaMatera STDN MROL#LaserStation16.70460944,40.64867006Laser StationActiveItalyDombarovsky Cosmodrome - x36#LaunchPad60.301504,51.270092Launch PadActiveRussiaGrasse STDN GRAL#LaserStation6.92157186,43.75463183Laser StationActiveFranceVillafranca VIL-2#GroundStation-3.95257,40.44558Ground StationActiveSpainDombarovsky Cosmodrome - xx9#LaunchPad59.551097,50.972694Launch PadActiveRussiaSGS Colerne Site#GroundStation-2.2771,51.444Ground StationActiveUnited KingdomXichang Satellite Launch Center - LA 3#LaunchPad102.0271,28.2455Launch PadActiveChinaAlbright Station#GroundStation-79.579,39.5686Ground StationActiveUnited StatesCape Canaveral AFS LC 22#LaunchPad-80.5398,28.461Launch PadInactiveUnited StatesGilmore Creek STDN GILE#GroundStation-147.49800047,64.97850711Ground StationActiveUnited StatesDSS 34 Tidbinbilla STDN DS34#GroundStation148.98196442,-35.39847883Ground StationActiveAustraliaFairbanks STDN ULA3#GroundStation-147.51338883,64.97214025Ground StationInactiveUnited StatesSvobodny Cosmodrome xx4#LaunchPad128.3386,51.7938Launch PadInactiveRussiaFGAN Radar#RadarStation7.129822,50.616569Radar StationActiveGermanyWallops Flight Facility - LA 4 HAD#LaunchPad-75.4696,37.8511Launch PadActiveUnited StatesAlaska 1 (AK1) Leolut#GroundStation-147.5173,64.973666Ground StationActiveUnited StatesKwajalein Phased Array Radar#RadarStation167.715317,8.725601Radar StationActiveMarshall IslandsBaikonur Cosmodrome LC 194#LaunchPad63.301,45.854Launch PadActiveKazakhstanLucknow#GroundStation80.957329,26.913182Ground StationActiveIndiaEOC Antenna 1#GroundStation139.3488,36.00386Ground StationActiveJapanCallao Leolut#GroundStation-77.1298,-12.030666Ground StationActivePeruTranquillon Peak STDN CALC#OpticalTrackingStation-120.56243417,34.58238614Optical Tracking StationActiveUnited StatesDSS 17 Goldstone STDN DS17#GroundStation-116.87345528,35.34222994Ground StationDemolishedUnited StatesKaena Point Radar STDN KPTQ#RadarStation-158.26658533,21.57211972Radar StationActiveUnited StatesSvalsat EUM 2#GroundStation15.3884,78.229Ground StationActiveNorwayMid-Atlantic Regional Spaceport 0-B#LaunchPad-75.4913,37.8312Launch PadActiveUnited StatesEsrange Rocket Range#LaunchSite21.105,67.893Launch SiteActiveSwedenTaiyuan#LaunchSite111.61,38.84Launch SiteActiveChinaBaikonur Cosmodrome LC 60-7#LaunchPad64.0173,46.0181Launch PadActiveKazakhstanTRADEX Radar STDN KMRF#RadarStation167.48214819,9.39874711Radar StationActiveMarshall IslandsOrbcomm Maghreb B#GroundStation-7.64393,33.04755Ground StationActiveMoroccoGuiana Space Center ZL2 STDN KR2P#LaunchPad-52.77567156,5.23240672Launch PadInactiveFrench GuianaTaeduk Radio Astronomy Observatory#GroundStation127.3752,36.3976Ground StationActiveSouth KoreaCar Nicobar Station#GroundStation92.82765,9.1548Ground StationActiveIndiaSatish Dhawan Sounding Rocket Pad#LaunchPad80.2404,13.759Launch PadActiveIndiaWallops Island STDN WAPS#GroundStation-75.4765225,37.92492556Ground StationActiveUnited StatesHai Phong Station#GroundStation106.7104,20.8005Ground StationActiveVietnamPlesetsk Cosmodrome - LC 41-1#LaunchPad40.529,62.9405Launch PadDemolishedRussiaPatrick AFB STDN PATQ#GroundStation-80.59927636,28.22640242Ground StationActiveUnited StatesAtom Peak STDN ATMY#GroundStation-106.36455683,33.73962981Ground StationActiveUnited StatesWoomera Test Range - LA 1#LaunchPad136.5037,-30.9587Launch PadActiveAustraliaWellington (2) Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandDSS 63 Robledo STDN DS63#GroundStation-4.24800856,40.43120975Ground StationActiveSpainEdwards AFB STDN FRCF#GroundStation-117.91149608,34.96080469Ground StationActiveUnited StatesBukit Timah Satellite Earth Station#GroundStation103.7911,1.3514Ground StationActiveSingaporeFort Meade SATCOM Terminal#GroundStation-76.757,39.104Ground StationActiveUnited StatesWhite Sands STDN ST2K#GroundStation-106.61208894,32.54297736Ground StationActiveUnited StatesAuScope VLBI Katherine#GroundStation132.1524,-14.3755Ground StationActiveAustraliaPlesetsk Cosmodrome LC 32-2#LaunchPad40.7895,62.9057Launch PadActiveRussiaCape Canaveral Air Force Station - LC 4#LaunchPad-80.5356,28.4669Launch PadInactiveUnited StatesWallops Island STDN WPSA#GroundStation-75.47498581,37.92727767Ground StationInactiveUnited StatesThule BMEWS#RadarStation-68.2992,76.5703Radar StationActiveGreenlandFauske Geolut#GroundStation15.302,67.237Ground StationActiveNorwayCape Canaveral Air Force Station - LC 6#LaunchPad-80.5726,28.4407Launch PadInactiveUnited StatesMILA Test BRT STDN MILJ#GroundStation-80.69301289,28.50598947Ground StationInactiveUnited StatesEsrange Station KSX#GroundStation21.0556028,67.8887061Ground StationActiveSwedenNeustrelitz STDN NSGS#GroundStation13.07,53.32972222Ground StationActiveGermanyQueensland Receiver#RadarStation143.1936,-24.2871Radar StationActiveAustraliaMcDonald Observatory STDN MLRL#LaserStation-104.01519731,30.68026717Laser StationActiveUnited StatesMidway Research Center#GroundStation-77.373,38.498Ground StationActiveUnited StatesAlaska 1 AK1 Leolut#GroundStation-147.5173,64.973666Ground StationActiveUnited StatesTromso EISCAT Dish Radar#RadarStation19.22637,69.58648Radar StationActiveNorwayOrbcomm St Johns B#GroundStation-109.554917,34.455501Ground StationActiveUnited StatesBretagne 1 Radar STDN KRUF#RadarStation-52.64498992,5.11400642Radar StationActiveFrench GuianaNauchny#OpticalTrackingStation34.01567,44.72785Optical Tracking StationActiveUkraineSocorro GEODSS, xx1#OpticalTrackingStation-106.66009,33.81727Optical Tracking StationActiveUnited StatesKuantan Station#GroundStation103.36,3.866Ground StationActiveMalaysiaOttawa (2) Geolut#GroundStation-75.674333,45.3438Ground StationActiveCanadaDombarovsky Cosmodrome x37#LaunchPad60.728749,51.301802Launch PadActiveRussiaNHS STDN NH2S#GroundStation-71.63032172,42.94474167Ground StationActiveUnited StatesVostochny Cosmodrome#LaunchSite128.25,51.817Launch SitePlannedRussiaPsary Station#GroundStation20.8542,50.9336Ground StationActivePolandCape Canaveral AFS SLC 17A STDN A17P#LaunchPad-80.56492617,28.44716106Launch PadActiveUnited StatesMiramar Station#GroundStation-80.2833,25.9756Ground StationActiveUnited StatesHolloman - A#LaunchPad-106.0714,32.8954Launch PadActiveUnited StatesCheia Station#GroundStation25.9465,45.4567Ground StationActiveRomaniaTularosa STDN TULF#GroundStation-106.15915417,33.09616153Ground StationActiveUnited StatesCSTARS_East_11m#GroundStation-80.3837,25.61336Ground StationActiveUnited StatesPillar Point STDN PPTY#GroundStation-122.49918072,37.49777411Ground StationActiveUnited StatesDongara Station AUWA01 STDN AUWS#GroundStation115.34866806,-29.04576808Ground StationActiveAustraliaGSE Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesOrbcomm Chong Ho Won#GroundStation127.66,37.14Ground StationActiveSouth KoreaWallops Flight Facility - LA 4#LaunchPad-75.4701,37.8508Launch PadActiveUnited StatesKapustin Yar - LC 86 4a#LaunchPad46.295,48.5508Launch PadActiveRussiaPoint Mugu LC 2#LaunchPad-119.121,34.0992Launch PadActiveUnited StatesGreenbelt STDN STAL#LaserStation-76.82777092,39.02029483Laser StationInactiveUnited StatesBeijing 2 Leolut#GroundStation116.42,39.908Ground StationActiveChinaEglin AFB AN-FPS 85 PAR STDN EG2F#RadarStation-86.21471742,30.57252794Radar StationActiveUnited StatesRas Al Khaimah Spaceport#LaunchSite55.941,25.617Launch SitePlannedUnited Arab EmiratesPatrick AFB STDN PA2Q#GroundStation-80.60609819,28.22732817Ground StationActiveUnited StatesSvobodny Cosmodrome xx1#LaunchPad128.3102,51.747Launch PadInactiveRussiaNOAA STDN SOCA#GroundStation-76.93222222,38.85002611Ground StationActiveUnited StatesSpaceport Singapore#LaunchSite103.99,1.36Launch SitePlannedSingaporeSpaceport America#LaunchSite-106.98,32.99Launch SitePlannedUnited StatesJiuquan#LaunchSite100.5,41.1Launch SiteActiveChinaJamesburg Earth Station#GroundStation-121.64704,36.40313Ground StationActiveUnited StatesWestford Radio Telescope#GroundStation-71.49377,42.61293Ground StationActiveUnited StatesAnkara 1 Leolut#GroundStation32.9897,40.140833Ground StationActiveTurkeyKapustin Yar - LC 107 2d#LaunchPad46.2959,48.5695Launch PadActiveRussiaBaikonur Cosmodrome - LC 45-2#LaunchPad63.6532,45.9433Launch PadActiveKazakhstanJiuquan SLS#LaunchPad100.2915,40.958Launch PadActiveChinaWhite Sands LC 50#LaunchPad-106.3488,32.4064Launch PadActiveUnited StatesSvobodny Cosmodrome xx3#LaunchPad128.185,51.792Launch PadInactiveRussiaVillafranca VIL-4#GroundStation-3.95173,40.44451Ground StationActiveSpainKwajalein BC-4 Cameras#OpticalTrackingStation167.719465,8.72304Optical Tracking StationActiveMarshall IslandsADSCGS#GroundStation114.842,-28.695Ground StationActiveAustraliaSvobodny Cosmodrome xx8#LaunchPad128.2764,51.8357Launch PadInactiveRussiaIncheon Leolut#GroundStation126.649,37.393Ground StationActiveSouth KoreaWoomera Test Range - LA 9#LaunchPad136.4871,-30.9031Launch PadActiveAustraliaWestern Australian Transmitter#RadarStation122.8435,-28.3174Radar StationActiveAustraliaWallops Island STDN WTDS#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesHauppauge Station#GroundStation-73.264,40.8202Ground StationActiveUnited StatesKeelung 1 Leolut#GroundStation121.7573,25.135333Ground StationActiveTaiwanSPTR-2#GroundStation0,-90Ground StationActiveAntarcticaBaikonur Cosmodrome LC 105#LaunchPad63.4962,45.9503Launch PadActiveKazakhstanBaikonur Cosmodrome - LC 110R#LaunchPad63.3102,45.9622Launch PadActiveKazakhstanYarragedee STDN YARZ#GroundStation115.34666667,-29.04664553Ground StationActiveAustraliaResolute Bay#LaunchSite-94.8962,74.687Launch SiteInactiveCanadaMaui GEODSS Cam3#OpticalTrackingStation-156.2575,20.7085Optical Tracking StationActiveUnited StatesAntigua Island STDN ANRQ#GroundStation-61.77522336,17.13728994Ground StationActiveAntigua and BarbudaCape Canaveral AFS LC 34#LaunchPad-80.5611,28.5218Launch PadInactiveUnited StatesCebreros DSA 2#GroundStation-4.367549,40.45269Ground StationActiveSpainBaikonur Cosmodrome - LC 109#LaunchPad63.4452,45.9525Launch PadActiveKazakhstanEsrange Station ELS#GroundStation21.062325,67.8765153Ground StationActiveSwedenAntigua STDN AN3S#GroundStation-61.77435,17.13695083Ground StationActiveAntigua and BarbudaWallops Flight Facility - LA 4 MAST#LaunchPad-75.4702,37.8508Launch PadActiveUnited StatesJoint Defense Facility Pine Gap#GroundStation133.737,-23.799Ground StationActiveAustraliaKarachi Leolut#GroundStation67.136,24.946Ground StationActivePakistanDarwin Station#GroundStation130.9812,-12.4765Ground StationActiveAustraliaBaikonur Cosmodrome - LC 110L#LaunchPad63.3049,45.9647Launch PadActiveKazakhstanSvalsat EUM 1#GroundStation15.4014,78.2286Ground StationActiveNorwayTidbinbilla STDN RGTS#GroundStation148.98241831,-35.40453633Ground StationActiveAustraliaSvalsat SG 4 STDN SG4S#GroundStation15.4097095,78.22801361Ground StationActiveNorwayTianshan Station#GroundStation120.09,43.874Ground StationActiveChinaMMW Radar#RadarStation167.48123,9.39731Radar StationActiveMarshall IslandsCTS STDN CTSS#GroundStation-104.52846914,38.80598842Ground StationActiveUnited StatesTromso EISCAT Cylinder Radar#RadarStation19.2219,69.5867Radar StationActiveNorwayKodiak Launch Complex#LaunchSite-152.3393,57.4352Launch SiteActiveUnited StatesEchoStar Mt Jackson Station#GroundStation-78.667,38.723Ground StationActiveUnited StatesItapetinga Radio Observatory#GroundStation-46.55823,-23.18524Ground StationActiveBrazilVostochny Cosmodrome#LaunchSite128.25,51.817Launch SitePlannedRussiaWallops Island STDN WLPQ#GroundStation-75.50929556,37.86026147Ground StationActiveUnited StatesHawaii 1 (HI1) Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesKapustin Yar LC 107 2a#LaunchPad46.2949,48.5609Launch PadActiveRussiaDombarovsky Cosmodrome x36#LaunchPad60.301504,51.270092Launch PadActiveRussiaWallops Island STDN WL53#GroundStation-75.4601,37.9468Ground StationActiveUnited StatesSvobodny Cosmodrome xx6#LaunchPad128.467,51.8054Launch PadInactiveRussiaDehmandro Station#GroundStation67.0992,25.1933Ground StationActivePakistanXTAR 16m#GroundStation-15.63132,27.76271Ground StationActiveSpainBaikonur Cosmodrome LC 1#LaunchPad63.3422,45.9203Launch PadActiveKazakhstanMakassar Leolut#GroundStation119.55,-5.066666Ground StationPlannedIndonesiaVandenberg AFB SLC 10W#LaunchPad-120.6244,34.7636Launch PadInactiveUnited StatesBiscarosse - CE#LaunchPad-1.2325,44.3917Launch PadActiveFranceBaikonur Cosmodrome LC 104#LaunchPad63.4197,45.9875Launch PadActiveKazakhstanBaikonur Cosmodrome LC 161-35#LaunchPad63.063,46.0335Launch PadActiveKazakhstanWallops Island STDN WPS8#GroundStation-75.47583319,37.92735903Ground StationActiveUnited StatesAnderson Peak STDN ANPC#OpticalTrackingStation-121.64434508,36.18052364Optical Tracking StationActiveUnited StatesCape Canaveral Air Force Station - SLC 46 (STDN A46P)#LaunchPad-80.52840256,28.45849161Launch PadInactiveUnited StatesPoint Mugu#LaunchSite-119.121,34.099Launch SiteActiveUnited StatesChilca#LaunchSite-76.799,-12.505Launch SiteActivePeruKueijen#GroundStation120.28,22.97Ground StationActiveTaiwanZimmerwald STDN ZIML#LaserStation7.46521981,46.87722883Laser StationActiveSwitzerlandFresnedillas Monitoring Station#GroundStation-4.1697,40.4555Ground StationActiveSpainDombarovsky Cosmodrome - x23#LaunchPad60.675969,51.146793Launch PadActiveRussiaHagerstown Teleport#GroundStation-77.757011,39.599906Ground StationActiveUnited StatesNapa Teleport#GroundStation-122.279965,38.245535Ground StationActiveUnited StatesDombarovsky Cosmodrome xx5#LaunchPad59.595072,50.837656Launch PadActiveRussiaDSS 25 Goldstone STDN DS25#GroundStation-116.87536319,35.33761197Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 22#LaunchPad-80.5398,28.461Launch PadInactiveUnited StatesEglin AFB Launch Site#LaunchSite-85.34588,29.67729Launch SiteActiveUnited StatesHolloman ZEL#LaunchPad-106.06,32.88Launch PadActiveUnited StatesOgasawara Downrange Station#GroundStation142.215439,27.079017Ground StationActiveJapanInuvik Station INU#GroundStation-133.54391,68.31788Ground StationActiveCanadaToulouse 1 Leolut#GroundStation1.4808,43.560666Ground StationActiveFranceBaikonur Cosmodrome - LC 165#LaunchPad62.9185,45.9912Launch PadActiveKazakhstanJiuquan LA 2B#LaunchPad100.3132,41.3061Launch PadActiveChinaPort Hedland STDN SWNS#GroundStation118.635,-20.38Ground StationActiveAustraliaTsukuba Space Center#GroundStation140.126716,36.071299Ground StationActiveJapanDSS 27 Goldstone STDN D27D#GroundStation-116.77665044,35.23827178Ground StationActiveUnited StatesTrivandrum#GroundStation76.8731,8.5365Ground StationActiveIndiaMaryland (2) Geolut#GroundStation-76.93,38.8503Ground StationActiveUnited StatesEsrange Station SfinX#GroundStation21.0525247,67.8882281Ground StationActiveSwedenTranquillon Peak STDN CA2F#GroundStation-120.56111489,34.58302833Ground StationActiveUnited StatesWhite Sands STDN WH2K#GroundStation-106.60855172,32.50073719Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 161-35#LaunchPad63.063,46.0335Launch PadActiveKazakhstanBaikonur Cosmodrome LC 45-1#LaunchPad63.655387,45.940067Launch PadActiveKazakhstanASF 11m STDN ASFS#GroundStation-147.85817536,64.85880858Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-3#LaunchPad63.6598,45.9754Launch PadActiveKazakhstanDSS 17 Goldstone STDN GDSA#GroundStation-116.87300133,35.34154264Ground StationDemolishedUnited StatesKodiak Ranging Site#GroundStation-152.3737,57.4554Ground StationActiveUnited StatesToulouse (2) Leolut#GroundStation1.4808,43.5605Ground StationActiveFranceDSS 15 Goldstone STDN DS15#GroundStation-116.88719511,35.42185328Ground StationActiveUnited StatesDomme SIGINT Station#GroundStation1.2381,44.7864Ground StationActiveFranceMullach Mor Radar Station#GroundStation-8.584962,57.819175Ground StationActiveUnited KingdomTula Peak STDN TULS#GroundStation-106.139091,33.02694489Ground StationInactiveUnited StatesESA Space Debris Telescope#OpticalTrackingStation-16.51189,28.30095Optical Tracking StationActiveSpainBaikonur Cosmodrome - LC 175-59#LaunchPad62.9862,46.0525Launch PadActiveKazakhstanCTSA#GroundStation-104.52848,38.8059353Ground StationActiveUnited StatesPanamsat Long Beach Operations Center#GroundStation-118.2112,33.8292Ground StationActiveUnited StatesDombarovsky Cosmodrome x34#LaunchPad60.461524,51.259129Launch PadActiveRussiaESRIN Maspalomas#GroundStation-15.63377,27.76433Ground StationActiveSpainBaikonur Cosmodrome - LC 60-8#LaunchPad64.0183,46.0174Launch PadActiveKazakhstanSary Shagan#LaunchSite73.562,45.812Launch SiteActiveKazakhstanNiles Canyon Station#GroundStation-121.944,37.6Ground StationActiveUnited StatesComcast Titan Station#GroundStation-105.025,39.514Ground StationActiveUnited StatesPrince Albert xx1#GroundStation-105.93291,53.21266Ground StationActiveCanadaTidbinbilla STDN CANS#GroundStation148.983058,-35.40466667Ground StationActiveAustraliaNATO Missile Firing Installation#LaunchSite24.175,35.573Launch SiteActiveCreteEldorado AFS PAVE PAWS#RadarStation-100.5529,30.9783Radar StationInactiveUnited StatesSanta Maria Station#GroundStation-25.136103,36.99694Ground StationActivePortugalBaikonur Cosmodrome - LC 1#LaunchPad63.3422,45.9203Launch PadActiveKazakhstanWallops Flight Facility - LA 2 AML-2#LaunchPad-75.483,37.8379Launch PadActiveUnited StatesTokyo STDN KA2S#GroundStation139.49177778,35.70876186Ground StationActiveJapanWallops Island STDN WP2Y#GroundStation-75.47685742,37.92558517Ground StationActiveUnited StatesFort Huachuca STDN FT2F#GroundStation-110.43817333,31.55676686Ground StationActiveUnited StatesGatineau xx1#GroundStation-75.80933,45.5858Ground StationActiveCanadaWallops Island Drop Zone#LaunchSite-75,37.5Launch SiteActiveUnited StatesHAARP#RadarStation-145.151,62.393Radar StationActiveUnited StatesDombarovsky Cosmodrome x35#LaunchPad60.858975,51.26491Launch PadActiveRussiaNatal Station STDN NATC#OpticalTrackingStation-35.16434436,-5.92780506Optical Tracking StationActiveBrazilCape Canaveral AFS SLC 36A STDN A36P#LaunchPad-80.53772211,28.47143831Launch PadDemolishedUnited StatesTrollSat Ground Station#GroundStation2.53838,-72.0117Ground StationActiveAntarcticaMount Pleasant STDN TSMF#GroundStation147.439,-42.805Ground StationActiveAustraliaDGS STDN DGIS#GroundStation72.36999861,-7.27003056Ground StationActiveUnited KingdomVela Antenna#GroundStation147.44165,-42.8034Ground StationActiveAustraliaWhite Sands STDN STWS#GroundStation-106.60856383,32.49950678Ground StationActiveUnited StatesEsrange Station Balder#GroundStation21.038,67.879Ground StationActiveSwedenBaikonur Cosmodrome - LC 192#LaunchPad63.2995,46.0243Launch PadActiveKazakhstanMerritt Island STDN MLAQ#GroundStation-80.66438767,28.42471119Ground StationActiveUnited StatesNCTS Naples#GroundStation14.049,40.929Ground StationActiveItalyKennedy Space Center#LaunchSite-80.62,28.62Launch SiteActiveUnited StatesRAF Menwith Hill#GroundStation-1.689492,54.008692Ground StationActiveUnited KingdomVLBA Kitt Peak#GroundStation-111.611992,31.956316Ground StationActiveUnited StatesDiego Garcia GEODSS, xx2#OpticalTrackingStation72.45244,-7.41163Optical Tracking StationActiveUnited KingdomBaikonur Cosmodrome LC 41-3#LaunchPad63.6598,45.9754Launch PadActiveKazakhstanLustbuhel Observatory#LaserStation15.4934,47.06714Laser StationActiveAustriaWoomera Test Range MRL#LaunchPad136.5332,-30.9573Launch PadActiveAustraliaCape Canaveral Air Force Station - SLC 37B (STDN B37P)#LaunchPad-80.56445806,28.53121944Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx7#LaunchPad59.73193,50.883614Launch PadActiveRussiaKunia Station#GroundStation-158.055314,21.473136Ground StationActiveUnited StatesGRGT STDN GW2S#GroundStation144.84087447,13.58758828Ground StationActiveUnited StatesJohnston Island#LaunchSite-169.53,16.733Launch SiteActiveUnited StatesFort Churchill#LaunchSite-93.820278,58.734167Launch SiteActiveCanadaHolloman - ZEL#LaunchPad-106.06,32.88Launch PadActiveUnited StatesNHSA#GroundStation-71.626559,42.9478333Ground StationActiveUnited StatesSondrestrom Rocket Range#LaunchSite-50.6,67.024Launch SiteActiveGreenlandWoomera Test Range HAD#LaunchPad136.5322,-30.9553Launch PadActiveAustraliaOrbcomm Wenatchee B#GroundStation-120.175247,47.550924Ground StationActiveUnited StatesHong Kong (1) Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaCape Canaveral Air Force Station - LC 5#LaunchPad-80.5733,28.4394Launch PadInactiveUnited StatesTTSB#GroundStation-68.5988147,76.5153639Ground StationActiveGreenlandScanEx Magadan Station#GroundStation150.81308,59.55632Ground StationActiveRussiaNemea Station#GroundStation22.6222,37.8461Ground StationActiveGreeceRoaring Creek Station#GroundStation-76.4393,40.8935Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 244#LaunchPad63.6346,45.8403Launch PadActiveKazakhstanRas Al Khaimah Station#GroundStation56.0408,25.7927Ground StationActiveUnited Arab EmiratesNaro Space Center#LaunchSite127.53507,34.43187Launch SiteActiveSouth KoreaFlorida 2 FL2 Leolut#GroundStation-80.3838,25.616333Ground StationActiveUnited StatesCape Canaveral Air Force Station - SLC 41 (STDN A41P)#LaunchPad-80.58287267,28.58345786Launch PadActiveUnited StatesCachoeira Paulista#GroundStation-45.000214,-22.68211Ground StationActiveBrazilGreen River Pad 1#LaunchPad-110.0775,38.9416Launch PadActiveUnited StatesEsrange Station ESX#GroundStation21.0634472,67.8781431Ground StationActiveSwedenToulouse 2 Leolut#GroundStation1.4808,43.5605Ground StationActiveFranceWallops Island STDN WP2Z#GroundStation-75.47375114,37.92891233Ground StationActiveUnited StatesGellinam#GroundStation167.727855,9.099416Ground StationActiveMarshall IslandsGreat Wall Station#GroundStation-58.9626,-62.2166Ground StationActiveAntarcticaBarking Sands - LC 19#LaunchPad-159.7813,22.0619Launch PadActiveUnited StatesThule STDN TH2S#GroundStation-68.59881472,76.51536389Ground StationActiveGreenlandOrbcomm Ocilla B#GroundStation-83.199585,31.500413Ground StationActiveUnited StatesWallops Island STDN HR2S#GroundStation-75.46209,37.94542583Ground StationActiveUnited StatesOhr Station#GroundStation9.33,52.06Ground StationActiveGermanyRozhen#OpticalTrackingStation24.744,41.693Optical Tracking StationActiveBulgariaOrroral Valley#GroundStation148.95713,-35.62982Ground StationRelocatedAustraliaBaikonur Cosmodrome - LC 242#LaunchPad63.4626,45.9407Launch PadActiveKazakhstanCape Canaveral Air Force Station - LC 9#LaunchPad-80.5594,28.4522Launch PadInactiveUnited StatesGCHQ Bude#GroundStation-4.5537,50.8862Ground StationActiveUnited KingdomVandenberg AFB SLC 576-E#LaunchPad-120.6191,34.7396Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx3#LaunchPad59.597872,50.756297Launch PadActiveRussiaPomonkey STDN PMKS#GroundStation-77.05774122,38.55771053Ground StationInactiveUnited StatesEaster Island Leolut#GroundStation-109.437,-27.150166Ground StationActiveChilePoint Mugu STDN PM3F#GroundStation-119.15476742,34.12288772Ground StationActiveUnited StatesCape Canaveral AFS LC 16#LaunchPad-80.5518,28.5016Launch PadInactiveUnited StatesAmman#GroundStation35.837,32.0808Ground StationActiveJordanBaikonur Cosmodrome LC 90-19#LaunchPad62.9144,46.0863Launch PadActiveKazakhstanKapustin Yar V2#LaunchPad45.9074,48.5709Launch PadActiveRussiaBarking Sands LC 42#LaunchPad-159.7727,22.0682Launch PadActiveUnited StatesCape Canaveral AFS SLC 37A#LaunchPad-80.568,28.534Launch PadInactiveUnited StatesBermuda STDN BDDQ#GroundStation-64.65344947,32.34794319Ground StationInactiveBermudaSouth Uist Range Control Unit#GroundStation-7.355771,57.340814Ground StationActiveUnited KingdomSvobodny Cosmodrome - x10#LaunchPad128.3323,51.877Launch PadInactiveRussiaWallops Flight Facility LA 3A#LaunchPad-75.4726,37.848Launch PadActiveUnited StatesKapustin Yar - LC 107 2c#LaunchPad46.2949,48.5695Launch PadActiveRussiaWhite Sands STDN WHSX#GroundStation-106.60801708,32.50094139Ground StationActiveUnited StatesTonopah Test Range#LaunchSite-116.77,37.8Launch SiteActiveUnited StatesWhite Sands STDN WH4K#GroundStation-106.60855389,32.50138961Ground StationActiveUnited StatesVandenberg AFB#LaunchSite-120.6,34.7Launch SiteActiveUnited StatesKapustin Yar#LaunchSite46.1,48.5Launch SiteActiveRussiaEl Palomar Geolut#GroundStation-58.6,-34.6Ground StationActiveArgentinaBiscarosse - BS#LaunchPad-1.2675,44.2882Launch PadInactiveFranceBad Aibling Station#GroundStation11.984444,47.879444Ground StationInactiveGermanyPlesetsk Cosmodrome#LaunchSite40.6,62.9Launch SiteActiveRussiaSHAR-2#GroundStation80.19633,13.67394Ground StationActiveIndiaMayaguez#GroundStation-67.1368,18.2111Ground StationActiveUnited StatesWallops Flight Facility LA 1 AML#LaunchPad-75.4871,37.8352Launch PadActiveUnited StatesBaikonur Cosmodrome LC 162-36#LaunchPad63.0668,46.0323Launch PadActiveKazakhstanValcartier#OpticalTrackingStation-71.47075,46.87545Optical Tracking StationActiveCanadaGRAVES Transmitter#RadarStation5.515,47.348Radar StationActiveFranceGuam 1 (GU1) Leolut#GroundStation144.939,13.578333Ground StationActiveUnited StatesSpaceport Sweden#LaunchSite20.331,67.822Launch SitePlannedSwedenKapustin Yar - LC 86 4c#LaunchPad46.297,48.5481Launch PadActiveRussiaSan Clemente#LaunchSite-118.48698,32.91771Launch SiteActiveUnited StatesMayport Drop Zone#LaunchSite-78.5,29Launch SiteActiveUnited StatesYokohama Satellite Control Center#GroundStation139.5145,35.5055Ground StationActiveJapanEsrange Station SSC-CNES#GroundStation21.0309,67.88225Ground StationActiveSwedenWallops Island STDN WL2F#GroundStation-75.46422336,37.94409914Ground StationActiveUnited StatesSuffield#OpticalTrackingStation-111.10471,50.29301Optical Tracking StationActiveCanadaBarking Sands - LC 12#LaunchPad-159.7819,22.0593Launch PadActiveUnited StatesBarking Sands - Kokole Point#LaunchPad-159.7627,21.988Launch PadActiveUnited StatesMedicina Radio Observatory#GroundStation11.646944,44.520833Ground StationActiveItalyVandenberg STDN CALY#GroundStation-120.50101308,34.56562375Ground StationActiveUnited StatesSemnan Launch Center xx1#LaunchPad53.896,35.222Launch PadActiveIranWallops Island STDN WP3Z#GroundStation-75.47589722,37.92820833Ground StationActiveUnited StatesNATO Missile Firing Installation#LaunchSite24.175,35.573Launch SiteActiveCreteYorii#GroundStation139.2,36.12Ground StationActiveJapanUsuda Deep Space Center#GroundStation138.3627,36.1325Ground StationActiveJapanEsrange Station ELS#GroundStation21.062325,67.8765153Ground StationActiveSwedenCorn Ranch#LaunchSite-104.7589,31.4233Launch SiteActiveUnited StatesWallops Flight Facility LA 2 ARC#LaunchPad-75.4841,37.838Launch PadActiveUnited StatesPennant Point Station#GroundStation-63.6133,44.4648Ground StationActiveCanadaSodankyla EISCAT Radar#RadarStation26.63043,67.36383Radar StationActiveFinlandWallops Island STDN WT3S#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesOttawa Leolut#GroundStation-75.6745,45.328666Ground StationActiveCanadaBiscarosse SUD#LaunchPad-1.276,44.295Launch PadActiveFranceTorrejon Air Base xx2#GroundStation-3.44474,40.48276Ground StationActiveSpainSPTR-1#GroundStation0,-90Ground StationInactiveAntarcticaPlesetsk Cosmodrome - LC 132-1#LaunchPad40.8686,62.8833Launch PadActiveRussiaEsrange Station ESSEX#GroundStation21.0630978,67.8904339Ground StationActiveSwedenDirecTV LA Broadcast Center LABC#GroundStation-118.425,33.9827Ground StationActiveUnited StatesYatharagga STDN ATFS#GroundStation115.35126589,-29.04494003Ground StationActiveAustraliaWhite Sands - LC 32#LaunchPad-106.4069,32.4068Launch PadActiveUnited StatesFairbanks STDN UL33#GroundStation-147.51806617,64.97681389Ground StationActiveUnited StatesCalifornia 1 CA1 Leolut#GroundStation-120.5515,34.6625Ground StationActiveUnited StatesESRIN#GroundStation12.67585,41.8275Ground StationActiveItalyDSS 16 Goldstone STDN GD28#GroundStation-116.87360467,35.34154264Ground StationInactiveUnited StatesUchinoura Mu Pad#LaunchPad131.08223,31.251028Launch PadActiveJapanPort Blair#GroundStation92.71247,11.63727Ground StationActiveIndiaAndrushevka#OpticalTrackingStation28.997306,50.000556Optical Tracking StationActiveUkraineMatera Station#GroundStation16.704079,40.649536Ground StationActiveItalyCape Canaveral AFS SLC 40 STDN A40P#LaunchPad-80.57719364,28.56198939Launch PadActiveUnited StatesEllenwood Teleport#GroundStation-84.272016,33.664202Ground StationActiveUnited StatesEdmonton Geolut#GroundStation-113.316166,53.6782Ground StationActiveCanadaCalifornia 2 CA2 Leolut#GroundStation-120.5517,34.662333Ground StationActiveUnited StatesPalmachim AFB#LaunchSite34.69,31.89Launch SiteActiveIsraelDombarovsky Cosmodrome x31#LaunchPad60.606992,51.241105Launch PadActiveRussiaGuiana Space Center - ELS#LaunchPad-52.8345,5.3047Launch PadActiveFrench GuianaVLBA Hancock#GroundStation-71.986581,42.933608Ground StationActiveUnited StatesEsrange Station ELS (STDN KILS)#GroundStation21.062337,67.876532Ground StationActiveSwedenClewiston Station#GroundStation-81.049,26.747Ground StationActiveUnited StatesEutelsat Paris#GroundStation2.277808,48.840264Ground StationActiveFrancePalmachim AFB Shavit Pad#LaunchPad34.6802,31.8848Launch PadActiveIsraelWhite Sands STDN WS1S#GroundStation-106.61209953,32.54075494Ground StationActiveUnited StatesBrasilia Leolut#GroundStation-47.9027,-15.857166Ground StationActiveBrazilCape Canaveral Air Force Station - LC 13#LaunchPad-80.5446,28.4859Launch PadInactiveUnited StatesOttawa (1) Geolut#GroundStation-75.674,45.329Ground StationActiveCanadaEsrange Station Balder#GroundStation21.038,67.879Ground StationActiveSwedenHaiphong Leolut#GroundStation106.71,20.801166Ground StationActiveVietnamFucino#GroundStation13.6018,41.9793Ground StationActiveItalyFuchsstadt Station#GroundStation9.924176,50.118315Ground StationActiveGermanySouth Point Station USHI01 STDN HWIS#GroundStation-155.66330125,19.0139045Ground StationActiveUnited StatesWhite Sands STDN WH2S#GroundStation-106.60855389,32.50129656Ground StationActiveUnited StatesPulantat Station#GroundStation144.75,13.42Ground StationActiveUnited StatesDombarovsky Cosmodrome xx8#LaunchPad60.52694,50.959329Launch PadActiveRussiaRAF Feltwell#GroundStation0.5205,52.4806Ground StationActiveUnited KingdomMondy#OpticalTrackingStation100.918792,51.621694Optical Tracking StationActiveRussiaNeu Golm Station#GroundStation14.0867,52.3137Ground StationActiveGermanyCape Canaveral AFS LC 4#LaunchPad-80.5356,28.4669Launch PadInactiveUnited StatesMerritt Island STDN MMTF#GroundStation-80.67478864,28.47857681Ground StationActiveUnited StatesVandenberg AFB SLC 4W#LaunchPad-120.6154,34.6331Launch PadInactiveUnited StatesPongdong-ri#LaunchSite124.705265,39.659987Launch SiteActiveNorth KoreaDombarovsky Cosmodrome x10#LaunchPad60.649859,50.985518Launch PadActiveRussiaJiuquan LA 3#LaunchPad100.78,41.0152Launch PadActiveChinaCape Canaveral STDN CNVF#GroundStation-80.57650917,28.48160589Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-15#LaunchPad63.6687,45.9763Launch PadActiveKazakhstanKapustin Yar - S#LaunchPad46.3175,48.4763Launch PadActiveRussiaDombarovsky Cosmodrome x42#LaunchPad59.972508,51.521988Launch PadActiveRussiaPlesetsk Cosmodrome - LC 132-2#LaunchPad40.8722,62.8834Launch PadActiveRussiaMountain Horse Station#GroundStation-121.5933,37.7502Ground StationActiveUnited StatesBarking Sands LC 12#LaunchPad-159.7819,22.0593Launch PadActiveUnited StatesLethbridge Station#GroundStation-112.873188,49.681208Ground StationActiveCanadaBaikonur Cosmodrome LC 41-15#LaunchPad63.6687,45.9763Launch PadActiveKazakhstanCape Canaveral AFS LC 21#LaunchPad-80.5403,28.4606Launch PadInactiveUnited StatesVandenberg Air Force Base - SLC 5#LaunchPad-120.6247,34.608Launch PadInactiveUnited StatesCSTARS_20m#GroundStation-80.38477,25.61375Ground StationActiveUnited StatesAlice Springs Receiver#RadarStation133.6792,-23.5213Radar StationActiveAustraliaBarking Sands Kokole Point#LaunchPad-159.7627,21.988Launch PadActiveUnited StatesBaikonur Cosmodrome LC 200-39#LaunchPad63.032,46.0399Launch PadActiveKazakhstanCotopaxi Station#GroundStation-78.578,-0.623Ground StationActiveEcuadorBaikonur Cosmodrome LC 245#LaunchPad63.5271,45.8175Launch PadActiveKazakhstanOuargla Leolut#GroundStation5.49,31.88Ground StationActiveAlgeriaDombarovsky Cosmodrome x14#LaunchPad59.958431,51.036288Launch PadActiveRussiaAbu Dhabi Leolut#GroundStation54.4478,24.4315Ground StationActiveUnited Arab EmiratesCape Canaveral AFS SLC 41 STDN A41P#LaunchPad-80.58287267,28.58345786Launch PadActiveUnited StatesHammaguira - Brigitte-A#LaunchPad-3.0081,30.8712Launch PadActiveAlgeriaSemnan Launch Center - xx4#LaunchPad53.955,35.258Launch PadActiveIranSvobodny Cosmodrome - xx4#LaunchPad128.3386,51.7938Launch PadInactiveRussiaFlorida 1 (FL1) Leolut#GroundStation-80.3838,25.616Ground StationActiveUnited StatesDongfeng Station#GroundStation125.53,42.68Ground StationActiveChinaGreen River - Pad 3#LaunchPad-110.0741,38.9426Launch PadActiveUnited StatesBaikonur Cosmodrome LC 193#LaunchPad63.389,45.9532Launch PadActiveKazakhstanBeijing Station#GroundStation116.2275,40.1172Ground StationActiveChinaBaikonur Cosmodrome LC 41-4#LaunchPad63.6649,45.9759Launch PadActiveKazakhstanBrunei#GroundStation114.929,4.946Ground StationActiveBruneiMatagorda Island#LaunchSite-96.46,28.32Launch SiteActiveUnited StatesDombarovsky Cosmodrome - x41#LaunchPad60.178331,51.50362Launch PadActiveRussiaMaiquetia (1) Leolut#GroundStation-66.982,10.598Ground StationPlannedVenezuelaBaikonur Cosmodrome - LC 250#LaunchPad63.3049,46.0083Launch PadActiveKazakhstanProspect Harbor Naval Sat Station#GroundStation-68.013,44.404Ground StationActiveUnited StatesGISTDA Ground Receiving Station#GroundStation100.788982,13.730775Ground StationActiveThailandKoganei xx1#GroundStation139.488503,35.711393Ground StationActiveJapanTanegashima Space Center - Sounding Rocket Pad#LaunchPad130.962691,30.376847Launch PadActiveJapanGatineau xx2#GroundStation-75.80811,45.58435Ground StationActiveCanadaGreen Bank Telescope 20m#GroundStation-79.82551,38.43684Ground StationActiveUnited StatesWest Freugh Ground Station#GroundStation-4.946,54.846Ground StationActiveUnited KingdomCUHK Station#GroundStation114.206591,22.421278Ground StationActiveChinaBaikonur Cosmodrome - LC 196#LaunchPad63.1477,45.8283Launch PadActiveKazakhstanMaspalomas 1 Geolut#GroundStation-15.634,27.764Ground StationActiveSpainKatsuura USB F2#GroundStation140.29975,35.20592Ground StationActiveJapanOrroral Valley STDN ORRL#LaserStation148.95480464,-35.62492722Laser StationActiveAustraliaBiscarosse BE#LaunchPad-1.2727,44.2742Launch PadActiveFranceKapustin Yar LC 107 2g#LaunchPad46.2958,48.5616Launch PadActiveRussiaDombarovsky Cosmodrome - x32#LaunchPad60.607974,51.241789Launch PadActiveRussiaWallops Flight Facility - LA 2 RAG#LaunchPad-75.4823,37.8385Launch PadActiveUnited StatesNorth Pole Station USAK01 STDN USAS#GroundStation-147.50021417,64.80424111Ground StationActiveUnited StatesDkhila Mateur#GroundStation9.714,36.88Ground StationActiveTunisiaCavalier AFS PARCS#RadarStation-97.8998,48.7246Radar StationActiveUnited StatesGreen River Pad 2#LaunchPad-110.0753,38.9413Launch PadActiveUnited StatesPoker Flat Station PF1 (STDN DX2S)#GroundStation-147.4311625,65.11783389Ground StationActiveUnited StatesHammaguira - Brigitte#LaunchPad-3.0357,30.8935Launch PadActiveAlgeriaBangkok (2) Leolut#GroundStation100.5432,13.717166Ground StationActiveThailandEglin AFB AN-FPS 85 PAR (STDN EG2F)#RadarStation-86.21471742,30.57252794Radar StationActiveUnited StatesPalmer Station#GroundStation-64.05107,-64.77413Ground StationActiveAntarcticaBaikonur Cosmodrome - LC 160#LaunchPad62.9423,46.0783Launch PadActiveKazakhstanOverberg Test Range xx2#LaunchPad20.25879,-34.61745Launch PadActiveSouth AfricaBarking Sands - LC 1#LaunchPad-159.777,22.058Launch PadActiveUnited StatesPonce de Leon STDN PDLS#GroundStation-80.91302136,29.06664839Ground StationActiveUnited StatesJiuquan Satellite Launch Center - SLS 2#LaunchPad100.2983,40.9607Launch PadActiveChinaCape Canaveral AFS LC 6#LaunchPad-80.5726,28.4407Launch PadInactiveUnited StatesOlenegorsk Radar#RadarStation33.9107,68.1137Radar StationActiveRussiaNHS STDN NHSS#GroundStation-71.62656253,42.94782133Ground StationActiveUnited StatesFort Huachuca STDN FTHF#GroundStation-110.37079797,31.57102425Ground StationActiveUnited StatesSvobodny Cosmodrome x12#LaunchPad128.252,51.8818Launch PadInactiveRussiaMount Stromlo SLR Station STDN MTSL#LaserStation149.00987869,-35.31614697Laser StationActiveAustraliaEuropean Direct Access Facility#GroundStation11.2789,48.0862Ground StationActiveGermanyWhite Sands - LC 50#LaunchPad-106.3488,32.4064Launch PadActiveUnited StatesGreen River - Pad 1#LaunchPad-110.0775,38.9416Launch PadActiveUnited StatesBaikonur Cosmodrome LC 90-20#LaunchPad62.9167,46.0855Launch PadActiveKazakhstanOrbcomm Matera B#GroundStation16.70869,40.64935Ground StationActiveItalyPulkovo#OpticalTrackingStation30.32737,59.77186Optical Tracking StationActiveRussiaTERSS#GroundStation147.42215,-42.92417Ground StationActiveAustraliaRaisting Station#GroundStation11.1126,47.8978Ground StationActiveGermanyYevpatoria 32m#GroundStation33.25016,45.17031Ground StationActiveUkraineAlcantara Launch Center UL Pad#LaunchPad-44.367,-2.316Launch PadActiveBrazilDombarovsky Cosmodrome - x25#LaunchPad59.524663,51.153297Launch PadActiveRussiaSeaMobile Holmdel Teleport#GroundStation-74.1732,40.3945Ground StationActiveUnited StatesIssus-Aussaguel STDN AUSS#GroundStation1.499412,43.428696Ground StationActiveFranceAlbany Leolut#GroundStation117.899,-35.12Ground StationActiveAustraliaVLBA Fort Davis#GroundStation-103.944817,30.635031Ground StationActiveUnited StatesBiscarosse - BE#LaunchPad-1.2727,44.2742Launch PadActiveFranceKwajalein RADOT#OpticalTrackingStation167.719221,8.723176Optical Tracking StationActiveMarshall IslandsPoker Flat - LC 6#LaunchPad-147.4621,65.1169Launch PadActiveUnited StatesLeuk#GroundStation7.645278,46.318056Ground StationActiveSwitzerlandGilmore Creek STDN GLBS#GroundStation-147.50869853,64.97348203Ground StationActiveUnited StatesWhite Sands STDN WH6F#GroundStation-106.65901228,33.81386394Ground StationActiveUnited StatesManaus Leolut#GroundStation-60.054,-3.023166Ground StationActiveBrazilAuScope VLBI Yarragadee#GroundStation115.3456,-29.0471Ground StationActiveAustraliaWallops Island STDN WL6S#GroundStation-75.4611,37.9456Ground StationActiveUnited StatesCape Canaveral ROCC#GroundStation-80.5949,28.4311Ground StationActiveUnited StatesPoint Mugu STDN PM2F#GroundStation-119.15379856,34.12250292Ground StationActiveUnited StatesXichang LA 2#LaunchPad102.0271,28.2455Launch PadActiveChinaNorth Pole Station USAK02#GroundStation-147.5003,64.8048Ground StationActiveUnited StatesBiscarosse - SUD#LaunchPad-1.276,44.295Launch PadActiveFranceDombarovsky Cosmodrome x33#LaunchPad60.142215,51.248931Launch PadActiveRussiaCayenne Station#GroundStation-52.30968,4.94777Ground StationActiveFrench GuianaMillstone Hill Radar#RadarStation-71.491,42.6174Radar StationActiveUnited StatesTranquillon Peak STDN VD4F#GroundStation-120.56111494,34.58304294Ground StationActiveUnited StatesEtam Earth Station#GroundStation-79.736,39.281Ground StationActiveUnited StatesCalifornia 2 (CA2) Leolut#GroundStation-120.5517,34.662333Ground StationActiveUnited StatesFort Meade#GroundStation-76.765,39.102Ground StationActiveUnited StatesInmarsat Station Pune#GroundStation73.957,19.151Ground StationActiveIndiaXiamen Station#GroundStation118.09,24.48Ground StationActiveChinaPlesetsk Cosmodrome - LC 43-3#LaunchPad40.4501,62.9273Launch PadActiveRussiaKapustin Yar - START Pioner#LaunchPad46.3009,48.6149Launch PadActiveRussiaBaikonur Cosmodrome - LC 31#LaunchPad63.5643,45.9961Launch PadActiveKazakhstanKaneohe Omega Transmitter#GroundStation-157.8307,21.4048Ground StationInactiveUnited StatesMerritt Island STDN EULY#GroundStation-80.65301219,28.46356408Ground StationActiveUnited StatesMt Lemmon STDN MTLF#GroundStation-110.78880306,32.44166322Ground StationActiveUnited StatesNakhodka Krona Complex#RadarStation132.577073,42.935333Radar StationActiveRussiaVTS STDN VT2S#GroundStation-120.50539767,34.82564078Ground StationActiveUnited StatesThermopylae Station#GroundStation22.6866,38.8231Ground StationActiveGreeceGrand Turk Island STDN GTKQ#GroundStation-71.13208822,21.46263161Ground StationActiveUnited KingdomFort Monmouth#GroundStation-74.036,40.321Ground StationActiveUnited StatesJonathan Dickinson STDN JDIY#GroundStation-80.10874725,26.98380394Ground StationActiveUnited StatesBaikonur Cosmodrome - LC 41-4#LaunchPad63.6649,45.9759Launch PadActiveKazakhstanOverberg Test Range - xx2#LaunchPad20.25879,-34.61745Launch PadActiveSouth AfricaDombarovsky Cosmodrome x45#LaunchPad59.956989,51.558712Launch PadActiveRussiaFairbanks STDN ULAE#GroundStation-147.51806617,64.97681389Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 3#LaunchPad-80.5363,28.4662Launch PadInactiveUnited StatesDombarovsky Cosmodrome - x34#LaunchPad60.461524,51.259129Launch PadActiveRussiaDombarovsky Cosmodrome - x40#LaunchPad60.372543,51.379286Launch PadActiveRussiaGunma Leolut#GroundStation138.955,36.426Ground StationActiveJapanOverberg Test Range xx1#LaunchPad20.30271,-34.60276Launch PadActiveSouth AfricaSatish Dhawan Pad 1#LaunchPad80.2346,13.7334Launch PadActiveIndiaCape Canaveral Air Force Station - LC 1#LaunchPad-80.5375,28.465Launch PadInactiveUnited StatesDSS 65 Robledo prior to 2005#GroundStation-4.25141796,40.42718512Ground StationRelocatedSpainSturup Station#GroundStation13.349467,55.541122Ground StationActiveSwedenBaikonur Cosmodrome LC 196#LaunchPad63.1477,45.8283Launch PadActiveKazakhstanMSSC Beam Director Tracker#OpticalTrackingStation-156.2576,20.70855Optical Tracking StationActiveUnited StatesPaumalu Teleport#GroundStation-158.034,21.67Ground StationActiveUnited StatesBaikonur Cosmodrome LC 242#LaunchPad63.4626,45.9407Launch PadActiveKazakhstanKerguelen Island STDN KGLQ#GroundStation70.25598389,-49.35191544Ground StationActiveFranceSantiago Leolut#GroundStation-70.7,-33.489Ground StationActiveChileDombarovsky Cosmodrome x16#LaunchPad59.484369,51.06922Launch PadActiveRussiaBaikonur Cosmodrome - LC 107#LaunchPad63.81,46.046Launch PadActiveKazakhstanPoint Mugu STDN PM4F#GroundStation-119.15283014,34.12211825Ground StationActiveUnited StatesVandenberg AFB SLC 1W#LaunchPad-120.6303,34.7571Launch PadInactiveUnited StatesSan Marco Launch Platform#LaunchSite40.2125,-2.9383Launch SiteInactiveKenyaEsrange Station ELS STDN KILS#GroundStation21.062337,67.876532Ground StationActiveSwedenGTS STDN GT2S#GroundStation144.85543775,13.61588064Ground StationActiveUnited StatesCold Lake#LaunchSite-110.28,54.41Launch SiteActiveCanadaSvobodny Cosmodrome - xx3#LaunchPad128.185,51.792Launch PadInactiveRussiaLabuan Teleport#GroundStation115.198261,5.33198Ground StationActiveMalaysiaFrance Sud#GroundStation2.12416,43.27977Ground StationActiveFranceBaikonur Cosmodrome#LaunchSite63.3,46Launch SiteActiveKazakhstanBaikonur Cosmodrome xx1#LaunchPad63.462718,45.939593Launch PadActiveKazakhstanCape Canaveral Air Force Station - LC 11#LaunchPad-80.5395,28.4753Launch PadInactiveUnited StatesWoomera Test Range#LaunchSite136.5,-30.95Launch SiteActiveAustraliaObninsk#GroundStation36.62,55.08Ground StationActiveRussiaPoker Flat#LaunchSite-147.485,65.129Launch SiteActiveUnited StatesHaystack Radar#RadarStation-71.4881,42.62329Radar StationActiveUnited StatesAlcantara Launch Center RAG Pad#LaunchPad-44.367,-2.3148Launch PadActiveBrazilMickelsen Safeguard Complex#RadarStation-98.3565,48.5895Radar StationInactiveUnited StatesXichang LA 4#LaunchPad102.0232,28.2495Launch PadActiveChinaBaikonur Cosmodrome LC 31#LaunchPad63.5643,45.9961Launch PadActiveKazakhstanFresnedillas STDN MAD8#GroundStation-4.16838497,40.45544939Ground StationRelocatedSpainCape Canaveral Air Force Station - LC 34#LaunchPad-80.5611,28.5218Launch PadInactiveUnited StatesKrona 20J6 Complex#RadarStation41.342745,43.825373Radar StationActiveRussiaBangalore Leolut#GroundStation77.5117,13.034833Ground StationActiveIndiaBaikonur Cosmodrome - LC 105#LaunchPad63.4962,45.9503Launch PadActiveKazakhstanGreenbelt STDN BLT3#GroundStation-76.84276675,38.9984475Ground StationInactiveUnited StatesRiyadh#GroundStation46.5865,24.4259Ground StationActiveSaudi ArabiaBaikonur Cosmodrome - LC 246#LaunchPad63.4236,45.7656Launch PadActiveKazakhstanAscent Media Northvale#GroundStation-73.9414,41.0161Ground StationActiveUnited StatesHTSA#GroundStation-158.242109,21.56228Ground StationActiveUnited StatesCordoba#GroundStation-64.4636,-31.5242Ground StationActiveArgentinaOnsala Observatory 20m#GroundStation11.92632,57.39582Ground StationActiveSwedenPoker Flat Station PF2 (STDN DXAS)#GroundStation-147.43350389,65.11792972Ground StationActiveUnited StatesGreen Bank 85-1 Telescope#GroundStation-79.82817,38.43586Ground StationActiveUnited StatesOkinawa USB F2#GroundStation127.90353,26.49976Ground StationActiveJapanCape Canaveral AFS LC 26A#LaunchPad-80.5705,28.4446Launch PadInactiveUnited StatesNoto Radio Observatory#GroundStation14.989031,36.87605Ground StationActiveItalyGTSB#GroundStation144.8554464,13.6158802Ground StationActiveUnited StatesPalmachim Air Force Base - Arrow II Launcher#LaunchPad34.7023,31.8859Launch PadActiveIsraelCamp Roberts#GroundStation-120.754,35.736Ground StationActiveUnited StatesWoomera Test Range - LA 6A#LaunchPad136.4394,-31.074Launch PadActiveAustraliaHartebeesthoek STDN HB4S#GroundStation27.71260331,-25.88672544Ground StationActiveSouth AfricaDSS 66 Robledo STDN DS66#GroundStation-4.25141764,40.42997486Ground StationActiveSpainMojave Air and Space Port#LaunchSite-118.15,35.06Launch SitePlannedUnited StatesOrbcomm Almaty A#GroundStation76.78235,44.49732Ground StationActiveKazakhstanArabsat Amman Station#GroundStation35.9315,31.9053Ground StationActiveJordanAGS STDN AG1S#GroundStation-147.46120417,65.11670028Ground StationActiveUnited StatesWallops Island STDN WTDQ#GroundStation-75.47746889,37.92306592Ground StationActiveUnited StatesDSS 13 Goldstone legacy#GroundStation-116.79488,35.24773Ground StationInactiveUnited StatesDocklands Teleport#GroundStation0.058858,51.49939Ground StationActiveUnited KingdomJiuquan xx1#LaunchPad100.304946,41.280432Launch PadActiveChinaPoint Arguello Drop Zone#LaunchSite-123,36Launch SiteActiveUnited StatesKatsuura USB F1#GroundStation140.29886,35.21086Ground StationActiveJapanLandsat Station STDN SF2S#GroundStation-96.61943778,43.73428667Ground StationActiveUnited StatesCeduna Observatory#GroundStation133.80968,-31.86772Ground StationActiveAustraliaInuvik Station IVK#GroundStation-133.53816,68.31762Ground StationActiveCanadaAbuja Leolut#GroundStation7.493,9.076Ground StationActiveNigeriaTanegashima Space Center - Osaki Range#LaunchPad130.970274,30.39953Launch PadInactiveJapanJeddah (1) Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaWhite Sands LC 94#LaunchPad-106.4589,34.2049Launch PadActiveUnited StatesAerospace Data Facility Colorado#GroundStation-104.7765,39.7173Ground StationActiveUnited StatesMaspalomas STDN MPLS#GroundStation-15.6338,27.762892Ground StationActiveSpainAdelaide Satellite Facility#GroundStation138.572,-34.8627Ground StationActiveAustraliaGagan Super RADOT#OpticalTrackingStation167.5376,9.286663Optical Tracking StationActiveMarshall IslandsLasham Satellite Ground Station#GroundStation-1.044,51.184Ground StationDemolishedUnited KingdomDSS 11 Goldstone STDN PIOD#GroundStation-116.84937706,35.38951828Ground StationInactiveUnited StatesBaikonur Cosmodrome LC 70#LaunchPad63.0963,46.0329Launch PadActiveKazakhstanKapustin Yar LC 107 2f#LaunchPad46.2958,48.5688Launch PadActiveRussiaKootwijk STDN KOOL#LaserStation5.80976658,52.1783975Laser StationActiveNetherlandsOkinawa USB F1#GroundStation127.90179,26.49815Ground StationActiveJapanIP-5 Tracking Station#GroundStation63.34,45.704Ground StationActiveKazakhstanDombarovsky Cosmodrome x19#LaunchPad59.844333,51.093509Launch PadActiveRussiaTonopah Test Range#LaunchSite-116.77,37.8Launch SiteActiveUnited StatesOverberg Earth Station 4m#GroundStation20.2206,-34.6202Ground StationActiveSouth AfricaKapustin Yar LC 107 2e#LaunchPad46.2949,48.5688Launch PadActiveRussiaGila River Space Fence#RadarStation-112.031,33.113Radar StationActiveUnited StatesAtlantic Test Range#GroundStation-76.378,38.296Ground StationActiveUnited StatesCape Canaveral AFS LC 12#LaunchPad-80.5421,28.4806Launch PadInactiveUnited StatesTranquillon Peak STDN VD3F#GroundStation-120.56111494,34.58304294Ground StationActiveUnited StatesEdwards AFB STDN FR2F#GroundStation-117.91187492,34.95773825Ground StationActiveUnited StatesBigen Island#LaunchSite171.0428,8.3646Launch SiteActiveMarshall IslandsLovell Telescope#GroundStation-2.308724,53.236548Ground StationActiveUnited KingdomSonmiani#LaunchSite66.75,25.2Launch SiteActivePakistanSt Hubert#GroundStation-73.39675,45.518836Ground StationActiveCanadaHartebeesthoek, Europ Star#GroundStation27.70793,-25.88549Ground StationActiveSouth AfricaSanta Ana#OpticalTrackingStation-64.624,-21.5963Optical Tracking StationActiveBoliviaPlesetsk Cosmodrome - LC 32-1#LaunchPad40.7872,62.9073Launch PadActiveRussiaDombarovsky Cosmodrome - x10#LaunchPad60.649859,50.985518Launch PadActiveRussiaCuiaba Station#GroundStation-56.0734,-15.5525Ground StationActiveBrazilPlesetsk Cosmodrome LC 43-4#LaunchPad40.4572,62.9288Launch PadActiveRussiaKapustin Yar LC 86 4a#LaunchPad46.295,48.5508Launch PadActiveRussiaEVCF STDN EVCS#GroundStation-80.576,28.486Ground StationActiveUnited StatesPoker Flat LC 6#LaunchPad-147.4621,65.1169Launch PadActiveUnited StatesCape Canaveral AFS LC 32#LaunchPad-80.5556,28.4537Launch PadInactiveUnited StatesBarking Sands - LC 10#LaunchPad-159.7816,22.0569Launch PadActiveUnited StatesSvobodny Cosmodrome - xx2#LaunchPad128.4085,51.7734Launch PadInactiveRussiaBangkok 1 Leolut#GroundStation100.5433,13.717166Ground StationActiveThailandEdwards AFB STDN EA3F#GroundStation-118.09132969,34.93811433Ground StationActiveUnited StatesStellenbosch University#GroundStation18.86608,-33.92826Ground StationActiveSouth AfricaBeale AFB PAVE PAWS#RadarStation-121.3506,39.1361Radar StationActiveUnited StatesKourou Station#GroundStation-52.80466242,5.25143694Ground StationActiveFrench GuianaOnsala Observatory 25m#GroundStation11.9178,57.39307Ground StationActiveSwedenSvalsat SG 1 STDN SG1S#GroundStation15.38969589,78.23072231Ground StationActiveNorwayTVF1#GroundStation-80.575793,28.4858933Ground StationActiveUnited StatesWoomera Test Range LA 4#LaunchPad136.5155,-30.9053Launch PadActiveAustraliaHawaii 2 HI2 Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesUsingen Station#GroundStation8.48,50.333Ground StationActiveGermanyWallops Flight Facility - LA 2 MLS#LaunchPad-75.4835,37.8375Launch PadActiveUnited StatesLegan Super RADOT#OpticalTrackingStation167.578487,8.981922Optical Tracking StationActiveMarshall IslandsAscension AAF Radar#RadarStation-14.412,-7.951Radar StationActiveUnited KingdomDSS 41 Island Lagoon#GroundStation136.8875,-31.382Ground StationDemolishedAustraliaLushan Station#GroundStation116,29.7Ground StationActiveChinaEsrange Rocket Range C#LaunchPad21.1029,67.8931Launch PadActiveSwedenRota SATCOM Terminal#GroundStation-6.367,36.63747Ground StationActiveSpainWoomera Test Range LA 1#LaunchPad136.5037,-30.9587Launch PadActiveAustraliaWhite Sands STDN WH5K#GroundStation-106.60855361,32.50124183Ground StationActiveUnited StatesJordan Lake Space Fence#RadarStation-86.2636,32.6588Radar StationActiveUnited StatesTaiyuan Satellite Launch Center - South Pad#LaunchPad111.60666,38.83538Launch PadActiveChinaTanegashima Space Center - Yoshinobu LC, Pad 2#LaunchPad130.975497,30.400919Launch PadActiveJapanBaikonur Cosmodrome - LC 60-6#LaunchPad64.0161,46.0188Launch PadActiveKazakhstanTanegashima Sounding Rocket Pad#LaunchPad130.962691,30.376847Launch PadActiveJapanWallops Flight Facility - LA 3B#LaunchPad-75.4725,37.8494Launch PadActiveUnited StatesDombarovsky Cosmodrome - x15#LaunchPad60.492582,51.053644Launch PadActiveRussiaOverberg Test Range#LaunchSite20.28,-34.61Launch SiteActiveSouth AfricaDubna Teleport#GroundStation37.251,56.738Ground StationActiveRussiaDombarovsky Cosmodrome x32#LaunchPad60.607974,51.241789Launch PadActiveRussiaJiuquan Satellite Launch Center - LA 3B#LaunchPad100.7803,41.1593Launch PadActiveChinaTel Aviv#GroundStation34.9,32Ground StationActiveIsraelJoint Defense Facility Nurrungar#GroundStation136.776545,-31.323342Ground StationInactiveAustraliaCroughton Station#GroundStation-1.187,51.987Ground StationActiveUnited KingdomBermuda STDN BDAQ#GroundStation-64.65344947,32.34794319Ground StationInactiveBermudaOrbcomm Hartebeesthoek A#GroundStation27.70566,-25.88736Ground StationActiveSouth AfricaCape Canaveral Air Force Station - LC 30#LaunchPad-80.5803,28.4393Launch PadInactiveUnited StatesNobeyama Radio Observatory#GroundStation138.472609,35.944518Ground StationActiveJapanJeddah 1 Leolut#GroundStation39.1427,21.654833Ground StationActiveSaudi ArabiaDombarovsky Cosmodrome - x18#LaunchPad59.842142,51.094496Launch PadActiveRussiaNorth Pole Station USAK02#GroundStation-147.5003,64.8048Ground StationActiveUnited StatesRecife Leolut#GroundStation-34.925,-8.138333Ground StationActiveBrazilWoomera Test Range LA 3#LaunchPad136.5191,-30.93Launch PadActiveAustraliaSea Launch Mobile Platform#LaunchSite-154,0Launch SiteActiveInternationalPillar Point STDN PPTF#GroundStation-122.49869897,37.49769667Ground StationActiveUnited StatesSea Launch Mobile Platform#LaunchSite-154,0Launch SiteActiveInternationalBaikonur Cosmodrome - LC 194#LaunchPad63.301,45.854Launch PadActiveKazakhstanBurum Station#GroundStation6.2143,53.2842Ground StationActiveNetherlandsBarking Sands LC 19#LaunchPad-159.7813,22.0619Launch PadActiveUnited StatesWallops Flight Facility#LaunchSite-75.48,37.85Launch SiteActiveUnited StatesVernon Valley Teleport#GroundStation-74.5269,41.2018Ground StationActiveUnited StatesWoomera Test Range - LA 5B#LaunchPad136.4763,-30.9711Launch PadActiveAustraliaPalmachim Air Force Base - Shavit Pad#LaunchPad34.6802,31.8848Launch PadActiveIsraelCape Canaveral AFS LC 18A#LaunchPad-80.5623,28.4506Launch PadInactiveUnited StatesSouth Uist Missile Range#LaunchSite-7.4,57.36Launch SiteActiveUnited KingdomBrasilia Geolut#GroundStation-47.902666,-15.8572Ground StationActiveBrazilToulouse (1) Leolut#GroundStation1.4808,43.560666Ground StationActiveFranceGreen Bank Telescope 43m#GroundStation-79.83585,38.43773Ground StationActiveUnited StatesIstanbul ITU Station#GroundStation29.02729,41.1015Ground StationActiveTurkeyNashville Station#GroundStation-86.756,36.235Ground StationActiveUnited StatesCastle Rock Teleport#GroundStation-104.807303,39.276772Ground StationActiveUnited StatesRas Al Khaimah Spaceport#LaunchSite55.941,25.617Launch SitePlannedUnited Arab EmiratesBaikonur Cosmodrome LC 165#LaunchPad62.9185,45.9912Launch PadActiveKazakhstanBaikonur Cosmodrome LC 80-17#LaunchPad64.0198,46.0068Launch PadActiveKazakhstanL-3 Integrated Systems Test Range#GroundStation-96.056,33.07Ground StationActiveUnited StatesVandenberg AFB SLC 3E STDN WTEP#LaunchPad-120.59,34.64Launch PadActiveUnited StatesAndoya Rocket Range#LaunchSite16.021,69.294Launch SiteActiveNorwayAbastumani#OpticalTrackingStation42.82311,41.75444Optical Tracking StationActiveGeorgiaDombarovsky Cosmodrome x47#LaunchPad59.92692,51.600688Launch PadActiveRussiaThule STDN THUS#GroundStation-68.59901778,76.51629306Ground StationActiveGreenlandGuiana Space Center - ZL2 (STDN KR2P)#LaunchPad-52.77567156,5.23240672Launch PadInactiveFrench GuianaGreenbelt STDN BLTA#GroundStation-76.84193894,38.9982475Ground StationActiveUnited StatesIstana Station#GroundStation114.9231,4.8717Ground StationActiveBruneiWallops Flight Facility - LA 1#LaunchPad-75.4861,37.8352Launch PadActiveUnited StatesCape Canaveral AFS LC 14#LaunchPad-80.5471,28.4911Launch PadInactiveUnited StatesSGS Oakhanger Site STDN OTSS#GroundStation-0.89489706,51.11411733Ground StationActiveUnited KingdomALTAIR STDN KM2F#RadarStation167.47928833,9.39542881Radar StationActiveMarshall IslandsBangalore Geolut#GroundStation77.511666,13.0348Ground StationActiveIndiaLaurentides Station#GroundStation-74.5333,45.9444Ground StationActiveCanadaReach Stanley Station#GroundStation114.2722,22.2778Ground StationActiveChinaGagan#GroundStation167.53753,9.28686Ground StationActiveMarshall IslandsKapustin Yar - LC 107 2e#LaunchPad46.2949,48.5688Launch PadActiveRussiaHammaguira Beatrice#LaunchPad-3.0851,30.8601Launch PadActiveAlgeriaWoomera Test Range - LA 2#LaunchPad136.521,-30.9433Launch PadActiveAustraliaTRY ADD Radar#RadarStation73.573,45.8108Radar StationActiveKazakhstanKapustin Yar - LC 86 4b#LaunchPad46.295,48.5487Launch PadActiveRussiaBaikonur Cosmodrome LC 192#LaunchPad63.2995,46.0243Launch PadActiveKazakhstanPalmachim AFB Arrow II Launcher#LaunchPad34.7023,31.8859Launch PadActiveIsraelPoker Flat STDN WT2S#GroundStation-147.46154869,65.11673325Ground StationInactiveUnited StatesMaui GEODSS Cam2#OpticalTrackingStation-156.2575,20.7081Optical Tracking StationActiveUnited StatesVandenberg AFB SLC 6 STDN WT6P#LaunchPad-120.62609183,34.58163228Launch PadActiveUnited StatesAlcantara Launch Center - RAG Pad#LaunchPad-44.367,-2.3148Launch PadActiveBrazilPlesetsk Cosmodrome - LC 43-4#LaunchPad40.4572,62.9288Launch PadActiveRussiaDombarovsky Cosmodrome - x20#LaunchPad60.087307,51.096909Launch PadActiveRussiaDongara Station AUWA03#GroundStation115.3493,-29.04607Ground StationActiveAustraliaWhite Sands - LC 39#LaunchPad-106.2367,32.4161Launch PadActiveUnited StatesDombarovsky Cosmodrome - xx6#LaunchPad60.52463,50.877274Launch PadActiveRussiaSATCOM Ground Terminal Facility#GroundStation-75.890866,45.349824Ground StationActiveCanadaDombarovsky Cosmodrome - xx5#LaunchPad59.595072,50.837656Launch PadActiveRussiaBoa Vista Station#GroundStation-60.67,2.82Ground StationActiveBrazilMalargue DSA 3#GroundStation-69.3983,-35.776Ground StationPlannedArgentinaGuiana Space Center - ZLV#LaunchPad-52.775,5.236Launch PadActiveFrench GuianaBeijing (2) Leolut#GroundStation116.42,39.908Ground StationActiveChinaScanEx Irkutsk Station#GroundStation104.30864,52.275261Ground StationActiveRussiaPleumeur-Bodou Station#GroundStation-3.521,48.786Ground StationInactiveFranceSolna#GroundStation17.96916,59.35483Ground StationActiveSwedenCape Canaveral AFS LC 29#LaunchPad-80.5778,28.4285Launch PadInactiveUnited StatesMILA STDN MIL3#GroundStation-80.69339994,28.50812389Ground StationInactiveUnited StatesMaspalomas Leolut#GroundStation-15.634,27.764Ground StationActiveSpainWellington 1 Geolut#GroundStation175.5045,-41.152Ground StationActiveNew ZealandTanegashima Yoshinobu LC Pad 2#LaunchPad130.975497,30.400919Launch PadActiveJapanNARSS Aswan Station#GroundStation32.91,24.07Ground StationActiveEgyptMojave Apollo Station#GroundStation-116.88759,35.33163Ground StationActiveUnited StatesEsrange Station ESSEX#GroundStation21.0630978,67.8904339Ground StationActiveSwedenWallops Flight Facility - LA 2 AML-1#LaunchPad-75.4828,37.8381Launch PadActiveUnited StatesWallops Island STDN DS87#GroundStation-75.47630453,37.92658986Ground StationActiveUnited StatesHartebeesthoek STDN HB5S#GroundStation27.70667183,-25.8869335Ground StationActiveSouth AfricaRecife Station#GroundStation-34.9,-8.04Ground StationActiveBrazilVery Large Array#GroundStation-107.618283,34.078749Ground StationActiveUnited StatesClear AFS PAVE PAWS#RadarStation-149.189444,64.288611Radar StationActiveUnited StatesWallops Flight Facility - LA 2 JUP#LaunchPad-75.482,37.8394Launch PadActiveUnited StatesXTAR 6m 1#GroundStation-15.63075,27.76268Ground StationActiveSpainCape Canaveral AFS LC 13#LaunchPad-80.5446,28.4859Launch PadInactiveUnited StatesBari Leolut#GroundStation16.8477,41.137666Ground StationActiveItalyDombarovsky Cosmodrome x39#LaunchPad60.472701,51.346817Launch PadActiveRussiaPlesetsk Cosmodrome LC 16-2#LaunchPad40.6834,62.96Launch PadActiveRussiaEglin AFB Launch Site#LaunchSite-85.34588,29.67729Launch SiteActiveUnited StatesFlorida 1 FL1 Leolut#GroundStation-80.3838,25.616Ground StationActiveUnited StatesNudol Station#GroundStation36.5033,56.15Ground StationActiveRussiaGEROC Ikonos Station#GroundStation11.28006,48.08407Ground StationActiveGermanyBaikonur Cosmodrome - LC 245#LaunchPad63.5271,45.8175Launch PadActiveKazakhstanSwedish-ESO Submillimetre Telescope#GroundStation-70.73234,-29.26345Ground StationInactiveChileSantiago Station AGO 5 STDN AG23#GroundStation-70.66731169,-33.15179414Ground StationActiveChileSylmar Station#GroundStation-118.4875,34.3152Ground StationActiveUnited StatesCape Canaveral Air Force Station - SLC 40 (STDN A40P)#LaunchPad-80.57719364,28.56198939Launch PadActiveUnited StatesBaikonur Cosmodrome - xx4#LaunchPad62.935495,46.079747Launch PadActiveKazakhstanVikram Sarabhai#LaunchSite76.869836,8.530116Launch SiteActiveIndiaWallops Island STDN WP2S#GroundStation-75.47441625,37.92806697Ground StationActiveUnited StatesWhite Sands - LC 36#LaunchPad-106.322,32.417Launch PadActiveUnited StatesLake Cowichan Station#GroundStation-124.07536,48.843501Ground StationActiveCanadaTutuila BRT STDN AMSJ#GroundStation-170.71941667,-14.33144444Ground StationActiveUnited StatesOffutt AFB SATCOM Terminal#GroundStation-95.911,41.1086Ground StationActiveUnited StatesCape Canaveral AFS LC 31#LaunchPad-80.5563,28.4519Launch PadInactiveUnited StatesCape Canaveral Air Force Station - SLC 20#LaunchPad-80.5567,28.5122Launch PadActiveUnited StatesMaadi Earth Station#GroundStation31.275,29.967Ground StationActiveEgyptDombarovsky Cosmodrome - x44#LaunchPad59.911557,51.54739Launch PadActiveRussiaZenith Antenna#RadarStation-71.49122,42.619Radar StationActiveUnited StatesSaipan STDN SIPQ#GroundStation145.79621667,15.24914583Ground StationActiveUnited StatesDSS 54 Robledo STDN DS54#GroundStation-4.25409683,40.42562167Ground StationActiveSpainCape Canaveral Air Force Station - LC 32#LaunchPad-80.5556,28.4537Launch PadInactiveUnited StatesBaikonur Cosmodrome LC 108#LaunchPad63.815,46.046Launch PadActiveKazakhstanBACC#GroundStation116.257092,40.071989Ground StationActiveChinaWhite Sands STDN WHSK#GroundStation-106.60855172,32.50101206Ground StationActiveUnited StatesDombarovsky Cosmodrome x15#LaunchPad60.492582,51.053644Launch PadActiveRussiaPlesetsk Cosmodrome LC 132-1#LaunchPad40.8686,62.8833Launch PadActiveRussiaCape Canaveral Air Force Station - LC 21#LaunchPad-80.5403,28.4606Launch PadInactiveUnited StatesPoker Flat LC 3#LaunchPad-147.4851,65.1299Launch PadActiveUnited StatesPlesetsk Cosmodrome LC 43-3#LaunchPad40.4501,62.9273Launch PadActiveRussiaWeilheim STDN WU1S#GroundStation11.0853025,47.88006944Ground StationActiveGermanyKoganei STDN KOGL#LaserStation139.48827494,35.71027478Laser StationActiveJapanEsrange Rocket Range S#LaunchPad21.1054,67.8932Launch PadActiveSwedenSvalsat SG 2 STDN KLMS#GroundStation15.39813814,78.23022167Ground StationActiveNorwayNCTS Diego Garcia#GroundStation72.363,-7.266Ground StationActiveUnited KingdomNortheastern Space Radio Observatory#GroundStation-38.42599,-3.87812Ground StationActiveBrazilHTS STDN HT2S#GroundStation-158.26227883,21.56897181Ground StationActiveUnited StatesGreenbelt Leolut#GroundStation-76.841,38.998666Ground StationActiveUnited StatesDombarovsky Cosmodrome - x22#LaunchPad59.634472,51.11475Launch PadActiveRussiaWallops Island STDN WL3S#GroundStation-75.46043669,37.94579497Ground StationActiveUnited StatesWallops Flight Facility - LA 3#LaunchPad-75.4725,37.8506Launch PadActiveUnited StatesMartlesham Heath Station#GroundStation1.2874,52.0597Ground StationActiveUnited KingdomHawaii 2 (HI2) Leolut#GroundStation-157.9963,21.520666Ground StationActiveUnited StatesMaui GEODSS, xx1#OpticalTrackingStation-156.2575,20.7085Optical Tracking StationActiveUnited StatesHartebeesthoek Ku 13m#GroundStation27.70745,-25.88547Ground StationActiveSouth AfricaDombarovsky Cosmodrome - xx1#LaunchPad59.655376,50.658373Launch PadActiveRussiaSanya Ground Station#GroundStation109.311,18.313Ground StationActiveChinaClarksburg Teleport#GroundStation-77.270401,39.218075Ground StationActiveUnited StatesToulouse Geolut#GroundStation1.480833,43.5587Ground StationActiveFranceSvobodny Cosmodrome - xx9#LaunchPad128.3656,51.837Launch PadInactiveRussiaDombarovsky Cosmodrome - x16#LaunchPad59.484369,51.06922Launch PadActiveRussiaBaikonur Cosmodrome - LC 162-36#LaunchPad63.0668,46.0323Launch PadActiveKazakhstanHong Kong 2 Leolut#GroundStation114.1445,22.275833Ground StationActiveChinaDiego Garcia GEODSS, xx1#OpticalTrackingStation72.45203,-7.41162Optical Tracking StationActiveUnited KingdomBeijing 1 Leolut#GroundStation116.42,39.908Ground StationActiveChinaKapustin Yar - LC 107 2f#LaunchPad46.2958,48.5688Launch PadActiveRussiaFort Belvoir SATCOM Terminal#GroundStation-77.145,38.726Ground StationActiveUnited StatesCape Canaveral Air Force Station - LC 12#LaunchPad-80.5421,28.4806Launch PadInactiveUnited StatesBaikonur Cosmodrome LC 250#LaunchPad63.3049,46.0083Launch PadActiveKazakhstanDSS 33 Tidbinbilla#GroundStation148.98304,-35.400549Ground StationDemolishedAustraliaAscension Island BRT STDN ACNJ#GroundStation-14.39076792,-7.91791558Ground StationActiveUnited KingdomJonathan Dickinson STDN JDIQ#GroundStation-80.10820406,26.98299969Ground StationActiveUnited StatesKiruna Station 13m#GroundStation20.96688,67.858428Ground StationActiveSwedenKennedy Space Center#LaunchSite-80.62,28.62Launch SiteActiveUnited StatesJakarta Leolut#GroundStation106.656,-6.126166Ground StationActiveIndonesiaVandenberg AFB SLC 3W STDN WT3P#LaunchPad-120.59285703,34.64367528Launch PadActiveUnited StatesKapustin Yar LC 86 4b#LaunchPad46.295,48.5487Launch PadActiveRussiaWallops Flight Facility LA 4#LaunchPad-75.4701,37.8508Launch PadActiveUnited StatesSan Nicolas#LaunchSite-119.52208,33.27981Launch SiteActiveUnited StatesDombarovsky Cosmodrome - x14#LaunchPad59.958431,51.036288Launch PadActiveRussiaAnhueng#LaunchSite126.47,36.7Launch SiteActiveSouth KoreaBlaavand Station#GroundStation8.1137,55.5571Ground StationActiveDenmarkBari Geolut#GroundStation16.847,41.137Ground StationActiveItalyChung-Li Station#GroundStation121.18705,24.96783Ground StationActiveTaiwanCape Canaveral Air Force Station - LC 29#LaunchPad-80.5778,28.4285Launch PadInactiveUnited StatesDish Antenna Facility#GroundStation-122.179604,37.40854Ground StationActiveUnited StatesOrbcomm Arcade A#GroundStation-78.382119,42.524867Ground StationActiveUnited StatesGerman Space Operations Center#GroundStation11.28123,48.08716Ground StationActiveGermanyCRISP Station#GroundStation103.774,1.2977Ground StationActiveSingaporeDombarovsky Cosmodrome - x33#LaunchPad60.142215,51.248931Launch PadActiveRussiaKACST Station#GroundStation46.642,24.71Ground StationActiveSaudi ArabiaUsuda#GroundStation138.3627,36.1325Ground StationActiveJapanCape Canaveral Air Force Station - SLC 17A (STDN A17P)#LaunchPad-80.56492617,28.44716106Launch PadActiveUnited StatesJiuquan Satellite Launch Center - LA 2B#LaunchPad100.3132,41.3061Launch PadActiveChinaMaui GEODSS, xx2#OpticalTrackingStation-156.2578,20.7081Optical Tracking StationActiveUnited StatesComcast Denver Station#GroundStation-104.9386,39.5793Ground StationActiveUnited StatesSvobodny Cosmodrome xx7#LaunchPad128.301,51.823Launch PadInactiveRussiaDombarovsky Cosmodrome - x21#LaunchPad59.576451,51.102299Launch PadActiveRussiaTaiyuan South Pad#LaunchPad111.60666,38.83538Launch PadActiveChinaKapustin Yar - xx4#LaunchPad46.299018,48.615791Launch PadActiveRussiaSondrestrom Radar Facility#RadarStation-50.9459,66.9856Radar StationActiveGreenlandAlgiers Leolut#GroundStation3.381,36.753333Ground StationActiveAlgeriaBiscarosse - BESA#LaunchPad-1.2335,44.3917Launch PadActiveFranceSedlec-Prcice Station#GroundStation14.5247,49.5919Ground StationActiveCzech RepublicJonathan Dickinson STDN JD2Y#GroundStation-80.10756081,26.98222986Ground StationActiveUnited StatesSvalRak#LaunchSite11.850278,78.931389Launch SiteActiveNorwayWhite Sands#LaunchSite-106.3,32.4Launch SiteActiveUnited StatesSantiago Station AGO 3 (STDN AGO3)#GroundStation-70.666403,-33.15110747Ground StationActiveChileKwajalein X-band Radar#RadarStation167.733133,8.724319Radar StationActiveMarshall IslandsEl Palomar Leolut#GroundStation-58.6,-34.6Ground StationActiveArgentinaSary Shagan Radar#RadarStation74.52,46.62Radar StationActiveKazakhstan \ No newline at end of file diff --git a/Apps/Sandcastle/.eslintrc.json b/Apps/Sandcastle/.eslintrc.json new file mode 100644 index 000000000000..d740cb4f1c1f --- /dev/null +++ b/Apps/Sandcastle/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "extends": "../../.eslintrc.json", + "globals": { + "JSON": true, + "require": true, + "console": true, + "Sandcastle": true, + "Cesium": true + }, + "rules": { + "no-alert": ["off"], + "no-implicit-globals": "off", + "no-unused-vars": ["off"] + } +} diff --git a/Apps/Sandcastle/CesiumSandcastle.css b/Apps/Sandcastle/CesiumSandcastle.css index 047d6e0905ac..ee3b6324ebfa 100644 --- a/Apps/Sandcastle/CesiumSandcastle.css +++ b/Apps/Sandcastle/CesiumSandcastle.css @@ -136,12 +136,6 @@ html, body { border: none; width: 100%; height: 100%; - -moz-transition-property: -moz-transform; - -moz-transition-duration: 0.5s; - -moz-transform-origin: 200px 150px; - -webkit-transition-property: -webkit-transform; - -webkit-transition-duration: 0.5s; - -webkit-transform-origin: 200px 150px; transition-property: transform; transition-duration: 0.5s; transform-origin: 200px 150px; @@ -150,8 +144,6 @@ html, body { .makeThumbnail { width: 900px; height: 600px; - -moz-transform: scale(0.25); - -webkit-transform: scale(0.25); transform: scale(0.25); } diff --git a/Apps/Sandcastle/CesiumSandcastle.js b/Apps/Sandcastle/CesiumSandcastle.js index 92fdba14ffdd..105ed122cf4b 100644 --- a/Apps/Sandcastle/CesiumSandcastle.js +++ b/Apps/Sandcastle/CesiumSandcastle.js @@ -139,7 +139,7 @@ require({ var subtabs = {}; var docError = false; var galleryError = false; - var notFound = false; + var deferredLoadError = false; var galleryTooltipTimer; var activeGalleryTooltipDemo; var demoTileHeightRule = findCssStyle('.demoTileThumbnail'); @@ -367,6 +367,7 @@ require({ } // make a copy of the options, JSHint modifies the object it's given var options = JSON.parse(JSON.stringify(sandcastleJsHintOptions)); + /*eslint-disable new-cap*/ if (!JSHINT(getScriptFromEditor(false), options)) { var hints = JSHINT.errors; for (i = 0, len = hints.length; i < len; ++i) { @@ -378,6 +379,7 @@ require({ } } } + /*eslint-enable new-cap*/ } function scheduleHint() { @@ -695,7 +697,7 @@ require({ } function loadFromGallery(demo) { - notFound = false; + deferredLoadError = false; document.getElementById('saveAsFile').download = demo.name + '.html'; registry.byId('description').set('value', decodeHTML(demo.description).replace(/\\n/g, '\n')); registry.byId('label').set('value', decodeHTML(demo.label).replace(/\\n/g, '\n')); @@ -806,8 +808,8 @@ require({ if (galleryError) { appendConsole('consoleError', 'Error loading gallery, please run the build script.', true); } - if (notFound) { - appendConsole('consoleLog', 'Unable to load demo named ' + queryObject.src.replace('.html', '') + '\n', true); + if (deferredLoadError) { + appendConsole('consoleLog', 'Unable to load demo named ' + queryObject.src.replace('.html', '') + '. Redirecting to HelloWorld.\n', true); } } } else if (Cesium.defined(e.data.log)) { @@ -883,6 +885,8 @@ require({ scheduleHintNoChange(); }); + var searchContainer; + function hideSearchContainer() { if (dom.byId('searchContainer')) { var innerPanel = registry.byId('innerPanel'); @@ -1060,15 +1064,10 @@ require({ url : 'gallery/' + name + '.html', handleAs : 'text', error : function(error) { - if (error.status === 404) { loadFromGallery(gallery_demos[hello_world_index]) .then(function() { - notFound = true; + deferredLoadError = true; }); - } else { - galleryError = true; - appendConsole('consoleError', error, true); - } } }); } @@ -1294,7 +1293,6 @@ require({ }); } - var searchContainer; when(promise).then(function() { dom.byId('searchDemos').appendChild(galleryErrorMsg); searchContainer = registry.byId('searchContainer'); diff --git a/Apps/Sandcastle/Sandcastle-client.js b/Apps/Sandcastle/Sandcastle-client.js index 0cee2b145c70..72a97b627919 100644 --- a/Apps/Sandcastle/Sandcastle-client.js +++ b/Apps/Sandcastle/Sandcastle-client.js @@ -13,9 +13,8 @@ return 'null'; } else if (defined(value)) { return value.toString(); - } else { - return 'undefined'; } + return 'undefined'; } console.originalLog = console.log; @@ -62,10 +61,12 @@ lineEnd1 = lineEnd2; } if (lineEnd1 > lineStart) { + /*eslint-disable no-empty*/ try { lineNumber = parseInt(stack.substring(lineStart + 1, lineEnd1), 10); } catch (ex) { } + /*eslint-enable no-empty*/ } } } @@ -91,6 +92,7 @@ } if (lineNumber < 1) { // Change lineNumber to the local one for highlighting. + /*eslint-disable no-empty*/ try { var pos = errorMsg.indexOf(Sandcastle.bucket + ':'); if (pos < 0) { @@ -102,6 +104,7 @@ } } catch (ex) { } + /*eslint-enable no-empty*/ } window.parent.postMessage({ 'error' : errorMsg, @@ -119,6 +122,7 @@ }; Sandcastle.declare = function(obj) { + /*eslint-disable no-empty*/ try { //Browsers such as IE don't have a stack property until you actually throw the error. var stack = ''; @@ -147,6 +151,7 @@ } } catch (ex) { } + /*eslint-enable no-empty*/ }; Sandcastle.highlight = function(obj) { diff --git a/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.html b/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.html new file mode 100644 index 000000000000..2bbd03a82163 --- /dev/null +++ b/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.html @@ -0,0 +1,227 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.jpg b/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.jpg new file mode 100644 index 000000000000..22d9d9be59b9 Binary files /dev/null and b/Apps/Sandcastle/gallery/3D Tiles Batch Table Hierarchy.jpg differ diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html new file mode 100644 index 000000000000..6829e5da9e55 --- /dev/null +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html @@ -0,0 +1,231 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.jpg b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.jpg new file mode 100644 index 000000000000..11512b80b98d Binary files /dev/null and b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.jpg differ diff --git a/Apps/Sandcastle/gallery/3D Tiles.html b/Apps/Sandcastle/gallery/3D Tiles.html new file mode 100644 index 000000000000..39427cc36075 --- /dev/null +++ b/Apps/Sandcastle/gallery/3D Tiles.html @@ -0,0 +1,284 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ +
Shadows
+
Right click action:
+
Zoom to feature
+
Annotate
+
Middle click action:
+
Hide feature
+
+ + + diff --git a/Apps/Sandcastle/gallery/3D Tiles.jpg b/Apps/Sandcastle/gallery/3D Tiles.jpg new file mode 100644 index 000000000000..7d99bc035e2e Binary files /dev/null and b/Apps/Sandcastle/gallery/3D Tiles.jpg differ diff --git a/Apps/Sandcastle/gallery/ArcticDEM.html b/Apps/Sandcastle/gallery/ArcticDEM.html new file mode 100644 index 000000000000..788beae764b4 --- /dev/null +++ b/Apps/Sandcastle/gallery/ArcticDEM.html @@ -0,0 +1,101 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/ArcticDEM.jpg b/Apps/Sandcastle/gallery/ArcticDEM.jpg new file mode 100644 index 000000000000..12241d04d90b Binary files /dev/null and b/Apps/Sandcastle/gallery/ArcticDEM.jpg differ diff --git a/Apps/Sandcastle/gallery/Billboards.html b/Apps/Sandcastle/gallery/Billboards.html index 2a2f623f8704..6b404b457616 100644 --- a/Apps/Sandcastle/gallery/Billboards.html +++ b/Apps/Sandcastle/gallery/Billboards.html @@ -79,7 +79,7 @@ function sizeBillboardInMeters() { Sandcastle.declare(sizeBillboardInMeters); - + var entity = viewer.entities.add({ position : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), billboard : { @@ -87,7 +87,7 @@ sizeInMeters : true } }); - + viewer.zoomTo(entity); } @@ -221,6 +221,29 @@ }); } +var terrainProvider; +function disableDepthTest() { + Sandcastle.declare(disableDepthTest); + + terrainProvider = viewer.terrainProvider; + viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', + requestWaterMask : true, + requestVertexNormals : true + }); + viewer.scene.globe.depthTestAgainstTerrain = true; + + viewer.entities.add({ + position : Cesium.Cartesian3.fromDegrees(-122.1958, 46.1915), + billboard : { + image : '../images/facility.gif', + heightReference : Cesium.HeightReference.CLAMP_TO_GROUND, + disableDepthTestDistance : Number.POSITIVE_INFINITY + } + }); + viewer.zoomTo(viewer.entities); +} + Sandcastle.addToolbarMenu([{ text : 'Add billboard', onselect : function() { @@ -275,11 +298,23 @@ addMarkerBillboards(); Sandcastle.highlight(addMarkerBillboards); } +}, { + text : 'Disable the depth test when clamped to ground', + onselect : function() { + disableDepthTest(); + Sandcastle.highlight(disableDepthTest); + } }]); Sandcastle.reset = function () { viewer.camera.flyHome(0); viewer.entities.removeAll(); + + if (Cesium.defined(terrainProvider)) { + viewer.terrainProvider = terrainProvider; + terrainProvider = undefined; + viewer.scene.globe.depthTestAgainstTerrain = false; + } }; //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Box.html b/Apps/Sandcastle/gallery/CZML Box.html index eea50295d2de..501bb78a05ea 100644 --- a/Apps/Sandcastle/gallery/CZML Box.html +++ b/Apps/Sandcastle/gallery/CZML Box.html @@ -71,7 +71,7 @@ "outlineColor" : { "rgba" : [0, 0, 0, 255] } - } + } }, { "id" : "shape3", "name" : "Yellow box outline", @@ -87,15 +87,14 @@ "outlineColor" : { "rgba" : [255, 255, 0, 255] } - } + } }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); - - //Sandcastle_End +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); +//Sandcastle_End Sandcastle.finishedLoading(); } if (typeof Cesium !== "undefined") { diff --git a/Apps/Sandcastle/gallery/CZML Circles and Ellipses.html b/Apps/Sandcastle/gallery/CZML Circles and Ellipses.html index e9fd0a46f533..21dee0241acc 100644 --- a/Apps/Sandcastle/gallery/CZML Circles and Ellipses.html +++ b/Apps/Sandcastle/gallery/CZML Circles and Ellipses.html @@ -67,7 +67,7 @@ } } }, - "outline" : true, + "outline" : true, // height must be set for outlines to display "outlineColor" : { "rgba" : [255, 255, 255, 255] } @@ -95,9 +95,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Colors.html b/Apps/Sandcastle/gallery/CZML Colors.html index 777a0d5457ca..986b3ad71f38 100644 --- a/Apps/Sandcastle/gallery/CZML Colors.html +++ b/Apps/Sandcastle/gallery/CZML Colors.html @@ -34,7 +34,7 @@ "version" : "1.0" }, { "id" : "rgba", - "name" : "Rectangle using RGBA Colors", + "name" : "Rectangle with outline using RGBA Colors", "rectangle" : { "coordinates" : { "wsenDegrees" : [-120, 40, -110, 50] @@ -47,6 +47,7 @@ } } }, + "height" : 0, // disables ground clamping, needed for outlines "outline" : true, "outlineColor" : { "rgba" : [0, 0, 0, 255] @@ -65,17 +66,18 @@ } } }, + "height" : 0, // disables ground clamping, needed for outlines "outline" : true, "outlineColor" : { - "rgbaf" : [0, 0, 0, 1] + "rgba" : [0, 0, 0, 255] } } }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource);//Sandcastle_End +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise);//Sandcastle_End Sandcastle.finishedLoading(); } if (typeof Cesium !== "undefined") { diff --git a/Apps/Sandcastle/gallery/CZML Cones and Cylinders.html b/Apps/Sandcastle/gallery/CZML Cones and Cylinders.html index 52496feb03c2..b69d26d3cf4d 100644 --- a/Apps/Sandcastle/gallery/CZML Cones and Cylinders.html +++ b/Apps/Sandcastle/gallery/CZML Cones and Cylinders.html @@ -75,9 +75,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Corridor.html b/Apps/Sandcastle/gallery/CZML Corridor.html index 5fcf2d78a030..544d08676400 100644 --- a/Apps/Sandcastle/gallery/CZML Corridor.html +++ b/Apps/Sandcastle/gallery/CZML Corridor.html @@ -34,7 +34,7 @@ "version" : "1.0" }, { "id" : "redCorridor", - "name" : "Red corridor on surface with rounded corners and outline", + "name" : "Red corridor on surface with rounded corners", "corridor" : { "positions" : { "cartographicDegrees" : [ @@ -50,15 +50,11 @@ "rgba" : [255, 0, 0, 127] } } - }, - "outline" : true, - "outlineColor" : { - "rgba" : [255, 0, 0, 255] } } }, { "id" : "greenCorridor", - "name" : "Green corridor at height with mitered corners", + "name" : "Green corridor at height with mitered corners and outline", "corridor" : { "positions" : { "cartographicDegrees" : [ @@ -76,6 +72,10 @@ "rgba" : [0, 255, 0, 255] } } + }, + "outline" : true, // height must be set for outlines to display + "outlineColor" : { + "rgba" : [0, 0, 0, 255] } } }, { @@ -102,15 +102,15 @@ }, "outline" : true, "outlineColor" : { - "rgba" : [0, 0, 0, 255] + "rgba" : [255, 255, 255, 255] } } }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Custom Properties.html b/Apps/Sandcastle/gallery/CZML Custom Properties.html index 27a9825364cf..ce219d0e1d4d 100644 --- a/Apps/Sandcastle/gallery/CZML Custom Properties.html +++ b/Apps/Sandcastle/gallery/CZML Custom Properties.html @@ -86,8 +86,11 @@ } }]; +var viewer = new Cesium.Viewer('cesiumContainer'); +var dataSource = new Cesium.CzmlDataSource(); + // custom properties can be queried directly: -Sandcastle.addToolbarButton('Print Values', function() { +Sandcastle.addToolbarButton('Print values', function() { var entity = dataSource.entities.getById('custom_property_object'); // get the values of all properties at the current time. var propertyValues = entity.properties.getValue(viewer.clock.currentTime); @@ -124,8 +127,6 @@ }, property.isConstant); } -var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = new Cesium.CzmlDataSource(); dataSource.load(czml); viewer.dataSources.add(dataSource); viewer.zoomTo(dataSource);//Sandcastle_End diff --git a/Apps/Sandcastle/gallery/CZML Model - Node Transformations.html b/Apps/Sandcastle/gallery/CZML Model - Node Transformations.html index 65bb246b9c44..c92ad813d888 100644 --- a/Apps/Sandcastle/gallery/CZML Model - Node Transformations.html +++ b/Apps/Sandcastle/gallery/CZML Model - Node Transformations.html @@ -65,15 +65,15 @@ 0.31933321618140015, 0.5055578277509731, -0.5903490075872426, 0.5421490838170975 ] } - } + } } } }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var promise = viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)); +var dataSourcePromise = viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)); -promise.then(function(dataSource){ +dataSourcePromise.then(function(dataSource){ viewer.trackedEntity = dataSource.entities.getById('model'); }).otherwise(function(error){ window.alert(error); diff --git a/Apps/Sandcastle/gallery/CZML Model.html b/Apps/Sandcastle/gallery/CZML Model.html index 7d6a59da08d6..a6c2c22504b7 100644 --- a/Apps/Sandcastle/gallery/CZML Model.html +++ b/Apps/Sandcastle/gallery/CZML Model.html @@ -46,9 +46,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var promise = viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)); +var dataSourcePromise = viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)); -promise.then(function(dataSource){ +dataSourcePromise.then(function(dataSource){ viewer.trackedEntity = dataSource.entities.getById('aircraft model'); }).otherwise(function(error){ window.alert(error); diff --git a/Apps/Sandcastle/gallery/CZML Path.html b/Apps/Sandcastle/gallery/CZML Path.html index 763d1dba9aae..d8355b214090 100644 --- a/Apps/Sandcastle/gallery/CZML Path.html +++ b/Apps/Sandcastle/gallery/CZML Path.html @@ -1864,7 +1864,7 @@ }]; var terrainProvider = new Cesium.CesiumTerrainProvider({ -url : 'https://assets.agi.com/stk-terrain/world', +url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestVertexNormals : true }); diff --git a/Apps/Sandcastle/gallery/CZML Point.html b/Apps/Sandcastle/gallery/CZML Point.html index 60c23a87ef0f..536fc616aa6d 100644 --- a/Apps/Sandcastle/gallery/CZML Point.html +++ b/Apps/Sandcastle/gallery/CZML Point.html @@ -52,9 +52,9 @@ var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Polygon.html b/Apps/Sandcastle/gallery/CZML Polygon.html index 81b4323e1783..38816d8b1c9b 100644 --- a/Apps/Sandcastle/gallery/CZML Polygon.html +++ b/Apps/Sandcastle/gallery/CZML Polygon.html @@ -104,9 +104,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Polyline.html b/Apps/Sandcastle/gallery/CZML Polyline.html index 919bcc6c6fb9..daefe7c43916 100644 --- a/Apps/Sandcastle/gallery/CZML Polyline.html +++ b/Apps/Sandcastle/gallery/CZML Polyline.html @@ -114,12 +114,31 @@ "followSurface" : false, "width" : 10 } +}, { + "id" : "dashedLine", + "name" : "Blue dashed line", + "polyline" : { + "positions" : { + "cartographicDegrees" : [ + -75, 45, 500000, + -125, 45, 500000 + ] + }, + "material" : { + "polylineDash" : { + "color" : { + "rgba" : [0, 255, 255, 255] + } + } + }, + "width" : 4 + } }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Rectangle.html b/Apps/Sandcastle/gallery/CZML Rectangle.html index ba07c501272b..f1762a0adfba 100644 --- a/Apps/Sandcastle/gallery/CZML Rectangle.html +++ b/Apps/Sandcastle/gallery/CZML Rectangle.html @@ -110,9 +110,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Reference Properties.html b/Apps/Sandcastle/gallery/CZML Reference Properties.html index 7fde329e6a31..fb11549fab24 100644 --- a/Apps/Sandcastle/gallery/CZML Reference Properties.html +++ b/Apps/Sandcastle/gallery/CZML Reference Properties.html @@ -49,7 +49,7 @@ "fillColor" : { "rgba" : [255, 255, 255, 255] }, - "font" : "11pt Lucida Console", + "font" : "13pt Lucida Console", "horizontalOrigin" : "LEFT", "outlineColor" : { "rgba":[150, 0, 150, 255] @@ -72,7 +72,7 @@ "fillColor" : { "rgba" : [255, 255, 255, 255] }, - "font" : "11pt Lucida Console", + "font" : "13pt Lucida Console", "horizontalOrigin" : "LEFT", "pixelOffset" : { "cartesian2" : [20, 0] @@ -93,6 +93,7 @@ -102.0, 35.0, 0 ] }, + "height" : 0, "outline" : true, "outlineColor" : { "reference" : "outlineColor-reference#label.outlineColor" diff --git a/Apps/Sandcastle/gallery/CZML Spheres and Ellipsoids.html b/Apps/Sandcastle/gallery/CZML Spheres and Ellipsoids.html index 281be89a4fb5..f5794f04e63a 100644 --- a/Apps/Sandcastle/gallery/CZML Spheres and Ellipsoids.html +++ b/Apps/Sandcastle/gallery/CZML Spheres and Ellipsoids.html @@ -95,9 +95,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/CZML Wall.html b/Apps/Sandcastle/gallery/CZML Wall.html index 00f8486272a5..6ddcfe36bd03 100644 --- a/Apps/Sandcastle/gallery/CZML Wall.html +++ b/Apps/Sandcastle/gallery/CZML Wall.html @@ -61,9 +61,9 @@ }]; var viewer = new Cesium.Viewer('cesiumContainer'); -var dataSource = Cesium.CzmlDataSource.load(czml); -viewer.dataSources.add(dataSource); -viewer.zoomTo(dataSource); +var dataSourcePromise = Cesium.CzmlDataSource.load(czml); +viewer.dataSources.add(dataSourcePromise); +viewer.zoomTo(dataSourcePromise); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/Callback Property.html b/Apps/Sandcastle/gallery/Callback Property.html new file mode 100644 index 000000000000..e4c00147d169 --- /dev/null +++ b/Apps/Sandcastle/gallery/Callback Property.html @@ -0,0 +1,106 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Callback Property.jpg b/Apps/Sandcastle/gallery/Callback Property.jpg new file mode 100644 index 000000000000..3a5cf438d74b Binary files /dev/null and b/Apps/Sandcastle/gallery/Callback Property.jpg differ diff --git a/Apps/Sandcastle/gallery/Camera Tutorial.html b/Apps/Sandcastle/gallery/Camera Tutorial.html index 9053eee4e8e8..a11bb57af9cc 100644 --- a/Apps/Sandcastle/gallery/Camera Tutorial.html +++ b/Apps/Sandcastle/gallery/Camera Tutorial.html @@ -23,11 +23,25 @@

Loading...

-
Click on the Cesium display to start.
-
w/s - move forward/backward
-
a/d - move left/right
-
q/e - move up/down
-
left mouse button down plus mouse move changes the look direction
+ + + + + + + + + + + + + + + + + + +
Click on the Cesium display to start.
w/s - move forward/backward
a/d - move left/right
q/e - move up/down
left mouse button down plus mouse move changes the look direction
+ + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Clock.jpg b/Apps/Sandcastle/gallery/Clock.jpg new file mode 100644 index 000000000000..09031a408368 Binary files /dev/null and b/Apps/Sandcastle/gallery/Clock.jpg differ diff --git a/Apps/Sandcastle/gallery/Clustering.html b/Apps/Sandcastle/gallery/Clustering.html index f179d01cd9cb..0c9fcf18d4bb 100644 --- a/Apps/Sandcastle/gallery/Clustering.html +++ b/Apps/Sandcastle/gallery/Clustering.html @@ -10,10 +10,10 @@ @@ -93,6 +93,7 @@ removeListener = dataSource.clustering.clusterEvent.addEventListener(function(clusteredEntities, cluster) { cluster.label.show = false; cluster.billboard.show = true; + cluster.billboard.id = cluster.label.id; cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; if (clusteredEntities.length >= 50) { @@ -155,14 +156,14 @@ var ids = pickedLabel.id; if (Cesium.isArray(ids)) { for (var i = 0; i < ids.length; ++i) { - ids[i].label.fillColor = Cesium.Color.RED; + ids[i].billboard.color = Cesium.Color.RED; } } } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); }); //Sandcastle_End -Sandcastle.finishedLoading(); + Sandcastle.finishedLoading(); } if (typeof Cesium !== "undefined") { startup(Cesium); diff --git a/Apps/Sandcastle/gallery/Corridor.html b/Apps/Sandcastle/gallery/Corridor.html index b5e0290d40f5..ab0f8350e3d4 100644 --- a/Apps/Sandcastle/gallery/Corridor.html +++ b/Apps/Sandcastle/gallery/Corridor.html @@ -30,7 +30,7 @@ var viewer = new Cesium.Viewer('cesiumContainer'); var redCorridor = viewer.entities.add({ - name : 'Red corridor on surface with rounded corners and outline', + name : 'Red corridor on surface with rounded corners', corridor : { positions : Cesium.Cartesian3.fromDegreesArray([ -100.0, 40.0, @@ -38,14 +38,12 @@ -105.0, 35.0 ]), width : 200000.0, - material : Cesium.Color.RED.withAlpha(0.5), - outline : true, - outlineColor : Cesium.Color.RED + material : Cesium.Color.RED.withAlpha(0.5) } }); var greenCorridor = viewer.entities.add({ - name : 'Green corridor at height with mitered corners', + name : 'Green corridor at height with mitered corners and outline', corridor : { positions : Cesium.Cartesian3.fromDegreesArray([ -90.0, 40.0, @@ -55,7 +53,8 @@ height: 100000.0, width : 200000.0, cornerType: Cesium.CornerType.MITERED, - material : Cesium.Color.GREEN + material : Cesium.Color.GREEN, + outline : true // height required for outlines to display } }); @@ -72,8 +71,8 @@ width : 200000.0, cornerType: Cesium.CornerType.BEVELED, material : Cesium.Color.BLUE.withAlpha(0.5), - outline : true, - outlineColor : Cesium.Color.BLUE + outline : true, // height or extrudedHeight must be set for outlines to display + outlineColor : Cesium.Color.WHITE } }); diff --git a/Apps/Sandcastle/gallery/Custom Geocoder.html b/Apps/Sandcastle/gallery/Custom Geocoder.html index b1f388c92db4..3d26e5d591ba 100644 --- a/Apps/Sandcastle/gallery/Custom Geocoder.html +++ b/Apps/Sandcastle/gallery/Custom Geocoder.html @@ -4,16 +4,16 @@ - + Cesium Demo @@ -36,7 +36,6 @@ function startup(Cesium) { 'use strict'; //Sandcastle_Begin - /** * This class is an example of a custom geocoder. It provides geocoding through the OpenStreetMap Nominatim service. * @alias OpenStreetMapNominatimGeocoder @@ -78,13 +77,13 @@ }); //Sandcastle_End - Sandcastle.finishedLoading(); - } - if (typeof Cesium !== "undefined") { - startup(Cesium); - } else if (typeof require === "function") { - require(["Cesium"], startup); - } + Sandcastle.finishedLoading(); +} +if (typeof Cesium !== "undefined") { + startup(Cesium); +} else if (typeof require === "function") { + require(["Cesium"], startup); +} - \ No newline at end of file + diff --git a/Apps/Sandcastle/gallery/Google Earth Enterprise.html b/Apps/Sandcastle/gallery/Google Earth Enterprise.html new file mode 100644 index 000000000000..0e9bda72954c --- /dev/null +++ b/Apps/Sandcastle/gallery/Google Earth Enterprise.html @@ -0,0 +1,59 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Google Earth Enterprise.jpg b/Apps/Sandcastle/gallery/Google Earth Enterprise.jpg new file mode 100644 index 000000000000..5d635cf88088 Binary files /dev/null and b/Apps/Sandcastle/gallery/Google Earth Enterprise.jpg differ diff --git a/Apps/Sandcastle/gallery/Ground Clamping.html b/Apps/Sandcastle/gallery/Ground Clamping.html index f52903ab5a09..d72e3273eae6 100644 --- a/Apps/Sandcastle/gallery/Ground Clamping.html +++ b/Apps/Sandcastle/gallery/Ground Clamping.html @@ -34,7 +34,7 @@ //Sandcastle_Begin var viewer = new Cesium.Viewer('cesiumContainer'); var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); @@ -64,7 +64,8 @@ fillColor : Cesium.Color.BLACK, showBackground : true, backgroundColor : new Cesium.Color(1, 1, 1, 0.7), - backgroundPadding : new Cesium.Cartesian2(8, 4) + backgroundPadding : new Cesium.Cartesian2(8, 4), + disableDepthTestDistance : Number.POSITIVE_INFINITY // draws the label in front of terrain } }); @@ -149,6 +150,53 @@ viewer.trackedEntity = e; } +}, { + text : 'Sample line positions and draw with depth test disabled', + onselect : function() { + var length = 1000; + + var startLon = Cesium.Math.toRadians(86.953793); + var endLon = Cesium.Math.toRadians(86.896497); + + var lat = Cesium.Math.toRadians(27.988257); + + var terrainSamplePositions = []; + for (var i = 0; i < length; ++i) { + var lon = Cesium.Math.lerp(endLon, startLon, i / (length - 1)); + var position = new Cesium.Cartographic(lon, lat); + terrainSamplePositions.push(position); + } + + Cesium.when(Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, terrainSamplePositions), function(samples) { + var offset = 10.0; + for (var i = 0; i < samples.length; ++i) { + samples[i].height += offset; + } + + viewer.entities.add({ + polyline : { + positions : Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(samples), + followSurface : false, + width : 5, + material : new Cesium.PolylineOutlineMaterialProperty({ + color : Cesium.Color.ORANGE, + outlineWidth : 2, + outlineColor : Cesium.Color.BLACK + }), + depthFailMaterial : new Cesium.PolylineOutlineMaterialProperty({ + color : Cesium.Color.RED, + outlineWidth : 2, + outlineColor : Cesium.Color.BLACK + }) + } + }); + + var target = new Cesium.Cartesian3(300770.50872389384, 5634912.131394585, 2978152.2865545116); + offset = new Cesium.Cartesian3(6344.974098678562, -793.3419798081741, 2499.9508860763162); + viewer.camera.lookAt(target, offset); + viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); + }); + } }], 'zoomButtons'); Sandcastle.reset = function () { diff --git a/Apps/Sandcastle/gallery/HTML Overlays.html b/Apps/Sandcastle/gallery/HTML Overlays.html new file mode 100644 index 000000000000..482542945d1f --- /dev/null +++ b/Apps/Sandcastle/gallery/HTML Overlays.html @@ -0,0 +1,56 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+ + + + diff --git a/Apps/Sandcastle/gallery/HTML Overlays.jpg b/Apps/Sandcastle/gallery/HTML Overlays.jpg new file mode 100644 index 000000000000..981e03d1b7e4 Binary files /dev/null and b/Apps/Sandcastle/gallery/HTML Overlays.jpg differ diff --git a/Apps/Sandcastle/gallery/HeadingPitchRoll.html b/Apps/Sandcastle/gallery/HeadingPitchRoll.html index 7597de58a5fc..6dc4987bf4eb 100644 --- a/Apps/Sandcastle/gallery/HeadingPitchRoll.html +++ b/Apps/Sandcastle/gallery/HeadingPitchRoll.html @@ -25,7 +25,7 @@

Loading...

- +
diff --git a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html index dd2ca66c730b..24a49c1392b2 100644 --- a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html +++ b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html @@ -76,7 +76,7 @@ downLayer : null, selectedLayer : null, isSelectableLayer : function(layer) { - return baseLayers.indexOf(layer) >= 0; + return this.baseLayers.indexOf(layer) >= 0; }, raise : function(layer, index) { imageryLayers.raise(layer); @@ -99,10 +99,10 @@ return layerIndex >= 0 && layerIndex < imageryLayers.length - 1; } }; -Cesium.knockout.track(viewModel); - var baseLayers = viewModel.baseLayers; +Cesium.knockout.track(viewModel); + function setupLayers() { // Create all the base layers that this example will support. // These base layers aren't really special. It's possible to have multiple of them diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html index d16a2ecb08bf..174289f03513 100644 --- a/Apps/Sandcastle/gallery/Interpolation.html +++ b/Apps/Sandcastle/gallery/Interpolation.html @@ -4,7 +4,7 @@ - + Cesium Demo @@ -40,7 +40,7 @@ //Use STK World Terrain viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); diff --git a/Apps/Sandcastle/gallery/LocalToFixedFrame.html b/Apps/Sandcastle/gallery/LocalToFixedFrame.html index 744e647fa37a..e2ac326ceee2 100644 --- a/Apps/Sandcastle/gallery/LocalToFixedFrame.html +++ b/Apps/Sandcastle/gallery/LocalToFixedFrame.html @@ -25,7 +25,7 @@

Loading...

-
Click on the 3D window then use the keyboard to change settings.
+
@@ -128,8 +128,8 @@

Loading...

position : positionLabel, label : { text : comments, - font : '24px Helvetica', - fillColor : Cesium.Color.SKYBLUE, + font : '18px Helvetica', + fillColor : Cesium.Color.WHITE, outlineColor : Cesium.Color.BLACK, outlineWidth : 2, style : Cesium.LabelStyle.FILL_AND_OUTLINE, diff --git a/Apps/Sandcastle/gallery/Polygon.html b/Apps/Sandcastle/gallery/Polygon.html index 2d094d77f01c..f1c86edafe76 100644 --- a/Apps/Sandcastle/gallery/Polygon.html +++ b/Apps/Sandcastle/gallery/Polygon.html @@ -70,7 +70,7 @@ }); var bluePolygon = viewer.entities.add({ - name : 'Blue polygon with holes and outline', + name : 'Blue polygon with holes', polygon : { hierarchy : { positions : Cesium.Cartesian3.fromDegreesArray([-99.0, 30.0, @@ -103,8 +103,8 @@ }] }, material : Cesium.Color.BLUE.withAlpha(0.5), - outline : true, - outlineColor : Cesium.Color.BLACK + heigth : 0, + outline : true // height is required for outline to display } }); diff --git a/Apps/Sandcastle/gallery/Polyline Dash.html b/Apps/Sandcastle/gallery/Polyline Dash.html new file mode 100644 index 000000000000..e567c19bc4d3 --- /dev/null +++ b/Apps/Sandcastle/gallery/Polyline Dash.html @@ -0,0 +1,108 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Polyline Dash.jpg b/Apps/Sandcastle/gallery/Polyline Dash.jpg new file mode 100644 index 000000000000..51d6feb1564c Binary files /dev/null and b/Apps/Sandcastle/gallery/Polyline Dash.jpg differ diff --git a/Apps/Sandcastle/gallery/Polyline.html b/Apps/Sandcastle/gallery/Polyline.html index 9b08ddab76cb..0dcfc9a7f72a 100644 --- a/Apps/Sandcastle/gallery/Polyline.html +++ b/Apps/Sandcastle/gallery/Polyline.html @@ -77,6 +77,18 @@ } }); +var dashedLine = viewer.entities.add({ + name : 'Blue dashed line', + polyline : { + positions : Cesium.Cartesian3.fromDegreesArrayHeights([-75, 45, 500000, + -125, 45, 500000]), + width : 4, + material : new Cesium.PolylineDashMaterialProperty({ + color: Cesium.Color.CYAN + }) + } +}); + viewer.zoomTo(viewer.entities); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/Apps/Sandcastle/gallery/Rectangle.html b/Apps/Sandcastle/gallery/Rectangle.html index 7861a1cc0262..1acdbf0c99c0 100644 --- a/Apps/Sandcastle/gallery/Rectangle.html +++ b/Apps/Sandcastle/gallery/Rectangle.html @@ -30,12 +30,10 @@ var viewer = new Cesium.Viewer('cesiumContainer'); var redRectangle = viewer.entities.add({ - name : 'Red translucent rectangle with outline', + name : 'Red translucent rectangle', rectangle : { coordinates : Cesium.Rectangle.fromDegrees(-110.0, 20.0, -80.0, 25.0), - material : Cesium.Color.RED.withAlpha(0.5), - outline : true, - outlineColor : Cesium.Color.RED + material : Cesium.Color.RED.withAlpha(0.5) } }); @@ -47,8 +45,8 @@ rotation : Cesium.Math.toRadians(45), extrudedHeight : 300000.0, height : 100000.0, - outline : true, - outlineColor : Cesium.Color.GREEN + outline : true, // height must be set for outline to display + outlineColor : Cesium.Color.BLACK } }); diff --git a/Apps/Sandcastle/gallery/Shadows.html b/Apps/Sandcastle/gallery/Shadows.html index b0f24d060d8e..433cf3c80ac0 100644 --- a/Apps/Sandcastle/gallery/Shadows.html +++ b/Apps/Sandcastle/gallery/Shadows.html @@ -37,7 +37,7 @@ }); viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); diff --git a/Apps/Sandcastle/gallery/Terrain Exaggeration.html b/Apps/Sandcastle/gallery/Terrain Exaggeration.html index 212ec6d5fcdd..7dbc7b8aa3b6 100644 --- a/Apps/Sandcastle/gallery/Terrain Exaggeration.html +++ b/Apps/Sandcastle/gallery/Terrain Exaggeration.html @@ -34,7 +34,7 @@ }); var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 05f2c451b41c..999eba58dfce 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -37,7 +37,7 @@ viewer.scene.globe.enableLighting = true; var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); @@ -61,14 +61,14 @@ text : 'CesiumTerrainProvider - STK World Terrain - no effects', onselect : function() { viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); } }, { text : 'CesiumTerrainProvider - STK World Terrain w/ Lighting', onselect : function() { viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestVertexNormals : true }); viewer.scene.globe.enableLighting = true; @@ -77,7 +77,7 @@ text : 'CesiumTerrainProvider - STK World Terrain w/ Water', onselect : function() { viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true }); } diff --git a/Apps/Sandcastle/gallery/Wall.html b/Apps/Sandcastle/gallery/Wall.html index c2534659e40f..8564c8175e9e 100644 --- a/Apps/Sandcastle/gallery/Wall.html +++ b/Apps/Sandcastle/gallery/Wall.html @@ -47,7 +47,8 @@ -97.0, 40.0, 100000.0, -107.0, 40.0, 100000.0, -107.0, 43.0, 100000.0]), - material : Cesium.Color.GREEN + material : Cesium.Color.GREEN, + outline : true } }); diff --git a/Apps/Sandcastle/gallery/development/3D Models Instancing.html b/Apps/Sandcastle/gallery/development/3D Models Instancing.html new file mode 100644 index 000000000000..3bbbd4656925 --- /dev/null +++ b/Apps/Sandcastle/gallery/development/3D Models Instancing.html @@ -0,0 +1,255 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/development/3D Models Instancing.jpg b/Apps/Sandcastle/gallery/development/3D Models Instancing.jpg new file mode 100644 index 000000000000..34305b24dd90 Binary files /dev/null and b/Apps/Sandcastle/gallery/development/3D Models Instancing.jpg differ diff --git a/Apps/Sandcastle/gallery/development/BillboardClampToGround.html b/Apps/Sandcastle/gallery/development/BillboardClampToGround.html index 701953dfe3e1..754a1d333f05 100644 --- a/Apps/Sandcastle/gallery/development/BillboardClampToGround.html +++ b/Apps/Sandcastle/gallery/development/BillboardClampToGround.html @@ -35,7 +35,7 @@ var viewer = new Cesium.Viewer('cesiumContainer'); var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); viewer.terrainProvider = cesiumTerrainProviderMeshes; viewer.scene.globe.depthTestAgainstTerrain = true; @@ -60,7 +60,7 @@ var longitude = Cesium.Math.lerp(e.west, e.east, x / (gridWidth - 1)); var latitude = Cesium.Math.lerp(e.south, e.north, y / (gridHeight - 1)); var position = new Cesium.Cartographic(longitude, latitude); - + billboardCollection.add({ position : ellipsoid.cartographicToCartesian(position), image : '../images/facility.gif', diff --git a/Apps/Sandcastle/gallery/development/Billboards Instancing.html b/Apps/Sandcastle/gallery/development/Billboards Instancing.html index be64382c6cf2..791388d2474b 100644 --- a/Apps/Sandcastle/gallery/development/Billboards Instancing.html +++ b/Apps/Sandcastle/gallery/development/Billboards Instancing.html @@ -37,7 +37,7 @@ var context = scene.context; var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); viewer.terrainProvider = cesiumTerrainProviderMeshes; diff --git a/Apps/Sandcastle/gallery/development/Billboards Instancing.jpg b/Apps/Sandcastle/gallery/development/Billboards Instancing.jpg index a4f82ed55dc1..e01a4fe5cad5 100644 Binary files a/Apps/Sandcastle/gallery/development/Billboards Instancing.jpg and b/Apps/Sandcastle/gallery/development/Billboards Instancing.jpg differ diff --git a/Apps/Sandcastle/gallery/development/Fog.html b/Apps/Sandcastle/gallery/development/Fog.html index f8ec9a24ccf9..450f6c412191 100644 --- a/Apps/Sandcastle/gallery/development/Fog.html +++ b/Apps/Sandcastle/gallery/development/Fog.html @@ -45,7 +45,7 @@ //Sandcastle_Begin var viewer = new Cesium.Viewer('cesiumContainer'); viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); @@ -70,7 +70,7 @@ viewer.scene.fog.enabled = newValue; } ); - + Cesium.knockout.getObservable(viewModel, 'density').subscribe( function(newValue) { viewer.scene.fog.density = newValue; diff --git a/Apps/Sandcastle/gallery/development/Geometry and Appearances.html b/Apps/Sandcastle/gallery/development/Geometry and Appearances.html index e865176f58db..70eff33f1e86 100644 --- a/Apps/Sandcastle/gallery/development/Geometry and Appearances.html +++ b/Apps/Sandcastle/gallery/development/Geometry and Appearances.html @@ -33,7 +33,7 @@ var primitives = scene.primitives; var solidWhite = Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE); -// Combine instances for an rectangle, polygon, ellipse, and circle into a single primitive. +// Combine instances for a rectangle, polygon, ellipse, and circle into a single primitive. var rectangle = Cesium.Rectangle.fromDegrees(-92.0, 20.0, -86.0, 27.0); var rectangleInstance = new Cesium.GeometryInstance({ diff --git a/Apps/Sandcastle/gallery/development/Ground Primitive.html b/Apps/Sandcastle/gallery/development/Ground Primitive.html index 913431a2501c..b59eb7973999 100644 --- a/Apps/Sandcastle/gallery/development/Ground Primitive.html +++ b/Apps/Sandcastle/gallery/development/Ground Primitive.html @@ -32,7 +32,7 @@ var viewer = new Cesium.Viewer('cesiumContainer'); var scene = viewer.scene; scene.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestVertexNormals : true }); @@ -58,7 +58,7 @@ if (withAlpha) { color = color.withAlpha(0.5); } - + scene.groundPrimitives.add(new Cesium.GroundPrimitive({ geometryInstances : new Cesium.GeometryInstance({ geometry : new Cesium.PolygonGeometry({ @@ -79,7 +79,7 @@ if (withAlpha) { color = color.withAlpha(0.5); } - + scene.groundPrimitives.add(new Cesium.GroundPrimitive({ geometryInstances : new Cesium.GeometryInstance({ geometry : new Cesium.PolygonGeometry({ @@ -91,7 +91,7 @@ id : 'polygon 2' }) })); - + // Same polygon slightly offset and overlapping. positionsOffset = offsetPositions(positions, -0.01); polygonHierarchy = { positions : positionsOffset }; @@ -100,7 +100,7 @@ if (withAlpha) { color = color.withAlpha(0.5); } - + scene.groundPrimitives.add(new Cesium.GroundPrimitive({ geometryInstances : new Cesium.GeometryInstance({ geometry : new Cesium.PolygonGeometry({ @@ -135,9 +135,9 @@ if (Cesium.defined(currentObject)) { currentObject.primitive.getGeometryInstanceAttributes(currentObject.id).color = lastColor; } - + currentObject = pickedObject; - + var attributes = currentObject.primitive.getGeometryInstanceAttributes(currentObject.id); lastColor = attributes.color; attributes.color = [255, 255, 0, 128]; @@ -151,7 +151,7 @@ Sandcastle.addToolbarButton('Z-Order', function() { createOverlappingPolygons(false); viewOverlappingPolygons(); - + handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); handler.setInputAction(function(movement) { var pickedObject = scene.pick(movement.endPosition); @@ -324,7 +324,7 @@ Sandcastle.reset = function() { scene.groundPrimitives.removeAll(); handler = handler && handler.destroy(); - + //Set the camera to a US centered tilted view and switch back to moving in world coordinates. viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(-98.0, 40.0), new Cesium.Cartesian3(0.0, -4790000.0, 3930000.0)); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); diff --git a/Apps/Sandcastle/gallery/development/Multiple Shadows.html b/Apps/Sandcastle/gallery/development/Multiple Shadows.html index 798300e8c765..f9256fb6531f 100644 --- a/Apps/Sandcastle/gallery/development/Multiple Shadows.html +++ b/Apps/Sandcastle/gallery/development/Multiple Shadows.html @@ -44,7 +44,7 @@ timeline : false }); viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); diff --git a/Apps/Sandcastle/gallery/development/Shadows.html b/Apps/Sandcastle/gallery/development/Shadows.html index 2138e5fa77e3..51e07209b3c7 100644 --- a/Apps/Sandcastle/gallery/development/Shadows.html +++ b/Apps/Sandcastle/gallery/development/Shadows.html @@ -219,7 +219,7 @@ size : 1024, modelOptions : ['Wood Tower', 'Cesium Air', 'Cesium Man', 'Transparent Box', 'Shadow Tester', 'Shadow Tester 2', 'Shadow Tester 3', 'Shadow Tester 4', 'Shadow Tester Point'], model : 'Shadow Tester', - locationOptions : ['Exton', 'Everest', 'Pinnacle PA', 'Seneca Rocks', 'Half Dome'], + locationOptions : ['Exton', 'Everest', 'Pinnacle PA', 'Seneca Rocks', 'Half Dome', '3D Tiles'], location : 'Pinnacle PA', modelPositionOptions : ['Center', 'Ground', 'High', 'Higher', 'Space'], modelPosition : 'Center', @@ -302,6 +302,11 @@ 'Half Dome' : { 'centerLongitude' : -2.0862479628, 'centerLatitude' : 0.6587902522 + }, + '3D Tiles' : { + 'centerLongitude' : -1.31968, + 'centerLatitude' : 0.698874, + 'tileset' : '../../../Specs/Data/Cesium3DTiles/Tilesets/Tileset' } } }; @@ -344,7 +349,16 @@ biasMode.depthBias.subscribe(updateSettings); } +var viewer = new Cesium.Viewer('cesiumContainer', { + scene3DOnly : true, + infoBox : false, + selectionIndicator : false, + timeline : false +}); + var offset = new Cesium.Cartesian3(); +var scene = viewer.scene; +var freeformLightCamera = new Cesium.Camera(scene); function updateLightDirection() { var location = uiOptions.locations[viewModel.location]; @@ -358,6 +372,11 @@ freeformLightCamera.lookAt(center, offset); } +var context = scene.context; +var camera = scene.camera; +var globe = scene.globe; +var shadowMap; + function updateSettings() { shadowMap.maximumDistance = Number(viewModel.distance); shadowMap._pointLightRadius = Number(viewModel.radius); @@ -395,6 +414,11 @@ scene.skyAtmosphere.show = viewModel.globe; } +var sunCamera = scene._sunCamera; +var fixedLightCamera = new Cesium.Camera(scene); +var pointLightCamera = new Cesium.Camera(scene); +var spotLightCamera = new Cesium.Camera(scene); + function updateShadows() { var cascades = viewModel.cascades; var lightSource = viewModel.lightSource; @@ -460,29 +484,10 @@ }); } -var viewer = new Cesium.Viewer('cesiumContainer', { - scene3DOnly : true, - infoBox : false, - selectionIndicator : false, - timeline : false -}); - -var scene = viewer.scene; -var context = scene.context; -var camera = scene.camera; -var globe = scene.globe; -var shadowMap; - scene.debugShowFramesPerSecond = true; -var fixedLightCamera = new Cesium.Camera(scene); -var freeformLightCamera = new Cesium.Camera(scene); -var sunCamera = scene._sunCamera; -var pointLightCamera = new Cesium.Camera(scene); -var spotLightCamera = new Cesium.Camera(scene); - var cesiumTerrainProvider = new Cesium.CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); @@ -569,6 +574,10 @@ createBoxRTC(position2); createSphere(position1); + if (Cesium.defined(location.tileset)) { + createTileset(location.tileset); + } + // Add a grid of models if (viewModel.grid) { var spacing = 0.00002; @@ -584,6 +593,12 @@ } } +function createTileset(url) { + scene.primitives.add(new Cesium.Cesium3DTileset({ + url : url + })); +} + function createModel(url, origin) { var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, new Cesium.HeadingPitchRoll()); diff --git a/Apps/Sandcastle/templates/bucket.css b/Apps/Sandcastle/templates/bucket.css index e8ff0af68a0f..5d5f3d85cd66 100644 --- a/Apps/Sandcastle/templates/bucket.css +++ b/Apps/Sandcastle/templates/bucket.css @@ -57,3 +57,10 @@ body { padding: 2px 5px; position: absolute; } + +.infoPanel { + background: rgba(42, 42, 42, 0.8); + padding: 4px; + border: 1px solid #444; + border-radius: 4px; +} diff --git a/CHANGES.md b/CHANGES.md index b46258c9afbf..cdd0bb546add 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,71 @@ Change Log ========== +### 1.35 - 2017-07-05 + +* Deprecated + * `GoogleEarthImageryProvider` has been deprecated and will be removed in Cesium 1.37, use `GoogleEarthEnterpriseMapsProvider` instead. + * The `throttleRequest` parameter for `TerrainProvider.requestTileGeometry`, `CesiumTerrainProvider.requestTileGeometry`, `VRTheWorldTerrainProvider.requestTileGeometry`, and `EllipsoidTerrainProvider.requestTileGeometry` is deprecated and will be replaced with an optional `Request` object. The `throttleRequests` parameter will be removed in 1.37. Instead to throttle requests set the request's `throttle` property to `true`. + * The ability to provide a Promise for the `options.url` parameter of `loadWithXhr` and for the `url` parameter of `loadArrayBuffer`, `loadBlob`, `loadImageViaBlob`, `loadText`, `loadJson`, `loadXML`, `loadImage`, `loadCRN`, `loadKTX`, and `loadCubeMap` is deprecated. This will be removed in 1.37, instead `url` must be a string. +* Added an `options.request` parameter to `loadWithXhr` and a `request` parameter to `loadArrayBuffer`, `loadBlob`, `loadImageViaBlob`, `loadText`, `loadJson`, `loadJsonp`, `loadXML`, `loadImageFromTypedArray`, `loadImage`, `loadCRN`, and `loadKTX`. +* Fixed bug where if polylines were set to follow the surface of an undefined globe, Cesium would crash [#5413] https://github.com/AnalyticalGraphicsInc/cesium/pull/5413 +* Fixed a bug where picking clusters would return undefined instead of a list of the clustered entities. [#5286](https://github.com/AnalyticalGraphicsInc/cesium/issues/5286) +* Fixed a bug where picking would break when the Sun came into view [#5478](https://github.com/AnalyticalGraphicsInc/cesium/issues/5478) +* Reduced the amount of Sun bloom post-process effect near the horizon. [#5381](https://github.com/AnalyticalGraphicsInc/cesium/issues/5381) +* Updated glTF/glb MIME types. [#5420](https://github.com/AnalyticalGraphicsInc/cesium/issues/5420) +* Fixed a bug where camera zooming worked incorrectly when the display height was greater than the display width [#5421] (https://github.com/AnalyticalGraphicsInc/cesium/pull/5421) +* Added Sandcastle demo for ArcticDEM data. [#5224](https://github.com/AnalyticalGraphicsInc/cesium/issues/5224) +* `CzmlDataSource` and `KmlDataSource` load functions now take an optional `query` object, which will append query parameters to all network requests. [#5419](https://github.com/AnalyticalGraphicsInc/cesium/pull/5419), [#5434](https://github.com/AnalyticalGraphicsInc/cesium/pull/5434) +* Fixed geocoder bug so geocoder can accurately handle NSEW inputs [#5407] (https://github.com/AnalyticalGraphicsInc/cesium/pull/5407) +* Added support for [3D Tiles](https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md) for streaming massive heterogeneous 3D geospatial datasets. The new Cesium APIs are: + * `Cesium3DTileset` + * `Cesium3DTileStyle`, `StyleExpression`, `Expression`, and `ConditionsExpression` + * `Cesium3DTile` + * `Cesium3DTileContent` + * `Cesium3DTileFeature` + * `Cesium3DTilesInspector`, `Cesium3DTilesInspectorViewModel`, and `viewerCesium3DTilesInspectorMixin` + * `Cesium3DTileColorBlendMode` +* Added a Sandcastle demo for setting time with the Clock API [#5457](https://github.com/AnalyticalGraphicsInc/cesium/pull/5457); + +### 1.34 - 2017-06-01 + +* Deprecated + * Passing `options.clock` when creating a new `Viewer` instance has been deprecated and will be removed in Cesium 1.37, pass `options.clockViewModel` instead. +* Fix issue where polylines in a `PolylineCollection` would ignore the far distance when updating the distance display condition. [#5283](https://github.com/AnalyticalGraphicsInc/cesium/pull/5283) +* Fixed a crash when calling `Camera.pickEllipsoid` with a canvas of size 0. +* Fix `BoundingSphere.fromOrientedBoundingBox`. [#5334](https://github.com/AnalyticalGraphicsInc/cesium/issues/5334) +* Fixed bug where polylines would not update when `PolylineCollection` model matrix was updated. [#5327](https://github.com/AnalyticalGraphicsInc/cesium/pull/5327) +* Fixed a bug where adding a ground clamped label without a position would show up at a previous label's clamped position. [#5338](https://github.com/AnalyticalGraphicsInc/cesium/issues/5338) +* Fixed translucency bug for certain material types. [#5335](https://github.com/AnalyticalGraphicsInc/cesium/pull/5335) +* Fix picking polylines that use a depth fail appearance. [#5337](https://github.com/AnalyticalGraphicsInc/cesium/pull/5337) +* Fixed a crash when morphing from Columbus view to 3D. [#5311](https://github.com/AnalyticalGraphicsInc/cesium/issues/5311) +* Fixed a bug which prevented KML descriptions with relative paths from loading. [#5352](https://github.com/AnalyticalGraphicsInc/cesium/pull/5352) +* Fixed an issue where camera view could be invalid at the last frame of animation. [#4949](https://github.com/AnalyticalGraphicsInc/cesium/issues/4949) +* Fixed an issue where using the depth fail material for polylines would cause a crash in Edge. [#5359](https://github.com/AnalyticalGraphicsInc/cesium/pull/5359) +* Fixed a crash where `EllipsoidGeometry` and `EllipsoidOutlineGeometry` were given floating point values when expecting integers. [#5260](https://github.com/AnalyticalGraphicsInc/cesium/issues/5260) +* Fixed an issue where billboards were not properly aligned. [#2487](https://github.com/AnalyticalGraphicsInc/cesium/issues/2487) +* Fixed an issue where translucent objects could flicker when picking on mouse move. [#5307](https://github.com/AnalyticalGraphicsInc/cesium/issues/5307) +* Fixed a bug where billboards with `sizeInMeters` set to true would move upwards when zooming out. [#5373](https://github.com/AnalyticalGraphicsInc/cesium/issues/5373) +* Fixed a bug where `SampledProperty.setInterpolationOptions` does not ignore undefined `options`. [#3575](https://github.com/AnalyticalGraphicsInc/cesium/issues/3575) +* Added `basePath` option to `Cesium.Model.fromGltf`. [#5320](https://github.com/AnalyticalGraphicsInc/cesium/issues/5320) + +### 1.33 - 2017-05-01 + +* Breaking changes + * Removed left, right, bottom and top properties from `OrthographicFrustum`. Use `OrthographicOffCenterFrustum` instead. [#5109](https://github.com/AnalyticalGraphicsInc/cesium/issues/5109) +* Added `GoogleEarthEnterpriseTerrainProvider` and `GoogleEarthEnterpriseImageryProvider` to read data from Google Earth Enterprise servers. [#5189](https://github.com/AnalyticalGraphicsInc/cesium/pull/5189). +* Support for dashed polylines [#5159](https://github.com/AnalyticalGraphicsInc/cesium/pull/5159). + * Added `PolylineDash` Material type. + * Added `PolylineDashMaterialProperty` to the Entity API. + * Added CZML `polylineDash` property . +* Added `disableDepthTestDistance` to billboards, points and labels. This sets the distance to the camera where the depth test will be disabled. Setting it to zero (the default) will always enable the depth test. Setting it to `Number.POSITVE_INFINITY` will never enabled the depth test. Also added `scene.minimumDisableDepthTestDistance` to change the default value from zero. [#5166](https://github.com/AnalyticalGraphicsInc/cesium/pull/5166) +* Added a `depthFailMaterial` property to line entities, which is the material used to render the line when it fails the depth test. [#5160](https://github.com/AnalyticalGraphicsInc/cesium/pull/5160) +* Fixed billboards not initially clustering. [#5208](https://github.com/AnalyticalGraphicsInc/cesium/pull/5208) +* Fixed issue with displaying `MapboxImageryProvider` default token error message. [#5191](https://github.com/AnalyticalGraphicsInc/cesium/pull/5191) +* Fixed bug in conversion formula in `Matrix3.fromHeadingPitchRoll`. [#5195](https://github.com/AnalyticalGraphicsInc/cesium/issues/5195) +* Upgrade FXAA to version 3.11. [#5200](https://github.com/AnalyticalGraphicsInc/cesium/pull/5200) +* `Scene.pickPosition` now caches results per frame to increase performance. [#5117](https://github.com/AnalyticalGraphicsInc/cesium/issues/5117) + ### 1.32 - 2017-04-03 * Deprecated @@ -8,29 +73,26 @@ Change Log * Breaking changes * Removed `ArcGisImageServerTerrainProvider`. * The top-level `properties` in an `Entity` created by `GeoJsonDataSource` are now instances of `ConstantProperty` instead of raw values. -* Added `Camera.flyTo` and `Camera.flyToBoundingSphere` options [#5070](https://github.com/AnalyticalGraphicsInc/cesium/pull/5070) - * `flyOverLongitude` to select one of two possible on Globe paths which camera should fly. - * `flyOverLongitudeWeight` to set a threshold: how many times the `flyOverLongitude` way can be than shortest path. - * `pitchAdjustHeight` to adjust camera pitch during exaggerated flights, to keep Earth in viewport. -* Added the event `Viewer.trackedEntityChanged`, which is raised when the value of `viewer.trackedEntity` changes. [#5060](https://github.com/AnalyticalGraphicsInc/cesium/pull/5060) -* Added `Camera.DEFAULT_OFFSET` for default view of objects with bounding spheres [#4936](https://github.com/AnalyticalGraphicsInc/cesium/pull/4936) -* Changed billboard blending behavior [#5066](https://github.com/AnalyticalGraphicsInc/cesium/pull/5056) -* Decrease multi-frustum overlap to prevent depth artifacts between opaque and translucent draw calls. [#5116](https://github.com/AnalyticalGraphicsInc/cesium/pull/5116) -* Fix crunch compressed textures in IE11. [#5057](https://github.com/AnalyticalGraphicsInc/cesium/pull/5057) -* Fixed a bug in `Quaternion.fromHeadingPitchRoll` that made it erroneously throw an exception when passed individual angles in an unminified / debug build. -* Fix `GroundPrimitive` rendering in 2D and Columbus View [#5078](https://github.com/AnalyticalGraphicsInc/cesium/pull/5078) -* Fixed an issue with `PinBuilder` where inset images could have low-alpha fringes against an opaque background. [#5099](https://github.com/AnalyticalGraphicsInc/cesium/pull/5099) -* Fixed a bug in `ModelAnimationCache` causing different animations to reference the same animation. [#5064](https://github.com/AnalyticalGraphicsInc/cesium/pull/5064) -* Fixed a bug that caused an exception in `CesiumInspectorViewModel` when using the NW / NE / SW / SE / Parent buttons to navigate to a terrain tile that is not yet loaded. -* `ConstantProperty` now provides `valueOf` and `toString` methods that return the constant value. -* Added support for time-varying properties in CZML [#5105](https:/github.com/AnalyticalGraphicsInc/cesium/pull/5105). * Added support for an orthographic projection in 3D and Columbus view. * Set `projectionPicker` to `true` in the options when creating a `Viewer` to add a widget that will switch projections. [#5021](https://github.com/AnalyticalGraphicsInc/cesium/pull/5021) * Call `switchToOrthographicFrustum` or `switchToPerspectiveFrustum` on `Camera` to change projections. -* Fixed an issue with camera tracking of dynamic ellipsoids. [#5133](https://github.com/AnalyticalGraphicsInc/cesium/pull/5133) +* Added support for custom time-varying properties in CZML. [#5105](https:/github.com/AnalyticalGraphicsInc/cesium/pull/5105). +* Added new flight parameters to `Camera.flyTo` and `Camera.flyToBoundingSphere`: `flyOverLongitude`, `flyOverLongitudeWeight`, and `pitchAdjustHeight`. [#5070](https://github.com/AnalyticalGraphicsInc/cesium/pull/5070) +* Added the event `Viewer.trackedEntityChanged`, which is raised when the value of `viewer.trackedEntity` changes. [#5060](https://github.com/AnalyticalGraphicsInc/cesium/pull/5060) +* Added `Camera.DEFAULT_OFFSET` for default view of objects with bounding spheres. [#4936](https://github.com/AnalyticalGraphicsInc/cesium/pull/4936) +* Fixed an issue with `TileBoundingBox` that caused the terrain to disappear in certain places [4032](https://github.com/AnalyticalGraphicsInc/cesium/issues/4032) +* Fixed overlapping billboard blending. [#5066](https://github.com/AnalyticalGraphicsInc/cesium/pull/5066) +* Fixed an issue with `PinBuilder` where inset images could have low-alpha fringes against an opaque background. [#5099](https://github.com/AnalyticalGraphicsInc/cesium/pull/5099) * Fix billboard, point and label clustering in 2D and Columbus view. [#5136](https://github.com/AnalyticalGraphicsInc/cesium/pull/5136) +* Fixed `GroundPrimitive` rendering in 2D and Columbus View. [#5078](https://github.com/AnalyticalGraphicsInc/cesium/pull/5078) +* Fixed an issue with camera tracking of dynamic ellipsoids. [#5133](https://github.com/AnalyticalGraphicsInc/cesium/pull/5133) * Fixed issues with imagerySplitPosition and the international date line in 2D mode. [#5151](https://github.com/AnalyticalGraphicsInc/cesium/pull/5151) -* Fixed an issue with `TileBoundingBox` that caused the terrain to disappear in certain places [4032](https://github.com/AnalyticalGraphicsInc/cesium/issues/4032) +* Fixed a bug in `ModelAnimationCache` causing different animations to reference the same animation. [#5064](https://github.com/AnalyticalGraphicsInc/cesium/pull/5064) +* `ConstantProperty` now provides `valueOf` and `toString` methods that return the constant value. +* Improved depth artifacts between opaque and translucent primitives. [#5116](https://github.com/AnalyticalGraphicsInc/cesium/pull/5116) +* Fixed crunch compressed textures in IE11. [#5057](https://github.com/AnalyticalGraphicsInc/cesium/pull/5057) +* Fixed a bug in `Quaternion.fromHeadingPitchRoll` that made it erroneously throw an exception when passed individual angles in an unminified / debug build. +* Fixed a bug that caused an exception in `CesiumInspectorViewModel` when using the NW / NE / SW / SE / Parent buttons to navigate to a terrain tile that is not yet loaded. * `QuadtreePrimitive` now uses `frameState.afterRender` to fire `tileLoadProgressEvent` [#3450](https://github.com/AnalyticalGraphicsInc/cesium/issues/3450) ### 1.31 - 2017-03-01 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c5c771042c4..17c1e1d5d301 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,8 +52,8 @@ We love pull requests. We strive to promptly review them, provide feedback, and Before we can merge a pull request, we require a signed Contributor License Agreement. There is a CLA for: -* [individuals](http://www.agi.com/licenses/individual-cla-agi-v1.0.txt) and -* [corporations](http://www.agi.com/licenses/corporate-cla-agi-v1.0.txt). +* [individuals](Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt) and +* [corporations](Documentation/Contributors/CLAs/corporate-cla-agi-v1.0.txt). This only needs to be completed once. The CLA ensures you retain copyright to your contributions, and we have the right to use them and incorporate them into Cesium using the [Apache 2.0 License](LICENSE.md). @@ -72,7 +72,7 @@ Our code is our lifeblood so maintaining Cesium's high code quality is important * Once you are done making new commits to address feedback, add a comment to the pull request such as `"this is ready"` since GitHub doesn't notify us about commits. * Code and tests * Follow the [Coding Guide](Documentation/Contributors/CodingGuide/README.md). - * Verify your code passes [JSHint](http://www.jshint.com/). Run JSHint for all of Cesium with `npm run jsHint` or automatically run JSHint when files are saved with `npm run jsHint-watch`. See the [Build Guide](Documentation/Contributors/BuildGuide/README.md). + * Verify your code passes [ESLint](http://www.eslint.org/). Run ESLint for all of Cesium with `npm run eslint` or automatically run ESLint when files are saved with `npm run eslint-watch`. See the [Build Guide](Documentation/Contributors/BuildGuide/README.md). * Verify that all tests pass, and write new tests with excellent code coverage for new code. Follow the [Testing Guide](Documentation/Contributors/TestingGuide/README.md). * If you added new identifiers to the Cesium API: * Update [CHANGES.md](CHANGES.md). diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1d86d725cef5..f32f702a586b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,6 +1,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesium. The following people have contributed to Cesium, under the following agreements: -## [Corporate CLA](http://www.agi.com/licenses/corporate-cla-agi-v1.0.txt) +## [Corporate CLA](Documentation/Contributors/CLAs/corporate-cla-agi-v1.0.txt) * [Analytical Graphics, Inc.](http://www.agi.com/) * [Patrick Cozzi](https://github.com/pjcozzi) @@ -38,6 +38,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Austin Eng](https://github.com/austinEng) * [Shehzan Mohammed](https://github.com/shehzan10) * [Rachel Hwang](https://github.com/rahwang) + * [Joseph Klinger](https://github.com/klingerj) + * [Mohamad Moneimne](https://github.com/moneimne) + * [Ottavio Hartman](https://github.com/omh1280) + * [William Ho](https://github.com/williamkho) + * [Srinivas Kaza](https://github.com/AnimatedRNG) * [NICTA](http://www.nicta.com.au/) * [Chris Cooper](https://github.com/chris-cooper) * [Kevin Ring](https://github.com/kring) @@ -74,10 +79,14 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Guillaume Beraudo](https://github.com/gberaudo) * [EndPoint](https://www.endpoint.com/) * [Dmitry Kiselev](https://github.com/kiselev-dv) +* [Safe Software](https://www.safe.com) + * [Joel Depooter](https://github.com/JDepooter) * [Bentley Systems, Inc.](https://www.bentley.com) * [Paul Connelly](https://github.com/pmconne) +* [Flightradar24 AB](https://www.flightradar24.com) + * [Aleksei Kalmykov](https://github.com/kalmykov) -## [Individual CLA](http://www.agi.com/licenses/individual-cla-agi-v1.0.txt) +## [Individual CLA](Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt) * [Victor Berchet](https://github.com/vicb) * [Caleb Morse](https://github.com/cmorse) * [Ravi Agrawal](https://github.com/macoda) @@ -133,3 +142,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Prasanna Natarajan](https://github.com/PrasannaNatarajan) * [Joseph Klinger](https://github.com/klingerj) * [Grace Lee](https://github.com/glee2244) +* [Heiko Thiel](https://github.com/SunBlack) +* [Sravan Kumar Kilaru](https://github.com/kilarusravankumar) +* [Ryan King](https://github.com/ryki2658) +* [Jason Wohlgemuth](https://github.com/jhwohlgemuth) +* [Hülya Yurtman](https://github.com/hulyayurtman) +* [Esra ERİK](https://github.com/esraerik) \ No newline at end of file diff --git a/Documentation/Contributors/BuildGuide/README.md b/Documentation/Contributors/BuildGuide/README.md index 2a19759ac5b0..721ce500ba13 100644 --- a/Documentation/Contributors/BuildGuide/README.md +++ b/Documentation/Contributors/BuildGuide/README.md @@ -45,7 +45,7 @@ Cesium ships with a simple HTTP server for testing, run `npm start` after buildi ``` npm start -``` +``` Then browse to [http://localhost:8080/](http://localhost:8080/). The landing page includes apps and tools commonly used during development, including: @@ -56,7 +56,7 @@ Then browse to [http://localhost:8080/](http://localhost:8080/). The landing pag Cesium can be used in two different ways. Cesium can be either a set of modules using [Asynchronous Module Definition (AMD)](https://github.com/amdjs/amdjs-api/wiki/AMD), or it can be built as one combined file containing all modules. The basics: -* `npm run build` will build AMD Cesium. This also builds Cesium Viewer and Sandcastle. +* `npm run build` will build AMD Cesium. This also builds Cesium Viewer and Sandcastle. * `npm run minifyRelease` creates the built version of Cesium. This also builds Hello World. Read the complete list of build scripts below for more details. @@ -92,7 +92,7 @@ Here's the full set of scripts and what they do. * **Build scripts** -- build and package the source code and documentation * `build` - A fast, developer-oriented build that prepares the source tree for use as standard [Asynchronous Module Definition (AMD)](https://github.com/amdjs/amdjs-api/wiki/AMD) modules, suitable for running tests and most examples (some Sandcastle examples require running `combine`). Run this when a GLSL shader is changed since the .glsl file is converted to a .js file with a string for the GLSL source. This runs automatically when saving files in Eclipse. - * `build-watch` - A never-ending task that watches your file system for changes to Cesium and runs `build` on the source code as needed. + * `build-watch` - A never-ending task that watches your file system for changes to Cesium and runs `build` on the source code as needed. * `combine` - Runs `build`, plus the [the RequireJS optimizer](http://requirejs.org/docs/optimization.html) to combine Cesium and [the Almond AMD loader](http://requirejs.org/docs/faq-optimization.html#wrap) to produce all-in-one files in the `Build/Cesium` directory that exposes the entire Cesium API attached to a single global `Cesium` object. This version is useful if you don't want to use the modules directly with a standard AMD loader. * `minify` - Runs `combine`, plus [minifies](http://en.wikipedia.org/wiki/Minification_\(programming\)) Cesium.js using [UglifyJS2](https://github.com/mishoo/UglifyJS2) for a smaller deployable file. * `combineRelease` - Runs `combine`, plus uses the optimizer to remove debugging code that validates function input and throws DeveloperErrors. The removed sections are marked with `//>>includeStart('debug', pragmas.debug);` blocks in the code. @@ -104,8 +104,8 @@ Here's the full set of scripts and what they do. * `makeZipFile` - Builds a zip file containing all release files. This includes the source tree (suitable for use from an AMD-aware application), plus the combined and minified Cesium.js files, the generated documentation, the test suite, and the example applications (in both built and source form). * **Utility scripts** -- code coverage, static code analysis, and other utilities * `instrumentForCoverage` - Runs [JSCoverage](http://siliconforks.com/jscoverage/) on the source tree to allow running tests with coverage information. Use the link in index.html. Currently Windows only. - * `jsHint` - Runs [JSHint](http://www.jshint.com/), a static code analysis tool, on the entire source tree. - * `jsHint-watch` - A never-ending task that watches your file system for changes to Cesium and runs JSHint on any changed source files. + * `eslint` - Runs [ESLint](http://eslint.org/), a static code analysis tool, on the entire source tree. + * `eslint-watch` - A never-ending task that watches your file system for changes to Cesium and runs ESLint on any changed source files. * `clean` - Removes all generated build artifacts. * `cloc` - Runs [CLOC](https://github.com/AlDanial/cloc) to count the lines of code on the Source and Specs directories. This requires [Perl](http://www.perl.org/) to execute. * `sortRequires` - Alphabetically sorts the list of required modules in every `js` file. It also makes sure that the top of every source file uses the same formatting. diff --git a/Documentation/Contributors/CLAs/corporate-cla-agi-v1.0.txt b/Documentation/Contributors/CLAs/corporate-cla-agi-v1.0.txt new file mode 100644 index 000000000000..cf5b9a5f1f42 --- /dev/null +++ b/Documentation/Contributors/CLAs/corporate-cla-agi-v1.0.txt @@ -0,0 +1,146 @@ + Analytical Graphics, Inc. + Software Grant and Corporate Contributor License Agreement ("Agreement") + v1.0 + +In order to clarify the intellectual property license granted with +Contributions from any person or entity, Analytical Graphics, Inc. +("AGI") must have a Contributor License Agreement (CLA) on file that has +been signed by each Contributor, indicating agreement to the license +terms below. This license is for your protection as a Contributor as well +as the protection of AGI and its users; it does not change your rights to +use your own Contributions for any other purpose. + +This version of the Agreement allows an entity (the "Corporation") to +submit Contributions to AGI, to authorize Contributions submitted by its +designated employees to AGI, and to grant copyright and patent licenses thereto. + +Please send complete forms to cla@agi.com. + +Please read this document carefully before signing and keep a copy +for your records. + + Corporation name: ________________________________________________ + + Corporation address: ________________________________________________ + + ________________________________________________ + + ________________________________________________ + + Point of Contact: ________________________________________________ + + E-Mail: ________________________________________________ + + Telephone: _____________________ Fax: _____________________ + + +You accept and agree to the following terms and conditions for Your present +and future Contributions submitted to AGI. Except for the license granted +herein to AGI and recipients of software distributed by AGI, You reserve +all right, title, and interest in and to Your Contributions. + + 1. Definitions. + + "You" (or "Your") shall mean the copyright owner or legal entity + authorized by the copyright owner that is making this Agreement + with AGI. For legal entities, the entity making a Contribution and + all other entities that control, are controlled by, or are under + common control with that entity are considered to be a single + Contributor. For the purposes of this definition, "control" means + (i) the power, direct or indirect, to cause the direction or + management of such entity, whether by contract or otherwise, or + (ii) ownership of fifty percent (50%) or more of the outstanding + shares, or (iii) beneficial ownership of such entity. + + "Contribution" shall mean the code, documentation or other original + works of authorship expressly identified in Schedule B, as well as + any original work of authorship, including any modifications or + additions to an existing work, that is intentionally submitted by + You to AGI for inclusion in, or documentation of, any of the + products owned or managed by AGI (the "Work"). For the purposes of + this definition, "submitted" means any form of electronic, verbal, + or written communication sent to AGI or its representatives, + including but not limited to communication on electronic mailing + lists, source code control systems, and issue tracking systems + that are managed by, or on behalf of, AGI for the purpose of + discussing and improving the Work, but excluding communication + that is conspicuously marked or otherwise designated in writing by + You as "Not a Contribution." + + 2. Grant of Copyright License. Subject to the terms and conditions + of this Agreement, You hereby grant to AGI and to recipients of + software distributed by AGI a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license to reproduce, + prepare derivative works of, publicly display, publicly perform, + sublicense, and distribute Your Contributions and such derivative + works. + + 3. Grant of Patent License. Subject to the terms and conditions of + this Agreement, You hereby grant to AGI and to recipients of + software distributed by AGI a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this + section) patent license to make, have made, use, offer to sell, + sell, import, and otherwise transfer the Work, where such license + applies only to those patent claims licensable by You that are + necessarily infringed by Your Contribution(s) alone or by combination + of Your Contribution(s) with the Work to which such Contribution(s) + were submitted. If any entity institutes patent litigation against + You or any other entity (including a cross-claim or counterclaim in a + awsuit) alleging that your Contribution, or the Work to which you + have contributed, constitutes direct or contributory patent + infringement, then any patent licenses granted to that entity under + this Agreement for that Contribution or Work shall terminate as of + the date such litigation is filed. + + 4. You represent that You are legally entitled to grant the above + license. You represent further that each employee of the + Corporation designated on Schedule A below (or in a subsequent + written modification to that Schedule) is authorized to submit + Contributions on behalf of the Corporation. + + 5. You represent that each of Your Contributions is Your original + creation (see section 7 for submissions on behalf of others). + + 6. You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. Unless required by + applicable law or agreed to in writing, You provide Your + Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + OF ANY KIND, either express or implied, including, without + limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + + 7. Should You wish to submit work that is not Your original creation, + You may submit it to AGI separately from any Contribution, identifying + the complete details of its source and of any license or other + restriction (including, but not limited to, related patents, trademarks, + and license agreements) of which you are personally aware, and + conspicuously marking the work as "Submitted on behalf of a third- + party: [named here]". + + 8. It is your responsibility to notify AGI when any change is required + to the list of designated employees authorized to submit Contributions + on behalf of the Corporation, or to the Corporation's Point of Contact + with AGI. + +Sign by typing your full name. + + Please sign: __________________________________ Date: _______________ + + Title: __________________________________ + + Corporation: __________________________________ + + +Schedule A + + [Initial list of designated employees. NB: authorization is not + tied to particular Contributions.] + + + + +Schedule B + + [Identification of optional concurrent software grant. Would be + left blank or omitted if there is no concurrent software grant.] diff --git a/Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt b/Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt new file mode 100644 index 000000000000..c43e8bfc7871 --- /dev/null +++ b/Documentation/Contributors/CLAs/individual-cla-agi-v1.0.txt @@ -0,0 +1,127 @@ + Analytical Graphics, Inc. + Individual Contributor License Agreement ("Agreement") v1.0 + +In order to clarify the intellectual property license granted with +Contributions from any person or entity, Analytical Graphics, Inc. +("AGI") must have a Contributor License Agreement ("CLA") on file +that has been signed by each Contributor, indicating agreement to +the license terms below. This license is for your protection as a +Contributor as well as the protection of AGI; it does not change +your rights to use your own Contributions for any other purpose. + +Please send complete forms to cla@agi.com. + +Please read this document carefully before signing and keep a copy +for your records. + + Full name: ______________________________________________________ + + Mailing Address: ________________________________________________ + + ________________________________________________ + + Country: ______________________________________________________ + + Telephone: ______________________________________________________ + + E-Mail: ______________________________________________________ + + GitHub username: ________________________________________________ + +You accept and agree to the following terms and conditions for Your +present and future Contributions submitted to AGI. Except for the +license granted herein to AGI and recipients of software distributed +by AGI, You reserve all right, title, and interest in and to Your +Contributions. + +1. Definitions. + + "You" (or "Your") shall mean the copyright owner or legal entity + authorized by the copyright owner that is making this Agreement + with AGI. For legal entities, the entity making a Contribution + and all other entities that control, are controlled by, or are + under common control with that entity are considered to be a + single Contributor. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, that + is intentionally submitted by You to AGI for inclusion in, or + documentation of, any of the products owned or managed by the + AGI (the "Work"). For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication + sent to AGI or its representatives, including but not limited to + communication on electronic mailing lists, source code control + systems, and issue tracking systems that are managed by, or on + behalf of, AGI for the purpose of discussing and improving the + Work, but excluding communication that is conspicuously marked or + otherwise designated in writing by You as "Not a Contribution." + +2. Grant of Copyright License. Subject to the terms and conditions of + this Agreement, You hereby grant to AGI and to recipients of + software distributed by AGI a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, sublicense, and distribute Your Contributions and such + derivative works. + +3. Grant of Patent License. Subject to the terms and conditions of + this Agreement, You hereby grant to AGI and to recipients of + software distributed by AGI a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this + section) patent license to make, have made, use, offer to sell, + sell, import, and otherwise transfer the Work, where such license + applies only to those patent claims licensable by You that are + necessarily infringed by Your Contribution(s) alone or by + combination of Your Contribution(s) with the Work to which such + Contribution(s) was submitted. If any entity institutes patent + litigation against You or any other entity (including a cross-claim + or counterclaim in a lawsuit) alleging that your Contribution, or + the Work to which you have contributed, constitutes direct or + contributory patent infringement, then any patent licenses granted + to that entity under this Agreement for that Contribution or Work + shall terminate as of the date such litigation is filed. + +4. You represent that you are legally entitled to grant the above + license. If your employer(s) has rights to intellectual property + that you create that includes your Contributions, you represent + that you have received permission to make Contributions on behalf + of that employer, that your employer has waived such rights for + your Contributions to AGI, or that your employer has executed a + separate Corporate CLA with AGI. + +5. You represent that each of Your Contributions is Your original + creation (see section 7 for submissions on behalf of others). You + represent that Your Contribution submissions include complete + details of any third-party license or other restriction (including, + but not limited to, related patents and trademarks) of which you + are personally aware and which are associated with any part of Your + Contributions. + +6. You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. Unless required by + applicable law or agreed to in writing, You provide Your + Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + OF ANY KIND, either express or implied, including, without + limitation, any warranties or conditions of TITLE, NON- + INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + +7. Should You wish to submit work that is not Your original creation, + You may submit it to AGI separately from any Contribution, identifying + the complete details of its source and of any license or other + restriction (including, but not limited to, related patents, trademarks, + and license agreements) of which you are personally aware, and + conspicuously marking the work as "Submitted on behalf of a third- + party: [named here]". + +8. You agree to notify AGI of any facts or circumstances of which you + become aware that would make these representations inaccurate in any + respect. + +Sign by typing your full name. + +Please sign: __________________________________ Date: ________________ diff --git a/Documentation/Contributors/CodeReviewGuide/README.md b/Documentation/Contributors/CodeReviewGuide/README.md index 9a0323a4ead5..d56d5d6c44a9 100644 --- a/Documentation/Contributors/CodeReviewGuide/README.md +++ b/Documentation/Contributors/CodeReviewGuide/README.md @@ -47,7 +47,7 @@ This guide describes best practices for code reviews. ## Merging -* Cesium uses Travis CI for continuous integration. Travis automatically builds Cesium, runs JSHint, and generates the documentation for each branch pushed to GitHub. Before merging a pull request, verify that all Travis checks pass, indicated by the green check-mark and green `Merge pull request` button: +* Cesium uses Travis CI for continuous integration. Travis automatically builds Cesium, runs ESLint, and generates the documentation for each branch pushed to GitHub. Before merging a pull request, verify that all Travis checks pass, indicated by the green check-mark and green `Merge pull request` button: ![](Travis.jpg) @@ -99,7 +99,7 @@ When in doubt, merge. Futher Reading: [Merge vs Rebase](https://www.derekgourlay.com/blog/git-when-to-merge-vs-when-to-rebase/). #### Merge -With merge, your commits will become interleaved with other target branch commits based on timestamp. +With merge, your commits will become interleaved with other target branch commits based on timestamp. ``` git fetch --all # Fetch updates from all remotes git merge upstream/target @@ -114,6 +114,20 @@ git rebase -i upstream/target git push -f origin mybranch # Requires force push as it is changing existing history on remote ``` +### You want to checkout a pull-request for review + +GitHub's [hub](https://hub.github.com) makes checking-out PR's simple. For example, run: + + ```hub checkout https://github.com/AnalyticalGraphicsInc/cesium/pull/3941``` + + This will create a new branch with the contents of the pull request. Also, you can easily add remote + forks with: + + ```hub fetch boomer_jones,pjcozzi``` + + which will automatically add these repos as remotes and fetch them. See the hub [open-source maintainer section](https://hub.github.com/#maintainer) + for more info. + ## Resources * [Practice Conspicuous Code Review](http://producingoss.com/en/producingoss.html#code-review) in [Producing Open Source Software](http://producingoss.com/). diff --git a/Documentation/Contributors/CodingGuide/README.md b/Documentation/Contributors/CodingGuide/README.md index c4e1db5cff73..b46302464927 100644 --- a/Documentation/Contributors/CodingGuide/README.md +++ b/Documentation/Contributors/CodingGuide/README.md @@ -135,6 +135,16 @@ function Model(options) { * Text files, including JavaScript files, end with a newline to minimize the noise in diffs. +* When [disabling rules with inline comments](http://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments), place the comments on new lines and as close to the associated code as possible: +```js +/*eslint-disable no-empty*/ +try { + lineNumber = parseInt(stack.substring(lineStart + 1, lineEnd1), 10); +} catch(ex) { +} +/*eslint-enable no-empty*/ +``` + ## Units * Cesium uses SI units: @@ -290,12 +300,12 @@ Cesium3DTileset.prototype.update = function(frameState) { updateTiles(this, frameState); }; -function processTiles(tiles3D, frameState) { - var tiles = tiles3D._processingQueue; +function processTiles(tileset, frameState) { + var tiles = tileset._processingQueue; var length = tiles.length; for (var i = length - 1; i >= 0; --i) { - tiles[i].process(tiles3D, frameState); + tiles[i].process(tileset, frameState); } } ``` @@ -571,12 +581,12 @@ Cesium3DTileset.prototype.update = function(frameState) { // ... }; -Cesium3DTileset.prototype._processTiles(tiles3D, frameState) { +Cesium3DTileset.prototype._processTiles(tileset, frameState) { var tiles = this._processingQueue; var length = tiles.length; for (var i = length - 1; i >= 0; --i) { - tiles[i].process(tiles3D, frameState); + tiles[i].process(tileset, frameState); } } ``` @@ -587,12 +597,12 @@ Cesium3DTileset.prototype.update = function(frameState) { // ... }; -function processTiles(tiles3D, frameState) { - var tiles = tiles3D._processingQueue; +function processTiles(tileset, frameState) { + var tiles = tileset._processingQueue; var length = tiles.length; for (var i = length - 1; i >= 0; --i) { - tiles[i].process(tiles3D, frameState); + tiles[i].process(tileset, frameState); } } ``` @@ -672,13 +682,13 @@ Model.prototype.update = function(frameState) { It is convenient for the constructor function to be at the top of the file even if it requires that helper functions rely on **hoisting**, for example, `Cesium3DTileset.js`, ```javascript -function loadTilesJson(tileset, tilesJson, done) { +function loadTileset(tileset, tilesJson, done) { // ... } function Cesium3DTileset(options) { // ... - loadTilesJson(this, options.url, function(data) { + loadTileset(this, options.url, function(data) { // ... }); }; @@ -687,16 +697,16 @@ is better written as ```javascript function Cesium3DTileset(options) { // ... - loadTilesJson(this, options.url, function(data) { + loadTileset(this, options.url, function(data) { // ... }); }; -function loadTilesJson(tileset, tilesJson, done) { +function loadTileset(tileset, tilesJson, done) { // ... } ``` -even though it relies on implicitly hoisting the `loadTilesJson` function to the top of the file. +even though it relies on implicitly hoisting the `loadTileset` function to the top of the file. ## Design @@ -800,7 +810,7 @@ When using a subscription, always be sure to [dispose the subscription](https:// ``` fullscreenSubscription = subscribeAndEvaluate(fullscreenButton.viewModel, 'isFullscreenEnabled', function(isFullscreenEnabled) { ... }); // ...then later... -fullscreenSubscription.dispose(); +fullscreenSubscription.dispose(); ``` ## GLSL diff --git a/Documentation/Contributors/EclipseGuide/README.md b/Documentation/Contributors/EclipseGuide/README.md deleted file mode 100644 index e6a7dcb14391..000000000000 --- a/Documentation/Contributors/EclipseGuide/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Eclipse Guide - -While primarily known as a Java IDE, Eclipse can be configured to work well for web development too. These instructions are written for Eclipse Neon but the process is the same for any recent version. - -* Install [Java](http://www.java.com/en/download/manual.jsp) if it isn't already. -* Download the [Eclipse IDE for Java Developers](http://www.eclipse.org/downloads/eclipse-packages/). Extract to a directory of your choice and run. Create a workspace anywhere you like. - -* Install additional Eclipse components: JavaScript Development Tools and Eclipse Web Developer Tools. - * Help - Install New Software. Work with: select *Neon* from the list. - * Expand *Programming Languages*, check *JavaScript Development Tools*. - * Expand *Web, XML, Java EE and OSGi Enterprise Development*, check *Eclipse Web Developer Tools*. - * Next, Next, Accept, Finish, _wait_, No (we have more to install). - -![The Java Development Tools installation dialog](indigo.jpg) - -* Install the [JSHint](http://www.jshint.com/) plugin: - * Help - Install New Software. Work with: `http://github.eclipsesource.com/jshint-eclipse/updates/`. - * Check *JSHint*. Next, Next, Accept, Finish, _wait_, OK, _wait_, Restart. - -![The JSHint installation dialog](jshint.jpg) - -* Run Eclipse. Close the Welcome page. - -* Window - Preferences: - * General - Editors - Text Editors. Check Insert spaces for tabs. Apply. - * Web - CSS Files - Editor. Switch the radio button to "Indent using spaces". Change Indentation size to 4. Apply. - * Web - HTML Files - Editor. Switch the radio button to "Indent using spaces". Change Indentation size to 4. OK. - -![Configuring "Insert spaces for tabs"](tabs.jpg) - -* Import Cesium into your workspace: File - Import, General - Existing Projects into Workspace, Next. Fill in the path to the root Cesium directory, Finish. - -* Click the "Open Perspective" button in the upper right and select JavaScript. You can then right-click on the Java perspective and close it. - -* Window - Show View - Console. - -Also consider the [Optional Eclipse Configuration](#optionaleclipseconfiguration) options below. - -## Optional Eclipse Configuration - -These steps are optional depending on your preference. - -### GLSL Plugin - -If you edit WebGL shader files (.glsl) with Eclipse, install GLShaders for GLSL syntax highlighting. First exit Eclipse, then download [GLShaders](http://sourceforge.net/projects/glshaders/) and extract into Eclipse's dropins directory. - -![The Eclipse dropins directory](glshaders.jpg) - -### Git - -Most of us use Git from the command-line, but there is also Eclipse integration: - -* Window - Preferences: Team - Git - Configuration - * Verify Location in User Settings tab is set to .gitconfig in the default repository directory. - * Verify Location in Systems Settings tab is set to {Installed/Git/Location}/etc/gitconfig. - -* Right click on Cesium in the Script Explorer. Team - Share project. Select Git, Next. Check Use or create repository in parent directory of project. Finish. - -## Eclipse Tips - -* Use Ctrl-Shift-R to search and open files in the workspace. - -![The Open Resource dialog](openresource.jpg) - -* Use Ctrl-Shift-F to auto format selected code. diff --git a/Documentation/Contributors/EclipseGuide/glshaders.jpg b/Documentation/Contributors/EclipseGuide/glshaders.jpg deleted file mode 100644 index 1c2ddeabff62..000000000000 Binary files a/Documentation/Contributors/EclipseGuide/glshaders.jpg and /dev/null differ diff --git a/Documentation/Contributors/EclipseGuide/indigo.jpg b/Documentation/Contributors/EclipseGuide/indigo.jpg deleted file mode 100644 index c37c9713d27b..000000000000 Binary files a/Documentation/Contributors/EclipseGuide/indigo.jpg and /dev/null differ diff --git a/Documentation/Contributors/EclipseGuide/jshint.jpg b/Documentation/Contributors/EclipseGuide/jshint.jpg deleted file mode 100644 index 3d8b6c7c9f5a..000000000000 Binary files a/Documentation/Contributors/EclipseGuide/jshint.jpg and /dev/null differ diff --git a/Documentation/Contributors/EclipseGuide/openresource.jpg b/Documentation/Contributors/EclipseGuide/openresource.jpg deleted file mode 100644 index 2c6b21dd1f85..000000000000 Binary files a/Documentation/Contributors/EclipseGuide/openresource.jpg and /dev/null differ diff --git a/Documentation/Contributors/EclipseGuide/tabs.jpg b/Documentation/Contributors/EclipseGuide/tabs.jpg deleted file mode 100644 index cf3f5002e7fb..000000000000 Binary files a/Documentation/Contributors/EclipseGuide/tabs.jpg and /dev/null differ diff --git a/Documentation/Contributors/README.md b/Documentation/Contributors/README.md index d783cee68e4a..fd923758d4f1 100644 --- a/Documentation/Contributors/README.md +++ b/Documentation/Contributors/README.md @@ -2,9 +2,8 @@ * [CONTRIBUTING.md](../../CONTRIBUTING.md) - Start here. How to find something to work on, submit issues, and open pull requests. * [Build Guide](BuildGuide/README.md) - How to build and run Cesium locally. -* **IDEs** - use any IDE you want for Cesium development. Most contributors use WebStorm (commercial) or Eclipse (open source). +* **IDEs** - use any IDE you want for Cesium development. Most contributors use WebStorm (commercial) or VSCode (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. diff --git a/Documentation/Contributors/TestingGuide/README.md b/Documentation/Contributors/TestingGuide/README.md index 98d109ac4613..8dcc2f7150d8 100644 --- a/Documentation/Contributors/TestingGuide/README.md +++ b/Documentation/Contributors/TestingGuide/README.md @@ -15,7 +15,7 @@ All new code should have 100% code coverage and should pass all tests. Always r * [Run Only Non-WebGL Tests](#run-only-non-webgl-tests) * [Run All Tests against Combined File (Run All Tests against Combined File with Debug Code Removed)]() * [Run All Tests with Code Coverage (Build 'instrumentForCoverage' First)](#run-all-tests-against-combined-file-run-all-tests-against-combined-file-with-debug-code-removed) - * [Running Tests on the Command Line with Karma](#run-all-tests-on-the-command-line-with-karma) + * [Running Tests on the Command Line with Karma](#running-tests-on-the-command-line-with-karma) * [Testing Previous Versions of Cesium](#testing-previous-versions-of-cesium) * [`testfailure` Label for Issues](#testfailure-label-for-issues) * [Writing Tests](#writing-tests) @@ -630,7 +630,7 @@ it('can create a billboard using a URL', function() { return pollToPromise(function() { return b.ready; }).then(function() { - expect(scene.renderForSpecs()).toEqual([0, 255, 0, 255]); + expect(scene).toRender([0, 255, 0, 255]); }); }); ``` diff --git a/Documentation/Contributors/VSCodeGuide/README.md b/Documentation/Contributors/VSCodeGuide/README.md index 61cedd85f973..03d38e30c4f5 100644 --- a/Documentation/Contributors/VSCodeGuide/README.md +++ b/Documentation/Contributors/VSCodeGuide/README.md @@ -32,24 +32,29 @@ 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) +## VSCode Extensions (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 +extension, there are some that appear to be quite useful to Cesium. Just enter +the desired extension name in the search box and click install. You will need to +restart VSCode after you are done installing extensions. + +* **[eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)** +by Dirk Baeumer -- This extension picks up on Cesium's own eslint settings, +and will warn of any violations. The Cesium main repository should pass eslint +using the Cesium eslint settings with no warnings and no errors. Proposed +contributions to Cesium that introduce eslint 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. +* **[Shader languages support for VS Code](https://marketplace.visualstudio.com/items?itemName=slevesque.shader)** +by slevesque -- This extension provides syntax highlighting for Cesium's shader code. -* **Prettify JSON** by Mohsen Azimi -- This seems generally useful. +* **[Prettify JSON](https://marketplace.visualstudio.com/items?itemName=mohsen1.prettify-json)** + by Mohsen Azimi -- This seems generally useful. + +* **[glTF Extension for VS Code](https://marketplace.visualstudio.com/items?itemName=cesium.gltf-vscode)** +by CesiumJS.org -- This extension adds features for previewing and editing 3D models in glTF files. ## VSCode Tasks and Files diff --git a/LICENSE.md b/LICENSE.md index e95d8ff924ac..3f9c51b5058f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,12 +1,210 @@ Copyright 2011-2017 Cesium Contributors -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -Columbus View (Pat. Pend.) + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011-2017 Cesium Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Patent 9153063 + +Skipping Heuristics and Fusing (Pat. Pend.) Third-Party Code ================ @@ -214,11 +412,11 @@ https://gist.github.com/banksean/300494 > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -### NVIDIA Image-Based Anti-Aliasing +### NVIDIA GameWorks Graphics Samples -https://developer.nvidia.com/nvidia-graphics-sdk-11-direct3d +https://github.com/NVIDIAGameWorks/GraphicsSamples -> Copyright 2011 NVIDIA Corporation +> Copyright 2016 NVIDIA Corporation > > BY DOWNLOADING THE SOFTWARE AND OTHER AVAILABLE MATERIALS, YOU ("DEVELOPER") AGREE TO BE BOUND BY THE FOLLOWING TERMS AND CONDITIONS > @@ -252,6 +450,31 @@ https://github.com/richtr/NoSleep.js > Rich Tibbett > MIT license +### jsep + +https://github.com/soney/jsep + +> Copyright (c) 2013 Stephen Oney, http://jsep.from.so/ +> +> Permission is hereby granted, free of charge, to any person obtaining +> a copy of this software and associated documentation files (the +> "Software"), to deal in the Software without restriction, including +> without limitation the rights to use, copy, modify, merge, publish, +> distribute, sublicense, and/or sell copies of the Software, and to +> permit persons to whom the Software is furnished to do so, subject to +> the following conditions: +> +> The above copyright notice and this permission notice shall be +> included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### earcut https://github.com/mapbox/earcut @@ -367,6 +590,63 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +### pako + +https://github.com/nodeca/pako + +>(The MIT License) +> +>Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn +> +>Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +> +>The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +> +>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +### protobuf + +https://github.com/dcodeIO/ProtoBuf.js + +>Copyright (c) 2016, Daniel Wirtz All rights reserved. +> +>Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +>* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +>* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +>* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Tests ===== @@ -501,11 +781,11 @@ www.bigbuckbunny.org https://github.com/dataarts/webgl-globe > Copyright 2011 Google Data Arts Team -> +> > Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -> +> > http://www.apache.org/licenses/LICENSE-2.0 -> +> > Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ### Wooden Watch Tower ### diff --git a/README.md b/README.md index 7b1531411335..6143e463c93d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

[![Build Status](https://travis-ci.org/AnalyticalGraphicsInc/cesium.svg?branch=master)](https://travis-ci.org/AnalyticalGraphicsInc/cesium)  -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) [![Docs](https://img.shields.io/badge/docs-online-orange.svg)](http://cesiumjs.org/tutorials.html) +[![Docs](https://img.shields.io/badge/docs-online-orange.svg)](http://cesiumjs.org/tutorials.html) Cesium is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin. It uses WebGL for hardware-accelerated graphics, and is cross-platform, cross-browser, and tuned for dynamic-data visualization. diff --git a/Source/.jshintrc b/Source/.jshintrc deleted file mode 100644 index 24762c7dc745..000000000000 --- a/Source/.jshintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../.jshintrc", - "jasmine": false -} diff --git a/Source/Core/AttributeCompression.js b/Source/Core/AttributeCompression.js index 0d943901ceec..04dd27eea126 100644 --- a/Source/Core/AttributeCompression.js +++ b/Source/Core/AttributeCompression.js @@ -2,12 +2,14 @@ define([ './Cartesian2', './Cartesian3', + './Check', './defined', './DeveloperError', './Math' ], function( Cartesian2, Cartesian3, + Check, defined, DeveloperError, CesiumMath) { @@ -40,12 +42,8 @@ define([ */ AttributeCompression.octEncodeInRange = function(vector, rangeMax, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(vector)) { - throw new DeveloperError('vector is required.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.defined('vector', vector); + Check.defined('result', result); var magSquared = Cartesian3.magnitudeSquared(vector); if (Math.abs(magSquared - 1.0) > CesiumMath.EPSILON6) { throw new DeveloperError('vector must be normalized.'); @@ -98,9 +96,7 @@ define([ */ AttributeCompression.octDecodeInRange = function(x, y, rangeMax, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.defined('result', result); if (x < 0 || x > rangeMax || y < 0 || y > rangeMax) { throw new DeveloperError('x and y must be a signed normalized integer between 0 and ' + rangeMax); } @@ -145,9 +141,7 @@ define([ */ AttributeCompression.octPackFloat = function(encoded) { //>>includeStart('debug', pragmas.debug); - if (!defined(encoded)) { - throw new DeveloperError('encoded is required.'); - } + Check.defined('encoded', encoded); //>>includeEnd('debug'); return 256.0 * encoded.x + encoded.y; }; @@ -178,9 +172,7 @@ define([ */ AttributeCompression.octDecodeFloat = function(value, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required.'); - } + Check.defined('value', value); //>>includeEnd('debug'); var temp = value / 256.0; @@ -203,18 +195,10 @@ define([ */ AttributeCompression.octPack = function(v1, v2, v3, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(v1)) { - throw new DeveloperError('v1 is required.'); - } - if (!defined(v2)) { - throw new DeveloperError('v2 is required.'); - } - if (!defined(v3)) { - throw new DeveloperError('v3 is required.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.defined('v1', v1); + Check.defined('v2', v2); + Check.defined('v3', v3); + Check.defined('result', result); //>>includeEnd('debug'); var encoded1 = AttributeCompression.octEncodeFloat(v1); @@ -236,18 +220,10 @@ define([ */ AttributeCompression.octUnpack = function(packed, v1, v2, v3) { //>>includeStart('debug', pragmas.debug); - if (!defined(packed)) { - throw new DeveloperError('packed is required.'); - } - if (!defined(v1)) { - throw new DeveloperError('v1 is required.'); - } - if (!defined(v2)) { - throw new DeveloperError('v2 is required.'); - } - if (!defined(v3)) { - throw new DeveloperError('v3 is required.'); - } + Check.defined('packed', packed); + Check.defined('v1', v1); + Check.defined('v2', v2); + Check.defined('v3', v3); //>>includeEnd('debug'); var temp = packed.x / 65536.0; @@ -272,9 +248,7 @@ define([ */ AttributeCompression.compressTextureCoordinates = function(textureCoordinates) { //>>includeStart('debug', pragmas.debug); - if (!defined(textureCoordinates)) { - throw new DeveloperError('textureCoordinates is required.'); - } + Check.defined('textureCoordinates', textureCoordinates); //>>includeEnd('debug'); // Move x and y to the range 0-4095; @@ -293,12 +267,8 @@ define([ */ AttributeCompression.decompressTextureCoordinates = function(compressed, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(compressed)) { - throw new DeveloperError('compressed is required.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.defined('compressed', compressed); + Check.defined('result', result); //>>includeEnd('debug'); var temp = compressed / 4096.0; diff --git a/Source/Core/AxisAlignedBoundingBox.js b/Source/Core/AxisAlignedBoundingBox.js index 0225c6a5240b..bec857c9d3d5 100644 --- a/Source/Core/AxisAlignedBoundingBox.js +++ b/Source/Core/AxisAlignedBoundingBox.js @@ -1,15 +1,15 @@ /*global define*/ define([ './Cartesian3', + './Check', './defaultValue', './defined', - './DeveloperError', './Intersect' ], function( Cartesian3, + Check, defaultValue, defined, - DeveloperError, Intersect) { 'use strict'; @@ -170,12 +170,8 @@ define([ */ AxisAlignedBoundingBox.intersectPlane = function(box, plane) { //>>includeStart('debug', pragmas.debug); - if (!defined(box)) { - throw new DeveloperError('box is required.'); - } - if (!defined(plane)) { - throw new DeveloperError('plane is required.'); - } + Check.defined('box', box); + Check.defined('plane', plane); //>>includeEnd('debug'); intersectScratch = Cartesian3.subtract(box.maximum, box.minimum, intersectScratch); diff --git a/Source/Core/BingMapsGeocoderService.js b/Source/Core/BingMapsGeocoderService.js index e4130d8ed61a..c2f20daa0420 100644 --- a/Source/Core/BingMapsGeocoderService.js +++ b/Source/Core/BingMapsGeocoderService.js @@ -1,18 +1,18 @@ /*global define*/ define([ './BingMapsApi', + './Check', './defaultValue', './defined', './defineProperties', - './DeveloperError', './loadJsonp', './Rectangle' ], function( BingMapsApi, + Check, defaultValue, defined, defineProperties, - DeveloperError, loadJsonp, Rectangle) { 'use strict'; @@ -25,15 +25,13 @@ define([ * @constructor * * @param {Object} options Object with the following properties: - * @param {String} options.scene The scene + * @param {Scene} 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); //>>includeStart('debug', pragmas.debug); - if (!defined(options.scene)) { - throw new DeveloperError('options.scene is required.'); - } + Check.typeOf.object('options.scene', options.scene); //>>includeEnd('debug'); var key = options.key; @@ -81,9 +79,7 @@ define([ */ BingMapsGeocoderService.prototype.geocode = function(query) { //>>includeStart('debug', pragmas.debug); - if (!defined(query)) { - throw new DeveloperError('query must be defined'); - } + Check.typeOf.string('query', query); //>>includeEnd('debug'); var key = this.key; diff --git a/Source/Core/BoundingRectangle.js b/Source/Core/BoundingRectangle.js index ad7ca2a8a865..3fffcbfeca77 100644 --- a/Source/Core/BoundingRectangle.js +++ b/Source/Core/BoundingRectangle.js @@ -169,7 +169,7 @@ define([ var fromRectangleLowerLeft = new Cartographic(); var fromRectangleUpperRight = new Cartographic(); /** - * Computes a bounding rectangle from an rectangle. + * Computes a bounding rectangle from a rectangle. * * @param {Rectangle} rectangle The valid rectangle used to create a bounding rectangle. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. diff --git a/Source/Core/BoundingSphere.js b/Source/Core/BoundingSphere.js index f91c30777dcb..8f491a362d98 100644 --- a/Source/Core/BoundingSphere.js +++ b/Source/Core/BoundingSphere.js @@ -221,7 +221,7 @@ define([ var fromRectangle2DNortheast = new Cartographic(); /** - * Computes a bounding sphere from an rectangle projected in 2D. + * Computes a bounding sphere from a rectangle projected in 2D. * * @param {Rectangle} rectangle The rectangle around which to create a bounding sphere. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. @@ -233,7 +233,7 @@ define([ }; /** - * Computes a bounding sphere from an rectangle projected in 2D. The bounding sphere accounts for the + * Computes a bounding sphere from a rectangle projected in 2D. The bounding sphere accounts for the * object's minimum and maximum heights over the rectangle. * * @param {Rectangle} rectangle The rectangle around which to create a bounding sphere. @@ -279,7 +279,7 @@ define([ var fromRectangle3DScratch = []; /** - * Computes a bounding sphere from an rectangle in 3D. The bounding sphere is created using a subsample of points + * Computes a bounding sphere from a rectangle in 3D. The bounding sphere is created using a subsample of points * on the ellipsoid and contained in the rectangle. It may not be accurate for all rectangles on all types of ellipsoids. * * @param {Rectangle} rectangle The valid rectangle used to create a bounding sphere. @@ -752,6 +752,10 @@ define([ * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromOrientedBoundingBox = function(orientedBoundingBox, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('orientedBoundingBox', orientedBoundingBox); + //>>includeEnd('debug'); + if (!defined(result)) { result = new BoundingSphere(); } @@ -761,12 +765,11 @@ define([ var v = Matrix3.getColumn(halfAxes, 1, fromOrientedBoundingBoxScratchV); var w = Matrix3.getColumn(halfAxes, 2, fromOrientedBoundingBoxScratchW); - var uHalf = Cartesian3.magnitude(u); - var vHalf = Cartesian3.magnitude(v); - var wHalf = Cartesian3.magnitude(w); + Cartesian3.add(u, v, u); + Cartesian3.add(u, w, u); result.center = Cartesian3.clone(orientedBoundingBox.center, result.center); - result.radius = Math.max(uHalf, vHalf, wHalf); + result.radius = Cartesian3.magnitude(u); return result; }; diff --git a/Source/Core/BoxGeometry.js b/Source/Core/BoxGeometry.js index 40de894a0f37..ed7aaf5ff8b4 100644 --- a/Source/Core/BoxGeometry.js +++ b/Source/Core/BoxGeometry.js @@ -2,10 +2,10 @@ define([ './BoundingSphere', './Cartesian3', + './Check', './ComponentDatatype', './defaultValue', './defined', - './DeveloperError', './Geometry', './GeometryAttribute', './GeometryAttributes', @@ -14,10 +14,10 @@ define([ ], function( BoundingSphere, Cartesian3, + Check, ComponentDatatype, defaultValue, defined, - DeveloperError, Geometry, GeometryAttribute, GeometryAttributes, @@ -59,12 +59,8 @@ define([ var max = options.maximum; //>>includeStart('debug', pragmas.debug); - if (!defined(min)) { - throw new DeveloperError('options.minimum is required.'); - } - if (!defined(max)) { - throw new DeveloperError('options.maximum is required'); - } + Check.typeOf.object('min', min); + Check.typeOf.object('max', max); //>>includeEnd('debug'); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); @@ -100,12 +96,10 @@ define([ var dimensions = options.dimensions; //>>includeStart('debug', pragmas.debug); - if (!defined(dimensions)) { - throw new DeveloperError('options.dimensions is required.'); - } - if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) { - throw new DeveloperError('All dimensions components must be greater than or equal to zero.'); - } + Check.typeOf.object('dimensions', dimensions); + Check.typeOf.number.greaterThanOrEquals('dimensions.x', dimensions.x, 0); + Check.typeOf.number.greaterThanOrEquals('dimensions.y', dimensions.y, 0); + Check.typeOf.number.greaterThanOrEquals('dimensions.z', dimensions.z, 0); //>>includeEnd('debug'); var corner = Cartesian3.multiplyByScalar(dimensions, 0.5, new Cartesian3()); @@ -139,9 +133,7 @@ define([ */ BoxGeometry.fromAxisAlignedBoundingBox = function (boundingBox) { //>>includeStart('debug', pragmas.debug); - if (!defined(boundingBox)) { - throw new DeveloperError('boundingBox is required.'); - } + Check.typeOf.object('boundingBox', boundingBox); //>>includeEnd('debug'); return new BoxGeometry({ @@ -167,12 +159,8 @@ define([ */ BoxGeometry.pack = function(value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required'); - } - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('value', value); + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -203,9 +191,7 @@ define([ */ BoxGeometry.unpack = function(array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); diff --git a/Source/Core/BoxOutlineGeometry.js b/Source/Core/BoxOutlineGeometry.js index 4cca30c5c608..61b18b314426 100644 --- a/Source/Core/BoxOutlineGeometry.js +++ b/Source/Core/BoxOutlineGeometry.js @@ -2,10 +2,10 @@ define([ './BoundingSphere', './Cartesian3', + './Check', './ComponentDatatype', './defaultValue', './defined', - './DeveloperError', './Geometry', './GeometryAttribute', './GeometryAttributes', @@ -13,10 +13,10 @@ define([ ], function( BoundingSphere, Cartesian3, + Check, ComponentDatatype, defaultValue, defined, - DeveloperError, Geometry, GeometryAttribute, GeometryAttributes, @@ -53,12 +53,8 @@ define([ var max = options.maximum; //>>includeStart('debug', pragmas.debug); - if (!defined(min)) { - throw new DeveloperError('options.minimum is required.'); - } - if (!defined(max)) { - throw new DeveloperError('options.maximum is required'); - } + Check.typeOf.object('min', min); + Check.typeOf.object('max', max); //>>includeEnd('debug'); this._min = Cartesian3.clone(min); @@ -89,12 +85,10 @@ define([ var dimensions = options.dimensions; //>>includeStart('debug', pragmas.debug); - if (!defined(dimensions)) { - throw new DeveloperError('options.dimensions is required.'); - } - if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) { - throw new DeveloperError('All dimensions components must be greater than or equal to zero.'); - } + Check.typeOf.object('dimensions', dimensions); + Check.typeOf.number.greaterThanOrEquals('dimensions.x', dimensions.x, 0); + Check.typeOf.number.greaterThanOrEquals('dimensions.y', dimensions.y, 0); + Check.typeOf.number.greaterThanOrEquals('dimensions.z', dimensions.z, 0); //>>includeEnd('debug'); var corner = Cartesian3.multiplyByScalar(dimensions, 0.5, new Cartesian3()); @@ -127,9 +121,7 @@ define([ */ BoxOutlineGeometry.fromAxisAlignedBoundingBox = function(boundingBox) { //>>includeStart('debug', pragmas.debug); - if (!defined(boundingBox)) { - throw new DeveloperError('boundingBox is required.'); - } + Check.typeOf.object('boundindBox', boundingBox); //>>includeEnd('debug'); return new BoxOutlineGeometry({ @@ -155,12 +147,8 @@ define([ */ BoxOutlineGeometry.pack = function(value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required'); - } - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('value', value); + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -187,9 +175,7 @@ define([ */ BoxOutlineGeometry.unpack = function(array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); diff --git a/Source/Core/Cartesian2.js b/Source/Core/Cartesian2.js index 6b440bc6e4da..2c698e4ef35d 100644 --- a/Source/Core/Cartesian2.js +++ b/Source/Core/Cartesian2.js @@ -335,9 +335,8 @@ define([ */ Cartesian2.distance = function(left, right) { //>>includeStart('debug', pragmas.debug); - if (!defined(left) || !defined(right)) { - throw new DeveloperError('left and right are required.'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); //>>includeEnd('debug'); Cartesian2.subtract(left, right, distanceScratch); @@ -358,9 +357,8 @@ define([ */ Cartesian2.distanceSquared = function(left, right) { //>>includeStart('debug', pragmas.debug); - if (!defined(left) || !defined(right)) { - throw new DeveloperError('left and right are required.'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); //>>includeEnd('debug'); Cartesian2.subtract(left, right, distanceScratch); diff --git a/Source/Core/Cartesian3.js b/Source/Core/Cartesian3.js index c6c060088289..db2595cbbe35 100644 --- a/Source/Core/Cartesian3.js +++ b/Source/Core/Cartesian3.js @@ -473,15 +473,9 @@ define([ */ Cartesian3.divideComponents = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.x = left.x / right.x; diff --git a/Source/Core/Cartographic.js b/Source/Core/Cartographic.js index a2eac65ec663..9b6a6a31a05a 100644 --- a/Source/Core/Cartographic.js +++ b/Source/Core/Cartographic.js @@ -1,17 +1,17 @@ /*global define*/ define([ './Cartesian3', + './Check', './defaultValue', './defined', - './DeveloperError', './freezeObject', './Math', './scaleToGeodeticSurface' ], function( Cartesian3, + Check, defaultValue, defined, - DeveloperError, freezeObject, CesiumMath, scaleToGeodeticSurface) { @@ -63,12 +63,8 @@ define([ */ Cartographic.fromRadians = function(longitude, latitude, height, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(longitude)) { - throw new DeveloperError('longitude is required.'); - } - if (!defined(latitude)) { - throw new DeveloperError('latitude is required.'); - } + Check.typeOf.number('longitude', longitude); + Check.typeOf.number('latitude', latitude); //>>includeEnd('debug'); height = defaultValue(height, 0.0); @@ -96,12 +92,8 @@ define([ */ Cartographic.fromDegrees = function(longitude, latitude, height, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(longitude)) { - throw new DeveloperError('longitude is required.'); - } - if (!defined(latitude)) { - throw new DeveloperError('latitude is required.'); - } + Check.typeOf.number('longitude', longitude); + Check.typeOf.number('latitude', latitude); //>>includeEnd('debug'); longitude = CesiumMath.toRadians(longitude); latitude = CesiumMath.toRadians(latitude); @@ -204,9 +196,7 @@ define([ */ Cartographic.equalsEpsilon = function(left, right, epsilon) { //>>includeStart('debug', pragmas.debug); - if (typeof epsilon !== 'number') { - throw new DeveloperError('epsilon is required and must be a number.'); - } + Check.typeOf.number('epsilon', epsilon); //>>includeEnd('debug'); return (left === right) || diff --git a/Source/Core/CartographicGeocoderService.js b/Source/Core/CartographicGeocoderService.js index e7475fcdcca1..9b6f72ff895d 100644 --- a/Source/Core/CartographicGeocoderService.js +++ b/Source/Core/CartographicGeocoderService.js @@ -1,17 +1,17 @@ /*global define*/ define([ './Cartesian3', + './Check', './defaultValue', './defineProperties', './defined', - './DeveloperError', '../ThirdParty/when' ], function( Cartesian3, + Check, defaultValue, defineProperties, defined, - DeveloperError, when) { 'use strict'; @@ -33,9 +33,7 @@ define([ */ CartographicGeocoderService.prototype.geocode = function(query) { //>>includeStart('debug', pragmas.debug); - if (!defined(query)) { - throw new DeveloperError('query must be defined'); - } + Check.typeOf.string('query', query); //>>includeEnd('debug'); var splitQuery = query.match(/[^\s,\n]+/g); @@ -44,6 +42,20 @@ define([ var latitude = +splitQuery[1]; var height = (splitQuery.length === 3) ? +splitQuery[2] : 300.0; + if (isNaN(longitude) && isNaN(latitude)) { + var coordTest = /^(\d+.?\d*)([nsew])/i; + for (var i = 0; i < splitQuery.length; ++i) { + var splitCoord = splitQuery[i].match(coordTest); + if (coordTest.test(splitQuery[i]) && splitCoord.length === 3) { + if (/^[ns]/i.test(splitCoord[2])) { + latitude = (/^[n]/i.test(splitCoord[2])) ? +splitCoord[1] : -splitCoord[1]; + } else if (/^[ew]/i.test(splitCoord[2])) { + longitude = (/^[e]/i.test(splitCoord[2])) ? +splitCoord[1] : -splitCoord[1]; + } + } + } + } + if (!isNaN(longitude) && !isNaN(latitude) && !isNaN(height)) { var result = { displayName: query, diff --git a/Source/Core/CesiumTerrainProvider.js b/Source/Core/CesiumTerrainProvider.js index 4eac0e83263c..8a54460e8b81 100644 --- a/Source/Core/CesiumTerrainProvider.js +++ b/Source/Core/CesiumTerrainProvider.js @@ -8,6 +8,7 @@ define([ './defaultValue', './defined', './defineProperties', + './deprecationWarning', './DeveloperError', './Event', './GeographicTilingScheme', @@ -19,8 +20,9 @@ define([ './Math', './OrientedBoundingBox', './QuantizedMeshTerrainData', + './Request', + './RequestType', './TerrainProvider', - './throttleRequestByServer', './TileAvailability', './TileProviderError' ], function( @@ -32,6 +34,7 @@ define([ defaultValue, defined, defineProperties, + deprecationWarning, DeveloperError, Event, GeographicTilingScheme, @@ -43,14 +46,15 @@ define([ CesiumMath, OrientedBoundingBox, QuantizedMeshTerrainData, + Request, + RequestType, TerrainProvider, - throttleRequestByServer, TileAvailability, TileProviderError) { 'use strict'; /** - * A {@link TerrainProvider} that access terrain data in a Cesium terrain format. + * A {@link TerrainProvider} that accesses terrain data in a Cesium terrain format. * The format is described on the * {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Cesium-Terrain-Server|Cesium wiki}. * @@ -70,7 +74,7 @@ define([ * // Construct a terrain provider that uses per vertex normals for lighting * // to add shading detail to an imagery provider. * var terrainProvider = new Cesium.CesiumTerrainProvider({ - * url : 'https://assets.agi.com/stk-terrain/world', + * url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', * requestVertexNormals : true * }); * @@ -300,12 +304,11 @@ define([ return { Accept : 'application/vnd.quantized-mesh,application/octet-stream;q=0.9,*/*;q=0.01' }; - } else { - var extensions = extensionsList.join('-'); - return { - Accept : 'application/vnd.quantized-mesh;extensions=' + extensions + ',application/octet-stream;q=0.9,*/*;q=0.01' - }; } + var extensions = extensionsList.join('-'); + return { + Accept : 'application/vnd.quantized-mesh;extensions=' + extensions + ',application/octet-stream;q=0.9,*/*;q=0.01' + }; } function createHeightmapTerrainData(provider, buffer, level, x, y, tmsY) { @@ -489,9 +492,8 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited, - * or false if the request should be initiated regardless of the number of requests - * already in progress. + * @param {Request} [request] The request object. Intended for internal use only. + * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. @@ -499,7 +501,7 @@ define([ * @exception {DeveloperError} This function must not be called before {@link CesiumTerrainProvider#ready} * returns true. */ - CesiumTerrainProvider.prototype.requestTileGeometry = function(x, y, level, throttleRequests) { + CesiumTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError('requestTileGeometry must not be called before the terrain provider is ready.'); @@ -522,8 +524,6 @@ define([ url = proxy.getURL(url); } - var promise; - var extensionList = []; if (this._requestVertexNormals && this._hasVertexNormals) { extensionList.push(this._littleEndianExtensionSize ? 'octvertexnormals' : 'vertexnormals'); @@ -532,26 +532,27 @@ define([ extensionList.push('watermask'); } - function tileLoader(tileUrl) { - return loadArrayBuffer(tileUrl, getRequestHeader(extensionList)); + if (typeof request === 'boolean') { + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); + request = new Request({ + throttle : request, + throttleByServer : request, + type : RequestType.TERRAIN + }); } - throttleRequests = defaultValue(throttleRequests, true); - if (throttleRequests) { - promise = throttleRequestByServer(url, tileLoader); - if (!defined(promise)) { - return undefined; - } - } else { - promise = tileLoader(url); + + var promise = loadArrayBuffer(url, getRequestHeader(extensionList), request); + + if (!defined(promise)) { + return undefined; } var that = this; return when(promise, function(buffer) { if (defined(that._heightmapStructure)) { return createHeightmapTerrainData(that, buffer, level, x, y, tmsY); - } else { - return createQuantizedMeshTerrainData(that, buffer, level, x, y, tmsY); } + return createQuantizedMeshTerrainData(that, buffer, level, x, y, tmsY); }); }; diff --git a/Source/Core/Color.js b/Source/Core/Color.js index 3b0efe8d5bd3..1e138969ce35 100644 --- a/Source/Core/Color.js +++ b/Source/Core/Color.js @@ -1,15 +1,15 @@ /*global define*/ define([ + './Check', './defaultValue', './defined', - './DeveloperError', './FeatureDetection', './freezeObject', './Math' ], function( + Check, defaultValue, defined, - DeveloperError, FeatureDetection, freezeObject, CesiumMath) { @@ -84,9 +84,7 @@ define([ */ Color.fromCartesian4 = function(cartesian, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(cartesian)) { - throw new DeveloperError('cartesian is required'); - } + Check.typeOf.object('cartesian', cartesian); //>>includeEnd('debug'); if (!defined(result)) { @@ -141,12 +139,8 @@ define([ */ Color.fromAlpha = function(color, alpha, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(color)) { - throw new DeveloperError('color is required'); - } - if (!defined(alpha)) { - throw new DeveloperError('alpha is required'); - } + Check.typeOf.object('color', color); + Check.typeOf.number('alpha', alpha); //>>includeEnd('debug'); if (!defined(result)) { @@ -288,9 +282,7 @@ define([ var maximumRed = defaultValue(options.maximumRed, 1.0); //>>includeStart('debug', pragmas.debug); - if (minimumRed > maximumRed) { - throw new DeveloperError("minimumRed must be less than or equal to maximumRed"); - } + Check.typeOf.number.lessThanOrEquals('minimumRed', minimumRed, maximumRed); //>>includeEnd('debug'); red = minimumRed + (CesiumMath.nextRandomNumber() * (maximumRed - minimumRed)); @@ -302,11 +294,8 @@ define([ var maximumGreen = defaultValue(options.maximumGreen, 1.0); //>>includeStart('debug', pragmas.debug); - if (minimumGreen > maximumGreen) { - throw new DeveloperError("minimumGreen must be less than or equal to maximumGreen"); - } + Check.typeOf.number.lessThanOrEquals('minimumGreen', minimumGreen, maximumGreen); //>>includeEnd('debug'); - green = minimumGreen + (CesiumMath.nextRandomNumber() * (maximumGreen - minimumGreen)); } @@ -316,9 +305,7 @@ define([ var maximumBlue = defaultValue(options.maximumBlue, 1.0); //>>includeStart('debug', pragmas.debug); - if (minimumBlue > maximumBlue) { - throw new DeveloperError("minimumBlue must be less than or equal to maximumBlue"); - } + Check.typeOf.number.lessThanOrEquals('minimumBlue', minimumBlue, maximumBlue); //>>includeEnd('debug'); blue = minimumBlue + (CesiumMath.nextRandomNumber() * (maximumBlue - minimumBlue)); @@ -330,9 +317,7 @@ define([ var maximumAlpha = defaultValue(options.maximumAlpha, 1.0); //>>includeStart('debug', pragmas.debug); - if (minimumAlpha > maximumAlpha) { - throw new DeveloperError("minimumAlpha must be less than or equal to maximumAlpha"); - } + Check.typeOf.number.lessThanOrEquals('minumumAlpha', minimumAlpha, maximumAlpha); //>>includeEnd('debug'); alpha = minimumAlpha + (CesiumMath.nextRandomNumber() * (maximumAlpha - minimumAlpha)); @@ -374,9 +359,7 @@ define([ */ Color.fromCssColorString = function(color, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(color)) { - throw new DeveloperError('color is required'); - } + Check.typeOf.string('color', color); //>>includeEnd('debug'); if (!defined(result)) { @@ -445,12 +428,8 @@ define([ */ Color.pack = function(value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required'); - } - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('value', value); + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -472,9 +451,7 @@ define([ */ Color.unpack = function(array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -676,15 +653,9 @@ define([ */ Color.prototype.brighten = function(magnitude, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(magnitude)) { - throw new DeveloperError('magnitude is required.'); - } - if (magnitude < 0.0) { - throw new DeveloperError('magnitude must be positive.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.typeOf.number('magnitude', magnitude); + Check.typeOf.number.greaterThanOrEquals('magnitude', magnitude, 0.0); + Check.typeOf.object('result', result); //>>includeEnd('debug'); magnitude = (1.0 - magnitude); @@ -707,15 +678,9 @@ define([ */ Color.prototype.darken = function(magnitude, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(magnitude)) { - throw new DeveloperError('magnitude is required.'); - } - if (magnitude < 0.0) { - throw new DeveloperError('magnitude must be positive.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required.'); - } + Check.typeOf.number('magnitude', magnitude); + Check.typeOf.number.greaterThanOrEquals('magnitude', magnitude, 0.0); + Check.typeOf.object('result', result); //>>includeEnd('debug'); magnitude = (1.0 - magnitude); @@ -750,15 +715,9 @@ define([ */ Color.add = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = left.red + right.red; @@ -778,15 +737,9 @@ define([ */ Color.subtract = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = left.red - right.red; @@ -806,15 +759,9 @@ define([ */ Color.multiply = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = left.red * right.red; @@ -834,15 +781,9 @@ define([ */ Color.divide = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = left.red / right.red; @@ -862,15 +803,9 @@ define([ */ Color.mod = function(left, right, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(left)) { - throw new DeveloperError('left is required'); - } - if (!defined(right)) { - throw new DeveloperError('right is required'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('left', left); + Check.typeOf.object('right', right); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = left.red % right.red; @@ -890,15 +825,9 @@ define([ */ Color.multiplyByScalar = function(color, scalar, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(color)) { - throw new DeveloperError('cartesian is required'); - } - if (typeof scalar !== 'number') { - throw new DeveloperError('scalar is required and must be a number.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('color', color); + Check.typeOf.number('scalar', scalar); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = color.red * scalar; @@ -918,15 +847,9 @@ define([ */ Color.divideByScalar = function(color, scalar, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(color)) { - throw new DeveloperError('cartesian is required'); - } - if (typeof scalar !== 'number') { - throw new DeveloperError('scalar is required and must be a number.'); - } - if (!defined(result)) { - throw new DeveloperError('result is required'); - } + Check.typeOf.object('color', color); + Check.typeOf.number('scalar', scalar); + Check.typeOf.object('result', result); //>>includeEnd('debug'); result.red = color.red / scalar; diff --git a/Source/Core/CorridorGeometry.js b/Source/Core/CorridorGeometry.js index 1667c7e50d86..407a71b10684 100644 --- a/Source/Core/CorridorGeometry.js +++ b/Source/Core/CorridorGeometry.js @@ -591,6 +591,7 @@ define([ attributes.position.values = newPositions; attributes = extrudedAttributes(attributes, vertexFormat); + var i; var size = length / 3; if (params.shadowVolume) { var topNormals = attributes.normal.values; @@ -613,7 +614,6 @@ define([ } } - var i; var iLength = indices.length; var twoSize = size + size; var newIndices = IndexDatatype.createTypedArray(newPositions.length / 3, iLength * 2 + twoSize * 3); diff --git a/Source/Core/CorridorOutlineGeometry.js b/Source/Core/CorridorOutlineGeometry.js index 9e18d756ec25..a2b268591246 100644 --- a/Source/Core/CorridorOutlineGeometry.js +++ b/Source/Core/CorridorOutlineGeometry.js @@ -3,12 +3,12 @@ define([ './arrayRemoveDuplicates', './BoundingSphere', './Cartesian3', + './Check', './ComponentDatatype', './CornerType', './CorridorGeometryLibrary', './defaultValue', './defined', - './DeveloperError', './Ellipsoid', './Geometry', './GeometryAttribute', @@ -21,12 +21,12 @@ define([ arrayRemoveDuplicates, BoundingSphere, Cartesian3, + Check, ComponentDatatype, CornerType, CorridorGeometryLibrary, defaultValue, defined, - DeveloperError, Ellipsoid, Geometry, GeometryAttribute, @@ -331,12 +331,8 @@ define([ var width = options.width; //>>includeStart('debug', pragmas.debug); - if (!defined(positions)) { - throw new DeveloperError('options.positions is required.'); - } - if (!defined(width)) { - throw new DeveloperError('options.width is required.'); - } + Check.typeOf.object('options.positions', positions); + Check.typeOf.number('options.width', width); //>>includeEnd('debug'); this._positions = positions; @@ -366,12 +362,8 @@ define([ */ CorridorOutlineGeometry.pack = function(value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required'); - } - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('value', value); + Check.typeOf.object('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -417,9 +409,7 @@ define([ */ CorridorOutlineGeometry.unpack = function(array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); diff --git a/Source/Core/DoublyLinkedList.js b/Source/Core/DoublyLinkedList.js new file mode 100644 index 000000000000..969222203a8b --- /dev/null +++ b/Source/Core/DoublyLinkedList.js @@ -0,0 +1,105 @@ +/*global define*/ +define([ + '../Core/defined', + '../Core/defineProperties' + ], function( + defined, + defineProperties) { + 'use strict'; + + /** + * @private + */ + function DoublyLinkedList() { + this.head = undefined; + this.tail = undefined; + this._length = 0; + } + + defineProperties(DoublyLinkedList.prototype, { + length : { + get : function() { + return this._length; + } + } + }); + + function DoublyLinkedListNode(item, previous, next) { + this.item = item; + this.previous = previous; + this.next = next; + } + + DoublyLinkedList.prototype.add = function(item) { + var node = new DoublyLinkedListNode(item, this.tail, undefined); + + if (defined(this.tail)) { + this.tail.next = node; + this.tail = node; + } else { + // Insert into empty linked list + this.head = node; + this.tail = node; + } + + ++this._length; + + return node; + }; + + function remove(list, node) { + if (defined(node.previous) && defined(node.next)) { + node.previous.next = node.next; + node.next.previous = node.previous; + } else if (defined(node.previous)) { + // Remove last node + node.previous.next = undefined; + list.tail = node.previous; + } else if (defined(node.next)) { + // Remove first node + node.next.previous = undefined; + list.head = node.next; + } else { + // Remove last node in the linked list + list.head = undefined; + list.tail = undefined; + } + + node.next = undefined; + node.previous = undefined; + } + + DoublyLinkedList.prototype.remove = function(node) { + if (!defined(node)) { + return; + } + + remove(this, node); + + --this._length; + }; + + DoublyLinkedList.prototype.splice = function(node, nextNode) { + if (node === nextNode) { + return; + } + + // Remove nextNode, then insert after node + remove(this, nextNode); + + var oldNodeNext = node.next; + node.next = nextNode; + + // nextNode is the new tail + if (this.tail === node) { + this.tail = nextNode; + } else { + oldNodeNext.previous = nextNode; + } + + nextNode.next = oldNodeNext; + nextNode.previous = node; + }; + + return DoublyLinkedList; +}); diff --git a/Source/Core/Ellipsoid.js b/Source/Core/Ellipsoid.js index 6f3eba4bcfcb..d31c69aefaf9 100644 --- a/Source/Core/Ellipsoid.js +++ b/Source/Core/Ellipsoid.js @@ -1,5 +1,6 @@ /*global define*/ define([ + './Check', './Cartesian3', './Cartographic', './defaultValue', @@ -10,6 +11,7 @@ define([ './Math', './scaleToGeodeticSurface' ], function( + Check, Cartesian3, Cartographic, defaultValue, @@ -27,9 +29,9 @@ define([ z = defaultValue(z, 0.0); //>>includeStart('debug', pragmas.debug); - if (x < 0.0 || y < 0.0 || z < 0.0) { - throw new DeveloperError('All radii components must be greater than or equal to zero.'); - } + Check.typeOf.number.greaterThanOrEquals('x', x, 0.0); + Check.typeOf.number.greaterThanOrEquals('y', y, 0.0); + Check.typeOf.number.greaterThanOrEquals('z', z, 0.0); //>>includeEnd('debug'); ellipsoid._radii = new Cartesian3(x, y, z); @@ -283,12 +285,8 @@ define([ */ Ellipsoid.pack = function(value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); - if (!defined(value)) { - throw new DeveloperError('value is required'); - } - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.typeOf.object('value', value); + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -308,9 +306,7 @@ define([ */ Ellipsoid.unpack = function(array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required'); - } + Check.defined('array', array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); @@ -338,9 +334,7 @@ define([ */ Ellipsoid.prototype.geodeticSurfaceNormalCartographic = function(cartographic, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(cartographic)) { - throw new DeveloperError('cartographic is required.'); - } + Check.typeOf.object('cartographic', cartographic); //>>includeEnd('debug'); var longitude = cartographic.longitude; @@ -422,10 +416,8 @@ define([ */ Ellipsoid.prototype.cartographicArrayToCartesianArray = function(cartographics, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(cartographics)) { - throw new DeveloperError('cartographics is required.'); - } - //>>includeEnd('debug'); + Check.defined('cartographics', cartographics); + //>>includeEnd('debug') var length = cartographics.length; if (!defined(result)) { @@ -496,9 +488,7 @@ define([ */ Ellipsoid.prototype.cartesianArrayToCartographicArray = function(cartesians, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(cartesians)) { - throw new DeveloperError('cartesians is required.'); - } + Check.defined('cartesians', cartesians); //>>includeEnd('debug'); var length = cartesians.length; @@ -536,9 +526,7 @@ define([ */ Ellipsoid.prototype.scaleToGeocentricSurface = function(cartesian, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(cartesian)) { - throw new DeveloperError('cartesian is required.'); - } + Check.typeOf.object('cartesian', cartesian); //>>includeEnd('debug'); if (!defined(result)) { @@ -633,15 +621,13 @@ define([ */ Ellipsoid.prototype.getSurfaceNormalIntersectionWithZAxis = function(position, buffer, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(position)) { - throw new DeveloperError('position is required.'); - } + Check.typeOf.object('position', position); + if (!CesiumMath.equalsEpsilon(this._radii.x, this._radii.y, CesiumMath.EPSILON15)) { throw new DeveloperError('Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)'); } - if (this._radii.z === 0) { - throw new DeveloperError('Ellipsoid.radii.z must be greater than 0'); - } + + Check.typeOf.number.greaterThan('Ellipsoid.radii.z', this._radii.z, 0); //>>includeEnd('debug'); buffer = defaultValue(buffer, 0.0); diff --git a/Source/Core/EllipsoidGeodesic.js b/Source/Core/EllipsoidGeodesic.js index 4a84a85b7d04..166942f77dd1 100644 --- a/Source/Core/EllipsoidGeodesic.js +++ b/Source/Core/EllipsoidGeodesic.js @@ -172,6 +172,8 @@ define([ ellipsoidGeodesic._uSquared = uSquared; } + var scratchCart1 = new Cartesian3(); + var scratchCart2 = new Cartesian3(); function computeProperties(ellipsoidGeodesic, start, end, ellipsoid) { var firstCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(start, scratchCart2), scratchCart1); var lastCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(end, scratchCart2), scratchCart2); @@ -193,8 +195,6 @@ define([ setConstants(ellipsoidGeodesic); } - var scratchCart1 = new Cartesian3(); - var scratchCart2 = new Cartesian3(); /** * Initializes a geodesic on the ellipsoid connecting the two provided planetodetic points. * diff --git a/Source/Core/EllipsoidGeometry.js b/Source/Core/EllipsoidGeometry.js index 59e7fdf560fb..2d3ac20a64a2 100644 --- a/Source/Core/EllipsoidGeometry.js +++ b/Source/Core/EllipsoidGeometry.js @@ -71,8 +71,8 @@ define([ options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radii = defaultValue(options.radii, defaultRadii); - var stackPartitions = defaultValue(options.stackPartitions, 64); - var slicePartitions = defaultValue(options.slicePartitions, 64); + var stackPartitions = Math.round(defaultValue(options.stackPartitions, 64)); + var slicePartitions = Math.round(defaultValue(options.slicePartitions, 64)); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); //>>includeStart('debug', pragmas.debug); diff --git a/Source/Core/EllipsoidOutlineGeometry.js b/Source/Core/EllipsoidOutlineGeometry.js index 6e60a9fd1b0b..94c7aab3b732 100644 --- a/Source/Core/EllipsoidOutlineGeometry.js +++ b/Source/Core/EllipsoidOutlineGeometry.js @@ -61,9 +61,9 @@ define([ options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radii = defaultValue(options.radii, defaultRadii); - var stackPartitions = defaultValue(options.stackPartitions, 10); - var slicePartitions = defaultValue(options.slicePartitions, 8); - var subdivisions = defaultValue(options.subdivisions, 128); + var stackPartitions = Math.round(defaultValue(options.stackPartitions, 10)); + var slicePartitions = Math.round(defaultValue(options.slicePartitions, 8)); + var subdivisions = Math.round(defaultValue(options.subdivisions, 128)); //>>includeStart('debug', pragmas.debug); if (stackPartitions < 1) { diff --git a/Source/Core/EllipsoidTerrainProvider.js b/Source/Core/EllipsoidTerrainProvider.js index c0011c627996..58adf683ca83 100644 --- a/Source/Core/EllipsoidTerrainProvider.js +++ b/Source/Core/EllipsoidTerrainProvider.js @@ -152,14 +152,13 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited, - * or false if the request should be initiated regardless of the number of requests - * already in progress. + * @param {Request} [request] The request object. Intended for internal use only. + * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ - EllipsoidTerrainProvider.prototype.requestTileGeometry = function(x, y, level, throttleRequests) { + EllipsoidTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) { var width = 16; var height = 16; return new HeightmapTerrainData({ diff --git a/Source/Core/EllipsoidalOccluder.js b/Source/Core/EllipsoidalOccluder.js index 31c776faa4ee..3fad0be9bda6 100644 --- a/Source/Core/EllipsoidalOccluder.js +++ b/Source/Core/EllipsoidalOccluder.js @@ -241,7 +241,7 @@ define([ var subsampleScratch = []; /** - * Computes a point that can be used for horizon culling of an rectangle. If the point is below + * Computes a point that can be used for horizon culling of a rectangle. If the point is below * the horizon, the ellipsoid-conforming rectangle is guaranteed to be below the horizon as well. * The returned point is expressed in the ellipsoid-scaled space and is suitable for use with * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. diff --git a/Source/Core/Event.js b/Source/Core/Event.js index 06bd6eab4133..15e6204338fd 100644 --- a/Source/Core/Event.js +++ b/Source/Core/Event.js @@ -1,12 +1,12 @@ /*global define*/ define([ + './Check', './defined', - './defineProperties', - './DeveloperError' + './defineProperties' ], function( + Check, defined, - defineProperties, - DeveloperError) { + defineProperties) { 'use strict'; /** @@ -65,9 +65,7 @@ define([ */ Event.prototype.addEventListener = function(listener, scope) { //>>includeStart('debug', pragmas.debug); - if (typeof listener !== 'function') { - throw new DeveloperError('listener is required and must be a function.'); - } + Check.typeOf.func('listener', listener); //>>includeEnd('debug'); this._listeners.push(listener); @@ -91,9 +89,7 @@ define([ */ Event.prototype.removeEventListener = function(listener, scope) { //>>includeStart('debug', pragmas.debug); - if (typeof listener !== 'function') { - throw new DeveloperError('listener is required and must be a function.'); - } + Check.typeOf.func('listener', listener); //>>includeEnd('debug'); var listeners = this._listeners; diff --git a/Source/Core/GeographicTilingScheme.js b/Source/Core/GeographicTilingScheme.js index e7ab5ee8a15c..1ee704738475 100644 --- a/Source/Core/GeographicTilingScheme.js +++ b/Source/Core/GeographicTilingScheme.js @@ -104,7 +104,7 @@ define([ }; /** - * Transforms an rectangle specified in geodetic radians to the native coordinate system + * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * * @param {Rectangle} rectangle The rectangle to transform. @@ -137,7 +137,7 @@ define([ }; /** - * Converts tile x, y coordinates and level to an rectangle expressed in the native coordinates + * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * * @param {Number} x The integer x coordinate of the tile. diff --git a/Source/Core/Geometry.js b/Source/Core/Geometry.js index 17b5065f0692..51a4e5502b60 100644 --- a/Source/Core/Geometry.js +++ b/Source/Core/Geometry.js @@ -1,11 +1,13 @@ /*global define*/ define([ + './Check', './defaultValue', './defined', './DeveloperError', './GeometryType', './PrimitiveType' ], function( + Check, defaultValue, defined, DeveloperError, @@ -67,9 +69,7 @@ define([ options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); - if (!defined(options.attributes)) { - throw new DeveloperError('options.attributes is required.'); - } + Check.typeOf.object('options.attributes', options.attributes); //>>includeEnd('debug'); /** @@ -173,9 +173,7 @@ define([ */ Geometry.computeNumberOfVertices = function(geometry) { //>>includeStart('debug', pragmas.debug); - if (!defined(geometry)) { - throw new DeveloperError('geometry is required.'); - } + Check.typeOf.object('geometry', geometry); //>>includeEnd('debug'); var numberOfVertices = -1; diff --git a/Source/Core/GoogleEarthEnterpriseMetadata.js b/Source/Core/GoogleEarthEnterpriseMetadata.js new file mode 100644 index 000000000000..d5ee5dd6185b --- /dev/null +++ b/Source/Core/GoogleEarthEnterpriseMetadata.js @@ -0,0 +1,543 @@ +/*global define*/ +define([ + '../ThirdParty/google-earth-dbroot-parser', + '../ThirdParty/when', + './appendForwardSlash', + './Check', + './Credit', + './defaultValue', + './defined', + './defineProperties', + './GoogleEarthEnterpriseTileInformation', + './isBitSet', + './joinUrls', + './loadArrayBuffer', + './Math', + './Request', + './RequestType', + './RuntimeError', + './TaskProcessor' +], function( + dbrootParser, + when, + appendForwardSlash, + Check, + Credit, + defaultValue, + defined, + defineProperties, + GoogleEarthEnterpriseTileInformation, + isBitSet, + joinUrls, + loadArrayBuffer, + CesiumMath, + Request, + RequestType, + RuntimeError, + TaskProcessor) { + 'use strict'; + + function stringToBuffer(str) { + var len = str.length; + var buffer = new ArrayBuffer(len); + var ui8 = new Uint8Array(buffer); + for (var i = 0; i < len; ++i) { + ui8[i] = str.charCodeAt(i); + } + + return buffer; + } + + // Decodes packet with a key that has been around since the beginning of Google Earth Enterprise + var defaultKey = stringToBuffer('\x45\xf4\xbd\x0b\x79\xe2\x6a\x45\x22\x05\x92\x2c\x17\xcd\x06\x71\xf8\x49\x10\x46\x67\x51\x00\x42\x25\xc6\xe8\x61\x2c\x66\x29\x08\xc6\x34\xdc\x6a\x62\x25\x79\x0a\x77\x1d\x6d\x69\xd6\xf0\x9c\x6b\x93\xa1\xbd\x4e\x75\xe0\x41\x04\x5b\xdf\x40\x56\x0c\xd9\xbb\x72\x9b\x81\x7c\x10\x33\x53\xee\x4f\x6c\xd4\x71\x05\xb0\x7b\xc0\x7f\x45\x03\x56\x5a\xad\x77\x55\x65\x0b\x33\x92\x2a\xac\x19\x6c\x35\x14\xc5\x1d\x30\x73\xf8\x33\x3e\x6d\x46\x38\x4a\xb4\xdd\xf0\x2e\xdd\x17\x75\x16\xda\x8c\x44\x74\x22\x06\xfa\x61\x22\x0c\x33\x22\x53\x6f\xaf\x39\x44\x0b\x8c\x0e\x39\xd9\x39\x13\x4c\xb9\xbf\x7f\xab\x5c\x8c\x50\x5f\x9f\x22\x75\x78\x1f\xe9\x07\x71\x91\x68\x3b\xc1\xc4\x9b\x7f\xf0\x3c\x56\x71\x48\x82\x05\x27\x55\x66\x59\x4e\x65\x1d\x98\x75\xa3\x61\x46\x7d\x61\x3f\x15\x41\x00\x9f\x14\x06\xd7\xb4\x34\x4d\xce\x13\x87\x46\xb0\x1a\xd5\x05\x1c\xb8\x8a\x27\x7b\x8b\xdc\x2b\xbb\x4d\x67\x30\xc8\xd1\xf6\x5c\x8f\x50\xfa\x5b\x2f\x46\x9b\x6e\x35\x18\x2f\x27\x43\x2e\xeb\x0a\x0c\x5e\x10\x05\x10\xa5\x73\x1b\x65\x34\xe5\x6c\x2e\x6a\x43\x27\x63\x14\x23\x55\xa9\x3f\x71\x7b\x67\x43\x7d\x3a\xaf\xcd\xe2\x54\x55\x9c\xfd\x4b\xc6\xe2\x9f\x2f\x28\xed\xcb\x5c\xc6\x2d\x66\x07\x88\xa7\x3b\x2f\x18\x2a\x22\x4e\x0e\xb0\x6b\x2e\xdd\x0d\x95\x7d\x7d\x47\xba\x43\xb2\x11\xb2\x2b\x3e\x4d\xaa\x3e\x7d\xe6\xce\x49\x89\xc6\xe6\x78\x0c\x61\x31\x05\x2d\x01\xa4\x4f\xa5\x7e\x71\x20\x88\xec\x0d\x31\xe8\x4e\x0b\x00\x6e\x50\x68\x7d\x17\x3d\x08\x0d\x17\x95\xa6\x6e\xa3\x68\x97\x24\x5b\x6b\xf3\x17\x23\xf3\xb6\x73\xb3\x0d\x0b\x40\xc0\x9f\xd8\x04\x51\x5d\xfa\x1a\x17\x22\x2e\x15\x6a\xdf\x49\x00\xb9\xa0\x77\x55\xc6\xef\x10\x6a\xbf\x7b\x47\x4c\x7f\x83\x17\x05\xee\xdc\xdc\x46\x85\xa9\xad\x53\x07\x2b\x53\x34\x06\x07\xff\x14\x94\x59\x19\x02\xe4\x38\xe8\x31\x83\x4e\xb9\x58\x46\x6b\xcb\x2d\x23\x86\x92\x70\x00\x35\x88\x22\xcf\x31\xb2\x26\x2f\xe7\xc3\x75\x2d\x36\x2c\x72\x74\xb0\x23\x47\xb7\xd3\xd1\x26\x16\x85\x37\x72\xe2\x00\x8c\x44\xcf\x10\xda\x33\x2d\x1a\xde\x60\x86\x69\x23\x69\x2a\x7c\xcd\x4b\x51\x0d\x95\x54\x39\x77\x2e\x29\xea\x1b\xa6\x50\xa2\x6a\x8f\x6f\x50\x99\x5c\x3e\x54\xfb\xef\x50\x5b\x0b\x07\x45\x17\x89\x6d\x28\x13\x77\x37\x1d\xdb\x8e\x1e\x4a\x05\x66\x4a\x6f\x99\x20\xe5\x70\xe2\xb9\x71\x7e\x0c\x6d\x49\x04\x2d\x7a\xfe\x72\xc7\xf2\x59\x30\x8f\xbb\x02\x5d\x73\xe5\xc9\x20\xea\x78\xec\x20\x90\xf0\x8a\x7f\x42\x17\x7c\x47\x19\x60\xb0\x16\xbd\x26\xb7\x71\xb6\xc7\x9f\x0e\xd1\x33\x82\x3d\xd3\xab\xee\x63\x99\xc8\x2b\x53\xa0\x44\x5c\x71\x01\xc6\xcc\x44\x1f\x32\x4f\x3c\xca\xc0\x29\x3d\x52\xd3\x61\x19\x58\xa9\x7d\x65\xb4\xdc\xcf\x0d\xf4\x3d\xf1\x08\xa9\x42\xda\x23\x09\xd8\xbf\x5e\x50\x49\xf8\x4d\xc0\xcb\x47\x4c\x1c\x4f\xf7\x7b\x2b\xd8\x16\x18\xc5\x31\x92\x3b\xb5\x6f\xdc\x6c\x0d\x92\x88\x16\xd1\x9e\xdb\x3f\xe2\xe9\xda\x5f\xd4\x84\xe2\x46\x61\x5a\xde\x1c\x55\xcf\xa4\x00\xbe\xfd\xce\x67\xf1\x4a\x69\x1c\x97\xe6\x20\x48\xd8\x5d\x7f\x7e\xae\x71\x20\x0e\x4e\xae\xc0\x56\xa9\x91\x01\x3c\x82\x1d\x0f\x72\xe7\x76\xec\x29\x49\xd6\x5d\x2d\x83\xe3\xdb\x36\x06\xa9\x3b\x66\x13\x97\x87\x6a\xd5\xb6\x3d\x50\x5e\x52\xb9\x4b\xc7\x73\x57\x78\xc9\xf4\x2e\x59\x07\x95\x93\x6f\xd0\x4b\x17\x57\x19\x3e\x27\x27\xc7\x60\xdb\x3b\xed\x9a\x0e\x53\x44\x16\x3e\x3f\x8d\x92\x6d\x77\xa2\x0a\xeb\x3f\x52\xa8\xc6\x55\x5e\x31\x49\x37\x85\xf4\xc5\x1f\x26\x2d\xa9\x1c\xbf\x8b\x27\x54\xda\xc3\x6a\x20\xe5\x2a\x78\x04\xb0\xd6\x90\x70\x72\xaa\x8b\x68\xbd\x88\xf7\x02\x5f\x48\xb1\x7e\xc0\x58\x4c\x3f\x66\x1a\xf9\x3e\xe1\x65\xc0\x70\xa7\xcf\x38\x69\xaf\xf0\x56\x6c\x64\x49\x9c\x27\xad\x78\x74\x4f\xc2\x87\xde\x56\x39\x00\xda\x77\x0b\xcb\x2d\x1b\x89\xfb\x35\x4f\x02\xf5\x08\x51\x13\x60\xc1\x0a\x5a\x47\x4d\x26\x1c\x33\x30\x78\xda\xc0\x9c\x46\x47\xe2\x5b\x79\x60\x49\x6e\x37\x67\x53\x0a\x3e\xe9\xec\x46\x39\xb2\xf1\x34\x0d\xc6\x84\x53\x75\x6e\xe1\x0c\x59\xd9\x1e\xde\x29\x85\x10\x7b\x49\x49\xa5\x77\x79\xbe\x49\x56\x2e\x36\xe7\x0b\x3a\xbb\x4f\x03\x62\x7b\xd2\x4d\x31\x95\x2f\xbd\x38\x7b\xa8\x4f\x21\xe1\xec\x46\x70\x76\x95\x7d\x29\x22\x78\x88\x0a\x90\xdd\x9d\x5c\xda\xde\x19\x51\xcf\xf0\xfc\x59\x52\x65\x7c\x33\x13\xdf\xf3\x48\xda\xbb\x2a\x75\xdb\x60\xb2\x02\x15\xd4\xfc\x19\xed\x1b\xec\x7f\x35\xa8\xff\x28\x31\x07\x2d\x12\xc8\xdc\x88\x46\x7c\x8a\x5b\x22'); + + /** + * Provides metadata using the Google Earth Enterprise REST API. This is used by the GoogleEarthEnterpriseImageryProvider + * and GoogleEarthEnterpriseTerrainProvider to share metadata requests. + * + * @alias GoogleEarthEnterpriseMetadata + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {String} options.url The url of the Google Earth Enterprise server hosting the imagery. + * @param {Proxy} [options.proxy] A proxy to use for requests. This object is + * expected to have a getURL function which returns the proxied URL, if needed. + * + * @see GoogleEarthEnterpriseImageryProvider + * @see GoogleEarthEnterpriseTerrainProvider + * + */ + function GoogleEarthEnterpriseMetadata(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('options.url', options.url); + //>>includeEnd('debug'); + + /** + * True if imagery is available. + * @type {Boolean} + * @default true + */ + this.imageryPresent = true; + + /** + * True if imagery is sent as a protocol buffer, false if sent as plain images. If undefined we will try both. + * @type {Boolean} + * @default undefined + */ + this.protoImagery = undefined; + + /** + * True if terrain is available. + * @type {Boolean} + * @default true + */ + this.terrainPresent = true; + + /** + * Exponent used to compute constant to calculate negative height values. + * @type {Number} + * @default 32 + */ + this.negativeAltitudeExponentBias = 32; + + /** + * Threshold where any numbers smaller are actually negative values. They are multiplied by -2^negativeAltitudeExponentBias. + * @type {Number} + * @default EPSILON12 + */ + this.negativeAltitudeThreshold = CesiumMath.EPSILON12; + + /** + * Dictionary of provider id to copyright strings. + * @type {Object} + * @default {} + */ + this.providers = {}; + + /** + * Key used to decode packets + * @type {ArrayBuffer} + */ + this.key = undefined; + + this._quadPacketVersion = 1; + + this._url = appendForwardSlash(options.url); + this._proxy = options.proxy; + + this._tileInfo = {}; + this._subtreePromises = {}; + + var that = this; + this._readyPromise = requestDbRoot(this) + .then(function() { + return that.getQuadTreePacket('', that._quadPacketVersion); + }) + .then(function() { + return true; + }) + .otherwise(function(e) { + var message = 'An error occurred while accessing ' + getMetadataUrl(that, '', 1) + '.'; + return when.reject(new RuntimeError(message)); + }); + } + + defineProperties(GoogleEarthEnterpriseMetadata.prototype, { + /** + * Gets the name of the Google Earth Enterprise server. + * @memberof GoogleEarthEnterpriseMetadata.prototype + * @type {String} + * @readonly + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * Gets the proxy used for metadata requests. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Proxy} + * @readonly + */ + proxy : { + get : function() { + return this._proxy; + } + }, + + /** + * Gets a promise that resolves to true when the metadata is ready for use. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + return this._readyPromise; + } + } + }); + + /** + * Converts a tiles (x, y, level) position into a quadkey used to request an image + * from a Google Earth Enterprise server. + * + * @param {Number} x The tile's x coordinate. + * @param {Number} y The tile's y coordinate. + * @param {Number} level The tile's zoom level. + * + * @see GoogleEarthEnterpriseMetadata#quadKeyToTileXY + */ + GoogleEarthEnterpriseMetadata.tileXYToQuadKey = function(x, y, level) { + var quadkey = ''; + for (var i = level; i >= 0; --i) { + var bitmask = 1 << i; + var digit = 0; + + // Tile Layout + // ___ ___ + //| | | + //| 3 | 2 | + //|-------| + //| 0 | 1 | + //|___|___| + // + + if (!isBitSet(y, bitmask)) { // Top Row + digit |= 2; + if (!isBitSet(x, bitmask)) { // Right to left + digit |= 1; + } + } else { + if (isBitSet(x, bitmask)) { // Left to right + digit |= 1; + } + } + + quadkey += digit; + } + return quadkey; + }; + + /** + * Converts a tile's quadkey used to request an image from a Google Earth Enterprise server into the + * (x, y, level) position. + * + * @param {String} quadkey The tile's quad key + * + * @see GoogleEarthEnterpriseMetadata#tileXYToQuadKey + */ + GoogleEarthEnterpriseMetadata.quadKeyToTileXY = function(quadkey) { + var x = 0; + var y = 0; + var level = quadkey.length - 1; + for (var i = level; i >= 0; --i) { + var bitmask = 1 << i; + var digit = +quadkey[level - i]; + + if (isBitSet(digit, 2)) { // Top Row + if (!isBitSet(digit, 1)) { // // Right to left + x |= bitmask; + } + } else { + y |= bitmask; + if (isBitSet(digit, 1)) { // Left to right + x |= bitmask; + } + } + } + return { + x : x, + y : y, + level : level + }; + }; + + GoogleEarthEnterpriseMetadata.prototype.isValid = function(quadKey) { + var info = this.getTileInformationFromQuadKey(quadKey); + if (defined(info)) { + return info !== null; + } + + var valid = true; + var q = quadKey; + var last; + while (q.length > 1) { + last = q.substring(q.length - 1); + q = q.substring(0, q.length - 1); + info = this.getTileInformationFromQuadKey(q); + if (defined(info)) { + if (!info.hasSubtree() && + !info.hasChild(parseInt(last))) { + // We have no subtree or child available at some point in this node's ancestry + valid = false; + } + + break; + } else if (info === null) { + // Some node in the ancestry was loaded and said there wasn't a subtree + valid = false; + break; + } + } + + return valid; + }; + + var taskProcessor = new TaskProcessor('decodeGoogleEarthEnterprisePacket', Number.POSITIVE_INFINITY); + + /** + * Retrieves a Google Earth Enterprise quadtree packet. + * + * @param {String} [quadKey=''] The quadkey to retrieve the packet for. + * @param {Number} [version=1] The cnode version to be used in the request. + * @param {Request} [request] The request object. Intended for internal use only. + * + * @private + */ + GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket = function(quadKey, version, request) { + version = defaultValue(version, 1); + quadKey = defaultValue(quadKey, ''); + var url = getMetadataUrl(this, quadKey, version); + var proxy = this._proxy; + if (defined(proxy)) { + url = proxy.getURL(url); + } + + var promise = loadArrayBuffer(url, undefined, request); + + if (!defined(promise)) { + return undefined; // Throttled + } + + var tileInfo = this._tileInfo; + var key = this.key; + return promise + .then(function(metadata) { + var decodePromise = taskProcessor.scheduleTask({ + buffer : metadata, + quadKey : quadKey, + type : 'Metadata', + key: key + }, [metadata]); + + return decodePromise + .then(function(result) { + var root; + var topLevelKeyLength = -1; + if (quadKey !== '') { + // Root tile has no data except children bits, so put them into the tile info + topLevelKeyLength = quadKey.length + 1; + var top = result[quadKey]; + root = tileInfo[quadKey]; + root._bits |= top._bits; + + delete result[quadKey]; + } + + // Copy the resulting objects into tileInfo + // Make sure we start with shorter quadkeys first, so we know the parents have + // already been processed. Otherwise we can lose ancestorHasTerrain along the way. + var keys = Object.keys(result); + keys.sort(function(a, b) { + return a.length - b.length; + }); + var keysLength = keys.length; + for (var i = 0; i < keysLength; ++i) { + var key = keys[i]; + var r = result[key]; + if (r !== null) { + var info = GoogleEarthEnterpriseTileInformation.clone(result[key]); + var keyLength = key.length; + if (keyLength === topLevelKeyLength) { + info.setParent(root); + } else if(keyLength > 1){ + var parent = tileInfo[key.substring(0, key.length - 1)]; + info.setParent(parent); + } + tileInfo[key] = info; + } else { + tileInfo[key] = null; + } + } + }); + }); + }; + + /** + * Populates the metadata subtree down to the specified tile. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. + * + * @returns {Promise} A promise that resolves to the tile info for the requested quad key + * + * @private + */ + GoogleEarthEnterpriseMetadata.prototype.populateSubtree = function(x, y, level, request) { + var quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + return populateSubtree(this, quadkey, request); + }; + + function populateSubtree(that, quadKey, request) { + var tileInfo = that._tileInfo; + var q = quadKey; + var t = tileInfo[q]; + // If we have tileInfo make sure sure it is not a node with a subtree that's not loaded + if (defined(t) && (!t.hasSubtree() || t.hasChildren())) { + return t; + } + + while ((t === undefined) && q.length > 1) { + q = q.substring(0, q.length - 1); + t = tileInfo[q]; + } + + var subtreeRequest; + var subtreePromises = that._subtreePromises; + var promise = subtreePromises[q]; + if (defined(promise)) { + return promise + .then(function() { + // Recursively call this in case we need multiple subtree requests + subtreeRequest = new Request({ + throttle : request.throttle, + throttleByServer : request.throttleByServer, + type : request.type, + priorityFunction : request.priorityFunction + }); + return populateSubtree(that, quadKey, subtreeRequest); + }); + } + + // t is either + // null so one of its parents was a leaf node, so this tile doesn't exist + // exists but doesn't have a subtree to request + // undefined so no parent exists - this shouldn't ever happen once the provider is ready + if (!defined(t) || !t.hasSubtree()) { + return when.reject(new RuntimeError('Couldn\'t load metadata for tile ' + quadKey)); + } + + // We need to split up the promise here because when will execute syncronously if getQuadTreePacket + // is already resolved (like in the tests), so subtreePromises will never get cleared out. + // Only the initial request will also remove the promise from subtreePromises. + promise = that.getQuadTreePacket(q, t.cnodeVersion, request); + if (!defined(promise)) { + return undefined; + } + subtreePromises[q] = promise; + + return promise + .then(function() { + // Recursively call this in case we need multiple subtree requests + subtreeRequest = new Request({ + throttle : request.throttle, + throttleByServer : request.throttleByServer, + type : request.type, + priorityFunction : request.priorityFunction + }); + return populateSubtree(that, quadKey, subtreeRequest); + }) + .always(function() { + delete subtreePromises[q]; + }); + } + + /** + * Gets information about a tile + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded. + * + * @private + */ + GoogleEarthEnterpriseMetadata.prototype.getTileInformation = function(x, y, level) { + var quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + return this._tileInfo[quadkey]; + }; + + /** + * Gets information about a tile from a quadKey + * + * @param {String} quadkey The quadkey for the tile + * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded. + * + * @private + */ + GoogleEarthEnterpriseMetadata.prototype.getTileInformationFromQuadKey = function(quadkey) { + return this._tileInfo[quadkey]; + }; + + function getMetadataUrl(that, quadKey, version) { + return joinUrls(that._url, 'flatfile?q2-0' + quadKey + '-q.' + version.toString()); + } + + function requestDbRoot(that) { + var url = joinUrls(that._url, 'dbRoot.v5?output=proto'); + var proxy = that._proxy; + if (defined(proxy)) { + url = proxy.getURL(url); + } + + var promise = loadArrayBuffer(url) + .then(function(buf) { + var encryptedDbRootProto = dbrootParser.EncryptedDbRootProto.decode(new Uint8Array(buf)); + + var byteArray = encryptedDbRootProto.encryptionData; + var offset = byteArray.byteOffset; + var end = offset + byteArray.byteLength; + var key = that.key = byteArray.buffer.slice(offset, end); + + byteArray = encryptedDbRootProto.dbrootData; + offset = byteArray.byteOffset; + end = offset + byteArray.byteLength; + var dbRootCompressed = byteArray.buffer.slice(offset, end); + return taskProcessor.scheduleTask({ + buffer : dbRootCompressed, + type : 'DbRoot', + key: key + }, [dbRootCompressed]); + }) + .then(function(result) { + var dbRoot = dbrootParser.DbRootProto.decode(new Uint8Array(result.buffer)); + that.imageryPresent = defaultValue(dbRoot.imageryPresent, that.imageryPresent); + that.protoImagery = dbRoot.protoImagery; + that.terrainPresent = defaultValue(dbRoot.terrainPresent, that.terrainPresent); + if (defined(dbRoot.endSnippet) && defined(dbRoot.endSnippet.model)) { + var model = dbRoot.endSnippet.model; + that.negativeAltitudeExponentBias = defaultValue(model.negativeAltitudeExponentBias, that.negativeAltitudeExponentBias); + that.negativeAltitudeThreshold = defaultValue(model.compressedNegativeAltitudeThreshold, that.negativeAltitudeThreshold); + } + if (defined(dbRoot.databaseVersion)) { + that._quadPacketVersion = defaultValue(dbRoot.databaseVersion.quadtreeVersion, that._quadPacketVersion); + } + var providers = that.providers; + var providerInfo = defaultValue(dbRoot.providerInfo, []); + var count = providerInfo.length; + for(var i=0;i + * + * + * + * + * + *
Click on the 3D window then use the keyboard to change settings.
Bit PositionBit ValueChild Tile
01Southwest
12Southeast
24Northeast
38Northwest
+ * @param {Boolean} [options.createdByUpsampling=false] True if this instance was created by upsampling another instance; + * otherwise, false. + * @param {Credit[]} [options.credits] Array of credits for this tile. + * + * + * @example + * var buffer = ... + * var childTileMask = ... + * var terrainData = new Cesium.GoogleEarthEnterpriseTerrainData({ + * buffer : heightBuffer, + * childTileMask : childTileMask + * }); + * + * @see TerrainData + * @see HeightTerrainData + * @see QuantizedMeshTerrainData + */ + function GoogleEarthEnterpriseTerrainData(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('options.buffer', options.buffer); + Check.typeOf.number('options.negativeAltitudeExponentBias', options.negativeAltitudeExponentBias); + Check.typeOf.number('options.negativeElevationThreshold', options.negativeElevationThreshold); + //>>includeEnd('debug'); + + this._buffer = options.buffer; + this._credits = options.credits; + this._negativeAltitudeExponentBias = options.negativeAltitudeExponentBias; + this._negativeElevationThreshold = options.negativeElevationThreshold; + + // Convert from google layout to layout of other providers + // 3 2 -> 2 3 + // 0 1 -> 0 1 + var googleChildTileMask = defaultValue(options.childTileMask, 15); + var childTileMask = googleChildTileMask & 3; // Bottom row is identical + childTileMask |= (googleChildTileMask & 4) ? 8 : 0; // NE + childTileMask |= (googleChildTileMask & 8) ? 4 : 0; // NW + + this._childTileMask = childTileMask; + + this._createdByUpsampling = defaultValue(options.createdByUpsampling, false); + + this._skirtHeight = undefined; + this._bufferType = this._buffer.constructor; + this._mesh = undefined; + this._minimumHeight = undefined; + this._maximumHeight = undefined; + this._vertexCountWithoutSkirts = undefined; + this._skirtIndex = undefined; + } + + defineProperties(GoogleEarthEnterpriseTerrainData.prototype, { + /** + * An array of credits for this tile + * @memberof GoogleEarthEnterpriseTerrainData.prototype + * @type {Credit[]} + */ + credits : { + get : function() { + return this._credits; + } + }, + /** + * The water mask included in this terrain data, if any. A water mask is a rectangular + * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. + * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. + * @memberof GoogleEarthEnterpriseTerrainData.prototype + * @type {Uint8Array|Image|Canvas} + */ + waterMask : { + get : function() { + return undefined; + } + } + }); + + var taskProcessor = new TaskProcessor('createVerticesFromGoogleEarthEnterpriseBuffer'); + + var nativeRectangleScratch = new Rectangle(); + var rectangleScratch = new Rectangle(); + + /** + * Creates a {@link TerrainMesh} from this terrain data. + * + * @private + * + * @param {TilingScheme} tilingScheme The tiling scheme to which this tile belongs. + * @param {Number} x The X coordinate of the tile for which to create the terrain data. + * @param {Number} y The Y coordinate of the tile for which to create the terrain data. + * @param {Number} level The level of the tile for which to create the terrain data. + * @param {Number} [exaggeration=1.0] The scale used to exaggerate the terrain. + * @returns {Promise.|undefined} A promise for the terrain mesh, or undefined if too many + * asynchronous mesh creations are already in progress and the operation should + * be retried later. + */ + GoogleEarthEnterpriseTerrainData.prototype.createMesh = function(tilingScheme, x, y, level, exaggeration) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('tilingScheme', tilingScheme); + Check.typeOf.number('x', x); + Check.typeOf.number('y', y); + Check.typeOf.number('level', level); + //>>includeEnd('debug'); + + var ellipsoid = tilingScheme.ellipsoid; + tilingScheme.tileXYToNativeRectangle(x, y, level, nativeRectangleScratch); + tilingScheme.tileXYToRectangle(x, y, level, rectangleScratch); + exaggeration = defaultValue(exaggeration, 1.0); + + // Compute the center of the tile for RTC rendering. + var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangleScratch)); + + var levelZeroMaxError = 40075.16; // From Google's Doc + var thisLevelMaxError = levelZeroMaxError / (1 << level); + this._skirtHeight = Math.min(thisLevelMaxError * 8.0, 1000.0); + + var verticesPromise = taskProcessor.scheduleTask({ + buffer : this._buffer, + nativeRectangle : nativeRectangleScratch, + rectangle : rectangleScratch, + relativeToCenter : center, + ellipsoid : ellipsoid, + skirtHeight : this._skirtHeight, + exaggeration : exaggeration, + includeWebMercatorT : true, + negativeAltitudeExponentBias: this._negativeAltitudeExponentBias, + negativeElevationThreshold: this._negativeElevationThreshold + }); + + if (!defined(verticesPromise)) { + // Postponed + return undefined; + } + + var that = this; + return verticesPromise + .then(function(result) { + that._mesh = new TerrainMesh( + center, + new Float32Array(result.vertices), + new Uint16Array(result.indices), + result.minimumHeight, + result.maximumHeight, + result.boundingSphere3D, + result.occludeePointInScaledSpace, + result.numberOfAttributes, + result.orientedBoundingBox, + TerrainEncoding.clone(result.encoding), + exaggeration); + + that._vertexCountWithoutSkirts = result.vertexCountWithoutSkirts; + that._skirtIndex = result.skirtIndex; + that._minimumHeight = result.minimumHeight; + that._maximumHeight = result.maximumHeight; + + // Free memory received from server after mesh is created. + that._buffer = undefined; + return that._mesh; + }); + }; + + /** + * Computes the terrain height at a specified longitude and latitude. + * + * @param {Rectangle} rectangle The rectangle covered by this terrain data. + * @param {Number} longitude The longitude in radians. + * @param {Number} latitude The latitude in radians. + * @returns {Number} The terrain height at the specified position. If the position + * is outside the rectangle, this method will extrapolate the height, which is likely to be wildly + * incorrect for positions far outside the rectangle. + */ + GoogleEarthEnterpriseTerrainData.prototype.interpolateHeight = function(rectangle, longitude, latitude) { + var u = CesiumMath.clamp((longitude - rectangle.west) / rectangle.width, 0.0, 1.0); + var v = CesiumMath.clamp((latitude - rectangle.south) / rectangle.height, 0.0, 1.0); + + if (!defined(this._mesh)) { + return interpolateHeight(this, u, v, rectangle); + } + + return interpolateMeshHeight(this, u, v); + }; + + var upsampleTaskProcessor = new TaskProcessor('upsampleQuantizedTerrainMesh'); + + /** + * Upsamples this terrain data for use by a descendant tile. The resulting instance will contain a subset of the + * height samples in this instance, interpolated if necessary. + * + * @param {TilingScheme} tilingScheme The tiling scheme of this terrain data. + * @param {Number} thisX The X coordinate of this tile in the tiling scheme. + * @param {Number} thisY The Y coordinate of this tile in the tiling scheme. + * @param {Number} thisLevel The level of this tile in the tiling scheme. + * @param {Number} descendantX The X coordinate within the tiling scheme of the descendant tile for which we are upsampling. + * @param {Number} descendantY The Y coordinate within the tiling scheme of the descendant tile for which we are upsampling. + * @param {Number} descendantLevel The level within the tiling scheme of the descendant tile for which we are upsampling. + * @returns {Promise.|undefined} A promise for upsampled heightmap terrain data for the descendant tile, + * or undefined if too many asynchronous upsample operations are in progress and the request has been + * deferred. + */ + GoogleEarthEnterpriseTerrainData.prototype.upsample = function(tilingScheme, thisX, thisY, thisLevel, descendantX, descendantY, descendantLevel) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('tilingScheme', tilingScheme); + Check.typeOf.number('thisX', thisX); + Check.typeOf.number('thisY', thisY); + Check.typeOf.number('thisLevel', thisLevel); + Check.typeOf.number('descendantX', descendantX); + Check.typeOf.number('descendantY', descendantY); + Check.typeOf.number('descendantLevel', descendantLevel); + var levelDifference = descendantLevel - thisLevel; + if (levelDifference > 1) { + throw new DeveloperError('Upsampling through more than one level at a time is not currently supported.'); + } + //>>includeEnd('debug'); + + var mesh = this._mesh; + if (!defined(this._mesh)) { + return undefined; + } + + var isEastChild = thisX * 2 !== descendantX; + var isNorthChild = thisY * 2 === descendantY; + + var ellipsoid = tilingScheme.ellipsoid; + var childRectangle = tilingScheme.tileXYToRectangle(descendantX, descendantY, descendantLevel); + + var upsamplePromise = upsampleTaskProcessor.scheduleTask({ + vertices : mesh.vertices, + vertexCountWithoutSkirts : this._vertexCountWithoutSkirts, + indices : mesh.indices, + skirtIndex : this._skirtIndex, + encoding : mesh.encoding, + minimumHeight : this._minimumHeight, + maximumHeight : this._maximumHeight, + isEastChild : isEastChild, + isNorthChild : isNorthChild, + childRectangle : childRectangle, + ellipsoid : ellipsoid, + exaggeration : mesh.exaggeration + }); + + if (!defined(upsamplePromise)) { + // Postponed + return undefined; + } + + var that = this; + return upsamplePromise + .then(function(result) { + var quantizedVertices = new Uint16Array(result.vertices); + var indicesTypedArray = IndexDatatype.createTypedArray(quantizedVertices.length / 3, result.indices); + + var skirtHeight = that._skirtHeight; + + // Use QuantizedMeshTerrainData since we have what we need already parsed + return new QuantizedMeshTerrainData({ + quantizedVertices : quantizedVertices, + indices : indicesTypedArray, + minimumHeight : result.minimumHeight, + maximumHeight : result.maximumHeight, + boundingSphere : BoundingSphere.clone(result.boundingSphere), + orientedBoundingBox : OrientedBoundingBox.clone(result.orientedBoundingBox), + horizonOcclusionPoint : Cartesian3.clone(result.horizonOcclusionPoint), + westIndices : result.westIndices, + southIndices : result.southIndices, + eastIndices : result.eastIndices, + northIndices : result.northIndices, + westSkirtHeight : skirtHeight, + southSkirtHeight : skirtHeight, + eastSkirtHeight : skirtHeight, + northSkirtHeight : skirtHeight, + childTileMask : 0, + createdByUpsampling : true, + credits : that._credits + }); + }); + }; + + /** + * Determines if a given child tile is available, based on the + * {@link HeightmapTerrainData.childTileMask}. The given child tile coordinates are assumed + * to be one of the four children of this tile. If non-child tile coordinates are + * given, the availability of the southeast child tile is returned. + * + * @param {Number} thisX The tile X coordinate of this (the parent) tile. + * @param {Number} thisY The tile Y coordinate of this (the parent) tile. + * @param {Number} childX The tile X coordinate of the child tile to check for availability. + * @param {Number} childY The tile Y coordinate of the child tile to check for availability. + * @returns {Boolean} True if the child tile is available; otherwise, false. + */ + GoogleEarthEnterpriseTerrainData.prototype.isChildAvailable = function(thisX, thisY, childX, childY) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number('thisX', thisX); + Check.typeOf.number('thisY', thisY); + Check.typeOf.number('childX', childX); + Check.typeOf.number('childY', childY); + //>>includeEnd('debug'); + + var bitNumber = 2; // northwest child + if (childX !== thisX * 2) { + ++bitNumber; // east child + } + if (childY !== thisY * 2) { + bitNumber -= 2; // south child + } + + return (this._childTileMask & (1 << bitNumber)) !== 0; + }; + + /** + * Gets a value indicating whether or not this terrain data was created by upsampling lower resolution + * terrain data. If this value is false, the data was obtained from some other source, such + * as by downloading it from a remote server. This method should return true for instances + * returned from a call to {@link HeightmapTerrainData#upsample}. + * + * @returns {Boolean} True if this instance was created by upsampling; otherwise, false. + */ + GoogleEarthEnterpriseTerrainData.prototype.wasCreatedByUpsampling = function() { + return this._createdByUpsampling; + }; + + var texCoordScratch0 = new Cartesian2(); + var texCoordScratch1 = new Cartesian2(); + var texCoordScratch2 = new Cartesian2(); + var barycentricCoordinateScratch = new Cartesian3(); + + function interpolateMeshHeight(terrainData, u, v) { + var mesh = terrainData._mesh; + var vertices = mesh.vertices; + var encoding = mesh.encoding; + var indices = mesh.indices; + + for (var i = 0, len = indices.length; i < len; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var uv0 = encoding.decodeTextureCoordinates(vertices, i0, texCoordScratch0); + var uv1 = encoding.decodeTextureCoordinates(vertices, i1, texCoordScratch1); + var uv2 = encoding.decodeTextureCoordinates(vertices, i2, texCoordScratch2); + + var barycentric = Intersections2D.computeBarycentricCoordinates(u, v, uv0.x, uv0.y, uv1.x, uv1.y, uv2.x, uv2.y, barycentricCoordinateScratch); + if (barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15) { + var h0 = encoding.decodeHeight(vertices, i0); + var h1 = encoding.decodeHeight(vertices, i1); + var h2 = encoding.decodeHeight(vertices, i2); + return barycentric.x * h0 + barycentric.y * h1 + barycentric.z * h2; + } + } + + // Position does not lie in any triangle in this mesh. + return undefined; + } + + var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT; + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT; + var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT; + var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT; + + function interpolateHeight(terrainData, u, v, rectangle) { + var buffer = terrainData._buffer; + var quad = 0; // SW + var uStart = 0.0; + var vStart = 0.0; + if (v > 0.5) { // Upper row + if (u > 0.5) { // NE + quad = 2; + uStart = 0.5; + } else { // NW + quad = 3; + } + vStart = 0.5; + } else if (u > 0.5) { // SE + quad = 1; + uStart = 0.5; + } + + var dv = new DataView(buffer); + var offset = 0; + for (var q = 0; q < quad; ++q) { + offset += dv.getUint32(offset, true); + offset += sizeOfUint32; + } + offset += sizeOfUint32; // Skip length of quad + offset += 2 * sizeOfDouble; // Skip origin + + // Read sizes + var xSize = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + offset += sizeOfDouble; + var ySize = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + offset += sizeOfDouble; + + // Samples per quad + var xScale = rectangle.width / xSize / 2; + var yScale = rectangle.height / ySize / 2; + + // Number of points + var numPoints = dv.getInt32(offset, true); + offset += sizeOfInt32; + + // Number of faces + var numIndices = dv.getInt32(offset, true) * 3; + offset += sizeOfInt32; + + offset += sizeOfInt32; // Skip Level + + var uBuffer = new Array(numPoints); + var vBuffer = new Array(numPoints); + var heights = new Array(numPoints); + for (var i = 0; i < numPoints; ++i) { + uBuffer[i] = uStart + (dv.getUint8(offset++) * xScale); + vBuffer[i] = vStart + (dv.getUint8(offset++) * yScale); + + // Height is stored in units of (1/EarthRadius) or (1/6371010.0) + heights[i] = (dv.getFloat32(offset, true) * 6371010.0); + offset += sizeOfFloat; + } + + var indices = new Array(numIndices); + for (i = 0; i < numIndices; ++i) { + indices[i] = dv.getUint16(offset, true); + offset += sizeOfUint16; + } + + for (i = 0; i < numIndices; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var u0 = uBuffer[i0]; + var u1 = uBuffer[i1]; + var u2 = uBuffer[i2]; + + var v0 = vBuffer[i0]; + var v1 = vBuffer[i1]; + var v2 = vBuffer[i2]; + + var barycentric = Intersections2D.computeBarycentricCoordinates(u, v, u0, v0, u1, v1, u2, v2, barycentricCoordinateScratch); + if (barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15) { + return barycentric.x * heights[i0] + + barycentric.y * heights[i1] + + barycentric.z * heights[i2]; + } + } + + // Position does not lie in any triangle in this mesh. + return undefined; + } + + return GoogleEarthEnterpriseTerrainData; +}); diff --git a/Source/Core/GoogleEarthEnterpriseTerrainProvider.js b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js new file mode 100644 index 000000000000..d5e2928633b5 --- /dev/null +++ b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js @@ -0,0 +1,616 @@ +/*global define*/ +define([ + '../ThirdParty/when', + './Credit', + './defaultValue', + './defined', + './defineProperties', + './deprecationWarning', + './DeveloperError', + './Event', + './GeographicTilingScheme', + './GoogleEarthEnterpriseMetadata', + './GoogleEarthEnterpriseTerrainData', + './HeightmapTerrainData', + './JulianDate', + './loadArrayBuffer', + './Math', + './Rectangle', + './Request', + './RequestState', + './RequestType', + './RuntimeError', + './TaskProcessor', + './TileProviderError' +], function( + when, + Credit, + defaultValue, + defined, + defineProperties, + deprecationWarning, + DeveloperError, + Event, + GeographicTilingScheme, + GoogleEarthEnterpriseMetadata, + GoogleEarthEnterpriseTerrainData, + HeightmapTerrainData, + JulianDate, + loadArrayBuffer, + CesiumMath, + Rectangle, + Request, + RequestState, + RequestType, + RuntimeError, + TaskProcessor, + TileProviderError) { + 'use strict'; + + var TerrainState = { + UNKNOWN : 0, + NONE : 1, + SELF : 2, + PARENT : 3 + }; + + var julianDateScratch = new JulianDate(); + + function TerrainCache() { + this._terrainCache = {}; + this._lastTidy = JulianDate.now(); + } + + TerrainCache.prototype.add = function(quadKey, buffer) { + this._terrainCache[quadKey] = { + buffer : buffer, + timestamp : JulianDate.now() + }; + }; + + TerrainCache.prototype.get = function(quadKey) { + var terrainCache = this._terrainCache; + var result = terrainCache[quadKey]; + if (defined(result)) { + delete this._terrainCache[quadKey]; + return result.buffer; + } + }; + + TerrainCache.prototype.tidy = function() { + JulianDate.now(julianDateScratch); + if (JulianDate.secondsDifference(julianDateScratch, this._lastTidy) > 10) { + var terrainCache = this._terrainCache; + var keys = Object.keys(terrainCache); + var count = keys.length; + for (var i = 0; i < count; ++i) { + var k = keys[i]; + var e = terrainCache[k]; + if (JulianDate.secondsDifference(julianDateScratch, e.timestamp) > 10) { + delete terrainCache[k]; + } + } + + JulianDate.clone(julianDateScratch, this._lastTidy); + } + }; + + /** + * Provides tiled terrain using the Google Earth Enterprise REST API. + * + * @alias GoogleEarthEnterpriseTerrainProvider + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {String} options.url The url of the Google Earth Enterprise server hosting the imagery. + * @param {GoogleEarthEnterpriseMetadata} options.metadata A metadata object that can be used to share metadata requests with a GoogleEarthEnterpriseImageryProvider. + * @param {Proxy} [options.proxy] A proxy to use for requests. This object is + * expected to have a getURL function which returns the proxied URL, if needed. + * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. + * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. + * + * @see GoogleEarthEnterpriseImageryProvider + * @see CesiumTerrainProvider + * + * @example + * var geeMetadata = new GoogleEarthEnterpriseMetadata('http://www.earthenterprise.org/3d'); + * var gee = new Cesium.GoogleEarthEnterpriseTerrainProvider({ + * metadata : geeMetadata + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + */ + function GoogleEarthEnterpriseTerrainProvider(options) { + options = defaultValue(options, {}); + + //>>includeStart('debug', pragmas.debug); + if (!(defined(options.url) || defined(options.metadata))) { + throw new DeveloperError('options.url or options.metadata is required.'); + } + //>>includeEnd('debug'); + + var metadata; + if (defined(options.metadata)) { + metadata = this._metadata = options.metadata; + } else { + metadata = this._metadata = new GoogleEarthEnterpriseMetadata({ + url : options.url, + proxy : options.proxy + }); + } + this._proxy = defaultValue(options.proxy, this._metadata.proxy); + + this._tilingScheme = new GeographicTilingScheme({ + numberOfLevelZeroTilesX : 2, + numberOfLevelZeroTilesY : 2, + rectangle : new Rectangle(-CesiumMath.PI, -CesiumMath.PI, CesiumMath.PI, CesiumMath.PI), + ellipsoid : options.ellipsoid + }); + + var credit = options.credit; + if (typeof credit === 'string') { + credit = new Credit(credit); + } + this._credit = credit; + + // Pulled from Google's documentation + this._levelZeroMaximumGeometricError = 40075.16; + + this._terrainCache = new TerrainCache(); + this._terrainPromises = {}; + this._terrainRequests = {}; + + this._errorEvent = new Event(); + + this._ready = false; + var that = this; + var metadataError; + this._readyPromise = metadata.readyPromise + .then(function(result) { + if (!metadata.terrainPresent) { + var e = new RuntimeError('The server ' + metadata.url + ' doesn\'t have terrain'); + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e); + return when.reject(e); + } + + TileProviderError.handleSuccess(metadataError); + that._ready = result; + return result; + }) + .otherwise(function(e) { + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e); + return when.reject(e); + }); + } + + defineProperties(GoogleEarthEnterpriseTerrainProvider.prototype, { + /** + * Gets the name of the Google Earth Enterprise server url hosting the imagery. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {String} + * @readonly + */ + url : { + get : function() { + return this._metadata.url; + } + }, + + /** + * Gets the proxy used by this provider. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Proxy} + * @readonly + */ + proxy : { + get : function() { + return this._proxy; + } + }, + + /** + * Gets the tiling scheme used by this provider. This function should + * not be called before {@link GoogleEarthEnterpriseProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {TilingScheme} + * @readonly + */ + tilingScheme : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tilingScheme; + } + }, + + /** + * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing + * to the event, you will be notified of the error and can potentially recover from it. Event listeners + * are passed an instance of {@link TileProviderError}. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Event} + * @readonly + */ + errorEvent : { + get : function() { + return this._errorEvent; + } + }, + + /** + * Gets a value indicating whether or not the provider is ready for use. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Boolean} + * @readonly + */ + ready : { + get : function() { + return this._ready; + } + }, + + /** + * Gets a promise that resolves to true when the provider is ready for use. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + return this._readyPromise; + } + }, + + /** + * Gets the credit to display when this terrain provider is active. Typically this is used to credit + * the source of the terrain. This function should not be called before {@link GoogleEarthEnterpriseProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Credit} + * @readonly + */ + credit : { + get : function() { + return this._credit; + } + }, + + /** + * Gets a value indicating whether or not the provider includes a water mask. The water mask + * indicates which areas of the globe are water rather than land, so they can be rendered + * as a reflective surface with animated waves. This function should not be + * called before {@link GoogleEarthEnterpriseProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Boolean} + */ + hasWaterMask : { + get : function() { + return false; + } + }, + + /** + * Gets a value indicating whether or not the requested tiles include vertex normals. + * This function should not be called before {@link GoogleEarthEnterpriseProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {Boolean} + */ + hasVertexNormals : { + get : function() { + return false; + } + }, + + /** + * Gets an object that can be used to determine availability of terrain from this provider, such as + * at points and in rectangles. This function should not be called before + * {@link GoogleEarthEnterpriseProvider#ready} returns true. This property may be undefined if availability + * information is not available. + * @memberof GoogleEarthEnterpriseProvider.prototype + * @type {TileAvailability} + */ + availability : { + get : function() { + return undefined; + } + } + }); + + var taskProcessor = new TaskProcessor('decodeGoogleEarthEnterprisePacket', Number.POSITIVE_INFINITY); + + // If the tile has its own terrain, then you can just use its child bitmask. If it was requested using it's parent + // then you need to check all of its children to see if they have terrain. + function computeChildMask(quadKey, info, metadata) { + var childMask = info.getChildBitmask(); + if (info.terrainState === TerrainState.PARENT) { + childMask = 0; + for (var i = 0; i < 4; ++i) { + var child = metadata.getTileInformationFromQuadKey(quadKey + i.toString()); + if (defined(child) && child.hasTerrain()) { + childMask |= (1 << i); + } + } + } + + return childMask; + } + + /** + * Requests the geometry for a given tile. This function should not be called before + * {@link GoogleEarthEnterpriseProvider#ready} returns true. The result must include terrain data and + * may optionally include a water mask and an indication of which child tiles are available. + * + * @param {Number} x The X coordinate of the tile for which to request geometry. + * @param {Number} y The Y coordinate of the tile for which to request geometry. + * @param {Number} level The level of the tile for which to request geometry. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} A promise for the requested geometry. If this method + * returns undefined instead of a promise, it is an indication that too many requests are already + * pending and the request will be retried later. + * + * @exception {DeveloperError} This function must not be called before {@link GoogleEarthEnterpriseProvider#ready} + * returns true. + */ + GoogleEarthEnterpriseTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) { + //>>includeStart('debug', pragmas.debug) + if (!this._ready) { + throw new DeveloperError('requestTileGeometry must not be called before the terrain provider is ready.'); + } + //>>includeEnd('debug'); + + var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + var terrainCache = this._terrainCache; + var metadata = this._metadata; + var info = metadata.getTileInformationFromQuadKey(quadKey); + + // Check if this tile is even possibly available + if (!defined(info)) { + return when.reject(new RuntimeError('Terrain tile doesn\'t exist')); + } + + var terrainState = info.terrainState; + if (!defined(terrainState)) { + // First time we have tried to load this tile, so set terrain state to UNKNOWN + terrainState = info.terrainState = TerrainState.UNKNOWN; + } + + // If its in the cache, return it + var buffer = terrainCache.get(quadKey); + if (defined(buffer)) { + var credit = metadata.providers[info.terrainProvider]; + return new GoogleEarthEnterpriseTerrainData({ + buffer : buffer, + childTileMask : computeChildMask(quadKey, info, metadata), + credits : defined(credit) ? [credit] : undefined, + negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias, + negativeElevationThreshold: metadata.negativeAltitudeThreshold + }); + } + + // Clean up the cache + terrainCache.tidy(); + + // We have a tile, check to see if no ancestors have terrain or that we know for sure it doesn't + if (!info.ancestorHasTerrain) { + // We haven't reached a level with terrain, so return the ellipsoid + return new HeightmapTerrainData({ + buffer : new Uint8Array(16 * 16), + width : 16, + height : 16 + }); + } else if (terrainState === TerrainState.NONE) { + // Already have info and there isn't any terrain here + return when.reject(new RuntimeError('Terrain tile doesn\'t exist')); + } + + // Figure out where we are getting the terrain and what version + var parentInfo; + var q = quadKey; + var terrainVersion = -1; + switch (terrainState) { + case TerrainState.SELF: // We have terrain and have retrieved it before + terrainVersion = info.terrainVersion; + break; + case TerrainState.PARENT: // We have terrain in our parent + q = q.substring(0, q.length - 1); + parentInfo = metadata.getTileInformationFromQuadKey(q); + terrainVersion = parentInfo.terrainVersion; + break; + case TerrainState.UNKNOWN: // We haven't tried to retrieve terrain yet + if (info.hasTerrain()) { + terrainVersion = info.terrainVersion; // We should have terrain + } else { + q = q.substring(0, q.length - 1); + parentInfo = metadata.getTileInformationFromQuadKey(q); + if (defined(parentInfo) && parentInfo.hasTerrain()) { + terrainVersion = parentInfo.terrainVersion; // Try checking in the parent + } + } + break; + } + + // We can't figure out where to get the terrain + if (terrainVersion < 0) { + return when.reject(new RuntimeError('Terrain tile doesn\'t exist')); + } + + // Load that terrain + var terrainPromises = this._terrainPromises; + var terrainRequests = this._terrainRequests; + var url = buildTerrainUrl(this, q, terrainVersion); + var sharedPromise; + var sharedRequest; + if (defined(terrainPromises[q])) { // Already being loaded possibly from another child, so return existing promise + sharedPromise = terrainPromises[q]; + sharedRequest = terrainRequests[q]; + } else { // Create new request for terrain + if (typeof request === 'boolean') { + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); + request = new Request({ + throttle : request, + throttleByServer : request, + type : RequestType.TERRAIN + }); + } + + sharedRequest = request; + var requestPromise = loadArrayBuffer(url, undefined, sharedRequest); + + if (!defined(requestPromise)) { + return undefined; // Throttled + } + + sharedPromise = requestPromise + .then(function(terrain) { + if (defined(terrain)) { + return taskProcessor.scheduleTask({ + buffer : terrain, + type : 'Terrain', + key : metadata.key + }, [terrain]) + .then(function(terrainTiles) { + // Add requested tile and mark it as SELF + var requestedInfo = metadata.getTileInformationFromQuadKey(q); + requestedInfo.terrainState = TerrainState.SELF; + terrainCache.add(q, terrainTiles[0]); + var provider = requestedInfo.terrainProvider; + + // Add children to cache + var count = terrainTiles.length - 1; + for (var j = 0; j < count; ++j) { + var childKey = q + j.toString(); + var child = metadata.getTileInformationFromQuadKey(childKey); + if (defined(child)) { + terrainCache.add(childKey, terrainTiles[j + 1]); + child.terrainState = TerrainState.PARENT; + if (child.terrainProvider === 0) { + child.terrainProvider = provider; + } + } + } + }); + } + + return when.reject(new RuntimeError('Failed to load terrain.')); + }); + + terrainPromises[q] = sharedPromise; // Store promise without delete from terrainPromises + terrainRequests[q] = sharedRequest; + + // Set promise so we remove from terrainPromises just one time + sharedPromise = sharedPromise + .always(function() { + delete terrainPromises[q]; + delete terrainRequests[q]; + }); + } + + return sharedPromise + .then(function() { + var buffer = terrainCache.get(quadKey); + if (defined(buffer)) { + var credit = metadata.providers[info.terrainProvider]; + return new GoogleEarthEnterpriseTerrainData({ + buffer : buffer, + childTileMask : computeChildMask(quadKey, info, metadata), + credits : defined(credit) ? [credit] : undefined, + negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias, + negativeElevationThreshold: metadata.negativeAltitudeThreshold + }); + } + + return when.reject(new RuntimeError('Failed to load terrain.')); + }) + .otherwise(function(error) { + if (sharedRequest.state === RequestState.CANCELLED) { + request.state = sharedRequest.state; + return when.reject(error); + } + info.terrainState = TerrainState.NONE; + return when.reject(error); + }); + }; + + /** + * Gets the maximum geometric error allowed in a tile at a given level. + * + * @param {Number} level The tile level for which to get the maximum geometric error. + * @returns {Number} The maximum geometric error. + */ + GoogleEarthEnterpriseTerrainProvider.prototype.getLevelMaximumGeometricError = function(level) { + return this._levelZeroMaximumGeometricError / (1 << level); + }; + + /** + * Determines whether data for a tile is available to be loaded. + * + * @param {Number} x The X coordinate of the tile for which to request geometry. + * @param {Number} y The Y coordinate of the tile for which to request geometry. + * @param {Number} level The level of the tile for which to request geometry. + * @returns {Boolean} Undefined if not supported, otherwise true or false. + */ + GoogleEarthEnterpriseTerrainProvider.prototype.getTileDataAvailable = function(x, y, level) { + var metadata = this._metadata; + var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + + var info = metadata.getTileInformation(x, y, level); + if (info === null) { + return false; + } + + if (defined(info)) { + if (!info.ancestorHasTerrain) { + return true; // We'll just return the ellipsoid + } + + var terrainState = info.terrainState; + if (terrainState === TerrainState.NONE) { + return false; // Terrain is not available + } + + if (!defined(terrainState) || (terrainState === TerrainState.UNKNOWN)) { + info.terrainState = TerrainState.UNKNOWN; + if (!info.hasTerrain()) { + quadKey = quadKey.substring(0, quadKey.length - 1); + var parentInfo = metadata.getTileInformationFromQuadKey(quadKey); + if (!defined(parentInfo) || !parentInfo.hasTerrain()) { + return false; + } + } + } + + return true; + } + + if (metadata.isValid(quadKey)) { + // We will need this tile, so request metadata and return false for now + var request = new Request({ + throttle : true, + throttleByServer : true, + type : RequestType.TERRAIN + }); + metadata.populateSubtree(x, y, level, request); + } + return false; + }; + + // + // Functions to handle imagery packets + // + function buildTerrainUrl(terrainProvider, quadKey, version) { + version = (defined(version) && version > 0) ? version : 1; + var terrainUrl = terrainProvider.url + 'flatfile?f1c-0' + quadKey + '-t.' + version.toString(); + + var proxy = terrainProvider._proxy; + if (defined(proxy)) { + terrainUrl = proxy.getURL(terrainUrl); + } + + return terrainUrl; + } + + return GoogleEarthEnterpriseTerrainProvider; +}); diff --git a/Source/Core/GoogleEarthEnterpriseTileInformation.js b/Source/Core/GoogleEarthEnterpriseTileInformation.js new file mode 100644 index 000000000000..88099aae61e8 --- /dev/null +++ b/Source/Core/GoogleEarthEnterpriseTileInformation.js @@ -0,0 +1,131 @@ +/*global define*/ +define([ + './defined', + './isBitSet' +], function( + defined, + isBitSet) { + 'use strict'; + + // Bitmask for checking tile properties + var childrenBitmasks = [0x01, 0x02, 0x04, 0x08]; + var anyChildBitmask = 0x0F; + var cacheFlagBitmask = 0x10; // True if there is a child subtree + var imageBitmask = 0x40; + var terrainBitmask = 0x80; + + /** + * Contains information about each tile from a Google Earth Enterprise server + * + * @param {Number} bits Bitmask that contains the type of data and available children for each tile. + * @param {Number} cnodeVersion Version of the request for subtree metadata. + * @param {Number} imageryVersion Version of the request for imagery tile. + * @param {Number} terrainVersion Version of the request for terrain tile. + * @param {Number} imageryProvider Id of imagery provider. + * @param {Number} terrainProvider Id of terrain provider. + * + * @private + */ + function GoogleEarthEnterpriseTileInformation(bits, cnodeVersion, imageryVersion, terrainVersion, imageryProvider, terrainProvider) { + this._bits = bits; + this.cnodeVersion = cnodeVersion; + this.imageryVersion = imageryVersion; + this.terrainVersion = terrainVersion; + this.imageryProvider = imageryProvider; + this.terrainProvider = terrainProvider; + this.ancestorHasTerrain = false; // Set it later once we find its parent + this.terrainState = undefined; + } + + /** + * Creates GoogleEarthEnterpriseTileInformation from an object + * + * @param {Object} info Object to be cloned + * @param {GoogleEarthEnterpriseTileInformation} [result] The object onto which to store the result. + * @returns {GoogleEarthEnterpriseTileInformation} The modified result parameter or a new GoogleEarthEnterpriseTileInformation instance if none was provided. + */ + GoogleEarthEnterpriseTileInformation.clone = function(info, result) { + if (!defined(result)) { + result = new GoogleEarthEnterpriseTileInformation(info._bits, info.cnodeVersion, info.imageryVersion, info.terrainVersion, + info.imageryProvider, info.terrainProvider); + } else { + result._bits = info._bits; + result.cnodeVersion = info.cnodeVersion; + result.imageryVersion = info.imageryVersion; + result.terrainVersion = info.terrainVersion; + result.imageryProvider = info.imageryProvider; + result.terrainProvider = info.terrainProvider; + } + result.ancestorHasTerrain = info.ancestorHasTerrain; + result.terrainState = info.terrainState; + + return result; + }; + + /** + * Sets the parent for the tile + * + * @param {GoogleEarthEnterpriseTileInformation} parent Parent tile + */ + GoogleEarthEnterpriseTileInformation.prototype.setParent = function(parent) { + this.ancestorHasTerrain = parent.ancestorHasTerrain || this.hasTerrain(); + }; + + /** + * Gets whether a subtree is available + * + * @returns {Boolean} true if subtree is available, false otherwise. + */ + GoogleEarthEnterpriseTileInformation.prototype.hasSubtree = function() { + return isBitSet(this._bits, cacheFlagBitmask); + }; + + /** + * Gets whether imagery is available + * + * @returns {Boolean} true if imagery is available, false otherwise. + */ + GoogleEarthEnterpriseTileInformation.prototype.hasImagery = function() { + return isBitSet(this._bits, imageBitmask); + }; + + /** + * Gets whether terrain is available + * + * @returns {Boolean} true if terrain is available, false otherwise. + */ + GoogleEarthEnterpriseTileInformation.prototype.hasTerrain = function() { + return isBitSet(this._bits, terrainBitmask); + }; + + /** + * Gets whether any children are present + * + * @returns {Boolean} true if any children are available, false otherwise. + */ + GoogleEarthEnterpriseTileInformation.prototype.hasChildren = function() { + return isBitSet(this._bits, anyChildBitmask); + }; + + /** + * Gets whether a specified child is available + * + * @param {Number} index Index of child tile + * + * @returns {Boolean} true if child is available, false otherwise + */ + GoogleEarthEnterpriseTileInformation.prototype.hasChild = function(index) { + return isBitSet(this._bits, childrenBitmasks[index]); + }; + + /** + * Gets bitmask containing children + * + * @returns {Number} Children bitmask + */ + GoogleEarthEnterpriseTileInformation.prototype.getChildBitmask = function() { + return this._bits & anyChildBitmask; + }; + + return GoogleEarthEnterpriseTileInformation; +}); diff --git a/Source/Core/HeadingPitchRoll.js b/Source/Core/HeadingPitchRoll.js index 6428fc73a83b..2b9cabaf3e52 100644 --- a/Source/Core/HeadingPitchRoll.js +++ b/Source/Core/HeadingPitchRoll.js @@ -32,7 +32,7 @@ define([ * Computes the heading, pitch and roll from a quaternion (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles ) * * @param {Quaternion} quaternion The quaternion from which to retrieve heading, pitch, and roll, all expressed in radians. - * @param {Quaternion} [result] The object in which to store the result. If not provided, a new instance is created and returned. + * @param {HeadingPitchRoll} [result] The object in which to store the result. If not provided, a new instance is created and returned. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. */ HeadingPitchRoll.fromQuaternion = function(quaternion, result) { diff --git a/Source/Core/Heap.js b/Source/Core/Heap.js new file mode 100644 index 000000000000..11f379213a36 --- /dev/null +++ b/Source/Core/Heap.js @@ -0,0 +1,235 @@ +/*global define*/ +define([ + './Check', + './defaultValue', + './defined', + './defineProperties' + ], function( + Check, + defaultValue, + defined, + defineProperties) { + 'use strict'; + + /** + * Array implementation of a heap. + * + * @alias Heap + * @constructor + * @private + * + * @param {Object} options Object with the following properties: + * @param {Heap~ComparatorCallback} options.comparator The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. + */ + function Heap(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('options', options); + Check.defined('options.comparator', options.comparator); + //>>includeEnd('debug'); + + this._comparator = options.comparator; + this._array = []; + this._length = 0; + this._maximumLength = undefined; + } + + defineProperties(Heap.prototype, { + /** + * Gets the length of the heap. + * + * @memberof Heap.prototype + * + * @type {Number} + * @readonly + */ + length : { + get : function() { + return this._length; + } + }, + + /** + * Gets the internal array. + * + * @memberof Heap.prototype + * + * @type {Array} + * @readonly + */ + internalArray : { + get : function() { + return this._array; + } + }, + + /** + * Gets and sets the maximum length of the heap. + * + * @memberof Heap.prototype + * + * @type {Number} + */ + maximumLength : { + get : function() { + return this._maximumLength; + }, + set : function(value) { + this._maximumLength = value; + if (this._length > value && value > 0) { + this._length = value; + this._array.length = value; + } + } + }, + + /** + * The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. + * + * @memberof Heap.prototype + * + * @type {Heap~ComparatorCallback} + */ + comparator : { + get : function() { + return this._comparator; + } + } + }); + + function swap(array, a, b) { + var temp = array[a]; + array[a] = array[b]; + array[b] = temp; + } + + /** + * Resizes the internal array of the heap. + * + * @param {Number} [length] The length to resize internal array to. Defaults to the current length of the heap. + */ + Heap.prototype.reserve = function(length) { + length = defaultValue(length, this._length); + this._array.length = length; + }; + + /** + * Update the heap so that index and all descendants satisfy the heap property. + * + * @param {Number} [index=0] The starting index to heapify from. + */ + Heap.prototype.heapify = function(index) { + index = defaultValue(index, 0); + var length = this._length; + var comparator = this._comparator; + var array = this._array; + var candidate = -1; + var inserting = true; + + while (inserting) { + var right = 2 * (index + 1); + var left = right - 1; + + if (left < length && comparator(array[left], array[index]) < 0) { + candidate = left; + } else { + candidate = index; + } + + if (right < length && comparator(array[right], array[candidate]) < 0) { + candidate = right; + } + if (candidate !== index) { + swap(array, candidate, index); + index = candidate; + } else { + inserting = false; + } + } + }; + + /** + * Resort the heap. + */ + Heap.prototype.resort = function() { + var length = this._length; + for (var i = Math.ceil(length / 2); i >= 0; --i) { + this.heapify(i); + } + }; + + /** + * Insert an element into the heap. If the length would grow greater than maximumLength + * of the heap, extra elements are removed. + * + * @param {*} element The element to insert + * + * @return {*} The element that was removed from the heap if the heap is at full capacity. + */ + Heap.prototype.insert = function(element) { + //>>includeStart('debug', pragmas.debug); + Check.defined('element', element); + //>>includeEnd('debug'); + + var array = this._array; + var comparator = this._comparator; + var maximumLength = this._maximumLength; + + var index = this._length++; + if (index < array.length) { + array[index] = element; + } else { + array.push(element); + } + + while (index !== 0) { + var parent = Math.floor((index - 1) / 2); + if (comparator(array[index], array[parent]) < 0) { + swap(array, index, parent); + index = parent; + } else { + break; + } + } + + var removedElement; + + if (defined(maximumLength) && (this._length > maximumLength)) { + removedElement = array[maximumLength]; + this._length = maximumLength; + } + + return removedElement; + }; + + /** + * Remove the element specified by index from the heap and return it. + * + * @param {Number} [index=0] The index to remove. + * @returns {*} The specified element of the heap. + */ + Heap.prototype.pop = function(index) { + index = defaultValue(index, 0); + if (this._length === 0) { + return undefined; + } + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.lessThan('index', index, this._length); + //>>includeEnd('debug'); + + var array = this._array; + var root = array[index]; + swap(array, index, --this._length); + this.heapify(index); + return root; + }; + + /** + * The comparator to use for the heap. + * @callback Heap~ComparatorCallback + * @param {*} a An element in the heap. + * @param {*} b An element in the heap. + * @returns {Number} If the result of the comparison is less than 0, sort a to a lower index than b, otherwise sort to a higher index. + */ + + return Heap; +}); diff --git a/Source/Core/HeightmapTerrainData.js b/Source/Core/HeightmapTerrainData.js index 3a402797ba1d..83c1dc88e263 100644 --- a/Source/Core/HeightmapTerrainData.js +++ b/Source/Core/HeightmapTerrainData.js @@ -142,6 +142,16 @@ define([ } defineProperties(HeightmapTerrainData.prototype, { + /** + * An array of credits for this tile. + * @memberof HeightmapTerrainData.prototype + * @type {Credit[]} + */ + credits : { + get : function() { + return undefined; + } + }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. diff --git a/Source/Core/HeightmapTessellator.js b/Source/Core/HeightmapTessellator.js index 2195f819a985..ebf11928f13a 100644 --- a/Source/Core/HeightmapTessellator.js +++ b/Source/Core/HeightmapTessellator.js @@ -73,7 +73,7 @@ define([ * @param {Number} options.width The width of the heightmap, in height samples. * @param {Number} options.height The height of the heightmap, in height samples. * @param {Number} options.skirtHeight The height of skirts to drape at the edges of the heightmap. - * @param {Rectangle} options.nativeRectangle An rectangle in the native coordinates of the heightmap's projection. For + * @param {Rectangle} options.nativeRectangle A rectangle in the native coordinates of the heightmap's projection. For * a heightmap with a geographic projection, this is degrees. For the web mercator * projection, this is meters. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. diff --git a/Source/Core/IndexDatatype.js b/Source/Core/IndexDatatype.js index 4aa9a3d73aad..2922700c3556 100644 --- a/Source/Core/IndexDatatype.js +++ b/Source/Core/IndexDatatype.js @@ -96,7 +96,7 @@ define([ * or Uint32Array depending on the number of vertices. * * @param {Number} numberOfVertices Number of vertices that the indices will reference. - * @param {Any} indicesLengthOrArray Passed through to the typed array constructor. + * @param {*} indicesLengthOrArray Passed through to the typed array constructor. * @returns {Uint16Array|Uint32Array} A Uint16Array or Uint32Array constructed with indicesLengthOrArray. * * @example diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 3bde8428fed3..495dd1bcb7b8 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -449,11 +449,10 @@ define([ start : root1, stop : root0 }; - } else { - // qw2 == product. Repeated roots (2 intersections). - var root = Math.sqrt(difference / w2); - return new Interval(root, root); } + // qw2 == product. Repeated roots (2 intersections). + var root = Math.sqrt(difference / w2); + return new Interval(root, root); } else if (q2 < 1.0) { // Inside ellipsoid (2 intersections). difference = q2 - 1.0; // Negatively valued. @@ -463,17 +462,16 @@ define([ discriminant = qw * qw - product; temp = -qw + Math.sqrt(discriminant); // Positively valued. return new Interval(0.0, temp / w2); - } else { - // q2 == 1.0. On ellipsoid. - if (qw < 0.0) { - // Looking inward. - w2 = Cartesian3.magnitudeSquared(w); - return new Interval(0.0, -qw / w2); - } - - // qw >= 0.0. Looking outward or tangent. - return undefined; } + // q2 == 1.0. On ellipsoid. + if (qw < 0.0) { + // Looking inward. + w2 = Cartesian3.magnitudeSquared(w); + return new Interval(0.0, -qw / w2); + } + + // qw >= 0.0. Looking outward or tangent. + return undefined; }; function addWithCancellationCheck(left, right, tolerance) { diff --git a/Source/Core/Intersections2D.js b/Source/Core/Intersections2D.js index 8da31a274455..1ad68bd56f8e 100644 --- a/Source/Core/Intersections2D.js +++ b/Source/Core/Intersections2D.js @@ -275,9 +275,8 @@ define([ result.y = l2; result.z = l3; return result; - } else { - return new Cartesian3(l1, l2, l3); } + return new Cartesian3(l1, l2, l3); }; return Intersections2D; diff --git a/Source/Core/JulianDate.js b/Source/Core/JulianDate.js index 838b5999ea0a..72d652b836b6 100644 --- a/Source/Core/JulianDate.js +++ b/Source/Core/JulianDate.js @@ -634,7 +634,7 @@ define([ } //>>includeEnd('debug'); - var gDate = JulianDate.toGregorianDate(julianDate, gDate); + var gDate = JulianDate.toGregorianDate(julianDate, gregorianDateScratch); var millisecondStr; if (!defined(precision) && gDate.millisecond !== 0) { diff --git a/Source/Core/ManagedArray.js b/Source/Core/ManagedArray.js new file mode 100644 index 000000000000..ec172bec83f1 --- /dev/null +++ b/Source/Core/ManagedArray.js @@ -0,0 +1,145 @@ +/*global define*/ +define([ + './defaultValue', + './defineProperties', + './Check' + ], function( + defaultValue, + defineProperties, + Check) { + 'use strict'; + + /** + * A wrapper around arrays so that the internal length of the array can be manually managed. + * + * @alias ManagedArray + * @constructor + * @private + * + * @param {Number} [length=0] The initial length of the array. + */ + function ManagedArray(length) { + length = defaultValue(length, 0); + this._array = new Array(length); + this._length = length; + } + + defineProperties(ManagedArray.prototype, { + /** + * Gets or sets the length of the array. + * If the set length is greater than the length of the internal array, the internal array is resized. + * + * @type Number + */ + length : { + get : function() { + return this._length; + }, + set : function(length) { + this._length = length; + if (length > this._array.length) { + this._array.length = length; + } + } + }, + + /** + * Gets the internal array. + * + * @type Array + * @readonly + */ + values : { + get : function() { + return this._array; + } + } + }); + + /** + * Gets the element at an index. + * + * @param {Number} index The index to get. + */ + ManagedArray.prototype.get = function(index) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.lessThan('index', index, this._array.length); + //>>includeEnd('debug'); + + return this._array[index]; + }; + + /** + * Sets the element at an index. Resizes the array if index is greater than the length of the array. + * + * @param {Number} index The index to set. + * @param {*} value The value to set at index. + */ + ManagedArray.prototype.set = function(index, value) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number('index', index); + //>>includeEnd('debug'); + + if (index >= this.length) { + this.length = index + 1; + } + this._array[index] = value; + }; + + /** + * Push an element into the array. + */ + ManagedArray.prototype.push = function(element) { + var index = this.length++; + this._array[index] = element; + }; + + /** + * Pop an element from the array. + * + * @returns {*} The last element in the array. + */ + ManagedArray.prototype.pop = function() { + return this._array[--this.length]; + }; + + /** + * Resize the internal array if length > _array.length. + * + * @param {Number} length The length. + */ + ManagedArray.prototype.reserve = function(length) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('length', length, 0); + //>>includeEnd('debug'); + + if (length > this._array.length) { + this._array.length = length; + } + }; + + /** + * Resize the array. + * + * @param {Number} length The length. + */ + ManagedArray.prototype.resize = function(length) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('length', length, 0); + //>>includeEnd('debug'); + + this.length = length; + }; + + /** + * Trim the internal array to the specified length. Defaults to the current length. + * + * @param {Number} [length] The length. + */ + ManagedArray.prototype.trim = function(length) { + length = defaultValue(length, this.length); + this._array.length = length; + }; + + return ManagedArray; +}); diff --git a/Source/Core/Matrix3.js b/Source/Core/Matrix3.js index 9cbf90c63002..21bb14a5483c 100644 --- a/Source/Core/Matrix3.js +++ b/Source/Core/Matrix3.js @@ -318,7 +318,7 @@ define([ var m10 = cosTheta * sinPsi; var m11 = cosPhi * cosPsi + sinPhi * sinTheta * sinPsi; - var m12 = -sinTheta * cosPhi + cosPhi * sinTheta * sinPsi; + var m12 = -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi; var m20 = -sinTheta; var m21 = sinPhi * cosTheta; diff --git a/Source/Core/Occluder.js b/Source/Core/Occluder.js index ce3658e72633..3fc9cc003616 100644 --- a/Source/Core/Occluder.js +++ b/Source/Core/Occluder.js @@ -174,7 +174,7 @@ define([ * var occluder = new Cesium.Occluder(littleSphere, cameraPosition); * var point = new Cesium.Cartesian3(0, 0, -3); * occluder.isPointVisible(point); //returns true - * + * * @see Occluder#computeVisibility */ Occluder.prototype.isPointVisible = function(occludee) { @@ -206,7 +206,7 @@ define([ * var occluder = new Cesium.Occluder(littleSphere, cameraPosition); * var bigSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -3), 1); * occluder.isBoundingSphereVisible(bigSphere); //returns true - * + * * @see Occluder#computeVisibility */ Occluder.prototype.isBoundingSphereVisible = function(occludee) { @@ -265,7 +265,7 @@ define([ * var cameraPosition = new Cesium.Cartesian3(0, 0, 0); * var occluder = new Cesium.Occluder(sphere1, cameraPosition); * occluder.computeVisibility(sphere2); //returns Visibility.NONE - * + * * @see Occluder#isVisible */ Occluder.prototype.computeVisibility = function(occludeeBS) { @@ -406,7 +406,7 @@ define([ var computeOccludeePointFromRectangleScratch = []; /** - * Computes a point that can be used as the occludee position to the visibility functions from an rectangle. + * Computes a point that can be used as the occludee position to the visibility functions from a rectangle. * * @param {Rectangle} rectangle The rectangle used to create a bounding sphere. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle. diff --git a/Source/Core/OrientedBoundingBox.js b/Source/Core/OrientedBoundingBox.js index 53e0f48e28ee..53e6441b4839 100644 --- a/Source/Core/OrientedBoundingBox.js +++ b/Source/Core/OrientedBoundingBox.js @@ -182,7 +182,7 @@ define([ v3 = Cartesian3.multiplyByScalar(v3, 0.5 * (l3 + u3), v3); var center = Cartesian3.add(v1, v2, result.center); - center = Cartesian3.add(center, v3, center); + Cartesian3.add(center, v3, center); var scale = scratchCartesian3; scale.x = u1 - l1; diff --git a/Source/Core/PixelFormat.js b/Source/Core/PixelFormat.js index ee8f9c48c12d..80f5bf07d4a6 100644 --- a/Source/Core/PixelFormat.js +++ b/Source/Core/PixelFormat.js @@ -1,8 +1,10 @@ /*global define*/ define([ + '../Renderer/PixelDatatype', './freezeObject', './WebGLConstants' ], function( + PixelDatatype, freezeObject, WebGLConstants) { 'use strict'; @@ -141,6 +143,26 @@ define([ */ RGB_ETC1 : WebGLConstants.COMPRESSED_RGB_ETC1_WEBGL, + /** + * @private + */ + componentsLength : function(pixelFormat) { + switch (pixelFormat) { + // Many GPUs store RGB as RGBA internally + // https://devtalk.nvidia.com/default/topic/699479/general-graphics-programming/rgb-auto-converted-to-rgba/post/4142379/#4142379 + case PixelFormat.RGB: + case PixelFormat.RGBA: + return 4; + case PixelFormat.LUMINANCE_ALPHA: + return 2; + case PixelFormat.ALPHA: + case PixelFormat.LUMINANCE: + return 1; + default: + return 1; + } + }, + /** * @private */ @@ -227,7 +249,7 @@ define([ /** * @private */ - compressedTextureSize : function(pixelFormat, width, height) { + compressedTextureSizeInBytes : function(pixelFormat, width, height) { switch (pixelFormat) { case PixelFormat.RGB_DXT1: case PixelFormat.RGBA_DXT1: @@ -249,6 +271,17 @@ define([ default: return 0; } + }, + + /** + * @private + */ + textureSizeInBytes : function(pixelFormat, pixelDatatype, width, height) { + var componentsLength = PixelFormat.componentsLength(pixelFormat); + if (PixelDatatype.isPacked(pixelDatatype)) { + componentsLength = 1; + } + return componentsLength * PixelDatatype.sizeInBytes(pixelDatatype) * width * height; } }; diff --git a/Source/Core/Plane.js b/Source/Core/Plane.js index 7cd653d3970e..1b659c7118b0 100644 --- a/Source/Core/Plane.js +++ b/Source/Core/Plane.js @@ -3,11 +3,13 @@ define([ './Cartesian3', './defined', './DeveloperError', + './Math', './freezeObject' ], function( Cartesian3, defined, DeveloperError, + CesiumMath, freezeObject) { 'use strict'; @@ -33,12 +35,17 @@ define([ * @example * // The plane x=0 * var plane = new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0); + * + * @exception {DeveloperError} Normal must be normalized */ function Plane(normal, distance) { //>>includeStart('debug', pragmas.debug); if (!defined(normal)) { throw new DeveloperError('normal is required.'); } + if (!CesiumMath.equalsEpsilon(Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6)) { + throw new DeveloperError('normal must be normalized.'); + } if (!defined(distance)) { throw new DeveloperError('distance is required.'); } @@ -75,6 +82,8 @@ define([ * var point = Cesium.Cartesian3.fromDegrees(-72.0, 40.0); * var normal = ellipsoid.geodeticSurfaceNormal(point); * var tangentPlane = Cesium.Plane.fromPointNormal(point, normal); + * + * @exception {DeveloperError} Normal must be normalized */ Plane.fromPointNormal = function(point, normal, result) { //>>includeStart('debug', pragmas.debug); @@ -84,6 +93,9 @@ define([ if (!defined(normal)) { throw new DeveloperError('normal is required.'); } + if (!CesiumMath.equalsEpsilon(Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6)) { + throw new DeveloperError('normal must be normalized.'); + } //>>includeEnd('debug'); var distance = -Cartesian3.dot(normal, point); @@ -104,6 +116,8 @@ define([ * @param {Cartesian4} coefficients The plane's normal (normalized). * @param {Plane} [result] The object onto which to store the result. * @returns {Plane} A new plane instance or the modified result parameter. + * + * @exception {DeveloperError} Normal must be normalized */ Plane.fromCartesian4 = function(coefficients, result) { //>>includeStart('debug', pragmas.debug); @@ -115,13 +129,18 @@ define([ var normal = Cartesian3.fromCartesian4(coefficients, scratchNormal); var distance = coefficients.w; + //>>includeStart('debug', pragmas.debug); + if (!CesiumMath.equalsEpsilon(Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6)) { + throw new DeveloperError('normal must be normalized.'); + } + //>>includeEnd('debug'); + if (!defined(result)) { return new Plane(normal, distance); - } else { - Cartesian3.clone(normal, result.normal); - result.distance = distance; - return result; } + Cartesian3.clone(normal, result.normal); + result.distance = distance; + return result; }; /** diff --git a/Source/Core/PointGeometry.js b/Source/Core/PointGeometry.js deleted file mode 100644 index a0336fee833a..000000000000 --- a/Source/Core/PointGeometry.js +++ /dev/null @@ -1,104 +0,0 @@ -/*global define*/ -define([ - './BoundingSphere', - './ComponentDatatype', - './defaultValue', - './defined', - './DeveloperError', - './Geometry', - './GeometryAttribute', - './GeometryAttributes', - './PrimitiveType' - ], function( - BoundingSphere, - ComponentDatatype, - defaultValue, - defined, - DeveloperError, - Geometry, - GeometryAttribute, - GeometryAttributes, - PrimitiveType) { - 'use strict'; - - /** - * Describes a collection of points made up of positions and colors. - * - * @alias PointGeometry - * @constructor - * - * @param {Object} options Object with the following properties: - * @param {TypedArray} options.positionsTypedArray The position values of the points stored in a typed array. Positions are stored as packed (x, y, z) floats. - * @param {TypedArray} options.colorsTypedArray The color values of the points stored in a typed array. Colors are stored as packed (r, g, b) unsigned bytes. - * @param {BoundingSphere} [options.boundingSphere] Optional precomputed bounding sphere to save computation time. - * - * @example - * // Create a PointGeometry with two points - * var points = new Cesium.PointGeometry({ - * positionsTypedArray : new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]), - * colorsTypedArray : new Uint8Array([255, 0, 0, 127, 127, 127]), - * boundingSphere : boundingSphere - * }); - * var geometry = Cesium.PointGeometry.createGeometry(points); - * - * @private - */ - function PointGeometry(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - //>>includeStart('debug', pragmas.debug); - if (!defined(options.positionsTypedArray)) { - throw new DeveloperError('options.positionsTypedArray is required.'); - } - if (!defined(options.colorsTypedArray)) { - throw new DeveloperError('options.colorsTypedArray is required'); - } - //>>includeEnd('debug'); - - this._positionsTypedArray = options.positionsTypedArray; - this._colorsTypedArray = options.colorsTypedArray; - this._boundingSphere = BoundingSphere.clone(options.boundingSphere); - - this._workerName = 'createPointGeometry'; - } - - /** - * Computes the geometric representation a point collection, including its vertices and a bounding sphere. - * - * @param {PointGeometry} pointGeometry A description of the points. - * @returns {Geometry} The computed vertices. - */ - PointGeometry.createGeometry = function(pointGeometry) { - var positions = pointGeometry._positionsTypedArray; - var componentByteLength = positions.byteLength / positions.length; - var componentDatatype = componentByteLength === 4 ? ComponentDatatype.FLOAT : ComponentDatatype.DOUBLE; - - var attributes = new GeometryAttributes(); - attributes.position = new GeometryAttribute({ - componentDatatype : componentDatatype, - componentsPerAttribute : 3, - values : positions - }); - - attributes.color = new GeometryAttribute({ - componentDatatype : ComponentDatatype.UNSIGNED_BYTE, - componentsPerAttribute : 3, - values : pointGeometry._colorsTypedArray, - normalize : true - }); - - // User provided bounding sphere to save computation time. - var boundingSphere = pointGeometry._boundingSphere; - if (!defined(boundingSphere)) { - boundingSphere = BoundingSphere.fromVertices(positions); - } - - return new Geometry({ - attributes : attributes, - primitiveType : PrimitiveType.POINTS, - boundingSphere : boundingSphere - }); - }; - - return PointGeometry; -}); diff --git a/Source/Core/PolylinePipeline.js b/Source/Core/PolylinePipeline.js index 51f642a88fd8..96574e817fdb 100644 --- a/Source/Core/PolylinePipeline.js +++ b/Source/Core/PolylinePipeline.js @@ -51,9 +51,9 @@ define([ var wrapLongitudeInversMatrix = new Matrix4(); var wrapLongitudeOrigin = new Cartesian3(); var wrapLongitudeXZNormal = new Cartesian3(); - var wrapLongitudeXZPlane = new Plane(Cartesian3.ZERO, 0.0); + var wrapLongitudeXZPlane = new Plane(Cartesian3.UNIT_X, 0.0); var wrapLongitudeYZNormal = new Cartesian3(); - var wrapLongitudeYZPlane = new Plane(Cartesian3.ZERO, 0.0); + var wrapLongitudeYZPlane = new Plane(Cartesian3.UNIT_X, 0.0); var wrapLongitudeIntersection = new Cartesian3(); var wrapLongitudeOffset = new Cartesian3(); @@ -152,9 +152,9 @@ define([ var inverseModelMatrix = Matrix4.inverseTransformation(modelMatrix, wrapLongitudeInversMatrix); var origin = Matrix4.multiplyByPoint(inverseModelMatrix, Cartesian3.ZERO, wrapLongitudeOrigin); - var xzNormal = Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_Y, wrapLongitudeXZNormal); + var xzNormal = Cartesian3.normalize(Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_Y, wrapLongitudeXZNormal), wrapLongitudeXZNormal); var xzPlane = Plane.fromPointNormal(origin, xzNormal, wrapLongitudeXZPlane); - var yzNormal = Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_X, wrapLongitudeYZNormal); + var yzNormal = Cartesian3.normalize(Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_X, wrapLongitudeYZNormal), wrapLongitudeYZNormal); var yzPlane = Plane.fromPointNormal(origin, yzNormal, wrapLongitudeYZPlane); var count = 1; diff --git a/Source/Core/PolylineVolumeGeometryLibrary.js b/Source/Core/PolylineVolumeGeometryLibrary.js index f12510318b16..a6429bc7a76f 100644 --- a/Source/Core/PolylineVolumeGeometryLibrary.js +++ b/Source/Core/PolylineVolumeGeometryLibrary.js @@ -86,6 +86,9 @@ define([ return heights; } + var nextScratch = new Cartesian3(); + var prevScratch = new Cartesian3(); + function computeRotationAngle(start, end, position, ellipsoid) { var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid); var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, start, nextScratch), nextScratch); @@ -252,8 +255,6 @@ define([ return cleanedPositions; }; - var nextScratch = new Cartesian3(); - var prevScratch = new Cartesian3(); PolylineVolumeGeometryLibrary.angleIsGreaterThanPi = function(forward, backward, position, ellipsoid) { var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid); var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, forward, nextScratch), nextScratch); diff --git a/Source/Core/QuantizedMeshTerrainData.js b/Source/Core/QuantizedMeshTerrainData.js index d1b8323d96e6..8459fe4e6776 100644 --- a/Source/Core/QuantizedMeshTerrainData.js +++ b/Source/Core/QuantizedMeshTerrainData.js @@ -77,6 +77,7 @@ define([ * otherwise, false. * @param {Uint8Array} [options.encodedNormals] The buffer containing per vertex normals, encoded using 'oct' encoding * @param {Uint8Array} [options.waterMask] The buffer containing the watermask. + * @param {Credit[]} [options.credits] Array of credits for this tile. * * * @example @@ -165,6 +166,7 @@ define([ this._boundingSphere = options.boundingSphere; this._orientedBoundingBox = options.orientedBoundingBox; this._horizonOcclusionPoint = options.horizonOcclusionPoint; + this._credits = options.credits; var vertexCount = this._quantizedVertices.length / 3; var uValues = this._uValues = this._quantizedVertices.subarray(0, vertexCount); @@ -199,6 +201,16 @@ define([ } defineProperties(QuantizedMeshTerrainData.prototype, { + /** + * An array of credits for this tile. + * @memberof QuantizedMeshTerrainData.prototype + * @type {Credit[]} + */ + credits : { + get : function() { + return this._credits; + } + }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. @@ -227,9 +239,8 @@ define([ if (needsSort) { arrayScratch.sort(sortFunction); return IndexDatatype.createTypedArray(vertexCount, arrayScratch); - } else { - return indices; } + return indices; } var createMeshTaskProcessor = new TaskProcessor('createVerticesFromQuantizedTerrainMesh'); @@ -483,7 +494,7 @@ define([ return interpolateHeight(this, u, v); } - interpolateMeshHeight(this, u, v); + return interpolateMeshHeight(this, u, v); }; var texCoordScratch0 = new Cartesian2(); diff --git a/Source/Core/QuarticRealPolynomial.js b/Source/Core/QuarticRealPolynomial.js index 145d6856016f..704207eca111 100644 --- a/Source/Core/QuarticRealPolynomial.js +++ b/Source/Core/QuarticRealPolynomial.js @@ -231,9 +231,8 @@ define([ return [roots1[0], roots2[0], roots2[1], roots1[1]]; } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) { return [roots2[0], roots1[0], roots2[1], roots1[1]]; - } else { - return [roots1[0], roots2[0], roots1[1], roots2[1]]; } + return [roots1[0], roots2[0], roots1[1], roots2[1]]; } return roots1; } diff --git a/Source/Core/Quaternion.js b/Source/Core/Quaternion.js index 30042c4873c1..070f82866a8a 100644 --- a/Source/Core/Quaternion.js +++ b/Source/Core/Quaternion.js @@ -183,9 +183,7 @@ define([ * negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about * the positive x axis. * - * @param {Number} heading The heading angle in radians. - * @param {Number} pitch The pitch angle in radians. - * @param {Number} roll The roll angle in radians. + * @param {HeadingPitchRoll} headingPitchRoll The rotation expressed as a heading, pitch and roll. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided. */ diff --git a/Source/Core/Rectangle.js b/Source/Core/Rectangle.js index fcbfeace3509..db03159872ba 100644 --- a/Source/Core/Rectangle.js +++ b/Source/Core/Rectangle.js @@ -177,7 +177,7 @@ define([ }; /** - * Creates an rectangle given the boundary longitude and latitude in degrees. + * Creates a rectangle given the boundary longitude and latitude in degrees. * * @param {Number} [west=0.0] The westernmost longitude in degrees in the range [-180.0, 180.0]. * @param {Number} [south=0.0] The southernmost latitude in degrees in the range [-90.0, 90.0]. @@ -208,7 +208,7 @@ define([ }; /** - * Creates an rectangle given the boundary longitude and latitude in radians. + * Creates a rectangle given the boundary longitude and latitude in radians. * * @param {Number} [west=0.0] The westernmost longitude in radians in the range [-Math.PI, Math.PI]. * @param {Number} [south=0.0] The southernmost latitude in radians in the range [-Math.PI/2, Math.PI/2]. @@ -299,6 +299,7 @@ define([ //>>includeStart('debug', pragmas.debug); Check.defined('cartesians', cartesians); //>>includeEnd('debug'); + ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var west = Number.MAX_VALUE; var east = -Number.MAX_VALUE; @@ -343,7 +344,7 @@ define([ }; /** - * Duplicates an Rectangle. + * Duplicates a Rectangle. * * @param {Rectangle} rectangle The rectangle to clone. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. @@ -426,7 +427,7 @@ define([ }; /** - * Checks an Rectangle's properties and throws if they are not in valid ranges. + * Checks a Rectangle's properties and throws if they are not in valid ranges. * * @param {Rectangle} rectangle The rectangle to validate * @@ -458,7 +459,7 @@ define([ }; /** - * Computes the southwest corner of an rectangle. + * Computes the southwest corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @param {Cartographic} [result] The object onto which to store the result. @@ -479,7 +480,7 @@ define([ }; /** - * Computes the northwest corner of an rectangle. + * Computes the northwest corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @param {Cartographic} [result] The object onto which to store the result. @@ -500,7 +501,7 @@ define([ }; /** - * Computes the northeast corner of an rectangle. + * Computes the northeast corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @param {Cartographic} [result] The object onto which to store the result. @@ -521,7 +522,7 @@ define([ }; /** - * Computes the southeast corner of an rectangle. + * Computes the southeast corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @param {Cartographic} [result] The object onto which to store the result. @@ -542,7 +543,7 @@ define([ }; /** - * Computes the center of an rectangle. + * Computes the center of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the center * @param {Cartographic} [result] The object onto which to store the result. @@ -776,7 +777,7 @@ define([ var subsampleLlaScratch = new Cartographic(); /** - * Samples an rectangle so that it includes a list of Cartesian points suitable for passing to + * Samples a rectangle so that it includes a list of Cartesian points suitable for passing to * {@link BoundingSphere#fromPoints}. Sampling is necessary to account * for rectangles that cover the poles or cross the equator. * diff --git a/Source/Core/RectangleGeometry.js b/Source/Core/RectangleGeometry.js index 034e8d4fa45d..3eb2a510a391 100644 --- a/Source/Core/RectangleGeometry.js +++ b/Source/Core/RectangleGeometry.js @@ -624,7 +624,7 @@ define([ * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Rectangle.html|Cesium Sandcastle Rectangle Demo} * * @example - * // 1. create an rectangle + * // 1. create a rectangle * var rectangle = new Cesium.RectangleGeometry({ * ellipsoid : Cesium.Ellipsoid.WGS84, * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0), @@ -809,7 +809,7 @@ define([ var quaternionScratch = new Quaternion(); var centerScratch = new Cartographic(); /** - * Computes the geometric representation of an rectangle, including its vertices, indices, and a bounding sphere. + * Computes the geometric representation of a rectangle, including its vertices, indices, and a bounding sphere. * * @param {RectangleGeometry} rectangleGeometry A description of the rectangle. * @returns {Geometry|undefined} The computed vertices and indices. diff --git a/Source/Core/RectangleOutlineGeometry.js b/Source/Core/RectangleOutlineGeometry.js index 1d6cc7ce96f1..785a381b42ce 100644 --- a/Source/Core/RectangleOutlineGeometry.js +++ b/Source/Core/RectangleOutlineGeometry.js @@ -320,7 +320,7 @@ define([ var nwScratch = new Cartographic(); /** - * Computes the geometric representation of an outline of an rectangle, including its vertices, indices, and a bounding sphere. + * Computes the geometric representation of an outline of a rectangle, including its vertices, indices, and a bounding sphere. * * @param {RectangleOutlineGeometry} rectangleGeometry A description of the rectangle outline. * @returns {Geometry|undefined} The computed vertices and indices. diff --git a/Source/Core/Request.js b/Source/Core/Request.js new file mode 100644 index 000000000000..42ed344b1c50 --- /dev/null +++ b/Source/Core/Request.js @@ -0,0 +1,174 @@ +/*global define*/ +define([ + './defaultValue', + './defined', + './defineProperties', + './RequestState', + './RequestType' + ], function( + defaultValue, + defined, + defineProperties, + RequestState, + RequestType) { + 'use strict'; + + /** + * Stores information for making a request. In general this does not need to be constructed directly. + * + * @alias Request + * @constructor + * + * @param {Object} [options] An object with the following properties: + * @param {Boolean} [options.url] The url to request. + * @param {Request~RequestCallback} [options.requestFunction] The function that makes the actual data request. + * @param {Request~CancelCallback} [options.cancelFunction] The function that is called when the request is cancelled. + * @param {Request~PriorityCallback} [options.priorityFunction] The function that is called to update the request's priority, which occurs once per frame. + * @param {Number} [options.priority=0.0] The initial priority of the request. + * @param {Boolean} [options.throttle=false] Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the request will be throttled and sent based on priority. + * @param {Boolean} [options.throttleByServer=false] Whether to throttle the request by server. + * @param {RequestType} [options.type=RequestType.OTHER] The type of request. + */ + function Request(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var throttleByServer = defaultValue(options.throttleByServer, false); + var throttle = throttleByServer || defaultValue(options.throttle, false); + + /** + * The URL to request. + * + * @type {String} + */ + this.url = options.url; + + /** + * The function that makes the actual data request. + * + * @type {Request~RequestCallback} + */ + this.requestFunction = options.requestFunction; + + /** + * The function that is called when the request is cancelled. + * + * @type {Request~CancelCallback} + */ + this.cancelFunction = options.cancelFunction; + + /** + * The function that is called to update the request's priority, which occurs once per frame. + * + * @type {Request~PriorityCallback} + */ + this.priorityFunction = options.priorityFunction; + + /** + * Priority is a unit-less value where lower values represent higher priority. + * For world-based objects, this is usually the distance from the camera. + * A request that does not have a priority function defaults to a priority of 0. + * + * If priorityFunction is defined, this value is updated every frame with the result of that call. + * + * @type {Number} + * @default 0.0 + */ + this.priority = defaultValue(options.priority, 0.0); + + /** + * Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the + * request will be throttled and sent based on priority. + * + * @type {Boolean} + * @readonly + * + * @default false + */ + this.throttle = throttle; + + /** + * Whether to throttle the request by server. Browsers typically support about 6-8 parallel connections + * for HTTP/1 servers, and an unlimited amount of connections for HTTP/2 servers. Setting this value + * to true is preferable for requests going through HTTP/1 servers. + * + * @type {Boolean} + * @readonly + * + * @default false + */ + this.throttleByServer = throttleByServer; + + /** + * Type of request. + * + * @type {RequestType} + * @readonly + * + * @default RequestType.OTHER + */ + this.type = defaultValue(options.type, RequestType.OTHER); + + /** + * A key used to identify the server that a request is going to. It is derived from the url's authority and scheme. + * + * @type {String} + * + * @private + */ + this.serverKey = undefined; + + /** + * The current state of the request. + * + * @type {RequestState} + * @readonly + */ + this.state = RequestState.UNISSUED; + + /** + * The requests's deferred promise. + * + * @type {Object} + * + * @private + */ + this.deferred = undefined; + + /** + * Whether the request was explicitly cancelled. + * + * @type {Boolean} + * + * @private + */ + this.cancelled = false; + } + + /** + * Mark the request as cancelled. + * + * @private + */ + Request.prototype.cancel = function() { + this.cancelled = true; + }; + + /** + * The function that makes the actual data request. + * @callback Request~RequestCallback + * @returns {Promise} A promise for the requested data. + */ + + /** + * The function that is called when the request is cancelled. + * @callback Request~CancelCallback + */ + + /** + * The function that is called to update the request's priority, which occurs once per frame. + * @callback Request~PriorityCallback + * @returns {Number} The updated priority value. + */ + + return Request; +}); diff --git a/Source/Core/RequestScheduler.js b/Source/Core/RequestScheduler.js new file mode 100644 index 000000000000..264838a5b826 --- /dev/null +++ b/Source/Core/RequestScheduler.js @@ -0,0 +1,424 @@ +/*global define*/ +define([ + './clone', + './Check', + './defined', + './defineProperties', + './Heap', + './isBlobUri', + './isDataUri', + './RequestState', + '../ThirdParty/Uri', + '../ThirdParty/when' + ], function( + clone, + Check, + defined, + defineProperties, + Heap, + isBlobUri, + isDataUri, + RequestState, + Uri, + when) { + 'use strict'; + + function sortRequests(a, b) { + return a.priority - b.priority; + } + + var statistics = { + numberOfAttemptedRequests : 0, + numberOfActiveRequests : 0, + numberOfCancelledRequests : 0, + numberOfCancelledActiveRequests : 0, + numberOfFailedRequests : 0, + numberOfActiveRequestsEver : 0 + }; + + var priorityHeapLength = 20; + var requestHeap = new Heap({ + comparator : sortRequests + }); + requestHeap.maximumLength = priorityHeapLength; + requestHeap.reserve(priorityHeapLength); + + var activeRequests = []; + var numberOfActiveRequestsByServer = {}; + + var pageUri = typeof document !== 'undefined' ? new Uri(document.location.href) : new Uri(); + + /** + * Tracks the number of active requests and prioritizes incoming requests. + * + * @exports RequestScheduler + * + * @private + */ + function RequestScheduler() { + } + + /** + * The maximum number of simultaneous active requests. Un-throttled requests do not observe this limit. + * @type {Number} + * @default 50 + */ + RequestScheduler.maximumRequests = 50; + + /** + * The maximum number of simultaneous active requests per server. Un-throttled requests do not observe this limit. + * @type {Number} + * @default 6 + */ + RequestScheduler.maximumRequestsPerServer = 6; + + /** + * Specifies if the request scheduler should throttle incoming requests, or let the browser queue requests under its control. + * @type {Boolean} + * @default true + */ + RequestScheduler.throttleRequests = true; + + /** + * When true, log statistics to the console every frame + * @type {Boolean} + * @default false + */ + RequestScheduler.debugShowStatistics = false; + + defineProperties(RequestScheduler, { + /** + * Returns the statistics used by the request scheduler. + * + * @memberof RequestScheduler + * + * @type Object + * @readonly + */ + statistics : { + get : function() { + return statistics; + } + }, + + /** + * The maximum size of the priority heap. This limits the number of requests that are sorted by priority. Only applies to requests that are not yet active. + * + * @memberof RequestScheduler + * + * @type {Number} + * @default 20 + */ + priorityHeapLength : { + get : function() { + return priorityHeapLength; + }, + set : function(value) { + // If the new length shrinks the heap, need to cancel some of the requests. + // Since this value is not intended to be tweaked regularly it is fine to just cancel the high priority requests. + if (value < priorityHeapLength) { + while (requestHeap.length > value) { + var request = requestHeap.pop(); + cancelRequest(request); + } + } + priorityHeapLength = value; + requestHeap.maximumLength = value; + requestHeap.reserve(value); + } + } + }); + + function updatePriority(request) { + if (defined(request.priorityFunction)) { + request.priority = request.priorityFunction(); + } + } + + function serverHasOpenSlots(serverKey) { + return numberOfActiveRequestsByServer[serverKey] < RequestScheduler.maximumRequestsPerServer; + } + + function issueRequest(request) { + if (request.state === RequestState.UNISSUED) { + request.state = RequestState.ISSUED; + request.deferred = when.defer(); + } + return request.deferred.promise; + } + + function getRequestReceivedFunction(request) { + return function(results) { + if (request.state === RequestState.CANCELLED) { + // If the data request comes back but the request is cancelled, ignore it. + return; + } + --statistics.numberOfActiveRequests; + --numberOfActiveRequestsByServer[request.serverKey]; + request.state = RequestState.RECEIVED; + request.deferred.resolve(results); + }; + } + + function getRequestFailedFunction(request) { + return function(error) { + if (request.state === RequestState.CANCELLED) { + // If the data request comes back but the request is cancelled, ignore it. + return; + } + ++statistics.numberOfFailedRequests; + --statistics.numberOfActiveRequests; + --numberOfActiveRequestsByServer[request.serverKey]; + request.state = RequestState.FAILED; + request.deferred.reject(error); + }; + } + + function startRequest(request) { + var promise = issueRequest(request); + request.state = RequestState.ACTIVE; + activeRequests.push(request); + ++statistics.numberOfActiveRequests; + ++statistics.numberOfActiveRequestsEver; + ++numberOfActiveRequestsByServer[request.serverKey]; + request.requestFunction().then(getRequestReceivedFunction(request)).otherwise(getRequestFailedFunction(request)); + return promise; + } + + function cancelRequest(request) { + var active = request.state === RequestState.ACTIVE; + request.state = RequestState.CANCELLED; + ++statistics.numberOfCancelledRequests; + request.deferred.reject(); + + if (active) { + --statistics.numberOfActiveRequests; + --numberOfActiveRequestsByServer[request.serverKey]; + ++statistics.numberOfCancelledActiveRequests; + } + + if (defined(request.cancelFunction)) { + request.cancelFunction(); + } + } + + /** + * Sort requests by priority and start requests. + */ + RequestScheduler.update = function() { + var i; + var request; + + // Loop over all active requests. Cancelled, failed, or received requests are removed from the array to make room for new requests. + var removeCount = 0; + var activeLength = activeRequests.length; + for (i = 0; i < activeLength; ++i) { + request = activeRequests[i]; + if (request.cancelled) { + // Request was explicitly cancelled + cancelRequest(request); + } + if (request.state !== RequestState.ACTIVE) { + // Request is no longer active, remove from array + ++removeCount; + continue; + } + if (removeCount > 0) { + // Shift back to fill in vacated slots from completed requests + activeRequests[i - removeCount] = request; + } + } + activeRequests.length -= removeCount; + + // Update priority of issued requests and resort the heap + var issuedRequests = requestHeap.internalArray; + var issuedLength = requestHeap.length; + for (i = 0; i < issuedLength; ++i) { + updatePriority(issuedRequests[i]); + } + requestHeap.resort(); + + // Get the number of open slots and fill with the highest priority requests. + // Un-throttled requests are automatically added to activeRequests, so activeRequests.length may exceed maximumRequests + var openSlots = Math.max(RequestScheduler.maximumRequests - activeRequests.length, 0); + var filledSlots = 0; + while (filledSlots < openSlots && requestHeap.length > 0) { + // Loop until all open slots are filled or the heap becomes empty + request = requestHeap.pop(); + if (request.cancelled) { + // Request was explicitly cancelled + cancelRequest(request); + continue; + } + + if (request.throttleByServer && !serverHasOpenSlots(request.serverKey)) { + // Open slots are available, but the request is throttled by its server. Cancel and try again later. + cancelRequest(request); + continue; + } + + startRequest(request); + ++filledSlots; + } + + updateStatistics(); + }; + + /** + * Get the server key from a given url. + * + * @param {String} url The url. + * @returns {String} The server key. + */ + RequestScheduler.getServerKey = function(url) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('url', url); + //>>includeEnd('debug'); + + var uri = new Uri(url).resolve(pageUri); + uri.normalize(); + var serverKey = uri.authority; + if (!/:/.test(serverKey)) { + // If the authority does not contain a port number, add port 443 for https or port 80 for http + serverKey = serverKey + ':' + (uri.scheme === 'https' ? '443' : '80'); + } + + var length = numberOfActiveRequestsByServer[serverKey]; + if (!defined(length)) { + numberOfActiveRequestsByServer[serverKey] = 0; + } + + return serverKey; + }; + + /** + * Issue a request. If request.throttle is false, the request is sent immediately. Otherwise the request will be + * queued and sorted by priority before being sent. + * + * @param {Request} request The request object. + * + * @returns {Promise|undefined} A Promise for the requested data, or undefined if this request does not have high enough priority to be issued. + */ + RequestScheduler.request = function(request) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('request', request); + Check.typeOf.string('request.url', request.url); + Check.typeOf.func('request.requestFunction', request.requestFunction); + //>>includeEnd('debug'); + + if (isDataUri(request.url) || isBlobUri(request.url)) { + request.state = RequestState.RECEIVED; + return request.requestFunction(); + } + + ++statistics.numberOfAttemptedRequests; + + if (!defined(request.serverKey)) { + request.serverKey = RequestScheduler.getServerKey(request.url); + } + + if (!RequestScheduler.throttleRequests || !request.throttle) { + return startRequest(request); + } + + if (activeRequests.length >= RequestScheduler.maximumRequests) { + // Active requests are saturated. Try again later. + return undefined; + } + + if (request.throttleByServer && !serverHasOpenSlots(request.serverKey)) { + // Server is saturated. Try again later. + return undefined; + } + + // Insert into the priority heap and see if a request was bumped off. If this request is the lowest + // priority it will be returned. + updatePriority(request); + var removedRequest = requestHeap.insert(request); + + if (defined(removedRequest)) { + if (removedRequest === request) { + // Request does not have high enough priority to be issued + return undefined; + } + // A previously issued request has been bumped off the priority heap, so cancel it + cancelRequest(removedRequest); + } + + return issueRequest(request); + }; + + function clearStatistics() { + statistics.numberOfAttemptedRequests = 0; + statistics.numberOfCancelledRequests = 0; + statistics.numberOfCancelledActiveRequests = 0; + } + + function updateStatistics() { + if (!RequestScheduler.debugShowStatistics) { + return; + } + + if (statistics.numberOfAttemptedRequests > 0) { + console.log('Number of attempted requests: ' + statistics.numberOfAttemptedRequests); + } + if (statistics.numberOfActiveRequests > 0) { + console.log('Number of active requests: ' + statistics.numberOfActiveRequests); + } + if (statistics.numberOfCancelledRequests > 0) { + console.log('Number of cancelled requests: ' + statistics.numberOfCancelledRequests); + } + if (statistics.numberOfCancelledActiveRequests > 0) { + console.log('Number of cancelled active requests: ' + statistics.numberOfCancelledActiveRequests); + } + if (statistics.numberOfFailedRequests > 0) { + console.log('Number of failed requests: ' + statistics.numberOfFailedRequests); + } + + clearStatistics(); + } + + /** + * For testing only. Clears any requests that may not have completed from previous tests. + * + * @private + */ + RequestScheduler.clearForSpecs = function() { + while (requestHeap.length > 0) { + var request = requestHeap.pop(); + cancelRequest(request); + } + var length = activeRequests.length; + for (var i = 0; i < length; ++i) { + cancelRequest(activeRequests[i]); + } + activeRequests.length = 0; + numberOfActiveRequestsByServer = {}; + + // Clear stats + statistics.numberOfAttemptedRequests = 0; + statistics.numberOfActiveRequests = 0; + statistics.numberOfCancelledRequests = 0; + statistics.numberOfCancelledActiveRequests = 0; + statistics.numberOfFailedRequests = 0; + statistics.numberOfActiveRequestsEver = 0; + }; + + /** + * For testing only. + * + * @private + */ + RequestScheduler.numberOfActiveRequestsByServer = function(serverKey) { + return numberOfActiveRequestsByServer[serverKey]; + }; + + /** + * For testing only. + * + * @private + */ + RequestScheduler.requestHeap = requestHeap; + + return RequestScheduler; +}); diff --git a/Source/Core/RequestState.js b/Source/Core/RequestState.js new file mode 100644 index 000000000000..23498ef57fc7 --- /dev/null +++ b/Source/Core/RequestState.js @@ -0,0 +1,64 @@ +/*global define*/ +define([ + '../Core/freezeObject' +], function( + freezeObject) { + 'use strict'; + + /** + * State of the request. + * + * @exports RequestState + */ + var RequestState = { + /** + * Initial unissued state. + * + * @type Number + * @constant + */ + UNISSUED : 0, + + /** + * Issued but not yet active. Will become active when open slots are available. + * + * @type Number + * @constant + */ + ISSUED : 1, + + /** + * Actual http request has been sent. + * + * @type Number + * @constant + */ + ACTIVE : 2, + + /** + * Request completed successfully. + * + * @type Number + * @constant + */ + RECEIVED : 3, + + /** + * Request was cancelled, either explicitly or automatically because of low priority. + * + * @type Number + * @constant + */ + CANCELLED : 4, + + /** + * Request failed. + * + * @type Number + * @constant + */ + FAILED : 5 + }; + + return freezeObject(RequestState); +}); diff --git a/Source/Core/RequestType.js b/Source/Core/RequestType.js new file mode 100644 index 000000000000..d01348b54d53 --- /dev/null +++ b/Source/Core/RequestType.js @@ -0,0 +1,48 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * An enum identifying the type of request. Used for finer grained logging and priority sorting. + * + * @exports RequestType + */ + var RequestType = { + /** + * Terrain request. + * + * @type Number + * @constant + */ + TERRAIN : 0, + + /** + * Imagery request. + * + * @type Number + * @constant + */ + IMAGERY : 1, + + /** + * 3D Tiles request. + * + * @type Number + * @constant + */ + TILES3D : 2, + + /** + * Other request. + * + * @type Number + * @constant + */ + OTHER : 3 + }; + + return freezeObject(RequestType); +}); diff --git a/Source/Core/Simon1994PlanetaryPositions.js b/Source/Core/Simon1994PlanetaryPositions.js index 1a824012333e..3962010540c2 100644 --- a/Source/Core/Simon1994PlanetaryPositions.js +++ b/Source/Core/Simon1994PlanetaryPositions.js @@ -135,9 +135,8 @@ define([ return 'Elliptical'; } else if (eccentricity <= 1.0 + tolerance) { return 'Parabolic'; - } else { - return 'Hyperbolic'; } + return 'Hyperbolic'; } // Calculates the true anomaly given the mean anomaly and the eccentricity. diff --git a/Source/Core/TerrainData.js b/Source/Core/TerrainData.js index 1e69d02c8ba5..d9e6e17f3475 100644 --- a/Source/Core/TerrainData.js +++ b/Source/Core/TerrainData.js @@ -22,6 +22,14 @@ define([ } defineProperties(TerrainData.prototype, { + /** + * An array of credits for this tile. + * @memberof TerrainData.prototype + * @type {Credit[]} + */ + credits : { + get : DeveloperError.throwInstantiationError + }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. diff --git a/Source/Core/TerrainEncoding.js b/Source/Core/TerrainEncoding.js index 9b91b0f1c9e9..744ba8fdad65 100644 --- a/Source/Core/TerrainEncoding.js +++ b/Source/Core/TerrainEncoding.js @@ -354,22 +354,20 @@ define([ offsetInBytes : numCompressed0 * sizeInBytes, strideInBytes : stride }]; - } else { - return [{ - index : attributes.compressed0, - vertexBuffer : buffer, - componentDatatype : datatype, - componentsPerAttribute : numCompressed0 - }]; } + return [{ + index : attributes.compressed0, + vertexBuffer : buffer, + componentDatatype : datatype, + componentsPerAttribute : numCompressed0 + }]; }; TerrainEncoding.prototype.getAttributeLocations = function() { if (this.quantization === TerrainQuantization.NONE) { return attributesNone; - } else { - return attributes; } + return attributes; }; TerrainEncoding.clone = function(encoding, result) { diff --git a/Source/Core/TerrainProvider.js b/Source/Core/TerrainProvider.js index 5163bae2986f..6bdc2e000016 100644 --- a/Source/Core/TerrainProvider.js +++ b/Source/Core/TerrainProvider.js @@ -21,6 +21,7 @@ define([ * * @see EllipsoidTerrainProvider * @see CesiumTerrainProvider + * @see VRTheWorldTerrainProvider */ function TerrainProvider() { DeveloperError.throwInstantiationError(); @@ -197,9 +198,8 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited, - * or false if the request should be initiated regardless of the number of requests - * already in progress. + * @param {Request} [request] The request object. Intended for internal use only. + * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. diff --git a/Source/Core/TileAvailability.js b/Source/Core/TileAvailability.js index 066ee2639b31..69ce609fe5f4 100644 --- a/Source/Core/TileAvailability.js +++ b/Source/Core/TileAvailability.js @@ -313,7 +313,8 @@ define([ var maxLevel = 0; // Find the deepest quadtree node containing this point. - while (true) { + var found = false; + while (!found) { var nw = node._nw && rectangleContainsPosition(node._nw.extent, position); var ne = node._ne && rectangleContainsPosition(node._ne.extent, position); var sw = node._sw && rectangleContainsPosition(node._sw.extent, position); @@ -345,7 +346,7 @@ define([ } else if (se) { node = node._se; } else { - break; + found = true; } } diff --git a/Source/Core/TilingScheme.js b/Source/Core/TilingScheme.js index f5e203ad9c5d..1e98a22e5703 100644 --- a/Source/Core/TilingScheme.js +++ b/Source/Core/TilingScheme.js @@ -75,7 +75,7 @@ define([ TilingScheme.prototype.getNumberOfYTilesAtLevel = DeveloperError.throwInstantiationError; /** - * Transforms an rectangle specified in geodetic radians to the native coordinate system + * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * @function * @@ -88,7 +88,7 @@ define([ TilingScheme.prototype.rectangleToNativeRectangle = DeveloperError.throwInstantiationError; /** - * Converts tile x, y coordinates and level to an rectangle expressed in the native coordinates + * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * @function * diff --git a/Source/Core/Transforms.js b/Source/Core/Transforms.js index df5684d91a93..27edaabc93ec 100644 --- a/Source/Core/Transforms.js +++ b/Source/Core/Transforms.js @@ -794,14 +794,17 @@ define([ return result; }; + var swizzleMatrix = new Matrix4( + 0.0, 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + var scratchCartographic = new Cartographic(); var scratchCartesian3Projection = new Cartesian3(); - var scratchCartesian3 = new Cartesian3(); - var scratchCartesian4Origin = new Cartesian4(); - var scratchCartesian4NewOrigin = new Cartesian4(); - var scratchCartesian4NewXAxis = new Cartesian4(); - var scratchCartesian4NewYAxis = new Cartesian4(); - var scratchCartesian4NewZAxis = new Cartesian4(); + var scratchCenter = new Cartesian3(); + var scratchRotation = new Matrix3(); var scratchFromENU = new Matrix4(); var scratchToENU = new Matrix4(); @@ -821,60 +824,25 @@ define([ } //>>includeEnd('debug'); + var rtcCenter = Matrix4.getTranslation(matrix, scratchCenter); var ellipsoid = projection.ellipsoid; - var origin = Matrix4.getColumn(matrix, 3, scratchCartesian4Origin); - var cartographic = ellipsoid.cartesianToCartographic(origin, scratchCartographic); + // Get the 2D Center + var cartographic = ellipsoid.cartesianToCartographic(rtcCenter, scratchCartographic); + var projectedPosition = projection.project(cartographic, scratchCartesian3Projection); + Cartesian3.fromElements(projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition); - var fromENU = Transforms.eastNorthUpToFixedFrame(origin, ellipsoid, scratchFromENU); + // Assuming the instance are positioned in WGS84, invert the WGS84 transform to get the local transform and then convert to 2D + var fromENU = Transforms.eastNorthUpToFixedFrame(rtcCenter, ellipsoid, scratchFromENU); var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); - - var projectedPosition = projection.project(cartographic, scratchCartesian3Projection); - var newOrigin = scratchCartesian4NewOrigin; - newOrigin.x = projectedPosition.z; - newOrigin.y = projectedPosition.x; - newOrigin.z = projectedPosition.y; - newOrigin.w = 1.0; - - var xAxis = Matrix4.getColumn(matrix, 0, scratchCartesian3); - var xScale = Cartesian3.magnitude(xAxis); - var newXAxis = Matrix4.multiplyByVector(toENU, xAxis, scratchCartesian4NewXAxis); - Cartesian4.fromElements(newXAxis.z, newXAxis.x, newXAxis.y, 0.0, newXAxis); - - var yAxis = Matrix4.getColumn(matrix, 1, scratchCartesian3); - var yScale = Cartesian3.magnitude(yAxis); - var newYAxis = Matrix4.multiplyByVector(toENU, yAxis, scratchCartesian4NewYAxis); - Cartesian4.fromElements(newYAxis.z, newYAxis.x, newYAxis.y, 0.0, newYAxis); - - var zAxis = Matrix4.getColumn(matrix, 2, scratchCartesian3); - var zScale = Cartesian3.magnitude(zAxis); - - var newZAxis = scratchCartesian4NewZAxis; - Cartesian3.cross(newXAxis, newYAxis, newZAxis); - Cartesian3.normalize(newZAxis, newZAxis); - Cartesian3.cross(newYAxis, newZAxis, newXAxis); - Cartesian3.normalize(newXAxis, newXAxis); - Cartesian3.cross(newZAxis, newXAxis, newYAxis); - Cartesian3.normalize(newYAxis, newYAxis); - - Cartesian3.multiplyByScalar(newXAxis, xScale, newXAxis); - Cartesian3.multiplyByScalar(newYAxis, yScale, newYAxis); - Cartesian3.multiplyByScalar(newZAxis, zScale, newZAxis); - - Matrix4.setColumn(result, 0, newXAxis, result); - Matrix4.setColumn(result, 1, newYAxis, result); - Matrix4.setColumn(result, 2, newZAxis, result); - Matrix4.setColumn(result, 3, newOrigin, result); + var rotation = Matrix4.getRotation(matrix, scratchRotation); + var local = Matrix4.multiplyByMatrix3(toENU, rotation, result); + Matrix4.multiply(swizzleMatrix, local, result); // Swap x, y, z for 2D + Matrix4.setTranslation(result, projectedPosition, result); // Use the projected center return result; }; - var swizzleMatrix = new Matrix4( - 0.0, 0.0, 1.0, 0.0, - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 1.0); - /** * @private */ @@ -898,13 +866,9 @@ define([ var cartographic = ellipsoid.cartesianToCartographic(center, scratchCartographic); var projectedPosition = projection.project(cartographic, scratchCartesian3Projection); - var newOrigin = scratchCartesian4NewOrigin; - newOrigin.x = projectedPosition.z; - newOrigin.y = projectedPosition.x; - newOrigin.z = projectedPosition.y; - newOrigin.w = 1.0; + Cartesian3.fromElements(projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition); - var translation = Matrix4.fromTranslation(newOrigin, scratchFromENU); + var translation = Matrix4.fromTranslation(projectedPosition, scratchFromENU); Matrix4.multiply(swizzleMatrix, toENU, result); Matrix4.multiply(translation, result, result); diff --git a/Source/Core/VRTheWorldTerrainProvider.js b/Source/Core/VRTheWorldTerrainProvider.js index 4c6524b01f40..5339deb9c6c8 100644 --- a/Source/Core/VRTheWorldTerrainProvider.js +++ b/Source/Core/VRTheWorldTerrainProvider.js @@ -5,6 +5,7 @@ define([ './defaultValue', './defined', './defineProperties', + './deprecationWarning', './DeveloperError', './Ellipsoid', './Event', @@ -15,8 +16,9 @@ define([ './loadXML', './Math', './Rectangle', + './Request', + './RequestType', './TerrainProvider', - './throttleRequestByServer', './TileProviderError' ], function( when, @@ -24,6 +26,7 @@ define([ defaultValue, defined, defineProperties, + deprecationWarning, DeveloperError, Ellipsoid, Event, @@ -34,8 +37,9 @@ define([ loadXML, CesiumMath, Rectangle, + Request, + RequestType, TerrainProvider, - throttleRequestByServer, TileProviderError) { 'use strict'; @@ -256,14 +260,12 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited, - * or false if the request should be initiated regardless of the number of requests - * already in progress. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ - VRTheWorldTerrainProvider.prototype.requestTileGeometry = function(x, y, level, throttleRequests) { + VRTheWorldTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError('requestTileGeometry must not be called before ready returns true.'); @@ -278,16 +280,18 @@ define([ url = proxy.getURL(url); } - var promise; + if (typeof request === 'boolean') { + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); + request = new Request({ + throttle : request, + throttleByServer : request, + type : RequestType.TERRAIN + }); + } - throttleRequests = defaultValue(throttleRequests, true); - if (throttleRequests) { - promise = throttleRequestByServer(url, loadImage); - if (!defined(promise)) { - return undefined; - } - } else { - promise = loadImage(url); + var promise = loadImage(url, undefined, request); + if (!defined(promise)) { + return undefined; } var that = this; diff --git a/Source/Core/WebMercatorTilingScheme.js b/Source/Core/WebMercatorTilingScheme.js index 1790351242cf..5d347d2ad086 100644 --- a/Source/Core/WebMercatorTilingScheme.js +++ b/Source/Core/WebMercatorTilingScheme.js @@ -121,7 +121,7 @@ define([ }; /** - * Transforms an rectangle specified in geodetic radians to the native coordinate system + * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * * @param {Rectangle} rectangle The rectangle to transform. @@ -147,7 +147,7 @@ define([ }; /** - * Converts tile x, y coordinates and level to an rectangle expressed in the native coordinates + * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * * @param {Number} x The integer x coordinate of the tile. diff --git a/Source/Core/arrayFill.js b/Source/Core/arrayFill.js new file mode 100644 index 000000000000..ac6d36ce4a2a --- /dev/null +++ b/Source/Core/arrayFill.js @@ -0,0 +1,56 @@ +/*global define*/ +define([ + './Check', + './defaultValue', + './defined' + ], function( + Check, + defaultValue, + defined) { + 'use strict'; + + /** + * Fill an array or a portion of an array with a given value. + * + * @param {Array} array The array to fill. + * @param {*} value The value to fill the array with. + * @param {Number} [start=0] The index to start filling at. + * @param {Number} [end=array.length] The index to end stop at. + * + * @returns {Array} The resulting array. + * @private + */ + function arrayFill(array, value, start, end) { + //>>includeStart('debug', pragmas.debug); + Check.defined('array', array); + Check.defined('value', value); + if (defined(start)) { + Check.typeOf.number('start', start); + } + if (defined(end)) { + Check.typeOf.number('end', end); + } + //>>includeEnd('debug'); + + if (typeof array.fill === 'function') { + return array.fill(value, start, end); + } + + var length = array.length >>> 0; + var relativeStart = defaultValue(start, 0); + // If negative, find wrap around position + var k = (relativeStart < 0) ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length); + var relativeEnd = defaultValue(end, length); + // If negative, find wrap around position + var final = (relativeEnd < 0) ? Math.max(length + relativeEnd, 0) : Math.min(relativeEnd, length); + + // Fill array accordingly + while (k < final) { + array[k] = value; + k++; + } + return array; + } + + return arrayFill; +}); diff --git a/Source/Core/arrayRemoveDuplicates.js b/Source/Core/arrayRemoveDuplicates.js index 6ca1682cc974..23c9f1b821ee 100644 --- a/Source/Core/arrayRemoveDuplicates.js +++ b/Source/Core/arrayRemoveDuplicates.js @@ -1,13 +1,13 @@ /*global define*/ define([ + './Check', './defaultValue', './defined', - './DeveloperError', './Math' ], function( + Check, defaultValue, defined, - DeveloperError, CesiumMath) { 'use strict'; @@ -45,9 +45,7 @@ define([ */ function arrayRemoveDuplicates(values, equalsEpsilon, wrapAround) { //>>includeStart('debug', pragmas.debug); - if (!defined(equalsEpsilon)) { - throw new DeveloperError('equalsEpsilon is required.'); - } + Check.defined('equalsEpsilon', equalsEpsilon); //>>includeEnd('debug'); if (!defined(values)) { diff --git a/Source/Core/barycentricCoordinates.js b/Source/Core/barycentricCoordinates.js index d7731dacaa8c..25b83e20307b 100644 --- a/Source/Core/barycentricCoordinates.js +++ b/Source/Core/barycentricCoordinates.js @@ -2,13 +2,13 @@ define([ './Cartesian2', './Cartesian3', - './defined', - './DeveloperError' + './Check', + './defined' ], function( Cartesian2, Cartesian3, - defined, - DeveloperError) { + Check, + defined ) { 'use strict'; var scratchCartesian1 = new Cartesian3(); @@ -37,9 +37,10 @@ define([ */ function barycentricCoordinates(point, p0, p1, p2, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(point) || !defined(p0) || !defined(p1) || !defined(p2)) { - throw new DeveloperError('point, p0, p1, and p2 are required.'); - } + Check.defined('point', point); + Check.defined('p0', p0); + Check.defined('p1', p1); + Check.defined('p2', p2); //>>includeEnd('debug'); diff --git a/Source/Core/binarySearch.js b/Source/Core/binarySearch.js index e12fc246e50d..7ee44ffe1269 100644 --- a/Source/Core/binarySearch.js +++ b/Source/Core/binarySearch.js @@ -1,10 +1,10 @@ /*global define*/ define([ - './defined', - './DeveloperError' + './Check', + './defined' ], function( - defined, - DeveloperError) { + Check, + defined) { 'use strict'; /** @@ -31,15 +31,9 @@ define([ */ function binarySearch(array, itemToFind, comparator) { //>>includeStart('debug', pragmas.debug); - if (!defined(array)) { - throw new DeveloperError('array is required.'); - } - if (!defined(itemToFind)) { - throw new DeveloperError('itemToFind is required.'); - } - if (!defined(comparator)) { - throw new DeveloperError('comparator is required.'); - } + Check.defined('array', array); + Check.defined('itemToFind', itemToFind); + Check.defined('comparator', comparator); //>>includeEnd('debug'); var low = 0; diff --git a/Source/Core/decodeGoogleEarthEnterpriseData.js b/Source/Core/decodeGoogleEarthEnterpriseData.js new file mode 100644 index 000000000000..a90533016bfe --- /dev/null +++ b/Source/Core/decodeGoogleEarthEnterpriseData.js @@ -0,0 +1,93 @@ +/*global define*/ +define([ + './Check', + './defined', + './RuntimeError' +], function( + Check, + defined, + RuntimeError) { + 'use strict'; + + var compressedMagic = 0x7468dead; + var compressedMagicSwap = 0xadde6874; + + /** + * Decodes data that is received from the Google Earth Enterprise server. + * + * @param {ArrayBuffer} key The key used during decoding. + * @param {ArrayBuffer} data The data to be decoded. + * + * @private + */ + function decodeGoogleEarthEnterpriseData(key, data) { + if (decodeGoogleEarthEnterpriseData.passThroughDataForTesting) { + return data; + } + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('key', key); + Check.typeOf.object('data', data); + //>>includeEnd('debug'); + + var keyLength = key.byteLength; + if (keyLength === 0 || (keyLength % 4) !== 0) { + throw new RuntimeError('The length of key must be greater than 0 and a multiple of 4.'); + } + + var dataView = new DataView(data); + var magic = dataView.getUint32(0, true); + if (magic === compressedMagic || magic === compressedMagicSwap) { + // Occasionally packets don't come back encoded, so just return + return data; + } + + var keyView = new DataView(key); + + var dp = 0; + var dpend = data.byteLength; + var dpend64 = dpend - (dpend % 8); + var kpend = keyLength; + var kp; + var off = 8; + + // This algorithm is intentionally asymmetric to make it more difficult to + // guess. Security through obscurity. :-( + + // while we have a full uint64 (8 bytes) left to do + // assumes buffer is 64bit aligned (or processor doesn't care) + while (dp < dpend64) { + // rotate the key each time through by using the offets 16,0,8,16,0,8,... + off = (off + 8) % 24; + kp = off; + + // run through one key length xor'ing one uint64 at a time + // then drop out to rotate the key for the next bit + while ((dp < dpend64) && (kp < kpend)) { + dataView.setUint32(dp, dataView.getUint32(dp, true) ^ keyView.getUint32(kp, true), true); + dataView.setUint32(dp + 4, dataView.getUint32(dp + 4, true) ^ keyView.getUint32(kp + 4, true), true); + dp += 8; + kp += 24; + } + } + + // now the remaining 1 to 7 bytes + if (dp < dpend) { + if (kp >= kpend) { + // rotate the key one last time (if necessary) + off = (off + 8) % 24; + kp = off; + } + + while (dp < dpend) { + dataView.setUint8(dp, dataView.getUint8(dp) ^ keyView.getUint8(kp)); + dp++; + kp++; + } + } + } + + decodeGoogleEarthEnterpriseData.passThroughDataForTesting = false; + + return decodeGoogleEarthEnterpriseData; +}); diff --git a/Source/Core/isBitSet.js b/Source/Core/isBitSet.js new file mode 100644 index 000000000000..48b82a831dbc --- /dev/null +++ b/Source/Core/isBitSet.js @@ -0,0 +1,13 @@ +/*global define*/ +define([], function() { + 'use strict'; + + /** + * @private + */ + function isBitSet(bits, mask) { + return ((bits & mask) !== 0); + } + + return isBitSet; +}); diff --git a/Source/Core/isBlobUri.js b/Source/Core/isBlobUri.js new file mode 100644 index 000000000000..dc4afb78502f --- /dev/null +++ b/Source/Core/isBlobUri.js @@ -0,0 +1,29 @@ +/*global define*/ +define([ + './Check' + ], function( + Check) { + 'use strict'; + + var blobUriRegex = /^blob:/i; + + /** + * Determines if the specified uri is a blob uri. + * + * @exports isBlobUri + * + * @param {String} uri The uri to test. + * @returns {Boolean} true when the uri is a blob uri; otherwise, false. + * + * @private + */ + function isBlobUri(uri) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('uri', uri); + //>>includeEnd('debug'); + + return blobUriRegex.test(uri); + } + + return isBlobUri; +}); diff --git a/Source/Core/isDataUri.js b/Source/Core/isDataUri.js new file mode 100644 index 000000000000..0cd24a228c1e --- /dev/null +++ b/Source/Core/isDataUri.js @@ -0,0 +1,29 @@ +/*global define*/ +define([ + './Check' + ], function( + Check) { + 'use strict'; + + var dataUriRegex = /^data:/i; + + /** + * Determines if the specified uri is a data uri. + * + * @exports isDataUri + * + * @param {String} uri The uri to test. + * @returns {Boolean} true when the uri is a data uri; otherwise, false. + * + * @private + */ + function isDataUri(uri) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('uri', uri); + //>>includeEnd('debug'); + + return dataUriRegex.test(uri); + } + + return isDataUri; +}); diff --git a/Source/Core/joinUrls.js b/Source/Core/joinUrls.js index 0eeab040e0a4..3a47587edbd6 100644 --- a/Source/Core/joinUrls.js +++ b/Source/Core/joinUrls.js @@ -18,6 +18,8 @@ define([ * @param {String|Uri} first The base URL. * @param {String|Uri} second The URL path to join to the base URL. If this URL is absolute, it is returned unmodified. * @param {Boolean} [appendSlash=true] The boolean determining whether there should be a forward slash between first and second. + * + * @return {String} The combined url * @private */ function joinUrls(first, second, appendSlash) { @@ -40,6 +42,16 @@ define([ second = new Uri(second); } + // Don't try to join a data uri + if (first.scheme === 'data') { + return first.toString(); + } + + // Don't try to join a data uri + if (second.scheme === 'data') { + return second.toString(); + } + // Uri.isAbsolute returns false for a URL like '//foo.com'. So if we have an authority but // not a scheme, add a scheme matching the page's scheme. if (defined(second.authority) && !defined(second.scheme)) { diff --git a/Source/Core/loadArrayBuffer.js b/Source/Core/loadArrayBuffer.js index ff557505b73c..cc56d573ff6c 100644 --- a/Source/Core/loadArrayBuffer.js +++ b/Source/Core/loadArrayBuffer.js @@ -13,10 +13,10 @@ define([ * * @exports loadArrayBuffer * - * @param {String|Promise.} url The URL of the binary data, or a promise for the URL. + * @param {String} url The URL of the binary data. * @param {Object} [headers] HTTP headers to send with the requests. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. - * + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example * // load a single URL asynchronously @@ -25,15 +25,16 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadArrayBuffer(url, headers) { + function loadArrayBuffer(url, headers, request) { return loadWithXhr({ url : url, responseType : 'arraybuffer', - headers : headers + headers : headers, + request : request }); } diff --git a/Source/Core/loadBlob.js b/Source/Core/loadBlob.js index 7d6ef776d5c3..0e136d22c37c 100644 --- a/Source/Core/loadBlob.js +++ b/Source/Core/loadBlob.js @@ -13,10 +13,10 @@ define([ * * @exports loadBlob * - * @param {String|Promise.} url The URL of the data, or a promise for the URL. + * @param {String} url The URL of the data. * @param {Object} [headers] HTTP headers to send with the requests. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. - * + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example * // load a single URL asynchronously @@ -25,15 +25,16 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadBlob(url, headers) { + function loadBlob(url, headers, request) { return loadWithXhr({ url : url, responseType : 'blob', - headers : headers + headers : headers, + request : request }); } diff --git a/Source/Core/loadCRN.js b/Source/Core/loadCRN.js index f50ebefbfb38..eac7121e1004 100644 --- a/Source/Core/loadCRN.js +++ b/Source/Core/loadCRN.js @@ -26,9 +26,10 @@ define([ * * @exports loadCRN * - * @param {String|Promise.|ArrayBuffer} urlOrBuffer The URL of the binary data, a promise for the URL, or an ArrayBuffer. + * @param {String|ArrayBuffer} urlOrBuffer The URL of the binary data or an ArrayBuffer. * @param {Object} [headers] HTTP headers to send with the requests. - * @returns {Promise.} A promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Unsupported compressed format. * @@ -48,7 +49,7 @@ define([ * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadCRN(urlOrBuffer, headers) { + function loadCRN(urlOrBuffer, headers, request) { //>>includeStart('debug', pragmas.debug); if (!defined(urlOrBuffer)) { throw new DeveloperError('urlOrBuffer is required.'); @@ -59,7 +60,11 @@ define([ if (urlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(urlOrBuffer)) { loadPromise = when.resolve(urlOrBuffer); } else { - loadPromise = loadArrayBuffer(urlOrBuffer, headers); + loadPromise = loadArrayBuffer(urlOrBuffer, headers, request); + } + + if (!defined(loadPromise)) { + return undefined; } return loadPromise.then(function(data) { @@ -81,4 +86,4 @@ define([ } return loadCRN; -}); \ No newline at end of file +}); diff --git a/Source/Core/loadImage.js b/Source/Core/loadImage.js index ba299a24c860..79c0c95673ae 100644 --- a/Source/Core/loadImage.js +++ b/Source/Core/loadImage.js @@ -1,33 +1,42 @@ /*global define*/ define([ '../ThirdParty/when', + './Check', './defaultValue', './defined', + './deprecationWarning', './DeveloperError', './isCrossOriginUrl', + './isDataUri', + './Request', + './RequestScheduler', './TrustedServers' ], function( when, + Check, defaultValue, defined, + deprecationWarning, DeveloperError, isCrossOriginUrl, + isDataUri, + Request, + RequestScheduler, TrustedServers) { 'use strict'; - var dataUriRegex = /^data:/; - /** * Asynchronously loads the given image URL. Returns a promise that will resolve to * an {@link Image} once loaded, or reject if the image failed to load. * * @exports loadImage * - * @param {String|Promise.} url The source of the image, or a promise for the URL. + * @param {String} url The source URL of the image. * @param {Boolean} [allowCrossOrigin=true] Whether to request the image using Cross-Origin * Resource Sharing (CORS). CORS is only actually used if the image URL is actually cross-origin. * Data URIs are never requested using CORS. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -42,24 +51,37 @@ define([ * when.all([loadImage('image1.png'), loadImage('image2.png')]).then(function(images) { * // images is an array containing all the loaded images * }); - * + * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadImage(url, allowCrossOrigin) { + function loadImage(url, allowCrossOrigin, request) { //>>includeStart('debug', pragmas.debug); - if (!defined(url)) { - throw new DeveloperError('url is required.'); - } + Check.defined('url', url); //>>includeEnd('debug'); allowCrossOrigin = defaultValue(allowCrossOrigin, true); - return when(url, function(url) { + if (typeof url !== 'string') { + // Returning a promise here is okay because it is unlikely that anyone using the deprecated functionality is also + // providing a Request object marked as throttled. + deprecationWarning('url promise', 'url as a Promise is deprecated and will be removed in 1.37'); + return url.then(function(url) { + return makeRequest(url, allowCrossOrigin, request); + }); + } + + return makeRequest(url, allowCrossOrigin, request); + } + + function makeRequest(url, allowCrossOrigin, request) { + request = defined(request) ? request : new Request(); + request.url = url; + request.requestFunction = function() { var crossOrigin; // data URIs can't have allowCrossOrigin set. - if (dataUriRegex.test(url) || !allowCrossOrigin) { + if (isDataUri(url) || !allowCrossOrigin) { crossOrigin = false; } else { crossOrigin = isCrossOriginUrl(url); @@ -70,7 +92,9 @@ define([ loadImage.createImage(url, crossOrigin, deferred); return deferred.promise; - }); + }; + + return RequestScheduler.request(request); } // This is broken out into a separate function so that it can be mocked for testing purposes. diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index 9be887ab39cd..e7f636031aa6 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -1,11 +1,13 @@ /*global define*/ define([ '../ThirdParty/when', + './Check', './defined', './DeveloperError', './loadImage' ], function( when, + Check, defined, DeveloperError, loadImage) { @@ -14,15 +16,10 @@ define([ /** * @private */ - function loadImageFromTypedArray(uint8Array, format) { + function loadImageFromTypedArray(uint8Array, format, request) { //>>includeStart('debug', pragmas.debug); - if (!defined(uint8Array)) { - throw new DeveloperError('uint8Array is required.'); - } - - if (!defined(format)) { - throw new DeveloperError('format is required.'); - } + Check.typeOf.object('uint8Array', uint8Array); + Check.typeOf.string('format', format); //>>includeEnd('debug'); var blob = new Blob([uint8Array], { @@ -30,7 +27,7 @@ define([ }); var blobUrl = window.URL.createObjectURL(blob); - return loadImage(blobUrl, false).then(function(image) { + return loadImage(blobUrl, false, request).then(function(image) { window.URL.revokeObjectURL(blobUrl); return image; }, function(error) { diff --git a/Source/Core/loadImageViaBlob.js b/Source/Core/loadImageViaBlob.js index 35118d556585..384531a9bab0 100644 --- a/Source/Core/loadImageViaBlob.js +++ b/Source/Core/loadImageViaBlob.js @@ -1,15 +1,28 @@ /*global define*/ define([ '../ThirdParty/when', + './defined', + './isDataUri', './loadBlob', './loadImage' ], function( when, + defined, + isDataUri, loadBlob, loadImage) { 'use strict'; - var dataUriRegex = /^data:/; + var xhrBlobSupported = (function() { + try { + var xhr = new XMLHttpRequest(); + xhr.open('GET', '#', true); + xhr.responseType = 'blob'; + return xhr.responseType === 'blob'; + } catch (e) { + return false; + } + })(); /** * Asynchronously loads the given image URL by first downloading it as a blob using @@ -25,8 +38,9 @@ define([ * * @exports loadImageViaBlob * - * @param {String|Promise.} url The source of the image, or a promise for the URL. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {String} url The source URL of the image. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -42,16 +56,21 @@ define([ * when.all([loadImageViaBlob('image1.png'), loadImageViaBlob('image2.png')]).then(function(images) { * // images is an array containing all the loaded images * }); - * + * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadImageViaBlob(url) { - if (dataUriRegex.test(url)) { - return loadImage(url); + function loadImageViaBlob(url, request) { + if (!xhrBlobSupported || isDataUri(url)) { + return loadImage(url, undefined, request); + } + + var blobPromise = loadBlob(url, undefined, request); + if (!defined(blobPromise)) { + return undefined; } - return loadBlob(url).then(function(blob) { + return blobPromise.then(function(blob) { var blobUrl = window.URL.createObjectURL(blob); return loadImage(blobUrl, false).then(function(image) { @@ -65,16 +84,5 @@ define([ }); } - var xhrBlobSupported = (function() { - try { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '#', true); - xhr.responseType = 'blob'; - return xhr.responseType === 'blob'; - } catch (e) { - return false; - } - })(); - - return xhrBlobSupported ? loadImageViaBlob : loadImage; + return loadImageViaBlob; }); diff --git a/Source/Core/loadJson.js b/Source/Core/loadJson.js index 01cce7033f19..1fd1de5133c0 100644 --- a/Source/Core/loadJson.js +++ b/Source/Core/loadJson.js @@ -26,11 +26,12 @@ define([ * * @exports loadJson * - * @param {String|Promise.} url The URL to request, or a promise for the URL. + * @param {String} url The URL to request. * @param {Object} [headers] HTTP headers to send with the request. * 'Accept: application/json,*/*;q=0.01' is added to the request headers automatically * if not specified. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -39,12 +40,12 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see loadText * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadJson(url, headers) { + function loadJson(url, headers, request) { //>>includeStart('debug', pragmas.debug); if (!defined(url)) { throw new DeveloperError('url is required.'); @@ -59,7 +60,12 @@ define([ headers.Accept = defaultHeaders.Accept; } - return loadText(url, headers).then(function(value) { + var textPromise = loadText(url, headers, request); + if (!defined(textPromise)) { + return undefined; + } + + return textPromise.then(function(value) { return JSON.parse(value); }); } diff --git a/Source/Core/loadJsonp.js b/Source/Core/loadJsonp.js index 730c19b865b3..c63f3b58b0b0 100644 --- a/Source/Core/loadJsonp.js +++ b/Source/Core/loadJsonp.js @@ -7,7 +7,9 @@ define([ './defined', './DeveloperError', './objectToQuery', - './queryToObject' + './queryToObject', + './Request', + './RequestScheduler' ], function( Uri, when, @@ -16,7 +18,9 @@ define([ defined, DeveloperError, objectToQuery, - queryToObject) { + queryToObject, + Request, + RequestScheduler) { 'use strict'; /** @@ -29,7 +33,8 @@ define([ * @param {Object} [options.parameters] Any extra query parameters to append to the URL. * @param {String} [options.callbackParameterName='callback'] The callback parameter name that the server expects. * @param {Proxy} [options.proxy] A proxy to use for the request. This object is expected to have a getURL function which returns the proxied URL, if needed. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -39,10 +44,10 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadJsonp(url, options) { + function loadJsonp(url, options, request) { //>>includeStart('debug', pragmas.debug); if (!defined(url)) { throw new DeveloperError('url is required.'); @@ -57,19 +62,6 @@ define([ functionName = 'loadJsonp' + Math.random().toString().substring(2, 8); } while (defined(window[functionName])); - var deferred = when.defer(); - - //assign a function with that name in the global scope - window[functionName] = function(data) { - deferred.resolve(data); - - try { - delete window[functionName]; - } catch (e) { - window[functionName] = undefined; - } - }; - var uri = new Uri(url); var queryOptions = queryToObject(defaultValue(uri.query, '')); @@ -90,9 +82,27 @@ define([ url = proxy.getURL(url); } - loadJsonp.loadAndExecuteScript(url, functionName, deferred); + request = defined(request) ? request : new Request(); + request.url = url; + request.requestFunction = function() { + var deferred = when.defer(); + + //assign a function with that name in the global scope + window[functionName] = function(data) { + deferred.resolve(data); + + try { + delete window[functionName]; + } catch (e) { + window[functionName] = undefined; + } + }; + + loadJsonp.loadAndExecuteScript(url, functionName, deferred); + return deferred.promise; + }; - return deferred.promise; + return RequestScheduler.request(request); } // This is broken out into a separate function so that it can be mocked for testing purposes. diff --git a/Source/Core/loadKTX.js b/Source/Core/loadKTX.js index 997f669b9dd9..cd8600b10d14 100644 --- a/Source/Core/loadKTX.js +++ b/Source/Core/loadKTX.js @@ -37,9 +37,10 @@ define([ * * @exports loadKTX * - * @param {String|Promise.|ArrayBuffer} urlOrBuffer The URL of the binary data, a promise for the URL, or an ArrayBuffer. + * @param {String|ArrayBuffer} urlOrBuffer The URL of the binary data or an ArrayBuffer. * @param {Object} [headers] HTTP headers to send with the requests. - * @returns {Promise.} A promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Invalid KTX file. * @exception {RuntimeError} File is the wrong endianness. @@ -69,7 +70,7 @@ define([ * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadKTX(urlOrBuffer, headers) { + function loadKTX(urlOrBuffer, headers, request) { //>>includeStart('debug', pragmas.debug); if (!defined(urlOrBuffer)) { throw new DeveloperError('urlOrBuffer is required.'); @@ -80,7 +81,11 @@ define([ if (urlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(urlOrBuffer)) { loadPromise = when.resolve(urlOrBuffer); } else { - loadPromise = loadArrayBuffer(urlOrBuffer, headers); + loadPromise = loadArrayBuffer(urlOrBuffer, headers, request); + } + + if (!defined(loadPromise)) { + return undefined; } return loadPromise.then(function(data) { @@ -209,7 +214,7 @@ define([ // Only use the level 0 mipmap if (PixelFormat.isCompressedFormat(glInternalFormat) && numberOfMipmapLevels > 1) { - var levelSize = PixelFormat.compressedTextureSize(glInternalFormat, pixelWidth, pixelHeight); + var levelSize = PixelFormat.compressedTextureSizeInBytes(glInternalFormat, pixelWidth, pixelHeight); texture = texture.slice(0, levelSize); } diff --git a/Source/Core/loadText.js b/Source/Core/loadText.js index 342963ae9b25..8ca386fe1bfa 100644 --- a/Source/Core/loadText.js +++ b/Source/Core/loadText.js @@ -13,9 +13,10 @@ define([ * * @exports loadText * - * @param {String|Promise.} url The URL to request, or a promise for the URL. + * @param {String} url The URL to request. * @param {Object} [headers] HTTP headers to send with the request. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -27,15 +28,16 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadText(url, headers) { + function loadText(url, headers, request) { return loadWithXhr({ url : url, - headers : headers + headers : headers, + request : request }); } diff --git a/Source/Core/loadWithXhr.js b/Source/Core/loadWithXhr.js index a44debf511db..f5e2690aa778 100644 --- a/Source/Core/loadWithXhr.js +++ b/Source/Core/loadWithXhr.js @@ -1,18 +1,26 @@ /*global define*/ define([ '../ThirdParty/when', + './Check', './defaultValue', './defined', + './deprecationWarning', './DeveloperError', + './Request', './RequestErrorEvent', + './RequestScheduler', './RuntimeError', './TrustedServers' ], function( when, + Check, defaultValue, defined, + deprecationWarning, DeveloperError, + Request, RequestErrorEvent, + RequestScheduler, RuntimeError, TrustedServers) { 'use strict'; @@ -26,13 +34,14 @@ define([ * @exports loadWithXhr * * @param {Object} options Object with the following properties: - * @param {String|Promise.} options.url The URL of the data, or a promise for the URL. + * @param {String} options.url The URL of the data. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.method='GET'] The HTTP method to use. * @param {String} [options.data] The data to send with the request, if any. * @param {Object} [options.headers] HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [options.request] The request object. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -57,24 +66,45 @@ define([ options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); - if (!defined(options.url)) { - throw new DeveloperError('options.url is required.'); - } + Check.defined('options.url', options.url); //>>includeEnd('debug'); + var url = options.url; + + if (typeof url !== 'string') { + // Returning a promise here is okay because it is unlikely that anyone using the deprecated functionality is also + // providing a Request object marked as throttled. + deprecationWarning('url promise', 'options.url as a Promise is deprecated and will be removed in Cesium 1.37'); + return url.then(function(url) { + return makeRequest(options, url); + }); + } + + return makeRequest(options); + } + + function makeRequest(options, url) { var responseType = options.responseType; var method = defaultValue(options.method, 'GET'); var data = options.data; var headers = options.headers; var overrideMimeType = options.overrideMimeType; + url = defaultValue(url, options.url); - return when(options.url, function(url) { + var request = defined(options.request) ? options.request : new Request(); + request.url = url; + request.requestFunction = function() { var deferred = when.defer(); - - loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType); - + var xhr = loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType); + if (defined(xhr) && defined(xhr.abort)) { + request.cancelFunction = function() { + xhr.abort(); + }; + } return deferred.promise; - }); + }; + + return RequestScheduler.request(request); } var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; @@ -192,6 +222,8 @@ define([ }; xhr.send(data); + + return xhr; }; loadWithXhr.defaultLoad = loadWithXhr.load; diff --git a/Source/Core/loadXML.js b/Source/Core/loadXML.js index b9f6915d4e30..780ff6ad5eea 100644 --- a/Source/Core/loadXML.js +++ b/Source/Core/loadXML.js @@ -13,9 +13,10 @@ define([ * * @exports loadXML * - * @param {String|Promise.} url The URL to request, or a promise for the URL. + * @param {String} url The URL to request. * @param {Object} [headers] HTTP headers to send with the request. - * @returns {Promise.} a promise that will resolve to the requested data when loaded. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -27,17 +28,18 @@ define([ * }).otherwise(function(error) { * // an error occurred * }); - * + * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - function loadXML(url, headers) { + function loadXML(url, headers, request) { return loadWithXhr({ url : url, responseType : 'document', headers : headers, - overrideMimeType : 'text/xml' + overrideMimeType : 'text/xml', + request : request }); } diff --git a/Source/Core/sampleTerrain.js b/Source/Core/sampleTerrain.js index 5c67e870b1a8..8a43a84b8627 100644 --- a/Source/Core/sampleTerrain.js +++ b/Source/Core/sampleTerrain.js @@ -31,7 +31,7 @@ define([ * @example * // Query the terrain height of two Cartographic positions * var terrainProvider = new Cesium.CesiumTerrainProvider({ - * url : 'https://assets.agi.com/stk-terrain/world' + * url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' * }); * var positions = [ * Cesium.Cartographic.fromDegrees(86.925145, 27.988257), @@ -107,7 +107,7 @@ define([ var tilePromises = []; for (i = 0; i < tileRequests.length; ++i) { var tileRequest = tileRequests[i]; - var requestPromise = tileRequest.terrainProvider.requestTileGeometry(tileRequest.x, tileRequest.y, tileRequest.level, false); + var requestPromise = tileRequest.terrainProvider.requestTileGeometry(tileRequest.x, tileRequest.y, tileRequest.level); var tilePromise = when(requestPromise, createInterpolateFunction(tileRequest), createMarkFailedFunction(tileRequest)); tilePromises.push(tilePromise); } diff --git a/Source/Core/sampleTerrainMostDetailed.js b/Source/Core/sampleTerrainMostDetailed.js index 027fd8de22c8..53b08c6985ba 100644 --- a/Source/Core/sampleTerrainMostDetailed.js +++ b/Source/Core/sampleTerrainMostDetailed.js @@ -24,7 +24,7 @@ define([ * @example * // Query the terrain height of two Cartographic positions * var terrainProvider = new Cesium.CesiumTerrainProvider({ - * url : '//assets.agi.com/stk-terrain/world' + * url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' * }); * var positions = [ * Cesium.Cartographic.fromDegrees(86.925145, 27.988257), diff --git a/Source/Core/throttleRequestByServer.js b/Source/Core/throttleRequestByServer.js deleted file mode 100644 index 14debf754c5f..000000000000 --- a/Source/Core/throttleRequestByServer.js +++ /dev/null @@ -1,95 +0,0 @@ -/*global define*/ -define([ - '../ThirdParty/Uri', - '../ThirdParty/when', - './defaultValue' - ], function( - Uri, - when, - defaultValue) { - 'use strict'; - - var activeRequests = {}; - - var pageUri = typeof document !== 'undefined' ? new Uri(document.location.href) : new Uri(); - function getServer(url) { - var uri = new Uri(url).resolve(pageUri); - uri.normalize(); - var server = uri.authority; - if (!/:/.test(server)) { - server = server + ':' + (uri.scheme === 'https' ? '443' : '80'); - } - return server; - } - - /** - * Because browsers throttle the number of parallel requests allowed to each server, - * this function tracks the number of active requests in progress to each server, and - * returns undefined immediately if the request would exceed the maximum, allowing - * the caller to retry later, instead of queueing indefinitely under the browser's control. - * - * @exports throttleRequestByServer - * - * @param {String} url The URL to request. - * @param {throttleRequestByServer~RequestFunction} requestFunction The actual function that - * makes the request. - * @returns {Promise.|undefined} Either undefined, meaning the request would exceed the maximum number of - * parallel requests, or a Promise for the requested data. - * - * - * @example - * // throttle requests for an image - * var url = 'http://madeupserver.example.com/myImage.png'; - * function requestFunction(url) { - * // in this simple example, loadImage could be used directly as requestFunction. - * return Cesium.loadImage(url); - * }; - * var promise = Cesium.throttleRequestByServer(url, requestFunction); - * if (!Cesium.defined(promise)) { - * // too many active requests in progress, try again later. - * } else { - * promise.then(function(image) { - * // handle loaded image - * }); - * } - * - * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} - */ - function throttleRequestByServer(url, requestFunction) { - var server = getServer(url); - - var activeRequestsForServer = defaultValue(activeRequests[server], 0); - if (activeRequestsForServer >= throttleRequestByServer.maximumRequestsPerServer) { - return undefined; - } - - activeRequests[server] = activeRequestsForServer + 1; - - return when(requestFunction(url), function(result) { - activeRequests[server]--; - return result; - }).otherwise(function(error) { - activeRequests[server]--; - return when.reject(error); - }); - } - - /** - * Specifies the maximum number of requests that can be simultaneously open to a single server. If this value is higher than - * the number of requests per server actually allowed by the web browser, Cesium's ability to prioritize requests will be adversely - * affected. - * @type {Number} - * @default 6 - */ - throttleRequestByServer.maximumRequestsPerServer = 6; - - /** - * A function that will make a request if there are available slots to the server. - * @callback throttleRequestByServer~RequestFunction - * - * @param {String} url The url to request. - * @returns {Promise.} A promise for the requested data. - */ - - return throttleRequestByServer; -}); diff --git a/Source/DataSources/BillboardGraphics.js b/Source/DataSources/BillboardGraphics.js index 628768b00007..27e33c9f1728 100644 --- a/Source/DataSources/BillboardGraphics.js +++ b/Source/DataSources/BillboardGraphics.js @@ -89,6 +89,8 @@ define([ this._sizeInMetersSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; + this._disableDepthTestDistance = undefined; + this._disableDepthTestDistanceSubscription = undefined; this._definitionChanged = new Event(); this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); @@ -316,7 +318,15 @@ define([ * @memberof BillboardGraphics.prototype * @type {Property} */ - distanceDisplayCondition : createPropertyDescriptor('distanceDisplayCondition') + distanceDisplayCondition : createPropertyDescriptor('distanceDisplayCondition'), + + /** + * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. + * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. + * @memberof BillboardGraphics.prototype + * @type {Property} + */ + disableDepthTestDistance : createPropertyDescriptor('disableDepthTestDistance') }); /** @@ -348,6 +358,7 @@ define([ result.pixelOffsetScaleByDistance = this._pixelOffsetScaleByDistance; result.sizeInMeters = this._sizeInMeters; result.distanceDisplayCondition = this._distanceDisplayCondition; + result.disableDepthTestDistance = this._disableDepthTestDistance; return result; }; @@ -383,6 +394,7 @@ define([ this.pixelOffsetScaleByDistance = defaultValue(this._pixelOffsetScaleByDistance, source.pixelOffsetScaleByDistance); this.sizeInMeters = defaultValue(this._sizeInMeters, source.sizeInMeters); this.distanceDisplayCondition = defaultValue(this._distanceDisplayCondition, source.distanceDisplayCondition); + this.disableDepthTestDistance = defaultValue(this._disableDepthTestDistance, source.disableDepthTestDistance); }; return BillboardGraphics; diff --git a/Source/DataSources/BillboardVisualizer.js b/Source/DataSources/BillboardVisualizer.js index b1595fd5dca5..fd64b9db3000 100644 --- a/Source/DataSources/BillboardVisualizer.js +++ b/Source/DataSources/BillboardVisualizer.js @@ -43,6 +43,7 @@ define([ var defaultHorizontalOrigin = HorizontalOrigin.CENTER; var defaultVerticalOrigin = VerticalOrigin.CENTER; var defaultSizeInMeters = false; + var defaultDisableDepthTestDistance = 0.0; var position = new Cartesian3(); var color = new Color(); @@ -154,8 +155,9 @@ define([ billboard.scaleByDistance = Property.getValueOrUndefined(billboardGraphics._scaleByDistance, time, scaleByDistance); billboard.translucencyByDistance = Property.getValueOrUndefined(billboardGraphics._translucencyByDistance, time, translucencyByDistance); billboard.pixelOffsetScaleByDistance = Property.getValueOrUndefined(billboardGraphics._pixelOffsetScaleByDistance, time, pixelOffsetScaleByDistance); - billboard.sizeInMeters = Property.getValueOrDefault(billboardGraphics._sizeInMeters, defaultSizeInMeters); + billboard.sizeInMeters = Property.getValueOrDefault(billboardGraphics._sizeInMeters, time, defaultSizeInMeters); billboard.distanceDisplayCondition = Property.getValueOrUndefined(billboardGraphics._distanceDisplayCondition, time, distanceDisplayCondition); + billboard.disableDepthTestDistance = Property.getValueOrDefault(billboardGraphics._disableDepthTestDistance, time, defaultDisableDepthTestDistance); var subRegion = Property.getValueOrUndefined(billboardGraphics._imageSubRegion, time, boundingRectangle); if (defined(subRegion)) { diff --git a/Source/DataSources/ConstantProperty.js b/Source/DataSources/ConstantProperty.js index bcdd9522f9c0..96e46a5914c1 100644 --- a/Source/DataSources/ConstantProperty.js +++ b/Source/DataSources/ConstantProperty.js @@ -78,12 +78,11 @@ define([ var hasClone = isDefined && typeof value.clone === 'function'; var hasEquals = isDefined && typeof value.equals === 'function'; - this._hasClone = hasClone; - this._hasEquals = hasEquals; - var changed = !hasEquals || !value.equals(oldValue); if (changed) { - this._value = !hasClone ? value : value.clone(); + this._hasClone = hasClone; + this._hasEquals = hasEquals; + this._value = !hasClone ? value : value.clone(this._value); this._definitionChanged.raiseEvent(this); } } diff --git a/Source/DataSources/CylinderGraphics.js b/Source/DataSources/CylinderGraphics.js index e1a7c01cbe44..8f1a5b3a1f18 100644 --- a/Source/DataSources/CylinderGraphics.js +++ b/Source/DataSources/CylinderGraphics.js @@ -118,7 +118,7 @@ define([ * Gets or sets the Property specifying the number of edges around the perimeter of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property} - * @default 16 + * @default 128 */ slices : createPropertyDescriptor('slices'), diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index 4de1f406ddb8..cd573e18235e 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -21,12 +21,14 @@ define([ '../Core/HermitePolynomialApproximation', '../Core/isArray', '../Core/Iso8601', + '../Core/joinUrls', '../Core/JulianDate', '../Core/LagrangePolynomialApproximation', '../Core/LinearApproximation', '../Core/loadJson', '../Core/Math', '../Core/NearFarScalar', + '../Core/objectToQuery', '../Core/Quaternion', '../Core/Rectangle', '../Core/ReferenceFrame', @@ -67,6 +69,7 @@ define([ './PointGraphics', './PolygonGraphics', './PolylineArrowMaterialProperty', + './PolylineDashMaterialProperty', './PolylineGlowMaterialProperty', './PolylineGraphics', './PolylineOutlineMaterialProperty', @@ -106,12 +109,14 @@ define([ HermitePolynomialApproximation, isArray, Iso8601, + joinUrls, JulianDate, LagrangePolynomialApproximation, LinearApproximation, loadJson, CesiumMath, NearFarScalar, + objectToQuery, Quaternion, Rectangle, ReferenceFrame, @@ -152,6 +157,7 @@ define([ PointGraphics, PolygonGraphics, PolylineArrowMaterialProperty, + PolylineDashMaterialProperty, PolylineGlowMaterialProperty, PolylineGraphics, PolylineOutlineMaterialProperty, @@ -212,10 +218,14 @@ define([ return rgbaf; } - function unwrapUriInterval(czmlInterval, sourceUri) { + function unwrapUriInterval(czmlInterval, sourceUri, query) { var result = defaultValue(czmlInterval.uri, czmlInterval); if (defined(sourceUri)) { result = getAbsoluteUri(result, getAbsoluteUri(sourceUri)); + + if (defined(query)) { + result = joinUrls(result, '?' + query, false); + } } return result; } @@ -255,21 +265,21 @@ define([ scratchSpherical.cone = unitSpherical[1]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; - } else { - var result = new Array(length / 3 * 4); - for (var i = 0, j = 0; i < length; i += 3, j += 4) { - result[j] = unitSpherical[i]; + } - scratchSpherical.clock = unitSpherical[i + 1]; - scratchSpherical.cone = unitSpherical[i + 2]; - Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); + var result = new Array(length / 3 * 4); + for (var i = 0, j = 0; i < length; i += 3, j += 4) { + result[j] = unitSpherical[i]; - result[j + 1] = scratchCartesian.x; - result[j + 2] = scratchCartesian.y; - result[j + 3] = scratchCartesian.z; - } - return result; + scratchSpherical.clock = unitSpherical[i + 1]; + scratchSpherical.cone = unitSpherical[i + 2]; + Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); + + result[j + 1] = scratchCartesian.x; + result[j + 2] = scratchCartesian.y; + result[j + 3] = scratchCartesian.z; } + return result; } function convertSphericalToCartesian(spherical) { @@ -280,22 +290,22 @@ define([ scratchSpherical.magnitude = spherical[2]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; - } else { - var result = new Array(length); - for (var i = 0; i < length; i += 4) { - result[i] = spherical[i]; - - scratchSpherical.clock = spherical[i + 1]; - scratchSpherical.cone = spherical[i + 2]; - scratchSpherical.magnitude = spherical[i + 3]; - Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); - - result[i + 1] = scratchCartesian.x; - result[i + 2] = scratchCartesian.y; - result[i + 3] = scratchCartesian.z; - } - return result; } + + var result = new Array(length); + for (var i = 0; i < length; i += 4) { + result[i] = spherical[i]; + + scratchSpherical.clock = spherical[i + 1]; + scratchSpherical.cone = spherical[i + 2]; + scratchSpherical.magnitude = spherical[i + 3]; + Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); + + result[i + 1] = scratchCartesian.x; + result[i + 2] = scratchCartesian.y; + result[i + 3] = scratchCartesian.z; + } + return result; } function convertCartographicRadiansToCartesian(cartographicRadians) { @@ -306,22 +316,22 @@ define([ scratchCartographic.height = cartographicRadians[2]; Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; - } else { - var result = new Array(length); - for (var i = 0; i < length; i += 4) { - result[i] = cartographicRadians[i]; - - scratchCartographic.longitude = cartographicRadians[i + 1]; - scratchCartographic.latitude = cartographicRadians[i + 2]; - scratchCartographic.height = cartographicRadians[i + 3]; - Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); - - result[i + 1] = scratchCartesian.x; - result[i + 2] = scratchCartesian.y; - result[i + 3] = scratchCartesian.z; - } - return result; } + + var result = new Array(length); + for (var i = 0; i < length; i += 4) { + result[i] = cartographicRadians[i]; + + scratchCartographic.longitude = cartographicRadians[i + 1]; + scratchCartographic.latitude = cartographicRadians[i + 2]; + scratchCartographic.height = cartographicRadians[i + 3]; + Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); + + result[i + 1] = scratchCartesian.x; + result[i + 2] = scratchCartesian.y; + result[i + 3] = scratchCartesian.z; + } + return result; } function convertCartographicDegreesToCartesian(cartographicDegrees) { @@ -332,22 +342,22 @@ define([ scratchCartographic.height = cartographicDegrees[2]; Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; - } else { - var result = new Array(length); - for (var i = 0; i < length; i += 4) { - result[i] = cartographicDegrees[i]; - - scratchCartographic.longitude = CesiumMath.toRadians(cartographicDegrees[i + 1]); - scratchCartographic.latitude = CesiumMath.toRadians(cartographicDegrees[i + 2]); - scratchCartographic.height = cartographicDegrees[i + 3]; - Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); - - result[i + 1] = scratchCartesian.x; - result[i + 2] = scratchCartesian.y; - result[i + 3] = scratchCartesian.z; - } - return result; } + + var result = new Array(length); + for (var i = 0; i < length; i += 4) { + result[i] = cartographicDegrees[i]; + + scratchCartographic.longitude = CesiumMath.toRadians(cartographicDegrees[i + 1]); + scratchCartographic.latitude = CesiumMath.toRadians(cartographicDegrees[i + 2]); + scratchCartographic.height = cartographicDegrees[i + 3]; + Ellipsoid.WGS84.cartographicToCartesian(scratchCartographic, scratchCartesian); + + result[i + 1] = scratchCartesian.x; + result[i + 2] = scratchCartesian.y; + result[i + 3] = scratchCartesian.z; + } + return result; } function unwrapCartesianInterval(czmlInterval) { @@ -486,16 +496,14 @@ define([ return Uri; } else if (czmlInterval.hasOwnProperty('verticalOrigin')) { return VerticalOrigin; - } else { - // fallback case - return Object; } + // fallback case + return Object; } - function unwrapInterval(type, czmlInterval, sourceUri) { + function unwrapInterval(type, czmlInterval, sourceUri, query) { // The associations in this function need to be kept in sync with the // associations in getPropertyType - /*jshint sub:true*/ switch (type) { case Array: return czmlInterval.array; @@ -518,7 +526,7 @@ define([ case HorizontalOrigin: return HorizontalOrigin[defaultValue(czmlInterval.horizontalOrigin, czmlInterval)]; case Image: - return unwrapUriInterval(czmlInterval, sourceUri); + return unwrapUriInterval(czmlInterval, sourceUri, query); case JulianDate: return JulianDate.fromIso8601(defaultValue(czmlInterval.date, czmlInterval)); case LabelStyle: @@ -542,7 +550,7 @@ define([ case Rectangle: return unwrapRectangleInterval(czmlInterval); case Uri: - return unwrapUriInterval(czmlInterval, sourceUri); + return unwrapUriInterval(czmlInterval, sourceUri, query); case VerticalOrigin: return VerticalOrigin[defaultValue(czmlInterval.verticalOrigin, czmlInterval)]; default: @@ -586,7 +594,11 @@ define([ } } - function processProperty(type, object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection) { + var iso8601Scratch = { + iso8601 : undefined + }; + + function processProperty(type, object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, query) { var combinedInterval; var packetInterval = packetData.interval; if (defined(packetInterval)) { @@ -607,7 +619,7 @@ define([ var hasInterval = defined(combinedInterval) && !combinedInterval.equals(Iso8601.MAXIMUM_INTERVAL); if (!isReference) { - unwrappedInterval = unwrapInterval(type, packetData, sourceUri); + unwrappedInterval = unwrapInterval(type, packetData, sourceUri, query); packedLength = defaultValue(type.packedLength, 1); unwrappedIntervalLength = defaultValue(unwrappedInterval.length, 1); isSampled = !defined(packetData.array) && (typeof unwrappedInterval !== 'string') && (unwrappedIntervalLength > packedLength) && (type !== Object); @@ -737,21 +749,21 @@ define([ updateInterpolationSettings(packetData, interval.data); } - function processPacketData(type, object, propertyName, packetData, interval, sourceUri, entityCollection) { + function processPacketData(type, object, propertyName, packetData, interval, sourceUri, entityCollection, query) { if (!defined(packetData)) { return; } if (isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; i++) { - processProperty(type, object, propertyName, packetData[i], interval, sourceUri, entityCollection); + processProperty(type, object, propertyName, packetData[i], interval, sourceUri, entityCollection, query); } } else { - processProperty(type, object, propertyName, packetData, interval, sourceUri, entityCollection); + processProperty(type, object, propertyName, packetData, interval, sourceUri, entityCollection, query); } } - function processPositionProperty(object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection) { + function processPositionProperty(object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, query) { var combinedInterval; var packetInterval = packetData.interval; if (defined(packetInterval)) { @@ -897,21 +909,21 @@ define([ updateInterpolationSettings(packetData, interval.data); } - function processPositionPacketData(object, propertyName, packetData, interval, sourceUri, entityCollection) { + function processPositionPacketData(object, propertyName, packetData, interval, sourceUri, entityCollection, query) { if (!defined(packetData)) { return; } if (isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; i++) { - processPositionProperty(object, propertyName, packetData[i], interval, sourceUri, entityCollection); + processPositionProperty(object, propertyName, packetData[i], interval, sourceUri, entityCollection, query); } } else { - processPositionProperty(object, propertyName, packetData, interval, sourceUri, entityCollection); + processPositionProperty(object, propertyName, packetData, interval, sourceUri, entityCollection, query); } } - function processMaterialProperty(object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection) { + function processMaterialProperty(object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, query) { var combinedInterval; var packetInterval = packetData.interval; if (defined(packetInterval)) { @@ -964,51 +976,60 @@ define([ existingMaterial = new GridMaterialProperty(); } materialData = packetData.grid; - processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection); - processPacketData(Number, existingMaterial, 'cellAlpha', materialData.cellAlpha, undefined, sourceUri, entityCollection); - processPacketData(Cartesian2, existingMaterial, 'lineCount', materialData.lineCount, undefined, sourceUri, entityCollection); - processPacketData(Cartesian2, existingMaterial, 'lineThickness', materialData.lineThickness, undefined, sourceUri, entityCollection); - processPacketData(Cartesian2, existingMaterial, 'lineOffset', materialData.lineOffset, undefined, sourceUri, entityCollection); + processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'cellAlpha', materialData.cellAlpha, undefined, sourceUri, entityCollection, query); + processPacketData(Cartesian2, existingMaterial, 'lineCount', materialData.lineCount, undefined, sourceUri, entityCollection, query); + processPacketData(Cartesian2, existingMaterial, 'lineThickness', materialData.lineThickness, undefined, sourceUri, entityCollection, query); + processPacketData(Cartesian2, existingMaterial, 'lineOffset', materialData.lineOffset, undefined, sourceUri, entityCollection, query); } else if (defined(packetData.image)) { if (!(existingMaterial instanceof ImageMaterialProperty)) { existingMaterial = new ImageMaterialProperty(); } materialData = packetData.image; - processPacketData(Image, existingMaterial, 'image', materialData.image, undefined, sourceUri, entityCollection); - processPacketData(Cartesian2, existingMaterial, 'repeat', materialData.repeat, undefined, sourceUri, entityCollection); - processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection); - processPacketData(Boolean, existingMaterial, 'transparent', materialData.transparent, undefined, sourceUri, entityCollection); + processPacketData(Image, existingMaterial, 'image', materialData.image, undefined, sourceUri, entityCollection, query); + processPacketData(Cartesian2, existingMaterial, 'repeat', materialData.repeat, undefined, sourceUri, entityCollection, query); + processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection, query); + processPacketData(Boolean, existingMaterial, 'transparent', materialData.transparent, undefined, sourceUri, entityCollection, query); } else if (defined(packetData.stripe)) { if (!(existingMaterial instanceof StripeMaterialProperty)) { existingMaterial = new StripeMaterialProperty(); } materialData = packetData.stripe; - processPacketData(StripeOrientation, existingMaterial, 'orientation', materialData.orientation, undefined, sourceUri, entityCollection); - processPacketData(Color, existingMaterial, 'evenColor', materialData.evenColor, undefined, sourceUri, entityCollection); - processPacketData(Color, existingMaterial, 'oddColor', materialData.oddColor, undefined, sourceUri, entityCollection); - processPacketData(Number, existingMaterial, 'offset', materialData.offset, undefined, sourceUri, entityCollection); - processPacketData(Number, existingMaterial, 'repeat', materialData.repeat, undefined, sourceUri, entityCollection); + processPacketData(StripeOrientation, existingMaterial, 'orientation', materialData.orientation, undefined, sourceUri, entityCollection, query); + processPacketData(Color, existingMaterial, 'evenColor', materialData.evenColor, undefined, sourceUri, entityCollection, query); + processPacketData(Color, existingMaterial, 'oddColor', materialData.oddColor, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'offset', materialData.offset, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'repeat', materialData.repeat, undefined, sourceUri, entityCollection, query); } else if (defined(packetData.polylineOutline)) { if (!(existingMaterial instanceof PolylineOutlineMaterialProperty)) { existingMaterial = new PolylineOutlineMaterialProperty(); } materialData = packetData.polylineOutline; - processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection); - processPacketData(Color, existingMaterial, 'outlineColor', materialData.outlineColor, undefined, sourceUri, entityCollection); - processPacketData(Number, existingMaterial, 'outlineWidth', materialData.outlineWidth, undefined, sourceUri, entityCollection); + processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection, query); + processPacketData(Color, existingMaterial, 'outlineColor', materialData.outlineColor, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'outlineWidth', materialData.outlineWidth, undefined, sourceUri, entityCollection, query); } else if (defined(packetData.polylineGlow)) { if (!(existingMaterial instanceof PolylineGlowMaterialProperty)) { existingMaterial = new PolylineGlowMaterialProperty(); } materialData = packetData.polylineGlow; - processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection); - processPacketData(Number, existingMaterial, 'glowPower', materialData.glowPower, undefined, sourceUri, entityCollection); + processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'glowPower', materialData.glowPower, undefined, sourceUri, entityCollection, query); } else if (defined(packetData.polylineArrow)) { if (!(existingMaterial instanceof PolylineArrowMaterialProperty)) { existingMaterial = new PolylineArrowMaterialProperty(); } materialData = packetData.polylineArrow; processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, undefined, entityCollection); + } else if (defined(packetData.polylineDash)) { + if (!(existingMaterial instanceof PolylineDashMaterialProperty)) { + existingMaterial = new PolylineDashMaterialProperty(); + } + materialData = packetData.polylineDash; + processPacketData(Color, existingMaterial, 'color', materialData.color, undefined, undefined, entityCollection); + processPacketData(Color, existingMaterial, 'gapColor', materialData.gapColor, undefined, undefined, entityCollection); + processPacketData(Number, existingMaterial, 'dashLength', materialData.dashLength, undefined, sourceUri, entityCollection, query); + processPacketData(Number, existingMaterial, 'dashPattern', materialData.dashPattern, undefined, sourceUri, entityCollection, query); } if (defined(existingInterval)) { @@ -1018,53 +1039,53 @@ define([ } } - function processMaterialPacketData(object, propertyName, packetData, interval, sourceUri, entityCollection) { + function processMaterialPacketData(object, propertyName, packetData, interval, sourceUri, entityCollection, query) { if (!defined(packetData)) { return; } if (isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; i++) { - processMaterialProperty(object, propertyName, packetData[i], interval, sourceUri, entityCollection); + processMaterialProperty(object, propertyName, packetData[i], interval, sourceUri, entityCollection, query); } } else { - processMaterialProperty(object, propertyName, packetData, interval, sourceUri, entityCollection); + processMaterialProperty(object, propertyName, packetData, interval, sourceUri, entityCollection, query); } } - function processName(entity, packet, entityCollection, sourceUri) { + function processName(entity, packet, entityCollection, sourceUri, query) { entity.name = defaultValue(packet.name, entity.name); } - function processDescription(entity, packet, entityCollection, sourceUri) { + function processDescription(entity, packet, entityCollection, sourceUri, query) { var descriptionData = packet.description; if (defined(descriptionData)) { - processPacketData(String, entity, 'description', descriptionData, undefined, sourceUri, entityCollection); + processPacketData(String, entity, 'description', descriptionData, undefined, sourceUri, entityCollection, query); } } - function processPosition(entity, packet, entityCollection, sourceUri) { + function processPosition(entity, packet, entityCollection, sourceUri, query) { var positionData = packet.position; if (defined(positionData)) { - processPositionPacketData(entity, 'position', positionData, undefined, sourceUri, entityCollection); + processPositionPacketData(entity, 'position', positionData, undefined, sourceUri, entityCollection, query); } } - function processViewFrom(entity, packet, entityCollection, sourceUri) { + function processViewFrom(entity, packet, entityCollection, sourceUri, query) { var viewFromData = packet.viewFrom; if (defined(viewFromData)) { - processPacketData(Cartesian3, entity, 'viewFrom', viewFromData, undefined, sourceUri, entityCollection); + processPacketData(Cartesian3, entity, 'viewFrom', viewFromData, undefined, sourceUri, entityCollection, query); } } - function processOrientation(entity, packet, entityCollection, sourceUri) { + function processOrientation(entity, packet, entityCollection, sourceUri, query) { var orientationData = packet.orientation; if (defined(orientationData)) { - processPacketData(Quaternion, entity, 'orientation', orientationData, undefined, sourceUri, entityCollection); + processPacketData(Quaternion, entity, 'orientation', orientationData, undefined, sourceUri, entityCollection, query); } } - function processProperties(entity, packet, entityCollection, sourceUri) { + function processProperties(entity, packet, entityCollection, sourceUri, query) { var propertiesData = packet.properties; if (defined(propertiesData)) { if (!defined(entity.properties)) { @@ -1083,10 +1104,10 @@ define([ var propertyData = propertiesData[key]; if (isArray(propertyData)) { for (var i = 0, len = propertyData.length; i < len; i++) { - processProperty(getPropertyType(propertyData[i]), entity.properties, key, propertyData[i], undefined, sourceUri, entityCollection); + processProperty(getPropertyType(propertyData[i]), entity.properties, key, propertyData[i], undefined, sourceUri, entityCollection, query); } } else { - processProperty(getPropertyType(propertyData), entity.properties, key, propertyData, undefined, sourceUri, entityCollection); + processProperty(getPropertyType(propertyData), entity.properties, key, propertyData, undefined, sourceUri, entityCollection, query); } } } @@ -1178,7 +1199,7 @@ define([ } } - function processAvailability(entity, packet, entityCollection, sourceUri) { + function processAvailability(entity, packet, entityCollection, sourceUri, query) { var interval; var packetData = packet.availability; if (!defined(packetData)) { @@ -1205,11 +1226,7 @@ define([ entity.availability = intervals; } - var iso8601Scratch = { - iso8601 : undefined - }; - - function processAlignedAxis(billboard, packetData, interval, sourceUri, entityCollection) { + function processAlignedAxis(billboard, packetData, interval, sourceUri, entityCollection, query) { if (!defined(packetData)) { return; } @@ -1217,11 +1234,11 @@ define([ if (defined(packetData.velocityReference)) { billboard.alignedAxis = new VelocityVectorProperty(makeReference(entityCollection, packetData.velocityReference), true); } else { - processPacketData(Cartesian3, billboard, 'alignedAxis', packetData, interval, sourceUri, entityCollection); + processPacketData(Cartesian3, billboard, 'alignedAxis', packetData, interval, sourceUri, entityCollection, query); } } - function processBillboard(entity, packet, entityCollection, sourceUri) { + function processBillboard(entity, packet, entityCollection, sourceUri, query) { var billboardData = packet.billboard; if (!defined(billboardData)) { return; @@ -1239,27 +1256,27 @@ define([ entity.billboard = billboard = new BillboardGraphics(); } - processPacketData(Boolean, billboard, 'show', billboardData.show, interval, sourceUri, entityCollection); - processPacketData(Image, billboard, 'image', billboardData.image, interval, sourceUri, entityCollection); - processPacketData(Number, billboard, 'scale', billboardData.scale, interval, sourceUri, entityCollection); - processPacketData(Cartesian2, billboard, 'pixelOffset', billboardData.pixelOffset, interval, sourceUri, entityCollection); - processPacketData(Cartesian3, billboard, 'eyeOffset', billboardData.eyeOffset, interval, sourceUri, entityCollection); - processPacketData(HorizontalOrigin, billboard, 'horizontalOrigin', billboardData.horizontalOrigin, interval, sourceUri, entityCollection); - processPacketData(VerticalOrigin, billboard, 'verticalOrigin', billboardData.verticalOrigin, interval, sourceUri, entityCollection); - processPacketData(HeightReference, billboard, 'heightReference', billboardData.heightReference, interval, sourceUri, entityCollection); - processPacketData(Color, billboard, 'color', billboardData.color, interval, sourceUri, entityCollection); - processPacketData(Rotation, billboard, 'rotation', billboardData.rotation, interval, sourceUri, entityCollection); - processAlignedAxis(billboard, billboardData.alignedAxis, interval, sourceUri, entityCollection); - processPacketData(Boolean, billboard, 'sizeInMeters', billboardData.sizeInMeters, interval, sourceUri, entityCollection); - processPacketData(Number, billboard, 'width', billboardData.width, interval, sourceUri, entityCollection); - processPacketData(Number, billboard, 'height', billboardData.height, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, billboard, 'scaleByDistance', billboardData.scaleByDistance, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, billboard, 'translucencyByDistance', billboardData.translucencyByDistance, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, billboard, 'pixelOffsetScaleByDistance', billboardData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection); - processPacketData(BoundingRectangle, billboard, 'imageSubRegion', billboardData.imageSubRegion, interval, sourceUri, entityCollection); + processPacketData(Boolean, billboard, 'show', billboardData.show, interval, sourceUri, entityCollection, query); + processPacketData(Image, billboard, 'image', billboardData.image, interval, sourceUri, entityCollection, query); + processPacketData(Number, billboard, 'scale', billboardData.scale, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian2, billboard, 'pixelOffset', billboardData.pixelOffset, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian3, billboard, 'eyeOffset', billboardData.eyeOffset, interval, sourceUri, entityCollection, query); + processPacketData(HorizontalOrigin, billboard, 'horizontalOrigin', billboardData.horizontalOrigin, interval, sourceUri, entityCollection, query); + processPacketData(VerticalOrigin, billboard, 'verticalOrigin', billboardData.verticalOrigin, interval, sourceUri, entityCollection, query); + processPacketData(HeightReference, billboard, 'heightReference', billboardData.heightReference, interval, sourceUri, entityCollection, query); + processPacketData(Color, billboard, 'color', billboardData.color, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, billboard, 'rotation', billboardData.rotation, interval, sourceUri, entityCollection, query); + processAlignedAxis(billboard, billboardData.alignedAxis, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, billboard, 'sizeInMeters', billboardData.sizeInMeters, interval, sourceUri, entityCollection, query); + processPacketData(Number, billboard, 'width', billboardData.width, interval, sourceUri, entityCollection, query); + processPacketData(Number, billboard, 'height', billboardData.height, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, billboard, 'scaleByDistance', billboardData.scaleByDistance, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, billboard, 'translucencyByDistance', billboardData.translucencyByDistance, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, billboard, 'pixelOffsetScaleByDistance', billboardData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection, query); + processPacketData(BoundingRectangle, billboard, 'imageSubRegion', billboardData.imageSubRegion, interval, sourceUri, entityCollection, query); } - function processBox(entity, packet, entityCollection, sourceUri) { + function processBox(entity, packet, entityCollection, sourceUri, query) { var boxData = packet.box; if (!defined(boxData)) { return; @@ -1277,17 +1294,17 @@ define([ entity.box = box = new BoxGraphics(); } - processPacketData(Boolean, box, 'show', boxData.show, interval, sourceUri, entityCollection); - processPacketData(Cartesian3, box, 'dimensions', boxData.dimensions, interval, sourceUri, entityCollection); - processPacketData(Boolean, box, 'fill', boxData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(box, 'material', boxData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, box, 'outline', boxData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, box, 'outlineColor', boxData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, box, 'outlineWidth', boxData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, box, 'shadows', boxData.shadows, interval, sourceUri, entityCollection); + processPacketData(Boolean, box, 'show', boxData.show, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian3, box, 'dimensions', boxData.dimensions, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, box, 'fill', boxData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(box, 'material', boxData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, box, 'outline', boxData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, box, 'outlineColor', boxData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, box, 'outlineWidth', boxData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, box, 'shadows', boxData.shadows, interval, sourceUri, entityCollection, query); } - function processCorridor(entity, packet, entityCollection, sourceUri) { + function processCorridor(entity, packet, entityCollection, sourceUri, query) { var corridorData = packet.corridor; if (!defined(corridorData)) { return; @@ -1305,22 +1322,22 @@ define([ entity.corridor = corridor = new CorridorGraphics(); } - processPacketData(Boolean, corridor, 'show', corridorData.show, interval, sourceUri, entityCollection); + processPacketData(Boolean, corridor, 'show', corridorData.show, interval, sourceUri, entityCollection, query); processPositions(corridor, 'positions', corridorData.positions, entityCollection); - processPacketData(Number, corridor, 'width', corridorData.width, interval, sourceUri, entityCollection); - processPacketData(Number, corridor, 'height', corridorData.height, interval, sourceUri, entityCollection); - processPacketData(Number, corridor, 'extrudedHeight', corridorData.extrudedHeight, interval, sourceUri, entityCollection); - processPacketData(CornerType, corridor, 'cornerType', corridorData.cornerType, interval, sourceUri, entityCollection); - processPacketData(Number, corridor, 'granularity', corridorData.granularity, interval, sourceUri, entityCollection); - processPacketData(Boolean, corridor, 'fill', corridorData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(corridor, 'material', corridorData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, corridor, 'outline', corridorData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, corridor, 'outlineColor', corridorData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, corridor, 'outlineWidth', corridorData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, corridor, 'shadows', corridorData.shadows, interval, sourceUri, entityCollection); + processPacketData(Number, corridor, 'width', corridorData.width, interval, sourceUri, entityCollection, query); + processPacketData(Number, corridor, 'height', corridorData.height, interval, sourceUri, entityCollection, query); + processPacketData(Number, corridor, 'extrudedHeight', corridorData.extrudedHeight, interval, sourceUri, entityCollection, query); + processPacketData(CornerType, corridor, 'cornerType', corridorData.cornerType, interval, sourceUri, entityCollection, query); + processPacketData(Number, corridor, 'granularity', corridorData.granularity, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, corridor, 'fill', corridorData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(corridor, 'material', corridorData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, corridor, 'outline', corridorData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, corridor, 'outlineColor', corridorData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, corridor, 'outlineWidth', corridorData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, corridor, 'shadows', corridorData.shadows, interval, sourceUri, entityCollection, query); } - function processCylinder(entity, packet, entityCollection, sourceUri) { + function processCylinder(entity, packet, entityCollection, sourceUri, query) { var cylinderData = packet.cylinder; if (!defined(cylinderData)) { return; @@ -1338,18 +1355,18 @@ define([ entity.cylinder = cylinder = new CylinderGraphics(); } - processPacketData(Boolean, cylinder, 'show', cylinderData.show, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'length', cylinderData.length, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'topRadius', cylinderData.topRadius, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'bottomRadius', cylinderData.bottomRadius, interval, sourceUri, entityCollection); - processPacketData(Boolean, cylinder, 'fill', cylinderData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(cylinder, 'material', cylinderData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, cylinder, 'outline', cylinderData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, cylinder, 'outlineColor', cylinderData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'outlineWidth', cylinderData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'numberOfVerticalLines', cylinderData.numberOfVerticalLines, interval, sourceUri, entityCollection); - processPacketData(Number, cylinder, 'slices', cylinderData.slices, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, cylinder, 'shadows', cylinderData.shadows, interval, sourceUri, entityCollection); + processPacketData(Boolean, cylinder, 'show', cylinderData.show, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'length', cylinderData.length, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'topRadius', cylinderData.topRadius, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'bottomRadius', cylinderData.bottomRadius, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, cylinder, 'fill', cylinderData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(cylinder, 'material', cylinderData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, cylinder, 'outline', cylinderData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, cylinder, 'outlineColor', cylinderData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'outlineWidth', cylinderData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'numberOfVerticalLines', cylinderData.numberOfVerticalLines, interval, sourceUri, entityCollection, query); + processPacketData(Number, cylinder, 'slices', cylinderData.slices, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, cylinder, 'shadows', cylinderData.shadows, interval, sourceUri, entityCollection, query); } function processDocument(packet, dataSource) { @@ -1397,7 +1414,7 @@ define([ } } - function processEllipse(entity, packet, entityCollection, sourceUri) { + function processEllipse(entity, packet, entityCollection, sourceUri, query) { var ellipseData = packet.ellipse; if (!defined(ellipseData)) { return; @@ -1415,24 +1432,24 @@ define([ entity.ellipse = ellipse = new EllipseGraphics(); } - processPacketData(Boolean, ellipse, 'show', ellipseData.show, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'semiMajorAxis', ellipseData.semiMajorAxis, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'semiMinorAxis', ellipseData.semiMinorAxis, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'height', ellipseData.height, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'extrudedHeight', ellipseData.extrudedHeight, interval, sourceUri, entityCollection); - processPacketData(Rotation, ellipse, 'rotation', ellipseData.rotation, interval, sourceUri, entityCollection); - processPacketData(Rotation, ellipse, 'stRotation', ellipseData.stRotation, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'granularity', ellipseData.granularity, interval, sourceUri, entityCollection); - processPacketData(Boolean, ellipse, 'fill', ellipseData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(ellipse, 'material', ellipseData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, ellipse, 'outline', ellipseData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, ellipse, 'outlineColor', ellipseData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'outlineWidth', ellipseData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(Number, ellipse, 'numberOfVerticalLines', ellipseData.numberOfVerticalLines, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, ellipse, 'shadows', ellipseData.shadows, interval, sourceUri, entityCollection); + processPacketData(Boolean, ellipse, 'show', ellipseData.show, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'semiMajorAxis', ellipseData.semiMajorAxis, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'semiMinorAxis', ellipseData.semiMinorAxis, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'height', ellipseData.height, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'extrudedHeight', ellipseData.extrudedHeight, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, ellipse, 'rotation', ellipseData.rotation, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, ellipse, 'stRotation', ellipseData.stRotation, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'granularity', ellipseData.granularity, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, ellipse, 'fill', ellipseData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(ellipse, 'material', ellipseData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, ellipse, 'outline', ellipseData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, ellipse, 'outlineColor', ellipseData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'outlineWidth', ellipseData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipse, 'numberOfVerticalLines', ellipseData.numberOfVerticalLines, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, ellipse, 'shadows', ellipseData.shadows, interval, sourceUri, entityCollection, query); } - function processEllipsoid(entity, packet, entityCollection, sourceUri) { + function processEllipsoid(entity, packet, entityCollection, sourceUri, query) { var ellipsoidData = packet.ellipsoid; if (!defined(ellipsoidData)) { return; @@ -1450,20 +1467,20 @@ define([ entity.ellipsoid = ellipsoid = new EllipsoidGraphics(); } - processPacketData(Boolean, ellipsoid, 'show', ellipsoidData.show, interval, sourceUri, entityCollection); - processPacketData(Cartesian3, ellipsoid, 'radii', ellipsoidData.radii, interval, sourceUri, entityCollection); - processPacketData(Boolean, ellipsoid, 'fill', ellipsoidData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(ellipsoid, 'material', ellipsoidData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, ellipsoid, 'outline', ellipsoidData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, ellipsoid, 'outlineColor', ellipsoidData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, ellipsoid, 'outlineWidth', ellipsoidData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(Number, ellipsoid, 'stackPartitions', ellipsoidData.stackPartitions, interval, sourceUri, entityCollection); - processPacketData(Number, ellipsoid, 'slicePartitions', ellipsoidData.slicePartitions, interval, sourceUri, entityCollection); - processPacketData(Number, ellipsoid, 'subdivisions', ellipsoidData.subdivisions, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, ellipsoid, 'shadows', ellipsoidData.shadows, interval, sourceUri, entityCollection); + processPacketData(Boolean, ellipsoid, 'show', ellipsoidData.show, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian3, ellipsoid, 'radii', ellipsoidData.radii, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, ellipsoid, 'fill', ellipsoidData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(ellipsoid, 'material', ellipsoidData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, ellipsoid, 'outline', ellipsoidData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, ellipsoid, 'outlineColor', ellipsoidData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipsoid, 'outlineWidth', ellipsoidData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipsoid, 'stackPartitions', ellipsoidData.stackPartitions, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipsoid, 'slicePartitions', ellipsoidData.slicePartitions, interval, sourceUri, entityCollection, query); + processPacketData(Number, ellipsoid, 'subdivisions', ellipsoidData.subdivisions, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, ellipsoid, 'shadows', ellipsoidData.shadows, interval, sourceUri, entityCollection, query); } - function processLabel(entity, packet, entityCollection, sourceUri) { + function processLabel(entity, packet, entityCollection, sourceUri, query) { var labelData = packet.label; if (!defined(labelData)) { return; @@ -1481,27 +1498,27 @@ define([ entity.label = label = new LabelGraphics(); } - processPacketData(Boolean, label, 'show', labelData.show, interval, sourceUri, entityCollection); - processPacketData(String, label, 'text', labelData.text, interval, sourceUri, entityCollection); - processPacketData(String, label, 'font', labelData.font, interval, sourceUri, entityCollection); - processPacketData(LabelStyle, label, 'style', labelData.style, interval, sourceUri, entityCollection); - processPacketData(Number, label, 'scale', labelData.scale, interval, sourceUri, entityCollection); - processPacketData(Boolean, label, 'showBackground', labelData.showBackground, interval, sourceUri, entityCollection); - processPacketData(Color, label, 'backgroundColor', labelData.backgroundColor, interval, sourceUri, entityCollection); - processPacketData(Cartesian2, label, 'backgroundPadding', labelData.backgroundPadding, interval, sourceUri, entityCollection); - processPacketData(Cartesian2, label, 'pixelOffset', labelData.pixelOffset, interval, sourceUri, entityCollection); - processPacketData(Cartesian3, label, 'eyeOffset', labelData.eyeOffset, interval, sourceUri, entityCollection); - processPacketData(HorizontalOrigin, label, 'horizontalOrigin', labelData.horizontalOrigin, interval, sourceUri, entityCollection); - processPacketData(VerticalOrigin, label, 'verticalOrigin', labelData.verticalOrigin, interval, sourceUri, entityCollection); - processPacketData(HeightReference, label, 'heightReference', labelData.heightReference, interval, sourceUri, entityCollection); - processPacketData(Color, label, 'fillColor', labelData.fillColor, interval, sourceUri, entityCollection); - processPacketData(Color, label, 'outlineColor', labelData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, label, 'outlineWidth', labelData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, label, 'translucencyByDistance', labelData.translucencyByDistance, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, label, 'pixelOffsetScaleByDistance', labelData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection); + processPacketData(Boolean, label, 'show', labelData.show, interval, sourceUri, entityCollection, query); + processPacketData(String, label, 'text', labelData.text, interval, sourceUri, entityCollection, query); + processPacketData(String, label, 'font', labelData.font, interval, sourceUri, entityCollection, query); + processPacketData(LabelStyle, label, 'style', labelData.style, interval, sourceUri, entityCollection, query); + processPacketData(Number, label, 'scale', labelData.scale, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, label, 'showBackground', labelData.showBackground, interval, sourceUri, entityCollection, query); + processPacketData(Color, label, 'backgroundColor', labelData.backgroundColor, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian2, label, 'backgroundPadding', labelData.backgroundPadding, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian2, label, 'pixelOffset', labelData.pixelOffset, interval, sourceUri, entityCollection, query); + processPacketData(Cartesian3, label, 'eyeOffset', labelData.eyeOffset, interval, sourceUri, entityCollection, query); + processPacketData(HorizontalOrigin, label, 'horizontalOrigin', labelData.horizontalOrigin, interval, sourceUri, entityCollection, query); + processPacketData(VerticalOrigin, label, 'verticalOrigin', labelData.verticalOrigin, interval, sourceUri, entityCollection, query); + processPacketData(HeightReference, label, 'heightReference', labelData.heightReference, interval, sourceUri, entityCollection, query); + processPacketData(Color, label, 'fillColor', labelData.fillColor, interval, sourceUri, entityCollection, query); + processPacketData(Color, label, 'outlineColor', labelData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, label, 'outlineWidth', labelData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, label, 'translucencyByDistance', labelData.translucencyByDistance, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, label, 'pixelOffsetScaleByDistance', labelData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection, query); } - function processModel(entity, packet, entityCollection, sourceUri) { + function processModel(entity, packet, entityCollection, sourceUri, query) { var modelData = packet.model; if (!defined(modelData)) { return; @@ -1519,34 +1536,34 @@ define([ entity.model = model = new ModelGraphics(); } - processPacketData(Boolean, model, 'show', modelData.show, interval, sourceUri, entityCollection); - processPacketData(Uri, model, 'uri', modelData.gltf, interval, sourceUri, entityCollection); - processPacketData(Number, model, 'scale', modelData.scale, interval, sourceUri, entityCollection); - processPacketData(Number, model, 'minimumPixelSize', modelData.minimumPixelSize, interval, sourceUri, entityCollection); - processPacketData(Number, model, 'maximumScale', modelData.maximumScale, interval, sourceUri, entityCollection); - processPacketData(Boolean, model, 'incrementallyLoadTextures', modelData.incrementallyLoadTextures, interval, sourceUri, entityCollection); - processPacketData(Boolean, model, 'runAnimations', modelData.runAnimations, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, model, 'shadows', modelData.shadows, interval, sourceUri, entityCollection); - processPacketData(HeightReference, model, 'heightReference', modelData.heightReference, interval, sourceUri, entityCollection); - processPacketData(Color, model, 'silhouetteColor', modelData.silhouetteColor, interval, sourceUri, entityCollection); - processPacketData(Number, model, 'silhouetteSize', modelData.silhouetteSize, interval, sourceUri, entityCollection); - processPacketData(Color, model, 'color', modelData.color, interval, sourceUri, entityCollection); - processPacketData(ColorBlendMode, model, 'colorBlendMode', modelData.colorBlendMode, interval, sourceUri, entityCollection); - processPacketData(Number, model, 'colorBlendAmount', modelData.colorBlendAmount, interval, sourceUri, entityCollection); + processPacketData(Boolean, model, 'show', modelData.show, interval, sourceUri, entityCollection, query); + processPacketData(Uri, model, 'uri', modelData.gltf, interval, sourceUri, entityCollection, query); + processPacketData(Number, model, 'scale', modelData.scale, interval, sourceUri, entityCollection, query); + processPacketData(Number, model, 'minimumPixelSize', modelData.minimumPixelSize, interval, sourceUri, entityCollection, query); + processPacketData(Number, model, 'maximumScale', modelData.maximumScale, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, model, 'incrementallyLoadTextures', modelData.incrementallyLoadTextures, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, model, 'runAnimations', modelData.runAnimations, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, model, 'shadows', modelData.shadows, interval, sourceUri, entityCollection, query); + processPacketData(HeightReference, model, 'heightReference', modelData.heightReference, interval, sourceUri, entityCollection, query); + processPacketData(Color, model, 'silhouetteColor', modelData.silhouetteColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, model, 'silhouetteSize', modelData.silhouetteSize, interval, sourceUri, entityCollection, query); + processPacketData(Color, model, 'color', modelData.color, interval, sourceUri, entityCollection, query); + processPacketData(ColorBlendMode, model, 'colorBlendMode', modelData.colorBlendMode, interval, sourceUri, entityCollection, query); + processPacketData(Number, model, 'colorBlendAmount', modelData.colorBlendAmount, interval, sourceUri, entityCollection, query); var nodeTransformationsData = modelData.nodeTransformations; if (defined(nodeTransformationsData)) { if (isArray(nodeTransformationsData)) { for (var i = 0, len = nodeTransformationsData.length; i < len; i++) { - processNodeTransformations(model, nodeTransformationsData[i], interval, sourceUri, entityCollection); + processNodeTransformations(model, nodeTransformationsData[i], interval, sourceUri, entityCollection, query); } } else { - processNodeTransformations(model, nodeTransformationsData, interval, sourceUri, entityCollection); + processNodeTransformations(model, nodeTransformationsData, interval, sourceUri, entityCollection, query); } } } - function processNodeTransformations(model, nodeTransformationsData, constrainedInterval, sourceUri, entityCollection) { + function processNodeTransformations(model, nodeTransformationsData, constrainedInterval, sourceUri, entityCollection, query) { var combinedInterval; var packetInterval = nodeTransformationsData.interval; if (defined(packetInterval)) { @@ -1587,13 +1604,13 @@ define([ nodeTransformations[nodeName] = nodeTransformation = new NodeTransformationProperty(); } - processPacketData(Cartesian3, nodeTransformation, 'translation', nodeTransformationData.translation, combinedInterval, sourceUri, entityCollection); - processPacketData(Quaternion, nodeTransformation, 'rotation', nodeTransformationData.rotation, combinedInterval, sourceUri, entityCollection); - processPacketData(Cartesian3, nodeTransformation, 'scale', nodeTransformationData.scale, combinedInterval, sourceUri, entityCollection); + processPacketData(Cartesian3, nodeTransformation, 'translation', nodeTransformationData.translation, combinedInterval, sourceUri, entityCollection, query); + processPacketData(Quaternion, nodeTransformation, 'rotation', nodeTransformationData.rotation, combinedInterval, sourceUri, entityCollection, query); + processPacketData(Cartesian3, nodeTransformation, 'scale', nodeTransformationData.scale, combinedInterval, sourceUri, entityCollection, query); } } - function processPath(entity, packet, entityCollection, sourceUri) { + function processPath(entity, packet, entityCollection, sourceUri, query) { var pathData = packet.path; if (!defined(pathData)) { return; @@ -1611,15 +1628,15 @@ define([ entity.path = path = new PathGraphics(); } - processPacketData(Boolean, path, 'show', pathData.show, interval, sourceUri, entityCollection); - processPacketData(Number, path, 'width', pathData.width, interval, sourceUri, entityCollection); - processPacketData(Number, path, 'resolution', pathData.resolution, interval, sourceUri, entityCollection); - processPacketData(Number, path, 'leadTime', pathData.leadTime, interval, sourceUri, entityCollection); - processPacketData(Number, path, 'trailTime', pathData.trailTime, interval, sourceUri, entityCollection); - processMaterialPacketData(path, 'material', pathData.material, interval, sourceUri, entityCollection); + processPacketData(Boolean, path, 'show', pathData.show, interval, sourceUri, entityCollection, query); + processPacketData(Number, path, 'width', pathData.width, interval, sourceUri, entityCollection, query); + processPacketData(Number, path, 'resolution', pathData.resolution, interval, sourceUri, entityCollection, query); + processPacketData(Number, path, 'leadTime', pathData.leadTime, interval, sourceUri, entityCollection, query); + processPacketData(Number, path, 'trailTime', pathData.trailTime, interval, sourceUri, entityCollection, query); + processMaterialPacketData(path, 'material', pathData.material, interval, sourceUri, entityCollection, query); } - function processPoint(entity, packet, entityCollection, sourceUri) { + function processPoint(entity, packet, entityCollection, sourceUri, query) { var pointData = packet.point; if (!defined(pointData)) { return; @@ -1637,17 +1654,17 @@ define([ entity.point = point = new PointGraphics(); } - processPacketData(Boolean, point, 'show', pointData.show, interval, sourceUri, entityCollection); - processPacketData(Number, point, 'pixelSize', pointData.pixelSize, interval, sourceUri, entityCollection); - processPacketData(HeightReference, point, 'heightReference', pointData.heightReference, interval, sourceUri, entityCollection); - processPacketData(Color, point, 'color', pointData.color, interval, sourceUri, entityCollection); - processPacketData(Color, point, 'outlineColor', pointData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, point, 'outlineWidth', pointData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, point, 'scaleByDistance', pointData.scaleByDistance, interval, sourceUri, entityCollection); - processPacketData(NearFarScalar, point, 'translucencyByDistance', pointData.translucencyByDistance, interval, sourceUri, entityCollection); + processPacketData(Boolean, point, 'show', pointData.show, interval, sourceUri, entityCollection, query); + processPacketData(Number, point, 'pixelSize', pointData.pixelSize, interval, sourceUri, entityCollection, query); + processPacketData(HeightReference, point, 'heightReference', pointData.heightReference, interval, sourceUri, entityCollection, query); + processPacketData(Color, point, 'color', pointData.color, interval, sourceUri, entityCollection, query); + processPacketData(Color, point, 'outlineColor', pointData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, point, 'outlineWidth', pointData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, point, 'scaleByDistance', pointData.scaleByDistance, interval, sourceUri, entityCollection, query); + processPacketData(NearFarScalar, point, 'translucencyByDistance', pointData.translucencyByDistance, interval, sourceUri, entityCollection, query); } - function processPolygon(entity, packet, entityCollection, sourceUri) { + function processPolygon(entity, packet, entityCollection, sourceUri, query) { var polygonData = packet.polygon; if (!defined(polygonData)) { return; @@ -1665,24 +1682,24 @@ define([ entity.polygon = polygon = new PolygonGraphics(); } - processPacketData(Boolean, polygon, 'show', polygonData.show, interval, sourceUri, entityCollection); + processPacketData(Boolean, polygon, 'show', polygonData.show, interval, sourceUri, entityCollection, query); processPositions(polygon, 'hierarchy', polygonData.positions, entityCollection); - processPacketData(Number, polygon, 'height', polygonData.height, interval, sourceUri, entityCollection); - processPacketData(Number, polygon, 'extrudedHeight', polygonData.extrudedHeight, interval, sourceUri, entityCollection); - processPacketData(Rotation, polygon, 'stRotation', polygonData.stRotation, interval, sourceUri, entityCollection); - processPacketData(Number, polygon, 'granularity', polygonData.granularity, interval, sourceUri, entityCollection); - processPacketData(Boolean, polygon, 'fill', polygonData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(polygon, 'material', polygonData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, polygon, 'outline', polygonData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, polygon, 'outlineColor', polygonData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, polygon, 'outlineWidth', polygonData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(Boolean, polygon, 'perPositionHeight', polygonData.perPositionHeight, interval, sourceUri, entityCollection); - processPacketData(Boolean, polygon, 'closeTop', polygonData.closeTop, interval, sourceUri, entityCollection); - processPacketData(Boolean, polygon, 'closeBottom', polygonData.closeBottom, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, polygon, 'shadows', polygonData.shadows, interval, sourceUri, entityCollection); + processPacketData(Number, polygon, 'height', polygonData.height, interval, sourceUri, entityCollection, query); + processPacketData(Number, polygon, 'extrudedHeight', polygonData.extrudedHeight, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, polygon, 'stRotation', polygonData.stRotation, interval, sourceUri, entityCollection, query); + processPacketData(Number, polygon, 'granularity', polygonData.granularity, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polygon, 'fill', polygonData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(polygon, 'material', polygonData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polygon, 'outline', polygonData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, polygon, 'outlineColor', polygonData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, polygon, 'outlineWidth', polygonData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polygon, 'perPositionHeight', polygonData.perPositionHeight, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polygon, 'closeTop', polygonData.closeTop, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polygon, 'closeBottom', polygonData.closeBottom, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, polygon, 'shadows', polygonData.shadows, interval, sourceUri, entityCollection, query); } - function processPolyline(entity, packet, entityCollection, sourceUri) { + function processPolyline(entity, packet, entityCollection, sourceUri, query) { var polylineData = packet.polyline; if (!defined(polylineData)) { return; @@ -1700,16 +1717,16 @@ define([ entity.polyline = polyline = new PolylineGraphics(); } - processPacketData(Boolean, polyline, 'show', polylineData.show, interval, sourceUri, entityCollection); + processPacketData(Boolean, polyline, 'show', polylineData.show, interval, sourceUri, entityCollection, query); processPositions(polyline, 'positions', polylineData.positions, entityCollection); - processPacketData(Number, polyline, 'width', polylineData.width, interval, sourceUri, entityCollection); - processPacketData(Number, polyline, 'granularity', polylineData.granularity, interval, sourceUri, entityCollection); - processMaterialPacketData(polyline, 'material', polylineData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, polyline, 'followSurface', polylineData.followSurface, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, polyline, 'shadows', polylineData.shadows, interval, sourceUri, entityCollection); + processPacketData(Number, polyline, 'width', polylineData.width, interval, sourceUri, entityCollection, query); + processPacketData(Number, polyline, 'granularity', polylineData.granularity, interval, sourceUri, entityCollection, query); + processMaterialPacketData(polyline, 'material', polylineData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, polyline, 'followSurface', polylineData.followSurface, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, polyline, 'shadows', polylineData.shadows, interval, sourceUri, entityCollection, query); } - function processRectangle(entity, packet, entityCollection, sourceUri) { + function processRectangle(entity, packet, entityCollection, sourceUri, query) { var rectangleData = packet.rectangle; if (!defined(rectangleData)) { return; @@ -1727,24 +1744,24 @@ define([ entity.rectangle = rectangle = new RectangleGraphics(); } - processPacketData(Boolean, rectangle, 'show', rectangleData.show, interval, sourceUri, entityCollection); - processPacketData(Rectangle, rectangle, 'coordinates', rectangleData.coordinates, interval, sourceUri, entityCollection); - processPacketData(Number, rectangle, 'height', rectangleData.height, interval, sourceUri, entityCollection); - processPacketData(Number, rectangle, 'extrudedHeight', rectangleData.extrudedHeight, interval, sourceUri, entityCollection); - processPacketData(Rotation, rectangle, 'rotation', rectangleData.rotation, interval, sourceUri, entityCollection); - processPacketData(Rotation, rectangle, 'stRotation', rectangleData.stRotation, interval, sourceUri, entityCollection); - processPacketData(Number, rectangle, 'granularity', rectangleData.granularity, interval, sourceUri, entityCollection); - processPacketData(Boolean, rectangle, 'fill', rectangleData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(rectangle, 'material', rectangleData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, rectangle, 'outline', rectangleData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, rectangle, 'outlineColor', rectangleData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, rectangle, 'outlineWidth', rectangleData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(Boolean, rectangle, 'closeTop', rectangleData.closeTop, interval, sourceUri, entityCollection); - processPacketData(Boolean, rectangle, 'closeBottom', rectangleData.closeBottom, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, rectangle, 'shadows', rectangleData.shadows, interval, sourceUri, entityCollection); + processPacketData(Boolean, rectangle, 'show', rectangleData.show, interval, sourceUri, entityCollection, query); + processPacketData(Rectangle, rectangle, 'coordinates', rectangleData.coordinates, interval, sourceUri, entityCollection, query); + processPacketData(Number, rectangle, 'height', rectangleData.height, interval, sourceUri, entityCollection, query); + processPacketData(Number, rectangle, 'extrudedHeight', rectangleData.extrudedHeight, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, rectangle, 'rotation', rectangleData.rotation, interval, sourceUri, entityCollection, query); + processPacketData(Rotation, rectangle, 'stRotation', rectangleData.stRotation, interval, sourceUri, entityCollection, query); + processPacketData(Number, rectangle, 'granularity', rectangleData.granularity, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, rectangle, 'fill', rectangleData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(rectangle, 'material', rectangleData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, rectangle, 'outline', rectangleData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, rectangle, 'outlineColor', rectangleData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, rectangle, 'outlineWidth', rectangleData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, rectangle, 'closeTop', rectangleData.closeTop, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, rectangle, 'closeBottom', rectangleData.closeBottom, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, rectangle, 'shadows', rectangleData.shadows, interval, sourceUri, entityCollection, query); } - function processWall(entity, packet, entityCollection, sourceUri) { + function processWall(entity, packet, entityCollection, sourceUri, query) { var wallData = packet.wall; if (!defined(wallData)) { return; @@ -1762,20 +1779,20 @@ define([ entity.wall = wall = new WallGraphics(); } - processPacketData(Boolean, wall, 'show', wallData.show, interval, sourceUri, entityCollection); + processPacketData(Boolean, wall, 'show', wallData.show, interval, sourceUri, entityCollection, query); processPositions(wall, 'positions', wallData.positions, entityCollection); processArray(wall, 'minimumHeights', wallData.minimumHeights, entityCollection); processArray(wall, 'maximumHeights', wallData.maximumHeights, entityCollection); - processPacketData(Number, wall, 'granularity', wallData.granularity, interval, sourceUri, entityCollection); - processPacketData(Boolean, wall, 'fill', wallData.fill, interval, sourceUri, entityCollection); - processMaterialPacketData(wall, 'material', wallData.material, interval, sourceUri, entityCollection); - processPacketData(Boolean, wall, 'outline', wallData.outline, interval, sourceUri, entityCollection); - processPacketData(Color, wall, 'outlineColor', wallData.outlineColor, interval, sourceUri, entityCollection); - processPacketData(Number, wall, 'outlineWidth', wallData.outlineWidth, interval, sourceUri, entityCollection); - processPacketData(ShadowMode, wall, 'shadows', wallData.shadows, interval, sourceUri, entityCollection); + processPacketData(Number, wall, 'granularity', wallData.granularity, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, wall, 'fill', wallData.fill, interval, sourceUri, entityCollection, query); + processMaterialPacketData(wall, 'material', wallData.material, interval, sourceUri, entityCollection, query); + processPacketData(Boolean, wall, 'outline', wallData.outline, interval, sourceUri, entityCollection, query); + processPacketData(Color, wall, 'outlineColor', wallData.outlineColor, interval, sourceUri, entityCollection, query); + processPacketData(Number, wall, 'outlineWidth', wallData.outlineWidth, interval, sourceUri, entityCollection, query); + processPacketData(ShadowMode, wall, 'shadows', wallData.shadows, interval, sourceUri, entityCollection, query); } - function processCzmlPacket(packet, entityCollection, updaterFunctions, sourceUri, dataSource) { + function processCzmlPacket(packet, entityCollection, updaterFunctions, sourceUri, dataSource, query) { var objectId = packet.id; if (!defined(objectId)) { objectId = createGuid(); @@ -1800,7 +1817,7 @@ define([ } for (var i = updaterFunctions.length - 1; i > -1; i--) { - updaterFunctions[i](entity, packet, entityCollection, sourceUri); + updaterFunctions[i](entity, packet, entityCollection, sourceUri, query); } } @@ -1879,10 +1896,15 @@ define([ //>>includeEnd('debug'); options = defaultValue(options, defaultValue.EMPTY_OBJECT); - var promise = czml; var sourceUri = options.sourceUri; + var query = defined(options.query) ? objectToQuery(options.query) : undefined; + + // If the czml is a URL if (typeof czml === 'string') { + if (defined(query)) { + czml = joinUrls(czml, '?' + query, false); + } promise = loadJson(czml); sourceUri = defaultValue(sourceUri, czml); } @@ -1890,7 +1912,7 @@ define([ DataSource.setLoading(dataSource, true); return when(promise, function(czml) { - return loadCzml(dataSource, czml, sourceUri, clear); + return loadCzml(dataSource, czml, sourceUri, clear, query); }).otherwise(function(error) { DataSource.setLoading(dataSource, false); dataSource._error.raiseEvent(dataSource, error); @@ -1899,7 +1921,7 @@ define([ }); } - function loadCzml(dataSource, czml, sourceUri, clear) { + function loadCzml(dataSource, czml, sourceUri, clear, query) { DataSource.setLoading(dataSource, true); var entityCollection = dataSource._entityCollection; @@ -1909,7 +1931,7 @@ define([ entityCollection.removeAll(); } - CzmlDataSource._processCzml(czml, entityCollection, sourceUri, undefined, dataSource); + CzmlDataSource._processCzml(czml, entityCollection, sourceUri, undefined, dataSource, query); var raiseChangedEvent = updateClock(dataSource); @@ -1963,6 +1985,7 @@ define([ * @param {String|Object} czml A url or CZML object to be processed. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] Overrides the url to use for resolving relative links. + * @param {Object} [options.query] Key-value pairs which are appended to all URIs in the CZML. * @returns {Promise.} A promise that resolves to the new instance once the data is processed. */ CzmlDataSource.load = function(czml, options) { @@ -2111,6 +2134,7 @@ define([ * @param {String|Object} czml A url or CZML object to be processed. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] Overrides the url to use for resolving relative links. + * @param {Object} [options.query] Key-value pairs which are appended to all URIs in the CZML. * @returns {Promise.} A promise that resolves to this instances once the data is processed. */ CzmlDataSource.prototype.process = function(czml, options) { @@ -2123,6 +2147,7 @@ define([ * @param {String|Object} czml A url or CZML object to be processed. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] Overrides the url to use for resolving relative links. + * @param {Object} [options.query] Key-value pairs which are appended to all URIs in the CZML. * @returns {Promise.} A promise that resolves to this instances once the data is processed. */ CzmlDataSource.prototype.load = function(czml, options) { @@ -2172,15 +2197,15 @@ define([ */ CzmlDataSource.processMaterialPacketData = processMaterialPacketData; - CzmlDataSource._processCzml = function(czml, entityCollection, sourceUri, updaterFunctions, dataSource) { + CzmlDataSource._processCzml = function(czml, entityCollection, sourceUri, updaterFunctions, dataSource, query) { updaterFunctions = defined(updaterFunctions) ? updaterFunctions : CzmlDataSource.updaters; if (isArray(czml)) { for (var i = 0, len = czml.length; i < len; i++) { - processCzmlPacket(czml[i], entityCollection, updaterFunctions, sourceUri, dataSource); + processCzmlPacket(czml[i], entityCollection, updaterFunctions, sourceUri, dataSource, query); } } else { - processCzmlPacket(czml, entityCollection, updaterFunctions, sourceUri, dataSource); + processCzmlPacket(czml, entityCollection, updaterFunctions, sourceUri, dataSource, query); } }; diff --git a/Source/DataSources/DataSourceDisplay.js b/Source/DataSources/DataSourceDisplay.js index 3ca8cef027f0..2f6f95f9db30 100644 --- a/Source/DataSources/DataSourceDisplay.js +++ b/Source/DataSources/DataSourceDisplay.js @@ -1,6 +1,7 @@ /*global define*/ define([ '../Core/BoundingSphere', + '../Core/Check', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -28,6 +29,7 @@ define([ './WallGeometryUpdater' ], function( BoundingSphere, + Check, defaultValue, defined, defineProperties, @@ -69,17 +71,11 @@ define([ */ function DataSourceDisplay(options) { //>>includeStart('debug', pragmas.debug); - if (!defined(options)) { - throw new DeveloperError('options is required.'); - } - if (!defined(options.scene)) { - throw new DeveloperError('scene is required.'); - } - if (!defined(options.dataSourceCollection)) { - throw new DeveloperError('dataSourceCollection is required.'); - } + Check.typeOf.object('options', options); + Check.typeOf.object('options.scene', options.scene); + Check.typeOf.object('options.dataSourceCollection', options.dataSourceCollection); //>>includeEnd('debug'); - + GroundPrimitive.initializeTerrainHeights(); var scene = options.scene; @@ -239,7 +235,7 @@ define([ this._ready = false; return false; } - + var result = true; var i; diff --git a/Source/DataSources/EntityCluster.js b/Source/DataSources/EntityCluster.js index c20ce4d3e895..1382684c228d 100644 --- a/Source/DataSources/EntityCluster.js +++ b/Source/DataSources/EntityCluster.js @@ -149,6 +149,7 @@ define([ cluster.point.show = false; cluster.label.show = true; cluster.label.text = numPoints.toLocaleString(); + cluster.label.id = ids; cluster.billboard.position = cluster.label.position = cluster.point.position = position; entityCluster._clusterEvent.raiseEvent(ids, cluster); @@ -723,13 +724,24 @@ define([ // If clustering is enabled before the label collection is updated, // the glyphs haven't been created so the screen space bounding boxes // are incorrect. + var commandList; if (defined(this._labelCollection) && this._labelCollection.length > 0 && this._labelCollection.get(0)._glyphs.length === 0) { - var commandList = frameState.commandList; + commandList = frameState.commandList; frameState.commandList = []; this._labelCollection.update(frameState); frameState.commandList = commandList; } + // If clustering is enabled before the billboard collection is updated, + // the images haven't been added to the image atlas so the screen space bounding boxes + // are incorrect. + if (defined(this._billboardCollection) && this._billboardCollection.length > 0 && !defined(this._billboardCollection.get(0).width)) { + commandList = frameState.commandList; + frameState.commandList = []; + this._billboardCollection.update(frameState); + frameState.commandList = commandList; + } + if (this._enabledDirty) { this._enabledDirty = false; updateEnable(this); diff --git a/Source/DataSources/EntityView.js b/Source/DataSources/EntityView.js index b8857df00372..05610b38e61f 100644 --- a/Source/DataSources/EntityView.js +++ b/Source/DataSources/EntityView.js @@ -47,6 +47,7 @@ define([ var cartesian = positionProperty.getValue(time, that._lastCartesian); if (defined(cartesian)) { var hasBasis = false; + var invertVelocity = false; var xBasis; var yBasis; var zBasis; @@ -54,8 +55,16 @@ define([ if (mode === SceneMode.SCENE3D) { // The time delta was determined based on how fast satellites move compared to vehicles near the surface. // Slower moving vehicles will most likely default to east-north-up, while faster ones will be VVLH. - deltaTime = JulianDate.addSeconds(time, 0.001, deltaTime); + JulianDate.addSeconds(time, 0.001, deltaTime); var deltaCartesian = positionProperty.getValue(deltaTime, updateTransformCartesian3Scratch1); + + // If no valid position at (time + 0.001), sample at (time - 0.001) and invert the vector + if (!defined(deltaCartesian)) { + JulianDate.addSeconds(time, -0.001, deltaTime); + deltaCartesian = positionProperty.getValue(deltaTime, updateTransformCartesian3Scratch1); + invertVelocity = true; + } + if (defined(deltaCartesian)) { var toInertial = Transforms.computeFixedToIcrfMatrix(time, updateTransformMatrix3Scratch1); var toInertialDelta = Transforms.computeFixedToIcrfMatrix(deltaTime, updateTransformMatrix3Scratch2); @@ -114,6 +123,11 @@ define([ // Y is along the angular momentum vector (e.g. "orbit normal") yBasis = Cartesian3.cross(zBasis, inertialDeltaCartesian, updateTransformCartesian3Scratch3); + + if(invertVelocity) { + yBasis = Cartesian3.multiplyByScalar(yBasis, -1, yBasis); + } + if (!Cartesian3.equalsEpsilon(yBasis, Cartesian3.ZERO, CesiumMath.EPSILON7)) { // X is along the cross of y and z (right handed basis / in the direction of motion) xBasis = Cartesian3.cross(yBasis, zBasis, updateTransformCartesian3Scratch1); diff --git a/Source/DataSources/GeoJsonDataSource.js b/Source/DataSources/GeoJsonDataSource.js index 9434069a73a9..072c5cec0779 100644 --- a/Source/DataSources/GeoJsonDataSource.js +++ b/Source/DataSources/GeoJsonDataSource.js @@ -79,10 +79,6 @@ define([ var defaultFill = Color.fromBytes(255, 255, 0, 100); var defaultClampToGround = false; - var defaultStrokeWidthProperty = new ConstantProperty(defaultStrokeWidth); - var defaultStrokeMaterialProperty = new ColorMaterialProperty(defaultStroke); - var defaultFillMaterialProperty = new ColorMaterialProperty(defaultFill); - var sizes = { small : 24, medium : 48, @@ -210,6 +206,30 @@ define([ return positions; } + var geoJsonObjectTypes = { + Feature : processFeature, + FeatureCollection : processFeatureCollection, + GeometryCollection : processGeometryCollection, + LineString : processLineString, + MultiLineString : processMultiLineString, + MultiPoint : processMultiPoint, + MultiPolygon : processMultiPolygon, + Point : processPoint, + Polygon : processPolygon, + Topology : processTopology + }; + + var geometryTypes = { + GeometryCollection : processGeometryCollection, + LineString : processLineString, + MultiLineString : processMultiLineString, + MultiPoint : processMultiPoint, + MultiPolygon : processMultiPolygon, + Point : processPoint, + Polygon : processPolygon, + Topology : processTopology + }; + // GeoJSON processing functions function processFeature(dataSource, feature, notUsed, crsFunction, options) { if (feature.geometry === null) { @@ -461,30 +481,6 @@ define([ } } - var geoJsonObjectTypes = { - Feature : processFeature, - FeatureCollection : processFeatureCollection, - GeometryCollection : processGeometryCollection, - LineString : processLineString, - MultiLineString : processMultiLineString, - MultiPoint : processMultiPoint, - MultiPolygon : processMultiPolygon, - Point : processPoint, - Polygon : processPolygon, - Topology : processTopology - }; - - var geometryTypes = { - GeometryCollection : processGeometryCollection, - LineString : processLineString, - MultiLineString : processMultiLineString, - MultiPoint : processMultiPoint, - MultiPolygon : processMultiPolygon, - Point : processPoint, - Polygon : processPolygon, - Topology : processTopology - }; - /** * A {@link DataSource} which processes both * {@link http://www.geojson.org/|GeoJSON} and {@link https://github.com/mbostock/topojson|TopoJSON} data. @@ -597,7 +593,6 @@ define([ }, set : function(value) { defaultStroke = value; - defaultStrokeMaterialProperty.color.setValue(value); } }, /** @@ -612,7 +607,6 @@ define([ }, set : function(value) { defaultStrokeWidth = value; - defaultStrokeWidthProperty.setValue(value); } }, /** @@ -627,7 +621,6 @@ define([ }, set : function(value) { defaultFill = value; - defaultFillMaterialProperty = new ColorMaterialProperty(defaultFill); } }, /** diff --git a/Source/DataSources/GeometryVisualizer.js b/Source/DataSources/GeometryVisualizer.js index df3cc9e6f5e0..ad62ca0c7be7 100644 --- a/Source/DataSources/GeometryVisualizer.js +++ b/Source/DataSources/GeometryVisualizer.js @@ -95,21 +95,31 @@ define([ that._outlineBatches[shadows].add(time, updater); } + var multiplier = 0; + if (defined(updater.depthFailMaterialProperty)) { + multiplier = updater.depthFailMaterialProperty instanceof ColorMaterialProperty ? 1 : 2; + } + + var index; + if (defined(shadows)) { + index = shadows + multiplier * ShadowMode.NUMBER_OF_SHADOW_MODES; + } + if (updater.fillEnabled) { if (updater.onTerrain) { that._groundColorBatch.add(time, updater); } else { if (updater.isClosed) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - that._closedColorBatches[shadows].add(time, updater); + that._closedColorBatches[index].add(time, updater); } else { - that._closedMaterialBatches[shadows].add(time, updater); + that._closedMaterialBatches[index].add(time, updater); } } else { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - that._openColorBatches[shadows].add(time, updater); + that._openColorBatches[index].add(time, updater); } else { - that._openMaterialBatches[shadows].add(time, updater); + that._openMaterialBatches[index].add(time, updater); } } } @@ -152,17 +162,28 @@ define([ var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; this._outlineBatches = new Array(numberOfShadowModes); - this._closedColorBatches = new Array(numberOfShadowModes); - this._closedMaterialBatches = new Array(numberOfShadowModes); - this._openColorBatches = new Array(numberOfShadowModes); - this._openMaterialBatches = new Array(numberOfShadowModes); + this._closedColorBatches = new Array(numberOfShadowModes * 3); + this._closedMaterialBatches = new Array(numberOfShadowModes * 3); + this._openColorBatches = new Array(numberOfShadowModes * 3); + this._openMaterialBatches = new Array(numberOfShadowModes * 3); for (var i = 0; i < numberOfShadowModes; ++i) { this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i); - this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, true, i); - this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, true, i); - this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, false, i); - this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, false, i); + + this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, undefined, true, i); + this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, undefined, true, i); + this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, undefined, false, i); + this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, undefined, false, i); + + this._closedColorBatches[i + numberOfShadowModes] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, type.perInstanceColorAppearanceType, true, i); + this._closedMaterialBatches[i + numberOfShadowModes] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, type.perInstanceColorAppearanceType, true, i); + this._openColorBatches[i + numberOfShadowModes] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, type.perInstanceColorAppearanceType, false, i); + this._openMaterialBatches[i + numberOfShadowModes] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, type.perInstanceColorAppearanceType, false, i); + + this._closedColorBatches[i + numberOfShadowModes * 2] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, type.materialAppearanceType, true, i); + this._closedMaterialBatches[i + numberOfShadowModes * 2] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, type.materialAppearanceType, true, i); + this._openColorBatches[i + numberOfShadowModes * 2] = new StaticGeometryColorBatch(primitives, type.perInstanceColorAppearanceType, type.materialAppearanceType, false, i); + this._openMaterialBatches[i + numberOfShadowModes * 2] = new StaticGeometryPerMaterialBatch(primitives, type.materialAppearanceType, type.materialAppearanceType, false, i); } this._groundColorBatch = new StaticGroundGeometryColorBatch(groundPrimitives); diff --git a/Source/DataSources/KmlDataSource.js b/Source/DataSources/KmlDataSource.js index 0807ee87b7bd..1f4eac18977d 100644 --- a/Source/DataSources/KmlDataSource.js +++ b/Source/DataSources/KmlDataSource.js @@ -25,6 +25,7 @@ define([ '../Core/loadXML', '../Core/Math', '../Core/NearFarScalar', + '../Core/objectToQuery', '../Core/PinBuilder', '../Core/PolygonHierarchy', '../Core/Rectangle', @@ -84,6 +85,7 @@ define([ loadXML, CesiumMath, NearFarScalar, + objectToQuery, PinBuilder, PolygonHierarchy, Rectangle, @@ -253,7 +255,7 @@ define([ }); } - function replaceAttributes(div, elementType, attributeName, uriResolver) { + function embedDataUris(div, elementType, attributeName, uriResolver) { var keys = uriResolver.keys; var baseUri = new Uri('.'); var elements = div.querySelectorAll(elementType); @@ -272,12 +274,25 @@ define([ } } - function proxyUrl(url, proxy) { + function applyBasePath(div, elementType, attributeName, proxy, sourceUri, query) { + var elements = div.querySelectorAll(elementType); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + var value = element.getAttribute(attributeName); + var uri = resolveHref(value, proxy, sourceUri, query); + element.setAttribute(attributeName, uri); + } + } + + function proxyUrl(url, proxy, query) { if (defined(proxy)) { if (new Uri(url).isAbsolute()) { url = proxy.getURL(url); } } + if (defined(query)) { + url = joinUrls(url, '?' + query, false); + } return url; } @@ -457,7 +472,7 @@ define([ return undefined; } - function resolveHref(href, proxy, sourceUri, uriResolver) { + function resolveHref(href, proxy, sourceUri, uriResolver, query) { if (!defined(href)) { return undefined; } @@ -477,9 +492,11 @@ define([ } } } - if (!hrefResolved && defined(sourceUri)) { - href = getAbsoluteUri(href, getAbsoluteUri(sourceUri)); - href = proxyUrl(href, proxy); + if (!hrefResolved) { + if (defined(sourceUri)) { + href = getAbsoluteUri(href, getAbsoluteUri(sourceUri)); + } + href = proxyUrl(href, proxy, query); } return href; } @@ -620,7 +637,7 @@ define([ return label; } - function getIconHref(iconNode, dataSource, sourceUri, uriResolver, canRefresh) { + function getIconHref(iconNode, dataSource, sourceUri, uriResolver, canRefresh, query) { var href = queryStringValue(iconNode, 'href', namespaces.kml); if (!defined(href) || (href.length === 0)) { return undefined; @@ -639,7 +656,7 @@ define([ href = 'https://maps.google.com/mapfiles/kml/pal' + palette + '/icon' + iconNum + '.png'; } - href = resolveHref(href, dataSource._proxy, sourceUri, uriResolver); + href = resolveHref(href, dataSource._proxy, sourceUri, uriResolver, query); if (canRefresh) { var refreshMode = queryStringValue(iconNode, 'refreshMode', namespaces.kml); @@ -663,13 +680,13 @@ define([ return href; } - function processBillboardIcon(dataSource, node, targetEntity, sourceUri, uriResolver) { + function processBillboardIcon(dataSource, node, targetEntity, sourceUri, uriResolver, query) { var scale = queryNumericValue(node, 'scale', namespaces.kml); var heading = queryNumericValue(node, 'heading', namespaces.kml); var color = queryColorValue(node, 'color', namespaces.kml); var iconNode = queryFirstNode(node, 'Icon', namespaces.kml); - var icon = getIconHref(iconNode, dataSource, sourceUri, uriResolver, false); + var icon = getIconHref(iconNode, dataSource, sourceUri, uriResolver, false, query); var x = queryNumericValue(iconNode, 'x', namespaces.gx); var y = queryNumericValue(iconNode, 'y', namespaces.gx); var w = queryNumericValue(iconNode, 'w', namespaces.gx); @@ -738,11 +755,11 @@ define([ } } - function applyStyle(dataSource, styleNode, targetEntity, sourceUri, uriResolver) { + function applyStyle(dataSource, styleNode, targetEntity, sourceUri, uriResolver, query) { for (var i = 0, len = styleNode.childNodes.length; i < len; i++) { var node = styleNode.childNodes.item(i); if (node.localName === 'IconStyle') { - processBillboardIcon(dataSource, node, targetEntity, sourceUri, uriResolver); + processBillboardIcon(dataSource, node, targetEntity, sourceUri, uriResolver, query); } else if (node.localName === 'LabelStyle') { var label = targetEntity.label; if (!defined(label)) { @@ -804,7 +821,7 @@ define([ } //Processes and merges any inline styles for the provided node into the provided entity. - function computeFinalStyle(entity, dataSource, placeMark, styleCollection, sourceUri, uriResolver) { + function computeFinalStyle(entity, dataSource, placeMark, styleCollection, sourceUri, uriResolver, query) { var result = new Entity(); var styleEntity; @@ -822,7 +839,7 @@ define([ if (styleIndex !== -1) { var inlineStyleNode = childNodes[styleIndex]; if (inlineStyleNode.localName === 'Style') { - applyStyle(dataSource, inlineStyleNode, result, sourceUri, uriResolver); + applyStyle(dataSource, inlineStyleNode, result, sourceUri, uriResolver, query); } else { // StyleMap var pairs = queryChildNodes(inlineStyleNode, 'Pair', namespaces.kml); for (var p = 0; p < pairs.length; p++) { @@ -840,7 +857,7 @@ define([ } } else { var node = queryFirstNode(pair, 'Style', namespaces.kml); - applyStyle(dataSource, node, result, sourceUri, uriResolver); + applyStyle(dataSource, node, result, sourceUri, uriResolver, query); } } else { console.log('KML - Unsupported StyleMap key: ' + key); @@ -875,8 +892,8 @@ define([ } //Asynchronously processes an external style file. - function processExternalStyles(dataSource, uri, styleCollection) { - return loadXML(proxyUrl(uri, dataSource._proxy)).then(function(styleKml) { + function processExternalStyles(dataSource, uri, styleCollection, query) { + return loadXML(proxyUrl(uri, dataSource._proxy, query)).then(function(styleKml) { return processStyles(dataSource, styleKml, styleCollection, uri, true); }); } @@ -885,7 +902,7 @@ define([ //their id into the provided styleCollection. //Returns an array of promises that will resolve when //each style is loaded. - function processStyles(dataSource, kml, styleCollection, sourceUri, isExternal, uriResolver) { + function processStyles(dataSource, kml, styleCollection, sourceUri, isExternal, uriResolver, query) { var i; var id; var styleEntity; @@ -907,7 +924,7 @@ define([ id : id }); styleCollection.add(styleEntity); - applyStyle(dataSource, node, styleEntity, sourceUri, uriResolver); + applyStyle(dataSource, node, styleEntity, sourceUri, uriResolver, query); } } } @@ -948,7 +965,7 @@ define([ } } else { node = queryFirstNode(pair, 'Style', namespaces.kml); - applyStyle(dataSource, node, styleEntity, sourceUri, uriResolver); + applyStyle(dataSource, node, styleEntity, sourceUri, uriResolver, query); } } } else { @@ -977,7 +994,7 @@ define([ if (defined(sourceUri)) { uri = getAbsoluteUri(uri, getAbsoluteUri(sourceUri)); } - promises.push(processExternalStyles(dataSource, uri, styleCollection, sourceUri)); + promises.push(processExternalStyles(dataSource, uri, styleCollection, query)); } } } @@ -1371,6 +1388,17 @@ define([ return true; } + var geometryTypes = { + Point : processPoint, + LineString : processLineStringOrLinearRing, + LinearRing : processLineStringOrLinearRing, + Polygon : processPolygon, + Track : processTrack, + MultiTrack : processMultiTrack, + MultiGeometry : processMultiGeometry, + Model : processUnsupportedGeometry + }; + function processMultiGeometry(dataSource, entityCollection, geometryNode, entity, styleEntity, context) { var childNodes = geometryNode.childNodes; var hasGeometry = false; @@ -1431,7 +1459,7 @@ define([ var scratchDiv = document.createElement('div'); - function processDescription(node, entity, styleEntity, uriResolver) { + function processDescription(node, entity, styleEntity, uriResolver, proxy, sourceUri) { var i; var key; var keys; @@ -1516,10 +1544,14 @@ define([ //Rewrite any KMZ embedded urls if (defined(uriResolver) && uriResolver.keys.length > 1) { - replaceAttributes(scratchDiv, 'a', 'href', uriResolver); - replaceAttributes(scratchDiv, 'img', 'src', uriResolver); + embedDataUris(scratchDiv, 'a', 'href', uriResolver); + embedDataUris(scratchDiv, 'img', 'src', uriResolver); } + //Make relative urls absolute using the sourceUri + applyBasePath(scratchDiv, 'a', 'href', proxy, sourceUri); + applyBasePath(scratchDiv, 'img', 'src', proxy, sourceUri); + var tmp = '
>includeStart('debug', pragmas.debug); if (!defined(options)) { - throw new DeveloperError('options is required.'); + return; } - //>>includeEnd('debug'); var valuesChanged = false; var interpolationAlgorithm = options.interpolationAlgorithm; var interpolationDegree = options.interpolationDegree; - if (this._interpolationAlgorithm !== interpolationAlgorithm) { + if (defined(interpolationAlgorithm) && this._interpolationAlgorithm !== interpolationAlgorithm) { this._interpolationAlgorithm = interpolationAlgorithm; valuesChanged = true; } - if (this._interpolationDegree !== interpolationDegree) { + if (defined(interpolationDegree) && this._interpolationDegree !== interpolationDegree) { this._interpolationDegree = interpolationDegree; valuesChanged = true; } diff --git a/Source/DataSources/StaticGeometryColorBatch.js b/Source/DataSources/StaticGeometryColorBatch.js index eca570d7a0ef..782075db8e9a 100644 --- a/Source/DataSources/StaticGeometryColorBatch.js +++ b/Source/DataSources/StaticGeometryColorBatch.js @@ -9,6 +9,8 @@ define([ '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', + './ColorMaterialProperty', + './MaterialProperty', './Property' ], function( AssociativeArray, @@ -20,15 +22,20 @@ define([ ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, + ColorMaterialProperty, + MaterialProperty, Property) { 'use strict'; var colorScratch = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); - function Batch(primitives, translucent, appearanceType, closed, shadows) { + function Batch(primitives, translucent, appearanceType, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.translucent = translucent; this.appearanceType = appearanceType; + this.depthFailAppearanceType = depthFailAppearanceType; + this.depthFailMaterialProperty = depthFailMaterialProperty; + this.depthFailMaterial = undefined; this.closed = closed; this.shadows = shadows; this.primitives = primitives; @@ -43,8 +50,31 @@ define([ this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); this.itemsToRemove = []; + this.invalidated = false; + + var removeMaterialSubscription; + if (defined(depthFailMaterialProperty)) { + removeMaterialSubscription = depthFailMaterialProperty.definitionChanged.addEventListener(Batch.prototype.onMaterialChanged, this); + } + this.removeMaterialSubscription = removeMaterialSubscription; } + Batch.prototype.onMaterialChanged = function() { + this.invalidated = true; + }; + + Batch.prototype.isMaterial = function(updater) { + var material = this.depthFailMaterialProperty; + var updaterMaterial = updater.depthFailMaterialProperty; + if (updaterMaterial === material) { + return true; + } + if (defined(material)) { + return material.equals(updaterMaterial); + } + return false; + }; + Batch.prototype.add = function(updater, instance) { var id = updater.entity.id; this.createPrimitive = true; @@ -107,9 +137,24 @@ define([ if (defined(originalAttributes.color)) { originalAttributes.color.value = attributes.color; } + if (defined(originalAttributes.depthFailColor)) { + originalAttributes.depthFailColor.value = attributes.depthFailColor; + } } } + var depthFailAppearance; + if (defined(this.depthFailAppearanceType)) { + if (defined(this.depthFailMaterialProperty)) { + this.depthFailMaterial = MaterialProperty.getValue(time, this.depthFailMaterialProperty, this.depthFailMaterial); + } + depthFailAppearance = new this.depthFailAppearanceType({ + material : this.depthFailMaterial, + translucent : this.translucent, + closed : this.closed + }); + } + primitive = new Primitive({ asynchronous : true, geometryInstances : geometries, @@ -117,6 +162,7 @@ define([ translucent : this.translucent, closed : this.closed }), + depthFailAppearance : depthFailAppearance, shadows : this.shadows }); primitives.add(primitive); @@ -142,6 +188,12 @@ define([ primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } + + if (defined(this.depthFailAppearanceType) && !(this.depthFailMaterialProperty instanceof ColorMaterialProperty)) { + this.depthFailMaterial = MaterialProperty.getValue(time, this.depthFailMaterialProperty, this.depthFailMaterial); + this.primitive.depthFailAppearance.material = this.depthFailMaterial; + } + var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; var waitingOnCreate = this.waitingOnCreate; @@ -167,6 +219,15 @@ define([ } } + if (defined(this.depthFailAppearanceType) && this.depthFailAppearanceType instanceof ColorMaterialProperty && (!updater.depthFailMaterialProperty.isConstant || waitingOnCreate)) { + var depthFailColorProperty = updater.depthFailMaterialProperty.color; + depthFailColorProperty.getValue(time, colorScratch); + if (!Color.equals(attributes._lastDepthFailColor, colorScratch)) { + attributes._lastDepthFailColor = Color.clone(colorScratch, attributes._lastDepthFailColor); + attributes.depthFailColor = ColorGeometryInstanceAttribute.toValue(colorScratch, attributes.depthFailColor); + } + } + var show = updater.entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { @@ -250,80 +311,171 @@ define([ } }; + Batch.prototype.destroy = function() { + var primitive = this.primitive; + var primitives = this.primitives; + if (defined(primitive)) { + primitives.remove(primitive); + } + var oldPrimitive = this.oldPrimitive; + if (defined(oldPrimitive)) { + primitives.remove(oldPrimitive); + } + if(defined(this.removeMaterialSubscription)) { + this.removeMaterialSubscription(); + } + }; + /** * @private */ - function StaticGeometryColorBatch(primitives, appearanceType, closed, shadows) { - this._solidBatch = new Batch(primitives, false, appearanceType, closed, shadows); - this._translucentBatch = new Batch(primitives, true, appearanceType, closed, shadows); + function StaticGeometryColorBatch(primitives, appearanceType, depthFailAppearanceType, closed, shadows) { + this._solidItems = []; + this._translucentItems = []; + this._primitives = primitives; + this._appearanceType = appearanceType; + this._depthFailAppearanceType = depthFailAppearanceType; + this._closed = closed; + this._shadows = shadows; } StaticGeometryColorBatch.prototype.add = function(time, updater) { + var items; + var translucent; var instance = updater.createFillGeometryInstance(time); if (instance.attributes.color.value[3] === 255) { - this._solidBatch.add(updater, instance); + items = this._solidItems; + translucent = false; } else { - this._translucentBatch.add(updater, instance); + items = this._translucentItems; + translucent = true; + } + + var length = items.length; + for (var i = 0; i < length; i++) { + var item = items[i]; + if (item.isMaterial(updater)) { + item.add(updater, instance); + return; + } } + var batch = new Batch(this._primitives, translucent, this._appearanceType, this._depthFailAppearanceType, updater.depthFailMaterialProperty, this._closed, this._shadows); + batch.add(updater, instance); + items.push(batch); }; + function removeItem(items, updater) { + var length = items.length; + for (var i = length - 1; i >= 0; i--) { + var item = items[i]; + if (item.remove(updater)) { + if (item.updaters.length === 0) { + items.splice(i, 1); + item.destroy(); + return true; + } + } + } + return false; + } + StaticGeometryColorBatch.prototype.remove = function(updater) { - if (!this._solidBatch.remove(updater)) { - this._translucentBatch.remove(updater); + if (!removeItem(this._solidItems, updater)) { + removeItem(this._translucentItems, updater); } }; - StaticGeometryColorBatch.prototype.update = function(time) { + function moveItems(batch, items, time) { + var itemsMoved = false; + var length = items.length; + for (var i = 0; i < length; ++i) { + var item = items[i]; + var itemsToRemove = item.itemsToRemove; + var itemsToMoveLength = itemsToRemove.length; + if (itemsToMoveLength > 0) { + for (i = 0; i < itemsToMoveLength; i++) { + var updater = itemsToRemove[i]; + item.remove(updater); + batch.add(time, updater); + itemsMoved = true; + } + } + } + return itemsMoved; + } + + function updateItems(batch, items, time, isUpdated) { + var length = items.length; var i; - var updater; + for (i = length - 1; i >= 0; i--) { + var item = items[i]; + if (item.invalidated) { + items.splice(i, 1); + var updaters = item.updaters.values; + var updatersLength = updaters.length; + for (var h = 0; h < updatersLength; h++) { + batch.add(time, updaters[h]); + } + item.destroy(); + } + } + + length = items.length; + for (i = 0; i < length; ++i) { + isUpdated = items[i].update(time) && isUpdated; + } + return isUpdated; + } + StaticGeometryColorBatch.prototype.update = function(time) { //Perform initial update - var isUpdated = this._solidBatch.update(time); - isUpdated = this._translucentBatch.update(time) && isUpdated; + var isUpdated = updateItems(this, this._solidItems, time, true); + isUpdated = updateItems(this, this._translucentItems, time, isUpdated) && isUpdated; //If any items swapped between solid/translucent, we need to //move them between batches - var itemsToRemove = this._solidBatch.itemsToRemove; - var solidsToMoveLength = itemsToRemove.length; - if (solidsToMoveLength > 0) { - for (i = 0; i < solidsToMoveLength; i++) { - updater = itemsToRemove[i]; - this._solidBatch.remove(updater); - this._translucentBatch.add(updater, updater.createFillGeometryInstance(time)); - } - } - - itemsToRemove = this._translucentBatch.itemsToRemove; - var translucentToMoveLength = itemsToRemove.length; - if (translucentToMoveLength > 0) { - for (i = 0; i < translucentToMoveLength; i++) { - updater = itemsToRemove[i]; - this._translucentBatch.remove(updater); - this._solidBatch.add(updater, updater.createFillGeometryInstance(time)); - } - } + var solidsMoved = moveItems(this, this._solidItems, time); + var translucentsMoved = moveItems(this, this._translucentItems, time); //If we moved anything around, we need to re-build the primitive - if (solidsToMoveLength > 0 || translucentToMoveLength > 0) { - isUpdated = this._solidBatch.update(time) && isUpdated; - isUpdated = this._translucentBatch.update(time) && isUpdated; + if (solidsMoved || translucentsMoved) { + isUpdated = updateItems(this, this._solidItems, time, isUpdated) && isUpdated; + isUpdated = updateItems(this, this._translucentItems, time, isUpdated)&& isUpdated; } return isUpdated; }; - StaticGeometryColorBatch.prototype.getBoundingSphere = function(entity, result) { - if (this._solidBatch.contains(entity)) { - return this._solidBatch.getBoundingSphere(entity, result); - } else if (this._translucentBatch.contains(entity)) { - return this._translucentBatch.getBoundingSphere(entity, result); + function getBoundingSphere(items, entity, result) { + var length = items.length; + for (var i = 0; i < length; i++) { + var item = items[i]; + if(item.contains(entity)){ + return item.getBoundingSphere(entity, result); + } } return BoundingSphereState.FAILED; + } + + StaticGeometryColorBatch.prototype.getBoundingSphere = function(entity, result) { + var boundingSphere = getBoundingSphere(this._solidItems, entity, result); + if (boundingSphere === BoundingSphereState.FAILED) { + return getBoundingSphere(this._translucentItems, entity, result); + } + return boundingSphere; }; + function removeAllPrimitives(items) { + var length = items.length; + for (var i = 0; i < length; i++) { + items[i].destroy(); + } + items.length = 0; + } + StaticGeometryColorBatch.prototype.removeAllPrimitives = function() { - this._solidBatch.removeAllPrimitives(); - this._translucentBatch.removeAllPrimitives(); + removeAllPrimitives(this._solidItems); + removeAllPrimitives(this._translucentItems); }; return StaticGeometryColorBatch; diff --git a/Source/DataSources/StaticGeometryPerMaterialBatch.js b/Source/DataSources/StaticGeometryPerMaterialBatch.js index df16c8ebf9cc..527c6690130c 100644 --- a/Source/DataSources/StaticGeometryPerMaterialBatch.js +++ b/Source/DataSources/StaticGeometryPerMaterialBatch.js @@ -1,32 +1,40 @@ /*global define*/ define([ '../Core/AssociativeArray', + '../Core/Color', + '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', + './ColorMaterialProperty', './MaterialProperty', './Property' ], function( AssociativeArray, + Color, + ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, + ColorMaterialProperty, MaterialProperty, Property) { 'use strict'; var distanceDisplayConditionScratch = new DistanceDisplayCondition(); - function Batch(primitives, appearanceType, materialProperty, closed, shadows) { + function Batch(primitives, appearanceType, materialProperty, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.primitives = primitives; this.appearanceType = appearanceType; this.materialProperty = materialProperty; + this.depthFailAppearanceType = depthFailAppearanceType; + this.depthFailMaterialProperty = depthFailMaterialProperty; this.closed = closed; this.shadows = shadows; this.updaters = new AssociativeArray(); @@ -35,6 +43,7 @@ define([ this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.material = undefined; + this.depthFailMaterial = undefined; this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.invalidated = false; @@ -42,6 +51,7 @@ define([ this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); } + Batch.prototype.onMaterialChanged = function() { this.invalidated = true; }; @@ -49,13 +59,15 @@ define([ Batch.prototype.isMaterial = function(updater) { var material = this.materialProperty; var updaterMaterial = updater.fillMaterialProperty; - if (updaterMaterial === material) { + var depthFailMaterial = this.depthFailMaterialProperty; + var updaterDepthFailMaterial = updater.depthFailMaterialProperty; + + if (updaterMaterial === material && updaterDepthFailMaterial === depthFailMaterial) { return true; } - if (defined(material)) { - return material.equals(updaterMaterial); - } - return false; + var equals = defined(material) && material.equals(updaterMaterial); + equals = ((!defined(depthFailMaterial) && !defined(updaterDepthFailMaterial)) || (defined(depthFailMaterial) && depthFailMaterial.equals(updaterDepthFailMaterial))) && equals; + return equals; }; Batch.prototype.add = function(time, updater) { @@ -89,6 +101,8 @@ define([ return this.createPrimitive; }; + var colorScratch = new Color(); + Batch.prototype.update = function(time) { var isUpdated = true; var primitive = this.primitive; @@ -120,10 +134,30 @@ define([ if (defined(originalAttributes.color)) { originalAttributes.color.value = attributes.color; } + if (defined(originalAttributes.depthFailColor)) { + originalAttributes.depthFailColor.value = attributes.depthFailColor; + } } } this.material = MaterialProperty.getValue(time, this.materialProperty, this.material); + + var depthFailAppearance; + if (defined(this.depthFailMaterialProperty)) { + var translucent; + if (this.depthFailMaterialProperty instanceof MaterialProperty) { + this.depthFailMaterial = MaterialProperty.getValue(time, this.depthFailMaterialProperty, this.depthFailMaterial); + translucent = this.depthFailMaterial.isTranslucent(); + } else { + translucent = this.material.isTranslucent(); + } + depthFailAppearance = new this.depthFailAppearanceType({ + material : this.depthFailMaterial, + translucent : translucent, + closed : this.closed + }); + } + primitive = new Primitive({ asynchronous : true, geometryInstances : geometries, @@ -132,6 +166,7 @@ define([ translucent : this.material.isTranslucent(), closed : this.closed }), + depthFailAppearance : depthFailAppearance, shadows : this.shadows }); @@ -161,6 +196,11 @@ define([ this.material = MaterialProperty.getValue(time, this.materialProperty, this.material); this.primitive.appearance.material = this.material; + if (defined(this.depthFailAppearanceType) && !(this.depthFailMaterialProperty instanceof ColorMaterialProperty)) { + this.depthFailMaterial = MaterialProperty.getValue(time, this.depthFailMaterialProperty, this.depthFailMaterial); + this.primitive.depthFailAppearance.material = this.depthFailMaterial; + } + var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; for (i = 0; i < length; i++) { @@ -174,6 +214,15 @@ define([ this.attributes.set(instance.id.id, attributes); } + if (defined(this.depthFailAppearanceType) && this.depthFailAppearanceType instanceof ColorMaterialProperty && !updater.depthFailMaterialProperty.isConstant) { + var depthFailColorProperty = updater.depthFailMaterialProperty.color; + depthFailColorProperty.getValue(time, colorScratch); + if (!Color.equals(attributes._lastDepthFailColor, colorScratch)) { + attributes._lastDepthFailColor = Color.clone(colorScratch, attributes._lastDepthFailColor); + attributes.depthFailColor = ColorGeometryInstanceAttribute.toValue(colorScratch, attributes.depthFailColor); + } + } + var show = entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { @@ -238,7 +287,7 @@ define([ return BoundingSphereState.DONE; }; - Batch.prototype.destroy = function(time) { + Batch.prototype.destroy = function() { var primitive = this.primitive; var primitives = this.primitives; if (defined(primitive)) { @@ -254,13 +303,15 @@ define([ /** * @private */ - function StaticGeometryPerMaterialBatch(primitives, appearanceType, closed, shadows) { + function StaticGeometryPerMaterialBatch(primitives, appearanceType, depthFailAppearanceType, closed, shadows) { this._items = []; this._primitives = primitives; this._appearanceType = appearanceType; + this._depthFailAppearanceType = depthFailAppearanceType; this._closed = closed; this._shadows = shadows; } + StaticGeometryPerMaterialBatch.prototype.add = function(time, updater) { var items = this._items; var length = items.length; @@ -271,7 +322,7 @@ define([ return; } } - var batch = new Batch(this._primitives, this._appearanceType, updater.fillMaterialProperty, this._closed, this._shadows); + var batch = new Batch(this._primitives, this._appearanceType, updater.fillMaterialProperty, this._depthFailAppearanceType, updater.depthFailMaterialProperty, this._closed, this._shadows); batch.add(time, updater); items.push(batch); }; diff --git a/Source/DataSources/VelocityVectorProperty.js b/Source/DataSources/VelocityVectorProperty.js index 9b88f8856517..456e2651a308 100644 --- a/Source/DataSources/VelocityVectorProperty.js +++ b/Source/DataSources/VelocityVectorProperty.js @@ -190,9 +190,9 @@ define([ var velocity = Cartesian3.subtract(position2, position1, velocityResult); if (this._normalize) { return Cartesian3.normalize(velocity, velocityResult); - } else { - return Cartesian3.divideByScalar(velocity, step, velocityResult); } + + return Cartesian3.divideByScalar(velocity, step, velocityResult); }; /** diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index 5ea3871b6985..53d4b59b5825 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -1540,11 +1540,27 @@ define([ * @glslUniform */ czm_geometricToleranceOverMeter : new AutomaticUniform({ - size: 1, - datatype: WebGLConstants.FLOAT, - getValue: function(uniformState) { + size : 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { return uniformState.geometricToleranceOverMeter; } + }), + + /** + * An automatic GLSL uniform representing the distance from the camera at which to disable the depth test of billboards, labels and points + * to, for example, prevent clipping against terrain. When set to zero, the depth test should always be applied. When less than zero, + * the depth test should never be applied. + * + * @alias czm_minimumDisableDepthTestDistance + * @glslUniform + */ + czm_minimumDisableDepthTestDistance : new AutomaticUniform({ + size : 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { + return uniformState.minimumDisableDepthTestDistance; + } }) }; diff --git a/Source/Renderer/ClearCommand.js b/Source/Renderer/ClearCommand.js index 8df9098f2e54..898282117f65 100644 --- a/Source/Renderer/ClearCommand.js +++ b/Source/Renderer/ClearCommand.js @@ -77,6 +77,15 @@ define([ * @see Scene#debugCommandFilter */ this.owner = options.owner; + + /** + * The pass in which to run this command. + * + * @type {Pass} + * + * @default undefined + */ + this.pass = options.pass; } /** diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index 3f092b8346fb..7966c47e0418 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -138,11 +138,10 @@ define([ var glWrapper = {}; - /*jslint forin: true*/ - /*jshint forin: false*/ - // JSLint normally demands that a for..in loop must directly contain an if, + // JavaScript linters normally demand that a for..in loop must directly contain an if, // but in our loop below, we actually intend to iterate all properties, including // those in the prototype. + /*eslint-disable guard-for-in*/ for (var propertyName in gl) { var property = gl[propertyName]; @@ -153,6 +152,7 @@ define([ Object.defineProperty(glWrapper, propertyName, makeGetterSetter(gl, propertyName, logFunction)); } } + /*eslint-enable guard-for-in*/ return glWrapper; } diff --git a/Source/Renderer/CubeMap.js b/Source/Renderer/CubeMap.js index 4947d22d9e72..8f29baa1343b 100644 --- a/Source/Renderer/CubeMap.js +++ b/Source/Renderer/CubeMap.js @@ -108,6 +108,8 @@ define([ } //>>includeEnd('debug'); + var sizeInBytes = PixelFormat.textureSizeInBytes(pixelFormat, pixelDatatype, size, size) * 6; + // Use premultiplied alpha for opaque textures should perform better on Chrome: // http://media.tojicode.com/webglCamp4/#20 var preMultiplyAlpha = options.preMultiplyAlpha || ((pixelFormat === PixelFormat.RGB) || (pixelFormat === PixelFormat.LUMINANCE)); @@ -156,6 +158,8 @@ define([ this._pixelFormat = pixelFormat; this._pixelDatatype = pixelDatatype; this._size = size; + this._hasMipmap = false; + this._sizeInBytes = sizeInBytes; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; this._sampler = undefined; @@ -253,11 +257,19 @@ define([ return this._size; } }, - height: { + height : { get : function() { return this._size; } }, + sizeInBytes : { + get : function() { + if (this._hasMipmap) { + return Math.floor(this._sizeInBytes * 4 / 3); + } + return this._sizeInBytes; + } + }, preMultiplyAlpha : { get : function() { return this._preMultiplyAlpha; @@ -306,6 +318,8 @@ define([ } //>>includeEnd('debug'); + this._hasMipmap = true; + var gl = this._gl; var target = this._textureTarget; gl.hint(gl.GENERATE_MIPMAP_HINT, hint); diff --git a/Source/Renderer/Pass.js b/Source/Renderer/Pass.js index 636d02a17e58..6691bc45f9f3 100644 --- a/Source/Renderer/Pass.js +++ b/Source/Renderer/Pass.js @@ -21,11 +21,12 @@ define([ ENVIRONMENT : 0, COMPUTE : 1, GLOBE : 2, - GROUND : 3, - OPAQUE : 4, - TRANSLUCENT : 5, - OVERLAY : 6, - NUMBER_OF_PASSES : 7 + CESIUM_3D_TILE : 3, + GROUND : 4, + OPAQUE : 5, + TRANSLUCENT : 6, + OVERLAY : 7, + NUMBER_OF_PASSES : 8 }; return freezeObject(Pass); diff --git a/Source/Renderer/PixelDatatype.js b/Source/Renderer/PixelDatatype.js index 96358380f8c9..ad5b4846a8ee 100644 --- a/Source/Renderer/PixelDatatype.js +++ b/Source/Renderer/PixelDatatype.js @@ -20,6 +20,29 @@ define([ UNSIGNED_SHORT_5_5_5_1 : WebGLConstants.UNSIGNED_SHORT_5_5_5_1, UNSIGNED_SHORT_5_6_5 : WebGLConstants.UNSIGNED_SHORT_5_6_5, + isPacked : function(pixelDatatype) { + return pixelDatatype === PixelDatatype.UNSIGNED_INT_24_8 || + pixelDatatype === PixelDatatype.UNSIGNED_SHORT_4_4_4_4 || + pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_5_5_1 || + pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_6_5; + }, + + sizeInBytes : function(pixelDatatype) { + switch (pixelDatatype) { + case PixelDatatype.UNSIGNED_BYTE: + return 1; + case PixelDatatype.UNSIGNED_SHORT: + case PixelDatatype.UNSIGNED_SHORT_4_4_4_4: + case PixelDatatype.UNSIGNED_SHORT_5_5_5_1: + case PixelDatatype.UNSIGNED_SHORT_5_6_5: + return 2; + case PixelDatatype.UNSIGNED_INT: + case PixelDatatype.FLOAT: + case PixelDatatype.UNSIGNED_INT_24_8: + return 4; + } + }, + validate : function(pixelDatatype) { return ((pixelDatatype === PixelDatatype.UNSIGNED_BYTE) || (pixelDatatype === PixelDatatype.UNSIGNED_SHORT) || diff --git a/Source/Renderer/Texture.js b/Source/Renderer/Texture.js index 242c91bfba4d..a330e3aecfc0 100644 --- a/Source/Renderer/Texture.js +++ b/Source/Renderer/Texture.js @@ -139,7 +139,7 @@ define([ throw new DeveloperError('When options.pixelFormat is ETC1 compressed, this WebGL implementation must support the WEBGL_texture_compression_etc1 extension. Check context.etc1.'); } - if (PixelFormat.compressedTextureSize(internalFormat, width, height) !== source.arrayBufferView.byteLength) { + if (PixelFormat.compressedTextureSizeInBytes(internalFormat, width, height) !== source.arrayBufferView.byteLength) { throw new DeveloperError('The byte length of the array buffer is invalid for the compressed texture with the given width and height.'); } } @@ -189,6 +189,13 @@ define([ } gl.bindTexture(textureTarget, null); + var sizeInBytes; + if (isCompressed) { + sizeInBytes = PixelFormat.compressedTextureSizeInBytes(pixelFormat, width, height); + } else { + sizeInBytes = PixelFormat.textureSizeInBytes(pixelFormat, pixelDatatype, width, height); + } + this._context = context; this._textureFilterAnisotropic = context._textureFilterAnisotropic; this._textureTarget = textureTarget; @@ -198,6 +205,8 @@ define([ this._width = width; this._height = height; this._dimensions = new Cartesian2(width, height); + this._hasMipmap = false; + this._sizeInBytes = sizeInBytes; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; this._sampler = undefined; @@ -380,6 +389,14 @@ define([ return this._height; } }, + sizeInBytes : { + get : function() { + if (this._hasMipmap) { + return Math.floor(this._sizeInBytes * 4 / 3); + } + return this._sizeInBytes; + } + }, _target : { get : function() { return this._textureTarget; @@ -527,6 +544,7 @@ define([ * @param {MipmapHint} [hint=MipmapHint.DONT_CARE] optional. * * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL. + * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is a compressed format. * @exception {DeveloperError} hint is invalid. * @exception {DeveloperError} This texture's width must be a power of two to call generateMipmap(). * @exception {DeveloperError} This texture's height must be a power of two to call generateMipmap(). @@ -553,6 +571,8 @@ define([ } //>>includeEnd('debug'); + this._hasMipmap = true; + var gl = this._context._gl; var target = this._textureTarget; diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 43c1e827d9f7..7b4a71a3e62b 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -161,6 +161,8 @@ define([ this._imagerySplitPosition = 0.0; this._pixelSizePerMeter = undefined; this._geometricToleranceOverMeter = undefined; + + this._minimumDisableDepthTestDistance = undefined; } defineProperties(UniformState.prototype, { @@ -822,6 +824,20 @@ define([ get : function() { return this._imagerySplitPosition; } + }, + + /** + * The distance from the camera at which to disable the depth test of billboards, labels and points + * to, for example, prevent clipping against terrain. When set to zero, the depth test should always + * be applied. When less than zero, the depth test should never be applied. + * + * @memberof UniformState.prototype + * @type {Number} + */ + minimumDisableDepthTestDistance : { + get : function() { + return this._minimumDisableDepthTestDistance; + } } }); @@ -1001,6 +1017,12 @@ define([ this._geometricToleranceOverMeter = pixelSizePerMeter * frameState.maximumScreenSpaceError; Color.clone(frameState.backgroundColor, this._backgroundColor); + + this._minimumDisableDepthTestDistance = frameState.minimumDisableDepthTestDistance; + this._minimumDisableDepthTestDistance *= this._minimumDisableDepthTestDistance; + if (this._minimumDisableDepthTestDistance === Number.POSITIVE_INFINITY) { + this._minimumDisableDepthTestDistance = -1.0; + } }; function cleanViewport(uniformState) { diff --git a/Source/Renderer/loadCubeMap.js b/Source/Renderer/loadCubeMap.js index 1428fb75ef0b..81d6528aff59 100644 --- a/Source/Renderer/loadCubeMap.js +++ b/Source/Renderer/loadCubeMap.js @@ -20,7 +20,7 @@ define([ * @exports loadCubeMap * * @param {Context} context The context to use to create the cube map. - * @param {Object} urls The source of each image, or a promise for each URL. See the example below. + * @param {Object} urls The source URL of each image. See the example below. * @param {Boolean} [allowCrossOrigin=true] Whether to request the image using Cross-Origin * Resource Sharing (CORS). CORS is only actually used if the image URL is actually cross-origin. * Data URIs are never requested using CORS. diff --git a/Source/Scene/ArcGisMapServerImageryProvider.js b/Source/Scene/ArcGisMapServerImageryProvider.js index 7a9d4c328f9f..fa53f26b766e 100644 --- a/Source/Scene/ArcGisMapServerImageryProvider.js +++ b/Source/Scene/ArcGisMapServerImageryProvider.js @@ -92,7 +92,7 @@ define([ * a tiled server. * * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider @@ -547,7 +547,7 @@ define([ /** * Gets the comma-separated list of layer IDs to show. * @memberof ArcGisMapServerImageryProvider.prototype - * + * * @type {String} */ layers : { @@ -579,6 +579,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -586,7 +587,7 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - ArcGisMapServerImageryProvider.prototype.requestImage = function(x, y, level) { + ArcGisMapServerImageryProvider.prototype.requestImage = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug); if (!this._ready) { throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); @@ -594,7 +595,7 @@ define([ //>>includeEnd('debug'); var url = buildImageUrl(this, x, y, level); - return ImageryProvider.loadImage(this, url); + return ImageryProvider.loadImage(this, url, request); }; /** diff --git a/Source/Scene/AttributeType.js b/Source/Scene/AttributeType.js new file mode 100644 index 000000000000..fb380f843b26 --- /dev/null +++ b/Source/Scene/AttributeType.js @@ -0,0 +1,74 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * An enum describing the attribute type for glTF and 3D Tiles. + * + * @exports AttributeType + * + * @private + */ + var AttributeType = { + /** + * The attribute is a single component. + * + * @type {String} + * @constant + */ + SCALAR : 'SCALAR', + + /** + * The attribute is a two-component vector. + * + * @type {String} + * @constant + */ + VEC2 : 'VEC2', + + /** + * The attribute is a three-component vector. + * + * @type {String} + * @constant + */ + VEC3 : 'VEC3', + + /** + * The attribute is a four-component vector. + * + * @type {String} + * @constant + */ + VEC4 : 'VEC4', + + /** + * The attribute is a 2x2 matrix. + * + * @type {String} + * @constant + */ + MAT2 : 'MAT2', + + /** + * The attribute is a 3x3 matrix. + * + * @type {String} + * @constant + */ + MAT3 : 'MAT3', + + /** + * The attribute is a 4x4 matrix. + * + * @type {String} + * @constant + */ + MAT4 : 'MAT4' + }; + + return freezeObject(AttributeType); +}); diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js new file mode 100644 index 000000000000..21b279df17a3 --- /dev/null +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -0,0 +1,466 @@ +/*global define*/ +define([ + '../Core/Check', + '../Core/Color', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/deprecationWarning', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/FeatureDetection', + '../Core/getAbsoluteUri', + '../Core/getBaseUri', + '../Core/getStringFromTypedArray', + '../Core/RequestType', + '../Core/RuntimeError', + './Cesium3DTileBatchTable', + './Cesium3DTileFeature', + './Cesium3DTileFeatureTable', + './getAttributeOrUniformBySemantic', + './Model' + ], function( + Check, + Color, + defaultValue, + defined, + defineProperties, + deprecationWarning, + destroyObject, + DeveloperError, + FeatureDetection, + getAbsoluteUri, + getBaseUri, + getStringFromTypedArray, + RequestType, + RuntimeError, + Cesium3DTileBatchTable, + Cesium3DTileFeature, + Cesium3DTileFeatureTable, + getAttributeOrUniformBySemantic, + Model) { + 'use strict'; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + /** + * Represents the contents of a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md|Batched 3D Model} + * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset. + *

+ * Implements the {@link Cesium3DTileContent} interface. + *

+ * + * @alias Batched3DModel3DTileContent + * @constructor + * + * @private + */ + function Batched3DModel3DTileContent(tileset, tile, url, arrayBuffer, byteOffset) { + this._tileset = tileset; + this._tile = tile; + this._url = url; + this._model = undefined; + this._batchTable = undefined; + this._features = undefined; + + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + this.featurePropertiesDirty = false; + + initialize(this, arrayBuffer, byteOffset); + } + + // This can be overridden for testing purposes + Batched3DModel3DTileContent._deprecationWarning = deprecationWarning; + + defineProperties(Batched3DModel3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featuresLength + */ + featuresLength : { + get : function() { + return this._batchTable.featuresLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#pointsLength + */ + pointsLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#trianglesLength + */ + trianglesLength : { + get : function() { + return this._model.trianglesLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#geometryByteLength + */ + geometryByteLength : { + get : function() { + return this._model.geometryByteLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#texturesByteLength + */ + texturesByteLength : { + get : function() { + return this._model.texturesByteLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTableByteLength + */ + batchTableByteLength : { + get : function() { + return this._batchTable.memorySizeInBytes; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return this._model.readyPromise; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url: { + get: function() { + return this._url; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTable + */ + batchTable : { + get : function() { + return this._batchTable; + } + } + }); + + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + + function getBatchIdAttributeName(gltf) { + var batchIdAttributeName = getAttributeOrUniformBySemantic(gltf, '_BATCHID'); + if (!defined(batchIdAttributeName)) { + batchIdAttributeName = getAttributeOrUniformBySemantic(gltf, 'BATCHID'); + if (defined(batchIdAttributeName)) { + Batched3DModel3DTileContent._deprecationWarning('b3dm-legacy-batchid', 'The glTF in this b3dm uses the semantic `BATCHID`. Application-specific semantics should be prefixed with an underscore: `_BATCHID`.'); + } + } + return batchIdAttributeName; + } + + function getVertexShaderCallback(content) { + return function(vs) { + var batchTable = content._batchTable; + var gltf = content._model.gltf; + var batchIdAttributeName = getBatchIdAttributeName(gltf); + var callback = batchTable.getVertexShaderCallback(true, batchIdAttributeName); + return defined(callback) ? callback(vs) : vs; + }; + } + + function getPickVertexShaderCallback(content) { + return function(vs) { + var batchTable = content._batchTable; + var gltf = content._model.gltf; + var batchIdAttributeName = getBatchIdAttributeName(gltf); + var callback = batchTable.getPickVertexShaderCallback(batchIdAttributeName); + return defined(callback) ? callback(vs) : vs; + }; + } + + function getFragmentShaderCallback(content) { + return function(fs) { + var batchTable = content._batchTable; + var gltf = content._model.gltf; + var diffuseUniformName = getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE'); + var callback = batchTable.getFragmentShaderCallback(true, diffuseUniformName); + return defined(callback) ? callback(fs) : fs; + }; + } + + function initialize(content, arrayBuffer, byteOffset) { + var tileset = content._tileset; + var tile = content._tile; + var basePath = getAbsoluteUri(getBaseUri(content._url, true)); + + var byteStart = defaultValue(byteOffset, 0); + byteOffset = byteStart; + + var uint8Array = new Uint8Array(arrayBuffer); + var view = new DataView(arrayBuffer); + byteOffset += sizeOfUint32; // Skip magic + + var version = view.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError('Only Batched 3D Model version 1 is supported. Version ' + version + ' is not.'); + } + byteOffset += sizeOfUint32; + + var byteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchLength; + + // Legacy header #1: [batchLength] [batchTableByteLength] + // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] + // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] + // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. + // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. + // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead + if (batchTableJsonByteLength >= 570425344) { + // First legacy check + byteOffset -= sizeOfUint32 * 2; + batchLength = featureTableJsonByteLength; + batchTableJsonByteLength = featureTableBinaryByteLength; + batchTableBinaryByteLength = 0; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.'); + } else if (batchTableBinaryByteLength >= 570425344) { + // Second legacy check + byteOffset -= sizeOfUint32; + batchLength = batchTableJsonByteLength; + batchTableJsonByteLength = featureTableJsonByteLength; + batchTableBinaryByteLength = featureTableBinaryByteLength; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.'); + } + + var featureTableJson; + if (featureTableJsonByteLength === 0) { + featureTableJson = { + BATCH_LENGTH : defaultValue(batchLength, 0) + }; + } else { + var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength); + featureTableJson = JSON.parse(featureTableString); + byteOffset += featureTableJsonByteLength; + } + + var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength); + byteOffset += featureTableBinaryByteLength; + + var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); + + batchLength = featureTable.getGlobalProperty('BATCH_LENGTH'); + featureTable.featuresLength = batchLength; + + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the + // arraybuffer/string compressed in memory and then decompress it when it is first accessed. + // + // We could also make another request for it, but that would make the property set/get + // API async, and would double the number of numbers in some cases. + var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength); + batchTableJson = JSON.parse(batchTableString); + byteOffset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength); + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + byteOffset += batchTableBinaryByteLength; + } + } + + var batchTable = new Cesium3DTileBatchTable(content, batchLength, batchTableJson, batchTableBinary); + content._batchTable = batchTable; + + var gltfByteLength = byteStart + byteLength - byteOffset; + if (gltfByteLength === 0) { + throw new RuntimeError('glTF byte length must be greater than 0.'); + } + var gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); + + var pickObject = { + content : content, + primitive : tileset + }; + + // PERFORMANCE_IDEA: patch the shader on demand, e.g., the first time show/color changes. + // The pick shader still needs to be patched. + content._model = new Model({ + gltf : gltfView, + cull : false, // The model is already culled by 3D Tiles + releaseGltfJson : true, // Models are unique and will not benefit from caching so save memory + basePath : basePath, + requestType : RequestType.TILES3D, + modelMatrix : tile.computedTransform, + upAxis : tileset._gltfUpAxis, + shadows: tileset.shadows, + debugWireframe: tileset.debugWireframe, + incrementallyLoadTextures : false, + vertexShaderLoaded : getVertexShaderCallback(content), + fragmentShaderLoaded : getFragmentShaderCallback(content), + uniformMapLoaded : batchTable.getUniformMapCallback(), + pickVertexShaderLoaded : getPickVertexShaderCallback(content), + pickFragmentShaderLoaded : batchTable.getPickFragmentShaderCallback(), + pickUniformMapLoaded : batchTable.getPickUniformMapCallback(), + addBatchIdToGeneratedShaders : (batchLength > 0), // If the batch table has values in it, generated shaders will need a batchId attribute + pickObject : pickObject + }); + } + + function createFeatures(content) { + var tileset = content._tileset; + var featuresLength = content.featuresLength; + if (!defined(content._features) && (featuresLength > 0)) { + var features = new Array(featuresLength); + for (var i = 0; i < featuresLength; ++i) { + features[i] = new Cesium3DTileFeature(tileset, content, i); + } + content._features = features; + } + } + + /** + * @inheritdoc Cesium3DTileContent#hasProperty + */ + Batched3DModel3DTileContent.prototype.hasProperty = function(batchId, name) { + return this._batchTable.hasProperty(batchId, name); + }; + + /** + * @inheritdoc Cesium3DTileContent#getFeature + */ + Batched3DModel3DTileContent.prototype.getFeature = function(batchId) { + //>>includeStart('debug', pragmas.debug); + var featuresLength = this.featuresLength; + if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) { + throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').'); + } + //>>includeEnd('debug'); + + createFeatures(this); + return this._features[batchId]; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + Batched3DModel3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + color = enabled ? color : Color.WHITE; + if (this.featuresLength === 0) { + this._model.color = color; + } else { + this._batchTable.setAllColor(color); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + Batched3DModel3DTileContent.prototype.applyStyle = function(frameState, style) { + this._batchTable.applyStyle(frameState, style); + }; + + /** + * @inheritdoc Cesium3DTileContent#update + */ + Batched3DModel3DTileContent.prototype.update = function(tileset, frameState) { + var commandStart = frameState.commandList.length; + + // In the PROCESSING state we may be calling update() to move forward + // the content's resource loading. In the READY state, it will + // actually generate commands. + this._batchTable.update(tileset, frameState); + this._model.modelMatrix = this._tile.computedTransform; + this._model.shadows = this._tileset.shadows; + this._model.debugWireframe = this._tileset.debugWireframe; + this._model.update(frameState); + + // If any commands were pushed, add derived commands + var commandEnd = frameState.commandList.length; + if ((commandStart < commandEnd) && frameState.passes.render) { + this._batchTable.addDerivedCommands(frameState, commandStart); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + Batched3DModel3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + Batched3DModel3DTileContent.prototype.destroy = function() { + this._model = this._model && this._model.destroy(); + this._batchTable = this._batchTable && this._batchTable.destroy(); + return destroyObject(this); + }; + + return Batched3DModel3DTileContent; +}); diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index 08aa929bdcb7..3fc847c3adc6 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -89,6 +89,9 @@ define([ if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) { throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near'); } + if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.'); + } //>>includeEnd('debug'); this._show = defaultValue(options.show, true); @@ -111,6 +114,7 @@ define([ this._pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance; this._sizeInMeters = defaultValue(options.sizeInMeters, false); this._distanceDisplayCondition = options.distanceDisplayCondition; + this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0); this._id = options.id; this._collection = defaultValue(options.collection, billboardCollection); @@ -178,7 +182,8 @@ define([ var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX = 12; var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = 13; var DISTANCE_DISPLAY_CONDITION = Billboard.DISTANCE_DISPLAY_CONDITION = 14; - Billboard.NUMBER_OF_PROPERTIES = 15; + var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE = 15; + Billboard.NUMBER_OF_PROPERTIES = 16; function makeDirty(billboard, propertyChanged) { var billboardCollection = billboard._billboardCollection; @@ -234,7 +239,6 @@ define([ if (!Cartesian3.equals(position, value)) { Cartesian3.clone(value, position); Cartesian3.clone(value, this._actualPosition); - this._updateClamping(); makeDirty(this, POSITION_INDEX); } @@ -748,6 +752,30 @@ define([ } }, + /** + * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. + * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. + * @memberof Billboard.prototype + * @type {Number} + * @default 0.0 + */ + disableDepthTestDistance : { + get : function() { + return this._disableDepthTestDistance; + }, + set : function(value) { + if (this._disableDepthTestDistance !== value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value) || value < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.'); + } + //>>includeEnd('debug'); + this._disableDepthTestDistance = value; + makeDirty(this, DISABLE_DEPTH_DISTANCE); + } + } + }, + /** * Gets or sets the user-defined object returned when the billboard is picked. * @memberof Billboard.prototype @@ -927,6 +955,7 @@ define([ var position = ellipsoid.cartesianToCartographic(owner._position); if (!defined(position)) { + owner._actualClampedPosition = undefined; return; } @@ -1278,7 +1307,8 @@ define([ NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && NearFarScalar.equals(this._pixelOffsetScaleByDistance, other._pixelOffsetScaleByDistance) && - DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition); + DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition) && + this._disableDepthTestDistance === other._disableDepthTestDistance; }; Billboard.prototype._destroy = function() { diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index 5ce469cbbbec..8f6cd1168bb6 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -86,6 +86,7 @@ define([ var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX; var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX; var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION_INDEX; + var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE; var NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES; var attributeLocations; @@ -99,7 +100,7 @@ define([ eyeOffset : 5, // 4 bytes free scaleByDistance : 6, pixelOffsetScaleByDistance : 7, - distanceDisplayCondition : 8 + distanceDisplayConditionAndDisableDepth : 8 }; var attributeLocationsInstanced = { @@ -112,7 +113,7 @@ define([ eyeOffset : 6, // texture range in w scaleByDistance : 7, pixelOffsetScaleByDistance : 8, - distanceDisplayCondition : 9 + distanceDisplayConditionAndDisableDepth : 9 }; /** @@ -209,6 +210,10 @@ define([ this._compiledShaderDistanceDisplayCondition = false; this._compiledShaderDistanceDisplayConditionPick = false; + this._shaderDisableDepthDistance = false; + this._compiledShaderDisableDepthDistance = false; + this._compiledShaderDisableDepthDistancePick = false; + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxSize = 0.0; @@ -729,8 +734,8 @@ define([ componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX] }, { - index : attributeLocations.distanceDisplayCondition, - componentsPerAttribute : 2, + index : attributeLocations.distanceDisplayConditionAndDisableDepth, + componentsPerAttribute : 3, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX] }]; @@ -1146,9 +1151,9 @@ define([ } } - function writeDistanceDisplayCondition(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + function writeDistanceDisplayConditionAndDepthDisable(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { var i; - var writer = vafWriters[attributeLocations.distanceDisplayCondition]; + var writer = vafWriters[attributeLocations.distanceDisplayConditionAndDisableDepth]; var near = 0.0; var far = Number.MAX_VALUE; @@ -1157,18 +1162,30 @@ define([ near = distanceDisplayCondition.near; far = distanceDisplayCondition.far; + near *= near; + far *= far; + billboardCollection._shaderDistanceDisplayCondition = true; } + var disableDepthTestDistance = billboard.disableDepthTestDistance; + disableDepthTestDistance *= disableDepthTestDistance; + if (disableDepthTestDistance > 0.0) { + billboardCollection._shaderDisableDepthDistance = true; + if (disableDepthTestDistance === Number.POSITIVE_INFINITY) { + disableDepthTestDistance = -1.0; + } + } + if (billboardCollection._instanced) { i = billboard._index; - writer(i, near, far); + writer(i, near, far, disableDepthTestDistance); } else { i = billboard._index * 4; - writer(i + 0, near, far); - writer(i + 1, near, far); - writer(i + 2, near, far); - writer(i + 3, near, far); + writer(i + 0, near, far, disableDepthTestDistance); + writer(i + 1, near, far, disableDepthTestDistance); + writer(i + 2, near, far, disableDepthTestDistance); + writer(i + 3, near, far, disableDepthTestDistance); } } @@ -1180,7 +1197,7 @@ define([ writeEyeOffset(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writePixelOffsetScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); - writeDistanceDisplayCondition(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeDistanceDisplayConditionAndDepthDisable(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); } function recomputeActualPositions(billboardCollection, billboards, length, frameState, modelMatrix, recomputeBoundingVolume) { @@ -1376,8 +1393,8 @@ define([ writers.push(writePixelOffsetScaleByDistance); } - if (properties[DISTANCE_DISPLAY_CONDITION_INDEX]) { - writers.push(writeDistanceDisplayCondition); + if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE]) { + writers.push(writeDistanceDisplayConditionAndDepthDisable); } var numWriters = writers.length; @@ -1481,13 +1498,18 @@ define([ } } + this._shaderDisableDepthDistance = this._shaderDisableDepthDistance || frameState.minimumDisableDepthTestDistance !== 0.0; + var vs; + var fs; + if (blendOptionChanged || (this._shaderRotation !== this._compiledShaderRotation) || (this._shaderAlignedAxis !== this._compiledShaderAlignedAxis) || (this._shaderScaleByDistance !== this._compiledShaderScaleByDistance) || (this._shaderTranslucencyByDistance !== this._compiledShaderTranslucencyByDistance) || (this._shaderPixelOffsetScaleByDistance !== this._compiledShaderPixelOffsetScaleByDistance) || - (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayCondition)) { + (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayCondition) || + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance)) { vs = new ShaderSource({ sources : [BillboardCollectionVS] @@ -1513,6 +1535,9 @@ define([ if (this._shaderDistanceDisplayCondition) { vs.defines.push('DISTANCE_DISPLAY_CONDITION'); } + if (this._shaderDisableDepthDistance) { + vs.defines.push('DISABLE_DEPTH_DISTANCE'); + } if (this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT) { fs = new ShaderSource({ @@ -1572,6 +1597,7 @@ define([ this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; + this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; } if (!defined(this._spPick) || @@ -1580,7 +1606,8 @@ define([ (this._shaderScaleByDistance !== this._compiledShaderScaleByDistancePick) || (this._shaderTranslucencyByDistance !== this._compiledShaderTranslucencyByDistancePick) || (this._shaderPixelOffsetScaleByDistance !== this._compiledShaderPixelOffsetScaleByDistancePick) || - (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayConditionPick)) { + (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayConditionPick) || + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistancePick)) { vs = new ShaderSource({ defines : ['RENDER_FOR_PICK'], @@ -1608,6 +1635,9 @@ define([ if (this._shaderDistanceDisplayCondition) { vs.defines.push('DISTANCE_DISPLAY_CONDITION'); } + if (this._shaderDisableDepthDistance) { + vs.defines.push('DISABLE_DEPTH_DISTANCE'); + } fs = new ShaderSource({ defines : ['RENDER_FOR_PICK'], @@ -1627,13 +1657,12 @@ define([ this._compiledShaderTranslucencyByDistancePick = this._shaderTranslucencyByDistance; this._compiledShaderPixelOffsetScaleByDistancePick = this._shaderPixelOffsetScaleByDistance; this._compiledShaderDistanceDisplayConditionPick = this._shaderDistanceDisplayCondition; + this._compiledShaderDisableDepthDistancePick = this._shaderDisableDepthDistance; } var va; var vaLength; var command; - var vs; - var fs; var j; var commandList = frameState.commandList; diff --git a/Source/Scene/BingMapsImageryProvider.js b/Source/Scene/BingMapsImageryProvider.js index 262d2ae84cfc..fdcf1531b7c6 100644 --- a/Source/Scene/BingMapsImageryProvider.js +++ b/Source/Scene/BingMapsImageryProvider.js @@ -76,7 +76,7 @@ define([ * expected to have a getURL function which returns the proxied URL, if needed. * * @see ArcGisMapServerImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider @@ -522,6 +522,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -529,7 +530,7 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - BingMapsImageryProvider.prototype.requestImage = function(x, y, level) { + BingMapsImageryProvider.prototype.requestImage = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug); if (!this._ready) { throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); @@ -537,7 +538,7 @@ define([ //>>includeEnd('debug'); var url = buildImageUrl(this, x, y, level); - return ImageryProvider.loadImage(this, url); + return ImageryProvider.loadImage(this, url, request); }; /** diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 67b6f8c6e628..029b8dfc6ad7 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -608,14 +608,17 @@ define([ if (directionChanged || transformChanged) { camera._directionWC = Matrix4.multiplyByPointAsVector(transform, direction, camera._directionWC); + Cartesian3.normalize(camera._directionWC, camera._directionWC); } if (upChanged || transformChanged) { camera._upWC = Matrix4.multiplyByPointAsVector(transform, up, camera._upWC); + Cartesian3.normalize(camera._upWC, camera._upWC); } if (rightChanged || transformChanged) { camera._rightWC = Matrix4.multiplyByPointAsVector(transform, right, camera._rightWC); + Cartesian3.normalize(camera._rightWC, camera._rightWC); } if (positionChanged || directionChanged || upChanged || rightChanged || transformChanged) { @@ -1817,30 +1820,57 @@ define([ } //>>includeEnd('debug'); + var ratio; amount = amount * 0.5; - var newRight = frustum.right - amount; - var newLeft = frustum.left + amount; - var maxRight = camera._maxCoord.x; - if (camera._scene.mapMode2D === MapMode2D.ROTATE) { - maxRight *= camera.maximumZoomFactor; - } + if((Math.abs(frustum.top) + Math.abs(frustum.bottom)) > (Math.abs(frustum.left) + Math.abs(frustum.right))) { + var newTop = frustum.top - amount; + var newBottom = frustum.bottom + amount; - if (newRight > maxRight) { - newRight = maxRight; - newLeft = -maxRight; - } + var maxBottom = camera._maxCoord.y; + if (camera._scene.mapMode2D === MapMode2D.ROTATE) { + maxBottom *= camera.maximumZoomFactor; + } - if (newRight <= newLeft) { - newRight = 1.0; - newLeft = -1.0; - } + if (newBottom > maxBottom) { + newBottom = maxBottom; + newTop = -maxBottom; + } + + if (newTop <= newBottom) { + newTop = 1.0; + newBottom = -1.0; + } + + ratio = frustum.right / frustum.top; + frustum.top = newTop; + frustum.bottom = newBottom; + frustum.right = frustum.top * ratio; + frustum.left = -frustum.right; + } else { + var newRight = frustum.right - amount; + var newLeft = frustum.left + amount; - var ratio = frustum.top / frustum.right; - frustum.right = newRight; - frustum.left = newLeft; - frustum.top = frustum.right * ratio; - frustum.bottom = -frustum.top; + var maxRight = camera._maxCoord.x; + if (camera._scene.mapMode2D === MapMode2D.ROTATE) { + maxRight *= camera.maximumZoomFactor; + } + + if (newRight > maxRight) { + newRight = maxRight; + newLeft = -maxRight; + } + + if (newRight <= newLeft) { + newRight = 1.0; + newLeft = -1.0; + } + ratio = frustum.top / frustum.right; + frustum.right = newRight; + frustum.left = newLeft; + frustum.top = frustum.right * ratio; + frustum.bottom = -frustum.top; + } } function zoom3D(camera, amount) { @@ -2307,7 +2337,7 @@ define([ } /** - * Get the camera position needed to view an rectangle on an ellipsoid or map + * Get the camera position needed to view a rectangle on an ellipsoid or map * * @param {Rectangle} rectangle The rectangle to view. * @param {Cartesian3} [result] The camera position needed to view the rectangle @@ -2395,6 +2425,11 @@ define([ } //>>includeEnd('debug'); + var canvas = this._scene.canvas; + if (canvas.clientWidth === 0 || canvas.clientHeight === 0) { + return undefined; + } + if (!defined(result)) { result = new Cartesian3(); } diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js new file mode 100644 index 000000000000..bb1b96551045 --- /dev/null +++ b/Source/Scene/Cesium3DTile.js @@ -0,0 +1,1062 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/Cartesian3', + '../Core/Color', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/deprecationWarning', + '../Core/destroyObject', + '../Core/getMagic', + '../Core/Intersect', + '../Core/joinUrls', + '../Core/JulianDate', + '../Core/loadArrayBuffer', + '../Core/Matrix3', + '../Core/Matrix4', + '../Core/Rectangle', + '../Core/Request', + '../Core/RequestScheduler', + '../Core/RequestState', + '../Core/RequestType', + '../Core/RuntimeError', + '../ThirdParty/when', + './Cesium3DTileChildrenVisibility', + './Cesium3DTileContentFactory', + './Cesium3DTileContentState', + './Cesium3DTileOptimizationHint', + './Cesium3DTileRefine', + './Empty3DTileContent', + './SceneMode', + './TileBoundingRegion', + './TileBoundingSphere', + './TileOrientedBoundingBox' + ], function( + BoundingSphere, + Cartesian3, + Color, + defaultValue, + defined, + defineProperties, + deprecationWarning, + destroyObject, + getMagic, + Intersect, + joinUrls, + JulianDate, + loadArrayBuffer, + Matrix3, + Matrix4, + Rectangle, + Request, + RequestScheduler, + RequestState, + RequestType, + RuntimeError, + when, + Cesium3DTileChildrenVisibility, + Cesium3DTileContentFactory, + Cesium3DTileContentState, + Cesium3DTileOptimizationHint, + Cesium3DTileRefine, + Empty3DTileContent, + SceneMode, + TileBoundingRegion, + TileBoundingSphere, + TileOrientedBoundingBox) { + 'use strict'; + + /** + * A tile in a {@link Cesium3DTileset}. When a tile is first created, its content is not loaded; + * the content is loaded on-demand when needed based on the view. + *

+ * Do not construct this directly, instead access tiles through {@link Cesium3DTileset#tileVisible}. + *

+ * + * @alias Cesium3DTile + * @constructor + */ + function Cesium3DTile(tileset, basePath, header, parent) { + this._tileset = tileset; + this._header = header; + var contentHeader = header.content; + + /** + * The local transform of this tile + * @type {Matrix4} + */ + this.transform = defined(header.transform) ? Matrix4.unpack(header.transform) : Matrix4.clone(Matrix4.IDENTITY); + + var parentTransform = defined(parent) ? parent.computedTransform : tileset.modelMatrix; + var computedTransform = Matrix4.multiply(parentTransform, this.transform, new Matrix4()); + + /** + * The final computed transform of this tile + * @type {Matrix4} + * @readonly + */ + this.computedTransform = computedTransform; + + this._boundingVolume = this.createBoundingVolume(header.boundingVolume, computedTransform); + this._boundingVolume2D = undefined; + + var contentBoundingVolume; + + if (defined(contentHeader) && defined(contentHeader.boundingVolume)) { + // Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume + // around only the features in the tile. This box is useful for culling for rendering, + // but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e., + // since it only bounds features in the tile, not the entire tile, children may be + // outside of this box. + contentBoundingVolume = this.createBoundingVolume(contentHeader.boundingVolume, computedTransform); + } + this._contentBoundingVolume = contentBoundingVolume; + this._contentBoundingVolume2D = undefined; + + var viewerRequestVolume; + if (defined(header.viewerRequestVolume)) { + viewerRequestVolume = this.createBoundingVolume(header.viewerRequestVolume, computedTransform); + } + this._viewerRequestVolume = viewerRequestVolume; + + /** + * The error, in meters, introduced if this tile is rendered and its children are not. + * This is used to compute screen space error, i.e., the error measured in pixels. + * + * @type {Number} + * @readonly + */ + this.geometricError = header.geometricError; + + if (!defined(this.geometricError)) { + throw new RuntimeError('geometricError must be defined'); + } + + var refine; + if (defined(header.refine)) { + if (header.refine === 'replace' || header.refine === 'add') { + Cesium3DTile._deprecationWarning('lowercase-refine', 'This tile uses a lowercase refine "' + header.refine + '". Instead use "' + header.refine.toUpperCase() + '".'); + } + refine = (header.refine.toUpperCase() === 'REPLACE') ? Cesium3DTileRefine.REPLACE : Cesium3DTileRefine.ADD; + } else if (defined(parent)) { + // Inherit from parent tile if omitted. + refine = parent.refine; + } else { + refine = Cesium3DTileRefine.REPLACE; + } + + /** + * Specifies the type of refinment that is used when traversing this tile for rendering. + * + * @type {Cesium3DTileRefine} + * @readonly + * @private + */ + this.refine = refine; + + /** + * Gets the tile's children. + * + * @type {Cesium3DTile[]} + * @readonly + */ + this.children = []; + + /** + * This tile's parent or undefined if this tile is the root. + *

+ * When a tile's content points to an external tileset.json, the external tileset's + * root tile's parent is not undefined; instead, the parent references + * the tile (with its content pointing to an external tileset.json) as if the two tilesets were merged. + *

+ * + * @type {Cesium3DTile} + * @readonly + */ + this.parent = parent; + + var content; + var hasEmptyContent; + var contentState; + var contentUrl; + var serverKey; + + if (defined(contentHeader)) { + hasEmptyContent = false; + contentState = Cesium3DTileContentState.UNLOADED; + contentUrl = joinUrls(basePath, contentHeader.url); + serverKey = RequestScheduler.getServerKey(contentUrl); + } else { + content = new Empty3DTileContent(tileset, this); + hasEmptyContent = true; + contentState = Cesium3DTileContentState.READY; + } + + this._content = content; + this._contentUrl = contentUrl; + this._contentState = contentState; + this._contentReadyToProcessPromise = undefined; + this._contentReadyPromise = undefined; + this._expiredContent = undefined; + + this._serverKey = serverKey; + + /** + * When true, the tile has no content. + * + * @type {Boolean} + * @readonly + * + * @private + */ + this.hasEmptyContent = hasEmptyContent; + + /** + * When true, the tile's content is renderable. + *

+ * This is false until the tile's content is loaded. + *

+ * + * @type {Boolean} + * @readonly + * + * @private + */ + this.hasRenderableContent = false; + + /** + * When true, the tile's content points to an external tileset. + *

+ * This is false until the tile's content is loaded. + *

+ * + * @type {Boolean} + * @readonly + * + * @private + */ + this.hasTilesetContent = false; + + /** + * The corresponding node in the cache replacement list. + * + * @type {DoublyLinkedListNode} + * @readonly + * + * @private + */ + this.replacementNode = undefined; + + var expire = header.expire; + var expireDuration; + var expireDate; + if (defined(expire)) { + expireDuration = expire.duration; + if (defined(expire.date)) { + expireDate = JulianDate.fromIso8601(expire.date); + } + } + + /** + * The time in seconds after the tile's content is ready when the content expires and new content is requested. + * + * @type {Number} + */ + this.expireDuration = expireDuration; + + /** + * The date when the content expires and new content is requested. + * + * @type {JulianDate} + */ + this.expireDate = expireDate; + + /** + * Marks if the tile is selected this frame. + * + * @type {Boolean} + * + * @private + */ + this.selected = false; + + /** + * The time when a style was last applied to this tile. + * + * @type {Number} + * + * @private + */ + this.lastStyleTime = 0; + + /** + * Marks whether the tile's children bounds are fully contained within the tile's bounds + * + * @type {Cesium3DTileOptimizationHint} + * + * @private + */ + this._optimChildrenWithinParent = Cesium3DTileOptimizationHint.NOT_COMPUTED; + + // Members that are updated every frame for tree traversal and rendering optimizations: + this._distanceToCamera = 0; + this._visibilityPlaneMask = 0; + this._childrenVisibility = Cesium3DTileChildrenVisibility.VISIBLE; + this._lastSelectedFrameNumber = -1; + this._screenSpaceError = 0; + this._screenSpaceErrorComputedFrame = -1; + this._finalResolution = true; + this._depth = 0; + this._centerZDepth = 0; + this._stackLength = 0; + this._selectedFrame = -1; + this._selectionDepth = 0; + this._lastSelectionDepth = undefined; + this._requestedFrame = undefined; + this._lastVisitedFrame = undefined; + this._ancestorWithContent = undefined; + this._ancestorWithLoadedContent = undefined; + + this._debugBoundingVolume = undefined; + this._debugContentBoundingVolume = undefined; + this._debugViewerRequestVolume = undefined; + this._debugColor = Color.fromRandom({ alpha : 1.0 }); + this._debugColorizeTiles = false; + + this._commandsLength = 0; + + this._color = undefined; + this._colorDirty = false; + } + + // This can be overridden for testing purposes + Cesium3DTile._deprecationWarning = deprecationWarning; + + defineProperties(Cesium3DTile.prototype, { + /** + * The tileset containing this tile. + * + * @memberof Cesium3DTile.prototype + * + * @type {Cesium3DTileset} + * @readonly + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * The tile's content. This represents the actual tile's payload, + * not the content's metadata in tileset.json. + * + * @memberof Cesium3DTile.prototype + * + * @type {Cesium3DTileContent} + * @readonly + */ + content : { + get : function() { + return this._content; + } + }, + + /** + * Get the bounding volume of the tile's contents. This defaults to the + * tile's bounding volume when the content's bounding volume is + * undefined. + * + * @memberof Cesium3DTile.prototype + * + * @type {TileBoundingVolume} + * @readonly + * @private + */ + contentBoundingVolume : { + get : function() { + return defaultValue(this._contentBoundingVolume, this._boundingVolume); + } + }, + + /** + * Get the bounding sphere derived from the tile's bounding volume. + * + * @memberof Cesium3DTile.prototype + * + * @type {BoundingSphere} + * @readonly + */ + boundingSphere : { + get : function() { + return this._boundingVolume.boundingSphere; + } + }, + + /** + * Gets or sets the tile's highlight color. + * + * @memberof Cesium3DTile.prototype + * + * @type {Color} + * + * @default {@link Color.WHITE} + * + * @private + */ + color : { + get : function() { + if (!defined(this._color)) { + this._color = new Color(); + } + return Color.clone(this._color); + }, + set : function(value) { + this._color = Color.clone(value, this._color); + this._colorDirty = true; + } + }, + + /** + * Determines if the tile has available content to render. true if the tile's + * content is ready or if it has expired content that renders while new content loads; otherwise, + * false. + * + * @memberof Cesium3DTile.prototype + * + * @type {Boolean} + * @readonly + * + * @private + */ + contentAvailable : { + get : function() { + return this.contentReady || (defined(this._expiredContent) && this._contentState !== Cesium3DTileContentState.FAILED); + } + }, + + /** + * Determines if the tile is ready to render. true if the tile + * is ready to render; otherwise, false. + * + * @memberof Cesium3DTile.prototype + * + * @type {Boolean} + * @readonly + * + * @private + */ + contentReady : { + get : function() { + return this._contentState === Cesium3DTileContentState.READY; + } + }, + + /** + * Determines if the tile's content has not be requested. true if tile's + * content has not be requested; otherwise, false. + * + * @memberof Cesium3DTile.prototype + * + * @type {Boolean} + * @readonly + * + * @private + */ + contentUnloaded : { + get : function() { + return this._contentState === Cesium3DTileContentState.UNLOADED; + } + }, + + /** + * Determines if the tile's content is expired. true if tile's + * content is expired; otherwise, false. + * + * @memberof Cesium3DTile.prototype + * + * @type {Boolean} + * @readonly + * + * @private + */ + contentExpired : { + get : function() { + return this._contentState === Cesium3DTileContentState.EXPIRED; + } + }, + + /** + * Gets the promise that will be resolved when the tile's content is ready to process. + * This happens after the content is downloaded but before the content is ready + * to render. + *

+ * The promise remains undefined until the tile's content is requested. + *

+ * + * @type {Promise.} + * @readonly + * + * @private + */ + contentReadyToProcessPromise : { + get : function() { + if (defined(this._contentReadyToProcessPromise)) { + return this._contentReadyToProcessPromise.promise; + } + } + }, + + /** + * Gets the promise that will be resolved when the tile's content is ready to render. + *

+ * The promise remains undefined until the tile's content is requested. + *

+ * + * @type {Promise.} + * @readonly + * + * @private + */ + contentReadyPromise : { + get : function() { + if (defined(this._contentReadyPromise)) { + return this._contentReadyPromise.promise; + } + } + }, + + /** + * Returns the number of draw commands used by this tile. + * + * @readonly + * + * @private + */ + commandsLength : { + get : function() { + return this._commandsLength; + } + } + }); + + var scratchJulianDate = new JulianDate(); + + /** + * Update whether the tile has expired. + * + * @private + */ + Cesium3DTile.prototype.updateExpiration = function() { + if (defined(this.expireDate) && this.contentReady && !this.hasEmptyContent) { + var now = JulianDate.now(scratchJulianDate); + if (JulianDate.lessThan(this.expireDate, now)) { + this._contentState = Cesium3DTileContentState.EXPIRED; + this._expiredContent = this._content; + } + } + }; + + function updateExpireDate(tile) { + if (defined(tile.expireDuration)) { + var expireDurationDate = JulianDate.now(scratchJulianDate); + JulianDate.addSeconds(expireDurationDate, tile.expireDuration, expireDurationDate); + + if (defined(tile.expireDate)) { + if (JulianDate.lessThan(tile.expireDate, expireDurationDate)) { + JulianDate.clone(expireDurationDate, tile.expireDate); + } + } else { + tile.expireDate = JulianDate.clone(expireDurationDate); + } + } + } + + function getContentFailedFunction(tile) { + return function(error) { + tile._contentState = Cesium3DTileContentState.FAILED; + tile._contentReadyPromise.reject(error); + tile._contentReadyToProcessPromise.reject(error); + }; + } + + function createPriorityFunction(tile) { + return function() { + return tile._distanceToCamera; + }; + } + + /** + * Requests the tile's content. + *

+ * The request may not be made if the Cesium Request Scheduler can't prioritize it. + *

+ * + * @private + */ + Cesium3DTile.prototype.requestContent = function() { + var that = this; + var tileset = this._tileset; + + if (this.hasEmptyContent) { + return false; + } + + var url = this._contentUrl; + var expired = this.contentExpired; + if (expired) { + // Append a query parameter of the tile expiration date to prevent caching + var timestampQuery = '?expired=' + this.expireDate.toString(); + url = joinUrls(url, timestampQuery, false); + } + + var request = new Request({ + throttle : true, + throttleByServer : true, + type : RequestType.TILES3D, + priorityFunction : createPriorityFunction(this), + serverKey : this._serverKey + }); + + var promise = loadArrayBuffer(url, undefined, request); + + if (!defined(promise)) { + return false; + } + + var contentState = this._contentState; + this._contentState = Cesium3DTileContentState.LOADING; + this._contentReadyToProcessPromise = when.defer(); + this._contentReadyPromise = when.defer(); + + if (expired) { + this.expireDate = undefined; + } + + var contentFailedFunction = getContentFailedFunction(this); + + promise.then(function(arrayBuffer) { + if (that.isDestroyed()) { + // Tile is unloaded before the content finishes loading + contentFailedFunction(); + return; + } + var uint8Array = new Uint8Array(arrayBuffer); + var magic = getMagic(uint8Array); + var contentFactory = Cesium3DTileContentFactory[magic]; + var content; + + if (defined(contentFactory)) { + content = contentFactory(tileset, that, that._contentUrl, arrayBuffer, 0); + that.hasRenderableContent = true; + } else { + // The content may be json instead + content = Cesium3DTileContentFactory.json(tileset, that, that._contentUrl, arrayBuffer, 0); + that.hasTilesetContent = true; + } + + that._content = content; + that._contentState = Cesium3DTileContentState.PROCESSING; + that._contentReadyToProcessPromise.resolve(content); + + return content.readyPromise.then(function(content) { + if (that.isDestroyed()) { + // Tile is unloaded before the content finishes processing + contentFailedFunction(); + return; + } + updateExpireDate(that); + + // Refresh style for expired content + that.lastStyleTime = 0; + + that._contentState = Cesium3DTileContentState.READY; + that._contentReadyPromise.resolve(content); + }); + }).otherwise(function(error) { + if (request.state === RequestState.CANCELLED) { + // Cancelled due to low priority - try again later. + that._contentState = contentState; + --tileset.statistics.numberOfPendingRequests; + ++tileset.statistics.numberOfAttemptedRequests; + return; + } + contentFailedFunction(error); + }); + + return true; + }; + + /** + * Unloads the tile's content. + * + * @private + */ + Cesium3DTile.prototype.unloadContent = function() { + if (!this.hasRenderableContent) { + return; + } + + this._content = this._content && this._content.destroy(); + this._contentState = Cesium3DTileContentState.UNLOADED; + this._contentReadyToProcessPromise = undefined; + this._contentReadyPromise = undefined; + + this.replacementNode = undefined; + + this.lastStyleTime = 0; + + this._debugColorizeTiles = false; + + this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); + this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); + this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); + }; + + var scratchProjectedBoundingSphere = new BoundingSphere(); + + function getBoundingVolume(tile, frameState) { + if (frameState.mode !== SceneMode.SCENE3D && !defined(tile._boundingVolume2D)) { + var boundingSphere = tile._boundingVolume.boundingSphere; + var sphere = BoundingSphere.projectTo2D(boundingSphere, frameState.mapProjection, scratchProjectedBoundingSphere); + tile._boundingVolume2D = new TileBoundingSphere(sphere.center, sphere.radius); + } + + return frameState.mode !== SceneMode.SCENE3D ? tile._boundingVolume2D : tile._boundingVolume; + } + + function getContentBoundingVolume(tile, frameState) { + if (frameState.mode !== SceneMode.SCENE3D && !defined(tile._contentBoundingVolume2D)) { + var boundingSphere = tile._contentBoundingVolume.boundingSphere; + var sphere = BoundingSphere.projectTo2D(boundingSphere, frameState.mapProjection, scratchProjectedBoundingSphere); + tile._contentBoundingVolume2D = new TileBoundingSphere(sphere.center, sphere.radius); + } + return frameState.mode !== SceneMode.SCENE3D ? tile._contentBoundingVolume2D : tile._contentBoundingVolume; + } + + /** + * Determines whether the tile's bounding volume intersects the culling volume. + * + * @param {FrameState} frameState The frame state. + * @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check. + * @returns {Number} A plane mask as described above in {@link CullingVolume#computeVisibilityWithPlaneMask}. + * + * @private + */ + Cesium3DTile.prototype.visibility = function(frameState, parentVisibilityPlaneMask) { + var cullingVolume = frameState.cullingVolume; + var boundingVolume = getBoundingVolume(this, frameState); + return cullingVolume.computeVisibilityWithPlaneMask(boundingVolume, parentVisibilityPlaneMask); + }; + + /** + * Assuming the tile's bounding volume intersects the culling volume, determines + * whether the tile's content's bounding volume intersects the culling volume. + * + * @param {FrameState} frameState The frame state. + * @returns {Intersect} The result of the intersection: the tile's content is completely outside, completely inside, or intersecting the culling volume. + * + * @private + */ + Cesium3DTile.prototype.contentVisibility = function(frameState) { + // Assumes the tile's bounding volume intersects the culling volume already, so + // just return Intersect.INSIDE if there is no content bounding volume. + if (!defined(this._contentBoundingVolume)) { + return Intersect.INSIDE; + } + + // PERFORMANCE_IDEA: is it possible to burn less CPU on this test since we know the + // tile's (not the content's) bounding volume intersects the culling volume? + var cullingVolume = frameState.cullingVolume; + var boundingVolume = getContentBoundingVolume(this, frameState); + return cullingVolume.computeVisibility(boundingVolume); + }; + + /** + * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera. + * + * @param {FrameState} frameState The frame state. + * @returns {Number} The distance, in meters, or zero if the camera is inside the bounding volume. + * + * @private + */ + Cesium3DTile.prototype.distanceToTile = function(frameState) { + var boundingVolume = getBoundingVolume(this, frameState); + return boundingVolume.distanceToCamera(frameState); + }; + + var scratchToTileCenter = new Cartesian3(); + + /** + * Computes the distance from the center of the tile's bounding volume to the camera. + * + * @param {FrameState} frameState The frame state. + * @returns {Number} The distance, in meters, or zero if the camera is inside the bounding volume. + * + * @private + */ + Cesium3DTile.prototype.distanceToTileCenter = function(frameState) { + var tileBoundingVolume = getBoundingVolume(this, frameState); + var boundingVolume = tileBoundingVolume.boundingVolume; // Gets the underlying OrientedBoundingBox or BoundingSphere + var toCenter = Cartesian3.subtract(boundingVolume.center, frameState.camera.positionWC, scratchToTileCenter); + var distance = Cartesian3.magnitude(toCenter); + Cartesian3.divideByScalar(toCenter, distance, toCenter); + var dot = Cartesian3.dot(frameState.camera.directionWC, toCenter); + return distance * dot; + }; + + /** + * Checks if the camera is inside the viewer request volume. + * + * @param {FrameState} frameState The frame state. + * @returns {Boolean} Whether the camera is inside the volume. + * + * @private + */ + Cesium3DTile.prototype.insideViewerRequestVolume = function(frameState) { + var viewerRequestVolume = this._viewerRequestVolume; + return !defined(viewerRequestVolume) || (viewerRequestVolume.distanceToCamera(frameState) === 0.0); + }; + + var scratchMatrix = new Matrix3(); + var scratchScale = new Cartesian3(); + var scratchHalfAxes = new Matrix3(); + var scratchCenter = new Cartesian3(); + var scratchRectangle = new Rectangle(); + + function createBox(box, transform, result) { + var center = Cartesian3.fromElements(box[0], box[1], box[2], scratchCenter); + var halfAxes = Matrix3.fromArray(box, 3, scratchHalfAxes); + + // Find the transformed center and halfAxes + center = Matrix4.multiplyByPoint(transform, center, center); + var rotationScale = Matrix4.getRotation(transform, scratchMatrix); + halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes); + + if (defined(result)) { + result.update(center, halfAxes); + return result; + } + return new TileOrientedBoundingBox(center, halfAxes); + } + + function createRegion(region, result) { + var rectangleRegion = Rectangle.unpack(region, 0, scratchRectangle); + + if (defined(result)) { + // Don't update regions when the transform changes + return result; + } + return new TileBoundingRegion({ + rectangle : rectangleRegion, + minimumHeight : region[4], + maximumHeight : region[5] + }); + } + + function createSphere(sphere, transform, result) { + var center = Cartesian3.fromElements(sphere[0], sphere[1], sphere[2], scratchCenter); + var radius = sphere[3]; + + // Find the transformed center and radius + center = Matrix4.multiplyByPoint(transform, center, center); + var scale = Matrix4.getScale(transform, scratchScale); + var uniformScale = Cartesian3.maximumComponent(scale); + radius *= uniformScale; + + if (defined(result)) { + result.update(center, radius); + return result; + } + return new TileBoundingSphere(center, radius); + } + + /** + * Create a bounding volume from the tile's bounding volume header. + * + * @param {Object} boundingVolumeHeader The tile's bounding volume header. + * @param {Matrix4} transform The transform to apply to the bounding volume. + * @param {TileBoundingVolume} [result] The object onto which to store the result. + * + * @returns {TileBoundingVolume} The modified result parameter or a new TileBoundingVolume instance if none was provided. + * + * @private + */ + Cesium3DTile.prototype.createBoundingVolume = function(boundingVolumeHeader, transform, result) { + if (!defined(boundingVolumeHeader)) { + throw new RuntimeError('boundingVolume must be defined'); + } + if (defined(boundingVolumeHeader.box)) { + return createBox(boundingVolumeHeader.box, transform, result); + } + if (defined(boundingVolumeHeader.region)) { + return createRegion(boundingVolumeHeader.region, result); + } + if (defined(boundingVolumeHeader.sphere)) { + return createSphere(boundingVolumeHeader.sphere, transform, result); + } + throw new RuntimeError('boundingVolume must contain a sphere, region, or box'); + }; + + var scratchTransform = new Matrix4(); + + /** + * Update the tile's transform. The transform is applied to the tile's bounding volumes. + * + * @private + */ + Cesium3DTile.prototype.updateTransform = function(parentTransform) { + parentTransform = defaultValue(parentTransform, Matrix4.IDENTITY); + var computedTransform = Matrix4.multiply(parentTransform, this.transform, scratchTransform); + var transformChanged = !Matrix4.equals(computedTransform, this.computedTransform); + + if (!transformChanged) { + return; + } + + Matrix4.clone(computedTransform, this.computedTransform); + + // Update the bounding volumes + var header = this._header; + var content = this._header.content; + this._boundingVolume = this.createBoundingVolume(header.boundingVolume, computedTransform, this._boundingVolume); + if (defined(this._contentBoundingVolume)) { + this._contentBoundingVolume = this.createBoundingVolume(content.boundingVolume, computedTransform, this._contentBoundingVolume); + } + if (defined(this._viewerRequestVolume)) { + this._viewerRequestVolume = this.createBoundingVolume(header.viewerRequestVolume, computedTransform, this._viewerRequestVolume); + } + + // Destroy the debug bounding volumes. They will be generated fresh. + this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); + this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); + this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); + }; + + function applyDebugSettings(tile, tileset, frameState) { + var hasContentBoundingVolume = defined(tile._header.content) && defined(tile._header.content.boundingVolume); + + var showVolume = tileset.debugShowBoundingVolume || (tileset.debugShowContentBoundingVolume && !hasContentBoundingVolume); + if (showVolume) { + if (!defined(tile._debugBoundingVolume)) { + var color = tile._finalResolution ? (hasContentBoundingVolume ? Color.WHITE : Color.RED) : Color.YELLOW; + tile._debugBoundingVolume = tile._boundingVolume.createDebugVolume(color); + } + tile._debugBoundingVolume.update(frameState); + } else if (!showVolume && defined(tile._debugBoundingVolume)) { + tile._debugBoundingVolume = tile._debugBoundingVolume.destroy(); + } + + if (tileset.debugShowContentBoundingVolume && hasContentBoundingVolume) { + if (!defined(tile._debugContentBoundingVolume)) { + tile._debugContentBoundingVolume = tile._contentBoundingVolume.createDebugVolume(Color.BLUE); + } + tile._debugContentBoundingVolume.update(frameState); + } else if (!tileset.debugShowContentBoundingVolume && defined(tile._debugContentBoundingVolume)) { + tile._debugContentBoundingVolume = tile._debugContentBoundingVolume.destroy(); + } + + if (tileset.debugShowViewerRequestVolume && defined(tile._viewerRequestVolume)) { + if (!defined(tile._debugViewerRequestVolume)) { + tile._debugViewerRequestVolume = tile._viewerRequestVolume.createDebugVolume(Color.YELLOW); + } + tile._debugViewerRequestVolume.update(frameState); + } else if (!tileset.debugShowViewerRequestVolume && defined(tile._debugViewerRequestVolume)) { + tile._debugViewerRequestVolume = tile._debugViewerRequestVolume.destroy(); + } + + var debugColorizeTilesOn = tileset.debugColorizeTiles && !tile._debugColorizeTiles; + var debugColorizeTilesOff = !tileset.debugColorizeTiles && tile._debugColorizeTiles; + + if (debugColorizeTilesOn) { + tile._debugColorizeTiles = true; + tile.color = tile._debugColor; + } else if (debugColorizeTilesOff) { + tile._debugColorizeTiles = false; + tile.color = Color.WHITE; + } + + if (tile._colorDirty) { + tile._colorDirty = false; + tile._content.applyDebugSettings(true, tile._color); + } + + if (debugColorizeTilesOff) { + tileset.makeStyleDirty(); // Re-apply style now that colorize is switched off + } + } + + function updateContent(tile, tileset, frameState) { + var content = tile._content; + var expiredContent = tile._expiredContent; + + if (defined(expiredContent)) { + if (!tile.contentReady) { + // Render the expired content while the content loads + expiredContent.update(tileset, frameState); + return; + } + + // New content is ready, destroy expired content + tile._expiredContent.destroy(); + tile._expiredContent = undefined; + } + + content.update(tileset, frameState); + } + + /** + * Get the draw commands needed to render this tile. + * + * @private + */ + Cesium3DTile.prototype.update = function(tileset, frameState) { + var initCommandLength = frameState.commandList.length; + applyDebugSettings(this, tileset, frameState); + updateContent(this, tileset, frameState); + this._commandsLength = frameState.commandList.length - initCommandLength; + }; + + var scratchCommandList = []; + + /** + * Processes the tile's content, e.g., create WebGL resources, to move from the PROCESSING to READY state. + * + * @param {Cesium3DTileset} tileset The tileset containing this tile. + * @param {FrameState} frameState The frame state. + * + * @private + */ + Cesium3DTile.prototype.process = function(tileset, frameState) { + var savedCommandList = frameState.commandList; + frameState.commandList = scratchCommandList; + + this._content.update(tileset, frameState); + + scratchCommandList.length = 0; + frameState.commandList = savedCommandList; + }; + + /** + * @private + */ + Cesium3DTile.prototype.isDestroyed = function() { + return false; + }; + + /** + * @private + */ + Cesium3DTile.prototype.destroy = function() { + // For the interval between new content being requested and downloaded, expiredContent === content, so don't destroy twice + this._content = this._content && this._content.destroy(); + this._expiredContent = this._expiredContent && !this._expiredContent.isDestroyed() && this._expiredContent.destroy(); + this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); + this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); + this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); + return destroyObject(this); + }; + + return Cesium3DTile; +}); diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js new file mode 100644 index 000000000000..69f20ac1be21 --- /dev/null +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -0,0 +1,1467 @@ +/*global define*/ +define([ + '../Core/arrayFill', + '../Core/Cartesian2', + '../Core/Cartesian4', + '../Core/Check', + '../Core/clone', + '../Core/Color', + '../Core/combine', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/Math', + '../Core/PixelFormat', + '../Core/RuntimeError', + '../Renderer/ContextLimits', + '../Renderer/DrawCommand', + '../Renderer/Pass', + '../Renderer/PixelDatatype', + '../Renderer/RenderState', + '../Renderer/Sampler', + '../Renderer/ShaderSource', + '../Renderer/Texture', + '../Renderer/TextureMagnificationFilter', + '../Renderer/TextureMinificationFilter', + './AttributeType', + './BlendingState', + './Cesium3DTileColorBlendMode', + './CullFace', + './getBinaryAccessor', + './StencilFunction', + './StencilOperation' + ], function( + arrayFill, + Cartesian2, + Cartesian4, + Check, + clone, + Color, + combine, + ComponentDatatype, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + CesiumMath, + PixelFormat, + RuntimeError, + ContextLimits, + DrawCommand, + Pass, + PixelDatatype, + RenderState, + Sampler, + ShaderSource, + Texture, + TextureMagnificationFilter, + TextureMinificationFilter, + AttributeType, + BlendingState, + Cesium3DTileColorBlendMode, + CullFace, + getBinaryAccessor, + StencilFunction, + StencilOperation) { + 'use strict'; + + /** + * @private + */ + function Cesium3DTileBatchTable(content, featuresLength, batchTableJson, batchTableBinary) { + /** + * @readonly + */ + this.featuresLength = featuresLength; + + this._translucentFeaturesLength = 0; // Number of features in the tile that are translucent + + /** + * @private + */ + this.batchTableJson = batchTableJson; + + /** + * @private + */ + this.batchTableBinary = batchTableBinary; + + var batchTableHierarchy; + var batchTableBinaryProperties; + if (defined(batchTableJson)) { + // Extract the hierarchy and remove it from the batch table json + batchTableHierarchy = batchTableJson.HIERARCHY; + if (defined(batchTableHierarchy)) { + delete batchTableJson.HIERARCHY; + batchTableHierarchy = initializeHierarchy(batchTableHierarchy, batchTableBinary); + } + // Get the binary properties + batchTableBinaryProperties = Cesium3DTileBatchTable.getBinaryProperties(featuresLength, batchTableJson, batchTableBinary); + } + + this._batchTableHierarchy = batchTableHierarchy; + this._batchTableBinaryProperties = batchTableBinaryProperties; + + // PERFORMANCE_IDEA: These parallel arrays probably generate cache misses in get/set color/show + // and use A LOT of memory. How can we use less memory? + this._showAlphaProperties = undefined; // [Show (0 or 255), Alpha (0 to 255)] property for each feature + this._batchValues = undefined; // Per-feature RGBA (A is based on the color's alpha and feature's show property) + + this._batchValuesDirty = false; + this._batchTexture = undefined; + this._defaultTexture = undefined; + + this._pickTexture = undefined; + this._pickIds = []; + + this._content = content; + + // Dimensions for batch and pick textures + var textureDimensions; + var textureStep; + + if (featuresLength > 0) { + // PERFORMANCE_IDEA: this can waste memory in the last row in the uncommon case + // when more than one row is needed (e.g., > 16K features in one tile) + var width = Math.min(featuresLength, ContextLimits.maximumTextureSize); + var height = Math.ceil(featuresLength / ContextLimits.maximumTextureSize); + var stepX = 1.0 / width; + var centerX = stepX * 0.5; + var stepY = 1.0 / height; + var centerY = stepY * 0.5; + + textureDimensions = new Cartesian2(width, height); + textureStep = new Cartesian4(stepX, centerX, stepY, centerY); + } + + this._textureDimensions = textureDimensions; + this._textureStep = textureStep; + } + + defineProperties(Cesium3DTileBatchTable.prototype, { + memorySizeInBytes : { + get : function() { + var memory = 0; + if (defined(this._pickTexture)) { + memory += this._pickTexture.sizeInBytes; + } + if (defined(this._batchTexture)) { + memory += this._batchTexture.sizeInBytes; + } + return memory; + } + } + }); + + function initializeHierarchy(json, binary) { + var i; + var classId; + var binaryAccessor; + + var instancesLength = json.instancesLength; + var classes = json.classes; + var classIds = json.classIds; + var parentCounts = json.parentCounts; + var parentIds = json.parentIds; + var parentIdsLength = instancesLength; + + if (defined(classIds.byteOffset)) { + classIds.componentType = defaultValue(classIds.componentType, ComponentDatatype.UNSIGNED_SHORT); + classIds.type = AttributeType.SCALAR; + binaryAccessor = getBinaryAccessor(classIds); + classIds = binaryAccessor.createArrayBufferView(binary.buffer, binary.byteOffset + classIds.byteOffset, instancesLength); + } + + var parentIndexes; + if (defined(parentCounts)) { + if (defined(parentCounts.byteOffset)) { + parentCounts.componentType = defaultValue(parentCounts.componentType, ComponentDatatype.UNSIGNED_SHORT); + parentCounts.type = AttributeType.SCALAR; + binaryAccessor = getBinaryAccessor(parentCounts); + parentCounts = binaryAccessor.createArrayBufferView(binary.buffer, binary.byteOffset + parentCounts.byteOffset, instancesLength); + } + parentIndexes = new Uint16Array(instancesLength); + parentIdsLength = 0; + for (i = 0; i < instancesLength; ++i) { + parentIndexes[i] = parentIdsLength; + parentIdsLength += parentCounts[i]; + } + } + + if (defined(parentIds) && defined(parentIds.byteOffset)) { + parentIds.componentType = defaultValue(parentIds.componentType, ComponentDatatype.UNSIGNED_SHORT); + parentIds.type = AttributeType.SCALAR; + binaryAccessor = getBinaryAccessor(parentIds); + parentIds = binaryAccessor.createArrayBufferView(binary.buffer, binary.byteOffset + parentIds.byteOffset, parentIdsLength); + } + + var classesLength = classes.length; + for (i = 0; i < classesLength; ++i) { + var classInstancesLength = classes[i].length; + var properties = classes[i].instances; + var binaryProperties = Cesium3DTileBatchTable.getBinaryProperties(classInstancesLength, properties, binary); + classes[i].instances = combine(binaryProperties, properties); + } + + var classCounts = arrayFill(new Array(classesLength), 0); + var classIndexes = new Uint16Array(instancesLength); + for (i = 0; i < instancesLength; ++i) { + classId = classIds[i]; + classIndexes[i] = classCounts[classId]; + ++classCounts[classId]; + } + + var hierarchy = { + classes : classes, + classIds : classIds, + classIndexes : classIndexes, + parentCounts : parentCounts, + parentIndexes : parentIndexes, + parentIds : parentIds + }; + + //>>includeStart('debug', pragmas.debug); + validateHierarchy(hierarchy); + //>>includeEnd('debug'); + + return hierarchy; + } + + //>>includeStart('debug', pragmas.debug); + var scratchValidateStack = []; + function validateHierarchy(hierarchy) { + var stack = scratchValidateStack; + stack.length = 0; + + var classIds = hierarchy.classIds; + var instancesLength = classIds.length; + + for (var i = 0; i < instancesLength; ++i) { + validateInstance(hierarchy, i, stack); + } + } + + function validateInstance(hierarchy, instanceIndex, stack) { + var parentCounts = hierarchy.parentCounts; + var parentIds = hierarchy.parentIds; + var parentIndexes = hierarchy.parentIndexes; + var classIds = hierarchy.classIds; + var instancesLength = classIds.length; + + if (!defined(parentIds)) { + // No need to validate if there are no parents + return; + } + + if (instanceIndex >= instancesLength) { + throw new DeveloperError('Parent index ' + instanceIndex + ' exceeds the total number of instances: ' + instancesLength); + } + if (stack.indexOf(instanceIndex) > -1) { + throw new DeveloperError('Circular dependency detected in the batch table hierarchy.'); + } + + stack.push(instanceIndex); + var parentCount = defined(parentCounts) ? parentCounts[instanceIndex] : 1; + var parentIndex = defined(parentCounts) ? parentIndexes[instanceIndex] : instanceIndex; + for (var i = 0; i < parentCount; ++i) { + var parentId = parentIds[parentIndex + i]; + // Stop the traversal when the instance has no parent (its parentId equals itself), else continue the traversal. + if (parentId !== instanceIndex) { + validateInstance(hierarchy, parentId, stack); + } + } + stack.pop(instanceIndex); + } + //>>includeEnd('debug'); + + Cesium3DTileBatchTable.getBinaryProperties = function(featuresLength, json, binary) { + var binaryProperties; + for (var name in json) { + if (json.hasOwnProperty(name)) { + var property = json[name]; + var byteOffset = property.byteOffset; + if (defined(byteOffset)) { + // This is a binary property + var componentType = property.componentType; + var type = property.type; + if (!defined(componentType)) { + throw new RuntimeError('componentType is required.'); + } + if (!defined(type)) { + throw new RuntimeError('type is required.'); + } + if (!defined(binary)) { + throw new RuntimeError('Property ' + name + ' requires a batch table binary.'); + } + + var binaryAccessor = getBinaryAccessor(property); + var componentCount = binaryAccessor.componentsPerAttribute; + var classType = binaryAccessor.classType; + var typedArray = binaryAccessor.createArrayBufferView(binary.buffer, binary.byteOffset + byteOffset, featuresLength); + + if (!defined(binaryProperties)) { + binaryProperties = {}; + } + + // Store any information needed to access the binary data, including the typed array, + // componentCount (e.g. a VEC4 would be 4), and the type used to pack and unpack (e.g. Cartesian4). + binaryProperties[name] = { + typedArray : typedArray, + componentCount : componentCount, + type : classType + }; + } + } + } + return binaryProperties; + }; + + function getByteLength(batchTable) { + var dimensions = batchTable._textureDimensions; + return (dimensions.x * dimensions.y) * 4; + } + + function getBatchValues(batchTable) { + if (!defined(batchTable._batchValues)) { + // Default batch texture to RGBA = 255: white highlight (RGB) and show/alpha = true/255 (A). + var byteLength = getByteLength(batchTable); + var bytes = new Uint8Array(byteLength); + arrayFill(bytes, 255); + batchTable._batchValues = bytes; + } + + return batchTable._batchValues; + } + + function getShowAlphaProperties(batchTable) { + if (!defined(batchTable._showAlphaProperties)) { + var byteLength = 2 * batchTable.featuresLength; + var bytes = new Uint8Array(byteLength); + // [Show = true, Alpha = 255] + arrayFill(bytes, 255); + batchTable._showAlphaProperties = bytes; + } + return batchTable._showAlphaProperties; + } + + function checkBatchId(batchId, featuresLength) { + if (!defined(batchId) || (batchId < 0) || (batchId > featuresLength)) { + throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + featuresLength - + ').'); + } + } + + Cesium3DTileBatchTable.prototype.setShow = function(batchId, show) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.bool('show', show); + //>>includeEnd('debug'); + + if (show && !defined(this._showAlphaProperties)) { + // Avoid allocating since the default is show = true + return; + } + + var showAlphaProperties = getShowAlphaProperties(this); + var propertyOffset = batchId * 2; + + var newShow = show ? 255 : 0; + if (showAlphaProperties[propertyOffset] !== newShow) { + showAlphaProperties[propertyOffset] = newShow; + + var batchValues = getBatchValues(this); + + // Compute alpha used in the shader based on show and color.alpha properties + var offset = (batchId * 4) + 3; + batchValues[offset] = show ? showAlphaProperties[propertyOffset + 1] : 0; + + this._batchValuesDirty = true; + } + }; + + Cesium3DTileBatchTable.prototype.setAllShow = function(show) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.bool('show', show); + //>>includeEnd('debug'); + + var featuresLength = this.featuresLength; + for (var i = 0; i < featuresLength; ++i) { + this.setShow(i, show); + } + }; + + Cesium3DTileBatchTable.prototype.getShow = function(batchId) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + //>>includeEnd('debug'); + + if (!defined(this._showAlphaProperties)) { + // Avoid allocating since the default is show = true + return true; + } + + var offset = batchId * 2; + return (this._showAlphaProperties[offset] === 255); + }; + + var scratchColorBytes = new Array(4); + + Cesium3DTileBatchTable.prototype.setColor = function(batchId, color) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.object('color', color); + //>>includeEnd('debug'); + + if (Color.equals(color, Color.WHITE) && !defined(this._batchValues)) { + // Avoid allocating since the default is white + return; + } + + var newColor = color.toBytes(scratchColorBytes); + var newAlpha = newColor[3]; + + var batchValues = getBatchValues(this); + var offset = batchId * 4; + + var showAlphaProperties = getShowAlphaProperties(this); + var propertyOffset = batchId * 2; + + if ((batchValues[offset] !== newColor[0]) || + (batchValues[offset + 1] !== newColor[1]) || + (batchValues[offset + 2] !== newColor[2]) || + (showAlphaProperties[propertyOffset + 1] !== newAlpha)) { + + batchValues[offset] = newColor[0]; + batchValues[offset + 1] = newColor[1]; + batchValues[offset + 2] = newColor[2]; + + var wasTranslucent = (showAlphaProperties[propertyOffset + 1] !== 255); + + // Compute alpha used in the shader based on show and color.alpha properties + var show = showAlphaProperties[propertyOffset] !== 0; + batchValues[offset + 3] = show ? newAlpha : 0; + showAlphaProperties[propertyOffset + 1] = newAlpha; + + // Track number of translucent features so we know if this tile needs + // opaque commands, translucent commands, or both for rendering. + var isTranslucent = (newAlpha !== 255); + if (isTranslucent && !wasTranslucent) { + ++this._translucentFeaturesLength; + } else if (!isTranslucent && wasTranslucent) { + --this._translucentFeaturesLength; + } + + this._batchValuesDirty = true; + } + }; + + Cesium3DTileBatchTable.prototype.setAllColor = function(color) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('color', color); + //>>includeEnd('debug'); + + var featuresLength = this.featuresLength; + for (var i = 0; i < featuresLength; ++i) { + this.setColor(i, color); + } + }; + + Cesium3DTileBatchTable.prototype.getColor = function(batchId, result) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.object('result', result); + //>>includeEnd('debug'); + + if (!defined(this._batchValues)) { + return Color.clone(Color.WHITE, result); + } + + var batchValues = this._batchValues; + var offset = batchId * 4; + + var showAlphaProperties = this._showAlphaProperties; + var propertyOffset = batchId * 2; + + return Color.fromBytes(batchValues[offset], + batchValues[offset + 1], + batchValues[offset + 2], + showAlphaProperties[propertyOffset + 1], + result); + }; + + var scratchColor = new Color(); + + Cesium3DTileBatchTable.prototype.applyStyle = function(frameState, style) { + if (!defined(style)) { + this.setAllColor(Color.WHITE); + this.setAllShow(true); + return; + } + + var content = this._content; + var length = this.featuresLength; + for (var i = 0; i < length; ++i) { + var feature = content.getFeature(i); + var color = style.color.evaluateColor(frameState, feature, scratchColor); + var show = style.show.evaluate(frameState, feature); + this.setColor(i, color); + this.setShow(i, show); + } + }; + + function getBinaryProperty(binaryProperty, index) { + var typedArray = binaryProperty.typedArray; + var componentCount = binaryProperty.componentCount; + if (componentCount === 1) { + return typedArray[index]; + } + return binaryProperty.type.unpack(typedArray, index * componentCount); + } + + function setBinaryProperty(binaryProperty, index, value) { + var typedArray = binaryProperty.typedArray; + var componentCount = binaryProperty.componentCount; + if (componentCount === 1) { + typedArray[index] = value; + } else { + binaryProperty.type.pack(value, typedArray, index * componentCount); + } + } + + // The size of this array equals the maximum instance count among all loaded tiles, which has the potential to be large. + var scratchVisited = []; + var scratchStack = []; + var marker = 0; + function traverseHierarchyMultipleParents(hierarchy, instanceIndex, endConditionCallback) { + var classIds = hierarchy.classIds; + var parentCounts = hierarchy.parentCounts; + var parentIds = hierarchy.parentIds; + var parentIndexes = hierarchy.parentIndexes; + var instancesLength = classIds.length; + + // Ignore instances that have already been visited. This occurs in diamond inheritance situations. + // Use a marker value to indicate that an instance has been visited, which increments with each run. + // This is more efficient than clearing the visited array every time. + var visited = scratchVisited; + visited.length = Math.max(visited.length, instancesLength); + var visitedMarker = ++marker; + + var stack = scratchStack; + stack.length = 0; + stack.push(instanceIndex); + + while (stack.length > 0) { + instanceIndex = stack.pop(); + if (visited[instanceIndex] === visitedMarker) { + // This instance has already been visited, stop traversal + continue; + } + visited[instanceIndex] = visitedMarker; + var result = endConditionCallback(hierarchy, instanceIndex); + if (defined(result)) { + // The end condition was met, stop the traversal and return the result + return result; + } + var parentCount = parentCounts[instanceIndex]; + var parentIndex = parentIndexes[instanceIndex]; + for (var i = 0; i < parentCount; ++i) { + var parentId = parentIds[parentIndex + i]; + // Stop the traversal when the instance has no parent (its parentId equals itself) + // else add the parent to the stack to continue the traversal. + if (parentId !== instanceIndex) { + stack.push(parentId); + } + } + } + } + + function traverseHierarchySingleParent(hierarchy, instanceIndex, endConditionCallback) { + var hasParent = true; + while (hasParent) { + var result = endConditionCallback(hierarchy, instanceIndex); + if (defined(result)) { + // The end condition was met, stop the traversal and return the result + return result; + } + var parentId = hierarchy.parentIds[instanceIndex]; + hasParent = parentId !== instanceIndex; + instanceIndex = parentId; + } + } + + function traverseHierarchy(hierarchy, instanceIndex, endConditionCallback) { + // Traverse over the hierarchy and process each instance with the endConditionCallback. + // When the endConditionCallback returns a value, the traversal stops and that value is returned. + var parentCounts = hierarchy.parentCounts; + var parentIds = hierarchy.parentIds; + if (!defined(parentIds)) { + return endConditionCallback(hierarchy, instanceIndex); + } else if (defined(parentCounts)) { + return traverseHierarchyMultipleParents(hierarchy, instanceIndex, endConditionCallback); + } + return traverseHierarchySingleParent(hierarchy, instanceIndex, endConditionCallback); + } + + function hasPropertyInHierarchy(batchTable, batchId, name) { + var hierarchy = batchTable._batchTableHierarchy; + var result = traverseHierarchy(hierarchy, batchId, function(hierarchy, instanceIndex) { + var classId = hierarchy.classIds[instanceIndex]; + var instances = hierarchy.classes[classId].instances; + if (defined(instances[name])) { + return true; + } + }); + return defined(result); + } + + function getPropertyNamesInHierarchy(batchTable, batchId, results) { + var hierarchy = batchTable._batchTableHierarchy; + traverseHierarchy(hierarchy, batchId, function(hierarchy, instanceIndex) { + var classId = hierarchy.classIds[instanceIndex]; + var instances = hierarchy.classes[classId].instances; + for (var name in instances) { + if (instances.hasOwnProperty(name)) { + if (results.indexOf(name) === -1) { + results.push(name); + } + } + } + }); + } + + function getHierarchyProperty(batchTable, batchId, name) { + var hierarchy = batchTable._batchTableHierarchy; + return traverseHierarchy(hierarchy, batchId, function(hierarchy, instanceIndex) { + var classId = hierarchy.classIds[instanceIndex]; + var instanceClass = hierarchy.classes[classId]; + var indexInClass = hierarchy.classIndexes[instanceIndex]; + var propertyValues = instanceClass.instances[name]; + if (defined(propertyValues)) { + if (defined(propertyValues.typedArray)) { + return getBinaryProperty(propertyValues, indexInClass); + } + return clone(propertyValues[indexInClass], true); + } + }); + } + + function setHierarchyProperty(batchTable, batchId, name, value) { + var hierarchy = batchTable._batchTableHierarchy; + var result = traverseHierarchy(hierarchy, batchId, function(hierarchy, instanceIndex) { + var classId = hierarchy.classIds[instanceIndex]; + var instanceClass = hierarchy.classes[classId]; + var indexInClass = hierarchy.classIndexes[instanceIndex]; + var propertyValues = instanceClass.instances[name]; + if (defined(propertyValues)) { + //>>includeStart('debug', pragmas.debug); + if (instanceIndex !== batchId) { + throw new DeveloperError('Inherited property "' + name + '" is read-only.'); + } + //>>includeEnd('debug'); + if (defined(propertyValues.typedArray)) { + setBinaryProperty(propertyValues, indexInClass, value); + } else { + propertyValues[indexInClass] = clone(value, true); + } + return true; + } + }); + return defined(result); + } + + Cesium3DTileBatchTable.prototype.isClass = function(batchId, className) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.string('className', className); + //>>includeEnd('debug'); + + // PERFORMANCE_IDEA : cache results in the ancestor classes to speed up this check if this area becomes a hotspot + var hierarchy = this._batchTableHierarchy; + if (!defined(hierarchy)) { + return false; + } + + // PERFORMANCE_IDEA : treat class names as integers for faster comparisons + var result = traverseHierarchy(hierarchy, batchId, function(hierarchy, instanceIndex) { + var classId = hierarchy.classIds[instanceIndex]; + var instanceClass = hierarchy.classes[classId]; + if (instanceClass.name === className) { + return true; + } + }); + return defined(result); + }; + + Cesium3DTileBatchTable.prototype.isExactClass = function(batchId, className) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('className', className); + //>>includeEnd('debug'); + + return (this.getExactClassName(batchId) === className); + }; + + Cesium3DTileBatchTable.prototype.getExactClassName = function(batchId) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + //>>includeEnd('debug'); + + var hierarchy = this._batchTableHierarchy; + if (!defined(hierarchy)) { + return undefined; + } + var classId = hierarchy.classIds[batchId]; + var instanceClass = hierarchy.classes[classId]; + return instanceClass.name; + }; + + Cesium3DTileBatchTable.prototype.hasProperty = function(batchId, name) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.string('name', name); + //>>includeEnd('debug'); + + var json = this.batchTableJson; + return (defined(json) && defined(json[name])) || (defined(this._batchTableHierarchy) && hasPropertyInHierarchy(this, batchId, name)); + }; + + Cesium3DTileBatchTable.prototype.getPropertyNames = function(batchId, results) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + //>>includeEnd('debug'); + + results = defined(results) ? results : []; + results.length = 0; + + var json = this.batchTableJson; + for (var name in json) { + if (json.hasOwnProperty(name)) { + results.push(name); + } + } + + if (defined(this._batchTableHierarchy)) { + getPropertyNamesInHierarchy(this, batchId, results); + } + + return results; + }; + + Cesium3DTileBatchTable.prototype.getProperty = function(batchId, name) { + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, this.featuresLength); + Check.typeOf.string('name', name); + //>>includeEnd('debug'); + + if (!defined(this.batchTableJson)) { + return undefined; + } + + if (defined(this._batchTableBinaryProperties)) { + var binaryProperty = this._batchTableBinaryProperties[name]; + if (defined(binaryProperty)) { + return getBinaryProperty(binaryProperty, batchId); + } + } + + var propertyValues = this.batchTableJson[name]; + if (defined(propertyValues)) { + return clone(propertyValues[batchId], true); + } + + if (defined(this._batchTableHierarchy)) { + var hierarchyProperty = getHierarchyProperty(this, batchId, name); + if (defined(hierarchyProperty)) { + return hierarchyProperty; + } + } + + return undefined; + }; + + Cesium3DTileBatchTable.prototype.setProperty = function(batchId, name, value) { + var featuresLength = this.featuresLength; + //>>includeStart('debug', pragmas.debug); + checkBatchId(batchId, featuresLength); + Check.typeOf.string('name', name); + //>>includeEnd('debug'); + + if (defined(this._batchTableBinaryProperties)) { + var binaryProperty = this._batchTableBinaryProperties[name]; + if (defined(binaryProperty)) { + setBinaryProperty(binaryProperty, batchId, value); + return; + } + } + + if (defined(this._batchTableHierarchy)) { + if (setHierarchyProperty(this, batchId, name, value)) { + return; + } + } + + if (!defined(this.batchTableJson)) { + // Tile payload did not have a batch table. Create one for new user-defined properties. + this.batchTableJson = {}; + } + + var propertyValues = this.batchTableJson[name]; + + if (!defined(propertyValues)) { + // Property does not exist. Create it. + this.batchTableJson[name] = new Array(featuresLength); + propertyValues = this.batchTableJson[name]; + } + + propertyValues[batchId] = clone(value, true); + }; + + function getGlslComputeSt(batchTable) { + // GLSL batchId is zero-based: [0, featuresLength - 1] + if (batchTable._textureDimensions.y === 1) { + return 'uniform vec4 tile_textureStep; \n' + + 'vec2 computeSt(float batchId) \n' + + '{ \n' + + ' float stepX = tile_textureStep.x; \n' + + ' float centerX = tile_textureStep.y; \n' + + ' return vec2(centerX + (batchId * stepX), 0.5); \n' + + '} \n'; + } + + return 'uniform vec4 tile_textureStep; \n' + + 'uniform vec2 tile_textureDimensions; \n' + + 'vec2 computeSt(float batchId) \n' + + '{ \n' + + ' float stepX = tile_textureStep.x; \n' + + ' float centerX = tile_textureStep.y; \n' + + ' float stepY = tile_textureStep.z; \n' + + ' float centerY = tile_textureStep.w; \n' + + ' float xId = mod(batchId, tile_textureDimensions.x); \n' + + ' float yId = floor(batchId / tile_textureDimensions.x); \n' + + ' return vec2(centerX + (xId * stepX), 1.0 - (centerY + (yId * stepY))); \n' + + '} \n'; + } + + Cesium3DTileBatchTable.prototype.getVertexShaderCallback = function(handleTranslucent, batchIdAttributeName) { + if (this.featuresLength === 0) { + return; + } + + var that = this; + return function(source) { + var renamedSource = ShaderSource.replaceMain(source, 'tile_main'); + var newMain; + + if (ContextLimits.maximumVertexTextureImageUnits > 0) { + // When VTF is supported, perform per-feature show/hide in the vertex shader + newMain = + 'uniform sampler2D tile_batchTexture; \n' + + 'uniform bool tile_translucentCommand; \n' + + 'varying vec4 tile_featureColor; \n' + + 'void main() \n' + + '{ \n' + + ' tile_main(); \n' + + ' vec2 st = computeSt(' + batchIdAttributeName + '); \n' + + ' vec4 featureProperties = texture2D(tile_batchTexture, st); \n' + + ' float show = ceil(featureProperties.a); \n' + // 0 - false, non-zeo - true + ' gl_Position *= show; \n'; // Per-feature show/hide + if (handleTranslucent) { + newMain += + ' bool isStyleTranslucent = (featureProperties.a != 1.0); \n' + + ' if (czm_pass == czm_passTranslucent) \n' + + ' { \n' + + ' if (!isStyleTranslucent && !tile_translucentCommand) \n' + // Do not render opaque features in the translucent pass + ' { \n' + + ' gl_Position *= 0.0; \n' + + ' } \n' + + ' } \n' + + ' else \n' + + ' { \n' + + ' if (isStyleTranslucent) \n' + // Do not render translucent features in the opaque pass + ' { \n' + + ' gl_Position *= 0.0; \n' + + ' } \n' + + ' } \n'; + } + newMain += + ' tile_featureColor = featureProperties; \n' + + '}'; + } else { + newMain = + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' tile_main(); \n' + + ' tile_featureSt = computeSt(' + batchIdAttributeName + '); \n' + + '}'; + } + + return renamedSource + '\n' + getGlslComputeSt(that) + newMain; + }; + }; + + function getHighlightOnlyShader(source) { + source = ShaderSource.replaceMain(source, 'tile_main'); + return source + + 'void tile_color(vec4 tile_featureColor) \n' + + '{ \n' + + ' tile_main(); \n' + + ' gl_FragColor *= tile_featureColor; \n' + + '} \n'; + } + + function modifyDiffuse(source, diffuseUniformName) { + // If the glTF does not specify the _3DTILESDIFFUSE semantic, return a basic highlight shader. + // Otherwise if _3DTILESDIFFUSE is defined prefer the shader below that can switch the color mode at runtime. + if (!defined(diffuseUniformName)) { + return getHighlightOnlyShader(source); + } + + // Find the diffuse uniform. Examples matches: + // uniform vec3 u_diffuseColor; + // uniform sampler2D diffuseTexture; + var regex = new RegExp('uniform\\s+(vec[34]|sampler2D)\\s+' + diffuseUniformName + ';'); + var uniformMatch = source.match(regex); + + if (!defined(uniformMatch)) { + // Could not find uniform declaration of type vec3, vec4, or sampler2D + return getHighlightOnlyShader(source); + } + + var declaration = uniformMatch[0]; + var type = uniformMatch[1]; + + source = ShaderSource.replaceMain(source, 'tile_main'); + source = source.replace(declaration, ''); // Remove uniform declaration for now so the replace below doesn't affect it + + // If the tile color is white, use the source color. This implies the feature has not been styled. + // Highlight: tile_colorBlend is 0.0 and the source color is used + // Replace: tile_colorBlend is 1.0 and the tile color is used + // Mix: tile_colorBlend is between 0.0 and 1.0, causing the source color and tile color to mix + var finalDiffuseFunction = + 'vec4 tile_diffuse_final(vec4 sourceDiffuse, vec4 tileDiffuse) \n' + + '{ \n' + + ' vec4 blendDiffuse = mix(sourceDiffuse, tileDiffuse, tile_colorBlend); \n' + + ' vec4 diffuse = (tileDiffuse.rgb == vec3(1.0)) ? sourceDiffuse : blendDiffuse; \n' + + ' return vec4(diffuse.rgb, sourceDiffuse.a); \n' + + '} \n'; + + // The color blend mode is intended for the RGB channels so alpha is always just multiplied. + // gl_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight) + var applyHighlight = + ' gl_FragColor.a *= tile_featureColor.a; \n' + + ' float highlight = ceil(tile_colorBlend); \n' + + ' gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n'; + + var setColor; + if (type === 'vec3' || type === 'vec4') { + var sourceDiffuse = (type === 'vec3') ? ('vec4(' + diffuseUniformName + ', 1.0)') : diffuseUniformName; + var replaceDiffuse = (type === 'vec3') ? 'tile_diffuse.xyz' : 'tile_diffuse'; + regex = new RegExp(diffuseUniformName, 'g'); + source = source.replace(regex, replaceDiffuse); + setColor = + ' vec4 source = ' + sourceDiffuse + '; \n' + + ' tile_diffuse = tile_diffuse_final(source, tile_featureColor); \n' + + ' tile_main(); \n'; + } else if (type === 'sampler2D') { + regex = new RegExp('texture2D\\(' + diffuseUniformName + '.*?\\)', 'g'); + source = source.replace(regex, 'tile_diffuse_final($&, tile_diffuse)'); + setColor = + ' tile_diffuse = tile_featureColor; \n' + + ' tile_main(); \n'; + } + + source = + 'uniform float tile_colorBlend; \n' + + 'vec4 tile_diffuse = vec4(1.0); \n' + + finalDiffuseFunction + + declaration + '\n' + + source + '\n' + + 'void tile_color(vec4 tile_featureColor) \n' + + '{ \n' + + setColor + + applyHighlight + + '} \n'; + + return source; + } + + Cesium3DTileBatchTable.prototype.getFragmentShaderCallback = function(handleTranslucent, diffuseUniformName) { + if (this.featuresLength === 0) { + return; + } + return function(source) { + source = modifyDiffuse(source, diffuseUniformName); + if (ContextLimits.maximumVertexTextureImageUnits > 0) { + // When VTF is supported, per-feature show/hide already happened in the fragment shader + source += + 'varying vec4 tile_featureColor; \n' + + 'void main() \n' + + '{ \n' + + ' tile_color(tile_featureColor); \n' + + '}'; + } else { + source += + 'uniform sampler2D tile_batchTexture; \n' + + 'uniform bool tile_translucentCommand; \n' + + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' vec4 featureProperties = texture2D(tile_batchTexture, tile_featureSt); \n' + + ' if (featureProperties.a == 0.0) { \n' + // show: alpha == 0 - false, non-zeo - true + ' discard; \n' + + ' } \n'; + + if (handleTranslucent) { + source += + ' bool isStyleTranslucent = (featureProperties.a != 1.0); \n' + + ' if (czm_pass == czm_passTranslucent) \n' + + ' { \n' + + ' if (!isStyleTranslucent && !tile_translucentCommand) \n' + // Do not render opaque features in the translucent pass + ' { \n' + + ' discard; \n' + + ' } \n' + + ' } \n' + + ' else \n' + + ' { \n' + + ' if (isStyleTranslucent) \n' + // Do not render translucent features in the opaque pass + ' { \n' + + ' discard; \n' + + ' } \n' + + ' } \n'; + } + + source += + ' tile_color(featureProperties); \n' + + '} \n'; + } + return source; + }; + }; + + function getColorBlend(batchTable) { + var tileset = batchTable._content._tileset; + var colorBlendMode = tileset.colorBlendMode; + var colorBlendAmount = tileset.colorBlendAmount; + if (colorBlendMode === Cesium3DTileColorBlendMode.HIGHLIGHT) { + return 0.0; + } + if (colorBlendMode === Cesium3DTileColorBlendMode.REPLACE) { + return 1.0; + } + if (colorBlendMode === Cesium3DTileColorBlendMode.MIX) { + // The value 0.0 is reserved for highlight, so clamp to just above 0.0. + return CesiumMath.clamp(colorBlendAmount, CesiumMath.EPSILON4, 1.0); + } + //>>includeStart('debug', pragmas.debug); + throw new DeveloperError('Invalid color blend mode "' + colorBlendMode + '".'); + //>>includeEnd('debug'); + } + + Cesium3DTileBatchTable.prototype.getUniformMapCallback = function() { + if (this.featuresLength === 0) { + return; + } + + var that = this; + return function(uniformMap) { + var batchUniformMap = { + tile_batchTexture : function() { + // PERFORMANCE_IDEA: we could also use a custom shader that avoids the texture read. + return defaultValue(that._batchTexture, that._defaultTexture); + }, + tile_textureDimensions : function() { + return that._textureDimensions; + }, + tile_textureStep : function() { + return that._textureStep; + }, + tile_colorBlend : function() { + return getColorBlend(that); + } + }; + + return combine(uniformMap, batchUniformMap); + }; + }; + + Cesium3DTileBatchTable.prototype.getPickVertexShaderCallback = function(batchIdAttributeName) { + if (this.featuresLength === 0) { + return; + } + + var that = this; + return function(source) { + var renamedSource = ShaderSource.replaceMain(source, 'tile_main'); + var newMain; + + if (ContextLimits.maximumVertexTextureImageUnits > 0) { + // When VTF is supported, perform per-feature show/hide in the vertex shader + newMain = + 'uniform sampler2D tile_batchTexture; \n' + + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' tile_main(); \n' + + ' vec2 st = computeSt(' + batchIdAttributeName + '); \n' + + ' vec4 featureProperties = texture2D(tile_batchTexture, st); \n' + + ' float show = ceil(featureProperties.a); \n' + // 0 - false, non-zero - true + ' gl_Position *= show; \n' + // Per-feature show/hide + ' tile_featureSt = st; \n' + + '}'; + } else { + newMain = + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' tile_main(); \n' + + ' tile_featureSt = computeSt(' + batchIdAttributeName + '); \n' + + '}'; + } + + return renamedSource + '\n' + getGlslComputeSt(that) + newMain; + }; + }; + + Cesium3DTileBatchTable.prototype.getPickFragmentShaderCallback = function() { + if (this.featuresLength === 0) { + return; + } + + return function(source) { + var renamedSource = ShaderSource.replaceMain(source, 'tile_main'); + var newMain; + + // Pick shaders do not need to take into account per-feature color/alpha. + // (except when alpha is zero, which is treated as if show is false, so + // it does not write depth in the color or pick pass). + if (ContextLimits.maximumVertexTextureImageUnits > 0) { + // When VTF is supported, per-feature show/hide already happened in the fragment shader + newMain = + 'uniform sampler2D tile_pickTexture; \n' + + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' tile_main(); \n' + + ' if (gl_FragColor.a == 0.0) { \n' + // per-feature show: alpha == 0 - false, non-zeo - true + ' discard; \n' + + ' } \n' + + ' gl_FragColor = texture2D(tile_pickTexture, tile_featureSt); \n' + + '}'; + } else { + newMain = + 'uniform sampler2D tile_pickTexture; \n' + + 'uniform sampler2D tile_batchTexture; \n' + + 'varying vec2 tile_featureSt; \n' + + 'void main() \n' + + '{ \n' + + ' vec4 featureProperties = texture2D(tile_batchTexture, tile_featureSt); \n' + + ' if (featureProperties.a == 0.0) { \n' + // per-feature show: alpha == 0 - false, non-zeo - true + ' discard; \n' + + ' } \n' + + ' tile_main(); \n' + + ' if (gl_FragColor.a == 0.0) { \n' + + ' discard; \n' + + ' } \n' + + ' gl_FragColor = texture2D(tile_pickTexture, tile_featureSt); \n' + + '}'; + } + + return renamedSource + '\n' + newMain; + }; + }; + + Cesium3DTileBatchTable.prototype.getPickUniformMapCallback = function() { + if (this.featuresLength === 0) { + return; + } + + var that = this; + return function(uniformMap) { + var batchUniformMap = { + tile_batchTexture : function() { + return defaultValue(that._batchTexture, that._defaultTexture); + }, + tile_textureDimensions : function() { + return that._textureDimensions; + }, + tile_textureStep : function() { + return that._textureStep; + }, + tile_pickTexture : function() { + return that._pickTexture; + } + }; + + return combine(batchUniformMap, uniformMap); + }; + }; + + /////////////////////////////////////////////////////////////////////////// + + var StyleCommandsNeeded = { + ALL_OPAQUE : 0, + ALL_TRANSLUCENT : 1, + OPAQUE_AND_TRANSLUCENT : 2 + }; + + Cesium3DTileBatchTable.prototype.addDerivedCommands = function(frameState, commandStart) { + var commandList = frameState.commandList; + var commandEnd = commandList.length; + var tile = this._content._tile; + var tileset = tile._tileset; + var bivariateVisibilityTest = tileset.skipLevelOfDetail && tileset._hasMixedContent && frameState.context.stencilBuffer; + var styleCommandsNeeded = getStyleCommandsNeeded(this); + + for (var i = commandStart; i < commandEnd; ++i) { + var command = commandList[i]; + var derivedCommands = command.derivedCommands.tileset; + if (!defined(derivedCommands)) { + derivedCommands = {}; + command.derivedCommands.tileset = derivedCommands; + derivedCommands.originalCommand = deriveCommand(command); + } + + updateDerivedCommand(derivedCommands.originalCommand, command); + + if (styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE) { + if (!defined(derivedCommands.translucent)) { + derivedCommands.translucent = deriveTranslucentCommand(derivedCommands.originalCommand); + } + updateDerivedCommand(derivedCommands.translucent, command); + } + + if (bivariateVisibilityTest) { + if (command.pass !== Pass.TRANSLUCENT) { + if (!defined(derivedCommands.zback)) { + derivedCommands.zback = deriveZBackfaceCommand(derivedCommands.originalCommand); + } + tileset._backfaceCommands.push(derivedCommands.zback); + } + if (!defined(derivedCommands.stencil) || tile._selectionDepth !== tile._lastSelectionDepth) { + derivedCommands.stencil = deriveStencilCommand(derivedCommands.originalCommand, tile._selectionDepth); + tile._lastSelectionDepth = tile._selectionDepth; + } + updateDerivedCommand(derivedCommands.stencil, command); + } + + var opaqueCommand = bivariateVisibilityTest ? derivedCommands.stencil : derivedCommands.originalCommand; + var translucentCommand = derivedCommands.translucent; + + // If the command was originally opaque: + // * If the styling applied to the tile is all opaque, use the original command + // (with one additional uniform needed for the shader). + // * If the styling is all translucent, use new (cached) derived commands (front + // and back faces) with a translucent render state. + // * If the styling causes both opaque and translucent features in this tile, + // then use both sets of commands. + if (command.pass !== Pass.TRANSLUCENT) { + if (styleCommandsNeeded === StyleCommandsNeeded.ALL_OPAQUE) { + commandList[i] = opaqueCommand; + } + if (styleCommandsNeeded === StyleCommandsNeeded.ALL_TRANSLUCENT) { + commandList[i] = translucentCommand; + } + if (styleCommandsNeeded === StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT) { + // PERFORMANCE_IDEA: if the tile has multiple commands, we do not know what features are in what + // commands so this case may be overkill. + commandList[i] = opaqueCommand; + commandList.push(translucentCommand); + } + } else { + // Command was originally translucent so no need to derive new commands; + // as of now, a style can't change an originally translucent feature to + // opaque since the style's alpha is modulated, not a replacement. When + // this changes, we need to derive new opaque commands here. + commandList[i] = opaqueCommand; + } + } + }; + + function updateDerivedCommand(derivedCommand, command) { + derivedCommand.castShadows = command.castShadows; + derivedCommand.receiveShadows = command.receiveShadows; + derivedCommand.primitiveType = command.primitiveType; + } + + function getStyleCommandsNeeded(batchTable) { + var translucentFeaturesLength = batchTable._translucentFeaturesLength; + + if (translucentFeaturesLength === 0) { + return StyleCommandsNeeded.ALL_OPAQUE; + } else if (translucentFeaturesLength === batchTable.featuresLength) { + return StyleCommandsNeeded.ALL_TRANSLUCENT; + } + + return StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT; + } + + function deriveCommand(command) { + var derivedCommand = DrawCommand.shallowClone(command); + + // Add a uniform to indicate if the original command was translucent so + // the shader knows not to cull vertices that were originally transparent + // even though their style is opaque. + var translucentCommand = (derivedCommand.pass === Pass.TRANSLUCENT); + + if (!translucentCommand) { + derivedCommand.pass = Pass.CESIUM_3D_TILE; + } + + derivedCommand.uniformMap = defined(derivedCommand.uniformMap) ? derivedCommand.uniformMap : {}; + derivedCommand.uniformMap.tile_translucentCommand = function() { + return translucentCommand; + }; + + return derivedCommand; + } + + function deriveTranslucentCommand(command) { + var derivedCommand = DrawCommand.shallowClone(command); + derivedCommand.pass = Pass.TRANSLUCENT; + derivedCommand.renderState = getTranslucentRenderState(command.renderState); + return derivedCommand; + } + + function deriveZBackfaceCommand(command) { + // Write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front + var derivedCommand = DrawCommand.shallowClone(command); + var rs = clone(derivedCommand.renderState, true); + rs.cull.enabled = true; + rs.cull.face = CullFace.FRONT; + derivedCommand.renderState = RenderState.fromCache(rs); + derivedCommand.castShadows = false; + derivedCommand.receiveShadows = false; + return derivedCommand; + } + + function deriveStencilCommand(command, reference) { + var derivedCommand = command; + if (command.renderState.depthMask) { // ignore if tile does not write depth (ex. translucent) + // Tiles only draw if their selection depth is >= the tile drawn already. They write their + // selection depth to the stencil buffer to prevent ancestor tiles from drawing on top + derivedCommand = DrawCommand.shallowClone(command); + var rs = clone(derivedCommand.renderState, true); + rs.stencilTest.enabled = true; + rs.stencilTest.reference = reference; + rs.stencilTest.frontFunction = StencilFunction.GREATER_OR_EQUAL; + rs.stencilTest.frontOperation.zPass = StencilOperation.REPLACE; + derivedCommand.renderState = RenderState.fromCache(rs); + } + return derivedCommand; + } + + function getTranslucentRenderState(renderState) { + var rs = clone(renderState, true); + rs.cull.enabled = false; + rs.depthTest.enabled = true; + rs.depthMask = false; + rs.blending = BlendingState.ALPHA_BLEND; + + return RenderState.fromCache(rs); + } + + /////////////////////////////////////////////////////////////////////////// + + function createTexture(batchTable, context, bytes) { + var dimensions = batchTable._textureDimensions; + return new Texture({ + context : context, + pixelFormat : PixelFormat.RGBA, + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + source : { + width : dimensions.x, + height : dimensions.y, + arrayBufferView : bytes + }, + sampler : new Sampler({ + minificationFilter : TextureMinificationFilter.NEAREST, + magnificationFilter : TextureMagnificationFilter.NEAREST + }) + }); + } + + function createPickTexture(batchTable, context) { + var featuresLength = batchTable.featuresLength; + if (!defined(batchTable._pickTexture) && (featuresLength > 0)) { + var pickIds = batchTable._pickIds; + var byteLength = getByteLength(batchTable); + var bytes = new Uint8Array(byteLength); + var content = batchTable._content; + + // PERFORMANCE_IDEA: we could skip the pick texture completely by allocating + // a continuous range of pickIds and then converting the base pickId + batchId + // to RGBA in the shader. The only consider is precision issues, which might + // not be an issue in WebGL 2. + for (var i = 0; i < featuresLength; ++i) { + var pickId = context.createPickId(content.getFeature(i)); + pickIds.push(pickId); + + var pickColor = pickId.color; + var offset = i * 4; + bytes[offset] = Color.floatToByte(pickColor.red); + bytes[offset + 1] = Color.floatToByte(pickColor.green); + bytes[offset + 2] = Color.floatToByte(pickColor.blue); + bytes[offset + 3] = Color.floatToByte(pickColor.alpha); + } + + batchTable._pickTexture = createTexture(batchTable, context, bytes); + content._tileset._statistics.batchTableByteLength += batchTable._pickTexture.sizeInBytes; + } + } + + function updateBatchTexture(batchTable) { + var dimensions = batchTable._textureDimensions; + // PERFORMANCE_IDEA: Instead of rewriting the entire texture, use fine-grained + // texture updates when less than, for example, 10%, of the values changed. Or + // even just optimize the common case when one feature show/color changed. + batchTable._batchTexture.copyFrom({ + width : dimensions.x, + height : dimensions.y, + arrayBufferView : batchTable._batchValues + }); + } + + Cesium3DTileBatchTable.prototype.update = function(tileset, frameState) { + var context = frameState.context; + this._defaultTexture = context.defaultTexture; + + if (frameState.passes.pick) { + // Create pick texture on-demand + createPickTexture(this, context); + } + + if (this._batchValuesDirty) { + this._batchValuesDirty = false; + + // Create batch texture on-demand + if (!defined(this._batchTexture)) { + this._batchTexture = createTexture(this, context, this._batchValues); + tileset._statistics.batchTableByteLength += this._batchTexture.sizeInBytes; + } + + updateBatchTexture(this); // Apply per-feature show/color updates + } + }; + + Cesium3DTileBatchTable.prototype.isDestroyed = function() { + return false; + }; + + Cesium3DTileBatchTable.prototype.destroy = function() { + this._batchTexture = this._batchTexture && this._batchTexture.destroy(); + this._pickTexture = this._pickTexture && this._pickTexture.destroy(); + + var pickIds = this._pickIds; + var length = pickIds.length; + for (var i = 0; i < length; ++i) { + pickIds[i].destroy(); + } + + return destroyObject(this); + }; + + return Cesium3DTileBatchTable; +}); diff --git a/Source/Scene/Cesium3DTileChildrenVisibility.js b/Source/Scene/Cesium3DTileChildrenVisibility.js new file mode 100644 index 000000000000..338fff0e205f --- /dev/null +++ b/Source/Scene/Cesium3DTileChildrenVisibility.js @@ -0,0 +1,20 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * @private + */ + var Cesium3DTileChildrenVisibility = { + NONE : 0, // No children visible + 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 + VISIBLE_NOT_IN_REQUEST_VOLUME : 8 // At least one child visible but not in viewer request volume + }; + + return freezeObject(Cesium3DTileChildrenVisibility); +}); diff --git a/Source/Scene/Cesium3DTileColorBlendMode.js b/Source/Scene/Cesium3DTileColorBlendMode.js new file mode 100644 index 000000000000..1c1d7c4fbb17 --- /dev/null +++ b/Source/Scene/Cesium3DTileColorBlendMode.js @@ -0,0 +1,57 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * Defines how per-feature colors set from the Cesium API or declarative styling blend with the source colors from + * the original feature, e.g. glTF material or per-point color in the tile. + *

+ * When REPLACE or MIX are used and the source color is a glTF material, the technique must assign the + * _3DTILESDIFFUSE semantic to the diffuse color parameter. Otherwise only HIGHLIGHT is supported. + *

+ *

+     * "techniques": {
+     *   "technique0": {
+     *     "parameters": {
+     *       "diffuse": {
+     *         "semantic": "_3DTILESDIFFUSE",
+     *         "type": 35666
+     *       }
+     *     }
+     *   }
+     * }
+     * 
+ * + * @exports Cesium3DTileColorBlendMode + */ + var Cesium3DTileColorBlendMode = { + /** + * Multiplies the source color by the feature color. + * + * @type {Number} + * @constant + */ + HIGHLIGHT : 0, + + /** + * Replaces the source color with the feature color. + * + * @type {Number} + * @constant + */ + REPLACE : 1, + + /** + * Blends the source color and feature color together. + * + * @type {Number} + * @constant + */ + MIX : 2 + }; + + return freezeObject(Cesium3DTileColorBlendMode); +}); diff --git a/Source/Scene/Cesium3DTileContent.js b/Source/Scene/Cesium3DTileContent.js new file mode 100644 index 000000000000..f39056a7c2be --- /dev/null +++ b/Source/Scene/Cesium3DTileContent.js @@ -0,0 +1,346 @@ +/*global define*/ +define([ + '../Core/defineProperties', + '../Core/DeveloperError' + ], function( + defineProperties, + DeveloperError) { + 'use strict'; + + /** + * The content of a tile in a {@link Cesium3DTileset}. + *

+ * Derived classes of this interface provide access to individual features in the tile. + * Access derived objects through {@link Cesium3DTile#content}. + *

+ *

+ * This type describes an interface and is not intended to be instantiated directly. + *

+ * + * @alias Cesium3DTileContent + * @constructor + */ + function Cesium3DTileContent(tileset, tile, url, arrayBuffer, byteOffset) { + /** + * Gets or sets if any feature's property changed. Used to + * optimized applying a style when a feature's property changed. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @type {Boolean} + * + * @private + */ + this.featurePropertiesDirty = false; + } + + defineProperties(Cesium3DTileContent.prototype, { + /** + * Gets the number of features in the tile. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + featuresLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the number of points in the tile. + *

+ * Only applicable for tiles with Point Cloud content. This is different than {@link Cesium3DTileContent#featuresLength} which + * equals the number of groups of points as distinguished by the BATCH_ID feature table semantic. + *

+ * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/PointCloud/README.md#batched-points} + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + pointsLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the number of triangles in the tile. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + trianglesLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the tile's geometry memory in bytes. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + geometryByteLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the tile's texture memory in bytes. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + texturesByteLength : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the amount of memory used by the batch table textures, in bytes. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Number} + * @readonly + */ + batchTableByteLength : { + 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. + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Composite/README.md} + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Array} + * @readonly + */ + innerContents : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the promise that will be resolved when the tile's content is ready to render. + * + * @memberof Cesium3DTileContent.prototype + * + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the tileset for this tile. + * + * @type {Cesium3DTileset} + * @readonly + */ + tileset : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the tile containing this content. + * + * @type {Cesium3DTile} + * @readonly + */ + tile : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the url of the tile's content. + * @memberof Cesium3DTileContent.prototype + * + * @type {String} + * @readonly + */ + url : { + get : function() { + DeveloperError.throwInstantiationError(); + } + }, + + /** + * Gets the batch table for this content. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @type {Cesium3DTileBatchTable} + * @readonly + * + * @private + */ + batchTable : { + get : function() { + DeveloperError.throwInstantiationError(); + } + } + }); + + /** + * Determines if the tile's batch table has a property. If it does, each feature in + * the tile will have the property. + * + * @param {Number} batchId The batchId for the feature. + * @param {String} name The case-sensitive name of the property. + * @returns {Boolean} true if the property exists; otherwise, false. + */ + Cesium3DTileContent.prototype.hasProperty = function(batchId, name) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Returns the {@link Cesium3DTileFeature} object for the feature with the + * given batchId. This object is used to get and modify the + * feature's properties. + *

+ * Features in a tile are ordered by batchId, an index used to retrieve their metadata from the batch table. + *

+ * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/TileFormats/BatchTable}. + * + * @param {Number} batchId The batchId for the feature. + * @returns {Cesium3DTileFeature} The corresponding {@link Cesium3DTileFeature} object. + * + * @exception {DeveloperError} batchId must be between zero and {@link Cesium3DTileContent#featuresLength} - 1. + */ + Cesium3DTileContent.prototype.getFeature = function(batchId) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Called when {@link Cesium3DTileset#debugColorizeTiles} changes. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @param {Boolean} enabled Whether to enable or disable debug settings. + * @returns {Cesium3DTileFeature} The corresponding {@link Cesium3DTileFeature} object. + + * @private + */ + Cesium3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Apply a style to the content + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @param {FrameSate} frameState The frame state. + * @param {Cesium3DTileStyle} style The style. + * + * @private + */ + Cesium3DTileContent.prototype.applyStyle = function(frameState, style) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Called by the tile during tileset traversal to get the draw commands needed to render this content. + * When the tile's content is in the PROCESSING state, this creates WebGL resources to ultimately + * move to the READY state. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @param {Cesium3DTileset} tileset The tileset containing this tile. + * @param {FrameState} frameState The frame state. + * + * @private + */ + Cesium3DTileContent.prototype.update = function(tileset, frameState) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Returns true if this object was destroyed; otherwise, false. + *

+ * If this object was destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @returns {Boolean} true if this object was destroyed; otherwise, false. + * + * @see Cesium3DTileContent#destroy + * + * @private + */ + Cesium3DTileContent.prototype.isDestroyed = function() { + DeveloperError.throwInstantiationError(); + }; + + /** + * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic + * release of WebGL resources, instead of relying on the garbage collector to destroy this object. + *

+ * Once an object is destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. Therefore, + * assign the return value (undefined) to the object as done in the example. + *

+ * This is used to implement the Cesium3DTileContent interface, but is + * not part of the public Cesium API. + *

+ * + * @returns {undefined} + * + * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. + * + * @example + * content = content && content.destroy(); + * + * @see Cesium3DTileContent#isDestroyed + * + * @private + */ + Cesium3DTileContent.prototype.destroy = function() { + DeveloperError.throwInstantiationError(); + }; + + return Cesium3DTileContent; +}); diff --git a/Source/Scene/Cesium3DTileContentFactory.js b/Source/Scene/Cesium3DTileContentFactory.js new file mode 100644 index 000000000000..c90d50b4917b --- /dev/null +++ b/Source/Scene/Cesium3DTileContentFactory.js @@ -0,0 +1,41 @@ +/*global define*/ +define([ + './Batched3DModel3DTileContent', + './Composite3DTileContent', + './Instanced3DModel3DTileContent', + './PointCloud3DTileContent', + './Tileset3DTileContent' + ], function( + Batched3DModel3DTileContent, + Composite3DTileContent, + Instanced3DModel3DTileContent, + PointCloud3DTileContent, + Tileset3DTileContent) { + 'use strict'; + + /** + * Maps a tile's magic field in its header to a new content object for the tile's payload. + * + * @private + */ + var Cesium3DTileContentFactory = { + b3dm : function(tileset, tile, url, arrayBuffer, byteOffset) { + return new Batched3DModel3DTileContent(tileset, tile, url, arrayBuffer, byteOffset); + }, + pnts : function(tileset, tile, url, arrayBuffer, byteOffset) { + return new PointCloud3DTileContent(tileset, tile, url, arrayBuffer, byteOffset); + }, + i3dm : function(tileset, tile, url, arrayBuffer, byteOffset) { + return new Instanced3DModel3DTileContent(tileset, tile, url, arrayBuffer, byteOffset); + }, + cmpt : function(tileset, tile, url, arrayBuffer, byteOffset) { + // Send in the factory in order to avoid a cyclical dependency + return new Composite3DTileContent(tileset, tile, url, arrayBuffer, byteOffset, Cesium3DTileContentFactory); + }, + json : function(tileset, tile, url, arrayBuffer, byteOffset) { + return new Tileset3DTileContent(tileset, tile, url, arrayBuffer, byteOffset); + } + }; + + return Cesium3DTileContentFactory; +}); diff --git a/Source/Scene/Cesium3DTileContentState.js b/Source/Scene/Cesium3DTileContentState.js new file mode 100644 index 000000000000..98fdad9a96bf --- /dev/null +++ b/Source/Scene/Cesium3DTileContentState.js @@ -0,0 +1,21 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * @private + */ + var Cesium3DTileContentState = { + UNLOADED : 0, // Has never been requested + LOADING : 1, // Is waiting on a pending request + PROCESSING : 2, // Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data. + READY : 3, // Ready to render. + EXPIRED : 4, // Is expired and will be unloaded once new content is loaded. + FAILED : 5 // Request failed. + }; + + return freezeObject(Cesium3DTileContentState); +}); diff --git a/Source/Scene/Cesium3DTileFeature.js b/Source/Scene/Cesium3DTileFeature.js new file mode 100644 index 000000000000..5e03d3fc4cf8 --- /dev/null +++ b/Source/Scene/Cesium3DTileFeature.js @@ -0,0 +1,270 @@ +/*global define*/ +define([ + '../Core/Color', + '../Core/defined', + '../Core/defineProperties' + ], function( + Color, + defined, + defineProperties) { + 'use strict'; + + /** + * A feature of a {@link Cesium3DTileset}. + *

+ * Provides access to a feature's properties stored in the tile's batch table, as well + * as the ability to show/hide a feature and change its highlight color via + * {@link Cesium3DTileFeature#show} and {@link Cesium3DTileFeature#color}, respectively. + *

+ *

+ * Modifications to a Cesium3DTileFeature object have the lifetime of the tile's + * content. If the tile's content is unloaded, e.g., due to it going out of view and needing + * to free space in the cache for visible tiles, listen to the {@link Cesium3DTileset#tileUnload} event to save any + * modifications. Also listen to the {@link Cesium3DTileset#tileVisible} event to reapply any modifications. + *

+ *

+ * Do not construct this directly. Access it through {@link Cesium3DTileContent#getFeature} + * or picking using {@link Scene#pick} and {@link Scene#pickPosition}. + *

+ * + * @alias Cesium3DTileFeature + * @constructor + * + * @example + * // On mouse over, display all the properties for a feature in the console log. + * handler.setInputAction(function(movement) { + * var feature = scene.pick(movement.endPosition); + * if (feature instanceof Cesium.Cesium3DTileFeature) { + * var propertyNames = feature.getPropertyNames(); + * var length = propertyNames.length; + * for (var i = 0; i < length; ++i) { + * var propertyName = propertyNames[i]; + * console.log(propertyName + ': ' + feature.getProperty(propertyName)); + * } + * } + * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + */ + function Cesium3DTileFeature(tileset, content, batchId) { + this._content = content; + this._batchId = batchId; + this._color = undefined; // for calling getColor + } + + defineProperties(Cesium3DTileFeature.prototype, { + /** + * Gets or sets if the feature will be shown. This is set for all features + * when a style's show is evaluated. + * + * @memberof Cesium3DTileFeature.prototype + * + * @type {Boolean} + * + * @default true + */ + show : { + get : function() { + return this._content.batchTable.getShow(this._batchId); + }, + set : function(value) { + this._content.batchTable.setShow(this._batchId, value); + } + }, + + /** + * Gets or sets the highlight color multiplied with the feature's color. When + * this is white, the feature's color is not changed. This is set for all features + * when a style's color is evaluated. + * + * @memberof Cesium3DTileFeature.prototype + * + * @type {Color} + * + * @default {@link Color.WHITE} + */ + color : { + get : function() { + if (!defined(this._color)) { + this._color = new Color(); + } + return this._content.batchTable.getColor(this._batchId, this._color); + }, + set : function(value) { + this._content.batchTable.setColor(this._batchId, value); + } + }, + + /** + * Gets the content of the tile containing the feature. + * + * @memberof Cesium3DTileFeature.prototype + * + * @type {Cesium3DTileContent} + * + * @readonly + * @private + */ + content : { + get : function() { + return this._content; + } + }, + + /** + * Gets the tileset containing the feature. + * + * @memberof Cesium3DTileFeature.prototype + * + * @type {Cesium3DTileset} + * + * @readonly + */ + tileset : { + get : function() { + return this._content.tileset; + } + }, + + /** + * All objects returned by {@link Scene#pick} have a primitive property. This returns + * the tileset containing the feature. + * + * @memberof Cesium3DTileFeature.prototype + * + * @type {Cesium3DTileset} + * + * @readonly + */ + primitive : { + get : function() { + return this._content.tileset; + } + } + }); + + /** + * Returns whether the feature contains this property. This includes properties from this feature's + * class and inherited classes when using a batch table hierarchy. + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/TileFormats/BatchTable#batch-table-hierarchy} + * + * @param {String} name The case-sensitive name of the property. + * @returns {Boolean} Whether the feature contains this property. + */ + Cesium3DTileFeature.prototype.hasProperty = function(name) { + return this._content.batchTable.hasProperty(this._batchId, name); + }; + + /** + * Returns an array of property names for the feature. This includes properties from this feature's + * class and inherited classes when using a batch table hierarchy. + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/TileFormats/BatchTable#batch-table-hierarchy} + * + * @param {String[]} results An array into which to store the results. + * @returns {String[]} The names of the feature's properties. + */ + Cesium3DTileFeature.prototype.getPropertyNames = function(results) { + return this._content.batchTable.getPropertyNames(this._batchId, results); + }; + + /** + * Returns a copy of the value of the feature's property with the given name. This includes properties from this feature's + * class and inherited classes when using a batch table hierarchy. + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/TileFormats/BatchTable#batch-table-hierarchy} + * + * @param {String} name The case-sensitive name of the property. + * @returns {*} The value of the property or undefined if the property does not exist. + * + * @example + * // Display all the properties for a feature in the console log. + * var propertyNames = feature.getPropertyNames(); + * var length = propertyNames.length; + * for (var i = 0; i < length; ++i) { + * var propertyName = propertyNames[i]; + * console.log(propertyName + ': ' + feature.getProperty(propertyName)); + * } + */ + Cesium3DTileFeature.prototype.getProperty = function(name) { + return this._content.batchTable.getProperty(this._batchId, name); + }; + + /** + * Sets the value of the feature's property with the given name. + *

+ * If a property with the given name doesn't exist, it is created. + *

+ * + * @param {String} name The case-sensitive name of the property. + * @param {*} value The value of the property that will be copied. + * + * @exception {DeveloperError} Inherited batch table hierarchy property is read only. + * + * @example + * var height = feature.getProperty('Height'); // e.g., the height of a building + * + * @example + * var name = 'clicked'; + * if (feature.getProperty(name)) { + * console.log('already clicked'); + * } else { + * feature.setProperty(name, true); + * console.log('first click'); + * } + */ + Cesium3DTileFeature.prototype.setProperty = function(name, value) { + this._content.batchTable.setProperty(this._batchId, name, value); + + // PERFORMANCE_IDEA: Probably overkill, but maybe only mark the tile dirty if the + // property is in one of the style's expressions or - if it can be done quickly - + // if the new property value changed the result of an expression. + this._content.featurePropertiesDirty = true; + }; + + /** + * Returns whether the feature's class name equals className. Unlike {@link Cesium3DTileFeature#isClass} + * this function only checks the feature's exact class and not inherited classes. + *

+ * This function returns false if no batch table hierarchy is present. + *

+ * + * @param {String} className The name to check against. + * @returns {Boolean} Whether the feature's class name equals className + * + * @private + */ + Cesium3DTileFeature.prototype.isExactClass = function(className) { + return this._content.batchTable.isExactClass(this._batchId, className); + }; + + /** + * Returns whether the feature's class or any inherited classes are named className. + *

+ * This function returns false if no batch table hierarchy is present. + *

+ * + * @param {String} className The name to check against. + * @returns {Boolean} Whether the feature's class or inherited classes are named className + * + * @private + */ + Cesium3DTileFeature.prototype.isClass = function(className) { + return this._content.batchTable.isClass(this._batchId, className); + }; + + /** + * Returns the feature's class name. + *

+ * This function returns undefined if no batch table hierarchy is present. + *

+ * + * @returns {String} The feature's class name. + * + * @private + */ + Cesium3DTileFeature.prototype.getExactClassName = function() { + return this._content.batchTable.getExactClassName(this._batchId); + }; + + return Cesium3DTileFeature; +}); diff --git a/Source/Scene/Cesium3DTileFeatureTable.js b/Source/Scene/Cesium3DTileFeatureTable.js new file mode 100644 index 000000000000..ff6283cd18e7 --- /dev/null +++ b/Source/Scene/Cesium3DTileFeatureTable.js @@ -0,0 +1,93 @@ +/*global define*/ +define([ + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined' + ], function( + ComponentDatatype, + defaultValue, + defined) { + 'use strict'; + + /** + * @private + */ + function Cesium3DTileFeatureTable(featureTableJson, featureTableBinary) { + this.json = featureTableJson; + this.buffer = featureTableBinary; + this._cachedTypedArrays = {}; + this.featuresLength = 0; + } + + function getTypedArrayFromBinary(featureTable, semantic, componentType, componentLength, count, byteOffset) { + var cachedTypedArrays = featureTable._cachedTypedArrays; + var typedArray = cachedTypedArrays[semantic]; + if (!defined(typedArray)) { + typedArray = ComponentDatatype.createArrayBufferView(componentType, featureTable.buffer.buffer, featureTable.buffer.byteOffset + byteOffset, count * componentLength); + cachedTypedArrays[semantic] = typedArray; + } + return typedArray; + } + + function getTypedArrayFromArray(featureTable, semantic, componentType, array) { + var cachedTypedArrays = featureTable._cachedTypedArrays; + var typedArray = cachedTypedArrays[semantic]; + if (!defined(typedArray)) { + typedArray = ComponentDatatype.createTypedArray(componentType, array); + cachedTypedArrays[semantic] = typedArray; + } + return typedArray; + } + + Cesium3DTileFeatureTable.prototype.getGlobalProperty = function(semantic, componentType, componentLength) { + var jsonValue = this.json[semantic]; + if (!defined(jsonValue)) { + return undefined; + } + + if (defined(jsonValue.byteOffset)) { + componentType = defaultValue(componentType, ComponentDatatype.UNSIGNED_INT); + componentLength = defaultValue(componentLength, 1); + return getTypedArrayFromBinary(this, semantic, componentType, componentLength, 1, jsonValue.byteOffset); + } + + return jsonValue; + }; + + Cesium3DTileFeatureTable.prototype.getPropertyArray = function(semantic, componentType, componentLength) { + var jsonValue = this.json[semantic]; + if (!defined(jsonValue)) { + return undefined; + } + + if (defined(jsonValue.byteOffset)) { + if (defined(jsonValue.componentType)) { + componentType = ComponentDatatype.fromName(jsonValue.componentType); + } + return getTypedArrayFromBinary(this, semantic, componentType, componentLength, this.featuresLength, jsonValue.byteOffset); + } + + return getTypedArrayFromArray(this, semantic, componentType, jsonValue); + }; + + Cesium3DTileFeatureTable.prototype.getProperty = function(semantic, componentType, componentLength, featureId, result) { + var jsonValue = this.json[semantic]; + if (!defined(jsonValue)) { + return undefined; + } + + var typedArray = this.getPropertyArray(semantic, componentType, componentLength); + + if (componentLength === 1) { + return typedArray[featureId]; + } + + for (var i = 0; i < componentLength; ++i) { + result[i] = typedArray[componentLength * featureId + i]; + } + + return result; + }; + + return Cesium3DTileFeatureTable; +}); diff --git a/Source/Scene/Cesium3DTileOptimizationHint.js b/Source/Scene/Cesium3DTileOptimizationHint.js new file mode 100644 index 000000000000..b76479cbc6f3 --- /dev/null +++ b/Source/Scene/Cesium3DTileOptimizationHint.js @@ -0,0 +1,22 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * Hint defining optimization support for a 3D tile + * + * @exports Cesium3DTileOptimizationHint + * + * @private + */ + 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..60b040574792 --- /dev/null +++ b/Source/Scene/Cesium3DTileOptimizations.js @@ -0,0 +1,106 @@ +/*global define*/ +define([ + '../Core/Cartesian3', + '../Core/Check', + './Cesium3DTileOptimizationHint', + './TileBoundingRegion', + './TileOrientedBoundingBox' + ], function( + Cartesian3, + Check, + Cesium3DTileOptimizationHint, + TileBoundingRegion, + TileOrientedBoundingBox) { + 'use strict'; + + /** + * Utility functions for computing optimization hints for a {@link Cesium3DTileset}. + * + * @exports Cesium3DTileOptimizations + * + * @private + */ + var Cesium3DTileOptimizations = {}; + + var scratchAxis = new Cartesian3(); + + /** + * 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. + * @returns {Boolean} Whether the childrenWithinParent optimization is supported. + */ + Cesium3DTileOptimizations.checkChildrenWithinParent = function(tile) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('tile', tile); + //>>includeEnd('debug'); + + 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)) { + // Do not support if the parent and child both do not have oriented bounding boxes. + tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint.SKIP_OPTIMIZATION; + break; + } + + 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; + } + } + } + + return tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint.USE_OPTIMIZATION; + }; + + return Cesium3DTileOptimizations; +}); diff --git a/Source/Scene/Cesium3DTileRefine.js b/Source/Scene/Cesium3DTileRefine.js new file mode 100644 index 000000000000..8b3773ba8a4f --- /dev/null +++ b/Source/Scene/Cesium3DTileRefine.js @@ -0,0 +1,38 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * The refinement approach for a tile. + *

+ * See the {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/schema/tile.schema.json|tile schema} + * in the 3D Tiles spec. + *

+ * + * @exports Cesium3DTileRefine + * + * @private + */ + var Cesium3DTileRefine = { + /** + * Render this tile and, if it doesn't meet the screen space error, also refine to its children. + * + * @type {Number} + * @constant + */ + ADD : 0, + + /** + * Render this tile or, if it doesn't meet the screen space error, refine to its descendants instead. + * + * @type {Number} + * @constant + */ + REPLACE : 1 + }; + + return freezeObject(Cesium3DTileRefine); +}); diff --git a/Source/Scene/Cesium3DTileStyle.js b/Source/Scene/Cesium3DTileStyle.js new file mode 100644 index 000000000000..2b04c683e432 --- /dev/null +++ b/Source/Scene/Cesium3DTileStyle.js @@ -0,0 +1,442 @@ +/*global define*/ +define([ + '../Core/clone', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/loadJson', + '../Core/RequestScheduler', + '../ThirdParty/when', + './ConditionsExpression', + './Expression' + ], function( + clone, + defaultValue, + defined, + defineProperties, + DeveloperError, + loadJson, + RequestScheduler, + when, + ConditionsExpression, + Expression) { + 'use strict'; + + var DEFAULT_JSON_COLOR_EXPRESSION = 'color("#ffffff")'; + var DEFAULT_JSON_BOOLEAN_EXPRESSION = true; + var DEFAULT_JSON_NUMBER_EXPRESSION = 1.0; + + /** + * A style that is applied to a {@link Cesium3DTileset}. + *

+ * Evaluates an expression defined using the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}. + *

+ * + * @alias Cesium3DTileStyle + * @constructor + * + * @param {String|Object} [style] The url of a style or an object defining a style. + * + * @example + * tileset.style = new Cesium.Cesium3DTileStyle({ + * color : { + * conditions : [ + * ['${Height} >= 100', 'color("purple", 0.5)'], + * ['${Height} >= 50', 'color("red")'], + * ['true', 'color("blue")'] + * ] + * }, + * show : '${Height} > 0', + * meta : { + * description : '"Building id ${id} has height ${Height}."' + * } + * }); + * + * @example + * tileset.style = new Cesium.Cesium3DTileStyle({ + * color : 'vec4(${Temperature})', + * pointSize : '${Temperature} * 2.0' + * }); + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language} + */ + function Cesium3DTileStyle(style) { + this._style = undefined; + this._ready = false; + this._color = undefined; + this._show = undefined; + this._pointSize = undefined; + this._meta = undefined; + + this._colorShaderFunction = undefined; + this._showShaderFunction = undefined; + this._pointSizeShaderFunction = undefined; + this._colorShaderFunctionReady = false; + this._showShaderFunctionReady = false; + this._pointSizeShaderFunctionReady = false; + + var promise; + if (typeof style === 'string') { + promise = loadJson(style); + } else { + promise = when.resolve(style); + } + + var that = this; + this._readyPromise = promise.then(function(styleJson) { + setup(that, styleJson); + return that; + }); + } + + function setup(that, styleJson) { + that._style = clone(styleJson, true); + + styleJson = defaultValue(styleJson, defaultValue.EMPTY_OBJECT); + + that._colorShaderFunctionReady = !defined(styleJson.color); + that._showShaderFunctionReady = !defined(styleJson.show); + that._pointSizeShaderFunctionReady = !defined(styleJson.pointSize); + + var colorExpression = defaultValue(styleJson.color, DEFAULT_JSON_COLOR_EXPRESSION); + var showExpression = defaultValue(styleJson.show, DEFAULT_JSON_BOOLEAN_EXPRESSION); + var pointSizeExpression = defaultValue(styleJson.pointSize, DEFAULT_JSON_NUMBER_EXPRESSION); + + var defines = styleJson.defines; + + var color; + if (typeof colorExpression === 'string') { + color = new Expression(colorExpression, defines); + } else if (defined(colorExpression.conditions)) { + color = new ConditionsExpression(colorExpression, defines); + } + + that._color = color; + + var show; + if (typeof showExpression === 'boolean') { + show = new Expression(String(showExpression), defines); + } else if (typeof showExpression === 'string') { + show = new Expression(showExpression, defines); + } else if (defined(showExpression.conditions)) { + show = new ConditionsExpression(showExpression, defines); + } + + that._show = show; + + var pointSize; + if (typeof pointSizeExpression === 'number') { + pointSize = new Expression(String(pointSizeExpression), defines); + } else if (typeof pointSizeExpression === 'string') { + pointSize = new Expression(pointSizeExpression, defines); + } else if (defined(pointSizeExpression.conditions)) { + pointSize = new ConditionsExpression(pointSizeExpression, defines); + } + + that._pointSize = pointSize; + + var meta = {}; + if (defined(styleJson.meta)) { + var metaJson = defaultValue(styleJson.meta, defaultValue.EMPTY_OBJECT); + for (var property in metaJson) { + if (metaJson.hasOwnProperty(property)) { + meta[property] = new Expression(metaJson[property], defines); + } + } + } + + that._meta = meta; + + that._ready = true; + } + + defineProperties(Cesium3DTileStyle.prototype, { + /** + * Gets the object defining the style using the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}. + * + * @memberof Cesium3DTileStyle.prototype + * + * @type {Object} + * @readonly + * + * @default undefined + * + * @exception {DeveloperError} The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true. + */ + style : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._style; + } + }, + + /** + * When true, the style is ready and its expressions can be evaluated. When + * a style is constructed with an object, as opposed to a url, this is true immediately. + * + * @memberof Cesium3DTileStyle.prototype + * + * @type {Boolean} + * @readonly + * + * @default false + */ + ready : { + get : function() { + return this._ready; + } + }, + + /** + * Gets the promise that will be resolved when the the style is ready and its expressions can be evaluated. + * + * @memberof Cesium3DTileStyle.prototype + * + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + return this._readyPromise; + } + }, + + /** + * Gets or sets the {@link StyleExpression} object used to evaluate the style's show property. + *

+ * The expression must return or convert to a Boolean. + *

+ * + * @memberof Cesium3DTileStyle.prototype + * + * @type {StyleExpression} + * + * @exception {DeveloperError} The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true. + * + * @example + * var style = new Cesium3DTileStyle({ + * show : '(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)' + * }); + * style.show.evaluate(frameState, feature); // returns true or false depending on the feature's properties + * + * @example + * var style = new Cesium.Cesium3DTileStyle(); + * // Override show expression with a custom function + * style.show = { + * evaluate : function(frameState, feature) { + * return true; + * } + * }; + */ + show : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._show; + }, + set : function(value) { + this._showShaderFunctionReady = false; + this._show = value; + } + }, + + /** + * Gets or sets the {@link StyleExpression} object used to evaluate the style's color property. + *

+ * The expression must return a Color. + *

+ * + * @memberof Cesium3DTileStyle.prototype + * + * @type {StyleExpression} + * + * @exception {DeveloperError} The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true. + * + * @example + * var style = new Cesium3DTileStyle({ + * color : '(${Temperature} > 90) ? color("red") : color("white")' + * }); + * style.color.evaluateColor(frameState, feature, result); // returns a Cesium.Color object + * + * @example + * var style = new Cesium.Cesium3DTileStyle(); + * // Override color expression with a custom function + * style.color = { + * evaluateColor : function(frameState, feature, result) { + * return Cesium.Color.clone(Cesium.Color.WHITE, result); + * } + * }; + */ + color : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._color; + }, + set : function(value) { + this._colorShaderFunctionReady = false; + this._color = value; + } + }, + + /** + * Gets or sets the {@link StyleExpression} object used to evaluate the style's pointSize property. + *

+ * The expression must return or convert to a Number. + *

+ * + * @memberof Cesium3DTileStyle.prototype + * + * @type {StyleExpression} + * + * @exception {DeveloperError} The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true. + * + * @example + * var style = new Cesium3DTileStyle({ + * pointSize : '(${Temperature} > 90) ? 2.0 : 1.0' + * }); + * style.pointSize.evaluate(frameState, feature); // returns a Number + * + * @example + * var style = new Cesium.Cesium3DTileStyle(); + * // Override pointSize expression with a custom function + * style.pointSize = { + * evaluate : function(frameState, feature) { + * return 1.0; + * } + * }; + */ + pointSize : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._pointSize; + }, + set : function(value) { + this._pointSizeShaderFunctionReady = false; + this._pointSize = value; + } + }, + + /** + * Gets or sets the object containing application-specific expression that can be explicitly + * evaluated, e.g., for display in a UI. + * + * @memberof Cesium3DTileStyle.prototype + * + * @type {StyleExpression} + * + * @exception {DeveloperError} The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true. + * + * @example + * var style = new Cesium3DTileStyle({ + * meta : { + * description : '"Building id ${id} has height ${Height}."' + * } + * }); + * style.meta.description.evaluate(frameState, feature); // returns a String with the substituted variables + */ + meta : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('The style is not loaded. Use Cesium3DTileStyle.readyPromise or wait for Cesium3DTileStyle.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._meta; + }, + set : function(value) { + this._meta = value; + } + } + }); + + /** + * Gets the color shader function for this style. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * + * @returns {String} The shader function. + * + * @private + */ + Cesium3DTileStyle.prototype.getColorShaderFunction = function(functionName, attributePrefix, shaderState) { + if (this._colorShaderFunctionReady) { + // Return the cached result, may be undefined + return this._colorShaderFunction; + } + + this._colorShaderFunctionReady = true; + this._colorShaderFunction = this.color.getShaderFunction(functionName, attributePrefix, shaderState, 'vec4'); + return this._colorShaderFunction; + }; + + /** + * Gets the show shader function for this style. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * + * @returns {String} The shader function. + * + * @private + */ + Cesium3DTileStyle.prototype.getShowShaderFunction = function(functionName, attributePrefix, shaderState) { + if (this._showShaderFunctionReady) { + // Return the cached result, may be undefined + return this._showShaderFunction; + } + + this._showShaderFunctionReady = true; + this._showShaderFunction = this.show.getShaderFunction(functionName, attributePrefix, shaderState, 'bool'); + return this._showShaderFunction; + }; + + /** + * Gets the pointSize shader function for this style. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * + * @returns {String} The shader function. + * + * @private + */ + Cesium3DTileStyle.prototype.getPointSizeShaderFunction = function(functionName, attributePrefix, shaderState) { + if (this._pointSizeShaderFunctionReady) { + // Return the cached result, may be undefined + return this._pointSizeShaderFunction; + } + + this._pointSizeShaderFunctionReady = true; + this._pointSizeShaderFunction = this.pointSize.getShaderFunction(functionName, attributePrefix, shaderState, 'float'); + return this._pointSizeShaderFunction; + }; + + return Cesium3DTileStyle; +}); diff --git a/Source/Scene/Cesium3DTileStyleEngine.js b/Source/Scene/Cesium3DTileStyleEngine.js new file mode 100644 index 000000000000..a74bb5e536dc --- /dev/null +++ b/Source/Scene/Cesium3DTileStyleEngine.js @@ -0,0 +1,83 @@ +/*global define*/ +define([ + '../Core/defined', + '../Core/defineProperties' + ], function( + defined, + defineProperties) { + 'use strict'; + + /** + * @private + */ + function Cesium3DTileStyleEngine() { + this._style = undefined; // The style provided by the user + this._styleDirty = false; // true when the style is reassigned + this._lastStyleTime = 0; // The "time" when the last style was assigned + } + + defineProperties(Cesium3DTileStyleEngine.prototype, { + style : { + get : function() { + return this._style; + }, + set : function(value) { + this._style = value; + this._styleDirty = true; + } + } + }); + + Cesium3DTileStyleEngine.prototype.makeDirty = function() { + this._styleDirty = true; + }; + + Cesium3DTileStyleEngine.prototype.applyStyle = function(tileset, frameState) { + if (!tileset.ready) { + return; + } + + if (defined(this._style) && !this._style.ready) { + return; + } + + var styleDirty = this._styleDirty; + + if (frameState.passes.render) { + // Don't reset until the color pass, e.g., for mouse-over picking + this._styleDirty = false; + } + + if (styleDirty) { + // Increase "time", so the style is applied to all visible tiles + ++this._lastStyleTime; + } + + var lastStyleTime = this._lastStyleTime; + var statistics = tileset._statistics; + + // If a new style was assigned, loop through all the visible tiles; otherwise, loop through + // only the tiles that are newly visible, i.e., they are visible this frame, but were not + // visible last frame. In many cases, the newly selected tiles list will be short or empty. + var tiles = styleDirty ? tileset._selectedTiles : tileset._selectedTilesToStyle; + // PERFORMANCE_IDEA: does mouse-over picking basically trash this? We need to style on + // pick, for example, because a feature's show may be false. + + var length = tiles.length; + for (var i = 0; i < length; ++i) { + var tile = tiles[i]; + if (tile.selected && (tile.lastStyleTime !== lastStyleTime)) { + // Apply the style to this tile if it wasn't already applied because: + // 1) the user assigned a new style to the tileset + // 2) this tile is now visible, but it wasn't visible when the style was first assigned + var content = tile.content; + tile.lastStyleTime = lastStyleTime; + content.applyStyle(frameState, this._style); + statistics.numberOfFeaturesStyled += content.featuresLength; + ++statistics.numberOfTilesStyled; + } + } + }; + + return Cesium3DTileStyleEngine; +}); diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js new file mode 100644 index 000000000000..26ab3dbb49dc --- /dev/null +++ b/Source/Scene/Cesium3DTileset.js @@ -0,0 +1,1800 @@ +/*global define*/ +define([ + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartographic', + '../Core/Check', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/DoublyLinkedList', + '../Core/Event', + '../Core/getBaseUri', + '../Core/getExtensionFromUri', + '../Core/Heap', + '../Core/isDataUri', + '../Core/joinUrls', + '../Core/JulianDate', + '../Core/loadJson', + '../Core/ManagedArray', + '../Core/Math', + '../Core/Matrix4', + '../Core/RuntimeError', + '../Renderer/ClearCommand', + '../Renderer/Pass', + '../ThirdParty/when', + './Axis', + './Cesium3DTile', + './Cesium3DTileColorBlendMode', + './Cesium3DTileOptimizations', + './Cesium3DTileRefine', + './Cesium3DTilesetStatistics', + './Cesium3DTilesetTraversal', + './Cesium3DTileStyleEngine', + './LabelCollection', + './SceneMode', + './ShadowMode', + './TileBoundingRegion', + './TileBoundingSphere', + './TileOrientedBoundingBox' + ], function( + Cartesian2, + Cartesian3, + Cartographic, + Check, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + DoublyLinkedList, + Event, + getBaseUri, + getExtensionFromUri, + Heap, + isDataUri, + joinUrls, + JulianDate, + loadJson, + ManagedArray, + CesiumMath, + Matrix4, + RuntimeError, + ClearCommand, + Pass, + when, + Axis, + Cesium3DTile, + Cesium3DTileColorBlendMode, + Cesium3DTileOptimizations, + Cesium3DTileRefine, + Cesium3DTilesetStatistics, + Cesium3DTilesetTraversal, + Cesium3DTileStyleEngine, + LabelCollection, + SceneMode, + ShadowMode, + TileBoundingRegion, + TileBoundingSphere, + TileOrientedBoundingBox) { + 'use strict'; + + /** + * A {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles tileset}, + * used for streaming massive heterogeneous 3D geospatial datasets. + * + * @alias Cesium3DTileset + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {String} options.url The url to a tileset.json file or to a directory containing a tileset.json file. + * @param {Boolean} [options.show=true] Determines if the tileset will be shown. + * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the tileset's root tile. + * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source. + * @param {Number} [options.maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. + * @param {Number} [options.maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. + * @param {Boolean} [options.cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. + * @param {Boolean} [options.dynamicScreenSpaceError=false] Optimization option. 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. + * @param {Number} [options.dynamicScreenSpaceErrorHeightFalloff=0.25] A ratio of the tileset's height at which the density starts to falloff. + * @param {Boolean} [options.skipLevelOfDetail=true] Optimization option. Determines if level of detail skipping should be applied during the traversal. + * @param {Number} [options.baseScreenSpaceError=1024] When skipLevelOfDetail is true, the screen space error that must be reached before skipping levels of detail. + * @param {Number} [options.skipScreenSpaceErrorFactor=16] When skipLevelOfDetail is true, a multiplier defining the minimum screen space error to skip. Used in conjunction with skipLevels to determine which tiles to load. + * @param {Number} [options.skipLevels=1] When skipLevelOfDetail is true, a constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjunction with skipScreenSpaceErrorFactor to determine which tiles to load. + * @param {Boolean} [options.immediatelyLoadDesiredLevelOfDetail=false] When skipLevelOfDetail is true, only tiles that meet the maximum screen space error will ever be downloaded. Skipping factors are ignored and just the desired tiles are loaded. + * @param {Boolean} [options.loadSiblings=false] When skipLevelOfDetail is true, determines whether siblings of visible tiles are always downloaded during traversal. + * @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering. + * @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile. + * @param {Boolean} [options.debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe. + * @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 {Boolean} [options.debugShowRenderingStatistics=false] For debugging only. When true, draws labels to indicate the number of commands, points, triangles and features for each tile. + * @param {Boolean} [options.debugShowMemoryUsage=false] For debugging only. When true, draws labels to indicate the texture and geometry memory in megabytes used by each tile. + * + * @exception {DeveloperError} The tileset must be 3D Tiles version 0.0 or 1.0. See {@link https://github.com/AnalyticalGraphicsInc/3d-tiles#spec-status} + * + * @example + * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ + * url : 'http://localhost:8002/tilesets/Seattle' + * })); + * + * @example + * // Common setting for the skipLevelOfDetail optimization + * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ + * url : 'http://localhost:8002/tilesets/Seattle', + * skipLevelOfDetail : true, + * baseScreenSpaceError : 1024, + * skipScreenSpaceErrorFactor : 16, + * skipLevels : 1, + * immediatelyLoadDesiredLevelOfDetail : false, + * loadSiblings : false, + * cullWithChildrenBounds : true + * })); + * + * @example + * // Common settings for the dynamicScreenSpaceError optimization + * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ + * url : 'http://localhost:8002/tilesets/Seattle', + * dynamicScreenSpaceError : true, + * dynamicScreenSpaceErrorDensity : 0.00278, + * dynamicScreenSpaceErrorFactor : 4.0, + * dynamicScreenSpaceErrorHeightFalloff : 0.25 + * })); + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles specification} + */ + function Cesium3DTileset(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var url = options.url; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('options.url', url); + //>>includeEnd('debug'); + + var tilesetUrl; + var basePath; + + if (getExtensionFromUri(url) === 'json') { + tilesetUrl = url; + basePath = getBaseUri(url, true); + } else if (isDataUri(url)) { + tilesetUrl = url; + basePath = ''; + } else { + basePath = url; + tilesetUrl = joinUrls(basePath, 'tileset.json'); + } + + this._url = url; + this._basePath = basePath; + this._tilesetUrl = tilesetUrl; + this._root = undefined; + this._asset = undefined; // Metadata for the entire tileset + this._properties = undefined; // Metadata for per-model/point/etc properties + this._geometricError = undefined; // Geometric error when the tree is not rendered at all + this._gltfUpAxis = undefined; + this._processingQueue = []; + this._selectedTiles = []; + this._requestedTiles = []; + this._desiredTiles = new ManagedArray(); + this._selectedTilesToStyle = []; + this._loadTimestamp = undefined; + this._timeSinceLoad = 0.0; + + var replacementList = new DoublyLinkedList(); + + // [head, sentinel) -> tiles that weren't selected this frame and may be replaced + // (sentinel, tail] -> tiles that were selected this frame + this._replacementList = replacementList; // Tiles with content loaded. For cache management. + this._replacementSentinel = replacementList.add(); + this._trimTiles = false; + + this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true); + + this._hasMixedContent = false; + + this._baseTraversal = new Cesium3DTilesetTraversal.BaseTraversal(); + this._skipTraversal = new Cesium3DTilesetTraversal.SkipTraversal({ + selectionHeuristic : selectionHeuristic + }); + + this._backfaceCommands = new ManagedArray(); + + this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16); + this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512); + + this._styleEngine = new Cesium3DTileStyleEngine(); + + this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY); + + this._statistics = new Cesium3DTilesetStatistics(); + this._statisticsLastColor = new Cesium3DTilesetStatistics(); + this._statisticsLastPick = new Cesium3DTilesetStatistics(); + + this._tilesLoaded = false; + + this._tileDebugLabels = undefined; + + this._readyPromise = when.defer(); + + /** + * Optimization option. Whether the tileset 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 + * tiles and making less requests, but may result in a slight drop in visual quality for tiles in the distance. + * The algorithm is biased towards "street views" where the camera is close to the ground plane of the tileset and looking + * at the horizon. In addition results are more accurate for tightly fitting bounding volumes like box and region. + * + * @type {Boolean} + * @default false + */ + this.dynamicScreenSpaceError = defaultValue(options.dynamicScreenSpaceError, false); + + /** + * A scalar that determines the density used to adjust the dynamic screen space error, similar to {@link Fog}. Increasing this + * value has the effect of increasing the maximum screen space error for all tiles, but in a non-linear fashion. + * The error starts at 0.0 and increases exponentially until a midpoint is reached, and then approaches 1.0 asymptotically. + * This has the effect of keeping high detail in the closer tiles and lower detail in the further tiles, with all tiles + * beyond a certain distance all roughly having an error of 1.0. + *

+ * The dynamic error is in the range [0.0, 1.0) and is multiplied by dynamicScreenSpaceErrorFactor to produce the + * final dynamic error. This dynamic error is then subtracted from the tile's actual screen space error. + *

+ *

+ * Increasing dynamicScreenSpaceErrorDensity has the effect of moving the error midpoint closer to the camera. + * It is analogous to moving fog closer to the camera. + *

+ * + * @type {Number} + * @default 0.00278 + */ + this.dynamicScreenSpaceErrorDensity = 0.00278; + + /** + * A factor used to increase the screen space error of tiles for dynamic screen space error. As this value increases less tiles + * are requested for rendering and tiles in the distance will have lower detail. If set to zero, the feature will be disabled. + * + * @type {Number} + * @default 4.0 + */ + this.dynamicScreenSpaceErrorFactor = 4.0; + + /** + * A ratio of the tileset's height at which the density starts to falloff. If the camera is below this height the + * full computed density is applied, otherwise the density falls off. This has the effect of higher density at + * street level views. + *

+ * Valid values are between 0.0 and 1.0. + *

+ * + * @type {Number} + * @default 0.25 + */ + this.dynamicScreenSpaceErrorHeightFalloff = 0.25; + + this._dynamicScreenSpaceErrorComputedDensity = 0.0; // Updated based on the camera position and direction + + /** + * Determines whether the tileset casts or receives shadows from each light source. + *

+ * Enabling shadows has a performance impact. A tileset that casts shadows must be rendered twice, once from the camera and again from the light's point of view. + *

+ *

+ * Shadows are rendered only when {@link Viewer#shadows} is true. + *

+ * + * @type {ShadowMode} + * @default ShadowMode.ENABLED + */ + this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED); + + /** + * Determines if the tileset will be shown. + * + * @type {Boolean} + * @default true + */ + this.show = defaultValue(options.show, true); + + /** + * Defines how per-feature colors set from the Cesium API or declarative styling blend with the source colors from + * the original feature, e.g. glTF material or per-point color in the tile. + * + * @type {Cesium3DTileColorBlendMode} + * @default Cesium3DTileColorBlendMode.HIGHLIGHT + */ + this.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT; + + /** + * Defines the value used to linearly interpolate between the source color and feature color when the {@link Cesium3DTileset#colorBlendMode} is MIX. + * A value of 0.0 results in the source color while a value of 1.0 results in the feature color, with any value in-between + * resulting in a mix of the source color and feature color. + * + * @type {Number} + * @default 0.5 + */ + this.colorBlendAmount = 0.5; + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * Determines if only the tiles from last frame should be used for rendering. This + * effectively "freezes" the tileset to the previous frame so it is possible to zoom + * out and see what was rendered. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugFreezeFrame = defaultValue(options.debugFreezeFrame, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, assigns a random color to each tile. This is useful for visualizing + * what models belong to what tiles, especially with additive refinement where models + * from parent tiles may be interleaved with models from child tiles. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugColorizeTiles = defaultValue(options.debugColorizeTiles, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders each tile's content as a wireframe. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugWireframe = defaultValue(options.debugWireframe, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders the bounding volume for each visible tile. The bounding volume is + * white if the tile's content has an explicit bounding volume; otherwise, it + * is red. Tiles that are not at final resolution are yellow. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders a blue bounding volume for each tile's content. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowContentBoundingVolume = defaultValue(options.debugShowContentBoundingVolume, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders the viewer request volume for each tile. + *

+ * + * @type {Boolean} + * @default false + */ + 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._tileDebugLabels = undefined; + this.debugPickedTileLabelOnly = false; + this.debugPickedTile = undefined; + this.debugPickPosition = undefined; + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, draws labels to indicate the number of commands, points, triangles and features of each tile. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowRenderingStatistics = defaultValue(options.debugShowRenderingStatistics, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, draws labels to indicate the geometry and texture memory usage of each tile. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowMemoryUsage = defaultValue(options.debugShowMemoryUsage, false); + + /** + * 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 + * processed and is ready to render. + *

+ * The number of pending tile requests, numberOfPendingRequests, and number of tiles + * processing, numberOfTilesProcessing are passed to the event listener. + *

+ *

+ * This event is fired at the end of the frame after the scene is rendered. + *

+ * + * @type {Event} + * @default new Event() + * + * @example + * tileset.loadProgress.addEventListener(function(numberOfPendingRequests, numberOfTilesProcessing) { + * if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) { + * console.log('Stopped loading'); + * return; + * } + * + * console.log('Loading: requests: ' + numberOfPendingRequests + ', processing: ' + numberOfTilesProcessing); + * }); + */ + this.loadProgress = new Event(); + + /** + * The event fired to indicate that all tiles that meet the screen space error this frame are loaded. The tileset + * is completely loaded for this view. + *

+ * This event is fired at the end of the frame after the scene is rendered. + *

+ * + * @type {Event} + * @default new Event() + * + * @example + * tileset.allTilesLoaded.addEventListener(function() { + * console.log('All tiles are loaded'); + * }); + * + * @see Cesium3DTileset#tilesLoaded + */ + this.allTilesLoaded = new Event(); + + /** + * The event fired to indicate that a tile's content was unloaded. + *

+ * The unloaded {@link Cesium3DTile} is passed to the event listener. + *

+ *

+ * This event is fired immediately before the tile's content is unloaded while the frame is being + * rendered so that the event listener has access to the tile's content. Do not create + * or modify Cesium entities or primitives during the event listener. + *

+ * + * @type {Event} + * @default new Event() + * + * @example + * tileset.tileUnload.addEventListener(function(tile) { + * console.log('A tile was unloaded from the cache.'); + * }); + * + * @see Cesium3DTileset#maximumMemoryUsage + * @see Cesium3DTileset#trimLoadedTiles + */ + this.tileUnload = new Event(); + + /** + * This event fires once for each visible tile in a frame. This can be used to manually + * style a tileset. + *

+ * The visible {@link Cesium3DTile} is passed to the event listener. + *

+ *

+ * This event is fired during the tileset traversal while the frame is being rendered + * so that updates to the tile take effect in the same frame. Do not create or modify + * Cesium entities or primitives during the event listener. + *

+ * + * @type {Event} + * @default new Event() + * + * @example + * tileset.tileVisible.addEventListener(function(tile) { + * if (tile.content instanceof Cesium.Batched3DModel3DTileContent) { + * console.log('A Batched 3D Model tile is visible.'); + * } + * }); + * + * @example + * // Apply a red style and then manually set random colors for every other feature when the tile becomes visible. + * tileset.style = new Cesium.Cesium3DTileStyle({ + * color : 'color("red")' + * }); + * tileset.tileVisible.addEventListener(function(tile) { + * var content = tile.content; + * var featuresLength = content.featuresLength; + * for (var i = 0; i < featuresLength; i+=2) { + * content.getFeature(i).color = Cesium.Color.fromRandom(); + * } + * }); + */ + this.tileVisible = new Event(); + + /** + * Optimization option. Determines if level of detail skipping should be applied during the traversal. + *

+ * The common strategy for replacement-refinement traversal is to store all levels of the tree in memory and require + * all children to be loaded before the parent can refine. With this optimization levels of the tree can be skipped + * entirely and children can be rendered alongside their parents. The tileset requires significantly less memory when + * using this optimization. + *

+ * + * @type {Boolean} + * @default true + */ + this.skipLevelOfDetail = defaultValue(options.skipLevelOfDetail, true); + + /** + * The screen space error that must be reached before skipping levels of detail. + *

+ * Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. + *

+ * + * @type {Number} + * @default 1024 + */ + this.baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 1024); + + /** + * Multiplier defining the minimum screen space error to skip. + * For example, if a tile has screen space error of 100, no tiles will be loaded unless they + * are leaves or have a screen space error <= 100 / skipScreenSpaceErrorFactor. + *

+ * Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. + *

+ * + * @type {Number} + * @default 16 + */ + this.skipScreenSpaceErrorFactor = defaultValue(options.skipScreenSpaceErrorFactor, 16); + + /** + * Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. + * For example, if a tile is level 1, no tiles will be loaded unless they are at level greater than 2. + *

+ * Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. + *

+ * + * @type {Number} + * @default 1 + */ + this.skipLevels = defaultValue(options.skipLevels, 1); + + /** + * When true, only tiles that meet the maximum screen space error will ever be downloaded. + * Skipping factors are ignored and just the desired tiles are loaded. + *

+ * Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. + *

+ * + * @type {Boolean} + * @default false + */ + this.immediatelyLoadDesiredLevelOfDetail = defaultValue(options.immediatelyLoadDesiredLevelOfDetail, false); + + /** + * Determines whether siblings of visible tiles are always downloaded during traversal. + * This may be useful for ensuring that tiles are already available when the viewer turns left/right. + *

+ * Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. + *

+ * + * @type {Boolean} + * @default false + */ + this.loadSiblings = defaultValue(options.loadSiblings, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * Determines if only the tiles from last frame should be used for rendering. This + * effectively "freezes" the tileset to the previous frame so it is possible to zoom + * out and see what was rendered. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugFreezeFrame = defaultValue(options.debugFreezeFrame, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, assigns a random color to each tile. This is useful for visualizing + * what features belong to what tiles, especially with additive refinement where features + * from parent tiles may be interleaved with features from child tiles. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugColorizeTiles = defaultValue(options.debugColorizeTiles, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders each tile's content as a wireframe. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugWireframe = defaultValue(options.debugWireframe, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders the bounding volume for each visible tile. The bounding volume is + * white if the tile has a content bounding volume; otherwise, it is red. Tiles that don't meet the + * screen space error and are still refining to their descendants are yellow. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders the bounding volume for each visible tile's content. The bounding volume is + * blue if the tile has a content bounding volume; otherwise it is red. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowContentBoundingVolume = defaultValue(options.debugShowContentBoundingVolume, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, renders the viewer request volume for each tile. + *

+ * + * @type {Boolean} + * @default false + */ + 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 property is for debugging only; it is not optimized for production use. + *

+ * When true, draws labels to indicate the number of commands, points, triangles and features of each tile. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowRenderingStatistics = defaultValue(options.debugShowRenderingStatistics, false); + + /** + * This property is for debugging only; it is not optimized for production use. + *

+ * When true, draws labels to indicate the geometry and texture memory usage of each tile. + *

+ * + * @type {Boolean} + * @default false + */ + this.debugShowMemoryUsage = defaultValue(options.debugShowMemoryUsage, false); + + var that = this; + + // We don't know the distance of the tileset until tileset.json is loaded, so use the default distance for now + loadJson(tilesetUrl).then(function(tilesetJson) { + that._root = that.loadTileset(tilesetUrl, tilesetJson); + var gltfUpAxis = defined(tilesetJson.asset.gltfUpAxis) ? Axis.fromName(tilesetJson.asset.gltfUpAxis) : Axis.Y; + that._asset = tilesetJson.asset; + that._properties = tilesetJson.properties; + that._geometricError = tilesetJson.geometricError; + that._gltfUpAxis = gltfUpAxis; + that._readyPromise.resolve(that); + }).otherwise(function(error) { + that._readyPromise.reject(error); + }); + } + + defineProperties(Cesium3DTileset.prototype, { + /** + * Gets the tileset's asset object property, which contains metadata about the tileset. + *

+ * See the {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/schema/asset.schema.json|asset schema} + * in the 3D Tiles spec for the full set of properties. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Object} + * @readonly + * + * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. + */ + asset : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this.ready) { + throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._asset; + } + }, + + /** + * Gets the tileset's properties dictionary object, which contains metadata about per-feature properties. + *

+ * See the {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/schema/properties.schema.json|properties schema} + * in the 3D Tiles spec for the full set of properties. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Object} + * @readonly + * + * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. + * + * @example + * console.log('Maximum building height: ' + tileset.properties.height.maximum); + * console.log('Minimum building height: ' + tileset.properties.height.minimum); + * + * @see Cesium3DTileFeature#getProperty + * @see Cesium3DTileFeature#setProperty + */ + properties : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this.ready) { + throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._properties; + } + }, + + /** + * When true, the tileset's root tile is loaded and the tileset is ready to render. + * This is set to true right before {@link Cesium3DTileset#readyPromise} is resolved. + * + * @memberof Cesium3DTileset.prototype + * + * @type {Boolean} + * @readonly + * + * @default false + */ + ready : { + get : function() { + return defined(this._root); + } + }, + + /** + * Gets the promise that will be resolved when the tileset's root tile is loaded and the tileset is ready to render. + *

+ * This promise is resolved at the end of the frame before the first frame the tileset is rendered in. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Promise.} + * @readonly + * + * @example + * tileset.readyPromise.then(function(tileset) { + * // tile.properties is not defined until readyPromise resolves. + * var properties = tileset.properties; + * if (Cesium.defined(properties)) { + * for (var name in properties) { + * console.log(properties[name]); + * } + * } + * }); + */ + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + }, + + /** + * When true, all tiles that meet the screen space error this frame are loaded. The tileset is + * completely loaded for this view. + * + * @memberof Cesium3DTileset.prototype + * + * @type {Boolean} + * @readonly + * + * @default false + * + * @see Cesium3DTileset#allTilesLoaded + */ + tilesLoaded : { + get : function() { + return this._tilesLoaded; + } + }, + + /** + * The url to a tileset.json file or to a directory containing a tileset.json file. + * + * @memberof Cesium3DTileset.prototype + * + * @type {String} + * @readonly + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * The base path that non-absolute paths in tileset.json are relative to. + * + * @memberof Cesium3DTileset.prototype + * + * @type {String} + * @readonly + */ + basePath : { + get : function() { + return this._basePath; + } + }, + + /** + * The style, defined using the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}, + * applied to each feature in the tileset. + *

+ * Assign undefined to remove the style, which will restore the visual + * appearance of the tileset to its default when no style was applied. + *

+ *

+ * The style is applied to a tile before the {@link Cesium3DTileset#tileVisible} + * event is raised, so code in tileVisible can manually set a feature's + * properties (e.g. color and show) after the style is applied. When + * a new style is assigned any manually set properties are overwritten. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Cesium3DTileStyle} + * + * @default undefined + * + * @example + * tileset.style = new Cesium.Cesium3DTileStyle({ + * color : { + * conditions : [ + * ['${Height} >= 100', 'color("purple", 0.5)'], + * ['${Height} >= 50', 'color("red")'], + * ['true', 'color("blue")'] + * ] + * }, + * show : '${Height} > 0', + * meta : { + * description : '"Building id ${id} has height ${Height}."' + * } + * }); + * + * @see {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language} + */ + style : { + get : function() { + return this._styleEngine.style; + }, + set : function(value) { + this._styleEngine.style = value; + } + }, + + /** + * The maximum screen space error used to drive level of detail refinement. This value helps determine when a tile + * refines to its descendants, and therefore plays a major role in balancing performance with visual quality. + *

+ * A tile's screen space error is roughly equivalent to the number of pixels wide that would be drawn if a sphere with a + * radius equal to the tile's geometric error were rendered at the tile's position. If this value exceeds + * maximumScreenSpaceError the tile refines to its descendants. + *

+ *

+ * Depending on the tileset, maximumScreenSpaceError may need to be tweaked to achieve the right balance. + * Higher values provide better performance but lower visual quality. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Number} + * @default 16 + * + * @exception {DeveloperError} maximumScreenSpaceError must be greater than or equal to zero. + */ + maximumScreenSpaceError : { + get : function() { + return this._maximumScreenSpaceError; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('maximumScreenSpaceError', value, 0); + //>>includeEnd('debug'); + + this._maximumScreenSpaceError = value; + } + }, + + /** + * The maximum amount of GPU memory (in MB) that may be used to cache tiles. This value is estimated from + * geometry, textures, and batch table textures of loaded tiles. For point clouds, this value also + * includes per-point metadata. + *

+ * Tiles not in view are unloaded to enforce this. + *

+ *

+ * If decreasing this value results in unloading tiles, the tiles are unloaded the next frame. + *

+ *

+ * If tiles sized more than maximumMemoryUsage are needed + * to meet the desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, + * for the current view, then the memory usage of the tiles loaded will exceed + * maximumMemoryUsage. For example, if the maximum is 256 MB, but + * 300 MB of tiles are needed to meet the screen space error, then 300 MB of tiles may be loaded. When + * these tiles go out of view, they will be unloaded. + *

+ * + * @memberof Cesium3DTileset.prototype + * + * @type {Number} + * @default 512 + * + * @exception {DeveloperError} maximumMemoryUsage must be greater than or equal to zero. + * @see Cesium3DTileset#totalMemoryUsageInBytes + */ + maximumMemoryUsage : { + get : function() { + return this._maximumMemoryUsage; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('value', value, 0); + //>>includeEnd('debug'); + + this._maximumMemoryUsage = value; + } + }, + + /** + * The tileset's bounding sphere. + * + * @memberof Cesium3DTileset.prototype + * + * @type {BoundingSphere} + * @readonly + * + * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. + * + * @example + * var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ + * url : 'http://localhost:8002/tilesets/Seattle' + * })); + * + * tileset.readyPromise.then(function(tileset) { + * // Set the camera to view the newly added tileset + * viewer.camera.viewBoundingSphere(tileset.boundingSphere, new Cesium.HeadingPitchRange(0, -0.5, 0)); + * }); + */ + boundingSphere : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this.ready) { + throw new DeveloperError('The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.'); + } + //>>includeEnd('debug'); + + return this._root.boundingSphere; + } + }, + + /** + * A 4x4 transformation matrix that transforms the entire tileset. + * + * @memberof Cesium3DTileset.prototype + * + * @type {Matrix4} + * @default Matrix4.IDENTITY + * + * @example + * // Adjust a tileset's height from the globe's surface. + * var heightOffset = 20.0; + * var boundingSphere = tileset.boundingSphere; + * var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center); + * var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0); + * var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset); + * var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3()); + * tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation); + */ + modelMatrix : { + get : function() { + return this._modelMatrix; + }, + set : function(value) { + this._modelMatrix = Matrix4.clone(value, this._modelMatrix); + if (defined(this._root)) { + // Update the root transform right away instead of waiting for the next update loop. + // Useful, for example, when setting the modelMatrix and then having the camera view the tileset. + this._root.updateTransform(this._modelMatrix); + } + } + }, + + /** + * Returns the time, in milliseconds, since the tileset was loaded and first updated. + * + * @memberof Cesium3DTileset.prototype + * + * @type {Number} + * @readonly + */ + timeSinceLoad : { + get : function() { + return this._timeSinceLoad; + } + }, + + /** + * The total amount of GPU memory in bytes used by the tileset. This value is estimated from + * geometry, texture, and batch table textures of loaded tiles. For point clouds, this value also + * includes per-point metadata. + * + * @memberof Cesium3DTileset.prototype + * + * @type {Number} + * @readonly + * + * @see Cesium3DTileset#maximumMemoryUsage + */ + totalMemoryUsageInBytes : { + get : function() { + var statistics = this._statistics; + return statistics.texturesByteLength + statistics.geometryByteLength + statistics.batchTableByteLength; + } + }, + + /** + * @private + */ + styleEngine : { + get : function() { + return this._styleEngine; + } + }, + + /** + * @private + */ + statistics : { + get : function() { + return this._statistics; + } + } + }); + + /** + * Marks the tileset's {@link Cesium3DTileset#style} as dirty, which forces all + * features to re-evaluate the style in the next frame each is visible. + */ + Cesium3DTileset.prototype.makeStyleDirty = function() { + this._styleEngine.makeDirty(); + }; + + /** + * Loads the main tileset.json or a tileset.json referenced from a tile. + * + * @private + */ + Cesium3DTileset.prototype.loadTileset = function(tilesetUrl, tilesetJson, parentTile) { + var asset = tilesetJson.asset; + if (!defined(asset)) { + throw new RuntimeError('Tileset must have an asset property.'); + } + if (asset.version !== '0.0' && asset.version !== '1.0') { + throw new RuntimeError('The tileset must be 3D Tiles version 0.0 or 1.0. See https://github.com/AnalyticalGraphicsInc/3d-tiles#spec-status'); + } + + var statistics = this._statistics; + + // Append the tileset version to the basePath + var hasVersionQuery = /[?&]v=/.test(tilesetUrl); + if (!hasVersionQuery) { + var versionQuery = '?v=' + defaultValue(asset.tilesetVersion, '0.0'); + this._basePath = joinUrls(this._basePath, 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 basePath relative to the external tileset. + var basePath = getBaseUri(tilesetUrl, true); + var rootTile = new Cesium3DTile(this, basePath, tilesetJson.root, parentTile); + + // If there is a parentTile, add the root of the currently loading tileset + // to parentTile's children, and update its _depth. + if (defined(parentTile)) { + parentTile.children.push(rootTile); + rootTile._depth = parentTile._depth + 1; + } + + ++statistics.numberOfTilesTotal; + + var stack = []; + stack.push({ + header : tilesetJson.root, + tile3D : rootTile + }); + + while (stack.length > 0) { + var tile = stack.pop(); + var tile3D = tile.tile3D; + var children = tile.header.children; + if (defined(children)) { + var length = children.length; + for (var i = 0; i < length; ++i) { + var childHeader = children[i]; + var childTile = new Cesium3DTile(this, basePath, childHeader, tile3D); + tile3D.children.push(childTile); + childTile._depth = tile3D._depth + 1; + ++statistics.numberOfTilesTotal; + stack.push({ + header : childHeader, + tile3D : childTile + }); + } + } + + if (this._cullWithChildrenBounds) { + Cesium3DTileOptimizations.checkChildrenWithinParent(tile3D); + } + } + + return rootTile; + }; + + var scratchPositionNormal = new Cartesian3(); + var scratchCartographic = new Cartographic(); + var scratchMatrix = new Matrix4(); + var scratchCenter = new Cartesian3(); + var scratchPosition = new Cartesian3(); + var scratchDirection = new Cartesian3(); + + function updateDynamicScreenSpaceError(tileset, frameState) { + var up; + var direction; + var height; + var minimumHeight; + var maximumHeight; + + var camera = frameState.camera; + var root = tileset._root; + var tileBoundingVolume = root.contentBoundingVolume; + + if (tileBoundingVolume instanceof TileBoundingRegion) { + up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal); + direction = camera.directionWC; + height = camera.positionCartographic.height; + minimumHeight = tileBoundingVolume.minimumHeight; + maximumHeight = tileBoundingVolume.maximumHeight; + } else { + // Transform camera position and direction into the local coordinate system of the tileset + var transformLocal = Matrix4.inverseTransformation(root.computedTransform, scratchMatrix); + var ellipsoid = frameState.mapProjection.ellipsoid; + var boundingVolume = tileBoundingVolume.boundingVolume; + var centerLocal = Matrix4.multiplyByPoint(transformLocal, boundingVolume.center, scratchCenter); + if (Cartesian3.magnitude(centerLocal) > ellipsoid.minimumRadius) { + // The tileset is defined in WGS84. Approximate the minimum and maximum height. + var centerCartographic = Cartographic.fromCartesian(centerLocal, ellipsoid, scratchCartographic); + up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal); + direction = camera.directionWC; + height = camera.positionCartographic.height; + minimumHeight = 0.0; + maximumHeight = centerCartographic.height * 2.0; + } else { + // The tileset is defined in local coordinates (z-up) + var positionLocal = Matrix4.multiplyByPoint(transformLocal, camera.positionWC, scratchPosition); + up = Cartesian3.UNIT_Z; + direction = Matrix4.multiplyByPointAsVector(transformLocal, camera.directionWC, scratchDirection); + direction = Cartesian3.normalize(direction, direction); + height = positionLocal.z; + if (tileBoundingVolume instanceof TileOrientedBoundingBox) { + // Assuming z-up, the last component stores the half-height of the box + var boxHeight = root._header.boundingVolume.box[11]; + minimumHeight = centerLocal.z - boxHeight; + maximumHeight = centerLocal.z + boxHeight; + } else if (tileBoundingVolume instanceof TileBoundingSphere) { + var radius = boundingVolume.radius; + minimumHeight = centerLocal.z - radius; + maximumHeight = centerLocal.z + radius; + } + } + } + + // The range where the density starts to lessen. Start at the quarter height of the tileset. + var heightFalloff = tileset.dynamicScreenSpaceErrorHeightFalloff; + var heightClose = minimumHeight + (maximumHeight - minimumHeight) * heightFalloff; + var heightFar = maximumHeight; + + var t = CesiumMath.clamp((height - heightClose) / (heightFar - heightClose), 0.0, 1.0); + + // Increase density as the camera tilts towards the horizon + var dot = Math.abs(Cartesian3.dot(direction, up)); + var horizonFactor = 1.0 - dot; + + // Weaken the horizon factor as the camera height increases, implying the camera is further away from the tileset. + // The goal is to increase density for the "street view", not when viewing the tileset from a distance. + horizonFactor = horizonFactor * (1.0 - t); + + var density = tileset.dynamicScreenSpaceErrorDensity; + density *= horizonFactor; + + tileset._dynamicScreenSpaceErrorComputedDensity = density; + } + + function selectionHeuristic(tileset, ancestor, tile) { + var skipLevels = tileset.skipLevelOfDetail ? tileset.skipLevels : 0; + var skipScreenSpaceErrorFactor = tileset.skipLevelOfDetail ? tileset.skipScreenSpaceErrorFactor : 1.0; + + return (ancestor !== tile && !tile.hasEmptyContent && !tileset.immediatelyLoadDesiredLevelOfDetail) && + (tile._screenSpaceError < ancestor._screenSpaceError / skipScreenSpaceErrorFactor) && + (tile._depth > ancestor._depth + skipLevels); + } + + /////////////////////////////////////////////////////////////////////////// + + function requestContent(tileset, tile) { + if (tile.hasEmptyContent) { + return; + } + + var statistics = tileset._statistics; + var expired = tile.contentExpired; + var requested = tile.requestContent(); + + if (!requested) { + ++statistics.numberOfAttemptedRequests; + return; + } + + if (expired) { + if (tile.hasRenderableContent) { + statistics.decrementLoadCounts(tile.content); + --tileset._statistics.numberOfTilesWithContentReady; + } else if (tile.hasTilesetContent) { + destroySubtree(tileset, tile); + } + } + + ++statistics.numberOfPendingRequests; + + var removeFunction = removeFromProcessingQueue(tileset, tile); + tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile)); + tile.contentReadyPromise.then(removeFunction).otherwise(removeFunction); + } + + function requestTiles(tileset, outOfCore) { + if (!outOfCore) { + return; + } + var requestedTiles = tileset._requestedTiles; + var length = requestedTiles.length; + for (var i = 0; i < length; ++i) { + requestContent(tileset, requestedTiles[i]); + } + } + + function addToProcessingQueue(tileset, tile) { + return function() { + tileset._processingQueue.push(tile); + + --tileset._statistics.numberOfPendingRequests; + ++tileset._statistics.numberOfTilesProcessing; + }; + } + + function removeFromProcessingQueue(tileset, tile) { + return function() { + var index = tileset._processingQueue.indexOf(tile); + if (index === -1) { + // Not in processing queue + // For example, when a url request fails and the ready promise is rejected + --tileset._statistics.numberOfPendingRequests; + return; + } + + // Remove from processing queue + tileset._processingQueue.splice(index, 1); + --tileset._statistics.numberOfTilesProcessing; + + if (tile.hasRenderableContent) { + // RESEARCH_IDEA: ability to unload tiles (without content) for an + // external tileset when all the tiles are unloaded. + tileset._statistics.incrementLoadCounts(tile.content); + ++tileset._statistics.numberOfTilesWithContentReady; + + // Add to the tile cache. Previously expired tiles are already in the cache. + if (!defined(tile.replacementNode)) { + tile.replacementNode = tileset._replacementList.add(tile); + } + } + }; + } + + function processTiles(tileset, frameState) { + var tiles = tileset._processingQueue; + var length = tiles.length; + + // Process tiles in the PROCESSING state so they will eventually move to the READY state. + // Traverse backwards in case a tile is removed as a result of calling process() + for (var i = length - 1; i >= 0; --i) { + tiles[i].process(tileset, frameState); + } + } + + /////////////////////////////////////////////////////////////////////////// + + var scratchCartesian = new Cartesian3(); + + var stringOptions = { + maximumFractionDigits : 3 + }; + + function formatMemoryString(memorySizeInBytes) { + var memoryInMegabytes = memorySizeInBytes / 1048576; + if (memoryInMegabytes < 1.0) { + return memoryInMegabytes.toLocaleString(undefined, stringOptions); + } + return Math.round(memoryInMegabytes).toLocaleString(); + } + + function computeTileLabelPosition(tile) { + 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); + } + return position; + } + + function addTileDebugLabel(tile, tileset, position) { + var labelString = ''; + var attributes = 0; + + if (tileset.debugShowGeometricError) { + labelString += '\nGeometric error: ' + tile.geometricError; + attributes++; + } + + if (tileset.debugShowRenderingStatistics) { + labelString += '\nCommands: ' + tile.commandsLength; + attributes++; + + // Don't display number of points or triangles if 0. + var numberOfPoints = tile.content.pointsLength; + if (numberOfPoints > 0) { + labelString += '\nPoints: ' + tile.content.pointsLength; + attributes++; + } + + var numberOfTriangles = tile.content.trianglesLength; + if (numberOfTriangles > 0) { + labelString += '\nTriangles: ' + tile.content.trianglesLength; + attributes++; + } + + labelString += '\nFeatures: ' + tile.content.featuresLength; + attributes ++; + } + + if (tileset.debugShowMemoryUsage) { + labelString += '\nTexture Memory: ' + formatMemoryString(tile.content.texturesByteLength); + labelString += '\nGeometry Memory: ' + formatMemoryString(tile.content.geometryByteLength); + attributes += 2; + } + + var newLabel = { + text : labelString.substring(1), + position : position, + font : (19-attributes) + 'px sans-serif', + showBackground : true, + disableDepthTestDistance : Number.POSITIVE_INFINITY + }; + + return tileset._tileDebugLabels.add(newLabel); + } + + function updateTileDebugLabels(tileset, frameState) { + var selectedTiles = tileset._selectedTiles; + var length = selectedTiles.length; + tileset._tileDebugLabels.removeAll(); + + if (tileset.debugPickedTileLabelOnly) { + if (defined(tileset.debugPickedTile)) { + var position = (defined(tileset.debugPickPosition)) ? tileset.debugPickPosition : computeTileLabelPosition(tileset.debugPickedTile); + var label = addTileDebugLabel(tileset.debugPickedTile, tileset, position); + label.pixelOffset = new Cartesian2(15, -15); // Offset to avoid picking the label. + } + } else { + for (var i = 0; i < length; ++i) { + var tile = selectedTiles[i]; + addTileDebugLabel(tile, tileset, computeTileLabelPosition(tile)); + } + } + tileset._tileDebugLabels.update(frameState); + } + + var stencilClearCommand = new ClearCommand({ + stencil : 0, + pass : Pass.CESIUM_3D_TILE + }); + + function updateTiles(tileset, frameState) { + tileset._styleEngine.applyStyle(tileset, frameState); + + var statistics = tileset._statistics; + var commandList = frameState.commandList; + var numberOfInitialCommands = commandList.length; + var selectedTiles = tileset._selectedTiles; + var length = selectedTiles.length; + var tileVisible = tileset.tileVisible; + var i; + + var bivariateVisibilityTest = tileset.skipLevelOfDetail && tileset._hasMixedContent && frameState.context.stencilBuffer && length > 0; + + tileset._backfaceCommands.length = 0; + + if (bivariateVisibilityTest) { + commandList.push(stencilClearCommand); + } + + var lengthBeforeUpdate = commandList.length; + for (i = 0; i < length; ++i) { + var tile = selectedTiles[i]; + // tiles may get unloaded and destroyed between selection and update + if (tile.selected) { + // Raise the tileVisible event before update in case the tileVisible event + // handler makes changes that update needs to apply to WebGL resources + tileVisible.raiseEvent(tile); + tile.update(tileset, frameState); + statistics.incrementSelectionCounts(tile.content); + ++statistics.selected; + } + } + var lengthAfterUpdate = commandList.length; + + tileset._backfaceCommands.trim(); + + if (bivariateVisibilityTest) { + /** + * Consider 'effective leaf' tiles as selected tiles that have no selected descendants. They may have children, + * but they are currently our effective leaves because they do not have selected descendants. These tiles + * are those where with tile._finalResolution === true. + * Let 'unresolved' tiles be those with tile._finalResolution === false. + * + * 1. Render just the backfaces of unresolved tiles in order to lay down z + * 2. Render all frontfaces wherever tile._selectionDepth > stencilBuffer. + * Replace stencilBuffer with tile._selectionDepth, when passing the z test. + * Because children are always drawn before ancestors {@link Cesium3DTilesetTraversal#traverseAndSelect}, + * this effectively draws children first and does not draw ancestors if a descendant has already + * been drawn at that pixel. + * Step 1 prevents child tiles from appearing on top when they are truly behind ancestor content. + * If they are behind the backfaces of the ancestor, then they will not be drawn. + * + * NOTE: Step 2 sometimes causes visual artifacts when backfacing child content has some faces that + * partially face the camera and are inside of the ancestor content. Because they are inside, they will + * not be culled by the depth writes in Step 1, and because they partially face the camera, the stencil tests + * will draw them on top of the ancestor content. + * + * NOTE: Because we always render backfaces of unresolved tiles, if the camera is looking at the backfaces + * of an object, they will always be drawn while loading, even if backface culling is enabled. + */ + + var backfaceCommands = tileset._backfaceCommands.values; + var addedCommandsLength = (lengthAfterUpdate - lengthBeforeUpdate); + var backfaceCommandsLength = backfaceCommands.length; + + commandList.length += backfaceCommands.length; + + // copy commands to the back of the commandList + for (i = addedCommandsLength - 1; i >= 0; --i) { + commandList[lengthBeforeUpdate + backfaceCommandsLength + i] = commandList[lengthBeforeUpdate + i]; + } + + // move backface commands to the front of the commandList + for (i = 0; i < backfaceCommandsLength; ++i) { + commandList[lengthBeforeUpdate + i] = backfaceCommands[i]; + } + } + + // Number of commands added by each update above + statistics.numberOfCommands = (commandList.length - numberOfInitialCommands); + + if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage) { + if (!defined(tileset._tileDebugLabels)) { + tileset._tileDebugLabels = new LabelCollection(); + } + updateTileDebugLabels(tileset, frameState); + } else { + tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); + } + } + + var scratchStack = []; + + function destroySubtree(tileset, tile) { + var root = tile; + var statistics = tileset._statistics; + var stack = scratchStack; + stack.push(tile); + while (stack.length > 0) { + tile = stack.pop(); + var children = tile.children; + var length = children.length; + for (var i = 0; i < length; ++i) { + stack.push(children[i]); + } + if (tile !== root) { + unloadTileFromCache(tileset, tile); + tile.destroy(); + --statistics.numberOfTilesTotal; + } + } + root.children = []; + } + + function unloadTileFromCache(tileset, tile) { + var node = tile.replacementNode; + if (!defined(node)) { + return; + } + + var statistics = tileset._statistics; + var replacementList = tileset._replacementList; + var tileUnload = tileset.tileUnload; + + tileUnload.raiseEvent(tile); + replacementList.remove(node); + statistics.decrementLoadCounts(tile.content); + --statistics.numberOfTilesWithContentReady; + } + + function unloadTiles(tileset) { + var trimTiles = tileset._trimTiles; + tileset._trimTiles = false; + + var replacementList = tileset._replacementList; + + var totalMemoryUsageInBytes = tileset.totalMemoryUsageInBytes; + var maximumMemoryUsageInBytes = tileset._maximumMemoryUsage * 1024 * 1024; + + // Traverse the list only to the sentinel since tiles/nodes to the + // right of the sentinel were used this frame. + // + // The sub-list to the left of the sentinel is ordered from LRU to MRU. + var sentinel = tileset._replacementSentinel; + var node = replacementList.head; + while ((node !== sentinel) && ((totalMemoryUsageInBytes > maximumMemoryUsageInBytes) || trimTiles)) { + var tile = node.item; + node = node.next; + unloadTileFromCache(tileset, tile); + tile.unloadContent(); + totalMemoryUsageInBytes = tileset.totalMemoryUsageInBytes; + } + } + + /** + * Unloads all tiles that weren't selected the previous frame. This can be used to + * explicitly manage the tile cache and reduce the total number of tiles loaded below + * {@link Cesium3DTileset#maximumMemoryUsage}. + *

+ * Tile unloads occur at the next frame to keep all the WebGL delete calls + * within the render loop. + *

+ */ + Cesium3DTileset.prototype.trimLoadedTiles = function() { + // Defer to next frame so WebGL delete calls happen inside the render loop + this._trimTiles = true; + }; + + /////////////////////////////////////////////////////////////////////////// + + function raiseLoadProgressEvent(tileset, frameState) { + var statistics = tileset._statistics; + var statisticsLast = tileset._statisticsLastColor; + var numberOfPendingRequests = statistics.numberOfPendingRequests; + var numberOfTilesProcessing = statistics.numberOfTilesProcessing; + var lastNumberOfPendingRequest = statisticsLast.numberOfPendingRequests; + var lastNumberOfTilesProcessing = statisticsLast.numberOfTilesProcessing; + + var progressChanged = (numberOfPendingRequests !== lastNumberOfPendingRequest) || (numberOfTilesProcessing !== lastNumberOfTilesProcessing); + + if (progressChanged) { + frameState.afterRender.push(function() { + tileset.loadProgress.raiseEvent(numberOfPendingRequests, numberOfTilesProcessing); + }); + } + + tileset._tilesLoaded = (statistics.numberOfPendingRequests === 0) && (statistics.numberOfTilesProcessing === 0) && (statistics.numberOfAttemptedRequests === 0); + + if (progressChanged && tileset._tilesLoaded) { + frameState.afterRender.push(function() { + tileset.allTilesLoaded.raiseEvent(); + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + + /** + * Called when {@link Viewer} or {@link CesiumWidget} render the scene to + * get the draw commands needed to render this primitive. + *

+ * Do not call this function directly. This is documented just to + * list the exceptions that may be propagated when the scene is rendered: + *

+ */ + Cesium3DTileset.prototype.update = function(frameState) { + if (frameState.mode === SceneMode.MORPHING) { + return; + } + + if (!this.show || !this.ready) { + return; + } + + if (!defined(this._loadTimestamp)) { + this._loadTimestamp = JulianDate.clone(frameState.time); + } + + this._timeSinceLoad = Math.max(JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000, 0.0); + + // Do not do out-of-core operations (new content requests, cache removal, + // process new tiles) during the pick pass. + var passes = frameState.passes; + var isPick = (passes.pick && !passes.render); + var outOfCore = !isPick; + + var statistics = this._statistics; + statistics.clear(); + + if (outOfCore) { + processTiles(this, frameState); + } + + if (this.dynamicScreenSpaceError) { + updateDynamicScreenSpaceError(this, frameState); + } + + Cesium3DTilesetTraversal.selectTiles(this, frameState, outOfCore); + requestTiles(this, outOfCore); + updateTiles(this, frameState); + + if (outOfCore) { + unloadTiles(this); + } + + // Events are raised (added to the afterRender queue) here since promises + // may resolve outside of the update loop that then raise events, e.g., + // model's readyPromise. + raiseLoadProgressEvent(this, frameState); + + // Update last statistics + var statisticsLast = isPick ? this._statisticsLastPick : this._statisticsLastColor; + Cesium3DTilesetStatistics.clone(statistics, statisticsLast); + }; + + /** + * Returns true if this object was destroyed; otherwise, false. + *

+ * If this object was destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. + * + * @returns {Boolean} true if this object was destroyed; otherwise, false. + * + * @see Cesium3DTileset#destroy + */ + Cesium3DTileset.prototype.isDestroyed = function() { + return false; + }; + + /** + * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic + * release of WebGL resources, instead of relying on the garbage collector to destroy this object. + *

+ * Once an object is destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. Therefore, + * assign the return value (undefined) to the object as done in the example. + * + * @returns {undefined} + * + * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. + * + * @example + * tileset = tileset && tileset.destroy(); + * + * @see Cesium3DTileset#isDestroyed + */ + Cesium3DTileset.prototype.destroy = function() { + // Destroy debug labels + this._tileDebugLabels = this._tileDebugLabels && this._tileDebugLabels.destroy(); + + // Traverse the tree and destroy all tiles + if (defined(this._root)) { + var stack = scratchStack; + stack.push(this._root); + + while (stack.length > 0) { + var tile = stack.pop(); + tile.destroy(); + + var children = tile.children; + var length = children.length; + for (var i = 0; i < length; ++i) { + stack.push(children[i]); + } + } + } + + this._root = undefined; + return destroyObject(this); + }; + + return Cesium3DTileset; +}); diff --git a/Source/Scene/Cesium3DTilesetStatistics.js b/Source/Scene/Cesium3DTilesetStatistics.js new file mode 100644 index 000000000000..85442f395427 --- /dev/null +++ b/Source/Scene/Cesium3DTilesetStatistics.js @@ -0,0 +1,117 @@ +/*global define*/ +define([ + '../Core/defined' + ], function( + defined) { + 'use strict'; + + /** + * @private + */ + function Cesium3DTilesetStatistics() { + // Rendering statistics + this.selected = 0; + this.visited = 0; + // Loading statistics + this.numberOfCommands = 0; + this.numberOfAttemptedRequests = 0; + this.numberOfPendingRequests = 0; + this.numberOfTilesProcessing = 0; + this.numberOfTilesWithContentReady = 0; // Number of tiles with content loaded, does not include empty tiles + this.numberOfTilesTotal = 0; // Number of tiles in tileset.json (and other tileset.json files as they are loaded) + // Features statistics + this.numberOfFeaturesSelected = 0; // Number of features rendered + this.numberOfFeaturesLoaded = 0; // Number of features in memory + this.numberOfPointsSelected = 0; + this.numberOfPointsLoaded = 0; + this.numberOfTrianglesSelected = 0; + // Styling statistics + this.numberOfTilesStyled = 0; + this.numberOfFeaturesStyled = 0; + // Optimization statistics + this.numberOfTilesCulledWithChildrenUnion = 0; + // Memory statistics + this.geometryByteLength = 0; + this.texturesByteLength = 0; + this.batchTableByteLength = 0; + } + + Cesium3DTilesetStatistics.prototype.clear = function() { + this.selected = 0; + this.visited = 0; + this.numberOfCommands = 0; + this.numberOfAttemptedRequests = 0; + this.numberOfFeaturesSelected = 0; + this.numberOfPointsSelected = 0; + this.numberOfTrianglesSelected = 0; + this.numberOfTilesStyled = 0; + this.numberOfFeaturesStyled = 0; + this.numberOfTilesCulledWithChildrenUnion = 0; + }; + + function updatePointAndFeatureCounts(statistics, content, decrement, load) { + var contents = content.innerContents; + var pointsLength = content.pointsLength; + var trianglesLength = content.trianglesLength; + var featuresLength = content.featuresLength; + var geometryByteLength = content.geometryByteLength; + var texturesByteLength = content.texturesByteLength; + var batchTableByteLength = content.batchTableByteLength; + + if (load) { + statistics.numberOfFeaturesLoaded += decrement ? -featuresLength : featuresLength; + statistics.numberOfPointsLoaded += decrement ? -pointsLength : pointsLength; + statistics.geometryByteLength += decrement ? -geometryByteLength : geometryByteLength; + statistics.texturesByteLength += decrement ? -texturesByteLength : texturesByteLength; + statistics.batchTableByteLength += decrement ? -batchTableByteLength : batchTableByteLength; + } else { + statistics.numberOfFeaturesSelected += decrement ? -featuresLength : featuresLength; + statistics.numberOfPointsSelected += decrement ? -pointsLength : pointsLength; + statistics.numberOfTrianglesSelected += decrement ? -trianglesLength : trianglesLength; + } + + if (defined(contents)) { + var length = contents.length; + for (var i = 0; i < length; ++i) { + updatePointAndFeatureCounts(statistics, contents[i], decrement, load); + } + } + } + + Cesium3DTilesetStatistics.prototype.incrementSelectionCounts = function(content) { + updatePointAndFeatureCounts(this, content, false, false); + }; + + Cesium3DTilesetStatistics.prototype.incrementLoadCounts = function(content) { + updatePointAndFeatureCounts(this, content, false, true); + }; + + Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function(content) { + updatePointAndFeatureCounts(this, content, true, true); + }; + + Cesium3DTilesetStatistics.clone = function(statistics, result) { + result.selected = statistics.selected; + result.visited = statistics.visited; + result.numberOfCommands = statistics.numberOfCommands; + result.selected = statistics.selected; + result.numberOfAttemptedRequests = statistics.numberOfAttemptedRequests; + result.numberOfPendingRequests = statistics.numberOfPendingRequests; + result.numberOfTilesProcessing = statistics.numberOfTilesProcessing; + result.numberOfTilesWithContentReady = statistics.numberOfTilesWithContentReady; + result.numberOfTilesTotal = statistics.numberOfTilesTotal; + result.numberOfFeaturesSelected = statistics.numberOfFeaturesSelected; + result.numberOfFeaturesLoaded = statistics.numberOfFeaturesLoaded; + result.numberOfPointsSelected = statistics.numberOfPointsSelected; + result.numberOfPointsLoaded = statistics.numberOfPointsLoaded; + result.numberOfTrianglesSelected = statistics.numberOfTrianglesSelected; + result.numberOfTilesStyled = statistics.numberOfTilesStyled; + result.numberOfFeaturesStyled = statistics.numberOfFeaturesStyled; + result.numberOfTilesCulledWithChildrenUnion = statistics.numberOfTilesCulledWithChildrenUnion; + result.geometryByteLength = statistics.geometryByteLength; + result.texturesByteLength = statistics.texturesByteLength; + result.batchTableByteLength = statistics.batchTableByteLength; + }; + + return Cesium3DTilesetStatistics; +}); diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js new file mode 100644 index 000000000000..6722b3bf368f --- /dev/null +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -0,0 +1,839 @@ +/*global define*/ +define([ + '../Core/defined', + '../Core/freezeObject', + '../Core/Intersect', + '../Core/ManagedArray', + '../Core/Math', + './Cesium3DTileChildrenVisibility', + './Cesium3DTileRefine', + './CullingVolume', + './OrthographicFrustum', + './SceneMode' + ], function( + defined, + freezeObject, + Intersect, + ManagedArray, + CesiumMath, + Cesium3DTileChildrenVisibility, + Cesium3DTileRefine, + CullingVolume, + OrthographicFrustum, + SceneMode) { + 'use strict'; + + /** + * @private + */ + var Cesium3DTilesetTraversal = {}; + + function selectTiles(tileset, frameState, outOfCore) { + if (tileset.debugFreezeFrame) { + return; + } + + var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + + tileset._desiredTiles.length = 0; + tileset._selectedTiles.length = 0; + tileset._requestedTiles.length = 0; + tileset._selectedTilesToStyle.length = 0; + tileset._hasMixedContent = false; + + // Move sentinel node to the tail so, at the start of the frame, all tiles + // may be potentially replaced. Tiles are moved to the right of the sentinel + // when they are selected so they will not be replaced. + var replacementList = tileset._replacementList; + replacementList.splice(replacementList.tail, tileset._replacementSentinel); + + var root = tileset._root; + root.updateTransform(tileset._modelMatrix); + + if (!root.insideViewerRequestVolume(frameState)) { + return; + } + + root._distanceToCamera = root.distanceToTile(frameState); + + if (getScreenSpaceError(tileset, tileset._geometricError, root, frameState) <= maximumScreenSpaceError) { + // The SSE of not rendering the tree is small enough that the tree does not need to be rendered + return; + } + + root._visibilityPlaneMask = root.visibility(frameState, CullingVolume.MASK_INDETERMINATE); + if (root._visibilityPlaneMask === CullingVolume.MASK_OUTSIDE) { + return; + } + + loadTile(tileset, root, frameState); + + if (!tileset.skipLevelOfDetail) { + // just execute base traversal and add tiles to _desiredTiles + tileset._baseTraversal.execute(tileset, root, maximumScreenSpaceError, frameState, outOfCore); + var leaves = tileset._baseTraversal.leaves; + var length = leaves.length; + for (var i = 0; i < length; ++i) { + tileset._desiredTiles.push(leaves.get(i)); + } + } else { + if (tileset.immediatelyLoadDesiredLevelOfDetail) { + tileset._skipTraversal.execute(tileset, root, frameState, outOfCore); + } else { + // leaves of the base traversal is where we start the skip traversal + tileset._baseTraversal.leaves = tileset._skipTraversal.queue1; + + // load and select tiles without skipping up to tileset.baseScreenSpaceError + tileset._baseTraversal.execute(tileset, root, tileset.baseScreenSpaceError, frameState, outOfCore); + + // skip traversal starts from a prepopulated queue from the base traversal + tileset._skipTraversal.execute(tileset, undefined, frameState, outOfCore); + } + } + + // mark tiles for selection or their nearest loaded ancestor + markLoadedTilesForSelection(tileset, frameState, outOfCore); + + // sort selected tiles by distance to camera and call selectTile on each + // set tile._selectionDepth on all tiles + traverseAndSelect(tileset, root, frameState); + + tileset._desiredTiles.trim(); + } + + var descendantStack = []; + + function markLoadedTilesForSelection(tileset, frameState, outOfCore) { + var tiles = tileset._desiredTiles; + var length = tiles.length; + for (var i = 0; i < length; ++i) { + var original = tiles.get(i); + + if (hasAdditiveContent(original)) { + original.selected = true; + original._selectedFrame = frameState.frameNumber; + continue; + } + + var loadedTile = original._ancestorWithLoadedContent; + if (original.hasRenderableContent && original.contentAvailable) { + loadedTile = original; + } + + if (defined(loadedTile)) { + loadedTile.selected = true; + loadedTile._selectedFrame = frameState.frameNumber; + } else { + // if no ancestors are ready, traverse down and select ready tiles to minimize empty regions + descendantStack.push(original); + while (descendantStack.length > 0) { + var tile = descendantStack.pop(); + var children = tile.children; + var childrenLength = children.length; + for (var j = 0; j < childrenLength; ++j) { + var child = children[j]; + touch(tileset, child, outOfCore); + if (child.contentAvailable) { + child.selected = true; + child._finalResolution = true; + child._selectedFrame = frameState.frameNumber; + } + if (child._depth - original._depth < 2) { // prevent traversing too far + if (!child.contentAvailable || child.refine === Cesium3DTileRefine.ADD) { + descendantStack.push(child); + } + } + } + } + } + } + } + + var scratchStack = []; + var scratchStack2 = []; + + /** + * Traverse the tree while tiles are visible and check if their selected frame is the current frame. + * If so, add it to a selection queue. + * Tiles are sorted near to far so we can take advantage of early Z. + * Furthermore, this is a preorder traversal so children tiles are selected before ancestor tiles. + * + * The reason for the preorder traversal is so that tiles can easily be marked with their + * selection depth. A tile's _selectionDepth is its depth in the tree where all non-selected tiles are removed. + * This property is important for use in the stencil test because we want to render deeper tiles on top of their + * ancestors. If a tileset is very deep, the depth is unlikely to fit into the stencil buffer. + * + * We want to select children before their ancestors because there is no guarantee on the relationship between + * the children's z-depth and the ancestor's z-depth. We cannot rely on Z because we want the child to appear on top + * of ancestor regardless of true depth. The stencil tests used require children to be drawn first. @see {@link updateTiles} + * + * NOTE: this will no longer work when there is a chain of selected tiles that is longer than the size of the + * stencil buffer (usually 8 bits). In other words, the subset of the tree containing only selected tiles must be + * no deeper than 255. It is very, very unlikely this will cause a problem. + */ + function traverseAndSelect(tileset, root, frameState) { + var stack = scratchStack; + var ancestorStack = scratchStack2; + + var lastAncestor; + stack.push(root); + while (stack.length > 0 || ancestorStack.length > 0) { + if (ancestorStack.length > 0) { + var waitingTile = ancestorStack[ancestorStack.length - 1]; + if (waitingTile._stackLength === stack.length) { + ancestorStack.pop(); + if (waitingTile === lastAncestor) { + waitingTile._finalResolution = true; + } + selectTile(tileset, waitingTile, frameState); + continue; + } + } + + var tile = stack.pop(); + if (!defined(tile) || !isVisited(tile, frameState)) { + continue; + } + + var shouldSelect = tile.selected && tile._selectedFrame === frameState.frameNumber && tile.hasRenderableContent; + + var children = tile.children; + var childrenLength = children.length; + + children.sort(sortChildrenByDistanceToCamera); + + if (shouldSelect) { + if (tile.refine === Cesium3DTileRefine.ADD) { + tile._finalResolution = true; + selectTile(tileset, tile, frameState); + } else { + tile._selectionDepth = ancestorStack.length; + + if (tile._selectionDepth > 0) { + tileset._hasMixedContent = true; + } + + lastAncestor = tile; + + if (childrenLength === 0) { + tile._finalResolution = true; + selectTile(tileset, tile, frameState); + continue; + } + + ancestorStack.push(tile); + tile._stackLength = stack.length; + } + } + + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + stack.push(child); + } + } + } + + function selectTile(tileset, tile, frameState) { + // There may also be a tight box around just the tile's contents, e.g., for a city, we may be + // zoomed into a neighborhood and can cull the skyscrapers in the root tile. + if (tile.contentAvailable && ( + (tile._visibilityPlaneMask === CullingVolume.MASK_INSIDE) || + (tile.contentVisibility(frameState) !== Intersect.OUTSIDE) + )) { + tileset._selectedTiles.push(tile); + + var tileContent = tile.content; + if (tileContent.featurePropertiesDirty) { + // A feature's property in this tile changed, the tile needs to be re-styled. + tileContent.featurePropertiesDirty = false; + tile.lastStyleTime = 0; // Force applying the style to this tile + tileset._selectedTilesToStyle.push(tile); + } else if ((tile._lastSelectedFrameNumber !== frameState.frameNumber - 1)) { + // Tile is newly selected; it is selected this frame, but was not selected last frame. + tileset._selectedTilesToStyle.push(tile); + } + tile._lastSelectedFrameNumber = frameState.frameNumber; + } + } + + // PERFORMANCE_IDEA: is it worth exploiting frame-to-frame coherence in the sort, i.e., the + // list of children are probably fully or mostly sorted unless the camera moved significantly? + function sortChildrenByDistanceToCamera(a, b) { + // Sort by farthest child first since this is going on a stack + if (b._distanceToCamera === 0 && a._distanceToCamera === 0) { + return b._centerZDepth - a._centerZDepth; + } + + return b._distanceToCamera - a._distanceToCamera; + } + + var emptyArray = freezeObject([]); + + function BaseTraversal() { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.stack = new ManagedArray(); + this.leaves = new ManagedArray(); + this.baseScreenSpaceError = undefined; + this.internalDFS = new InternalBaseTraversal(); + } + + BaseTraversal.prototype.execute = function(tileset, root, baseScreenSpaceError, frameState, outOfCore) { + this.tileset = tileset; + this.frameState = frameState; + this.outOfCore = outOfCore; + this.leaves.length = 0; + this.baseScreenSpaceError = Math.max(baseScreenSpaceError, this.tileset._maximumScreenSpaceError); + this.internalDFS.tileset = this.tileset; + this.internalDFS.frameState = this.frameState; + this.internalDFS.outOfCore = this.outOfCore; + this.internalDFS.baseScreenSpaceError = this.baseScreenSpaceError; + depthFirstSearch(root, this); + }; + + BaseTraversal.prototype.visitStart = function(tile) { + if (!isVisited(tile, this.frameState)) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } + }; + + BaseTraversal.prototype.visitEnd = function(tile) { + tile._lastVisitedFrame = this.frameState.frameNumber; + }; + + BaseTraversal.prototype.getChildren = function(tile) { + var tileset = this.tileset; + var outOfCore = this.outOfCore; + var frameState = this.frameState; + if (!baseUpdateAndCheckChildren(tileset, tile, this.baseScreenSpaceError, frameState)) { + return emptyArray; + } + + var children = tile.children; + var childrenLength = children.length; + var allReady = true; + var replacementWithContent = tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent; + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + loadTile(tileset, child, frameState); + touch(tileset, child, outOfCore); + + // content cannot be replaced until all of the nearest descendants with content are all loaded + if (replacementWithContent) { + if (!child.hasEmptyContent) { + allReady = allReady && child.contentAvailable; + } else { + allReady = allReady && this.internalDFS.execute(child); + } + } + } + + if (allReady) { + return children; + } + + return emptyArray; + }; + + function baseUpdateAndCheckChildren(tileset, tile, baseScreenSpaceError, frameState) { + if (hasAdditiveContent(tile)) { + tileset._desiredTiles.push(tile); + } + + // Stop traversal on the subtree since it will be destroyed + if (tile.hasTilesetContent && tile.contentExpired) { + return false; + } + + // stop traversal when we've attained the desired level of error + if (tile._screenSpaceError <= baseScreenSpaceError && !tile.hasTilesetContent) { + // update children so the leaf handler can check if any are visible for the children union bound optimization + updateChildren(tile, frameState); + return false; + } + + var childrenVisibility = updateChildren(tile, frameState); + var showAdditive = tile.refine === Cesium3DTileRefine.ADD; + var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + + return showAdditive || showReplacement || tile.hasTilesetContent || !defined(tile._ancestorWithContent); + } + + BaseTraversal.prototype.shouldVisit = function(tile) { + return isVisible(tile._visibilityPlaneMask); + }; + + BaseTraversal.prototype.leafHandler = function(tile) { + // if skipLevelOfDetail is off, leaves of the base traversal get pushed to tileset._desiredTiles. additive tiles have already been pushed + if (this.tileset.skipLevelOfDetail || !hasAdditiveContent(tile)) { + if (tile.refine === Cesium3DTileRefine.REPLACE && !childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return; + } + this.leaves.push(tile); + } + }; + + function InternalBaseTraversal() { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.baseScreenSpaceError = undefined; + this.stack = new ManagedArray(); + this.allLoaded = undefined; + } + + InternalBaseTraversal.prototype.execute = function(root) { + this.allLoaded = true; + depthFirstSearch(root, this); + return this.allLoaded; + }; + + InternalBaseTraversal.prototype.visitStart = function(tile) { + if (!isVisited(tile, this.frameState)) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } + }; + + InternalBaseTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; + + // Continue traversing until we have renderable content. We want the first descendants with content of the root to load + InternalBaseTraversal.prototype.shouldVisit = function(tile) { + return !tile.hasRenderableContent && isVisible(tile._visibilityPlaneMask); + }; + + InternalBaseTraversal.prototype.getChildren = function(tile) { + var tileset = this.tileset; + var frameState = this.frameState; + var outOfCore = this.outOfCore; + + if (!baseUpdateAndCheckChildren(tileset, tile, this.baseScreenSpaceError, frameState)) { + return emptyArray; + } + + var children = tile.children; + var childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + loadTile(tileset, child, frameState); + touch(tileset, child, outOfCore); + if (!tile.contentAvailable) { + this.allLoaded = false; + } + } + return children; + }; + + InternalBaseTraversal.prototype.updateAndCheckChildren = BaseTraversal.prototype.updateAndCheckChildren; + + function SkipTraversal(options) { + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.queue1 = new ManagedArray(); + this.queue2 = new ManagedArray(); + this.internalDFS = new InternalSkipTraversal(options.selectionHeuristic); + this.maxChildrenLength = 0; + this.scratchQueue = new ManagedArray(); + } + + SkipTraversal.prototype.execute = function(tileset, root, frameState, outOfCore) { + this.tileset = tileset; + this.frameState = frameState; + this.outOfCore = outOfCore; + this.internalDFS.frameState = frameState; + this.internalDFS.outOfCore = outOfCore; + + this.maxChildrenLength = 0; + breadthFirstSearch(root, this); + this.queue1.length = 0; + this.queue2.length = 0; + this.scratchQueue.length = 0; + this.scratchQueue.trim(this.maxChildrenLength); + }; + + SkipTraversal.prototype.visitStart = function(tile) { + if (!isVisited(tile, this.frameState)) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } + }; + + SkipTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; + + SkipTraversal.prototype.getChildren = function(tile) { + this.scratchQueue.length = 0; + this.internalDFS.execute(tile, this.scratchQueue); + this.maxChildrenLength = Math.max(this.maxChildrenLength, this.scratchQueue.length); + return this.scratchQueue; + }; + + SkipTraversal.prototype.leafHandler = function(tile) { + // additive tiles have already been pushed + if (!hasAdditiveContent(tile) && !isVisited(tile, this.frameState)) { + this.tileset._desiredTiles.push(tile); + } + }; + + function InternalSkipTraversal(selectionHeuristic) { + this.selectionHeuristic = selectionHeuristic; + this.tileset = undefined; + this.frameState = undefined; + this.outOfCore = undefined; + this.root = undefined; + this.queue = undefined; + this.stack = new ManagedArray(); + } + + InternalSkipTraversal.prototype.execute = function(root, queue) { + this.tileset = root._tileset; + this.root = root; + this.queue = queue; + depthFirstSearch(root, this); + }; + + InternalSkipTraversal.prototype.visitStart = function(tile) { + if (!isVisited(tile, this.frameState)) { + visitTile(this.tileset, tile, this.frameState, this.outOfCore); + } + }; + + InternalSkipTraversal.prototype.visitEnd = BaseTraversal.prototype.visitEnd; + + InternalSkipTraversal.prototype.getChildren = function(tile) { + var tileset = this.tileset; + var maximumScreenSpaceError = tileset._maximumScreenSpaceError; + + // Stop traversal on the subtree since it will be destroyed + if (tile.hasTilesetContent && tile.contentExpired) { + return emptyArray; + } + + if (!tile.hasTilesetContent) { + if (hasAdditiveContent(tile)) { + tileset._desiredTiles.push(tile); + } + + // stop traversal when we've attained the desired level of error + if (tile._screenSpaceError <= maximumScreenSpaceError) { + updateChildren(tile, this.frameState); + return emptyArray; + } + + // if we have reached the skipping threshold without any loaded ancestors, return empty so this tile is loaded + if ( + (!tile.hasEmptyContent && tile.contentUnloaded) && + defined(tile._ancestorWithLoadedContent) && + this.selectionHeuristic(tileset, tile._ancestorWithLoadedContent, tile)) { + updateChildren(tile, this.frameState); + return emptyArray; + } + } + + var childrenVisibility = updateChildren(tile, this.frameState); + var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > maximumScreenSpaceError; + var showReplacement = tile.refine === Cesium3DTileRefine.REPLACE && (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_IN_REQUEST_VOLUME) !== 0; + + // at least one child is visible, but is not in request volume. the parent must be selected + if (childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME && tile.refine === Cesium3DTileRefine.REPLACE) { + this.tileset._desiredTiles.push(tile); + } + + if (showAdditive || showReplacement || tile.hasTilesetContent) { + var children = tile.children; + var childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + touch(tileset, children[i], this.outOfCore); + } + return children; + } + + return emptyArray; + }; + + InternalSkipTraversal.prototype.shouldVisit = function(tile) { + var maximumScreenSpaceError = this.tileset._maximumScreenSpaceError; + var parent = tile.parent; + if (!defined(parent)) { + return isVisible(tile._visibilityPlaneMask); + } + var showAdditive = parent.refine === Cesium3DTileRefine.ADD && parent._screenSpaceError > maximumScreenSpaceError; + + return isVisible(tile._visibilityPlaneMask) && (!showAdditive || getScreenSpaceError(this.tileset, parent.geometricError, tile, this.frameState) > maximumScreenSpaceError); + }; + + InternalSkipTraversal.prototype.leafHandler = function(tile) { + if (tile !== this.root) { + if (tile.refine === Cesium3DTileRefine.REPLACE && !childrenAreVisible(tile)) { + ++this.tileset._statistics.numberOfTilesCulledWithChildrenUnion; + return; + } + if (!tile.hasEmptyContent) { + if (this.tileset.loadSiblings) { + var parent = tile.parent; + var tiles = parent.children; + var length = tiles.length; + for (var i = 0; i < length; ++i) { + loadTile(this.tileset, tiles[i], this.frameState); + touch(this.tileset, tiles[i], this.outOfCore); + } + } else { + loadTile(this.tileset, tile, this.frameState); + touch(this.tileset, tile, this.outOfCore); + } + } + this.queue.push(tile); + } else if (!hasAdditiveContent(tile)) { + // additive tiles have already been pushed + this.tileset._desiredTiles.push(tile); + } + }; + + function updateChildren(tile, frameState) { + if (isVisited(tile, frameState)) { + return tile._childrenVisibility; + } + + var children = tile.children; + + updateTransforms(children, tile.computedTransform); + computeDistanceToCamera(children, frameState); + + return computeChildrenVisibility(tile, frameState); + } + + function isVisited(tile, frameState) { + // because the leaves of one tree traversal are the root of the subsequent traversal, avoid double visitation + return tile._lastVisitedFrame === frameState.frameNumber; + } + + function visitTile(tileset, tile, frameState, outOfCore) { + ++tileset._statistics.visited; + tile.selected = false; + tile._finalResolution = false; + computeSSE(tile, frameState); + touch(tileset, tile, outOfCore); + tile.updateExpiration(); + tile._ancestorWithContent = undefined; + tile._ancestorWithLoadedContent = undefined; + var parent = tile.parent; + if (defined(parent)) { + var replace = parent.refine === Cesium3DTileRefine.REPLACE; + tile._ancestorWithContent = (replace && parent.hasRenderableContent) ? parent : parent._ancestorWithContent; + tile._ancestorWithLoadedContent = (replace && parent.hasRenderableContent && parent.contentAvailable) ? parent : parent._ancestorWithLoadedContent; + } + } + + function touch(tileset, tile, outOfCore) { + if (!outOfCore) { + return; + } + var node = tile.replacementNode; + if (defined(node)) { + tileset._replacementList.splice(tileset._replacementSentinel, node); + } + } + + function computeSSE(tile, frameState) { + if (tile._screenSpaceErrorComputedFrame !== frameState.frameNumber) { + tile._screenSpaceErrorComputedFrame = frameState.frameNumber; + tile._screenSpaceError = getScreenSpaceError(tile._tileset, tile.geometricError, tile, frameState); + } + } + + function loadTile(tileset, tile, frameState) { + if ((tile.contentUnloaded || tile.contentExpired) && tile._requestedFrame !== frameState.frameNumber) { + tile._requestedFrame = frameState.frameNumber; + tileset._requestedTiles.push(tile); + } + } + + function computeChildrenVisibility(tile, frameState) { + 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 (!child.insideViewerRequestVolume(frameState)) { + if (isVisible(visibilityMask)) { + flag |= Cesium3DTileChildrenVisibility.VISIBLE_NOT_IN_REQUEST_VOLUME; + } + 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 getScreenSpaceError(tileset, geometricError, tile, frameState) { + if (geometricError === 0.0) { + // Leaf tiles do not have any error so save the computation + return 0.0; + } + + // Avoid divide by zero when viewer is inside the tile + var camera = frameState.camera; + var frustum = camera.frustum; + var context = frameState.context; + var height = context.drawingBufferHeight; + + var error; + if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { + if (defined(frustum._offCenterFrustum)) { + frustum = frustum._offCenterFrustum; + } + var width = context.drawingBufferWidth; + var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); + error = geometricError / pixelSize; + } else { + var distance = Math.max(tile._distanceToCamera, CesiumMath.EPSILON7); + var sseDenominator = camera.frustum.sseDenominator; + error = (geometricError * height) / (distance * sseDenominator); + + if (tileset.dynamicScreenSpaceError) { + var density = tileset._dynamicScreenSpaceErrorComputedDensity; + var factor = tileset.dynamicScreenSpaceErrorFactor; + var dynamicError = CesiumMath.fog(distance, density) * factor; + error -= dynamicError; + } + } + + return error; + } + + function computeDistanceToCamera(children, frameState) { + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = children[i]; + child._distanceToCamera = child.distanceToTile(frameState); + child._centerZDepth = child.distanceToTileCenter(frameState); + } + } + + function updateTransforms(children, parentTransform) { + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = children[i]; + child.updateTransform(parentTransform); + } + } + + function isVisible(visibilityPlaneMask) { + return visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; + } + + function childrenAreVisible(tile) { + // optimization does not apply for additive refinement + return tile.refine === Cesium3DTileRefine.ADD || tile.children.length === 0 || tile._childrenVisibility & Cesium3DTileChildrenVisibility.VISIBLE !== 0; + } + + function hasAdditiveContent(tile) { + return tile.refine === Cesium3DTileRefine.ADD && tile.hasRenderableContent; + } + + function depthFirstSearch(root, options) { + var stack = options.stack; + + if (defined(root) && (!defined(options.shouldVisit) || options.shouldVisit(root))) { + stack.push(root); + } + + var maxLength = 0; + while (stack.length > 0) { + maxLength = Math.max(maxLength, stack.length); + + var tile = stack.pop(); + options.visitStart(tile); + var children = options.getChildren(tile); + var isNativeArray = !defined(children.get); + var length = children.length; + for (var i = 0; i < length; ++i) { + var child = isNativeArray ? children[i] : children.get(i); + + if (!defined(options.shouldVisit) || options.shouldVisit(child)) { + stack.push(child); + } + } + + if (length === 0 && defined(options.leafHandler)) { + options.leafHandler(tile); + } + options.visitEnd(tile); + } + + stack.trim(maxLength); + } + + function breadthFirstSearch(root, options) { + var queue1 = options.queue1; + var queue2 = options.queue2; + + if (defined(root) && (!defined(options.shouldVisit) || options.shouldVisit(root))) { + queue1.push(root); + } + + var maxLength = 0; + while (queue1.length > 0) { + var length = queue1.length; + maxLength = Math.max(maxLength, length); + + for (var i = 0; i < length; ++i) { + var tile = queue1.get(i); + options.visitStart(tile); + var children = options.getChildren(tile); + var isNativeArray = !defined(children.get); + var childrenLength = children.length; + for (var j = 0; j < childrenLength; ++j) { + var child = isNativeArray ? children[j] : children.get(j); + + if (!defined(options.shouldVisit) || options.shouldVisit(child)) { + queue2.push(child); + } + } + + if (childrenLength === 0 && defined(options.leafHandler)) { + options.leafHandler(tile); + } + options.visitEnd(tile); + } + + queue1.length = 0; + var temp = queue1; + queue1 = queue2; + queue2 = temp; + options.queue1 = queue1; + options.queue2 = queue2; + } + + queue1.length = 0; + queue2.length = 0; + + queue1.trim(maxLength); + queue2.trim(maxLength); + } + + Cesium3DTilesetTraversal.selectTiles = selectTiles; + + Cesium3DTilesetTraversal.BaseTraversal = BaseTraversal; + + Cesium3DTilesetTraversal.SkipTraversal = SkipTraversal; + + return Cesium3DTilesetTraversal; +}); diff --git a/Source/Scene/Composite3DTileContent.js b/Source/Scene/Composite3DTileContent.js new file mode 100644 index 000000000000..39b0cf0ab799 --- /dev/null +++ b/Source/Scene/Composite3DTileContent.js @@ -0,0 +1,310 @@ +/*global define*/ +define([ + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/FeatureDetection', + '../Core/getMagic', + '../Core/RuntimeError', + '../ThirdParty/when' + ], function( + defaultValue, + defined, + defineProperties, + destroyObject, + FeatureDetection, + getMagic, + RuntimeError, + when) { + 'use strict'; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + /** + * Represents the contents of a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Composite/README.md|Composite} + * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset. + *

+ * Implements the {@link Cesium3DTileContent} interface. + *

+ * + * @alias Composite3DTileContent + * @constructor + * + * @private + */ + function Composite3DTileContent(tileset, tile, url, arrayBuffer, byteOffset, factory) { + this._tileset = tileset; + this._tile = tile; + this._url = url; + this._contents = []; + this._readyPromise = when.defer(); + + initialize(this, arrayBuffer, byteOffset, factory); + } + + defineProperties(Composite3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + featurePropertiesDirty : { + get : function() { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + if (contents[i].featurePropertiesDirty) { + return true; + } + } + + return false; + }, + set : function(value) { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + contents[i].featurePropertiesDirty = value; + } + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call featuresLength for a tile in the composite. + */ + featuresLength : { + get : function() { + return 0; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call pointsLength for a tile in the composite. + */ + pointsLength : { + get : function() { + return 0; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call trianglesLength for a tile in the composite. + */ + trianglesLength : { + get : function() { + return 0; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call geometryByteLength for a tile in the composite. + */ + geometryByteLength : { + get : function() { + return 0; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call texturesByteLength for a tile in the composite. + */ + texturesByteLength : { + get : function() { + return 0; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns 0. Instead call batchTableByteLength for a tile in the composite. + */ + batchTableByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return this._contents; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns undefined. Instead call batchTable for a tile in the composite. + */ + batchTable : { + get : function() { + return undefined; + } + } + }); + + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + + function initialize(content, arrayBuffer, byteOffset, factory) { + byteOffset = defaultValue(byteOffset, 0); + + var uint8Array = new Uint8Array(arrayBuffer); + var view = new DataView(arrayBuffer); + byteOffset += sizeOfUint32; // Skip magic + + var version = view.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError('Only Composite Tile version 1 is supported. Version ' + version + ' is not.'); + } + byteOffset += sizeOfUint32; + + // Skip byteLength + byteOffset += sizeOfUint32; + + var tilesLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var contentPromises = []; + + for (var i = 0; i < tilesLength; ++i) { + var tileType = getMagic(uint8Array, byteOffset); + + // Tile byte length is stored after magic and version + var tileByteLength = view.getUint32(byteOffset + sizeOfUint32 * 2, true); + + var contentFactory = factory[tileType]; + + if (defined(contentFactory)) { + var innerContent = contentFactory(content._tileset, content._tile, content._url, arrayBuffer, byteOffset); + content._contents.push(innerContent); + contentPromises.push(innerContent.readyPromise); + } else { + throw new RuntimeError('Unknown tile content type, ' + tileType + ', inside Composite tile'); + } + + byteOffset += tileByteLength; + } + + when.all(contentPromises).then(function() { + content._readyPromise.resolve(content); + }).otherwise(function(error) { + content._readyPromise.reject(error); + }); + } + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns false. Instead call hasProperty for a tile in the composite. + */ + Composite3DTileContent.prototype.hasProperty = function(batchId, name) { + return false; + }; + + /** + * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent + * always returns undefined. Instead call getFeature for a tile in the composite. + */ + Composite3DTileContent.prototype.getFeature = function(batchId) { + return undefined; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + Composite3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + contents[i].applyDebugSettings(enabled, color); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + Composite3DTileContent.prototype.applyStyle = function(frameState, style) { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + contents[i].applyStyle(frameState, style); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#update + */ + Composite3DTileContent.prototype.update = function(tileset, frameState) { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + contents[i].update(tileset, frameState); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + Composite3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + Composite3DTileContent.prototype.destroy = function() { + var contents = this._contents; + var length = contents.length; + for (var i = 0; i < length; ++i) { + contents[i].destroy(); + } + return destroyObject(this); + }; + + return Composite3DTileContent; +}); diff --git a/Source/Scene/ConditionsExpression.js b/Source/Scene/ConditionsExpression.js new file mode 100644 index 000000000000..21f1a7eaaa32 --- /dev/null +++ b/Source/Scene/ConditionsExpression.js @@ -0,0 +1,188 @@ +/*global define*/ +define([ + '../Core/clone', + '../Core/defined', + '../Core/defineProperties', + './Expression' + ], function( + clone, + defined, + defineProperties, + Expression) { + 'use strict'; + + /** + * An expression for a style applied to a {@link Cesium3DTileset}. + *

+ * Evaluates a conditions expression defined using the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}. + *

+ *

+ * Implements the {@link StyleExpression} interface. + *

+ * + * @alias ConditionsExpression + * @constructor + * + * @param {Object} [conditionsExpression] The conditions expression defined using the 3D Tiles Styling language. + * @param {Object} [defines] Defines in the style. + * + * @example + * var expression = new Cesium.ConditionsExpression({ + * conditions : [ + * ['${Area} > 10, 'color("#FF0000")'], + * ['${id} !== "1"', 'color("#00FF00")'], + * ['true', 'color("#FFFFFF")'] + * ] + * }); + * expression.evaluateColor(frameState, feature, result); // returns a Cesium.Color object + */ + function ConditionsExpression(conditionsExpression, defines) { + this._conditionsExpression = clone(conditionsExpression, true); + this._conditions = conditionsExpression.conditions; + this._runtimeConditions = undefined; + + setRuntime(this, defines); + } + + defineProperties(ConditionsExpression.prototype, { + /** + * Gets the conditions expression defined in the 3D Tiles Styling language. + * + * @memberof ConditionsExpression.prototype + * + * @type {Object} + * @readonly + * + * @default undefined + */ + conditionsExpression : { + get : function() { + return this._conditionsExpression; + } + } + }); + + function Statement(condition, expression) { + this.condition = condition; + this.expression = expression; + } + + function setRuntime(expression, defines) { + var runtimeConditions = []; + var conditions = expression._conditions; + if (!defined(conditions)) { + return; + } + var length = conditions.length; + for (var i = 0; i < length; ++i) { + var statement = conditions[i]; + var cond = String(statement[0]); + var condExpression = String(statement[1]); + runtimeConditions.push(new Statement( + new Expression(cond, defines), + new Expression(condExpression, defines) + )); + } + expression._runtimeConditions = runtimeConditions; + } + + /** + * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of + * the expression in the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language} + * is of type Boolean, Number, or String, the corresponding JavaScript + * primitive type will be returned. If the result is a RegExp, a Javascript RegExp + * object will be returned. If the result is a Cartesian2, Cartesian3, or Cartesian4, + * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the result argument is + * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned. + * + * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Object} [result] The object onto which to store the result. + * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression. + */ + ConditionsExpression.prototype.evaluate = function(frameState, feature, result) { + var conditions = this._runtimeConditions; + if (!defined(conditions)) { + return undefined; + } + var length = conditions.length; + for (var i = 0; i < length; ++i) { + var statement = conditions[i]; + if (statement.condition.evaluate(frameState, feature)) { + return statement.expression.evaluate(frameState, feature, result); + } + } + }; + + /** + * Evaluates the result of a Color expression, using the values defined by a feature. + *

+ * This is equivalent to {@link ConditionsExpression#evaluate} but always returns a {@link Color} object. + *

+ * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Color} [result] The object in which to store the result + * @returns {Color} The modified result parameter or a new Color instance if one was not provided. + */ + ConditionsExpression.prototype.evaluateColor = function(frameState, feature, result) { + var conditions = this._runtimeConditions; + if (!defined(conditions)) { + return undefined; + } + var length = conditions.length; + for (var i = 0; i < length; ++i) { + var statement = conditions[i]; + if (statement.condition.evaluate(frameState, feature)) { + return statement.expression.evaluateColor(frameState, feature, result); + } + } + }; + + /** + * Gets the shader function for this expression. + * Returns undefined if the shader function can't be generated from this expression. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * @param {String} returnType The return type of the generated function. + * + * @returns {String} The shader function. + * + * @private + */ + ConditionsExpression.prototype.getShaderFunction = function(functionName, attributePrefix, shaderState, returnType) { + var conditions = this._runtimeConditions; + if (!defined(conditions) || conditions.length === 0) { + return undefined; + } + + var shaderFunction = ''; + var length = conditions.length; + for (var i = 0; i < length; ++i) { + var statement = conditions[i]; + + var condition = statement.condition.getShaderExpression(attributePrefix, shaderState); + var expression = statement.expression.getShaderExpression(attributePrefix, shaderState); + + // Build the if/else chain from the list of conditions + shaderFunction += + ' ' + ((i === 0) ? 'if' : 'else if') + ' (' + condition + ') \n' + + ' { \n' + + ' return ' + expression + '; \n' + + ' } \n'; + } + + shaderFunction = returnType + ' ' + functionName + '() \n' + + '{ \n' + + shaderFunction + + ' return ' + returnType + '(1.0); \n' + // Return a default value if no conditions are met + '} \n'; + + return shaderFunction; + }; + + return ConditionsExpression; +}); diff --git a/Source/Scene/CullingVolume.js b/Source/Scene/CullingVolume.js index fac7043f9980..5c350c946495 100644 --- a/Source/Scene/CullingVolume.js +++ b/Source/Scene/CullingVolume.js @@ -43,7 +43,7 @@ define([ var scratchPlaneCenter = new Cartesian3(); var scratchPlaneNormal = new Cartesian3(); - var scratchPlane = new Plane(new Cartesian3(), 0.0); + var scratchPlane = new Plane(new Cartesian3(1.0, 0.0, 0.0), 0.0); /** * Constructs a culling volume from a bounding sphere. Creates six planes that create a box containing the sphere. diff --git a/Source/Scene/DebugCameraPrimitive.js b/Source/Scene/DebugCameraPrimitive.js index 90030e73a2f9..b5f4cb09f24a 100644 --- a/Source/Scene/DebugCameraPrimitive.js +++ b/Source/Scene/DebugCameraPrimitive.js @@ -151,7 +151,6 @@ define([ for (var i = 0; i < 4; ++i) { var corner = Cartesian4.clone(frustumCornersNDC[i], scratchFrustumCorners[i]); - var worldCoords; if (!defined(inverseViewProjection)) { if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; @@ -171,7 +170,7 @@ define([ corner.z = (corner.z * (near - far) - near - far) * 0.5; corner.w = 1.0; - worldCoords = Matrix4.multiplyByVector(inverseView, corner, corner); + Matrix4.multiplyByVector(inverseView, corner, corner); } else { corner = Matrix4.multiplyByVector(inverseViewProjection, corner, corner); diff --git a/Source/Scene/Empty3DTileContent.js b/Source/Scene/Empty3DTileContent.js new file mode 100644 index 000000000000..f463c70d9ce1 --- /dev/null +++ b/Source/Scene/Empty3DTileContent.js @@ -0,0 +1,192 @@ +/*global define*/ +define([ + '../Core/defineProperties', + '../Core/destroyObject' + ], function( + defineProperties, + destroyObject) { + 'use strict'; + + /** + * Represents empty content for tiles in a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset that + * do not have content, e.g., because they are used to optimize hierarchical culling. + *

+ * Implements the {@link Cesium3DTileContent} interface. + *

+ * + * @alias Empty3DTileContent + * @constructor + * + * @private + */ + function Empty3DTileContent(tileset, tile) { + this._tileset = tileset; + this._tile = tile; + + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + this.featurePropertiesDirty = false; + } + + defineProperties(Empty3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featuresLength + */ + featuresLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#pointsLength + */ + pointsLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#trianglesLength + */ + trianglesLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#geometryByteLength + */ + geometryByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#texturesByteLength + */ + texturesByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTableByteLength + */ + batchTableByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url: { + get: function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTable + */ + batchTable : { + get : function() { + return undefined; + } + } + }); + + /** + * Part of the {@link Cesium3DTileContent} interface. Empty3DTileContent + * always returns false since a tile of this type does not have any features. + */ + Empty3DTileContent.prototype.hasProperty = function(batchId, name) { + return false; + }; + + /** + * Part of the {@link Cesium3DTileContent} interface. Empty3DTileContent + * always returns undefined since a tile of this type does not have any features. + */ + Empty3DTileContent.prototype.getFeature = function(batchId) { + return undefined; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + Empty3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + Empty3DTileContent.prototype.applyStyle = function(frameState, style) { + }; + + /** + * @inheritdoc Cesium3DTileContent#update + */ + Empty3DTileContent.prototype.update = function(tileset, frameState) { + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + Empty3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + Empty3DTileContent.prototype.destroy = function() { + return destroyObject(this); + }; + + return Empty3DTileContent; +}); diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js new file mode 100644 index 000000000000..d00383c5b9ba --- /dev/null +++ b/Source/Scene/Expression.js @@ -0,0 +1,1742 @@ +/*global define*/ +define([ + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/Check', + '../Core/Color', + '../Core/defined', + '../Core/defineProperties', + '../Core/isArray', + '../Core/Math', + '../Core/RuntimeError', + '../ThirdParty/jsep', + './ExpressionNodeType' + ], function( + Cartesian2, + Cartesian3, + Cartesian4, + Check, + Color, + defined, + defineProperties, + isArray, + CesiumMath, + RuntimeError, + jsep, + ExpressionNodeType) { + 'use strict'; + + /** + * An expression for a style applied to a {@link Cesium3DTileset}. + *

+ * Evaluates an expression defined using the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}. + *

+ *

+ * Implements the {@link StyleExpression} interface. + *

+ * + * @alias Expression + * @constructor + * + * @param {String} [expression] The expression defined using the 3D Tiles Styling language. + * @param {Object} [defines] Defines in the style. + * + * @example + * var expression = new Cesium.Expression('(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)'); + * expression.evaluate(frameState, feature); // returns true or false depending on the feature's properties + * + * @example + * var expression = new Cesium.Expression('(${Temperature} > 90) ? color("red") : color("white")'); + * expression.evaluateColor(frameState, feature, result); // returns a Cesium.Color object + */ + function Expression(expression, defines) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('expression', expression); + //>>includeEnd('debug'); + + this._expression = expression; + expression = replaceDefines(expression, defines); + expression = replaceVariables(removeBackslashes(expression)); + + // customize jsep operators + jsep.addBinaryOp('=~', 0); + jsep.addBinaryOp('!~', 0); + + var ast; + try { + ast = jsep(expression); + } catch (e) { + throw new RuntimeError(e); + } + + this._runtimeAst = createRuntimeAst(this, ast); + } + + defineProperties(Expression.prototype, { + /** + * Gets the expression defined in the 3D Tiles Styling language. + * + * @memberof Expression.prototype + * + * @type {String} + * @readonly + * + * @default undefined + */ + expression : { + get : function() { + return this._expression; + } + } + }); + + // Scratch storage manager while evaluating deep expressions. + // For example, an expression like dot(vec4(${red}), vec4(${green}) * vec4(${blue}) requires 3 scratch Cartesian4's + var scratchStorage = { + arrayIndex : 0, + arrayArray : [[]], + cartesian2Index : 0, + cartesian3Index : 0, + cartesian4Index : 0, + cartesian2Array : [new Cartesian2()], + cartesian3Array : [new Cartesian3()], + cartesian4Array : [new Cartesian4()], + reset : function() { + this.arrayIndex = 0; + this.cartesian2Index = 0; + this.cartesian3Index = 0; + this.cartesian4Index = 0; + }, + getArray : function() { + if (this.arrayIndex >= this.arrayArray.length) { + this.arrayArray.push([]); + } + var array = this.arrayArray[this.arrayIndex++]; + array.length = 0; + return array; + }, + getCartesian2 : function() { + if (this.cartesian2Index >= this.cartesian2Array.length) { + this.cartesian2Array.push(new Cartesian2()); + } + return this.cartesian2Array[this.cartesian2Index++]; + }, + getCartesian3 : function() { + if (this.cartesian3Index >= this.cartesian3Array.length) { + this.cartesian3Array.push(new Cartesian3()); + } + return this.cartesian3Array[this.cartesian3Index++]; + }, + getCartesian4 : function() { + if (this.cartesian4Index >= this.cartesian4Array.length) { + this.cartesian4Array.push(new Cartesian4()); + } + return this.cartesian4Array[this.cartesian4Index++]; + } + }; + + /** + * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of + * the expression in the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language} + * is of type Boolean, Number, or String, the corresponding JavaScript + * primitive type will be returned. If the result is a RegExp, a Javascript RegExp + * object will be returned. If the result is a Cartesian2, Cartesian3, or Cartesian4, + * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the result argument is + * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned. + * + * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Object} [result] The object onto which to store the result. + * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression. + */ + Expression.prototype.evaluate = function(frameState, feature, result) { + scratchStorage.reset(); + var value = this._runtimeAst.evaluate(frameState, feature); + if ((result instanceof Color) && (value instanceof Cartesian4)) { + return Color.fromCartesian4(value, result); + } + if ((value instanceof Cartesian2) || (value instanceof Cartesian3) || (value instanceof Cartesian4)) { + return value.clone(result); + } + return value; + }; + + /** + * Evaluates the result of a Color expression, optionally using the provided feature's properties. + *

+ * This is equivalent to {@link Expression#evaluate} but always returns a {@link Color} object. + *

+ * + * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Color} [result] The object in which to store the result + * @returns {Color} The modified result parameter or a new Color instance if one was not provided. + */ + Expression.prototype.evaluateColor = function(frameState, feature, result) { + scratchStorage.reset(); + var color = this._runtimeAst.evaluate(frameState, feature); + return Color.fromCartesian4(color, result); + }; + + /** + * Gets the shader function for this expression. + * Returns undefined if the shader function can't be generated from this expression. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * @param {String} returnType The return type of the generated function. + * + * @returns {String} The shader function. + * + * @private + */ + Expression.prototype.getShaderFunction = function(functionName, attributePrefix, shaderState, returnType) { + var shaderExpression = this.getShaderExpression(attributePrefix, shaderState); + + shaderExpression = returnType + ' ' + functionName + '() \n' + + '{ \n' + + ' return ' + shaderExpression + '; \n' + + '} \n'; + + return shaderExpression; + }; + + /** + * Gets the shader expression for this expression. + * Returns undefined if the shader expression can't be generated from this expression. + * + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * + * @returns {String} The shader expression. + * + * @private + */ + Expression.prototype.getShaderExpression = function(attributePrefix, shaderState) { + return this._runtimeAst.getShaderExpression(attributePrefix, shaderState); + }; + + var unaryOperators = ['!', '-', '+']; + var binaryOperators = ['+', '-', '*', '/', '%', '===', '!==', '>', '>=', '<', '<=', '&&', '||', '!~', '=~']; + + var variableRegex = /\${(.*?)}/g; // Matches ${variable_name} + var backslashRegex = /\\/g; + var backslashReplacement = '@#%'; + var replacementRegex = /@#%/g; + + var scratchColor = new Color(); + + var unaryFunctions = { + abs : getEvaluateUnaryComponentwise(Math.abs), + sqrt : getEvaluateUnaryComponentwise(Math.sqrt), + cos : getEvaluateUnaryComponentwise(Math.cos), + sin : getEvaluateUnaryComponentwise(Math.sin), + tan : getEvaluateUnaryComponentwise(Math.tan), + acos : getEvaluateUnaryComponentwise(Math.acos), + asin : getEvaluateUnaryComponentwise(Math.asin), + atan : getEvaluateUnaryComponentwise(Math.atan), + radians : getEvaluateUnaryComponentwise(CesiumMath.toRadians), + degrees : getEvaluateUnaryComponentwise(CesiumMath.toDegrees), + sign : getEvaluateUnaryComponentwise(CesiumMath.sign), + floor : getEvaluateUnaryComponentwise(Math.floor), + ceil : getEvaluateUnaryComponentwise(Math.ceil), + round : getEvaluateUnaryComponentwise(Math.round), + exp : getEvaluateUnaryComponentwise(Math.exp), + exp2 : getEvaluateUnaryComponentwise(exp2), + log : getEvaluateUnaryComponentwise(Math.log), + log2 : getEvaluateUnaryComponentwise(log2), + fract : getEvaluateUnaryComponentwise(fract), + length : length, + normalize: normalize + }; + + var binaryFunctions = { + atan2 : getEvaluateBinaryCommponentwise(Math.atan2, false), + pow : getEvaluateBinaryCommponentwise(Math.pow, false), + min : getEvaluateBinaryCommponentwise(Math.min, true), + max : getEvaluateBinaryCommponentwise(Math.max, true), + distance : distance, + dot : dot, + cross : cross + }; + + var ternaryFunctions = { + clamp : getEvaluateTernaryCommponentwise(CesiumMath.clamp, true), + mix : getEvaluateTernaryCommponentwise(CesiumMath.lerp, true) + }; + + function fract(number) { + return number - Math.floor(number); + } + + function exp2(exponent) { + return Math.pow(2.0,exponent); + } + + function log2(number) { + return CesiumMath.logBase(number, 2.0); + } + + function getEvaluateUnaryComponentwise(operation) { + return function(call, left) { + if (typeof left === 'number') { + return operation(left); + } else if (left instanceof Cartesian2) { + return Cartesian2.fromElements(operation(left.x), operation(left.y), scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3) { + return Cartesian3.fromElements(operation(left.x), operation(left.y), operation(left.z), scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4) { + return Cartesian4.fromElements(operation(left.x), operation(left.y), operation(left.z), operation(left.w), scratchStorage.getCartesian4()); + } + throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.'); + }; + } + + function getEvaluateBinaryCommponentwise(operation, allowScalar) { + return function(call, left, right) { + if (allowScalar && typeof right === 'number') { + if (typeof left === 'number') { + return operation(left, right); + } else if (left instanceof Cartesian2) { + return Cartesian2.fromElements(operation(left.x, right), operation(left.y, right), scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3) { + return Cartesian3.fromElements(operation(left.x, right), operation(left.y, right), operation(left.z, right), scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4) { + return Cartesian4.fromElements(operation(left.x, right), operation(left.y, right), operation(left.z, right), operation(left.w, right), scratchStorage.getCartesian4()); + } + } + + if (typeof left === 'number' && typeof right === 'number') { + return operation(left, right); + } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { + return Cartesian2.fromElements(operation(left.x, right.x), operation(left.y, right.y), scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { + return Cartesian3.fromElements(operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { + return Cartesian4.fromElements(operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), operation(left.w, right.w), scratchStorage.getCartesian4()); + } + + throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.'); + }; + } + + function getEvaluateTernaryCommponentwise(operation, allowScalar) { + return function(call, left, right, test) { + if (allowScalar && typeof test === 'number') { + if (typeof left === 'number' && typeof right === 'number') { + return operation(left, right, test); + } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { + return Cartesian2.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { + return Cartesian3.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { + return Cartesian4.fromElements(operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), operation(left.w, right.w, test), scratchStorage.getCartesian4()); + } + } + + if (typeof left === 'number' && typeof right === 'number' && typeof test === 'number') { + return operation(left, right, test); + } else if (left instanceof Cartesian2 && right instanceof Cartesian2 && test instanceof Cartesian2) { + return Cartesian2.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3 && right instanceof Cartesian3 && test instanceof Cartesian3) { + return Cartesian3.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4 && right instanceof Cartesian4 && test instanceof Cartesian4) { + return Cartesian4.fromElements(operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), operation(left.w, right.w, test.w), scratchStorage.getCartesian4()); + } + + throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ', ' + right + ', and ' + test + '.'); + }; + } + + function length(call, left) { + if (typeof left === 'number') { + return Math.abs(left); + } else if (left instanceof Cartesian2) { + return Cartesian2.magnitude(left); + } else if (left instanceof Cartesian3) { + return Cartesian3.magnitude(left); + } else if (left instanceof Cartesian4) { + return Cartesian4.magnitude(left); + } + + throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.'); + } + + function normalize(call, left) { + if (typeof left === 'number') { + return 1.0; + } else if (left instanceof Cartesian2) { + return Cartesian2.normalize(left, scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3) { + return Cartesian3.normalize(left, scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4) { + return Cartesian4.normalize(left, scratchStorage.getCartesian4()); + } + + throw new RuntimeError('Function "' + call + '" requires a vector or number argument. Argument is ' + left + '.'); + } + + function distance(call, left, right) { + if (typeof left === 'number' && typeof right === 'number') { + return Math.abs(left - right); + } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { + return Cartesian2.distance(left, right); + } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { + return Cartesian3.distance(left, right); + } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { + return Cartesian4.distance(left, right); + } + + throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.'); + } + + function dot(call, left, right) { + if (typeof left === 'number' && typeof right === 'number') { + return left * right; + } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { + return Cartesian2.dot(left, right); + } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { + return Cartesian3.dot(left, right); + } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { + return Cartesian4.dot(left, right); + } + + throw new RuntimeError('Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.'); + } + + function cross(call, left, right) { + if (left instanceof Cartesian3 && right instanceof Cartesian3) { + return Cartesian3.cross(left, right, scratchStorage.getCartesian3()); + } + + throw new RuntimeError('Function "' + call + '" requires vec3 arguments. Arguments are ' + left + ' and ' + right + '.'); + } + + function Node(type, value, left, right, test) { + this._type = type; + this._value = value; + this._left = left; + this._right = right; + this._test = test; + this.evaluate = undefined; + + setEvaluateFunction(this); + } + + function replaceDefines(expression, defines) { + if (!defined(defines)) { + return expression; + } + for (var key in defines) { + if (defines.hasOwnProperty(key)) { + var definePlaceholder = new RegExp('\\$\\{' + key + '\\}', 'g'); + var defineReplace = '(' + defines[key] + ')'; + if (defined(defineReplace)) { + expression = expression.replace(definePlaceholder, defineReplace); + } + } + } + return expression; + } + + function removeBackslashes(expression) { + return expression.replace(backslashRegex, backslashReplacement); + } + + function replaceBackslashes(expression) { + return expression.replace(replacementRegex, '\\'); + } + + function replaceVariables(expression) { + var exp = expression; + var result = ''; + var i = exp.indexOf('${'); + while (i >= 0) { + // Check if string is inside quotes + var openSingleQuote = exp.indexOf('\''); + var openDoubleQuote = exp.indexOf('"'); + var closeQuote; + if (openSingleQuote >= 0 && openSingleQuote < i) { + closeQuote = exp.indexOf('\'', openSingleQuote + 1); + result += exp.substr(0, closeQuote + 1); + exp = exp.substr(closeQuote + 1); + i = exp.indexOf('${'); + } else if (openDoubleQuote >= 0 && openDoubleQuote < i) { + closeQuote = exp.indexOf('"', openDoubleQuote + 1); + result += exp.substr(0, closeQuote + 1); + exp = exp.substr(closeQuote + 1); + i = exp.indexOf('${'); + } else { + result += exp.substr(0, i); + var j = exp.indexOf('}'); + if (j < 0) { + throw new RuntimeError('Unmatched {.'); + } + result += "czm_" + exp.substr(i + 2, j - (i + 2)); + exp = exp.substr(j + 1); + i = exp.indexOf('${'); + } + } + result += exp; + return result; + } + + function parseLiteral(ast) { + var type = typeof ast.value; + if (ast.value === null) { + return new Node(ExpressionNodeType.LITERAL_NULL, null); + } else if (type === 'boolean') { + return new Node(ExpressionNodeType.LITERAL_BOOLEAN, ast.value); + } else if (type === 'number') { + return new Node(ExpressionNodeType.LITERAL_NUMBER, ast.value); + } else if (type === 'string') { + if (ast.value.indexOf('${') >= 0) { + return new Node(ExpressionNodeType.VARIABLE_IN_STRING, ast.value); + } + return new Node(ExpressionNodeType.LITERAL_STRING, replaceBackslashes(ast.value)); + } + } + + function parseCall(expression, ast) { + var args = ast.arguments; + var argsLength = args.length; + var call; + var val, left, right; + + // Member function calls + if (ast.callee.type === 'MemberExpression') { + call = ast.callee.property.name; + var object = ast.callee.object; + if (call === 'test' || call === 'exec') { + // Make sure this is called on a valid type + if (object.callee.name !== 'regExp') { + throw new RuntimeError(call + ' is not a function.'); + } + if (argsLength === 0) { + if (call === 'test') { + return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false); + } + return new Node(ExpressionNodeType.LITERAL_NULL, null); + } + left = createRuntimeAst(expression, object); + right = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.FUNCTION_CALL, call, left, right); + } else if (call === 'toString') { + val = createRuntimeAst(expression, object); + return new Node(ExpressionNodeType.FUNCTION_CALL, call, val); + } + + throw new RuntimeError('Unexpected function call "' + call + '".'); + } + + // Non-member function calls + call = ast.callee.name; + if (call === 'color') { + if (argsLength === 0) { + return new Node(ExpressionNodeType.LITERAL_COLOR, call); + } + val = createRuntimeAst(expression, args[0]); + if (defined(args[1])) { + var alpha = createRuntimeAst(expression, args[1]); + return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val, alpha]); + } + return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val]); + } else if (call === 'rgb' || call === 'hsl') { + if (argsLength < 3) { + throw new RuntimeError(call + ' requires three arguments.'); + } + val = [ + createRuntimeAst(expression, args[0]), + createRuntimeAst(expression, args[1]), + createRuntimeAst(expression, args[2]) + ]; + return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); + } else if (call === 'rgba' || call === 'hsla') { + if (argsLength < 4) { + throw new RuntimeError(call + ' requires four arguments.'); + } + val = [ + createRuntimeAst(expression, args[0]), + createRuntimeAst(expression, args[1]), + createRuntimeAst(expression, args[2]), + createRuntimeAst(expression, args[3]) + ]; + return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); + } else if (call === 'vec2' || call === 'vec3' || call === 'vec4') { + // Check for invalid constructors at evaluation time + val = new Array(argsLength); + for (var i = 0; i < argsLength; ++i) { + val[i] = createRuntimeAst(expression, args[i]); + } + return new Node(ExpressionNodeType.LITERAL_VECTOR, call, val); + } else if (call === 'isNaN' || call === 'isFinite') { + if (argsLength === 0) { + if (call === 'isNaN') { + return new Node(ExpressionNodeType.LITERAL_BOOLEAN, true); + } + return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (call === 'isExactClass' || call === 'isClass') { + if (argsLength < 1 || argsLength > 1) { + throw new RuntimeError(call + ' requires exactly one argument.'); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (call === 'getExactClassName') { + if (argsLength > 0) { + throw new RuntimeError(call + ' does not take any argument.'); + } + return new Node(ExpressionNodeType.UNARY, call); + } else if (defined(unaryFunctions[call])) { + if (argsLength !== 1) { + throw new RuntimeError(call + ' requires exactly one argument.'); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (defined(binaryFunctions[call])) { + if (argsLength !== 2) { + throw new RuntimeError(call + ' requires exactly two arguments.'); + } + left = createRuntimeAst(expression, args[0]); + right = createRuntimeAst(expression, args[1]); + return new Node(ExpressionNodeType.BINARY, call, left, right); + } else if (defined(ternaryFunctions[call])) { + if (argsLength !== 3) { + throw new RuntimeError(call + ' requires exactly three arguments.'); + } + left = createRuntimeAst(expression, args[0]); + right = createRuntimeAst(expression, args[1]); + var test = createRuntimeAst(expression, args[2]); + return new Node(ExpressionNodeType.TERNARY, call, left, right, test); + } else if (call === 'Boolean') { + if (argsLength === 0) { + return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (call === 'Number') { + if (argsLength === 0) { + return new Node(ExpressionNodeType.LITERAL_NUMBER, 0); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (call === 'String') { + if (argsLength === 0) { + return new Node(ExpressionNodeType.LITERAL_STRING, ''); + } + val = createRuntimeAst(expression, args[0]); + return new Node(ExpressionNodeType.UNARY, call, val); + } else if (call === 'regExp') { + return parseRegex(expression, ast); + } + + throw new RuntimeError('Unexpected function call "' + call + '".'); + } + + function parseRegex(expression, ast) { + var args = ast.arguments; + // no arguments, return default regex + if (args.length === 0) { + return new Node(ExpressionNodeType.LITERAL_REGEX, new RegExp()); + } + + var pattern = createRuntimeAst(expression, args[0]); + var exp; + + // optional flag argument supplied + if (args.length > 1) { + var flags = createRuntimeAst(expression, args[1]); + if (isLiteralType(pattern) && isLiteralType(flags)) { + try { + exp = new RegExp(replaceBackslashes(String(pattern._value)), flags._value); + } catch (e) { + throw new RuntimeError(e); + } + return new Node(ExpressionNodeType.LITERAL_REGEX, exp); + } + return new Node(ExpressionNodeType.REGEX, pattern, flags); + } + + // only pattern argument supplied + if (isLiteralType(pattern)) { + try { + exp = new RegExp(replaceBackslashes(String(pattern._value))); + } catch (e) { + throw new RuntimeError(e); + } + return new Node(ExpressionNodeType.LITERAL_REGEX, exp); + } + return new Node(ExpressionNodeType.REGEX, pattern); + } + + function parseKeywordsAndVariables(ast) { + if (isVariable(ast.name)) { + var name = getPropertyName(ast.name); + if (name.substr(0, 8) === 'tiles3d_') { + return new Node(ExpressionNodeType.BUILTIN_VARIABLE, name); + } + return new Node(ExpressionNodeType.VARIABLE, name); + } else if (ast.name === 'NaN') { + return new Node(ExpressionNodeType.LITERAL_NUMBER, NaN); + } else if (ast.name === 'Infinity') { + return new Node(ExpressionNodeType.LITERAL_NUMBER, Infinity); + } else if (ast.name === 'undefined') { + return new Node(ExpressionNodeType.LITERAL_UNDEFINED, undefined); + } + + throw new RuntimeError(ast.name + ' is not defined.'); + } + + function parseMathConstant(ast) { + var name = ast.property.name; + if (name === 'PI') { + return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.PI); + } else if (name === 'E') { + return new Node(ExpressionNodeType.LITERAL_NUMBER, Math.E); + } + } + + function parseMemberExpression(expression, ast) { + if (ast.object.name === 'Math') { + return parseMathConstant(ast); + } + + var val; + var obj = createRuntimeAst(expression, ast.object); + if (ast.computed) { + val = createRuntimeAst(expression, ast.property); + return new Node(ExpressionNodeType.MEMBER, 'brackets', obj, val); + } + + val = new Node(ExpressionNodeType.LITERAL_STRING, ast.property.name); + return new Node(ExpressionNodeType.MEMBER, 'dot', obj, val); + } + + function isLiteralType(node) { + return (node._type >= ExpressionNodeType.LITERAL_NULL); + } + + function isVariable(name) { + return (name.substr(0, 4) === 'czm_'); + } + + function getPropertyName(variable) { + return variable.substr(4); + } + + function createRuntimeAst(expression, ast) { + var node; + var op; + var left; + var right; + + if (ast.type === 'Literal') { + node = parseLiteral(ast); + } else if (ast.type === 'CallExpression') { + node = parseCall(expression, ast); + } else if (ast.type === 'Identifier') { + node = parseKeywordsAndVariables(ast); + } else if (ast.type === 'UnaryExpression') { + op = ast.operator; + var child = createRuntimeAst(expression, ast.argument); + if (unaryOperators.indexOf(op) > -1) { + node = new Node(ExpressionNodeType.UNARY, op, child); + } else { + throw new RuntimeError('Unexpected operator "' + op + '".'); + } + } else if (ast.type === 'BinaryExpression') { + op = ast.operator; + left = createRuntimeAst(expression, ast.left); + right = createRuntimeAst(expression, ast.right); + if (binaryOperators.indexOf(op) > -1) { + node = new Node(ExpressionNodeType.BINARY, op, left, right); + } else { + throw new RuntimeError('Unexpected operator "' + op + '".'); + } + } else if (ast.type === 'LogicalExpression') { + op = ast.operator; + left = createRuntimeAst(expression, ast.left); + right = createRuntimeAst(expression, ast.right); + if (binaryOperators.indexOf(op) > -1) { + node = new Node(ExpressionNodeType.BINARY, op, left, right); + } + } else if (ast.type === 'ConditionalExpression') { + var test = createRuntimeAst(expression, ast.test); + left = createRuntimeAst(expression, ast.consequent); + right = createRuntimeAst(expression, ast.alternate); + node = new Node(ExpressionNodeType.CONDITIONAL, '?', left, right, test); + } else if (ast.type === 'MemberExpression') { + node = parseMemberExpression(expression, ast); + } else if (ast.type === 'ArrayExpression') { + var val = []; + for (var i = 0; i < ast.elements.length; i++) { + val[i] = createRuntimeAst(expression, ast.elements[i]); + } + node = new Node(ExpressionNodeType.ARRAY, val); + } else if (ast.type === 'Compound') { + // empty expression or multiple expressions + throw new RuntimeError('Provide exactly one expression.'); + } else { + throw new RuntimeError('Cannot parse expression.'); + } + + return node; + } + + function setEvaluateFunction(node) { + if (node._type === ExpressionNodeType.CONDITIONAL) { + node.evaluate = node._evaluateConditional; + } else if (node._type === ExpressionNodeType.FUNCTION_CALL) { + if (node._value === 'test') { + node.evaluate = node._evaluateRegExpTest; + } else if (node._value === 'exec') { + node.evaluate = node._evaluateRegExpExec; + } else if (node._value === 'toString') { + node.evaluate = node._evaluateToString; + } + } else if (node._type === ExpressionNodeType.UNARY) { + if (node._value === '!') { + node.evaluate = node._evaluateNot; + } else if (node._value === '-') { + node.evaluate = node._evaluateNegative; + } else if (node._value === '+') { + node.evaluate = node._evaluatePositive; + } else if (node._value === 'isNaN') { + node.evaluate = node._evaluateNaN; + } else if (node._value === 'isFinite') { + node.evaluate = node._evaluateIsFinite; + } else if (node._value === 'isExactClass') { + node.evaluate = node._evaluateIsExactClass; + } else if (node._value === 'isClass') { + node.evaluate = node._evaluateIsClass; + } else if (node._value === 'getExactClassName') { + node.evaluate = node._evaluategetExactClassName; + } else if (node._value === 'Boolean') { + node.evaluate = node._evaluateBooleanConversion; + } else if (node._value === 'Number') { + node.evaluate = node._evaluateNumberConversion; + } else if (node._value === 'String') { + node.evaluate = node._evaluateStringConversion; + } else if (defined(unaryFunctions[node._value])) { + node.evaluate = getEvaluateUnaryFunction(node._value); + } + } else if (node._type === ExpressionNodeType.BINARY) { + if (node._value === '+') { + node.evaluate = node._evaluatePlus; + } else if (node._value === '-') { + node.evaluate = node._evaluateMinus; + } else if (node._value === '*') { + node.evaluate = node._evaluateTimes; + } else if (node._value === '/') { + node.evaluate = node._evaluateDivide; + } else if (node._value === '%') { + node.evaluate = node._evaluateMod; + } else if (node._value === '===') { + node.evaluate = node._evaluateEqualsStrict; + } else if (node._value === '!==') { + node.evaluate = node._evaluateNotEqualsStrict; + } else if (node._value === '<') { + node.evaluate = node._evaluateLessThan; + } else if (node._value === '<=') { + node.evaluate = node._evaluateLessThanOrEquals; + } else if (node._value === '>') { + node.evaluate = node._evaluateGreaterThan; + } else if (node._value === '>=') { + node.evaluate = node._evaluateGreaterThanOrEquals; + } else if (node._value === '&&') { + node.evaluate = node._evaluateAnd; + } else if (node._value === '||') { + node.evaluate = node._evaluateOr; + } else if (node._value === '=~') { + node.evaluate = node._evaluateRegExpMatch; + } else if (node._value === '!~') { + node.evaluate = node._evaluateRegExpNotMatch; + } else if (defined(binaryFunctions[node._value])) { + node.evaluate = getEvaluateBinaryFunction(node._value); + } + } else if (node._type === ExpressionNodeType.TERNARY) { + node.evaluate = getEvaluateTernaryFunction(node._value); + } else if (node._type === ExpressionNodeType.MEMBER) { + if (node._value === 'brackets') { + node.evaluate = node._evaluateMemberBrackets; + } else { + node.evaluate = node._evaluateMemberDot; + } + } else if (node._type === ExpressionNodeType.ARRAY) { + node.evaluate = node._evaluateArray; + } else if (node._type === ExpressionNodeType.VARIABLE) { + node.evaluate = node._evaluateVariable; + } else if (node._type === ExpressionNodeType.VARIABLE_IN_STRING) { + node.evaluate = node._evaluateVariableString; + } else if (node._type === ExpressionNodeType.LITERAL_COLOR) { + node.evaluate = node._evaluateLiteralColor; + } else if (node._type === ExpressionNodeType.LITERAL_VECTOR) { + node.evaluate = node._evaluateLiteralVector; + } else if (node._type === ExpressionNodeType.LITERAL_STRING) { + node.evaluate = node._evaluateLiteralString; + } else if (node._type === ExpressionNodeType.REGEX) { + node.evaluate = node._evaluateRegExp; + } else if (node._type === ExpressionNodeType.BUILTIN_VARIABLE) { + if (node._value === 'tiles3d_tileset_time') { + node.evaluate = evaluateTilesetTime; + } + } else { + node.evaluate = node._evaluateLiteral; + } + } + + function evaluateTilesetTime(frameState, feature) { + return feature.content.tileset.timeSinceLoad; + } + + function getEvaluateUnaryFunction(call) { + var evaluate = unaryFunctions[call]; + return function(feature) { + var left = this._left.evaluate(feature); + return evaluate(call, left); + }; + } + + function getEvaluateBinaryFunction(call) { + var evaluate = binaryFunctions[call]; + return function(feature) { + var left = this._left.evaluate(feature); + var right = this._right.evaluate(feature); + return evaluate(call, left, right); + }; + } + + function getEvaluateTernaryFunction(call) { + var evaluate = ternaryFunctions[call]; + return function(feature) { + var left = this._left.evaluate(feature); + var right = this._right.evaluate(feature); + var test = this._test.evaluate(feature); + return evaluate(call, left, right, test); + }; + } + + Node.prototype._evaluateLiteral = function(frameState, feature) { + return this._value; + }; + + Node.prototype._evaluateLiteralColor = function(frameState, feature) { + var color = scratchColor; + var args = this._left; + if (this._value === 'color') { + if (!defined(args)) { + Color.fromBytes(255, 255, 255, 255, color); + } else if (args.length > 1) { + Color.fromCssColorString(args[0].evaluate(frameState, feature), color); + color.alpha = args[1].evaluate(frameState, feature); + } else { + Color.fromCssColorString(args[0].evaluate(frameState, feature), color); + } + } else if (this._value === 'rgb') { + Color.fromBytes( + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + 255, color); + } else if (this._value === 'rgba') { + // convert between css alpha (0 to 1) and cesium alpha (0 to 255) + var a = args[3].evaluate(frameState, feature) * 255; + Color.fromBytes( + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + a, color); + } else if (this._value === 'hsl') { + Color.fromHsl( + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + 1.0, color); + } else if (this._value === 'hsla') { + Color.fromHsl( + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + args[3].evaluate(frameState, feature), + color); + } + return Cartesian4.fromColor(color, scratchStorage.getCartesian4()); + }; + + Node.prototype._evaluateLiteralVector = function(frameState, feature) { + // Gather the components that make up the vector, which includes components from interior vectors. + // For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid. + // + // If the number of components does not equal the vector's size, then a RuntimeError is thrown - with two exceptions: + // 1. A vector may be constructed from a larger vector and drop the extra components. + // 2. A vector may be constructed from a single component - vec3(1) will become vec3(1, 1, 1). + // + // Examples of invalid constructors include: + // vec4(1, 2) // not enough components + // vec3(vec2(1, 2)) // not enough components + // vec3(1, 2, 3, 4) // too many components + // vec2(vec4(1), 1) // too many components + + var components = scratchStorage.getArray(); + var call = this._value; + var args = this._left; + var argsLength = args.length; + for (var i = 0; i < argsLength; ++i) { + var value = args[i].evaluate(frameState, feature); + if (typeof value === 'number') { + components.push(value); + } else if (value instanceof Cartesian2) { + components.push(value.x, value.y); + } else if (value instanceof Cartesian3) { + components.push(value.x, value.y, value.z); + } else if (value instanceof Cartesian4) { + components.push(value.x, value.y, value.z, value.w); + } else { + throw new RuntimeError(call + ' argument must be a vector or number. Argument is ' + value + '.'); + } + } + + var componentsLength = components.length; + var vectorLength = parseInt(call.charAt(3)); + + if (componentsLength === 0) { + throw new RuntimeError('Invalid ' + call + ' constructor. No valid arguments.'); + } else if ((componentsLength < vectorLength) && (componentsLength > 1)) { + throw new RuntimeError('Invalid ' + call + ' constructor. Not enough arguments.'); + } else if ((componentsLength > vectorLength) && (argsLength > 1)) { + throw new RuntimeError('Invalid ' + call + ' constructor. Too many arguments.'); + } + + if (componentsLength === 1) { + // Add the same component 3 more times + var component = components[0]; + components.push(component, component, component); + } + + if (call === 'vec2') { + return Cartesian2.fromArray(components, 0, scratchStorage.getCartesian2()); + } else if (call === 'vec3') { + return Cartesian3.fromArray(components, 0, scratchStorage.getCartesian3()); + } else if (call === 'vec4') { + return Cartesian4.fromArray(components, 0, scratchStorage.getCartesian4()); + } + }; + + Node.prototype._evaluateLiteralString = function(frameState, feature) { + return this._value; + }; + + Node.prototype._evaluateVariableString = function(frameState, feature) { + var result = this._value; + var match = variableRegex.exec(result); + while (match !== null) { + var placeholder = match[0]; + var variableName = match[1]; + var property = feature.getProperty(variableName); + if (!defined(property)) { + property = ''; + } + result = result.replace(placeholder, property); + match = variableRegex.exec(result); + } + return result; + }; + + Node.prototype._evaluateVariable = function(frameState, feature) { + // evaluates to undefined if the property name is not defined for that feature + return feature.getProperty(this._value); + }; + + function checkFeature (ast) { + return (ast._value === 'feature'); + } + + // PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime + Node.prototype._evaluateMemberDot = function(frameState, feature) { + if (checkFeature(this._left)) { + return feature.getProperty(this._right.evaluate(frameState, feature)); + } + var property = this._left.evaluate(frameState, feature); + if (!defined(property)) { + return undefined; + } + + var member = this._right.evaluate(frameState, feature); + if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) { + // Vector components may be accessed with .r, .g, .b, .a and implicitly with .x, .y, .z, .w + if (member === 'r') { + return property.x; + } else if (member === 'g') { + return property.y; + } else if (member === 'b') { + return property.z; + } else if (member === 'a') { + return property.w; + } + } + return property[member]; + }; + + Node.prototype._evaluateMemberBrackets = function(frameState, feature) { + if (checkFeature(this._left)) { + return feature.getProperty(this._right.evaluate(frameState, feature)); + } + var property = this._left.evaluate(frameState, feature); + if (!defined(property)) { + return undefined; + } + + var member = this._right.evaluate(frameState, feature); + if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) { + // Vector components may be accessed with [0][1][2][3], ['r']['g']['b']['a'] and implicitly with ['x']['y']['z']['w'] + // For Cartesian2 and Cartesian3 out-of-range components will just return undefined + if (member === 0 || member === 'r') { + return property.x; + } else if (member === 1 || member === 'g') { + return property.y; + } else if (member === 2 || member === 'b') { + return property.z; + } else if (member === 3 || member === 'a') { + return property.w; + } + } + return property[member]; + }; + + Node.prototype._evaluateArray = function(frameState, feature) { + var array = []; + for (var i = 0; i < this._value.length; i++) { + array[i] = this._value[i].evaluate(frameState, feature); + } + return array; + }; + + // PERFORMANCE_IDEA: Have "fast path" functions that deal only with specific types + // that we can assign if we know the types before runtime + + Node.prototype._evaluateNot = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + if (typeof left !== 'boolean') { + throw new RuntimeError('Operator "!" requires a boolean argument. Argument is ' + left + '.'); + } + return !left; + }; + + Node.prototype._evaluateNegative = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + if (left instanceof Cartesian2) { + return Cartesian2.negate(left, scratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3) { + return Cartesian3.negate(left, scratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4) { + return Cartesian4.negate(left, scratchStorage.getCartesian4()); + } else if (typeof left === 'number') { + return -left; + } + + throw new RuntimeError('Operator "-" requires a vector or number argument. Argument is ' + left + '.'); + }; + + Node.prototype._evaluatePositive = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + + if (!((left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4) || (typeof left === 'number'))) { + throw new RuntimeError('Operator "+" requires a vector or number argument. Argument is ' + left + '.'); + } + + return left; + }; + + Node.prototype._evaluateLessThan = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((typeof left !== 'number') || (typeof right !== 'number')) { + throw new RuntimeError('Operator "<" requires number arguments. Arguments are ' + left + ' and ' + right + '.'); + } + + return left < right; + }; + + Node.prototype._evaluateLessThanOrEquals = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((typeof left !== 'number') || (typeof right !== 'number')) { + throw new RuntimeError('Operator "<=" requires number arguments. Arguments are ' + left + ' and ' + right + '.'); + } + + return left <= right; + }; + + Node.prototype._evaluateGreaterThan = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((typeof left !== 'number') || (typeof right !== 'number')) { + throw new RuntimeError('Operator ">" requires number arguments. Arguments are ' + left + ' and ' + right + '.'); + } + + return left > right; + }; + + Node.prototype._evaluateGreaterThanOrEquals = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((typeof left !== 'number') || (typeof right !== 'number')) { + throw new RuntimeError('Operator ">=" requires number arguments. Arguments are ' + left + ' and ' + right + '.'); + } + + return left >= right; + }; + + Node.prototype._evaluateOr = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + if (typeof left !== 'boolean') { + throw new RuntimeError('Operator "||" requires boolean arguments. First argument is ' + left + '.'); + } + + // short circuit the expression + if (left) { + return true; + } + + var right = this._right.evaluate(frameState, feature); + if (typeof right !== 'boolean') { + throw new RuntimeError('Operator "||" requires boolean arguments. Second argument is ' + right + '.'); + } + + return left || right; + }; + + Node.prototype._evaluateAnd = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + if (typeof left !== 'boolean') { + throw new RuntimeError('Operator "&&" requires boolean arguments. First argument is ' + left + '.'); + } + + // short circuit the expression + if (!left) { + return false; + } + + var right = this._right.evaluate(frameState, feature); + if (typeof right !== 'boolean') { + throw new RuntimeError('Operator "&&" requires boolean arguments. Second argument is ' + right + '.'); + } + + return left && right; + }; + + Node.prototype._evaluatePlus = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.add(left, right, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.add(left, right, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.add(left, right, scratchStorage.getCartesian4()); + } else if ((typeof left === 'string') || (typeof right === 'string')) { + // If only one argument is a string the other argument calls its toString function. + return left + right; + } else if ((typeof left === 'number') && (typeof right === 'number')) { + return left + right; + } + + throw new RuntimeError('Operator "+" requires vector or number arguments of matching types, or at least one string argument. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateMinus = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.subtract(left, right, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.subtract(left, right, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.subtract(left, right, scratchStorage.getCartesian4()); + } else if ((typeof left === 'number') && (typeof right === 'number')) { + return left - right; + } + + throw new RuntimeError('Operator "-" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateTimes = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.multiplyComponents(left, right, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian2) && (typeof left === 'number')) { + return Cartesian2.multiplyByScalar(right, left, scratchStorage.getCartesian2()); + } else if ((left instanceof Cartesian2) && (typeof right === 'number')) { + return Cartesian2.multiplyByScalar(left, right, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.multiplyComponents(left, right, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian3) && (typeof left === 'number')) { + return Cartesian3.multiplyByScalar(right, left, scratchStorage.getCartesian3()); + } else if ((left instanceof Cartesian3) && (typeof right === 'number')) { + return Cartesian3.multiplyByScalar(left, right, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.multiplyComponents(left, right, scratchStorage.getCartesian4()); + } else if ((right instanceof Cartesian4) && (typeof left === 'number')) { + return Cartesian4.multiplyByScalar(right, left, scratchStorage.getCartesian4()); + } else if ((left instanceof Cartesian4) && (typeof right === 'number')) { + return Cartesian4.multiplyByScalar(left, right, scratchStorage.getCartesian4()); + } else if ((typeof left === 'number') && (typeof right === 'number')) { + return left * right; + } + + throw new RuntimeError('Operator "*" requires vector or number arguments. If both arguments are vectors they must be matching types. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateDivide = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.divideComponents(left, right, scratchStorage.getCartesian2()); + } else if ((left instanceof Cartesian2) && (typeof right === 'number')) { + return Cartesian2.divideByScalar(left, right, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.divideComponents(left, right, scratchStorage.getCartesian3()); + } else if ((left instanceof Cartesian3) && (typeof right === 'number')) { + return Cartesian3.divideByScalar(left, right, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.divideComponents(left, right, scratchStorage.getCartesian4()); + } else if ((left instanceof Cartesian4) && (typeof right === 'number')) { + return Cartesian4.divideByScalar(left, right, scratchStorage.getCartesian4()); + } else if ((typeof left === 'number') && (typeof right === 'number')) { + return left / right; + } + + throw new RuntimeError('Operator "/" requires vector or number arguments of matching types, or a number as the second argument. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateMod = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.fromElements(left.x % right.x, left.y % right.y, scratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, scratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, scratchStorage.getCartesian4()); + } else if ((typeof left === 'number') && (typeof right === 'number')) { + return left % right; + } + + throw new RuntimeError('Operator "%" requires vector or number arguments of matching types. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateEqualsStrict = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return left.equals(right); + } + return left === right; + }; + + Node.prototype._evaluateNotEqualsStrict = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + if ((right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return !left.equals(right); + } + return left !== right; + }; + + Node.prototype._evaluateConditional = function(frameState, feature) { + var test = this._test.evaluate(frameState, feature); + + if (typeof test !== 'boolean') { + throw new RuntimeError('Conditional argument of conditional expression must be a boolean. Argument is ' + test + '.'); + } + + if (test) { + return this._left.evaluate(frameState, feature); + } + return this._right.evaluate(frameState, feature); + }; + + Node.prototype._evaluateNaN = function(frameState, feature) { + return isNaN(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateIsFinite = function(frameState, feature) { + return isFinite(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateIsExactClass = function(frameState, feature) { + return feature.isExactClass(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateIsClass = function(frameState, feature) { + return feature.isClass(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluategetExactClassName = function(frameState, feature) { + return feature.getExactClassName(); + }; + + Node.prototype._evaluateBooleanConversion = function(frameState, feature) { + return Boolean(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateNumberConversion = function(frameState, feature) { + return Number(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateStringConversion = function(frameState, feature) { + return String(this._left.evaluate(frameState, feature)); + }; + + Node.prototype._evaluateRegExp = function(frameState, feature) { + var pattern = this._value.evaluate(frameState, feature); + var flags = ''; + + if (defined(this._left)) { + flags = this._left.evaluate(frameState, feature); + } + + var exp; + try { + exp = new RegExp(pattern, flags); + } catch (e) { + throw new RuntimeError(e); + } + return exp; + }; + + Node.prototype._evaluateRegExpTest = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if (!((left instanceof RegExp) && (typeof right === 'string'))) { + throw new RuntimeError('RegExp.test requires the first argument to be a RegExp and the second argument to be a string. Arguments are ' + left + ' and ' + right + '.'); + } + + return left.test(right); + }; + + Node.prototype._evaluateRegExpMatch = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((left instanceof RegExp) && (typeof right === 'string')) { + return left.test(right); + } else if ((right instanceof RegExp) && (typeof left === 'string')) { + return right.test(left); + } + + throw new RuntimeError('Operator "=~" requires one RegExp argument and one string argument. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateRegExpNotMatch = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if ((left instanceof RegExp) && (typeof right === 'string')) { + return !(left.test(right)); + } else if ((right instanceof RegExp) && (typeof left === 'string')) { + return !(right.test(left)); + } + + throw new RuntimeError('Operator "!~" requires one RegExp argument and one string argument. Arguments are ' + left + ' and ' + right + '.'); + }; + + Node.prototype._evaluateRegExpExec = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + var right = this._right.evaluate(frameState, feature); + + if (!((left instanceof RegExp) && (typeof right === 'string'))) { + throw new RuntimeError('RegExp.exec requires the first argument to be a RegExp and the second argument to be a string. Arguments are ' + left + ' and ' + right + '.'); + } + + var exec = left.exec(right); + if (!defined(exec)) { + return null; + } + return exec[1]; + }; + + Node.prototype._evaluateToString = function(frameState, feature) { + var left = this._left.evaluate(frameState, feature); + if ((left instanceof RegExp) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) { + return String(left); + } + + throw new RuntimeError('Unexpected function call "' + this._value + '".'); + }; + + function convertHSLToRGB(ast) { + // Check if the color contains any nested expressions to see if the color can be converted here. + // E.g. "hsl(0.9, 0.6, 0.7)" is able to convert directly to rgb, "hsl(0.9, 0.6, ${Height})" is not. + var channels = ast._left; + var length = channels.length; + for (var i = 0; i < length; ++i) { + if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) { + return undefined; + } + } + var h = channels[0]._value; + var s = channels[1]._value; + var l = channels[2]._value; + var a = (length === 4) ? channels[3]._value : 1.0; + return Color.fromHsl(h, s, l, a, scratchColor); + } + + function convertRGBToColor(ast) { + // Check if the color contains any nested expressions to see if the color can be converted here. + // E.g. "rgb(255, 255, 255)" is able to convert directly to Color, "rgb(255, 255, ${Height})" is not. + var channels = ast._left; + var length = channels.length; + for (var i = 0; i < length; ++i) { + if (channels[i]._type !== ExpressionNodeType.LITERAL_NUMBER) { + return undefined; + } + } + var color = scratchColor; + color.red = channels[0]._value / 255.0; + color.green = channels[1]._value / 255.0; + color.blue = channels[2]._value / 255.0; + color.alpha = (length === 4) ? channels[3]._value : 1.0; + return color; + } + + function numberToString(number) { + if (number % 1 === 0) { + // Add a .0 to whole numbers + return number.toFixed(1); + } + + return number.toString(); + } + + function colorToVec3(color) { + var r = numberToString(color.red); + var g = numberToString(color.green); + var b = numberToString(color.blue); + return 'vec3(' + r + ', ' + g + ', ' + b + ')'; + } + + function colorToVec4(color) { + var r = numberToString(color.red); + var g = numberToString(color.green); + var b = numberToString(color.blue); + var a = numberToString(color.alpha); + return 'vec4(' + r + ', ' + g + ', ' + b + ', ' + a + ')'; + } + + function getExpressionArray(array, attributePrefix, shaderState, parent) { + var length = array.length; + var expressions = new Array(length); + for (var i = 0; i < length; ++i) { + expressions[i] = array[i].getShaderExpression(attributePrefix, shaderState, parent); + } + return expressions; + } + + Node.prototype.getShaderExpression = function(attributePrefix, shaderState, parent) { + var color; + var left; + var right; + var test; + + var type = this._type; + var value = this._value; + + if (defined(this._left)) { + if (isArray(this._left)) { + // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR + left = getExpressionArray(this._left, attributePrefix, shaderState, this); + } else { + left = this._left.getShaderExpression(attributePrefix, shaderState, this); + } + } + + if (defined(this._right)) { + right = this._right.getShaderExpression(attributePrefix, shaderState, this); + } + + if (defined(this._test)) { + test = this._test.getShaderExpression(attributePrefix, shaderState, this); + } + + if (isArray(this._value)) { + // For ARRAY type + value = getExpressionArray(this._value, attributePrefix, shaderState, this); + } + + switch (type) { + case ExpressionNodeType.VARIABLE: + return attributePrefix + value; + case ExpressionNodeType.UNARY: + // Supported types: +, -, !, Boolean, Number + if (value === 'Boolean') { + return 'bool(' + left + ')'; + } else if (value === 'Number') { + return 'float(' + left + ')'; + } else if (value === 'round') { + return 'floor(' + left + ' + 0.5)'; + } else if (defined(unaryFunctions[value])) { + return value + '(' + left + ')'; + } else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) { + throw new RuntimeError('Error generating style shader: "' + value + '" is not supported.'); + } else if (defined(unaryFunctions[value])) { + return value + '(' + left + ')'; + } + return value + left; + case ExpressionNodeType.BINARY: + // Supported types: ||, &&, ===, !==, <, >, <=, >=, +, -, *, /, % + if (value === '%') { + return 'mod(' + left + ', ' + right + ')'; + } else if (value === '===') { + return '(' + left + ' == ' + right + ')'; + } else if (value === '!==') { + return '(' + left + ' != ' + right + ')'; + } else if (value === 'atan2') { + return 'atan(' + left + ', ' + right + ')'; + } else if (defined(binaryFunctions[value])) { + return value + '(' + left + ', ' + right + ')'; + } + return '(' + left + ' ' + value + ' ' + right + ')'; + case ExpressionNodeType.TERNARY: + if (defined(ternaryFunctions[value])) { + return value + '(' + left + ', ' + right + ', ' + test + ')'; + } + break; + case ExpressionNodeType.CONDITIONAL: + return '(' + test + ' ? ' + left + ' : ' + right + ')'; + case ExpressionNodeType.MEMBER: + // This is intended for accessing the components of vector properties. String members aren't supported. + // Check for 0.0 rather than 0 because all numbers are previously converted to decimals. + if (right === 'r' || right === 'x' || right === '0.0') { + return left + '[0]'; + } else if (right === 'g' || right === 'y' || right === '1.0') { + return left + '[1]'; + } else if (right === 'b' || right === 'z' || right === '2.0') { + return left + '[2]'; + } else if (right === 'a' || right === 'w' || right === '3.0') { + return left + '[3]'; + } + return left + '[int(' + right + ')]'; + case ExpressionNodeType.FUNCTION_CALL: + throw new RuntimeError('Error generating style shader: "' + value + '" is not supported.'); + case ExpressionNodeType.ARRAY: + if (value.length === 4) { + return 'vec4(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', ' + value[3] + ')'; + } else if (value.length === 3) { + return 'vec3(' + value[0] + ', ' + value[1] + ', ' + value[2] + ')'; + } else if (value.length === 2) { + return 'vec2(' + value[0] + ', ' + value[1] + ')'; + } + throw new RuntimeError('Error generating style shader: Invalid array length. Array length should be 2, 3, or 4.'); + case ExpressionNodeType.REGEX: + throw new RuntimeError('Error generating style shader: Regular expressions are not supported.'); + case ExpressionNodeType.VARIABLE_IN_STRING: + throw new RuntimeError('Error generating style shader: Converting a variable to a string is not supported.'); + case ExpressionNodeType.LITERAL_NULL: + throw new RuntimeError('Error generating style shader: null is not supported.'); + case ExpressionNodeType.LITERAL_BOOLEAN: + return value ? 'true' : 'false'; + case ExpressionNodeType.LITERAL_NUMBER: + return numberToString(value); + case ExpressionNodeType.LITERAL_STRING: + if (defined(parent) && (parent._type === ExpressionNodeType.MEMBER)) { + if (value === 'r' || value === 'g' || value === 'b' || value === 'a' || + value === 'x' || value === 'y' || value === 'z' || value === 'w') { + return value; + } + } + // Check for css color strings + color = Color.fromCssColorString(value, scratchColor); + if (defined(color)) { + return colorToVec3(color); + } + throw new RuntimeError('Error generating style shader: String literals are not supported.'); + case ExpressionNodeType.LITERAL_COLOR: + var args = left; + if (value === 'color') { + if (!defined(args)) { + return 'vec4(1.0)'; + } else if (args.length > 1) { + var rgb = args[0]; + var alpha = args[1]; + if (alpha !== '1.0') { + shaderState.translucent = true; + } + return 'vec4(' + rgb + ', ' + alpha + ')'; + } + return 'vec4(' + args[0] + ', 1.0)'; + } else if (value === 'rgb') { + color = convertRGBToColor(this); + if (defined(color)) { + return colorToVec4(color); + } + return 'vec4(' + args[0] + ' / 255.0, ' + args[1] + ' / 255.0, ' + args[2] + ' / 255.0, 1.0)'; + } else if (value === 'rgba') { + if (args[3] !== '1.0') { + shaderState.translucent = true; + } + color = convertRGBToColor(this); + if (defined(color)) { + return colorToVec4(color); + } + return 'vec4(' + args[0] + ' / 255.0, ' + args[1] + ' / 255.0, ' + args[2] + ' / 255.0, ' + args[3] + ')'; + } else if (value === 'hsl') { + color = convertHSLToRGB(this); + if (defined(color)) { + return colorToVec4(color); + } + return 'vec4(czm_HSLToRGB(vec3(' + args[0] + ', ' + args[1] + ', ' + args[2] + ')), 1.0)'; + } else if (value === 'hsla') { + color = convertHSLToRGB(this); + if (defined(color)) { + if (color.alpha !== 1.0) { + shaderState.translucent = true; + } + return colorToVec4(color); + } + if (args[3] !== '1.0') { + shaderState.translucent = true; + } + return 'vec4(czm_HSLToRGB(vec3(' + args[0] + ', ' + args[1] + ', ' + args[2] + ')), ' + args[3] + ')'; + } + break; + case ExpressionNodeType.LITERAL_VECTOR: + var length = left.length; + var vectorExpression = value + '('; + for (var i = 0; i < length; ++i) { + vectorExpression += left[i]; + if (i < (length - 1)) { + vectorExpression += ', '; + } + } + vectorExpression += ')'; + return vectorExpression; + case ExpressionNodeType.LITERAL_REGEX: + throw new RuntimeError('Error generating style shader: Regular expressions are not supported.'); + case ExpressionNodeType.LITERAL_UNDEFINED: + throw new RuntimeError('Error generating style shader: undefined is not supported.'); + case ExpressionNodeType.BUILTIN_VARIABLE: + if (value === 'tiles3d_tileset_time') { + return 'u_tilesetTime'; + } + } + }; + + return Expression; +}); diff --git a/Source/Scene/ExpressionNodeType.js b/Source/Scene/ExpressionNodeType.js new file mode 100644 index 000000000000..b6529ef93632 --- /dev/null +++ b/Source/Scene/ExpressionNodeType.js @@ -0,0 +1,34 @@ +/*global define*/ +define([ + '../Core/freezeObject' +], function( + freezeObject) { + 'use strict'; + + /** + * @private + */ + var ExpressionNodeType = { + VARIABLE : 0, + UNARY : 1, + BINARY : 2, + TERNARY : 3, + CONDITIONAL : 4, + MEMBER : 5, + FUNCTION_CALL : 6, + ARRAY : 7, + REGEX: 8, + VARIABLE_IN_STRING : 9, + LITERAL_NULL : 10, + LITERAL_BOOLEAN : 11, + LITERAL_NUMBER : 12, + LITERAL_STRING : 13, + LITERAL_COLOR : 14, + LITERAL_VECTOR : 15, + LITERAL_REGEX : 16, + LITERAL_UNDEFINED : 17, + BUILTIN_VARIABLE : 18 + }; + + return freezeObject(ExpressionNodeType); +}); diff --git a/Source/Scene/FXAA.js b/Source/Scene/FXAA.js index 783bb7485c49..6696c6651820 100644 --- a/Source/Scene/FXAA.js +++ b/Source/Scene/FXAA.js @@ -12,8 +12,13 @@ define([ '../Renderer/Renderbuffer', '../Renderer/RenderbufferFormat', '../Renderer/RenderState', + '../Renderer/Sampler', '../Renderer/Texture', - '../Shaders/PostProcessFilters/FXAA' + '../Renderer/TextureMagnificationFilter', + '../Renderer/TextureMinificationFilter', + '../Renderer/TextureWrap', + '../Shaders/PostProcessFilters/FXAA', + '../ThirdParty/Shaders/FXAA3_11' ], function( BoundingRectangle, Cartesian2, @@ -27,14 +32,19 @@ define([ Renderbuffer, RenderbufferFormat, RenderState, + Sampler, Texture, - FXAAFS) { + TextureMagnificationFilter, + TextureMinificationFilter, + TextureWrap, + FXAAFS, + FXAA3_11) { 'use strict'; /** * @private */ - function FXAA(context) { + function FXAA() { this._texture = undefined; this._depthStencilTexture = undefined; this._depthStencilRenderbuffer = undefined; @@ -44,12 +54,17 @@ define([ this._viewport = new BoundingRectangle(); this._rs = undefined; + this._useScissorTest = false; + this._scissorRectangle = undefined; + var clearCommand = new ClearCommand({ color : new Color(0.0, 0.0, 0.0, 0.0), depth : 1.0, owner : this }); this._clearCommand = clearCommand; + + this._qualityPreset = 39; } function destroyResources(fxaa) { @@ -69,7 +84,7 @@ define([ } } - FXAA.prototype.update = function(context) { + FXAA.prototype.update = function(context, passState) { var width = context.drawingBufferWidth; var height = context.drawingBufferHeight; @@ -85,7 +100,13 @@ define([ width : width, height : height, pixelFormat : PixelFormat.RGBA, - pixelDatatype : PixelDatatype.UNSIGNED_BYTE + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + sampler : new Sampler({ + wrapS : TextureWrap.CLAMP_TO_EDGE, + wrapT : TextureWrap.CLAMP_TO_EDGE, + minificationFilter : TextureMinificationFilter.LINEAR, + magnificationFilter : TextureMagnificationFilter.LINEAR + }) }); if (context.depthTexture) { @@ -119,7 +140,12 @@ define([ } if (!defined(this._command)) { - this._command = context.createViewportQuadCommand(FXAAFS, { + var fs = + '#define FXAA_QUALITY_PRESET ' + this._qualityPreset + '\n' + + FXAA3_11 + '\n' + + FXAAFS; + + this._command = context.createViewportQuadCommand(fs, { owner : this }); } @@ -127,9 +153,22 @@ define([ this._viewport.width = width; this._viewport.height = height; - if (!defined(this._rs) || !BoundingRectangle.equals(this._rs.viewport, this._viewport)) { + var useScissorTest = !BoundingRectangle.equals(this._viewport, passState.viewport); + var updateScissor = useScissorTest !== this._useScissorTest; + this._useScissorTest = useScissorTest; + + if (!BoundingRectangle.equals(this._scissorRectangle, passState.viewport)) { + this._scissorRectangle = BoundingRectangle.clone(passState.viewport, this._scissorRectangle); + updateScissor = true; + } + + if (!defined(this._rs) || !BoundingRectangle.equals(this._rs.viewport, this._viewport) || updateScissor) { this._rs = RenderState.fromCache({ - viewport : this._viewport + viewport : this._viewport, + scissorTest : { + enabled : this._useScissorTest, + rectangle : this._scissorRectangle + } }); } @@ -137,13 +176,13 @@ define([ if (textureChanged) { var that = this; - var step = new Cartesian2(1.0 / this._texture.width, 1.0 / this._texture.height); + var rcpFrame = new Cartesian2(1.0 / this._texture.width, 1.0 / this._texture.height); this._command.uniformMap = { u_texture : function() { return that._texture; }, - u_step : function() { - return step; + u_fxaaQualityRcpFrame : function() { + return rcpFrame; } }; } diff --git a/Source/Scene/Fog.js b/Source/Scene/Fog.js index 15d9d4b2aa84..7e8ae1126354 100644 --- a/Source/Scene/Fog.js +++ b/Source/Scene/Fog.js @@ -131,7 +131,7 @@ define([ // Fade fog in as the camera tilts toward the horizon. var positionNormal = Cartesian3.normalize(camera.positionWC, scratchPositionNormal); - var dot = CesiumMath.clamp(Cartesian3.dot(camera.directionWC, positionNormal), 0.0, 1.0); + var dot = Math.abs(Cartesian3.dot(camera.directionWC, positionNormal)); density *= 1.0 - dot; frameState.fog.density = density; diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index d852e14c1bfc..da3a84c4d2bb 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -9,23 +9,26 @@ define([ * State information about the current frame. An instance of this class * is provided to update functions. * - * @param {Context} context The rendering context. + * @param {Context} context The rendering context * @param {CreditDisplay} creditDisplay Handles adding and removing credits from an HTML element + * @param {JobScheduler} jobScheduler The job scheduler * * @alias FrameState * @constructor * * @private */ - function FrameState(context, creditDisplay) { + function FrameState(context, creditDisplay, jobScheduler) { /** * The rendering context. + * * @type {Context} */ this.context = context; /** * An array of rendering commands. + * * @type {DrawCommand[]} */ this.commandList = []; @@ -38,6 +41,7 @@ define([ /** * The current mode of the scene. + * * @type {SceneMode} * @default {@link SceneMode.SCENE3D} */ @@ -67,6 +71,13 @@ define([ */ this.time = undefined; + /** + * The job scheduler. + * + * @type {JobScheduler} + */ + this.jobScheduler = jobScheduler; + /** * The map projection to use in 2D and Columbus View modes. * @@ -77,6 +88,7 @@ define([ /** * The current camera. + * * @type {Camera} * @default undefined */ @@ -84,6 +96,7 @@ define([ /** * The culling volume. + * * @type {CullingVolume} * @default undefined */ @@ -91,6 +104,7 @@ define([ /** * The current occluder. + * * @type {Occluder} * @default undefined */ @@ -108,12 +122,14 @@ define([ this.passes = { /** * true if the primitive should update for a render pass, false otherwise. + * * @type {Boolean} * @default false */ render : false, /** * true if the primitive should update for a picking pass, false otherwise. + * * @type {Boolean} * @default false */ @@ -129,6 +145,7 @@ define([ /** * The credit display. + * * @type {CreditDisplay} */ this.creditDisplay = creditDisplay; @@ -154,6 +171,7 @@ define([ /** * Gets whether or not to optimized for 3D only. + * * @type {Boolean} * @default false */ @@ -168,12 +186,14 @@ define([ enabled : false, /** * A positive number used to mix the color and fog color based on camera distance. + * * @type {Number} * @default undefined */ density : undefined, /** * A scalar used to modify the screen space error of geometry partially in fog. + * * @type {Number} * @default undefined */ @@ -181,9 +201,10 @@ define([ }; /** - * A scalar used to exaggerate the terrain. - * @type {Number} - */ + * A scalar used to exaggerate the terrain. + * @type {Number} + * @default 1.0 + */ this.terrainExaggeration = 1.0; this.shadowHints = { @@ -256,10 +277,19 @@ define([ * @type {Color} */ this.backgroundColor = undefined; + + /** + * The distance from the camera at which to disable the depth test of billboards, labels and points + * to, for example, prevent clipping against terrain. When set to zero, the depth test should always + * be applied. When less than zero, the depth test should never be applied. + * @type {Number} + */ + this.minimumDisableDepthTestDistance = undefined; } /** * A function that will be called at the end of the frame. + * * @callback FrameState~AfterRenderCallback */ diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 20fa390c0bdf..2254e270b81f 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -43,6 +43,9 @@ define([ this._viewport = new BoundingRectangle(); this._rs = undefined; + this._useScissorTest = false; + this._scissorRectangle = undefined; + this._debugGlobeDepthViewportCommand = undefined; } @@ -111,7 +114,7 @@ define([ }); } - function createFramebuffers(globeDepth, context, width, height) { + function createFramebuffers(globeDepth, context) { globeDepth.framebuffer = new Framebuffer({ context : context, colorTextures : [globeDepth._colorTexture], @@ -133,17 +136,30 @@ define([ destroyTextures(globeDepth); destroyFramebuffers(globeDepth); createTextures(globeDepth, context, width, height); - createFramebuffers(globeDepth, context, width, height); + createFramebuffers(globeDepth, context); } } - function updateCopyCommands(globeDepth, context, width, height) { + function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._viewport.width = width; globeDepth._viewport.height = height; - if (!defined(globeDepth._rs) || !BoundingRectangle.equals(globeDepth._viewport, globeDepth._rs.viewport)) { + var useScissorTest = !BoundingRectangle.equals(globeDepth._viewport, passState.viewport); + var updateScissor = useScissorTest !== globeDepth._useScissorTest; + globeDepth._useScissorTest = useScissorTest; + + if (!BoundingRectangle.equals(globeDepth._scissorRectangle, passState.viewport)) { + globeDepth._scissorRectangle = BoundingRectangle.clone(passState.viewport, globeDepth._scissorRectangle); + updateScissor = true; + } + + if (!defined(globeDepth._rs) || !BoundingRectangle.equals(globeDepth._viewport, globeDepth._rs.viewport) || updateScissor) { globeDepth._rs = RenderState.fromCache({ - viewport : globeDepth._viewport + viewport : globeDepth._viewport, + scissorTest : { + enabled : globeDepth._useScissorTest, + rectangle : globeDepth._scissorRectangle + } }); } @@ -196,12 +212,12 @@ define([ executeDebugGlobeDepth(this, context, passState); }; - GlobeDepth.prototype.update = function(context) { + GlobeDepth.prototype.update = function(context, passState) { var width = context.drawingBufferWidth; var height = context.drawingBufferHeight; updateFramebuffers(this, context, width, height); - updateCopyCommands(this, context, width, height); + updateCopyCommands(this, context, width, height, passState); context.uniformState.globeDepthTexture = undefined; }; diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 3d0a226758ac..1e27a4df4f80 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -18,6 +18,7 @@ define([ './QuadtreeTileLoadState', './SceneMode', './TerrainState', + './TileBoundingRegion', './TileTerrain' ], function( BoundingSphere, @@ -38,6 +39,7 @@ define([ QuadtreeTileLoadState, SceneMode, TerrainState, + TileBoundingRegion, TileTerrain) { 'use strict'; @@ -69,7 +71,7 @@ define([ this.boundingSphere3D = new BoundingSphere(); this.boundingSphere2D = new BoundingSphere(); this.orientedBoundingBox = undefined; - this.tileBoundingBox = undefined; + this.tileBoundingRegion = undefined; this.occludeePointInScaledSpace = new Cartesian3(); this.loadedTerrain = undefined; @@ -235,10 +237,39 @@ define([ } }; + function createTileBoundingRegion(tile) { + var minimumHeight; + var maximumHeight; + if (defined(tile.parent) && defined(tile.parent.data)) { + minimumHeight = tile.parent.data.minimumHeight; + maximumHeight = tile.parent.data.maximumHeight; + } + return new TileBoundingRegion({ + rectangle : tile.rectangle, + ellipsoid : tile.tilingScheme.ellipsoid, + minimumHeight : minimumHeight, + maximumHeight : maximumHeight + }); + } + + function createPriorityFunction(surfaceTile, frameState) { + return function() { + return surfaceTile.tileBoundingRegion.distanceToCamera(frameState); + }; + } + GlobeSurfaceTile.processStateMachine = function(tile, frameState, terrainProvider, imageryLayerCollection, vertexArraysToDestroy) { var surfaceTile = tile.data; if (!defined(surfaceTile)) { surfaceTile = tile.data = new GlobeSurfaceTile(); + // Create the TileBoundingRegion now in order to estimate the distance, which is used to prioritize the request. + // Since the terrain isn't loaded yet, estimate the heights using its parent's values. + surfaceTile.tileBoundingRegion = createTileBoundingRegion(tile); + } + + if (!defined(tile._priorityFunction)) { + // The priority function is used to prioritize requests among all requested tiles + tile._priorityFunction = createPriorityFunction(surfaceTile, frameState); } if (tile.state === QuadtreeTileLoadState.START) { @@ -305,6 +336,7 @@ define([ if (isDoneLoading) { tile.state = QuadtreeTileLoadState.DONE; + tile._priorityFunction = undefined; } } }; @@ -337,7 +369,7 @@ define([ var suspendUpsampling = false; if (defined(loaded)) { - loaded.processLoadStateMachine(frameState, terrainProvider, tile.x, tile.y, tile.level); + loaded.processLoadStateMachine(frameState, terrainProvider, tile.x, tile.y, tile.level, tile._priorityFunction); // Publish the terrain data on the tile as soon as it is available. // We'll potentially need it to upsample child tiles. diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 15b234d32be3..60cf90f421f1 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -570,8 +570,8 @@ define([ */ GlobeSurfaceTileProvider.prototype.computeDistanceToTile = function(tile, frameState) { var surfaceTile = tile.data; - var tileBoundingBox = surfaceTile.tileBoundingBox; - return tileBoundingBox.distanceToCamera(frameState); + var tileBoundingRegion = surfaceTile.tileBoundingRegion; + return tileBoundingRegion.distanceToCamera(frameState); }; /** @@ -918,6 +918,16 @@ define([ function addDrawCommandsForTile(tileProvider, tile, frameState) { var surfaceTile = tile.data; + var creditDisplay = frameState.creditDisplay; + + var terrainData = surfaceTile.terrainData; + if (defined(terrainData) && defined(terrainData.credits)) { + var tileCredits = terrainData.credits; + for (var tileCreditIndex = 0, + tileCreditLength = tileCredits.length; tileCreditIndex < tileCreditLength; ++tileCreditIndex) { + creditDisplay.addCredit(tileCredits[tileCreditIndex]); + } + } var maxTextures = ContextLimits.maximumTextureImageUnits; @@ -1139,7 +1149,6 @@ define([ applySplit = applySplit || uniformMapProperties.dayTextureSplit[numberOfDayTextures] !== 0.0; if (defined(imagery.credits)) { - var creditDisplay = frameState.creditDisplay; var credits = imagery.credits; for (var creditIndex = 0, creditLength = credits.length; creditIndex < creditLength; ++creditIndex) { creditDisplay.addCredit(credits[creditIndex]); diff --git a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js new file mode 100644 index 000000000000..de89b45a60ed --- /dev/null +++ b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js @@ -0,0 +1,621 @@ +/*global define*/ +define([ + '../Core/Credit', + '../Core/decodeGoogleEarthEnterpriseData', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/Event', + '../Core/GeographicTilingScheme', + '../Core/GoogleEarthEnterpriseMetadata', + '../Core/loadArrayBuffer', + '../Core/loadImageFromTypedArray', + '../Core/Math', + '../Core/Rectangle', + '../Core/Request', + '../Core/RequestType', + '../Core/RuntimeError', + '../Core/TileProviderError', + '../ThirdParty/protobuf-minimal', + '../ThirdParty/when' +], function( + Credit, + decodeGoogleEarthEnterpriseData, + defaultValue, + defined, + defineProperties, + DeveloperError, + Event, + GeographicTilingScheme, + GoogleEarthEnterpriseMetadata, + loadArrayBuffer, + loadImageFromTypedArray, + CesiumMath, + Rectangle, + Request, + RequestType, + RuntimeError, + TileProviderError, + protobuf, + when) { + 'use strict'; + + function GoogleEarthEnterpriseDiscardPolicy() { + this._image = new Image(); + } + + /** + * Determines if the discard policy is ready to process images. + * @returns {Boolean} True if the discard policy is ready to process images; otherwise, false. + */ + GoogleEarthEnterpriseDiscardPolicy.prototype.isReady = function() { + return true; + }; + + /** + * Given a tile image, decide whether to discard that image. + * + * @param {Image} image An image to test. + * @returns {Boolean} True if the image should be discarded; otherwise, false. + */ + GoogleEarthEnterpriseDiscardPolicy.prototype.shouldDiscardImage = function(image) { + return (image === this._image); + }; + + /** + * Provides tiled imagery using the Google Earth Enterprise REST API. + * + * Notes: This provider is for use with the 3D Earth API of Google Earth Enterprise, + * {@link GoogleEarthEnterpriseMapsProvider} should be used with 2D Maps API. + * + * @alias GoogleEarthEnterpriseImageryProvider + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {String} options.url The url of the Google Earth Enterprise server hosting the imagery. + * @param {GoogleEarthEnterpriseMetadata} options.metadata A metadata object that can be used to share metadata requests with a GoogleEarthEnterpriseTerrainProvider. + * @param {Proxy} [options.proxy] A proxy to use for requests. This object is + * expected to have a getURL function which returns the proxied URL, if needed. + * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. + * @param {TileDiscardPolicy} [options.tileDiscardPolicy] The policy that determines if a tile + * is invalid and should be discarded. If this value is not specified, a default + * is to discard tiles that fail to download. + * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. + * + * @see GoogleEarthEnterpriseTerrainProvider + * @see ArcGisMapServerImageryProvider + * @see GoogleEarthEnterpriseMapsProvider + * @see createOpenStreetMapImageryProvider + * @see SingleTileImageryProvider + * @see createTileMapServiceImageryProvider + * @see WebMapServiceImageryProvider + * @see WebMapTileServiceImageryProvider + * @see UrlTemplateImageryProvider + * + * + * @example + * var geeMetadata = new GoogleEarthEnterpriseMetadata('http://www.earthenterprise.org/3d'); + * var gee = new Cesium.GoogleEarthEnterpriseImageryProvider({ + * metadata : geeMetadata + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + */ + function GoogleEarthEnterpriseImageryProvider(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + //>>includeStart('debug', pragmas.debug); + if (!(defined(options.url) || defined(options.metadata))) { + throw new DeveloperError('options.url or options.metadata is required.'); + } + //>>includeEnd('debug'); + + var metadata; + if (defined(options.metadata)) { + metadata = this._metadata = options.metadata; + } else { + metadata = this._metadata = new GoogleEarthEnterpriseMetadata({ + url : options.url, + proxy : options.proxy + }); + } + this._tileDiscardPolicy = options.tileDiscardPolicy; + this._proxy = defaultValue(options.proxy, this._metadata.proxy); + + this._tilingScheme = new GeographicTilingScheme({ + numberOfLevelZeroTilesX : 2, + numberOfLevelZeroTilesY : 2, + rectangle : new Rectangle(-CesiumMath.PI, -CesiumMath.PI, CesiumMath.PI, CesiumMath.PI), + ellipsoid : options.ellipsoid + }); + + var credit = options.credit; + if (typeof credit === 'string') { + credit = new Credit(credit); + } + this._credit = credit; + + this._tileWidth = 256; + this._tileHeight = 256; + this._maximumLevel = 23; + + // Install the default tile discard policy if none has been supplied. + if (!defined(this._tileDiscardPolicy)) { + this._tileDiscardPolicy = new GoogleEarthEnterpriseDiscardPolicy(); + } + + this._errorEvent = new Event(); + + this._ready = false; + var that = this; + var metadataError; + this._readyPromise = metadata.readyPromise + .then(function(result) { + if (!metadata.imageryPresent) { + var e = new RuntimeError('The server ' + metadata.url + ' doesn\'t have imagery'); + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e); + return when.reject(e); + } + + TileProviderError.handleSuccess(metadataError); + that._ready = result; + return result; + }) + .otherwise(function(e) { + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e); + return when.reject(e); + }); + } + + defineProperties(GoogleEarthEnterpriseImageryProvider.prototype, { + /** + * Gets the name of the Google Earth Enterprise server url hosting the imagery. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {String} + * @readonly + */ + url : { + get : function() { + return this._metadata.url; + } + }, + + /** + * Gets the proxy used by this provider. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Proxy} + * @readonly + */ + proxy : { + get : function() { + return this._proxy; + } + }, + + /** + * Gets the width of each tile, in pixels. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Number} + * @readonly + */ + tileWidth : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileWidth must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileWidth; + } + }, + + /** + * Gets the height of each tile, in pixels. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Number} + * @readonly + */ + tileHeight : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileHeight; + } + }, + + /** + * Gets the maximum level-of-detail that can be requested. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Number} + * @readonly + */ + maximumLevel : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('maximumLevel must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._maximumLevel; + } + }, + + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Number} + * @readonly + */ + minimumLevel : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('minimumLevel must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return 0; + } + }, + + /** + * Gets the tiling scheme used by this provider. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {TilingScheme} + * @readonly + */ + tilingScheme : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tilingScheme; + } + }, + + /** + * Gets the rectangle, in radians, of the imagery provided by this instance. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Rectangle} + * @readonly + */ + rectangle : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('rectangle must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tilingScheme.rectangle; + } + }, + + /** + * Gets the tile discard policy. If not undefined, the discard policy is responsible + * for filtering out "missing" tiles via its shouldDiscardImage function. If this function + * returns undefined, no tiles are filtered. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {TileDiscardPolicy} + * @readonly + */ + tileDiscardPolicy : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileDiscardPolicy must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileDiscardPolicy; + } + }, + + /** + * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing + * to the event, you will be notified of the error and can potentially recover from it. Event listeners + * are passed an instance of {@link TileProviderError}. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Event} + * @readonly + */ + errorEvent : { + get : function() { + return this._errorEvent; + } + }, + + /** + * Gets a value indicating whether or not the provider is ready for use. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Boolean} + * @readonly + */ + ready : { + get : function() { + return this._ready; + } + }, + + /** + * Gets a promise that resolves to true when the provider is ready for use. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + return this._readyPromise; + } + }, + + /** + * Gets the credit to display when this imagery provider is active. Typically this is used to credit + * the source of the imagery. This function should not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Credit} + * @readonly + */ + credit : { + get : function() { + return this._credit; + } + }, + + /** + * Gets a value indicating whether or not the images provided by this imagery provider + * include an alpha channel. If this property is false, an alpha channel, if present, will + * be ignored. If this property is true, any images without an alpha channel will be treated + * as if their alpha is 1.0 everywhere. Setting this property to false reduces memory usage + * and texture upload time. + * @memberof GoogleEarthEnterpriseImageryProvider.prototype + * @type {Boolean} + * @readonly + */ + hasAlphaChannel : { + get : function() { + return false; + } + } + }); + + /** + * Gets the credits to be displayed when a given tile is displayed. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level; + * @returns {Credit[]} The credits to be displayed when the tile is displayed. + * + * @exception {DeveloperError} getTileCredits must not be called before the imagery provider is ready. + */ + GoogleEarthEnterpriseImageryProvider.prototype.getTileCredits = function(x, y, level) { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('getTileCredits must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + var metadata = this._metadata; + var info = metadata.getTileInformation(x, y, level); + if (defined(info)) { + var credit = metadata.providers[info.imageryProvider]; + if (defined(credit)) { + return [credit]; + } + } + + return undefined; + }; + + /** + * Requests the image for a given tile. This function should + * not be called before {@link GoogleEarthEnterpriseImageryProvider#ready} returns true. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or + * undefined if there are too many active requests to the server, and the request + * should be retried later. The resolved image may be either an + * Image or a Canvas DOM object. + * + * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. + */ + GoogleEarthEnterpriseImageryProvider.prototype.requestImage = function(x, y, level, request) { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + var invalidImage = this._tileDiscardPolicy._image; // Empty image or undefined depending on discard policy + var metadata = this._metadata; + var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + var info = metadata.getTileInformation(x, y, level); + if (!defined(info)) { + if (metadata.isValid(quadKey)) { + var metadataRequest = new Request({ + throttle : request.throttle, + throttleByServer : request.throttleByServer, + type : request.type, + priorityFunction : request.priorityFunction + }); + metadata.populateSubtree(x, y, level, metadataRequest); + return undefined; // No metadata so return undefined so we can be loaded later + } + return invalidImage; // Image doesn't exist + } + + if (!info.hasImagery()) { + // Already have info and there isn't any imagery here + return invalidImage; + } + // Load the + var url = buildImageUrl(this, info, x, y, level); + var promise = loadArrayBuffer(url, undefined, request); + if (!defined(promise)) { + return undefined; // Throttled + } + + return promise + .then(function(image) { + decodeGoogleEarthEnterpriseData(metadata.key, image); + var a = new Uint8Array(image); + var type; + + var protoImagery = metadata.protoImagery; + if (!defined(protoImagery) || !protoImagery) { + type = getImageType(a); + } + + if (!defined(type) && (!defined(protoImagery) || protoImagery)) { + var message = decodeEarthImageryPacket(a); + type = message.imageType; + a = message.imageData; + } + + if (!defined(type) || !defined(a)) { + return invalidImage; + } + + return loadImageFromTypedArray(a, type); + }); + }; + + /** + * Picking features is not currently supported by this imagery provider, so this function simply returns + * undefined. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @param {Number} longitude The longitude at which to pick features. + * @param {Number} latitude The latitude at which to pick features. + * @return {Promise.|undefined} A promise for the picked features that will resolve when the asynchronous + * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo} + * instances. The array may be empty if no features are found at the given location. + * It may also be undefined if picking is not supported. + */ + GoogleEarthEnterpriseImageryProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) { + return undefined; + }; + + // + // Functions to handle imagery packets + // + function buildImageUrl(imageryProvider, info, x, y, level) { + var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); + var version = info.imageryVersion; + version = (defined(version) && version > 0) ? version : 1; + var imageUrl = imageryProvider.url + 'flatfile?f1-0' + quadKey + '-i.' + version.toString(); + + var proxy = imageryProvider._proxy; + if (defined(proxy)) { + imageUrl = proxy.getURL(imageUrl); + } + + return imageUrl; + } + + // Detects if a Uint8Array is a JPEG or PNG + function getImageType(image) { + var jpeg = 'JFIF'; + if (image[6] === jpeg.charCodeAt(0) && image[7] === jpeg.charCodeAt(1) && + image[8] === jpeg.charCodeAt(2) && image[9] === jpeg.charCodeAt(3)) { + return 'image/jpeg'; + } + + var png = 'PNG'; + if (image[1] === png.charCodeAt(0) && image[2] === png.charCodeAt(1) && image[3] === png.charCodeAt(2)) { + return 'image/png'; + } + + return undefined; + } + + // Decodes an Imagery protobuf into the message + // Partially generated with the help of protobuf.js static generator + function decodeEarthImageryPacket(data) { + var reader = protobuf.Reader.create(data); + var end = reader.len; + var message = {}; + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.imageType = reader.uint32(); + break; + case 2: + message.imageData = reader.bytes(); + break; + case 3: + message.alphaType = reader.uint32(); + break; + case 4: + message.imageAlpha = reader.bytes(); + break; + case 5: + var copyrightIds = message.copyrightIds; + if (!defined(copyrightIds)) { + copyrightIds = message.copyrightIds = []; + } + if ((tag & 7) === 2) { + var end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + copyrightIds.push(reader.uint32()); + } + } else { + copyrightIds.push(reader.uint32()); + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + + var imageType = message.imageType; + if (defined(imageType)) { + switch (imageType) { + case 0: + message.imageType = 'image/jpeg'; + break; + case 4: + message.imageType = 'image/png'; + break; + default: + throw new RuntimeError('GoogleEarthEnterpriseImageryProvider: Unsupported image type.'); + } + } + + var alphaType = message.alphaType; + if (defined(alphaType) && alphaType !== 0) { + console.log('GoogleEarthEnterpriseImageryProvider: External alpha not supported.'); + delete message.alphaType; + delete message.imageAlpha; + } + + return message; + } + + return GoogleEarthEnterpriseImageryProvider; +}); diff --git a/Source/Scene/GoogleEarthEnterpriseMapsProvider.js b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js new file mode 100644 index 000000000000..4251d3d2d40a --- /dev/null +++ b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js @@ -0,0 +1,598 @@ +/*global define*/ +define([ + '../Core/Credit', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/Event', + '../Core/GeographicTilingScheme', + '../Core/loadText', + '../Core/Rectangle', + '../Core/RuntimeError', + '../Core/TileProviderError', + '../Core/WebMercatorTilingScheme', + '../ThirdParty/when', + './ImageryProvider' + ], function( + Credit, + defaultValue, + defined, + defineProperties, + DeveloperError, + Event, + GeographicTilingScheme, + loadText, + Rectangle, + RuntimeError, + TileProviderError, + WebMercatorTilingScheme, + when, + ImageryProvider) { + 'use strict'; + + /** + * Provides tiled imagery using the Google Earth Imagery API. + * + * Notes: This imagery provider does not work with the public Google Earth servers. It works with the + * Google Earth Enterprise Server. + * + * By default the Google Earth Enterprise server does not set the + * {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} headers. You can either + * use a proxy server which adds these headers, or in the /opt/google/gehttpd/conf/gehttpd.conf + * and add the 'Header set Access-Control-Allow-Origin "*"' option to the '<Directory />' and + * '<Directory "/opt/google/gehttpd/htdocs">' directives. + * + * This provider is for use with 2D Maps API as part of Google Earth Enterprise. For 3D Earth API uses, it + * is necessary to use {@link GoogleEarthEnterpriseImageryProvider} + * + * @alias GoogleEarthEnterpriseMapsProvider + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {String} options.url The url of the Google Earth server hosting the imagery. + * @param {Number} options.channel The channel (id) to be used when requesting data from the server. + * The channel number can be found by looking at the json file located at: + * earth.localdomain/default_map/query?request=Json&vars=geeServerDefs The /default_map path may + * differ depending on your Google Earth Enterprise server configuration. Look for the "id" that + * is associated with a "ImageryMaps" requestType. There may be more than one id available. + * Example: + * { + * layers: [ + * { + * id: 1002, + * requestType: "ImageryMaps" + * }, + * { + * id: 1007, + * requestType: "VectorMapsRaster" + * } + * ] + * } + * @param {String} [options.path="/default_map"] The path of the Google Earth server hosting the imagery. + * @param {Number} [options.maximumLevel] The maximum level-of-detail supported by the Google Earth + * Enterprise server, or undefined if there is no limit. + * @param {TileDiscardPolicy} [options.tileDiscardPolicy] The policy that determines if a tile + * is invalid and should be discarded. To ensure that no tiles are discarded, construct and pass + * a {@link NeverTileDiscardPolicy} for this parameter. + * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. + * @param {Proxy} [options.proxy] A proxy to use for requests. This object is + * expected to have a getURL function which returns the proxied URL, if needed. + * + * @exception {RuntimeError} Could not find layer with channel (id) of options.channel. + * @exception {RuntimeError} Could not find a version in channel (id) options.channel. + * @exception {RuntimeError} Unsupported projection data.projection. + * + * @see ArcGisMapServerImageryProvider + * @see BingMapsImageryProvider + * @see createOpenStreetMapImageryProvider + * @see SingleTileImageryProvider + * @see createTileMapServiceImageryProvider + * @see WebMapServiceImageryProvider + * @see WebMapTileServiceImageryProvider + * @see UrlTemplateImageryProvider + * + * + * @example + * var google = new Cesium.GoogleEarthEnterpriseMapsProvider({ + * url : 'https://earth.localdomain', + * channel : 1008 + * }); + * + * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} + */ + function GoogleEarthEnterpriseMapsProvider(options) { + options = defaultValue(options, {}); + + //>>includeStart('debug', pragmas.debug); + if (!defined(options.url)) { + throw new DeveloperError('options.url is required.'); + } + if (!defined(options.channel)) { + throw new DeveloperError('options.channel is required.'); + } + //>>includeEnd('debug'); + + this._url = options.url; + this._path = defaultValue(options.path, '/default_map'); + this._tileDiscardPolicy = options.tileDiscardPolicy; + this._proxy = options.proxy; + this._channel = options.channel; + this._requestType = 'ImageryMaps'; + this._credit = new Credit('Google Imagery', GoogleEarthEnterpriseMapsProvider._logoData, 'http://www.google.com/enterprise/mapsearth/products/earthenterprise.html'); + + /** + * The default {@link ImageryLayer#gamma} to use for imagery layers created for this provider. + * By default, this is set to 1.9. Changing this value after creating an {@link ImageryLayer} for this provider will have + * no effect. Instead, set the layer's {@link ImageryLayer#gamma} property. + * + * @type {Number} + * @default 1.9 + */ + this.defaultGamma = 1.9; + + this._tilingScheme = undefined; + + this._version = undefined; + + this._tileWidth = 256; + this._tileHeight = 256; + this._maximumLevel = options.maximumLevel; + this._imageUrlTemplate = this._url + this._path + '/query?request={request}&channel={channel}&version={version}&x={x}&y={y}&z={zoom}'; + + this._errorEvent = new Event(); + + this._ready = false; + this._readyPromise = when.defer(); + + var metadataUrl = this._url + this._path + '/query?request=Json&vars=geeServerDefs&is2d=t'; + var that = this; + var metadataError; + + function metadataSuccess(text) { + var data; + + // The Google Earth server sends malformed JSON data currently... + try { + // First, try parsing it like normal in case a future version sends correctly formatted JSON + data = JSON.parse(text); + } catch (e) { + // Quote object strings manually, then try parsing again + data = JSON.parse(text.replace(/([\[\{,])[\n\r ]*([A-Za-z0-9]+)[\n\r ]*:/g, '$1"$2":')); + } + + var layer; + for (var i = 0; i < data.layers.length; i++) { + if (data.layers[i].id === that._channel) { + layer = data.layers[i]; + break; + } + } + + var message; + + if (!defined(layer)) { + message = 'Could not find layer with channel (id) of ' + that._channel + '.'; + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); + throw new RuntimeError(message); + } + + if (!defined(layer.version)) { + message = 'Could not find a version in channel (id) ' + that._channel + '.'; + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); + throw new RuntimeError(message); + } + that._version = layer.version; + + if (defined(data.projection) && data.projection === 'flat') { + that._tilingScheme = new GeographicTilingScheme({ + numberOfLevelZeroTilesX : 2, + numberOfLevelZeroTilesY : 2, + rectangle : new Rectangle(-Math.PI, -Math.PI, Math.PI, Math.PI), + ellipsoid : options.ellipsoid + }); + // Default to mercator projection when projection is undefined + } else if (!defined(data.projection) || data.projection === 'mercator') { + that._tilingScheme = new WebMercatorTilingScheme({ + numberOfLevelZeroTilesX : 2, + numberOfLevelZeroTilesY : 2, + ellipsoid : options.ellipsoid + }); + } else { + message = 'Unsupported projection ' + data.projection + '.'; + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); + throw new RuntimeError(message); + } + + that._imageUrlTemplate = that._imageUrlTemplate.replace('{request}', that._requestType) + .replace('{channel}', that._channel).replace('{version}', that._version); + + that._ready = true; + that._readyPromise.resolve(true); + TileProviderError.handleSuccess(metadataError); + } + + function metadataFailure(e) { + var message = 'An error occurred while accessing ' + metadataUrl + '.'; + metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); + that._readyPromise.reject(new RuntimeError(message)); + } + + function requestMetadata() { + var url = (!defined(that._proxy)) ? metadataUrl : that._proxy.getURL(metadataUrl); + + var metadata = loadText(url); + when(metadata, metadataSuccess, metadataFailure); + } + + requestMetadata(); + } + + defineProperties(GoogleEarthEnterpriseMapsProvider.prototype, { + /** + * Gets the URL of the Google Earth MapServer. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {String} + * @readonly + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * Gets the url path of the data on the Google Earth server. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {String} + * @readonly + */ + path : { + get : function() { + return this._path; + } + }, + + /** + * Gets the proxy used by this provider. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Proxy} + * @readonly + */ + proxy : { + get : function() { + return this._proxy; + } + }, + + /** + * Gets the imagery channel (id) currently being used. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + channel : { + get : function() { + return this._channel; + } + }, + + /** + * Gets the width of each tile, in pixels. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + tileWidth : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileWidth must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileWidth; + } + }, + + /** + * Gets the height of each tile, in pixels. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + tileHeight : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileHeight; + } + }, + + /** + * Gets the maximum level-of-detail that can be requested. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + maximumLevel : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('maximumLevel must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._maximumLevel; + } + }, + + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + minimumLevel : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('minimumLevel must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return 0; + } + }, + + /** + * Gets the tiling scheme used by this provider. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {TilingScheme} + * @readonly + */ + tilingScheme : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tilingScheme; + } + }, + + /** + * Gets the version of the data used by this provider. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Number} + * @readonly + */ + version : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('version must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._version; + } + }, + + /** + * Gets the type of data that is being requested from the provider. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {String} + * @readonly + */ + requestType : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('requestType must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._requestType; + } + }, + /** + * Gets the rectangle, in radians, of the imagery provided by this instance. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Rectangle} + * @readonly + */ + rectangle : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('rectangle must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tilingScheme.rectangle; + } + }, + + /** + * Gets the tile discard policy. If not undefined, the discard policy is responsible + * for filtering out "missing" tiles via its shouldDiscardImage function. If this function + * returns undefined, no tiles are filtered. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {TileDiscardPolicy} + * @readonly + */ + tileDiscardPolicy : { + get : function() { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('tileDiscardPolicy must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + return this._tileDiscardPolicy; + } + }, + + /** + * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing + * to the event, you will be notified of the error and can potentially recover from it. Event listeners + * are passed an instance of {@link TileProviderError}. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Event} + * @readonly + */ + errorEvent : { + get : function() { + return this._errorEvent; + } + }, + + /** + * Gets a value indicating whether or not the provider is ready for use. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Boolean} + * @readonly + */ + ready : { + get : function() { + return this._ready; + } + }, + + /** + * Gets a promise that resolves to true when the provider is ready for use. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Promise.} + * @readonly + */ + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + }, + + /** + * Gets the credit to display when this imagery provider is active. Typically this is used to credit + * the source of the imagery. This function should not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Credit} + * @readonly + */ + credit : { + get : function() { + return this._credit; + } + }, + + /** + * Gets a value indicating whether or not the images provided by this imagery provider + * include an alpha channel. If this property is false, an alpha channel, if present, will + * be ignored. If this property is true, any images without an alpha channel will be treated + * as if their alpha is 1.0 everywhere. When this property is false, memory usage + * and texture upload time are reduced. + * @memberof GoogleEarthEnterpriseMapsProvider.prototype + * @type {Boolean} + * @readonly + */ + hasAlphaChannel : { + get : function() { + return true; + } + } + }); + + /** + * Gets the credits to be displayed when a given tile is displayed. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level; + * @returns {Credit[]} The credits to be displayed when the tile is displayed. + * + * @exception {DeveloperError} getTileCredits must not be called before the imagery provider is ready. + */ + GoogleEarthEnterpriseMapsProvider.prototype.getTileCredits = function(x, y, level) { + return undefined; + }; + + /** + * Requests the image for a given tile. This function should + * not be called before {@link GoogleEarthEnterpriseMapsProvider#ready} returns true. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. + * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or + * undefined if there are too many active requests to the server, and the request + * should be retried later. The resolved image may be either an + * Image or a Canvas DOM object. + * + * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. + */ + GoogleEarthEnterpriseMapsProvider.prototype.requestImage = function(x, y, level, request) { + //>>includeStart('debug', pragmas.debug); + if (!this._ready) { + throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); + } + //>>includeEnd('debug'); + + var url = buildImageUrl(this, x, y, level); + return ImageryProvider.loadImage(this, url, request); + }; + + /** + * Picking features is not currently supported by this imagery provider, so this function simply returns + * undefined. + * + * @param {Number} x The tile X coordinate. + * @param {Number} y The tile Y coordinate. + * @param {Number} level The tile level. + * @param {Number} longitude The longitude at which to pick features. + * @param {Number} latitude The latitude at which to pick features. + * @return {Promise.|undefined} A promise for the picked features that will resolve when the asynchronous + * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo} + * instances. The array may be empty if no features are found at the given location. + * It may also be undefined if picking is not supported. + */ + GoogleEarthEnterpriseMapsProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) { + return undefined; + }; + + GoogleEarthEnterpriseMapsProvider._logoData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAAnCAYAAACmP2LfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAHdElNRQfcDB4TJDr1mp5kAAAAGnRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuNS4xMDD0cqEAAB1zSURBVHhe7ZwHeFTFFsf/u+l9N70npOxuSAKEFFIhCSH0qhEQUHkgKCgWUFGBB6IoCAoo0ntooaRvEkIIBBBpoYSa3nvvfd+5u4sQUigPfMX8v2/Y3Tkzs3fv/d0z58zcgF69Ql1SY+MM1wQJem44ZeiJk8beEOqPwG6uC7ZqyElb9eo/JZEIkH2nRQkBIlNMauuPCS3uGN/kjkmNDghoskBAgzrZ2NLmf1+JwIKQpYsoxdmIV9+N07onCegzBPM9bOdmYKnazF6g/1N6UySPqSJzvCaaiLHtP8G/Phq+FRfgU5ogKWUXMLT6Mvzqr2BE40mMadqO8c3zMabBC6PqDDC8SlY60t9HByCLVTKu+ERmHr5TWI9wjVxEaOZivWo1pil8D1tZeWnLXv1l8iZ3PF2kjymiWRgvCoJv5U243IyAXcQq8A9Mg9W+4bDe6wv+kVGwCZkL+4Sf4ZR+BZ5VGQR3EkbWn8Hopm3wq54Lz2JD6ah/P21XGopQ9Qoc16jGSqVyTJWbQbUsibFXf42mihTwZpsvAtp3k0dOhFOSEH1+ngaDefrgjFCgFkxY8fCisCBvKgODzxRh9qslBFGfYmDGLbiV5mBwRRo8KtPhVBgPu8teMP7u73chD6kMRYRGBY5xqrFKqQwz5SdTbS/Qf5mmUYw8rf01CjHC4VP7AHZxO6E3qy9ZZCQNnio2rE/4o9/tkxiQUYp+KRXgx8XC5FsXcLz/hkCrDUU4pxLHuDVYpdwL9F+qqSJZKlPwenskfOoI5tN7YPCJGVme7wKYr5EBXzgYfW+mwTI0Gjrznaj2WW+I/y8dVPdDGLcKRzXrsEqlHO8oTKHaXqAZWe9hQXCi63NhHWYI3ilfWIW/YLjqL2JRiOFBJRz+LffhcPs09D+0J8vzn3zXdBnYnp8Mi6NboTWzH9X8fVc+DhDQodxqAroe36lU9AJNWr4cEAjNwI8OAC9cT1rbUfzwGeCfKiL7dGnNc+q1NiO80b4BY1oT4V6WDcsdc6j2xbyq4wMWrA9rQmeWFn36ey/jBaoPQ4hmLYI0G/AtAf22fC/QDols8ITrIYi/Bl6knbS2o3gRbxHQxQQ0k0S/gCa2v4OJovPwacqAQ1ICjL40klr+UrWoQbFBETo18jCpZsOoFODkvuCNJYoHW3QKXFEM7ETRcKfiQe8d6NVIFImXvg4skhY40mxnQYVRIIeA1qrHEc1GrFSpxFtP99AiFbDbNKDZpAzzGkVYVcvBuBJQEo/9/6C+dyjPitwLwak74D8V6Bfw0P5VShjXFoTR7TfhUZkL29M/wfATJan1lauWC3aDOgyaVDCuTgbf1bFkfmtkye1ogsK2asivLYfCglIoD8qCknI2NHuG4QSVGMgQyMbt0fioRYh9VYcRU7QX55uDcaHtFOJEsThMtmWtQgxsDodsWaC0c3ea3MzGBJEqxrfbYmzr6xjfPAeTmt5HQPO7eK1xDibUz8eY+k8xtHYJPCtXwvHOu7AXMrMTsF/TH8HajTis1YwVqpWY0TXQDKy1OpBr5EJA52Fukxx+bmKxtjWx2DuaWawNlZD5qhzyo9KhpHAbKpJO/6t65UCPbPHA2PYrGNacgkElabCJJDev/MpDhUKKnuq44LRoYEK1IiswkS1zYCfk5y+F0qjvoTwqBOof34dGeAnUL1ZCLboEnJ9zoe0QD/Nuj00UBVXRabzVLETM3S0ICfwA8yc7Y6C3ANYbZsA7aQ1W1xzEfZEQ6dT2BkG9pP4ouo7jGE1u42JS20QMrzkCr4xwuN4+AM+cYII3EaNar2J86zmMrP8DHulCON4NhU3YWuhOYy6SZENpH9cfx7WacFC7BSvUqjBDsRPQIiugURvazeqYVaqAw6dYrJ9WQy7gayj4nYDy3HtQOVQGpYRqKEWXQf2HdGha/AFdae9Xr4czz0ubISRA75ECbSut7agegO75OLxpahze8j5GtifBpzEDLiV30Dd2mNT6StWiCbVmLt5rUkBQCEt2zWzIMSA8HgrIBkLD+Sp0jhHISYXQ/KMYukfvQ3fQxq68XCTBHId/tMTg7LV1CFs4BszJ6hBarBgHlcRv8H7tbuSKQpFPYGe0BmND+nZ0npECaPKf0r4UIxsuoF/IMpitsAVnrA4s15uh3x8fwLXkLobUZGJIXTqcUzbDaJE5FAVq0t4S7dEcjqMEc6B2K5arVWN6Z6AbdOmm5mJelQKOHWSxF44Cy4CqxW0s6RwchCovFRohdGNfLgX3WiZ0N4aD++y7jfwYJUrAPCle/ZjKV+BFTSegrGAZIm3QjXhBytTWB3zhByzryMUU986jz16wD+96ijCNUIAgmkc3tS6G7GERjCbgR82B4OTbEESqIiCIcqsIYzoGGyrBEMSmgh8xBoIIAR2fAHZhj8Z9DOhl9FHeKkSDvn809fuc+iyCddRYaiOZBTvIt1YJfs0b4N+WDO+GHPLQN2Ab7S61vjJV60C9SRPvNSqzTpxlyQfS1dGUmjppK7gW16B/LhN6abnQu5cDwzO3YNhhqqK4WJY887sEdGzWFpxfOxmDpKZOOvgWFB8sx9L6nShvP4FyUQjKGg5gScpGKEqbUE7RxiGYv6QQ4zIG/r4D2m88sjEy/EIW/a6+TQ4gHe5VhXCvy4JL7gLYnesI2i6t4Tii04r92u1YKt767gB0ozrkGzmY26zEOh7Hkt+kAKhLTX9qOVVdg9aoNOjcToR+wUVKLYKgN0Zq7l7884wn9CKgr4AfWw/B6SwqKQRKOdXVghe9CpbherASSjtIpGpxRIHFjwygNreoXy0lb+lU7lHJBP9kPcGXQnBNghUB/Lh44fbUp5JA+5Hs71LbPPLCVRDEJZDNGIJgeQI6mG6KegKzldq1U7tGKjQmHR8vwl86kgRoAQN0xBw6ztn0nQ/ocxEdQ7L4d/BjG6g+m8aZTL/xsXPuW82Fb8t+DG1Ox5D6XAwqvQ67OA+p9ZWoUQPsei78mjSwNU9GLmEzVGZJTd3qFPTn3YZhXgYMMjNhlHsDxms/hNWfoUdrNPgEc2h7BG5d/Bo7Blt0BuNxXf4MVmXrkdRyEHWiY6hr2oc7mevRX2wc18gioEeI1+N9a+/CNnImVAZ0mhEoNOPAJT8MHjUF8KTiWhqHgbfMpVaJdhLQh3XasU9bJAZ6ekeg6zQwgEKuLSWysmd3QGmatLqD8qDNug3dCX/AIPk4jGr2wDB/JXTmkan70IvmZTY/rB9BdZlKLkG0lG0d5klAObKsw1+jzyFiWPnRawiaDrMYwTyMwMwh220WP2IWFVfqN4CKO8E3n0C6R/ZUej9Y2kUiMdDRFTRePH3nA3q/m7xpAEtAXl0QrkTwscnmS/3eptdzNEYevZLnZ5booqk8tuYs9tAny+n1LL1mghezlcULH0VtHamOZhvhIvoNOXQsd2EZIbluYnlWaMO75TCFG9kYXJ8H14o76H/10Z3yClSrCm6jGtbWK7LC7kIlYRfUmY2XHnUa+mbXYRSfCuNCptyE6b1jMBD/EPKwchQPLxGdxOWWI8iKXYBPqLozgI8pfA5YBWvxbfMeNLUfRmPTLjRnr8YKsdGvRQ5j2zZTSSRQ78H+7GhxfScFAINypsG9ukDspZ0LKKE+O0pqlGi71ggcIqD3dga6RhFKjSqYT+VEFkvu/E9Q+HNWKaE2VVDgVkPFqwAaay5CN3En9M59BM2vfKDs7AvljjPGE5LlharQdL+LoCmhOHU0rIUyD+NgVTOa+q2iVQiIcAKpHtbhXuJOjPqeVCRYThNE6VTvKNs3hM3cHGIxntxKyCbP7Erj1lHZJbVIJAG6iiCroZCAPGukvOyASJbvCgoaAoKoAQ1kHcGC7nmZDkmhBR2PfSQLtkcl4zCSAE2eO6qExYuYxrE4KqdvelBiM4+ncYQy1IY8d0wbhUSLJAZGbsUceNYdwJCGPAyuy4NbZToG3JoO1Qk9AvHvqF4ejo0KCKlisyl04Jw+AE1ma71HRUJP+QqM1t2HcVEyTEoSYVYQCuN3HenCt4XDhGA+KorAnYZ9KIj5ELOl3XpU/k/wrt+OmraDaG7cjpacbxFvYAAZDG5Vw/DWCxjRdp+ATsWAS6+D69H1+XDNsoVb1T06b0VwzCmBIOYdqUWibTojcFBH1CXQctBtUcA6Oh/RmVC4sBmKA5j6erC1qqE4sRpqG25A43QIOHuXgvOmP5R4ZH6m5UY2L9SSLjZ5sKjjsI/o8olH8ngjCZoSgmw9DMIl3t42Up0g+pq89/sEjLK47knZhSkSuDepJP4JOyNJyEFAR8VQKMOR1nbWM69yxNJYwh+VLE90ffPyxLE3EwL9Jq0huWQqwL1iA7zq8+FVl0+epgBO6T+gb2TH+OglqgastxtZrNNlkLt8E5oJx6HZdab7mFZBk3UZRjMewCT7HkzLfodZxREYr5sBjiIBPYiAPt8ehvSGPSg5vwjzpd16VNkmmDTswp22QDTXbkJrxhJkzHGDFoUQmvBpvo2hrZl0TnLhlLIYfUO7nt7dSg3hURcP1/JiDEgphuXBqVKLRFsfA3oJAf3mI6Cr2OjTwGYdqWGzzmZD6WoYVCfehdqsZKjuuwS1oB1Q+5piHac3oaxBzZ9vLZ4nHEeesoXg6niDPSYWP9yUgD5PHu48eKE64krHcErchHIEuRysTpAXjObQWIYEHiV4EQYEojp5aEoyY+IIpOQugKYYOnIdJXrdJ63PtWwXMQM6m6SVT4gfZkbHV0XHsVtaQ3K8yoJr0YfwoHDDq5ZiQSqDik/B4Q9taYtn18gyNia1qGJsmTrGlUjK2FJ1jCjRwOASDnkxDvN95ZD/og5yl0qgfCMJ2leDoeksHaFHXYOJVyrMkm/DrPwMzGr2wmjnLGipthyHL0W7t9pDkduwF2U3lmGFtvbTdyirt0OreT+iWwPRUrUBbSkLkT/fCUZwKVYikBMwpDlPXNzLwuAQ2rWX8KzUh2dDDJyLSmB7/S5Mf3WRWiR6CPSezkCXQs6qBnLCKsheyoXqnTCoL9oOFd9/Qtl9KJT6UJMX3/zhCz8iuCjhiviSYtMx3ZTJBN8lCE7eIRgF0p6krRRaRBDskTTGySBKws5SuUjJHYUiMQdpzCUE0Q3y5MnSDhJJQg5JUvjSgO5hHZofaioGmvc40IycMgbRtJktjgOZ5Ma9irzSg46xYHcaVEZevkgBHqUWGFK+FENKQ+BdGAq/wiMYWbwHI6h4FwTDOes0BMKFMHxPNg9qn1dANakYanfuQSs5FJoTpaP1qBswsSGgb9+EeUU0Af0LDH4dBhXlmv3wajuOpPYQFDcEojxtNQ6sn9ZzUsiofjfUWg/iYOt+tJatRtvN95DqZgxNuKTKwLV4Jdyqc8Wz1uCGTLjmDIVDQqewQ8anwpJi6GsYkF4Ey2O/QvsfXKlJIgboAwT07s5AZ0G1TylUIsuhdKMI6vcuQ3PVAqg+9UZ8JvGEywiuNoIwD4IzaV2X+HSa1otgE3+NwJImVkycG0kx8snfyUZJW+QFApeSu+hN9BpIn6n+ZBp9bqDv+C8Fum+8IpzzJNOmR3UhTaGFcC07iAHXmamuZw28C/S/aIt+CcthF7+ToN0EQdhqOFzcBu/Sm/ApvAGX3DzYXIiF9jtWTJf74L6ZC83UfGg8SId2xnloSZKxp+gWjC0J6KSrMK8KhmnlSugtInpkCzaBV78Hl5oPoaLpECrLt+Bi4jfgS7t1q+YDUGsPwj5KDFsLlqD97JuIpmpZmP+TftM1ezjlxsOllM4H3eReDWHwKrOBW84jqMeK5OBTv4Bu6HxxgqU1s/N3MkAHSoH+ioCe+gjoJHB0s8ENLID6/UJo3E+GVlwoNEwY278tXhR50RhmeexzgmM8JXjdF36MHwEoiXn70Csv6gxBm8PiRc6gJFD1HDzFpq1cP0omo5QJZAfqQzH0f6uHZjQgeR4cC/IJZCnUtSkYVPAWBiX2/CdU/S7Ql+9TgtFCTaiP0qAEXA2yRsqwuzECziWZcM4tgv2DSljF7ID+l+JNh9+hY38HuvcYmLOhk5EEnVPfQOmpW+33YGaXhj53E2BWuxvGebOh5cPUX/sWSgXrsa9mB2qaDqCK4C7I2IA3jn8u7tat2g6D034MIbWb0fZgHlr2DscXUhNNuYdkYRPrg/7JiXDMLYBrZS6GNEZgVJM/JjWY4I16G4xr/BCDq2nKjjoAvY+Zpwo7eXBskQK9Swr0lEdAn4a2wk3o/DMNWmn54KYUQIuZsebGQuXFQ42H4kfNk4QckSOkNZ1lGkGAUoInOKkAm2jJsVtH+om9Nj9ytZxNcNdhljXByo+JJXj/i4G2u2xM02YInPJLxFB7VudTPH0ZHkWu0hbPpwHpfnAszoFDVgVsb1fDMmoL9L8S7wTFQE/1AvR33oB+QSp0czKgl34B2iO9uwJCKib5SGaZjbqLPlkhMG1YDr1gQyioSs24vQTDitagsnIL6loCUVu9C2EJK9FjYtsWBNP2Q7hb9A155zdwY5mTeGexo0w32hEcy2F7JQaOqZfgk38KY6rDMKFBiGHNt+iGPgCNYd0/s/sbAb2fgN5JQC9Wq8bkR0AzioOOx3Xo30mGbnY+tNMKoJOQCm03qfnFxRf6E1yUFAqZJcyuZRWuQmB+TWHJcgJfkjPxImcSSIUsXviMx/O9DvqfALrPDjb6nhuBAWkZ5JFKKTYuIqhz4FUdAo9CGwzO7Ra2LjUg0w9OxdlwyKxAXzHQm8lDi4HeAT1WMPSHnYXR7aswKE6Gfl4K9PdfgZ6+uG8XSmMbKyXD/LsEmFduglH2NHA7rA3Hvg+Ve1/gYO4KNFRvQUPLQVRU7MG4yn1dJ4eiULAo3JhW9xsa77+Hml8GY8FQ425uAM5wRRivNoPlTjs4XhoH35oLGFZ/S/wglyDkbWmrrsUAvY+A3kHlSwJ6ihKzCvLnuQyElmIs9LdfhmHxA+jn5kI3jcrRFOjxU6DTbTx9DybsOBh0f034EeYEVyaFD0IYhnQ9y1pTIsiPvU5AnKYkUBL78yKmQhDLgDRPSWtPp/HFkFtHqFCfRBr73wX67qsD+qFsEubCnqKBAZllcCkkT12RjSHVMfApH0bJXfcH+aQGZg6FU1EWeeoK2NwgoMM3Q++zP/fq/Smf2g392ZEwzk2Acfl9GBHURmuSYPyn132oHBizH8B8wjX0SadQI2cWtOZZQbHTdEgRn8XN93EiczFayn5GU3Mg7lJMPab5SEeoCWZZ0TF4Ne/A/ZSPUbXdDz9Qdddrrk/KtcwR7jX34VXDzGCFGFT0GzyLu922x069kdiv145tOu34jlOHBWoz4arUAZQt0LYOhmFcHJ2H6zAsYnZDc2FwKhv60+m9UQrLUJ4hSYQAVhpM1O6jj30EDD33Q6frZyoY8cMVaWZZR560kuB5V9H6iVUas+Py5L1/IHsT2ZldR4nEkMdkUd8Y8tYd43mLIMhYhenDWvgjQSQiGFOkiEv0rEAzK2u8yG10M2WwBWFdb6q9NKDNd6rCOuYD9L2VI/57QMfcEniU5cCnJgG+lR9haAnz4MzT5ZjmA4e8HBqnGtYXamF+nK7bpx0uwHxoqGyE3sKD5HHjYVJ1C6Z5qTD5Ph2G1hnQEV/0LBhxU2E+4yYsbgTCJGsuNBfYQrnjA0CPxDo2CRYJ0xGesgD1ZWvQ3LQbKeSJ54uC0UcUDVVRGExFR/FB2y7cSf4C+Zv9sXSUeQ9P2z2pQdnmBHQsPKqKqFCyWJsM75o1GMw8O/iEhFZs/KK9CD9wRfhCTYTP1dqwnBOHrQYz8IuuH5ZxxI/MLQZH5kfoeu6D4cVQGNecgXHFbRgXZsD4Xg5MjqfDeE0KTBbRDLXsLiwOR8HkxCJoOs+Eavdr08ZBBGdYP7rYzAZILsH3LYUYtgSsAXlYRwLqW0r8Ksl2id4/Onaz47IE+kayUfwddYhsgwkqXRrLgOpHEuyhVF9B7ytoTAL//qNjeFagGfGEi5nvYPEifqOx/ek4p1J/8aKBWC8N6Icy2+oL6zOhECTmw46SuoHZpXBn/pK7/DK8K1bCp3Q0vAv7wqfIBD55OuS9teFVYASPfAFccseThw+E4Ho5LOMqYB6ZCeOdK6H1bleJH2sOOPZradqlC3otDqY5F2GafQmmCZdgFnMBZteEML2yCnprh0CZWVp66gbDuD5Q2uSLUacm43jSB0gq+h55JeuRX7wRqUUbkJL8DS4GTcPqCdZgduZ6XiZjgvcp9fIY3aAH/yY+3KvcMDBjLSXQBXDML4VbaQG8a9PgUxcOzyIneKY/Or6FHDO8q7INY+RiMFJaJijE4i2VeEylej/FDs99TAPH8Dvofv8bDK/vhVHxMRhX0W+vOgXTijiY5UXANGkNnYeRUGN2VrsPNx6XVaQNgRNM03sBgUjeOKJJ/Cr+LNzFsg61YB5/elyKtic0qM031CaZAG0gqJnVEuYBIoI49gy9D6DXrQR3GoU2j3YE+WE2FI9TGBG1FLywnhNbPt1Y/OhY+o5iGqsGNmdLaVxfqZUB+g0Iztwi2AOkNZ3FCzOm30bHeHK9tKYHKfPZMFhlAtM9c2EpjALv93zY3qlE/8xyOOUVUTiSBrfy83CvDIdbRZC4uJSGwzHzd0qgkmEVfRnGW/dC79vPobtkFLRmm0HDpVt43MnrzoOm/dfQeeOf0P3wB+guJogXrIDuhHfAsdOFbKdQ5GkaYQbNNYNht2c8/AOnYNKB6Ri//Q14zRwIuohdPC76pCbWKGFCkx9GNC7B0NZD8CiJh8Odi7A59zud7EuwvU4hVUYZBhUXwqsqA56V0RiUM1Dam36UoiyFuprQhc6fRZuKKhV5+rcLKD2hrPQ+NPsvgNb0j6C9eCG0v/kU2l9/BK0ZM8EdRJQ833noG8Qib6lDkA0lYD6i8GIJlffZ/IhhbJtQjW4TP164EiWWztTnH9T+a4L/MxpjAn02hWWYDAQnefSZzm7Io7zDOpiSzGh3grwPwd3zDccPZdH4phBEkXcWBrD4wlE07qObw5pmBUGsK43T/YPfgmAFWEe5U2EeCXhGcV5nQ3u2KrTf6w+jdTNhtud7mB/ZC4vg43QAwbAMDYLF0e3os+8HGP80D7oLx0F9dD+oj9AGZ4Y85K0Yj/Vs3kQiFgeybFPIySiDzdwAz9O3JzHjPNtYk8gjv948FOOatlGodR0Dk07Bau9n0F8wFBp+luBO1CXeuDD51Q3830PRP7UIzgUlcC0vhHPRSdic6eI53ecT3W0sKyjI2EFRxhzyz3sOO8voBkEUTclYhAyshCwr642PR79diwlbBOEs8vLMFjgbbuelhpeoz5rEDxsNNl/+9ON5RWJOLsXCysQdh5IhWWbzhUmoel6v/l/RxGpZTKgbh3EtEZQMp5AX2ASd2f3AVu7695ky/7nOuc2U/BZSCFIGp+I82F/rfprsVa/+Mk0sZ2F0tTvGNZ+gRO8B7C/HQ92beWine+/IDWDBbJUmbBN/hUNOGRyyStH34vfQeP3ZV4R61atXIu9Kefg1rIB/XRJciwso9nymLXmxbP+wxcCsVAxIKwfv1AZoDH96jN6rXr1SuVeowKsuFINrs+BSXATbc59JLU/XwCwdDMw7B/vUEpgHfQYZ7v9HCNar/2E55ynDpSwYrhXF4uKUeQiY0/Oy3kM555nCITcJgmvp0F30Yo8L9KpXL1X9E2XhkPoVBuYWwbmolKDOhmv+WHiXyGNkgbTRE1pOublXkRycCz+AfUoRzPdsgKJN1w/19KpXf7n6xlnCPikE/SkWdswrozDkNoZUfIWhFTYYWaPy4a6NkgSR2XAZXSOLIWUWcCv7FP1T7sH8wFZwp7ycxz971auXIm4AG+b77MFLEKLv7ULJMy0FefCsPAOv0t0YUrIMg0s+gVfxYrgVbIJLUSzsrl2F2ZZl4L7J/Pdp/956ca969UrEna0O41/HwSJ4F3in42Fz5Trsbt5Bv3u30e9uImyvnoV15GGY/LIA6kOZP1966pZ8r3r1n5eqhwZ0F/aB4ToHGK9zh/FPHjD60RE6H1tDaaA2cdy7mvFfI+BffksPNrEksu0AAAAASUVORK5CYII='; + + function buildImageUrl(imageryProvider, x, y, level) { + var imageUrl = imageryProvider._imageUrlTemplate; + + imageUrl = imageUrl.replace('{x}', x); + imageUrl = imageUrl.replace('{y}', y); + // Google Earth starts with a zoom level of 1, not 0 + imageUrl = imageUrl.replace('{zoom}', (level + 1)); + + var proxy = imageryProvider._proxy; + if (defined(proxy)) { + imageUrl = proxy.getURL(imageUrl); + } + + return imageUrl; + } + + return GoogleEarthEnterpriseMapsProvider; +}); + diff --git a/Source/Scene/GoogleEarthImageryProvider.js b/Source/Scene/GoogleEarthImageryProvider.js index 8dc4d9e283e2..cca47c60bf32 100644 --- a/Source/Scene/GoogleEarthImageryProvider.js +++ b/Source/Scene/GoogleEarthImageryProvider.js @@ -1,595 +1,36 @@ /*global define*/ define([ - '../Core/Credit', - '../Core/defaultValue', - '../Core/defined', - '../Core/defineProperties', - '../Core/DeveloperError', - '../Core/Event', - '../Core/GeographicTilingScheme', - '../Core/loadText', - '../Core/Rectangle', - '../Core/RuntimeError', - '../Core/TileProviderError', - '../Core/WebMercatorTilingScheme', - '../ThirdParty/when', - './ImageryProvider' + '../Core/deprecationWarning', + './GoogleEarthEnterpriseMapsProvider' ], function( - Credit, - defaultValue, - defined, - defineProperties, - DeveloperError, - Event, - GeographicTilingScheme, - loadText, - Rectangle, - RuntimeError, - TileProviderError, - WebMercatorTilingScheme, - when, - ImageryProvider) { + deprecationWarning, + GoogleEarthEnterpriseMapsProvider) { 'use strict'; /** * Provides tiled imagery using the Google Earth Imagery API. * - * Notes: This imagery provider does not work with the public Google Earth servers. It works with the - * Google Earth Enterprise Server. - * - * By default the Google Earth Enterprise server does not set the - * {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} headers. You can either - * use a proxy server which adds these headers, or in the /opt/google/gehttpd/conf/gehttpd.conf - * and add the 'Header set Access-Control-Allow-Origin "*"' option to the '<Directory />' and - * '<Directory "/opt/google/gehttpd/htdocs">' directives. + * Notes: This imagery provider was deprecated in Cesium 1.35 and replaced with {@link GoogleEarthEnterpriseMapsProvider}. + * These are for use with the 2D Maps API. For 3D Earth API uses, see {@link GoogleEarthEnterpriseImageryProvider}. + * GoogleEarthImageryProvider will be removed in Cesium 1.37. * * @alias GoogleEarthImageryProvider * @constructor + * @deprecated * - * @param {Object} options Object with the following properties: - * @param {String} options.url The url of the Google Earth server hosting the imagery. - * @param {Number} options.channel The channel (id) to be used when requesting data from the server. - * The channel number can be found by looking at the json file located at: - * earth.localdomain/default_map/query?request=Json&vars=geeServerDefs The /default_map path may - * differ depending on your Google Earth Enterprise server configuration. Look for the "id" that - * is associated with a "ImageryMaps" requestType. There may be more than one id available. - * Example: - * { - * layers: [ - * { - * id: 1002, - * requestType: "ImageryMaps" - * }, - * { - * id: 1007, - * requestType: "VectorMapsRaster" - * } - * ] - * } - * @param {String} [options.path="/default_map"] The path of the Google Earth server hosting the imagery. - * @param {Number} [options.maximumLevel] The maximum level-of-detail supported by the Google Earth - * Enterprise server, or undefined if there is no limit. - * @param {TileDiscardPolicy} [options.tileDiscardPolicy] The policy that determines if a tile - * is invalid and should be discarded. To ensure that no tiles are discarded, construct and pass - * a {@link NeverTileDiscardPolicy} for this parameter. - * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. - * @param {Proxy} [options.proxy] A proxy to use for requests. This object is - * expected to have a getURL function which returns the proxied URL, if needed. - * - * @exception {RuntimeError} Could not find layer with channel (id) of options.channel. - * @exception {RuntimeError} Could not find a version in channel (id) options.channel. - * @exception {RuntimeError} Unsupported projection data.projection. - * - * @see ArcGisMapServerImageryProvider - * @see BingMapsImageryProvider - * @see createOpenStreetMapImageryProvider - * @see SingleTileImageryProvider - * @see createTileMapServiceImageryProvider - * @see WebMapServiceImageryProvider - * @see WebMapTileServiceImageryProvider - * @see UrlTemplateImageryProvider - * + * @see GoogleEarthEnterpriseMapsProvider * * @example * var google = new Cesium.GoogleEarthImageryProvider({ * url : 'https://earth.localdomain', * channel : 1008 * }); - * - * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} */ function GoogleEarthImageryProvider(options) { - options = defaultValue(options, {}); - - //>>includeStart('debug', pragmas.debug); - if (!defined(options.url)) { - throw new DeveloperError('options.url is required.'); - } - if (!defined(options.channel)) { - throw new DeveloperError('options.channel is required.'); - } - //>>includeEnd('debug'); - - this._url = options.url; - this._path = defaultValue(options.path, '/default_map'); - this._tileDiscardPolicy = options.tileDiscardPolicy; - this._proxy = options.proxy; - this._channel = options.channel; - this._requestType = 'ImageryMaps'; - this._credit = new Credit('Google Imagery', GoogleEarthImageryProvider._logoData, 'http://www.google.com/enterprise/mapsearth/products/earthenterprise.html'); - - /** - * The default {@link ImageryLayer#gamma} to use for imagery layers created for this provider. - * By default, this is set to 1.9. Changing this value after creating an {@link ImageryLayer} for this provider will have - * no effect. Instead, set the layer's {@link ImageryLayer#gamma} property. - * - * @type {Number} - * @default 1.9 - */ - this.defaultGamma = 1.9; - - this._tilingScheme = undefined; - - this._version = undefined; - - - this._tileWidth = 256; - this._tileHeight = 256; - this._maximumLevel = options.maximumLevel; - this._imageUrlTemplate = this._url + this._path + '/query?request={request}&channel={channel}&version={version}&x={x}&y={y}&z={zoom}'; - - this._errorEvent = new Event(); - - this._ready = false; - this._readyPromise = when.defer(); - - var metadataUrl = this._url + this._path + '/query?request=Json&vars=geeServerDefs&is2d=t'; - var that = this; - var metadataError; - - function metadataSuccess(text) { - var data; - - // The Google Earth server sends malformed JSON data currently... - try { - // First, try parsing it like normal in case a future version sends correctly formatted JSON - data = JSON.parse(text); - } catch(e) { - // Quote object strings manually, then try parsing again - data = JSON.parse(text.replace(/([\[\{,])[\n\r ]*([A-Za-z0-9]+)[\n\r ]*:/g, '$1"$2":')); - } - - var layer; - for(var i = 0; i < data.layers.length; i++) { - if(data.layers[i].id === that._channel) { - layer = data.layers[i]; - break; - } - } - - var message; + deprecationWarning('GoogleEarthImageryProvider', 'GoogleEarthImageryProvider was deprecated in Cesium 1.35, it will be removed in 1.37. Use GoogleEarthEnterpriseMapsProvider instead.'); - if(!defined(layer)) { - message = 'Could not find layer with channel (id) of ' + that._channel + '.'; - metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); - throw new RuntimeError(message); - } - - if(!defined(layer.version)) { - message = 'Could not find a version in channel (id) ' + that._channel + '.'; - metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); - throw new RuntimeError(message); - } - that._version = layer.version; - - if(defined(data.projection) && data.projection === 'flat') { - that._tilingScheme = new GeographicTilingScheme({ - numberOfLevelZeroTilesX : 2, - numberOfLevelZeroTilesY : 2, - rectangle: new Rectangle(-Math.PI, -Math.PI, Math.PI, Math.PI), - ellipsoid : options.ellipsoid - }); - // Default to mercator projection when projection is undefined - } else if(!defined(data.projection) || data.projection === 'mercator') { - that._tilingScheme = new WebMercatorTilingScheme({ - numberOfLevelZeroTilesX : 2, - numberOfLevelZeroTilesY : 2, - ellipsoid : options.ellipsoid - }); - } else { - message = 'Unsupported projection ' + data.projection + '.'; - metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); - throw new RuntimeError(message); - } - - that._imageUrlTemplate = that._imageUrlTemplate.replace('{request}', that._requestType) - .replace('{channel}', that._channel).replace('{version}', that._version); - - that._ready = true; - that._readyPromise.resolve(true); - TileProviderError.handleSuccess(metadataError); - } - - function metadataFailure(e) { - var message = 'An error occurred while accessing ' + metadataUrl + '.'; - metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); - that._readyPromise.reject(new RuntimeError(message)); - } - - function requestMetadata() { - var url = (!defined(that._proxy)) ? metadataUrl : that._proxy.getURL(metadataUrl); - - var metadata = loadText(url); - when(metadata, metadataSuccess, metadataFailure); - } - - requestMetadata(); - } - - defineProperties(GoogleEarthImageryProvider.prototype, { - /** - * Gets the URL of the Google Earth MapServer. - * @memberof GoogleEarthImageryProvider.prototype - * @type {String} - * @readonly - */ - url : { - get : function() { - return this._url; - } - }, - - /** - * Gets the url path of the data on the Google Earth server. - * @memberof GoogleEarthImageryProvider.prototype - * @type {String} - * @readonly - */ - path : { - get : function() { - return this._path; - } - }, - - /** - * Gets the proxy used by this provider. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Proxy} - * @readonly - */ - proxy : { - get : function() { - return this._proxy; - } - }, - - /** - * Gets the imagery channel (id) currently being used. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - channel : { - get : function() { - return this._channel; - } - }, - - /** - * Gets the width of each tile, in pixels. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - tileWidth : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('tileWidth must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._tileWidth; - } - }, - - /** - * Gets the height of each tile, in pixels. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - tileHeight: { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._tileHeight; - } - }, - - /** - * Gets the maximum level-of-detail that can be requested. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - maximumLevel : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('maximumLevel must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._maximumLevel; - } - }, - - /** - * Gets the minimum level-of-detail that can be requested. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - minimumLevel : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('minimumLevel must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return 0; - } - }, - - /** - * Gets the tiling scheme used by this provider. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {TilingScheme} - * @readonly - */ - tilingScheme : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._tilingScheme; - } - }, - - /** - * Gets the version of the data used by this provider. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Number} - * @readonly - */ - version : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('version must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._version; - } - }, - - /** - * Gets the type of data that is being requested from the provider. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {String} - * @readonly - */ - requestType : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('requestType must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._requestType; - } - }, - /** - * Gets the rectangle, in radians, of the imagery provided by this instance. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Rectangle} - * @readonly - */ - rectangle : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('rectangle must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._tilingScheme.rectangle; - } - }, - - /** - * Gets the tile discard policy. If not undefined, the discard policy is responsible - * for filtering out "missing" tiles via its shouldDiscardImage function. If this function - * returns undefined, no tiles are filtered. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {TileDiscardPolicy} - * @readonly - */ - tileDiscardPolicy : { - get : function() { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('tileDiscardPolicy must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - return this._tileDiscardPolicy; - } - }, - - /** - * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing - * to the event, you will be notified of the error and can potentially recover from it. Event listeners - * are passed an instance of {@link TileProviderError}. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Event} - * @readonly - */ - errorEvent : { - get : function() { - return this._errorEvent; - } - }, - - /** - * Gets a value indicating whether or not the provider is ready for use. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Boolean} - * @readonly - */ - ready : { - get : function() { - return this._ready; - } - }, - - /** - * Gets a promise that resolves to true when the provider is ready for use. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Promise.} - * @readonly - */ - readyPromise : { - get : function() { - return this._readyPromise.promise; - } - }, - - /** - * Gets the credit to display when this imagery provider is active. Typically this is used to credit - * the source of the imagery. This function should not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Credit} - * @readonly - */ - credit : { - get : function() { - return this._credit; - } - }, - - /** - * Gets a value indicating whether or not the images provided by this imagery provider - * include an alpha channel. If this property is false, an alpha channel, if present, will - * be ignored. If this property is true, any images without an alpha channel will be treated - * as if their alpha is 1.0 everywhere. When this property is false, memory usage - * and texture upload time are reduced. - * @memberof GoogleEarthImageryProvider.prototype - * @type {Boolean} - * @readonly - */ - hasAlphaChannel : { - get : function() { - return true; - } - } - }); - - /** - * Gets the credits to be displayed when a given tile is displayed. - * - * @param {Number} x The tile X coordinate. - * @param {Number} y The tile Y coordinate. - * @param {Number} level The tile level; - * @returns {Credit[]} The credits to be displayed when the tile is displayed. - * - * @exception {DeveloperError} getTileCredits must not be called before the imagery provider is ready. - */ - GoogleEarthImageryProvider.prototype.getTileCredits = function(x, y, level) { - return undefined; - }; - - /** - * Requests the image for a given tile. This function should - * not be called before {@link GoogleEarthImageryProvider#ready} returns true. - * - * @param {Number} x The tile X coordinate. - * @param {Number} y The tile Y coordinate. - * @param {Number} level The tile level. - * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or - * undefined if there are too many active requests to the server, and the request - * should be retried later. The resolved image may be either an - * Image or a Canvas DOM object. - * - * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. - */ - GoogleEarthImageryProvider.prototype.requestImage = function(x, y, level) { - //>>includeStart('debug', pragmas.debug); - if (!this._ready) { - throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); - } - //>>includeEnd('debug'); - - var url = buildImageUrl(this, x, y, level); - return ImageryProvider.loadImage(this, url); - }; - - /** - * Picking features is not currently supported by this imagery provider, so this function simply returns - * undefined. - * - * @param {Number} x The tile X coordinate. - * @param {Number} y The tile Y coordinate. - * @param {Number} level The tile level. - * @param {Number} longitude The longitude at which to pick features. - * @param {Number} latitude The latitude at which to pick features. - * @return {Promise.|undefined} A promise for the picked features that will resolve when the asynchronous - * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo} - * instances. The array may be empty if no features are found at the given location. - * It may also be undefined if picking is not supported. - */ - GoogleEarthImageryProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) { - return undefined; - }; - - GoogleEarthImageryProvider._logoData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAAnCAYAAACmP2LfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAHdElNRQfcDB4TJDr1mp5kAAAAGnRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuNS4xMDD0cqEAAB1zSURBVHhe7ZwHeFTFFsf/u+l9N70npOxuSAKEFFIhCSH0qhEQUHkgKCgWUFGBB6IoCAoo0ntooaRvEkIIBBBpoYSa3nvvfd+5u4sQUigPfMX8v2/Y3Tkzs3fv/d0z58zcgF69Ql1SY+MM1wQJem44ZeiJk8beEOqPwG6uC7ZqyElb9eo/JZEIkH2nRQkBIlNMauuPCS3uGN/kjkmNDghoskBAgzrZ2NLmf1+JwIKQpYsoxdmIV9+N07onCegzBPM9bOdmYKnazF6g/1N6UySPqSJzvCaaiLHtP8G/Phq+FRfgU5ogKWUXMLT6Mvzqr2BE40mMadqO8c3zMabBC6PqDDC8SlY60t9HByCLVTKu+ERmHr5TWI9wjVxEaOZivWo1pil8D1tZeWnLXv1l8iZ3PF2kjymiWRgvCoJv5U243IyAXcQq8A9Mg9W+4bDe6wv+kVGwCZkL+4Sf4ZR+BZ5VGQR3EkbWn8Hopm3wq54Lz2JD6ah/P21XGopQ9Qoc16jGSqVyTJWbQbUsibFXf42mihTwZpsvAtp3k0dOhFOSEH1+ngaDefrgjFCgFkxY8fCisCBvKgODzxRh9qslBFGfYmDGLbiV5mBwRRo8KtPhVBgPu8teMP7u73chD6kMRYRGBY5xqrFKqQwz5SdTbS/Qf5mmUYw8rf01CjHC4VP7AHZxO6E3qy9ZZCQNnio2rE/4o9/tkxiQUYp+KRXgx8XC5FsXcLz/hkCrDUU4pxLHuDVYpdwL9F+qqSJZKlPwenskfOoI5tN7YPCJGVme7wKYr5EBXzgYfW+mwTI0Gjrznaj2WW+I/y8dVPdDGLcKRzXrsEqlHO8oTKHaXqAZWe9hQXCi63NhHWYI3ilfWIW/YLjqL2JRiOFBJRz+LffhcPs09D+0J8vzn3zXdBnYnp8Mi6NboTWzH9X8fVc+DhDQodxqAroe36lU9AJNWr4cEAjNwI8OAC9cT1rbUfzwGeCfKiL7dGnNc+q1NiO80b4BY1oT4V6WDcsdc6j2xbyq4wMWrA9rQmeWFn36ey/jBaoPQ4hmLYI0G/AtAf22fC/QDols8ITrIYi/Bl6knbS2o3gRbxHQxQQ0k0S/gCa2v4OJovPwacqAQ1ICjL40klr+UrWoQbFBETo18jCpZsOoFODkvuCNJYoHW3QKXFEM7ETRcKfiQe8d6NVIFImXvg4skhY40mxnQYVRIIeA1qrHEc1GrFSpxFtP99AiFbDbNKDZpAzzGkVYVcvBuBJQEo/9/6C+dyjPitwLwak74D8V6Bfw0P5VShjXFoTR7TfhUZkL29M/wfATJan1lauWC3aDOgyaVDCuTgbf1bFkfmtkye1ogsK2asivLYfCglIoD8qCknI2NHuG4QSVGMgQyMbt0fioRYh9VYcRU7QX55uDcaHtFOJEsThMtmWtQgxsDodsWaC0c3ea3MzGBJEqxrfbYmzr6xjfPAeTmt5HQPO7eK1xDibUz8eY+k8xtHYJPCtXwvHOu7AXMrMTsF/TH8HajTis1YwVqpWY0TXQDKy1OpBr5EJA52Fukxx+bmKxtjWx2DuaWawNlZD5qhzyo9KhpHAbKpJO/6t65UCPbPHA2PYrGNacgkElabCJJDev/MpDhUKKnuq44LRoYEK1IiswkS1zYCfk5y+F0qjvoTwqBOof34dGeAnUL1ZCLboEnJ9zoe0QD/Nuj00UBVXRabzVLETM3S0ICfwA8yc7Y6C3ANYbZsA7aQ1W1xzEfZEQ6dT2BkG9pP4ouo7jGE1u42JS20QMrzkCr4xwuN4+AM+cYII3EaNar2J86zmMrP8DHulCON4NhU3YWuhOYy6SZENpH9cfx7WacFC7BSvUqjBDsRPQIiugURvazeqYVaqAw6dYrJ9WQy7gayj4nYDy3HtQOVQGpYRqKEWXQf2HdGha/AFdae9Xr4czz0ubISRA75ECbSut7agegO75OLxpahze8j5GtifBpzEDLiV30Dd2mNT6StWiCbVmLt5rUkBQCEt2zWzIMSA8HgrIBkLD+Sp0jhHISYXQ/KMYukfvQ3fQxq68XCTBHId/tMTg7LV1CFs4BszJ6hBarBgHlcRv8H7tbuSKQpFPYGe0BmND+nZ0npECaPKf0r4UIxsuoF/IMpitsAVnrA4s15uh3x8fwLXkLobUZGJIXTqcUzbDaJE5FAVq0t4S7dEcjqMEc6B2K5arVWN6Z6AbdOmm5mJelQKOHWSxF44Cy4CqxW0s6RwchCovFRohdGNfLgX3WiZ0N4aD++y7jfwYJUrAPCle/ZjKV+BFTSegrGAZIm3QjXhBytTWB3zhByzryMUU986jz16wD+96ijCNUIAgmkc3tS6G7GERjCbgR82B4OTbEESqIiCIcqsIYzoGGyrBEMSmgh8xBoIIAR2fAHZhj8Z9DOhl9FHeKkSDvn809fuc+iyCddRYaiOZBTvIt1YJfs0b4N+WDO+GHPLQN2Ab7S61vjJV60C9SRPvNSqzTpxlyQfS1dGUmjppK7gW16B/LhN6abnQu5cDwzO3YNhhqqK4WJY887sEdGzWFpxfOxmDpKZOOvgWFB8sx9L6nShvP4FyUQjKGg5gScpGKEqbUE7RxiGYv6QQ4zIG/r4D2m88sjEy/EIW/a6+TQ4gHe5VhXCvy4JL7gLYnesI2i6t4Tii04r92u1YKt767gB0ozrkGzmY26zEOh7Hkt+kAKhLTX9qOVVdg9aoNOjcToR+wUVKLYKgN0Zq7l7884wn9CKgr4AfWw/B6SwqKQRKOdXVghe9CpbherASSjtIpGpxRIHFjwygNreoXy0lb+lU7lHJBP9kPcGXQnBNghUB/Lh44fbUp5JA+5Hs71LbPPLCVRDEJZDNGIJgeQI6mG6KegKzldq1U7tGKjQmHR8vwl86kgRoAQN0xBw6ztn0nQ/ocxEdQ7L4d/BjG6g+m8aZTL/xsXPuW82Fb8t+DG1Ox5D6XAwqvQ67OA+p9ZWoUQPsei78mjSwNU9GLmEzVGZJTd3qFPTn3YZhXgYMMjNhlHsDxms/hNWfoUdrNPgEc2h7BG5d/Bo7Blt0BuNxXf4MVmXrkdRyEHWiY6hr2oc7mevRX2wc18gioEeI1+N9a+/CNnImVAZ0mhEoNOPAJT8MHjUF8KTiWhqHgbfMpVaJdhLQh3XasU9bJAZ6ekeg6zQwgEKuLSWysmd3QGmatLqD8qDNug3dCX/AIPk4jGr2wDB/JXTmkan70IvmZTY/rB9BdZlKLkG0lG0d5klAObKsw1+jzyFiWPnRawiaDrMYwTyMwMwh220WP2IWFVfqN4CKO8E3n0C6R/ZUej9Y2kUiMdDRFTRePH3nA3q/m7xpAEtAXl0QrkTwscnmS/3eptdzNEYevZLnZ5booqk8tuYs9tAny+n1LL1mghezlcULH0VtHamOZhvhIvoNOXQsd2EZIbluYnlWaMO75TCFG9kYXJ8H14o76H/10Z3yClSrCm6jGtbWK7LC7kIlYRfUmY2XHnUa+mbXYRSfCuNCptyE6b1jMBD/EPKwchQPLxGdxOWWI8iKXYBPqLozgI8pfA5YBWvxbfMeNLUfRmPTLjRnr8YKsdGvRQ5j2zZTSSRQ78H+7GhxfScFAINypsG9ukDspZ0LKKE+O0pqlGi71ggcIqD3dga6RhFKjSqYT+VEFkvu/E9Q+HNWKaE2VVDgVkPFqwAaay5CN3En9M59BM2vfKDs7AvljjPGE5LlharQdL+LoCmhOHU0rIUyD+NgVTOa+q2iVQiIcAKpHtbhXuJOjPqeVCRYThNE6VTvKNs3hM3cHGIxntxKyCbP7Erj1lHZJbVIJAG6iiCroZCAPGukvOyASJbvCgoaAoKoAQ1kHcGC7nmZDkmhBR2PfSQLtkcl4zCSAE2eO6qExYuYxrE4KqdvelBiM4+ncYQy1IY8d0wbhUSLJAZGbsUceNYdwJCGPAyuy4NbZToG3JoO1Qk9AvHvqF4ejo0KCKlisyl04Jw+AE1ma71HRUJP+QqM1t2HcVEyTEoSYVYQCuN3HenCt4XDhGA+KorAnYZ9KIj5ELOl3XpU/k/wrt+OmraDaG7cjpacbxFvYAAZDG5Vw/DWCxjRdp+ATsWAS6+D69H1+XDNsoVb1T06b0VwzCmBIOYdqUWibTojcFBH1CXQctBtUcA6Oh/RmVC4sBmKA5j6erC1qqE4sRpqG25A43QIOHuXgvOmP5R4ZH6m5UY2L9SSLjZ5sKjjsI/o8olH8ngjCZoSgmw9DMIl3t42Up0g+pq89/sEjLK47knZhSkSuDepJP4JOyNJyEFAR8VQKMOR1nbWM69yxNJYwh+VLE90ffPyxLE3EwL9Jq0huWQqwL1iA7zq8+FVl0+epgBO6T+gb2TH+OglqgastxtZrNNlkLt8E5oJx6HZdab7mFZBk3UZRjMewCT7HkzLfodZxREYr5sBjiIBPYiAPt8ehvSGPSg5vwjzpd16VNkmmDTswp22QDTXbkJrxhJkzHGDFoUQmvBpvo2hrZl0TnLhlLIYfUO7nt7dSg3hURcP1/JiDEgphuXBqVKLRFsfA3oJAf3mI6Cr2OjTwGYdqWGzzmZD6WoYVCfehdqsZKjuuwS1oB1Q+5piHac3oaxBzZ9vLZ4nHEeesoXg6niDPSYWP9yUgD5PHu48eKE64krHcErchHIEuRysTpAXjObQWIYEHiV4EQYEojp5aEoyY+IIpOQugKYYOnIdJXrdJ63PtWwXMQM6m6SVT4gfZkbHV0XHsVtaQ3K8yoJr0YfwoHDDq5ZiQSqDik/B4Q9taYtn18gyNia1qGJsmTrGlUjK2FJ1jCjRwOASDnkxDvN95ZD/og5yl0qgfCMJ2leDoeksHaFHXYOJVyrMkm/DrPwMzGr2wmjnLGipthyHL0W7t9pDkduwF2U3lmGFtvbTdyirt0OreT+iWwPRUrUBbSkLkT/fCUZwKVYikBMwpDlPXNzLwuAQ2rWX8KzUh2dDDJyLSmB7/S5Mf3WRWiR6CPSezkCXQs6qBnLCKsheyoXqnTCoL9oOFd9/Qtl9KJT6UJMX3/zhCz8iuCjhiviSYtMx3ZTJBN8lCE7eIRgF0p6krRRaRBDskTTGySBKws5SuUjJHYUiMQdpzCUE0Q3y5MnSDhJJQg5JUvjSgO5hHZofaioGmvc40IycMgbRtJktjgOZ5Ma9irzSg46xYHcaVEZevkgBHqUWGFK+FENKQ+BdGAq/wiMYWbwHI6h4FwTDOes0BMKFMHxPNg9qn1dANakYanfuQSs5FJoTpaP1qBswsSGgb9+EeUU0Af0LDH4dBhXlmv3wajuOpPYQFDcEojxtNQ6sn9ZzUsiofjfUWg/iYOt+tJatRtvN95DqZgxNuKTKwLV4Jdyqc8Wz1uCGTLjmDIVDQqewQ8anwpJi6GsYkF4Ey2O/QvsfXKlJIgboAwT07s5AZ0G1TylUIsuhdKMI6vcuQ3PVAqg+9UZ8JvGEywiuNoIwD4IzaV2X+HSa1otgE3+NwJImVkycG0kx8snfyUZJW+QFApeSu+hN9BpIn6n+ZBp9bqDv+C8Fum+8IpzzJNOmR3UhTaGFcC07iAHXmamuZw28C/S/aIt+CcthF7+ToN0EQdhqOFzcBu/Sm/ApvAGX3DzYXIiF9jtWTJf74L6ZC83UfGg8SId2xnloSZKxp+gWjC0J6KSrMK8KhmnlSugtInpkCzaBV78Hl5oPoaLpECrLt+Bi4jfgS7t1q+YDUGsPwj5KDFsLlqD97JuIpmpZmP+TftM1ezjlxsOllM4H3eReDWHwKrOBW84jqMeK5OBTv4Bu6HxxgqU1s/N3MkAHSoH+ioCe+gjoJHB0s8ENLID6/UJo3E+GVlwoNEwY278tXhR50RhmeexzgmM8JXjdF36MHwEoiXn70Csv6gxBm8PiRc6gJFD1HDzFpq1cP0omo5QJZAfqQzH0f6uHZjQgeR4cC/IJZCnUtSkYVPAWBiX2/CdU/S7Ql+9TgtFCTaiP0qAEXA2yRsqwuzECziWZcM4tgv2DSljF7ID+l+JNh9+hY38HuvcYmLOhk5EEnVPfQOmpW+33YGaXhj53E2BWuxvGebOh5cPUX/sWSgXrsa9mB2qaDqCK4C7I2IA3jn8u7tat2g6D034MIbWb0fZgHlr2DscXUhNNuYdkYRPrg/7JiXDMLYBrZS6GNEZgVJM/JjWY4I16G4xr/BCDq2nKjjoAvY+Zpwo7eXBskQK9Swr0lEdAn4a2wk3o/DMNWmn54KYUQIuZsebGQuXFQ42H4kfNk4QckSOkNZ1lGkGAUoInOKkAm2jJsVtH+om9Nj9ytZxNcNdhljXByo+JJXj/i4G2u2xM02YInPJLxFB7VudTPH0ZHkWu0hbPpwHpfnAszoFDVgVsb1fDMmoL9L8S7wTFQE/1AvR33oB+QSp0czKgl34B2iO9uwJCKib5SGaZjbqLPlkhMG1YDr1gQyioSs24vQTDitagsnIL6loCUVu9C2EJK9FjYtsWBNP2Q7hb9A155zdwY5mTeGexo0w32hEcy2F7JQaOqZfgk38KY6rDMKFBiGHNt+iGPgCNYd0/s/sbAb2fgN5JQC9Wq8bkR0AzioOOx3Xo30mGbnY+tNMKoJOQCm03qfnFxRf6E1yUFAqZJcyuZRWuQmB+TWHJcgJfkjPxImcSSIUsXviMx/O9DvqfALrPDjb6nhuBAWkZ5JFKKTYuIqhz4FUdAo9CGwzO7Ra2LjUg0w9OxdlwyKxAXzHQm8lDi4HeAT1WMPSHnYXR7aswKE6Gfl4K9PdfgZ6+uG8XSmMbKyXD/LsEmFduglH2NHA7rA3Hvg+Ve1/gYO4KNFRvQUPLQVRU7MG4yn1dJ4eiULAo3JhW9xsa77+Hml8GY8FQ425uAM5wRRivNoPlTjs4XhoH35oLGFZ/S/wglyDkbWmrrsUAvY+A3kHlSwJ6ihKzCvLnuQyElmIs9LdfhmHxA+jn5kI3jcrRFOjxU6DTbTx9DybsOBh0f034EeYEVyaFD0IYhnQ9y1pTIsiPvU5AnKYkUBL78yKmQhDLgDRPSWtPp/HFkFtHqFCfRBr73wX67qsD+qFsEubCnqKBAZllcCkkT12RjSHVMfApH0bJXfcH+aQGZg6FU1EWeeoK2NwgoMM3Q++zP/fq/Smf2g392ZEwzk2Acfl9GBHURmuSYPyn132oHBizH8B8wjX0SadQI2cWtOZZQbHTdEgRn8XN93EiczFayn5GU3Mg7lJMPab5SEeoCWZZ0TF4Ne/A/ZSPUbXdDz9Qdddrrk/KtcwR7jX34VXDzGCFGFT0GzyLu922x069kdiv145tOu34jlOHBWoz4arUAZQt0LYOhmFcHJ2H6zAsYnZDc2FwKhv60+m9UQrLUJ4hSYQAVhpM1O6jj30EDD33Q6frZyoY8cMVaWZZR560kuB5V9H6iVUas+Py5L1/IHsT2ZldR4nEkMdkUd8Y8tYd43mLIMhYhenDWvgjQSQiGFOkiEv0rEAzK2u8yG10M2WwBWFdb6q9NKDNd6rCOuYD9L2VI/57QMfcEniU5cCnJgG+lR9haAnz4MzT5ZjmA4e8HBqnGtYXamF+nK7bpx0uwHxoqGyE3sKD5HHjYVJ1C6Z5qTD5Ph2G1hnQEV/0LBhxU2E+4yYsbgTCJGsuNBfYQrnjA0CPxDo2CRYJ0xGesgD1ZWvQ3LQbKeSJ54uC0UcUDVVRGExFR/FB2y7cSf4C+Zv9sXSUeQ9P2z2pQdnmBHQsPKqKqFCyWJsM75o1GMw8O/iEhFZs/KK9CD9wRfhCTYTP1dqwnBOHrQYz8IuuH5ZxxI/MLQZH5kfoeu6D4cVQGNecgXHFbRgXZsD4Xg5MjqfDeE0KTBbRDLXsLiwOR8HkxCJoOs+Eavdr08ZBBGdYP7rYzAZILsH3LYUYtgSsAXlYRwLqW0r8Ksl2id4/Onaz47IE+kayUfwddYhsgwkqXRrLgOpHEuyhVF9B7ytoTAL//qNjeFagGfGEi5nvYPEifqOx/ek4p1J/8aKBWC8N6Icy2+oL6zOhECTmw46SuoHZpXBn/pK7/DK8K1bCp3Q0vAv7wqfIBD55OuS9teFVYASPfAFccseThw+E4Ho5LOMqYB6ZCeOdK6H1bleJH2sOOPZradqlC3otDqY5F2GafQmmCZdgFnMBZteEML2yCnprh0CZWVp66gbDuD5Q2uSLUacm43jSB0gq+h55JeuRX7wRqUUbkJL8DS4GTcPqCdZgduZ6XiZjgvcp9fIY3aAH/yY+3KvcMDBjLSXQBXDML4VbaQG8a9PgUxcOzyIneKY/Or6FHDO8q7INY+RiMFJaJijE4i2VeEylej/FDs99TAPH8Dvofv8bDK/vhVHxMRhX0W+vOgXTijiY5UXANGkNnYeRUGN2VrsPNx6XVaQNgRNM03sBgUjeOKJJ/Cr+LNzFsg61YB5/elyKtic0qM031CaZAG0gqJnVEuYBIoI49gy9D6DXrQR3GoU2j3YE+WE2FI9TGBG1FLywnhNbPt1Y/OhY+o5iGqsGNmdLaVxfqZUB+g0Iztwi2AOkNZ3FCzOm30bHeHK9tKYHKfPZMFhlAtM9c2EpjALv93zY3qlE/8xyOOUVUTiSBrfy83CvDIdbRZC4uJSGwzHzd0qgkmEVfRnGW/dC79vPobtkFLRmm0HDpVt43MnrzoOm/dfQeeOf0P3wB+guJogXrIDuhHfAsdOFbKdQ5GkaYQbNNYNht2c8/AOnYNKB6Ri//Q14zRwIuohdPC76pCbWKGFCkx9GNC7B0NZD8CiJh8Odi7A59zud7EuwvU4hVUYZBhUXwqsqA56V0RiUM1Dam36UoiyFuprQhc6fRZuKKhV5+rcLKD2hrPQ+NPsvgNb0j6C9eCG0v/kU2l9/BK0ZM8EdRJQ833noG8Qib6lDkA0lYD6i8GIJlffZ/IhhbJtQjW4TP164EiWWztTnH9T+a4L/MxpjAn02hWWYDAQnefSZzm7Io7zDOpiSzGh3grwPwd3zDccPZdH4phBEkXcWBrD4wlE07qObw5pmBUGsK43T/YPfgmAFWEe5U2EeCXhGcV5nQ3u2KrTf6w+jdTNhtud7mB/ZC4vg43QAwbAMDYLF0e3os+8HGP80D7oLx0F9dD+oj9AGZ4Y85K0Yj/Vs3kQiFgeybFPIySiDzdwAz9O3JzHjPNtYk8gjv948FOOatlGodR0Dk07Bau9n0F8wFBp+luBO1CXeuDD51Q3830PRP7UIzgUlcC0vhHPRSdic6eI53ecT3W0sKyjI2EFRxhzyz3sOO8voBkEUTclYhAyshCwr642PR79diwlbBOEs8vLMFjgbbuelhpeoz5rEDxsNNl/+9ON5RWJOLsXCysQdh5IhWWbzhUmoel6v/l/RxGpZTKgbh3EtEZQMp5AX2ASd2f3AVu7695ky/7nOuc2U/BZSCFIGp+I82F/rfprsVa/+Mk0sZ2F0tTvGNZ+gRO8B7C/HQ92beWine+/IDWDBbJUmbBN/hUNOGRyyStH34vfQeP3ZV4R61atXIu9Kefg1rIB/XRJciwso9nymLXmxbP+wxcCsVAxIKwfv1AZoDH96jN6rXr1SuVeowKsuFINrs+BSXATbc59JLU/XwCwdDMw7B/vUEpgHfQYZ7v9HCNar/2E55ynDpSwYrhXF4uKUeQiY0/Oy3kM555nCITcJgmvp0F30Yo8L9KpXL1X9E2XhkPoVBuYWwbmolKDOhmv+WHiXyGNkgbTRE1pOublXkRycCz+AfUoRzPdsgKJN1w/19KpXf7n6xlnCPikE/SkWdswrozDkNoZUfIWhFTYYWaPy4a6NkgSR2XAZXSOLIWUWcCv7FP1T7sH8wFZwp7ycxz971auXIm4AG+b77MFLEKLv7ULJMy0FefCsPAOv0t0YUrIMg0s+gVfxYrgVbIJLUSzsrl2F2ZZl4L7J/Pdp/956ca969UrEna0O41/HwSJ4F3in42Fz5Trsbt5Bv3u30e9uImyvnoV15GGY/LIA6kOZP1966pZ8r3r1n5eqhwZ0F/aB4ToHGK9zh/FPHjD60RE6H1tDaaA2cdy7mvFfI+BffksPNrEksu0AAAAASUVORK5CYII='; - - function buildImageUrl(imageryProvider, x, y, level) { - var imageUrl = imageryProvider._imageUrlTemplate; - - imageUrl = imageUrl.replace('{x}', x); - imageUrl = imageUrl.replace('{y}', y); - // Google Earth starts with a zoom level of 1, not 0 - imageUrl = imageUrl.replace('{zoom}', (level + 1)); - - var proxy = imageryProvider._proxy; - if (defined(proxy)) { - imageUrl = proxy.getURL(imageUrl); - } - - return imageUrl; + return new GoogleEarthEnterpriseMapsProvider(options); } return GoogleEarthImageryProvider; }); - diff --git a/Source/Scene/GridImageryProvider.js b/Source/Scene/GridImageryProvider.js index 779449aa53b0..7bfa4f16485e 100644 --- a/Source/Scene/GridImageryProvider.js +++ b/Source/Scene/GridImageryProvider.js @@ -321,12 +321,13 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an * Image or a Canvas DOM object. */ - GridImageryProvider.prototype.requestImage = function(x, y, level) { + GridImageryProvider.prototype.requestImage = function(x, y, level, request) { return this._canvas; }; diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index ff898956dc1a..2479ede3964b 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -239,6 +239,8 @@ define([ }); var readOnlyAttributes; + var readOnlyInstanceAttributesScratch = ['color']; + if (defined(this.geometryInstances) && isArray(this.geometryInstances) && this.geometryInstances.length > 1) { readOnlyAttributes = readOnlyInstanceAttributesScratch; } @@ -260,8 +262,6 @@ define([ }; } - var readOnlyInstanceAttributesScratch = ['color']; - defineProperties(GroundPrimitive.prototype, { /** * When true, geometry vertices are optimized for the pre and post-vertex-shader caches. diff --git a/Source/Scene/Imagery.js b/Source/Scene/Imagery.js index 607fb1319cc9..07414e2ac3e7 100644 --- a/Source/Scene/Imagery.js +++ b/Source/Scene/Imagery.js @@ -2,10 +2,12 @@ define([ '../Core/defined', '../Core/destroyObject', + '../Core/RequestState', './ImageryState' ], function( defined, destroyObject, + RequestState, ImageryState) { 'use strict'; @@ -20,6 +22,7 @@ define([ this.x = x; this.y = y; this.level = level; + this.request = undefined; if (level !== 0) { var parentX = x / 2 | 0; @@ -84,10 +87,10 @@ define([ return this.referenceCount; }; - Imagery.prototype.processStateMachine = function(frameState, needGeographicProjection) { + Imagery.prototype.processStateMachine = function(frameState, needGeographicProjection, priorityFunction) { if (this.state === ImageryState.UNLOADED) { this.state = ImageryState.TRANSITIONING; - this.imageryLayer._requestImagery(this); + this.imageryLayer._requestImagery(this, priorityFunction); } if (this.state === ImageryState.RECEIVED) { diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 9fef8eeac772..39d7014322fd 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -12,6 +12,9 @@ define([ '../Core/Math', '../Core/PixelFormat', '../Core/Rectangle', + '../Core/Request', + '../Core/RequestState', + '../Core/RequestType', '../Core/TerrainProvider', '../Core/TileProviderError', '../Core/WebMercatorProjection', @@ -49,6 +52,9 @@ define([ CesiumMath, PixelFormat, Rectangle, + Request, + RequestState, + RequestType, TerrainProvider, TileProviderError, WebMercatorProjection, @@ -656,8 +662,9 @@ define([ * @private * * @param {Imagery} imagery The imagery to request. + * @param {Function} [priorityFunction] The priority function used for sorting the imagery request. */ - ImageryLayer.prototype._requestImagery = function(imagery) { + ImageryLayer.prototype._requestImagery = function(imagery, priorityFunction) { var imageryProvider = this._imageryProvider; var that = this; @@ -669,14 +676,23 @@ define([ imagery.image = image; imagery.state = ImageryState.RECEIVED; + imagery.request = undefined; TileProviderError.handleSuccess(that._requestImageError); } function failure(e) { + if (imagery.request.state === RequestState.CANCELLED) { + // Cancelled due to low priority - try again later. + imagery.state = ImageryState.UNLOADED; + imagery.request = undefined; + return; + } + // Initially assume failure. handleError may retry, in which case the state will // change to TRANSITIONING. imagery.state = ImageryState.FAILED; + imagery.request = undefined; var message = 'Failed to obtain image tile X: ' + imagery.x + ' Y: ' + imagery.y + ' Level: ' + imagery.level + '.'; that._requestImageError = TileProviderError.handleError( @@ -690,12 +706,20 @@ define([ } function doRequest() { + var request = new Request({ + throttle : true, + throttleByServer : true, + type : RequestType.IMAGERY, + priorityFunction : priorityFunction + }); + imagery.request = request; imagery.state = ImageryState.TRANSITIONING; - var imagePromise = imageryProvider.requestImage(imagery.x, imagery.y, imagery.level); + var imagePromise = imageryProvider.requestImage(imagery.x, imagery.y, imagery.level, request); if (!defined(imagePromise)) { // Too many parallel requests, so postpone loading tile. imagery.state = ImageryState.UNLOADED; + imagery.request = undefined; return; } diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js index 2a0500a0f7b7..36c5a2eb573e 100644 --- a/Source/Scene/ImageryProvider.js +++ b/Source/Scene/ImageryProvider.js @@ -6,8 +6,7 @@ define([ '../Core/loadCRN', '../Core/loadImage', '../Core/loadImageViaBlob', - '../Core/loadKTX', - '../Core/throttleRequestByServer' + '../Core/loadKTX' ], function( defined, defineProperties, @@ -15,8 +14,7 @@ define([ loadCRN, loadImage, loadImageViaBlob, - loadKTX, - throttleRequestByServer) { + loadKTX) { 'use strict'; /** @@ -29,7 +27,7 @@ define([ * @see ArcGisMapServerImageryProvider * @see SingleTileImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see MapboxImageryProvider * @see createOpenStreetMapImageryProvider * @see WebMapTileServiceImageryProvider @@ -268,6 +266,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -308,20 +307,22 @@ define([ * * @param {ImageryProvider} imageryProvider The imagery provider for the URL. * @param {String} url The URL of the image. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an * Image or a Canvas DOM object. */ - ImageryProvider.loadImage = function(imageryProvider, url) { + ImageryProvider.loadImage = function(imageryProvider, url, request) { if (ktxRegex.test(url)) { - return throttleRequestByServer(url, loadKTX); + return loadKTX(url, undefined, request); } else if (crnRegex.test(url)) { - return throttleRequestByServer(url, loadCRN); + return loadCRN(url, undefined, request); } else if (defined(imageryProvider.tileDiscardPolicy)) { - return throttleRequestByServer(url, loadImageViaBlob); + return loadImageViaBlob(url, request); } - return throttleRequestByServer(url, loadImage); + + return loadImage(url, undefined, request); }; return ImageryProvider; diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js new file mode 100644 index 000000000000..a4c347774a88 --- /dev/null +++ b/Source/Scene/Instanced3DModel3DTileContent.js @@ -0,0 +1,523 @@ +/*global define*/ +define([ + '../Core/AttributeCompression', + '../Core/Cartesian3', + '../Core/Color', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/FeatureDetection', + '../Core/Ellipsoid', + '../Core/getAbsoluteUri', + '../Core/getBaseUri', + '../Core/getStringFromTypedArray', + '../Core/joinUrls', + '../Core/Matrix3', + '../Core/Matrix4', + '../Core/Quaternion', + '../Core/RequestType', + '../Core/RuntimeError', + '../Core/Transforms', + '../Core/TranslationRotationScale', + './Cesium3DTileBatchTable', + './Cesium3DTileFeature', + './Cesium3DTileFeatureTable', + './ModelInstanceCollection' + ], function( + AttributeCompression, + Cartesian3, + Color, + ComponentDatatype, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + FeatureDetection, + Ellipsoid, + getAbsoluteUri, + getBaseUri, + getStringFromTypedArray, + joinUrls, + Matrix3, + Matrix4, + Quaternion, + RequestType, + RuntimeError, + Transforms, + TranslationRotationScale, + Cesium3DTileBatchTable, + Cesium3DTileFeature, + Cesium3DTileFeatureTable, + ModelInstanceCollection) { + 'use strict'; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + /** + * Represents the contents of a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Instanced3DModel/README.md|Instanced 3D Model} + * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset. + *

+ * Implements the {@link Cesium3DTileContent} interface. + *

+ * + * @alias Instanced3DModel3DTileContent + * @constructor + * + * @private + */ + function Instanced3DModel3DTileContent(tileset, tile, url, arrayBuffer, byteOffset) { + this._tileset = tileset; + this._tile = tile; + this._url = url; + this._modelInstanceCollection = undefined; + this._batchTable = undefined; + this._features = undefined; + + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + this.featurePropertiesDirty = false; + + initialize(this, arrayBuffer, byteOffset); + } + + defineProperties(Instanced3DModel3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featuresLength + */ + featuresLength : { + get : function() { + return this._batchTable.featuresLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#pointsLength + */ + pointsLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#trianglesLength + */ + trianglesLength : { + get : function() { + var model = this._modelInstanceCollection._model; + if (defined(model)) { + return model.trianglesLength; + } + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#geometryByteLength + */ + geometryByteLength : { + get : function() { + var model = this._modelInstanceCollection._model; + if (defined(model)) { + return model.geometryByteLength; + } + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#texturesByteLength + */ + texturesByteLength : { + get : function() { + var model = this._modelInstanceCollection._model; + if (defined(model)) { + return model.texturesByteLength; + } + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTableByteLength + */ + batchTableByteLength : { + get : function() { + return this._batchTable.memorySizeInBytes; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return this._modelInstanceCollection.readyPromise; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url: { + get: function() { + return this._url; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTable + */ + batchTable : { + get : function() { + return this._batchTable; + } + } + }); + + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + var propertyScratch1 = new Array(4); + var propertyScratch2 = new Array(4); + + function initialize(content, arrayBuffer, byteOffset) { + var byteStart = defaultValue(byteOffset, 0); + byteOffset = byteStart; + + var uint8Array = new Uint8Array(arrayBuffer); + var view = new DataView(arrayBuffer); + byteOffset += sizeOfUint32; // Skip magic + + var version = view.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError('Only Instanced 3D Model version 1 is supported. Version ' + version + ' is not.'); + } + byteOffset += sizeOfUint32; + + var byteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableJsonByteLength = view.getUint32(byteOffset, true); + if (featureTableJsonByteLength === 0) { + throw new RuntimeError('featureTableJsonByteLength is zero, the feature table must be defined.'); + } + byteOffset += sizeOfUint32; + + var featureTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var gltfFormat = view.getUint32(byteOffset, true); + if (gltfFormat !== 1 && gltfFormat !== 0) { + throw new RuntimeError('Only glTF format 0 (uri) or 1 (embedded) are supported. Format ' + gltfFormat + ' is not.'); + } + byteOffset += sizeOfUint32; + + var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength); + var featureTableJson = JSON.parse(featureTableString); + byteOffset += featureTableJsonByteLength; + + var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength); + byteOffset += featureTableBinaryByteLength; + + var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); + var instancesLength = featureTable.getGlobalProperty('INSTANCES_LENGTH'); + featureTable.featuresLength = instancesLength; + + if (!defined(instancesLength)) { + throw new RuntimeError('Feature table global property: INSTANCES_LENGTH must be defined'); + } + + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength); + batchTableJson = JSON.parse(batchTableString); + byteOffset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength); + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + byteOffset += batchTableBinaryByteLength; + } + } + + content._batchTable = new Cesium3DTileBatchTable(content, instancesLength, batchTableJson, batchTableBinary); + + var gltfByteLength = byteStart + byteLength - byteOffset; + if (gltfByteLength === 0) { + throw new RuntimeError('glTF byte length is zero, i3dm must have a glTF to instance.'); + } + var gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); + byteOffset += gltfByteLength; + + // Create model instance collection + var collectionOptions = { + instances : new Array(instancesLength), + batchTable : content._batchTable, + cull : false, // Already culled by 3D Tiles + url : undefined, + requestType : RequestType.TILES3D, + gltf : undefined, + basePath : undefined, + incrementallyLoadTextures : false, + upAxis : content._tileset._gltfUpAxis + }; + + if (gltfFormat === 0) { + var gltfUrl = getStringFromTypedArray(gltfView); + collectionOptions.url = getAbsoluteUri(joinUrls(getBaseUri(content._url, true), gltfUrl)); + } else { + collectionOptions.gltf = gltfView; + collectionOptions.basePath = getAbsoluteUri(getBaseUri(content._url, true)); + } + + var eastNorthUp = featureTable.getGlobalProperty('EAST_NORTH_UP'); + + var rtcCenter; + var rtcCenterArray = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3); + if (defined(rtcCenterArray)) { + rtcCenter = Cartesian3.unpack(rtcCenterArray); + } + + var instances = collectionOptions.instances; + var instancePosition = new Cartesian3(); + var instancePositionArray = new Array(3); + var instanceNormalRight = new Cartesian3(); + var instanceNormalUp = new Cartesian3(); + var instanceNormalForward = new Cartesian3(); + var instanceRotation = new Matrix3(); + var instanceQuaternion = new Quaternion(); + var instanceScale = new Cartesian3(); + var instanceTranslationRotationScale = new TranslationRotationScale(); + var instanceTransform = new Matrix4(); + for (var i = 0; i < instancesLength; i++) { + // Get the instance position + var position = featureTable.getProperty('POSITION', ComponentDatatype.FLOAT, 3, i, propertyScratch1); + if (!defined(position)) { + position = instancePositionArray; + var positionQuantized = featureTable.getProperty('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3, i, propertyScratch1); + if (!defined(positionQuantized)) { + throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined for each instance.'); + } + var quantizedVolumeOffset = featureTable.getGlobalProperty('QUANTIZED_VOLUME_OFFSET', ComponentDatatype.FLOAT, 3); + if (!defined(quantizedVolumeOffset)) { + throw new RuntimeError('Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.'); + } + var quantizedVolumeScale = featureTable.getGlobalProperty('QUANTIZED_VOLUME_SCALE', ComponentDatatype.FLOAT, 3); + if (!defined(quantizedVolumeScale)) { + throw new RuntimeError('Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions.'); + } + for (var j = 0; j < 3; j++) { + position[j] = (positionQuantized[j] / 65535.0 * quantizedVolumeScale[j]) + quantizedVolumeOffset[j]; + } + } + Cartesian3.unpack(position, 0, instancePosition); + if (defined(rtcCenter)) { + Cartesian3.add(instancePosition, rtcCenter, instancePosition); + } + instanceTranslationRotationScale.translation = instancePosition; + + // Get the instance rotation + var normalUp = featureTable.getProperty('NORMAL_UP', ComponentDatatype.FLOAT, 3, i, propertyScratch1); + var normalRight = featureTable.getProperty('NORMAL_RIGHT', ComponentDatatype.FLOAT, 3, i, propertyScratch2); + var hasCustomOrientation = false; + if (defined(normalUp)) { + if (!defined(normalRight)) { + throw new RuntimeError('To define a custom orientation, both NORMAL_UP and NORMAL_RIGHT must be defined.'); + } + Cartesian3.unpack(normalUp, 0, instanceNormalUp); + Cartesian3.unpack(normalRight, 0, instanceNormalRight); + hasCustomOrientation = true; + } else { + var octNormalUp = featureTable.getProperty('NORMAL_UP_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch1); + var octNormalRight = featureTable.getProperty('NORMAL_RIGHT_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch2); + if (defined(octNormalUp)) { + if (!defined(octNormalRight)) { + throw new RuntimeError('To define a custom orientation with oct-encoded vectors, both NORMAL_UP_OCT32P and NORMAL_RIGHT_OCT32P must be defined.'); + } + AttributeCompression.octDecodeInRange(octNormalUp[0], octNormalUp[1], 65535, instanceNormalUp); + AttributeCompression.octDecodeInRange(octNormalRight[0], octNormalRight[1], 65535, instanceNormalRight); + hasCustomOrientation = true; + } else if (eastNorthUp) { + Transforms.eastNorthUpToFixedFrame(instancePosition, Ellipsoid.WGS84, instanceTransform); + Matrix4.getRotation(instanceTransform, instanceRotation); + } else { + Matrix3.clone(Matrix3.IDENTITY, instanceRotation); + } + } + if (hasCustomOrientation) { + Cartesian3.cross(instanceNormalRight, instanceNormalUp, instanceNormalForward); + Cartesian3.normalize(instanceNormalForward, instanceNormalForward); + Matrix3.setColumn(instanceRotation, 0, instanceNormalRight, instanceRotation); + Matrix3.setColumn(instanceRotation, 1, instanceNormalUp, instanceRotation); + Matrix3.setColumn(instanceRotation, 2, instanceNormalForward, instanceRotation); + } + Quaternion.fromRotationMatrix(instanceRotation, instanceQuaternion); + instanceTranslationRotationScale.rotation = instanceQuaternion; + + // Get the instance scale + instanceScale = Cartesian3.fromElements(1.0, 1.0, 1.0, instanceScale); + var scale = featureTable.getProperty('SCALE', ComponentDatatype.FLOAT, 1, i); + if (defined(scale)) { + Cartesian3.multiplyByScalar(instanceScale, scale, instanceScale); + } + var nonUniformScale = featureTable.getProperty('SCALE_NON_UNIFORM', ComponentDatatype.FLOAT, 3, i, propertyScratch1); + if (defined(nonUniformScale)) { + instanceScale.x *= nonUniformScale[0]; + instanceScale.y *= nonUniformScale[1]; + instanceScale.z *= nonUniformScale[2]; + } + instanceTranslationRotationScale.scale = instanceScale; + + // Get the batchId + var batchId = featureTable.getProperty('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1, i); + if (!defined(batchId)) { + // If BATCH_ID semantic is undefined, batchId is just the instance number + batchId = i; + } + + // Create the model matrix and the instance + Matrix4.fromTranslationRotationScale(instanceTranslationRotationScale, instanceTransform); + var modelMatrix = instanceTransform.clone(); + instances[i] = { + modelMatrix : modelMatrix, + batchId : batchId + }; + } + + content._modelInstanceCollection = new ModelInstanceCollection(collectionOptions); + } + + function createFeatures(content) { + var tileset = content._tileset; + var featuresLength = content.featuresLength; + if (!defined(content._features) && (featuresLength > 0)) { + var features = new Array(featuresLength); + for (var i = 0; i < featuresLength; ++i) { + features[i] = new Cesium3DTileFeature(tileset, content, i); + } + content._features = features; + } + } + + /** + * @inheritdoc Cesium3DTileContent#hasProperty + */ + Instanced3DModel3DTileContent.prototype.hasProperty = function(batchId, name) { + return this._batchTable.hasProperty(batchId, name); + }; + + /** + * @inheritdoc Cesium3DTileContent#getFeature + */ + Instanced3DModel3DTileContent.prototype.getFeature = function(batchId) { + var featuresLength = this.featuresLength; + //>>includeStart('debug', pragmas.debug); + if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) { + throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').'); + } + //>>includeEnd('debug'); + + createFeatures(this); + return this._features[batchId]; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + Instanced3DModel3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + color = enabled ? color : Color.WHITE; + this._batchTable.setAllColor(color); + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + Instanced3DModel3DTileContent.prototype.applyStyle = function(frameState, style) { + this._batchTable.applyStyle(frameState, style); + }; + + /** + * @inheritdoc Cesium3DTileContent#update + */ + Instanced3DModel3DTileContent.prototype.update = function(tileset, frameState) { + var commandStart = frameState.commandList.length; + + // In the PROCESSING state we may be calling update() to move forward + // the content's resource loading. In the READY state, it will + // actually generate commands. + this._batchTable.update(tileset, frameState); + this._modelInstanceCollection.modelMatrix = this._tile.computedTransform; + this._modelInstanceCollection.shadows = this._tileset.shadows; + this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe; + this._modelInstanceCollection.update(frameState); + + // If any commands were pushed, add derived commands + var commandEnd = frameState.commandList.length; + if ((commandStart < commandEnd) && frameState.passes.render) { + this._batchTable.addDerivedCommands(frameState, commandStart); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + Instanced3DModel3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + Instanced3DModel3DTileContent.prototype.destroy = function() { + this._modelInstanceCollection = this._modelInstanceCollection && this._modelInstanceCollection.destroy(); + this._batchTable = this._batchTable && this._batchTable.destroy(); + + return destroyObject(this); + }; + return Instanced3DModel3DTileContent; +}); diff --git a/Source/Scene/JobScheduler.js b/Source/Scene/JobScheduler.js new file mode 100644 index 000000000000..cc39fafcb580 --- /dev/null +++ b/Source/Scene/JobScheduler.js @@ -0,0 +1,194 @@ +/*global define*/ +define([ + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/getTimestamp', + './JobType' + ], function( + defined, + defineProperties, + DeveloperError, + getTimestamp, + JobType) { + 'use strict'; + + function JobTypeBudget(total) { + /** + * Total budget, in milliseconds, allowed for one frame + */ + this._total = total; + + /** + * Time, in milliseconds, used so far during this frame + */ + this.usedThisFrame = 0.0; + + /** + * Time, in milliseconds, that other job types stole this frame + */ + this.stolenFromMeThisFrame = 0.0; + + /** + * Indicates if this job type was starved this frame, i.e., a job + * tried to run but didn't have budget + */ + this.starvedThisFrame = false; + + /** + * Indicates if this job was starved last frame. This prevents it + * from being stolen from this frame. + */ + this.starvedLastFrame = false; + } + + defineProperties(JobTypeBudget.prototype, { + total : { + get : function() { + return this._total; + } + } + }); + + /** + * Engine for time slicing jobs during a frame to amortize work over multiple frames. This supports: + *
    + *
  • + * Separate budgets for different job types, e.g., texture, shader program, and buffer creation. This + * allows all job types to make progress each frame. + *
  • + *
  • + * Stealing from other jobs type budgets if they were not exhausted in the previous frame. This allows + * using the entire budget for all job types each frame even if, for example, all the jobs are the same type. + *
  • + *
  • + * Guaranteed progress on all job types each frame, even if it means exceeding the total budget for the frame. + * This prevents, for example, several expensive texture uploads over many frames from prevent a shader compile. + *
  • + *
+ * + * @private + */ + function JobScheduler(budgets) { + //>>includeStart('debug', pragmas.debug); + if (defined(budgets) && (budgets.length !== JobType.NUMBER_OF_JOB_TYPES)) { + throw new DeveloperError('A budget must be specified for each job type; budgets.length should equal JobType.NUMBER_OF_JOB_TYPES.'); + } + //>>includeEnd('debug'); + + // Total for defaults is half of of one frame at 10 fps + var jobBudgets = new Array(JobType.NUMBER_OF_JOB_TYPES); + jobBudgets[JobType.TEXTURE] = new JobTypeBudget(defined(budgets) ? budgets[JobType.TEXTURE] : 10.0); + // On cache miss, this most likely only allows one shader compile per frame + jobBudgets[JobType.PROGRAM] = new JobTypeBudget(defined(budgets) ? budgets[JobType.PROGRAM] : 10.0); + jobBudgets[JobType.BUFFER] = new JobTypeBudget(defined(budgets) ? budgets[JobType.BUFFER] : 30.0); + + var length = jobBudgets.length; + var i; + + var totalBudget = 0.0; + for (i = 0; i < length; ++i) { + totalBudget += jobBudgets[i].total; + } + + var executedThisFrame = new Array(length); + for (i = 0; i < length; ++i) { + executedThisFrame[i] = false; + } + + this._totalBudget = totalBudget; + this._totalUsedThisFrame = 0.0; + this._budgets = jobBudgets; + this._executedThisFrame = executedThisFrame; + } + + // For unit testing + JobScheduler.getTimestamp = getTimestamp; + + defineProperties(JobScheduler.prototype, { + totalBudget : { + get : function() { + return this._totalBudget; + } + } + }); + + JobScheduler.prototype.disableThisFrame = function() { + // Prevent jobs from running this frame + this._totalUsedThisFrame = this._totalBudget; + }; + + JobScheduler.prototype.resetBudgets = function() { + var budgets = this._budgets; + var length = budgets.length; + for (var i = 0; i < length; ++i) { + var budget = budgets[i]; + budget.starvedLastFrame = budget.starvedThisFrame; + budget.starvedThisFrame = false; + budget.usedThisFrame = 0.0; + budget.stolenFromMeThisFrame = 0.0; + } + this._totalUsedThisFrame = 0.0; + }; + + JobScheduler.prototype.execute = function(job, jobType) { + var budgets = this._budgets; + var budget = budgets[jobType]; + + // This ensures each job type makes progress each frame by executing at least once + var progressThisFrame = this._executedThisFrame[jobType]; + + if ((this._totalUsedThisFrame >= this._totalBudget) && progressThisFrame) { + // No budget left this frame for jobs of any type + budget.starvedThisFrame = true; + return false; + } + + var stolenBudget; + + if ((budget.usedThisFrame + budget.stolenFromMeThisFrame >= budget.total)) { + // No budget remaining for jobs of this type. Try to steal from other job types. + var length = budgets.length; + for (var i = 0; i < length; ++i) { + stolenBudget = budgets[i]; + + // Steal from this budget if it has time left and it wasn't starved last fame + if ((stolenBudget.usedThisFrame + stolenBudget.stolenFromMeThisFrame < stolenBudget.total) && + (!stolenBudget.starvedLastFrame)) { + break; + } + } + + if ((i === length) && progressThisFrame) { + // No other job types can give up their budget this frame, and + // this job type already progressed this frame + return false; + } + + if (progressThisFrame) { + // It is considered "starved" even if it executes using stolen time so that + // next frame, no other job types can steal time from it. + budget.starvedThisFrame = true; + } + } + + var startTime = JobScheduler.getTimestamp(); + job.execute(); + var duration = JobScheduler.getTimestamp() - startTime; + + // Track both time remaining for this job type and all jobs + // so budget stealing does send us way over the total budget. + this._totalUsedThisFrame += duration; + + if (stolenBudget) { + stolenBudget.stolenFromMeThisFrame += duration; + } else { + budget.usedThisFrame += duration; + } + this._executedThisFrame[jobType] = true; + + return true; + }; + + return JobScheduler; +}); diff --git a/Source/Scene/JobType.js b/Source/Scene/JobType.js new file mode 100644 index 000000000000..b5fd6d1d1ede --- /dev/null +++ b/Source/Scene/JobType.js @@ -0,0 +1,19 @@ +/*global define*/ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * @private + */ + var JobType = { + TEXTURE : 0, + PROGRAM : 1, + BUFFER : 2, + NUMBER_OF_JOB_TYPES : 3 + }; + + return freezeObject(JobType); +}); diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 3fe84a1b5057..1d3b4803684a 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -81,6 +81,9 @@ define([ if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) { throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near'); } + if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than 0.0.'); + } //>>includeEnd('debug'); this._text = defaultValue(options.text, ''); @@ -105,6 +108,7 @@ define([ this._scaleByDistance = options.scaleByDistance; this._heightReference = defaultValue(options.heightReference, HeightReference.NONE); this._distanceDisplayCondition = options.distanceDisplayCondition; + this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0); this._labelCollection = labelCollection; this._glyphs = []; @@ -884,6 +888,41 @@ define([ } }, + /** + * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. + * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. + * @memberof Label.prototype + * @type {Number} + * @default 0.0 + */ + disableDepthTestDistance : { + get : function() { + return this._disableDepthTestDistance; + }, + set : function(value) { + if (this._disableDepthTestDistance !== value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value) || value < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than 0.0.'); + } + //>>includeEnd('debug'); + this._disableDepthTestDistance = value; + + var glyphs = this._glyphs; + for (var i = 0, len = glyphs.length; i < len; i++) { + var glyph = glyphs[i]; + if (defined(glyph.billboard)) { + glyph.billboard.disableDepthTestDistance = value; + } + } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.disableDepthTestDistance = value; + } + } + } + }, + /** * Gets or sets the user-defined object returned when the label is picked. * @memberof Label.prototype @@ -1124,6 +1163,7 @@ define([ NearFarScalar.equals(this._pixelOffsetScaleByDistance, other._pixelOffsetScaleByDistance) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition) && + this._disableDepthTestDistance === other._disableDepthTestDistance && this._id === other._id; }; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 9f3763f94586..817306b0974c 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -171,6 +171,7 @@ define([ backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; backgroundBillboard.scaleByDistance = label._scaleByDistance; backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; + backgroundBillboard.disableDepthTestDistance = label._disableDepthTestDistance; } var glyphTextureCache = labelCollection._glyphTextureCache; @@ -264,6 +265,7 @@ define([ billboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; billboard.scaleByDistance = label._scaleByDistance; billboard.distanceDisplayCondition = label._distanceDisplayCondition; + billboard.disableDepthTestDistance = label._disableDepthTestDistance; } } diff --git a/Source/Scene/MapboxImageryProvider.js b/Source/Scene/MapboxImageryProvider.js index fd17812a3d9d..ac2949df0c54 100644 --- a/Source/Scene/MapboxImageryProvider.js +++ b/Source/Scene/MapboxImageryProvider.js @@ -65,7 +65,7 @@ define([ this._url = url; this._mapId = mapId; this._accessToken = MapboxApi.getAccessToken(options.accessToken); - this._accessTokenErrorCredit = MapboxApi.getErrorCredit(options.key); + this._accessTokenErrorCredit = MapboxApi.getErrorCredit(options.accessToken); var format = defaultValue(options.format, 'png'); if (!/\./.test(format)) { format = '.' + format; @@ -317,6 +317,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -324,8 +325,8 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - MapboxImageryProvider.prototype.requestImage = function(x, y, level) { - return this._imageryProvider.requestImage(x, y, level); + MapboxImageryProvider.prototype.requestImage = function(x, y, level, request) { + return this._imageryProvider.requestImage(x, y, level, request); }; /** diff --git a/Source/Scene/Material.js b/Source/Scene/Material.js index c48952fa276f..b16f4e32275d 100644 --- a/Source/Scene/Material.js +++ b/Source/Scene/Material.js @@ -26,6 +26,7 @@ define([ '../Shaders/Materials/GridMaterial', '../Shaders/Materials/NormalMapMaterial', '../Shaders/Materials/PolylineArrowMaterial', + '../Shaders/Materials/PolylineDashMaterial', '../Shaders/Materials/PolylineGlowMaterial', '../Shaders/Materials/PolylineOutlineMaterial', '../Shaders/Materials/RimLightingMaterial', @@ -59,6 +60,7 @@ define([ GridMaterial, NormalMapMaterial, PolylineArrowMaterial, + PolylineDashMaterial, PolylineGlowMaterial, PolylineOutlineMaterial, RimLightingMaterial, @@ -207,6 +209,13 @@ define([ *
    *
  • color: diffuse color and alpha.
  • *
+ *
  • PolylineDash
  • + *
      + *
    • color: color for the line.
    • + *
    • gapColor: color for the gaps in the line.
    • + *
    • dashLength: Dash length in pixels.
    • + *
    • dashPattern: The 16 bit stipple pattern for the line..
    • + *
    *
  • PolylineGlow
  • *
      *
    • color: color and maximum alpha for the glow on the line.
    • @@ -1259,7 +1268,7 @@ define([ }, translucent : function(material) { var uniforms = material.uniforms; - return (uniforms.evenColor.alpha < 1.0) || (uniforms.oddColor.alpha < 0.0); + return (uniforms.evenColor.alpha < 1.0) || (uniforms.oddColor.alpha < 1.0); } }); @@ -1281,7 +1290,7 @@ define([ }, translucent : function(material) { var uniforms = material.uniforms; - return (uniforms.lightColor.alpha < 1.0) || (uniforms.darkColor.alpha < 0.0); + return (uniforms.lightColor.alpha < 1.0) || (uniforms.darkColor.alpha < 1.0); } }); @@ -1303,7 +1312,7 @@ define([ }, translucent : function(material) { var uniforms = material.uniforms; - return (uniforms.lightColor.alpha < 1.0) || (uniforms.darkColor.alpha < 0.0); + return (uniforms.lightColor.alpha < 1.0) || (uniforms.darkColor.alpha < 1.0); } }); @@ -1331,7 +1340,7 @@ define([ }, translucent : function(material) { var uniforms = material.uniforms; - return (uniforms.baseWaterColor.alpha < 1.0) || (uniforms.blendColor.alpha < 0.0); + return (uniforms.baseWaterColor.alpha < 1.0) || (uniforms.blendColor.alpha < 1.0); } }); @@ -1353,7 +1362,7 @@ define([ }, translucent : function(material) { var uniforms = material.uniforms; - return (uniforms.color.alpha < 1.0) || (uniforms.rimColor.alpha < 0.0); + return (uniforms.color.alpha < 1.0) || (uniforms.rimColor.alpha < 1.0); } }); @@ -1402,6 +1411,26 @@ define([ translucent : true }); + /** + * Gets the name of the polyline glow material. + * @type {String} + * @readonly + */ + Material.PolylineDashType = 'PolylineDash'; + Material._materialCache.addMaterial(Material.PolylineDashType, { + fabric : { + type : Material.PolylineDashType, + uniforms : { + color : new Color(1.0, 0.0, 1.0, 1.0), + gapColor : new Color(0.0, 0.0, 0.0, 0.0), + dashLength : 16.0, + dashPattern : 255.0 + }, + source : PolylineDashMaterial + }, + translucent : true + }); + /** * Gets the name of the polyline glow material. * @type {String} diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index fbe7a1f0645d..789ae00fc670 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -20,6 +20,7 @@ define([ '../Core/getMagic', '../Core/getStringFromTypedArray', '../Core/IndexDatatype', + '../Core/joinUrls', '../Core/loadArrayBuffer', '../Core/loadCRN', '../Core/loadImage', @@ -60,11 +61,13 @@ define([ '../ThirdParty/GltfPipeline/updateVersion', '../ThirdParty/Uri', '../ThirdParty/when', + './AttributeType', './Axis', './BlendingState', './ColorBlendMode', './getAttributeOrUniformBySemantic', './HeightReference', + './JobType', './ModelAnimationCache', './ModelAnimationCollection', './ModelMaterial', @@ -93,6 +96,7 @@ define([ getMagic, getStringFromTypedArray, IndexDatatype, + joinUrls, loadArrayBuffer, loadCRN, loadImage, @@ -133,11 +137,13 @@ define([ updateVersion, Uri, when, + AttributeType, Axis, BlendingState, ColorBlendMode, getAttributeOrUniformBySemantic, HeightReference, + JobType, ModelAnimationCache, ModelAnimationCollection, ModelMaterial, @@ -162,11 +168,12 @@ define([ FAILED : 3 }; - // GLTF_SPEC: Figure out correct mime types (https://github.com/KhronosGroup/glTF/issues/412) - var defaultModelAccept = 'model/vnd.gltf.binary,model/vnd.gltf+json,model/gltf.binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01'; + // glTF MIME types discussed in https://github.com/KhronosGroup/glTF/issues/412 and https://github.com/KhronosGroup/glTF/issues/943 + var defaultModelAccept = 'model/gltf-binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01'; function LoadResources() { - this.buffersToCreate = new Queue(); + this.vertexBuffersToCreate = new Queue(); + this.indexBuffersToCreate = new Queue(); this.buffers = {}; this.pendingBufferLoads = 0; @@ -200,7 +207,9 @@ define([ }; LoadResources.prototype.finishedBuffersCreation = function() { - return ((this.pendingBufferLoads === 0) && (this.buffersToCreate.length === 0)); + return ((this.pendingBufferLoads === 0) && + (this.vertexBuffersToCreate.length === 0) && + (this.indexBuffersToCreate.length === 0)); }; LoadResources.prototype.finishedProgramCreation = function() { @@ -221,7 +230,8 @@ define([ (this.pendingBufferLoads === 0) && (this.pendingShaderLoads === 0); var finishedResourceCreation = - (this.buffersToCreate.length === 0) && + (this.vertexBuffersToCreate.length === 0) && + (this.indexBuffersToCreate.length === 0) && (this.programsToCreate.length === 0) && (this.pendingBufferViewToImage === 0); @@ -336,6 +346,7 @@ define([ * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. + * @param {Boolean} [options.addBatchIdToGeneratedShaders=false] Determines if shaders generated for materials using the KHR_materials_common extension should include a batchId attribute. For models contained in b3dm tiles. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. @@ -379,13 +390,15 @@ define([ cachedGltf = new CachedGltf({ gltf : parsedGltf, - ready : true + ready : true, + addBatchIdToGeneratedShaders : options.addBatchIdToGeneratedShaders }); } else { // Normal glTF (JSON) cachedGltf = new CachedGltf({ gltf : options.gltf, - ready : true + ready : true, + addBatchIdToGeneratedShaders : options.addBatchIdToGeneratedShaders }); } @@ -399,10 +412,8 @@ define([ setCachedGltf(this, cachedGltf); this._basePath = defaultValue(options.basePath, ''); - - var docUri = new Uri(document.location.href); - var modelUri = new Uri(this._basePath); - this._baseUri = modelUri.resolve(docUri); + var baseUri = getBaseUri(document.location.href); + this._baseUri = joinUrls(baseUri, this._basePath); /** * Determines if the model primitive will be shown. @@ -524,8 +535,7 @@ define([ * * @private */ - this.pickPrimitive = options.pickPrimitive; - + this._pickObject = options.pickObject; this._allowPicking = defaultValue(options.allowPicking, true); this._ready = false; @@ -621,6 +631,7 @@ define([ this._pickFragmentShaderLoaded = options.pickFragmentShaderLoaded; this._pickUniformMapLoaded = options.pickUniformMapLoaded; this._ignoreCommands = defaultValue(options.ignoreCommands, false); + this._requestType = options.requestType; this._upAxis = defaultValue(options.upAxis, Axis.Y); /** @@ -666,7 +677,6 @@ define([ pickPrograms : {}, silhouettePrograms: {}, textures : {}, - samplers : {}, renderStates : {} }; @@ -674,6 +684,12 @@ define([ this._loadRendererResourcesFromCache = false; this._updatedGltfVersion = false; + this._cachedGeometryByteLength = 0; + this._cachedTexturesByteLength = 0; + this._geometryByteLength = 0; + this._texturesByteLength = 0; + this._trianglesLength = 0; + this._nodeCommands = []; this._pickIds = []; @@ -993,6 +1009,61 @@ define([ get : function() { return this._upAxis; } + }, + + /** + * Gets the model's triangle count. + * + * @private + */ + trianglesLength : { + get : function() { + return this._trianglesLength; + } + }, + + /** + * Gets the model's geometry memory in bytes. This includes all vertex and index buffers. + * + * @private + */ + geometryByteLength : { + get : function() { + return this._geometryByteLength; + } + }, + + /** + * Gets the model's texture memory in bytes. + * + * @private + */ + texturesByteLength : { + get : function() { + return this._texturesByteLength; + } + }, + + /** + * Gets the model's cached geometry memory in bytes. This includes all vertex and index buffers. + * + * @private + */ + cachedGeometryByteLength : { + get : function() { + return this._cachedGeometryByteLength; + } + }, + + /** + * Gets the model's cached texture memory in bytes. + * + * @private + */ + cachedTexturesByteLength : { + get : function() { + return this._cachedTexturesByteLength; + } } }); @@ -1041,6 +1112,7 @@ define([ * @param {Object} options Object with the following properties: * @param {String} options.url The url to the .gltf file. * @param {Object} [options.headers] HTTP headers to send with the request. + * @param {String} [options.basePath] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. @@ -1098,10 +1170,15 @@ define([ // If no cache key is provided, use the absolute URL, since two URLs with // different relative paths could point to the same model. var cacheKey = defaultValue(options.cacheKey, getAbsoluteUri(url)); + var basePath = defaultValue(options.basePath, getBaseUri(url, true)); options = clone(options); - options.basePath = getBaseUri(url); + if (defined(options.basePath) && !defined(options.cacheKey)) { + cacheKey += basePath; + } + options.cacheKey = cacheKey; + options.basePath = basePath; var model = new Model(options); options.headers = defined(options.headers) ? clone(options.headers) : {}; @@ -1331,8 +1408,7 @@ define([ if (defined(buffer.extras._pipeline.source)) { loadResources.buffers[id] = buffer.extras._pipeline.source; } else { - var uri = new Uri(buffer.uri); - var bufferPath = uri.resolve(model._baseUri).toString(); + var bufferPath = joinUrls(model._baseUri, buffer.uri); ++loadResources.pendingBufferLoads; loadArrayBuffer(bufferPath).then(bufferLoad(model, id)).otherwise(getFailedLoadFunction(model, 'buffer', bufferPath)); } @@ -1341,18 +1417,47 @@ define([ } function parseBufferViews(model) { + var bufferViews = model.gltf.bufferViews; + + var vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate; + + // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below. ForEach.bufferView(model.gltf, function(bufferView, id) { if (bufferView.target === WebGLConstants.ARRAY_BUFFER) { - model._loadResources.buffersToCreate.enqueue(id); + vertexBuffersToCreate.enqueue(id); + } + }); + + var indexBuffersToCreate = model._loadResources.indexBuffersToCreate; + var indexBufferIds = {}; + + // The Cesium Renderer requires knowing the datatype for an index buffer + // at creation type, which is not part of the glTF bufferview so loop + // through glTF accessors to create the bufferview's index buffer. + ForEach.accessor(model.gltf, function(accessor) { + var bufferViewId = accessor.bufferView; + var bufferView = bufferViews[bufferViewId]; + + if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(indexBufferIds[bufferViewId])) { + indexBufferIds[bufferViewId] = true; + indexBuffersToCreate.enqueue({ + id : bufferViewId, + // In theory, several glTF accessors with different componentTypes could + // point to the same glTF bufferView, which would break this. + // In practice, it is unlikely, but possible with uint32 indices in glTF 2.0. +// TODO: Is this disallowed in latest glTF 2.0 spec?? + componentType : accessor.componentType + }); } }); } - function shaderLoad(model, id) { + function shaderLoad(model, type, id) { return function(source) { var loadResources = model._loadResources; loadResources.shaders[id] = { source : source, + type : type, bufferView : undefined }; --loadResources.pendingShaderLoads; @@ -1384,9 +1489,8 @@ define([ }; } else { ++model._loadResources.pendingShaderLoads; - var uri = new Uri(shader.uri); - var shaderPath = uri.resolve(model._baseUri).toString(); - loadText(shaderPath).then(shaderLoad(model, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath)); + var shaderPath = joinUrls(model._baseUri, shader.uri); + loadText(shaderPath).then(shaderLoad(model, shader.type, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath)); } }); } @@ -1420,13 +1524,14 @@ define([ function parseTextures(model, context) { var gltf = model.gltf; var images = gltf.images; + var uri; ForEach.texture(gltf, function(texture, id) { var imageId = texture.source; var gltfImage = images[imageId]; var extras = gltfImage.extras; var bufferViewId = gltfImage.bufferView; - var uri = gltfImage.uri; + uri = gltfImage.uri; // First check for a compressed texture if (defined(extras) && defined(extras.compressedImage3DTiles)) { @@ -1473,14 +1578,17 @@ define([ } else { ++model._loadResources.pendingTextureLoads; uri = new Uri(uri); - var imagePath = uri.resolve(model._baseUri).toString(); + var imagePath = joinUrls(model._baseUri, gltfImage.uri); + + var promise; if (ktxRegex.test(imagePath)) { - loadKTX(imagePath).then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = loadKTX(imagePath); } else if (crnRegex.test(imagePath)) { - loadCRN(imagePath).then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = loadCRN(imagePath); } else { - loadImage(imagePath).then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); + promise = loadImage(imagePath); } + promise.then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imagePath)); } }); } @@ -1637,51 +1745,119 @@ define([ /////////////////////////////////////////////////////////////////////////// - function createBuffers(model, context) { + var CreateVertexBufferJob = function() { + this.id = undefined; + this.model = undefined; + this.context = undefined; + }; + + CreateVertexBufferJob.prototype.set = function(id, model, context) { + this.id = id; + this.model = model; + this.context = context; + }; + + CreateVertexBufferJob.prototype.execute = function() { + createVertexBuffer(this.id, this.model, this.context); + }; + + /////////////////////////////////////////////////////////////////////////// + + function createVertexBuffer(bufferViewId, model, context) { var loadResources = model._loadResources; + var bufferViews = model.gltf.bufferViews; + var bufferView = bufferViews[bufferViewId]; - if (loadResources.pendingBufferLoads !== 0) { - return; - } + var vertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : loadResources.getBuffer(bufferView), + usage : BufferUsage.STATIC_DRAW + }); + vertexBuffer.vertexArrayDestroyable = false; + model._rendererResources.buffers[bufferViewId] = vertexBuffer; + model._geometryByteLength += vertexBuffer.sizeInBytes; + } + + /////////////////////////////////////////////////////////////////////////// - var bufferView; + var CreateIndexBufferJob = function() { + this.id = undefined; + this.componentType = undefined; + this.model = undefined; + this.context = undefined; + }; + + CreateIndexBufferJob.prototype.set = function(id, componentType, model, context) { + this.id = id; + this.componentType = componentType; + this.model = model; + this.context = context; + }; + + CreateIndexBufferJob.prototype.execute = function() { + createIndexBuffer(this.id, this.componentType, this.model, this.context); + }; + + /////////////////////////////////////////////////////////////////////////// + + function createIndexBuffer(bufferViewId, componentType, model, context) { + var loadResources = model._loadResources; var bufferViews = model.gltf.bufferViews; - var rendererBuffers = model._rendererResources.buffers; + var bufferView = bufferViews[bufferViewId]; - while (loadResources.buffersToCreate.length > 0) { - var bufferViewId = loadResources.buffersToCreate.dequeue(); - bufferView = bufferViews[bufferViewId]; + var indexBuffer = Buffer.createIndexBuffer({ + context : context, + typedArray : loadResources.getBuffer(bufferView), + usage : BufferUsage.STATIC_DRAW, + indexDatatype : componentType + }); + indexBuffer.vertexArrayDestroyable = false; + model._rendererResources.buffers[bufferViewId] = indexBuffer; + model._geometryByteLength += indexBuffer.sizeInBytes; + } - // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below. - var vertexBuffer = Buffer.createVertexBuffer({ - context : context, - typedArray : loadResources.getBuffer(bufferView), - usage : BufferUsage.STATIC_DRAW - }); - vertexBuffer.vertexArrayDestroyable = false; - rendererBuffers[bufferViewId] = vertexBuffer; + var scratchVertexBufferJob = new CreateVertexBufferJob(); + var scratchIndexBufferJob = new CreateIndexBufferJob(); + + function createBuffers(model, frameState) { + var loadResources = model._loadResources; + + if (loadResources.pendingBufferLoads !== 0) { + return; } - // The Cesium Renderer requires knowing the datatype for an index buffer - // at creation type, which is not part of the glTF bufferview so loop - // through glTF accessors to create the bufferview's index buffer. - ForEach.accessor(model.gltf, function(accessor) { - bufferView = bufferViews[accessor.bufferView]; + var context = frameState.context; + var vertexBuffersToCreate = loadResources.vertexBuffersToCreate; + var indexBuffersToCreate = loadResources.indexBuffersToCreate; + var i; - if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(rendererBuffers[accessor.bufferView])) { - var indexBuffer = Buffer.createIndexBuffer({ - context : context, - typedArray : loadResources.getBuffer(bufferView), - usage : BufferUsage.STATIC_DRAW, - indexDatatype : accessor.componentType - }); - indexBuffer.vertexArrayDestroyable = false; - rendererBuffers[accessor.bufferView] = indexBuffer; - // In theory, several glTF accessors with different componentTypes could - // point to the same glTF bufferView, which would break this. - // In practice, it is unlikely, but possible with uint32 indices in glTF 2.0. + if (model.asynchronous) { + while (vertexBuffersToCreate.length > 0) { + scratchVertexBufferJob.set(vertexBuffersToCreate.peek(), model, context); + if (!frameState.jobScheduler.execute(scratchVertexBufferJob, JobType.BUFFER)) { + break; + } + vertexBuffersToCreate.dequeue(); } - }); + + while (indexBuffersToCreate.length > 0) { + i = indexBuffersToCreate.peek(); + scratchIndexBufferJob.set(i.id, i.componentType, model, context); + if (!frameState.jobScheduler.execute(scratchIndexBufferJob, JobType.BUFFER)) { + break; + } + indexBuffersToCreate.dequeue(); + } + } else { + while (vertexBuffersToCreate.length > 0) { + createVertexBuffer(vertexBuffersToCreate.dequeue(), model, context); + } + + while (indexBuffersToCreate.length > 0) { + i = indexBuffersToCreate.dequeue(); + createIndexBuffer(i.id, i.componentType, model, context); + } + } } function createAttributeLocations(model, attributes) { @@ -1695,7 +1871,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; @@ -1882,6 +2058,24 @@ define([ return shader; } + var CreateProgramJob = function() { + this.id = undefined; + this.model = undefined; + this.context = undefined; + }; + + CreateProgramJob.prototype.set = function(id, model, context) { + this.id = id; + this.model = model; + this.context = context; + }; + + CreateProgramJob.prototype.execute = function() { + createProgram(this.id, this.model, this.context); + }; + + /////////////////////////////////////////////////////////////////////////// + function createProgram(id, model, context) { var programs = model.gltf.programs; var shaders = model.gltf.shaders; @@ -1937,9 +2131,11 @@ define([ } } - function createPrograms(model, context) { + var scratchCreateProgramJob = new CreateProgramJob(); + + function createPrograms(model, frameState) { var loadResources = model._loadResources; - var id; + var programsToCreate = loadResources.programsToCreate; if (loadResources.pendingShaderLoads !== 0) { return; @@ -1951,17 +2147,20 @@ define([ return; } + var context = frameState.context; + if (model.asynchronous) { - // Create one program per frame - if (loadResources.programsToCreate.length > 0) { - id = loadResources.programsToCreate.dequeue(); - createProgram(id, model, context); + while (programsToCreate.length > 0) { + scratchCreateProgramJob.set(programsToCreate.peek(), model, context); + if (!frameState.jobScheduler.execute(scratchCreateProgramJob, JobType.PROGRAM)) { + break; + } + programsToCreate.dequeue(); } } else { // Create all loaded programs this frame - while (loadResources.programsToCreate.length > 0) { - id = loadResources.programsToCreate.dequeue(); - createProgram(id, model, context); + while (programsToCreate.length > 0) { + createProgram(programsToCreate.dequeue(), model, context); } } } @@ -2031,6 +2230,26 @@ define([ } } + /////////////////////////////////////////////////////////////////////////// + + var CreateTextureJob = function() { + this.gltfTexture = undefined; + this.model = undefined; + this.context = undefined; + }; + + CreateTextureJob.prototype.set = function(gltfTexture, model, context) { + this.gltfTexture = gltfTexture; + this.model = model; + this.context = context; + }; + + CreateTextureJob.prototype.execute = function() { + createTexture(this.gltfTexture, this.model, this.context); + }; + + /////////////////////////////////////////////////////////////////////////// + function createTexture(gltfTexture, model, context) { var textures = model.gltf.textures; var texture = textures[gltfTexture.id]; @@ -2097,23 +2316,27 @@ define([ } model._rendererResources.textures[gltfTexture.id] = tx; + model._texturesByteLength += tx.sizeInBytes; } - function createTextures(model, context) { - var loadResources = model._loadResources; - var gltfTexture; + var scratchCreateTextureJob = new CreateTextureJob(); + + function createTextures(model, frameState) { + var context = frameState.context; + var texturesToCreate = model._loadResources.texturesToCreate; if (model.asynchronous) { - // Create one texture per frame - if (loadResources.texturesToCreate.length > 0) { - gltfTexture = loadResources.texturesToCreate.dequeue(); - createTexture(gltfTexture, model, context); + while (texturesToCreate.length > 0) { + scratchCreateTextureJob.set(texturesToCreate.peek(), model, context); + if (!frameState.jobScheduler.execute(scratchCreateTextureJob, JobType.TEXTURE)) { + break; + } + texturesToCreate.dequeue(); } } else { // Create all loaded textures this frame - while (loadResources.texturesToCreate.length > 0) { - gltfTexture = loadResources.texturesToCreate.dequeue(); - createTexture(gltfTexture, model, context); + while (texturesToCreate.length > 0) { + createTexture(texturesToCreate.dequeue(), model, context); } } } @@ -2126,21 +2349,37 @@ 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; var attributes = technique.attributes; - var programAttributeLocations = model._rendererResources.programs[technique.program].vertexAttributes; + var program = model._rendererResources.programs[technique.program]; + 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]; - var index = programAttributeLocations[location].index; + index = programVertexAttributes[location].index; if (defined(attribute)) { var parameter = parameters[attribute]; attributeLocations[parameter.semantic] = index; - } else { - // Pre-created attributes + } + } + } + + // 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; } } @@ -2361,6 +2600,7 @@ define([ if (defined(a.normalized) && a.normalized) { normalize = true; } + attributes.push({ index : attributeLocation, vertexBuffer : rendererBuffers[a.bufferView], @@ -2959,19 +3199,19 @@ define([ var uniformVariable = 'gltf_u_dec_' + attribute.toLowerCase(); switch (a.type) { - case 'SCALAR': + case AttributeType.SCALAR: uniformMap[uniformVariable] = getMat2UniformFunction(decodeMatrix, model).func; setUniforms[uniformVariable] = true; break; - case 'VEC2': + case AttributeType.VEC2: uniformMap[uniformVariable] = getMat3UniformFunction(decodeMatrix, model).func; setUniforms[uniformVariable] = true; break; - case 'VEC3': + case AttributeType.VEC3: uniformMap[uniformVariable] = getMat4UniformFunction(decodeMatrix, model).func; setUniforms[uniformVariable] = true; break; - case 'VEC4': + case AttributeType.VEC4: // VEC4 attributes are split into scale and translate because there is no mat5 in GLSL var uniformVariableScale = uniformVariable + '_scale'; var uniformVariableTranslate = uniformVariable + '_translate'; @@ -3047,6 +3287,18 @@ define([ }; } + function triangleCountFromPrimitiveIndices(primitive, indicesCount) { + switch (primitive.mode) { + case PrimitiveType.TRIANGLES: + return (indicesCount / 3); + case PrimitiveType.TRIANGLE_STRIP: + case PrimitiveType.TRIANGLE_FAN: + return Math.max(indicesCount - 2, 0); + default: + return 0; + } + } + function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) { var nodeCommands = model._nodeCommands; var pickIds = model._pickIds; @@ -3103,6 +3355,9 @@ define([ offset = 0; } + // Update model triangle count using number of indices + model._trianglesLength += triangleCountFromPrimitiveIndices(primitive, count); + var um = uniformMaps[primitive.material]; var uniformMap = um.uniformMap; if (defined(um.jointMatrixUniformName)) { @@ -3132,12 +3387,16 @@ define([ // GLTF_SPEC: Offical means to determine translucency. https://github.com/KhronosGroup/glTF/issues/105 var isTranslucent = rs.blending.enabled; - var owner = { - primitive : defaultValue(model.pickPrimitive, model), - id : model.id, - node : runtimeNode.publicNode, - mesh : runtimeMeshesByName[mesh.name] - }; + + var owner = model._pickObject; + if (!defined(owner)) { + owner = { + primitive : model, + id : model.id, + node : runtimeNode.publicNode, + mesh : runtimeMeshesByName[mesh.name] + }; + } var castShadows = ShadowMode.castShadows(model._shadows); var receiveShadows = ShadowMode.receiveShadows(model._shadows); @@ -3336,6 +3595,26 @@ define([ model._runtime.nodes = runtimeNodes; } + function getGeometryByteLength(buffers) { + var memory = 0; + for (var id in buffers) { + if (buffers.hasOwnProperty(id)) { + memory += buffers[id].sizeInBytes; + } + } + return memory; + } + + function getTexturesByteLength(textures) { + var memory = 0; + for (var id in textures) { + if (textures.hasOwnProperty(id)) { + memory += textures[id].sizeInBytes; + } + } + return memory; + } + function createResources(model, frameState) { var context = frameState.context; var scene3DOnly = frameState.scene3DOnly; @@ -3358,12 +3637,15 @@ define([ if (defined(model._precreatedAttributes)) { createVertexArrays(model, context); } + + model._cachedGeometryByteLength += getGeometryByteLength(cachedResources.buffers); + model._cachedTexturesByteLength += getTexturesByteLength(cachedResources.textures); } else { - createBuffers(model, context); // using glTF bufferViews - createPrograms(model, context); + createBuffers(model, frameState); // using glTF bufferViews + createPrograms(model, frameState); createSamplers(model, context); loadTexturesFromBufferViews(model); - createTextures(model, context); + createTextures(model, frameState); } createSkins(model); @@ -3413,7 +3695,7 @@ define([ var nodeStack = scratchNodeStack; var computedModelMatrix = model._computedModelMatrix; - if (model._mode !== SceneMode.SCENE3D) { + if ((model._mode !== SceneMode.SCENE3D) && !model._ignoreCommands) { var translation = Matrix4.getColumn(computedModelMatrix, 3, scratchComputedTranslation); if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { computedModelMatrix = Transforms.basisTo2D(projection, computedModelMatrix, scratchComputedMatrixIn2D); @@ -3546,7 +3828,6 @@ define([ } } - function updatePerNodeShow(model) { // Totally not worth it, but we could optimize this: // http://blogs.agi.com/insight3d/index.php/2008/02/13/deletion-in-bounding-volume-hierarchies/ diff --git a/Source/Scene/ModelAnimationCache.js b/Source/Scene/ModelAnimationCache.js index d332512586ee..0f0a552b5339 100644 --- a/Source/Scene/ModelAnimationCache.js +++ b/Source/Scene/ModelAnimationCache.js @@ -1,5 +1,6 @@ /*global define*/ define([ + './AttributeType', '../Core/Cartesian3', '../Core/ComponentDatatype', '../Core/defaultValue', @@ -12,6 +13,7 @@ define([ '../ThirdParty/GltfPipeline/getAccessorByteStride', '../ThirdParty/GltfPipeline/numberOfComponentsForType' ], function( + AttributeType, Cartesian3, ComponentDatatype, defaultValue, @@ -180,7 +182,7 @@ define([ matrices = new Array(count); - if ((componentType === WebGLConstants.FLOAT) && (type === 'MAT4')) { + if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.MAT4)) { for (var i = 0; i < count; ++i) { var typedArrayView = ComponentDatatype.createArrayBufferView(componentType, source.buffer, byteOffset, numberOfComponents); matrices[i] = Matrix4.fromArray(typedArrayView); diff --git a/Source/Scene/ModelInstance.js b/Source/Scene/ModelInstance.js new file mode 100644 index 000000000000..399893de4528 --- /dev/null +++ b/Source/Scene/ModelInstance.js @@ -0,0 +1,43 @@ +/*global define*/ +define([ + '../Core/defineProperties', + '../Core/Matrix4' +], function( + defineProperties, + Matrix4) { + 'use strict'; + + /** + * @private + */ + function ModelInstance(collection, modelMatrix, instanceId) { + this.primitive = collection; + this._modelMatrix = Matrix4.clone(modelMatrix); + this._instanceId = instanceId; + } + + defineProperties(ModelInstance.prototype, { + instanceId : { + get : function() { + return this._instanceId; + } + }, + model : { + get : function() { + return this.primitive._model; + } + }, + modelMatrix : { + get : function() { + return Matrix4.clone(this._modelMatrix); + }, + set : function(value) { + Matrix4.clone(value, this._modelMatrix); + this.primitive.expandBoundingSphere(this._modelMatrix); + this.primitive._dirty = true; + } + } + }); + + return ModelInstance; +}); diff --git a/Source/Scene/ModelInstanceCollection.js b/Source/Scene/ModelInstanceCollection.js new file mode 100644 index 000000000000..e76f677a8261 --- /dev/null +++ b/Source/Scene/ModelInstanceCollection.js @@ -0,0 +1,1000 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/Cartesian3', + '../Core/clone', + '../Core/Color', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/Matrix4', + '../Core/PrimitiveType', + '../Core/RuntimeError', + '../Core/Transforms', + '../Renderer/Buffer', + '../Renderer/BufferUsage', + '../Renderer/DrawCommand', + '../Renderer/ShaderSource', + '../ThirdParty/when', + './getAttributeOrUniformBySemantic', + './Model', + './ModelInstance', + './SceneMode', + './ShadowMode' + ], function( + BoundingSphere, + Cartesian3, + clone, + Color, + ComponentDatatype, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + Matrix4, + PrimitiveType, + RuntimeError, + Transforms, + Buffer, + BufferUsage, + DrawCommand, + ShaderSource, + when, + getAttributeOrUniformBySemantic, + Model, + ModelInstance, + SceneMode, + ShadowMode) { + 'use strict'; + + var LoadState = { + NEEDS_LOAD : 0, + LOADING : 1, + LOADED : 2, + FAILED : 3 + }; + + /** + * A 3D model instance collection. All instances reference the same underlying model, but have unique + * per-instance properties like model matrix, pick id, etc. + * + * Instances are rendered relative-to-center and for best results instances should be positioned close to one another. + * Otherwise there may be precision issues if, for example, instances are placed on opposite sides of the globe. + * + * @alias ModelInstanceCollection + * @constructor + * + * @param {Object} options Object with the following properties: + * @param {Object[]} [options.instances] An array of instances, where each instance contains a modelMatrix and optional batchId when options.batchTable is defined. + * @param {Cesium3DTileBatchTable} [options.batchTable] The batch table of the instanced 3D Tile. + * @param {String} [options.url] The url to the .gltf file. + * @param {Object} [options.headers] HTTP headers to send with the request. + * @param {Object} [options.requestType] The request type, used for request prioritization + * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] The object for the glTF JSON or an arraybuffer of Binary glTF defined by the CESIUM_binary_glTF extension. + * @param {String} [options.basePath=''] The base path that paths in the glTF JSON are relative to. + * @param {Boolean} [options.dynamic=false] Hint if instance model matrices will be updated frequently. + * @param {Boolean} [options.show=true] Determines if the collection will be shown. + * @param {Boolean} [options.allowPicking=true] When true, each instance is pickable with {@link Scene#pick}. + * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. + * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. + * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the collection casts or receives shadows from each light source. + * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for the collection. + * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the instances in wireframe. + * + * @exception {DeveloperError} Must specify either or , but not both. + * @exception {DeveloperError} Shader program cannot be optimized for instancing. Parameters cannot have any of the following semantics: MODEL, MODELINVERSE, MODELVIEWINVERSE, MODELVIEWPROJECTIONINVERSE, MODELINVERSETRANSPOSE. + * + * @private + */ + function ModelInstanceCollection(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + //>>includeStart('debug', pragmas.debug); + if (!defined(options.gltf) && !defined(options.url)) { + throw new DeveloperError('Either options.gltf or options.url is required.'); + } + + if (defined(options.gltf) && defined(options.url)) { + throw new DeveloperError('Cannot pass in both options.gltf and options.url.'); + } + //>>includeEnd('debug'); + + this.show = defaultValue(options.show, true); + + this._instancingSupported = false; + this._dynamic = defaultValue(options.dynamic, false); + this._allowPicking = defaultValue(options.allowPicking, true); + this._cull = defaultValue(options.cull, true); // Undocumented option + this._ready = false; + this._readyPromise = when.defer(); + this._state = LoadState.NEEDS_LOAD; + this._dirty = false; + + this._instances = createInstances(this, options.instances); + + // When the model instance collection is backed by an i3dm tile, + // use its batch table resources to modify the shaders, attributes, and uniform maps. + this._batchTable = options.batchTable; + + this._model = undefined; + this._vertexBufferTypedArray = undefined; // Hold onto the vertex buffer contents when dynamic is true + this._vertexBuffer = undefined; + this._batchIdBuffer = undefined; + this._instancedUniformsByProgram = undefined; + + this._drawCommands = []; + this._pickCommands = []; + this._modelCommands = undefined; + + this._boundingSphere = createBoundingSphere(this); + this._center = Cartesian3.clone(this._boundingSphere.center); + this._rtcTransform = new Matrix4(); + this._rtcModelView = new Matrix4(); // Holds onto uniform + + this._mode = undefined; + + this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY); + this._modelMatrix = Matrix4.clone(this.modelMatrix); + + // Passed on to Model + this._url = options.url; + this._headers = options.headers; + this._requestType = options.requestType; + this._gltf = options.gltf; + this._basePath = options.basePath; + this._asynchronous = options.asynchronous; + this._incrementallyLoadTextures = options.incrementallyLoadTextures; + this._upAxis = options.upAxis; // Undocumented option + + this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED); + this._shadows = this.shadows; + + this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false); + this._debugShowBoundingVolume = false; + + this.debugWireframe = defaultValue(options.debugWireframe, false); + this._debugWireframe = false; + } + + defineProperties(ModelInstanceCollection.prototype, { + allowPicking : { + get : function() { + return this._allowPicking; + } + }, + length : { + get : function() { + return this._instances.length; + } + }, + activeAnimations : { + get : function() { + return this._model.activeAnimations; + } + }, + ready : { + get : function() { + return this._ready; + } + }, + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + } + }); + + function createInstances(collection, instancesOptions) { + instancesOptions = defaultValue(instancesOptions, []); + var length = instancesOptions.length; + var instances = new Array(length); + for (var i = 0; i < length; ++i) { + var instanceOptions = instancesOptions[i]; + var modelMatrix = instanceOptions.modelMatrix; + var instanceId = defaultValue(instanceOptions.batchId, i); + instances[i] = new ModelInstance(collection, modelMatrix, instanceId); + } + return instances; + } + + function createBoundingSphere(collection) { + var instancesLength = collection.length; + var points = new Array(instancesLength); + for (var i = 0; i < instancesLength; ++i) { + points[i] = Matrix4.getTranslation(collection._instances[i]._modelMatrix, new Cartesian3()); + } + + return BoundingSphere.fromPoints(points); + } + + var scratchCartesian = new Cartesian3(); + var scratchMatrix = new Matrix4(); + + ModelInstanceCollection.prototype.expandBoundingSphere = function(instanceModelMatrix) { + var translation = Matrix4.getTranslation(instanceModelMatrix, scratchCartesian); + BoundingSphere.expand(this._boundingSphere, translation, this._boundingSphere); + }; + + function getInstancedUniforms(collection, programName) { + if (defined(collection._instancedUniformsByProgram)) { + return collection._instancedUniformsByProgram[programName]; + } + + var instancedUniformsByProgram = {}; + collection._instancedUniformsByProgram = instancedUniformsByProgram; + + // When using CESIUM_RTC_MODELVIEW the CESIUM_RTC center is ignored. Instances are always rendered relative-to-center. + var modelSemantics = ['MODEL', 'MODELVIEW', 'CESIUM_RTC_MODELVIEW', 'MODELVIEWPROJECTION', 'MODELINVERSE', 'MODELVIEWINVERSE', 'MODELVIEWPROJECTIONINVERSE', 'MODELINVERSETRANSPOSE', 'MODELVIEWINVERSETRANSPOSE']; + var supportedSemantics = ['MODELVIEW', 'CESIUM_RTC_MODELVIEW', 'MODELVIEWPROJECTION', 'MODELVIEWINVERSETRANSPOSE']; + + var gltf = collection._model.gltf; + var techniques = gltf.techniques; + for (var techniqueName in techniques) { + if (techniques.hasOwnProperty(techniqueName)) { + var technique = techniques[techniqueName]; + var parameters = technique.parameters; + var uniforms = technique.uniforms; + var program = technique.program; + + // Different techniques may share the same program, skip if already processed. + // This assumes techniques that share a program do not declare different semantics for the same uniforms. + if (!defined(instancedUniformsByProgram[program])) { + var uniformMap = {}; + instancedUniformsByProgram[program] = uniformMap; + for (var uniformName in uniforms) { + if (uniforms.hasOwnProperty(uniformName)) { + var parameterName = uniforms[uniformName]; + var parameter = parameters[parameterName]; + var semantic = parameter.semantic; + if (defined(semantic) && (modelSemantics.indexOf(semantic) > -1)) { + if (supportedSemantics.indexOf(semantic) > -1) { + uniformMap[uniformName] = semantic; + } else { + throw new RuntimeError('Shader program cannot be optimized for instancing. ' + + 'Parameter "' + parameter + '" in program "' + programName + + '" uses unsupported semantic "' + semantic + '"' + ); + } + } + } + } + } + } + } + + return instancedUniformsByProgram[programName]; + } + + var vertexShaderCached; + + function getVertexShaderCallback(collection) { + return function(vs, programName) { + var instancedUniforms = getInstancedUniforms(collection, programName); + var usesBatchTable = defined(collection._batchTable); + + var renamedSource = ShaderSource.replaceMain(vs, 'czm_instancing_main'); + + var globalVarsHeader = ''; + var globalVarsMain = ''; + for (var uniform in instancedUniforms) { + if (instancedUniforms.hasOwnProperty(uniform)) { + var semantic = instancedUniforms[uniform]; + var varName; + if (semantic === 'MODELVIEW' || semantic === 'CESIUM_RTC_MODELVIEW') { + varName = 'czm_instanced_modelView'; + } else if (semantic === 'MODELVIEWPROJECTION') { + varName = 'czm_instanced_modelViewProjection'; + globalVarsHeader += 'mat4 czm_instanced_modelViewProjection;\n'; + globalVarsMain += 'czm_instanced_modelViewProjection = czm_projection * czm_instanced_modelView;\n'; + } else if (semantic === 'MODELVIEWINVERSETRANSPOSE') { + varName = 'czm_instanced_modelViewInverseTranspose'; + globalVarsHeader += 'mat3 czm_instanced_modelViewInverseTranspose;\n'; + globalVarsMain += 'czm_instanced_modelViewInverseTranspose = mat3(czm_instanced_modelView);\n'; + } + + // Remove the uniform declaration + var regex = new RegExp('uniform.*' + uniform + '.*'); + renamedSource = renamedSource.replace(regex, ''); + + // Replace all occurrences of the uniform with the global variable + regex = new RegExp(uniform + '\\b', 'g'); + renamedSource = renamedSource.replace(regex, varName); + } + } + + // czm_instanced_model is the model matrix of the instance relative to center + // czm_instanced_modifiedModelView is the transform from the center to view + // czm_instanced_nodeTransform is the local offset of the node within the model + var uniforms = + 'uniform mat4 czm_instanced_modifiedModelView;\n' + + 'uniform mat4 czm_instanced_nodeTransform;\n'; + + var batchIdAttribute = usesBatchTable ? 'attribute float a_batchId;\n' : ''; + + var instancedSource = + uniforms + + globalVarsHeader + + 'mat4 czm_instanced_modelView;\n' + + 'attribute vec4 czm_modelMatrixRow0;\n' + + 'attribute vec4 czm_modelMatrixRow1;\n' + + 'attribute vec4 czm_modelMatrixRow2;\n' + + batchIdAttribute + + renamedSource + + 'void main()\n' + + '{\n' + + ' mat4 czm_instanced_model = mat4(czm_modelMatrixRow0.x, czm_modelMatrixRow1.x, czm_modelMatrixRow2.x, 0.0, czm_modelMatrixRow0.y, czm_modelMatrixRow1.y, czm_modelMatrixRow2.y, 0.0, czm_modelMatrixRow0.z, czm_modelMatrixRow1.z, czm_modelMatrixRow2.z, 0.0, czm_modelMatrixRow0.w, czm_modelMatrixRow1.w, czm_modelMatrixRow2.w, 1.0);\n' + + ' czm_instanced_modelView = czm_instanced_modifiedModelView * czm_instanced_model * czm_instanced_nodeTransform;\n' + + globalVarsMain + + ' czm_instancing_main();\n' + + '}'; + + vertexShaderCached = instancedSource; + + if (usesBatchTable) { + instancedSource = collection._batchTable.getVertexShaderCallback(true, 'a_batchId')(instancedSource); + } + + return instancedSource; + }; + } + + function getFragmentShaderCallback(collection) { + return function(fs) { + var batchTable = collection._batchTable; + if (defined(batchTable)) { + var gltf = collection._model.gltf; + var diffuseUniformName = getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE'); + fs = batchTable.getFragmentShaderCallback(true, diffuseUniformName)(fs); + } + return fs; + }; + } + + function getPickVertexShaderCallback(collection) { + return function (vs) { + // Use the vertex shader that was generated earlier + vs = vertexShaderCached; + var usesBatchTable = defined(collection._batchTable); + var allowPicking = collection._allowPicking; + if (usesBatchTable) { + vs = collection._batchTable.getPickVertexShaderCallback('a_batchId')(vs); + } else if (allowPicking) { + vs = ShaderSource.createPickVertexShaderSource(vs); + } + return vs; + }; + } + + function getPickFragmentShaderCallback(collection) { + return function(fs) { + var usesBatchTable = defined(collection._batchTable); + var allowPicking = collection._allowPicking; + if (usesBatchTable) { + fs = collection._batchTable.getPickFragmentShaderCallback()(fs); + } else if (allowPicking) { + fs = ShaderSource.createPickFragmentShaderSource(fs, 'varying'); + } + return fs; + }; + } + + function createModifiedModelView(collection, context) { + return function() { + return Matrix4.multiply(context.uniformState.view, collection._rtcTransform, collection._rtcModelView); + }; + } + + function createNodeTransformFunction(node) { + return function() { + return node.computedMatrix; + }; + } + + function getUniformMapCallback(collection, context) { + return function(uniformMap, programName, node) { + uniformMap = clone(uniformMap); + uniformMap.czm_instanced_modifiedModelView = createModifiedModelView(collection, context); + uniformMap.czm_instanced_nodeTransform = createNodeTransformFunction(node); + + // Remove instanced uniforms from the uniform map + var instancedUniforms = getInstancedUniforms(collection, programName); + for (var uniform in instancedUniforms) { + if (instancedUniforms.hasOwnProperty(uniform)) { + delete uniformMap[uniform]; + } + } + + if (defined(collection._batchTable)) { + uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap); + } + + return uniformMap; + }; + } + + function getPickUniformMapCallback(collection) { + return function(uniformMap) { + // Uses the uniform map generated from getUniformMapCallback + if (defined(collection._batchTable)) { + uniformMap = collection._batchTable.getPickUniformMapCallback()(uniformMap); + } + return uniformMap; + }; + } + + function getVertexShaderNonInstancedCallback(collection) { + return function(vs) { + if (defined(collection._batchTable)) { + vs = collection._batchTable.getVertexShaderCallback(true, 'a_batchId')(vs); + // Treat a_batchId as a uniform rather than a vertex attribute + vs = 'uniform float a_batchId\n;' + vs; + } + return vs; + }; + } + + function getPickVertexShaderNonInstancedCallback(collection) { + return function(vs) { + if (defined(collection._batchTable)) { + vs = collection._batchTable.getPickVertexShaderCallback('a_batchId')(vs); + // Treat a_batchId as a uniform rather than a vertex attribute + vs = 'uniform float a_batchId\n;' + vs; + } + return vs; + }; + } + + function getPickFragmentShaderNonInstancedCallback(collection) { + return function(fs) { + var usesBatchTable = defined(collection._batchTable); + var allowPicking = collection._allowPicking; + if (usesBatchTable) { + fs = collection._batchTable.getPickFragmentShaderCallback()(fs); + } else if (allowPicking) { + fs = ShaderSource.createPickFragmentShaderSource(fs, 'uniform'); + } + return fs; + }; + } + + function getUniformMapNonInstancedCallback(collection) { + return function(uniformMap) { + if (defined(collection._batchTable)) { + uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap); + } + + return uniformMap; + }; + } + + function getVertexBufferTypedArray(collection) { + var instances = collection._instances; + var instancesLength = collection.length; + var collectionCenter = collection._center; + var vertexSizeInFloats = 12; + + var bufferData = collection._vertexBufferTypedArray; + if (!defined(bufferData)) { + bufferData = new Float32Array(instancesLength * vertexSizeInFloats); + } + if (collection._dynamic) { + // Hold onto the buffer data so we don't have to allocate new memory every frame. + collection._vertexBufferTypedArray = bufferData; + } + + for (var i = 0; i < instancesLength; ++i) { + var modelMatrix = instances[i]._modelMatrix; + + // Instance matrix is relative to center + var instanceMatrix = Matrix4.clone(modelMatrix, scratchMatrix); + instanceMatrix[12] -= collectionCenter.x; + instanceMatrix[13] -= collectionCenter.y; + instanceMatrix[14] -= collectionCenter.z; + + var offset = i * vertexSizeInFloats; + + // First three rows of the model matrix + bufferData[offset + 0] = instanceMatrix[0]; + bufferData[offset + 1] = instanceMatrix[4]; + bufferData[offset + 2] = instanceMatrix[8]; + bufferData[offset + 3] = instanceMatrix[12]; + bufferData[offset + 4] = instanceMatrix[1]; + bufferData[offset + 5] = instanceMatrix[5]; + bufferData[offset + 6] = instanceMatrix[9]; + bufferData[offset + 7] = instanceMatrix[13]; + bufferData[offset + 8] = instanceMatrix[2]; + bufferData[offset + 9] = instanceMatrix[6]; + bufferData[offset + 10] = instanceMatrix[10]; + bufferData[offset + 11] = instanceMatrix[14]; + } + + return bufferData; + } + + function createVertexBuffer(collection, context) { + var i; + var instances = collection._instances; + var instancesLength = collection.length; + var dynamic = collection._dynamic; + var usesBatchTable = defined(collection._batchTable); + var allowPicking = collection._allowPicking; + + if (usesBatchTable) { + var batchIdBufferData = new Uint16Array(instancesLength); + for (i = 0; i < instancesLength; ++i) { + batchIdBufferData[i] = instances[i]._instanceId; + } + collection._batchIdBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : batchIdBufferData, + usage : BufferUsage.STATIC_DRAW + }); + } + + if (allowPicking && !usesBatchTable) { + var pickIdBuffer = new Uint8Array(instancesLength * 4); + for (i = 0; i < instancesLength; ++i) { + var pickId = collection._pickIds[i]; + var pickColor = pickId.color; + var offset = i * 4; + pickIdBuffer[offset] = Color.floatToByte(pickColor.red); + pickIdBuffer[offset + 1] = Color.floatToByte(pickColor.green); + pickIdBuffer[offset + 2] = Color.floatToByte(pickColor.blue); + pickIdBuffer[offset + 3] = Color.floatToByte(pickColor.alpha); + } + collection._pickIdBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : pickIdBuffer, + usage : BufferUsage.STATIC_DRAW + }); + } + + var vertexBufferTypedArray = getVertexBufferTypedArray(collection); + collection._vertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : vertexBufferTypedArray, + usage : dynamic ? BufferUsage.STREAM_DRAW : BufferUsage.STATIC_DRAW + }); + } + + function updateVertexBuffer(collection) { + var vertexBufferTypedArray = getVertexBufferTypedArray(collection); + collection._vertexBuffer.copyFromArrayView(vertexBufferTypedArray); + } + + function createPickIds(collection, context) { + // PERFORMANCE_IDEA: we could skip the pick buffer completely by allocating + // a continuous range of pickIds and then converting the base pickId + batchId + // to RGBA in the shader. The only consider is precision issues, which might + // not be an issue in WebGL 2. + var instances = collection._instances; + var instancesLength = instances.length; + var pickIds = new Array(instancesLength); + for (var i = 0; i < instancesLength; ++i) { + pickIds[i] = context.createPickId(instances[i]); + } + return pickIds; + } + + function createModel(collection, context) { + var instancingSupported = collection._instancingSupported; + var usesBatchTable = defined(collection._batchTable); + var allowPicking = collection._allowPicking; + + var modelOptions = { + url : collection._url, + headers : collection._headers, + requestType : collection._requestType, + gltf : collection._gltf, + basePath : collection._basePath, + shadows : collection._shadows, + cacheKey : undefined, + asynchronous : collection._asynchronous, + allowPicking : allowPicking, + incrementallyLoadTextures : collection._incrementallyLoadTextures, + upAxis : collection._upAxis, + precreatedAttributes : undefined, + vertexShaderLoaded : undefined, + fragmentShaderLoaded : undefined, + uniformMapLoaded : undefined, + pickVertexShaderLoaded : undefined, + pickFragmentShaderLoaded : undefined, + pickUniformMapLoaded : undefined, + ignoreCommands : true + }; + + if (allowPicking && !usesBatchTable) { + collection._pickIds = createPickIds(collection, context); + } + + if (instancingSupported) { + createVertexBuffer(collection, context); + + var vertexSizeInFloats = 12; + var componentSizeInBytes = ComponentDatatype.getSizeInBytes(ComponentDatatype.FLOAT); + + var instancedAttributes = { + czm_modelMatrixRow0 : { + index : 0, // updated in Model + vertexBuffer : collection._vertexBuffer, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + normalize : false, + offsetInBytes : 0, + strideInBytes : componentSizeInBytes * vertexSizeInFloats, + instanceDivisor : 1 + }, + czm_modelMatrixRow1 : { + index : 0, // updated in Model + vertexBuffer : collection._vertexBuffer, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + normalize : false, + offsetInBytes : componentSizeInBytes * 4, + strideInBytes : componentSizeInBytes * vertexSizeInFloats, + instanceDivisor : 1 + }, + czm_modelMatrixRow2 : { + index : 0, // updated in Model + vertexBuffer : collection._vertexBuffer, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + normalize : false, + offsetInBytes : componentSizeInBytes * 8, + strideInBytes : componentSizeInBytes * vertexSizeInFloats, + instanceDivisor : 1 + } + }; + + // When using a batch table, add a batch id attribute + if (usesBatchTable) { + instancedAttributes.a_batchId = { + index : 0, // updated in Model + vertexBuffer : collection._batchIdBuffer, + componentsPerAttribute : 1, + componentDatatype : ComponentDatatype.UNSIGNED_SHORT, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0, + instanceDivisor : 1 + }; + } + + if (allowPicking && !usesBatchTable) { + instancedAttributes.pickColor = { + index : 0, // updated in Model + vertexBuffer : collection._pickIdBuffer, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + normalize : true, + offsetInBytes : 0, + strideInBytes : 0, + instanceDivisor : 1 + }; + } + + modelOptions.precreatedAttributes = instancedAttributes; + modelOptions.vertexShaderLoaded = getVertexShaderCallback(collection); + modelOptions.fragmentShaderLoaded = getFragmentShaderCallback(collection); + modelOptions.uniformMapLoaded = getUniformMapCallback(collection, context); + modelOptions.pickVertexShaderLoaded = getPickVertexShaderCallback(collection); + modelOptions.pickFragmentShaderLoaded = getPickFragmentShaderCallback(collection); + modelOptions.pickUniformMapLoaded = getPickUniformMapCallback(collection); + + if (defined(collection._url)) { + modelOptions.cacheKey = collection._url + '#instanced'; + } + } else { + modelOptions.vertexShaderLoaded = getVertexShaderNonInstancedCallback(collection); + modelOptions.fragmentShaderLoaded = getFragmentShaderCallback(collection); + modelOptions.uniformMapLoaded = getUniformMapNonInstancedCallback(collection, context); + modelOptions.pickVertexShaderLoaded = getPickVertexShaderNonInstancedCallback(collection); + modelOptions.pickFragmentShaderLoaded = getPickFragmentShaderNonInstancedCallback(collection); + modelOptions.pickUniformMapLoaded = getPickUniformMapCallback(collection); + } + + if (defined(collection._url)) { + collection._model = Model.fromGltf(modelOptions); + } else { + collection._model = new Model(modelOptions); + } + } + + function updateWireframe(collection) { + if (collection._debugWireframe !== collection.debugWireframe) { + collection._debugWireframe = collection.debugWireframe; + + // This assumes the original primitive was TRIANGLES and that the triangles + // are connected for the wireframe to look perfect. + var primitiveType = collection.debugWireframe ? PrimitiveType.LINES : PrimitiveType.TRIANGLES; + var commands = collection._drawCommands; + var length = commands.length; + for (var i = 0; i < length; ++i) { + commands[i].primitiveType = primitiveType; + } + } + } + function updateShowBoundingVolume(collection) { + if (collection.debugShowBoundingVolume !== collection._debugShowBoundingVolume) { + collection._debugShowBoundingVolume = collection.debugShowBoundingVolume; + + var commands = collection._drawCommands; + var length = commands.length; + for (var i = 0; i < length; ++i) { + commands[i].debugShowBoundingVolume = collection.debugShowBoundingVolume; + } + } + } + + function createCommands(collection, drawCommands, pickCommands) { + var commandsLength = drawCommands.length; + var instancesLength = collection.length; + var allowPicking = collection.allowPicking; + var boundingSphere = collection._boundingSphere; + var cull = collection._cull; + + for (var i = 0; i < commandsLength; ++i) { + var drawCommand = DrawCommand.shallowClone(drawCommands[i]); + drawCommand.instanceCount = instancesLength; + drawCommand.boundingVolume = boundingSphere; + drawCommand.cull = cull; + collection._drawCommands.push(drawCommand); + + if (allowPicking) { + var pickCommand = DrawCommand.shallowClone(pickCommands[i]); + pickCommand.instanceCount = instancesLength; + pickCommand.boundingVolume = boundingSphere; + pickCommand.cull = cull; + collection._pickCommands.push(pickCommand); + } + } + } + + function createBatchIdFunction(batchId) { + return function() { + return batchId; + }; + } + + function createPickColorFunction(color) { + return function() { + return color; + }; + } + + function createCommandsNonInstanced(collection, drawCommands, pickCommands) { + // When instancing is disabled, create commands for every instance. + var instances = collection._instances; + var commandsLength = drawCommands.length; + var instancesLength = collection.length; + var allowPicking = collection.allowPicking; + var usesBatchTable = defined(collection._batchTable); + var cull = collection._cull; + + 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 updateCommandsNonInstanced + drawCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced + drawCommand.cull = cull; + drawCommand.uniformMap = clone(drawCommand.uniformMap); + if (usesBatchTable) { + drawCommand.uniformMap.a_batchId = createBatchIdFunction(instances[j]._instanceId); + } + collection._drawCommands.push(drawCommand); + + if (allowPicking) { + var pickCommand = DrawCommand.shallowClone(pickCommands[i]); + pickCommand.modelMatrix = new Matrix4(); // Updated in updateCommandsNonInstanced + pickCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced + pickCommand.cull = cull; + pickCommand.uniformMap = clone(pickCommand.uniformMap); + if (usesBatchTable) { + pickCommand.uniformMap.a_batchId = createBatchIdFunction(instances[j]._instanceId); + } else if (allowPicking) { + var pickId = collection._pickIds[j]; + pickCommand.uniformMap.czm_pickColor = createPickColorFunction(pickId.color); + } + collection._pickCommands.push(pickCommand); + } + } + } + } + + function updateCommandsNonInstanced(collection) { + var modelCommands = collection._modelCommands; + var commandsLength = modelCommands.length; + var instancesLength = collection.length; + var allowPicking = collection.allowPicking; + var collectionTransform = collection._rtcTransform; + var collectionCenter = collection._center; + + for (var i = 0; i < commandsLength; ++i) { + var modelCommand = modelCommands[i]; + for (var j = 0; j < instancesLength; ++j) { + var commandIndex = i * instancesLength + j; + var drawCommand = collection._drawCommands[commandIndex]; + var instanceMatrix = Matrix4.clone(collection._instances[j]._modelMatrix, scratchMatrix); + instanceMatrix[12] -= collectionCenter.x; + instanceMatrix[13] -= collectionCenter.y; + instanceMatrix[14] -= collectionCenter.z; + instanceMatrix = Matrix4.multiply(collectionTransform, instanceMatrix, scratchMatrix); + var nodeMatrix = modelCommand.modelMatrix; + var modelMatrix = drawCommand.modelMatrix; + Matrix4.multiply(instanceMatrix, nodeMatrix, modelMatrix); + + var nodeBoundingSphere = modelCommand.boundingVolume; + var boundingSphere = drawCommand.boundingVolume; + BoundingSphere.transform(nodeBoundingSphere, instanceMatrix, boundingSphere); + + if (allowPicking) { + var pickCommand = collection._pickCommands[commandIndex]; + Matrix4.clone(modelMatrix, pickCommand.modelMatrix); + BoundingSphere.clone(boundingSphere, pickCommand.boundingVolume); + } + } + } + } + + function getModelCommands(model) { + var nodeCommands = model._nodeCommands; + var length = nodeCommands.length; + + var drawCommands = []; + var pickCommands = []; + + for (var i = 0; i < length; ++i) { + var nc = nodeCommands[i]; + if (nc.show) { + drawCommands.push(nc.command); + pickCommands.push(nc.pickCommand); + } + } + + return { + draw: drawCommands, + pick: pickCommands + }; + } + + function updateShadows(collection) { + if (collection.shadows !== collection._shadows) { + collection._shadows = collection.shadows; + + var castShadows = ShadowMode.castShadows(collection.shadows); + var receiveShadows = ShadowMode.receiveShadows(collection.shadows); + + var drawCommands = collection._drawCommands; + var length = drawCommands.length; + for (var i = 0; i < length; ++i) { + var drawCommand = drawCommands[i]; + drawCommand.castShadows = castShadows; + drawCommand.receiveShadows = receiveShadows; + } + } + } + + ModelInstanceCollection.prototype.update = function(frameState) { + if (frameState.mode === SceneMode.MORPHING) { + return; + } + + if (!this.show) { + return; + } + + if (this.length === 0) { + return; + } + + var context = frameState.context; + + if (this._state === LoadState.NEEDS_LOAD) { + this._state = LoadState.LOADING; + this._instancingSupported = context.instancedArrays; + createModel(this, context); + var that = this; + this._model.readyPromise.otherwise(function(error) { + that._state = LoadState.FAILED; + that._readyPromise.reject(error); + }); + } + + var instancingSupported = this._instancingSupported; + var model = this._model; + model.update(frameState); + + if (model.ready && (this._state === LoadState.LOADING)) { + this._state = LoadState.LOADED; + this._ready = true; + + // 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; + + if (instancingSupported) { + createCommands(this, modelCommands.draw, modelCommands.pick); + } else { + createCommandsNonInstanced(this, modelCommands.draw, modelCommands.pick); + updateCommandsNonInstanced(this); + } + + this._readyPromise.resolve(this); + return; + } + + if (this._state !== LoadState.LOADED) { + return; + } + + var modeChanged = (frameState.mode !== this._mode); + var modelMatrix = this.modelMatrix; + var modelMatrixChanged = !Matrix4.equals(this._modelMatrix, modelMatrix); + + if (modeChanged || modelMatrixChanged) { + this._mode = frameState.mode; + Matrix4.clone(modelMatrix, this._modelMatrix); + var rtcTransform = Matrix4.multiplyByTranslation(this._modelMatrix, this._center, this._rtcTransform); + if (this._mode !== SceneMode.SCENE3D) { + rtcTransform = Transforms.basisTo2D(frameState.mapProjection, rtcTransform, rtcTransform); + } + Matrix4.getTranslation(rtcTransform, this._boundingSphere.center); + } + + if (instancingSupported && this._dirty) { + // If at least one instance has moved assume the collection is now dynamic + this._dynamic = true; + this._dirty = false; + + // PERFORMANCE_IDEA: only update dirty sub-sections instead of the whole collection + updateVertexBuffer(this); + } + + // If any node changes due to an animation, update the commands. This could be inefficient if the model is + // composed of many nodes and only one changes, however it is probably fine in the general use case. + // Only applies when instancing is disabled. The instanced shader automatically handles node transformations. + if (!instancingSupported && (model.dirty || this._dirty || modeChanged || modelMatrixChanged)) { + updateCommandsNonInstanced(this); + } + + updateShadows(this); + updateWireframe(this); + updateShowBoundingVolume(this); + + var passes = frameState.passes; + var commandList = frameState.commandList; + var commands = passes.render ? this._drawCommands : this._pickCommands; + var commandsLength = commands.length; + + for (var i = 0; i < commandsLength; ++i) { + commandList.push(commands[i]); + } + }; + + ModelInstanceCollection.prototype.isDestroyed = function() { + return false; + }; + + ModelInstanceCollection.prototype.destroy = function() { + this._model = this._model && this._model.destroy(); + + var pickIds = this._pickIds; + if (defined(pickIds)) { + var length = pickIds.length; + for (var i = 0; i < length; ++i) { + pickIds[i].destroy(); + } + } + + return destroyObject(this); + }; + + return ModelInstanceCollection; +}); diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index c74b7e945611..8f0131f6cae2 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -87,6 +87,9 @@ define([ this._viewport = new BoundingRectangle(); this._rs = undefined; + + this._useScissorTest = false; + this._scissorRectangle = undefined; } function destroyTextures(oit) { @@ -200,7 +203,7 @@ define([ return supported; } - OIT.prototype.update = function(context, framebuffer) { + OIT.prototype.update = function(context, passState, framebuffer) { if (!this.isSupported()) { return; } @@ -312,9 +315,22 @@ define([ this._viewport.width = width; this._viewport.height = height; - if (!defined(this._rs) || !BoundingRectangle.equals(this._viewport, this._rs.viewport)) { + var useScissorTest = !BoundingRectangle.equals(this._viewport, passState.viewport); + var updateScissor = useScissorTest !== this._useScissorTest; + this._useScissorTest = useScissorTest; + + if (!BoundingRectangle.equals(this._scissorRectangle, passState.viewport)) { + this._scissorRectangle = BoundingRectangle.clone(passState.viewport, this._scissorRectangle); + updateScissor = true; + } + + if (!defined(this._rs) || !BoundingRectangle.equals(this._viewport, this._rs.viewport) || updateScissor) { this._rs = RenderState.fromCache({ - viewport : this._viewport + viewport : this._viewport, + scissorTest : { + enabled : this._useScissorTest, + rectangle : this._scissorRectangle + } }); } diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js index 383a2da4681c..a95f3470de5a 100644 --- a/Source/Scene/OrthographicFrustum.js +++ b/Source/Scene/OrthographicFrustum.js @@ -2,13 +2,11 @@ define([ '../Core/defined', '../Core/defineProperties', - '../Core/deprecationWarning', '../Core/DeveloperError', './OrthographicOffCenterFrustum' ], function( defined, defineProperties, - deprecationWarning, DeveloperError, OrthographicOffCenterFrustum) { 'use strict'; @@ -26,10 +24,6 @@ define([ * var maxRadii = ellipsoid.maximumRadius; * * var frustum = new Cesium.OrthographicOffCenterFrustum(); - * frustum.right = maxRadii * Cesium.Math.PI; - * frustum.left = -c.frustum.right; - * frustum.top = c.frustum.right * (canvas.clientHeight / canvas.clientWidth); - * frustum.bottom = -c.frustum.top; * frustum.near = 0.01 * maxRadii; * frustum.far = 50.0 * maxRadii; */ @@ -57,8 +51,6 @@ define([ */ this.far = 500000000.0; this._far = this.far; - - this._useDeprecated = false; } function update(frustum) { @@ -86,15 +78,14 @@ define([ frustum._near = frustum.near; frustum._far = frustum.far; - if (!frustum._useDeprecated) { - var ratio = 1.0 / frustum.aspectRatio; - f.right = frustum.width * 0.5; - f.left = -f.right; - f.top = ratio * f.right; - f.bottom = -f.top; - f.near = frustum.near; - f.far = frustum.far; - } + var ratio = 1.0 / frustum.aspectRatio; + f.right = frustum.width * 0.5; + f.left = -f.right; + f.top = ratio * f.right; + f.bottom = -f.top; + f.near = frustum.near; + f.far = frustum.far; + } } @@ -110,75 +101,8 @@ define([ update(this); return this._offCenterFrustum.projectionMatrix; } - }, - - /** - * The left clipping plane. - * @type {Number} - * @default undefined - */ - left : { - get : function() { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - return this._offCenterFrustum.left; - }, - set : function(value) { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - this._useDeprecated = true; - this._offCenterFrustum.left = value; - } - }, - - /** - * The right clipping plane. - * @type {Number} - * @default undefined - */ - right : { - get : function() { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - return this._offCenterFrustum.right; - }, - set : function(value) { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - this._useDeprecated = true; - this._offCenterFrustum.right = value; - } - }, - - /** - * The top clipping plane. - * @type {Number} - * @default undefined - */ - top : { - get : function() { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - return this._offCenterFrustum.top; - }, - set : function(value) { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - this._useDeprecated = true; - this._offCenterFrustum.top = value; - } - }, - - /** - * The bottom clipping plane. - * @type {Number} - * @default undefined - */ - bottom : { - get : function() { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - return this._offCenterFrustum.bottom; - }, - set : function(value) { - deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.'); - this._useDeprecated = true; - this._offCenterFrustum.bottom = value; - } } + }); /** diff --git a/Source/Scene/OrthographicOffCenterFrustum.js b/Source/Scene/OrthographicOffCenterFrustum.js index d14053b6ec51..1d84ac5b253f 100644 --- a/Source/Scene/OrthographicOffCenterFrustum.js +++ b/Source/Scene/OrthographicOffCenterFrustum.js @@ -180,6 +180,7 @@ define([ var f = this.far; var right = Cartesian3.cross(direction, up, getPlanesRight); + Cartesian3.normalize(right, right); var nearCenter = getPlanesNearCenter; Cartesian3.multiplyByScalar(direction, n, nearCenter); Cartesian3.add(position, nearCenter, nearCenter); diff --git a/Source/Scene/PerformanceDisplay.js b/Source/Scene/PerformanceDisplay.js index d624d002b561..7030602551b7 100644 --- a/Source/Scene/PerformanceDisplay.js +++ b/Source/Scene/PerformanceDisplay.js @@ -44,11 +44,10 @@ define([ display.appendChild(fpsElement); this._container.appendChild(display); - this._lastFpsSampleTime = undefined; - this._frameCount = 0; - this._time = undefined; - this._fps = 0; - this._frameTime = 0; + this._lastFpsSampleTime = getTimestamp(); + this._lastMsSampleTime = getTimestamp(); + this._fpsFrameCount = 0; + this._msFrameCount = 0; } /** @@ -56,39 +55,24 @@ define([ * each call records a frame in the internal buffer and redraws the display. */ PerformanceDisplay.prototype.update = function() { - if (!defined(this._time)) { - //first update - this._lastFpsSampleTime = getTimestamp(); - this._time = getTimestamp(); - return; - } - - var previousTime = this._time; var time = getTimestamp(); - this._time = time; - - var frameTime = time - previousTime; - this._frameCount++; - var fps = this._fps; + this._fpsFrameCount++; var fpsElapsedTime = time - this._lastFpsSampleTime; if (fpsElapsedTime > 1000) { - fps = this._frameCount * 1000 / fpsElapsedTime | 0; - - this._lastFpsSampleTime = time; - this._frameCount = 0; - } - - if (fps !== this._fps) { + var fps = this._fpsFrameCount * 1000 / fpsElapsedTime | 0; this._fpsText.nodeValue = fps + ' FPS'; - this._fps = fps; + this._lastFpsSampleTime = time; + this._fpsFrameCount = 0; } - if (frameTime !== this._frameTime) { - this._msText.nodeValue = frameTime.toFixed(2) + ' MS'; - this._frameTime = frameTime; + this._msFrameCount++; + var msElapsedTime = time - this._lastMsSampleTime; + if (msElapsedTime > 200) { + this._msText.nodeValue = (msElapsedTime / this._msFrameCount).toFixed(2) + ' MS'; + this._lastMsSampleTime = time; + this._msFrameCount = 0; } - }; /** diff --git a/Source/Scene/PerspectiveOffCenterFrustum.js b/Source/Scene/PerspectiveOffCenterFrustum.js index fdb5322fe548..fe23401db8cc 100644 --- a/Source/Scene/PerspectiveOffCenterFrustum.js +++ b/Source/Scene/PerspectiveOffCenterFrustum.js @@ -220,6 +220,7 @@ define([ Cartesian3.subtract(normal, position, normal); Cartesian3.normalize(normal, normal); Cartesian3.cross(normal, up, normal); + Cartesian3.normalize(normal, normal); var plane = planes[0]; if (!defined(plane)) { @@ -234,8 +235,8 @@ define([ Cartesian3.multiplyByScalar(right, r, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); - Cartesian3.normalize(normal, normal); Cartesian3.cross(up, normal, normal); + Cartesian3.normalize(normal, normal); plane = planes[1]; if (!defined(plane)) { @@ -250,8 +251,8 @@ define([ Cartesian3.multiplyByScalar(up, b, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); - Cartesian3.normalize(normal, normal); Cartesian3.cross(right, normal, normal); + Cartesian3.normalize(normal, normal); plane = planes[2]; if (!defined(plane)) { @@ -266,8 +267,8 @@ define([ Cartesian3.multiplyByScalar(up, t, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); - Cartesian3.normalize(normal, normal); Cartesian3.cross(normal, right, normal); + Cartesian3.normalize(normal, normal); plane = planes[3]; if (!defined(plane)) { diff --git a/Source/Scene/PointAppearance.js b/Source/Scene/PointAppearance.js deleted file mode 100644 index ee481d512c1c..000000000000 --- a/Source/Scene/PointAppearance.js +++ /dev/null @@ -1,233 +0,0 @@ -/*global define*/ -define([ - '../Core/Color', - '../Core/combine', - '../Core/defaultValue', - '../Core/defined', - '../Core/defineProperties', - '../Core/VertexFormat', - '../Shaders/Appearances/PointAppearanceFS', - '../Shaders/Appearances/PointAppearanceVS', - './Appearance' - ], function( - Color, - combine, - defaultValue, - defined, - defineProperties, - VertexFormat, - PointAppearanceFS, - PointAppearanceVS, - Appearance) { - 'use strict'; - - /** - * An appearance for point geometry {@link PointGeometry} - * - * @alias PointAppearance - * @constructor - * - * @param {Object} [options] Object with the following properties: - * @param {Boolean} [options.translucent=false] When true, the geometry is expected to appear translucent so {@link PointAppearance#renderState} has alpha blending enabled. - * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. - * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. - * @param {RenderState} [options.renderState] Optional render state to override the default render state. - * @param {Object} [options.uniforms] Additional uniforms that are used by the vertex and fragment shaders. - * @param {Number} [options.pointSize] Point size in pixels. - * @param {Color} [options.highlightColor] Color multiplier in the fragment shader. The alpha channel is used for alpha blending when translucency is enabled. - * - * @example - * var primitive = new Cesium.Primitive({ - * geometryInstances : new Cesium.GeometryInstance({ - * geometry : new Cesium.PointGeometry({ - * positionsTypedArray : positions, - * colorsTypedArray : colors, - * boundingSphere : boundingSphere - * }) - * }), - * appearance : new Cesium.PointAppearance({ - * translucent : true - * }) - * }); - * - * @private - */ - function PointAppearance(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - this._vertexShaderSource = defaultValue(options.vertexShaderSource, PointAppearanceVS); - this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, PointAppearanceFS); - this._renderState = Appearance.getDefaultRenderState(false, false, options.renderState); - this._pointSize = defaultValue(options.pointSize, 2.0); - this._highlightColor = defined(options.highlightColor) ? options.highlightColor : new Color(); - - /** - * This property is part of the {@link Appearance} interface, but is not - * used by {@link PointAppearance} since a fully custom fragment shader is used. - * - * @type Material - * - * @default undefined - */ - this.material = undefined; - - /** - * When true, the geometry is expected to appear translucent. - * - * @type {Boolean} - * - * @default false - */ - this.translucent = defaultValue(options.translucent, false); - - /** - * @private - */ - this.uniforms = { - highlightColor : this._highlightColor, - pointSize : this._pointSize - }; - - // Combine default uniforms and additional uniforms - var optionsUniforms = options.uniforms; - this.uniforms = combine(this.uniforms, optionsUniforms, true); - } - - defineProperties(PointAppearance.prototype, { - /** - * The GLSL source code for the vertex shader. - * - * @memberof PointAppearance.prototype - * - * @type {String} - * @readonly - */ - vertexShaderSource : { - get : function() { - return this._vertexShaderSource; - } - }, - - /** - * The GLSL source code for the fragment shader. The full fragment shader - * source is built procedurally taking into account the {@link PointAppearance#material}. - * Use {@link PointAppearance#getFragmentShaderSource} to get the full source. - * - * @memberof PointAppearance.prototype - * - * @type {String} - * @readonly - */ - fragmentShaderSource : { - get : function() { - return this._fragmentShaderSource; - } - }, - - /** - * The WebGL fixed-function state to use when rendering the geometry. - * - * @memberof PointAppearance.prototype - * - * @type {Object} - * @readonly - */ - renderState : { - get : function() { - return this._renderState; - } - }, - - /** - * When true, the geometry is expected to be closed. - * - * @memberof PointAppearance.prototype - * - * @type {Boolean} - * @readonly - * - * @default false - */ - closed : { - get : function() { - return false; - } - }, - - /** - * The {@link VertexFormat} that this appearance instance is compatible with. - * A geometry can have more vertex attributes and still be compatible - at a - * potential performance cost - but it can't have less. - * - * @memberof PointAppearance.prototype - * - * @type VertexFormat - * @readonly - * - * @default {@link PointAppearance.VERTEX_FORMAT} - */ - vertexFormat : { - get : function() { - return PointAppearance.VERTEX_FORMAT; - } - }, - - /** - * The size in pixels used when rendering the primitive. This helps calculate an accurate - * bounding volume for point rendering and other appearances that are defined in pixel sizes. - * - * @memberof PointAppearance.prototype - * - * @type {Number} - * @readonly - */ - pixelSize : { - get : function() { - return this._pointSize; - } - } - }); - - /** - * The {@link VertexFormat} that all {@link PointAppearance} instances - * are compatible with, which requires only position and color - * attributes. Other attributes are procedurally computed in the fragment shader. - * - * @type VertexFormat - * - * @constant - */ - PointAppearance.VERTEX_FORMAT = VertexFormat.POSITION_AND_COLOR; - - /** - * Returns the full GLSL fragment shader source, which for {@link PointAppearance} is just - * {@link PointAppearance#fragmentShaderSource}. - * - * @function - * - * @returns {String} The full GLSL fragment shader source. - */ - PointAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource; - - /** - * Determines if the geometry is translucent based on {@link PointAppearance#translucent}. - * - * @function - * - * @returns {Boolean} true if the appearance is translucent. - */ - PointAppearance.prototype.isTranslucent = Appearance.prototype.isTranslucent; - - /** - * Creates a render state. This is not the final render state instance; instead, - * it can contain a subset of render state properties identical to the render state - * created in the context. - * - * @function - * - * @returns {Object} The render state. - */ - PointAppearance.prototype.getRenderState = Appearance.prototype.getRenderState; - - return PointAppearance; -}); diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js new file mode 100644 index 000000000000..4e9675c9e966 --- /dev/null +++ b/Source/Scene/PointCloud3DTileContent.js @@ -0,0 +1,1264 @@ +/*global define*/ +define([ + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/Color', + '../Core/combine', + '../Core/ComponentDatatype', + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/FeatureDetection', + '../Core/getStringFromTypedArray', + '../Core/Matrix4', + '../Core/oneTimeWarning', + '../Core/PrimitiveType', + '../Core/RuntimeError', + '../Core/Transforms', + '../Renderer/Buffer', + '../Renderer/BufferUsage', + '../Renderer/DrawCommand', + '../Renderer/Pass', + '../Renderer/RenderState', + '../Renderer/ShaderProgram', + '../Renderer/ShaderSource', + '../Renderer/VertexArray', + '../ThirdParty/when', + './BlendingState', + './Cesium3DTileBatchTable', + './Cesium3DTileFeature', + './Cesium3DTileFeatureTable', + './SceneMode', + './ShadowMode' + ], function( + Cartesian2, + Cartesian3, + Cartesian4, + Color, + combine, + ComponentDatatype, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + FeatureDetection, + getStringFromTypedArray, + Matrix4, + oneTimeWarning, + PrimitiveType, + RuntimeError, + Transforms, + Buffer, + BufferUsage, + DrawCommand, + Pass, + RenderState, + ShaderProgram, + ShaderSource, + VertexArray, + when, + BlendingState, + Cesium3DTileBatchTable, + Cesium3DTileFeature, + Cesium3DTileFeatureTable, + SceneMode, + ShadowMode) { + 'use strict'; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + /** + * Represents the contents of a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/PointCloud/README.md|Points} + * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset. + *

      + * Implements the {@link Cesium3DTileContent} interface. + *

      + * + * @alias PointCloud3DTileContent + * @constructor + * + * @private + */ + function PointCloud3DTileContent(tileset, tile, url, arrayBuffer, byteOffset) { + this._tileset = tileset; + this._tile = tile; + this._url = url; + + // Hold onto the payload until the render resources are created + this._parsedContent = undefined; + + this._drawCommand = undefined; + this._pickCommand = undefined; + this._pickId = undefined; // Only defined when batchTable is undefined + this._isTranslucent = false; + this._styleTranslucent = false; + this._constantColor = Color.clone(Color.WHITE); + this._rtcCenter = undefined; + this._batchTable = undefined; // Used when feature table contains BATCH_ID semantic + + // These values are used to regenerate the shader when the style changes + this._styleableShaderAttributes = undefined; + this._isQuantized = false; + this._isOctEncoded16P = false; + this._isRGB565 = false; + this._hasColors = false; + this._hasNormals = false; + this._hasBatchIds = false; + + // Use per-point normals to hide back-facing points. + this.backFaceCulling = false; + this._backFaceCulling = false; + + this._opaqueRenderState = undefined; + this._translucentRenderState = undefined; + + this._highlightColor = Color.clone(Color.WHITE); + this._pointSize = 1.0; + this._quantizedVolumeScale = undefined; + this._quantizedVolumeOffset = undefined; + + this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); + this._mode = undefined; + + this._readyPromise = when.defer(); + this._pointsLength = 0; + this._geometryByteLength = 0; + + this._features = undefined; + + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + this.featurePropertiesDirty = false; + + initialize(this, arrayBuffer, byteOffset); + } + + defineProperties(PointCloud3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featuresLength + */ + featuresLength : { + get : function() { + if (defined(this._batchTable)) { + return this._batchTable.featuresLength; + } + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#pointsLength + */ + pointsLength : { + get : function() { + return this._pointsLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#trianglesLength + */ + trianglesLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#geometryByteLength + */ + geometryByteLength : { + get : function() { + return this._geometryByteLength; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#texturesByteLength + */ + texturesByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTableByteLength + */ + batchTableByteLength : { + get : function() { + if (defined(this._batchTable)) { + return this._batchTable.memorySizeInBytes; + } + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTable + */ + batchTable : { + get : function() { + return this._batchTable; + } + } + }); + + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + + function initialize(content, arrayBuffer, byteOffset) { + byteOffset = defaultValue(byteOffset, 0); + + var uint8Array = new Uint8Array(arrayBuffer); + var view = new DataView(arrayBuffer); + byteOffset += sizeOfUint32; // Skip magic + + var version = view.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError('Only Point Cloud tile version 1 is supported. Version ' + version + ' is not.'); + } + byteOffset += sizeOfUint32; + + // Skip byteLength + byteOffset += sizeOfUint32; + + var featureTableJsonByteLength = view.getUint32(byteOffset, true); + if (featureTableJsonByteLength === 0) { + throw new RuntimeError('Feature table must have a byte length greater than zero'); + } + byteOffset += sizeOfUint32; + + var featureTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + var batchTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength); + var featureTableJson = JSON.parse(featureTableString); + byteOffset += featureTableJsonByteLength; + + var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength); + byteOffset += featureTableBinaryByteLength; + + // Get the batch table JSON and binary + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + // Has a batch table JSON + var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength); + batchTableJson = JSON.parse(batchTableString); + byteOffset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength); + byteOffset += batchTableBinaryByteLength; + } + } + + var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); + + var pointsLength = featureTable.getGlobalProperty('POINTS_LENGTH'); + featureTable.featuresLength = pointsLength; + + if (!defined(pointsLength)) { + throw new RuntimeError('Feature table global property: POINTS_LENGTH must be defined'); + } + + // Get the positions + var positions; + var isQuantized = false; + + if (defined(featureTableJson.POSITION)) { + positions = featureTable.getPropertyArray('POSITION', ComponentDatatype.FLOAT, 3); + var rtcCenter = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3); + if (defined(rtcCenter)) { + content._rtcCenter = Cartesian3.unpack(rtcCenter); + } + } else if (defined(featureTableJson.POSITION_QUANTIZED)) { + positions = featureTable.getPropertyArray('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3); + isQuantized = true; + + var quantizedVolumeScale = featureTable.getGlobalProperty('QUANTIZED_VOLUME_SCALE', ComponentDatatype.FLOAT, 3); + if (!defined(quantizedVolumeScale)) { + throw new RuntimeError('Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions.'); + } + content._quantizedVolumeScale = Cartesian3.unpack(quantizedVolumeScale); + + var quantizedVolumeOffset = featureTable.getGlobalProperty('QUANTIZED_VOLUME_OFFSET', ComponentDatatype.FLOAT, 3); + if (!defined(quantizedVolumeOffset)) { + throw new RuntimeError('Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.'); + } + content._quantizedVolumeOffset = Cartesian3.unpack(quantizedVolumeOffset); + } + + if (!defined(positions)) { + throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined.'); + } + + // Get the colors + var colors; + var isTranslucent = false; + var isRGB565 = false; + + if (defined(featureTableJson.RGBA)) { + colors = featureTable.getPropertyArray('RGBA', ComponentDatatype.UNSIGNED_BYTE, 4); + isTranslucent = true; + } else if (defined(featureTableJson.RGB)) { + colors = featureTable.getPropertyArray('RGB', ComponentDatatype.UNSIGNED_BYTE, 3); + } else if (defined(featureTableJson.RGB565)) { + colors = featureTable.getPropertyArray('RGB565', ComponentDatatype.UNSIGNED_SHORT, 1); + isRGB565 = true; + } else if (defined(featureTableJson.CONSTANT_RGBA)) { + var constantRGBA = featureTable.getGlobalProperty('CONSTANT_RGBA', ComponentDatatype.UNSIGNED_BYTE, 4); + content._constantColor = Color.fromBytes(constantRGBA[0], constantRGBA[1], constantRGBA[2], constantRGBA[3], content._constantColor); + } else { + // Use a default constant color + content._constantColor = Color.clone(Color.DARKGRAY, content._constantColor); + } + + content._isTranslucent = isTranslucent; + + // Get the normals + var normals; + var isOctEncoded16P = false; + + if (defined(featureTableJson.NORMAL)) { + normals = featureTable.getPropertyArray('NORMAL', ComponentDatatype.FLOAT, 3); + } else if (defined(featureTableJson.NORMAL_OCT16P)) { + normals = featureTable.getPropertyArray('NORMAL_OCT16P', ComponentDatatype.UNSIGNED_BYTE, 2); + isOctEncoded16P = true; + } + + // Get the batchIds and batch table. BATCH_ID does not need to be defined when the point cloud has per-point properties. + var batchIds; + if (defined(featureTableJson.BATCH_ID)) { + batchIds = featureTable.getPropertyArray('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1); + + var batchLength = featureTable.getGlobalProperty('BATCH_LENGTH'); + if (!defined(batchLength)) { + throw new RuntimeError('Global property: BATCH_LENGTH must be defined when BATCH_ID is defined.'); + } + + if (defined(batchTableBinary)) { + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + } + content._batchTable = new Cesium3DTileBatchTable(content, batchLength, batchTableJson, batchTableBinary); + } + + // If points are not batched and there are per-point properties, use these properties for styling purposes + var styleableProperties; + if (!defined(batchIds) && defined(batchTableBinary)) { + styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(pointsLength, batchTableJson, batchTableBinary); + + // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT. + for (var name in styleableProperties) { + if (styleableProperties.hasOwnProperty(name)) { + var property = styleableProperties[name]; + var typedArray = property.typedArray; + var componentDatatype = ComponentDatatype.fromTypedArray(typedArray); + if (componentDatatype === ComponentDatatype.INT || componentDatatype === ComponentDatatype.UNSIGNED_INT || componentDatatype === ComponentDatatype.DOUBLE) { + oneTimeWarning('Cast pnts property to floats', 'Point cloud property "' + name + '" will be casted to a float array because INT, UNSIGNED_INT, and DOUBLE are not valid WebGL vertex attribute types. Some precision may be lost.'); + property.typedArray = new Float32Array(typedArray); + } + } + } + } + + content._parsedContent = { + positions : positions, + colors : colors, + normals : normals, + batchIds : batchIds, + styleableProperties : styleableProperties + }; + content._pointsLength = pointsLength; + content._isQuantized = isQuantized; + content._isOctEncoded16P = isOctEncoded16P; + content._isRGB565 = isRGB565; + content._hasColors = defined(colors); + content._hasNormals = defined(normals); + content._hasBatchIds = defined(batchIds); + } + + var scratchPointSizeAndTilesetTime = new Cartesian2(); + + var positionLocation = 0; + var colorLocation = 1; + var normalLocation = 2; + var batchIdLocation = 3; + var numberOfAttributes = 4; + + function createResources(content, frameState) { + var context = frameState.context; + var parsedContent = content._parsedContent; + var pointsLength = content._pointsLength; + var positions = parsedContent.positions; + var colors = parsedContent.colors; + var normals = parsedContent.normals; + var batchIds = parsedContent.batchIds; + var styleableProperties = parsedContent.styleableProperties; + var hasStyleableProperties = defined(styleableProperties); + var isQuantized = content._isQuantized; + var isOctEncoded16P = content._isOctEncoded16P; + var isRGB565 = content._isRGB565; + var isTranslucent = content._isTranslucent; + var hasColors = content._hasColors; + var hasNormals = content._hasNormals; + var hasBatchIds = content._hasBatchIds; + + var batchTable = content._batchTable; + var hasBatchTable = defined(batchTable); + + var styleableVertexAttributes = []; + var styleableShaderAttributes = {}; + content._styleableShaderAttributes = styleableShaderAttributes; + + if (hasStyleableProperties) { + var attributeLocation = numberOfAttributes; + + for (var name in styleableProperties) { + if (styleableProperties.hasOwnProperty(name)) { + var property = styleableProperties[name]; + var typedArray = property.typedArray; + var componentCount = property.componentCount; + var componentDatatype = ComponentDatatype.fromTypedArray(typedArray); + + var vertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : property.typedArray, + usage : BufferUsage.STATIC_DRAW + }); + + content._geometryByteLength += vertexBuffer.sizeInBytes; + + var vertexAttribute = { + index : attributeLocation, + vertexBuffer : vertexBuffer, + componentsPerAttribute : componentCount, + componentDatatype : componentDatatype, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }; + + styleableVertexAttributes.push(vertexAttribute); + styleableShaderAttributes[name] = { + location : attributeLocation, + componentCount : componentCount + }; + ++attributeLocation; + } + } + } + + var uniformMap = { + u_pointSizeAndTilesetTime : function() { + scratchPointSizeAndTilesetTime.x = content._pointSize; + scratchPointSizeAndTilesetTime.y = content._tileset.timeSinceLoad; + return scratchPointSizeAndTilesetTime; + }, + u_highlightColor : function() { + return content._highlightColor; + }, + u_constantColor : function() { + return content._constantColor; + } + }; + + if (isQuantized) { + uniformMap = combine(uniformMap, { + u_quantizedVolumeScale : function() { + return content._quantizedVolumeScale; + } + }); + } + + var positionsVertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : positions, + usage : BufferUsage.STATIC_DRAW + }); + content._geometryByteLength += positionsVertexBuffer.sizeInBytes; + + var colorsVertexBuffer; + if (hasColors) { + colorsVertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : colors, + usage : BufferUsage.STATIC_DRAW + }); + content._geometryByteLength += colorsVertexBuffer.sizeInBytes; + } + + var normalsVertexBuffer; + if (hasNormals) { + normalsVertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : normals, + usage : BufferUsage.STATIC_DRAW + }); + content._geometryByteLength += normalsVertexBuffer.sizeInBytes; + } + + var batchIdsVertexBuffer; + if (hasBatchIds) { + batchIdsVertexBuffer = Buffer.createVertexBuffer({ + context : context, + typedArray : batchIds, + usage : BufferUsage.STATIC_DRAW + }); + content._geometryByteLength += batchIdsVertexBuffer.sizeInBytes; + } + + var attributes = []; + if (isQuantized) { + attributes.push({ + index : positionLocation, + vertexBuffer : positionsVertexBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.UNSIGNED_SHORT, + normalize : true, // Convert position to 0 to 1 before entering the shader + offsetInBytes : 0, + strideInBytes : 0 + }); + } else { + attributes.push({ + index : positionLocation, + vertexBuffer : positionsVertexBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }); + } + + if (hasColors) { + if (isRGB565) { + attributes.push({ + index : colorLocation, + vertexBuffer : colorsVertexBuffer, + componentsPerAttribute : 1, + componentDatatype : ComponentDatatype.UNSIGNED_SHORT, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }); + } else { + var colorComponentsPerAttribute = isTranslucent ? 4 : 3; + attributes.push({ + index : colorLocation, + vertexBuffer : colorsVertexBuffer, + componentsPerAttribute : colorComponentsPerAttribute, + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + normalize : true, + offsetInBytes : 0, + strideInBytes : 0 + }); + } + } + + if (hasNormals) { + if (isOctEncoded16P) { + attributes.push({ + index : normalLocation, + vertexBuffer : normalsVertexBuffer, + componentsPerAttribute : 2, + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }); + } else { + attributes.push({ + index : normalLocation, + vertexBuffer : normalsVertexBuffer, + componentsPerAttribute : 3, + componentDatatype : ComponentDatatype.FLOAT, + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }); + } + } + + if (hasBatchIds) { + attributes.push({ + index : batchIdLocation, + vertexBuffer : batchIdsVertexBuffer, + componentsPerAttribute : 1, + componentDatatype : ComponentDatatype.fromTypedArray(batchIds), + normalize : false, + offsetInBytes : 0, + strideInBytes : 0 + }); + } + + if (hasStyleableProperties) { + attributes = attributes.concat(styleableVertexAttributes); + } + + var vertexArray = new VertexArray({ + context : context, + attributes : attributes + }); + + var drawUniformMap = uniformMap; + + if (hasBatchTable) { + drawUniformMap = batchTable.getUniformMapCallback()(uniformMap); + } + + var pickUniformMap; + + if (hasBatchTable) { + pickUniformMap = batchTable.getPickUniformMapCallback()(uniformMap); + } else { + content._pickId = context.createPickId({ + primitive : content._tileset, + content : content + }); + + pickUniformMap = combine(uniformMap, { + czm_pickColor : function() { + return content._pickId.color; + } + }); + } + + content._opaqueRenderState = RenderState.fromCache({ + depthTest : { + enabled : true + } + }); + + content._translucentRenderState = RenderState.fromCache({ + depthTest : { + enabled : true + }, + depthMask : false, + blending : BlendingState.ALPHA_BLEND + }); + + content._drawCommand = new DrawCommand({ + boundingVolume : undefined, // Updated in update + cull : false, // Already culled by 3D Tiles + modelMatrix : new Matrix4(), + primitiveType : PrimitiveType.POINTS, + vertexArray : vertexArray, + count : pointsLength, + shaderProgram : undefined, // Updated in createShaders + uniformMap : drawUniformMap, + renderState : isTranslucent ? content._translucentRenderState : content._opaqueRenderState, + pass : isTranslucent ? Pass.TRANSLUCENT : Pass.CESIUM_3D_TILE, + owner : content, + castShadows : false, + receiveShadows : false + }); + + content._pickCommand = new DrawCommand({ + boundingVolume : undefined, // Updated in update + cull : false, // Already culled by 3D Tiles + modelMatrix : new Matrix4(), + primitiveType : PrimitiveType.POINTS, + vertexArray : vertexArray, + count : pointsLength, + shaderProgram : undefined, // Updated in createShaders + uniformMap : pickUniformMap, + renderState : isTranslucent ? content._translucentRenderState : content._opaqueRenderState, + pass : isTranslucent ? Pass.TRANSLUCENT : Pass.CESIUM_3D_TILE, + owner : content + }); + } + + var defaultProperties = ['POSITION', 'COLOR', 'NORMAL', 'POSITION_ABSOLUTE']; + + function getStyleableProperties(source, properties) { + // Get all the properties used by this style + var regex = /czm_tiles3d_style_(\w+)/g; + var matches = regex.exec(source); + while (matches !== null) { + var name = matches[1]; + if (properties.indexOf(name) === -1) { + properties.push(name); + } + matches = regex.exec(source); + } + } + + function getVertexAttribute(vertexArray, index) { + var numberOfAttributes = vertexArray.numberOfAttributes; + for (var i = 0; i < numberOfAttributes; ++i) { + var attribute = vertexArray.getAttribute(i); + if (attribute.index === index) { + return attribute; + } + } + } + + function modifyStyleFunction(source) { + // Replace occurrences of czm_tiles3d_style_DEFAULTPROPERTY + var length = defaultProperties.length; + for (var i = 0; i < length; ++i) { + var property = defaultProperties[i]; + var styleName = 'czm_tiles3d_style_' + property; + var replaceName = property.toLowerCase(); + source = source.replace(new RegExp(styleName + '(\\W)', 'g'), replaceName + '$1'); + } + + // Edit the function header to accept the point position, color, and normal + return source.replace('()', '(vec3 position, vec3 position_absolute, vec4 color, vec3 normal)'); + } + + function createShaders(content, frameState, style) { + var i; + var name; + var attribute; + + var context = frameState.context; + var batchTable = content._batchTable; + var hasBatchTable = defined(batchTable); + var hasStyle = defined(style); + var isQuantized = content._isQuantized; + var isOctEncoded16P = content._isOctEncoded16P; + var isRGB565 = content._isRGB565; + var isTranslucent = content._isTranslucent; + var hasColors = content._hasColors; + var hasNormals = content._hasNormals; + var hasBatchIds = content._hasBatchIds; + var backFaceCulling = content._backFaceCulling; + var vertexArray = content._drawCommand.vertexArray; + + var colorStyleFunction; + var showStyleFunction; + var pointSizeStyleFunction; + var styleTranslucent = isTranslucent; + + if (hasBatchTable) { + // Styling is handled in the batch table + hasStyle = false; + } + + if (hasStyle) { + var shaderState = { + translucent : false + }; + colorStyleFunction = style.getColorShaderFunction('getColorFromStyle', 'czm_tiles3d_style_', shaderState); + showStyleFunction = style.getShowShaderFunction('getShowFromStyle', 'czm_tiles3d_style_', shaderState); + pointSizeStyleFunction = style.getPointSizeShaderFunction('getPointSizeFromStyle', 'czm_tiles3d_style_', shaderState); + if (defined(colorStyleFunction) && shaderState.translucent) { + styleTranslucent = true; + } + } + + content._styleTranslucent = styleTranslucent; + + var hasColorStyle = defined(colorStyleFunction); + var hasShowStyle = defined(showStyleFunction); + var hasPointSizeStyle = defined(pointSizeStyleFunction); + + // Get the properties in use by the style + var styleableProperties = []; + + if (hasColorStyle) { + getStyleableProperties(colorStyleFunction, styleableProperties); + colorStyleFunction = modifyStyleFunction(colorStyleFunction); + } + if (hasShowStyle) { + getStyleableProperties(showStyleFunction, styleableProperties); + showStyleFunction = modifyStyleFunction(showStyleFunction); + } + if (hasPointSizeStyle) { + getStyleableProperties(pointSizeStyleFunction, styleableProperties); + pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction); + } + + var usesColorSemantic = styleableProperties.indexOf('COLOR') >= 0; + var usesNormalSemantic = styleableProperties.indexOf('NORMAL') >= 0; + + // Split default properties from user properties + var userProperties = styleableProperties.filter(function(property) { return defaultProperties.indexOf(property) === -1; }); + + if (usesNormalSemantic && !hasNormals) { + throw new RuntimeError('Style references the NORMAL semantic but the point cloud does not have normals'); + } + + // Disable vertex attributes that aren't used in the style, enable attributes that are + var styleableShaderAttributes = content._styleableShaderAttributes; + for (name in styleableShaderAttributes) { + if (styleableShaderAttributes.hasOwnProperty(name)) { + attribute = styleableShaderAttributes[name]; + var enabled = (userProperties.indexOf(name) >= 0); + var vertexAttribute = getVertexAttribute(vertexArray, attribute.location); + vertexAttribute.enabled = enabled; + } + } + + var usesColors = hasColors && (!hasColorStyle || usesColorSemantic); + if (hasColors) { + // Disable the color vertex attribute if the color style does not reference the color semantic + var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation); + colorVertexAttribute.enabled = usesColors; + } + + var attributeLocations = { + a_position : positionLocation + }; + if (usesColors) { + attributeLocations.a_color = colorLocation; + } + if (hasNormals) { + attributeLocations.a_normal = normalLocation; + } + if (hasBatchIds) { + attributeLocations.a_batchId = batchIdLocation; + } + + var attributeDeclarations = ''; + + var length = userProperties.length; + for (i = 0; i < length; ++i) { + name = userProperties[i]; + attribute = styleableShaderAttributes[name]; + if (!defined(attribute)) { + throw new RuntimeError('Style references a property "' + name + '" that does not exist or is not styleable.'); + } + + var componentCount = attribute.componentCount; + var attributeName = 'czm_tiles3d_style_' + name; + var attributeType; + if (componentCount === 1) { + attributeType = 'float'; + } else { + attributeType = 'vec' + componentCount; + } + + attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; + attributeLocations[attributeName] = attribute.location; + } + + var vs = 'attribute vec3 a_position; \n' + + 'varying vec4 v_color; \n' + + 'uniform vec2 u_pointSizeAndTilesetTime; \n' + + 'uniform vec4 u_constantColor; \n' + + 'uniform vec4 u_highlightColor; \n' + + 'float u_pointSize; \n' + + 'float u_tilesetTime; \n'; + + vs += attributeDeclarations; + + if (usesColors) { + if (isTranslucent) { + vs += 'attribute vec4 a_color; \n'; + } else if (isRGB565) { + vs += 'attribute float a_color; \n' + + 'const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n' + + 'const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n' + + 'const float SHIFT_LEFT_11 = 2048.0; \n' + + 'const float SHIFT_LEFT_5 = 32.0; \n' + + 'const float NORMALIZE_6 = 1.0 / 64.0; \n' + + 'const float NORMALIZE_5 = 1.0 / 32.0; \n'; + } else { + vs += 'attribute vec3 a_color; \n'; + } + } + if (hasNormals) { + if (isOctEncoded16P) { + vs += 'attribute vec2 a_normal; \n'; + } else { + vs += 'attribute vec3 a_normal; \n'; + } + } + + if (hasBatchIds) { + vs += 'attribute float a_batchId; \n'; + } + + if (isQuantized) { + vs += 'uniform vec3 u_quantizedVolumeScale; \n'; + } + + if (hasColorStyle) { + vs += colorStyleFunction; + } + + if (hasShowStyle) { + vs += showStyleFunction; + } + + if (hasPointSizeStyle) { + vs += pointSizeStyleFunction; + } + + vs += 'void main() \n' + + '{ \n' + + ' u_pointSize = u_pointSizeAndTilesetTime.x; \n' + + ' u_tilesetTime = u_pointSizeAndTilesetTime.y; \n'; + + if (usesColors) { + if (isTranslucent) { + vs += ' vec4 color = a_color; \n'; + } else if (isRGB565) { + vs += ' float compressed = a_color; \n' + + ' float r = floor(compressed * SHIFT_RIGHT_11); \n' + + ' compressed -= r * SHIFT_LEFT_11; \n' + + ' float g = floor(compressed * SHIFT_RIGHT_5); \n' + + ' compressed -= g * SHIFT_LEFT_5; \n' + + ' float b = compressed; \n' + + ' vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n' + + ' vec4 color = vec4(rgb, 1.0); \n'; + } else { + vs += ' vec4 color = vec4(a_color, 1.0); \n'; + } + } else { + vs += ' vec4 color = u_constantColor; \n'; + } + + if (isQuantized) { + vs += ' vec3 position = a_position * u_quantizedVolumeScale; \n'; + } else { + vs += ' vec3 position = a_position; \n'; + } + vs += ' vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n'; + + if (hasNormals) { + if (isOctEncoded16P) { + vs += ' vec3 normal = czm_octDecode(a_normal); \n'; + } else { + vs += ' vec3 normal = a_normal; \n'; + } + } else { + vs += ' vec3 normal = vec3(1.0); \n'; + } + + if (hasColorStyle) { + vs += ' color = getColorFromStyle(position, position_absolute, color, normal); \n'; + } + + if (hasShowStyle) { + vs += ' float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n'; + } + + if (hasPointSizeStyle) { + vs += ' gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal); \n'; + } else { + vs += ' gl_PointSize = u_pointSize; \n'; + } + + vs += ' color = color * u_highlightColor; \n'; + + if (hasNormals) { + vs += ' normal = czm_normal * normal; \n' + + ' float diffuseStrength = czm_getLambertDiffuse(czm_sunDirectionEC, normal); \n' + + ' diffuseStrength = max(diffuseStrength, 0.4); \n' + // Apply some ambient lighting + ' color *= diffuseStrength; \n'; + } + + vs += ' v_color = color; \n' + + ' gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n'; + + if (hasNormals && backFaceCulling) { + vs += ' float visible = step(-normal.z, 0.0); \n' + + ' gl_Position *= visible; \n'; + } + + if (hasShowStyle) { + vs += ' gl_Position *= show; \n'; + } + + vs += '} \n'; + + var fs = 'varying vec4 v_color; \n' + + 'void main() \n' + + '{ \n' + + ' gl_FragColor = v_color; \n' + + '} \n'; + + var drawVS = vs; + var drawFS = fs; + + if (hasBatchTable) { + // Batched points always use the HIGHLIGHT color blend mode + drawVS = batchTable.getVertexShaderCallback(false, 'a_batchId')(drawVS); + drawFS = batchTable.getFragmentShaderCallback(false, undefined)(drawFS); + } + + var pickVS = vs; + var pickFS = fs; + + if (hasBatchTable) { + pickVS = batchTable.getPickVertexShaderCallback('a_batchId')(pickVS); + pickFS = batchTable.getPickFragmentShaderCallback()(pickFS); + } else { + pickFS = ShaderSource.createPickFragmentShaderSource(pickFS, 'uniform'); + } + + var drawCommand = content._drawCommand; + if (defined(drawCommand.shaderProgram)) { + // Destroy the old shader + drawCommand.shaderProgram.destroy(); + } + drawCommand.shaderProgram = ShaderProgram.fromCache({ + context : context, + vertexShaderSource : drawVS, + fragmentShaderSource : drawFS, + attributeLocations : attributeLocations + }); + + var pickCommand = content._pickCommand; + if (defined(pickCommand.shaderProgram)) { + // Destroy the old shader + pickCommand.shaderProgram.destroy(); + } + pickCommand.shaderProgram = ShaderProgram.fromCache({ + context : context, + vertexShaderSource : pickVS, + fragmentShaderSource : pickFS, + attributeLocations : attributeLocations + }); + + try { + // Check if the shader compiles correctly. If not there is likely a syntax error with the style. + drawCommand.shaderProgram._bind(); + } catch (error) { + // Rephrase the error. + throw new RuntimeError('Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error.'); + } + } + + function createFeatures(content) { + var tileset = content._tileset; + var featuresLength = content.featuresLength; + if (!defined(content._features) && (featuresLength > 0)) { + var features = new Array(featuresLength); + for (var i = 0; i < featuresLength; ++i) { + features[i] = new Cesium3DTileFeature(tileset, content, i); + } + content._features = features; + } + } + + /** + * @inheritdoc Cesium3DTileContent#hasProperty + */ + PointCloud3DTileContent.prototype.hasProperty = function(batchId, name) { + if (defined(this._batchTable)) { + return this._batchTable.hasProperty(batchId, name); + } + return false; + }; + + /** + * Part of the {@link Cesium3DTileContent} interface. + * + * In this context a feature refers to a group of points that share the same BATCH_ID. + * For example all the points that represent a door in a house point cloud would be a feature. + * + * Features are backed by a batch table and can be colored, shown/hidden, picked, etc like features + * in b3dm and i3dm. + * + * When the BATCH_ID semantic is omitted and the point cloud stores per-point properties, they + * are not accessible by getFeature. They are only used for dynamic styling. + */ + PointCloud3DTileContent.prototype.getFeature = function(batchId) { + if (!defined(this._batchTable)) { + return undefined; + } + var featuresLength = this.featuresLength; + //>>includeStart('debug', pragmas.debug); + if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) { + throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').'); + } + //>>includeEnd('debug'); + createFeatures(this); + return this._features[batchId]; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + PointCloud3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + this._highlightColor = enabled ? color : Color.WHITE; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + PointCloud3DTileContent.prototype.applyStyle = function(frameState, style) { + if (defined(this._batchTable)) { + this._batchTable.applyStyle(frameState, style); + } else { + createShaders(this, frameState, style); + } + }; + + var scratchComputedTranslation = new Cartesian4(); + var scratchComputedMatrixIn2D = new Matrix4(); + + /** + * @inheritdoc Cesium3DTileContent#update + */ + PointCloud3DTileContent.prototype.update = function(tileset, frameState) { + var modelMatrix = this._tile.computedTransform; + var modelMatrixChanged = !Matrix4.equals(this._modelMatrix, modelMatrix); + var updateModelMatrix = modelMatrixChanged || this._mode !== frameState.mode; + + this._mode = frameState.mode; + + if (!defined(this._drawCommand)) { + createResources(this, frameState); + createShaders(this, frameState, tileset.style); + updateModelMatrix = true; + + this._readyPromise.resolve(this); + this._parsedContent = undefined; // Unload + } + + if (updateModelMatrix) { + Matrix4.clone(modelMatrix, this._modelMatrix); + if (defined(this._rtcCenter)) { + Matrix4.multiplyByTranslation(modelMatrix, this._rtcCenter, this._drawCommand.modelMatrix); + } else if (defined(this._quantizedVolumeOffset)) { + Matrix4.multiplyByTranslation(modelMatrix, this._quantizedVolumeOffset, this._drawCommand.modelMatrix); + } else { + Matrix4.clone(modelMatrix, this._drawCommand.modelMatrix); + } + + if (frameState.mode !== SceneMode.SCENE3D) { + var projection = frameState.mapProjection; + modelMatrix = this._drawCommand.modelMatrix; + var translation = Matrix4.getColumn(modelMatrix, 3, scratchComputedTranslation); + if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { + Transforms.basisTo2D(projection, modelMatrix, modelMatrix); + } else { + var center = this._tile.boundingSphere.center; + var to2D = Transforms.wgs84To2DModelMatrix(projection, center, scratchComputedMatrixIn2D); + Matrix4.multiply(to2D, modelMatrix, modelMatrix); + } + } + + Matrix4.clone(this._drawCommand.modelMatrix, this._pickCommand.modelMatrix); + + var boundingVolume; + if (defined(this._tile._contentBoundingVolume)) { + boundingVolume = this._mode === SceneMode.SCENE3D ? this._tile._contentBoundingVolume.boundingSphere : this._tile._contentBoundingVolume2D.boundingSphere; + } else { + boundingVolume = this._mode === SceneMode.SCENE3D ? this._tile._boundingVolume.boundingSphere : this._tile._boundingVolume2D.boundingSphere; + } + + this._drawCommand.boundingVolume = boundingVolume; + this._pickCommand.boundingVolume = boundingVolume; + } + + this._drawCommand.castShadows = ShadowMode.castShadows(tileset.shadows); + this._drawCommand.receiveShadows = ShadowMode.receiveShadows(tileset.shadows); + + if (this.backFaceCulling !== this._backFaceCulling) { + this._backFaceCulling = this.backFaceCulling; + createShaders(this, frameState, tileset.style); + } + + // Update the render state + var isTranslucent = (this._highlightColor.alpha < 1.0) || (this._constantColor.alpha < 1.0) || this._styleTranslucent; + this._drawCommand.renderState = isTranslucent ? this._translucentRenderState : this._opaqueRenderState; + this._drawCommand.pass = isTranslucent ? Pass.TRANSLUCENT : Pass.CESIUM_3D_TILE; + + if (defined(this._batchTable)) { + this._batchTable.update(tileset, frameState); + } + + var commandList = frameState.commandList; + + var passes = frameState.passes; + if (passes.render) { + commandList.push(this._drawCommand); + } + if (passes.pick) { + commandList.push(this._pickCommand); + } + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + PointCloud3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + PointCloud3DTileContent.prototype.destroy = function() { + var command = this._drawCommand; + var pickCommand = this._pickCommand; + if (defined(command)) { + command.vertexArray = command.vertexArray && command.vertexArray.destroy(); + command.shaderProgram = command.shaderProgram && command.shaderProgram.destroy(); + pickCommand.shaderProgram = pickCommand.shaderProgram && pickCommand.shaderProgram.destroy(); + } + this._batchTable = this._batchTable && this._batchTable.destroy(); + return destroyObject(this); + }; + + return PointCloud3DTileContent; +}); diff --git a/Source/Scene/PointPrimitive.js b/Source/Scene/PointPrimitive.js index cf2ca7323b17..a6be423d2071 100644 --- a/Source/Scene/PointPrimitive.js +++ b/Source/Scene/PointPrimitive.js @@ -69,6 +69,9 @@ define([ if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) { throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near'); } + if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.'); + } //>>includeEnd('debug'); this._show = defaultValue(options.show, true); @@ -81,6 +84,7 @@ define([ this._scaleByDistance = options.scaleByDistance; this._translucencyByDistance = options.translucencyByDistance; this._distanceDisplayCondition = options.distanceDisplayCondition; + this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0); this._id = options.id; this._collection = defaultValue(options.collection, pointPrimitiveCollection); @@ -100,8 +104,9 @@ define([ var PIXEL_SIZE_INDEX = PointPrimitive.PIXEL_SIZE_INDEX = 5; var SCALE_BY_DISTANCE_INDEX = PointPrimitive.SCALE_BY_DISTANCE_INDEX = 6; var TRANSLUCENCY_BY_DISTANCE_INDEX = PointPrimitive.TRANSLUCENCY_BY_DISTANCE_INDEX = 7; - var DISTANCE_DISPLAY_CONDITION_INDEX = PointPrimitive.DISTANCE_DISPLAY_CONDITION = 8; - PointPrimitive.NUMBER_OF_PROPERTIES = 9; + var DISTANCE_DISPLAY_CONDITION_INDEX = PointPrimitive.DISTANCE_DISPLAY_CONDITION_INDEX = 8; + var DISABLE_DEPTH_DISTANCE_INDEX = PointPrimitive.DISABLE_DEPTH_DISTANCE_INDEX = 9; + PointPrimitive.NUMBER_OF_PROPERTIES = 10; function makeDirty(pointPrimitive, propertyChanged) { var pointPrimitiveCollection = pointPrimitive._pointPrimitiveCollection; @@ -374,6 +379,30 @@ define([ } }, + /** + * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. + * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. + * @memberof PointPrimitive.prototype + * @type {Number} + * @default 0.0 + */ + disableDepthTestDistance : { + get : function() { + return this._disableDepthTestDistance; + }, + set : function(value) { + if (this._disableDepthTestDistance !== value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value) || value < 0.0) { + throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.'); + } + //>>includeEnd('debug'); + this._disableDepthTestDistance = value; + makeDirty(this, DISABLE_DEPTH_DISTANCE_INDEX); + } + } + }, + /** * Gets or sets the user-defined object returned when the point is picked. * @memberof PointPrimitive.prototype @@ -539,7 +568,8 @@ define([ Color.equals(this._outlineColor, other._outlineColor) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && - DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition); + DistanceDisplayCondition.equals(this._distanceDisplayCondition, other._distanceDisplayCondition) && + this._disableDepthTestDistance === other._disableDepthTestDistance; }; PointPrimitive.prototype._destroy = function() { diff --git a/Source/Scene/PointPrimitiveCollection.js b/Source/Scene/PointPrimitiveCollection.js index a00d101618d5..efae834c158c 100644 --- a/Source/Scene/PointPrimitiveCollection.js +++ b/Source/Scene/PointPrimitiveCollection.js @@ -66,6 +66,7 @@ define([ var SCALE_BY_DISTANCE_INDEX = PointPrimitive.SCALE_BY_DISTANCE_INDEX; var TRANSLUCENCY_BY_DISTANCE_INDEX = PointPrimitive.TRANSLUCENCY_BY_DISTANCE_INDEX; var DISTANCE_DISPLAY_CONDITION_INDEX = PointPrimitive.DISTANCE_DISPLAY_CONDITION_INDEX; + var DISABLE_DEPTH_DISTANCE_INDEX = PointPrimitive.DISABLE_DEPTH_DISTANCE_INDEX; var NUMBER_OF_PROPERTIES = PointPrimitive.NUMBER_OF_PROPERTIES; var attributeLocations = { @@ -74,7 +75,7 @@ define([ compressedAttribute0 : 2, // color, outlineColor, pick color compressedAttribute1 : 3, // show, translucency by distance, some free space scaleByDistance : 4, - distanceDisplayCondition : 5 + distanceDisplayConditionAndDisableDepth : 5 }; /** @@ -144,6 +145,10 @@ define([ this._compiledShaderDistanceDisplayCondition = false; this._compiledShaderDistanceDisplayConditionPick = false; + this._shaderDisableDepthDistance = false; + this._compiledShaderDisableDepthDistance = false; + this._compiledShaderDisableDepthDistancePick = false; + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxPixelSize = 1.0; @@ -489,8 +494,8 @@ define([ componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[SCALE_BY_DISTANCE_INDEX] }, { - index : attributeLocations.distanceDisplayCondition, - componentsPerAttribute : 2, + index : attributeLocations.distanceDisplayConditionAndDisableDepth, + componentsPerAttribute : 3, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX] }], numberOfPointPrimitives); // 1 vertex per pointPrimitive @@ -628,9 +633,9 @@ define([ writer(i, near, nearValue, far, farValue); } - function writeDistanceDisplayCondition(pointPrimitiveCollection, context, vafWriters, pointPrimitive) { + function writeDistanceDisplayConditionAndDepthDisable(pointPrimitiveCollection, context, vafWriters, pointPrimitive) { var i = pointPrimitive._index; - var writer = vafWriters[attributeLocations.distanceDisplayCondition]; + var writer = vafWriters[attributeLocations.distanceDisplayConditionAndDisableDepth]; var near = 0.0; var far = Number.MAX_VALUE; @@ -638,10 +643,23 @@ define([ if (defined(distanceDisplayCondition)) { near = distanceDisplayCondition.near; far = distanceDisplayCondition.far; + + near *= near; + far *= far; + pointPrimitiveCollection._shaderDistanceDisplayCondition = true; } - writer(i, near, far); + var disableDepthTestDistance = pointPrimitive.disableDepthTestDistance; + disableDepthTestDistance *= disableDepthTestDistance; + if (disableDepthTestDistance > 0.0) { + pointPrimitiveCollection._shaderDisableDepthDistance = true; + if (disableDepthTestDistance === Number.POSITIVE_INFINITY) { + disableDepthTestDistance = -1.0; + } + } + + writer(i, near, far, disableDepthTestDistance); } function writePointPrimitive(pointPrimitiveCollection, context, vafWriters, pointPrimitive) { @@ -649,7 +667,7 @@ define([ writeCompressedAttrib0(pointPrimitiveCollection, context, vafWriters, pointPrimitive); writeCompressedAttrib1(pointPrimitiveCollection, context, vafWriters, pointPrimitive); writeScaleByDistance(pointPrimitiveCollection, context, vafWriters, pointPrimitive); - writeDistanceDisplayCondition(pointPrimitiveCollection, context, vafWriters, pointPrimitive); + writeDistanceDisplayConditionAndDepthDisable(pointPrimitiveCollection, context, vafWriters, pointPrimitive); } function recomputeActualPositions(pointPrimitiveCollection, pointPrimitives, length, frameState, modelMatrix, recomputeBoundingVolume) { @@ -788,8 +806,8 @@ define([ writers.push(writeScaleByDistance); } - if (properties[DISTANCE_DISPLAY_CONDITION_INDEX]) { - writers.push(writeDistanceDisplayCondition); + if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE_INDEX]) { + writers.push(writeDistanceDisplayConditionAndDepthDisable); } var numWriters = writers.length; @@ -883,10 +901,15 @@ define([ } } + this._shaderDisableDepthDistance = this._shaderDisableDepthDistance || frameState.minimumDisableDepthTestDistance !== 0.0; + var vs; + var fs; + if (blendOptionChanged || (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance) || (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistance) || - (this._shaderDistanceDisplayCondition && !this._compiledShaderDistanceDisplayCondition)) { + (this._shaderDistanceDisplayCondition && !this._compiledShaderDistanceDisplayCondition) || + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance)) { vs = new ShaderSource({ sources : [PointPrimitiveCollectionVS] @@ -900,6 +923,9 @@ define([ if (this._shaderDistanceDisplayCondition) { vs.defines.push('DISTANCE_DISPLAY_CONDITION'); } + if (this._shaderDisableDepthDistance) { + vs.defines.push('DISABLE_DEPTH_DISTANCE'); + } if (this._blendOption === BlendOption.OPAQUE_AND_TRANSLUCENT) { fs = new ShaderSource({ @@ -956,12 +982,14 @@ define([ this._compiledShaderScaleByDistance = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; + this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; } if (!defined(this._spPick) || (this._shaderScaleByDistance && !this._compiledShaderScaleByDistancePick) || (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistancePick) || - (this._shaderDistanceDisplayCondition && !this._compiledShaderDistanceDisplayConditionPick)) { + (this._shaderDistanceDisplayCondition && !this._compiledShaderDistanceDisplayConditionPick) || + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistancePick)) { vs = new ShaderSource({ defines : ['RENDER_FOR_PICK'], @@ -977,6 +1005,9 @@ define([ if (this._shaderDistanceDisplayCondition) { vs.defines.push('DISTANCE_DISPLAY_CONDITION'); } + if (this._shaderDisableDepthDistance) { + vs.defines.push('DISABLE_DEPTH_DISTANCE'); + } fs = new ShaderSource({ defines : ['RENDER_FOR_PICK'], @@ -994,14 +1025,13 @@ define([ this._compiledShaderScaleByDistancePick = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistancePick = this._shaderTranslucencyByDistance; this._compiledShaderDistanceDisplayConditionPick = this._shaderDistanceDisplayCondition; + this._compiledShaderDisableDepthDistancePick = this._shaderDisableDepthDistance; } var va; var vaLength; var command; var j; - var vs; - var fs; var commandList = frameState.commandList; diff --git a/Source/Scene/Polyline.js b/Source/Scene/Polyline.js index f73352d37eca..b579e50693f2 100644 --- a/Source/Scene/Polyline.js +++ b/Source/Scene/Polyline.js @@ -89,7 +89,7 @@ define([ this._actualLength = undefined; - this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); //eslint-disable-line no-use-before-define this._polylineCollection = polylineCollection; this._dirty = false; this._pickId = undefined; @@ -335,7 +335,7 @@ define([ this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, modelMatrix, this._boundingVolumeWC); } - this._modelMatrix = modelMatrix; + this._modelMatrix = Matrix4.clone(modelMatrix, this._modelMatrix); if (this._segments.positions.length !== segmentPositionsLength) { // number of positions changed diff --git a/Source/Scene/PolylineCollection.js b/Source/Scene/PolylineCollection.js index 4d491863be01..815f95d93dc7 100644 --- a/Source/Scene/PolylineCollection.js +++ b/Source/Scene/PolylineCollection.js @@ -244,7 +244,7 @@ define([ Cesium.Cartographic.fromDegrees(-77.02, 38.53)]), * width : 1 * }); - * + * * @see PolylineCollection#remove * @see PolylineCollection#removeAll * @see PolylineCollection#update @@ -276,7 +276,7 @@ define([ * @example * var p = polylines.add(...); * polylines.remove(p); // Returns true - * + * * @see PolylineCollection#add * @see PolylineCollection#removeAll * @see PolylineCollection#update @@ -313,7 +313,7 @@ define([ * polylines.add(...); * polylines.add(...); * polylines.removeAll(); - * + * * @see PolylineCollection#add * @see PolylineCollection#remove * @see PolylineCollection#update @@ -502,7 +502,7 @@ define([ var distanceDisplayCondition = polyline.distanceDisplayCondition; if (defined(distanceDisplayCondition)) { nearFarCartesian.x = distanceDisplayCondition.near; - nearFarCartesian.x = distanceDisplayCondition.far; + nearFarCartesian.y = distanceDisplayCondition.far; } this._batchTable.setBatchedAttribute(polyline._index, 4, nearFarCartesian); @@ -735,7 +735,7 @@ define([ * * @example * polylines = polylines && polylines.destroy(); - * + * * @see PolylineCollection#isDestroyed */ PolylineCollection.prototype.destroy = function() { diff --git a/Source/Scene/PolylineMaterialAppearance.js b/Source/Scene/PolylineMaterialAppearance.js index 5254260b7f10..050e2bc5c5ff 100644 --- a/Source/Scene/PolylineMaterialAppearance.js +++ b/Source/Scene/PolylineMaterialAppearance.js @@ -106,7 +106,11 @@ define([ */ vertexShaderSource : { get : function() { - return this._vertexShaderSource; + var vs = this._vertexShaderSource; + if (this.material.shaderSource.search(/varying\s+float\s+v_angle;/g) !== -1) { + vs = '#define POLYLINE_DASH\n' + vs; + } + return vs; } }, diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js index 8dac7a799c42..34b10c3a0b9f 100644 --- a/Source/Scene/Primitive.js +++ b/Source/Scene/Primitive.js @@ -35,6 +35,7 @@ define([ '../ThirdParty/when', './BatchTable', './CullFace', + './DepthFunction', './PrimitivePipeline', './PrimitiveState', './SceneMode', @@ -75,6 +76,7 @@ define([ when, BatchTable, CullFace, + DepthFunction, PrimitivePipeline, PrimitiveState, SceneMode, @@ -207,7 +209,7 @@ define([ this.geometryInstances = options.geometryInstances; /** - * The {@link Appearance} used to shade this primitive. Each geometry + * The {@link Appearance} used to shade this primitive. Each geometry * instance is shaded with the same appearance. Some appearances, like * {@link PerInstanceColorAppearance} allow giving each instance unique * properties. @@ -220,6 +222,29 @@ define([ this._appearance = undefined; this._material = undefined; + /** + * The {@link Appearance} used to shade this primitive when it fails the depth test. Each geometry + * instance is shaded with the same appearance. Some appearances, like + * {@link PerInstanceColorAppearance} allow giving each instance unique + * properties. + * + *

      + * When using an appearance that requires a color attribute, like PerInstanceColorAppearance, + * add a depthFailColor per-instance attribute instead. + *

      + * + *

      + * Requires the EXT_frag_depth WebGL extension to render properly. If the extension is not supported, + * there may be artifacts. + *

      + * @type Appearance + * + * @default undefined + */ + this.depthFailAppearance = options.depthFailAppearance; + this._depthFailAppearance = undefined; + this._depthFailMaterial = undefined; + /** * The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates. * When this is the identity matrix, the primitive is drawn in world coordinates, i.e., Earth's WGS84 coordinates. @@ -325,6 +350,11 @@ define([ this._backFaceRS = undefined; this._sp = undefined; + this._depthFailAppearance = undefined; + this._spDepthFail = undefined; + this._frontFaceDepthFailRS = undefined; + this._backFaceDepthFailRS = undefined; + this._pickRS = undefined; this._pickSP = undefined; this._pickIds = []; @@ -794,10 +824,10 @@ define([ return renamedVS + '\n' + showMain; }; - Primitive._updateColorAttribute = function(primitive, vertexShaderSource) { + Primitive._updateColorAttribute = function(primitive, vertexShaderSource, isDepthFail) { // some appearances have a color attribute for per vertex color. // only remove if color is a per instance attribute. - if (!defined(primitive._batchTableAttributeIndices.color)) { + if (!defined(primitive._batchTableAttributeIndices.color) && !defined(primitive._batchTableAttributeIndices.depthFailColor)) { return vertexShaderSource; } @@ -805,9 +835,19 @@ define([ return vertexShaderSource; } + //>>includeStart('debug', pragmas.debug); + if (isDepthFail && !defined(primitive._batchTableAttributeIndices.depthFailColor)) { + throw new DeveloperError('A depthFailColor per-instance attribute is required when using a depth fail appearance that uses a color attribute.'); + } + //>>includeEnd('debug'); + var modifiedVS = vertexShaderSource; modifiedVS = modifiedVS.replace(/attribute\s+vec4\s+color;/g, ''); - modifiedVS = modifiedVS.replace(/(\b)color(\b)/g, '$1czm_batchTable_color(batchId)$2'); + if (!isDepthFail) { + modifiedVS = modifiedVS.replace(/(\b)color(\b)/g, '$1czm_batchTable_color(batchId)$2'); + } else { + modifiedVS = modifiedVS.replace(/(\b)color(\b)/g, '$1czm_batchTable_depthFailColor(batchId)$2'); + } return modifiedVS; }; @@ -947,6 +987,41 @@ define([ return [attributeDecl, globalDecl, modifiedVS, compressedMain].join('\n'); } + function depthClampVS(vertexShaderSource) { + var modifiedVS = ShaderSource.replaceMain(vertexShaderSource, 'czm_non_depth_clamp_main'); + // The varying should be surround by #ifdef GL_EXT_frag_depth as an optimization. + // It is not to workaround an issue with Edge: + // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12120362/ + modifiedVS += + 'varying float v_WindowZ;\n' + + 'void main() {\n' + + ' czm_non_depth_clamp_main();\n' + + ' vec4 position = gl_Position;\n' + + ' v_WindowZ = (0.5 * (position.z / position.w) + 0.5) * position.w;\n' + + ' position.z = min(position.z, position.w);\n' + + ' gl_Position = position;' + + '}\n'; + return modifiedVS; + } + + function depthClampFS(fragmentShaderSource) { + var modifiedFS = ShaderSource.replaceMain(fragmentShaderSource, 'czm_non_depth_clamp_main'); + modifiedFS += + 'varying float v_WindowZ;\n' + + 'void main() {\n' + + ' czm_non_depth_clamp_main();\n' + + '#ifdef GL_EXT_frag_depth\n' + + ' gl_FragDepthEXT = min(v_WindowZ * gl_FragCoord.w, 1.0);\n' + + '#endif\n' + + '}\n'; + modifiedFS = + '#ifdef GL_EXT_frag_depth\n' + + '#extension GL_EXT_frag_depth : enable\n' + + '#endif\n' + + modifiedFS; + return modifiedFS; + } + function validateShaderMatching(shaderProgram, attributeLocations) { // For a VAO and shader program to be compatible, the VAO must have // all active attribute in the shader program. The VAO may have @@ -1287,18 +1362,21 @@ define([ primitive._backFaceRS = primitive._frontFaceRS; } + rs = clone(renderState, false); + if (defined(primitive._depthFailAppearance)) { + rs.depthTest.enabled = false; + } + if (primitive.allowPicking) { if (twoPasses) { - rs = clone(renderState, false); rs.cull = { enabled : false }; primitive._pickRS = RenderState.fromCache(rs); } else { - primitive._pickRS = primitive._frontFaceRS; + primitive._pickRS = RenderState.fromCache(rs); } } else { - rs = clone(renderState, false); rs.colorMask = { red : false, green : false, @@ -1315,6 +1393,25 @@ define([ primitive._pickRS = RenderState.fromCache(rs); } } + + if (defined(primitive._depthFailAppearance)) { + renderState = primitive._depthFailAppearance.getRenderState(); + rs = clone(renderState, false); + rs.depthTest.func = DepthFunction.GREATER; + if (twoPasses) { + rs.cull = { + enabled : true, + face : CullFace.BACK + }; + primitive._frontFaceDepthFailRS = RenderState.fromCache(rs); + + rs.cull.face = CullFace.FRONT; + primitive._backFaceDepthFailRS = RenderState.fromCache(rs); + } else { + primitive._frontFaceDepthFailRS = RenderState.fromCache(rs); + primitive._backFaceDepthFailRS = primitive._frontFaceRS; + } + } } function createShaderProgram(primitive, frameState, appearance) { @@ -1325,7 +1422,7 @@ define([ var vs = primitive._batchTable.getVertexShaderCallback()(appearance.vertexShaderSource); vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs, frameState.scene3DOnly); - vs = Primitive._updateColorAttribute(primitive, vs); + vs = Primitive._updateColorAttribute(primitive, vs, false); vs = modifyForEncodedNormals(primitive, vs); vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly); var fs = appearance.getFragmentShaderSource(); @@ -1360,12 +1457,33 @@ define([ attributeLocations : attributeLocations }); validateShaderMatching(primitive._sp, attributeLocations); + + if (defined(primitive._depthFailAppearance)) { + vs = primitive._batchTable.getVertexShaderCallback()(primitive._depthFailAppearance.vertexShaderSource); + vs = Primitive._appendShowToShader(primitive, vs); + vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs, frameState.scene3DOnly); + vs = Primitive._updateColorAttribute(primitive, vs, true); + vs = modifyForEncodedNormals(primitive, vs); + vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly); + vs = depthClampVS(vs); + + fs = depthClampFS(primitive._depthFailAppearance.getFragmentShaderSource()); + + primitive._spDepthFail = ShaderProgram.replaceCache({ + context : context, + shaderProgram : primitive._spDepthFail, + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : attributeLocations + }); + validateShaderMatching(primitive._spDepthFail, attributeLocations); + } } var modifiedModelViewScratch = new Matrix4(); var rtcScratch = new Cartesian3(); - function createCommands(primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands, frameState) { + function getUniforms(primitive, appearance, material, frameState) { // Create uniform map by combining uniforms from the appearance and material if either have uniforms. var materialUniformMap = defined(material) ? material._uniforms : undefined; var appearanceUniformMap = {}; @@ -1398,9 +1516,23 @@ define([ }; } + return uniforms; + } + + function createCommands(primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands, frameState) { + var uniforms = getUniforms(primitive, appearance, material, frameState); + + var depthFailUniforms; + if (defined(primitive._depthFailAppearance)) { + depthFailUniforms = getUniforms(primitive, primitive._depthFailAppearance, primitive._depthFailAppearance.material, frameState); + } + var pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE; - colorCommands.length = primitive._va.length * (twoPasses ? 2 : 1); + var multiplier = twoPasses ? 2 : 1; + multiplier *= defined(primitive._depthFailAppearance) ? 2 : 1; + + colorCommands.length = primitive._va.length * multiplier; pickCommands.length = primitive._va.length; var length = colorCommands.length; @@ -1439,6 +1571,40 @@ define([ colorCommand.uniformMap = uniforms; colorCommand.pass = pass; + if (defined(primitive._depthFailAppearance)) { + if (twoPasses) { + ++i; + + colorCommand = colorCommands[i]; + if (!defined(colorCommand)) { + colorCommand = colorCommands[i] = new DrawCommand({ + owner : primitive, + primitiveType : primitive._primitiveType + }); + } + colorCommand.vertexArray = primitive._va[vaIndex]; + colorCommand.renderState = primitive._backFaceDepthFailRS; + colorCommand.shaderProgram = primitive._spDepthFail; + colorCommand.uniformMap = depthFailUniforms; + colorCommand.pass = pass; + } + + ++i; + + colorCommand = colorCommands[i]; + if (!defined(colorCommand)) { + colorCommand = colorCommands[i] = new DrawCommand({ + owner : primitive, + primitiveType : primitive._primitiveType + }); + } + colorCommand.vertexArray = primitive._va[vaIndex]; + colorCommand.renderState = primitive._frontFaceDepthFailRS; + colorCommand.shaderProgram = primitive._spDepthFail; + colorCommand.uniformMap = depthFailUniforms; + colorCommand.pass = pass; + } + var pickCommand = pickCommands[m]; if (!defined(pickCommand)) { pickCommand = pickCommands[m] = new DrawCommand({ @@ -1515,8 +1681,12 @@ define([ var castShadows = ShadowMode.castShadows(primitive.shadows); var receiveShadows = ShadowMode.receiveShadows(primitive.shadows); var colorLength = colorCommands.length; + + var factor = twoPasses ? 2 : 1; + factor *= defined(primitive._depthFailAppearance) ? 2 : 1; + for (var j = 0; j < colorLength; ++j) { - var sphereIndex = twoPasses ? Math.floor(j / 2) : j; + var sphereIndex = Math.floor(j / factor); var colorCommand = colorCommands[j]; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingSpheres[sphereIndex]; @@ -1622,6 +1792,19 @@ define([ createSP = true; } + var depthFailAppearance = this.depthFailAppearance; + var depthFailMaterial = defined(depthFailAppearance) ? depthFailAppearance.material : undefined; + + if (this._depthFailAppearance !== depthFailAppearance) { + this._depthFailAppearance = depthFailAppearance; + this._depthFailMaterial = depthFailMaterial; + createRS = true; + createSP = true; + } else if (this._depthFailMaterial !== depthFailMaterial) { + this._depthFailMaterial = depthFailMaterial; + createSP = true; + } + var translucent = this._appearance.isTranslucent(); if (this._translucent !== translucent) { this._translucent = translucent; diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index c5b736112777..faab9989d1fa 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -100,7 +100,6 @@ define([ this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads. this._tileReplacementQueue = new TileReplacementQueue(); this._levelZeroTiles = undefined; - this._levelZeroTilesReady = false; this._loadQueueTimeSlice = 5.0; this._addHeightCallbacks = []; diff --git a/Source/Scene/QuadtreeTile.js b/Source/Scene/QuadtreeTile.js index 5ebea3fe0051..65c45f7274a1 100644 --- a/Source/Scene/QuadtreeTile.js +++ b/Source/Scene/QuadtreeTile.js @@ -67,6 +67,7 @@ define([ // distance - for example, by using the natural ordering of a quadtree. // QuadtreePrimitive gets/sets this private property. this._distance = 0.0; + this._priorityFunction = undefined; this._customData = []; this._frameUpdated = undefined; diff --git a/Source/Scene/QuadtreeTileProvider.js b/Source/Scene/QuadtreeTileProvider.js index f577b1596c6e..55fc8b4434c3 100644 --- a/Source/Scene/QuadtreeTileProvider.js +++ b/Source/Scene/QuadtreeTileProvider.js @@ -105,7 +105,7 @@ define([ * Gets the maximum geometric error allowed in a tile at a given level, in meters. This function should not be * called before {@link QuadtreeTileProvider#ready} returns true. * - * @see {QuadtreeTileProvider.computeDefaultLevelZeroMaximumGeometricError} + * @see QuadtreeTileProvider#computeDefaultLevelZeroMaximumGeometricError * * @memberof QuadtreeTileProvider * @function @@ -205,7 +205,7 @@ define([ * * @example * provider = provider && provider(); - * + * * @see QuadtreeTileProvider#isDestroyed */ QuadtreeTileProvider.prototype.destroy = DeveloperError.throwInstantiationError; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c4268a84c08d..acf60fb72974 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -30,6 +30,7 @@ define([ '../Core/mergeSort', '../Core/Occluder', '../Core/PixelFormat', + '../Core/RequestScheduler', '../Core/ShowGeometryInstanceAttribute', '../Core/Transforms', '../Renderer/ClearCommand', @@ -59,6 +60,7 @@ define([ './FrustumCommands', './FXAA', './GlobeDepth', + './JobScheduler', './MapMode2D', './OIT', './OrthographicFrustum', @@ -108,6 +110,7 @@ define([ mergeSort, Occluder, PixelFormat, + RequestScheduler, ShowGeometryInstanceAttribute, Transforms, ClearCommand, @@ -137,6 +140,7 @@ define([ FrustumCommands, FXAA, GlobeDepth, + JobScheduler, MapMode2D, OIT, OrthographicFrustum, @@ -250,7 +254,8 @@ define([ } this._id = createGuid(); - this._frameState = new FrameState(context, new CreditDisplay(creditContainer)); + this._jobScheduler = new JobScheduler(); + this._frameState = new FrameState(context, new CreditDisplay(creditContainer), this._jobScheduler); this._frameState.scene3DOnly = defaultValue(options.scene3DOnly, false); var ps = new PassState(context); @@ -329,6 +334,11 @@ define([ this._cameraStartFired = false; this._cameraMovedTime = undefined; + this._pickPositionCache = {}; + this._pickPositionCacheDirty = false; + + this._minimumDisableDepthTestDistance = 0.0; + /** * Exceptions occurring in render are always caught in order to raise the * renderError event. If this property is true, the error is rethrown @@ -1168,10 +1178,32 @@ define([ get: function() { return this._frameState.imagerySplitPosition; }, - set: function(value) { this._frameState.imagerySplitPosition = value; } + }, + + /** + * The distance from the camera at which to disable the depth test of billboards, labels and points + * to, for example, prevent clipping against terrain. When set to zero, the depth test should always + * be applied. When less than zero, the depth test should never be applied. Setting the disableDepthTestDistance + * property of a billboard, label or point will override this value. + * @memberof Scene.prototype + * @type {Number} + * @default 0.0 + */ + minimumDisableDepthTestDistance : { + get : function() { + return this._minimumDisableDepthTestDistance; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value) || value < 0.0) { + throw new DeveloperError('minimumDisableDepthTestDistance must be greater than or equal to 0.0.'); + } + //>>includeEnd('debug'); + this._minimumDisableDepthTestDistance = value; + } } }); @@ -1286,6 +1318,7 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); frameState.occluder = getOccluder(scene); frameState.terrainExaggeration = scene._terrainExaggeration; + frameState.minimumDisableDepthTestDistance = scene._minimumDisableDepthTestDistance; if (defined(scene.globe)) { frameState.maximumScreenSpaceError = scene.globe.maximumScreenSpaceError; } else { @@ -1344,7 +1377,7 @@ define([ break; } - var pass = command instanceof ClearCommand ? Pass.OPAQUE : command.pass; + var pass = command.pass; var index = frustumCommands.indices[pass]++; frustumCommands.commands[pass][index] = command; @@ -1423,12 +1456,6 @@ define([ } cullingVolume = scratchCullingVolume; - // Determine visibility of celestial and terrestrial environment effects. - var environmentState = scene._environmentState; - environmentState.isSkyAtmosphereVisible = defined(environmentState.skyAtmosphereCommand) && environmentState.isReadyForAtmosphere; - environmentState.isSunVisible = isVisible(environmentState.sunDrawCommand, cullingVolume, occluder); - environmentState.isMoonVisible = isVisible(environmentState.moonCommand, cullingVolume, occluder); - var length = commandList.length; for (var i = 0; i < length; ++i) { var command = commandList[i]; @@ -1633,6 +1660,11 @@ define([ return; } + if (command instanceof ClearCommand) { + command.execute(context, passState); + return; + } + var shadowsEnabled = scene.frameState.shadowHints.shadowsEnabled; var lightShadowsEnabled = shadowsEnabled && (scene.frameState.shadowHints.lightShadowMaps.length > 0); @@ -1680,7 +1712,7 @@ define([ scene._debugVolume = new Primitive({ geometryInstances : new GeometryInstance({ geometry : geometry, - modelMatrix : Matrix4.multiplyByTranslation(Matrix4.IDENTITY, center, new Matrix4()), + modelMatrix : Matrix4.fromTranslation(center), attributes : { color : new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0) } @@ -1900,7 +1932,7 @@ define([ } if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer && (scene.copyGlobeDepth || scene.debugShowGlobeDepth)) { - globeDepth.update(context); + globeDepth.update(context, passState); globeDepth.executeCopyDepth(context, passState); } @@ -1927,6 +1959,13 @@ define([ } } + us.updatePass(Pass.CESIUM_3D_TILE); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + // Execute commands in order by pass up to the translucent pass. // Translucent geometry needs special handling (sorting/OIT). var startPass = Pass.GROUND + 1; @@ -2000,7 +2039,7 @@ define([ var command = commandList[i]; updateDerivedCommands(scene, command); - if (command.castShadows && (command.pass === Pass.GLOBE || command.pass === Pass.OPAQUE || command.pass === Pass.TRANSLUCENT)) { + if (command.castShadows && (command.pass === Pass.GLOBE || command.pass === Pass.CESIUM_3D_TILE || command.pass === Pass.OPAQUE || command.pass === Pass.TRANSLUCENT)) { if (isVisible(command, shadowVolume)) { if (isPointLight) { for (var k = 0; k < numberOfPasses; ++k) { @@ -2074,10 +2113,16 @@ define([ } } + var scratchEyeTranslation = new Cartesian3(); + function updateAndExecuteCommands(scene, passState, backgroundColor) { var context = scene._context; var viewport = passState.viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = context.drawingBufferWidth; + viewport.height = context.drawingBufferHeight; var frameState = scene._frameState; var camera = frameState.camera; @@ -2085,12 +2130,13 @@ define([ var depthOnly = frameState.passes.depth; if (scene._useWebVR && mode !== SceneMode.SCENE2D) { + updateAndClearFramebuffers(scene, passState, backgroundColor); + if (!depthOnly) { updatePrimitives(scene); } createPotentiallyVisibleSet(scene); - updateAndClearFramebuffers(scene, passState, backgroundColor); if (!depthOnly) { executeComputeCommands(scene); @@ -2130,15 +2176,11 @@ define([ Camera.clone(savedCamera, camera); } else { - viewport.x = 0; - viewport.y = 0; - viewport.width = context.drawingBufferWidth; - viewport.height = context.drawingBufferHeight; - + updateAndClearFramebuffers(scene, passState, backgroundColor); if (mode !== SceneMode.SCENE2D || scene._mapMode2D === MapMode2D.ROTATE) { - executeCommandsInViewport(true, scene, passState, backgroundColor); + executeCommandsInViewport(true, scene, passState); } else { - execute2DViewportCommands(scene, passState, backgroundColor); + execute2DViewportCommands(scene, passState); } } } @@ -2150,12 +2192,16 @@ define([ var scratch2DViewportCameraTransform = new Matrix4(); var scratch2DViewportEyePoint = new Cartesian3(); var scratch2DViewportWindowCoords = new Cartesian3(); + var scratch2DViewport = new BoundingRectangle(); - function execute2DViewportCommands(scene, passState, backgroundColor) { + function execute2DViewportCommands(scene, passState) { var context = scene.context; var frameState = scene.frameState; var camera = scene.camera; - var viewport = passState.viewport; + + var originalViewport = passState.viewport; + var viewport = BoundingRectangle.clone(originalViewport, scratch2DViewport); + passState.viewport = viewport; var maxCartographic = scratch2DViewportCartographic; var maxCoord = scratch2DViewportMaxCoord; @@ -2181,10 +2227,10 @@ define([ var viewportX = viewport.x; var viewportWidth = viewport.width; - if (x === 0.0 || windowCoordinates.x <= 0.0 || windowCoordinates.x >= context.drawingBufferWidth) { - executeCommandsInViewport(true, scene, passState, backgroundColor); - } else if (Math.abs(context.drawingBufferWidth * 0.5 - windowCoordinates.x) < 1.0) { - viewport.width = windowCoordinates.x; + if (x === 0.0 || windowCoordinates.x <= viewportX || windowCoordinates.x >= viewportX + viewportWidth) { + executeCommandsInViewport(true, scene, passState); + } else if (Math.abs(viewportX + viewportWidth * 0.5 - windowCoordinates.x) < 1.0) { + viewport.width = windowCoordinates.x - viewport.x; camera.position.x *= CesiumMath.sign(camera.position.x); @@ -2193,9 +2239,9 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor); + executeCommandsInViewport(true, scene, passState); - viewport.x = viewport.width; + viewport.x = windowCoordinates.x; camera.position.x = -camera.position.x; @@ -2205,9 +2251,9 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor); - } else if (windowCoordinates.x > context.drawingBufferWidth * 0.5) { - viewport.width = windowCoordinates.x; + executeCommandsInViewport(false, scene, passState); + } else if (windowCoordinates.x > viewportX + viewportWidth * 0.5) { + viewport.width = windowCoordinates.x - viewportX; var right = camera.frustum.right; camera.frustum.right = maxCoord.x - x; @@ -2215,10 +2261,10 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor); + executeCommandsInViewport(true, scene, passState); - viewport.x += windowCoordinates.x; - viewport.width = context.drawingBufferWidth - windowCoordinates.x; + viewport.x = windowCoordinates.x; + viewport.width = viewportX + viewportWidth - windowCoordinates.x; camera.position.x = -camera.position.x; @@ -2228,10 +2274,10 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor); + executeCommandsInViewport(false, scene, passState); } else { viewport.x = windowCoordinates.x; - viewport.width = context.drawingBufferWidth - windowCoordinates.x; + viewport.width = viewportX + viewportWidth - windowCoordinates.x; var left = camera.frustum.left; camera.frustum.left = -maxCoord.x - x; @@ -2239,10 +2285,10 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(true, scene, passState, backgroundColor); + executeCommandsInViewport(true, scene, passState); - viewport.x = 0; - viewport.width = windowCoordinates.x; + viewport.x = viewportX; + viewport.width = windowCoordinates.x - viewportX; camera.position.x = -camera.position.x; @@ -2252,18 +2298,16 @@ define([ frameState.cullingVolume = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC); context.uniformState.update(frameState); - executeCommandsInViewport(false, scene, passState, backgroundColor); + executeCommandsInViewport(false, scene, passState); } camera._setTransform(transform); Cartesian3.clone(position, camera.position); camera.frustum = frustum.clone(); - - viewport.x = viewportX; - viewport.width = viewportWidth; + passState.viewport = originalViewport; } - function executeCommandsInViewport(firstViewport, scene, passState, backgroundColor) { + function executeCommandsInViewport(firstViewport, scene, passState) { var depthOnly = scene.frameState.passes.depth; if (!firstViewport && !depthOnly) { @@ -2276,18 +2320,15 @@ define([ createPotentiallyVisibleSet(scene); - if (firstViewport) { - updateAndClearFramebuffers(scene, passState, backgroundColor); - if (!depthOnly) { - executeComputeCommands(scene); - executeShadowMapCastCommands(scene); - } + if (firstViewport && !depthOnly) { + executeComputeCommands(scene); + executeShadowMapCastCommands(scene); } executeCommands(scene, passState); } - function updateEnvironment(scene) { + function updateEnvironment(scene, passState) { var frameState = scene._frameState; // Update celestial and terrestrial environment effects. @@ -2309,7 +2350,7 @@ define([ } environmentState.skyAtmosphereCommand = defined(skyAtmosphere) ? skyAtmosphere.update(frameState) : undefined; environmentState.skyBoxCommand = defined(scene.skyBox) ? scene.skyBox.update(frameState) : undefined; - var sunCommands = defined(scene.sun) ? scene.sun.update(scene) : undefined; + var sunCommands = defined(scene.sun) ? scene.sun.update(frameState, passState) : undefined; environmentState.sunDrawCommand = defined(sunCommands) ? sunCommands.drawCommand : undefined; environmentState.sunComputeCommand = defined(sunCommands) ? sunCommands.computeCommand : undefined; environmentState.moonCommand = defined(scene.moon) ? scene.moon.update(frameState) : undefined; @@ -2323,6 +2364,21 @@ define([ // of the globe are not picked. scene._depthPlane.update(frameState); } + + var occluder = (frameState.mode === SceneMode.SCENE3D) ? frameState.occluder: undefined; + var cullingVolume = frameState.cullingVolume; + + // get user culling volume minus the far plane. + var planes = scratchCullingVolume.planes; + for (var k = 0; k < 5; ++k) { + planes[k] = cullingVolume.planes[k]; + } + cullingVolume = scratchCullingVolume; + + // Determine visibility of celestial and terrestrial environment effects. + environmentState.isSkyAtmosphereVisible = defined(environmentState.skyAtmosphereCommand) && environmentState.isReadyForAtmosphere; + environmentState.isSunVisible = isVisible(environmentState.sunDrawCommand, cullingVolume, occluder); + environmentState.isMoonVisible = isVisible(environmentState.moonCommand, cullingVolume, occluder); } function updateDebugFrustumPlanes(scene) { @@ -2436,25 +2492,14 @@ define([ // Update globe depth rendering based on the current context and clear the globe depth framebuffer. var useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer = !picking && defined(scene._globeDepth); if (useGlobeDepthFramebuffer) { - scene._globeDepth.update(context); + scene._globeDepth.update(context, passState); scene._globeDepth.clear(context, passState, clearColor); } - // Determine if there are any translucent surfaces in any of the frustums. - var renderTranslucentCommands = false; - var frustumCommandsList = scene._frustumCommandsList; - var numFrustums = frustumCommandsList.length; - for (var i = 0; i < numFrustums; ++i) { - if (frustumCommandsList[i].indices[Pass.TRANSLUCENT] > 0) { - renderTranslucentCommands = true; - break; - } - } - // If supported, configure OIT to use the globe depth framebuffer and clear the OIT framebuffer. - var useOIT = environmentState.useOIT = !picking && renderTranslucentCommands && defined(scene._oit) && scene._oit.isSupported(); + var useOIT = environmentState.useOIT = !picking && defined(scene._oit) && scene._oit.isSupported(); if (useOIT) { - scene._oit.update(context, scene._globeDepth.framebuffer); + scene._oit.update(context, passState, scene._globeDepth.framebuffer); scene._oit.clear(context, passState, clearColor); environmentState.useOIT = scene._oit.isSupported(); } @@ -2462,7 +2507,7 @@ define([ // If supported, configure FXAA to use the globe depth color texture and clear the FXAA framebuffer. var useFXAA = environmentState.useFXAA = !picking && scene.fxaa; if (useFXAA) { - scene._fxaa.update(context); + scene._fxaa.update(context, passState); scene._fxaa.clear(context, passState, clearColor); } @@ -2549,9 +2594,9 @@ define([ this._camera._updateCameraChanged(); }; - var scratchEyeTranslation = new Cartesian3(); - function render(scene, time) { + scene._pickPositionCacheDirty = true; + if (!defined(time)) { time = JulianDate.now(); } @@ -2570,6 +2615,7 @@ define([ } scene._preRender.raiseEvent(scene, time); + scene._jobScheduler.resetBudgets(); var context = scene.context; var us = context.uniformState; @@ -2609,7 +2655,7 @@ define([ scene.globe.beginFrame(frameState); } - updateEnvironment(scene); + updateEnvironment(scene, passState); updateAndExecuteCommands(scene, passState, backgroundColor); resolveFramebuffers(scene, passState); executeOverlayCommands(scene, passState); @@ -2638,6 +2684,7 @@ define([ } context.endFrame(); + RequestScheduler.update(); callAfterRenderFunctions(frameState); scene._postRender.raiseEvent(scene, time); @@ -2764,6 +2811,18 @@ define([ * Returns an object with a `primitive` property that contains the first (top) primitive in the scene * at a particular window coordinate or undefined if nothing is at the location. Other properties may * potentially be set depending on the type of primitive. + *

      + * When a feature of a 3D Tiles tileset is picked, pick returns a {@link Cesium3DTileFeature} object. + *

      + * + * @example + * // On mouse over, color the feature yellow. + * handler.setInputAction(function(movement) { + * var feature = scene.pick(movement.endPosition); + * if (feature instanceof Cesium.Cesium3DTileFeature) { + * feature.color = Cesium.Color.YELLOW; + * } + * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); * * @param {Cartesian2} windowPosition Window coordinates to perform picking on. * @returns {Object} Object containing the picked primitive. @@ -2787,6 +2846,8 @@ define([ this._pickFramebuffer = context.createPickFramebuffer(); } + this._jobScheduler.disableThisFrame(); + // Update with previous frame's number and time, assuming that render is called before picking. updateFrameState(this, frameState.frameNumber, frameState.time); frameState.cullingVolume = getPickCullingVolume(this, drawingBufferPosition, rectangleWidth, rectangleHeight); @@ -2799,6 +2860,7 @@ define([ var passState = this._pickFramebuffer.begin(scratchRectangle); + updateEnvironment(this, passState); updateAndExecuteCommands(this, passState, scratchColorZero); resolveFramebuffers(this, passState); @@ -2945,6 +3007,7 @@ define([ passState.scissorTest.rectangle.width = 1; passState.scissorTest.rectangle.height = 1; + updateEnvironment(scene, passState); updateAndExecuteCommands(scene, passState, scratchColorZero); resolveFramebuffers(scene, passState); @@ -2985,6 +3048,15 @@ define([ } //>>includeEnd('debug'); + var cacheKey = windowPosition.toString(); + + if (this._pickPositionCacheDirty){ + this._pickPositionCache = {}; + this._pickPositionCacheDirty = false; + } else if (this._pickPositionCache.hasOwnProperty(cacheKey)){ + return Cartesian3.clone(this._pickPositionCache[cacheKey], result); + } + var context = this._context; var uniformState = context.uniformState; @@ -3046,10 +3118,12 @@ define([ uniformState.update(this.frameState); } + this._pickPositionCache[cacheKey] = Cartesian3.clone(result); return result; } } + this._pickPositionCache[cacheKey] = undefined; return undefined; }; @@ -3084,6 +3158,7 @@ define([ var cart = projection.unproject(result, scratchPickPositionCartographic); ellipsoid.cartographicToCartesian(cart, result); } + return result; }; @@ -3291,5 +3366,27 @@ define([ return destroyObject(this); }; + /** + * Transforms a position in cartesian coordinates to canvas coordinates. This is commonly used to place an + * HTML element at the same screen position as an object in the scene. + * + * @param {Cartesian3} position The position in cartesian coordinates. + * @param {Cartesian2} [result] An optional object to return the input position transformed to canvas coordinates. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. This may be undefined if the input position is near the center of the ellipsoid. + * + * @example + * // Output the canvas position of longitude/latitude (0, 0) every time the mouse moves. + * var scene = widget.scene; + * var ellipsoid = scene.globe.ellipsoid; + * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0); + * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); + * handler.setInputAction(function(movement) { + * console.log(scene.cartesianToCanvasCoordinates(position)); + * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + */ + Scene.prototype.cartesianToCanvasCoordinates = function(position, result) { + return SceneTransforms.wgs84ToWindowCoordinates(this, position, result); + }; + return Scene; }); diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js index 7c0f2fb6fe85..c04d553e089d 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../Core/Check', '../Core/Cartesian3', '../Core/Cartographic', '../Core/defined', @@ -18,6 +19,7 @@ define([ './PerspectiveFrustum', './SceneMode' ], function( + Check, Cartesian3, Cartographic, defined, @@ -42,9 +44,7 @@ define([ */ function SceneTransitioner(scene) { //>>includeStart('debug', pragmas.debug); - if (!defined(scene)) { - throw new DeveloperError('scene is required.'); - } + Check.typeOf.object('scene', scene); //>>includeEnd('debug'); this._scene = scene; @@ -195,6 +195,14 @@ define([ } }; + var scratchCVTo3DCamera = { + position : new Cartesian3(), + direction : new Cartesian3(), + up : new Cartesian3(), + frustum : undefined + }; + var scratch2DTo3DFrustumPersp = new PerspectiveFrustum(); + SceneTransitioner.prototype.morphTo3D = function(duration, ellipsoid) { if (defined(this._completeMorph)) { this._completeMorph(); @@ -224,6 +232,18 @@ define([ } else { camera3D = getColumbusViewTo3DCamera(this, ellipsoid); } + + var frustum; + var camera = scene.camera; + if (camera.frustum instanceof OrthographicFrustum) { + frustum = camera.frustum.clone(); + } else { + frustum = scratch2DTo3DFrustumPersp; + frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + frustum.fov = CesiumMath.toRadians(60.0); + } + camera3D.frustum = frustum; + var complete = complete3DCallback(camera3D); createMorphHandler(this, complete); @@ -290,12 +310,6 @@ define([ var scratchCVTo3DCartographic = new Cartographic(); var scratchCVTo3DSurfacePoint = new Cartesian3(); var scratchCVTo3DFromENU = new Matrix4(); - var scratchCVTo3DCamera = { - position : new Cartesian3(), - direction : new Cartesian3(), - up : new Cartesian3(), - frustum : undefined - }; function getColumbusViewTo3DCamera(transitioner, ellipsoid) { var scene = transitioner._scene; @@ -364,8 +378,13 @@ define([ transitioner._currentTweens.push(tween); } - var scratch2DTo3DFrustumPersp = new PerspectiveFrustum(); var scratch2DTo3DFrustumOrtho = new OrthographicFrustum(); + var scratch3DToCVStartPos = new Cartesian3(); + var scratch3DToCVStartDir = new Cartesian3(); + var scratch3DToCVStartUp = new Cartesian3(); + var scratch3DToCVEndPos = new Cartesian3(); + var scratch3DToCVEndDir = new Cartesian3(); + var scratch3DToCVEndUp = new Cartesian3(); function morphFrom2DTo3D(transitioner, duration, ellipsoid) { duration /= 3.0; @@ -386,6 +405,19 @@ define([ camera3D = getColumbusViewTo3DCamera(transitioner, ellipsoid); } + var frustum; + if (transitioner._morphToOrthographic) { + frustum = scratch2DTo3DFrustumOrtho; + frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + frustum.width = camera.frustum.right - camera.frustum.left; + } else { + frustum = scratch2DTo3DFrustumPersp; + frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + frustum.fov = CesiumMath.toRadians(60.0); + } + + camera3D.frustum = frustum; + var complete = complete3DCallback(camera3D); createMorphHandler(transitioner, complete); @@ -417,21 +449,12 @@ define([ } var morph; - var frustum; if (transitioner._morphToOrthographic) { morph = function() { - frustum = scratch2DTo3DFrustumOrtho; - frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; - frustum.width = camera.frustum.right - camera.frustum.left; - camera.frustum = frustum; morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete); }; } else { morph = function() { - frustum = scratch2DTo3DFrustumPersp; - frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; - frustum.fov = CesiumMath.toRadians(60.0); - camera3D.frustum = frustum; morphOrthographicToPerspective(transitioner, duration, camera3D, function() { morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete); }); @@ -771,13 +794,6 @@ define([ } } - var scratch3DToCVStartPos = new Cartesian3(); - var scratch3DToCVStartDir = new Cartesian3(); - var scratch3DToCVStartUp = new Cartesian3(); - var scratch3DToCVEndPos = new Cartesian3(); - var scratch3DToCVEndDir = new Cartesian3(); - var scratch3DToCVEndUp = new Cartesian3(); - function morphFrom3DToColumbusView(transitioner, duration, endCamera, complete) { var scene = transitioner._scene; var camera = scene.camera; @@ -853,6 +869,8 @@ define([ Cartesian3.clone(camera3D.up, camera.up); Cartesian3.cross(camera.direction, camera.up, camera.right); Cartesian3.normalize(camera.right, camera.right); + + camera.frustum = camera3D.frustum.clone(); } var wasMorphing = defined(transitioner._completeMorph); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 6d07334507e4..9ae72235cc2d 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -838,7 +838,7 @@ define([ var translateCVEndPos = new Cartesian3(); var translatCVDifference = new Cartesian3(); var translateCVOrigin = new Cartesian3(); - var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); + var translateCVPlane = new Plane(Cartesian3.UNIT_X, 0.0); var translateCVStartMouse = new Cartesian2(); var translateCVEndMouse = new Cartesian2(); @@ -920,12 +920,13 @@ define([ var rotateCVTransform = new Matrix4(); var rotateCVVerticalTransform = new Matrix4(); var rotateCVOrigin = new Cartesian3(); - var rotateCVPlane = new Plane(Cartesian3.ZERO, 0.0); + var rotateCVPlane = new Plane(Cartesian3.UNIT_X, 0.0); var rotateCVCartesian3 = new Cartesian3(); var rotateCVCart = new Cartographic(); var rotateCVOldTransform = new Matrix4(); var rotateCVQuaternion = new Quaternion(); var rotateCVMatrix = new Matrix3(); + var tilt3DCartesian3 = new Cartesian3(); function rotateCV(controller, startPosition, movement) { if (defined(movement.angleAndHeight)) { @@ -1242,9 +1243,10 @@ define([ } var scratchStrafeRay = new Ray(); - var scratchStrafePlane = new Plane(Cartesian3.ZERO, 0.0); + var scratchStrafePlane = new Plane(Cartesian3.UNIT_X, 0.0); var scratchStrafeIntersection = new Cartesian3(); var scratchStrafeDirection = new Cartesian3(); + var scratchMousePos = new Cartesian3(); function strafe(controller, startPosition, movement) { var scene = controller._scene; @@ -1279,7 +1281,6 @@ define([ var spin3DPick = new Cartesian3(); var scratchCartographic = new Cartographic(); - var scratchMousePos = new Cartesian3(); var scratchRadii = new Cartesian3(); var scratchEllipsoid = new Ellipsoid(); var scratchLookUp = new Cartesian3(); @@ -1334,11 +1335,10 @@ define([ pan3D(controller, startPosition, movement, ellipsoid); } return; - } else { - controller._looking = false; - controller._rotating = false; - controller._strafing = false; } + controller._looking = false; + controller._rotating = false; + controller._strafing = false; if (defined(globe) && height < controller._minimumPickingTerrainHeight) { if (defined(mousePos)) { @@ -1558,7 +1558,6 @@ define([ var tilt3DVerticalCenter = new Cartesian3(); var tilt3DTransform = new Matrix4(); var tilt3DVerticalTransform = new Matrix4(); - var tilt3DCartesian3 = new Cartesian3(); var tilt3DOldTransform = new Matrix4(); var tilt3DQuaternion = new Quaternion(); var tilt3DMatrix = new Matrix3(); diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js index d65cf65528a8..c4a118563545 100644 --- a/Source/Scene/ShadowMap.js +++ b/Source/Scene/ShadowMap.js @@ -1003,6 +1003,8 @@ define([ var scratchSplits = new Array(5); var scratchFrustum = new PerspectiveFrustum(); var scratchCascadeDistances = new Array(4); + var scratchMin = new Cartesian3(); + var scratchMax = new Cartesian3(); function computeCascades(shadowMap, frameState) { var shadowMapCamera = shadowMap._shadowMapCamera; @@ -1127,8 +1129,6 @@ define([ var scratchLightView = new Matrix4(); var scratchRight = new Cartesian3(); var scratchUp = new Cartesian3(); - var scratchMin = new Cartesian3(); - var scratchMax = new Cartesian3(); var scratchTranslation = new Cartesian3(); function fitShadowMapToScene(shadowMap, frameState) { diff --git a/Source/Scene/ShadowMode.js b/Source/Scene/ShadowMode.js index 68983ad7febd..d2a837485af9 100644 --- a/Source/Scene/ShadowMode.js +++ b/Source/Scene/ShadowMode.js @@ -74,9 +74,8 @@ define([ return ShadowMode.CAST_ONLY; } else if (receiveShadows) { return ShadowMode.RECEIVE_ONLY; - } else { - return ShadowMode.DISABLED; } + return ShadowMode.DISABLED; }; return freezeObject(ShadowMode); diff --git a/Source/Scene/SingleTileImageryProvider.js b/Source/Scene/SingleTileImageryProvider.js index b7cdcd10e2b8..9e869c6b5b62 100644 --- a/Source/Scene/SingleTileImageryProvider.js +++ b/Source/Scene/SingleTileImageryProvider.js @@ -43,7 +43,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see createTileMapServiceImageryProvider * @see WebMapServiceImageryProvider @@ -370,6 +370,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -377,7 +378,7 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - SingleTileImageryProvider.prototype.requestImage = function(x, y, level) { + SingleTileImageryProvider.prototype.requestImage = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug); if (!this._ready) { throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); diff --git a/Source/Scene/SkyBox.js b/Source/Scene/SkyBox.js index 7344060ac3ad..37552d15d71a 100644 --- a/Source/Scene/SkyBox.js +++ b/Source/Scene/SkyBox.js @@ -69,7 +69,7 @@ define([ * negativeZ : 'skybox_nz.png' * } * }); - * + * * @see Scene#skyBox * @see Transforms.computeTemeToPseudoFixedMatrix */ @@ -113,6 +113,8 @@ define([ * @exception {DeveloperError} this.sources properties must all be the same type. */ SkyBox.prototype.update = function(frameState) { + var that = this; + if (!this.show) { return undefined; } @@ -170,8 +172,6 @@ define([ var command = this._command; if (!defined(command.vertexArray)) { - var that = this; - command.uniformMap = { u_cubeMap: function() { return that._cubeMap; @@ -239,7 +239,7 @@ define([ * * @example * skyBox = skyBox && skyBox.destroy(); - * + * * @see SkyBox#isDestroyed */ SkyBox.prototype.destroy = function() { diff --git a/Source/Scene/StyleExpression.js b/Source/Scene/StyleExpression.js new file mode 100644 index 000000000000..81fd8315fe7b --- /dev/null +++ b/Source/Scene/StyleExpression.js @@ -0,0 +1,79 @@ +/*global define*/ +define([ + '../Core/DeveloperError' + ], function( + DeveloperError) { + 'use strict'; + + /** + * An expression for a style applied to a {@link Cesium3DTileset}. + *

      + * Derived classes of this interface evaluate expressions in the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language}. + *

      + *

      + * This type describes an interface and is not intended to be instantiated directly. + *

      + * + * @alias StyleExpression + * @constructor + * + * @see Expression + * @see ConditionsExpression + */ + function StyleExpression() { + } + + /** + * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of + * the expression in the + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/Styling|3D Tiles Styling language} + * is of type Boolean, Number, or String, the corresponding JavaScript + * primitive type will be returned. If the result is a RegExp, a Javascript RegExp + * object will be returned. If the result is a Cartesian2, Cartesian3, or Cartesian4, + * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the result argument is + * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned. + * + * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Object} [result] The object onto which to store the result. + * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression. + */ + StyleExpression.prototype.evaluate = function(frameState, feature, result) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Evaluates the result of a Color expression, optionally using the provided feature's properties. + *

      + * This is equivalent to {@link StyleExpression#evaluate} but always returns a {@link Color} object. + *

      + * + * @param {FrameState} frameState The frame state. + * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. + * @param {Color} [result] The object in which to store the result. + * @returns {Color} The modified result parameter or a new Color instance if one was not provided. + */ + StyleExpression.prototype.evaluateColor = function(frameState, feature, result) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Gets the shader function for this expression. + * Returns undefined if the shader function can't be generated from this expression. + * + * @param {String} functionName Name to give to the generated function. + * @param {String} attributePrefix Prefix that is added to any variable names to access vertex attributes. + * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. + * @param {String} returnType The return type of the generated function. + * + * @returns {String} The shader function. + * + * @private + */ + StyleExpression.prototype.getShaderFunction = function(functionName, attributePrefix, shaderState, returnType) { + DeveloperError.throwInstantiationError(); + }; + + return StyleExpression; +}); diff --git a/Source/Scene/Sun.js b/Source/Scene/Sun.js index e0e13a079827..30bc89760f35 100644 --- a/Source/Scene/Sun.js +++ b/Source/Scene/Sun.js @@ -139,11 +139,7 @@ define([ /** * @private */ - Sun.prototype.update = function(scene) { - var passState = scene._passState; - var frameState = scene.frameState; - var context = scene.context; - + Sun.prototype.update = function(frameState, passState) { if (!this.show) { return undefined; } @@ -157,6 +153,7 @@ define([ return undefined; } + var context = frameState.context; var drawingBufferWidth = passState.viewport.width; var drawingBufferHeight = passState.viewport.height; @@ -289,7 +286,7 @@ define([ var position = SceneTransforms.computeActualWgs84Position(frameState, sunPosition, scratchCartesian4); - var dist = Cartesian3.magnitude(Cartesian3.subtract(position, scene.camera.position, scratchCartesian4)); + var dist = Cartesian3.magnitude(Cartesian3.subtract(position, frameState.camera.position, scratchCartesian4)); var projMatrix = context.uniformState.projection; var positionEC = scratchPositionEC; diff --git a/Source/Scene/SunPostProcess.js b/Source/Scene/SunPostProcess.js index f26c7fc5575f..7dfb7e275dfd 100644 --- a/Source/Scene/SunPostProcess.js +++ b/Source/Scene/SunPostProcess.js @@ -363,7 +363,7 @@ define([ scissorRectangle.height = Math.min(size.y, height); this._uCenter = Cartesian2.clone(sunPositionWC, this._uCenter); - this._uRadius = Math.max(size.x, size.y) * 0.5; + this._uRadius = Math.max(size.x, size.y) * 0.15; // create down sampled render state viewportTransformation = Matrix4.computeViewportTransformation(downSampleViewport, 0.0, 1.0, postProcessMatrix4Scratch); diff --git a/Source/Scene/TileBoundingBox.js b/Source/Scene/TileBoundingRegion.js similarity index 71% rename from Source/Scene/TileBoundingBox.js rename to Source/Scene/TileBoundingRegion.js index 9acf9a3ce90e..2eb8c6b480ed 100644 --- a/Source/Scene/TileBoundingBox.js +++ b/Source/Scene/TileBoundingRegion.js @@ -1,46 +1,63 @@ /*global define*/ define([ + '../Core/BoundingSphere', '../Core/Cartesian3', '../Core/Cartographic', + '../Core/Check', + '../Core/ColorGeometryInstanceAttribute', '../Core/defaultValue', - '../Core/defined', - '../Core/DeveloperError', + '../Core/defineProperties', '../Core/Ellipsoid', + '../Core/GeometryInstance', '../Core/IntersectionTests', + '../Core/Matrix4', + '../Core/OrientedBoundingBox', '../Core/Plane', '../Core/Ray', '../Core/Rectangle', + '../Core/RectangleOutlineGeometry', + './PerInstanceColorAppearance', + './Primitive', './SceneMode' ], function( + BoundingSphere, Cartesian3, Cartographic, + Check, + ColorGeometryInstanceAttribute, defaultValue, - defined, - DeveloperError, + defineProperties, Ellipsoid, + GeometryInstance, IntersectionTests, + Matrix4, + OrientedBoundingBox, Plane, Ray, Rectangle, + RectangleOutlineGeometry, + PerInstanceColorAppearance, + Primitive, SceneMode) { 'use strict'; /** + * A tile bounding volume specified as a longitude/latitude/height region. + * @alias TileBoundingRegion + * @constructor + * * @param {Object} options Object with the following properties: - * @param {Rectangle} options.rectangle - * @param {Number} [options.minimumHeight=0.0] - * @param {Number} [options.maximumHeight=0.0] - * @param {Ellipsoid} [options.ellipsoid=Cesium.Ellipsoid.WGS84] + * @param {Rectangle} options.rectangle The rectangle specifying the longitude and latitude range of the region. + * @param {Number} [options.minimumHeight=0.0] The minimum height of the region. + * @param {Number} [options.maximumHeight=0.0] The maximum height of the region. + * @param {Ellipsoid} [options.ellipsoid=Cesium.Ellipsoid.WGS84] The ellipsoid. * * @private */ - var TileBoundingBox = function(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - + function TileBoundingRegion(options) { //>>includeStart('debug', pragmas.debug); - if (!defined(options.rectangle)) { - throw new DeveloperError('options.url is required.'); - } + Check.typeOf.object('options', options); + Check.typeOf.object('options.rectangle', options.rectangle); //>>includeEnd('debug'); this.rectangle = Rectangle.clone(options.rectangle); @@ -105,7 +122,41 @@ define([ var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); computeBox(this, options.rectangle, ellipsoid); - }; + + // An oriented bounding box that encloses this tile's region. This is used to calculate tile visibility. + this._orientedBoundingBox = OrientedBoundingBox.fromRectangle(this.rectangle, this.minimumHeight, this.maximumHeight, ellipsoid); + + this._boundingSphere = BoundingSphere.fromOrientedBoundingBox(this._orientedBoundingBox); + } + + defineProperties(TileBoundingRegion.prototype, { + /** + * The underlying bounding volume + * + * @memberof TileBoundingRegion.prototype + * + * @type {Object} + * @readonly + */ + boundingVolume : { + get : function() { + return this._orientedBoundingBox; + } + }, + /** + * The underlying bounding sphere + * + * @memberof TileBoundingRegion.prototype + * + * @type {BoundingSphere} + * @readonly + */ + boundingSphere : { + get : function() { + return this._boundingSphere; + } + } + }); var cartesian3Scratch = new Cartesian3(); var cartesian3Scratch2 = new Cartesian3(); @@ -114,7 +165,7 @@ define([ var westernMidpointScratch = new Cartesian3(); var easternMidpointScratch = new Cartesian3(); var cartographicScratch = new Cartographic(); - var planeScratch = new Plane(Cartesian3.ZERO, 0.0); + var planeScratch = new Plane(Cartesian3.UNIT_X, 0.0); var rayScratch = new Ray(); function computeBox(tileBB, rectangle, ellipsoid) { @@ -191,13 +242,15 @@ define([ var vectorScratch = new Cartesian3(); /** - * Gets the distance from the camera to the closest point on the tile. This is used for level-of-detail selection. + * Gets the distance from the camera to the closest point on the tile. This is used for level of detail selection. * * @param {FrameState} frameState The state information of the current rendering frame. - * * @returns {Number} The distance from the camera to the closest point on the tile, in meters. */ - TileBoundingBox.prototype.distanceToCamera = function(frameState) { + TileBoundingRegion.prototype.distanceToCamera = function(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.defined('frameState', frameState); + //>>includeEnd('debug'); var camera = frameState.camera; var cameraCartesianPosition = camera.positionWC; var cameraCartographicPosition = camera.positionCartographic; @@ -263,5 +316,58 @@ define([ return Math.sqrt(result); }; - return TileBoundingBox; + /** + * Determines which side of a plane this box is located. + * + * @param {Plane} plane The plane to test against. + * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane + * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is + * on the opposite side, and {@link Intersect.INTERSECTING} if the box + * intersects the plane. + */ + TileBoundingRegion.prototype.intersectPlane = function(plane) { + //>>includeStart('debug', pragmas.debug); + Check.defined('plane', plane); + //>>includeEnd('debug'); + return this._orientedBoundingBox.intersectPlane(plane); + }; + + /** + * Creates a debug primitive that shows the outline of the tile bounding region. + * + * @param {Color} color The desired color of the primitive's mesh + * @return {Primitive} + * + * @private + */ + TileBoundingRegion.prototype.createDebugVolume = function(color) { + //>>includeStart('debug', pragmas.debug); + Check.defined('color', color); + //>>includeEnd('debug'); + + var modelMatrix = new Matrix4.clone(Matrix4.IDENTITY); + var geometry = new RectangleOutlineGeometry({ + rectangle : this.rectangle, + height : this.minimumHeight, + extrudedHeight: this.maximumHeight + }); + var instance = new GeometryInstance({ + geometry : geometry, + modelMatrix : modelMatrix, + attributes : { + color : ColorGeometryInstanceAttribute.fromColor(color) + } + }); + + return new Primitive({ + geometryInstances : instance, + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true + }), + asynchronous : false + }); + }; + + return TileBoundingRegion; }); diff --git a/Source/Scene/TileBoundingSphere.js b/Source/Scene/TileBoundingSphere.js new file mode 100644 index 000000000000..2f99c5e4d2e7 --- /dev/null +++ b/Source/Scene/TileBoundingSphere.js @@ -0,0 +1,172 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/Cartesian3', + '../Core/Check', + '../Core/ColorGeometryInstanceAttribute', + '../Core/defineProperties', + '../Core/GeometryInstance', + '../Core/Matrix4', + '../Core/SphereOutlineGeometry', + './PerInstanceColorAppearance', + './Primitive' + ], function( + BoundingSphere, + Cartesian3, + Check, + ColorGeometryInstanceAttribute, + defineProperties, + GeometryInstance, + Matrix4, + SphereOutlineGeometry, + PerInstanceColorAppearance, + Primitive) { + 'use strict'; + + /** + * A tile bounding volume specified as a sphere. + * @alias TileBoundingSphere + * @constructor + * + * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the bounding sphere. + * @param {Number} [radius=0.0] The radius of the bounding sphere. + * + * @private + */ + function TileBoundingSphere(center, radius) { + this._boundingSphere = new BoundingSphere(center, radius); + } + + defineProperties(TileBoundingSphere.prototype, { + /** + * The center of the bounding sphere + * + * @memberof TileBoundingSphere.prototype + * + * @type {Cartesian3} + * @readonly + */ + center : { + get : function() { + return this._boundingSphere.center; + } + }, + + /** + * The radius of the bounding sphere + * + * @memberof TileBoundingSphere.prototype + * + * @type {Number} + * @readonly + */ + radius : { + get : function() { + return this._boundingSphere.radius; + } + }, + + /** + * The underlying bounding volume + * + * @memberof TileBoundingSphere.prototype + * + * @type {Object} + * @readonly + */ + boundingVolume : { + get : function() { + return this._boundingSphere; + } + }, + /** + * The underlying bounding sphere + * + * @memberof TileBoundingSphere.prototype + * + * @type {BoundingSphere} + * @readonly + */ + boundingSphere : { + get : function() { + return this._boundingSphere; + } + } + }); + + /** + * Computes the distance between this bounding sphere and the camera attached to frameState. + * + * @param {FrameState} frameState The frameState to which the camera is attached. + * @returns {Number} The distance between the camera and the bounding sphere in meters. Returns 0 if the camera is inside the bounding volume. + * + */ + TileBoundingSphere.prototype.distanceToCamera = function(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.defined('frameState', frameState); + //>>includeEnd('debug'); + var boundingSphere = this._boundingSphere; + return Math.max(0.0, Cartesian3.distance(boundingSphere.center, frameState.camera.positionWC) - boundingSphere.radius); + }; + + /** + * Determines which side of a plane this sphere is located. + * + * @param {Plane} plane The plane to test against. + * @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane + * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is + * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere + * intersects the plane. + */ + TileBoundingSphere.prototype.intersectPlane = function(plane) { + //>>includeStart('debug', pragmas.debug); + Check.defined('plane', plane); + //>>includeEnd('debug'); + return BoundingSphere.intersectPlane(this._boundingSphere, plane); + }; + + /** + * Update the bounding sphere after the tile is transformed. + * + * @param {Cartesian3} center The center of the bounding sphere. + * @param {Number} radius The radius of the bounding sphere. + */ + TileBoundingSphere.prototype.update = function(center, radius) { + Cartesian3.clone(center, this._boundingSphere.center); + this._boundingSphere.radius = radius; + }; + + /** + * Creates a debug primitive that shows the outline of the sphere. + * + * @param {Color} color The desired color of the primitive's mesh + * @return {Primitive} + */ + TileBoundingSphere.prototype.createDebugVolume = function(color) { + //>>includeStart('debug', pragmas.debug); + Check.defined('color', color); + //>>includeEnd('debug'); + var geometry = new SphereOutlineGeometry({ + radius: this.radius + }); + var modelMatrix = Matrix4.fromTranslation(this.center, new Matrix4.clone(Matrix4.IDENTITY)); + var instance = new GeometryInstance({ + geometry : geometry, + modelMatrix : modelMatrix, + attributes : { + color : ColorGeometryInstanceAttribute.fromColor(color) + } + }); + + return new Primitive({ + geometryInstances : instance, + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true + }), + asynchronous : false + }); + }; + + return TileBoundingSphere; +}); diff --git a/Source/Scene/TileBoundingVolume.js b/Source/Scene/TileBoundingVolume.js new file mode 100644 index 000000000000..6227dc5b55e8 --- /dev/null +++ b/Source/Scene/TileBoundingVolume.js @@ -0,0 +1,77 @@ +/*global define*/ +define([ + '../Core/DeveloperError' + ], function( + DeveloperError) { + 'use strict'; + + /** + * Defines a bounding volume for a tile. This type describes an interface + * and is not intended to be instantiated directly. + * + * @see TileBoundingRegion + * @see TileBoundingSphere + * @see TileOrientedBoundingBox + * + * @private + */ + function TileBoundingVolume() { + } + + /** + * The underlying bounding volume. + * + * @memberof TileBoundingVolume.prototype + * + * @type {Object} + * @readonly + */ + TileBoundingVolume.prototype.boundingVolume = undefined; + + /** + * The underlying bounding sphere. + * + * @memberof TileBoundingVolume.prototype + * + * @type {BoundingSphere} + * @readonly + */ + TileBoundingVolume.prototype.boundingSphere = undefined; + + /** + * Calculates the distance between the tile and the camera. + * + * @param {FrameState} frameState The frame state. + * @return {Number} The distance between the tile and the camera, in meters. + * Returns 0.0 if the camera is inside the tile. + */ + TileBoundingVolume.prototype.distanceToCamera = function(frameState) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Determines which side of a plane this volume is located. + * + * @param {Plane} plane The plane to test against. + * @returns {Intersect} {@link Intersect.INSIDE} if the entire volume is on the side of the plane + * the normal is pointing, {@link Intersect.OUTSIDE} if the entire volume is + * on the opposite side, and {@link Intersect.INTERSECTING} if the volume + * intersects the plane. + */ + TileBoundingVolume.prototype.intersectPlane = function(plane) { + DeveloperError.throwInstantiationError(); + }; + + /** + * Creates a debug primitive that shows the outline of the tile bounding + * volume. + * + * @param {Color} color The desired color of the primitive's mesh + * @return {Primitive} + */ + TileBoundingVolume.prototype.createDebugVolume = function(color) { + DeveloperError.throwInstantiationError(); + }; + + return TileBoundingVolume; +}); diff --git a/Source/Scene/TileCoordinatesImageryProvider.js b/Source/Scene/TileCoordinatesImageryProvider.js index 35531470ac0d..0ecf383b481f 100644 --- a/Source/Scene/TileCoordinatesImageryProvider.js +++ b/Source/Scene/TileCoordinatesImageryProvider.js @@ -240,12 +240,13 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an * Image or a Canvas DOM object. */ - TileCoordinatesImageryProvider.prototype.requestImage = function(x, y, level) { + TileCoordinatesImageryProvider.prototype.requestImage = function(x, y, level, request) { var canvas = document.createElement('canvas'); canvas.width = 256; canvas.height = 256; diff --git a/Source/Scene/TileImagery.js b/Source/Scene/TileImagery.js index a38510ebae22..256dadb9f7c8 100644 --- a/Source/Scene/TileImagery.js +++ b/Source/Scene/TileImagery.js @@ -50,7 +50,7 @@ define([ var loadingImagery = this.loadingImagery; var imageryLayer = loadingImagery.imageryLayer; - loadingImagery.processStateMachine(frameState, !this.useWebMercatorT); + loadingImagery.processStateMachine(frameState, !this.useWebMercatorT, tile._priorityFunction); if (loadingImagery.state === ImageryState.READY) { if (defined(this.readyImagery)) { @@ -92,12 +92,11 @@ define([ // Push the ancestor's load process along a bit. This is necessary because some ancestor imagery // tiles may not be attached directly to a terrain tile. Such tiles will never load if // we don't do it here. - closestAncestorThatNeedsLoading.processStateMachine(frameState, !this.useWebMercatorT); + closestAncestorThatNeedsLoading.processStateMachine(frameState, !this.useWebMercatorT, tile._priorityFunction); return false; // not done loading - } else { - // This imagery tile is failed or invalid, and we have the "best available" substitute. - return true; // done loading } + // This imagery tile is failed or invalid, and we have the "best available" substitute. + return true; // done loading } return false; // not done loading diff --git a/Source/Scene/TileOrientedBoundingBox.js b/Source/Scene/TileOrientedBoundingBox.js new file mode 100644 index 000000000000..a074396ab48a --- /dev/null +++ b/Source/Scene/TileOrientedBoundingBox.js @@ -0,0 +1,155 @@ +/*global define*/ +define([ + '../Core/BoundingSphere', + '../Core/BoxOutlineGeometry', + '../Core/Cartesian3', + '../Core/Check', + '../Core/ColorGeometryInstanceAttribute', + '../Core/defineProperties', + '../Core/GeometryInstance', + '../Core/Matrix3', + '../Core/Matrix4', + '../Core/OrientedBoundingBox', + './PerInstanceColorAppearance', + './Primitive' + ], function( + BoundingSphere, + BoxOutlineGeometry, + Cartesian3, + Check, + ColorGeometryInstanceAttribute, + defineProperties, + GeometryInstance, + Matrix3, + Matrix4, + OrientedBoundingBox, + PerInstanceColorAppearance, + Primitive) { + 'use strict'; + + /** + * A tile bounding volume specified as an oriented bounding box. + * @alias TileOrientedBoundingBox + * @constructor + * + * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box. + * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box. + * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 + * cube centered at the origin. + * + * @private + */ + function TileOrientedBoundingBox(center, halfAxes) { + this._orientedBoundingBox = new OrientedBoundingBox(center, halfAxes); + this._boundingSphere = BoundingSphere.fromOrientedBoundingBox(this._orientedBoundingBox); + } + + defineProperties(TileOrientedBoundingBox.prototype, { + /** + * The underlying bounding volume. + * + * @memberof TileOrientedBoundingBox.prototype + * + * @type {Object} + * @readonly + */ + boundingVolume : { + get : function() { + return this._orientedBoundingBox; + } + }, + /** + * The underlying bounding sphere. + * + * @memberof TileOrientedBoundingBox.prototype + * + * @type {BoundingSphere} + * @readonly + */ + boundingSphere : { + get : function() { + return this._boundingSphere; + } + } + }); + + /** + * Computes the distance between this bounding box and the camera attached to frameState. + * + * @param {FrameState} frameState The frameState to which the camera is attached. + * @returns {Number} The distance between the camera and the bounding box in meters. Returns 0 if the camera is inside the bounding volume. + */ + TileOrientedBoundingBox.prototype.distanceToCamera = function(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.defined('frameState', frameState); + //>>includeEnd('debug'); + return Math.sqrt(this._orientedBoundingBox.distanceSquaredTo(frameState.camera.positionWC)); + }; + + /** + * Determines which side of a plane this box is located. + * + * @param {Plane} plane The plane to test against. + * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane + * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is + * on the opposite side, and {@link Intersect.INTERSECTING} if the box + * intersects the plane. + */ + TileOrientedBoundingBox.prototype.intersectPlane = function(plane) { + //>>includeStart('debug', pragmas.debug); + Check.defined('plane', plane); + //>>includeEnd('debug'); + return this._orientedBoundingBox.intersectPlane(plane); + }; + + /** + * Update the bounding box after the tile is transformed. + * + * @param {Cartesian3} center The center of the box. + * @param {Matrix3} halfAxes The three orthogonal half-axes of the bounding box. + * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 + * cube centered at the origin. + */ + TileOrientedBoundingBox.prototype.update = function(center, halfAxes) { + Cartesian3.clone(center, this._orientedBoundingBox.center); + Matrix3.clone(halfAxes, this._orientedBoundingBox.halfAxes); + BoundingSphere.fromOrientedBoundingBox(this._orientedBoundingBox, this._boundingSphere); + }; + + /** + * Creates a debug primitive that shows the outline of the box. + * + * @param {Color} color The desired color of the primitive's mesh + * @return {Primitive} + */ + TileOrientedBoundingBox.prototype.createDebugVolume = function(color) { + //>>includeStart('debug', pragmas.debug); + Check.defined('color', color); + //>>includeEnd('debug'); + + var geometry = new BoxOutlineGeometry({ + // Make a 2x2x2 cube + minimum: new Cartesian3(-1.0, -1.0, -1.0), + maximum: new Cartesian3(1.0, 1.0, 1.0) + }); + var modelMatrix = Matrix4.fromRotationTranslation(this.boundingVolume.halfAxes, this.boundingVolume.center); + var instance = new GeometryInstance({ + geometry : geometry, + modelMatrix : modelMatrix, + attributes : { + color : ColorGeometryInstanceAttribute.fromColor(color) + } + }); + + return new Primitive({ + geometryInstances : instance, + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true + }), + asynchronous : false + }); + }; + + return TileOrientedBoundingBox; +}); diff --git a/Source/Scene/TileTerrain.js b/Source/Scene/TileTerrain.js index f152e32cdbb7..98c315518c93 100644 --- a/Source/Scene/TileTerrain.js +++ b/Source/Scene/TileTerrain.js @@ -6,13 +6,15 @@ define([ '../Core/DeveloperError', '../Core/IndexDatatype', '../Core/OrientedBoundingBox', + '../Core/Request', + '../Core/RequestState', + '../Core/RequestType', '../Core/TileProviderError', '../Renderer/Buffer', '../Renderer/BufferUsage', '../Renderer/VertexArray', '../ThirdParty/when', - './TerrainState', - './TileBoundingBox' + './TerrainState' ], function( BoundingSphere, Cartesian3, @@ -20,13 +22,15 @@ define([ DeveloperError, IndexDatatype, OrientedBoundingBox, + Request, + RequestState, + RequestType, TileProviderError, Buffer, BufferUsage, VertexArray, when, - TerrainState, - TileBoundingBox) { + TerrainState) { 'use strict'; /** @@ -52,6 +56,7 @@ define([ this.mesh = undefined; this.vertexArray = undefined; this.upsampleDetails = upsampleDetails; + this.request = undefined; } TileTerrain.prototype.freeResources = function() { @@ -83,19 +88,14 @@ define([ surfaceTile.maximumHeight = mesh.maximumHeight; surfaceTile.boundingSphere3D = BoundingSphere.clone(mesh.boundingSphere3D, surfaceTile.boundingSphere3D); surfaceTile.orientedBoundingBox = OrientedBoundingBox.clone(mesh.orientedBoundingBox, surfaceTile.orientedBoundingBox); - surfaceTile.tileBoundingBox = new TileBoundingBox({ - rectangle : tile.rectangle, - minimumHeight : mesh.minimumHeight, - maximumHeight : mesh.maximumHeight, - ellipsoid : tile.tilingScheme.ellipsoid - }); - + surfaceTile.tileBoundingRegion.minimumHeight = mesh.minimumHeight; + surfaceTile.tileBoundingRegion.maximumHeight = mesh.maximumHeight; tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace); }; - TileTerrain.prototype.processLoadStateMachine = function(frameState, terrainProvider, x, y, level) { + TileTerrain.prototype.processLoadStateMachine = function(frameState, terrainProvider, x, y, level, priorityFunction) { if (this.state === TerrainState.UNLOADED) { - requestTileGeometry(this, terrainProvider, x, y, level); + requestTileGeometry(this, terrainProvider, x, y, level, priorityFunction); } if (this.state === TerrainState.RECEIVED) { @@ -107,16 +107,26 @@ define([ } }; - function requestTileGeometry(tileTerrain, terrainProvider, x, y, level) { + function requestTileGeometry(tileTerrain, terrainProvider, x, y, level, priorityFunction) { function success(terrainData) { tileTerrain.data = terrainData; tileTerrain.state = TerrainState.RECEIVED; + tileTerrain.request = undefined; } function failure() { + if (tileTerrain.request.state === RequestState.CANCELLED) { + // Cancelled due to low priority - try again later. + tileTerrain.data = undefined; + tileTerrain.state = TerrainState.UNLOADED; + tileTerrain.request = undefined; + return; + } + // Initially assume failure. handleError may retry, in which case the state will // change to RECEIVING or UNLOADED. tileTerrain.state = TerrainState.FAILED; + tileTerrain.request = undefined; var message = 'Failed to obtain terrain tile X: ' + x + ' Y: ' + y + ' Level: ' + level + '.'; terrainProvider._requestError = TileProviderError.handleError( @@ -130,17 +140,24 @@ define([ function doRequest() { // Request the terrain from the terrain provider. - tileTerrain.data = terrainProvider.requestTileGeometry(x, y, level); + var request = new Request({ + throttle : true, + throttleByServer : true, + type : RequestType.TERRAIN, + priorityFunction : priorityFunction + }); + tileTerrain.request = request; + tileTerrain.data = terrainProvider.requestTileGeometry(x, y, level, request); // If the request method returns undefined (instead of a promise), the request // has been deferred. if (defined(tileTerrain.data)) { tileTerrain.state = TerrainState.RECEIVING; - when(tileTerrain.data, success, failure); } else { // Deferred - try again later. tileTerrain.state = TerrainState.UNLOADED; + tileTerrain.request = undefined; } } diff --git a/Source/Scene/Tileset3DTileContent.js b/Source/Scene/Tileset3DTileContent.js new file mode 100644 index 000000000000..e258f23c577a --- /dev/null +++ b/Source/Scene/Tileset3DTileContent.js @@ -0,0 +1,221 @@ +/*global define*/ +define([ + '../Core/defaultValue', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/getStringFromTypedArray', + '../Core/RuntimeError', + '../ThirdParty/when' + ], function( + defaultValue, + defineProperties, + destroyObject, + getStringFromTypedArray, + RuntimeError, + when) { + 'use strict'; + + /** + * Represents content for a tile in a + * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset whose + * content points to another 3D Tiles tileset. + *

      + * Implements the {@link Cesium3DTileContent} interface. + *

      + * + * @alias Tileset3DTileContent + * @constructor + * + * @private + */ + function Tileset3DTileContent(tileset, tile, url, arrayBuffer, byteOffset) { + this._tileset = tileset; + this._tile = tile; + this._url = url; + this._readyPromise = when.defer(); + + /** + * @inheritdoc Cesium3DTileContent#featurePropertiesDirty + */ + this.featurePropertiesDirty = false; + + initialize(this, arrayBuffer, byteOffset); + } + + defineProperties(Tileset3DTileContent.prototype, { + /** + * @inheritdoc Cesium3DTileContent#featuresLength + */ + featuresLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#pointsLength + */ + pointsLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#trianglesLength + */ + trianglesLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#geometryByteLength + */ + geometryByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#texturesByteLength + */ + texturesByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTableByteLength + */ + batchTableByteLength : { + get : function() { + return 0; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#innerContents + */ + innerContents : { + get : function() { + return undefined; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#readyPromise + */ + readyPromise : { + get : function() { + return this._readyPromise.promise; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tileset + */ + tileset : { + get : function() { + return this._tileset; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#tile + */ + tile : { + get : function() { + return this._tile; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#url + */ + url : { + get : function() { + return this._url; + } + }, + + /** + * @inheritdoc Cesium3DTileContent#batchTable + */ + batchTable : { + get : function() { + return undefined; + } + } + }); + + function initialize(content, arrayBuffer, byteOffset) { + byteOffset = defaultValue(byteOffset, 0); + var uint8Array = new Uint8Array(arrayBuffer); + var jsonString = getStringFromTypedArray(uint8Array, byteOffset); + var tilesetJson; + + try { + tilesetJson = JSON.parse(jsonString); + } catch (error) { + content._readyPromise.reject(new RuntimeError('Invalid tile content.')); + return; + } + + content._tileset.loadTileset(content._url, tilesetJson, content._tile); + content._readyPromise.resolve(content); + } + + /** + * Part of the {@link Cesium3DTileContent} interface. Tileset3DTileContent + * always returns false since a tile of this type does not have any features. + */ + Tileset3DTileContent.prototype.hasProperty = function(batchId, name) { + return false; + }; + + /** + * Part of the {@link Cesium3DTileContent} interface. Tileset3DTileContent + * always returns undefined since a tile of this type does not have any features. + */ + Tileset3DTileContent.prototype.getFeature = function(batchId) { + return undefined; + }; + + /** + * @inheritdoc Cesium3DTileContent#applyDebugSettings + */ + Tileset3DTileContent.prototype.applyDebugSettings = function(enabled, color) { + }; + + /** + * @inheritdoc Cesium3DTileContent#applyStyle + */ + Tileset3DTileContent.prototype.applyStyle = function(frameState, style) { + }; + + /** + * @inheritdoc Cesium3DTileContent#update + */ + Tileset3DTileContent.prototype.update = function(tileset, frameState) { + }; + + /** + * @inheritdoc Cesium3DTileContent#isDestroyed + */ + Tileset3DTileContent.prototype.isDestroyed = function() { + return false; + }; + + /** + * @inheritdoc Cesium3DTileContent#destroy + */ + Tileset3DTileContent.prototype.destroy = function() { + return destroyObject(this); + }; + + return Tileset3DTileContent; +}); diff --git a/Source/Scene/UrlTemplateImageryProvider.js b/Source/Scene/UrlTemplateImageryProvider.js index 4ed3a72bb76a..c30ede35ec07 100644 --- a/Source/Scene/UrlTemplateImageryProvider.js +++ b/Source/Scene/UrlTemplateImageryProvider.js @@ -161,7 +161,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider @@ -265,7 +265,6 @@ define([ } }, - /** * Gets the URL template to use to use to pick features. If this property is not specified, * {@link UrlTemplateImageryProvider#pickFeatures} will immediately returned undefined, indicating no @@ -570,8 +569,8 @@ define([ } that._credit = credit; - that._urlParts = urlTemplateToParts(that._url, tags); - that._pickFeaturesUrlParts = urlTemplateToParts(that._pickFeaturesUrl, pickFeaturesTags); + that._urlParts = urlTemplateToParts(that._url, tags); //eslint-disable-line no-use-before-define + that._pickFeaturesUrlParts = urlTemplateToParts(that._pickFeaturesUrl, pickFeaturesTags); //eslint-disable-line no-use-before-define return true; }); }; @@ -602,19 +601,20 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an * Image or a Canvas DOM object. */ - UrlTemplateImageryProvider.prototype.requestImage = function(x, y, level) { + UrlTemplateImageryProvider.prototype.requestImage = function(x, y, level, request) { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError('requestImage must not be called before the imagery provider is ready.'); } //>>includeEnd('debug'); var url = buildImageUrl(this, x, y, level); - return ImageryProvider.loadImage(this, url); + return ImageryProvider.loadImage(this, url, request); }; /** @@ -667,17 +667,21 @@ define([ return loadXML(url).then(format.callback).otherwise(doRequest); } else if (format.type === 'text' || format.type === 'html') { return loadText(url).then(format.callback).otherwise(doRequest); - } else { - return loadWithXhr({ - url: url, - responseType: format.format - }).then(handleResponse.bind(undefined, format)).otherwise(doRequest); } + return loadWithXhr({ + url : url, + responseType : format.format + }).then(handleResponse.bind(undefined, format)).otherwise(doRequest); } return doRequest(); }; + var degreesScratchComputed = false; + var degreesScratch = new Rectangle(); + var projectedScratchComputed = false; + var projectedScratch = new Rectangle(); + function buildImageUrl(imageryProvider, x, y, level) { degreesScratchComputed = false; projectedScratchComputed = false; @@ -687,6 +691,10 @@ define([ }); } + var ijScratchComputed = false; + var ijScratch = new Cartesian2(); + var longitudeLatitudeProjectedScratchComputed = false; + function buildPickFeaturesUrl(imageryProvider, x, y, level, longitude, latitude, format) { degreesScratchComputed = false; projectedScratchComputed = false; @@ -805,9 +813,6 @@ define([ return imageryProvider._subdomains[index]; } - var degreesScratchComputed = false; - var degreesScratch = new Rectangle(); - function computeDegrees(imageryProvider, x, y, level) { if (degreesScratchComputed) { return; @@ -842,9 +847,6 @@ define([ return degreesScratch.north; } - var projectedScratchComputed = false; - var projectedScratch = new Rectangle(); - function computeProjected(imageryProvider, x, y, level) { if (projectedScratchComputed) { return; @@ -883,9 +885,6 @@ define([ return imageryProvider.tileHeight; } - var ijScratchComputed = false; - var ijScratch = new Cartesian2(); - function iTag(imageryProvider, x, y, level, longitude, latitude, format) { computeIJ(imageryProvider, x, y, level, longitude, latitude); return ijScratch.x; @@ -907,6 +906,7 @@ define([ } var rectangleScratch = new Rectangle(); + var longitudeLatitudeProjectedScratch = new Cartesian3(); function computeIJ(imageryProvider, x, y, level, longitude, latitude, format) { if (ijScratchComputed) { @@ -930,9 +930,6 @@ define([ return CesiumMath.toDegrees(latitude); } - var longitudeLatitudeProjectedScratchComputed = false; - var longitudeLatitudeProjectedScratch = new Cartesian3(); - function longitudeProjectedTag(imageryProvider, x, y, level, longitude, latitude, format) { computeLongitudeLatitudeProjected(imageryProvider, x, y, level, longitude, latitude); return longitudeLatitudeProjectedScratch.x; @@ -950,7 +947,6 @@ define([ return; } - var projected; if (imageryProvider.tilingScheme instanceof GeographicTilingScheme) { longitudeLatitudeProjectedScratch.x = CesiumMath.toDegrees(longitude); longitudeLatitudeProjectedScratch.y = CesiumMath.toDegrees(latitude); @@ -958,7 +954,7 @@ define([ var cartographic = cartographicScratch; cartographic.longitude = longitude; cartographic.latitude = latitude; - projected = imageryProvider.tilingScheme.projection.project(cartographic, longitudeLatitudeProjectedScratch); + imageryProvider.tilingScheme.projection.project(cartographic, longitudeLatitudeProjectedScratch); } longitudeLatitudeProjectedScratchComputed = true; diff --git a/Source/Scene/WebMapServiceImageryProvider.js b/Source/Scene/WebMapServiceImageryProvider.js index 9de05226af25..93e98bc4a010 100644 --- a/Source/Scene/WebMapServiceImageryProvider.js +++ b/Source/Scene/WebMapServiceImageryProvider.js @@ -71,7 +71,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider @@ -433,6 +433,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -440,8 +441,8 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - WebMapServiceImageryProvider.prototype.requestImage = function(x, y, level) { - return this._tileProvider.requestImage(x, y, level); + WebMapServiceImageryProvider.prototype.requestImage = function(x, y, level, request) { + return this._tileProvider.requestImage(x, y, level, request); }; /** diff --git a/Source/Scene/WebMapTileServiceImageryProvider.js b/Source/Scene/WebMapTileServiceImageryProvider.js index 88650ecee7ac..21986a61866f 100644 --- a/Source/Scene/WebMapTileServiceImageryProvider.js +++ b/Source/Scene/WebMapTileServiceImageryProvider.js @@ -92,7 +92,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider @@ -432,6 +432,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -439,9 +440,9 @@ define([ * * @exception {DeveloperError} requestImage must not be called before the imagery provider is ready. */ - WebMapTileServiceImageryProvider.prototype.requestImage = function(x, y, level) { + WebMapTileServiceImageryProvider.prototype.requestImage = function(x, y, level, request) { var url = buildImageUrl(this, x, y, level); - return ImageryProvider.loadImage(this, url); + return ImageryProvider.loadImage(this, url, request); }; /** diff --git a/Source/Scene/createOpenStreetMapImageryProvider.js b/Source/Scene/createOpenStreetMapImageryProvider.js index a8b8bed13ae3..80dc64ff387f 100644 --- a/Source/Scene/createOpenStreetMapImageryProvider.js +++ b/Source/Scene/createOpenStreetMapImageryProvider.js @@ -41,7 +41,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see SingleTileImageryProvider * @see createTileMapServiceImageryProvider * @see WebMapServiceImageryProvider diff --git a/Source/Scene/createTileMapServiceImageryProvider.js b/Source/Scene/createTileMapServiceImageryProvider.js index bc71467d1d25..53ef9cd1866b 100644 --- a/Source/Scene/createTileMapServiceImageryProvider.js +++ b/Source/Scene/createTileMapServiceImageryProvider.js @@ -61,7 +61,7 @@ define([ * * @see ArcGisMapServerImageryProvider * @see BingMapsImageryProvider - * @see GoogleEarthImageryProvider + * @see GoogleEarthEnterpriseMapsProvider * @see createOpenStreetMapImageryProvider * @see SingleTileImageryProvider * @see WebMapServiceImageryProvider @@ -105,8 +105,7 @@ define([ var tileSetRegex = /tileset/i; var tileSetsRegex = /tilesets/i; var bboxRegex = /boundingbox/i; - var srsRegex = /srs/i; - var format, bbox, tilesets, srs; + var format, bbox, tilesets; var tilesetsList = []; //list of TileSets // Allowing options properties (already copied to that) to override XML values @@ -128,8 +127,6 @@ define([ } } else if (bboxRegex.test(nodeList.item(i).nodeName)) { bbox = nodeList.item(i); - } else if (srsRegex.test(nodeList.item(i).nodeName)) { - srs = nodeList.item(i).textContent; } } diff --git a/Source/Scene/getBinaryAccessor.js b/Source/Scene/getBinaryAccessor.js new file mode 100644 index 000000000000..e4aa0036f26a --- /dev/null +++ b/Source/Scene/getBinaryAccessor.js @@ -0,0 +1,64 @@ +/*global define*/ +define([ + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/ComponentDatatype', + '../Core/Matrix2', + '../Core/Matrix3', + '../Core/Matrix4' + ], function( + Cartesian2, + Cartesian3, + Cartesian4, + ComponentDatatype, + Matrix2, + Matrix3, + Matrix4) { + 'use strict'; + + var ComponentsPerAttribute = { + SCALAR : 1, + VEC2 : 2, + VEC3 : 3, + VEC4 : 4, + MAT2 : 4, + MAT3 : 9, + MAT4 : 16 + }; + + var ClassPerType = { + SCALAR : undefined, + VEC2 : Cartesian2, + VEC3 : Cartesian3, + VEC4 : Cartesian4, + MAT2 : Matrix2, + MAT3 : Matrix3, + MAT4 : Matrix4 + }; + + /** + * @private + */ + function getBinaryAccessor(accessor) { + var componentType = accessor.componentType; + var componentDatatype; + if (typeof componentType === 'string') { + componentDatatype = ComponentDatatype.fromName(componentType); + } else { + componentDatatype = componentType; + } + + var componentsPerAttribute = ComponentsPerAttribute[accessor.type]; + var classType = ClassPerType[accessor.type]; + return { + componentsPerAttribute : componentsPerAttribute, + classType : classType, + createArrayBufferView : function(buffer, byteOffset, length) { + return ComponentDatatype.createArrayBufferView(componentDatatype, buffer, byteOffset, componentsPerAttribute * length); + } + }; + } + + return getBinaryAccessor; +}); diff --git a/Source/Shaders/Appearances/PointAppearanceFS.glsl b/Source/Shaders/Appearances/PointAppearanceFS.glsl deleted file mode 100644 index c34c2e3e67f9..000000000000 --- a/Source/Shaders/Appearances/PointAppearanceFS.glsl +++ /dev/null @@ -1,8 +0,0 @@ -uniform vec4 highlightColor; - -varying vec3 v_color; - -void main() -{ - gl_FragColor = vec4(v_color * highlightColor.rgb, highlightColor.a); -} diff --git a/Source/Shaders/Appearances/PointAppearanceVS.glsl b/Source/Shaders/Appearances/PointAppearanceVS.glsl deleted file mode 100644 index 4d41d523be98..000000000000 --- a/Source/Shaders/Appearances/PointAppearanceVS.glsl +++ /dev/null @@ -1,16 +0,0 @@ -attribute vec3 position3DHigh; -attribute vec3 position3DLow; -attribute vec3 color; -attribute float batchId; - -uniform float pointSize; - -varying vec3 v_positionEC; -varying vec3 v_color; - -void main() -{ - v_color = color; - gl_Position = czm_modelViewProjectionRelativeToEye * czm_computePosition(); - gl_PointSize = pointSize; -} diff --git a/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl b/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl index db7c610d10d6..5bb9d0188828 100644 --- a/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl @@ -10,18 +10,19 @@ attribute float batchId; varying vec4 v_color; -void main() +void main() { float expandDir = expandAndWidth.x; float width = abs(expandAndWidth.y) + 0.5; bool usePrev = expandAndWidth.y < 0.0; - + vec4 p = czm_computePosition(); vec4 prev = czm_computePrevPosition(); vec4 next = czm_computeNextPosition(); - + v_color = color; - - vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev); + + float angle; + vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle); gl_Position = czm_viewportOrthographic * positionWC; } diff --git a/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl index dcb40b5ad322..a0040255a0d3 100644 --- a/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl @@ -10,20 +10,21 @@ attribute float batchId; varying float v_width; varying vec2 v_st; +varying float v_angle; -void main() +void main() { float expandDir = expandAndWidth.x; float width = abs(expandAndWidth.y) + 0.5; bool usePrev = expandAndWidth.y < 0.0; - + vec4 p = czm_computePosition(); vec4 prev = czm_computePrevPosition(); vec4 next = czm_computeNextPosition(); - + v_width = width; v_st = st; - - vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev); + + vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, v_angle); gl_Position = czm_viewportOrthographic * positionWC; } diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index e9448a6cdf69..aa4c4e15baae 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -3,13 +3,13 @@ attribute vec2 direction; #endif attribute vec4 positionHighAndScale; 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 -attribute vec4 eyeOffset; // eye offset in meters, 4 bytes free (texture range) -attribute vec4 scaleByDistance; // near, nearScale, far, farScale -attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale -attribute vec2 distanceDisplayCondition; // near, far +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 +attribute vec4 eyeOffset; // eye offset in meters, 4 bytes free (texture range) +attribute vec4 scaleByDistance; // near, nearScale, far, farScale +attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale +attribute vec3 distanceDisplayConditionAndDisableDepth; // near, far, disableDepthTestDistance varying vec2 v_textureCoordinates; @@ -51,11 +51,9 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca float angle = rotation; if (validAlignedAxis) { - vec3 pos = positionEC.xyz + czm_encodedCameraPositionMCHigh + czm_encodedCameraPositionMCLow; - vec3 normal = normalize(cross(alignedAxis, pos)); - vec4 tangent = vec4(normalize(cross(pos, normal)), 0.0); - tangent = czm_modelViewProjection * tangent; - angle += sign(-tangent.x) * acos(tangent.y / length(tangent.xy)); + vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0); + angle += sign(-projectedAlignedAxis.x) * acos( sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) / + (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y) ); } float cosTheta = cos(angle); @@ -74,7 +72,7 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca if (sizeInMeters) { - originTranslate += originTranslate / czm_metersPerPixel(positionEC); + originTranslate /= czm_metersPerPixel(positionEC); } positionWC.xy += originTranslate; @@ -205,7 +203,7 @@ void main() /////////////////////////////////////////////////////////////////////////// -#if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(EYE_DISTANCE_PIXEL_OFFSET) || defined(DISTANCE_DISPLAY_CONDITION) +#if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(EYE_DISTANCE_PIXEL_OFFSET) || defined(DISTANCE_DISPLAY_CONDITION) || defined(DISABLE_DEPTH_DISTANCE) float lengthSq; if (czm_sceneMode == czm_sceneMode2D) { @@ -246,8 +244,8 @@ void main() #endif #ifdef DISTANCE_DISPLAY_CONDITION - float nearSq = distanceDisplayCondition.x * distanceDisplayCondition.x; - float farSq = distanceDisplayCondition.y * distanceDisplayCondition.y; + float nearSq = distanceDisplayConditionAndDisableDepth.x; + float farSq = distanceDisplayConditionAndDisableDepth.y; if (lengthSq < nearSq || lengthSq > farSq) { positionEC.xyz = vec3(0.0); @@ -258,6 +256,25 @@ void main() gl_Position = czm_viewportOrthographic * vec4(positionWC.xy, -positionWC.z, 1.0); v_textureCoordinates = textureCoordinates; +#ifdef DISABLE_DEPTH_DISTANCE + float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z; + if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) + { + disableDepthTestDistance = czm_minimumDisableDepthTestDistance; + } + + if (disableDepthTestDistance != 0.0) + { + gl_Position.z = min(gl_Position.z, gl_Position.w); + + bool clipped = gl_Position.z < -gl_Position.w || gl_Position.z > gl_Position.w; + if (!clipped && (disableDepthTestDistance < 0.0 || (lengthSq > 0.0 && lengthSq < disableDepthTestDistance))) + { + gl_Position.z = -gl_Position.w; + } + } +#endif + #ifdef RENDER_FOR_PICK v_pickColor = pickColor; #else diff --git a/Source/Shaders/Builtin/Constants/passCesium3DTile.glsl b/Source/Shaders/Builtin/Constants/passCesium3DTile.glsl new file mode 100644 index 000000000000..0899985e516e --- /dev/null +++ b/Source/Shaders/Builtin/Constants/passCesium3DTile.glsl @@ -0,0 +1,9 @@ +/** + * The automatic GLSL constant for {@link Pass#CESIUM_3D_TILE} + * + * @name czm_passCesium3DTile + * @glslConstant + * + * @see czm_pass + */ +const float czm_passCesium3DTile = 3.0; diff --git a/Source/Shaders/Builtin/Constants/passGround.glsl b/Source/Shaders/Builtin/Constants/passGround.glsl index f8fe879839e8..427bc235d4ba 100644 --- a/Source/Shaders/Builtin/Constants/passGround.glsl +++ b/Source/Shaders/Builtin/Constants/passGround.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passGround = 3.0; +const float czm_passGround = 4.0; diff --git a/Source/Shaders/Builtin/Constants/passOpaque.glsl b/Source/Shaders/Builtin/Constants/passOpaque.glsl index bf9006c27097..7f7fbe11276e 100644 --- a/Source/Shaders/Builtin/Constants/passOpaque.glsl +++ b/Source/Shaders/Builtin/Constants/passOpaque.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passOpaque = 4.0; +const float czm_passOpaque = 5.0; diff --git a/Source/Shaders/Builtin/Constants/passOverlay.glsl b/Source/Shaders/Builtin/Constants/passOverlay.glsl index 60226ad2e9b3..f780993b6638 100644 --- a/Source/Shaders/Builtin/Constants/passOverlay.glsl +++ b/Source/Shaders/Builtin/Constants/passOverlay.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passOverlay = 6.0; +const float czm_passOverlay = 7.0; diff --git a/Source/Shaders/Builtin/Constants/passTranslucent.glsl b/Source/Shaders/Builtin/Constants/passTranslucent.glsl index 25aac109e526..eb2bb2d4b7a1 100644 --- a/Source/Shaders/Builtin/Constants/passTranslucent.glsl +++ b/Source/Shaders/Builtin/Constants/passTranslucent.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passTranslucent = 5.0; +const float czm_passTranslucent = 6.0; diff --git a/Source/Shaders/Builtin/Functions/HSBToRGB.glsl b/Source/Shaders/Builtin/Functions/HSBToRGB.glsl new file mode 100644 index 000000000000..63036c4f1478 --- /dev/null +++ b/Source/Shaders/Builtin/Functions/HSBToRGB.glsl @@ -0,0 +1,24 @@ +/** + * Converts an HSB color (hue, saturation, brightness) to RGB + * HSB <-> RGB conversion with minimal branching: {@link http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl} + * + * @name czm_HSBToRGB + * @glslFunction + * + * @param {vec3} hsb The color in HSB. + * + * @returns {vec3} The color in RGB. + * + * @example + * vec3 hsb = czm_RGBToHSB(rgb); + * hsb.z *= 0.1; + * rgb = czm_HSBToRGB(hsb); + */ + +const vec4 K_HSB2RGB = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + +vec3 czm_HSBToRGB(vec3 hsb) +{ + vec3 p = abs(fract(hsb.xxx + K_HSB2RGB.xyz) * 6.0 - K_HSB2RGB.www); + return hsb.z * mix(K_HSB2RGB.xxx, clamp(p - K_HSB2RGB.xxx, 0.0, 1.0), hsb.y); +} diff --git a/Source/Shaders/Builtin/Functions/HSLToRGB.glsl b/Source/Shaders/Builtin/Functions/HSLToRGB.glsl new file mode 100644 index 000000000000..59b06220cd2f --- /dev/null +++ b/Source/Shaders/Builtin/Functions/HSLToRGB.glsl @@ -0,0 +1,31 @@ +/** + * Converts an HSL color (hue, saturation, lightness) to RGB + * HSL <-> RGB conversion: {@link http://www.chilliant.com/rgb2hsv.html} + * + * @name czm_HSLToRGB + * @glslFunction + * + * @param {vec3} rgb The color in HSL. + * + * @returns {vec3} The color in RGB. + * + * @example + * vec3 hsl = czm_RGBToHSL(rgb); + * hsl.z *= 0.1; + * rgb = czm_HSLToRGB(hsl); + */ + +vec3 hueToRGB(float hue) +{ + float r = abs(hue * 6.0 - 3.0) - 1.0; + float g = 2.0 - abs(hue * 6.0 - 2.0); + float b = 2.0 - abs(hue * 6.0 - 4.0); + return clamp(vec3(r, g, b), 0.0, 1.0); +} + +vec3 czm_HSLToRGB(vec3 hsl) +{ + vec3 rgb = hueToRGB(hsl.x); + float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y; + return (rgb - 0.5) * c + hsl.z; +} diff --git a/Source/Shaders/Builtin/Functions/RGBToHSB.glsl b/Source/Shaders/Builtin/Functions/RGBToHSB.glsl new file mode 100644 index 000000000000..9826d72723e2 --- /dev/null +++ b/Source/Shaders/Builtin/Functions/RGBToHSB.glsl @@ -0,0 +1,27 @@ +/** + * Converts an RGB color to HSB (hue, saturation, brightness) + * HSB <-> RGB conversion with minimal branching: {@link http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl} + * + * @name czm_RGBToHSB + * @glslFunction + * + * @param {vec3} rgb The color in RGB. + * + * @returns {vec3} The color in HSB. + * + * @example + * vec3 hsb = czm_RGBToHSB(rgb); + * hsb.z *= 0.1; + * rgb = czm_HSBToRGB(hsb); + */ + +const vec4 K_RGB2HSB = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + +vec3 czm_RGBToHSB(vec3 rgb) +{ + vec4 p = mix(vec4(rgb.bg, K_RGB2HSB.wz), vec4(rgb.gb, K_RGB2HSB.xy), step(rgb.b, rgb.g)); + vec4 q = mix(vec4(p.xyw, rgb.r), vec4(rgb.r, p.yzx), step(p.x, rgb.r)); + + float d = q.x - min(q.w, q.y); + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + czm_epsilon7)), d / (q.x + czm_epsilon7), q.x); +} diff --git a/Source/Shaders/Builtin/Functions/RGBToHSL.glsl b/Source/Shaders/Builtin/Functions/RGBToHSL.glsl new file mode 100644 index 000000000000..190f1d63c7bf --- /dev/null +++ b/Source/Shaders/Builtin/Functions/RGBToHSL.glsl @@ -0,0 +1,34 @@ +/** + * Converts an RGB color to HSL (hue, saturation, lightness) + * HSL <-> RGB conversion: {@link http://www.chilliant.com/rgb2hsv.html} + * + * @name czm_RGBToHSL + * @glslFunction + * + * @param {vec3} rgb The color in RGB. + * + * @returns {vec3} The color in HSL. + * + * @example + * vec3 hsl = czm_RGBToHSL(rgb); + * hsl.z *= 0.1; + * rgb = czm_HSLToRGB(hsl); + */ + +vec3 RGBtoHCV(vec3 rgb) +{ + // Based on work by Sam Hocevar and Emil Persson + vec4 p = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0 / 3.0) : vec4(rgb.gb, 0.0, -1.0 / 3.0); + vec4 q = (rgb.r < p.x) ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx); + float c = q.x - min(q.w, q.y); + float h = abs((q.w - q.y) / (6.0 * c + czm_epsilon7) + q.z); + return vec3(h, c, q.x); +} + +vec3 czm_RGBToHSL(vec3 rgb) +{ + vec3 hcv = RGBtoHCV(rgb); + float l = hcv.z - hcv.y * 0.5; + float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + czm_epsilon7); + return vec3(hcv.x, s, l); +} diff --git a/Source/Shaders/CompositeOITFS.glsl b/Source/Shaders/CompositeOITFS.glsl index b2efbceba787..dbe6c46d5801 100644 --- a/Source/Shaders/CompositeOITFS.glsl +++ b/Source/Shaders/CompositeOITFS.glsl @@ -3,7 +3,7 @@ * - http://jcgt.org/published/0002/02/09/ * - http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html */ - + uniform sampler2D u_opaque; uniform sampler2D u_accumulation; uniform sampler2D u_revealage; @@ -15,12 +15,17 @@ void main() vec4 opaque = texture2D(u_opaque, v_textureCoordinates); vec4 accum = texture2D(u_accumulation, v_textureCoordinates); float r = texture2D(u_revealage, v_textureCoordinates).r; - + #ifdef MRT vec4 transparent = vec4(accum.rgb / clamp(r, 1e-4, 5e4), accum.a); #else vec4 transparent = vec4(accum.rgb / clamp(accum.a, 1e-4, 5e4), r); #endif - + gl_FragColor = (1.0 - transparent.a) * transparent + transparent.a * opaque; + + if (opaque != czm_backgroundColor) + { + gl_FragColor.a = 1.0; + } } diff --git a/Source/Shaders/Materials/PolylineDashMaterial.glsl b/Source/Shaders/Materials/PolylineDashMaterial.glsl new file mode 100644 index 000000000000..9a1a6d39bfe7 --- /dev/null +++ b/Source/Shaders/Materials/PolylineDashMaterial.glsl @@ -0,0 +1,38 @@ +uniform vec4 color; +uniform vec4 gapColor; +uniform float dashLength; +uniform float dashPattern; +varying float v_angle; + +const float maskLength = 16.0; + +mat2 rotate(float rad) { + float c = cos(rad); + float s = sin(rad); + return mat2( + c, s, + -s, c + ); +} + +czm_material czm_getMaterial(czm_materialInput materialInput) +{ + czm_material material = czm_getDefaultMaterial(materialInput); + + vec2 pos = rotate(v_angle) * gl_FragCoord.xy; + + // Get the relative position within the dash from 0 to 1 + float dashPosition = fract(pos.x / dashLength); + // Figure out the mask index. + float maskIndex = floor(dashPosition * maskLength); + // Test the bit mask. + float maskTest = floor(dashPattern / pow(2.0, maskIndex)); + vec4 fragColor = (mod(maskTest, 2.0) < 1.0) ? gapColor : color; + if (fragColor.a < 0.005) { // matches 0/255 and 1/255 + discard; + } + + material.emission = fragColor.rgb; + material.alpha = fragColor.a; + return material; +} \ No newline at end of file diff --git a/Source/Shaders/PointPrimitiveCollectionVS.glsl b/Source/Shaders/PointPrimitiveCollectionVS.glsl index a11ea4b95f6b..cdb0fa4faf4c 100644 --- a/Source/Shaders/PointPrimitiveCollectionVS.glsl +++ b/Source/Shaders/PointPrimitiveCollectionVS.glsl @@ -2,10 +2,10 @@ uniform float u_maxTotalPointSize; attribute vec4 positionHighAndSize; attribute vec4 positionLowAndOutline; -attribute vec4 compressedAttribute0; // color, outlineColor, pick color -attribute vec4 compressedAttribute1; // show, translucency by distance, some free space -attribute vec4 scaleByDistance; // near, nearScale, far, farScale -attribute vec2 distanceDisplayCondition; // near, far +attribute vec4 compressedAttribute0; // color, outlineColor, pick color +attribute vec4 compressedAttribute1; // show, translucency by distance, some free space +attribute vec4 scaleByDistance; // near, nearScale, far, farScale +attribute vec3 distanceDisplayConditionAndDisableDepth; // near, far, disableDepthTestDistance varying vec4 v_color; varying vec4 v_outlineColor; @@ -102,7 +102,7 @@ void main() /////////////////////////////////////////////////////////////////////////// -#if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(DISTANCE_DISPLAY_CONDITION) +#if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(DISTANCE_DISPLAY_CONDITION) || defined(DISABLE_DEPTH_DISTANCE) float lengthSq; if (czm_sceneMode == czm_sceneMode2D) { @@ -140,8 +140,8 @@ void main() #endif #ifdef DISTANCE_DISPLAY_CONDITION - float nearSq = distanceDisplayCondition.x * distanceDisplayCondition.x; - float farSq = distanceDisplayCondition.y * distanceDisplayCondition.y; + float nearSq = distanceDisplayConditionAndDisableDepth.x; + float farSq = distanceDisplayConditionAndDisableDepth.y; if (lengthSq < nearSq || lengthSq > farSq) { positionEC.xyz = vec3(0.0); } @@ -151,6 +151,25 @@ void main() gl_Position = czm_viewportOrthographic * vec4(positionWC.xy, -positionWC.z, 1.0); +#ifdef DISABLE_DEPTH_DISTANCE + float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z; + if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) + { + disableDepthTestDistance = czm_minimumDisableDepthTestDistance; + } + + if (disableDepthTestDistance != 0.0) + { + gl_Position.z = min(gl_Position.z, gl_Position.w); + + bool clipped = gl_Position.z < -gl_Position.w || gl_Position.z > gl_Position.w; + if (!clipped && (disableDepthTestDistance < 0.0 || (lengthSq > 0.0 && lengthSq < disableDepthTestDistance))) + { + gl_Position.z = -gl_Position.w; + } + } +#endif + v_color = color; v_color.a *= translucency; v_outlineColor = outlineColor; diff --git a/Source/Shaders/PolylineCommon.glsl b/Source/Shaders/PolylineCommon.glsl index 89b876d4951e..664f926e4013 100644 --- a/Source/Shaders/PolylineCommon.glsl +++ b/Source/Shaders/PolylineCommon.glsl @@ -7,13 +7,13 @@ void clipLineSegmentToNearPlane( { culledByNearPlane = false; clipped = false; - + vec3 p1ToP0 = p1 - p0; float magnitude = length(p1ToP0); vec3 direction = normalize(p1ToP0); float endPoint0Distance = -(czm_currentFrustum.x + p0.z); float denominator = -direction.z; - + if (endPoint0Distance < 0.0 && abs(denominator) < czm_epsilon7) { culledByNearPlane = true; @@ -32,30 +32,50 @@ void clipLineSegmentToNearPlane( clipped = true; } } - + positionWC = czm_eyeToWindowCoordinates(vec4(p0, 1.0)); } -vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious) { +vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle) { vec4 endPointWC, p0, p1; bool culledByNearPlane, clipped; - + vec4 positionEC = czm_modelViewRelativeToEye * position; vec4 prevEC = czm_modelViewRelativeToEye * previous; vec4 nextEC = czm_modelViewRelativeToEye * next; - + + // Compute the window coordinates of the points. + vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC); + vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC); + vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC); + +#ifdef POLYLINE_DASH + // Determine the relative screen space direction of the line. + vec2 lineDir; + if (usePrevious) { + lineDir = normalize(positionWindow.xy - previousWindow.xy); + } + else { + lineDir = normalize(nextWindow.xy - positionWindow.xy); + } + angle = atan(lineDir.x, lineDir.y) - 1.570796327; // precomputed atan(1,0) + + // Quantize the angle so it doesn't change rapidly between segments. + angle = floor(angle / czm_piOverFour + 0.5) * czm_piOverFour; +#endif + clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, p0, clipped, culledByNearPlane); clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, p1, clipped, culledByNearPlane); clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, endPointWC, clipped, culledByNearPlane); - + if (culledByNearPlane) { return vec4(0.0, 0.0, 0.0, 1.0); } - + vec2 prevWC = normalize(p0.xy - endPointWC.xy); vec2 nextWC = normalize(p1.xy - endPointWC.xy); - + float expandWidth = width * 0.5; vec2 direction; diff --git a/Source/Shaders/PolylineVS.glsl b/Source/Shaders/PolylineVS.glsl index 6b72184c19a2..67c88ab93daf 100644 --- a/Source/Shaders/PolylineVS.glsl +++ b/Source/Shaders/PolylineVS.glsl @@ -15,8 +15,9 @@ attribute vec4 texCoordExpandAndBatchIndex; varying vec2 v_st; varying float v_width; varying vec4 czm_pickColor; +varying float v_angle; -void main() +void main() { float texCoord = texCoordExpandAndBatchIndex.x; float expandDir = texCoordExpandAndBatchIndex.y; @@ -33,7 +34,7 @@ void main() } vec4 pickColor = batchTable_getPickColor(batchTableIndex); - + vec4 p, prev, next; if (czm_morphTime == 1.0) { @@ -88,10 +89,10 @@ void main() show = 0.0; } #endif - - vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev); + + vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, v_angle); gl_Position = czm_viewportOrthographic * positionWC * show; - + v_st = vec2(texCoord, clamp(expandDir, 0.0, 1.0)); v_width = width; czm_pickColor = pickColor; diff --git a/Source/Shaders/PostProcessFilters/FXAA.glsl b/Source/Shaders/PostProcessFilters/FXAA.glsl index edd2b911e4be..09067f48970e 100644 --- a/Source/Shaders/PostProcessFilters/FXAA.glsl +++ b/Source/Shaders/PostProcessFilters/FXAA.glsl @@ -1,245 +1,21 @@ -/** - * @license - * Copyright (c) 2011 NVIDIA Corporation. All rights reserved. - * - * TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED - * *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS - * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT,IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA - * OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR - * CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS - * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY - * OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, - * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - */ - -/* -FXAA_PRESET - Choose compile-in knob preset 0-5. ------------------------------------------------------------------------------- -FXAA_EDGE_THRESHOLD - The minimum amount of local contrast required - to apply algorithm. - 1.0/3.0 - too little - 1.0/4.0 - good start - 1.0/8.0 - applies to more edges - 1.0/16.0 - overkill ------------------------------------------------------------------------------- -FXAA_EDGE_THRESHOLD_MIN - Trims the algorithm from processing darks. - Perf optimization. - 1.0/32.0 - visible limit (smaller isn't visible) - 1.0/16.0 - good compromise - 1.0/12.0 - upper limit (seeing artifacts) ------------------------------------------------------------------------------- -FXAA_SEARCH_STEPS - Maximum number of search steps for end of span. ------------------------------------------------------------------------------- -FXAA_SEARCH_THRESHOLD - Controls when to stop searching. - 1.0/4.0 - seems to be the best quality wise ------------------------------------------------------------------------------- -FXAA_SUBPIX_TRIM - Controls sub-pixel aliasing removal. - 1.0/2.0 - low removal - 1.0/3.0 - medium removal - 1.0/4.0 - default removal - 1.0/8.0 - high removal - 0.0 - complete removal ------------------------------------------------------------------------------- -FXAA_SUBPIX_CAP - Insures fine detail is not completely removed. - This is important for the transition of sub-pixel detail, - like fences and wires. - 3.0/4.0 - default (medium amount of filtering) - 7.0/8.0 - high amount of filtering - 1.0 - no capping of sub-pixel aliasing removal -*/ - -#ifndef FXAA_PRESET - #define FXAA_PRESET 3 -#endif -#if (FXAA_PRESET == 3) - #define FXAA_EDGE_THRESHOLD (1.0/8.0) - #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) - #define FXAA_SEARCH_STEPS 16 - #define FXAA_SEARCH_THRESHOLD (1.0/4.0) - #define FXAA_SUBPIX_CAP (3.0/4.0) - #define FXAA_SUBPIX_TRIM (1.0/4.0) -#endif -#if (FXAA_PRESET == 4) - #define FXAA_EDGE_THRESHOLD (1.0/8.0) - #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) - #define FXAA_SEARCH_STEPS 24 - #define FXAA_SEARCH_THRESHOLD (1.0/4.0) - #define FXAA_SUBPIX_CAP (3.0/4.0) - #define FXAA_SUBPIX_TRIM (1.0/4.0) -#endif -#if (FXAA_PRESET == 5) - #define FXAA_EDGE_THRESHOLD (1.0/8.0) - #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) - #define FXAA_SEARCH_STEPS 32 - #define FXAA_SEARCH_THRESHOLD (1.0/4.0) - #define FXAA_SUBPIX_CAP (3.0/4.0) - #define FXAA_SUBPIX_TRIM (1.0/4.0) -#endif - -#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM)) - -// Return the luma, the estimation of luminance from rgb inputs. -// This approximates luma using one FMA instruction, -// skipping normalization and tossing out blue. -// FxaaLuma() will range 0.0 to 2.963210702. -float FxaaLuma(vec3 rgb) { - return rgb.y * (0.587/0.299) + rgb.x; -} - -vec3 FxaaLerp3(vec3 a, vec3 b, float amountOfA) { - return (vec3(-amountOfA) * b) + ((a * vec3(amountOfA)) + b); -} - -vec4 FxaaTexOff(sampler2D tex, vec2 pos, ivec2 off, vec2 rcpFrame) { - float x = pos.x + float(off.x) * rcpFrame.x; - float y = pos.y + float(off.y) * rcpFrame.y; - return texture2D(tex, vec2(x, y)); -} - -// pos is the output of FxaaVertexShader interpolated across screen. -// xy -> actual texture position {0.0 to 1.0} -// rcpFrame should be a uniform equal to {1.0/frameWidth, 1.0/frameHeight} -vec3 FxaaPixelShader(vec2 pos, sampler2D tex, vec2 rcpFrame) -{ - vec3 rgbN = FxaaTexOff(tex, pos.xy, ivec2( 0,-1), rcpFrame).xyz; - vec3 rgbW = FxaaTexOff(tex, pos.xy, ivec2(-1, 0), rcpFrame).xyz; - vec3 rgbM = FxaaTexOff(tex, pos.xy, ivec2( 0, 0), rcpFrame).xyz; - vec3 rgbE = FxaaTexOff(tex, pos.xy, ivec2( 1, 0), rcpFrame).xyz; - vec3 rgbS = FxaaTexOff(tex, pos.xy, ivec2( 0, 1), rcpFrame).xyz; - - float lumaN = FxaaLuma(rgbN); - float lumaW = FxaaLuma(rgbW); - float lumaM = FxaaLuma(rgbM); - float lumaE = FxaaLuma(rgbE); - float lumaS = FxaaLuma(rgbS); - float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE))); - float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE))); - - float range = rangeMax - rangeMin; - if(range < max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * FXAA_EDGE_THRESHOLD)) - { - return rgbM; - } - - vec3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS; - - float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25; - float rangeL = abs(lumaL - lumaM); - float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE; - blendL = min(FXAA_SUBPIX_CAP, blendL); - - vec3 rgbNW = FxaaTexOff(tex, pos.xy, ivec2(-1,-1), rcpFrame).xyz; - vec3 rgbNE = FxaaTexOff(tex, pos.xy, ivec2( 1,-1), rcpFrame).xyz; - vec3 rgbSW = FxaaTexOff(tex, pos.xy, ivec2(-1, 1), rcpFrame).xyz; - vec3 rgbSE = FxaaTexOff(tex, pos.xy, ivec2( 1, 1), rcpFrame).xyz; - rgbL += (rgbNW + rgbNE + rgbSW + rgbSE); - rgbL *= vec3(1.0/9.0); - - float lumaNW = FxaaLuma(rgbNW); - float lumaNE = FxaaLuma(rgbNE); - float lumaSW = FxaaLuma(rgbSW); - float lumaSE = FxaaLuma(rgbSE); - - float edgeVert = - abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) + - abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) + - abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE)); - float edgeHorz = - abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) + - abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) + - abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE)); - - bool horzSpan = edgeHorz >= edgeVert; - float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x; - - if(!horzSpan) - { - lumaN = lumaW; - lumaS = lumaE; - } - - float gradientN = abs(lumaN - lumaM); - float gradientS = abs(lumaS - lumaM); - lumaN = (lumaN + lumaM) * 0.5; - lumaS = (lumaS + lumaM) * 0.5; - - if (gradientN < gradientS) - { - lumaN = lumaS; - lumaN = lumaS; - gradientN = gradientS; - lengthSign *= -1.0; - } - - vec2 posN; - posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5); - posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0); - - gradientN *= FXAA_SEARCH_THRESHOLD; - - vec2 posP = posN; - vec2 offNP = horzSpan ? vec2(rcpFrame.x, 0.0) : vec2(0.0, rcpFrame.y); - float lumaEndN = lumaN; - float lumaEndP = lumaN; - bool doneN = false; - bool doneP = false; - posN += offNP * vec2(-1.0, -1.0); - posP += offNP * vec2( 1.0, 1.0); - - for(int i = 0; i < FXAA_SEARCH_STEPS; i++) { - if(!doneN) - { - lumaEndN = FxaaLuma(texture2D(tex, posN.xy).xyz); - } - if(!doneP) - { - lumaEndP = FxaaLuma(texture2D(tex, posP.xy).xyz); - } - - doneN = doneN || (abs(lumaEndN - lumaN) >= gradientN); - doneP = doneP || (abs(lumaEndP - lumaN) >= gradientN); - - if(doneN && doneP) - { - break; - } - if(!doneN) - { - posN -= offNP; - } - if(!doneP) - { - posP += offNP; - } - } - - float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y; - float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y; - bool directionN = dstN < dstP; - lumaEndN = directionN ? lumaEndN : lumaEndP; - - if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0)) - { - lengthSign = 0.0; - } - - - float spanLength = (dstP + dstN); - dstN = directionN ? dstN : dstP; - float subPixelOffset = (0.5 + (dstN * (-1.0/spanLength))) * lengthSign; - vec3 rgbF = texture2D(tex, vec2( - pos.x + (horzSpan ? 0.0 : subPixelOffset), - pos.y + (horzSpan ? subPixelOffset : 0.0))).xyz; - return FxaaLerp3(rgbL, rgbF, blendL); -} +varying vec2 v_textureCoordinates; uniform sampler2D u_texture; -uniform vec2 u_step; +uniform vec2 u_fxaaQualityRcpFrame; -varying vec2 v_textureCoordinates; +const float fxaaQualitySubpix = 0.5; +const float fxaaQualityEdgeThreshold = 0.125; +const float fxaaQualityEdgeThresholdMin = 0.0833; void main() { - gl_FragColor = vec4(FxaaPixelShader(v_textureCoordinates, u_texture, u_step), 1.0); + vec4 color = FxaaPixelShader( + v_textureCoordinates, + u_texture, + u_fxaaQualityRcpFrame, + fxaaQualitySubpix, + fxaaQualityEdgeThreshold, + fxaaQualityEdgeThresholdMin); + float alpha = texture2D(u_texture, v_textureCoordinates).a; + gl_FragColor = vec4(color.rgb, alpha); } diff --git a/Source/Shaders/SkyAtmosphereFS.glsl b/Source/Shaders/SkyAtmosphereFS.glsl index 9fd4000bb26e..f067c1e71797 100644 --- a/Source/Shaders/SkyAtmosphereFS.glsl +++ b/Source/Shaders/SkyAtmosphereFS.glsl @@ -2,11 +2,11 @@ * @license * Copyright (c) 2000-2005, Sean O'Neil (s_p_oneil@hotmail.com) * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, @@ -15,7 +15,7 @@ * * Neither the name of the project nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -29,10 +29,9 @@ * * Modifications made by Analytical Graphics, Inc. */ - + // Code: http://sponeil.net/ // GPU Gems 2 Article: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html - // HSV/HSB <-> RGB conversion with minimal branching: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl #ifdef COLOR_CORRECT uniform vec3 u_hsbShift; // Hue, saturation, brightness @@ -40,40 +39,21 @@ uniform vec3 u_hsbShift; // Hue, saturation, brightness const float g = -0.95; const float g2 = g * g; -const vec4 K_RGB2HSB = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); -const vec4 K_HSB2RGB = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); varying vec3 v_rayleighColor; varying vec3 v_mieColor; varying vec3 v_toCamera; varying vec3 v_positionEC; -#ifdef COLOR_CORRECT -vec3 rgb2hsb(vec3 rgbColor) -{ - vec4 p = mix(vec4(rgbColor.bg, K_RGB2HSB.wz), vec4(rgbColor.gb, K_RGB2HSB.xy), step(rgbColor.b, rgbColor.g)); - vec4 q = mix(vec4(p.xyw, rgbColor.r), vec4(rgbColor.r, p.yzx), step(p.x, rgbColor.r)); - - float d = q.x - min(q.w, q.y); - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + czm_epsilon7)), d / (q.x + czm_epsilon7), q.x); -} - -vec3 hsb2rgb(vec3 hsbColor) -{ - vec3 p = abs(fract(hsbColor.xxx + K_HSB2RGB.xyz) * 6.0 - K_HSB2RGB.www); - return hsbColor.z * mix(K_HSB2RGB.xxx, clamp(p - K_HSB2RGB.xxx, 0.0, 1.0), hsbColor.y); -} -#endif - void main (void) { // Extra normalize added for Android float cosAngle = dot(czm_sunDirectionWC, normalize(v_toCamera)) / length(v_toCamera); float rayleighPhase = 0.75 * (1.0 + cosAngle * cosAngle); float miePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cosAngle * cosAngle) / pow(1.0 + g2 - 2.0 * g * cosAngle, 1.5); - + const float exposure = 2.0; - + vec3 rgb = rayleighPhase * v_rayleighColor + miePhase * v_mieColor; rgb = vec3(1.0) - exp(-exposure * rgb); // Compute luminance before color correction to avoid strangely gray night skies @@ -81,13 +61,13 @@ void main (void) #ifdef COLOR_CORRECT // Convert rgb color to hsb - vec3 hsb = rgb2hsb(rgb); + vec3 hsb = czm_RGBToHSB(rgb); // Perform hsb shift hsb.x += u_hsbShift.x; // hue hsb.y = clamp(hsb.y + u_hsbShift.y, 0.0, 1.0); // saturation hsb.z = hsb.z > czm_epsilon7 ? hsb.z + u_hsbShift.z : 0.0; // brightness // Convert shifted hsb back to rgb - rgb = hsb2rgb(hsb); + rgb = czm_HSBToRGB(hsb); // Check if correction decreased the luminance to 0 l = min(l, czm_luminance(rgb)); diff --git a/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js b/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js index ee0389747212..076ed4798264 100644 --- a/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js +++ b/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js @@ -202,7 +202,7 @@ define([ var techniqueParameters = { // Add matrices modelViewMatrix: { - semantic: 'MODELVIEW', + semantic: options.useCesiumRTCMatrixInShaders ? 'CESIUM_RTC_MODELVIEW' : 'MODELVIEW', type: WebGLConstants.FLOAT_MAT4 }, projectionMatrix: { @@ -381,6 +381,15 @@ define([ vertexShader += 'attribute ' + attributeType + ' a_weight;\n'; } + if (options.addBatchIdToGeneratedShaders) { + techniqueAttributes.a_batchId = 'batchId'; + techniqueParameters.batchId = { + semantic: '_BATCHID', + type: WebGLConstants.FLOAT + }; + vertexShader += 'attribute float a_batchId;\n'; + } + var hasSpecular = hasNormals && ((lightingModel === 'BLINN') || (lightingModel === 'PHONG')) && defined(techniqueParameters.specular) && defined(techniqueParameters.shininess); @@ -882,6 +891,8 @@ define([ return undefined; } + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var hasExtension = false; var extensionsRequired = gltf.extensionsRequired; var extensionsUsed = gltf.extensionsUsed; diff --git a/Source/ThirdParty/Shaders/FXAA3_11.glsl b/Source/ThirdParty/Shaders/FXAA3_11.glsl new file mode 100644 index 000000000000..3499e14f1cfa --- /dev/null +++ b/Source/ThirdParty/Shaders/FXAA3_11.glsl @@ -0,0 +1,648 @@ +/** + * @license + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// NVIDIA GameWorks Graphics Samples GitHub link: https://github.com/NVIDIAGameWorks/GraphicsSamples +// Original FXAA 3.11 shader link: https://github.com/NVIDIAGameWorks/GraphicsSamples/blob/master/samples/es3-kepler/FXAA/FXAA3_11.h + +// Steps used to integrate into Cesium: +// * The following defines are set: +// #define FXAA_PC 1 +// #define FXAA_WEBGL_1 1 +// #define FXAA_GREEN_AS_LUMA 1 +// #define FXAA_EARLY_EXIT 1 +// #define FXAA_GLSL_120 1 +// * All other preprocessor directives besides the FXAA_QUALITY__P* directives were removed. +// * Double underscores are invalid for preprocessor directives so replace them with a single underscore. Replace +// /FXAA_QUALITY__P(.*)/g with /FXAA_QUALITY__P$1/. +// * There are no implicit conversions from ivec* to vec* so replace: +// #define FxaaInt2 ivec2 +// with +// #define FxaaInt2 vec2 +// * The texture2DLod function is only available in vertex shaders so replace: +// #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) +// #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) +// with +// #define FxaaTexTop(t, p) texture2D(t, p) +// #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r)) +// * FXAA_QUALITY_PRESET is prepended in the javascript code. We may want to expose that setting in the future. +// * The following parameters to FxaaPixelShader are unused and can be removed: +// fxaaConsolePosPos +// fxaaConsoleRcpFrameOpt +// fxaaConsoleRcpFrameOpt2 +// fxaaConsole360RcpFrameOpt2 +// fxaaConsoleEdgeSharpness +// fxaaConsoleEdgeThreshold +// fxaaConsoleEdgeThresholdMi +// fxaaConsole360ConstDir + +// +// Choose the quality preset. +// This needs to be compiled into the shader as it effects code. +// Best option to include multiple presets is to +// in each shader define the preset, then include this file. +// +// OPTIONS +// ----------------------------------------------------------------------- +// 10 to 15 - default medium dither (10=fastest, 15=highest quality) +// 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) +// 39 - no dither, very expensive +// +// NOTES +// ----------------------------------------------------------------------- +// 12 = slightly faster then FXAA 3.9 and higher edge quality (default) +// 13 = about same speed as FXAA 3.9 and better than 12 +// 23 = closest to FXAA 3.9 visually and performance wise +// _ = the lowest digit is directly related to performance +// _ = the highest digit is directly related to style +// +//#define FXAA_QUALITY_PRESET 12 + + +#if (FXAA_QUALITY_PRESET == 10) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 3.0 + #define FXAA_QUALITY_P2 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 11) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 3.0 + #define FXAA_QUALITY_P3 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 12) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 4.0 + #define FXAA_QUALITY_P4 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 13) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 4.0 + #define FXAA_QUALITY_P5 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 14) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 4.0 + #define FXAA_QUALITY_P6 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 15) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 12.0 +#endif +#if (FXAA_QUALITY_PRESET == 20) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 2.0 + #define FXAA_QUALITY_P2 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 21) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 22) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 23) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 24) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 3.0 + #define FXAA_QUALITY_P6 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 25) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 26) + #define FXAA_QUALITY_PS 9 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 4.0 + #define FXAA_QUALITY_P8 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 27) + #define FXAA_QUALITY_PS 10 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 4.0 + #define FXAA_QUALITY_P9 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 28) + #define FXAA_QUALITY_PS 11 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 4.0 + #define FXAA_QUALITY_P10 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 29) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif +#if (FXAA_QUALITY_PRESET == 39) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.0 + #define FXAA_QUALITY_P2 1.0 + #define FXAA_QUALITY_P3 1.0 + #define FXAA_QUALITY_P4 1.0 + #define FXAA_QUALITY_P5 1.5 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif + +#define FxaaBool bool +#define FxaaFloat float +#define FxaaFloat2 vec2 +#define FxaaFloat3 vec3 +#define FxaaFloat4 vec4 +#define FxaaHalf float +#define FxaaHalf2 vec2 +#define FxaaHalf3 vec3 +#define FxaaHalf4 vec4 +#define FxaaInt2 vec2 +#define FxaaTex sampler2D + +#define FxaaSat(x) clamp(x, 0.0, 1.0) +#define FxaaTexTop(t, p) texture2D(t, p) +#define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r)) + +FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } + +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #define lumaM rgbyM.y + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY_PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); +} diff --git a/Source/ThirdParty/Shaders/FXAA3_11.js b/Source/ThirdParty/Shaders/FXAA3_11.js deleted file mode 100644 index 2df97cb85114..000000000000 --- a/Source/ThirdParty/Shaders/FXAA3_11.js +++ /dev/null @@ -1,682 +0,0 @@ -/** - * @license - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of NVIDIA CORPORATION nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -//This file is automatically rebuilt by the Cesium build process. -/*global define*/ -define(function() { - 'use strict'; - return "/**\n\ - * @license\n\ - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.\n\ - *\n\ - * Redistribution and use in source and binary forms, with or without\n\ - * modification, are permitted provided that the following conditions\n\ - * are met:\n\ - * * Redistributions of source code must retain the above copyright\n\ - * notice, this list of conditions and the following disclaimer.\n\ - * * Redistributions in binary form must reproduce the above copyright\n\ - * notice, this list of conditions and the following disclaimer in the\n\ - * documentation and/or other materials provided with the distribution.\n\ - * * Neither the name of NVIDIA CORPORATION nor the names of its\n\ - * contributors may be used to endorse or promote products derived\n\ - * from this software without specific prior written permission.\n\ - *\n\ - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n\ - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\ - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n\ - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n\ - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n\ - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n\ - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n\ - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n\ - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n\ - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n\ - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ - */\n\ -\n\ -// NVIDIA GameWorks Graphics Samples GitHub link: https://github.com/NVIDIAGameWorks/GraphicsSamples\n\ -// Original FXAA 3.11 shader link: https://github.com/NVIDIAGameWorks/GraphicsSamples/blob/master/samples/es3-kepler/FXAA/FXAA3_11.h\n\ -\n\ -// Steps used to integrate into Cesium:\n\ -// * The following defines are set:\n\ -// #define FXAA_PC 1\n\ -// #define FXAA_WEBGL_1 1\n\ -// #define FXAA_GREEN_AS_LUMA 1\n\ -// #define FXAA_EARLY_EXIT 1\n\ -// #define FXAA_GLSL_120 1\n\ -// * All other preprocessor directives besides the FXAA_QUALITY__P* directives were removed.\n\ -// * Double underscores are invalid for preprocessor directives so replace them with a single underscore. Replace\n\ -// /FXAA_QUALITY__P(.*)/g with /FXAA_QUALITY__P$1/.\n\ -// * There are no implicit conversions from ivec* to vec* so replace:\n\ -// #define FxaaInt2 ivec2\n\ -// with\n\ -// #define FxaaInt2 vec2\n\ -// * The texture2DLod function is only available in vertex shaders so replace:\n\ -// #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0)\n\ -// #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0)\n\ -// with\n\ -// #define FxaaTexTop(t, p) texture2D(t, p)\n\ -// #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r))\n\ -// * FXAA_QUALITY_PRESET is prepended in the javascript code. We may want to expose that setting in the future.\n\ -// * The following parameters to FxaaPixelShader are unused and can be removed:\n\ -// fxaaConsolePosPos\n\ -// fxaaConsoleRcpFrameOpt\n\ -// fxaaConsoleRcpFrameOpt2\n\ -// fxaaConsole360RcpFrameOpt2\n\ -// fxaaConsoleEdgeSharpness\n\ -// fxaaConsoleEdgeThreshold\n\ -// fxaaConsoleEdgeThresholdMi\n\ -// fxaaConsole360ConstDir\n\ -\n\ -//\n\ -// Choose the quality preset.\n\ -// This needs to be compiled into the shader as it effects code.\n\ -// Best option to include multiple presets is to\n\ -// in each shader define the preset, then include this file.\n\ -//\n\ -// OPTIONS\n\ -// -----------------------------------------------------------------------\n\ -// 10 to 15 - default medium dither (10=fastest, 15=highest quality)\n\ -// 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality)\n\ -// 39 - no dither, very expensive\n\ -//\n\ -// NOTES\n\ -// -----------------------------------------------------------------------\n\ -// 12 = slightly faster then FXAA 3.9 and higher edge quality (default)\n\ -// 13 = about same speed as FXAA 3.9 and better than 12\n\ -// 23 = closest to FXAA 3.9 visually and performance wise\n\ -// _ = the lowest digit is directly related to performance\n\ -// _ = the highest digit is directly related to style\n\ -//\n\ -//#define FXAA_QUALITY_PRESET 12\n\ -\n\ -\n\ -#if (FXAA_QUALITY_PRESET == 10)\n\ - #define FXAA_QUALITY_PS 3\n\ - #define FXAA_QUALITY_P0 1.5\n\ - #define FXAA_QUALITY_P1 3.0\n\ - #define FXAA_QUALITY_P2 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 11)\n\ - #define FXAA_QUALITY_PS 4\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 3.0\n\ - #define FXAA_QUALITY_P3 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 12)\n\ - #define FXAA_QUALITY_PS 5\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 4.0\n\ - #define FXAA_QUALITY_P4 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 13)\n\ - #define FXAA_QUALITY_PS 6\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 4.0\n\ - #define FXAA_QUALITY_P5 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 14)\n\ - #define FXAA_QUALITY_PS 7\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 4.0\n\ - #define FXAA_QUALITY_P6 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 15)\n\ - #define FXAA_QUALITY_PS 8\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 4.0\n\ - #define FXAA_QUALITY_P7 12.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 20)\n\ - #define FXAA_QUALITY_PS 3\n\ - #define FXAA_QUALITY_P0 1.5\n\ - #define FXAA_QUALITY_P1 2.0\n\ - #define FXAA_QUALITY_P2 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 21)\n\ - #define FXAA_QUALITY_PS 4\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 22)\n\ - #define FXAA_QUALITY_PS 5\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 23)\n\ - #define FXAA_QUALITY_PS 6\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 24)\n\ - #define FXAA_QUALITY_PS 7\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 3.0\n\ - #define FXAA_QUALITY_P6 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 25)\n\ - #define FXAA_QUALITY_PS 8\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 4.0\n\ - #define FXAA_QUALITY_P7 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 26)\n\ - #define FXAA_QUALITY_PS 9\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 2.0\n\ - #define FXAA_QUALITY_P7 4.0\n\ - #define FXAA_QUALITY_P8 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 27)\n\ - #define FXAA_QUALITY_PS 10\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 2.0\n\ - #define FXAA_QUALITY_P7 2.0\n\ - #define FXAA_QUALITY_P8 4.0\n\ - #define FXAA_QUALITY_P9 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 28)\n\ - #define FXAA_QUALITY_PS 11\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 2.0\n\ - #define FXAA_QUALITY_P7 2.0\n\ - #define FXAA_QUALITY_P8 2.0\n\ - #define FXAA_QUALITY_P9 4.0\n\ - #define FXAA_QUALITY_P10 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 29)\n\ - #define FXAA_QUALITY_PS 12\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.5\n\ - #define FXAA_QUALITY_P2 2.0\n\ - #define FXAA_QUALITY_P3 2.0\n\ - #define FXAA_QUALITY_P4 2.0\n\ - #define FXAA_QUALITY_P5 2.0\n\ - #define FXAA_QUALITY_P6 2.0\n\ - #define FXAA_QUALITY_P7 2.0\n\ - #define FXAA_QUALITY_P8 2.0\n\ - #define FXAA_QUALITY_P9 2.0\n\ - #define FXAA_QUALITY_P10 4.0\n\ - #define FXAA_QUALITY_P11 8.0\n\ -#endif\n\ -#if (FXAA_QUALITY_PRESET == 39)\n\ - #define FXAA_QUALITY_PS 12\n\ - #define FXAA_QUALITY_P0 1.0\n\ - #define FXAA_QUALITY_P1 1.0\n\ - #define FXAA_QUALITY_P2 1.0\n\ - #define FXAA_QUALITY_P3 1.0\n\ - #define FXAA_QUALITY_P4 1.0\n\ - #define FXAA_QUALITY_P5 1.5\n\ - #define FXAA_QUALITY_P6 2.0\n\ - #define FXAA_QUALITY_P7 2.0\n\ - #define FXAA_QUALITY_P8 2.0\n\ - #define FXAA_QUALITY_P9 2.0\n\ - #define FXAA_QUALITY_P10 4.0\n\ - #define FXAA_QUALITY_P11 8.0\n\ -#endif\n\ -\n\ -#define FxaaBool bool\n\ -#define FxaaFloat float\n\ -#define FxaaFloat2 vec2\n\ -#define FxaaFloat3 vec3\n\ -#define FxaaFloat4 vec4\n\ -#define FxaaHalf float\n\ -#define FxaaHalf2 vec2\n\ -#define FxaaHalf3 vec3\n\ -#define FxaaHalf4 vec4\n\ -#define FxaaInt2 vec2\n\ -#define FxaaTex sampler2D\n\ -\n\ -#define FxaaSat(x) clamp(x, 0.0, 1.0)\n\ -#define FxaaTexTop(t, p) texture2D(t, p)\n\ -#define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r))\n\ -\n\ -FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; }\n\ -\n\ -FxaaFloat4 FxaaPixelShader(\n\ - //\n\ - // Use noperspective interpolation here (turn off perspective interpolation).\n\ - // {xy} = center of pixel\n\ - FxaaFloat2 pos,\n\ - //\n\ - // Input color texture.\n\ - // {rgb_} = color in linear or perceptual color space\n\ - // if (FXAA_GREEN_AS_LUMA == 0)\n\ - // {___a} = luma in perceptual color space (not linear)\n\ - FxaaTex tex,\n\ - //\n\ - // Only used on FXAA Quality.\n\ - // This must be from a constant/uniform.\n\ - // {x_} = 1.0/screenWidthInPixels\n\ - // {_y} = 1.0/screenHeightInPixels\n\ - FxaaFloat2 fxaaQualityRcpFrame,\n\ - //\n\ - // Only used on FXAA Quality.\n\ - // This used to be the FXAA_QUALITY_SUBPIX define.\n\ - // It is here now to allow easier tuning.\n\ - // Choose the amount of sub-pixel aliasing removal.\n\ - // This can effect sharpness.\n\ - // 1.00 - upper limit (softer)\n\ - // 0.75 - default amount of filtering\n\ - // 0.50 - lower limit (sharper, less sub-pixel aliasing removal)\n\ - // 0.25 - almost off\n\ - // 0.00 - completely off\n\ - FxaaFloat fxaaQualitySubpix,\n\ - //\n\ - // Only used on FXAA Quality.\n\ - // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define.\n\ - // It is here now to allow easier tuning.\n\ - // The minimum amount of local contrast required to apply algorithm.\n\ - // 0.333 - too little (faster)\n\ - // 0.250 - low quality\n\ - // 0.166 - default\n\ - // 0.125 - high quality\n\ - // 0.063 - overkill (slower)\n\ - FxaaFloat fxaaQualityEdgeThreshold,\n\ - //\n\ - // Only used on FXAA Quality.\n\ - // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define.\n\ - // It is here now to allow easier tuning.\n\ - // Trims the algorithm from processing darks.\n\ - // 0.0833 - upper limit (default, the start of visible unfiltered edges)\n\ - // 0.0625 - high quality (faster)\n\ - // 0.0312 - visible limit (slower)\n\ - // Special notes when using FXAA_GREEN_AS_LUMA,\n\ - // Likely want to set this to zero.\n\ - // As colors that are mostly not-green\n\ - // will appear very dark in the green channel!\n\ - // Tune by looking at mostly non-green content,\n\ - // then start at zero and increase until aliasing is a problem.\n\ - FxaaFloat fxaaQualityEdgeThresholdMin\n\ -) {\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat2 posM;\n\ - posM.x = pos.x;\n\ - posM.y = pos.y;\n\ - FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);\n\ - #define lumaM rgbyM.y\n\ - FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy));\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat maxSM = max(lumaS, lumaM);\n\ - FxaaFloat minSM = min(lumaS, lumaM);\n\ - FxaaFloat maxESM = max(lumaE, maxSM);\n\ - FxaaFloat minESM = min(lumaE, minSM);\n\ - FxaaFloat maxWN = max(lumaN, lumaW);\n\ - FxaaFloat minWN = min(lumaN, lumaW);\n\ - FxaaFloat rangeMax = max(maxWN, maxESM);\n\ - FxaaFloat rangeMin = min(minWN, minESM);\n\ - FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;\n\ - FxaaFloat range = rangeMax - rangeMin;\n\ - FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);\n\ - FxaaBool earlyExit = range < rangeMaxClamped;\n\ -/*--------------------------------------------------------------------------*/\n\ - if(earlyExit)\n\ - return rgbyM;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy));\n\ - FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat lumaNS = lumaN + lumaS;\n\ - FxaaFloat lumaWE = lumaW + lumaE;\n\ - FxaaFloat subpixRcpRange = 1.0/range;\n\ - FxaaFloat subpixNSWE = lumaNS + lumaWE;\n\ - FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS;\n\ - FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat lumaNESE = lumaNE + lumaSE;\n\ - FxaaFloat lumaNWNE = lumaNW + lumaNE;\n\ - FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE;\n\ - FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat lumaNWSW = lumaNW + lumaSW;\n\ - FxaaFloat lumaSWSE = lumaSW + lumaSE;\n\ - FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);\n\ - FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);\n\ - FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;\n\ - FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE;\n\ - FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4;\n\ - FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE;\n\ - FxaaFloat lengthSign = fxaaQualityRcpFrame.x;\n\ - FxaaBool horzSpan = edgeHorz >= edgeVert;\n\ - FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;\n\ -/*--------------------------------------------------------------------------*/\n\ - if(!horzSpan) lumaN = lumaW;\n\ - if(!horzSpan) lumaS = lumaE;\n\ - if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;\n\ - FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat gradientN = lumaN - lumaM;\n\ - FxaaFloat gradientS = lumaS - lumaM;\n\ - FxaaFloat lumaNN = lumaN + lumaM;\n\ - FxaaFloat lumaSS = lumaS + lumaM;\n\ - FxaaBool pairN = abs(gradientN) >= abs(gradientS);\n\ - FxaaFloat gradient = max(abs(gradientN), abs(gradientS));\n\ - if(pairN) lengthSign = -lengthSign;\n\ - FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat2 posB;\n\ - posB.x = posM.x;\n\ - posB.y = posM.y;\n\ - FxaaFloat2 offNP;\n\ - offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;\n\ - offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;\n\ - if(!horzSpan) posB.x += lengthSign * 0.5;\n\ - if( horzSpan) posB.y += lengthSign * 0.5;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat2 posN;\n\ - posN.x = posB.x - offNP.x * FXAA_QUALITY_P0;\n\ - posN.y = posB.y - offNP.y * FXAA_QUALITY_P0;\n\ - FxaaFloat2 posP;\n\ - posP.x = posB.x + offNP.x * FXAA_QUALITY_P0;\n\ - posP.y = posB.y + offNP.y * FXAA_QUALITY_P0;\n\ - FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0;\n\ - FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));\n\ - FxaaFloat subpixE = subpixC * subpixC;\n\ - FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));\n\ -/*--------------------------------------------------------------------------*/\n\ - if(!pairN) lumaNN = lumaSS;\n\ - FxaaFloat gradientScaled = gradient * 1.0/4.0;\n\ - FxaaFloat lumaMM = lumaM - lumaNN * 0.5;\n\ - FxaaFloat subpixF = subpixD * subpixE;\n\ - FxaaBool lumaMLTZero = lumaMM < 0.0;\n\ -/*--------------------------------------------------------------------------*/\n\ - lumaEndN -= lumaNN * 0.5;\n\ - lumaEndP -= lumaNN * 0.5;\n\ - FxaaBool doneN = abs(lumaEndN) >= gradientScaled;\n\ - FxaaBool doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1;\n\ - FxaaBool doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1;\n\ -/*--------------------------------------------------------------------------*/\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 3)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 4)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 5)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 6)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 7)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 8)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 9)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 10)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 11)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11;\n\ -/*--------------------------------------------------------------------------*/\n\ - #if (FXAA_QUALITY_PS > 12)\n\ - if(doneNP) {\n\ - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));\n\ - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));\n\ - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n\ - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n\ - doneN = abs(lumaEndN) >= gradientScaled;\n\ - doneP = abs(lumaEndP) >= gradientScaled;\n\ - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12;\n\ - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12;\n\ - doneNP = (!doneN) || (!doneP);\n\ - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12;\n\ - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12;\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ - #endif\n\ -/*--------------------------------------------------------------------------*/\n\ - }\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat dstN = posM.x - posN.x;\n\ - FxaaFloat dstP = posP.x - posM.x;\n\ - if(!horzSpan) dstN = posM.y - posN.y;\n\ - if(!horzSpan) dstP = posP.y - posM.y;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;\n\ - FxaaFloat spanLength = (dstP + dstN);\n\ - FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;\n\ - FxaaFloat spanLengthRcp = 1.0/spanLength;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaBool directionN = dstN < dstP;\n\ - FxaaFloat dst = min(dstN, dstP);\n\ - FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP;\n\ - FxaaFloat subpixG = subpixF * subpixF;\n\ - FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5;\n\ - FxaaFloat subpixH = subpixG * fxaaQualitySubpix;\n\ -/*--------------------------------------------------------------------------*/\n\ - FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0;\n\ - FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH);\n\ - if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;\n\ - if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;\n\ - return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM);\n\ -}\n\ -"; -}); \ No newline at end of file diff --git a/Source/ThirdParty/google-earth-dbroot-parser.js b/Source/ThirdParty/google-earth-dbroot-parser.js new file mode 100644 index 000000000000..4354c7c45c39 --- /dev/null +++ b/Source/ThirdParty/google-earth-dbroot-parser.js @@ -0,0 +1,8151 @@ +/*global define*/ +define([ + './protobuf-minimal' +], function( + $protobuf) { + /* jshint curly: false, sub: true, newcap: false, shadow: true, unused: false*/ + 'use strict'; + + // + // Creates a parser for a dbroot protocol buffer + // Below code is generated using protobufjs with the following command + // + // ./pbjs --no-encode --no-create --no-comments --no-delimited -w amd -t static dbroot_v2.proto + // + // .proto file can be found here: https://github.com/google/earthenterprise/blob/master/earth_enterprise/src/keyhole/proto/dbroot/dbroot_v2.proto + + var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + + var $lazyTypes = []; + + var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + + $root.keyhole = (function() { + + var keyhole = {}; + + keyhole.dbroot = (function() { + + var dbroot = {}; + + dbroot.StringEntryProto = (function() { + + function StringEntryProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + StringEntryProto.prototype.stringId = 0; + StringEntryProto.prototype.stringValue = ""; + + StringEntryProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.StringEntryProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.stringId = reader.fixed32(); + break; + case 2: + message.stringValue = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + StringEntryProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.stringId)) + return "stringId: integer expected"; + if (!$util.isString(message.stringValue)) + return "stringValue: string expected"; + return null; + }; + + StringEntryProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.StringEntryProto) + return object; + var message = new $root.keyhole.dbroot.StringEntryProto(); + if (object.stringId !== undefined && object.stringId !== null) + message.stringId = object.stringId >>> 0; + if (object.stringValue !== undefined && object.stringValue !== null) + message.stringValue = String(object.stringValue); + return message; + }; + + StringEntryProto.from = StringEntryProto.fromObject; + + StringEntryProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.stringId = 0; + object.stringValue = ""; + } + if (message.stringId !== undefined && message.stringId !== null && message.hasOwnProperty("stringId")) + object.stringId = message.stringId; + if (message.stringValue !== undefined && message.stringValue !== null && message.hasOwnProperty("stringValue")) + object.stringValue = message.stringValue; + return object; + }; + + StringEntryProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + StringEntryProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StringEntryProto; + })(); + + dbroot.StringIdOrValueProto = (function() { + + function StringIdOrValueProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + StringIdOrValueProto.prototype.stringId = 0; + StringIdOrValueProto.prototype.value = ""; + + StringIdOrValueProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.StringIdOrValueProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.stringId = reader.fixed32(); + break; + case 2: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + StringIdOrValueProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.stringId !== undefined) + if (!$util.isInteger(message.stringId)) + return "stringId: integer expected"; + if (message.value !== undefined) + if (!$util.isString(message.value)) + return "value: string expected"; + return null; + }; + + StringIdOrValueProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.StringIdOrValueProto) + return object; + var message = new $root.keyhole.dbroot.StringIdOrValueProto(); + if (object.stringId !== undefined && object.stringId !== null) + message.stringId = object.stringId >>> 0; + if (object.value !== undefined && object.value !== null) + message.value = String(object.value); + return message; + }; + + StringIdOrValueProto.from = StringIdOrValueProto.fromObject; + + StringIdOrValueProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.stringId = 0; + object.value = ""; + } + if (message.stringId !== undefined && message.stringId !== null && message.hasOwnProperty("stringId")) + object.stringId = message.stringId; + if (message.value !== undefined && message.value !== null && message.hasOwnProperty("value")) + object.value = message.value; + return object; + }; + + StringIdOrValueProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + StringIdOrValueProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StringIdOrValueProto; + })(); + + dbroot.PlanetModelProto = (function() { + + function PlanetModelProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + PlanetModelProto.prototype.radius = 6378.137; + PlanetModelProto.prototype.flattening = 0.00335281066474748; + PlanetModelProto.prototype.elevationBias = 0; + PlanetModelProto.prototype.negativeAltitudeExponentBias = 0; + PlanetModelProto.prototype.compressedNegativeAltitudeThreshold = 0; + + PlanetModelProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.PlanetModelProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.radius = reader.double(); + break; + case 2: + message.flattening = reader.double(); + break; + case 4: + message.elevationBias = reader.double(); + break; + case 5: + message.negativeAltitudeExponentBias = reader.int32(); + break; + case 6: + message.compressedNegativeAltitudeThreshold = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + PlanetModelProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.radius !== undefined) + if (typeof message.radius !== "number") + return "radius: number expected"; + if (message.flattening !== undefined) + if (typeof message.flattening !== "number") + return "flattening: number expected"; + if (message.elevationBias !== undefined) + if (typeof message.elevationBias !== "number") + return "elevationBias: number expected"; + if (message.negativeAltitudeExponentBias !== undefined) + if (!$util.isInteger(message.negativeAltitudeExponentBias)) + return "negativeAltitudeExponentBias: integer expected"; + if (message.compressedNegativeAltitudeThreshold !== undefined) + if (typeof message.compressedNegativeAltitudeThreshold !== "number") + return "compressedNegativeAltitudeThreshold: number expected"; + return null; + }; + + PlanetModelProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.PlanetModelProto) + return object; + var message = new $root.keyhole.dbroot.PlanetModelProto(); + if (object.radius !== undefined && object.radius !== null) + message.radius = Number(object.radius); + if (object.flattening !== undefined && object.flattening !== null) + message.flattening = Number(object.flattening); + if (object.elevationBias !== undefined && object.elevationBias !== null) + message.elevationBias = Number(object.elevationBias); + if (object.negativeAltitudeExponentBias !== undefined && object.negativeAltitudeExponentBias !== null) + message.negativeAltitudeExponentBias = object.negativeAltitudeExponentBias | 0; + if (object.compressedNegativeAltitudeThreshold !== undefined && object.compressedNegativeAltitudeThreshold !== null) + message.compressedNegativeAltitudeThreshold = Number(object.compressedNegativeAltitudeThreshold); + return message; + }; + + PlanetModelProto.from = PlanetModelProto.fromObject; + + PlanetModelProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.radius = 6378.137; + object.flattening = 0.00335281066474748; + object.elevationBias = 0; + object.negativeAltitudeExponentBias = 0; + object.compressedNegativeAltitudeThreshold = 0; + } + if (message.radius !== undefined && message.radius !== null && message.hasOwnProperty("radius")) + object.radius = message.radius; + if (message.flattening !== undefined && message.flattening !== null && message.hasOwnProperty("flattening")) + object.flattening = message.flattening; + if (message.elevationBias !== undefined && message.elevationBias !== null && message.hasOwnProperty("elevationBias")) + object.elevationBias = message.elevationBias; + if (message.negativeAltitudeExponentBias !== undefined && message.negativeAltitudeExponentBias !== null && message.hasOwnProperty("negativeAltitudeExponentBias")) + object.negativeAltitudeExponentBias = message.negativeAltitudeExponentBias; + if (message.compressedNegativeAltitudeThreshold !== undefined && message.compressedNegativeAltitudeThreshold !== null && message.hasOwnProperty("compressedNegativeAltitudeThreshold")) + object.compressedNegativeAltitudeThreshold = message.compressedNegativeAltitudeThreshold; + return object; + }; + + PlanetModelProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + PlanetModelProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return PlanetModelProto; + })(); + + dbroot.ProviderInfoProto = (function() { + + function ProviderInfoProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + ProviderInfoProto.prototype.providerId = 0; + ProviderInfoProto.prototype.copyrightString = null; + ProviderInfoProto.prototype.verticalPixelOffset = -1; + + var $types = { + 1 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + ProviderInfoProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ProviderInfoProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.providerId = reader.int32(); + break; + case 2: + message.copyrightString = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.verticalPixelOffset = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + ProviderInfoProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.providerId)) + return "providerId: integer expected"; + if (message.copyrightString !== undefined && message.copyrightString !== null) { + var error = $types[1].verify(message.copyrightString); + if (error) + return "copyrightString." + error; + } + if (message.verticalPixelOffset !== undefined) + if (!$util.isInteger(message.verticalPixelOffset)) + return "verticalPixelOffset: integer expected"; + return null; + }; + + ProviderInfoProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ProviderInfoProto) + return object; + var message = new $root.keyhole.dbroot.ProviderInfoProto(); + if (object.providerId !== undefined && object.providerId !== null) + message.providerId = object.providerId | 0; + if (object.copyrightString !== undefined && object.copyrightString !== null) { + if (typeof object.copyrightString !== "object") + throw TypeError(".keyhole.dbroot.ProviderInfoProto.copyrightString: object expected"); + message.copyrightString = $types[1].fromObject(object.copyrightString); + } + if (object.verticalPixelOffset !== undefined && object.verticalPixelOffset !== null) + message.verticalPixelOffset = object.verticalPixelOffset | 0; + return message; + }; + + ProviderInfoProto.from = ProviderInfoProto.fromObject; + + ProviderInfoProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.providerId = 0; + object.copyrightString = null; + object.verticalPixelOffset = -1; + } + if (message.providerId !== undefined && message.providerId !== null && message.hasOwnProperty("providerId")) + object.providerId = message.providerId; + if (message.copyrightString !== undefined && message.copyrightString !== null && message.hasOwnProperty("copyrightString")) + object.copyrightString = $types[1].toObject(message.copyrightString, options); + if (message.verticalPixelOffset !== undefined && message.verticalPixelOffset !== null && message.hasOwnProperty("verticalPixelOffset")) + object.verticalPixelOffset = message.verticalPixelOffset; + return object; + }; + + ProviderInfoProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + ProviderInfoProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ProviderInfoProto; + })(); + + dbroot.PopUpProto = (function() { + + function PopUpProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + PopUpProto.prototype.isBalloonStyle = false; + PopUpProto.prototype.text = null; + PopUpProto.prototype.backgroundColorAbgr = 4294967295; + PopUpProto.prototype.textColorAbgr = 4278190080; + + var $types = { + 1 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + PopUpProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.PopUpProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.isBalloonStyle = reader.bool(); + break; + case 2: + message.text = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.backgroundColorAbgr = reader.fixed32(); + break; + case 4: + message.textColorAbgr = reader.fixed32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + PopUpProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.isBalloonStyle !== undefined) + if (typeof message.isBalloonStyle !== "boolean") + return "isBalloonStyle: boolean expected"; + if (message.text !== undefined && message.text !== null) { + var error = $types[1].verify(message.text); + if (error) + return "text." + error; + } + if (message.backgroundColorAbgr !== undefined) + if (!$util.isInteger(message.backgroundColorAbgr)) + return "backgroundColorAbgr: integer expected"; + if (message.textColorAbgr !== undefined) + if (!$util.isInteger(message.textColorAbgr)) + return "textColorAbgr: integer expected"; + return null; + }; + + PopUpProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.PopUpProto) + return object; + var message = new $root.keyhole.dbroot.PopUpProto(); + if (object.isBalloonStyle !== undefined && object.isBalloonStyle !== null) + message.isBalloonStyle = Boolean(object.isBalloonStyle); + if (object.text !== undefined && object.text !== null) { + if (typeof object.text !== "object") + throw TypeError(".keyhole.dbroot.PopUpProto.text: object expected"); + message.text = $types[1].fromObject(object.text); + } + if (object.backgroundColorAbgr !== undefined && object.backgroundColorAbgr !== null) + message.backgroundColorAbgr = object.backgroundColorAbgr >>> 0; + if (object.textColorAbgr !== undefined && object.textColorAbgr !== null) + message.textColorAbgr = object.textColorAbgr >>> 0; + return message; + }; + + PopUpProto.from = PopUpProto.fromObject; + + PopUpProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.isBalloonStyle = false; + object.text = null; + object.backgroundColorAbgr = 4294967295; + object.textColorAbgr = 4278190080; + } + if (message.isBalloonStyle !== undefined && message.isBalloonStyle !== null && message.hasOwnProperty("isBalloonStyle")) + object.isBalloonStyle = message.isBalloonStyle; + if (message.text !== undefined && message.text !== null && message.hasOwnProperty("text")) + object.text = $types[1].toObject(message.text, options); + if (message.backgroundColorAbgr !== undefined && message.backgroundColorAbgr !== null && message.hasOwnProperty("backgroundColorAbgr")) + object.backgroundColorAbgr = message.backgroundColorAbgr; + if (message.textColorAbgr !== undefined && message.textColorAbgr !== null && message.hasOwnProperty("textColorAbgr")) + object.textColorAbgr = message.textColorAbgr; + return object; + }; + + PopUpProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + PopUpProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return PopUpProto; + })(); + + dbroot.StyleAttributeProto = (function() { + + function StyleAttributeProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + StyleAttributeProto.prototype.styleId = ""; + StyleAttributeProto.prototype.providerId = 0; + StyleAttributeProto.prototype.polyColorAbgr = 4294967295; + StyleAttributeProto.prototype.lineColorAbgr = 4294967295; + StyleAttributeProto.prototype.lineWidth = 1; + StyleAttributeProto.prototype.labelColorAbgr = 4294967295; + StyleAttributeProto.prototype.labelScale = 1; + StyleAttributeProto.prototype.placemarkIconColorAbgr = 4294967295; + StyleAttributeProto.prototype.placemarkIconScale = 1; + StyleAttributeProto.prototype.placemarkIconPath = null; + StyleAttributeProto.prototype.placemarkIconX = 0; + StyleAttributeProto.prototype.placemarkIconY = 0; + StyleAttributeProto.prototype.placemarkIconWidth = 32; + StyleAttributeProto.prototype.placemarkIconHeight = 32; + StyleAttributeProto.prototype.popUp = null; + StyleAttributeProto.prototype.drawFlag = $util.emptyArray; + + var $types = { + 9 : "keyhole.dbroot.StringIdOrValueProto", + 14 : "keyhole.dbroot.PopUpProto", + 15 : "keyhole.dbroot.DrawFlagProto" + }; + $lazyTypes.push($types); + + StyleAttributeProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.StyleAttributeProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.styleId = reader.string(); + break; + case 3: + message.providerId = reader.int32(); + break; + case 4: + message.polyColorAbgr = reader.fixed32(); + break; + case 5: + message.lineColorAbgr = reader.fixed32(); + break; + case 6: + message.lineWidth = reader.float(); + break; + case 7: + message.labelColorAbgr = reader.fixed32(); + break; + case 8: + message.labelScale = reader.float(); + break; + case 9: + message.placemarkIconColorAbgr = reader.fixed32(); + break; + case 10: + message.placemarkIconScale = reader.float(); + break; + case 11: + message.placemarkIconPath = $types[9].decode(reader, reader.uint32()); + break; + case 12: + message.placemarkIconX = reader.int32(); + break; + case 13: + message.placemarkIconY = reader.int32(); + break; + case 14: + message.placemarkIconWidth = reader.int32(); + break; + case 15: + message.placemarkIconHeight = reader.int32(); + break; + case 16: + message.popUp = $types[14].decode(reader, reader.uint32()); + break; + case 17: + if (!(message.drawFlag && message.drawFlag.length)) + message.drawFlag = []; + message.drawFlag.push($types[15].decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + StyleAttributeProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.styleId)) + return "styleId: string expected"; + if (message.providerId !== undefined) + if (!$util.isInteger(message.providerId)) + return "providerId: integer expected"; + if (message.polyColorAbgr !== undefined) + if (!$util.isInteger(message.polyColorAbgr)) + return "polyColorAbgr: integer expected"; + if (message.lineColorAbgr !== undefined) + if (!$util.isInteger(message.lineColorAbgr)) + return "lineColorAbgr: integer expected"; + if (message.lineWidth !== undefined) + if (typeof message.lineWidth !== "number") + return "lineWidth: number expected"; + if (message.labelColorAbgr !== undefined) + if (!$util.isInteger(message.labelColorAbgr)) + return "labelColorAbgr: integer expected"; + if (message.labelScale !== undefined) + if (typeof message.labelScale !== "number") + return "labelScale: number expected"; + if (message.placemarkIconColorAbgr !== undefined) + if (!$util.isInteger(message.placemarkIconColorAbgr)) + return "placemarkIconColorAbgr: integer expected"; + if (message.placemarkIconScale !== undefined) + if (typeof message.placemarkIconScale !== "number") + return "placemarkIconScale: number expected"; + if (message.placemarkIconPath !== undefined && message.placemarkIconPath !== null) { + var error = $types[9].verify(message.placemarkIconPath); + if (error) + return "placemarkIconPath." + error; + } + if (message.placemarkIconX !== undefined) + if (!$util.isInteger(message.placemarkIconX)) + return "placemarkIconX: integer expected"; + if (message.placemarkIconY !== undefined) + if (!$util.isInteger(message.placemarkIconY)) + return "placemarkIconY: integer expected"; + if (message.placemarkIconWidth !== undefined) + if (!$util.isInteger(message.placemarkIconWidth)) + return "placemarkIconWidth: integer expected"; + if (message.placemarkIconHeight !== undefined) + if (!$util.isInteger(message.placemarkIconHeight)) + return "placemarkIconHeight: integer expected"; + if (message.popUp !== undefined && message.popUp !== null) { + var error = $types[14].verify(message.popUp); + if (error) + return "popUp." + error; + } + if (message.drawFlag !== undefined) { + if (!Array.isArray(message.drawFlag)) + return "drawFlag: array expected"; + for (var i = 0; i < message.drawFlag.length; ++i) { + var error = $types[15].verify(message.drawFlag[i]); + if (error) + return "drawFlag." + error; + } + } + return null; + }; + + StyleAttributeProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.StyleAttributeProto) + return object; + var message = new $root.keyhole.dbroot.StyleAttributeProto(); + if (object.styleId !== undefined && object.styleId !== null) + message.styleId = String(object.styleId); + if (object.providerId !== undefined && object.providerId !== null) + message.providerId = object.providerId | 0; + if (object.polyColorAbgr !== undefined && object.polyColorAbgr !== null) + message.polyColorAbgr = object.polyColorAbgr >>> 0; + if (object.lineColorAbgr !== undefined && object.lineColorAbgr !== null) + message.lineColorAbgr = object.lineColorAbgr >>> 0; + if (object.lineWidth !== undefined && object.lineWidth !== null) + message.lineWidth = Number(object.lineWidth); + if (object.labelColorAbgr !== undefined && object.labelColorAbgr !== null) + message.labelColorAbgr = object.labelColorAbgr >>> 0; + if (object.labelScale !== undefined && object.labelScale !== null) + message.labelScale = Number(object.labelScale); + if (object.placemarkIconColorAbgr !== undefined && object.placemarkIconColorAbgr !== null) + message.placemarkIconColorAbgr = object.placemarkIconColorAbgr >>> 0; + if (object.placemarkIconScale !== undefined && object.placemarkIconScale !== null) + message.placemarkIconScale = Number(object.placemarkIconScale); + if (object.placemarkIconPath !== undefined && object.placemarkIconPath !== null) { + if (typeof object.placemarkIconPath !== "object") + throw TypeError(".keyhole.dbroot.StyleAttributeProto.placemarkIconPath: object expected"); + message.placemarkIconPath = $types[9].fromObject(object.placemarkIconPath); + } + if (object.placemarkIconX !== undefined && object.placemarkIconX !== null) + message.placemarkIconX = object.placemarkIconX | 0; + if (object.placemarkIconY !== undefined && object.placemarkIconY !== null) + message.placemarkIconY = object.placemarkIconY | 0; + if (object.placemarkIconWidth !== undefined && object.placemarkIconWidth !== null) + message.placemarkIconWidth = object.placemarkIconWidth | 0; + if (object.placemarkIconHeight !== undefined && object.placemarkIconHeight !== null) + message.placemarkIconHeight = object.placemarkIconHeight | 0; + if (object.popUp !== undefined && object.popUp !== null) { + if (typeof object.popUp !== "object") + throw TypeError(".keyhole.dbroot.StyleAttributeProto.popUp: object expected"); + message.popUp = $types[14].fromObject(object.popUp); + } + if (object.drawFlag) { + if (!Array.isArray(object.drawFlag)) + throw TypeError(".keyhole.dbroot.StyleAttributeProto.drawFlag: array expected"); + message.drawFlag = []; + for (var i = 0; i < object.drawFlag.length; ++i) { + if (typeof object.drawFlag[i] !== "object") + throw TypeError(".keyhole.dbroot.StyleAttributeProto.drawFlag: object expected"); + message.drawFlag[i] = $types[15].fromObject(object.drawFlag[i]); + } + } + return message; + }; + + StyleAttributeProto.from = StyleAttributeProto.fromObject; + + StyleAttributeProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.drawFlag = []; + if (options.defaults) { + object.styleId = ""; + object.providerId = 0; + object.polyColorAbgr = 4294967295; + object.lineColorAbgr = 4294967295; + object.lineWidth = 1; + object.labelColorAbgr = 4294967295; + object.labelScale = 1; + object.placemarkIconColorAbgr = 4294967295; + object.placemarkIconScale = 1; + object.placemarkIconPath = null; + object.placemarkIconX = 0; + object.placemarkIconY = 0; + object.placemarkIconWidth = 32; + object.placemarkIconHeight = 32; + object.popUp = null; + } + if (message.styleId !== undefined && message.styleId !== null && message.hasOwnProperty("styleId")) + object.styleId = message.styleId; + if (message.providerId !== undefined && message.providerId !== null && message.hasOwnProperty("providerId")) + object.providerId = message.providerId; + if (message.polyColorAbgr !== undefined && message.polyColorAbgr !== null && message.hasOwnProperty("polyColorAbgr")) + object.polyColorAbgr = message.polyColorAbgr; + if (message.lineColorAbgr !== undefined && message.lineColorAbgr !== null && message.hasOwnProperty("lineColorAbgr")) + object.lineColorAbgr = message.lineColorAbgr; + if (message.lineWidth !== undefined && message.lineWidth !== null && message.hasOwnProperty("lineWidth")) + object.lineWidth = message.lineWidth; + if (message.labelColorAbgr !== undefined && message.labelColorAbgr !== null && message.hasOwnProperty("labelColorAbgr")) + object.labelColorAbgr = message.labelColorAbgr; + if (message.labelScale !== undefined && message.labelScale !== null && message.hasOwnProperty("labelScale")) + object.labelScale = message.labelScale; + if (message.placemarkIconColorAbgr !== undefined && message.placemarkIconColorAbgr !== null && message.hasOwnProperty("placemarkIconColorAbgr")) + object.placemarkIconColorAbgr = message.placemarkIconColorAbgr; + if (message.placemarkIconScale !== undefined && message.placemarkIconScale !== null && message.hasOwnProperty("placemarkIconScale")) + object.placemarkIconScale = message.placemarkIconScale; + if (message.placemarkIconPath !== undefined && message.placemarkIconPath !== null && message.hasOwnProperty("placemarkIconPath")) + object.placemarkIconPath = $types[9].toObject(message.placemarkIconPath, options); + if (message.placemarkIconX !== undefined && message.placemarkIconX !== null && message.hasOwnProperty("placemarkIconX")) + object.placemarkIconX = message.placemarkIconX; + if (message.placemarkIconY !== undefined && message.placemarkIconY !== null && message.hasOwnProperty("placemarkIconY")) + object.placemarkIconY = message.placemarkIconY; + if (message.placemarkIconWidth !== undefined && message.placemarkIconWidth !== null && message.hasOwnProperty("placemarkIconWidth")) + object.placemarkIconWidth = message.placemarkIconWidth; + if (message.placemarkIconHeight !== undefined && message.placemarkIconHeight !== null && message.hasOwnProperty("placemarkIconHeight")) + object.placemarkIconHeight = message.placemarkIconHeight; + if (message.popUp !== undefined && message.popUp !== null && message.hasOwnProperty("popUp")) + object.popUp = $types[14].toObject(message.popUp, options); + if (message.drawFlag !== undefined && message.drawFlag !== null && message.hasOwnProperty("drawFlag")) { + object.drawFlag = []; + for (var j = 0; j < message.drawFlag.length; ++j) + object.drawFlag[j] = $types[15].toObject(message.drawFlag[j], options); + } + return object; + }; + + StyleAttributeProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + StyleAttributeProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StyleAttributeProto; + })(); + + dbroot.StyleMapProto = (function() { + + function StyleMapProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + StyleMapProto.prototype.styleMapId = 0; + StyleMapProto.prototype.channelId = $util.emptyArray; + StyleMapProto.prototype.normalStyleAttribute = 0; + StyleMapProto.prototype.highlightStyleAttribute = 0; + + StyleMapProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.StyleMapProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.styleMapId = reader.int32(); + break; + case 2: + if (!(message.channelId && message.channelId.length)) + message.channelId = []; + if ((tag & 7) === 2) { + var end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.channelId.push(reader.int32()); + } else + message.channelId.push(reader.int32()); + break; + case 3: + message.normalStyleAttribute = reader.int32(); + break; + case 4: + message.highlightStyleAttribute = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + StyleMapProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.styleMapId)) + return "styleMapId: integer expected"; + if (message.channelId !== undefined) { + if (!Array.isArray(message.channelId)) + return "channelId: array expected"; + for (var i = 0; i < message.channelId.length; ++i) + if (!$util.isInteger(message.channelId[i])) + return "channelId: integer[] expected"; + } + if (message.normalStyleAttribute !== undefined) + if (!$util.isInteger(message.normalStyleAttribute)) + return "normalStyleAttribute: integer expected"; + if (message.highlightStyleAttribute !== undefined) + if (!$util.isInteger(message.highlightStyleAttribute)) + return "highlightStyleAttribute: integer expected"; + return null; + }; + + StyleMapProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.StyleMapProto) + return object; + var message = new $root.keyhole.dbroot.StyleMapProto(); + if (object.styleMapId !== undefined && object.styleMapId !== null) + message.styleMapId = object.styleMapId | 0; + if (object.channelId) { + if (!Array.isArray(object.channelId)) + throw TypeError(".keyhole.dbroot.StyleMapProto.channelId: array expected"); + message.channelId = []; + for (var i = 0; i < object.channelId.length; ++i) + message.channelId[i] = object.channelId[i] | 0; + } + if (object.normalStyleAttribute !== undefined && object.normalStyleAttribute !== null) + message.normalStyleAttribute = object.normalStyleAttribute | 0; + if (object.highlightStyleAttribute !== undefined && object.highlightStyleAttribute !== null) + message.highlightStyleAttribute = object.highlightStyleAttribute | 0; + return message; + }; + + StyleMapProto.from = StyleMapProto.fromObject; + + StyleMapProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.channelId = []; + if (options.defaults) { + object.styleMapId = 0; + object.normalStyleAttribute = 0; + object.highlightStyleAttribute = 0; + } + if (message.styleMapId !== undefined && message.styleMapId !== null && message.hasOwnProperty("styleMapId")) + object.styleMapId = message.styleMapId; + if (message.channelId !== undefined && message.channelId !== null && message.hasOwnProperty("channelId")) { + object.channelId = []; + for (var j = 0; j < message.channelId.length; ++j) + object.channelId[j] = message.channelId[j]; + } + if (message.normalStyleAttribute !== undefined && message.normalStyleAttribute !== null && message.hasOwnProperty("normalStyleAttribute")) + object.normalStyleAttribute = message.normalStyleAttribute; + if (message.highlightStyleAttribute !== undefined && message.highlightStyleAttribute !== null && message.hasOwnProperty("highlightStyleAttribute")) + object.highlightStyleAttribute = message.highlightStyleAttribute; + return object; + }; + + StyleMapProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + StyleMapProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StyleMapProto; + })(); + + dbroot.ZoomRangeProto = (function() { + + function ZoomRangeProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + ZoomRangeProto.prototype.minZoom = 0; + ZoomRangeProto.prototype.maxZoom = 0; + + ZoomRangeProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ZoomRangeProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.minZoom = reader.int32(); + break; + case 2: + message.maxZoom = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + ZoomRangeProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.minZoom)) + return "minZoom: integer expected"; + if (!$util.isInteger(message.maxZoom)) + return "maxZoom: integer expected"; + return null; + }; + + ZoomRangeProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ZoomRangeProto) + return object; + var message = new $root.keyhole.dbroot.ZoomRangeProto(); + if (object.minZoom !== undefined && object.minZoom !== null) + message.minZoom = object.minZoom | 0; + if (object.maxZoom !== undefined && object.maxZoom !== null) + message.maxZoom = object.maxZoom | 0; + return message; + }; + + ZoomRangeProto.from = ZoomRangeProto.fromObject; + + ZoomRangeProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.minZoom = 0; + object.maxZoom = 0; + } + if (message.minZoom !== undefined && message.minZoom !== null && message.hasOwnProperty("minZoom")) + object.minZoom = message.minZoom; + if (message.maxZoom !== undefined && message.maxZoom !== null && message.hasOwnProperty("maxZoom")) + object.maxZoom = message.maxZoom; + return object; + }; + + ZoomRangeProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + ZoomRangeProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ZoomRangeProto; + })(); + + dbroot.DrawFlagProto = (function() { + + function DrawFlagProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + DrawFlagProto.prototype.drawFlagType = 1; + + var $types = { + 0 : "keyhole.dbroot.DrawFlagProto.DrawFlagType" + }; + $lazyTypes.push($types); + + DrawFlagProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.DrawFlagProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.drawFlagType = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + DrawFlagProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + switch (message.drawFlagType) { + default: + return "drawFlagType: enum value expected"; + case 1: + case 2: + case 3: + case 4: + case 5: + break; + } + return null; + }; + + DrawFlagProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.DrawFlagProto) + return object; + var message = new $root.keyhole.dbroot.DrawFlagProto(); + switch (object.drawFlagType) { + case "TYPE_FILL_ONLY": + case 1: + message.drawFlagType = 1; + break; + case "TYPE_OUTLINE_ONLY": + case 2: + message.drawFlagType = 2; + break; + case "TYPE_FILL_AND_OUTLINE": + case 3: + message.drawFlagType = 3; + break; + case "TYPE_ANTIALIASING": + case 4: + message.drawFlagType = 4; + break; + case "TYPE_CENTER_LABEL": + case 5: + message.drawFlagType = 5; + break; + } + return message; + }; + + DrawFlagProto.from = DrawFlagProto.fromObject; + + DrawFlagProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.drawFlagType = options.enums === String ? "TYPE_FILL_ONLY" : 1; + if (message.drawFlagType !== undefined && message.drawFlagType !== null && message.hasOwnProperty("drawFlagType")) + object.drawFlagType = options.enums === String ? $types[0][message.drawFlagType] : message.drawFlagType; + return object; + }; + + DrawFlagProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + DrawFlagProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + DrawFlagProto.DrawFlagType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["TYPE_FILL_ONLY"] = 1; + values["TYPE_OUTLINE_ONLY"] = 2; + values["TYPE_FILL_AND_OUTLINE"] = 3; + values["TYPE_ANTIALIASING"] = 4; + values["TYPE_CENTER_LABEL"] = 5; + return values; + })(); + + return DrawFlagProto; + })(); + + dbroot.LayerProto = (function() { + + function LayerProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + LayerProto.prototype.zoomRange = $util.emptyArray; + LayerProto.prototype.preserveTextLevel = 30; + LayerProto.prototype.lodBeginTransition = false; + LayerProto.prototype.lodEndTransition = false; + + var $types = { + 0 : "keyhole.dbroot.ZoomRangeProto" + }; + $lazyTypes.push($types); + + LayerProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.LayerProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.zoomRange && message.zoomRange.length)) + message.zoomRange = []; + message.zoomRange.push($types[0].decode(reader, reader.uint32())); + break; + case 2: + message.preserveTextLevel = reader.int32(); + break; + case 4: + message.lodBeginTransition = reader.bool(); + break; + case 5: + message.lodEndTransition = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + LayerProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.zoomRange !== undefined) { + if (!Array.isArray(message.zoomRange)) + return "zoomRange: array expected"; + for (var i = 0; i < message.zoomRange.length; ++i) { + var error = $types[0].verify(message.zoomRange[i]); + if (error) + return "zoomRange." + error; + } + } + if (message.preserveTextLevel !== undefined) + if (!$util.isInteger(message.preserveTextLevel)) + return "preserveTextLevel: integer expected"; + if (message.lodBeginTransition !== undefined) + if (typeof message.lodBeginTransition !== "boolean") + return "lodBeginTransition: boolean expected"; + if (message.lodEndTransition !== undefined) + if (typeof message.lodEndTransition !== "boolean") + return "lodEndTransition: boolean expected"; + return null; + }; + + LayerProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.LayerProto) + return object; + var message = new $root.keyhole.dbroot.LayerProto(); + if (object.zoomRange) { + if (!Array.isArray(object.zoomRange)) + throw TypeError(".keyhole.dbroot.LayerProto.zoomRange: array expected"); + message.zoomRange = []; + for (var i = 0; i < object.zoomRange.length; ++i) { + if (typeof object.zoomRange[i] !== "object") + throw TypeError(".keyhole.dbroot.LayerProto.zoomRange: object expected"); + message.zoomRange[i] = $types[0].fromObject(object.zoomRange[i]); + } + } + if (object.preserveTextLevel !== undefined && object.preserveTextLevel !== null) + message.preserveTextLevel = object.preserveTextLevel | 0; + if (object.lodBeginTransition !== undefined && object.lodBeginTransition !== null) + message.lodBeginTransition = Boolean(object.lodBeginTransition); + if (object.lodEndTransition !== undefined && object.lodEndTransition !== null) + message.lodEndTransition = Boolean(object.lodEndTransition); + return message; + }; + + LayerProto.from = LayerProto.fromObject; + + LayerProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.zoomRange = []; + if (options.defaults) { + object.preserveTextLevel = 30; + object.lodBeginTransition = false; + object.lodEndTransition = false; + } + if (message.zoomRange !== undefined && message.zoomRange !== null && message.hasOwnProperty("zoomRange")) { + object.zoomRange = []; + for (var j = 0; j < message.zoomRange.length; ++j) + object.zoomRange[j] = $types[0].toObject(message.zoomRange[j], options); + } + if (message.preserveTextLevel !== undefined && message.preserveTextLevel !== null && message.hasOwnProperty("preserveTextLevel")) + object.preserveTextLevel = message.preserveTextLevel; + if (message.lodBeginTransition !== undefined && message.lodBeginTransition !== null && message.hasOwnProperty("lodBeginTransition")) + object.lodBeginTransition = message.lodBeginTransition; + if (message.lodEndTransition !== undefined && message.lodEndTransition !== null && message.hasOwnProperty("lodEndTransition")) + object.lodEndTransition = message.lodEndTransition; + return object; + }; + + LayerProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + LayerProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return LayerProto; + })(); + + dbroot.FolderProto = (function() { + + function FolderProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + FolderProto.prototype.isExpandable = true; + + FolderProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.FolderProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.isExpandable = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + FolderProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.isExpandable !== undefined) + if (typeof message.isExpandable !== "boolean") + return "isExpandable: boolean expected"; + return null; + }; + + FolderProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.FolderProto) + return object; + var message = new $root.keyhole.dbroot.FolderProto(); + if (object.isExpandable !== undefined && object.isExpandable !== null) + message.isExpandable = Boolean(object.isExpandable); + return message; + }; + + FolderProto.from = FolderProto.fromObject; + + FolderProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.isExpandable = true; + if (message.isExpandable !== undefined && message.isExpandable !== null && message.hasOwnProperty("isExpandable")) + object.isExpandable = message.isExpandable; + return object; + }; + + FolderProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + FolderProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return FolderProto; + })(); + + dbroot.RequirementProto = (function() { + + function RequirementProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + RequirementProto.prototype.requiredVram = ""; + RequirementProto.prototype.requiredClientVer = ""; + RequirementProto.prototype.probability = ""; + RequirementProto.prototype.requiredUserAgent = ""; + RequirementProto.prototype.requiredClientCapabilities = ""; + + RequirementProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.RequirementProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 3: + message.requiredVram = reader.string(); + break; + case 4: + message.requiredClientVer = reader.string(); + break; + case 5: + message.probability = reader.string(); + break; + case 6: + message.requiredUserAgent = reader.string(); + break; + case 7: + message.requiredClientCapabilities = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + RequirementProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.requiredVram !== undefined) + if (!$util.isString(message.requiredVram)) + return "requiredVram: string expected"; + if (message.requiredClientVer !== undefined) + if (!$util.isString(message.requiredClientVer)) + return "requiredClientVer: string expected"; + if (message.probability !== undefined) + if (!$util.isString(message.probability)) + return "probability: string expected"; + if (message.requiredUserAgent !== undefined) + if (!$util.isString(message.requiredUserAgent)) + return "requiredUserAgent: string expected"; + if (message.requiredClientCapabilities !== undefined) + if (!$util.isString(message.requiredClientCapabilities)) + return "requiredClientCapabilities: string expected"; + return null; + }; + + RequirementProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.RequirementProto) + return object; + var message = new $root.keyhole.dbroot.RequirementProto(); + if (object.requiredVram !== undefined && object.requiredVram !== null) + message.requiredVram = String(object.requiredVram); + if (object.requiredClientVer !== undefined && object.requiredClientVer !== null) + message.requiredClientVer = String(object.requiredClientVer); + if (object.probability !== undefined && object.probability !== null) + message.probability = String(object.probability); + if (object.requiredUserAgent !== undefined && object.requiredUserAgent !== null) + message.requiredUserAgent = String(object.requiredUserAgent); + if (object.requiredClientCapabilities !== undefined && object.requiredClientCapabilities !== null) + message.requiredClientCapabilities = String(object.requiredClientCapabilities); + return message; + }; + + RequirementProto.from = RequirementProto.fromObject; + + RequirementProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.requiredVram = ""; + object.requiredClientVer = ""; + object.probability = ""; + object.requiredUserAgent = ""; + object.requiredClientCapabilities = ""; + } + if (message.requiredVram !== undefined && message.requiredVram !== null && message.hasOwnProperty("requiredVram")) + object.requiredVram = message.requiredVram; + if (message.requiredClientVer !== undefined && message.requiredClientVer !== null && message.hasOwnProperty("requiredClientVer")) + object.requiredClientVer = message.requiredClientVer; + if (message.probability !== undefined && message.probability !== null && message.hasOwnProperty("probability")) + object.probability = message.probability; + if (message.requiredUserAgent !== undefined && message.requiredUserAgent !== null && message.hasOwnProperty("requiredUserAgent")) + object.requiredUserAgent = message.requiredUserAgent; + if (message.requiredClientCapabilities !== undefined && message.requiredClientCapabilities !== null && message.hasOwnProperty("requiredClientCapabilities")) + object.requiredClientCapabilities = message.requiredClientCapabilities; + return object; + }; + + RequirementProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + RequirementProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return RequirementProto; + })(); + + dbroot.LookAtProto = (function() { + + function LookAtProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + LookAtProto.prototype.longitude = 0; + LookAtProto.prototype.latitude = 0; + LookAtProto.prototype.range = 0; + LookAtProto.prototype.tilt = 0; + LookAtProto.prototype.heading = 0; + + LookAtProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.LookAtProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.longitude = reader.float(); + break; + case 2: + message.latitude = reader.float(); + break; + case 3: + message.range = reader.float(); + break; + case 4: + message.tilt = reader.float(); + break; + case 5: + message.heading = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + LookAtProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (typeof message.longitude !== "number") + return "longitude: number expected"; + if (typeof message.latitude !== "number") + return "latitude: number expected"; + if (message.range !== undefined) + if (typeof message.range !== "number") + return "range: number expected"; + if (message.tilt !== undefined) + if (typeof message.tilt !== "number") + return "tilt: number expected"; + if (message.heading !== undefined) + if (typeof message.heading !== "number") + return "heading: number expected"; + return null; + }; + + LookAtProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.LookAtProto) + return object; + var message = new $root.keyhole.dbroot.LookAtProto(); + if (object.longitude !== undefined && object.longitude !== null) + message.longitude = Number(object.longitude); + if (object.latitude !== undefined && object.latitude !== null) + message.latitude = Number(object.latitude); + if (object.range !== undefined && object.range !== null) + message.range = Number(object.range); + if (object.tilt !== undefined && object.tilt !== null) + message.tilt = Number(object.tilt); + if (object.heading !== undefined && object.heading !== null) + message.heading = Number(object.heading); + return message; + }; + + LookAtProto.from = LookAtProto.fromObject; + + LookAtProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.longitude = 0; + object.latitude = 0; + object.range = 0; + object.tilt = 0; + object.heading = 0; + } + if (message.longitude !== undefined && message.longitude !== null && message.hasOwnProperty("longitude")) + object.longitude = message.longitude; + if (message.latitude !== undefined && message.latitude !== null && message.hasOwnProperty("latitude")) + object.latitude = message.latitude; + if (message.range !== undefined && message.range !== null && message.hasOwnProperty("range")) + object.range = message.range; + if (message.tilt !== undefined && message.tilt !== null && message.hasOwnProperty("tilt")) + object.tilt = message.tilt; + if (message.heading !== undefined && message.heading !== null && message.hasOwnProperty("heading")) + object.heading = message.heading; + return object; + }; + + LookAtProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + LookAtProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return LookAtProto; + })(); + + dbroot.NestedFeatureProto = (function() { + + function NestedFeatureProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + NestedFeatureProto.prototype.featureType = 1; + NestedFeatureProto.prototype.kmlUrl = null; + NestedFeatureProto.prototype.databaseUrl = ""; + NestedFeatureProto.prototype.layer = null; + NestedFeatureProto.prototype.folder = null; + NestedFeatureProto.prototype.requirement = null; + NestedFeatureProto.prototype.channelId = 0; + NestedFeatureProto.prototype.displayName = null; + NestedFeatureProto.prototype.isVisible = true; + NestedFeatureProto.prototype.isEnabled = true; + NestedFeatureProto.prototype.isChecked = false; + NestedFeatureProto.prototype.layerMenuIconPath = "icons/773_l.png"; + NestedFeatureProto.prototype.description = null; + NestedFeatureProto.prototype.lookAt = null; + NestedFeatureProto.prototype.assetUuid = ""; + NestedFeatureProto.prototype.isSaveLocked = true; + NestedFeatureProto.prototype.children = $util.emptyArray; + NestedFeatureProto.prototype.clientConfigScriptName = ""; + NestedFeatureProto.prototype.dioramaDataChannelBase = -1; + NestedFeatureProto.prototype.replicaDataChannelBase = -1; + + var $types = { + 0 : "keyhole.dbroot.NestedFeatureProto.FeatureType", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.LayerProto", + 4 : "keyhole.dbroot.FolderProto", + 5 : "keyhole.dbroot.RequirementProto", + 7 : "keyhole.dbroot.StringIdOrValueProto", + 12 : "keyhole.dbroot.StringIdOrValueProto", + 13 : "keyhole.dbroot.LookAtProto", + 16 : "keyhole.dbroot.NestedFeatureProto" + }; + $lazyTypes.push($types); + + NestedFeatureProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.NestedFeatureProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.featureType = reader.uint32(); + break; + case 2: + message.kmlUrl = $types[1].decode(reader, reader.uint32()); + break; + case 21: + message.databaseUrl = reader.string(); + break; + case 3: + message.layer = $types[3].decode(reader, reader.uint32()); + break; + case 4: + message.folder = $types[4].decode(reader, reader.uint32()); + break; + case 5: + message.requirement = $types[5].decode(reader, reader.uint32()); + break; + case 6: + message.channelId = reader.int32(); + break; + case 7: + message.displayName = $types[7].decode(reader, reader.uint32()); + break; + case 8: + message.isVisible = reader.bool(); + break; + case 9: + message.isEnabled = reader.bool(); + break; + case 10: + message.isChecked = reader.bool(); + break; + case 11: + message.layerMenuIconPath = reader.string(); + break; + case 12: + message.description = $types[12].decode(reader, reader.uint32()); + break; + case 13: + message.lookAt = $types[13].decode(reader, reader.uint32()); + break; + case 15: + message.assetUuid = reader.string(); + break; + case 16: + message.isSaveLocked = reader.bool(); + break; + case 17: + if (!(message.children && message.children.length)) + message.children = []; + message.children.push($types[16].decode(reader, reader.uint32())); + break; + case 18: + message.clientConfigScriptName = reader.string(); + break; + case 19: + message.dioramaDataChannelBase = reader.int32(); + break; + case 20: + message.replicaDataChannelBase = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + NestedFeatureProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.featureType !== undefined) + switch (message.featureType) { + default: + return "featureType: enum value expected"; + case 1: + case 2: + case 3: + case 4: + break; + } + if (message.kmlUrl !== undefined && message.kmlUrl !== null) { + var error = $types[1].verify(message.kmlUrl); + if (error) + return "kmlUrl." + error; + } + if (message.databaseUrl !== undefined) + if (!$util.isString(message.databaseUrl)) + return "databaseUrl: string expected"; + if (message.layer !== undefined && message.layer !== null) { + var error = $types[3].verify(message.layer); + if (error) + return "layer." + error; + } + if (message.folder !== undefined && message.folder !== null) { + var error = $types[4].verify(message.folder); + if (error) + return "folder." + error; + } + if (message.requirement !== undefined && message.requirement !== null) { + var error = $types[5].verify(message.requirement); + if (error) + return "requirement." + error; + } + if (!$util.isInteger(message.channelId)) + return "channelId: integer expected"; + if (message.displayName !== undefined && message.displayName !== null) { + var error = $types[7].verify(message.displayName); + if (error) + return "displayName." + error; + } + if (message.isVisible !== undefined) + if (typeof message.isVisible !== "boolean") + return "isVisible: boolean expected"; + if (message.isEnabled !== undefined) + if (typeof message.isEnabled !== "boolean") + return "isEnabled: boolean expected"; + if (message.isChecked !== undefined) + if (typeof message.isChecked !== "boolean") + return "isChecked: boolean expected"; + if (message.layerMenuIconPath !== undefined) + if (!$util.isString(message.layerMenuIconPath)) + return "layerMenuIconPath: string expected"; + if (message.description !== undefined && message.description !== null) { + var error = $types[12].verify(message.description); + if (error) + return "description." + error; + } + if (message.lookAt !== undefined && message.lookAt !== null) { + var error = $types[13].verify(message.lookAt); + if (error) + return "lookAt." + error; + } + if (message.assetUuid !== undefined) + if (!$util.isString(message.assetUuid)) + return "assetUuid: string expected"; + if (message.isSaveLocked !== undefined) + if (typeof message.isSaveLocked !== "boolean") + return "isSaveLocked: boolean expected"; + if (message.children !== undefined) { + if (!Array.isArray(message.children)) + return "children: array expected"; + for (var i = 0; i < message.children.length; ++i) { + var error = $types[16].verify(message.children[i]); + if (error) + return "children." + error; + } + } + if (message.clientConfigScriptName !== undefined) + if (!$util.isString(message.clientConfigScriptName)) + return "clientConfigScriptName: string expected"; + if (message.dioramaDataChannelBase !== undefined) + if (!$util.isInteger(message.dioramaDataChannelBase)) + return "dioramaDataChannelBase: integer expected"; + if (message.replicaDataChannelBase !== undefined) + if (!$util.isInteger(message.replicaDataChannelBase)) + return "replicaDataChannelBase: integer expected"; + return null; + }; + + NestedFeatureProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.NestedFeatureProto) + return object; + var message = new $root.keyhole.dbroot.NestedFeatureProto(); + switch (object.featureType) { + case "TYPE_POINT_Z": + case 1: + message.featureType = 1; + break; + case "TYPE_POLYGON_Z": + case 2: + message.featureType = 2; + break; + case "TYPE_LINE_Z": + case 3: + message.featureType = 3; + break; + case "TYPE_TERRAIN": + case 4: + message.featureType = 4; + break; + } + if (object.kmlUrl !== undefined && object.kmlUrl !== null) { + if (typeof object.kmlUrl !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.kmlUrl: object expected"); + message.kmlUrl = $types[1].fromObject(object.kmlUrl); + } + if (object.databaseUrl !== undefined && object.databaseUrl !== null) + message.databaseUrl = String(object.databaseUrl); + if (object.layer !== undefined && object.layer !== null) { + if (typeof object.layer !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.layer: object expected"); + message.layer = $types[3].fromObject(object.layer); + } + if (object.folder !== undefined && object.folder !== null) { + if (typeof object.folder !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.folder: object expected"); + message.folder = $types[4].fromObject(object.folder); + } + if (object.requirement !== undefined && object.requirement !== null) { + if (typeof object.requirement !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.requirement: object expected"); + message.requirement = $types[5].fromObject(object.requirement); + } + if (object.channelId !== undefined && object.channelId !== null) + message.channelId = object.channelId | 0; + if (object.displayName !== undefined && object.displayName !== null) { + if (typeof object.displayName !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.displayName: object expected"); + message.displayName = $types[7].fromObject(object.displayName); + } + if (object.isVisible !== undefined && object.isVisible !== null) + message.isVisible = Boolean(object.isVisible); + if (object.isEnabled !== undefined && object.isEnabled !== null) + message.isEnabled = Boolean(object.isEnabled); + if (object.isChecked !== undefined && object.isChecked !== null) + message.isChecked = Boolean(object.isChecked); + if (object.layerMenuIconPath !== undefined && object.layerMenuIconPath !== null) + message.layerMenuIconPath = String(object.layerMenuIconPath); + if (object.description !== undefined && object.description !== null) { + if (typeof object.description !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.description: object expected"); + message.description = $types[12].fromObject(object.description); + } + if (object.lookAt !== undefined && object.lookAt !== null) { + if (typeof object.lookAt !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.lookAt: object expected"); + message.lookAt = $types[13].fromObject(object.lookAt); + } + if (object.assetUuid !== undefined && object.assetUuid !== null) + message.assetUuid = String(object.assetUuid); + if (object.isSaveLocked !== undefined && object.isSaveLocked !== null) + message.isSaveLocked = Boolean(object.isSaveLocked); + if (object.children) { + if (!Array.isArray(object.children)) + throw TypeError(".keyhole.dbroot.NestedFeatureProto.children: array expected"); + message.children = []; + for (var i = 0; i < object.children.length; ++i) { + if (typeof object.children[i] !== "object") + throw TypeError(".keyhole.dbroot.NestedFeatureProto.children: object expected"); + message.children[i] = $types[16].fromObject(object.children[i]); + } + } + if (object.clientConfigScriptName !== undefined && object.clientConfigScriptName !== null) + message.clientConfigScriptName = String(object.clientConfigScriptName); + if (object.dioramaDataChannelBase !== undefined && object.dioramaDataChannelBase !== null) + message.dioramaDataChannelBase = object.dioramaDataChannelBase | 0; + if (object.replicaDataChannelBase !== undefined && object.replicaDataChannelBase !== null) + message.replicaDataChannelBase = object.replicaDataChannelBase | 0; + return message; + }; + + NestedFeatureProto.from = NestedFeatureProto.fromObject; + + NestedFeatureProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.children = []; + if (options.defaults) { + object.featureType = options.enums === String ? "TYPE_POINT_Z" : 1; + object.kmlUrl = null; + object.databaseUrl = ""; + object.layer = null; + object.folder = null; + object.requirement = null; + object.channelId = 0; + object.displayName = null; + object.isVisible = true; + object.isEnabled = true; + object.isChecked = false; + object.layerMenuIconPath = "icons/773_l.png"; + object.description = null; + object.lookAt = null; + object.assetUuid = ""; + object.isSaveLocked = true; + object.clientConfigScriptName = ""; + object.dioramaDataChannelBase = -1; + object.replicaDataChannelBase = -1; + } + if (message.featureType !== undefined && message.featureType !== null && message.hasOwnProperty("featureType")) + object.featureType = options.enums === String ? $types[0][message.featureType] : message.featureType; + if (message.kmlUrl !== undefined && message.kmlUrl !== null && message.hasOwnProperty("kmlUrl")) + object.kmlUrl = $types[1].toObject(message.kmlUrl, options); + if (message.databaseUrl !== undefined && message.databaseUrl !== null && message.hasOwnProperty("databaseUrl")) + object.databaseUrl = message.databaseUrl; + if (message.layer !== undefined && message.layer !== null && message.hasOwnProperty("layer")) + object.layer = $types[3].toObject(message.layer, options); + if (message.folder !== undefined && message.folder !== null && message.hasOwnProperty("folder")) + object.folder = $types[4].toObject(message.folder, options); + if (message.requirement !== undefined && message.requirement !== null && message.hasOwnProperty("requirement")) + object.requirement = $types[5].toObject(message.requirement, options); + if (message.channelId !== undefined && message.channelId !== null && message.hasOwnProperty("channelId")) + object.channelId = message.channelId; + if (message.displayName !== undefined && message.displayName !== null && message.hasOwnProperty("displayName")) + object.displayName = $types[7].toObject(message.displayName, options); + if (message.isVisible !== undefined && message.isVisible !== null && message.hasOwnProperty("isVisible")) + object.isVisible = message.isVisible; + if (message.isEnabled !== undefined && message.isEnabled !== null && message.hasOwnProperty("isEnabled")) + object.isEnabled = message.isEnabled; + if (message.isChecked !== undefined && message.isChecked !== null && message.hasOwnProperty("isChecked")) + object.isChecked = message.isChecked; + if (message.layerMenuIconPath !== undefined && message.layerMenuIconPath !== null && message.hasOwnProperty("layerMenuIconPath")) + object.layerMenuIconPath = message.layerMenuIconPath; + if (message.description !== undefined && message.description !== null && message.hasOwnProperty("description")) + object.description = $types[12].toObject(message.description, options); + if (message.lookAt !== undefined && message.lookAt !== null && message.hasOwnProperty("lookAt")) + object.lookAt = $types[13].toObject(message.lookAt, options); + if (message.assetUuid !== undefined && message.assetUuid !== null && message.hasOwnProperty("assetUuid")) + object.assetUuid = message.assetUuid; + if (message.isSaveLocked !== undefined && message.isSaveLocked !== null && message.hasOwnProperty("isSaveLocked")) + object.isSaveLocked = message.isSaveLocked; + if (message.children !== undefined && message.children !== null && message.hasOwnProperty("children")) { + object.children = []; + for (var j = 0; j < message.children.length; ++j) + object.children[j] = $types[16].toObject(message.children[j], options); + } + if (message.clientConfigScriptName !== undefined && message.clientConfigScriptName !== null && message.hasOwnProperty("clientConfigScriptName")) + object.clientConfigScriptName = message.clientConfigScriptName; + if (message.dioramaDataChannelBase !== undefined && message.dioramaDataChannelBase !== null && message.hasOwnProperty("dioramaDataChannelBase")) + object.dioramaDataChannelBase = message.dioramaDataChannelBase; + if (message.replicaDataChannelBase !== undefined && message.replicaDataChannelBase !== null && message.hasOwnProperty("replicaDataChannelBase")) + object.replicaDataChannelBase = message.replicaDataChannelBase; + return object; + }; + + NestedFeatureProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + NestedFeatureProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + NestedFeatureProto.FeatureType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["TYPE_POINT_Z"] = 1; + values["TYPE_POLYGON_Z"] = 2; + values["TYPE_LINE_Z"] = 3; + values["TYPE_TERRAIN"] = 4; + return values; + })(); + + return NestedFeatureProto; + })(); + + dbroot.MfeDomainFeaturesProto = (function() { + + function MfeDomainFeaturesProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + MfeDomainFeaturesProto.prototype.countryCode = ""; + MfeDomainFeaturesProto.prototype.domainName = ""; + MfeDomainFeaturesProto.prototype.supportedFeatures = $util.emptyArray; + + var $types = { + 2 : "keyhole.dbroot.MfeDomainFeaturesProto.SupportedFeature" + }; + $lazyTypes.push($types); + + MfeDomainFeaturesProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.MfeDomainFeaturesProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.countryCode = reader.string(); + break; + case 2: + message.domainName = reader.string(); + break; + case 3: + if (!(message.supportedFeatures && message.supportedFeatures.length)) + message.supportedFeatures = []; + if ((tag & 7) === 2) { + var end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.supportedFeatures.push(reader.uint32()); + } else + message.supportedFeatures.push(reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + MfeDomainFeaturesProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.countryCode)) + return "countryCode: string expected"; + if (!$util.isString(message.domainName)) + return "domainName: string expected"; + if (message.supportedFeatures !== undefined) { + if (!Array.isArray(message.supportedFeatures)) + return "supportedFeatures: array expected"; + for (var i = 0; i < message.supportedFeatures.length; ++i) + switch (message.supportedFeatures[i]) { + default: + return "supportedFeatures: enum value[] expected"; + case 0: + case 1: + case 2: + break; + } + } + return null; + }; + + MfeDomainFeaturesProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.MfeDomainFeaturesProto) + return object; + var message = new $root.keyhole.dbroot.MfeDomainFeaturesProto(); + if (object.countryCode !== undefined && object.countryCode !== null) + message.countryCode = String(object.countryCode); + if (object.domainName !== undefined && object.domainName !== null) + message.domainName = String(object.domainName); + if (object.supportedFeatures) { + if (!Array.isArray(object.supportedFeatures)) + throw TypeError(".keyhole.dbroot.MfeDomainFeaturesProto.supportedFeatures: array expected"); + message.supportedFeatures = []; + for (var i = 0; i < object.supportedFeatures.length; ++i) + switch (object.supportedFeatures[i]) { + default: + case "GEOCODING": + case 0: + message.supportedFeatures[i] = 0; + break; + case "LOCAL_SEARCH": + case 1: + message.supportedFeatures[i] = 1; + break; + case "DRIVING_DIRECTIONS": + case 2: + message.supportedFeatures[i] = 2; + break; + } + } + return message; + }; + + MfeDomainFeaturesProto.from = MfeDomainFeaturesProto.fromObject; + + MfeDomainFeaturesProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.supportedFeatures = []; + if (options.defaults) { + object.countryCode = ""; + object.domainName = ""; + } + if (message.countryCode !== undefined && message.countryCode !== null && message.hasOwnProperty("countryCode")) + object.countryCode = message.countryCode; + if (message.domainName !== undefined && message.domainName !== null && message.hasOwnProperty("domainName")) + object.domainName = message.domainName; + if (message.supportedFeatures !== undefined && message.supportedFeatures !== null && message.hasOwnProperty("supportedFeatures")) { + object.supportedFeatures = []; + for (var j = 0; j < message.supportedFeatures.length; ++j) + object.supportedFeatures[j] = options.enums === String ? $types[2][message.supportedFeatures[j]] : message.supportedFeatures[j]; + } + return object; + }; + + MfeDomainFeaturesProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + MfeDomainFeaturesProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + MfeDomainFeaturesProto.SupportedFeature = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["GEOCODING"] = 0; + values["LOCAL_SEARCH"] = 1; + values["DRIVING_DIRECTIONS"] = 2; + return values; + })(); + + return MfeDomainFeaturesProto; + })(); + + dbroot.ClientOptionsProto = (function() { + + function ClientOptionsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + ClientOptionsProto.prototype.disableDiskCache = false; + ClientOptionsProto.prototype.disableEmbeddedBrowserVista = false; + ClientOptionsProto.prototype.drawAtmosphere = true; + ClientOptionsProto.prototype.drawStars = true; + ClientOptionsProto.prototype.shaderFilePrefix = ""; + ClientOptionsProto.prototype.useProtobufQuadtreePackets = false; + ClientOptionsProto.prototype.useExtendedCopyrightIds = true; + ClientOptionsProto.prototype.precipitationsOptions = null; + ClientOptionsProto.prototype.captureOptions = null; + ClientOptionsProto.prototype.show_2dMapsIcon = true; + ClientOptionsProto.prototype.disableInternalBrowser = false; + ClientOptionsProto.prototype.internalBrowserBlacklist = ""; + ClientOptionsProto.prototype.internalBrowserOriginWhitelist = "*"; + ClientOptionsProto.prototype.polarTileMergingLevel = 0; + ClientOptionsProto.prototype.jsBridgeRequestWhitelist = "http://*.google.com/*"; + ClientOptionsProto.prototype.mapsOptions = null; + + var $types = { + 7 : "keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions", + 8 : "keyhole.dbroot.ClientOptionsProto.CaptureOptions", + 15 : "keyhole.dbroot.ClientOptionsProto.MapsOptions" + }; + $lazyTypes.push($types); + + ClientOptionsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ClientOptionsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.disableDiskCache = reader.bool(); + break; + case 2: + message.disableEmbeddedBrowserVista = reader.bool(); + break; + case 3: + message.drawAtmosphere = reader.bool(); + break; + case 4: + message.drawStars = reader.bool(); + break; + case 5: + message.shaderFilePrefix = reader.string(); + break; + case 6: + message.useProtobufQuadtreePackets = reader.bool(); + break; + case 7: + message.useExtendedCopyrightIds = reader.bool(); + break; + case 8: + message.precipitationsOptions = $types[7].decode(reader, reader.uint32()); + break; + case 9: + message.captureOptions = $types[8].decode(reader, reader.uint32()); + break; + case 10: + message.show_2dMapsIcon = reader.bool(); + break; + case 11: + message.disableInternalBrowser = reader.bool(); + break; + case 12: + message.internalBrowserBlacklist = reader.string(); + break; + case 13: + message.internalBrowserOriginWhitelist = reader.string(); + break; + case 14: + message.polarTileMergingLevel = reader.int32(); + break; + case 15: + message.jsBridgeRequestWhitelist = reader.string(); + break; + case 16: + message.mapsOptions = $types[15].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + ClientOptionsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.disableDiskCache !== undefined) + if (typeof message.disableDiskCache !== "boolean") + return "disableDiskCache: boolean expected"; + if (message.disableEmbeddedBrowserVista !== undefined) + if (typeof message.disableEmbeddedBrowserVista !== "boolean") + return "disableEmbeddedBrowserVista: boolean expected"; + if (message.drawAtmosphere !== undefined) + if (typeof message.drawAtmosphere !== "boolean") + return "drawAtmosphere: boolean expected"; + if (message.drawStars !== undefined) + if (typeof message.drawStars !== "boolean") + return "drawStars: boolean expected"; + if (message.shaderFilePrefix !== undefined) + if (!$util.isString(message.shaderFilePrefix)) + return "shaderFilePrefix: string expected"; + if (message.useProtobufQuadtreePackets !== undefined) + if (typeof message.useProtobufQuadtreePackets !== "boolean") + return "useProtobufQuadtreePackets: boolean expected"; + if (message.useExtendedCopyrightIds !== undefined) + if (typeof message.useExtendedCopyrightIds !== "boolean") + return "useExtendedCopyrightIds: boolean expected"; + if (message.precipitationsOptions !== undefined && message.precipitationsOptions !== null) { + var error = $types[7].verify(message.precipitationsOptions); + if (error) + return "precipitationsOptions." + error; + } + if (message.captureOptions !== undefined && message.captureOptions !== null) { + var error = $types[8].verify(message.captureOptions); + if (error) + return "captureOptions." + error; + } + if (message.show_2dMapsIcon !== undefined) + if (typeof message.show_2dMapsIcon !== "boolean") + return "show_2dMapsIcon: boolean expected"; + if (message.disableInternalBrowser !== undefined) + if (typeof message.disableInternalBrowser !== "boolean") + return "disableInternalBrowser: boolean expected"; + if (message.internalBrowserBlacklist !== undefined) + if (!$util.isString(message.internalBrowserBlacklist)) + return "internalBrowserBlacklist: string expected"; + if (message.internalBrowserOriginWhitelist !== undefined) + if (!$util.isString(message.internalBrowserOriginWhitelist)) + return "internalBrowserOriginWhitelist: string expected"; + if (message.polarTileMergingLevel !== undefined) + if (!$util.isInteger(message.polarTileMergingLevel)) + return "polarTileMergingLevel: integer expected"; + if (message.jsBridgeRequestWhitelist !== undefined) + if (!$util.isString(message.jsBridgeRequestWhitelist)) + return "jsBridgeRequestWhitelist: string expected"; + if (message.mapsOptions !== undefined && message.mapsOptions !== null) { + var error = $types[15].verify(message.mapsOptions); + if (error) + return "mapsOptions." + error; + } + return null; + }; + + ClientOptionsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ClientOptionsProto) + return object; + var message = new $root.keyhole.dbroot.ClientOptionsProto(); + if (object.disableDiskCache !== undefined && object.disableDiskCache !== null) + message.disableDiskCache = Boolean(object.disableDiskCache); + if (object.disableEmbeddedBrowserVista !== undefined && object.disableEmbeddedBrowserVista !== null) + message.disableEmbeddedBrowserVista = Boolean(object.disableEmbeddedBrowserVista); + if (object.drawAtmosphere !== undefined && object.drawAtmosphere !== null) + message.drawAtmosphere = Boolean(object.drawAtmosphere); + if (object.drawStars !== undefined && object.drawStars !== null) + message.drawStars = Boolean(object.drawStars); + if (object.shaderFilePrefix !== undefined && object.shaderFilePrefix !== null) + message.shaderFilePrefix = String(object.shaderFilePrefix); + if (object.useProtobufQuadtreePackets !== undefined && object.useProtobufQuadtreePackets !== null) + message.useProtobufQuadtreePackets = Boolean(object.useProtobufQuadtreePackets); + if (object.useExtendedCopyrightIds !== undefined && object.useExtendedCopyrightIds !== null) + message.useExtendedCopyrightIds = Boolean(object.useExtendedCopyrightIds); + if (object.precipitationsOptions !== undefined && object.precipitationsOptions !== null) { + if (typeof object.precipitationsOptions !== "object") + throw TypeError(".keyhole.dbroot.ClientOptionsProto.precipitationsOptions: object expected"); + message.precipitationsOptions = $types[7].fromObject(object.precipitationsOptions); + } + if (object.captureOptions !== undefined && object.captureOptions !== null) { + if (typeof object.captureOptions !== "object") + throw TypeError(".keyhole.dbroot.ClientOptionsProto.captureOptions: object expected"); + message.captureOptions = $types[8].fromObject(object.captureOptions); + } + if (object.show_2dMapsIcon !== undefined && object.show_2dMapsIcon !== null) + message.show_2dMapsIcon = Boolean(object.show_2dMapsIcon); + if (object.disableInternalBrowser !== undefined && object.disableInternalBrowser !== null) + message.disableInternalBrowser = Boolean(object.disableInternalBrowser); + if (object.internalBrowserBlacklist !== undefined && object.internalBrowserBlacklist !== null) + message.internalBrowserBlacklist = String(object.internalBrowserBlacklist); + if (object.internalBrowserOriginWhitelist !== undefined && object.internalBrowserOriginWhitelist !== null) + message.internalBrowserOriginWhitelist = String(object.internalBrowserOriginWhitelist); + if (object.polarTileMergingLevel !== undefined && object.polarTileMergingLevel !== null) + message.polarTileMergingLevel = object.polarTileMergingLevel | 0; + if (object.jsBridgeRequestWhitelist !== undefined && object.jsBridgeRequestWhitelist !== null) + message.jsBridgeRequestWhitelist = String(object.jsBridgeRequestWhitelist); + if (object.mapsOptions !== undefined && object.mapsOptions !== null) { + if (typeof object.mapsOptions !== "object") + throw TypeError(".keyhole.dbroot.ClientOptionsProto.mapsOptions: object expected"); + message.mapsOptions = $types[15].fromObject(object.mapsOptions); + } + return message; + }; + + ClientOptionsProto.from = ClientOptionsProto.fromObject; + + ClientOptionsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.disableDiskCache = false; + object.disableEmbeddedBrowserVista = false; + object.drawAtmosphere = true; + object.drawStars = true; + object.shaderFilePrefix = ""; + object.useProtobufQuadtreePackets = false; + object.useExtendedCopyrightIds = true; + object.precipitationsOptions = null; + object.captureOptions = null; + object.show_2dMapsIcon = true; + object.disableInternalBrowser = false; + object.internalBrowserBlacklist = ""; + object.internalBrowserOriginWhitelist = "*"; + object.polarTileMergingLevel = 0; + object.jsBridgeRequestWhitelist = "http://*.google.com/*"; + object.mapsOptions = null; + } + if (message.disableDiskCache !== undefined && message.disableDiskCache !== null && message.hasOwnProperty("disableDiskCache")) + object.disableDiskCache = message.disableDiskCache; + if (message.disableEmbeddedBrowserVista !== undefined && message.disableEmbeddedBrowserVista !== null && message.hasOwnProperty("disableEmbeddedBrowserVista")) + object.disableEmbeddedBrowserVista = message.disableEmbeddedBrowserVista; + if (message.drawAtmosphere !== undefined && message.drawAtmosphere !== null && message.hasOwnProperty("drawAtmosphere")) + object.drawAtmosphere = message.drawAtmosphere; + if (message.drawStars !== undefined && message.drawStars !== null && message.hasOwnProperty("drawStars")) + object.drawStars = message.drawStars; + if (message.shaderFilePrefix !== undefined && message.shaderFilePrefix !== null && message.hasOwnProperty("shaderFilePrefix")) + object.shaderFilePrefix = message.shaderFilePrefix; + if (message.useProtobufQuadtreePackets !== undefined && message.useProtobufQuadtreePackets !== null && message.hasOwnProperty("useProtobufQuadtreePackets")) + object.useProtobufQuadtreePackets = message.useProtobufQuadtreePackets; + if (message.useExtendedCopyrightIds !== undefined && message.useExtendedCopyrightIds !== null && message.hasOwnProperty("useExtendedCopyrightIds")) + object.useExtendedCopyrightIds = message.useExtendedCopyrightIds; + if (message.precipitationsOptions !== undefined && message.precipitationsOptions !== null && message.hasOwnProperty("precipitationsOptions")) + object.precipitationsOptions = $types[7].toObject(message.precipitationsOptions, options); + if (message.captureOptions !== undefined && message.captureOptions !== null && message.hasOwnProperty("captureOptions")) + object.captureOptions = $types[8].toObject(message.captureOptions, options); + if (message.show_2dMapsIcon !== undefined && message.show_2dMapsIcon !== null && message.hasOwnProperty("show_2dMapsIcon")) + object.show_2dMapsIcon = message.show_2dMapsIcon; + if (message.disableInternalBrowser !== undefined && message.disableInternalBrowser !== null && message.hasOwnProperty("disableInternalBrowser")) + object.disableInternalBrowser = message.disableInternalBrowser; + if (message.internalBrowserBlacklist !== undefined && message.internalBrowserBlacklist !== null && message.hasOwnProperty("internalBrowserBlacklist")) + object.internalBrowserBlacklist = message.internalBrowserBlacklist; + if (message.internalBrowserOriginWhitelist !== undefined && message.internalBrowserOriginWhitelist !== null && message.hasOwnProperty("internalBrowserOriginWhitelist")) + object.internalBrowserOriginWhitelist = message.internalBrowserOriginWhitelist; + if (message.polarTileMergingLevel !== undefined && message.polarTileMergingLevel !== null && message.hasOwnProperty("polarTileMergingLevel")) + object.polarTileMergingLevel = message.polarTileMergingLevel; + if (message.jsBridgeRequestWhitelist !== undefined && message.jsBridgeRequestWhitelist !== null && message.hasOwnProperty("jsBridgeRequestWhitelist")) + object.jsBridgeRequestWhitelist = message.jsBridgeRequestWhitelist; + if (message.mapsOptions !== undefined && message.mapsOptions !== null && message.hasOwnProperty("mapsOptions")) + object.mapsOptions = $types[15].toObject(message.mapsOptions, options); + return object; + }; + + ClientOptionsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + ClientOptionsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + ClientOptionsProto.PrecipitationsOptions = (function() { + + function PrecipitationsOptions(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + PrecipitationsOptions.prototype.imageUrl = ""; + PrecipitationsOptions.prototype.imageExpireTime = 900; + PrecipitationsOptions.prototype.maxColorDistance = 20; + PrecipitationsOptions.prototype.imageLevel = 5; + PrecipitationsOptions.prototype.weatherMapping = $util.emptyArray; + PrecipitationsOptions.prototype.cloudsLayerUrl = ""; + PrecipitationsOptions.prototype.animationDecelerationDelay = 20; + + var $types = { + 4 : "keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.WeatherMapping" + }; + $lazyTypes.push($types); + + PrecipitationsOptions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.imageUrl = reader.string(); + break; + case 2: + message.imageExpireTime = reader.int32(); + break; + case 3: + message.maxColorDistance = reader.int32(); + break; + case 4: + message.imageLevel = reader.int32(); + break; + case 5: + if (!(message.weatherMapping && message.weatherMapping.length)) + message.weatherMapping = []; + message.weatherMapping.push($types[4].decode(reader, reader.uint32())); + break; + case 6: + message.cloudsLayerUrl = reader.string(); + break; + case 7: + message.animationDecelerationDelay = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + PrecipitationsOptions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.imageUrl !== undefined) + if (!$util.isString(message.imageUrl)) + return "imageUrl: string expected"; + if (message.imageExpireTime !== undefined) + if (!$util.isInteger(message.imageExpireTime)) + return "imageExpireTime: integer expected"; + if (message.maxColorDistance !== undefined) + if (!$util.isInteger(message.maxColorDistance)) + return "maxColorDistance: integer expected"; + if (message.imageLevel !== undefined) + if (!$util.isInteger(message.imageLevel)) + return "imageLevel: integer expected"; + if (message.weatherMapping !== undefined) { + if (!Array.isArray(message.weatherMapping)) + return "weatherMapping: array expected"; + for (var i = 0; i < message.weatherMapping.length; ++i) { + var error = $types[4].verify(message.weatherMapping[i]); + if (error) + return "weatherMapping." + error; + } + } + if (message.cloudsLayerUrl !== undefined) + if (!$util.isString(message.cloudsLayerUrl)) + return "cloudsLayerUrl: string expected"; + if (message.animationDecelerationDelay !== undefined) + if (typeof message.animationDecelerationDelay !== "number") + return "animationDecelerationDelay: number expected"; + return null; + }; + + PrecipitationsOptions.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions) + return object; + var message = new $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions(); + if (object.imageUrl !== undefined && object.imageUrl !== null) + message.imageUrl = String(object.imageUrl); + if (object.imageExpireTime !== undefined && object.imageExpireTime !== null) + message.imageExpireTime = object.imageExpireTime | 0; + if (object.maxColorDistance !== undefined && object.maxColorDistance !== null) + message.maxColorDistance = object.maxColorDistance | 0; + if (object.imageLevel !== undefined && object.imageLevel !== null) + message.imageLevel = object.imageLevel | 0; + if (object.weatherMapping) { + if (!Array.isArray(object.weatherMapping)) + throw TypeError(".keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.weatherMapping: array expected"); + message.weatherMapping = []; + for (var i = 0; i < object.weatherMapping.length; ++i) { + if (typeof object.weatherMapping[i] !== "object") + throw TypeError(".keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.weatherMapping: object expected"); + message.weatherMapping[i] = $types[4].fromObject(object.weatherMapping[i]); + } + } + if (object.cloudsLayerUrl !== undefined && object.cloudsLayerUrl !== null) + message.cloudsLayerUrl = String(object.cloudsLayerUrl); + if (object.animationDecelerationDelay !== undefined && object.animationDecelerationDelay !== null) + message.animationDecelerationDelay = Number(object.animationDecelerationDelay); + return message; + }; + + PrecipitationsOptions.from = PrecipitationsOptions.fromObject; + + PrecipitationsOptions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.weatherMapping = []; + if (options.defaults) { + object.imageUrl = ""; + object.imageExpireTime = 900; + object.maxColorDistance = 20; + object.imageLevel = 5; + object.cloudsLayerUrl = ""; + object.animationDecelerationDelay = 20; + } + if (message.imageUrl !== undefined && message.imageUrl !== null && message.hasOwnProperty("imageUrl")) + object.imageUrl = message.imageUrl; + if (message.imageExpireTime !== undefined && message.imageExpireTime !== null && message.hasOwnProperty("imageExpireTime")) + object.imageExpireTime = message.imageExpireTime; + if (message.maxColorDistance !== undefined && message.maxColorDistance !== null && message.hasOwnProperty("maxColorDistance")) + object.maxColorDistance = message.maxColorDistance; + if (message.imageLevel !== undefined && message.imageLevel !== null && message.hasOwnProperty("imageLevel")) + object.imageLevel = message.imageLevel; + if (message.weatherMapping !== undefined && message.weatherMapping !== null && message.hasOwnProperty("weatherMapping")) { + object.weatherMapping = []; + for (var j = 0; j < message.weatherMapping.length; ++j) + object.weatherMapping[j] = $types[4].toObject(message.weatherMapping[j], options); + } + if (message.cloudsLayerUrl !== undefined && message.cloudsLayerUrl !== null && message.hasOwnProperty("cloudsLayerUrl")) + object.cloudsLayerUrl = message.cloudsLayerUrl; + if (message.animationDecelerationDelay !== undefined && message.animationDecelerationDelay !== null && message.hasOwnProperty("animationDecelerationDelay")) + object.animationDecelerationDelay = message.animationDecelerationDelay; + return object; + }; + + PrecipitationsOptions.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + PrecipitationsOptions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + PrecipitationsOptions.WeatherMapping = (function() { + + function WeatherMapping(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + WeatherMapping.prototype.colorAbgr = 0; + WeatherMapping.prototype.weatherType = 0; + WeatherMapping.prototype.elongation = 1; + WeatherMapping.prototype.opacity = 0; + WeatherMapping.prototype.fogDensity = 0; + WeatherMapping.prototype.speed0 = 0; + WeatherMapping.prototype.speed1 = 0; + WeatherMapping.prototype.speed2 = 0; + WeatherMapping.prototype.speed3 = 0; + + var $types = { + 1 : "keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.WeatherMapping.WeatherType" + }; + $lazyTypes.push($types); + + WeatherMapping.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.WeatherMapping(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.colorAbgr = reader.uint32(); + break; + case 2: + message.weatherType = reader.uint32(); + break; + case 3: + message.elongation = reader.float(); + break; + case 4: + message.opacity = reader.float(); + break; + case 5: + message.fogDensity = reader.float(); + break; + case 6: + message.speed0 = reader.float(); + break; + case 7: + message.speed1 = reader.float(); + break; + case 8: + message.speed2 = reader.float(); + break; + case 9: + message.speed3 = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + WeatherMapping.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.colorAbgr)) + return "colorAbgr: integer expected"; + switch (message.weatherType) { + default: + return "weatherType: enum value expected"; + case 0: + case 1: + case 2: + break; + } + if (message.elongation !== undefined) + if (typeof message.elongation !== "number") + return "elongation: number expected"; + if (message.opacity !== undefined) + if (typeof message.opacity !== "number") + return "opacity: number expected"; + if (message.fogDensity !== undefined) + if (typeof message.fogDensity !== "number") + return "fogDensity: number expected"; + if (message.speed0 !== undefined) + if (typeof message.speed0 !== "number") + return "speed0: number expected"; + if (message.speed1 !== undefined) + if (typeof message.speed1 !== "number") + return "speed1: number expected"; + if (message.speed2 !== undefined) + if (typeof message.speed2 !== "number") + return "speed2: number expected"; + if (message.speed3 !== undefined) + if (typeof message.speed3 !== "number") + return "speed3: number expected"; + return null; + }; + + WeatherMapping.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.WeatherMapping) + return object; + var message = new $root.keyhole.dbroot.ClientOptionsProto.PrecipitationsOptions.WeatherMapping(); + if (object.colorAbgr !== undefined && object.colorAbgr !== null) + message.colorAbgr = object.colorAbgr >>> 0; + switch (object.weatherType) { + case "NO_PRECIPITATION": + case 0: + message.weatherType = 0; + break; + case "RAIN": + case 1: + message.weatherType = 1; + break; + case "SNOW": + case 2: + message.weatherType = 2; + break; + } + if (object.elongation !== undefined && object.elongation !== null) + message.elongation = Number(object.elongation); + if (object.opacity !== undefined && object.opacity !== null) + message.opacity = Number(object.opacity); + if (object.fogDensity !== undefined && object.fogDensity !== null) + message.fogDensity = Number(object.fogDensity); + if (object.speed0 !== undefined && object.speed0 !== null) + message.speed0 = Number(object.speed0); + if (object.speed1 !== undefined && object.speed1 !== null) + message.speed1 = Number(object.speed1); + if (object.speed2 !== undefined && object.speed2 !== null) + message.speed2 = Number(object.speed2); + if (object.speed3 !== undefined && object.speed3 !== null) + message.speed3 = Number(object.speed3); + return message; + }; + + WeatherMapping.from = WeatherMapping.fromObject; + + WeatherMapping.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.colorAbgr = 0; + object.weatherType = options.enums === String ? "NO_PRECIPITATION" : 0; + object.elongation = 1; + object.opacity = 0; + object.fogDensity = 0; + object.speed0 = 0; + object.speed1 = 0; + object.speed2 = 0; + object.speed3 = 0; + } + if (message.colorAbgr !== undefined && message.colorAbgr !== null && message.hasOwnProperty("colorAbgr")) + object.colorAbgr = message.colorAbgr; + if (message.weatherType !== undefined && message.weatherType !== null && message.hasOwnProperty("weatherType")) + object.weatherType = options.enums === String ? $types[1][message.weatherType] : message.weatherType; + if (message.elongation !== undefined && message.elongation !== null && message.hasOwnProperty("elongation")) + object.elongation = message.elongation; + if (message.opacity !== undefined && message.opacity !== null && message.hasOwnProperty("opacity")) + object.opacity = message.opacity; + if (message.fogDensity !== undefined && message.fogDensity !== null && message.hasOwnProperty("fogDensity")) + object.fogDensity = message.fogDensity; + if (message.speed0 !== undefined && message.speed0 !== null && message.hasOwnProperty("speed0")) + object.speed0 = message.speed0; + if (message.speed1 !== undefined && message.speed1 !== null && message.hasOwnProperty("speed1")) + object.speed1 = message.speed1; + if (message.speed2 !== undefined && message.speed2 !== null && message.hasOwnProperty("speed2")) + object.speed2 = message.speed2; + if (message.speed3 !== undefined && message.speed3 !== null && message.hasOwnProperty("speed3")) + object.speed3 = message.speed3; + return object; + }; + + WeatherMapping.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + WeatherMapping.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + WeatherMapping.WeatherType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["NO_PRECIPITATION"] = 0; + values["RAIN"] = 1; + values["SNOW"] = 2; + return values; + })(); + + return WeatherMapping; + })(); + + return PrecipitationsOptions; + })(); + + ClientOptionsProto.CaptureOptions = (function() { + + function CaptureOptions(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + CaptureOptions.prototype.allowSaveAsImage = true; + CaptureOptions.prototype.maxFreeCaptureRes = 2400; + CaptureOptions.prototype.maxPremiumCaptureRes = 4800; + + CaptureOptions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ClientOptionsProto.CaptureOptions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.allowSaveAsImage = reader.bool(); + break; + case 2: + message.maxFreeCaptureRes = reader.int32(); + break; + case 3: + message.maxPremiumCaptureRes = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + CaptureOptions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.allowSaveAsImage !== undefined) + if (typeof message.allowSaveAsImage !== "boolean") + return "allowSaveAsImage: boolean expected"; + if (message.maxFreeCaptureRes !== undefined) + if (!$util.isInteger(message.maxFreeCaptureRes)) + return "maxFreeCaptureRes: integer expected"; + if (message.maxPremiumCaptureRes !== undefined) + if (!$util.isInteger(message.maxPremiumCaptureRes)) + return "maxPremiumCaptureRes: integer expected"; + return null; + }; + + CaptureOptions.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ClientOptionsProto.CaptureOptions) + return object; + var message = new $root.keyhole.dbroot.ClientOptionsProto.CaptureOptions(); + if (object.allowSaveAsImage !== undefined && object.allowSaveAsImage !== null) + message.allowSaveAsImage = Boolean(object.allowSaveAsImage); + if (object.maxFreeCaptureRes !== undefined && object.maxFreeCaptureRes !== null) + message.maxFreeCaptureRes = object.maxFreeCaptureRes | 0; + if (object.maxPremiumCaptureRes !== undefined && object.maxPremiumCaptureRes !== null) + message.maxPremiumCaptureRes = object.maxPremiumCaptureRes | 0; + return message; + }; + + CaptureOptions.from = CaptureOptions.fromObject; + + CaptureOptions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.allowSaveAsImage = true; + object.maxFreeCaptureRes = 2400; + object.maxPremiumCaptureRes = 4800; + } + if (message.allowSaveAsImage !== undefined && message.allowSaveAsImage !== null && message.hasOwnProperty("allowSaveAsImage")) + object.allowSaveAsImage = message.allowSaveAsImage; + if (message.maxFreeCaptureRes !== undefined && message.maxFreeCaptureRes !== null && message.hasOwnProperty("maxFreeCaptureRes")) + object.maxFreeCaptureRes = message.maxFreeCaptureRes; + if (message.maxPremiumCaptureRes !== undefined && message.maxPremiumCaptureRes !== null && message.hasOwnProperty("maxPremiumCaptureRes")) + object.maxPremiumCaptureRes = message.maxPremiumCaptureRes; + return object; + }; + + CaptureOptions.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + CaptureOptions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return CaptureOptions; + })(); + + ClientOptionsProto.MapsOptions = (function() { + + function MapsOptions(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + MapsOptions.prototype.enableMaps = false; + MapsOptions.prototype.docsAutoDownloadEnabled = false; + MapsOptions.prototype.docsAutoDownloadInterval = 0; + MapsOptions.prototype.docsAutoUploadEnabled = false; + MapsOptions.prototype.docsAutoUploadDelay = 0; + + MapsOptions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ClientOptionsProto.MapsOptions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.enableMaps = reader.bool(); + break; + case 2: + message.docsAutoDownloadEnabled = reader.bool(); + break; + case 3: + message.docsAutoDownloadInterval = reader.int32(); + break; + case 4: + message.docsAutoUploadEnabled = reader.bool(); + break; + case 5: + message.docsAutoUploadDelay = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + MapsOptions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.enableMaps !== undefined) + if (typeof message.enableMaps !== "boolean") + return "enableMaps: boolean expected"; + if (message.docsAutoDownloadEnabled !== undefined) + if (typeof message.docsAutoDownloadEnabled !== "boolean") + return "docsAutoDownloadEnabled: boolean expected"; + if (message.docsAutoDownloadInterval !== undefined) + if (!$util.isInteger(message.docsAutoDownloadInterval)) + return "docsAutoDownloadInterval: integer expected"; + if (message.docsAutoUploadEnabled !== undefined) + if (typeof message.docsAutoUploadEnabled !== "boolean") + return "docsAutoUploadEnabled: boolean expected"; + if (message.docsAutoUploadDelay !== undefined) + if (!$util.isInteger(message.docsAutoUploadDelay)) + return "docsAutoUploadDelay: integer expected"; + return null; + }; + + MapsOptions.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ClientOptionsProto.MapsOptions) + return object; + var message = new $root.keyhole.dbroot.ClientOptionsProto.MapsOptions(); + if (object.enableMaps !== undefined && object.enableMaps !== null) + message.enableMaps = Boolean(object.enableMaps); + if (object.docsAutoDownloadEnabled !== undefined && object.docsAutoDownloadEnabled !== null) + message.docsAutoDownloadEnabled = Boolean(object.docsAutoDownloadEnabled); + if (object.docsAutoDownloadInterval !== undefined && object.docsAutoDownloadInterval !== null) + message.docsAutoDownloadInterval = object.docsAutoDownloadInterval | 0; + if (object.docsAutoUploadEnabled !== undefined && object.docsAutoUploadEnabled !== null) + message.docsAutoUploadEnabled = Boolean(object.docsAutoUploadEnabled); + if (object.docsAutoUploadDelay !== undefined && object.docsAutoUploadDelay !== null) + message.docsAutoUploadDelay = object.docsAutoUploadDelay | 0; + return message; + }; + + MapsOptions.from = MapsOptions.fromObject; + + MapsOptions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.enableMaps = false; + object.docsAutoDownloadEnabled = false; + object.docsAutoDownloadInterval = 0; + object.docsAutoUploadEnabled = false; + object.docsAutoUploadDelay = 0; + } + if (message.enableMaps !== undefined && message.enableMaps !== null && message.hasOwnProperty("enableMaps")) + object.enableMaps = message.enableMaps; + if (message.docsAutoDownloadEnabled !== undefined && message.docsAutoDownloadEnabled !== null && message.hasOwnProperty("docsAutoDownloadEnabled")) + object.docsAutoDownloadEnabled = message.docsAutoDownloadEnabled; + if (message.docsAutoDownloadInterval !== undefined && message.docsAutoDownloadInterval !== null && message.hasOwnProperty("docsAutoDownloadInterval")) + object.docsAutoDownloadInterval = message.docsAutoDownloadInterval; + if (message.docsAutoUploadEnabled !== undefined && message.docsAutoUploadEnabled !== null && message.hasOwnProperty("docsAutoUploadEnabled")) + object.docsAutoUploadEnabled = message.docsAutoUploadEnabled; + if (message.docsAutoUploadDelay !== undefined && message.docsAutoUploadDelay !== null && message.hasOwnProperty("docsAutoUploadDelay")) + object.docsAutoUploadDelay = message.docsAutoUploadDelay; + return object; + }; + + MapsOptions.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + MapsOptions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return MapsOptions; + })(); + + return ClientOptionsProto; + })(); + + dbroot.FetchingOptionsProto = (function() { + + function FetchingOptionsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + FetchingOptionsProto.prototype.maxRequestsPerQuery = 1; + FetchingOptionsProto.prototype.forceMaxRequestsPerQuery = false; + FetchingOptionsProto.prototype.sortBatches = false; + FetchingOptionsProto.prototype.maxDrawable = 2; + FetchingOptionsProto.prototype.maxImagery = 2; + FetchingOptionsProto.prototype.maxTerrain = 5; + FetchingOptionsProto.prototype.maxQuadtree = 5; + FetchingOptionsProto.prototype.maxDioramaMetadata = 1; + FetchingOptionsProto.prototype.maxDioramaData = 0; + FetchingOptionsProto.prototype.maxConsumerFetchRatio = 1; + FetchingOptionsProto.prototype.maxProEcFetchRatio = 0; + FetchingOptionsProto.prototype.safeOverallQps = 0; + FetchingOptionsProto.prototype.safeImageryQps = 0; + FetchingOptionsProto.prototype.domainsForHttps = "google.com gstatic.com"; + FetchingOptionsProto.prototype.hostsForHttp = ""; + + FetchingOptionsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.FetchingOptionsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.maxRequestsPerQuery = reader.int32(); + break; + case 12: + message.forceMaxRequestsPerQuery = reader.bool(); + break; + case 13: + message.sortBatches = reader.bool(); + break; + case 2: + message.maxDrawable = reader.int32(); + break; + case 3: + message.maxImagery = reader.int32(); + break; + case 4: + message.maxTerrain = reader.int32(); + break; + case 5: + message.maxQuadtree = reader.int32(); + break; + case 6: + message.maxDioramaMetadata = reader.int32(); + break; + case 7: + message.maxDioramaData = reader.int32(); + break; + case 8: + message.maxConsumerFetchRatio = reader.float(); + break; + case 9: + message.maxProEcFetchRatio = reader.float(); + break; + case 10: + message.safeOverallQps = reader.float(); + break; + case 11: + message.safeImageryQps = reader.float(); + break; + case 14: + message.domainsForHttps = reader.string(); + break; + case 15: + message.hostsForHttp = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + FetchingOptionsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.maxRequestsPerQuery !== undefined) + if (!$util.isInteger(message.maxRequestsPerQuery)) + return "maxRequestsPerQuery: integer expected"; + if (message.forceMaxRequestsPerQuery !== undefined) + if (typeof message.forceMaxRequestsPerQuery !== "boolean") + return "forceMaxRequestsPerQuery: boolean expected"; + if (message.sortBatches !== undefined) + if (typeof message.sortBatches !== "boolean") + return "sortBatches: boolean expected"; + if (message.maxDrawable !== undefined) + if (!$util.isInteger(message.maxDrawable)) + return "maxDrawable: integer expected"; + if (message.maxImagery !== undefined) + if (!$util.isInteger(message.maxImagery)) + return "maxImagery: integer expected"; + if (message.maxTerrain !== undefined) + if (!$util.isInteger(message.maxTerrain)) + return "maxTerrain: integer expected"; + if (message.maxQuadtree !== undefined) + if (!$util.isInteger(message.maxQuadtree)) + return "maxQuadtree: integer expected"; + if (message.maxDioramaMetadata !== undefined) + if (!$util.isInteger(message.maxDioramaMetadata)) + return "maxDioramaMetadata: integer expected"; + if (message.maxDioramaData !== undefined) + if (!$util.isInteger(message.maxDioramaData)) + return "maxDioramaData: integer expected"; + if (message.maxConsumerFetchRatio !== undefined) + if (typeof message.maxConsumerFetchRatio !== "number") + return "maxConsumerFetchRatio: number expected"; + if (message.maxProEcFetchRatio !== undefined) + if (typeof message.maxProEcFetchRatio !== "number") + return "maxProEcFetchRatio: number expected"; + if (message.safeOverallQps !== undefined) + if (typeof message.safeOverallQps !== "number") + return "safeOverallQps: number expected"; + if (message.safeImageryQps !== undefined) + if (typeof message.safeImageryQps !== "number") + return "safeImageryQps: number expected"; + if (message.domainsForHttps !== undefined) + if (!$util.isString(message.domainsForHttps)) + return "domainsForHttps: string expected"; + if (message.hostsForHttp !== undefined) + if (!$util.isString(message.hostsForHttp)) + return "hostsForHttp: string expected"; + return null; + }; + + FetchingOptionsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.FetchingOptionsProto) + return object; + var message = new $root.keyhole.dbroot.FetchingOptionsProto(); + if (object.maxRequestsPerQuery !== undefined && object.maxRequestsPerQuery !== null) + message.maxRequestsPerQuery = object.maxRequestsPerQuery | 0; + if (object.forceMaxRequestsPerQuery !== undefined && object.forceMaxRequestsPerQuery !== null) + message.forceMaxRequestsPerQuery = Boolean(object.forceMaxRequestsPerQuery); + if (object.sortBatches !== undefined && object.sortBatches !== null) + message.sortBatches = Boolean(object.sortBatches); + if (object.maxDrawable !== undefined && object.maxDrawable !== null) + message.maxDrawable = object.maxDrawable | 0; + if (object.maxImagery !== undefined && object.maxImagery !== null) + message.maxImagery = object.maxImagery | 0; + if (object.maxTerrain !== undefined && object.maxTerrain !== null) + message.maxTerrain = object.maxTerrain | 0; + if (object.maxQuadtree !== undefined && object.maxQuadtree !== null) + message.maxQuadtree = object.maxQuadtree | 0; + if (object.maxDioramaMetadata !== undefined && object.maxDioramaMetadata !== null) + message.maxDioramaMetadata = object.maxDioramaMetadata | 0; + if (object.maxDioramaData !== undefined && object.maxDioramaData !== null) + message.maxDioramaData = object.maxDioramaData | 0; + if (object.maxConsumerFetchRatio !== undefined && object.maxConsumerFetchRatio !== null) + message.maxConsumerFetchRatio = Number(object.maxConsumerFetchRatio); + if (object.maxProEcFetchRatio !== undefined && object.maxProEcFetchRatio !== null) + message.maxProEcFetchRatio = Number(object.maxProEcFetchRatio); + if (object.safeOverallQps !== undefined && object.safeOverallQps !== null) + message.safeOverallQps = Number(object.safeOverallQps); + if (object.safeImageryQps !== undefined && object.safeImageryQps !== null) + message.safeImageryQps = Number(object.safeImageryQps); + if (object.domainsForHttps !== undefined && object.domainsForHttps !== null) + message.domainsForHttps = String(object.domainsForHttps); + if (object.hostsForHttp !== undefined && object.hostsForHttp !== null) + message.hostsForHttp = String(object.hostsForHttp); + return message; + }; + + FetchingOptionsProto.from = FetchingOptionsProto.fromObject; + + FetchingOptionsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.maxRequestsPerQuery = 1; + object.forceMaxRequestsPerQuery = false; + object.sortBatches = false; + object.maxDrawable = 2; + object.maxImagery = 2; + object.maxTerrain = 5; + object.maxQuadtree = 5; + object.maxDioramaMetadata = 1; + object.maxDioramaData = 0; + object.maxConsumerFetchRatio = 1; + object.maxProEcFetchRatio = 0; + object.safeOverallQps = 0; + object.safeImageryQps = 0; + object.domainsForHttps = "google.com gstatic.com"; + object.hostsForHttp = ""; + } + if (message.maxRequestsPerQuery !== undefined && message.maxRequestsPerQuery !== null && message.hasOwnProperty("maxRequestsPerQuery")) + object.maxRequestsPerQuery = message.maxRequestsPerQuery; + if (message.forceMaxRequestsPerQuery !== undefined && message.forceMaxRequestsPerQuery !== null && message.hasOwnProperty("forceMaxRequestsPerQuery")) + object.forceMaxRequestsPerQuery = message.forceMaxRequestsPerQuery; + if (message.sortBatches !== undefined && message.sortBatches !== null && message.hasOwnProperty("sortBatches")) + object.sortBatches = message.sortBatches; + if (message.maxDrawable !== undefined && message.maxDrawable !== null && message.hasOwnProperty("maxDrawable")) + object.maxDrawable = message.maxDrawable; + if (message.maxImagery !== undefined && message.maxImagery !== null && message.hasOwnProperty("maxImagery")) + object.maxImagery = message.maxImagery; + if (message.maxTerrain !== undefined && message.maxTerrain !== null && message.hasOwnProperty("maxTerrain")) + object.maxTerrain = message.maxTerrain; + if (message.maxQuadtree !== undefined && message.maxQuadtree !== null && message.hasOwnProperty("maxQuadtree")) + object.maxQuadtree = message.maxQuadtree; + if (message.maxDioramaMetadata !== undefined && message.maxDioramaMetadata !== null && message.hasOwnProperty("maxDioramaMetadata")) + object.maxDioramaMetadata = message.maxDioramaMetadata; + if (message.maxDioramaData !== undefined && message.maxDioramaData !== null && message.hasOwnProperty("maxDioramaData")) + object.maxDioramaData = message.maxDioramaData; + if (message.maxConsumerFetchRatio !== undefined && message.maxConsumerFetchRatio !== null && message.hasOwnProperty("maxConsumerFetchRatio")) + object.maxConsumerFetchRatio = message.maxConsumerFetchRatio; + if (message.maxProEcFetchRatio !== undefined && message.maxProEcFetchRatio !== null && message.hasOwnProperty("maxProEcFetchRatio")) + object.maxProEcFetchRatio = message.maxProEcFetchRatio; + if (message.safeOverallQps !== undefined && message.safeOverallQps !== null && message.hasOwnProperty("safeOverallQps")) + object.safeOverallQps = message.safeOverallQps; + if (message.safeImageryQps !== undefined && message.safeImageryQps !== null && message.hasOwnProperty("safeImageryQps")) + object.safeImageryQps = message.safeImageryQps; + if (message.domainsForHttps !== undefined && message.domainsForHttps !== null && message.hasOwnProperty("domainsForHttps")) + object.domainsForHttps = message.domainsForHttps; + if (message.hostsForHttp !== undefined && message.hostsForHttp !== null && message.hasOwnProperty("hostsForHttp")) + object.hostsForHttp = message.hostsForHttp; + return object; + }; + + FetchingOptionsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + FetchingOptionsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return FetchingOptionsProto; + })(); + + dbroot.TimeMachineOptionsProto = (function() { + + function TimeMachineOptionsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + TimeMachineOptionsProto.prototype.serverUrl = ""; + TimeMachineOptionsProto.prototype.isTimemachine = false; + TimeMachineOptionsProto.prototype.dwellTimeMs = 500; + TimeMachineOptionsProto.prototype.discoverabilityAltitudeMeters = 15000; + + TimeMachineOptionsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.TimeMachineOptionsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.serverUrl = reader.string(); + break; + case 2: + message.isTimemachine = reader.bool(); + break; + case 3: + message.dwellTimeMs = reader.int32(); + break; + case 4: + message.discoverabilityAltitudeMeters = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + TimeMachineOptionsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.serverUrl !== undefined) + if (!$util.isString(message.serverUrl)) + return "serverUrl: string expected"; + if (message.isTimemachine !== undefined) + if (typeof message.isTimemachine !== "boolean") + return "isTimemachine: boolean expected"; + if (message.dwellTimeMs !== undefined) + if (!$util.isInteger(message.dwellTimeMs)) + return "dwellTimeMs: integer expected"; + if (message.discoverabilityAltitudeMeters !== undefined) + if (!$util.isInteger(message.discoverabilityAltitudeMeters)) + return "discoverabilityAltitudeMeters: integer expected"; + return null; + }; + + TimeMachineOptionsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.TimeMachineOptionsProto) + return object; + var message = new $root.keyhole.dbroot.TimeMachineOptionsProto(); + if (object.serverUrl !== undefined && object.serverUrl !== null) + message.serverUrl = String(object.serverUrl); + if (object.isTimemachine !== undefined && object.isTimemachine !== null) + message.isTimemachine = Boolean(object.isTimemachine); + if (object.dwellTimeMs !== undefined && object.dwellTimeMs !== null) + message.dwellTimeMs = object.dwellTimeMs | 0; + if (object.discoverabilityAltitudeMeters !== undefined && object.discoverabilityAltitudeMeters !== null) + message.discoverabilityAltitudeMeters = object.discoverabilityAltitudeMeters | 0; + return message; + }; + + TimeMachineOptionsProto.from = TimeMachineOptionsProto.fromObject; + + TimeMachineOptionsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.serverUrl = ""; + object.isTimemachine = false; + object.dwellTimeMs = 500; + object.discoverabilityAltitudeMeters = 15000; + } + if (message.serverUrl !== undefined && message.serverUrl !== null && message.hasOwnProperty("serverUrl")) + object.serverUrl = message.serverUrl; + if (message.isTimemachine !== undefined && message.isTimemachine !== null && message.hasOwnProperty("isTimemachine")) + object.isTimemachine = message.isTimemachine; + if (message.dwellTimeMs !== undefined && message.dwellTimeMs !== null && message.hasOwnProperty("dwellTimeMs")) + object.dwellTimeMs = message.dwellTimeMs; + if (message.discoverabilityAltitudeMeters !== undefined && message.discoverabilityAltitudeMeters !== null && message.hasOwnProperty("discoverabilityAltitudeMeters")) + object.discoverabilityAltitudeMeters = message.discoverabilityAltitudeMeters; + return object; + }; + + TimeMachineOptionsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + TimeMachineOptionsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return TimeMachineOptionsProto; + })(); + + dbroot.AutopiaOptionsProto = (function() { + + function AutopiaOptionsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + AutopiaOptionsProto.prototype.metadataServerUrl = "http://cbk0.google.com/cbk"; + AutopiaOptionsProto.prototype.depthmapServerUrl = "http://cbk0.google.com/cbk"; + AutopiaOptionsProto.prototype.coverageOverlayUrl = ""; + AutopiaOptionsProto.prototype.maxImageryQps = 0; + AutopiaOptionsProto.prototype.maxMetadataDepthmapQps = 0; + + AutopiaOptionsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.AutopiaOptionsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.metadataServerUrl = reader.string(); + break; + case 2: + message.depthmapServerUrl = reader.string(); + break; + case 3: + message.coverageOverlayUrl = reader.string(); + break; + case 4: + message.maxImageryQps = reader.float(); + break; + case 5: + message.maxMetadataDepthmapQps = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + AutopiaOptionsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.metadataServerUrl !== undefined) + if (!$util.isString(message.metadataServerUrl)) + return "metadataServerUrl: string expected"; + if (message.depthmapServerUrl !== undefined) + if (!$util.isString(message.depthmapServerUrl)) + return "depthmapServerUrl: string expected"; + if (message.coverageOverlayUrl !== undefined) + if (!$util.isString(message.coverageOverlayUrl)) + return "coverageOverlayUrl: string expected"; + if (message.maxImageryQps !== undefined) + if (typeof message.maxImageryQps !== "number") + return "maxImageryQps: number expected"; + if (message.maxMetadataDepthmapQps !== undefined) + if (typeof message.maxMetadataDepthmapQps !== "number") + return "maxMetadataDepthmapQps: number expected"; + return null; + }; + + AutopiaOptionsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.AutopiaOptionsProto) + return object; + var message = new $root.keyhole.dbroot.AutopiaOptionsProto(); + if (object.metadataServerUrl !== undefined && object.metadataServerUrl !== null) + message.metadataServerUrl = String(object.metadataServerUrl); + if (object.depthmapServerUrl !== undefined && object.depthmapServerUrl !== null) + message.depthmapServerUrl = String(object.depthmapServerUrl); + if (object.coverageOverlayUrl !== undefined && object.coverageOverlayUrl !== null) + message.coverageOverlayUrl = String(object.coverageOverlayUrl); + if (object.maxImageryQps !== undefined && object.maxImageryQps !== null) + message.maxImageryQps = Number(object.maxImageryQps); + if (object.maxMetadataDepthmapQps !== undefined && object.maxMetadataDepthmapQps !== null) + message.maxMetadataDepthmapQps = Number(object.maxMetadataDepthmapQps); + return message; + }; + + AutopiaOptionsProto.from = AutopiaOptionsProto.fromObject; + + AutopiaOptionsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.metadataServerUrl = "http://cbk0.google.com/cbk"; + object.depthmapServerUrl = "http://cbk0.google.com/cbk"; + object.coverageOverlayUrl = ""; + object.maxImageryQps = 0; + object.maxMetadataDepthmapQps = 0; + } + if (message.metadataServerUrl !== undefined && message.metadataServerUrl !== null && message.hasOwnProperty("metadataServerUrl")) + object.metadataServerUrl = message.metadataServerUrl; + if (message.depthmapServerUrl !== undefined && message.depthmapServerUrl !== null && message.hasOwnProperty("depthmapServerUrl")) + object.depthmapServerUrl = message.depthmapServerUrl; + if (message.coverageOverlayUrl !== undefined && message.coverageOverlayUrl !== null && message.hasOwnProperty("coverageOverlayUrl")) + object.coverageOverlayUrl = message.coverageOverlayUrl; + if (message.maxImageryQps !== undefined && message.maxImageryQps !== null && message.hasOwnProperty("maxImageryQps")) + object.maxImageryQps = message.maxImageryQps; + if (message.maxMetadataDepthmapQps !== undefined && message.maxMetadataDepthmapQps !== null && message.hasOwnProperty("maxMetadataDepthmapQps")) + object.maxMetadataDepthmapQps = message.maxMetadataDepthmapQps; + return object; + }; + + AutopiaOptionsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + AutopiaOptionsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return AutopiaOptionsProto; + })(); + + dbroot.CSIOptionsProto = (function() { + + function CSIOptionsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + CSIOptionsProto.prototype.samplingPercentage = 0; + CSIOptionsProto.prototype.experimentId = ""; + + CSIOptionsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.CSIOptionsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.samplingPercentage = reader.int32(); + break; + case 2: + message.experimentId = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + CSIOptionsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.samplingPercentage !== undefined) + if (!$util.isInteger(message.samplingPercentage)) + return "samplingPercentage: integer expected"; + if (message.experimentId !== undefined) + if (!$util.isString(message.experimentId)) + return "experimentId: string expected"; + return null; + }; + + CSIOptionsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.CSIOptionsProto) + return object; + var message = new $root.keyhole.dbroot.CSIOptionsProto(); + if (object.samplingPercentage !== undefined && object.samplingPercentage !== null) + message.samplingPercentage = object.samplingPercentage | 0; + if (object.experimentId !== undefined && object.experimentId !== null) + message.experimentId = String(object.experimentId); + return message; + }; + + CSIOptionsProto.from = CSIOptionsProto.fromObject; + + CSIOptionsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.samplingPercentage = 0; + object.experimentId = ""; + } + if (message.samplingPercentage !== undefined && message.samplingPercentage !== null && message.hasOwnProperty("samplingPercentage")) + object.samplingPercentage = message.samplingPercentage; + if (message.experimentId !== undefined && message.experimentId !== null && message.hasOwnProperty("experimentId")) + object.experimentId = message.experimentId; + return object; + }; + + CSIOptionsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + CSIOptionsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return CSIOptionsProto; + })(); + + dbroot.SearchTabProto = (function() { + + function SearchTabProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SearchTabProto.prototype.isVisible = false; + SearchTabProto.prototype.tabLabel = null; + SearchTabProto.prototype.baseUrl = ""; + SearchTabProto.prototype.viewportPrefix = ""; + SearchTabProto.prototype.inputBox = $util.emptyArray; + SearchTabProto.prototype.requirement = null; + + var $types = { + 1 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.SearchTabProto.InputBoxInfo", + 5 : "keyhole.dbroot.RequirementProto" + }; + $lazyTypes.push($types); + + SearchTabProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.SearchTabProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.isVisible = reader.bool(); + break; + case 2: + message.tabLabel = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.baseUrl = reader.string(); + break; + case 4: + message.viewportPrefix = reader.string(); + break; + case 5: + if (!(message.inputBox && message.inputBox.length)) + message.inputBox = []; + message.inputBox.push($types[4].decode(reader, reader.uint32())); + break; + case 6: + message.requirement = $types[5].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SearchTabProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (typeof message.isVisible !== "boolean") + return "isVisible: boolean expected"; + if (message.tabLabel !== undefined && message.tabLabel !== null) { + var error = $types[1].verify(message.tabLabel); + if (error) + return "tabLabel." + error; + } + if (message.baseUrl !== undefined) + if (!$util.isString(message.baseUrl)) + return "baseUrl: string expected"; + if (message.viewportPrefix !== undefined) + if (!$util.isString(message.viewportPrefix)) + return "viewportPrefix: string expected"; + if (message.inputBox !== undefined) { + if (!Array.isArray(message.inputBox)) + return "inputBox: array expected"; + for (var i = 0; i < message.inputBox.length; ++i) { + var error = $types[4].verify(message.inputBox[i]); + if (error) + return "inputBox." + error; + } + } + if (message.requirement !== undefined && message.requirement !== null) { + var error = $types[5].verify(message.requirement); + if (error) + return "requirement." + error; + } + return null; + }; + + SearchTabProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.SearchTabProto) + return object; + var message = new $root.keyhole.dbroot.SearchTabProto(); + if (object.isVisible !== undefined && object.isVisible !== null) + message.isVisible = Boolean(object.isVisible); + if (object.tabLabel !== undefined && object.tabLabel !== null) { + if (typeof object.tabLabel !== "object") + throw TypeError(".keyhole.dbroot.SearchTabProto.tabLabel: object expected"); + message.tabLabel = $types[1].fromObject(object.tabLabel); + } + if (object.baseUrl !== undefined && object.baseUrl !== null) + message.baseUrl = String(object.baseUrl); + if (object.viewportPrefix !== undefined && object.viewportPrefix !== null) + message.viewportPrefix = String(object.viewportPrefix); + if (object.inputBox) { + if (!Array.isArray(object.inputBox)) + throw TypeError(".keyhole.dbroot.SearchTabProto.inputBox: array expected"); + message.inputBox = []; + for (var i = 0; i < object.inputBox.length; ++i) { + if (typeof object.inputBox[i] !== "object") + throw TypeError(".keyhole.dbroot.SearchTabProto.inputBox: object expected"); + message.inputBox[i] = $types[4].fromObject(object.inputBox[i]); + } + } + if (object.requirement !== undefined && object.requirement !== null) { + if (typeof object.requirement !== "object") + throw TypeError(".keyhole.dbroot.SearchTabProto.requirement: object expected"); + message.requirement = $types[5].fromObject(object.requirement); + } + return message; + }; + + SearchTabProto.from = SearchTabProto.fromObject; + + SearchTabProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.inputBox = []; + if (options.defaults) { + object.isVisible = false; + object.tabLabel = null; + object.baseUrl = ""; + object.viewportPrefix = ""; + object.requirement = null; + } + if (message.isVisible !== undefined && message.isVisible !== null && message.hasOwnProperty("isVisible")) + object.isVisible = message.isVisible; + if (message.tabLabel !== undefined && message.tabLabel !== null && message.hasOwnProperty("tabLabel")) + object.tabLabel = $types[1].toObject(message.tabLabel, options); + if (message.baseUrl !== undefined && message.baseUrl !== null && message.hasOwnProperty("baseUrl")) + object.baseUrl = message.baseUrl; + if (message.viewportPrefix !== undefined && message.viewportPrefix !== null && message.hasOwnProperty("viewportPrefix")) + object.viewportPrefix = message.viewportPrefix; + if (message.inputBox !== undefined && message.inputBox !== null && message.hasOwnProperty("inputBox")) { + object.inputBox = []; + for (var j = 0; j < message.inputBox.length; ++j) + object.inputBox[j] = $types[4].toObject(message.inputBox[j], options); + } + if (message.requirement !== undefined && message.requirement !== null && message.hasOwnProperty("requirement")) + object.requirement = $types[5].toObject(message.requirement, options); + return object; + }; + + SearchTabProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SearchTabProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + SearchTabProto.InputBoxInfo = (function() { + + function InputBoxInfo(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + InputBoxInfo.prototype.label = null; + InputBoxInfo.prototype.queryVerb = ""; + InputBoxInfo.prototype.queryPrepend = ""; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + InputBoxInfo.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.SearchTabProto.InputBoxInfo(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.label = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.queryVerb = reader.string(); + break; + case 3: + message.queryPrepend = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + InputBoxInfo.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var error = $types[0].verify(message.label); + if (error) + return "label." + error; + if (!$util.isString(message.queryVerb)) + return "queryVerb: string expected"; + if (message.queryPrepend !== undefined) + if (!$util.isString(message.queryPrepend)) + return "queryPrepend: string expected"; + return null; + }; + + InputBoxInfo.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.SearchTabProto.InputBoxInfo) + return object; + var message = new $root.keyhole.dbroot.SearchTabProto.InputBoxInfo(); + if (object.label !== undefined && object.label !== null) { + if (typeof object.label !== "object") + throw TypeError(".keyhole.dbroot.SearchTabProto.InputBoxInfo.label: object expected"); + message.label = $types[0].fromObject(object.label); + } + if (object.queryVerb !== undefined && object.queryVerb !== null) + message.queryVerb = String(object.queryVerb); + if (object.queryPrepend !== undefined && object.queryPrepend !== null) + message.queryPrepend = String(object.queryPrepend); + return message; + }; + + InputBoxInfo.from = InputBoxInfo.fromObject; + + InputBoxInfo.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.label = null; + object.queryVerb = ""; + object.queryPrepend = ""; + } + if (message.label !== undefined && message.label !== null && message.hasOwnProperty("label")) + object.label = $types[0].toObject(message.label, options); + if (message.queryVerb !== undefined && message.queryVerb !== null && message.hasOwnProperty("queryVerb")) + object.queryVerb = message.queryVerb; + if (message.queryPrepend !== undefined && message.queryPrepend !== null && message.hasOwnProperty("queryPrepend")) + object.queryPrepend = message.queryPrepend; + return object; + }; + + InputBoxInfo.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + InputBoxInfo.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return InputBoxInfo; + })(); + + return SearchTabProto; + })(); + + dbroot.CobrandProto = (function() { + + function CobrandProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + CobrandProto.prototype.logoUrl = ""; + CobrandProto.prototype.xCoord = null; + CobrandProto.prototype.yCoord = null; + CobrandProto.prototype.tiePoint = 6; + CobrandProto.prototype.screenSize = 0; + + var $types = { + 1 : "keyhole.dbroot.CobrandProto.Coord", + 2 : "keyhole.dbroot.CobrandProto.Coord", + 3 : "keyhole.dbroot.CobrandProto.TiePoint" + }; + $lazyTypes.push($types); + + CobrandProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.CobrandProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.logoUrl = reader.string(); + break; + case 2: + message.xCoord = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.yCoord = $types[2].decode(reader, reader.uint32()); + break; + case 4: + message.tiePoint = reader.uint32(); + break; + case 5: + message.screenSize = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + CobrandProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.logoUrl)) + return "logoUrl: string expected"; + if (message.xCoord !== undefined && message.xCoord !== null) { + var error = $types[1].verify(message.xCoord); + if (error) + return "xCoord." + error; + } + if (message.yCoord !== undefined && message.yCoord !== null) { + var error = $types[2].verify(message.yCoord); + if (error) + return "yCoord." + error; + } + if (message.tiePoint !== undefined) + switch (message.tiePoint) { + default: + return "tiePoint: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + break; + } + if (message.screenSize !== undefined) + if (typeof message.screenSize !== "number") + return "screenSize: number expected"; + return null; + }; + + CobrandProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.CobrandProto) + return object; + var message = new $root.keyhole.dbroot.CobrandProto(); + if (object.logoUrl !== undefined && object.logoUrl !== null) + message.logoUrl = String(object.logoUrl); + if (object.xCoord !== undefined && object.xCoord !== null) { + if (typeof object.xCoord !== "object") + throw TypeError(".keyhole.dbroot.CobrandProto.xCoord: object expected"); + message.xCoord = $types[1].fromObject(object.xCoord); + } + if (object.yCoord !== undefined && object.yCoord !== null) { + if (typeof object.yCoord !== "object") + throw TypeError(".keyhole.dbroot.CobrandProto.yCoord: object expected"); + message.yCoord = $types[2].fromObject(object.yCoord); + } + switch (object.tiePoint) { + case "TOP_LEFT": + case 0: + message.tiePoint = 0; + break; + case "TOP_CENTER": + case 1: + message.tiePoint = 1; + break; + case "TOP_RIGHT": + case 2: + message.tiePoint = 2; + break; + case "MID_LEFT": + case 3: + message.tiePoint = 3; + break; + case "MID_CENTER": + case 4: + message.tiePoint = 4; + break; + case "MID_RIGHT": + case 5: + message.tiePoint = 5; + break; + case "BOTTOM_LEFT": + case 6: + message.tiePoint = 6; + break; + case "BOTTOM_CENTER": + case 7: + message.tiePoint = 7; + break; + case "BOTTOM_RIGHT": + case 8: + message.tiePoint = 8; + break; + } + if (object.screenSize !== undefined && object.screenSize !== null) + message.screenSize = Number(object.screenSize); + return message; + }; + + CobrandProto.from = CobrandProto.fromObject; + + CobrandProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.logoUrl = ""; + object.xCoord = null; + object.yCoord = null; + object.tiePoint = options.enums === String ? "BOTTOM_LEFT" : 6; + object.screenSize = 0; + } + if (message.logoUrl !== undefined && message.logoUrl !== null && message.hasOwnProperty("logoUrl")) + object.logoUrl = message.logoUrl; + if (message.xCoord !== undefined && message.xCoord !== null && message.hasOwnProperty("xCoord")) + object.xCoord = $types[1].toObject(message.xCoord, options); + if (message.yCoord !== undefined && message.yCoord !== null && message.hasOwnProperty("yCoord")) + object.yCoord = $types[2].toObject(message.yCoord, options); + if (message.tiePoint !== undefined && message.tiePoint !== null && message.hasOwnProperty("tiePoint")) + object.tiePoint = options.enums === String ? $types[3][message.tiePoint] : message.tiePoint; + if (message.screenSize !== undefined && message.screenSize !== null && message.hasOwnProperty("screenSize")) + object.screenSize = message.screenSize; + return object; + }; + + CobrandProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + CobrandProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + CobrandProto.Coord = (function() { + + function Coord(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + Coord.prototype.value = 0; + Coord.prototype.isRelative = false; + + Coord.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.CobrandProto.Coord(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.double(); + break; + case 2: + message.isRelative = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + Coord.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (typeof message.value !== "number") + return "value: number expected"; + if (message.isRelative !== undefined) + if (typeof message.isRelative !== "boolean") + return "isRelative: boolean expected"; + return null; + }; + + Coord.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.CobrandProto.Coord) + return object; + var message = new $root.keyhole.dbroot.CobrandProto.Coord(); + if (object.value !== undefined && object.value !== null) + message.value = Number(object.value); + if (object.isRelative !== undefined && object.isRelative !== null) + message.isRelative = Boolean(object.isRelative); + return message; + }; + + Coord.from = Coord.fromObject; + + Coord.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.value = 0; + object.isRelative = false; + } + if (message.value !== undefined && message.value !== null && message.hasOwnProperty("value")) + object.value = message.value; + if (message.isRelative !== undefined && message.isRelative !== null && message.hasOwnProperty("isRelative")) + object.isRelative = message.isRelative; + return object; + }; + + Coord.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + Coord.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Coord; + })(); + + CobrandProto.TiePoint = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["TOP_LEFT"] = 0; + values["TOP_CENTER"] = 1; + values["TOP_RIGHT"] = 2; + values["MID_LEFT"] = 3; + values["MID_CENTER"] = 4; + values["MID_RIGHT"] = 5; + values["BOTTOM_LEFT"] = 6; + values["BOTTOM_CENTER"] = 7; + values["BOTTOM_RIGHT"] = 8; + return values; + })(); + + return CobrandProto; + })(); + + dbroot.DatabaseDescriptionProto = (function() { + + function DatabaseDescriptionProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + DatabaseDescriptionProto.prototype.databaseName = null; + DatabaseDescriptionProto.prototype.databaseUrl = ""; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + DatabaseDescriptionProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.DatabaseDescriptionProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.databaseName = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.databaseUrl = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + DatabaseDescriptionProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.databaseName !== undefined && message.databaseName !== null) { + var error = $types[0].verify(message.databaseName); + if (error) + return "databaseName." + error; + } + if (!$util.isString(message.databaseUrl)) + return "databaseUrl: string expected"; + return null; + }; + + DatabaseDescriptionProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.DatabaseDescriptionProto) + return object; + var message = new $root.keyhole.dbroot.DatabaseDescriptionProto(); + if (object.databaseName !== undefined && object.databaseName !== null) { + if (typeof object.databaseName !== "object") + throw TypeError(".keyhole.dbroot.DatabaseDescriptionProto.databaseName: object expected"); + message.databaseName = $types[0].fromObject(object.databaseName); + } + if (object.databaseUrl !== undefined && object.databaseUrl !== null) + message.databaseUrl = String(object.databaseUrl); + return message; + }; + + DatabaseDescriptionProto.from = DatabaseDescriptionProto.fromObject; + + DatabaseDescriptionProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.databaseName = null; + object.databaseUrl = ""; + } + if (message.databaseName !== undefined && message.databaseName !== null && message.hasOwnProperty("databaseName")) + object.databaseName = $types[0].toObject(message.databaseName, options); + if (message.databaseUrl !== undefined && message.databaseUrl !== null && message.hasOwnProperty("databaseUrl")) + object.databaseUrl = message.databaseUrl; + return object; + }; + + DatabaseDescriptionProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + DatabaseDescriptionProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return DatabaseDescriptionProto; + })(); + + dbroot.ConfigScriptProto = (function() { + + function ConfigScriptProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + ConfigScriptProto.prototype.scriptName = ""; + ConfigScriptProto.prototype.scriptData = ""; + + ConfigScriptProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.ConfigScriptProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.scriptName = reader.string(); + break; + case 2: + message.scriptData = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + ConfigScriptProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.scriptName)) + return "scriptName: string expected"; + if (!$util.isString(message.scriptData)) + return "scriptData: string expected"; + return null; + }; + + ConfigScriptProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.ConfigScriptProto) + return object; + var message = new $root.keyhole.dbroot.ConfigScriptProto(); + if (object.scriptName !== undefined && object.scriptName !== null) + message.scriptName = String(object.scriptName); + if (object.scriptData !== undefined && object.scriptData !== null) + message.scriptData = String(object.scriptData); + return message; + }; + + ConfigScriptProto.from = ConfigScriptProto.fromObject; + + ConfigScriptProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.scriptName = ""; + object.scriptData = ""; + } + if (message.scriptName !== undefined && message.scriptName !== null && message.hasOwnProperty("scriptName")) + object.scriptName = message.scriptName; + if (message.scriptData !== undefined && message.scriptData !== null && message.hasOwnProperty("scriptData")) + object.scriptData = message.scriptData; + return object; + }; + + ConfigScriptProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + ConfigScriptProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ConfigScriptProto; + })(); + + dbroot.SwoopParamsProto = (function() { + + function SwoopParamsProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SwoopParamsProto.prototype.startDistInMeters = 0; + + SwoopParamsProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.SwoopParamsProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.startDistInMeters = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SwoopParamsProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.startDistInMeters !== undefined) + if (typeof message.startDistInMeters !== "number") + return "startDistInMeters: number expected"; + return null; + }; + + SwoopParamsProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.SwoopParamsProto) + return object; + var message = new $root.keyhole.dbroot.SwoopParamsProto(); + if (object.startDistInMeters !== undefined && object.startDistInMeters !== null) + message.startDistInMeters = Number(object.startDistInMeters); + return message; + }; + + SwoopParamsProto.from = SwoopParamsProto.fromObject; + + SwoopParamsProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.startDistInMeters = 0; + if (message.startDistInMeters !== undefined && message.startDistInMeters !== null && message.hasOwnProperty("startDistInMeters")) + object.startDistInMeters = message.startDistInMeters; + return object; + }; + + SwoopParamsProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SwoopParamsProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SwoopParamsProto; + })(); + + dbroot.PostingServerProto = (function() { + + function PostingServerProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + PostingServerProto.prototype.name = null; + PostingServerProto.prototype.baseUrl = null; + PostingServerProto.prototype.postWizardPath = null; + PostingServerProto.prototype.fileSubmitPath = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 2 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + PostingServerProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.PostingServerProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.baseUrl = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.postWizardPath = $types[2].decode(reader, reader.uint32()); + break; + case 4: + message.fileSubmitPath = $types[3].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + PostingServerProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name !== undefined && message.name !== null) { + var error = $types[0].verify(message.name); + if (error) + return "name." + error; + } + if (message.baseUrl !== undefined && message.baseUrl !== null) { + var error = $types[1].verify(message.baseUrl); + if (error) + return "baseUrl." + error; + } + if (message.postWizardPath !== undefined && message.postWizardPath !== null) { + var error = $types[2].verify(message.postWizardPath); + if (error) + return "postWizardPath." + error; + } + if (message.fileSubmitPath !== undefined && message.fileSubmitPath !== null) { + var error = $types[3].verify(message.fileSubmitPath); + if (error) + return "fileSubmitPath." + error; + } + return null; + }; + + PostingServerProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.PostingServerProto) + return object; + var message = new $root.keyhole.dbroot.PostingServerProto(); + if (object.name !== undefined && object.name !== null) { + if (typeof object.name !== "object") + throw TypeError(".keyhole.dbroot.PostingServerProto.name: object expected"); + message.name = $types[0].fromObject(object.name); + } + if (object.baseUrl !== undefined && object.baseUrl !== null) { + if (typeof object.baseUrl !== "object") + throw TypeError(".keyhole.dbroot.PostingServerProto.baseUrl: object expected"); + message.baseUrl = $types[1].fromObject(object.baseUrl); + } + if (object.postWizardPath !== undefined && object.postWizardPath !== null) { + if (typeof object.postWizardPath !== "object") + throw TypeError(".keyhole.dbroot.PostingServerProto.postWizardPath: object expected"); + message.postWizardPath = $types[2].fromObject(object.postWizardPath); + } + if (object.fileSubmitPath !== undefined && object.fileSubmitPath !== null) { + if (typeof object.fileSubmitPath !== "object") + throw TypeError(".keyhole.dbroot.PostingServerProto.fileSubmitPath: object expected"); + message.fileSubmitPath = $types[3].fromObject(object.fileSubmitPath); + } + return message; + }; + + PostingServerProto.from = PostingServerProto.fromObject; + + PostingServerProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = null; + object.baseUrl = null; + object.postWizardPath = null; + object.fileSubmitPath = null; + } + if (message.name !== undefined && message.name !== null && message.hasOwnProperty("name")) + object.name = $types[0].toObject(message.name, options); + if (message.baseUrl !== undefined && message.baseUrl !== null && message.hasOwnProperty("baseUrl")) + object.baseUrl = $types[1].toObject(message.baseUrl, options); + if (message.postWizardPath !== undefined && message.postWizardPath !== null && message.hasOwnProperty("postWizardPath")) + object.postWizardPath = $types[2].toObject(message.postWizardPath, options); + if (message.fileSubmitPath !== undefined && message.fileSubmitPath !== null && message.hasOwnProperty("fileSubmitPath")) + object.fileSubmitPath = $types[3].toObject(message.fileSubmitPath, options); + return object; + }; + + PostingServerProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + PostingServerProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return PostingServerProto; + })(); + + dbroot.PlanetaryDatabaseProto = (function() { + + function PlanetaryDatabaseProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + PlanetaryDatabaseProto.prototype.url = null; + PlanetaryDatabaseProto.prototype.name = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + PlanetaryDatabaseProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.PlanetaryDatabaseProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.name = $types[1].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + PlanetaryDatabaseProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + var error = $types[1].verify(message.name); + if (error) + return "name." + error; + return null; + }; + + PlanetaryDatabaseProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.PlanetaryDatabaseProto) + return object; + var message = new $root.keyhole.dbroot.PlanetaryDatabaseProto(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.PlanetaryDatabaseProto.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + if (object.name !== undefined && object.name !== null) { + if (typeof object.name !== "object") + throw TypeError(".keyhole.dbroot.PlanetaryDatabaseProto.name: object expected"); + message.name = $types[1].fromObject(object.name); + } + return message; + }; + + PlanetaryDatabaseProto.from = PlanetaryDatabaseProto.fromObject; + + PlanetaryDatabaseProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = null; + object.name = null; + } + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + if (message.name !== undefined && message.name !== null && message.hasOwnProperty("name")) + object.name = $types[1].toObject(message.name, options); + return object; + }; + + PlanetaryDatabaseProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + PlanetaryDatabaseProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return PlanetaryDatabaseProto; + })(); + + dbroot.LogServerProto = (function() { + + function LogServerProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + LogServerProto.prototype.url = null; + LogServerProto.prototype.enable = false; + LogServerProto.prototype.throttlingFactor = 1; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + LogServerProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.LogServerProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.enable = reader.bool(); + break; + case 3: + message.throttlingFactor = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + LogServerProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url !== undefined && message.url !== null) { + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + } + if (message.enable !== undefined) + if (typeof message.enable !== "boolean") + return "enable: boolean expected"; + if (message.throttlingFactor !== undefined) + if (!$util.isInteger(message.throttlingFactor)) + return "throttlingFactor: integer expected"; + return null; + }; + + LogServerProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.LogServerProto) + return object; + var message = new $root.keyhole.dbroot.LogServerProto(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.LogServerProto.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + if (object.enable !== undefined && object.enable !== null) + message.enable = Boolean(object.enable); + if (object.throttlingFactor !== undefined && object.throttlingFactor !== null) + message.throttlingFactor = object.throttlingFactor | 0; + return message; + }; + + LogServerProto.from = LogServerProto.fromObject; + + LogServerProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = null; + object.enable = false; + object.throttlingFactor = 1; + } + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + if (message.enable !== undefined && message.enable !== null && message.hasOwnProperty("enable")) + object.enable = message.enable; + if (message.throttlingFactor !== undefined && message.throttlingFactor !== null && message.hasOwnProperty("throttlingFactor")) + object.throttlingFactor = message.throttlingFactor; + return object; + }; + + LogServerProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + LogServerProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return LogServerProto; + })(); + + dbroot.EndSnippetProto = (function() { + + function EndSnippetProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + EndSnippetProto.prototype.model = null; + EndSnippetProto.prototype.authServerUrl = null; + EndSnippetProto.prototype.disableAuthentication = false; + EndSnippetProto.prototype.mfeDomains = $util.emptyArray; + EndSnippetProto.prototype.mfeLangParam = "hl=$5Bhl5D"; + EndSnippetProto.prototype.adsUrlPatterns = ""; + EndSnippetProto.prototype.reverseGeocoderUrl = null; + EndSnippetProto.prototype.reverseGeocoderProtocolVersion = 3; + EndSnippetProto.prototype.skyDatabaseIsAvailable = true; + EndSnippetProto.prototype.skyDatabaseUrl = null; + EndSnippetProto.prototype.defaultWebPageIntlUrl = null; + EndSnippetProto.prototype.numStartUpTips = 17; + EndSnippetProto.prototype.startUpTipsUrl = null; + EndSnippetProto.prototype.numProStartUpTips = 0; + EndSnippetProto.prototype.proStartUpTipsUrl = null; + EndSnippetProto.prototype.startupTipsIntlUrl = null; + EndSnippetProto.prototype.userGuideIntlUrl = null; + EndSnippetProto.prototype.supportCenterIntlUrl = null; + EndSnippetProto.prototype.businessListingIntlUrl = null; + EndSnippetProto.prototype.supportAnswerIntlUrl = null; + EndSnippetProto.prototype.supportTopicIntlUrl = null; + EndSnippetProto.prototype.supportRequestIntlUrl = null; + EndSnippetProto.prototype.earthIntlUrl = null; + EndSnippetProto.prototype.addContentUrl = null; + EndSnippetProto.prototype.sketchupNotInstalledUrl = null; + EndSnippetProto.prototype.sketchupErrorUrl = null; + EndSnippetProto.prototype.freeLicenseUrl = null; + EndSnippetProto.prototype.proLicenseUrl = null; + EndSnippetProto.prototype.tutorialUrl = null; + EndSnippetProto.prototype.keyboardShortcutsUrl = null; + EndSnippetProto.prototype.releaseNotesUrl = null; + EndSnippetProto.prototype.hideUserData = false; + EndSnippetProto.prototype.useGeLogo = true; + EndSnippetProto.prototype.dioramaDescriptionUrlBase = null; + EndSnippetProto.prototype.dioramaDefaultColor = 4291281607; + EndSnippetProto.prototype.dioramaBlacklistUrl = null; + EndSnippetProto.prototype.clientOptions = null; + EndSnippetProto.prototype.fetchingOptions = null; + EndSnippetProto.prototype.timeMachineOptions = null; + EndSnippetProto.prototype.csiOptions = null; + EndSnippetProto.prototype.searchTab = $util.emptyArray; + EndSnippetProto.prototype.cobrandInfo = $util.emptyArray; + EndSnippetProto.prototype.validDatabase = $util.emptyArray; + EndSnippetProto.prototype.configScript = $util.emptyArray; + EndSnippetProto.prototype.deauthServerUrl = null; + EndSnippetProto.prototype.swoopParameters = null; + EndSnippetProto.prototype.bbsServerInfo = null; + EndSnippetProto.prototype.dataErrorServerInfo = null; + EndSnippetProto.prototype.planetaryDatabase = $util.emptyArray; + EndSnippetProto.prototype.logServer = null; + EndSnippetProto.prototype.autopiaOptions = null; + EndSnippetProto.prototype.searchConfig = null; + EndSnippetProto.prototype.searchInfo = null; + EndSnippetProto.prototype.elevationServiceBaseUrl = "http://maps.google.com/maps/api/elevation/"; + EndSnippetProto.prototype.elevationProfileQueryDelay = 500; + EndSnippetProto.prototype.proUpgradeUrl = null; + EndSnippetProto.prototype.earthCommunityUrl = null; + EndSnippetProto.prototype.googleMapsUrl = null; + EndSnippetProto.prototype.sharingUrl = null; + EndSnippetProto.prototype.privacyPolicyUrl = null; + EndSnippetProto.prototype.doGplusUserCheck = false; + EndSnippetProto.prototype.rocktreeDataProto = null; + EndSnippetProto.prototype.filmstripConfig = $util.emptyArray; + EndSnippetProto.prototype.showSigninButton = false; + EndSnippetProto.prototype.proMeasureUpsellUrl = null; + EndSnippetProto.prototype.proPrintUpsellUrl = null; + EndSnippetProto.prototype.starDataProto = null; + EndSnippetProto.prototype.feedbackUrl = null; + EndSnippetProto.prototype.oauth2LoginUrl = null; + + var $types = { + 0 : "keyhole.dbroot.PlanetModelProto", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.MfeDomainFeaturesProto", + 6 : "keyhole.dbroot.StringIdOrValueProto", + 9 : "keyhole.dbroot.StringIdOrValueProto", + 10 : "keyhole.dbroot.StringIdOrValueProto", + 12 : "keyhole.dbroot.StringIdOrValueProto", + 14 : "keyhole.dbroot.StringIdOrValueProto", + 15 : "keyhole.dbroot.StringIdOrValueProto", + 16 : "keyhole.dbroot.StringIdOrValueProto", + 17 : "keyhole.dbroot.StringIdOrValueProto", + 18 : "keyhole.dbroot.StringIdOrValueProto", + 19 : "keyhole.dbroot.StringIdOrValueProto", + 20 : "keyhole.dbroot.StringIdOrValueProto", + 21 : "keyhole.dbroot.StringIdOrValueProto", + 22 : "keyhole.dbroot.StringIdOrValueProto", + 23 : "keyhole.dbroot.StringIdOrValueProto", + 24 : "keyhole.dbroot.StringIdOrValueProto", + 25 : "keyhole.dbroot.StringIdOrValueProto", + 26 : "keyhole.dbroot.StringIdOrValueProto", + 27 : "keyhole.dbroot.StringIdOrValueProto", + 28 : "keyhole.dbroot.StringIdOrValueProto", + 29 : "keyhole.dbroot.StringIdOrValueProto", + 30 : "keyhole.dbroot.StringIdOrValueProto", + 33 : "keyhole.dbroot.StringIdOrValueProto", + 35 : "keyhole.dbroot.StringIdOrValueProto", + 36 : "keyhole.dbroot.ClientOptionsProto", + 37 : "keyhole.dbroot.FetchingOptionsProto", + 38 : "keyhole.dbroot.TimeMachineOptionsProto", + 39 : "keyhole.dbroot.CSIOptionsProto", + 40 : "keyhole.dbroot.SearchTabProto", + 41 : "keyhole.dbroot.CobrandProto", + 42 : "keyhole.dbroot.DatabaseDescriptionProto", + 43 : "keyhole.dbroot.ConfigScriptProto", + 44 : "keyhole.dbroot.StringIdOrValueProto", + 45 : "keyhole.dbroot.SwoopParamsProto", + 46 : "keyhole.dbroot.PostingServerProto", + 47 : "keyhole.dbroot.PostingServerProto", + 48 : "keyhole.dbroot.PlanetaryDatabaseProto", + 49 : "keyhole.dbroot.LogServerProto", + 50 : "keyhole.dbroot.AutopiaOptionsProto", + 51 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto", + 52 : "keyhole.dbroot.EndSnippetProto.SearchInfoProto", + 55 : "keyhole.dbroot.StringIdOrValueProto", + 56 : "keyhole.dbroot.StringIdOrValueProto", + 57 : "keyhole.dbroot.StringIdOrValueProto", + 58 : "keyhole.dbroot.StringIdOrValueProto", + 59 : "keyhole.dbroot.StringIdOrValueProto", + 61 : "keyhole.dbroot.EndSnippetProto.RockTreeDataProto", + 62 : "keyhole.dbroot.EndSnippetProto.FilmstripConfigProto", + 64 : "keyhole.dbroot.StringIdOrValueProto", + 65 : "keyhole.dbroot.StringIdOrValueProto", + 66 : "keyhole.dbroot.EndSnippetProto.StarDataProto", + 67 : "keyhole.dbroot.StringIdOrValueProto", + 68 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + EndSnippetProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.model = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.authServerUrl = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.disableAuthentication = reader.bool(); + break; + case 4: + if (!(message.mfeDomains && message.mfeDomains.length)) + message.mfeDomains = []; + message.mfeDomains.push($types[3].decode(reader, reader.uint32())); + break; + case 5: + message.mfeLangParam = reader.string(); + break; + case 6: + message.adsUrlPatterns = reader.string(); + break; + case 7: + message.reverseGeocoderUrl = $types[6].decode(reader, reader.uint32()); + break; + case 8: + message.reverseGeocoderProtocolVersion = reader.int32(); + break; + case 9: + message.skyDatabaseIsAvailable = reader.bool(); + break; + case 10: + message.skyDatabaseUrl = $types[9].decode(reader, reader.uint32()); + break; + case 11: + message.defaultWebPageIntlUrl = $types[10].decode(reader, reader.uint32()); + break; + case 12: + message.numStartUpTips = reader.int32(); + break; + case 13: + message.startUpTipsUrl = $types[12].decode(reader, reader.uint32()); + break; + case 51: + message.numProStartUpTips = reader.int32(); + break; + case 52: + message.proStartUpTipsUrl = $types[14].decode(reader, reader.uint32()); + break; + case 64: + message.startupTipsIntlUrl = $types[15].decode(reader, reader.uint32()); + break; + case 14: + message.userGuideIntlUrl = $types[16].decode(reader, reader.uint32()); + break; + case 15: + message.supportCenterIntlUrl = $types[17].decode(reader, reader.uint32()); + break; + case 16: + message.businessListingIntlUrl = $types[18].decode(reader, reader.uint32()); + break; + case 17: + message.supportAnswerIntlUrl = $types[19].decode(reader, reader.uint32()); + break; + case 18: + message.supportTopicIntlUrl = $types[20].decode(reader, reader.uint32()); + break; + case 19: + message.supportRequestIntlUrl = $types[21].decode(reader, reader.uint32()); + break; + case 20: + message.earthIntlUrl = $types[22].decode(reader, reader.uint32()); + break; + case 21: + message.addContentUrl = $types[23].decode(reader, reader.uint32()); + break; + case 22: + message.sketchupNotInstalledUrl = $types[24].decode(reader, reader.uint32()); + break; + case 23: + message.sketchupErrorUrl = $types[25].decode(reader, reader.uint32()); + break; + case 24: + message.freeLicenseUrl = $types[26].decode(reader, reader.uint32()); + break; + case 25: + message.proLicenseUrl = $types[27].decode(reader, reader.uint32()); + break; + case 48: + message.tutorialUrl = $types[28].decode(reader, reader.uint32()); + break; + case 49: + message.keyboardShortcutsUrl = $types[29].decode(reader, reader.uint32()); + break; + case 50: + message.releaseNotesUrl = $types[30].decode(reader, reader.uint32()); + break; + case 26: + message.hideUserData = reader.bool(); + break; + case 27: + message.useGeLogo = reader.bool(); + break; + case 28: + message.dioramaDescriptionUrlBase = $types[33].decode(reader, reader.uint32()); + break; + case 29: + message.dioramaDefaultColor = reader.uint32(); + break; + case 53: + message.dioramaBlacklistUrl = $types[35].decode(reader, reader.uint32()); + break; + case 30: + message.clientOptions = $types[36].decode(reader, reader.uint32()); + break; + case 31: + message.fetchingOptions = $types[37].decode(reader, reader.uint32()); + break; + case 32: + message.timeMachineOptions = $types[38].decode(reader, reader.uint32()); + break; + case 33: + message.csiOptions = $types[39].decode(reader, reader.uint32()); + break; + case 34: + if (!(message.searchTab && message.searchTab.length)) + message.searchTab = []; + message.searchTab.push($types[40].decode(reader, reader.uint32())); + break; + case 35: + if (!(message.cobrandInfo && message.cobrandInfo.length)) + message.cobrandInfo = []; + message.cobrandInfo.push($types[41].decode(reader, reader.uint32())); + break; + case 36: + if (!(message.validDatabase && message.validDatabase.length)) + message.validDatabase = []; + message.validDatabase.push($types[42].decode(reader, reader.uint32())); + break; + case 37: + if (!(message.configScript && message.configScript.length)) + message.configScript = []; + message.configScript.push($types[43].decode(reader, reader.uint32())); + break; + case 38: + message.deauthServerUrl = $types[44].decode(reader, reader.uint32()); + break; + case 39: + message.swoopParameters = $types[45].decode(reader, reader.uint32()); + break; + case 40: + message.bbsServerInfo = $types[46].decode(reader, reader.uint32()); + break; + case 41: + message.dataErrorServerInfo = $types[47].decode(reader, reader.uint32()); + break; + case 42: + if (!(message.planetaryDatabase && message.planetaryDatabase.length)) + message.planetaryDatabase = []; + message.planetaryDatabase.push($types[48].decode(reader, reader.uint32())); + break; + case 43: + message.logServer = $types[49].decode(reader, reader.uint32()); + break; + case 44: + message.autopiaOptions = $types[50].decode(reader, reader.uint32()); + break; + case 54: + message.searchConfig = $types[51].decode(reader, reader.uint32()); + break; + case 45: + message.searchInfo = $types[52].decode(reader, reader.uint32()); + break; + case 46: + message.elevationServiceBaseUrl = reader.string(); + break; + case 47: + message.elevationProfileQueryDelay = reader.int32(); + break; + case 55: + message.proUpgradeUrl = $types[55].decode(reader, reader.uint32()); + break; + case 56: + message.earthCommunityUrl = $types[56].decode(reader, reader.uint32()); + break; + case 57: + message.googleMapsUrl = $types[57].decode(reader, reader.uint32()); + break; + case 58: + message.sharingUrl = $types[58].decode(reader, reader.uint32()); + break; + case 59: + message.privacyPolicyUrl = $types[59].decode(reader, reader.uint32()); + break; + case 60: + message.doGplusUserCheck = reader.bool(); + break; + case 61: + message.rocktreeDataProto = $types[61].decode(reader, reader.uint32()); + break; + case 62: + if (!(message.filmstripConfig && message.filmstripConfig.length)) + message.filmstripConfig = []; + message.filmstripConfig.push($types[62].decode(reader, reader.uint32())); + break; + case 63: + message.showSigninButton = reader.bool(); + break; + case 65: + message.proMeasureUpsellUrl = $types[64].decode(reader, reader.uint32()); + break; + case 66: + message.proPrintUpsellUrl = $types[65].decode(reader, reader.uint32()); + break; + case 67: + message.starDataProto = $types[66].decode(reader, reader.uint32()); + break; + case 68: + message.feedbackUrl = $types[67].decode(reader, reader.uint32()); + break; + case 69: + message.oauth2LoginUrl = $types[68].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + EndSnippetProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.model !== undefined && message.model !== null) { + var error = $types[0].verify(message.model); + if (error) + return "model." + error; + } + if (message.authServerUrl !== undefined && message.authServerUrl !== null) { + var error = $types[1].verify(message.authServerUrl); + if (error) + return "authServerUrl." + error; + } + if (message.disableAuthentication !== undefined) + if (typeof message.disableAuthentication !== "boolean") + return "disableAuthentication: boolean expected"; + if (message.mfeDomains !== undefined) { + if (!Array.isArray(message.mfeDomains)) + return "mfeDomains: array expected"; + for (var i = 0; i < message.mfeDomains.length; ++i) { + var error = $types[3].verify(message.mfeDomains[i]); + if (error) + return "mfeDomains." + error; + } + } + if (message.mfeLangParam !== undefined) + if (!$util.isString(message.mfeLangParam)) + return "mfeLangParam: string expected"; + if (message.adsUrlPatterns !== undefined) + if (!$util.isString(message.adsUrlPatterns)) + return "adsUrlPatterns: string expected"; + if (message.reverseGeocoderUrl !== undefined && message.reverseGeocoderUrl !== null) { + var error = $types[6].verify(message.reverseGeocoderUrl); + if (error) + return "reverseGeocoderUrl." + error; + } + if (message.reverseGeocoderProtocolVersion !== undefined) + if (!$util.isInteger(message.reverseGeocoderProtocolVersion)) + return "reverseGeocoderProtocolVersion: integer expected"; + if (message.skyDatabaseIsAvailable !== undefined) + if (typeof message.skyDatabaseIsAvailable !== "boolean") + return "skyDatabaseIsAvailable: boolean expected"; + if (message.skyDatabaseUrl !== undefined && message.skyDatabaseUrl !== null) { + var error = $types[9].verify(message.skyDatabaseUrl); + if (error) + return "skyDatabaseUrl." + error; + } + if (message.defaultWebPageIntlUrl !== undefined && message.defaultWebPageIntlUrl !== null) { + var error = $types[10].verify(message.defaultWebPageIntlUrl); + if (error) + return "defaultWebPageIntlUrl." + error; + } + if (message.numStartUpTips !== undefined) + if (!$util.isInteger(message.numStartUpTips)) + return "numStartUpTips: integer expected"; + if (message.startUpTipsUrl !== undefined && message.startUpTipsUrl !== null) { + var error = $types[12].verify(message.startUpTipsUrl); + if (error) + return "startUpTipsUrl." + error; + } + if (message.numProStartUpTips !== undefined) + if (!$util.isInteger(message.numProStartUpTips)) + return "numProStartUpTips: integer expected"; + if (message.proStartUpTipsUrl !== undefined && message.proStartUpTipsUrl !== null) { + var error = $types[14].verify(message.proStartUpTipsUrl); + if (error) + return "proStartUpTipsUrl." + error; + } + if (message.startupTipsIntlUrl !== undefined && message.startupTipsIntlUrl !== null) { + var error = $types[15].verify(message.startupTipsIntlUrl); + if (error) + return "startupTipsIntlUrl." + error; + } + if (message.userGuideIntlUrl !== undefined && message.userGuideIntlUrl !== null) { + var error = $types[16].verify(message.userGuideIntlUrl); + if (error) + return "userGuideIntlUrl." + error; + } + if (message.supportCenterIntlUrl !== undefined && message.supportCenterIntlUrl !== null) { + var error = $types[17].verify(message.supportCenterIntlUrl); + if (error) + return "supportCenterIntlUrl." + error; + } + if (message.businessListingIntlUrl !== undefined && message.businessListingIntlUrl !== null) { + var error = $types[18].verify(message.businessListingIntlUrl); + if (error) + return "businessListingIntlUrl." + error; + } + if (message.supportAnswerIntlUrl !== undefined && message.supportAnswerIntlUrl !== null) { + var error = $types[19].verify(message.supportAnswerIntlUrl); + if (error) + return "supportAnswerIntlUrl." + error; + } + if (message.supportTopicIntlUrl !== undefined && message.supportTopicIntlUrl !== null) { + var error = $types[20].verify(message.supportTopicIntlUrl); + if (error) + return "supportTopicIntlUrl." + error; + } + if (message.supportRequestIntlUrl !== undefined && message.supportRequestIntlUrl !== null) { + var error = $types[21].verify(message.supportRequestIntlUrl); + if (error) + return "supportRequestIntlUrl." + error; + } + if (message.earthIntlUrl !== undefined && message.earthIntlUrl !== null) { + var error = $types[22].verify(message.earthIntlUrl); + if (error) + return "earthIntlUrl." + error; + } + if (message.addContentUrl !== undefined && message.addContentUrl !== null) { + var error = $types[23].verify(message.addContentUrl); + if (error) + return "addContentUrl." + error; + } + if (message.sketchupNotInstalledUrl !== undefined && message.sketchupNotInstalledUrl !== null) { + var error = $types[24].verify(message.sketchupNotInstalledUrl); + if (error) + return "sketchupNotInstalledUrl." + error; + } + if (message.sketchupErrorUrl !== undefined && message.sketchupErrorUrl !== null) { + var error = $types[25].verify(message.sketchupErrorUrl); + if (error) + return "sketchupErrorUrl." + error; + } + if (message.freeLicenseUrl !== undefined && message.freeLicenseUrl !== null) { + var error = $types[26].verify(message.freeLicenseUrl); + if (error) + return "freeLicenseUrl." + error; + } + if (message.proLicenseUrl !== undefined && message.proLicenseUrl !== null) { + var error = $types[27].verify(message.proLicenseUrl); + if (error) + return "proLicenseUrl." + error; + } + if (message.tutorialUrl !== undefined && message.tutorialUrl !== null) { + var error = $types[28].verify(message.tutorialUrl); + if (error) + return "tutorialUrl." + error; + } + if (message.keyboardShortcutsUrl !== undefined && message.keyboardShortcutsUrl !== null) { + var error = $types[29].verify(message.keyboardShortcutsUrl); + if (error) + return "keyboardShortcutsUrl." + error; + } + if (message.releaseNotesUrl !== undefined && message.releaseNotesUrl !== null) { + var error = $types[30].verify(message.releaseNotesUrl); + if (error) + return "releaseNotesUrl." + error; + } + if (message.hideUserData !== undefined) + if (typeof message.hideUserData !== "boolean") + return "hideUserData: boolean expected"; + if (message.useGeLogo !== undefined) + if (typeof message.useGeLogo !== "boolean") + return "useGeLogo: boolean expected"; + if (message.dioramaDescriptionUrlBase !== undefined && message.dioramaDescriptionUrlBase !== null) { + var error = $types[33].verify(message.dioramaDescriptionUrlBase); + if (error) + return "dioramaDescriptionUrlBase." + error; + } + if (message.dioramaDefaultColor !== undefined) + if (!$util.isInteger(message.dioramaDefaultColor)) + return "dioramaDefaultColor: integer expected"; + if (message.dioramaBlacklistUrl !== undefined && message.dioramaBlacklistUrl !== null) { + var error = $types[35].verify(message.dioramaBlacklistUrl); + if (error) + return "dioramaBlacklistUrl." + error; + } + if (message.clientOptions !== undefined && message.clientOptions !== null) { + var error = $types[36].verify(message.clientOptions); + if (error) + return "clientOptions." + error; + } + if (message.fetchingOptions !== undefined && message.fetchingOptions !== null) { + var error = $types[37].verify(message.fetchingOptions); + if (error) + return "fetchingOptions." + error; + } + if (message.timeMachineOptions !== undefined && message.timeMachineOptions !== null) { + var error = $types[38].verify(message.timeMachineOptions); + if (error) + return "timeMachineOptions." + error; + } + if (message.csiOptions !== undefined && message.csiOptions !== null) { + var error = $types[39].verify(message.csiOptions); + if (error) + return "csiOptions." + error; + } + if (message.searchTab !== undefined) { + if (!Array.isArray(message.searchTab)) + return "searchTab: array expected"; + for (var i = 0; i < message.searchTab.length; ++i) { + var error = $types[40].verify(message.searchTab[i]); + if (error) + return "searchTab." + error; + } + } + if (message.cobrandInfo !== undefined) { + if (!Array.isArray(message.cobrandInfo)) + return "cobrandInfo: array expected"; + for (var i = 0; i < message.cobrandInfo.length; ++i) { + var error = $types[41].verify(message.cobrandInfo[i]); + if (error) + return "cobrandInfo." + error; + } + } + if (message.validDatabase !== undefined) { + if (!Array.isArray(message.validDatabase)) + return "validDatabase: array expected"; + for (var i = 0; i < message.validDatabase.length; ++i) { + var error = $types[42].verify(message.validDatabase[i]); + if (error) + return "validDatabase." + error; + } + } + if (message.configScript !== undefined) { + if (!Array.isArray(message.configScript)) + return "configScript: array expected"; + for (var i = 0; i < message.configScript.length; ++i) { + var error = $types[43].verify(message.configScript[i]); + if (error) + return "configScript." + error; + } + } + if (message.deauthServerUrl !== undefined && message.deauthServerUrl !== null) { + var error = $types[44].verify(message.deauthServerUrl); + if (error) + return "deauthServerUrl." + error; + } + if (message.swoopParameters !== undefined && message.swoopParameters !== null) { + var error = $types[45].verify(message.swoopParameters); + if (error) + return "swoopParameters." + error; + } + if (message.bbsServerInfo !== undefined && message.bbsServerInfo !== null) { + var error = $types[46].verify(message.bbsServerInfo); + if (error) + return "bbsServerInfo." + error; + } + if (message.dataErrorServerInfo !== undefined && message.dataErrorServerInfo !== null) { + var error = $types[47].verify(message.dataErrorServerInfo); + if (error) + return "dataErrorServerInfo." + error; + } + if (message.planetaryDatabase !== undefined) { + if (!Array.isArray(message.planetaryDatabase)) + return "planetaryDatabase: array expected"; + for (var i = 0; i < message.planetaryDatabase.length; ++i) { + var error = $types[48].verify(message.planetaryDatabase[i]); + if (error) + return "planetaryDatabase." + error; + } + } + if (message.logServer !== undefined && message.logServer !== null) { + var error = $types[49].verify(message.logServer); + if (error) + return "logServer." + error; + } + if (message.autopiaOptions !== undefined && message.autopiaOptions !== null) { + var error = $types[50].verify(message.autopiaOptions); + if (error) + return "autopiaOptions." + error; + } + if (message.searchConfig !== undefined && message.searchConfig !== null) { + var error = $types[51].verify(message.searchConfig); + if (error) + return "searchConfig." + error; + } + if (message.searchInfo !== undefined && message.searchInfo !== null) { + var error = $types[52].verify(message.searchInfo); + if (error) + return "searchInfo." + error; + } + if (message.elevationServiceBaseUrl !== undefined) + if (!$util.isString(message.elevationServiceBaseUrl)) + return "elevationServiceBaseUrl: string expected"; + if (message.elevationProfileQueryDelay !== undefined) + if (!$util.isInteger(message.elevationProfileQueryDelay)) + return "elevationProfileQueryDelay: integer expected"; + if (message.proUpgradeUrl !== undefined && message.proUpgradeUrl !== null) { + var error = $types[55].verify(message.proUpgradeUrl); + if (error) + return "proUpgradeUrl." + error; + } + if (message.earthCommunityUrl !== undefined && message.earthCommunityUrl !== null) { + var error = $types[56].verify(message.earthCommunityUrl); + if (error) + return "earthCommunityUrl." + error; + } + if (message.googleMapsUrl !== undefined && message.googleMapsUrl !== null) { + var error = $types[57].verify(message.googleMapsUrl); + if (error) + return "googleMapsUrl." + error; + } + if (message.sharingUrl !== undefined && message.sharingUrl !== null) { + var error = $types[58].verify(message.sharingUrl); + if (error) + return "sharingUrl." + error; + } + if (message.privacyPolicyUrl !== undefined && message.privacyPolicyUrl !== null) { + var error = $types[59].verify(message.privacyPolicyUrl); + if (error) + return "privacyPolicyUrl." + error; + } + if (message.doGplusUserCheck !== undefined) + if (typeof message.doGplusUserCheck !== "boolean") + return "doGplusUserCheck: boolean expected"; + if (message.rocktreeDataProto !== undefined && message.rocktreeDataProto !== null) { + var error = $types[61].verify(message.rocktreeDataProto); + if (error) + return "rocktreeDataProto." + error; + } + if (message.filmstripConfig !== undefined) { + if (!Array.isArray(message.filmstripConfig)) + return "filmstripConfig: array expected"; + for (var i = 0; i < message.filmstripConfig.length; ++i) { + var error = $types[62].verify(message.filmstripConfig[i]); + if (error) + return "filmstripConfig." + error; + } + } + if (message.showSigninButton !== undefined) + if (typeof message.showSigninButton !== "boolean") + return "showSigninButton: boolean expected"; + if (message.proMeasureUpsellUrl !== undefined && message.proMeasureUpsellUrl !== null) { + var error = $types[64].verify(message.proMeasureUpsellUrl); + if (error) + return "proMeasureUpsellUrl." + error; + } + if (message.proPrintUpsellUrl !== undefined && message.proPrintUpsellUrl !== null) { + var error = $types[65].verify(message.proPrintUpsellUrl); + if (error) + return "proPrintUpsellUrl." + error; + } + if (message.starDataProto !== undefined && message.starDataProto !== null) { + var error = $types[66].verify(message.starDataProto); + if (error) + return "starDataProto." + error; + } + if (message.feedbackUrl !== undefined && message.feedbackUrl !== null) { + var error = $types[67].verify(message.feedbackUrl); + if (error) + return "feedbackUrl." + error; + } + if (message.oauth2LoginUrl !== undefined && message.oauth2LoginUrl !== null) { + var error = $types[68].verify(message.oauth2LoginUrl); + if (error) + return "oauth2LoginUrl." + error; + } + return null; + }; + + EndSnippetProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto(); + if (object.model !== undefined && object.model !== null) { + if (typeof object.model !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.model: object expected"); + message.model = $types[0].fromObject(object.model); + } + if (object.authServerUrl !== undefined && object.authServerUrl !== null) { + if (typeof object.authServerUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.authServerUrl: object expected"); + message.authServerUrl = $types[1].fromObject(object.authServerUrl); + } + if (object.disableAuthentication !== undefined && object.disableAuthentication !== null) + message.disableAuthentication = Boolean(object.disableAuthentication); + if (object.mfeDomains) { + if (!Array.isArray(object.mfeDomains)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.mfeDomains: array expected"); + message.mfeDomains = []; + for (var i = 0; i < object.mfeDomains.length; ++i) { + if (typeof object.mfeDomains[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.mfeDomains: object expected"); + message.mfeDomains[i] = $types[3].fromObject(object.mfeDomains[i]); + } + } + if (object.mfeLangParam !== undefined && object.mfeLangParam !== null) + message.mfeLangParam = String(object.mfeLangParam); + if (object.adsUrlPatterns !== undefined && object.adsUrlPatterns !== null) + message.adsUrlPatterns = String(object.adsUrlPatterns); + if (object.reverseGeocoderUrl !== undefined && object.reverseGeocoderUrl !== null) { + if (typeof object.reverseGeocoderUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.reverseGeocoderUrl: object expected"); + message.reverseGeocoderUrl = $types[6].fromObject(object.reverseGeocoderUrl); + } + if (object.reverseGeocoderProtocolVersion !== undefined && object.reverseGeocoderProtocolVersion !== null) + message.reverseGeocoderProtocolVersion = object.reverseGeocoderProtocolVersion | 0; + if (object.skyDatabaseIsAvailable !== undefined && object.skyDatabaseIsAvailable !== null) + message.skyDatabaseIsAvailable = Boolean(object.skyDatabaseIsAvailable); + if (object.skyDatabaseUrl !== undefined && object.skyDatabaseUrl !== null) { + if (typeof object.skyDatabaseUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.skyDatabaseUrl: object expected"); + message.skyDatabaseUrl = $types[9].fromObject(object.skyDatabaseUrl); + } + if (object.defaultWebPageIntlUrl !== undefined && object.defaultWebPageIntlUrl !== null) { + if (typeof object.defaultWebPageIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.defaultWebPageIntlUrl: object expected"); + message.defaultWebPageIntlUrl = $types[10].fromObject(object.defaultWebPageIntlUrl); + } + if (object.numStartUpTips !== undefined && object.numStartUpTips !== null) + message.numStartUpTips = object.numStartUpTips | 0; + if (object.startUpTipsUrl !== undefined && object.startUpTipsUrl !== null) { + if (typeof object.startUpTipsUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.startUpTipsUrl: object expected"); + message.startUpTipsUrl = $types[12].fromObject(object.startUpTipsUrl); + } + if (object.numProStartUpTips !== undefined && object.numProStartUpTips !== null) + message.numProStartUpTips = object.numProStartUpTips | 0; + if (object.proStartUpTipsUrl !== undefined && object.proStartUpTipsUrl !== null) { + if (typeof object.proStartUpTipsUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.proStartUpTipsUrl: object expected"); + message.proStartUpTipsUrl = $types[14].fromObject(object.proStartUpTipsUrl); + } + if (object.startupTipsIntlUrl !== undefined && object.startupTipsIntlUrl !== null) { + if (typeof object.startupTipsIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.startupTipsIntlUrl: object expected"); + message.startupTipsIntlUrl = $types[15].fromObject(object.startupTipsIntlUrl); + } + if (object.userGuideIntlUrl !== undefined && object.userGuideIntlUrl !== null) { + if (typeof object.userGuideIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.userGuideIntlUrl: object expected"); + message.userGuideIntlUrl = $types[16].fromObject(object.userGuideIntlUrl); + } + if (object.supportCenterIntlUrl !== undefined && object.supportCenterIntlUrl !== null) { + if (typeof object.supportCenterIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.supportCenterIntlUrl: object expected"); + message.supportCenterIntlUrl = $types[17].fromObject(object.supportCenterIntlUrl); + } + if (object.businessListingIntlUrl !== undefined && object.businessListingIntlUrl !== null) { + if (typeof object.businessListingIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.businessListingIntlUrl: object expected"); + message.businessListingIntlUrl = $types[18].fromObject(object.businessListingIntlUrl); + } + if (object.supportAnswerIntlUrl !== undefined && object.supportAnswerIntlUrl !== null) { + if (typeof object.supportAnswerIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.supportAnswerIntlUrl: object expected"); + message.supportAnswerIntlUrl = $types[19].fromObject(object.supportAnswerIntlUrl); + } + if (object.supportTopicIntlUrl !== undefined && object.supportTopicIntlUrl !== null) { + if (typeof object.supportTopicIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.supportTopicIntlUrl: object expected"); + message.supportTopicIntlUrl = $types[20].fromObject(object.supportTopicIntlUrl); + } + if (object.supportRequestIntlUrl !== undefined && object.supportRequestIntlUrl !== null) { + if (typeof object.supportRequestIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.supportRequestIntlUrl: object expected"); + message.supportRequestIntlUrl = $types[21].fromObject(object.supportRequestIntlUrl); + } + if (object.earthIntlUrl !== undefined && object.earthIntlUrl !== null) { + if (typeof object.earthIntlUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.earthIntlUrl: object expected"); + message.earthIntlUrl = $types[22].fromObject(object.earthIntlUrl); + } + if (object.addContentUrl !== undefined && object.addContentUrl !== null) { + if (typeof object.addContentUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.addContentUrl: object expected"); + message.addContentUrl = $types[23].fromObject(object.addContentUrl); + } + if (object.sketchupNotInstalledUrl !== undefined && object.sketchupNotInstalledUrl !== null) { + if (typeof object.sketchupNotInstalledUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.sketchupNotInstalledUrl: object expected"); + message.sketchupNotInstalledUrl = $types[24].fromObject(object.sketchupNotInstalledUrl); + } + if (object.sketchupErrorUrl !== undefined && object.sketchupErrorUrl !== null) { + if (typeof object.sketchupErrorUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.sketchupErrorUrl: object expected"); + message.sketchupErrorUrl = $types[25].fromObject(object.sketchupErrorUrl); + } + if (object.freeLicenseUrl !== undefined && object.freeLicenseUrl !== null) { + if (typeof object.freeLicenseUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.freeLicenseUrl: object expected"); + message.freeLicenseUrl = $types[26].fromObject(object.freeLicenseUrl); + } + if (object.proLicenseUrl !== undefined && object.proLicenseUrl !== null) { + if (typeof object.proLicenseUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.proLicenseUrl: object expected"); + message.proLicenseUrl = $types[27].fromObject(object.proLicenseUrl); + } + if (object.tutorialUrl !== undefined && object.tutorialUrl !== null) { + if (typeof object.tutorialUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.tutorialUrl: object expected"); + message.tutorialUrl = $types[28].fromObject(object.tutorialUrl); + } + if (object.keyboardShortcutsUrl !== undefined && object.keyboardShortcutsUrl !== null) { + if (typeof object.keyboardShortcutsUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.keyboardShortcutsUrl: object expected"); + message.keyboardShortcutsUrl = $types[29].fromObject(object.keyboardShortcutsUrl); + } + if (object.releaseNotesUrl !== undefined && object.releaseNotesUrl !== null) { + if (typeof object.releaseNotesUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.releaseNotesUrl: object expected"); + message.releaseNotesUrl = $types[30].fromObject(object.releaseNotesUrl); + } + if (object.hideUserData !== undefined && object.hideUserData !== null) + message.hideUserData = Boolean(object.hideUserData); + if (object.useGeLogo !== undefined && object.useGeLogo !== null) + message.useGeLogo = Boolean(object.useGeLogo); + if (object.dioramaDescriptionUrlBase !== undefined && object.dioramaDescriptionUrlBase !== null) { + if (typeof object.dioramaDescriptionUrlBase !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.dioramaDescriptionUrlBase: object expected"); + message.dioramaDescriptionUrlBase = $types[33].fromObject(object.dioramaDescriptionUrlBase); + } + if (object.dioramaDefaultColor !== undefined && object.dioramaDefaultColor !== null) + message.dioramaDefaultColor = object.dioramaDefaultColor >>> 0; + if (object.dioramaBlacklistUrl !== undefined && object.dioramaBlacklistUrl !== null) { + if (typeof object.dioramaBlacklistUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.dioramaBlacklistUrl: object expected"); + message.dioramaBlacklistUrl = $types[35].fromObject(object.dioramaBlacklistUrl); + } + if (object.clientOptions !== undefined && object.clientOptions !== null) { + if (typeof object.clientOptions !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.clientOptions: object expected"); + message.clientOptions = $types[36].fromObject(object.clientOptions); + } + if (object.fetchingOptions !== undefined && object.fetchingOptions !== null) { + if (typeof object.fetchingOptions !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.fetchingOptions: object expected"); + message.fetchingOptions = $types[37].fromObject(object.fetchingOptions); + } + if (object.timeMachineOptions !== undefined && object.timeMachineOptions !== null) { + if (typeof object.timeMachineOptions !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.timeMachineOptions: object expected"); + message.timeMachineOptions = $types[38].fromObject(object.timeMachineOptions); + } + if (object.csiOptions !== undefined && object.csiOptions !== null) { + if (typeof object.csiOptions !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.csiOptions: object expected"); + message.csiOptions = $types[39].fromObject(object.csiOptions); + } + if (object.searchTab) { + if (!Array.isArray(object.searchTab)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.searchTab: array expected"); + message.searchTab = []; + for (var i = 0; i < object.searchTab.length; ++i) { + if (typeof object.searchTab[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.searchTab: object expected"); + message.searchTab[i] = $types[40].fromObject(object.searchTab[i]); + } + } + if (object.cobrandInfo) { + if (!Array.isArray(object.cobrandInfo)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.cobrandInfo: array expected"); + message.cobrandInfo = []; + for (var i = 0; i < object.cobrandInfo.length; ++i) { + if (typeof object.cobrandInfo[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.cobrandInfo: object expected"); + message.cobrandInfo[i] = $types[41].fromObject(object.cobrandInfo[i]); + } + } + if (object.validDatabase) { + if (!Array.isArray(object.validDatabase)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.validDatabase: array expected"); + message.validDatabase = []; + for (var i = 0; i < object.validDatabase.length; ++i) { + if (typeof object.validDatabase[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.validDatabase: object expected"); + message.validDatabase[i] = $types[42].fromObject(object.validDatabase[i]); + } + } + if (object.configScript) { + if (!Array.isArray(object.configScript)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.configScript: array expected"); + message.configScript = []; + for (var i = 0; i < object.configScript.length; ++i) { + if (typeof object.configScript[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.configScript: object expected"); + message.configScript[i] = $types[43].fromObject(object.configScript[i]); + } + } + if (object.deauthServerUrl !== undefined && object.deauthServerUrl !== null) { + if (typeof object.deauthServerUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.deauthServerUrl: object expected"); + message.deauthServerUrl = $types[44].fromObject(object.deauthServerUrl); + } + if (object.swoopParameters !== undefined && object.swoopParameters !== null) { + if (typeof object.swoopParameters !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.swoopParameters: object expected"); + message.swoopParameters = $types[45].fromObject(object.swoopParameters); + } + if (object.bbsServerInfo !== undefined && object.bbsServerInfo !== null) { + if (typeof object.bbsServerInfo !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.bbsServerInfo: object expected"); + message.bbsServerInfo = $types[46].fromObject(object.bbsServerInfo); + } + if (object.dataErrorServerInfo !== undefined && object.dataErrorServerInfo !== null) { + if (typeof object.dataErrorServerInfo !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.dataErrorServerInfo: object expected"); + message.dataErrorServerInfo = $types[47].fromObject(object.dataErrorServerInfo); + } + if (object.planetaryDatabase) { + if (!Array.isArray(object.planetaryDatabase)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.planetaryDatabase: array expected"); + message.planetaryDatabase = []; + for (var i = 0; i < object.planetaryDatabase.length; ++i) { + if (typeof object.planetaryDatabase[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.planetaryDatabase: object expected"); + message.planetaryDatabase[i] = $types[48].fromObject(object.planetaryDatabase[i]); + } + } + if (object.logServer !== undefined && object.logServer !== null) { + if (typeof object.logServer !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.logServer: object expected"); + message.logServer = $types[49].fromObject(object.logServer); + } + if (object.autopiaOptions !== undefined && object.autopiaOptions !== null) { + if (typeof object.autopiaOptions !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.autopiaOptions: object expected"); + message.autopiaOptions = $types[50].fromObject(object.autopiaOptions); + } + if (object.searchConfig !== undefined && object.searchConfig !== null) { + if (typeof object.searchConfig !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.searchConfig: object expected"); + message.searchConfig = $types[51].fromObject(object.searchConfig); + } + if (object.searchInfo !== undefined && object.searchInfo !== null) { + if (typeof object.searchInfo !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.searchInfo: object expected"); + message.searchInfo = $types[52].fromObject(object.searchInfo); + } + if (object.elevationServiceBaseUrl !== undefined && object.elevationServiceBaseUrl !== null) + message.elevationServiceBaseUrl = String(object.elevationServiceBaseUrl); + if (object.elevationProfileQueryDelay !== undefined && object.elevationProfileQueryDelay !== null) + message.elevationProfileQueryDelay = object.elevationProfileQueryDelay | 0; + if (object.proUpgradeUrl !== undefined && object.proUpgradeUrl !== null) { + if (typeof object.proUpgradeUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.proUpgradeUrl: object expected"); + message.proUpgradeUrl = $types[55].fromObject(object.proUpgradeUrl); + } + if (object.earthCommunityUrl !== undefined && object.earthCommunityUrl !== null) { + if (typeof object.earthCommunityUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.earthCommunityUrl: object expected"); + message.earthCommunityUrl = $types[56].fromObject(object.earthCommunityUrl); + } + if (object.googleMapsUrl !== undefined && object.googleMapsUrl !== null) { + if (typeof object.googleMapsUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.googleMapsUrl: object expected"); + message.googleMapsUrl = $types[57].fromObject(object.googleMapsUrl); + } + if (object.sharingUrl !== undefined && object.sharingUrl !== null) { + if (typeof object.sharingUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.sharingUrl: object expected"); + message.sharingUrl = $types[58].fromObject(object.sharingUrl); + } + if (object.privacyPolicyUrl !== undefined && object.privacyPolicyUrl !== null) { + if (typeof object.privacyPolicyUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.privacyPolicyUrl: object expected"); + message.privacyPolicyUrl = $types[59].fromObject(object.privacyPolicyUrl); + } + if (object.doGplusUserCheck !== undefined && object.doGplusUserCheck !== null) + message.doGplusUserCheck = Boolean(object.doGplusUserCheck); + if (object.rocktreeDataProto !== undefined && object.rocktreeDataProto !== null) { + if (typeof object.rocktreeDataProto !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.rocktreeDataProto: object expected"); + message.rocktreeDataProto = $types[61].fromObject(object.rocktreeDataProto); + } + if (object.filmstripConfig) { + if (!Array.isArray(object.filmstripConfig)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.filmstripConfig: array expected"); + message.filmstripConfig = []; + for (var i = 0; i < object.filmstripConfig.length; ++i) { + if (typeof object.filmstripConfig[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.filmstripConfig: object expected"); + message.filmstripConfig[i] = $types[62].fromObject(object.filmstripConfig[i]); + } + } + if (object.showSigninButton !== undefined && object.showSigninButton !== null) + message.showSigninButton = Boolean(object.showSigninButton); + if (object.proMeasureUpsellUrl !== undefined && object.proMeasureUpsellUrl !== null) { + if (typeof object.proMeasureUpsellUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.proMeasureUpsellUrl: object expected"); + message.proMeasureUpsellUrl = $types[64].fromObject(object.proMeasureUpsellUrl); + } + if (object.proPrintUpsellUrl !== undefined && object.proPrintUpsellUrl !== null) { + if (typeof object.proPrintUpsellUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.proPrintUpsellUrl: object expected"); + message.proPrintUpsellUrl = $types[65].fromObject(object.proPrintUpsellUrl); + } + if (object.starDataProto !== undefined && object.starDataProto !== null) { + if (typeof object.starDataProto !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.starDataProto: object expected"); + message.starDataProto = $types[66].fromObject(object.starDataProto); + } + if (object.feedbackUrl !== undefined && object.feedbackUrl !== null) { + if (typeof object.feedbackUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.feedbackUrl: object expected"); + message.feedbackUrl = $types[67].fromObject(object.feedbackUrl); + } + if (object.oauth2LoginUrl !== undefined && object.oauth2LoginUrl !== null) { + if (typeof object.oauth2LoginUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.oauth2LoginUrl: object expected"); + message.oauth2LoginUrl = $types[68].fromObject(object.oauth2LoginUrl); + } + return message; + }; + + EndSnippetProto.from = EndSnippetProto.fromObject; + + EndSnippetProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.mfeDomains = []; + object.searchTab = []; + object.cobrandInfo = []; + object.validDatabase = []; + object.configScript = []; + object.planetaryDatabase = []; + object.filmstripConfig = []; + } + if (options.defaults) { + object.model = null; + object.authServerUrl = null; + object.disableAuthentication = false; + object.mfeLangParam = "hl=$5Bhl5D"; + object.adsUrlPatterns = ""; + object.reverseGeocoderUrl = null; + object.reverseGeocoderProtocolVersion = 3; + object.skyDatabaseIsAvailable = true; + object.skyDatabaseUrl = null; + object.defaultWebPageIntlUrl = null; + object.numStartUpTips = 17; + object.startUpTipsUrl = null; + object.numProStartUpTips = 0; + object.proStartUpTipsUrl = null; + object.startupTipsIntlUrl = null; + object.userGuideIntlUrl = null; + object.supportCenterIntlUrl = null; + object.businessListingIntlUrl = null; + object.supportAnswerIntlUrl = null; + object.supportTopicIntlUrl = null; + object.supportRequestIntlUrl = null; + object.earthIntlUrl = null; + object.addContentUrl = null; + object.sketchupNotInstalledUrl = null; + object.sketchupErrorUrl = null; + object.freeLicenseUrl = null; + object.proLicenseUrl = null; + object.tutorialUrl = null; + object.keyboardShortcutsUrl = null; + object.releaseNotesUrl = null; + object.hideUserData = false; + object.useGeLogo = true; + object.dioramaDescriptionUrlBase = null; + object.dioramaDefaultColor = 4291281607; + object.dioramaBlacklistUrl = null; + object.clientOptions = null; + object.fetchingOptions = null; + object.timeMachineOptions = null; + object.csiOptions = null; + object.deauthServerUrl = null; + object.swoopParameters = null; + object.bbsServerInfo = null; + object.dataErrorServerInfo = null; + object.logServer = null; + object.autopiaOptions = null; + object.searchConfig = null; + object.searchInfo = null; + object.elevationServiceBaseUrl = "http://maps.google.com/maps/api/elevation/"; + object.elevationProfileQueryDelay = 500; + object.proUpgradeUrl = null; + object.earthCommunityUrl = null; + object.googleMapsUrl = null; + object.sharingUrl = null; + object.privacyPolicyUrl = null; + object.doGplusUserCheck = false; + object.rocktreeDataProto = null; + object.showSigninButton = false; + object.proMeasureUpsellUrl = null; + object.proPrintUpsellUrl = null; + object.starDataProto = null; + object.feedbackUrl = null; + object.oauth2LoginUrl = null; + } + if (message.model !== undefined && message.model !== null && message.hasOwnProperty("model")) + object.model = $types[0].toObject(message.model, options); + if (message.authServerUrl !== undefined && message.authServerUrl !== null && message.hasOwnProperty("authServerUrl")) + object.authServerUrl = $types[1].toObject(message.authServerUrl, options); + if (message.disableAuthentication !== undefined && message.disableAuthentication !== null && message.hasOwnProperty("disableAuthentication")) + object.disableAuthentication = message.disableAuthentication; + if (message.mfeDomains !== undefined && message.mfeDomains !== null && message.hasOwnProperty("mfeDomains")) { + object.mfeDomains = []; + for (var j = 0; j < message.mfeDomains.length; ++j) + object.mfeDomains[j] = $types[3].toObject(message.mfeDomains[j], options); + } + if (message.mfeLangParam !== undefined && message.mfeLangParam !== null && message.hasOwnProperty("mfeLangParam")) + object.mfeLangParam = message.mfeLangParam; + if (message.adsUrlPatterns !== undefined && message.adsUrlPatterns !== null && message.hasOwnProperty("adsUrlPatterns")) + object.adsUrlPatterns = message.adsUrlPatterns; + if (message.reverseGeocoderUrl !== undefined && message.reverseGeocoderUrl !== null && message.hasOwnProperty("reverseGeocoderUrl")) + object.reverseGeocoderUrl = $types[6].toObject(message.reverseGeocoderUrl, options); + if (message.reverseGeocoderProtocolVersion !== undefined && message.reverseGeocoderProtocolVersion !== null && message.hasOwnProperty("reverseGeocoderProtocolVersion")) + object.reverseGeocoderProtocolVersion = message.reverseGeocoderProtocolVersion; + if (message.skyDatabaseIsAvailable !== undefined && message.skyDatabaseIsAvailable !== null && message.hasOwnProperty("skyDatabaseIsAvailable")) + object.skyDatabaseIsAvailable = message.skyDatabaseIsAvailable; + if (message.skyDatabaseUrl !== undefined && message.skyDatabaseUrl !== null && message.hasOwnProperty("skyDatabaseUrl")) + object.skyDatabaseUrl = $types[9].toObject(message.skyDatabaseUrl, options); + if (message.defaultWebPageIntlUrl !== undefined && message.defaultWebPageIntlUrl !== null && message.hasOwnProperty("defaultWebPageIntlUrl")) + object.defaultWebPageIntlUrl = $types[10].toObject(message.defaultWebPageIntlUrl, options); + if (message.numStartUpTips !== undefined && message.numStartUpTips !== null && message.hasOwnProperty("numStartUpTips")) + object.numStartUpTips = message.numStartUpTips; + if (message.startUpTipsUrl !== undefined && message.startUpTipsUrl !== null && message.hasOwnProperty("startUpTipsUrl")) + object.startUpTipsUrl = $types[12].toObject(message.startUpTipsUrl, options); + if (message.numProStartUpTips !== undefined && message.numProStartUpTips !== null && message.hasOwnProperty("numProStartUpTips")) + object.numProStartUpTips = message.numProStartUpTips; + if (message.proStartUpTipsUrl !== undefined && message.proStartUpTipsUrl !== null && message.hasOwnProperty("proStartUpTipsUrl")) + object.proStartUpTipsUrl = $types[14].toObject(message.proStartUpTipsUrl, options); + if (message.startupTipsIntlUrl !== undefined && message.startupTipsIntlUrl !== null && message.hasOwnProperty("startupTipsIntlUrl")) + object.startupTipsIntlUrl = $types[15].toObject(message.startupTipsIntlUrl, options); + if (message.userGuideIntlUrl !== undefined && message.userGuideIntlUrl !== null && message.hasOwnProperty("userGuideIntlUrl")) + object.userGuideIntlUrl = $types[16].toObject(message.userGuideIntlUrl, options); + if (message.supportCenterIntlUrl !== undefined && message.supportCenterIntlUrl !== null && message.hasOwnProperty("supportCenterIntlUrl")) + object.supportCenterIntlUrl = $types[17].toObject(message.supportCenterIntlUrl, options); + if (message.businessListingIntlUrl !== undefined && message.businessListingIntlUrl !== null && message.hasOwnProperty("businessListingIntlUrl")) + object.businessListingIntlUrl = $types[18].toObject(message.businessListingIntlUrl, options); + if (message.supportAnswerIntlUrl !== undefined && message.supportAnswerIntlUrl !== null && message.hasOwnProperty("supportAnswerIntlUrl")) + object.supportAnswerIntlUrl = $types[19].toObject(message.supportAnswerIntlUrl, options); + if (message.supportTopicIntlUrl !== undefined && message.supportTopicIntlUrl !== null && message.hasOwnProperty("supportTopicIntlUrl")) + object.supportTopicIntlUrl = $types[20].toObject(message.supportTopicIntlUrl, options); + if (message.supportRequestIntlUrl !== undefined && message.supportRequestIntlUrl !== null && message.hasOwnProperty("supportRequestIntlUrl")) + object.supportRequestIntlUrl = $types[21].toObject(message.supportRequestIntlUrl, options); + if (message.earthIntlUrl !== undefined && message.earthIntlUrl !== null && message.hasOwnProperty("earthIntlUrl")) + object.earthIntlUrl = $types[22].toObject(message.earthIntlUrl, options); + if (message.addContentUrl !== undefined && message.addContentUrl !== null && message.hasOwnProperty("addContentUrl")) + object.addContentUrl = $types[23].toObject(message.addContentUrl, options); + if (message.sketchupNotInstalledUrl !== undefined && message.sketchupNotInstalledUrl !== null && message.hasOwnProperty("sketchupNotInstalledUrl")) + object.sketchupNotInstalledUrl = $types[24].toObject(message.sketchupNotInstalledUrl, options); + if (message.sketchupErrorUrl !== undefined && message.sketchupErrorUrl !== null && message.hasOwnProperty("sketchupErrorUrl")) + object.sketchupErrorUrl = $types[25].toObject(message.sketchupErrorUrl, options); + if (message.freeLicenseUrl !== undefined && message.freeLicenseUrl !== null && message.hasOwnProperty("freeLicenseUrl")) + object.freeLicenseUrl = $types[26].toObject(message.freeLicenseUrl, options); + if (message.proLicenseUrl !== undefined && message.proLicenseUrl !== null && message.hasOwnProperty("proLicenseUrl")) + object.proLicenseUrl = $types[27].toObject(message.proLicenseUrl, options); + if (message.tutorialUrl !== undefined && message.tutorialUrl !== null && message.hasOwnProperty("tutorialUrl")) + object.tutorialUrl = $types[28].toObject(message.tutorialUrl, options); + if (message.keyboardShortcutsUrl !== undefined && message.keyboardShortcutsUrl !== null && message.hasOwnProperty("keyboardShortcutsUrl")) + object.keyboardShortcutsUrl = $types[29].toObject(message.keyboardShortcutsUrl, options); + if (message.releaseNotesUrl !== undefined && message.releaseNotesUrl !== null && message.hasOwnProperty("releaseNotesUrl")) + object.releaseNotesUrl = $types[30].toObject(message.releaseNotesUrl, options); + if (message.hideUserData !== undefined && message.hideUserData !== null && message.hasOwnProperty("hideUserData")) + object.hideUserData = message.hideUserData; + if (message.useGeLogo !== undefined && message.useGeLogo !== null && message.hasOwnProperty("useGeLogo")) + object.useGeLogo = message.useGeLogo; + if (message.dioramaDescriptionUrlBase !== undefined && message.dioramaDescriptionUrlBase !== null && message.hasOwnProperty("dioramaDescriptionUrlBase")) + object.dioramaDescriptionUrlBase = $types[33].toObject(message.dioramaDescriptionUrlBase, options); + if (message.dioramaDefaultColor !== undefined && message.dioramaDefaultColor !== null && message.hasOwnProperty("dioramaDefaultColor")) + object.dioramaDefaultColor = message.dioramaDefaultColor; + if (message.dioramaBlacklistUrl !== undefined && message.dioramaBlacklistUrl !== null && message.hasOwnProperty("dioramaBlacklistUrl")) + object.dioramaBlacklistUrl = $types[35].toObject(message.dioramaBlacklistUrl, options); + if (message.clientOptions !== undefined && message.clientOptions !== null && message.hasOwnProperty("clientOptions")) + object.clientOptions = $types[36].toObject(message.clientOptions, options); + if (message.fetchingOptions !== undefined && message.fetchingOptions !== null && message.hasOwnProperty("fetchingOptions")) + object.fetchingOptions = $types[37].toObject(message.fetchingOptions, options); + if (message.timeMachineOptions !== undefined && message.timeMachineOptions !== null && message.hasOwnProperty("timeMachineOptions")) + object.timeMachineOptions = $types[38].toObject(message.timeMachineOptions, options); + if (message.csiOptions !== undefined && message.csiOptions !== null && message.hasOwnProperty("csiOptions")) + object.csiOptions = $types[39].toObject(message.csiOptions, options); + if (message.searchTab !== undefined && message.searchTab !== null && message.hasOwnProperty("searchTab")) { + object.searchTab = []; + for (var j = 0; j < message.searchTab.length; ++j) + object.searchTab[j] = $types[40].toObject(message.searchTab[j], options); + } + if (message.cobrandInfo !== undefined && message.cobrandInfo !== null && message.hasOwnProperty("cobrandInfo")) { + object.cobrandInfo = []; + for (var j = 0; j < message.cobrandInfo.length; ++j) + object.cobrandInfo[j] = $types[41].toObject(message.cobrandInfo[j], options); + } + if (message.validDatabase !== undefined && message.validDatabase !== null && message.hasOwnProperty("validDatabase")) { + object.validDatabase = []; + for (var j = 0; j < message.validDatabase.length; ++j) + object.validDatabase[j] = $types[42].toObject(message.validDatabase[j], options); + } + if (message.configScript !== undefined && message.configScript !== null && message.hasOwnProperty("configScript")) { + object.configScript = []; + for (var j = 0; j < message.configScript.length; ++j) + object.configScript[j] = $types[43].toObject(message.configScript[j], options); + } + if (message.deauthServerUrl !== undefined && message.deauthServerUrl !== null && message.hasOwnProperty("deauthServerUrl")) + object.deauthServerUrl = $types[44].toObject(message.deauthServerUrl, options); + if (message.swoopParameters !== undefined && message.swoopParameters !== null && message.hasOwnProperty("swoopParameters")) + object.swoopParameters = $types[45].toObject(message.swoopParameters, options); + if (message.bbsServerInfo !== undefined && message.bbsServerInfo !== null && message.hasOwnProperty("bbsServerInfo")) + object.bbsServerInfo = $types[46].toObject(message.bbsServerInfo, options); + if (message.dataErrorServerInfo !== undefined && message.dataErrorServerInfo !== null && message.hasOwnProperty("dataErrorServerInfo")) + object.dataErrorServerInfo = $types[47].toObject(message.dataErrorServerInfo, options); + if (message.planetaryDatabase !== undefined && message.planetaryDatabase !== null && message.hasOwnProperty("planetaryDatabase")) { + object.planetaryDatabase = []; + for (var j = 0; j < message.planetaryDatabase.length; ++j) + object.planetaryDatabase[j] = $types[48].toObject(message.planetaryDatabase[j], options); + } + if (message.logServer !== undefined && message.logServer !== null && message.hasOwnProperty("logServer")) + object.logServer = $types[49].toObject(message.logServer, options); + if (message.autopiaOptions !== undefined && message.autopiaOptions !== null && message.hasOwnProperty("autopiaOptions")) + object.autopiaOptions = $types[50].toObject(message.autopiaOptions, options); + if (message.searchConfig !== undefined && message.searchConfig !== null && message.hasOwnProperty("searchConfig")) + object.searchConfig = $types[51].toObject(message.searchConfig, options); + if (message.searchInfo !== undefined && message.searchInfo !== null && message.hasOwnProperty("searchInfo")) + object.searchInfo = $types[52].toObject(message.searchInfo, options); + if (message.elevationServiceBaseUrl !== undefined && message.elevationServiceBaseUrl !== null && message.hasOwnProperty("elevationServiceBaseUrl")) + object.elevationServiceBaseUrl = message.elevationServiceBaseUrl; + if (message.elevationProfileQueryDelay !== undefined && message.elevationProfileQueryDelay !== null && message.hasOwnProperty("elevationProfileQueryDelay")) + object.elevationProfileQueryDelay = message.elevationProfileQueryDelay; + if (message.proUpgradeUrl !== undefined && message.proUpgradeUrl !== null && message.hasOwnProperty("proUpgradeUrl")) + object.proUpgradeUrl = $types[55].toObject(message.proUpgradeUrl, options); + if (message.earthCommunityUrl !== undefined && message.earthCommunityUrl !== null && message.hasOwnProperty("earthCommunityUrl")) + object.earthCommunityUrl = $types[56].toObject(message.earthCommunityUrl, options); + if (message.googleMapsUrl !== undefined && message.googleMapsUrl !== null && message.hasOwnProperty("googleMapsUrl")) + object.googleMapsUrl = $types[57].toObject(message.googleMapsUrl, options); + if (message.sharingUrl !== undefined && message.sharingUrl !== null && message.hasOwnProperty("sharingUrl")) + object.sharingUrl = $types[58].toObject(message.sharingUrl, options); + if (message.privacyPolicyUrl !== undefined && message.privacyPolicyUrl !== null && message.hasOwnProperty("privacyPolicyUrl")) + object.privacyPolicyUrl = $types[59].toObject(message.privacyPolicyUrl, options); + if (message.doGplusUserCheck !== undefined && message.doGplusUserCheck !== null && message.hasOwnProperty("doGplusUserCheck")) + object.doGplusUserCheck = message.doGplusUserCheck; + if (message.rocktreeDataProto !== undefined && message.rocktreeDataProto !== null && message.hasOwnProperty("rocktreeDataProto")) + object.rocktreeDataProto = $types[61].toObject(message.rocktreeDataProto, options); + if (message.filmstripConfig !== undefined && message.filmstripConfig !== null && message.hasOwnProperty("filmstripConfig")) { + object.filmstripConfig = []; + for (var j = 0; j < message.filmstripConfig.length; ++j) + object.filmstripConfig[j] = $types[62].toObject(message.filmstripConfig[j], options); + } + if (message.showSigninButton !== undefined && message.showSigninButton !== null && message.hasOwnProperty("showSigninButton")) + object.showSigninButton = message.showSigninButton; + if (message.proMeasureUpsellUrl !== undefined && message.proMeasureUpsellUrl !== null && message.hasOwnProperty("proMeasureUpsellUrl")) + object.proMeasureUpsellUrl = $types[64].toObject(message.proMeasureUpsellUrl, options); + if (message.proPrintUpsellUrl !== undefined && message.proPrintUpsellUrl !== null && message.hasOwnProperty("proPrintUpsellUrl")) + object.proPrintUpsellUrl = $types[65].toObject(message.proPrintUpsellUrl, options); + if (message.starDataProto !== undefined && message.starDataProto !== null && message.hasOwnProperty("starDataProto")) + object.starDataProto = $types[66].toObject(message.starDataProto, options); + if (message.feedbackUrl !== undefined && message.feedbackUrl !== null && message.hasOwnProperty("feedbackUrl")) + object.feedbackUrl = $types[67].toObject(message.feedbackUrl, options); + if (message.oauth2LoginUrl !== undefined && message.oauth2LoginUrl !== null && message.hasOwnProperty("oauth2LoginUrl")) + object.oauth2LoginUrl = $types[68].toObject(message.oauth2LoginUrl, options); + return object; + }; + + EndSnippetProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + EndSnippetProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + EndSnippetProto.SearchConfigProto = (function() { + + function SearchConfigProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SearchConfigProto.prototype.searchServer = $util.emptyArray; + SearchConfigProto.prototype.oneboxService = $util.emptyArray; + SearchConfigProto.prototype.kmlSearchUrl = null; + SearchConfigProto.prototype.kmlRenderUrl = null; + SearchConfigProto.prototype.searchHistoryUrl = null; + SearchConfigProto.prototype.errorPageUrl = null; + + var $types = { + 0 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer", + 1 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto", + 2 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.StringIdOrValueProto", + 5 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + SearchConfigProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (!(message.searchServer && message.searchServer.length)) + message.searchServer = []; + message.searchServer.push($types[0].decode(reader, reader.uint32())); + break; + case 2: + if (!(message.oneboxService && message.oneboxService.length)) + message.oneboxService = []; + message.oneboxService.push($types[1].decode(reader, reader.uint32())); + break; + case 3: + message.kmlSearchUrl = $types[2].decode(reader, reader.uint32()); + break; + case 4: + message.kmlRenderUrl = $types[3].decode(reader, reader.uint32()); + break; + case 6: + message.searchHistoryUrl = $types[4].decode(reader, reader.uint32()); + break; + case 5: + message.errorPageUrl = $types[5].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SearchConfigProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.searchServer !== undefined) { + if (!Array.isArray(message.searchServer)) + return "searchServer: array expected"; + for (var i = 0; i < message.searchServer.length; ++i) { + var error = $types[0].verify(message.searchServer[i]); + if (error) + return "searchServer." + error; + } + } + if (message.oneboxService !== undefined) { + if (!Array.isArray(message.oneboxService)) + return "oneboxService: array expected"; + for (var i = 0; i < message.oneboxService.length; ++i) { + var error = $types[1].verify(message.oneboxService[i]); + if (error) + return "oneboxService." + error; + } + } + if (message.kmlSearchUrl !== undefined && message.kmlSearchUrl !== null) { + var error = $types[2].verify(message.kmlSearchUrl); + if (error) + return "kmlSearchUrl." + error; + } + if (message.kmlRenderUrl !== undefined && message.kmlRenderUrl !== null) { + var error = $types[3].verify(message.kmlRenderUrl); + if (error) + return "kmlRenderUrl." + error; + } + if (message.searchHistoryUrl !== undefined && message.searchHistoryUrl !== null) { + var error = $types[4].verify(message.searchHistoryUrl); + if (error) + return "searchHistoryUrl." + error; + } + if (message.errorPageUrl !== undefined && message.errorPageUrl !== null) { + var error = $types[5].verify(message.errorPageUrl); + if (error) + return "errorPageUrl." + error; + } + return null; + }; + + SearchConfigProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto(); + if (object.searchServer) { + if (!Array.isArray(object.searchServer)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.searchServer: array expected"); + message.searchServer = []; + for (var i = 0; i < object.searchServer.length; ++i) { + if (typeof object.searchServer[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.searchServer: object expected"); + message.searchServer[i] = $types[0].fromObject(object.searchServer[i]); + } + } + if (object.oneboxService) { + if (!Array.isArray(object.oneboxService)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.oneboxService: array expected"); + message.oneboxService = []; + for (var i = 0; i < object.oneboxService.length; ++i) { + if (typeof object.oneboxService[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.oneboxService: object expected"); + message.oneboxService[i] = $types[1].fromObject(object.oneboxService[i]); + } + } + if (object.kmlSearchUrl !== undefined && object.kmlSearchUrl !== null) { + if (typeof object.kmlSearchUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.kmlSearchUrl: object expected"); + message.kmlSearchUrl = $types[2].fromObject(object.kmlSearchUrl); + } + if (object.kmlRenderUrl !== undefined && object.kmlRenderUrl !== null) { + if (typeof object.kmlRenderUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.kmlRenderUrl: object expected"); + message.kmlRenderUrl = $types[3].fromObject(object.kmlRenderUrl); + } + if (object.searchHistoryUrl !== undefined && object.searchHistoryUrl !== null) { + if (typeof object.searchHistoryUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.searchHistoryUrl: object expected"); + message.searchHistoryUrl = $types[4].fromObject(object.searchHistoryUrl); + } + if (object.errorPageUrl !== undefined && object.errorPageUrl !== null) { + if (typeof object.errorPageUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.errorPageUrl: object expected"); + message.errorPageUrl = $types[5].fromObject(object.errorPageUrl); + } + return message; + }; + + SearchConfigProto.from = SearchConfigProto.fromObject; + + SearchConfigProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.searchServer = []; + object.oneboxService = []; + } + if (options.defaults) { + object.kmlSearchUrl = null; + object.kmlRenderUrl = null; + object.searchHistoryUrl = null; + object.errorPageUrl = null; + } + if (message.searchServer !== undefined && message.searchServer !== null && message.hasOwnProperty("searchServer")) { + object.searchServer = []; + for (var j = 0; j < message.searchServer.length; ++j) + object.searchServer[j] = $types[0].toObject(message.searchServer[j], options); + } + if (message.oneboxService !== undefined && message.oneboxService !== null && message.hasOwnProperty("oneboxService")) { + object.oneboxService = []; + for (var j = 0; j < message.oneboxService.length; ++j) + object.oneboxService[j] = $types[1].toObject(message.oneboxService[j], options); + } + if (message.kmlSearchUrl !== undefined && message.kmlSearchUrl !== null && message.hasOwnProperty("kmlSearchUrl")) + object.kmlSearchUrl = $types[2].toObject(message.kmlSearchUrl, options); + if (message.kmlRenderUrl !== undefined && message.kmlRenderUrl !== null && message.hasOwnProperty("kmlRenderUrl")) + object.kmlRenderUrl = $types[3].toObject(message.kmlRenderUrl, options); + if (message.searchHistoryUrl !== undefined && message.searchHistoryUrl !== null && message.hasOwnProperty("searchHistoryUrl")) + object.searchHistoryUrl = $types[4].toObject(message.searchHistoryUrl, options); + if (message.errorPageUrl !== undefined && message.errorPageUrl !== null && message.hasOwnProperty("errorPageUrl")) + object.errorPageUrl = $types[5].toObject(message.errorPageUrl, options); + return object; + }; + + SearchConfigProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SearchConfigProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + SearchConfigProto.SearchServer = (function() { + + function SearchServer(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SearchServer.prototype.name = null; + SearchServer.prototype.url = null; + SearchServer.prototype.type = 0; + SearchServer.prototype.htmlTransformUrl = null; + SearchServer.prototype.kmlTransformUrl = null; + SearchServer.prototype.supplementalUi = null; + SearchServer.prototype.suggestion = $util.emptyArray; + SearchServer.prototype.searchlet = $util.emptyArray; + SearchServer.prototype.requirements = null; + SearchServer.prototype.suggestServer = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 2 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.ResultType", + 3 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.StringIdOrValueProto", + 5 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi", + 6 : "keyhole.dbroot.StringIdOrValueProto", + 7 : "keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto", + 8 : "keyhole.dbroot.RequirementProto", + 9 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + SearchServer.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.url = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.type = reader.uint32(); + break; + case 4: + message.htmlTransformUrl = $types[3].decode(reader, reader.uint32()); + break; + case 5: + message.kmlTransformUrl = $types[4].decode(reader, reader.uint32()); + break; + case 6: + message.supplementalUi = $types[5].decode(reader, reader.uint32()); + break; + case 9: + if (!(message.suggestion && message.suggestion.length)) + message.suggestion = []; + message.suggestion.push($types[6].decode(reader, reader.uint32())); + break; + case 7: + if (!(message.searchlet && message.searchlet.length)) + message.searchlet = []; + message.searchlet.push($types[7].decode(reader, reader.uint32())); + break; + case 8: + message.requirements = $types[8].decode(reader, reader.uint32()); + break; + case 10: + message.suggestServer = $types[9].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SearchServer.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name !== undefined && message.name !== null) { + var error = $types[0].verify(message.name); + if (error) + return "name." + error; + } + if (message.url !== undefined && message.url !== null) { + var error = $types[1].verify(message.url); + if (error) + return "url." + error; + } + if (message.type !== undefined) + switch (message.type) { + default: + return "type: enum value expected"; + case 0: + case 1: + break; + } + if (message.htmlTransformUrl !== undefined && message.htmlTransformUrl !== null) { + var error = $types[3].verify(message.htmlTransformUrl); + if (error) + return "htmlTransformUrl." + error; + } + if (message.kmlTransformUrl !== undefined && message.kmlTransformUrl !== null) { + var error = $types[4].verify(message.kmlTransformUrl); + if (error) + return "kmlTransformUrl." + error; + } + if (message.supplementalUi !== undefined && message.supplementalUi !== null) { + var error = $types[5].verify(message.supplementalUi); + if (error) + return "supplementalUi." + error; + } + if (message.suggestion !== undefined) { + if (!Array.isArray(message.suggestion)) + return "suggestion: array expected"; + for (var i = 0; i < message.suggestion.length; ++i) { + var error = $types[6].verify(message.suggestion[i]); + if (error) + return "suggestion." + error; + } + } + if (message.searchlet !== undefined) { + if (!Array.isArray(message.searchlet)) + return "searchlet: array expected"; + for (var i = 0; i < message.searchlet.length; ++i) { + var error = $types[7].verify(message.searchlet[i]); + if (error) + return "searchlet." + error; + } + } + if (message.requirements !== undefined && message.requirements !== null) { + var error = $types[8].verify(message.requirements); + if (error) + return "requirements." + error; + } + if (message.suggestServer !== undefined && message.suggestServer !== null) { + var error = $types[9].verify(message.suggestServer); + if (error) + return "suggestServer." + error; + } + return null; + }; + + SearchServer.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer(); + if (object.name !== undefined && object.name !== null) { + if (typeof object.name !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.name: object expected"); + message.name = $types[0].fromObject(object.name); + } + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.url: object expected"); + message.url = $types[1].fromObject(object.url); + } + switch (object.type) { + case "RESULT_TYPE_KML": + case 0: + message.type = 0; + break; + case "RESULT_TYPE_XML": + case 1: + message.type = 1; + break; + } + if (object.htmlTransformUrl !== undefined && object.htmlTransformUrl !== null) { + if (typeof object.htmlTransformUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.htmlTransformUrl: object expected"); + message.htmlTransformUrl = $types[3].fromObject(object.htmlTransformUrl); + } + if (object.kmlTransformUrl !== undefined && object.kmlTransformUrl !== null) { + if (typeof object.kmlTransformUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.kmlTransformUrl: object expected"); + message.kmlTransformUrl = $types[4].fromObject(object.kmlTransformUrl); + } + if (object.supplementalUi !== undefined && object.supplementalUi !== null) { + if (typeof object.supplementalUi !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.supplementalUi: object expected"); + message.supplementalUi = $types[5].fromObject(object.supplementalUi); + } + if (object.suggestion) { + if (!Array.isArray(object.suggestion)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.suggestion: array expected"); + message.suggestion = []; + for (var i = 0; i < object.suggestion.length; ++i) { + if (typeof object.suggestion[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.suggestion: object expected"); + message.suggestion[i] = $types[6].fromObject(object.suggestion[i]); + } + } + if (object.searchlet) { + if (!Array.isArray(object.searchlet)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.searchlet: array expected"); + message.searchlet = []; + for (var i = 0; i < object.searchlet.length; ++i) { + if (typeof object.searchlet[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.searchlet: object expected"); + message.searchlet[i] = $types[7].fromObject(object.searchlet[i]); + } + } + if (object.requirements !== undefined && object.requirements !== null) { + if (typeof object.requirements !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.requirements: object expected"); + message.requirements = $types[8].fromObject(object.requirements); + } + if (object.suggestServer !== undefined && object.suggestServer !== null) { + if (typeof object.suggestServer !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.suggestServer: object expected"); + message.suggestServer = $types[9].fromObject(object.suggestServer); + } + return message; + }; + + SearchServer.from = SearchServer.fromObject; + + SearchServer.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.suggestion = []; + object.searchlet = []; + } + if (options.defaults) { + object.name = null; + object.url = null; + object.type = options.enums === String ? "RESULT_TYPE_KML" : 0; + object.htmlTransformUrl = null; + object.kmlTransformUrl = null; + object.supplementalUi = null; + object.requirements = null; + object.suggestServer = null; + } + if (message.name !== undefined && message.name !== null && message.hasOwnProperty("name")) + object.name = $types[0].toObject(message.name, options); + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[1].toObject(message.url, options); + if (message.type !== undefined && message.type !== null && message.hasOwnProperty("type")) + object.type = options.enums === String ? $types[2][message.type] : message.type; + if (message.htmlTransformUrl !== undefined && message.htmlTransformUrl !== null && message.hasOwnProperty("htmlTransformUrl")) + object.htmlTransformUrl = $types[3].toObject(message.htmlTransformUrl, options); + if (message.kmlTransformUrl !== undefined && message.kmlTransformUrl !== null && message.hasOwnProperty("kmlTransformUrl")) + object.kmlTransformUrl = $types[4].toObject(message.kmlTransformUrl, options); + if (message.supplementalUi !== undefined && message.supplementalUi !== null && message.hasOwnProperty("supplementalUi")) + object.supplementalUi = $types[5].toObject(message.supplementalUi, options); + if (message.suggestion !== undefined && message.suggestion !== null && message.hasOwnProperty("suggestion")) { + object.suggestion = []; + for (var j = 0; j < message.suggestion.length; ++j) + object.suggestion[j] = $types[6].toObject(message.suggestion[j], options); + } + if (message.searchlet !== undefined && message.searchlet !== null && message.hasOwnProperty("searchlet")) { + object.searchlet = []; + for (var j = 0; j < message.searchlet.length; ++j) + object.searchlet[j] = $types[7].toObject(message.searchlet[j], options); + } + if (message.requirements !== undefined && message.requirements !== null && message.hasOwnProperty("requirements")) + object.requirements = $types[8].toObject(message.requirements, options); + if (message.suggestServer !== undefined && message.suggestServer !== null && message.hasOwnProperty("suggestServer")) + object.suggestServer = $types[9].toObject(message.suggestServer, options); + return object; + }; + + SearchServer.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SearchServer.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + SearchServer.ResultType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["RESULT_TYPE_KML"] = 0; + values["RESULT_TYPE_XML"] = 1; + return values; + })(); + + SearchServer.SupplementalUi = (function() { + + function SupplementalUi(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SupplementalUi.prototype.url = null; + SupplementalUi.prototype.label = null; + SupplementalUi.prototype.height = 160; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + SupplementalUi.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.label = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.height = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SupplementalUi.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url !== undefined && message.url !== null) { + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + } + if (message.label !== undefined && message.label !== null) { + var error = $types[1].verify(message.label); + if (error) + return "label." + error; + } + if (message.height !== undefined) + if (!$util.isInteger(message.height)) + return "height: integer expected"; + return null; + }; + + SupplementalUi.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + if (object.label !== undefined && object.label !== null) { + if (typeof object.label !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SupplementalUi.label: object expected"); + message.label = $types[1].fromObject(object.label); + } + if (object.height !== undefined && object.height !== null) + message.height = object.height | 0; + return message; + }; + + SupplementalUi.from = SupplementalUi.fromObject; + + SupplementalUi.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = null; + object.label = null; + object.height = 160; + } + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + if (message.label !== undefined && message.label !== null && message.hasOwnProperty("label")) + object.label = $types[1].toObject(message.label, options); + if (message.height !== undefined && message.height !== null && message.hasOwnProperty("height")) + object.height = message.height; + return object; + }; + + SupplementalUi.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SupplementalUi.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SupplementalUi; + })(); + + SearchServer.SearchletProto = (function() { + + function SearchletProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SearchletProto.prototype.url = null; + SearchletProto.prototype.name = null; + SearchletProto.prototype.requirements = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 2 : "keyhole.dbroot.RequirementProto" + }; + $lazyTypes.push($types); + + SearchletProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.name = $types[1].decode(reader, reader.uint32()); + break; + case 3: + message.requirements = $types[2].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SearchletProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url !== undefined && message.url !== null) { + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + } + if (message.name !== undefined && message.name !== null) { + var error = $types[1].verify(message.name); + if (error) + return "name." + error; + } + if (message.requirements !== undefined && message.requirements !== null) { + var error = $types[2].verify(message.requirements); + if (error) + return "requirements." + error; + } + return null; + }; + + SearchletProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + if (object.name !== undefined && object.name !== null) { + if (typeof object.name !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto.name: object expected"); + message.name = $types[1].fromObject(object.name); + } + if (object.requirements !== undefined && object.requirements !== null) { + if (typeof object.requirements !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.SearchServer.SearchletProto.requirements: object expected"); + message.requirements = $types[2].fromObject(object.requirements); + } + return message; + }; + + SearchletProto.from = SearchletProto.fromObject; + + SearchletProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = null; + object.name = null; + object.requirements = null; + } + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + if (message.name !== undefined && message.name !== null && message.hasOwnProperty("name")) + object.name = $types[1].toObject(message.name, options); + if (message.requirements !== undefined && message.requirements !== null && message.hasOwnProperty("requirements")) + object.requirements = $types[2].toObject(message.requirements, options); + return object; + }; + + SearchletProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SearchletProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SearchletProto; + })(); + + return SearchServer; + })(); + + SearchConfigProto.OneboxServiceProto = (function() { + + function OneboxServiceProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + OneboxServiceProto.prototype.serviceUrl = null; + OneboxServiceProto.prototype.requirements = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 1 : "keyhole.dbroot.RequirementProto" + }; + $lazyTypes.push($types); + + OneboxServiceProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.serviceUrl = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.requirements = $types[1].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + OneboxServiceProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.serviceUrl !== undefined && message.serviceUrl !== null) { + var error = $types[0].verify(message.serviceUrl); + if (error) + return "serviceUrl." + error; + } + if (message.requirements !== undefined && message.requirements !== null) { + var error = $types[1].verify(message.requirements); + if (error) + return "requirements." + error; + } + return null; + }; + + OneboxServiceProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto(); + if (object.serviceUrl !== undefined && object.serviceUrl !== null) { + if (typeof object.serviceUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto.serviceUrl: object expected"); + message.serviceUrl = $types[0].fromObject(object.serviceUrl); + } + if (object.requirements !== undefined && object.requirements !== null) { + if (typeof object.requirements !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.SearchConfigProto.OneboxServiceProto.requirements: object expected"); + message.requirements = $types[1].fromObject(object.requirements); + } + return message; + }; + + OneboxServiceProto.from = OneboxServiceProto.fromObject; + + OneboxServiceProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.serviceUrl = null; + object.requirements = null; + } + if (message.serviceUrl !== undefined && message.serviceUrl !== null && message.hasOwnProperty("serviceUrl")) + object.serviceUrl = $types[0].toObject(message.serviceUrl, options); + if (message.requirements !== undefined && message.requirements !== null && message.hasOwnProperty("requirements")) + object.requirements = $types[1].toObject(message.requirements, options); + return object; + }; + + OneboxServiceProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + OneboxServiceProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return OneboxServiceProto; + })(); + + return SearchConfigProto; + })(); + + EndSnippetProto.SearchInfoProto = (function() { + + function SearchInfoProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + SearchInfoProto.prototype.defaultUrl = "http://maps.google.com/maps"; + SearchInfoProto.prototype.geocodeParam = "q"; + + SearchInfoProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.SearchInfoProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.defaultUrl = reader.string(); + break; + case 2: + message.geocodeParam = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + SearchInfoProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.defaultUrl !== undefined) + if (!$util.isString(message.defaultUrl)) + return "defaultUrl: string expected"; + if (message.geocodeParam !== undefined) + if (!$util.isString(message.geocodeParam)) + return "geocodeParam: string expected"; + return null; + }; + + SearchInfoProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.SearchInfoProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.SearchInfoProto(); + if (object.defaultUrl !== undefined && object.defaultUrl !== null) + message.defaultUrl = String(object.defaultUrl); + if (object.geocodeParam !== undefined && object.geocodeParam !== null) + message.geocodeParam = String(object.geocodeParam); + return message; + }; + + SearchInfoProto.from = SearchInfoProto.fromObject; + + SearchInfoProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.defaultUrl = "http://maps.google.com/maps"; + object.geocodeParam = "q"; + } + if (message.defaultUrl !== undefined && message.defaultUrl !== null && message.hasOwnProperty("defaultUrl")) + object.defaultUrl = message.defaultUrl; + if (message.geocodeParam !== undefined && message.geocodeParam !== null && message.hasOwnProperty("geocodeParam")) + object.geocodeParam = message.geocodeParam; + return object; + }; + + SearchInfoProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + SearchInfoProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return SearchInfoProto; + })(); + + EndSnippetProto.RockTreeDataProto = (function() { + + function RockTreeDataProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + RockTreeDataProto.prototype.url = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + RockTreeDataProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.RockTreeDataProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + RockTreeDataProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url !== undefined && message.url !== null) { + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + } + return null; + }; + + RockTreeDataProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.RockTreeDataProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.RockTreeDataProto(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.RockTreeDataProto.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + return message; + }; + + RockTreeDataProto.from = RockTreeDataProto.fromObject; + + RockTreeDataProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.url = null; + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + return object; + }; + + RockTreeDataProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + RockTreeDataProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return RockTreeDataProto; + })(); + + EndSnippetProto.FilmstripConfigProto = (function() { + + function FilmstripConfigProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + FilmstripConfigProto.prototype.requirements = null; + FilmstripConfigProto.prototype.alleycatUrlTemplate = null; + FilmstripConfigProto.prototype.fallbackAlleycatUrlTemplate = null; + FilmstripConfigProto.prototype.metadataUrlTemplate = null; + FilmstripConfigProto.prototype.thumbnailUrlTemplate = null; + FilmstripConfigProto.prototype.kmlUrlTemplate = null; + FilmstripConfigProto.prototype.featuredToursUrl = null; + FilmstripConfigProto.prototype.enableViewportFallback = false; + FilmstripConfigProto.prototype.viewportFallbackDistance = 0; + FilmstripConfigProto.prototype.imageryType = $util.emptyArray; + + var $types = { + 0 : "keyhole.dbroot.RequirementProto", + 1 : "keyhole.dbroot.StringIdOrValueProto", + 2 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.StringIdOrValueProto", + 5 : "keyhole.dbroot.StringIdOrValueProto", + 6 : "keyhole.dbroot.StringIdOrValueProto", + 9 : "keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto" + }; + $lazyTypes.push($types); + + FilmstripConfigProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.requirements = $types[0].decode(reader, reader.uint32()); + break; + case 2: + message.alleycatUrlTemplate = $types[1].decode(reader, reader.uint32()); + break; + case 9: + message.fallbackAlleycatUrlTemplate = $types[2].decode(reader, reader.uint32()); + break; + case 3: + message.metadataUrlTemplate = $types[3].decode(reader, reader.uint32()); + break; + case 4: + message.thumbnailUrlTemplate = $types[4].decode(reader, reader.uint32()); + break; + case 5: + message.kmlUrlTemplate = $types[5].decode(reader, reader.uint32()); + break; + case 6: + message.featuredToursUrl = $types[6].decode(reader, reader.uint32()); + break; + case 7: + message.enableViewportFallback = reader.bool(); + break; + case 8: + message.viewportFallbackDistance = reader.uint32(); + break; + case 10: + if (!(message.imageryType && message.imageryType.length)) + message.imageryType = []; + message.imageryType.push($types[9].decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + FilmstripConfigProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.requirements !== undefined && message.requirements !== null) { + var error = $types[0].verify(message.requirements); + if (error) + return "requirements." + error; + } + if (message.alleycatUrlTemplate !== undefined && message.alleycatUrlTemplate !== null) { + var error = $types[1].verify(message.alleycatUrlTemplate); + if (error) + return "alleycatUrlTemplate." + error; + } + if (message.fallbackAlleycatUrlTemplate !== undefined && message.fallbackAlleycatUrlTemplate !== null) { + var error = $types[2].verify(message.fallbackAlleycatUrlTemplate); + if (error) + return "fallbackAlleycatUrlTemplate." + error; + } + if (message.metadataUrlTemplate !== undefined && message.metadataUrlTemplate !== null) { + var error = $types[3].verify(message.metadataUrlTemplate); + if (error) + return "metadataUrlTemplate." + error; + } + if (message.thumbnailUrlTemplate !== undefined && message.thumbnailUrlTemplate !== null) { + var error = $types[4].verify(message.thumbnailUrlTemplate); + if (error) + return "thumbnailUrlTemplate." + error; + } + if (message.kmlUrlTemplate !== undefined && message.kmlUrlTemplate !== null) { + var error = $types[5].verify(message.kmlUrlTemplate); + if (error) + return "kmlUrlTemplate." + error; + } + if (message.featuredToursUrl !== undefined && message.featuredToursUrl !== null) { + var error = $types[6].verify(message.featuredToursUrl); + if (error) + return "featuredToursUrl." + error; + } + if (message.enableViewportFallback !== undefined) + if (typeof message.enableViewportFallback !== "boolean") + return "enableViewportFallback: boolean expected"; + if (message.viewportFallbackDistance !== undefined) + if (!$util.isInteger(message.viewportFallbackDistance)) + return "viewportFallbackDistance: integer expected"; + if (message.imageryType !== undefined) { + if (!Array.isArray(message.imageryType)) + return "imageryType: array expected"; + for (var i = 0; i < message.imageryType.length; ++i) { + var error = $types[9].verify(message.imageryType[i]); + if (error) + return "imageryType." + error; + } + } + return null; + }; + + FilmstripConfigProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto(); + if (object.requirements !== undefined && object.requirements !== null) { + if (typeof object.requirements !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.requirements: object expected"); + message.requirements = $types[0].fromObject(object.requirements); + } + if (object.alleycatUrlTemplate !== undefined && object.alleycatUrlTemplate !== null) { + if (typeof object.alleycatUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.alleycatUrlTemplate: object expected"); + message.alleycatUrlTemplate = $types[1].fromObject(object.alleycatUrlTemplate); + } + if (object.fallbackAlleycatUrlTemplate !== undefined && object.fallbackAlleycatUrlTemplate !== null) { + if (typeof object.fallbackAlleycatUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.fallbackAlleycatUrlTemplate: object expected"); + message.fallbackAlleycatUrlTemplate = $types[2].fromObject(object.fallbackAlleycatUrlTemplate); + } + if (object.metadataUrlTemplate !== undefined && object.metadataUrlTemplate !== null) { + if (typeof object.metadataUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.metadataUrlTemplate: object expected"); + message.metadataUrlTemplate = $types[3].fromObject(object.metadataUrlTemplate); + } + if (object.thumbnailUrlTemplate !== undefined && object.thumbnailUrlTemplate !== null) { + if (typeof object.thumbnailUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.thumbnailUrlTemplate: object expected"); + message.thumbnailUrlTemplate = $types[4].fromObject(object.thumbnailUrlTemplate); + } + if (object.kmlUrlTemplate !== undefined && object.kmlUrlTemplate !== null) { + if (typeof object.kmlUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.kmlUrlTemplate: object expected"); + message.kmlUrlTemplate = $types[5].fromObject(object.kmlUrlTemplate); + } + if (object.featuredToursUrl !== undefined && object.featuredToursUrl !== null) { + if (typeof object.featuredToursUrl !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.featuredToursUrl: object expected"); + message.featuredToursUrl = $types[6].fromObject(object.featuredToursUrl); + } + if (object.enableViewportFallback !== undefined && object.enableViewportFallback !== null) + message.enableViewportFallback = Boolean(object.enableViewportFallback); + if (object.viewportFallbackDistance !== undefined && object.viewportFallbackDistance !== null) + message.viewportFallbackDistance = object.viewportFallbackDistance >>> 0; + if (object.imageryType) { + if (!Array.isArray(object.imageryType)) + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.imageryType: array expected"); + message.imageryType = []; + for (var i = 0; i < object.imageryType.length; ++i) { + if (typeof object.imageryType[i] !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.imageryType: object expected"); + message.imageryType[i] = $types[9].fromObject(object.imageryType[i]); + } + } + return message; + }; + + FilmstripConfigProto.from = FilmstripConfigProto.fromObject; + + FilmstripConfigProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.imageryType = []; + if (options.defaults) { + object.requirements = null; + object.alleycatUrlTemplate = null; + object.fallbackAlleycatUrlTemplate = null; + object.metadataUrlTemplate = null; + object.thumbnailUrlTemplate = null; + object.kmlUrlTemplate = null; + object.featuredToursUrl = null; + object.enableViewportFallback = false; + object.viewportFallbackDistance = 0; + } + if (message.requirements !== undefined && message.requirements !== null && message.hasOwnProperty("requirements")) + object.requirements = $types[0].toObject(message.requirements, options); + if (message.alleycatUrlTemplate !== undefined && message.alleycatUrlTemplate !== null && message.hasOwnProperty("alleycatUrlTemplate")) + object.alleycatUrlTemplate = $types[1].toObject(message.alleycatUrlTemplate, options); + if (message.fallbackAlleycatUrlTemplate !== undefined && message.fallbackAlleycatUrlTemplate !== null && message.hasOwnProperty("fallbackAlleycatUrlTemplate")) + object.fallbackAlleycatUrlTemplate = $types[2].toObject(message.fallbackAlleycatUrlTemplate, options); + if (message.metadataUrlTemplate !== undefined && message.metadataUrlTemplate !== null && message.hasOwnProperty("metadataUrlTemplate")) + object.metadataUrlTemplate = $types[3].toObject(message.metadataUrlTemplate, options); + if (message.thumbnailUrlTemplate !== undefined && message.thumbnailUrlTemplate !== null && message.hasOwnProperty("thumbnailUrlTemplate")) + object.thumbnailUrlTemplate = $types[4].toObject(message.thumbnailUrlTemplate, options); + if (message.kmlUrlTemplate !== undefined && message.kmlUrlTemplate !== null && message.hasOwnProperty("kmlUrlTemplate")) + object.kmlUrlTemplate = $types[5].toObject(message.kmlUrlTemplate, options); + if (message.featuredToursUrl !== undefined && message.featuredToursUrl !== null && message.hasOwnProperty("featuredToursUrl")) + object.featuredToursUrl = $types[6].toObject(message.featuredToursUrl, options); + if (message.enableViewportFallback !== undefined && message.enableViewportFallback !== null && message.hasOwnProperty("enableViewportFallback")) + object.enableViewportFallback = message.enableViewportFallback; + if (message.viewportFallbackDistance !== undefined && message.viewportFallbackDistance !== null && message.hasOwnProperty("viewportFallbackDistance")) + object.viewportFallbackDistance = message.viewportFallbackDistance; + if (message.imageryType !== undefined && message.imageryType !== null && message.hasOwnProperty("imageryType")) { + object.imageryType = []; + for (var j = 0; j < message.imageryType.length; ++j) + object.imageryType[j] = $types[9].toObject(message.imageryType[j], options); + } + return object; + }; + + FilmstripConfigProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + FilmstripConfigProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + FilmstripConfigProto.AlleycatImageryTypeProto = (function() { + + function AlleycatImageryTypeProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + AlleycatImageryTypeProto.prototype.imageryTypeId = 0; + AlleycatImageryTypeProto.prototype.imageryTypeLabel = ""; + AlleycatImageryTypeProto.prototype.metadataUrlTemplate = null; + AlleycatImageryTypeProto.prototype.thumbnailUrlTemplate = null; + AlleycatImageryTypeProto.prototype.kmlUrlTemplate = null; + + var $types = { + 2 : "keyhole.dbroot.StringIdOrValueProto", + 3 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + AlleycatImageryTypeProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.imageryTypeId = reader.int32(); + break; + case 2: + message.imageryTypeLabel = reader.string(); + break; + case 3: + message.metadataUrlTemplate = $types[2].decode(reader, reader.uint32()); + break; + case 4: + message.thumbnailUrlTemplate = $types[3].decode(reader, reader.uint32()); + break; + case 5: + message.kmlUrlTemplate = $types[4].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + AlleycatImageryTypeProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.imageryTypeId !== undefined) + if (!$util.isInteger(message.imageryTypeId)) + return "imageryTypeId: integer expected"; + if (message.imageryTypeLabel !== undefined) + if (!$util.isString(message.imageryTypeLabel)) + return "imageryTypeLabel: string expected"; + if (message.metadataUrlTemplate !== undefined && message.metadataUrlTemplate !== null) { + var error = $types[2].verify(message.metadataUrlTemplate); + if (error) + return "metadataUrlTemplate." + error; + } + if (message.thumbnailUrlTemplate !== undefined && message.thumbnailUrlTemplate !== null) { + var error = $types[3].verify(message.thumbnailUrlTemplate); + if (error) + return "thumbnailUrlTemplate." + error; + } + if (message.kmlUrlTemplate !== undefined && message.kmlUrlTemplate !== null) { + var error = $types[4].verify(message.kmlUrlTemplate); + if (error) + return "kmlUrlTemplate." + error; + } + return null; + }; + + AlleycatImageryTypeProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto(); + if (object.imageryTypeId !== undefined && object.imageryTypeId !== null) + message.imageryTypeId = object.imageryTypeId | 0; + if (object.imageryTypeLabel !== undefined && object.imageryTypeLabel !== null) + message.imageryTypeLabel = String(object.imageryTypeLabel); + if (object.metadataUrlTemplate !== undefined && object.metadataUrlTemplate !== null) { + if (typeof object.metadataUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto.metadataUrlTemplate: object expected"); + message.metadataUrlTemplate = $types[2].fromObject(object.metadataUrlTemplate); + } + if (object.thumbnailUrlTemplate !== undefined && object.thumbnailUrlTemplate !== null) { + if (typeof object.thumbnailUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto.thumbnailUrlTemplate: object expected"); + message.thumbnailUrlTemplate = $types[3].fromObject(object.thumbnailUrlTemplate); + } + if (object.kmlUrlTemplate !== undefined && object.kmlUrlTemplate !== null) { + if (typeof object.kmlUrlTemplate !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.FilmstripConfigProto.AlleycatImageryTypeProto.kmlUrlTemplate: object expected"); + message.kmlUrlTemplate = $types[4].fromObject(object.kmlUrlTemplate); + } + return message; + }; + + AlleycatImageryTypeProto.from = AlleycatImageryTypeProto.fromObject; + + AlleycatImageryTypeProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.imageryTypeId = 0; + object.imageryTypeLabel = ""; + object.metadataUrlTemplate = null; + object.thumbnailUrlTemplate = null; + object.kmlUrlTemplate = null; + } + if (message.imageryTypeId !== undefined && message.imageryTypeId !== null && message.hasOwnProperty("imageryTypeId")) + object.imageryTypeId = message.imageryTypeId; + if (message.imageryTypeLabel !== undefined && message.imageryTypeLabel !== null && message.hasOwnProperty("imageryTypeLabel")) + object.imageryTypeLabel = message.imageryTypeLabel; + if (message.metadataUrlTemplate !== undefined && message.metadataUrlTemplate !== null && message.hasOwnProperty("metadataUrlTemplate")) + object.metadataUrlTemplate = $types[2].toObject(message.metadataUrlTemplate, options); + if (message.thumbnailUrlTemplate !== undefined && message.thumbnailUrlTemplate !== null && message.hasOwnProperty("thumbnailUrlTemplate")) + object.thumbnailUrlTemplate = $types[3].toObject(message.thumbnailUrlTemplate, options); + if (message.kmlUrlTemplate !== undefined && message.kmlUrlTemplate !== null && message.hasOwnProperty("kmlUrlTemplate")) + object.kmlUrlTemplate = $types[4].toObject(message.kmlUrlTemplate, options); + return object; + }; + + AlleycatImageryTypeProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + AlleycatImageryTypeProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return AlleycatImageryTypeProto; + })(); + + return FilmstripConfigProto; + })(); + + EndSnippetProto.StarDataProto = (function() { + + function StarDataProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + StarDataProto.prototype.url = null; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto" + }; + $lazyTypes.push($types); + + StarDataProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EndSnippetProto.StarDataProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = $types[0].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + StarDataProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.url !== undefined && message.url !== null) { + var error = $types[0].verify(message.url); + if (error) + return "url." + error; + } + return null; + }; + + StarDataProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EndSnippetProto.StarDataProto) + return object; + var message = new $root.keyhole.dbroot.EndSnippetProto.StarDataProto(); + if (object.url !== undefined && object.url !== null) { + if (typeof object.url !== "object") + throw TypeError(".keyhole.dbroot.EndSnippetProto.StarDataProto.url: object expected"); + message.url = $types[0].fromObject(object.url); + } + return message; + }; + + StarDataProto.from = StarDataProto.fromObject; + + StarDataProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.url = null; + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = $types[0].toObject(message.url, options); + return object; + }; + + StarDataProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + StarDataProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return StarDataProto; + })(); + + return EndSnippetProto; + })(); + + dbroot.DbRootRefProto = (function() { + + function DbRootRefProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + DbRootRefProto.prototype.url = ""; + DbRootRefProto.prototype.isCritical = false; + DbRootRefProto.prototype.requirements = null; + + var $types = { + 2 : "keyhole.dbroot.RequirementProto" + }; + $lazyTypes.push($types); + + DbRootRefProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.DbRootRefProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 2: + message.url = reader.string(); + break; + case 1: + message.isCritical = reader.bool(); + break; + case 3: + message.requirements = $types[2].decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + DbRootRefProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isString(message.url)) + return "url: string expected"; + if (message.isCritical !== undefined) + if (typeof message.isCritical !== "boolean") + return "isCritical: boolean expected"; + if (message.requirements !== undefined && message.requirements !== null) { + var error = $types[2].verify(message.requirements); + if (error) + return "requirements." + error; + } + return null; + }; + + DbRootRefProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.DbRootRefProto) + return object; + var message = new $root.keyhole.dbroot.DbRootRefProto(); + if (object.url !== undefined && object.url !== null) + message.url = String(object.url); + if (object.isCritical !== undefined && object.isCritical !== null) + message.isCritical = Boolean(object.isCritical); + if (object.requirements !== undefined && object.requirements !== null) { + if (typeof object.requirements !== "object") + throw TypeError(".keyhole.dbroot.DbRootRefProto.requirements: object expected"); + message.requirements = $types[2].fromObject(object.requirements); + } + return message; + }; + + DbRootRefProto.from = DbRootRefProto.fromObject; + + DbRootRefProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.url = ""; + object.isCritical = false; + object.requirements = null; + } + if (message.url !== undefined && message.url !== null && message.hasOwnProperty("url")) + object.url = message.url; + if (message.isCritical !== undefined && message.isCritical !== null && message.hasOwnProperty("isCritical")) + object.isCritical = message.isCritical; + if (message.requirements !== undefined && message.requirements !== null && message.hasOwnProperty("requirements")) + object.requirements = $types[2].toObject(message.requirements, options); + return object; + }; + + DbRootRefProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + DbRootRefProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return DbRootRefProto; + })(); + + dbroot.DatabaseVersionProto = (function() { + + function DatabaseVersionProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + DatabaseVersionProto.prototype.quadtreeVersion = 0; + + DatabaseVersionProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.DatabaseVersionProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.quadtreeVersion = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + DatabaseVersionProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (!$util.isInteger(message.quadtreeVersion)) + return "quadtreeVersion: integer expected"; + return null; + }; + + DatabaseVersionProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.DatabaseVersionProto) + return object; + var message = new $root.keyhole.dbroot.DatabaseVersionProto(); + if (object.quadtreeVersion !== undefined && object.quadtreeVersion !== null) + message.quadtreeVersion = object.quadtreeVersion >>> 0; + return message; + }; + + DatabaseVersionProto.from = DatabaseVersionProto.fromObject; + + DatabaseVersionProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.quadtreeVersion = 0; + if (message.quadtreeVersion !== undefined && message.quadtreeVersion !== null && message.hasOwnProperty("quadtreeVersion")) + object.quadtreeVersion = message.quadtreeVersion; + return object; + }; + + DatabaseVersionProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + DatabaseVersionProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return DatabaseVersionProto; + })(); + + dbroot.DbRootProto = (function() { + + function DbRootProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + DbRootProto.prototype.databaseName = null; + DbRootProto.prototype.imageryPresent = true; + DbRootProto.prototype.protoImagery = false; + DbRootProto.prototype.terrainPresent = false; + DbRootProto.prototype.providerInfo = $util.emptyArray; + DbRootProto.prototype.nestedFeature = $util.emptyArray; + DbRootProto.prototype.styleAttribute = $util.emptyArray; + DbRootProto.prototype.styleMap = $util.emptyArray; + DbRootProto.prototype.endSnippet = null; + DbRootProto.prototype.translationEntry = $util.emptyArray; + DbRootProto.prototype.language = "en"; + DbRootProto.prototype.version = 5; + DbRootProto.prototype.dbrootReference = $util.emptyArray; + DbRootProto.prototype.databaseVersion = null; + DbRootProto.prototype.refreshTimeout = 0; + + var $types = { + 0 : "keyhole.dbroot.StringIdOrValueProto", + 4 : "keyhole.dbroot.ProviderInfoProto", + 5 : "keyhole.dbroot.NestedFeatureProto", + 6 : "keyhole.dbroot.StyleAttributeProto", + 7 : "keyhole.dbroot.StyleMapProto", + 8 : "keyhole.dbroot.EndSnippetProto", + 9 : "keyhole.dbroot.StringEntryProto", + 12 : "keyhole.dbroot.DbRootRefProto", + 13 : "keyhole.dbroot.DatabaseVersionProto" + }; + $lazyTypes.push($types); + + DbRootProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.DbRootProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 15: + message.databaseName = $types[0].decode(reader, reader.uint32()); + break; + case 1: + message.imageryPresent = reader.bool(); + break; + case 14: + message.protoImagery = reader.bool(); + break; + case 2: + message.terrainPresent = reader.bool(); + break; + case 3: + if (!(message.providerInfo && message.providerInfo.length)) + message.providerInfo = []; + message.providerInfo.push($types[4].decode(reader, reader.uint32())); + break; + case 4: + if (!(message.nestedFeature && message.nestedFeature.length)) + message.nestedFeature = []; + message.nestedFeature.push($types[5].decode(reader, reader.uint32())); + break; + case 5: + if (!(message.styleAttribute && message.styleAttribute.length)) + message.styleAttribute = []; + message.styleAttribute.push($types[6].decode(reader, reader.uint32())); + break; + case 6: + if (!(message.styleMap && message.styleMap.length)) + message.styleMap = []; + message.styleMap.push($types[7].decode(reader, reader.uint32())); + break; + case 7: + message.endSnippet = $types[8].decode(reader, reader.uint32()); + break; + case 8: + if (!(message.translationEntry && message.translationEntry.length)) + message.translationEntry = []; + message.translationEntry.push($types[9].decode(reader, reader.uint32())); + break; + case 9: + message.language = reader.string(); + break; + case 10: + message.version = reader.int32(); + break; + case 11: + if (!(message.dbrootReference && message.dbrootReference.length)) + message.dbrootReference = []; + message.dbrootReference.push($types[12].decode(reader, reader.uint32())); + break; + case 13: + message.databaseVersion = $types[13].decode(reader, reader.uint32()); + break; + case 16: + message.refreshTimeout = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + DbRootProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.databaseName !== undefined && message.databaseName !== null) { + var error = $types[0].verify(message.databaseName); + if (error) + return "databaseName." + error; + } + if (message.imageryPresent !== undefined) + if (typeof message.imageryPresent !== "boolean") + return "imageryPresent: boolean expected"; + if (message.protoImagery !== undefined) + if (typeof message.protoImagery !== "boolean") + return "protoImagery: boolean expected"; + if (message.terrainPresent !== undefined) + if (typeof message.terrainPresent !== "boolean") + return "terrainPresent: boolean expected"; + if (message.providerInfo !== undefined) { + if (!Array.isArray(message.providerInfo)) + return "providerInfo: array expected"; + for (var i = 0; i < message.providerInfo.length; ++i) { + var error = $types[4].verify(message.providerInfo[i]); + if (error) + return "providerInfo." + error; + } + } + if (message.nestedFeature !== undefined) { + if (!Array.isArray(message.nestedFeature)) + return "nestedFeature: array expected"; + for (var i = 0; i < message.nestedFeature.length; ++i) { + var error = $types[5].verify(message.nestedFeature[i]); + if (error) + return "nestedFeature." + error; + } + } + if (message.styleAttribute !== undefined) { + if (!Array.isArray(message.styleAttribute)) + return "styleAttribute: array expected"; + for (var i = 0; i < message.styleAttribute.length; ++i) { + var error = $types[6].verify(message.styleAttribute[i]); + if (error) + return "styleAttribute." + error; + } + } + if (message.styleMap !== undefined) { + if (!Array.isArray(message.styleMap)) + return "styleMap: array expected"; + for (var i = 0; i < message.styleMap.length; ++i) { + var error = $types[7].verify(message.styleMap[i]); + if (error) + return "styleMap." + error; + } + } + if (message.endSnippet !== undefined && message.endSnippet !== null) { + var error = $types[8].verify(message.endSnippet); + if (error) + return "endSnippet." + error; + } + if (message.translationEntry !== undefined) { + if (!Array.isArray(message.translationEntry)) + return "translationEntry: array expected"; + for (var i = 0; i < message.translationEntry.length; ++i) { + var error = $types[9].verify(message.translationEntry[i]); + if (error) + return "translationEntry." + error; + } + } + if (message.language !== undefined) + if (!$util.isString(message.language)) + return "language: string expected"; + if (message.version !== undefined) + if (!$util.isInteger(message.version)) + return "version: integer expected"; + if (message.dbrootReference !== undefined) { + if (!Array.isArray(message.dbrootReference)) + return "dbrootReference: array expected"; + for (var i = 0; i < message.dbrootReference.length; ++i) { + var error = $types[12].verify(message.dbrootReference[i]); + if (error) + return "dbrootReference." + error; + } + } + if (message.databaseVersion !== undefined && message.databaseVersion !== null) { + var error = $types[13].verify(message.databaseVersion); + if (error) + return "databaseVersion." + error; + } + if (message.refreshTimeout !== undefined) + if (!$util.isInteger(message.refreshTimeout)) + return "refreshTimeout: integer expected"; + return null; + }; + + DbRootProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.DbRootProto) + return object; + var message = new $root.keyhole.dbroot.DbRootProto(); + if (object.databaseName !== undefined && object.databaseName !== null) { + if (typeof object.databaseName !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.databaseName: object expected"); + message.databaseName = $types[0].fromObject(object.databaseName); + } + if (object.imageryPresent !== undefined && object.imageryPresent !== null) + message.imageryPresent = Boolean(object.imageryPresent); + if (object.protoImagery !== undefined && object.protoImagery !== null) + message.protoImagery = Boolean(object.protoImagery); + if (object.terrainPresent !== undefined && object.terrainPresent !== null) + message.terrainPresent = Boolean(object.terrainPresent); + if (object.providerInfo) { + if (!Array.isArray(object.providerInfo)) + throw TypeError(".keyhole.dbroot.DbRootProto.providerInfo: array expected"); + message.providerInfo = []; + for (var i = 0; i < object.providerInfo.length; ++i) { + if (typeof object.providerInfo[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.providerInfo: object expected"); + message.providerInfo[i] = $types[4].fromObject(object.providerInfo[i]); + } + } + if (object.nestedFeature) { + if (!Array.isArray(object.nestedFeature)) + throw TypeError(".keyhole.dbroot.DbRootProto.nestedFeature: array expected"); + message.nestedFeature = []; + for (var i = 0; i < object.nestedFeature.length; ++i) { + if (typeof object.nestedFeature[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.nestedFeature: object expected"); + message.nestedFeature[i] = $types[5].fromObject(object.nestedFeature[i]); + } + } + if (object.styleAttribute) { + if (!Array.isArray(object.styleAttribute)) + throw TypeError(".keyhole.dbroot.DbRootProto.styleAttribute: array expected"); + message.styleAttribute = []; + for (var i = 0; i < object.styleAttribute.length; ++i) { + if (typeof object.styleAttribute[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.styleAttribute: object expected"); + message.styleAttribute[i] = $types[6].fromObject(object.styleAttribute[i]); + } + } + if (object.styleMap) { + if (!Array.isArray(object.styleMap)) + throw TypeError(".keyhole.dbroot.DbRootProto.styleMap: array expected"); + message.styleMap = []; + for (var i = 0; i < object.styleMap.length; ++i) { + if (typeof object.styleMap[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.styleMap: object expected"); + message.styleMap[i] = $types[7].fromObject(object.styleMap[i]); + } + } + if (object.endSnippet !== undefined && object.endSnippet !== null) { + if (typeof object.endSnippet !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.endSnippet: object expected"); + message.endSnippet = $types[8].fromObject(object.endSnippet); + } + if (object.translationEntry) { + if (!Array.isArray(object.translationEntry)) + throw TypeError(".keyhole.dbroot.DbRootProto.translationEntry: array expected"); + message.translationEntry = []; + for (var i = 0; i < object.translationEntry.length; ++i) { + if (typeof object.translationEntry[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.translationEntry: object expected"); + message.translationEntry[i] = $types[9].fromObject(object.translationEntry[i]); + } + } + if (object.language !== undefined && object.language !== null) + message.language = String(object.language); + if (object.version !== undefined && object.version !== null) + message.version = object.version | 0; + if (object.dbrootReference) { + if (!Array.isArray(object.dbrootReference)) + throw TypeError(".keyhole.dbroot.DbRootProto.dbrootReference: array expected"); + message.dbrootReference = []; + for (var i = 0; i < object.dbrootReference.length; ++i) { + if (typeof object.dbrootReference[i] !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.dbrootReference: object expected"); + message.dbrootReference[i] = $types[12].fromObject(object.dbrootReference[i]); + } + } + if (object.databaseVersion !== undefined && object.databaseVersion !== null) { + if (typeof object.databaseVersion !== "object") + throw TypeError(".keyhole.dbroot.DbRootProto.databaseVersion: object expected"); + message.databaseVersion = $types[13].fromObject(object.databaseVersion); + } + if (object.refreshTimeout !== undefined && object.refreshTimeout !== null) + message.refreshTimeout = object.refreshTimeout | 0; + return message; + }; + + DbRootProto.from = DbRootProto.fromObject; + + DbRootProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.providerInfo = []; + object.nestedFeature = []; + object.styleAttribute = []; + object.styleMap = []; + object.translationEntry = []; + object.dbrootReference = []; + } + if (options.defaults) { + object.databaseName = null; + object.imageryPresent = true; + object.protoImagery = false; + object.terrainPresent = false; + object.endSnippet = null; + object.language = "en"; + object.version = 5; + object.databaseVersion = null; + object.refreshTimeout = 0; + } + if (message.databaseName !== undefined && message.databaseName !== null && message.hasOwnProperty("databaseName")) + object.databaseName = $types[0].toObject(message.databaseName, options); + if (message.imageryPresent !== undefined && message.imageryPresent !== null && message.hasOwnProperty("imageryPresent")) + object.imageryPresent = message.imageryPresent; + if (message.protoImagery !== undefined && message.protoImagery !== null && message.hasOwnProperty("protoImagery")) + object.protoImagery = message.protoImagery; + if (message.terrainPresent !== undefined && message.terrainPresent !== null && message.hasOwnProperty("terrainPresent")) + object.terrainPresent = message.terrainPresent; + if (message.providerInfo !== undefined && message.providerInfo !== null && message.hasOwnProperty("providerInfo")) { + object.providerInfo = []; + for (var j = 0; j < message.providerInfo.length; ++j) + object.providerInfo[j] = $types[4].toObject(message.providerInfo[j], options); + } + if (message.nestedFeature !== undefined && message.nestedFeature !== null && message.hasOwnProperty("nestedFeature")) { + object.nestedFeature = []; + for (var j = 0; j < message.nestedFeature.length; ++j) + object.nestedFeature[j] = $types[5].toObject(message.nestedFeature[j], options); + } + if (message.styleAttribute !== undefined && message.styleAttribute !== null && message.hasOwnProperty("styleAttribute")) { + object.styleAttribute = []; + for (var j = 0; j < message.styleAttribute.length; ++j) + object.styleAttribute[j] = $types[6].toObject(message.styleAttribute[j], options); + } + if (message.styleMap !== undefined && message.styleMap !== null && message.hasOwnProperty("styleMap")) { + object.styleMap = []; + for (var j = 0; j < message.styleMap.length; ++j) + object.styleMap[j] = $types[7].toObject(message.styleMap[j], options); + } + if (message.endSnippet !== undefined && message.endSnippet !== null && message.hasOwnProperty("endSnippet")) + object.endSnippet = $types[8].toObject(message.endSnippet, options); + if (message.translationEntry !== undefined && message.translationEntry !== null && message.hasOwnProperty("translationEntry")) { + object.translationEntry = []; + for (var j = 0; j < message.translationEntry.length; ++j) + object.translationEntry[j] = $types[9].toObject(message.translationEntry[j], options); + } + if (message.language !== undefined && message.language !== null && message.hasOwnProperty("language")) + object.language = message.language; + if (message.version !== undefined && message.version !== null && message.hasOwnProperty("version")) + object.version = message.version; + if (message.dbrootReference !== undefined && message.dbrootReference !== null && message.hasOwnProperty("dbrootReference")) { + object.dbrootReference = []; + for (var j = 0; j < message.dbrootReference.length; ++j) + object.dbrootReference[j] = $types[12].toObject(message.dbrootReference[j], options); + } + if (message.databaseVersion !== undefined && message.databaseVersion !== null && message.hasOwnProperty("databaseVersion")) + object.databaseVersion = $types[13].toObject(message.databaseVersion, options); + if (message.refreshTimeout !== undefined && message.refreshTimeout !== null && message.hasOwnProperty("refreshTimeout")) + object.refreshTimeout = message.refreshTimeout; + return object; + }; + + DbRootProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + DbRootProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return DbRootProto; + })(); + + dbroot.EncryptedDbRootProto = (function() { + + function EncryptedDbRootProto(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + this[keys[i]] = properties[keys[i]]; + } + + EncryptedDbRootProto.prototype.encryptionType = 0; + EncryptedDbRootProto.prototype.encryptionData = $util.newBuffer([]); + EncryptedDbRootProto.prototype.dbrootData = $util.newBuffer([]); + + var $types = { + 0 : "keyhole.dbroot.EncryptedDbRootProto.EncryptionType" + }; + $lazyTypes.push($types); + + EncryptedDbRootProto.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, + message = new $root.keyhole.dbroot.EncryptedDbRootProto(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.encryptionType = reader.uint32(); + break; + case 2: + message.encryptionData = reader.bytes(); + break; + case 3: + message.dbrootData = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + EncryptedDbRootProto.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.encryptionType !== undefined) + switch (message.encryptionType) { + default: + return "encryptionType: enum value expected"; + case 0: + break; + } + if (message.encryptionData !== undefined) + if (!(message.encryptionData && typeof message.encryptionData.length === "number" || $util.isString(message.encryptionData))) + return "encryptionData: buffer expected"; + if (message.dbrootData !== undefined) + if (!(message.dbrootData && typeof message.dbrootData.length === "number" || $util.isString(message.dbrootData))) + return "dbrootData: buffer expected"; + return null; + }; + + EncryptedDbRootProto.fromObject = function fromObject(object) { + if (object instanceof $root.keyhole.dbroot.EncryptedDbRootProto) + return object; + var message = new $root.keyhole.dbroot.EncryptedDbRootProto(); + switch (object.encryptionType) { + case "ENCRYPTION_XOR": + case 0: + message.encryptionType = 0; + break; + } + if (object.encryptionData !== undefined && object.encryptionData !== null) + if (typeof object.encryptionData === "string") + $util.base64.decode(object.encryptionData, message.encryptionData = $util.newBuffer($util.base64.length(object.encryptionData)), 0); + else if (object.encryptionData.length) + message.encryptionData = object.encryptionData; + if (object.dbrootData !== undefined && object.dbrootData !== null) + if (typeof object.dbrootData === "string") + $util.base64.decode(object.dbrootData, message.dbrootData = $util.newBuffer($util.base64.length(object.dbrootData)), 0); + else if (object.dbrootData.length) + message.dbrootData = object.dbrootData; + return message; + }; + + EncryptedDbRootProto.from = EncryptedDbRootProto.fromObject; + + EncryptedDbRootProto.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.encryptionType = options.enums === String ? "ENCRYPTION_XOR" : 0; + object.encryptionData = options.bytes === String ? "" : []; + object.dbrootData = options.bytes === String ? "" : []; + } + if (message.encryptionType !== undefined && message.encryptionType !== null && message.hasOwnProperty("encryptionType")) + object.encryptionType = options.enums === String ? $types[0][message.encryptionType] : message.encryptionType; + if (message.encryptionData !== undefined && message.encryptionData !== null && message.hasOwnProperty("encryptionData")) + object.encryptionData = options.bytes === String ? $util.base64.encode(message.encryptionData, 0, message.encryptionData.length) : options.bytes === Array ? Array.prototype.slice.call(message.encryptionData) : message.encryptionData; + if (message.dbrootData !== undefined && message.dbrootData !== null && message.hasOwnProperty("dbrootData")) + object.dbrootData = options.bytes === String ? $util.base64.encode(message.dbrootData, 0, message.dbrootData.length) : options.bytes === Array ? Array.prototype.slice.call(message.dbrootData) : message.dbrootData; + return object; + }; + + EncryptedDbRootProto.prototype.toObject = function toObject(options) { + return this.constructor.toObject(this, options); + }; + + EncryptedDbRootProto.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + EncryptedDbRootProto.EncryptionType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values["ENCRYPTION_XOR"] = 0; + return values; + })(); + + return EncryptedDbRootProto; + })(); + + return dbroot; + })(); + + return keyhole; + })(); + + $util.lazyResolve($root, $lazyTypes); + + // End generated code + + return $root.keyhole.dbroot; +}); diff --git a/Source/ThirdParty/jsep.js b/Source/ThirdParty/jsep.js new file mode 100644 index 000000000000..442b3d4531ca --- /dev/null +++ b/Source/ThirdParty/jsep.js @@ -0,0 +1,685 @@ +// JavaScript Expression Parser (JSEP) 0.3.1 +// JSEP may be freely distributed under the MIT License +// http://jsep.from.so/ + +/*global define*/ +define(function() { + +/*global module: true, exports: true, console: true */ +(function (root) { + 'use strict'; + // Node Types + // ---------- + + // This is the full set of types that any JSEP node can be. + // Store them here to save space when minified + var COMPOUND = 'Compound', + IDENTIFIER = 'Identifier', + MEMBER_EXP = 'MemberExpression', + LITERAL = 'Literal', + THIS_EXP = 'ThisExpression', + CALL_EXP = 'CallExpression', + UNARY_EXP = 'UnaryExpression', + BINARY_EXP = 'BinaryExpression', + LOGICAL_EXP = 'LogicalExpression', + CONDITIONAL_EXP = 'ConditionalExpression', + ARRAY_EXP = 'ArrayExpression', + + PERIOD_CODE = 46, // '.' + COMMA_CODE = 44, // ',' + SQUOTE_CODE = 39, // single quote + DQUOTE_CODE = 34, // double quotes + OPAREN_CODE = 40, // ( + CPAREN_CODE = 41, // ) + OBRACK_CODE = 91, // [ + CBRACK_CODE = 93, // ] + QUMARK_CODE = 63, // ? + SEMCOL_CODE = 59, // ; + COLON_CODE = 58, // : + + throwError = function(message, index) { + var error = new Error(message + ' at character ' + index); + error.index = index; + error.description = message; + throw error; + }, + + // Operations + // ---------- + + // Set `t` to `true` to save space (when minified, not gzipped) + t = true, + // Use a quickly-accessible map to store all of the unary operators + // Values are set to `true` (it really doesn't matter) + unary_ops = {'-': t, '!': t, '~': t, '+': t}, + // Also use a map for the binary operations but set their values to their + // binary precedence for quick reference: + // see [Order of operations](http://en.wikipedia.org/wiki/Order_of_operations#Programming_language) + binary_ops = { + '||': 1, '&&': 2, '|': 3, '^': 4, '&': 5, + '==': 6, '!=': 6, '===': 6, '!==': 6, + '<': 7, '>': 7, '<=': 7, '>=': 7, + '<<':8, '>>': 8, '>>>': 8, + '+': 9, '-': 9, + '*': 10, '/': 10, '%': 10 + }, + // Get return the longest key length of any object + getMaxKeyLen = function(obj) { + var max_len = 0, len; + for(var key in obj) { + if((len = key.length) > max_len && obj.hasOwnProperty(key)) { + max_len = len; + } + } + return max_len; + }, + max_unop_len = getMaxKeyLen(unary_ops), + max_binop_len = getMaxKeyLen(binary_ops), + // Literals + // ---------- + // Store the values to return for the various literals we may encounter + literals = { + 'true': true, + 'false': false, + 'null': null + }, + // Except for `this`, which is special. This could be changed to something like `'self'` as well + this_str = 'this', + // Returns the precedence of a binary operator or `0` if it isn't a binary operator + binaryPrecedence = function(op_val) { + return binary_ops[op_val] || 0; + }, + // Utility function (gets called from multiple places) + // Also note that `a && b` and `a || b` are *logical* expressions, not binary expressions + createBinaryExpression = function (operator, left, right) { + var type = (operator === '||' || operator === '&&') ? LOGICAL_EXP : BINARY_EXP; + return { + type: type, + operator: operator, + left: left, + right: right + }; + }, + // `ch` is a character code in the next three functions + isDecimalDigit = function(ch) { + return (ch >= 48 && ch <= 57); // 0...9 + }, + isIdentifierStart = function(ch) { + return (ch === 36) || (ch === 95) || // `$` and `_` + (ch >= 65 && ch <= 90) || // A...Z + (ch >= 97 && ch <= 122) || // a...z + (ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator + }, + isIdentifierPart = function(ch) { + return (ch === 36) || (ch === 95) || // `$` and `_` + (ch >= 65 && ch <= 90) || // A...Z + (ch >= 97 && ch <= 122) || // a...z + (ch >= 48 && ch <= 57) || // 0...9 + (ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator + }, + + // Parsing + // ------- + // `expr` is a string with the passed in expression + jsep = function(expr) { + // `index` stores the character number we are currently at while `length` is a constant + // All of the gobbles below will modify `index` as we move along + var index = 0, + charAtFunc = expr.charAt, + charCodeAtFunc = expr.charCodeAt, + exprI = function(i) { return charAtFunc.call(expr, i); }, + exprICode = function(i) { return charCodeAtFunc.call(expr, i); }, + length = expr.length, + + // Push `index` up to the next non-space character + gobbleSpaces = function() { + var ch = exprICode(index); + // space or tab + while(ch === 32 || ch === 9) { + ch = exprICode(++index); + } + }, + + // The main parsing function. Much of this code is dedicated to ternary expressions + gobbleExpression = function() { + var test = gobbleBinaryExpression(), + consequent, alternate; + gobbleSpaces(); + if(exprICode(index) === QUMARK_CODE) { + // Ternary expression: test ? consequent : alternate + index++; + consequent = gobbleExpression(); + if(!consequent) { + throwError('Expected expression', index); + } + gobbleSpaces(); + if(exprICode(index) === COLON_CODE) { + index++; + alternate = gobbleExpression(); + if(!alternate) { + throwError('Expected expression', index); + } + return { + type: CONDITIONAL_EXP, + test: test, + consequent: consequent, + alternate: alternate + }; + } else { + throwError('Expected :', index); + } + } else { + return test; + } + }, + + // Search for the operation portion of the string (e.g. `+`, `===`) + // Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`) + // and move down from 3 to 2 to 1 character until a matching binary operation is found + // then, return that binary operation + gobbleBinaryOp = function() { + gobbleSpaces(); + var biop, to_check = expr.substr(index, max_binop_len), tc_len = to_check.length; + while(tc_len > 0) { + if(binary_ops.hasOwnProperty(to_check)) { + index += tc_len; + return to_check; + } + to_check = to_check.substr(0, --tc_len); + } + return false; + }, + + // This function is responsible for gobbling an individual expression, + // e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)` + gobbleBinaryExpression = function() { + var ch_i, node, biop, prec, stack, biop_info, left, right, i; + + // First, try to get the leftmost thing + // Then, check to see if there's a binary operator operating on that leftmost thing + left = gobbleToken(); + biop = gobbleBinaryOp(); + + // If there wasn't a binary operator, just return the leftmost node + if(!biop) { + return left; + } + + // Otherwise, we need to start a stack to properly place the binary operations in their + // precedence structure + biop_info = { value: biop, prec: binaryPrecedence(biop)}; + + right = gobbleToken(); + if(!right) { + throwError("Expected expression after " + biop, index); + } + stack = [left, biop_info, right]; + + // Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm) + while((biop = gobbleBinaryOp())) { + prec = binaryPrecedence(biop); + + if(prec === 0) { + break; + } + biop_info = { value: biop, prec: prec }; + + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { + right = stack.pop(); + biop = stack.pop().value; + left = stack.pop(); + node = createBinaryExpression(biop, left, right); + stack.push(node); + } + + node = gobbleToken(); + if(!node) { + throwError("Expected expression after " + biop, index); + } + stack.push(biop_info, node); + } + + i = stack.length - 1; + node = stack[i]; + while(i > 1) { + node = createBinaryExpression(stack[i - 1].value, stack[i - 2], node); + i -= 2; + } + return node; + }, + + // An individual part of a binary expression: + // e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis) + gobbleToken = function() { + var ch, to_check, tc_len; + + gobbleSpaces(); + ch = exprICode(index); + + if(isDecimalDigit(ch) || ch === PERIOD_CODE) { + // Char code 46 is a dot `.` which can start off a numeric literal + return gobbleNumericLiteral(); + } else if(ch === SQUOTE_CODE || ch === DQUOTE_CODE) { + // Single or double quotes + return gobbleStringLiteral(); + } else if(isIdentifierStart(ch) || ch === OPAREN_CODE) { // open parenthesis + // `foo`, `bar.baz` + return gobbleVariable(); + } else if (ch === OBRACK_CODE) { + return gobbleArray(); + } else { + to_check = expr.substr(index, max_unop_len); + tc_len = to_check.length; + while(tc_len > 0) { + if(unary_ops.hasOwnProperty(to_check)) { + index += tc_len; + return { + type: UNARY_EXP, + operator: to_check, + argument: gobbleToken(), + prefix: true + }; + } + to_check = to_check.substr(0, --tc_len); + } + + return false; + } + }, + // Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to + // keep track of everything in the numeric literal and then calling `parseFloat` on that string + gobbleNumericLiteral = function() { + var number = '', ch, chCode; + while(isDecimalDigit(exprICode(index))) { + number += exprI(index++); + } + + if(exprICode(index) === PERIOD_CODE) { // can start with a decimal marker + number += exprI(index++); + + while(isDecimalDigit(exprICode(index))) { + number += exprI(index++); + } + } + + ch = exprI(index); + if(ch === 'e' || ch === 'E') { // exponent marker + number += exprI(index++); + ch = exprI(index); + if(ch === '+' || ch === '-') { // exponent sign + number += exprI(index++); + } + while(isDecimalDigit(exprICode(index))) { //exponent itself + number += exprI(index++); + } + if(!isDecimalDigit(exprICode(index-1)) ) { + throwError('Expected exponent (' + number + exprI(index) + ')', index); + } + } + + + chCode = exprICode(index); + // Check to make sure this isn't a variable name that start with a number (123abc) + if(isIdentifierStart(chCode)) { + throwError('Variable names cannot start with a number (' + + number + exprI(index) + ')', index); + } else if(chCode === PERIOD_CODE) { + throwError('Unexpected period', index); + } + + return { + type: LITERAL, + value: parseFloat(number), + raw: number + }; + }, + + // Parses a string literal, staring with single or double quotes with basic support for escape codes + // e.g. `"hello world"`, `'this is\nJSEP'` + gobbleStringLiteral = function() { + var str = '', quote = exprI(index++), closed = false, ch; + + while(index < length) { + ch = exprI(index++); + if(ch === quote) { + closed = true; + break; + } else if(ch === '\\') { + // Check for all of the common escape codes + ch = exprI(index++); + switch(ch) { + case 'n': str += '\n'; break; + case 'r': str += '\r'; break; + case 't': str += '\t'; break; + case 'b': str += '\b'; break; + case 'f': str += '\f'; break; + case 'v': str += '\x0B'; break; + default : str += '\\' + ch; + } + } else { + str += ch; + } + } + + if(!closed) { + throwError('Unclosed quote after "'+str+'"', index); + } + + return { + type: LITERAL, + value: str, + raw: quote + str + quote + }; + }, + + // Gobbles only identifiers + // e.g.: `foo`, `_value`, `$x1` + // Also, this function checks if that identifier is a literal: + // (e.g. `true`, `false`, `null`) or `this` + gobbleIdentifier = function() { + var ch = exprICode(index), start = index, identifier; + + if(isIdentifierStart(ch)) { + index++; + } else { + throwError('Unexpected ' + exprI(index), index); + } + + while(index < length) { + ch = exprICode(index); + if(isIdentifierPart(ch)) { + index++; + } else { + break; + } + } + identifier = expr.slice(start, index); + + if(literals.hasOwnProperty(identifier)) { + return { + type: LITERAL, + value: literals[identifier], + raw: identifier + }; + } else if(identifier === this_str) { + return { type: THIS_EXP }; + } else { + return { + type: IDENTIFIER, + name: identifier + }; + } + }, + + // Gobbles a list of arguments within the context of a function call + // or array literal. This function also assumes that the opening character + // `(` or `[` has already been gobbled, and gobbles expressions and commas + // until the terminator character `)` or `]` is encountered. + // e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]` + gobbleArguments = function(termination) { + var ch_i, args = [], node, closed = false; + while(index < length) { + gobbleSpaces(); + ch_i = exprICode(index); + if(ch_i === termination) { // done parsing + closed = true; + index++; + break; + } else if (ch_i === COMMA_CODE) { // between expressions + index++; + } else { + node = gobbleExpression(); + if(!node || node.type === COMPOUND) { + throwError('Expected comma', index); + } + args.push(node); + } + } + if (!closed) { + throwError('Expected ' + String.fromCharCode(termination), index); + } + return args; + }, + + // Gobble a non-literal variable name. This variable name may include properties + // e.g. `foo`, `bar.baz`, `foo['bar'].baz` + // It also gobbles function calls: + // e.g. `Math.acos(obj.angle)` + gobbleVariable = function() { + var ch_i, node; + ch_i = exprICode(index); + + if(ch_i === OPAREN_CODE) { + node = gobbleGroup(); + } else { + node = gobbleIdentifier(); + } + gobbleSpaces(); + ch_i = exprICode(index); + while(ch_i === PERIOD_CODE || ch_i === OBRACK_CODE || ch_i === OPAREN_CODE) { + index++; + if(ch_i === PERIOD_CODE) { + gobbleSpaces(); + node = { + type: MEMBER_EXP, + computed: false, + object: node, + property: gobbleIdentifier() + }; + } else if(ch_i === OBRACK_CODE) { + node = { + type: MEMBER_EXP, + computed: true, + object: node, + property: gobbleExpression() + }; + gobbleSpaces(); + ch_i = exprICode(index); + if(ch_i !== CBRACK_CODE) { + throwError('Unclosed [', index); + } + index++; + } else if(ch_i === OPAREN_CODE) { + // A function call is being made; gobble all the arguments + node = { + type: CALL_EXP, + 'arguments': gobbleArguments(CPAREN_CODE), + callee: node + }; + } + gobbleSpaces(); + ch_i = exprICode(index); + } + return node; + }, + + // Responsible for parsing a group of things within parentheses `()` + // This function assumes that it needs to gobble the opening parenthesis + // and then tries to gobble everything within that parenthesis, assuming + // that the next thing it should see is the close parenthesis. If not, + // then the expression probably doesn't have a `)` + gobbleGroup = function() { + index++; + var node = gobbleExpression(); + gobbleSpaces(); + if(exprICode(index) === CPAREN_CODE) { + index++; + return node; + } else { + throwError('Unclosed (', index); + } + }, + + // Responsible for parsing Array literals `[1, 2, 3]` + // This function assumes that it needs to gobble the opening bracket + // and then tries to gobble the expressions as arguments. + gobbleArray = function() { + index++; + return { + type: ARRAY_EXP, + elements: gobbleArguments(CBRACK_CODE) + }; + }, + + nodes = [], ch_i, node; + + while(index < length) { + ch_i = exprICode(index); + + // Expressions can be separated by semicolons, commas, or just inferred without any + // separators + if(ch_i === SEMCOL_CODE || ch_i === COMMA_CODE) { + index++; // ignore separators + } else { + // Try to gobble each expression individually + if((node = gobbleExpression())) { + nodes.push(node); + // If we weren't able to find a binary expression and are out of room, then + // the expression passed in probably has too much + } else if(index < length) { + throwError('Unexpected "' + exprI(index) + '"', index); + } + } + } + + // If there's only one expression just try returning the expression + if(nodes.length === 1) { + return nodes[0]; + } else { + return { + type: COMPOUND, + body: nodes + }; + } + }; + + // To be filled in by the template + jsep.version = '0.3.1'; + jsep.toString = function() { return 'JavaScript Expression Parser (JSEP) v' + jsep.version; }; + + /** + * @method jsep.addUnaryOp + * @param {string} op_name The name of the unary op to add + * @return jsep + */ + jsep.addUnaryOp = function(op_name) { + max_unop_len = Math.max(op_name.length, max_unop_len); + unary_ops[op_name] = t; return this; + }; + + /** + * @method jsep.addBinaryOp + * @param {string} op_name The name of the binary op to add + * @param {number} precedence The precedence of the binary op (can be a float) + * @return jsep + */ + jsep.addBinaryOp = function(op_name, precedence) { + max_binop_len = Math.max(op_name.length, max_binop_len); + binary_ops[op_name] = precedence; + return this; + }; + + /** + * @method jsep.addLiteral + * @param {string} literal_name The name of the literal to add + * @param {*} literal_value The value of the literal + * @return jsep + */ + jsep.addLiteral = function(literal_name, literal_value) { + literals[literal_name] = literal_value; + return this; + }; + + /** + * @method jsep.removeUnaryOp + * @param {string} op_name The name of the unary op to remove + * @return jsep + */ + jsep.removeUnaryOp = function(op_name) { + delete unary_ops[op_name]; + if(op_name.length === max_unop_len) { + max_unop_len = getMaxKeyLen(unary_ops); + } + return this; + }; + + /** + * @method jsep.removeAllUnaryOps + * @return jsep + */ + jsep.removeAllUnaryOps = function() { + unary_ops = {}; + max_unop_len = 0; + + return this; + }; + + /** + * @method jsep.removeBinaryOp + * @param {string} op_name The name of the binary op to remove + * @return jsep + */ + jsep.removeBinaryOp = function(op_name) { + delete binary_ops[op_name]; + if(op_name.length === max_binop_len) { + max_binop_len = getMaxKeyLen(binary_ops); + } + return this; + }; + + /** + * @method jsep.removeAllBinaryOps + * @return jsep + */ + jsep.removeAllBinaryOps = function() { + binary_ops = {}; + max_binop_len = 0; + + return this; + }; + + /** + * @method jsep.removeLiteral + * @param {string} literal_name The name of the literal to remove + * @return jsep + */ + jsep.removeLiteral = function(literal_name) { + delete literals[literal_name]; + return this; + }; + + /** + * @method jsep.removeAllLiterals + * @return jsep + */ + jsep.removeAllLiterals = function() { + literals = {}; + + return this; + }; + + // In desktop environments, have a way to restore the old value for `jsep` + if (typeof exports === 'undefined') { + var old_jsep = root.jsep; + // The star of the show! It's a function! + root.jsep = jsep; + // And a courteous function willing to move out of the way for other similarly-named objects! + jsep.noConflict = function() { + if(root.jsep === jsep) { + root.jsep = old_jsep; + } + return jsep; + }; + } else { + // In Node.JS environments + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = jsep; + } else { + exports.parse = jsep; + } + } +}(this)); + + return jsep.noConflict(); +}); diff --git a/Source/ThirdParty/pako_inflate.js b/Source/ThirdParty/pako_inflate.js new file mode 100644 index 000000000000..b46f693c763c --- /dev/null +++ b/Source/ThirdParty/pako_inflate.js @@ -0,0 +1,3125 @@ +/* pako 1.0.4 nodeca/pako */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pako = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); + } + _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start + + +// convert string to array (typed, when possible) + exports.string2buf = function (str) { + var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; + + // count binary size + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; + } + + // allocate buffer + buf = new utils.Buf8(buf_len); + + // convert + for (i = 0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + if (c < 0x80) { + /* one byte */ + buf[i++] = c; + } else if (c < 0x800) { + /* two bytes */ + buf[i++] = 0xC0 | (c >>> 6); + buf[i++] = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + /* three bytes */ + buf[i++] = 0xE0 | (c >>> 12); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } else { + /* four bytes */ + buf[i++] = 0xf0 | (c >>> 18); + buf[i++] = 0x80 | (c >>> 12 & 0x3f); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } + } + + return buf; + }; + +// Helper (used in 2 places) + function buf2binstring(buf, len) { + // use fallback for big arrays to avoid stack overflow + if (len < 65537) { + if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) { + return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len)); + } + } + + var result = ''; + for (var i = 0; i < len; i++) { + result += String.fromCharCode(buf[i]); + } + return result; + } + + +// Convert byte array to binary string + exports.buf2binstring = function (buf) { + return buf2binstring(buf, buf.length); + }; + + +// Convert binary string (typed, when possible) + exports.binstring2buf = function (str) { + var buf = new utils.Buf8(str.length); + for (var i = 0, len = buf.length; i < len; i++) { + buf[i] = str.charCodeAt(i); + } + return buf; + }; + + +// convert array to string + exports.buf2string = function (buf, max) { + var i, out, c, c_len; + var len = max || buf.length; + + // Reserve max possible length (2 words per char) + // NB: by unknown reasons, Array is significantly faster for + // String.fromCharCode.apply than Uint16Array. + var utf16buf = new Array(len * 2); + + for (out = 0, i = 0; i < len;) { + c = buf[i++]; + // quick process ascii + if (c < 0x80) { utf16buf[out++] = c; continue; } + + c_len = _utf8len[c]; + // skip 5 & 6 byte codes + if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; } + + // apply mask on first byte + c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; + // join the rest + while (c_len > 1 && i < len) { + c = (c << 6) | (buf[i++] & 0x3f); + c_len--; + } + + // terminated by end of string? + if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } + + if (c < 0x10000) { + utf16buf[out++] = c; + } else { + c -= 0x10000; + utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); + utf16buf[out++] = 0xdc00 | (c & 0x3ff); + } + } + + return buf2binstring(utf16buf, out); + }; + + +// Calculate max possible position in utf8 buffer, +// that will not break sequence. If that's not possible +// - (very small limits) return max size as is. +// +// buf[] - utf8 bytes array +// max - length limit (mandatory); + exports.utf8border = function (buf, max) { + var pos; + + max = max || buf.length; + if (max > buf.length) { max = buf.length; } + + // go back from last position, until start of sequence found + pos = max - 1; + while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } + + // Fuckup - very small and broken sequence, + // return max, because we should return something anyway. + if (pos < 0) { return max; } + + // If we came to start of buffer - that means vuffer is too small, + // return max too. + if (pos === 0) { return max; } + + return (pos + _utf8len[buf[pos]] > max) ? pos : max; + }; + +},{"./common":1}],3:[function(require,module,exports){ + 'use strict'; + +// Note: adler32 takes 12% for level 0 and 2% for level 6. +// It doesn't worth to make additional optimizationa as in original. +// Small size is preferable. + + function adler32(adler, buf, len, pos) { + var s1 = (adler & 0xffff) |0, + s2 = ((adler >>> 16) & 0xffff) |0, + n = 0; + + while (len !== 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) |0; + s2 = (s2 + s1) |0; + } while (--n); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) |0; + } + + + module.exports = adler32; + +},{}],4:[function(require,module,exports){ + 'use strict'; + + + module.exports = { + + /* Allowed flush values; see deflate() and inflate() below for details */ + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_TREES: 6, + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + //Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + //Z_VERSION_ERROR: -6, + + /* compression levels */ + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + + + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + + /* Possible values of the data_type field (though see inflate()) */ + Z_BINARY: 0, + Z_TEXT: 1, + //Z_ASCII: 1, // = Z_TEXT (deprecated) + Z_UNKNOWN: 2, + + /* The deflate compression method */ + Z_DEFLATED: 8 + //Z_NULL: null // Use -1 or null inline, depending on var type + }; + +},{}],5:[function(require,module,exports){ + 'use strict'; + +// Note: we can't get significant speed boost here. +// So write code to minimize size - no pregenerated tables +// and array tools dependencies. + + +// Use ordinary array, since untyped makes no boost here + function makeTable() { + var c, table = []; + + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; + } + +// Create table on load. Just 255 signed longs. Not a problem. + var crcTable = makeTable(); + + + function crc32(crc, buf, len, pos) { + var t = crcTable, + end = pos + len; + + crc ^= -1; + + for (var i = pos; i < end; i++) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; + } + + + module.exports = crc32; + +},{}],6:[function(require,module,exports){ + 'use strict'; + + + function GZheader() { + /* true if compressed data believed to be text */ + this.text = 0; + /* modification time */ + this.time = 0; + /* extra flags (not used when writing a gzip file) */ + this.xflags = 0; + /* operating system */ + this.os = 0; + /* pointer to extra field or Z_NULL if none */ + this.extra = null; + /* extra field length (valid if extra != Z_NULL) */ + this.extra_len = 0; // Actually, we don't need it in JS, + // but leave for few code modifications + + // + // Setup limits is not necessary because in js we should not preallocate memory + // for inflate use constant limit in 65536 bytes + // + + /* space at extra (only when reading header) */ + // this.extra_max = 0; + /* pointer to zero-terminated file name or Z_NULL */ + this.name = ''; + /* space at name (only when reading header) */ + // this.name_max = 0; + /* pointer to zero-terminated comment or Z_NULL */ + this.comment = ''; + /* space at comment (only when reading header) */ + // this.comm_max = 0; + /* true if there was or will be a header crc */ + this.hcrc = 0; + /* true when done reading gzip header (not used when writing a gzip file) */ + this.done = false; + } + + module.exports = GZheader; + +},{}],7:[function(require,module,exports){ + 'use strict'; + +// See state defs from inflate.js + var BAD = 30; /* got a data error -- remain here until reset */ + var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + + /* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ + module.exports = function inflate_fast(strm, start) { + var state; + var _in; /* local strm.input */ + var last; /* have enough input while in < last */ + var _out; /* local strm.output */ + var beg; /* inflate()'s initial strm.output */ + var end; /* while out < end, enough space available */ +//#ifdef INFLATE_STRICT + var dmax; /* maximum distance from zlib header */ +//#endif + var wsize; /* window size or zero if not using window */ + var whave; /* valid bytes in the window */ + var wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + var s_window; /* allocated sliding window, if wsize != 0 */ + var hold; /* local strm.hold */ + var bits; /* local strm.bits */ + var lcode; /* local strm.lencode */ + var dcode; /* local strm.distcode */ + var lmask; /* mask for first level of length codes */ + var dmask; /* mask for first level of distance codes */ + var here; /* retrieved table entry */ + var op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + var len; /* match length, unused bytes */ + var dist; /* match distance */ + var from; /* where to copy match from */ + var from_source; + + + var input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + state = strm.state; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); +//#ifdef INFLATE_STRICT + dmax = state.dmax; +//#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + dolen: + for (;;) { // Goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + if (op === 0) { /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff/*here.val*/; + } + else if (op & 16) { /* length base */ + len = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + dodist: + for (;;) { // goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + + if (op & 16) { /* distance base */ + dist = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); +//#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } +//#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } + +// (!) This block is disabled in zlib defailts, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// if (len <= op - whave) { +// do { +// output[_out++] = 0; +// } while (--len); +// continue top; +// } +// len -= op - whave; +// do { +// output[_out++] = 0; +// } while (--op > whave); +// if (op === 0) { +// from = _out - dist; +// do { +// output[_out++] = output[from++]; +// } while (--len); +// continue top; +// } +//#endif + } + from = 0; // window index + from_source = s_window; + if (wnext === 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } + else { + from = _out - dist; /* copy direct from output */ + do { /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } + else if ((op & 64) === 0) { /* 2nd level distance code */ + here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dodist; + } + else { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } + else if ((op & 64) === 0) { /* 2nd level length code */ + here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dolen; + } + else if (op & 32) { /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = TYPE; + break top; + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } while (_in < last && _out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); + strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); + state.hold = hold; + state.bits = bits; + return; + }; + +},{}],8:[function(require,module,exports){ + 'use strict'; + + + var utils = require('../utils/common'); + var adler32 = require('./adler32'); + var crc32 = require('./crc32'); + var inflate_fast = require('./inffast'); + var inflate_table = require('./inftrees'); + + var CODES = 0; + var LENS = 1; + var DISTS = 2; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + + /* Allowed flush values; see deflate() and inflate() below for details */ +//var Z_NO_FLUSH = 0; +//var Z_PARTIAL_FLUSH = 1; +//var Z_SYNC_FLUSH = 2; +//var Z_FULL_FLUSH = 3; + var Z_FINISH = 4; + var Z_BLOCK = 5; + var Z_TREES = 6; + + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + var Z_OK = 0; + var Z_STREAM_END = 1; + var Z_NEED_DICT = 2; +//var Z_ERRNO = -1; + var Z_STREAM_ERROR = -2; + var Z_DATA_ERROR = -3; + var Z_MEM_ERROR = -4; + var Z_BUF_ERROR = -5; +//var Z_VERSION_ERROR = -6; + + /* The deflate compression method */ + var Z_DEFLATED = 8; + + + /* STATES ====================================================================*/ + /* ===========================================================================*/ + + + var HEAD = 1; /* i: waiting for magic header */ + var FLAGS = 2; /* i: waiting for method and flags (gzip) */ + var TIME = 3; /* i: waiting for modification time (gzip) */ + var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ + var EXLEN = 5; /* i: waiting for extra length (gzip) */ + var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ + var NAME = 7; /* i: waiting for end of file name (gzip) */ + var COMMENT = 8; /* i: waiting for end of comment (gzip) */ + var HCRC = 9; /* i: waiting for header crc (gzip) */ + var DICTID = 10; /* i: waiting for dictionary check value */ + var DICT = 11; /* waiting for inflateSetDictionary() call */ + var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ + var STORED = 14; /* i: waiting for stored size (length and complement) */ + var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ + var COPY = 16; /* i/o: waiting for input or output to copy stored block */ + var TABLE = 17; /* i: waiting for dynamic block table lengths */ + var LENLENS = 18; /* i: waiting for code length code lengths */ + var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ + var LEN_ = 20; /* i: same as LEN below, but only first time in */ + var LEN = 21; /* i: waiting for length/lit/eob code */ + var LENEXT = 22; /* i: waiting for length extra bits */ + var DIST = 23; /* i: waiting for distance code */ + var DISTEXT = 24; /* i: waiting for distance extra bits */ + var MATCH = 25; /* o: waiting for output space to copy string */ + var LIT = 26; /* o: waiting for output space to write literal */ + var CHECK = 27; /* i: waiting for 32-bit check value */ + var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ + var DONE = 29; /* finished check, done -- remain here until reset */ + var BAD = 30; /* got a data error -- remain here until reset */ + var MEM = 31; /* got an inflate() memory error -- remain here until reset */ + var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + + /* ===========================================================================*/ + + + + var ENOUGH_LENS = 852; + var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + var MAX_WBITS = 15; + /* 32K LZ77 window */ + var DEF_WBITS = MAX_WBITS; + + + function zswap32(q) { + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); + } + + + function InflateState() { + this.mode = 0; /* current inflate mode */ + this.last = false; /* true if processing last block */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib) */ + this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ + this.check = 0; /* protected copy of check value */ + this.total = 0; /* protected copy of output count */ + // TODO: may be {} + this.head = null; /* where to save gzip header information */ + + /* sliding window */ + this.wbits = 0; /* log base 2 of requested window size */ + this.wsize = 0; /* window size or zero if not using window */ + this.whave = 0; /* valid bytes in the window */ + this.wnext = 0; /* window write index */ + this.window = null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + this.hold = 0; /* input bit accumulator */ + this.bits = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + this.length = 0; /* literal or length of data to copy */ + this.offset = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + this.extra = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + this.lencode = null; /* starting table for length/literal codes */ + this.distcode = null; /* starting table for distance codes */ + this.lenbits = 0; /* index bits for lencode */ + this.distbits = 0; /* index bits for distcode */ + + /* dynamic table building */ + this.ncode = 0; /* number of code length code lengths */ + this.nlen = 0; /* number of length code lengths */ + this.ndist = 0; /* number of distance code lengths */ + this.have = 0; /* number of code lengths in lens[] */ + this.next = null; /* next available space in codes[] */ + + this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ + this.work = new utils.Buf16(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ + this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ + this.distdyn = null; /* dynamic table for distance codes (JS specific) */ + this.sane = 0; /* if false, allow invalid distance too far */ + this.back = 0; /* bits back of last unprocessed length/lit */ + this.was = 0; /* initial length of match */ + } + + function inflateResetKeep(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ''; /*Z_NULL*/ + if (state.wrap) { /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null/*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); + state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK; + } + + function inflateReset(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); + + } + + function inflateReset2(strm, windowBits) { + var wrap; + var state; + + /* get the state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); + } + + function inflateInit2(strm, windowBits) { + var ret; + var state; + + if (!strm) { return Z_STREAM_ERROR; } + //strm.msg = Z_NULL; /* in case we return an error */ + + state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.window = null/*Z_NULL*/; + ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK) { + strm.state = null/*Z_NULL*/; + } + return ret; + } + + function inflateInit(strm) { + return inflateInit2(strm, DEF_WBITS); + } + + + /* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ + var virgin = true; + + var lenfix, distfix; // We have no pointers in JS, so keep tables separate + + function fixedtables(state) { + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + var sym; + + lenfix = new utils.Buf32(512); + distfix = new utils.Buf32(32); + + /* literal/length table */ + sym = 0; + while (sym < 144) { state.lens[sym++] = 8; } + while (sym < 256) { state.lens[sym++] = 9; } + while (sym < 280) { state.lens[sym++] = 7; } + while (sym < 288) { state.lens[sym++] = 8; } + + inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; + } + + + /* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ + function updatewindow(strm, src, end, copy) { + var dist; + var state = strm.state; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new utils.Buf8(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); + state.wnext = 0; + state.whave = state.wsize; + } + else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + utils.arraySet(state.window, src, end - copy, dist, state.wnext); + copy -= dist; + if (copy) { + //zmemcpy(state->window, end - copy, copy); + utils.arraySet(state.window, src, end - copy, copy, 0); + state.wnext = copy; + state.whave = state.wsize; + } + else { + state.wnext += dist; + if (state.wnext === state.wsize) { state.wnext = 0; } + if (state.whave < state.wsize) { state.whave += dist; } + } + } + return 0; + } + + function inflate(strm, flush) { + var state; + var input, output; // input/output buffers + var next; /* next input INDEX */ + var put; /* next output INDEX */ + var have, left; /* available input and output */ + var hold; /* bit buffer */ + var bits; /* bits in bit buffer */ + var _in, _out; /* save starting available input and output */ + var copy; /* number of stored or match bytes to copy */ + var from; /* where to copy match bytes from */ + var from_source; + var here = 0; /* current decoding table entry */ + var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) + //var last; /* parent table entry */ + var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + var len; /* length to copy for repeats, bits to drop */ + var ret; /* return code */ + var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ + var opts; + + var n; // temporary var for NEED_BITS + + var order = /* permutation of code lengths */ + [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + + + if (!strm || !strm.state || !strm.output || + (!strm.input && strm.avail_in !== 0)) { + return Z_STREAM_ERROR; + } + + state = strm.state; + if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ + + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = Z_OK; + + inf_leave: // goto emulation + for (;;) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ + state.check = 0/*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + break; + } + state.flags = 0; /* expect zlib header */ + if (state.head) { + state.head.done = false; + } + if (!(state.wrap & 1) || /* check if zlib header allowed */ + (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { + strm.msg = 'incorrect header check'; + state.mode = BAD; + break; + } + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } + else if (len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD; + break; + } + state.dmax = 1 << len; + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID : TYPE; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.flags = hold; + if ((state.flags & 0xff) !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + if (state.flags & 0xe000) { + strm.msg = 'unknown header flags set'; + state.mode = BAD; + break; + } + if (state.head) { + state.head.text = ((hold >> 8) & 1); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.time = hold; + } + if (state.flags & 0x0200) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = crc32(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.xflags = (hold & 0xff); + state.head.os = (hold >> 8); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length = hold; + if (state.head) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400) { + copy = state.length; + if (copy > have) { copy = have; } + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + // Use untyped array for more conveniend processing later + state.head.extra = new Array(state.head.extra_len); + } + utils.arraySet( + state.head.extra, + input, + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + copy, + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length) { break inf_leave; } + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.name_max*/)) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.comm_max*/)) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head) { + state.head.hcrc = ((state.flags >> 9) & 1); + state.head.done = true; + } + strm.adler = state.check = 0; + state.mode = TYPE; + break; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (state.havedict === 0) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return Z_NEED_DICT; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + /* falls through */ + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + break; + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.last = (hold & 0x01)/*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03)/*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush === Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break inf_leave; + } + break; + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + break; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { + strm.msg = 'invalid stored block lengths'; + state.mode = BAD; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy) { + if (copy > have) { copy = have; } + if (copy > left) { copy = left; } + if (copy === 0) { break inf_leave; } + //--- zmemcpy(put, next, copy); --- + utils.arraySet(output, input, next, copy, put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE; + break; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// +//#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = 'too many length or distance symbols'; + state.mode = BAD; + break; + } +//#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = { bits: state.lenbits }; + ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + + if (ret) { + strm.msg = 'invalid code lengths set'; + state.mode = BAD; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } + else { + if (here_val === 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have === 0) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03);//BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } + else if (here_val === 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f);//BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD) { break; } + + /* check for end-of-block code (better have one) */ + if (state.lens[256] === 0) { + strm.msg = 'invalid code -- missing end-of-block'; + state.mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = { bits: state.lenbits }; + ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret) { + strm.msg = 'invalid literal/lengths set'; + state.mode = BAD; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret) { + strm.msg = 'invalid distances set'; + state.mode = BAD; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + inflate_fast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode === TYPE) { + state.back = -1; + } + break; + } + state.back = 0; + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_op && (here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.lencode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE; + break; + } + if (here_op & 64) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + for (;;) { + here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if ((here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.distcode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64) { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } +//#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +//#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left === 0) { break inf_leave; } + copy = _out - left; + if (state.offset > copy) { /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +// (!) This block is disabled in zlib defailts, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// Trace((stderr, "inflate.c too far\n")); +// copy -= state.whave; +// if (copy > state.length) { copy = state.length; } +// if (copy > left) { copy = left; } +// left -= copy; +// state.length -= copy; +// do { +// output[put++] = 0; +// } while (--copy); +// if (state.length === 0) { state.mode = LEN; } +// break; +//#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } + else { + from = state.wnext - copy; + } + if (copy > state.length) { copy = state.length; } + from_source = state.window; + } + else { /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) { copy = left; } + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy); + if (state.length === 0) { state.mode = LEN; } + break; + case LIT: + if (left === 0) { break inf_leave; } + output[put++] = state.length; + left--; + state.mode = LEN; + break; + case CHECK: + if (state.wrap) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + // Use '|' insdead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = state.check = + /*UPDATE(state.check, put - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); + + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = Z_STREAM_END; + break inf_leave; + case BAD: + ret = Z_DATA_ERROR; + break inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + /* falls through */ + default: + return Z_STREAM_ERROR; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush !== Z_FINISH))) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { + state.mode = MEM; + return Z_MEM_ERROR; + } + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap && _out) { + strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE ? 128 : 0) + + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { + ret = Z_BUF_ERROR; + } + return ret; + } + + function inflateEnd(strm) { + + if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { + return Z_STREAM_ERROR; + } + + var state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK; + } + + function inflateGetHeader(strm, head) { + var state; + + /* check state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK; + } + + function inflateSetDictionary(strm, dictionary) { + var dictLength = dictionary.length; + + var state; + var dictid; + var ret; + + /* check state */ + if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; + } + + exports.inflateReset = inflateReset; + exports.inflateReset2 = inflateReset2; + exports.inflateResetKeep = inflateResetKeep; + exports.inflateInit = inflateInit; + exports.inflateInit2 = inflateInit2; + exports.inflate = inflate; + exports.inflateEnd = inflateEnd; + exports.inflateGetHeader = inflateGetHeader; + exports.inflateSetDictionary = inflateSetDictionary; + exports.inflateInfo = 'pako inflate (from Nodeca project)'; + + /* Not implemented + exports.inflateCopy = inflateCopy; + exports.inflateGetDictionary = inflateGetDictionary; + exports.inflateMark = inflateMark; + exports.inflatePrime = inflatePrime; + exports.inflateSync = inflateSync; + exports.inflateSyncPoint = inflateSyncPoint; + exports.inflateUndermine = inflateUndermine; + */ + +},{"../utils/common":1,"./adler32":3,"./crc32":5,"./inffast":7,"./inftrees":9}],9:[function(require,module,exports){ + 'use strict'; + + + var utils = require('../utils/common'); + + var MAXBITS = 15; + var ENOUGH_LENS = 852; + var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + var CODES = 0; + var LENS = 1; + var DISTS = 2; + + var lbase = [ /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + ]; + + var lext = [ /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 + ]; + + var dbase = [ /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0 + ]; + + var dext = [ /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64 + ]; + + module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) + { + var bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + var len = 0; /* a code's length in bits */ + var sym = 0; /* index of code symbols */ + var min = 0, max = 0; /* minimum and maximum code lengths */ + var root = 0; /* number of index bits for root table */ + var curr = 0; /* number of index bits for current table */ + var drop = 0; /* code bits to drop for sub-table */ + var left = 0; /* number of prefix codes available */ + var used = 0; /* code entries in table used */ + var huff = 0; /* Huffman code */ + var incr; /* for incrementing code, index */ + var fill; /* index for replicating entries */ + var low; /* low bits for current root entry */ + var mask; /* mask for low root bits */ + var next; /* next available space in table */ + var base = null; /* base value table to use */ + var base_index = 0; +// var shoextra; /* extra bits table to use */ + var end; /* use base and extra for symbol > end */ + var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ + var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ + var extra = null; + var extra_index = 0; + + var here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) { break; } + } + if (root > max) { + root = max; + } + if (max === 0) { /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) { break; } + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES || max !== 1)) { + return -1; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type === CODES) { + base = extra = work; /* dummy value--not used */ + end = 19; + + } else if (type === LENS) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + + } else { /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } + else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; + } while (fill !== 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + if (--count[len] === 0) { + if (len === max) { break; } + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) !== low) { + /* if first time, transition to sub-tables */ + if (drop === 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { break; } + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff !== 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return 0; + }; + +},{"../utils/common":1}],10:[function(require,module,exports){ + 'use strict'; + + module.exports = { + 2: 'need dictionary', /* Z_NEED_DICT 2 */ + 1: 'stream end', /* Z_STREAM_END 1 */ + 0: '', /* Z_OK 0 */ + '-1': 'file error', /* Z_ERRNO (-1) */ + '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ + '-3': 'data error', /* Z_DATA_ERROR (-3) */ + '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ + '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ + '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ + }; + +},{}],11:[function(require,module,exports){ + 'use strict'; + + + function ZStream() { + /* next input byte */ + this.input = null; // JS specific, because we have no pointers + this.next_in = 0; + /* number of bytes available at input */ + this.avail_in = 0; + /* total number of input bytes read so far */ + this.total_in = 0; + /* next output byte should be put there */ + this.output = null; // JS specific, because we have no pointers + this.next_out = 0; + /* remaining free space at output */ + this.avail_out = 0; + /* total number of bytes output so far */ + this.total_out = 0; + /* last error message, NULL if no error */ + this.msg = ''/*Z_NULL*/; + /* not visible by applications */ + this.state = null; + /* best guess about the data type: binary or text */ + this.data_type = 2/*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + this.adler = 0; + } + + module.exports = ZStream; + +},{}],"/lib/inflate.js":[function(require,module,exports){ + 'use strict'; + + + var zlib_inflate = require('./zlib/inflate'); + var utils = require('./utils/common'); + var strings = require('./utils/strings'); + var c = require('./zlib/constants'); + var msg = require('./zlib/messages'); + var ZStream = require('./zlib/zstream'); + var GZheader = require('./zlib/gzheader'); + + var toString = Object.prototype.toString; + + /** + * class Inflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[inflate]] + * and [[inflateRaw]]. + **/ + + /* internal + * inflate.chunks -> Array + * + * Chunks of output data, if [[Inflate#onData]] not overriden. + **/ + + /** + * Inflate.result -> Uint8Array|Array|String + * + * Uncompressed result, generated by default [[Inflate#onData]] + * and [[Inflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you + * push a chunk with explicit flush (call [[Inflate#push]] with + * `Z_SYNC_FLUSH` param). + **/ + + /** + * Inflate.err -> Number + * + * Error code after inflate finished. 0 (Z_OK) on success. + * Should be checked if broken data possible. + **/ + + /** + * Inflate.msg -> String + * + * Error message, if [[Inflate.err]] != 0 + **/ + + + /** + * new Inflate(options) + * - options (Object): zlib inflate options. + * + * Creates new inflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `windowBits` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw inflate + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * By default, when no options set, autodetect deflate/gzip data format via + * wrapper header. + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) + * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * var inflate = new pako.Inflate({ level: 3}); + * + * inflate.push(chunk1, false); + * inflate.push(chunk2, true); // true -> last chunk + * + * if (inflate.err) { throw new Error(inflate.err); } + * + * console.log(inflate.result); + * ``` + **/ + function Inflate(options) { + if (!(this instanceof Inflate)) return new Inflate(options); + + this.options = utils.assign({ + chunkSize: 16384, + windowBits: 0, + to: '' + }, options || {}); + + var opt = this.options; + + // Force window size for `raw` data, if not set directly, + // because we have no header for autodetect. + if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { + opt.windowBits = -opt.windowBits; + if (opt.windowBits === 0) { opt.windowBits = -15; } + } + + // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate + if ((opt.windowBits >= 0) && (opt.windowBits < 16) && + !(options && options.windowBits)) { + opt.windowBits += 32; + } + + // Gzip header has no info about windows size, we can do autodetect only + // for deflate. So, if window size not set, force it to max when gzip possible + if ((opt.windowBits > 15) && (opt.windowBits < 48)) { + // bit 3 (16) -> gzipped data + // bit 4 (32) -> autodetect gzip/deflate + if ((opt.windowBits & 15) === 0) { + opt.windowBits |= 15; + } + } + + this.err = 0; // error code, if happens (0 = Z_OK) + this.msg = ''; // error message + this.ended = false; // used to avoid multiple onEnd() calls + this.chunks = []; // chunks of compressed data + + this.strm = new ZStream(); + this.strm.avail_out = 0; + + var status = zlib_inflate.inflateInit2( + this.strm, + opt.windowBits + ); + + if (status !== c.Z_OK) { + throw new Error(msg[status]); + } + + this.header = new GZheader(); + + zlib_inflate.inflateGetHeader(this.strm, this.header); + } + + /** + * Inflate#push(data[, mode]) -> Boolean + * - data (Uint8Array|Array|ArrayBuffer|String): input data + * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH. + * + * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with + * new output chunks. Returns `true` on success. The last data block must have + * mode Z_FINISH (or `true`). That will flush internal pending buffers and call + * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you + * can use mode Z_SYNC_FLUSH, keeping the decompression context. + * + * On fail call [[Inflate#onEnd]] with error code and return false. + * + * We strongly recommend to use `Uint8Array` on input for best speed (output + * format is detected automatically). Also, don't skip last param and always + * use the same type in your code (boolean or number). That will improve JS speed. + * + * For regular `Array`-s make sure all elements are [0..255]. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + **/ + Inflate.prototype.push = function (data, mode) { + var strm = this.strm; + var chunkSize = this.options.chunkSize; + var dictionary = this.options.dictionary; + var status, _mode; + var next_out_utf8, tail, utf8str; + var dict; + + // Flag to properly process Z_BUF_ERROR on testing inflate call + // when we check that all output data was flushed. + var allowBufError = false; + + if (this.ended) { return false; } + _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH); + + // Convert data if needed + if (typeof data === 'string') { + // Only binary strings can be decompressed on practice + strm.input = strings.binstring2buf(data); + } else if (toString.call(data) === '[object ArrayBuffer]') { + strm.input = new Uint8Array(data); + } else { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + do { + if (strm.avail_out === 0) { + strm.output = new utils.Buf8(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */ + + if (status === c.Z_NEED_DICT && dictionary) { + // Convert data if needed + if (typeof dictionary === 'string') { + dict = strings.string2buf(dictionary); + } else if (toString.call(dictionary) === '[object ArrayBuffer]') { + dict = new Uint8Array(dictionary); + } else { + dict = dictionary; + } + + status = zlib_inflate.inflateSetDictionary(this.strm, dict); + + } + + if (status === c.Z_BUF_ERROR && allowBufError === true) { + status = c.Z_OK; + allowBufError = false; + } + + if (status !== c.Z_STREAM_END && status !== c.Z_OK) { + this.onEnd(status); + this.ended = true; + return false; + } + + if (strm.next_out) { + if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) { + + if (this.options.to === 'string') { + + next_out_utf8 = strings.utf8border(strm.output, strm.next_out); + + tail = strm.next_out - next_out_utf8; + utf8str = strings.buf2string(strm.output, next_out_utf8); + + // move tail + strm.next_out = tail; + strm.avail_out = chunkSize - tail; + if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); } + + this.onData(utf8str); + + } else { + this.onData(utils.shrinkBuf(strm.output, strm.next_out)); + } + } + } + + // When no more input data, we should check that internal inflate buffers + // are flushed. The only way to do it when avail_out = 0 - run one more + // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR. + // Here we set flag to process this error properly. + // + // NOTE. Deflate does not return error in this case and does not needs such + // logic. + if (strm.avail_in === 0 && strm.avail_out === 0) { + allowBufError = true; + } + + } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END); + + if (status === c.Z_STREAM_END) { + _mode = c.Z_FINISH; + } + + // Finalize on the last chunk. + if (_mode === c.Z_FINISH) { + status = zlib_inflate.inflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return status === c.Z_OK; + } + + // callback interim results if Z_SYNC_FLUSH. + if (_mode === c.Z_SYNC_FLUSH) { + this.onEnd(c.Z_OK); + strm.avail_out = 0; + return true; + } + + return true; + }; + + + /** + * Inflate#onData(chunk) -> Void + * - chunk (Uint8Array|Array|String): ouput data. Type of array depends + * on js engine support. When string output requested, each chunk + * will be string. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + **/ + Inflate.prototype.onData = function (chunk) { + this.chunks.push(chunk); + }; + + + /** + * Inflate#onEnd(status) -> Void + * - status (Number): inflate status. 0 (Z_OK) on success, + * other if not. + * + * Called either after you tell inflate that the input stream is + * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) + * or if an error happened. By default - join collected chunks, + * free memory and fill `results` / `err` properties. + **/ + Inflate.prototype.onEnd = function (status) { + // On success - join + if (status === c.Z_OK) { + if (this.options.to === 'string') { + // Glue & convert here, until we teach pako to send + // utf8 alligned strings to onData + this.result = this.chunks.join(''); + } else { + this.result = utils.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + }; + + + /** + * inflate(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * Decompress `data` with inflate/ungzip and `options`. Autodetect + * format via wrapper header by default. That's why we don't provide + * separate `ungzip` method. + * + * Supported options are: + * + * - windowBits + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , input = pako.deflate([1,2,3,4,5,6,7,8,9]) + * , output; + * + * try { + * output = pako.inflate(input); + * } catch (err) + * console.log(err); + * } + * ``` + **/ + function inflate(input, options) { + var inflator = new Inflate(options); + + inflator.push(input, true); + + // That will never happens, if you don't cheat with options :) + if (inflator.err) { throw inflator.msg || msg[inflator.err]; } + + return inflator.result; + } + + + /** + * inflateRaw(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * The same as [[inflate]], but creates raw data, without wrapper + * (header and adler32 crc). + **/ + function inflateRaw(input, options) { + options = options || {}; + options.raw = true; + return inflate(input, options); + } + + + /** + * ungzip(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * Just shortcut to [[inflate]], because it autodetects format + * by header.content. Done for convenience. + **/ + + + exports.Inflate = Inflate; + exports.inflate = inflate; + exports.inflateRaw = inflateRaw; + exports.ungzip = inflate; + +},{"./utils/common":1,"./utils/strings":2,"./zlib/constants":4,"./zlib/gzheader":6,"./zlib/inflate":8,"./zlib/messages":10,"./zlib/zstream":11}]},{},[])("/lib/inflate.js") +}); diff --git a/Source/ThirdParty/protobuf-minimal.js b/Source/ThirdParty/protobuf-minimal.js new file mode 100644 index 000000000000..7719d7e6c2bb --- /dev/null +++ b/Source/ThirdParty/protobuf-minimal.js @@ -0,0 +1,2465 @@ +/*! + * protobuf.js v6.7.0 (c) 2016, Daniel Wirtz + * Compiled Wed, 22 Mar 2017 17:30:26 UTC + * Licensed under the BSD-3-Clause License + * see: https://github.com/dcodeIO/protobuf.js for details + */ +(function(global,undefined){"use strict";(function prelude(modules, cache, entries) { + + // This is the prelude used to bundle protobuf.js for the browser. Wraps up the CommonJS + // sources through a conflict-free require shim and is again wrapped within an iife that + // provides a unified `global` and a minification-friendly `undefined` var plus a global + // "use strict" directive so that minification can remove the directives of each module. + + function $require(name) { + var $module = cache[name]; + if (!$module) + modules[name][0].call($module = cache[name] = { exports: {} }, $require, $module, $module.exports); + return $module.exports; + } + + // Expose globally + var protobuf = global.protobuf = $require(entries[0]); + + // Be nice to AMD + if (typeof define === "function" && define.amd) + define([], function() { + protobuf.configure(); + return protobuf; + }); + + // Be nice to CommonJS + if (typeof module === "object" && module && module.exports) + module.exports = protobuf; + +})/* end of prelude */({1:[function(require,module,exports){ +"use strict"; +module.exports = asPromise; + +/** + * Returns a promise from a node-style callback function. + * @memberof util + * @param {function(?Error, ...*)} fn Function to call + * @param {*} ctx Function context + * @param {...*} params Function arguments + * @returns {Promise<*>} Promisified function + */ +function asPromise(fn, ctx/*, varargs */) { + var params = []; + for (var i = 2; i < arguments.length;) + params.push(arguments[i++]); + var pending = true; + return new Promise(function asPromiseExecutor(resolve, reject) { + params.push(function asPromiseCallback(err/*, varargs */) { + if (pending) { + pending = false; + if (err) + reject(err); + else { + var args = []; + for (var i = 1; i < arguments.length;) + args.push(arguments[i++]); + resolve.apply(null, args); + } + } + }); + try { + fn.apply(ctx || this, params); // eslint-disable-line no-invalid-this + } catch (err) { + if (pending) { + pending = false; + reject(err); + } + } + }); +} + +},{}],2:[function(require,module,exports){ +"use strict"; + +/** + * A minimal base64 implementation for number arrays. + * @memberof util + * @namespace + */ +var base64 = exports; + +/** + * Calculates the byte length of a base64 encoded string. + * @param {string} string Base64 encoded string + * @returns {number} Byte length + */ +base64.length = function length(string) { + var p = string.length; + if (!p) + return 0; + var n = 0; + while (--p % 4 > 1 && string.charAt(p) === "=") + ++n; + return Math.ceil(string.length * 3) / 4 - n; +}; + +// Base64 encoding table +var b64 = new Array(64); + +// Base64 decoding table +var s64 = new Array(123); + +// 65..90, 97..122, 48..57, 43, 47 +for (var i = 0; i < 64;) + s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; + +/** + * Encodes a buffer to a base64 encoded string. + * @param {Uint8Array} buffer Source buffer + * @param {number} start Source start + * @param {number} end Source end + * @returns {string} Base64 encoded string + */ +base64.encode = function encode(buffer, start, end) { + var string = []; // alt: new Array(Math.ceil((end - start) / 3) * 4); + var i = 0, // output index + j = 0, // goto index + t; // temporary + while (start < end) { + var b = buffer[start++]; + switch (j) { + case 0: + string[i++] = b64[b >> 2]; + t = (b & 3) << 4; + j = 1; + break; + case 1: + string[i++] = b64[t | b >> 4]; + t = (b & 15) << 2; + j = 2; + break; + case 2: + string[i++] = b64[t | b >> 6]; + string[i++] = b64[b & 63]; + j = 0; + break; + } + } + if (j) { + string[i++] = b64[t]; + string[i ] = 61; + if (j === 1) + string[i + 1] = 61; + } + return String.fromCharCode.apply(String, string); +}; + +var invalidEncoding = "invalid encoding"; + +/** + * Decodes a base64 encoded string to a buffer. + * @param {string} string Source string + * @param {Uint8Array} buffer Destination buffer + * @param {number} offset Destination offset + * @returns {number} Number of bytes written + * @throws {Error} If encoding is invalid + */ +base64.decode = function decode(string, buffer, offset) { + var start = offset; + var j = 0, // goto index + t; // temporary + for (var i = 0; i < string.length;) { + var c = string.charCodeAt(i++); + if (c === 61 && j > 1) + break; + if ((c = s64[c]) === undefined) + throw Error(invalidEncoding); + switch (j) { + case 0: + t = c; + j = 1; + break; + case 1: + buffer[offset++] = t << 2 | (c & 48) >> 4; + t = c; + j = 2; + break; + case 2: + buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2; + t = c; + j = 3; + break; + case 3: + buffer[offset++] = (t & 3) << 6 | c; + j = 0; + break; + } + } + if (j === 1) + throw Error(invalidEncoding); + return offset - start; +}; + +/** + * Tests if the specified string appears to be base64 encoded. + * @param {string} string String to test + * @returns {boolean} `true` if probably base64 encoded, otherwise false + */ +base64.test = function test(string) { + return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string); +}; + +},{}],3:[function(require,module,exports){ +"use strict"; +module.exports = EventEmitter; + +/** + * Constructs a new event emitter instance. + * @classdesc A minimal event emitter. + * @memberof util + * @constructor + */ +function EventEmitter() { + + /** + * Registered listeners. + * @type {Object.} + * @private + */ + this._listeners = {}; +} + +/** + * Registers an event listener. + * @param {string} evt Event name + * @param {function} fn Listener + * @param {*} [ctx] Listener context + * @returns {util.EventEmitter} `this` + */ +EventEmitter.prototype.on = function on(evt, fn, ctx) { + (this._listeners[evt] || (this._listeners[evt] = [])).push({ + fn : fn, + ctx : ctx || this + }); + return this; +}; + +/** + * Removes an event listener or any matching listeners if arguments are omitted. + * @param {string} [evt] Event name. Removes all listeners if omitted. + * @param {function} [fn] Listener to remove. Removes all listeners of `evt` if omitted. + * @returns {util.EventEmitter} `this` + */ +EventEmitter.prototype.off = function off(evt, fn) { + if (evt === undefined) + this._listeners = {}; + else { + if (fn === undefined) + this._listeners[evt] = []; + else { + var listeners = this._listeners[evt]; + for (var i = 0; i < listeners.length;) + if (listeners[i].fn === fn) + listeners.splice(i, 1); + else + ++i; + } + } + return this; +}; + +/** + * Emits an event by calling its listeners with the specified arguments. + * @param {string} evt Event name + * @param {...*} args Arguments + * @returns {util.EventEmitter} `this` + */ +EventEmitter.prototype.emit = function emit(evt) { + var listeners = this._listeners[evt]; + if (listeners) { + var args = [], + i = 1; + for (; i < arguments.length;) + args.push(arguments[i++]); + for (i = 0; i < listeners.length;) + listeners[i].fn.apply(listeners[i++].ctx, args); + } + return this; +}; + +},{}],4:[function(require,module,exports){ +"use strict"; +module.exports = inquire; + +/** + * Requires a module only if available. + * @memberof util + * @param {string} moduleName Module to require + * @returns {?Object} Required module if available and not empty, otherwise `null` + */ +function inquire(moduleName) { + try { + var mod = eval("quire".replace(/^/,"re"))(moduleName); // eslint-disable-line no-eval + if (mod && (mod.length || Object.keys(mod).length)) + return mod; + } catch (e) {} // eslint-disable-line no-empty + return null; +} + +},{}],5:[function(require,module,exports){ +"use strict"; +module.exports = pool; + +/** + * An allocator as used by {@link util.pool}. + * @typedef PoolAllocator + * @type {function} + * @param {number} size Buffer size + * @returns {Uint8Array} Buffer + */ + +/** + * A slicer as used by {@link util.pool}. + * @typedef PoolSlicer + * @type {function} + * @param {number} start Start offset + * @param {number} end End offset + * @returns {Uint8Array} Buffer slice + * @this {Uint8Array} + */ + +/** + * A general purpose buffer pool. + * @memberof util + * @function + * @param {PoolAllocator} alloc Allocator + * @param {PoolSlicer} slice Slicer + * @param {number} [size=8192] Slab size + * @returns {PoolAllocator} Pooled allocator + */ +function pool(alloc, slice, size) { + var SIZE = size || 8192; + var MAX = SIZE >>> 1; + var slab = null; + var offset = SIZE; + return function pool_alloc(size) { + if (size < 1 || size > MAX) + return alloc(size); + if (offset + size > SIZE) { + slab = alloc(SIZE); + offset = 0; + } + var buf = slice.call(slab, offset, offset += size); + if (offset & 7) // align to 32 bit + offset = (offset | 7) + 1; + return buf; + }; +} + +},{}],6:[function(require,module,exports){ +"use strict"; + +/** + * A minimal UTF8 implementation for number arrays. + * @memberof util + * @namespace + */ +var utf8 = exports; + +/** + * Calculates the UTF8 byte length of a string. + * @param {string} string String + * @returns {number} Byte length + */ +utf8.length = function utf8_length(string) { + var len = 0, + c = 0; + for (var i = 0; i < string.length; ++i) { + c = string.charCodeAt(i); + if (c < 128) + len += 1; + else if (c < 2048) + len += 2; + else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) { + ++i; + len += 4; + } else + len += 3; + } + return len; +}; + +/** + * Reads UTF8 bytes as a string. + * @param {Uint8Array} buffer Source buffer + * @param {number} start Source start + * @param {number} end Source end + * @returns {string} String read + */ +utf8.read = function utf8_read(buffer, start, end) { + var len = end - start; + if (len < 1) + return ""; + var parts = null, + chunk = [], + i = 0, // char offset + t; // temporary + while (start < end) { + t = buffer[start++]; + if (t < 128) + chunk[i++] = t; + else if (t > 191 && t < 224) + chunk[i++] = (t & 31) << 6 | buffer[start++] & 63; + else if (t > 239 && t < 365) { + t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000; + chunk[i++] = 0xD800 + (t >> 10); + chunk[i++] = 0xDC00 + (t & 1023); + } else + chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63; + if (i > 8191) { + (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); + i = 0; + } + } + if (parts) { + if (i) + parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); + return parts.join(""); + } + return String.fromCharCode.apply(String, chunk.slice(0, i)); +}; + +/** + * Writes a string as UTF8 bytes. + * @param {string} string Source string + * @param {Uint8Array} buffer Destination buffer + * @param {number} offset Destination offset + * @returns {number} Bytes written + */ +utf8.write = function utf8_write(string, buffer, offset) { + var start = offset, + c1, // character 1 + c2; // character 2 + for (var i = 0; i < string.length; ++i) { + c1 = string.charCodeAt(i); + if (c1 < 128) { + buffer[offset++] = c1; + } else if (c1 < 2048) { + buffer[offset++] = c1 >> 6 | 192; + buffer[offset++] = c1 & 63 | 128; + } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) { + c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF); + ++i; + buffer[offset++] = c1 >> 18 | 240; + buffer[offset++] = c1 >> 12 & 63 | 128; + buffer[offset++] = c1 >> 6 & 63 | 128; + buffer[offset++] = c1 & 63 | 128; + } else { + buffer[offset++] = c1 >> 12 | 224; + buffer[offset++] = c1 >> 6 & 63 | 128; + buffer[offset++] = c1 & 63 | 128; + } + } + return offset - start; +}; + +},{}],7:[function(require,module,exports){ +"use strict"; +var protobuf = exports; + +/** + * Build type, one of `"full"`, `"light"` or `"minimal"`. + * @name build + * @type {string} + * @const + */ +protobuf.build = "minimal"; + +/** + * Named roots. + * This is where pbjs stores generated structures (the option `-r, --root` specifies a name). + * Can also be used manually to make roots available accross modules. + * @name roots + * @type {Object.} + * @example + * // pbjs -r myroot -o compiled.js ... + * + * // in another module: + * require("./compiled.js"); + * + * // in any subsequent module: + * var root = protobuf.roots["myroot"]; + */ +protobuf.roots = {}; + +// Serialization +protobuf.Writer = require(14); +protobuf.BufferWriter = require(15); +protobuf.Reader = require(8); +protobuf.BufferReader = require(9); + +// Utility +protobuf.util = require(13); +protobuf.rpc = require(10); +protobuf.configure = configure; + +/* istanbul ignore next */ +/** + * Reconfigures the library according to the environment. + * @returns {undefined} + */ +function configure() { + protobuf.Reader._configure(protobuf.BufferReader); + protobuf.util._configure(); +} + +// Configure serialization +protobuf.Writer._configure(protobuf.BufferWriter); +configure(); + +},{"10":10,"13":13,"14":14,"15":15,"8":8,"9":9}],8:[function(require,module,exports){ +"use strict"; +module.exports = Reader; + +var util = require(13); + +var BufferReader; // cyclic + +var LongBits = util.LongBits, + utf8 = util.utf8; + +/* istanbul ignore next */ +function indexOutOfRange(reader, writeLength) { + return RangeError("index out of range: " + reader.pos + " + " + (writeLength || 1) + " > " + reader.len); +} + +/** + * Constructs a new reader instance using the specified buffer. + * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`. + * @constructor + * @param {Uint8Array} buffer Buffer to read from + */ +function Reader(buffer) { + + /** + * Read buffer. + * @type {Uint8Array} + */ + this.buf = buffer; + + /** + * Read buffer position. + * @type {number} + */ + this.pos = 0; + + /** + * Read buffer length. + * @type {number} + */ + this.len = buffer.length; +} + +var create_array = typeof Uint8Array !== "undefined" + ? function create_typed_array(buffer) { + if (buffer instanceof Uint8Array || Array.isArray(buffer)) + return new Reader(buffer); + throw Error("illegal buffer"); + } + /* istanbul ignore next */ + : function create_array(buffer) { + if (Array.isArray(buffer)) + return new Reader(buffer); + throw Error("illegal buffer"); + }; + +/** + * Creates a new reader using the specified buffer. + * @function + * @param {Uint8Array|Buffer} buffer Buffer to read from + * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader} + * @throws {Error} If `buffer` is not a valid buffer + */ +Reader.create = util.Buffer + ? function create_buffer_setup(buffer) { + return (Reader.create = function create_buffer(buffer) { + return util.Buffer.isBuffer(buffer) + ? new BufferReader(buffer) + /* istanbul ignore next */ + : create_array(buffer); + })(buffer); + } + /* istanbul ignore next */ + : create_array; + +Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice; + +/** + * Reads a varint as an unsigned 32 bit value. + * @function + * @returns {number} Value read + */ +Reader.prototype.uint32 = (function read_uint32_setup() { + var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!) + return function read_uint32() { + value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value; + value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value; + value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value; + value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value; + value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value; + + /* istanbul ignore next */ + if ((this.pos += 5) > this.len) { + this.pos = this.len; + throw indexOutOfRange(this, 10); + } + return value; + }; +})(); + +/** + * Reads a varint as a signed 32 bit value. + * @returns {number} Value read + */ +Reader.prototype.int32 = function read_int32() { + return this.uint32() | 0; +}; + +/** + * Reads a zig-zag encoded varint as a signed 32 bit value. + * @returns {number} Value read + */ +Reader.prototype.sint32 = function read_sint32() { + var value = this.uint32(); + return value >>> 1 ^ -(value & 1) | 0; +}; + +/* eslint-disable no-invalid-this */ + +function readLongVarint() { + // tends to deopt with local vars for octet etc. + var bits = new LongBits(0, 0); + var i = 0; + if (this.len - this.pos > 4) { // fast route (lo) + for (; i < 4; ++i) { + // 1st..4th + bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; + if (this.buf[this.pos++] < 128) + return bits; + } + // 5th + bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0; + bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0; + if (this.buf[this.pos++] < 128) + return bits; + i = 0; + } else { + for (; i < 3; ++i) { + /* istanbul ignore next */ + if (this.pos >= this.len) + throw indexOutOfRange(this); + // 1st..3th + bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; + if (this.buf[this.pos++] < 128) + return bits; + } + // 4th + bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0; + return bits; + } + if (this.len - this.pos > 4) { // fast route (hi) + for (; i < 5; ++i) { + // 6th..10th + bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; + if (this.buf[this.pos++] < 128) + return bits; + } + } else { + for (; i < 5; ++i) { + /* istanbul ignore next */ + if (this.pos >= this.len) + throw indexOutOfRange(this); + // 6th..10th + bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; + if (this.buf[this.pos++] < 128) + return bits; + } + } + /* istanbul ignore next */ + throw Error("invalid varint encoding"); +} + +/* eslint-enable no-invalid-this */ + +/** + * Reads a varint as a signed 64 bit value. + * @name Reader#int64 + * @function + * @returns {Long|number} Value read + */ + +/** + * Reads a varint as an unsigned 64 bit value. + * @name Reader#uint64 + * @function + * @returns {Long|number} Value read + */ + +/** + * Reads a zig-zag encoded varint as a signed 64 bit value. + * @name Reader#sint64 + * @function + * @returns {Long|number} Value read + */ + +/** + * Reads a varint as a boolean. + * @returns {boolean} Value read + */ +Reader.prototype.bool = function read_bool() { + return this.uint32() !== 0; +}; + +function readFixed32(buf, end) { + return (buf[end - 4] + | buf[end - 3] << 8 + | buf[end - 2] << 16 + | buf[end - 1] << 24) >>> 0; +} + +/** + * Reads fixed 32 bits as an unsigned 32 bit integer. + * @returns {number} Value read + */ +Reader.prototype.fixed32 = function read_fixed32() { + + /* istanbul ignore next */ + if (this.pos + 4 > this.len) + throw indexOutOfRange(this, 4); + + return readFixed32(this.buf, this.pos += 4); +}; + +/** + * Reads fixed 32 bits as a signed 32 bit integer. + * @returns {number} Value read + */ +Reader.prototype.sfixed32 = function read_sfixed32() { + + /* istanbul ignore next */ + if (this.pos + 4 > this.len) + throw indexOutOfRange(this, 4); + + return readFixed32(this.buf, this.pos += 4) | 0; +}; + +/* eslint-disable no-invalid-this */ + +function readFixed64(/* this: Reader */) { + + /* istanbul ignore next */ + if (this.pos + 8 > this.len) + throw indexOutOfRange(this, 8); + + return new LongBits(readFixed32(this.buf, this.pos += 4), readFixed32(this.buf, this.pos += 4)); +} + +/* eslint-enable no-invalid-this */ + +/** + * Reads fixed 64 bits. + * @name Reader#fixed64 + * @function + * @returns {Long|number} Value read + */ + +/** + * Reads zig-zag encoded fixed 64 bits. + * @name Reader#sfixed64 + * @function + * @returns {Long|number} Value read + */ + +var readFloat = typeof Float32Array !== "undefined" + ? (function() { + var f32 = new Float32Array(1), + f8b = new Uint8Array(f32.buffer); + f32[0] = -0; + return f8b[3] // already le? + ? function readFloat_f32(buf, pos) { + f8b[0] = buf[pos ]; + f8b[1] = buf[pos + 1]; + f8b[2] = buf[pos + 2]; + f8b[3] = buf[pos + 3]; + return f32[0]; + } + /* istanbul ignore next */ + : function readFloat_f32_le(buf, pos) { + f8b[0] = buf[pos + 3]; + f8b[1] = buf[pos + 2]; + f8b[2] = buf[pos + 1]; + f8b[3] = buf[pos ]; + return f32[0]; + }; + })() + /* istanbul ignore next */ + : function readFloat_ieee754(buf, pos) { + var uint = readFixed32(buf, pos + 4), + sign = (uint >> 31) * 2 + 1, + exponent = uint >>> 23 & 255, + mantissa = uint & 8388607; + return exponent === 255 + ? mantissa + ? NaN + : sign * Infinity + : exponent === 0 // denormal + ? sign * 1.401298464324817e-45 * mantissa + : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608); + }; + +/** + * Reads a float (32 bit) as a number. + * @function + * @returns {number} Value read + */ +Reader.prototype.float = function read_float() { + + /* istanbul ignore next */ + if (this.pos + 4 > this.len) + throw indexOutOfRange(this, 4); + + var value = readFloat(this.buf, this.pos); + this.pos += 4; + return value; +}; + +var readDouble = typeof Float64Array !== "undefined" + ? (function() { + var f64 = new Float64Array(1), + f8b = new Uint8Array(f64.buffer); + f64[0] = -0; + return f8b[7] // already le? + ? function readDouble_f64(buf, pos) { + f8b[0] = buf[pos ]; + f8b[1] = buf[pos + 1]; + f8b[2] = buf[pos + 2]; + f8b[3] = buf[pos + 3]; + f8b[4] = buf[pos + 4]; + f8b[5] = buf[pos + 5]; + f8b[6] = buf[pos + 6]; + f8b[7] = buf[pos + 7]; + return f64[0]; + } + /* istanbul ignore next */ + : function readDouble_f64_le(buf, pos) { + f8b[0] = buf[pos + 7]; + f8b[1] = buf[pos + 6]; + f8b[2] = buf[pos + 5]; + f8b[3] = buf[pos + 4]; + f8b[4] = buf[pos + 3]; + f8b[5] = buf[pos + 2]; + f8b[6] = buf[pos + 1]; + f8b[7] = buf[pos ]; + return f64[0]; + }; + })() + /* istanbul ignore next */ + : function readDouble_ieee754(buf, pos) { + var lo = readFixed32(buf, pos + 4), + hi = readFixed32(buf, pos + 8); + var sign = (hi >> 31) * 2 + 1, + exponent = hi >>> 20 & 2047, + mantissa = 4294967296 * (hi & 1048575) + lo; + return exponent === 2047 + ? mantissa + ? NaN + : sign * Infinity + : exponent === 0 // denormal + ? sign * 5e-324 * mantissa + : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496); + }; + +/** + * Reads a double (64 bit float) as a number. + * @function + * @returns {number} Value read + */ +Reader.prototype.double = function read_double() { + + /* istanbul ignore next */ + if (this.pos + 8 > this.len) + throw indexOutOfRange(this, 4); + + var value = readDouble(this.buf, this.pos); + this.pos += 8; + return value; +}; + +/** + * Reads a sequence of bytes preceeded by its length as a varint. + * @returns {Uint8Array} Value read + */ +Reader.prototype.bytes = function read_bytes() { + var length = this.uint32(), + start = this.pos, + end = this.pos + length; + + /* istanbul ignore next */ + if (end > this.len) + throw indexOutOfRange(this, length); + + this.pos += length; + return start === end // fix for IE 10/Win8 and others' subarray returning array of size 1 + ? new this.buf.constructor(0) + : this._slice.call(this.buf, start, end); +}; + +/** + * Reads a string preceeded by its byte length as a varint. + * @returns {string} Value read + */ +Reader.prototype.string = function read_string() { + var bytes = this.bytes(); + return utf8.read(bytes, 0, bytes.length); +}; + +/** + * Skips the specified number of bytes if specified, otherwise skips a varint. + * @param {number} [length] Length if known, otherwise a varint is assumed + * @returns {Reader} `this` + */ +Reader.prototype.skip = function skip(length) { + if (typeof length === "number") { + /* istanbul ignore next */ + if (this.pos + length > this.len) + throw indexOutOfRange(this, length); + this.pos += length; + } else { + /* istanbul ignore next */ + do { + if (this.pos >= this.len) + throw indexOutOfRange(this); + } while (this.buf[this.pos++] & 128); + } + return this; +}; + +/** + * Skips the next element of the specified wire type. + * @param {number} wireType Wire type received + * @returns {Reader} `this` + */ +Reader.prototype.skipType = function(wireType) { + switch (wireType) { + case 0: + this.skip(); + break; + case 1: + this.skip(8); + break; + case 2: + this.skip(this.uint32()); + break; + case 3: + do { // eslint-disable-line no-constant-condition + if ((wireType = this.uint32() & 7) === 4) + break; + this.skipType(wireType); + } while (true); + break; + case 5: + this.skip(4); + break; + + /* istanbul ignore next */ + default: + throw Error("invalid wire type " + wireType + " at offset " + this.pos); + } + return this; +}; + +Reader._configure = function(BufferReader_) { + BufferReader = BufferReader_; + + var fn = util.Long ? "toLong" : /* istanbul ignore next */ "toNumber"; + util.merge(Reader.prototype, { + + int64: function read_int64() { + return readLongVarint.call(this)[fn](false); + }, + + uint64: function read_uint64() { + return readLongVarint.call(this)[fn](true); + }, + + sint64: function read_sint64() { + return readLongVarint.call(this).zzDecode()[fn](false); + }, + + fixed64: function read_fixed64() { + return readFixed64.call(this)[fn](true); + }, + + sfixed64: function read_sfixed64() { + return readFixed64.call(this)[fn](false); + } + + }); +}; + +},{"13":13}],9:[function(require,module,exports){ +"use strict"; +module.exports = BufferReader; + +// extends Reader +var Reader = require(8); +(BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader; + +var util = require(13); + +/** + * Constructs a new buffer reader instance. + * @classdesc Wire format reader using node buffers. + * @extends Reader + * @constructor + * @param {Buffer} buffer Buffer to read from + */ +function BufferReader(buffer) { + Reader.call(this, buffer); + + /** + * Read buffer. + * @name BufferReader#buf + * @type {Buffer} + */ +} + +/* istanbul ignore else */ +if (util.Buffer) + BufferReader.prototype._slice = util.Buffer.prototype.slice; + +/** + * @override + */ +BufferReader.prototype.string = function read_string_buffer() { + var len = this.uint32(); // modifies pos + return this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len)); +}; + +/** + * Reads a sequence of bytes preceeded by its length as a varint. + * @name BufferReader#bytes + * @function + * @returns {Buffer} Value read + */ + +},{"13":13,"8":8}],10:[function(require,module,exports){ +"use strict"; + +/** + * Streaming RPC helpers. + * @namespace + */ +var rpc = exports; + +/** + * RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets. + * @typedef RPCImpl + * @type {function} + * @param {Method|rpc.ServiceMethod} method Reflected or static method being called + * @param {Uint8Array} requestData Request data + * @param {RPCImplCallback} callback Callback function + * @returns {undefined} + * @example + * function rpcImpl(method, requestData, callback) { + * if (protobuf.util.lcFirst(method.name) !== "myMethod") // compatible with static code + * throw Error("no such method"); + * asynchronouslyObtainAResponse(requestData, function(err, responseData) { + * callback(err, responseData); + * }); + * } + */ + +/** + * Node-style callback as used by {@link RPCImpl}. + * @typedef RPCImplCallback + * @type {function} + * @param {?Error} error Error, if any, otherwise `null` + * @param {?Uint8Array} [response] Response data or `null` to signal end of stream, if there hasn't been an error + * @returns {undefined} + */ + +rpc.Service = require(11); + +},{"11":11}],11:[function(require,module,exports){ +"use strict"; +module.exports = Service; + +var util = require(13); + +// Extends EventEmitter +(Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service; + +/** + * A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}. + * + * Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`. + * @typedef rpc.ServiceMethodCallback + * @type {function} + * @param {?Error} error Error, if any + * @param {?Message} [response] Response message + * @returns {undefined} + */ + +/** + * A service method part of a {@link rpc.ServiceMethodMixin|ServiceMethodMixin} and thus {@link rpc.Service} as created by {@link Service.create}. + * @typedef rpc.ServiceMethod + * @type {function} + * @param {Message|Object.} request Request message or plain object + * @param {rpc.ServiceMethodCallback} [callback] Node-style callback called with the error, if any, and the response message + * @returns {Promise} Promise if `callback` has been omitted, otherwise `undefined` + */ + +/** + * A service method mixin. + * + * When using TypeScript, mixed in service methods are only supported directly with a type definition of a static module (used with reflection). Otherwise, explicit casting is required. + * @typedef rpc.ServiceMethodMixin + * @type {Object.} + * @example + * // Explicit casting with TypeScript + * (myRpcService["myMethod"] as protobuf.rpc.ServiceMethod)(...) + */ + +/** + * Constructs a new RPC service instance. + * @classdesc An RPC service as returned by {@link Service#create}. + * @exports rpc.Service + * @extends util.EventEmitter + * @augments rpc.ServiceMethodMixin + * @constructor + * @param {RPCImpl} rpcImpl RPC implementation + * @param {boolean} [requestDelimited=false] Whether requests are length-delimited + * @param {boolean} [responseDelimited=false] Whether responses are length-delimited + */ +function Service(rpcImpl, requestDelimited, responseDelimited) { + + if (typeof rpcImpl !== "function") + throw TypeError("rpcImpl must be a function"); + + util.EventEmitter.call(this); + + /** + * RPC implementation. Becomes `null` once the service is ended. + * @type {?RPCImpl} + */ + this.rpcImpl = rpcImpl; + + /** + * Whether requests are length-delimited. + * @type {boolean} + */ + this.requestDelimited = Boolean(requestDelimited); + + /** + * Whether responses are length-delimited. + * @type {boolean} + */ + this.responseDelimited = Boolean(responseDelimited); +} + +/** + * Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}. + * @param {Method|rpc.ServiceMethod} method Reflected or static method + * @param {function} requestCtor Request constructor + * @param {function} responseCtor Response constructor + * @param {Message|Object.} request Request message or plain object + * @param {rpc.ServiceMethodCallback} callback Service callback + * @returns {undefined} + */ +Service.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) { + + if (!request) + throw TypeError("request must be specified"); + + var self = this; + if (!callback) + return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request); + + if (!self.rpcImpl) { + setTimeout(function() { callback(Error("already ended")); }, 0); + return undefined; + } + + try { + return self.rpcImpl( + method, + requestCtor[self.requestDelimited ? "encodeDelimited" : "encode"](request).finish(), + function rpcCallback(err, response) { + + if (err) { + self.emit("error", err, method); + return callback(err); + } + + if (response === null) { + self.end(/* endedByRPC */ true); + return undefined; + } + + if (!(response instanceof responseCtor)) { + try { + response = responseCtor[self.responseDelimited ? "decodeDelimited" : "decode"](response); + } catch (err) { + self.emit("error", err, method); + return callback(err); + } + } + + self.emit("data", response, method); + return callback(null, response); + } + ); + } catch (err) { + self.emit("error", err, method); + setTimeout(function() { callback(err); }, 0); + return undefined; + } +}; + +/** + * Ends this service and emits the `end` event. + * @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation. + * @returns {rpc.Service} `this` + */ +Service.prototype.end = function end(endedByRPC) { + if (this.rpcImpl) { + if (!endedByRPC) // signal end to rpcImpl + this.rpcImpl(null, null, null); + this.rpcImpl = null; + this.emit("end").off(); + } + return this; +}; + +},{"13":13}],12:[function(require,module,exports){ +"use strict"; +module.exports = LongBits; + +var util = require(13); + +/** + * Any compatible Long instance. + * + * This is a minimal stand-alone definition of a Long instance. The actual type is that exported by long.js. + * @typedef Long + * @type {Object} + * @property {number} low Low bits + * @property {number} high High bits + * @property {boolean} unsigned Whether unsigned or not + */ + +/** + * Constructs new long bits. + * @classdesc Helper class for working with the low and high bits of a 64 bit value. + * @memberof util + * @constructor + * @param {number} lo Low 32 bits, unsigned + * @param {number} hi High 32 bits, unsigned + */ +function LongBits(lo, hi) { + + // note that the casts below are theoretically unnecessary as of today, but older statically + // generated converter code might still call the ctor with signed 32bits. kept for compat. + + /** + * Low bits. + * @type {number} + */ + this.lo = lo >>> 0; + + /** + * High bits. + * @type {number} + */ + this.hi = hi >>> 0; +} + +/** + * Zero bits. + * @memberof util.LongBits + * @type {util.LongBits} + */ +var zero = LongBits.zero = new LongBits(0, 0); + +zero.toNumber = function() { return 0; }; +zero.zzEncode = zero.zzDecode = function() { return this; }; +zero.length = function() { return 1; }; + +/** + * Zero hash. + * @memberof util.LongBits + * @type {string} + */ +var zeroHash = LongBits.zeroHash = "\0\0\0\0\0\0\0\0"; + +/** + * Constructs new long bits from the specified number. + * @param {number} value Value + * @returns {util.LongBits} Instance + */ +LongBits.fromNumber = function fromNumber(value) { + if (value === 0) + return zero; + var sign = value < 0; + if (sign) + value = -value; + var lo = value >>> 0, + hi = (value - lo) / 4294967296 >>> 0; + if (sign) { + hi = ~hi >>> 0; + lo = ~lo >>> 0; + if (++lo > 4294967295) { + lo = 0; + if (++hi > 4294967295) + hi = 0; + } + } + return new LongBits(lo, hi); +}; + +/** + * Constructs new long bits from a number, long or string. + * @param {Long|number|string} value Value + * @returns {util.LongBits} Instance + */ +LongBits.from = function from(value) { + if (typeof value === "number") + return LongBits.fromNumber(value); + if (util.isString(value)) { + /* istanbul ignore else */ + if (util.Long) + value = util.Long.fromString(value); + else + return LongBits.fromNumber(parseInt(value, 10)); + } + return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero; +}; + +/** + * Converts this long bits to a possibly unsafe JavaScript number. + * @param {boolean} [unsigned=false] Whether unsigned or not + * @returns {number} Possibly unsafe number + */ +LongBits.prototype.toNumber = function toNumber(unsigned) { + if (!unsigned && this.hi >>> 31) { + var lo = ~this.lo + 1 >>> 0, + hi = ~this.hi >>> 0; + if (!lo) + hi = hi + 1 >>> 0; + return -(lo + hi * 4294967296); + } + return this.lo + this.hi * 4294967296; +}; + +/** + * Converts this long bits to a long. + * @param {boolean} [unsigned=false] Whether unsigned or not + * @returns {Long} Long + */ +LongBits.prototype.toLong = function toLong(unsigned) { + return util.Long + ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned)) + /* istanbul ignore next */ + : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) }; +}; + +var charCodeAt = String.prototype.charCodeAt; + +/** + * Constructs new long bits from the specified 8 characters long hash. + * @param {string} hash Hash + * @returns {util.LongBits} Bits + */ +LongBits.fromHash = function fromHash(hash) { + if (hash === zeroHash) + return zero; + return new LongBits( + ( charCodeAt.call(hash, 0) + | charCodeAt.call(hash, 1) << 8 + | charCodeAt.call(hash, 2) << 16 + | charCodeAt.call(hash, 3) << 24) >>> 0 + , + ( charCodeAt.call(hash, 4) + | charCodeAt.call(hash, 5) << 8 + | charCodeAt.call(hash, 6) << 16 + | charCodeAt.call(hash, 7) << 24) >>> 0 + ); +}; + +/** + * Converts this long bits to a 8 characters long hash. + * @returns {string} Hash + */ +LongBits.prototype.toHash = function toHash() { + return String.fromCharCode( + this.lo & 255, + this.lo >>> 8 & 255, + this.lo >>> 16 & 255, + this.lo >>> 24 , + this.hi & 255, + this.hi >>> 8 & 255, + this.hi >>> 16 & 255, + this.hi >>> 24 + ); +}; + +/** + * Zig-zag encodes this long bits. + * @returns {util.LongBits} `this` + */ +LongBits.prototype.zzEncode = function zzEncode() { + var mask = this.hi >> 31; + this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0; + this.lo = ( this.lo << 1 ^ mask) >>> 0; + return this; +}; + +/** + * Zig-zag decodes this long bits. + * @returns {util.LongBits} `this` + */ +LongBits.prototype.zzDecode = function zzDecode() { + var mask = -(this.lo & 1); + this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0; + this.hi = ( this.hi >>> 1 ^ mask) >>> 0; + return this; +}; + +/** + * Calculates the length of this longbits when encoded as a varint. + * @returns {number} Length + */ +LongBits.prototype.length = function length() { + var part0 = this.lo, + part1 = (this.lo >>> 28 | this.hi << 4) >>> 0, + part2 = this.hi >>> 24; + return part2 === 0 + ? part1 === 0 + ? part0 < 16384 + ? part0 < 128 ? 1 : 2 + : part0 < 2097152 ? 3 : 4 + : part1 < 16384 + ? part1 < 128 ? 5 : 6 + : part1 < 2097152 ? 7 : 8 + : part2 < 128 ? 9 : 10; +}; + +},{"13":13}],13:[function(require,module,exports){ +"use strict"; +var util = exports; + +// used to return a Promise where callback is omitted +util.asPromise = require(1); + +// converts to / from base64 encoded strings +util.base64 = require(2); + +// base class of rpc.Service +util.EventEmitter = require(3); + +// requires modules optionally and hides the call from bundlers +util.inquire = require(4); + +// converts to / from utf8 encoded strings +util.utf8 = require(6); + +// provides a node-like buffer pool in the browser +util.pool = require(5); + +// utility to work with the low and high bits of a 64 bit value +util.LongBits = require(12); + +/** + * An immuable empty array. + * @memberof util + * @type {Array.<*>} + */ +util.emptyArray = Object.freeze ? Object.freeze([]) : /* istanbul ignore next */ []; // used on prototypes + +/** + * An immutable empty object. + * @type {Object} + */ +util.emptyObject = Object.freeze ? Object.freeze({}) : /* istanbul ignore next */ {}; // used on prototypes + +/** + * Whether running within node or not. + * @memberof util + * @type {boolean} + */ +util.isNode = Boolean(global.process && global.process.versions && global.process.versions.node); + +/** + * Tests if the specified value is an integer. + * @function + * @param {*} value Value to test + * @returns {boolean} `true` if the value is an integer + */ +util.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) { + return typeof value === "number" && isFinite(value) && Math.floor(value) === value; +}; + +/** + * Tests if the specified value is a string. + * @param {*} value Value to test + * @returns {boolean} `true` if the value is a string + */ +util.isString = function isString(value) { + return typeof value === "string" || value instanceof String; +}; + +/** + * Tests if the specified value is a non-null object. + * @param {*} value Value to test + * @returns {boolean} `true` if the value is a non-null object + */ +util.isObject = function isObject(value) { + return value && typeof value === "object"; +}; + +/** + * Node's Buffer class if available. + * @type {?function(new: Buffer)} + */ +util.Buffer = (function() { + try { + var Buffer = util.inquire("buffer").Buffer; + // refuse to use non-node buffers if not explicitly assigned (perf reasons): + return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null; + } catch (e) { + /* istanbul ignore next */ + return null; + } +})(); + +/** + * Internal alias of or polyfull for Buffer.from. + * @type {?function} + * @param {string|number[]} value Value + * @param {string} [encoding] Encoding if value is a string + * @returns {Uint8Array} + * @private + */ +util._Buffer_from = null; + +/** + * Internal alias of or polyfill for Buffer.allocUnsafe. + * @type {?function} + * @param {number} size Buffer size + * @returns {Uint8Array} + * @private + */ +util._Buffer_allocUnsafe = null; + +/** + * Creates a new buffer of whatever type supported by the environment. + * @param {number|number[]} [sizeOrArray=0] Buffer size or number array + * @returns {Uint8Array|Buffer} Buffer + */ +util.newBuffer = function newBuffer(sizeOrArray) { + /* istanbul ignore next */ + return typeof sizeOrArray === "number" + ? util.Buffer + ? util._Buffer_allocUnsafe(sizeOrArray) + : new util.Array(sizeOrArray) + : util.Buffer + ? util._Buffer_from(sizeOrArray) + : typeof Uint8Array === "undefined" + ? sizeOrArray + : new Uint8Array(sizeOrArray); +}; + +/** + * Array implementation used in the browser. `Uint8Array` if supported, otherwise `Array`. + * @type {?function(new: Uint8Array, *)} + */ +util.Array = typeof Uint8Array !== "undefined" ? Uint8Array /* istanbul ignore next */ : Array; + +/** + * Long.js's Long class if available. + * @type {?function(new: Long)} + */ +util.Long = /* istanbul ignore next */ global.dcodeIO && /* istanbul ignore next */ global.dcodeIO.Long || util.inquire("long"); + +/** + * Regular expression used to verify 2 bit (`bool`) map keys. + * @type {RegExp} + */ +util.key2Re = /^true|false|0|1$/; + +/** + * Regular expression used to verify 32 bit (`int32` etc.) map keys. + * @type {RegExp} + */ +util.key32Re = /^-?(?:0|[1-9][0-9]*)$/; + +/** + * Regular expression used to verify 64 bit (`int64` etc.) map keys. + * @type {RegExp} + */ +util.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/; + +/** + * Converts a number or long to an 8 characters long hash string. + * @param {Long|number} value Value to convert + * @returns {string} Hash + */ +util.longToHash = function longToHash(value) { + return value + ? util.LongBits.from(value).toHash() + : util.LongBits.zeroHash; +}; + +/** + * Converts an 8 characters long hash string to a long or number. + * @param {string} hash Hash + * @param {boolean} [unsigned=false] Whether unsigned or not + * @returns {Long|number} Original value + */ +util.longFromHash = function longFromHash(hash, unsigned) { + var bits = util.LongBits.fromHash(hash); + if (util.Long) + return util.Long.fromBits(bits.lo, bits.hi, unsigned); + return bits.toNumber(Boolean(unsigned)); +}; + +/** + * Merges the properties of the source object into the destination object. + * @memberof util + * @param {Object.} dst Destination object + * @param {Object.} src Source object + * @param {boolean} [ifNotSet=false] Merges only if the key is not already set + * @returns {Object.} Destination object + */ +function merge(dst, src, ifNotSet) { // used by converters + for (var keys = Object.keys(src), i = 0; i < keys.length; ++i) + if (dst[keys[i]] === undefined || !ifNotSet) + dst[keys[i]] = src[keys[i]]; + return dst; +} + +util.merge = merge; + +/** + * Converts the first character of a string to lower case. + * @param {string} str String to convert + * @returns {string} Converted string + */ +util.lcFirst = function lcFirst(str) { + return str.charAt(0).toLowerCase() + str.substring(1); +}; + +/** + * Creates a custom error constructor. + * @memberof util + * @param {string} name Error name + * @returns {function} Custom error constructor + */ +function newError(name) { + + function CustomError(message, properties) { + + if (!(this instanceof CustomError)) + return new CustomError(message, properties); + + // Error.call(this, message); + // ^ just returns a new error instance because the ctor can be called as a function + + Object.defineProperty(this, "message", { get: function() { return message; } }); + + /* istanbul ignore next */ + if (Error.captureStackTrace) // node + Error.captureStackTrace(this, CustomError); + else + Object.defineProperty(this, "stack", { value: (new Error()).stack || "" }); + + if (properties) + merge(this, properties); + } + + (CustomError.prototype = Object.create(Error.prototype)).constructor = CustomError; + + Object.defineProperty(CustomError.prototype, "name", { get: function() { return name; } }); + + CustomError.prototype.toString = function toString() { + return this.name + ": " + this.message; + }; + + return CustomError; +} + +util.newError = newError; + +/** + * Constructs a new protocol error. + * @classdesc Error subclass indicating a protocol specifc error. + * @memberof util + * @extends Error + * @constructor + * @param {string} message Error message + * @param {Object.=} properties Additional properties + * @example + * try { + * MyMessage.decode(someBuffer); // throws if required fields are missing + * } catch (e) { + * if (e instanceof ProtocolError && e.instance) + * console.log("decoded so far: " + JSON.stringify(e.instance)); + * } + */ +util.ProtocolError = newError("ProtocolError"); + +/** + * So far decoded message instance. + * @name util.ProtocolError#instance + * @type {Message} + */ + +/** + * Builds a getter for a oneof's present field name. + * @param {string[]} fieldNames Field names + * @returns {function():string|undefined} Unbound getter + */ +util.oneOfGetter = function getOneOf(fieldNames) { + var fieldMap = {}; + for (var i = 0; i < fieldNames.length; ++i) + fieldMap[fieldNames[i]] = 1; + + /** + * @returns {string|undefined} Set field name, if any + * @this Object + * @ignore + */ + return function() { // eslint-disable-line consistent-return + for (var keys = Object.keys(this), i = keys.length - 1; i > -1; --i) + if (fieldMap[keys[i]] === 1 && this[keys[i]] !== undefined && this[keys[i]] !== null) + return keys[i]; + }; +}; + +/** + * Builds a setter for a oneof's present field name. + * @param {string[]} fieldNames Field names + * @returns {function(?string):undefined} Unbound setter + */ +util.oneOfSetter = function setOneOf(fieldNames) { + + /** + * @param {string} name Field name + * @returns {undefined} + * @this Object + * @ignore + */ + return function(name) { + for (var i = 0; i < fieldNames.length; ++i) + if (fieldNames[i] !== name) + delete this[fieldNames[i]]; + }; +}; + +/** + * Lazily resolves fully qualified type names against the specified root. + * @param {Root} root Root instanceof + * @param {Object.} lazyTypes Type names + * @returns {undefined} + */ +util.lazyResolve = function lazyResolve(root, lazyTypes) { + for (var i = 0; i < lazyTypes.length; ++i) { + for (var keys = Object.keys(lazyTypes[i]), j = 0; j < keys.length; ++j) { + var path = lazyTypes[i][keys[j]].split("."), + ptr = root; + while (path.length) + ptr = ptr[path.shift()]; + lazyTypes[i][keys[j]] = ptr; + } + } +}; + +/** + * Default conversion options used for {@link Message#toJSON} implementations. Longs, enums and bytes are converted to strings by default. + * @type {ConversionOptions} + */ +util.toJSONOptions = { + longs: String, + enums: String, + bytes: String +}; + +util._configure = function() { + var Buffer = util.Buffer; + /* istanbul ignore if */ + if (!Buffer) { + util._Buffer_from = util._Buffer_allocUnsafe = null; + return; + } + // because node 4.x buffers are incompatible & immutable + // see: https://github.com/dcodeIO/protobuf.js/pull/665 + util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from || + /* istanbul ignore next */ + function Buffer_from(value, encoding) { + return new Buffer(value, encoding); + }; + util._Buffer_allocUnsafe = Buffer.allocUnsafe || + /* istanbul ignore next */ + function Buffer_allocUnsafe(size) { + return new Buffer(size); + }; +}; + +},{"1":1,"12":12,"2":2,"3":3,"4":4,"5":5,"6":6}],14:[function(require,module,exports){ +"use strict"; +module.exports = Writer; + +var util = require(13); + +var BufferWriter; // cyclic + +var LongBits = util.LongBits, + base64 = util.base64, + utf8 = util.utf8; + +/** + * Constructs a new writer operation instance. + * @classdesc Scheduled writer operation. + * @constructor + * @param {function(*, Uint8Array, number)} fn Function to call + * @param {number} len Value byte length + * @param {*} val Value to write + * @ignore + */ +function Op(fn, len, val) { + + /** + * Function to call. + * @type {function(Uint8Array, number, *)} + */ + this.fn = fn; + + /** + * Value byte length. + * @type {number} + */ + this.len = len; + + /** + * Next operation. + * @type {Writer.Op|undefined} + */ + this.next = undefined; + + /** + * Value to write. + * @type {*} + */ + this.val = val; // type varies +} + +/* istanbul ignore next */ +function noop() {} // eslint-disable-line no-empty-function + +/** + * Constructs a new writer state instance. + * @classdesc Copied writer state. + * @memberof Writer + * @constructor + * @param {Writer} writer Writer to copy state from + * @private + * @ignore + */ +function State(writer) { + + /** + * Current head. + * @type {Writer.Op} + */ + this.head = writer.head; + + /** + * Current tail. + * @type {Writer.Op} + */ + this.tail = writer.tail; + + /** + * Current buffer length. + * @type {number} + */ + this.len = writer.len; + + /** + * Next state. + * @type {?State} + */ + this.next = writer.states; +} + +/** + * Constructs a new writer instance. + * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`. + * @constructor + */ +function Writer() { + + /** + * Current length. + * @type {number} + */ + this.len = 0; + + /** + * Operations head. + * @type {Object} + */ + this.head = new Op(noop, 0, 0); + + /** + * Operations tail + * @type {Object} + */ + this.tail = this.head; + + /** + * Linked forked states. + * @type {?Object} + */ + this.states = null; + + // When a value is written, the writer calculates its byte length and puts it into a linked + // list of operations to perform when finish() is called. This both allows us to allocate + // buffers of the exact required size and reduces the amount of work we have to do compared + // to first calculating over objects and then encoding over objects. In our case, the encoding + // part is just a linked list walk calling operations with already prepared values. +} + +/** + * Creates a new writer. + * @function + * @returns {BufferWriter|Writer} A {@link BufferWriter} when Buffers are supported, otherwise a {@link Writer} + */ +Writer.create = util.Buffer + ? function create_buffer_setup() { + return (Writer.create = function create_buffer() { + return new BufferWriter(); + })(); + } + /* istanbul ignore next */ + : function create_array() { + return new Writer(); + }; + +/** + * Allocates a buffer of the specified size. + * @param {number} size Buffer size + * @returns {Uint8Array} Buffer + */ +Writer.alloc = function alloc(size) { + return new util.Array(size); +}; + +// Use Uint8Array buffer pool in the browser, just like node does with buffers +/* istanbul ignore else */ +if (util.Array !== Array) + Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray); + +/** + * Pushes a new operation to the queue. + * @param {function(Uint8Array, number, *)} fn Function to call + * @param {number} len Value byte length + * @param {number} val Value to write + * @returns {Writer} `this` + */ +Writer.prototype.push = function push(fn, len, val) { + this.tail = this.tail.next = new Op(fn, len, val); + this.len += len; + return this; +}; + +function writeByte(val, buf, pos) { + buf[pos] = val & 255; +} + +function writeVarint32(val, buf, pos) { + while (val > 127) { + buf[pos++] = val & 127 | 128; + val >>>= 7; + } + buf[pos] = val; +} + +/** + * Constructs a new varint writer operation instance. + * @classdesc Scheduled varint writer operation. + * @extends Op + * @constructor + * @param {number} len Value byte length + * @param {number} val Value to write + * @ignore + */ +function VarintOp(len, val) { + this.len = len; + this.next = undefined; + this.val = val; +} + +VarintOp.prototype = Object.create(Op.prototype); +VarintOp.prototype.fn = writeVarint32; + +/** + * Writes an unsigned 32 bit value as a varint. + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.uint32 = function write_uint32(value) { + // here, the call to this.push has been inlined and a varint specific Op subclass is used. + // uint32 is by far the most frequently used operation and benefits significantly from this. + this.len += (this.tail = this.tail.next = new VarintOp( + (value = value >>> 0) + < 128 ? 1 + : value < 16384 ? 2 + : value < 2097152 ? 3 + : value < 268435456 ? 4 + : 5, + value)).len; + return this; +}; + +/** + * Writes a signed 32 bit value as a varint. + * @function + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.int32 = function write_int32(value) { + return value < 0 + ? this.push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec + : this.uint32(value); +}; + +/** + * Writes a 32 bit value as a varint, zig-zag encoded. + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.sint32 = function write_sint32(value) { + return this.uint32((value << 1 ^ value >> 31) >>> 0); +}; + +function writeVarint64(val, buf, pos) { + while (val.hi) { + buf[pos++] = val.lo & 127 | 128; + val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0; + val.hi >>>= 7; + } + while (val.lo > 127) { + buf[pos++] = val.lo & 127 | 128; + val.lo = val.lo >>> 7; + } + buf[pos++] = val.lo; +} + +/** + * Writes an unsigned 64 bit value as a varint. + * @param {Long|number|string} value Value to write + * @returns {Writer} `this` + * @throws {TypeError} If `value` is a string and no long library is present. + */ +Writer.prototype.uint64 = function write_uint64(value) { + var bits = LongBits.from(value); + return this.push(writeVarint64, bits.length(), bits); +}; + +/** + * Writes a signed 64 bit value as a varint. + * @function + * @param {Long|number|string} value Value to write + * @returns {Writer} `this` + * @throws {TypeError} If `value` is a string and no long library is present. + */ +Writer.prototype.int64 = Writer.prototype.uint64; + +/** + * Writes a signed 64 bit value as a varint, zig-zag encoded. + * @param {Long|number|string} value Value to write + * @returns {Writer} `this` + * @throws {TypeError} If `value` is a string and no long library is present. + */ +Writer.prototype.sint64 = function write_sint64(value) { + var bits = LongBits.from(value).zzEncode(); + return this.push(writeVarint64, bits.length(), bits); +}; + +/** + * Writes a boolish value as a varint. + * @param {boolean} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.bool = function write_bool(value) { + return this.push(writeByte, 1, value ? 1 : 0); +}; + +function writeFixed32(val, buf, pos) { + buf[pos++] = val & 255; + buf[pos++] = val >>> 8 & 255; + buf[pos++] = val >>> 16 & 255; + buf[pos ] = val >>> 24; +} + +/** + * Writes an unsigned 32 bit value as fixed 32 bits. + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.fixed32 = function write_fixed32(value) { + return this.push(writeFixed32, 4, value >>> 0); +}; + +/** + * Writes a signed 32 bit value as fixed 32 bits. + * @function + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.sfixed32 = Writer.prototype.fixed32; + +/** + * Writes an unsigned 64 bit value as fixed 64 bits. + * @param {Long|number|string} value Value to write + * @returns {Writer} `this` + * @throws {TypeError} If `value` is a string and no long library is present. + */ +Writer.prototype.fixed64 = function write_fixed64(value) { + var bits = LongBits.from(value); + return this.push(writeFixed32, 4, bits.lo).push(writeFixed32, 4, bits.hi); +}; + +/** + * Writes a signed 64 bit value as fixed 64 bits. + * @function + * @param {Long|number|string} value Value to write + * @returns {Writer} `this` + * @throws {TypeError} If `value` is a string and no long library is present. + */ +Writer.prototype.sfixed64 = Writer.prototype.fixed64; + +var writeFloat = typeof Float32Array !== "undefined" + ? (function() { + var f32 = new Float32Array(1), + f8b = new Uint8Array(f32.buffer); + f32[0] = -0; + return f8b[3] // already le? + ? function writeFloat_f32(val, buf, pos) { + f32[0] = val; + buf[pos++] = f8b[0]; + buf[pos++] = f8b[1]; + buf[pos++] = f8b[2]; + buf[pos ] = f8b[3]; + } + /* istanbul ignore next */ + : function writeFloat_f32_le(val, buf, pos) { + f32[0] = val; + buf[pos++] = f8b[3]; + buf[pos++] = f8b[2]; + buf[pos++] = f8b[1]; + buf[pos ] = f8b[0]; + }; + })() + /* istanbul ignore next */ + : function writeFloat_ieee754(value, buf, pos) { + var sign = value < 0 ? 1 : 0; + if (sign) + value = -value; + if (value === 0) + writeFixed32(1 / value > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos); + else if (isNaN(value)) + writeFixed32(2147483647, buf, pos); + else if (value > 3.4028234663852886e+38) // +-Infinity + writeFixed32((sign << 31 | 2139095040) >>> 0, buf, pos); + else if (value < 1.1754943508222875e-38) // denormal + writeFixed32((sign << 31 | Math.round(value / 1.401298464324817e-45)) >>> 0, buf, pos); + else { + var exponent = Math.floor(Math.log(value) / Math.LN2), + mantissa = Math.round(value * Math.pow(2, -exponent) * 8388608) & 8388607; + writeFixed32((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos); + } + }; + +/** + * Writes a float (32 bit). + * @function + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.float = function write_float(value) { + return this.push(writeFloat, 4, value); +}; + +var writeDouble = typeof Float64Array !== "undefined" + ? (function() { + var f64 = new Float64Array(1), + f8b = new Uint8Array(f64.buffer); + f64[0] = -0; + return f8b[7] // already le? + ? function writeDouble_f64(val, buf, pos) { + f64[0] = val; + buf[pos++] = f8b[0]; + buf[pos++] = f8b[1]; + buf[pos++] = f8b[2]; + buf[pos++] = f8b[3]; + buf[pos++] = f8b[4]; + buf[pos++] = f8b[5]; + buf[pos++] = f8b[6]; + buf[pos ] = f8b[7]; + } + /* istanbul ignore next */ + : function writeDouble_f64_le(val, buf, pos) { + f64[0] = val; + buf[pos++] = f8b[7]; + buf[pos++] = f8b[6]; + buf[pos++] = f8b[5]; + buf[pos++] = f8b[4]; + buf[pos++] = f8b[3]; + buf[pos++] = f8b[2]; + buf[pos++] = f8b[1]; + buf[pos ] = f8b[0]; + }; + })() + /* istanbul ignore next */ + : function writeDouble_ieee754(value, buf, pos) { + var sign = value < 0 ? 1 : 0; + if (sign) + value = -value; + if (value === 0) { + writeFixed32(0, buf, pos); + writeFixed32(1 / value > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos + 4); + } else if (isNaN(value)) { + writeFixed32(4294967295, buf, pos); + writeFixed32(2147483647, buf, pos + 4); + } else if (value > 1.7976931348623157e+308) { // +-Infinity + writeFixed32(0, buf, pos); + writeFixed32((sign << 31 | 2146435072) >>> 0, buf, pos + 4); + } else { + var mantissa; + if (value < 2.2250738585072014e-308) { // denormal + mantissa = value / 5e-324; + writeFixed32(mantissa >>> 0, buf, pos); + writeFixed32((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + 4); + } else { + var exponent = Math.floor(Math.log(value) / Math.LN2); + if (exponent === 1024) + exponent = 1023; + mantissa = value * Math.pow(2, -exponent); + writeFixed32(mantissa * 4503599627370496 >>> 0, buf, pos); + writeFixed32((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + 4); + } + } + }; + +/** + * Writes a double (64 bit float). + * @function + * @param {number} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.double = function write_double(value) { + return this.push(writeDouble, 8, value); +}; + +var writeBytes = util.Array.prototype.set + ? function writeBytes_set(val, buf, pos) { + buf.set(val, pos); // also works for plain array values + } + /* istanbul ignore next */ + : function writeBytes_for(val, buf, pos) { + for (var i = 0; i < val.length; ++i) + buf[pos + i] = val[i]; + }; + +/** + * Writes a sequence of bytes. + * @param {Uint8Array|string} value Buffer or base64 encoded string to write + * @returns {Writer} `this` + */ +Writer.prototype.bytes = function write_bytes(value) { + var len = value.length >>> 0; + if (!len) + return this.push(writeByte, 1, 0); + if (util.isString(value)) { + var buf = Writer.alloc(len = base64.length(value)); + base64.decode(value, buf, 0); + value = buf; + } + return this.uint32(len).push(writeBytes, len, value); +}; + +/** + * Writes a string. + * @param {string} value Value to write + * @returns {Writer} `this` + */ +Writer.prototype.string = function write_string(value) { + var len = utf8.length(value); + return len + ? this.uint32(len).push(utf8.write, len, value) + : this.push(writeByte, 1, 0); +}; + +/** + * Forks this writer's state by pushing it to a stack. + * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state. + * @returns {Writer} `this` + */ +Writer.prototype.fork = function fork() { + this.states = new State(this); + this.head = this.tail = new Op(noop, 0, 0); + this.len = 0; + return this; +}; + +/** + * Resets this instance to the last state. + * @returns {Writer} `this` + */ +Writer.prototype.reset = function reset() { + if (this.states) { + this.head = this.states.head; + this.tail = this.states.tail; + this.len = this.states.len; + this.states = this.states.next; + } else { + this.head = this.tail = new Op(noop, 0, 0); + this.len = 0; + } + return this; +}; + +/** + * Resets to the last state and appends the fork state's current write length as a varint followed by its operations. + * @returns {Writer} `this` + */ +Writer.prototype.ldelim = function ldelim() { + var head = this.head, + tail = this.tail, + len = this.len; + this.reset().uint32(len); + if (len) { + this.tail.next = head.next; // skip noop + this.tail = tail; + this.len += len; + } + return this; +}; + +/** + * Finishes the write operation. + * @returns {Uint8Array} Finished buffer + */ +Writer.prototype.finish = function finish() { + var head = this.head.next, // skip noop + buf = this.constructor.alloc(this.len), + pos = 0; + while (head) { + head.fn(head.val, buf, pos); + pos += head.len; + head = head.next; + } + // this.head = this.tail = null; + return buf; +}; + +Writer._configure = function(BufferWriter_) { + BufferWriter = BufferWriter_; +}; + +},{"13":13}],15:[function(require,module,exports){ +"use strict"; +module.exports = BufferWriter; + +// extends Writer +var Writer = require(14); +(BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter; + +var util = require(13); + +var Buffer = util.Buffer; + +/** + * Constructs a new buffer writer instance. + * @classdesc Wire format writer using node buffers. + * @extends Writer + * @constructor + */ +function BufferWriter() { + Writer.call(this); +} + +/** + * Allocates a buffer of the specified size. + * @param {number} size Buffer size + * @returns {Buffer} Buffer + */ +BufferWriter.alloc = function alloc_buffer(size) { + return (BufferWriter.alloc = util._Buffer_allocUnsafe)(size); +}; + +var writeBytesBuffer = Buffer && Buffer.prototype instanceof Uint8Array && Buffer.prototype.set.name === "set" + ? function writeBytesBuffer_set(val, buf, pos) { + buf.set(val, pos); // faster than copy (requires node >= 4 where Buffers extend Uint8Array and set is properly inherited) + // also works for plain array values + } + /* istanbul ignore next */ + : function writeBytesBuffer_copy(val, buf, pos) { + if (val.copy) // Buffer values + val.copy(buf, pos, 0, val.length); + else for (var i = 0; i < val.length;) // plain array values + buf[pos++] = val[i++]; + }; + +/** + * @override + */ +BufferWriter.prototype.bytes = function write_bytes_buffer(value) { + if (util.isString(value)) + value = util._Buffer_from(value, "base64"); + var len = value.length >>> 0; + this.uint32(len); + if (len) + this.push(writeBytesBuffer, len, value); + return this; +}; + +function writeStringBuffer(val, buf, pos) { + if (val.length < 40) // plain js is faster for short strings (probably due to redundant assertions) + util.utf8.write(val, buf, pos); + else + buf.utf8Write(val, pos); +} + +/** + * @override + */ +BufferWriter.prototype.string = function write_string_buffer(value) { + var len = Buffer.byteLength(value); + this.uint32(len); + if (len) + this.push(writeStringBuffer, len, value); + return this; +}; + + +/** + * Finishes the write operation. + * @name BufferWriter#finish + * @function + * @returns {Buffer} Finished buffer + */ + +},{"13":13,"14":14}]},{},[7]) + +})(typeof window==="object"&&window||typeof self==="object"&&self||this); +//# sourceMappingURL=protobuf.js.map diff --git a/Source/Widgets/Animation/Animation.js b/Source/Widgets/Animation/Animation.js index 546986c6c71e..eae35055baef 100644 --- a/Source/Widgets/Animation/Animation.js +++ b/Source/Widgets/Animation/Animation.js @@ -611,6 +611,11 @@ define([ * removing the widget from layout. */ Animation.prototype.destroy = function() { + if (defined(this._observer)) { + this._observer.disconnect(); + this._observer = undefined; + } + var mouseCallback = this._mouseCallback; this._shuttleRingBackPanel.removeEventListener('mousedown', mouseCallback, true); this._shuttleRingBackPanel.removeEventListener('touchstart', mouseCallback, true); @@ -698,6 +703,27 @@ define([ * animation.applyThemeChanges(); */ Animation.prototype.applyThemeChanges = function() { + // Since we rely on computed styles for themeing, we can't actually + // do anything if the container has not yet been added to the DOM. + // Set up an observer to be notified when it is added and apply + // the changes at that time. + if (!document.body.contains(this._container)) { + if (defined(this._observer)) { + //Already listening. + return; + } + var that = this; + that._observer = new MutationObserver(function() { + if (document.body.contains(that._container)) { + that._observer.disconnect(); + that._observer = undefined; + that.applyThemeChanges(); + } + }); + that._observer.observe(document, {childList : true, subtree : true}); + return; + } + var buttonNormalBackColor = getElementColor(this._themeNormal); var buttonHoverBackColor = getElementColor(this._themeHover); var buttonToggledBackColor = getElementColor(this._themeSelect); diff --git a/Source/Widgets/BaseLayerPicker/BaseLayerPicker.css b/Source/Widgets/BaseLayerPicker/BaseLayerPicker.css index aeff8f6a8182..2dfdbc10c018 100644 --- a/Source/Widgets/BaseLayerPicker/BaseLayerPicker.css +++ b/Source/Widgets/BaseLayerPicker/BaseLayerPicker.css @@ -10,8 +10,6 @@ .cesium-baseLayerPicker-dropDown { display: block; position: absolute; - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; box-sizing: content-box; top: auto; right: 0; @@ -27,24 +25,16 @@ -webkit-user-select: none; -ms-user-select: none; user-select: none; - -webkit-transform: translate(0, -20%); - -moz-transform: translate(0, -20%); transform: translate(0, -20%); visibility: hidden; opacity: 0; - -webkit-transition: visibility 0s 0.2s, opacity 0.2s ease-in, -webkit-transform 0.2s ease-in; - -moz-transition: visibility 0s 0.2s, opacity 0.2s ease-in, -moz-transform 0.2s ease-in; transition: visibility 0s 0.2s, opacity 0.2s ease-in, transform 0.2s ease-in; } .cesium-baseLayerPicker-dropDown-visible { - -webkit-transform: translate(0, 0); - -moz-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; opacity: 1; - -webkit-transition: opacity 0.2s ease-out, -webkit-transform 0.2s ease-out; - -moz-transition: opacity 0.2s ease-out, -moz-transform 0.2s ease-out; transition: opacity 0.2s ease-out, transform 0.2s ease-out; } @@ -101,8 +91,6 @@ margin: 0; padding: 0; cursor: pointer; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; } diff --git a/Source/Widgets/BaseLayerPicker/createDefaultTerrainProviderViewModels.js b/Source/Widgets/BaseLayerPicker/createDefaultTerrainProviderViewModels.js index 674b484ec607..af24c65c3004 100644 --- a/Source/Widgets/BaseLayerPicker/createDefaultTerrainProviderViewModels.js +++ b/Source/Widgets/BaseLayerPicker/createDefaultTerrainProviderViewModels.js @@ -32,7 +32,7 @@ define([ tooltip : 'High-resolution, mesh-based terrain for the entire globe. Free for use on the Internet. Closed-network options are available.\nhttp://www.agi.com', creationFunction : function() { return new CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestWaterMask : true, requestVertexNormals : true }); diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css new file mode 100644 index 000000000000..196038b4a88e --- /dev/null +++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css @@ -0,0 +1,149 @@ + +ul.cesium-cesiumInspector-statistics { + margin: 0; + padding-top: 3px; + padding-bottom: 3px; +} + +ul.cesium-cesiumInspector-statistics + ul.cesium-cesiumInspector-statistics { + border-top: 1px solid #aaa; +} + +.cesium-cesiumInspector-slider { + margin-top: 5px; +} + +.cesium-cesiumInspector-slider input[type=number] { + text-align: left; + background-color: #222; + outline: none; + border: 1px solid #444; + color: #edffff; + width: 100px; + border-radius: 3px; + padding: 1px; + margin-left: 10px; + cursor: auto; +} + +.cesium-cesiumInspector-slider input[type=number]::-webkit-outer-spin-button, +.cesium-cesiumInspector-slider input[type=number]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.cesium-cesiumInspector-slider input[type=range] { + margin-left: 5px; + vertical-align: middle; + -webkit-appearance: none; + background: #ddd; + height: 3px; +} + +input[type=range]:focus { + outline: none; +} + +.cesium-cesiumInspector-slider input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + border: 1px solid #000000; + height: 10px; + width: 10px; + border-radius: 5px; + background: #ffffff; + cursor: pointer; +} + +.cesium-cesiumInspector-slider input[type=range]::-moz-range-thumb { + border: 1px solid #000000; + height: 10px; + width: 10px; + border-radius: 5px; + background: #ffffff; + cursor: pointer; +} + +.cesium-cesiumInspector-slider input[type=range]::-moz-range-thumb { + border: 1px solid #000000; + height: 10px; + width: 10px; + border-radius: 5px; + background: #ffffff; + cursor: pointer; +} + +.cesium-cesiumInspector-hide .cesium-cesiumInspector-styleEditor { + display: none; +} + +.cesium-cesiumInspector-styleEditor { + padding: 10px; + border-radius: 5px; + background: rgba(48, 51, 54, 0.8); + border: 1px solid #444; +} + +.cesium-cesiumInspector-styleEditor textarea { + width: 100%; + height: 300px; + background: transparent; + color: #edffff; + border: none; + padding: 0; + white-space: pre; + overflow-wrap: normal; + overflow-x: auto; +} + +.cesium-3DTilesInspector { + width: 300px; + pointer-events: all; +} + +.cesium-3DTilesInspector-statistics { + font-size: 11px; +} + +.cesium-3DTilesInspector div, .cesium-3DTilesInspector input[type=range] { + width: 100%; + box-sizing: border-box; +} + +.cesium-cesiumInspector-error { + color: #ff9e9e; + overflow: auto; +} + +.cesium-3DTilesInspector .cesium-cesiumInspector-section { + margin-top: 3px; +} + +.cesium-3DTilesInspector .cesium-cesiumInspector-sectionHeader + .cesium-cesiumInspector-show { + border-top: 1px solid white; +} + +input.cesium-cesiumInspector-url { + overflow: hidden; + white-space: nowrap; + overflow-x: scroll; + background-color: transparent; + color: white; + outline: none; + border: none; + height: 1em; + width: 100%; +} + +.cesium-cesiumInspector .field-group { + display: table; +} + +.cesium-cesiumInspector .field-group > label { + display: table-cell; + font-weight: bold; +} + +.cesium-cesiumInspector .field-group > .field { + display: table-cell; + width: 100%; +} diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.js b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.js new file mode 100644 index 000000000000..8c90f59d6a67 --- /dev/null +++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.js @@ -0,0 +1,284 @@ +/*global define*/ +define([ + '../../Core/Check', + '../../Core/defaultValue', + '../../Core/defined', + '../../Core/defineProperties', + '../../Core/destroyObject', + '../../ThirdParty/knockout', + '../getElement', + './Cesium3DTilesInspectorViewModel' +], function( + Check, + defaultValue, + defined, + defineProperties, + destroyObject, + knockout, + getElement, + Cesium3DTilesInspectorViewModel) { + 'use strict'; + + /** + * Inspector widget to aid in debugging 3D Tiles + * + * @alias Cesium3DTilesInspector + * @constructor + * + * @param {Element|String} container The DOM element or ID that will contain the widget. + * @param {Scene} scene the Scene instance to use. + */ + function Cesium3DTilesInspector(container, scene) { + //>includeStart('debug', pragmas.debug); + Check.defined('container', container); + Check.typeOf.object('scene', scene); + //>>includeEnd('debug'); + + container = getElement(container); + var element = document.createElement('div'); + var performanceContainer = document.createElement('div'); + performanceContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : performance, "cesium-cesiumInspector-hide" : !performance}'); + var viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + + this._viewModel = viewModel; + this._container = container; + this._element = element; + + var text = document.createElement('div'); + text.textContent = '3D Tiles Inspector'; + text.className = 'cesium-cesiumInspector-button'; + text.setAttribute('data-bind', 'click: toggleInspector'); + element.appendChild(text); + element.className = 'cesium-cesiumInspector cesium-3DTilesInspector'; + element.setAttribute('data-bind', 'css: { "cesium-cesiumInspector-visible" : inspectorVisible, "cesium-cesiumInspector-hidden" : !inspectorVisible}'); + container.appendChild(element); + + var tilesetPanelContents = document.createElement('div'); + var displayPanelContents = document.createElement('div'); + var updatePanelContents = document.createElement('div'); + var loggingPanelContents = document.createElement('div'); + var tileDebugLabelsPanelContents = document.createElement('div'); + var stylePanelContents = document.createElement('div'); + var optimizationPanelContents = document.createElement('div'); + + var properties = document.createElement('div'); + properties.className = 'field-group'; + var propertiesLabel = document.createElement('label'); + propertiesLabel.className = 'field-label'; + propertiesLabel.appendChild(document.createTextNode('Properties: ')); + var propertiesField = document.createElement('div'); + propertiesField.setAttribute('data-bind', 'text: properties'); + properties.appendChild(propertiesLabel); + properties.appendChild(propertiesField); + tilesetPanelContents.appendChild(properties); + tilesetPanelContents.appendChild(makeButton('togglePickTileset', 'Pick Tileset', 'pickActive')); + tilesetPanelContents.appendChild(makeButton('trimTilesCache', 'Trim Tiles Cache')); + tilesetPanelContents.appendChild(makeCheckbox('picking', 'Enable Picking')); + + displayPanelContents.appendChild(makeCheckbox('colorize', 'Colorize')); + displayPanelContents.appendChild(makeCheckbox('wireframe', 'Wireframe')); + displayPanelContents.appendChild(makeCheckbox('showBoundingVolumes', 'Bounding Volumes')); + displayPanelContents.appendChild(makeCheckbox('showContentBoundingVolumes', 'Content Volumes')); + displayPanelContents.appendChild(makeCheckbox('showRequestVolumes', 'Request Volumes')); + + updatePanelContents.appendChild(makeCheckbox('freezeFrame', 'Freeze Frame')); + updatePanelContents.appendChild(makeCheckbox('dynamicScreenSpaceError', 'Dynamic Screen Space Error')); + var sseContainer = document.createElement('div'); + sseContainer.appendChild(makeRangeInput('maximumScreenSpaceError', 0, 128, 1, 'Maximum Screen Space Error')); + updatePanelContents.appendChild(sseContainer); + var dynamicScreenSpaceErrorContainer = document.createElement('div'); + dynamicScreenSpaceErrorContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : dynamicScreenSpaceError, "cesium-cesiumInspector-hide" : !dynamicScreenSpaceError}'); + dynamicScreenSpaceErrorContainer.appendChild(makeRangeInput('dynamicScreenSpaceErrorDensitySliderValue', 0, 1, 0.005, 'Screen Space Error Density', 'dynamicScreenSpaceErrorDensity')); + dynamicScreenSpaceErrorContainer.appendChild(makeRangeInput('dynamicScreenSpaceErrorFactor', 1, 10, 0.1, 'Screen Space Error Factor')); + updatePanelContents.appendChild(dynamicScreenSpaceErrorContainer); + + loggingPanelContents.appendChild(makeCheckbox('performance', 'Performance')); + loggingPanelContents.appendChild(performanceContainer); + loggingPanelContents.appendChild(makeCheckbox('showStatistics', 'Statistics')); + var statistics = document.createElement('div'); + statistics.className = 'cesium-3dTilesInspector-statistics'; + statistics.setAttribute('data-bind', 'html: statisticsText, visible: showStatistics'); + loggingPanelContents.appendChild(statistics); + loggingPanelContents.appendChild(makeCheckbox('showPickStatistics', 'Pick Statistics')); + var pickStatistics = document.createElement('div'); + pickStatistics.className = 'cesium-3dTilesInspector-statistics'; + pickStatistics.setAttribute('data-bind', 'html: pickStatisticsText, visible: showPickStatistics'); + loggingPanelContents.appendChild(pickStatistics); + + stylePanelContents.appendChild(document.createTextNode('Color Blend Mode: ')); + var blendDropdown = document.createElement('select'); + blendDropdown.setAttribute('data-bind', 'options: colorBlendModes, ' + + 'optionsText: "text", ' + + 'optionsValue: "value", ' + + 'value: colorBlendMode'); + stylePanelContents.appendChild(blendDropdown); + var styleEditor = document.createElement('textarea'); + styleEditor.setAttribute('data-bind', 'textInput: styleString, event: { keydown: styleEditorKeyPress }'); + stylePanelContents.className = 'cesium-cesiumInspector-styleEditor'; + stylePanelContents.appendChild(styleEditor); + var closeStylesBtn = makeButton('compileStyle', 'Compile (Ctrl+Enter)'); + stylePanelContents.appendChild(closeStylesBtn); + var errorBox = document.createElement('div'); + errorBox.className = 'cesium-cesiumInspector-error'; + errorBox.setAttribute('data-bind', 'text: editorError'); + stylePanelContents.appendChild(errorBox); + + tileDebugLabelsPanelContents.appendChild(makeCheckbox('showOnlyPickedTileDebugLabel', 'Show Picked Only')); + tileDebugLabelsPanelContents.appendChild(makeCheckbox('showGeometricError', 'Geometric Error')); + tileDebugLabelsPanelContents.appendChild(makeCheckbox('showRenderingStatistics', 'Rendering Statistics')); + tileDebugLabelsPanelContents.appendChild(makeCheckbox('showMemoryUsage', 'Memory Usage (MB)')); + + optimizationPanelContents.appendChild(makeCheckbox('skipLevelOfDetail', 'Skip Tile LODs')); + var skipScreenSpaceErrorFactorContainer = document.createElement('div'); + skipScreenSpaceErrorFactorContainer.appendChild(makeRangeInput('skipScreenSpaceErrorFactor', 1, 50, 1, 'Skip SSE Factor')); + optimizationPanelContents.appendChild(skipScreenSpaceErrorFactorContainer); + var baseScreenSpaceError = document.createElement('div'); + baseScreenSpaceError.appendChild(makeRangeInput('baseScreenSpaceError', 0, 4096, 1, 'SSE before skipping LOD')); + optimizationPanelContents.appendChild(baseScreenSpaceError); + var skipLevelsContainer = document.createElement('div'); + skipLevelsContainer.appendChild(makeRangeInput('skipLevels', 0, 10, 1, 'Min. levels to skip')); + optimizationPanelContents.appendChild(skipLevelsContainer); + optimizationPanelContents.appendChild(makeCheckbox('immediatelyLoadDesiredLevelOfDetail', 'Load only tiles that meet the max. SSE.')); + optimizationPanelContents.appendChild(makeCheckbox('loadSiblings', 'Load siblings of visible tiles.')); + + var tilesetPanel = makeSection('Tileset', 'tilesetVisible', 'toggleTileset', tilesetPanelContents); + var displayPanel = makeSection('Display', 'displayVisible', 'toggleDisplay', displayPanelContents); + var updatePanel = makeSection('Update', 'updateVisible', 'toggleUpdate', updatePanelContents); + var loggingPanel = makeSection('Logging', 'loggingVisible', 'toggleLogging', loggingPanelContents); + var tileDebugLabelsPanel = makeSection('Tile Debug Labels', 'tileDebugLabelsVisible', 'toggleTileDebugLabels', tileDebugLabelsPanelContents); + var stylePanel = makeSection('Style', 'styleVisible', 'toggleStyle', stylePanelContents); + var optimizationPanel = makeSection('Optimization', 'optimizationVisible', 'toggleOptimization', optimizationPanelContents); + + // first add and bind all the toggleable panels + element.appendChild(tilesetPanel); + element.appendChild(displayPanel); + element.appendChild(updatePanel); + element.appendChild(loggingPanel); + element.appendChild(tileDebugLabelsPanel); + element.appendChild(stylePanel); + element.appendChild(optimizationPanel); + + knockout.applyBindings(viewModel, element); + } + + defineProperties(Cesium3DTilesInspector.prototype, { + /** + * Gets the parent container. + * @memberof Cesium3DTilesInspector.prototype + * + * @type {Element} + */ + container : { + get : function() { + return this._container; + } + }, + + /** + * Gets the view model. + * @memberof Cesium3DTilesInspector.prototype + * + * @type {Cesium3DTilesInspectorViewModel} + */ + viewModel : { + get : function() { + return this._viewModel; + } + } + }); + + /** + * @returns {Boolean} true if the object has been destroyed, false otherwise. + */ + Cesium3DTilesInspector.prototype.isDestroyed = function() { + return false; + }; + + /** + * Destroys the widget. Should be called if permanently + * removing the widget from layout. + */ + Cesium3DTilesInspector.prototype.destroy = function() { + knockout.cleanNode(this._element); + this._container.removeChild(this._element); + this.viewModel.destroy(); + + return destroyObject(this); + }; + + function makeSection(name, visibleProp, toggleProp, contents) { + var toggle = document.createElement('span'); + toggle.className = 'cesium-cesiumInspector-toggleSwitch'; + toggle.setAttribute('data-bind', 'text: ' + visibleProp + ' ? "-" : "+", click: ' + toggleProp); + + var header = document.createElement('div'); + header.className = 'cesium-cesiumInspector-sectionHeader'; + header.appendChild(toggle); + header.appendChild(document.createTextNode(name)); + + var section = document.createElement('div'); + section.className = 'cesium-cesiumInspector-section'; + section.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : ' + visibleProp + ', "cesium-cesiumInspector-hide" : !' + visibleProp + '}'); + section.appendChild(contents); + + var panel = document.createElement('div'); + panel.className = 'cesium-cesiumInspector-dropDown'; + panel.appendChild(header); + panel.appendChild(section); + + return panel; + } + + function makeCheckbox(property, text) { + var checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.setAttribute('data-bind', 'checked: ' + property); + + var container = document.createElement('div'); + container.appendChild(checkbox); + container.appendChild(document.createTextNode(text)); + + return container; + } + + function makeRangeInput(property, min, max, step, text, displayProperty) { + displayProperty = defaultValue(displayProperty, property); + var input = document.createElement('input'); + input.setAttribute('data-bind', 'value: ' + displayProperty); + input.type = 'number'; + + var slider = document.createElement('input'); + slider.type = 'range'; + slider.min = min; + slider.max = max; + slider.step = step; + slider.setAttribute('data-bind', 'valueUpdate: "input", value: ' + property); + + var wrapper = document.createElement('div'); + wrapper.appendChild(slider); + + var container = document.createElement('div'); + container.className = 'cesium-cesiumInspector-slider'; + container.appendChild(document.createTextNode(text)); + container.appendChild(input); + container.appendChild(wrapper); + + return container; + } + + function makeButton(action, text, active) { + var button = document.createElement('button'); + button.type = 'button'; + button.textContent = text; + button.className = 'cesium-cesiumInspector-pickButton'; + var binding = 'click: ' + action; + if (defined(active)) { + binding += ', css: {"cesium-cesiumInspector-pickButtonHighlight" : ' + active + '}'; + } + button.setAttribute('data-bind', binding); + + return button; + } + + return Cesium3DTilesInspector; +}); diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js new file mode 100644 index 000000000000..23b02d8ca357 --- /dev/null +++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js @@ -0,0 +1,1238 @@ +/*global define*/ +define([ + '../../Scene/Cesium3DTileFeature', + '../../Scene/Cesium3DTileset', + '../../Scene/Cesium3DTileStyle', + '../../Scene/Cesium3DTileColorBlendMode', + '../../Core/Check', + '../../Core/Color', + '../../Core/defined', + '../../Core/defineProperties', + '../../Core/destroyObject', + '../../ThirdParty/knockout', + '../../Scene/PerformanceDisplay', + '../../Core/ScreenSpaceEventHandler', + '../../Core/ScreenSpaceEventType' +], function( + Cesium3DTileFeature, + Cesium3DTileset, + Cesium3DTileStyle, + Cesium3DTileColorBlendMode, + Check, + Color, + defined, + defineProperties, + destroyObject, + knockout, + PerformanceDisplay, + ScreenSpaceEventHandler, + ScreenSpaceEventType) { + 'use strict'; + + function getPickTileset(viewModel) { + return function(e) { + var pick = viewModel._scene.pick(e.position); + if (defined(pick) && pick.primitive instanceof Cesium3DTileset) { + viewModel.tileset = pick.primitive; + } + viewModel.pickActive = false; + }; + } + + var stringOptions = { + maximumFractionDigits : 3 + }; + + function formatMemoryString(memorySizeInBytes) { + var memoryInMegabytes = memorySizeInBytes / 1048576; + if (memoryInMegabytes < 1.0) { + return memoryInMegabytes.toLocaleString(undefined, stringOptions); + } + return Math.round(memoryInMegabytes).toLocaleString(); + } + + function getStatistics(tileset, isPick) { + if (!defined(tileset)) { + return ''; + } + + var statistics = tileset.statistics; + + // Since the pick pass uses a smaller frustum around the pixel of interest, + // the statistics will be different than the normal render pass. + var s = '
        '; + s += + // --- Rendering statistics + '
      • Visited: ' + statistics.visited.toLocaleString() + '
      • ' + + // Number of commands returned is likely to be higher than the number of tiles selected + // because of tiles that create multiple commands. + '
      • Selected: ' + tileset._selectedTiles.length.toLocaleString() + '
      • ' + + // Number of commands executed is likely to be higher because of commands overlapping + // multiple frustums. + '
      • Commands: ' + statistics.numberOfCommands.toLocaleString() + '
      • '; + s += '
      '; + if (!isPick) { + s += '
        '; + s += + // --- Cache/loading statistics + '
      • Requests: ' + statistics.numberOfPendingRequests.toLocaleString() + '
      • ' + + '
      • Attempted: ' + statistics.numberOfAttemptedRequests.toLocaleString() + '
      • ' + + '
      • Processing: ' + statistics.numberOfTilesProcessing.toLocaleString() + '
      • ' + + '
      • Content Ready: ' + statistics.numberOfTilesWithContentReady.toLocaleString() + '
      • ' + + // Total number of tiles includes tiles without content, so "Ready" may never reach + // "Total." Total also will increase when a tile with a tileset.json content is loaded. + '
      • Total: ' + statistics.numberOfTilesTotal.toLocaleString() + '
      • '; + s += '
      '; + s += '
        '; + s += + // --- Features statistics + '
      • Features Selected: ' + statistics.numberOfFeaturesSelected.toLocaleString() + '
      • ' + + '
      • Features Loaded: ' + statistics.numberOfFeaturesLoaded.toLocaleString() + '
      • ' + + '
      • Points Selected: ' + statistics.numberOfPointsSelected.toLocaleString() + '
      • ' + + '
      • Points Loaded: ' + statistics.numberOfPointsLoaded.toLocaleString() + '
      • ' + + '
      • Triangles Selected: ' + statistics.numberOfTrianglesSelected.toLocaleString() + '
      • '; + s += '
      '; + s += '
        '; + s += + // --- Styling statistics + '
      • Tiles styled: ' + statistics.numberOfTilesStyled.toLocaleString() + '
      • ' + + '
      • Features styled: ' + statistics.numberOfFeaturesStyled.toLocaleString() + '
      • '; + s += '
      '; + s += '
        '; + s += + // --- Optimization statistics + '
      • Children Union Culled: ' + statistics.numberOfTilesCulledWithChildrenUnion.toLocaleString() + '
      • '; + s += '
      '; + s += '
        '; + s += + // --- Memory statistics + '
      • Geometry Memory (MB): ' + formatMemoryString(statistics.geometryByteLength) + '
      • ' + + '
      • Texture Memory (MB): ' + formatMemoryString(statistics.texturesByteLength) + '
      • ' + + '
      • Batch Table Memory (MB): ' + formatMemoryString(statistics.batchTableByteLength) + '
      • '; + s += '
      '; + } + return s; + } + + var colorBlendModes = [{ + text : 'Highlight', + value : Cesium3DTileColorBlendMode.HIGHLIGHT + }, { + text : 'Replace', + value : Cesium3DTileColorBlendMode.REPLACE + }, { + text : 'Mix', + value : Cesium3DTileColorBlendMode.MIX + }]; + + var highlightColor = new Color(1.0, 1.0, 0.0, 0.4); + var scratchColor = new Color(); + var oldColor = new Color(); + + /** + * The view model for {@link Cesium3DTilesInspector}. + * @alias Cesium3DTilesInspectorViewModel + * @constructor + * + * @param {Scene} scene The scene instance to use. + * @param {HTMLElement} performanceContainer The container for the performance display + */ + function Cesium3DTilesInspectorViewModel(scene, performanceContainer) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('scene', scene); + Check.typeOf.object('performanceContainer', performanceContainer); + //>>includeEnd('debug'); + + var that = this; + var canvas = scene.canvas; + this._eventHandler = new ScreenSpaceEventHandler(canvas); + this._scene = scene; + this._performanceContainer = performanceContainer; + this._canvas = canvas; + + this._performanceDisplay = new PerformanceDisplay({ + container : performanceContainer + }); + + this._statisticsText = ''; + this._pickStatisticsText = ''; + this._editorError = ''; + + /** + * Gets or sets the flag to enable performance display. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.performance = false; + + /** + * Gets or sets the flag to show statistics. This property is observable. + * + * @type {Boolean} + * @default true + */ + this.showStatistics = true; + + /** + * Gets or sets the flag to show pick statistics. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showPickStatistics = true; + + /** + * Gets or sets the flag to show the inspector. This property is observable. + * + * @type {Boolean} + * @default true + */ + this.inspectorVisible = true; + + /** + * Gets or sets the flag to show the tileset section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.tilesetVisible = false; + + /** + * Gets or sets the flag to show the display section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.displayVisible = false; + + /** + * Gets or sets the flag to show the update section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.updateVisible = false; + + /** + * Gets or sets the flag to show the logging section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.loggingVisible = false; + + /** + * Gets or sets the flag to show the style section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.styleVisible = false; + + /** + * Gets or sets the flag to show the tile info section. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.tileDebugLabelsVisible = false; + + /** + * Gets or sets the flag to show the optimization info section. This property is observable. + * + * @type {Boolean} + * @default false; + */ + this.optimizationVisible = false; + + /** + * Gets or sets the JSON for the tileset style. This property is observable. + * + * @type {String} + * @default '{}' + */ + this.styleString = '{}'; + + this._tileset = undefined; + this._feature = undefined; + this._tile = undefined; + + knockout.track(this, ['performance', 'inspectorVisible', '_statisticsText', '_pickStatisticsText', '_editorError', 'showPickStatistics', 'showStatistics', + 'tilesetVisible', 'displayVisible', 'updateVisible', 'loggingVisible', 'styleVisible', 'optimizationVisible', + 'tileDebugLabelsVisible', 'styleString', '_feature', '_tile']); + + this._properties = knockout.observable({}); + /** + * Gets the names of the properties in the tileset. This property is observable. + * @type {String[]} + * @readonly + */ + this.properties = []; + knockout.defineProperty(this, 'properties', function() { + var names = []; + var properties = that._properties(); + for (var prop in properties) { + if (properties.hasOwnProperty(prop)) { + names.push(prop); + } + } + return names; + }); + + var dynamicScreenSpaceError = knockout.observable(); + knockout.defineProperty(this, 'dynamicScreenSpaceError', { + get : function() { + return dynamicScreenSpaceError(); + }, + set : function(value) { + dynamicScreenSpaceError(value); + if (defined(that._tileset)) { + that._tileset.dynamicScreenSpaceError = value; + } + } + }); + /** + * Gets or sets the flag to enable dynamic screen space error. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.dynamicScreenSpaceError = false; + + var colorBlendMode = knockout.observable(); + knockout.defineProperty(this, 'colorBlendMode', { + get : function() { + return colorBlendMode(); + }, + set : function(value) { + colorBlendMode(value); + if (defined(that._tileset)) { + that._tileset.colorBlendMode = value; + } + } + }); + /** + * Gets or sets the color blend mode. This property is observable. + * + * @type {Cesium3DTileColorBlendMode} + * @default Cesium3DTileColorBlendMode.HIGHLIGHT + */ + this.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT; + + var picking = knockout.observable(); + knockout.defineProperty(this, 'picking', { + get : function() { + return picking(); + }, + set : function(value) { + picking(value); + if (value) { + that._eventHandler.setInputAction(function(e) { + var picked = scene.pick(e.endPosition); + if (picked instanceof Cesium3DTileFeature) { + // Picked a feature + that.feature = picked; + that.tile = picked.content.tile; + } else if (defined(picked) && defined(picked.content)) { + // Picked a tile + that.feature = undefined; + that.tile = picked.content.tile; + } else { + // Picked nothing + that.feature = undefined; + that.tile = undefined; + } + if (!defined(that._tileset)) { + return; + } + if (showOnlyPickedTileDebugLabel && defined(picked) && defined(picked.content)) { //eslint-disable-line no-use-before-define + var position; + if (scene.pickPositionSupported) { + position = scene.pickPosition(e.endPosition); + if (defined(position)) { + that._tileset.debugPickPosition = position; + } + } + that._tileset.debugPickedTile = picked.content.tile; + } else { + that._tileset.debugPickedTile = undefined; + } + }, ScreenSpaceEventType.MOUSE_MOVE); + } else { + that.feature = undefined; + that.tile = undefined; + that._eventHandler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE); + } + } + }); + /** + * Gets or sets the flag to enable picking. This property is observable. + * + * @type {Boolean} + * @default true + */ + this.picking = true; + + var colorize = knockout.observable(); + knockout.defineProperty(this, 'colorize', { + get : function() { + return colorize(); + }, + set : function(value) { + colorize(value); + if (defined(that._tileset)) { + that._tileset.debugColorizeTiles = value; + } + } + }); + /** + * Gets or sets the flag to colorize tiles. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.colorize = false; + + var wireframe = knockout.observable(); + knockout.defineProperty(this, 'wireframe', { + get : function() { + return wireframe(); + }, + set : function(value) { + wireframe(value); + if (defined(that._tileset)) { + that._tileset.debugWireframe = value; + } + } + }); + /** + * Gets or sets the flag to draw with wireframe. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.wireframe = false; + + var showBoundingVolumes = knockout.observable(); + knockout.defineProperty(this, 'showBoundingVolumes', { + get : function() { + return showBoundingVolumes(); + }, + set : function(value) { + showBoundingVolumes(value); + if (defined(that._tileset)) { + that._tileset.debugShowBoundingVolume = value; + } + } + }); + /** + * Gets or sets the flag to show bounding volumes. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showBoundingVolumes = false; + + var showContentBoundingVolumes = knockout.observable(); + knockout.defineProperty(this, 'showContentBoundingVolumes', { + get : function() { + return showContentBoundingVolumes(); + }, + set : function(value) { + showContentBoundingVolumes(value); + if (defined(that._tileset)) { + that._tileset.debugShowContentBoundingVolume = value; + } + } + }); + /** + * Gets or sets the flag to show content volumes. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showContentBoundingVolumes = false; + + var showRequestVolumes = knockout.observable(); + knockout.defineProperty(this, 'showRequestVolumes', { + get : function() { + return showRequestVolumes(); + }, + set : function(value) { + showRequestVolumes(value); + if (defined(that._tileset)) { + that._tileset.debugShowViewerRequestVolume = value; + } + } + }); + /** + * Gets or sets the flag to show request volumes. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showRequestVolumes = false; + + var freezeFrame = knockout.observable(); + knockout.defineProperty(this, 'freezeFrame', { + get : function() { + return freezeFrame(); + }, + set : function(value) { + freezeFrame(value); + if (defined(that._tileset)) { + that._tileset.debugFreezeFrame = value; + that._scene.debugShowFrustumPlanes = value; + } + } + }); + /** + * Gets or sets the flag to suspend updates. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.freezeFrame = false; + + var showOnlyPickedTileDebugLabel = knockout.observable(); + knockout.defineProperty(this, 'showOnlyPickedTileDebugLabel', { + get : function() { + return showOnlyPickedTileDebugLabel(); + }, + set : function(value) { + showOnlyPickedTileDebugLabel(value); + if (defined(that._tileset)) { + that._tileset.debugPickedTileLabelOnly = value; + } + } + }); + /** + * Gets or sets the flag to show debug labels only for the currently picked tile. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showOnlyPickedTileDebugLabel = false; + + var showGeometricError = knockout.observable(); + knockout.defineProperty(this, 'showGeometricError', { + get : function() { + return showGeometricError(); + }, + set : function(value) { + showGeometricError(value); + if (defined(that._tileset)) { + that._tileset.debugShowGeometricError = value; + } + } + }); + /** + * Gets or sets the flag to show tile geometric error. This property is observable. + * + * @type {Boolean} + * @default false + */ + this.showGeometricError = false; + + var showRenderingStatistics = knockout.observable(); + knockout.defineProperty(this, 'showRenderingStatistics', { + get : function() { + return showRenderingStatistics(); + }, + set : function(value) { + showRenderingStatistics(value); + if (defined(that._tileset)) { + that._tileset.debugShowRenderingStatistics = value; + } + } + }); + /** + * Displays the number of commands, points, triangles and features used per tile. This property is observable. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * + * @type {Boolean} + * @default false + */ + this.showRenderingStatistics = false; + + var showMemoryUsage = knockout.observable(); + knockout.defineProperty(this, 'showMemoryUsage', { + get : function() { + return showMemoryUsage(); + }, + set : function(value) { + showMemoryUsage(value); + if (defined(that._tileset)) { + that._tileset.debugShowMemoryUsage = value; + } + } + }); + /** + * Displays the memory used per tile. This property is observable. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * + * @type {Boolean} + * @default false + */ + this.showMemoryUsage = false; + + var maximumScreenSpaceError = knockout.observable(); + knockout.defineProperty(this, 'maximumScreenSpaceError', { + get : function() { + return maximumScreenSpaceError(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + maximumScreenSpaceError(value); + if (defined(that._tileset)) { + that._tileset.maximumScreenSpaceError = value; + } + } + } + }); + /** + * Gets or sets the maximum screen space error. This property is observable. + * + * @type {Number} + * @default 16 + */ + this.maximumScreenSpaceError = 16; + + var dynamicScreenSpaceErrorDensity = knockout.observable(); + knockout.defineProperty(this, 'dynamicScreenSpaceErrorDensity', { + get : function() { + return dynamicScreenSpaceErrorDensity(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + dynamicScreenSpaceErrorDensity(value); + if (defined(that._tileset)) { + that._tileset.dynamicScreenSpaceErrorDensity = value; + } + } + } + }); + /** + * Gets or sets the dynamic screen space error density. This property is observable. + * + * @type {Number} + * @default 0.00278 + */ + this.dynamicScreenSpaceErrorDensity = 0.00278; + + /** + * Gets or sets the dynamic screen space error density slider value. + * This allows the slider to be exponential because values tend to be closer to 0 than 1. + * This property is observable. + * + * @type {Number} + * @default 0.00278 + */ + this.dynamicScreenSpaceErrorDensitySliderValue = undefined; + knockout.defineProperty(this, 'dynamicScreenSpaceErrorDensitySliderValue', { + get : function() { + return Math.pow(dynamicScreenSpaceErrorDensity(), 1 / 6); + }, + set : function(value) { + dynamicScreenSpaceErrorDensity(Math.pow(value, 6)); + } + }); + + var dynamicScreenSpaceErrorFactor = knockout.observable(); + knockout.defineProperty(this, 'dynamicScreenSpaceErrorFactor', { + get : function() { + return dynamicScreenSpaceErrorFactor(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + dynamicScreenSpaceErrorFactor(value); + if (defined(that._tileset)) { + that._tileset.dynamicScreenSpaceErrorFactor = value; + } + } + } + }); + /** + * Gets or sets the dynamic screen space error factor. This property is observable. + * + * @type {Number} + * @default 4.0 + */ + this.dynamicScreenSpaceErrorFactor = 4.0; + + var pickTileset = getPickTileset(this); + var pickActive = knockout.observable(); + knockout.defineProperty(this, 'pickActive', { + get : function() { + return pickActive(); + }, + set : function(value) { + pickActive(value); + if (value) { + that._eventHandler.setInputAction(pickTileset, ScreenSpaceEventType.LEFT_CLICK); + } else { + that._eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK); + } + } + }); + /** + * Gets or sets the pick state + * + * @type {Boolean} + * @default false + */ + this.pickActive = false; + + var skipLevelOfDetail = knockout.observable(); + knockout.defineProperty(this, 'skipLevelOfDetail', { + get : function() { + return skipLevelOfDetail(); + }, + set : function(value) { + skipLevelOfDetail(value); + if (defined(that._tileset)) { + that._tileset.skipLevelOfDetail = value; + } + } + }); + /** + * Gets or sets the flag to determine if level of detail skipping should be applied during the traversal. + * This property is observable. + * @type {Boolean} + * @default true + */ + this.skipLevelOfDetail = true; + + var skipScreenSpaceErrorFactor = knockout.observable(); + knockout.defineProperty(this, 'skipScreenSpaceErrorFactor', { + get : function() { + return skipScreenSpaceErrorFactor(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + skipScreenSpaceErrorFactor(value); + if (defined(that._tileset)) { + that._tileset.skipScreenSpaceErrorFactor = value; + } + } + } + }); + /** + * Gets or sets the multiplier defining the minimum screen space error to skip. This property is observable. + * @type {Number} + * @default 16 + */ + this.skipScreenSpaceErrorFactor = 16; + + var baseScreenSpaceError = knockout.observable(); + knockout.defineProperty(this, 'baseScreenSpaceError', { + get : function() { + return baseScreenSpaceError(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + baseScreenSpaceError(value); + if (defined(that._tileset)) { + that._tileset.baseScreenSpaceError = value; + } + } + } + }); + /** + * Gets or sets the screen space error that must be reached before skipping levels of detail. This property is observable. + * @type {Number} + * @default 1024 + */ + this.baseScreenSpaceError = 1024; + + var skipLevels = knockout.observable(); + knockout.defineProperty(this, 'skipLevels', { + get : function() { + return skipLevels(); + }, + set : function(value) { + value = Number(value); + if (!isNaN(value)) { + skipLevels(value); + if (defined(that._tileset)) { + that._tileset.skipLevels = value; + } + } + } + }); + /** + * Gets or sets the constant defining the minimum number of levels to skip when loading tiles. This property is observable. + * @type {Number} + * @default 1 + */ + this.skipLevels = 1; + + var immediatelyLoadDesiredLevelOfDetail = knockout.observable(); + knockout.defineProperty(this, 'immediatelyLoadDesiredLevelOfDetail', { + get : function() { + return immediatelyLoadDesiredLevelOfDetail(); + }, + set : function(value) { + immediatelyLoadDesiredLevelOfDetail(value); + if (defined(that._tileset)) { + that._tileset.immediatelyLoadDesiredLevelOfDetail = value; + } + } + }); + /** + * Gets or sets the flag which, when true, only tiles that meet the maximum screen space error will ever be downloaded. + * This property is observable. + * @type {Boolean} + * @default false + */ + this.immediatelyLoadDesiredLevelOfDetail = false; + + var loadSiblings = knockout.observable(); + knockout.defineProperty(this, 'loadSiblings', { + get : function() { + loadSiblings(); + }, + set : function(value) { + loadSiblings(value); + if (defined(that._tileset)) { + that._tileset.loadSiblings = value; + } + } + }); + /** + * Gets or sets the flag which determines whether siblings of visible tiles are always downloaded during traversal. + * This property is observable + * @type {Boolean} + * @default false + */ + this.loadSiblings = false; + + this._style = undefined; + this._shouldStyle = false; + this._definedProperties = ['properties', 'dynamicScreenSpaceError', 'colorBlendMode', 'picking', 'colorize', 'wireframe', 'showBoundingVolumes', + 'showContentBoundingVolumes', 'showRequestVolumes', 'freezeFrame', 'maximumScreenSpaceError', 'dynamicScreenSpaceErrorDensity', 'baseScreenSpaceError', + 'skipScreenSpaceErrorFactor', 'skipLevelOfDetail', 'skipLevels', 'immediatelyLoadDesiredLevelOfDetail', 'loadSiblings', 'dynamicScreenSpaceErrorDensitySliderValue', + 'dynamicScreenSpaceErrorFactor', 'pickActive', 'showOnlyPickedTileDebugLabel', 'showGeometricError', 'showRenderingStatistics', 'showMemoryUsage']; + this._removePostRenderEvent = scene.postRender.addEventListener(function() { + that._update(); + }); + } + + defineProperties(Cesium3DTilesInspectorViewModel.prototype, { + /** + * Gets the scene + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {Scene} + * @readonly + */ + scene: { + get: function() { + return this._scene; + } + }, + /** + * Gets the performance container + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {HTMLElement} + * @readonly + */ + performanceContainer: { + get: function() { + return this._performanceContainer; + } + }, + + /** + * Gets the statistics text. This property is observable. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {String} + * @readonly + */ + statisticsText : { + get : function() { + return this._statisticsText; + } + }, + /** + * Gets the pick statistics text. This property is observable. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {String} + * @readonly + */ + pickStatisticsText : { + get : function() { + return this._pickStatisticsText; + } + }, + + /** + * Gets the available blend modes + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {Object[]} + * @readonly + */ + colorBlendModes : { + get : function() { + return colorBlendModes; + } + }, + + /** + * Gets the editor error message + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {String} + * @readonly + */ + editorError : { + get : function() { + return this._editorError; + } + }, + + /** + * Gets or sets the tileset of the view model. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {Cesium3DTileset} + */ + tileset : { + get : function() { + return this._tileset; + }, + set : function(tileset) { + this._tileset = tileset; + this._style = undefined; + this.styleString = '{}'; + this.feature = undefined; + this.tile = undefined; + + if (defined(tileset)) { + var that = this; + tileset.readyPromise.then(function(t) { + if (!that.isDestroyed()) { + that._properties(t.properties); + } + }); + + // update tileset with existing settings + var settings = ['colorize', + 'wireframe', + 'showBoundingVolumes', + 'showContentBoundingVolumes', + 'showRequestVolumes', + 'freezeFrame', + 'showOnlyPickedTileDebugLabel', + 'showGeometricError', + 'showRenderingStatistics', + 'showMemoryUsage']; + var length = settings.length; + for (var i = 0; i < length; ++i) { + var setting = settings[i]; + this[setting] = this[setting]; + } + + // update view model with existing tileset settings + this.maximumScreenSpaceError = tileset.maximumScreenSpaceError; + this.dynamicScreenSpaceError = tileset.dynamicScreenSpaceError; + this.dynamicScreenSpaceErrorDensity = tileset.dynamicScreenSpaceErrorDensity; + this.dynamicScreenSpaceErrorFactor = tileset.dynamicScreenSpaceErrorFactor; + this.colorBlendMode = tileset.colorBlendMode; + this.skipLevelOfDetail = tileset.skipLevelOfDetail; + this.skipScreenSpaceErrorFactor = tileset.skipScreenSpaceErrorFactor; + this.baseScreenSpaceError = tileset.baseScreenSpaceError; + this.skipLevels = tileset.skipLevels; + this.immediatelyLoadDesiredLevelOfDetail = tileset.immediatelyLoadDesiredLevelOfDetail; + this.loadSiblings = tileset.loadSiblings; + } else { + this._properties({}); + } + + this._statisticsText = getStatistics(tileset, false); + this._pickStatisticsText = getStatistics(tileset, true); + } + }, + + /** + * Gets the current feature of the view model. + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {Cesium3DTileFeature} + */ + feature : { + get : function() { + return this._feature; + }, + set : function(feature) { + if (this._feature === feature) { + return; + } + var currentFeature = this._feature; + if (defined(currentFeature)) { + // Restore original color to feature that is no longer selected + var frameState = this._scene.frameState; + if (!this.colorize && defined(this._style)) { + currentFeature.color = this._style.color.evaluateColor(frameState, currentFeature, scratchColor); + } else { + currentFeature.color = oldColor; + } + } + if (defined(feature)) { + // Highlight new feature + Color.clone(feature.color, oldColor); + feature.color = highlightColor; + } + this._feature = feature; + } + }, + + /** + * Gets the current tile of the view model + * @memberof Cesium3DTilesInspectorViewModel.prototype + * @type {Cesium3DTile} + */ + tile : { + get : function() { + return this._tile; + }, + set : function(tile) { + if (this._tile === tile) { + return; + } + var currentTile = this._tile; + + if (defined(currentTile) && !hasFeatures(currentTile.content)) { + // Restore original color to tile that is no longer selected + currentTile.color = oldColor; + } + + if (defined(tile) && !hasFeatures(tile.content)) { + // Highlight new tile + Color.clone(tile.color, oldColor); + tile.color = highlightColor; + } + this._tile = tile; + } + } + }); + + function hasFeatures(content) { + if (content.featuresLength > 0) { + return true; + } + var innerContents = content.innerContents; + if (defined(innerContents)) { + var length = innerContents.length; + for (var i = 0; i < length; ++i) { + if (!hasFeatures(innerContents[i])) { + return false; + } + } + return true; + } + return false; + } + + /** + * Toggles the pick tileset mode + */ + Cesium3DTilesInspectorViewModel.prototype.togglePickTileset = function() { + this.pickActive = !this.pickActive; + }; + + /** + * Toggles the inspector visibility + */ + Cesium3DTilesInspectorViewModel.prototype.toggleInspector = function() { + this.inspectorVisible = !this.inspectorVisible; + }; + + /** + * Toggles the visibility of the tileset section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleTileset = function() { + this.tilesetVisible = !this.tilesetVisible; + }; + + /** + * Toggles the visibility of the display section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleDisplay = function() { + this.displayVisible = !this.displayVisible; + }; + + /** + * Toggles the visibility of the update section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleUpdate = function() { + this.updateVisible = !this.updateVisible; + }; + + /** + * Toggles the visibility of the logging section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleLogging = function() { + this.loggingVisible = !this.loggingVisible; + }; + + /** + * Toggles the visibility of the style section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleStyle = function() { + this.styleVisible = !this.styleVisible; + }; + + /** + * Toggles the visibility of the tile Debug Info section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleTileDebugLabels = function() { + this.tileDebugLabelsVisible = !this.tileDebugLabelsVisible; + }; + + /** + * Toggles the visibility of the optimization section + */ + Cesium3DTilesInspectorViewModel.prototype.toggleOptimization = function() { + this.optimizationVisible = !this.optimizationVisible; + }; + + /** + * Trims tile cache + */ + Cesium3DTilesInspectorViewModel.prototype.trimTilesCache = function() { + if (defined(this._tileset)) { + this._tileset.trimLoadedTiles(); + } + }; + + /** + * Compiles the style in the style editor. + */ + Cesium3DTilesInspectorViewModel.prototype.compileStyle = function() { + var tileset = this._tileset; + if (!defined(tileset) || this.styleString === JSON.stringify(tileset.style)) { + return; + } + this._editorError = ''; + try { + if (this.styleString.length === 0) { + this.styleString = '{}'; + } + this._style = new Cesium3DTileStyle(JSON.parse(this.styleString)); + this._shouldStyle = true; + } catch (err) { + this._editorError = err.toString(); + } + + // set feature again so pick coloring is set + this.feature = this._feature; + this.tile = this._tile; + }; + + /** + * Handles key press events on the style editor. + */ + Cesium3DTilesInspectorViewModel.prototype.styleEditorKeyPress = function(sender, event) { + if (event.keyCode === 9) { //tab + event.preventDefault(); + var textArea = event.target; + var start = textArea.selectionStart; + var end = textArea.selectionEnd; + var newEnd = end; + var selected = textArea.value.slice(start, end); + var lines = selected.split('\n'); + var length = lines.length; + var i; + if (!event.shiftKey) { + for (i = 0; i < length; ++i) { + lines[i] = ' ' + lines[i]; + newEnd += 2; + } + } else { + for (i = 0; i < length; ++i) { + if (lines[i][0] === ' ') { + if (lines[i][1] === ' ') { + lines[i] = lines[i].substr(2); + newEnd -= 2; + } else { + lines[i] = lines[i].substr(1); + newEnd -= 1; + } + } + } + } + var newText = lines.join('\n'); + textArea.value = textArea.value.slice(0, start) + newText + textArea.value.slice(end); + textArea.selectionStart = start !== end ? start : newEnd; + textArea.selectionEnd = newEnd; + } else if (event.ctrlKey && (event.keyCode === 10 || event.keyCode === 13)) { //ctrl + enter + this.compileStyle(); + } + return true; + }; + + /** + * Updates the values of view model + * @private + */ + Cesium3DTilesInspectorViewModel.prototype._update = function() { + var tileset = this._tileset; + + if (this.performance) { + this._performanceDisplay.update(); + } + + if (defined(tileset)) { + var style = tileset.style; + if (this._style !== tileset.style) { + if (this._shouldStyle) { + tileset.style = this._style; + this._shouldStyle = false; + } else { + this._style = style; + this.styleString = JSON.stringify(style.style, null, ' '); + } + } + } + if (this.showStatistics) { + this._statisticsText = getStatistics(tileset, false); + this._pickStatisticsText = getStatistics(tileset, true); + } + }; + + /** + * @returns {Boolean} true if the object has been destroyed, false otherwise. + */ + Cesium3DTilesInspectorViewModel.prototype.isDestroyed = function() { + return false; + }; + + /** + * Destroys the widget. Should be called if permanently + * removing the widget from layout. + */ + Cesium3DTilesInspectorViewModel.prototype.destroy = function() { + this._eventHandler.destroy(); + this._removePostRenderEvent(); + + var that = this; + this._definedProperties.forEach(function(property) { + knockout.getObservable(that, property).dispose(); + }); + + return destroyObject(this); + }; + + /** + * Generates an HTML string of the statistics + * @param {Cesium3DTileset} tileset The tileset + * @param {Boolean} isPick Whether this is getting the statistics for the pick pass + * @returns {String} The formatted statistics + */ + Cesium3DTilesInspectorViewModel.getStatistics = getStatistics; + + return Cesium3DTilesInspectorViewModel; +}); diff --git a/Source/Widgets/CesiumInspector/CesiumInspector.css b/Source/Widgets/CesiumInspector/CesiumInspector.css index 77b3da5ee70e..89b8b66ee65e 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspector.css +++ b/Source/Widgets/CesiumInspector/CesiumInspector.css @@ -1,7 +1,5 @@ .cesium-cesiumInspector { border-radius: 5px; - -webkit-transition: width ease-in-out 0.25s; - -moz-transition: width ease-in-out 0.25s; transition: width ease-in-out 0.25s; background: rgba(48, 51, 54, 0.8); border: 1px solid #444; @@ -57,7 +55,7 @@ width: 185px; } -.cesium-cesiumInspector-frustumStats { +.cesium-cesiumInspector-frustumStatistics { padding-left: 10px; padding: 5px; background-color: rgba(80, 80, 80, 0.75); @@ -105,8 +103,6 @@ .cesium-cesiumInspector-section { margin-bottom: 10px; - -webkit-transition: max-height 0.25s; - -moz-transition: max-height 0.25s; transition: max-height 0.25s; } diff --git a/Source/Widgets/CesiumInspector/CesiumInspector.js b/Source/Widgets/CesiumInspector/CesiumInspector.js index ec0864d7daf6..d9dd5d9d360d 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspector.js +++ b/Source/Widgets/CesiumInspector/CesiumInspector.js @@ -26,9 +26,6 @@ define([ * @param {Element|String} container The DOM element or ID that will contain the widget. * @param {Scene} scene The Scene instance to use. * - * @exception {DeveloperError} container is required. - * @exception {DeveloperError} scene is required. - * * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Cesium%20Inspector.html|Cesium Sandcastle Cesium Inspector Demo} */ function CesiumInspector(container, scene) { @@ -83,15 +80,15 @@ define([ var debugShowFrustums = document.createElement('div'); generalSection.appendChild(debugShowFrustums); - var frustumStats = document.createElement('div'); - frustumStats.className = 'cesium-cesiumInspector-frustumStats'; - frustumStats.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : frustums, "cesium-cesiumInspector-hide" : !frustums}, html: frustumStatisticText'); + var frustumStatistics = document.createElement('div'); + frustumStatistics.className = 'cesium-cesiumInspector-frustumStatistics'; + frustumStatistics.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : frustums, "cesium-cesiumInspector-hide" : !frustums}, html: frustumStatisticText'); var frustumsCheckbox = document.createElement('input'); frustumsCheckbox.type = 'checkbox'; frustumsCheckbox.setAttribute('data-bind', 'checked: frustums'); debugShowFrustums.appendChild(frustumsCheckbox); debugShowFrustums.appendChild(document.createTextNode('Show Frustums')); - debugShowFrustums.appendChild(frustumStats); + debugShowFrustums.appendChild(frustumStatistics); var debugShowFrustumPlanes = document.createElement('div'); generalSection.appendChild(debugShowFrustumPlanes); @@ -271,7 +268,7 @@ define([ var tileText = document.createElement('div'); tileText.className = 'cesium-cesiumInspector-tileText'; - tileInfo.className = 'cesium-cesiumInspector-frustumStats'; + tileInfo.className = 'cesium-cesiumInspector-frustumStatistics'; tileInfo.appendChild(tileText); tileInfo.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : hasPickedTile, "cesium-cesiumInspector-hide" : !hasPickedTile}'); tileText.setAttribute('data-bind', 'html: tileText'); diff --git a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js index e1929395ae64..7082697ef435 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js @@ -27,11 +27,11 @@ define([ createCommand) { 'use strict'; - function frustumStatsToString(stats) { + function frustumStatisticsToString(statistics) { var str; - if (defined(stats)) { + if (defined(statistics)) { str = 'Command Statistics'; - var com = stats.commandsInFrustums; + var com = statistics.commandsInFrustums; for (var n in com) { if (com.hasOwnProperty(n)) { var num = parseInt(n, 10); @@ -52,7 +52,7 @@ define([ str += '
          ' + com[n] + ' in frustum ' + s; } } - str += '
      Total: ' + stats.totalCommands; + str += '
      Total: ' + statistics.totalCommands; } return str; @@ -71,8 +71,6 @@ define([ * * @param {Scene} scene The scene instance to use. * @param {PerformanceContainer} performanceContainer The instance to use for performance container. - * - * @exception {DeveloperError} scene is required. */ function CesiumInspectorViewModel(scene, performanceContainer) { //>>includeStart('debug', pragmas.debug); @@ -570,6 +568,10 @@ define([ eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK); } }); + + this._removePostRenderEvent = scene.postRender.addEventListener(function() { + that._update(); + }); } defineProperties(CesiumInspectorViewModel.prototype, { @@ -850,6 +852,9 @@ define([ * @type {Command} */ primitive : { + get : function() { + return this._primitive; + }, set : function(newPrimitive) { var oldPrimitive = this._primitive; if (newPrimitive !== oldPrimitive) { @@ -871,10 +876,6 @@ define([ this.showPrimitiveReferenceFrame(); this.doFilterPrimitive(); } - }, - - get : function() { - return this._primitive; } }, @@ -885,6 +886,9 @@ define([ * @type {Command} */ tile : { + get : function() { + return this._tile; + }, set : function(newTile) { if (defined(newTile)) { this.hasPickedTile = true; @@ -907,41 +911,36 @@ define([ this.hasPickedTile = false; this._tile = undefined; } - }, - - get : function() { - return this._tile; } - }, + } + }); - update : { - get : function() { - var that = this; - return function() { - if (that.frustums) { - that.frustumStatisticText = frustumStatsToString(that._scene.debugFrustumStatistics); - } + /** + * Updates the view model + * @private + */ + CesiumInspectorViewModel.prototype._update = function() { + if (this.frustums) { + this.frustumStatisticText = frustumStatisticsToString(this._scene.debugFrustumStatistics); + } - // Determine the number of frustums being used. - var numberOfFrustums = that._scene.numberOfFrustums; - that._numberOfFrustums = numberOfFrustums; - // Bound the frustum to be displayed. - that.depthFrustum = boundDepthFrustum(1, numberOfFrustums, that.depthFrustum); - // Update the displayed text. - that.depthFrustumText = that.depthFrustum + ' of ' + numberOfFrustums; + // Determine the number of frustums being used. + var numberOfFrustums = this._scene.numberOfFrustums; + this._numberOfFrustums = numberOfFrustums; + // Bound the frustum to be displayed. + this.depthFrustum = boundDepthFrustum(1, numberOfFrustums, this.depthFrustum); + // Update the displayed text. + this.depthFrustumText = this.depthFrustum + ' of ' + numberOfFrustums; - if (that.performance) { - that._performanceDisplay.update(); - } - if (that.primitiveReferenceFrame) { - that._modelMatrixPrimitive.modelMatrix = that._primitive.modelMatrix; - } - - that.shaderCacheText = 'Cached shaders: ' + that._scene.context.shaderCache.numberOfShaders; - }; - } + if (this.performance) { + this._performanceDisplay.update(); } - }); + if (this.primitiveReferenceFrame) { + this._modelMatrixPrimitive.modelMatrix = this._primitive.modelMatrix; + } + + this.shaderCacheText = 'Cached shaders: ' + this._scene.context.shaderCache.numberOfShaders; + }; /** * @returns {Boolean} true if the object has been destroyed, false otherwise. @@ -956,6 +955,7 @@ define([ */ CesiumInspectorViewModel.prototype.destroy = function() { this._eventHandler.destroy(); + this._removePostRenderEvent(); this._frustumsSubscription.dispose(); this._frustumPlanesSubscription.dispose(); this._performanceSubscription.dispose(); diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js index df22bdfa1342..8322cc4b9c5a 100644 --- a/Source/Widgets/CesiumWidget/CesiumWidget.js +++ b/Source/Widgets/CesiumWidget/CesiumWidget.js @@ -180,7 +180,7 @@ define([ * var widget = new Cesium.CesiumWidget('cesiumContainer', { * imageryProvider : Cesium.createOpenStreetMapImageryProvider(), * terrainProvider : new Cesium.CesiumTerrainProvider({ - * url : 'https://assets.agi.com/stk-terrain/world' + * url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' * }), * // Use high-res stars downloaded from https://github.com/AnalyticalGraphicsInc/cesium-assets * skyBox : new Cesium.SkyBox({ diff --git a/Source/Widgets/Geocoder/Geocoder.css b/Source/Widgets/Geocoder/Geocoder.css index 244e41de2c8d..3e94d84c1bef 100644 --- a/Source/Widgets/Geocoder/Geocoder.css +++ b/Source/Widgets/Geocoder/Geocoder.css @@ -9,11 +9,7 @@ margin: 0; padding: 0 32px 0 0; border-radius: 0; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; - -webkit-transition: width ease-in-out 0.25s, background-color 0.2s ease-in-out; - -moz-transition: width ease-in-out 0.25s, background-color 0.2s ease-in-out; transition: width ease-in-out 0.25s, background-color 0.2s ease-in-out; -webkit-appearance: none; } diff --git a/Source/Widgets/InfoBox/InfoBox.css b/Source/Widgets/InfoBox/InfoBox.css index 1ccf4389a853..61b8af1c476f 100644 --- a/Source/Widgets/InfoBox/InfoBox.css +++ b/Source/Widgets/InfoBox/InfoBox.css @@ -12,24 +12,16 @@ border-top-left-radius: 7px; border-bottom-left-radius: 7px; box-shadow: 0 0 10px 1px #000; - -webkit-transform: translate(100%, 0); - -moz-transform: translate(100%, 0); transform: translate(100%, 0); visibility: hidden; opacity: 0; - -webkit-transition: visibility 0s 0.2s, opacity 0.2s ease-in, -webkit-transform 0.2s ease-in; - -moz-transition: visibility 0s 0.2s, opacity 0.2s ease-in, -moz-transform 0.2s ease-in; transition: visibility 0s 0.2s, opacity 0.2s ease-in, transform 0.2s ease-in; } .cesium-infoBox-visible { - -webkit-transform: translate(0, 0); - -moz-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; opacity: 1; - -webkit-transition: opacity 0.2s ease-out, -webkit-transform 0.2s ease-out; - -moz-transition: opacity 0.2s ease-out, -moz-transform 0.2s ease-out; transition: opacity 0.2s ease-out, transform 0.2s ease-out; } @@ -43,8 +35,6 @@ text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; box-sizing: content-box; } diff --git a/Source/Widgets/NavigationHelpButton/NavigationHelpButton.css b/Source/Widgets/NavigationHelpButton/NavigationHelpButton.css index 9dbeebbbe2f2..0a40ef911d23 100644 --- a/Source/Widgets/NavigationHelpButton/NavigationHelpButton.css +++ b/Source/Widgets/NavigationHelpButton/NavigationHelpButton.css @@ -10,24 +10,14 @@ right: 2px; width: 250px; border-radius: 10px; - -webkit-transform: scale(0.01); - -moz-transform: scale(0.01); transform: scale(0.01); - -webkit-transform-origin: 234px -10px; - -moz-transform-origin: 234px -10px; transform-origin: 234px -10px; - -webkit-transition: visibility 0s 0.25s, -webkit-transform 0.25s ease-in; - -moz-transition: visibility 0s 0.25s, -moz-transform 0.25s ease-in; transition: visibility 0s 0.25s, transform 0.25s ease-in; } .cesium-navigation-help-visible { visibility: visible; - -webkit-transform: scale(1); - -moz-transform: scale(1); transform: scale(1); - -webkit-transition: -webkit-transform 0.25s ease-out; - -moz-transition: -moz-transform 0.25s ease-out; transition: transform 0.25s ease-out; } diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css index ec2ceb0c20e3..3dc90a848023 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.css +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css @@ -8,16 +8,12 @@ span.cesium-projectionPicker-wrapper { visibility: visible; opacity: 1; transition: opacity 0.25s linear; - -webkit-transition: opacity 0.25s linear; - -moz-transition: opacity 0.25s linear; } .cesium-projectionPicker-hidden { visibility: hidden; opacity: 0; transition: visibility 0s 0.25s, opacity 0.25s linear; - -webkit-transition: visibility 0s 0.25s, opacity 0.25s linear; - -moz-transition: visibility 0s 0.25s, opacity 0.25s linear; } .cesium-projectionPicker-wrapper .cesium-projectionPicker-none { @@ -25,8 +21,6 @@ span.cesium-projectionPicker-wrapper { } .cesium-projectionPicker-wrapper .cesium-projectionPicker-dropDown-icon { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; padding: 0; margin: 3px 0; diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index 31d13f044631..47efdeb637a2 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -1,35 +1,21 @@ /*global define*/ define([ - '../../Core/Cartesian2', - '../../Core/Cartesian3', - '../../Core/defaultValue', '../../Core/defined', '../../Core/defineProperties', '../../Core/destroyObject', '../../Core/DeveloperError', '../../Core/EventHelper', - '../../Core/Math', - '../../Core/Matrix4', - '../../Core/Ray', '../../Scene/OrthographicFrustum', - '../../Scene/PerspectiveFrustum', '../../Scene/SceneMode', '../../ThirdParty/knockout', '../createCommand' ], function( - Cartesian2, - Cartesian3, - defaultValue, defined, defineProperties, destroyObject, DeveloperError, EventHelper, - CesiumMath, - Matrix4, - Ray, OrthographicFrustum, - PerspectiveFrustum, SceneMode, knockout, createCommand) { diff --git a/Source/Widgets/SceneModePicker/SceneModePicker.css b/Source/Widgets/SceneModePicker/SceneModePicker.css index dd9d2a79e04e..400e9ef88e2a 100644 --- a/Source/Widgets/SceneModePicker/SceneModePicker.css +++ b/Source/Widgets/SceneModePicker/SceneModePicker.css @@ -8,16 +8,12 @@ span.cesium-sceneModePicker-wrapper { visibility: visible; opacity: 1; transition: opacity 0.25s linear; - -webkit-transition: opacity 0.25s linear; - -moz-transition: opacity 0.25s linear; } .cesium-sceneModePicker-hidden { visibility: hidden; opacity: 0; transition: visibility 0s 0.25s, opacity 0.25s linear; - -webkit-transition: visibility 0s 0.25s, opacity 0.25s linear; - -moz-transition: visibility 0s 0.25s, opacity 0.25s linear; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-none { @@ -25,16 +21,12 @@ span.cesium-sceneModePicker-wrapper { } .cesium-sceneModePicker-slide-svg { - -webkit-transition: left 2s; - -moz-transition: left 2s; transition: left 2s; top: 0; left: 0; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; padding: 0; margin: 3px 0; diff --git a/Source/Widgets/SelectionIndicator/SelectionIndicator.css b/Source/Widgets/SelectionIndicator/SelectionIndicator.css index de9c10a5fe23..83dbda4500a4 100644 --- a/Source/Widgets/SelectionIndicator/SelectionIndicator.css +++ b/Source/Widgets/SelectionIndicator/SelectionIndicator.css @@ -5,16 +5,12 @@ pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 0s 0.2s, opacity 0.2s ease-in; - -moz-transition: visibility 0s 0.2s, opacity 0.2s ease-in; transition: visibility 0s 0.2s, opacity 0.2s ease-in; } .cesium-selection-wrapper-visible { visibility: visible; opacity: 1; - -webkit-transition: opacity 0.2s ease-out; - -moz-transition: opacity 0.2s ease-out; transition: opacity 0.2s ease-out; } diff --git a/Source/Widgets/Timeline/Timeline.css b/Source/Widgets/Timeline/Timeline.css index ba10266ce25b..2ef7ead88d6b 100644 --- a/Source/Widgets/Timeline/Timeline.css +++ b/Source/Widgets/Timeline/Timeline.css @@ -39,11 +39,7 @@ cursor: pointer; width: 100%; height: 1.7em; - background-color: #fafafa; - background: rgba(32, 32, 32, 0.8); - background: -moz-linear-gradient(top, rgba(116,117,119,0.8) 0%, rgba(58,68,82,0.8) 11%, rgba(46,50,56,0.8) 46%, rgba(53,53,53,0.8) 81%, rgba(53,53,53,0.8) 100%); /* FF3.6+ */ - background: -webkit-linear-gradient(top, rgba(116,117,119,0.8) 0%,rgba(58,68,82,0.8) 11%,rgba(46,50,56,0.8) 46%,rgba(53,53,53,0.8) 81%,rgba(53,53,53,0.8) 100%); /* Chrome10+,Safari5.1+ */ - background: linear-gradient(to bottom, rgba(116,117,119,0.8) 0%,rgba(58,68,82,0.8) 11%,rgba(46,50,56,0.8) 46%,rgba(53,53,53,0.8) 81%,rgba(53,53,53,0.8) 100%); /* W3C */ + background: linear-gradient(to bottom, rgba(116,117,119,0.8) 0%,rgba(58,68,82,0.8) 11%,rgba(46,50,56,0.8) 46%,rgba(53,53,53,0.8) 81%,rgba(53,53,53,0.8) 100%); } .cesium-timeline-ruler { diff --git a/Source/Widgets/Timeline/lighter.css b/Source/Widgets/Timeline/lighter.css index c33c0d47adf9..89884700223d 100644 --- a/Source/Widgets/Timeline/lighter.css +++ b/Source/Widgets/Timeline/lighter.css @@ -1,7 +1,5 @@ .cesium-lighter .cesium-timeline-bar { - background: -moz-linear-gradient(top, #eeeeee 0%, #ffffff 50%, #fafafa 100%); /* FF3.6+ */ - background: -webkit-linear-gradient(top, #eeeeee 0%,#ffffff 50%,#fafafa 100%); /* Chrome10+,Safari5.1+ */ - background: linear-gradient(to bottom, #eeeeee 0%,#ffffff 50%,#fafafa 100%); /* W3C */ + background: linear-gradient(to bottom, #eeeeee 0%,#ffffff 50%,#fafafa 100%); } .cesium-lighter .cesium-timeline-ticLabel { diff --git a/Source/Widgets/Viewer/Viewer.css b/Source/Widgets/Viewer/Viewer.css index 9276afb714e1..8904dea9d882 100644 --- a/Source/Widgets/Viewer/Viewer.css +++ b/Source/Widgets/Viewer/Viewer.css @@ -95,3 +95,14 @@ display: inline-block; margin: 0 3px; } + +.cesium-viewer-cesium3DTilesInspectorContainer { + display: block; + position: absolute; + top: 50px; + right: 10px; + max-height: 100%; + padding-bottom: 70px; + box-sizing: border-box; + overflow: auto; +} diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index dad0543c9b1b..0203645386bd 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -2,9 +2,11 @@ define([ '../../Core/BoundingSphere', '../../Core/Cartesian3', + '../../Core/Clock', '../../Core/defaultValue', '../../Core/defined', '../../Core/defineProperties', + '../../Core/deprecationWarning', '../../Core/destroyObject', '../../Core/DeveloperError', '../../Core/Event', @@ -46,9 +48,11 @@ define([ ], function( BoundingSphere, Cartesian3, + Clock, defaultValue, defined, defineProperties, + deprecationWarning, destroyObject, DeveloperError, Event, @@ -259,7 +263,7 @@ define([ * @param {Boolean} [options.navigationHelpButton=true] If set to false, the navigation help button will not be created. * @param {Boolean} [options.navigationInstructionsInitiallyVisible=true] True if the navigation instructions should initially be visible, or false if the should not be shown until the user explicitly clicks the button. * @param {Boolean} [options.scene3DOnly=false] When true, each geometry instance will only be rendered in 3D to save GPU memory. - * @param {Clock} [options.clock=new Clock()] The clock to use to control current time. + * @param {ClockViewModel} [options.clockViewModel=new ClockViewModel(options.clock)] The clock view model to use to control current time. * @param {ProviderViewModel} [options.selectedImageryProviderViewModel] The view model for the current base imagery layer, if not supplied the first available base layer is used. This value is only valid if options.baseLayerPicker is set to true. * @param {ProviderViewModel[]} [options.imageryProviderViewModels=createDefaultImageryProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if options.baseLayerPicker is set to true. * @param {ProviderViewModel} [options.selectedTerrainProviderViewModel] The view model for the current base terrain layer, if not supplied the first available base layer is used. This value is only valid if options.baseLayerPicker is set to true. @@ -311,7 +315,7 @@ define([ * sceneMode : Cesium.SceneMode.COLUMBUS_VIEW, * //Use standard Cesium terrain * terrainProvider : new Cesium.CesiumTerrainProvider({ - * url : 'https://assets.agi.com/stk-terrain/world' + * url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' * }), * //Hide the base layer picker * baseLayerPicker : false, @@ -401,11 +405,28 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to var scene3DOnly = defaultValue(options.scene3DOnly, false); + var deprecatedClock = options.clock; + if (defined(deprecatedClock)) { + deprecationWarning('Viewer.options.clock', 'Passing options.clock when creating a new Viewer instance was deprecated in Cesium 1.34 and will be removed in Cesium 1.37, pass options.clockViewModel instead.'); + } + + var clock; + var clockViewModel; + var destroyClockViewModel = false; + if (defined(options.clockViewModel)) { + clockViewModel = options.clockViewModel; + clock = clockViewModel.clock; + } else { + clock = defined(deprecatedClock) ? deprecatedClock : new Clock(); + clockViewModel = new ClockViewModel(clock); + destroyClockViewModel = true; + } + // Cesium widget var cesiumWidget = new CesiumWidget(cesiumWidgetContainer, { terrainProvider : options.terrainProvider, imageryProvider : createBaseLayerPicker ? false : options.imageryProvider, - clock : options.clock, + clock : clock, skyBox : options.skyBox, skyAtmosphere : options.skyAtmosphere, sceneMode : options.sceneMode, @@ -436,8 +457,6 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to dataSourceCollection : dataSourceCollection }); - var clock = cesiumWidget.clock; - var clockViewModel = new ClockViewModel(clock); var eventHelper = new EventHelper(); eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); @@ -649,6 +668,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._destroyDataSourceCollection = destroyDataSourceCollection; this._dataSourceDisplay = dataSourceDisplay; this._clockViewModel = clockViewModel; + this._destroyClockViewModel = destroyClockViewModel; this._toolbar = toolbar; this._homeButton = homeButton; this._sceneModePicker = sceneModePicker; @@ -1067,7 +1087,19 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to */ clock : { get : function() { - return this._cesiumWidget.clock; + return this._clockViewModel.clock; + } + }, + + /** + * Gets the clock view model. + * @memberof Viewer.prototype + * @type {ClockViewModel} + * @readonly + */ + clockViewModel : { + get : function() { + return this._clockViewModel; } }, @@ -1492,7 +1524,9 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._selectionIndicator = this._selectionIndicator.destroy(); } - this._clockViewModel = this._clockViewModel.destroy(); + if (this._destroyClockViewModel) { + this._clockViewModel = this._clockViewModel.destroy(); + } this._dataSourceDisplay = this._dataSourceDisplay.destroy(); this._cesiumWidget = this._cesiumWidget.destroy(); diff --git a/Source/Widgets/Viewer/viewerCesium3DTilesInspectorMixin.js b/Source/Widgets/Viewer/viewerCesium3DTilesInspectorMixin.js new file mode 100644 index 000000000000..069189bc5f3b --- /dev/null +++ b/Source/Widgets/Viewer/viewerCesium3DTilesInspectorMixin.js @@ -0,0 +1,44 @@ +/*global define*/ +define([ + '../../Core/Check', + '../../Core/defineProperties', + '../Cesium3DTilesInspector/Cesium3DTilesInspector' + ], function( + Check, + defineProperties, + Cesium3DTilesInspector) { + 'use strict'; + + /** + * A mixin which adds the {@link Cesium3DTilesInspector} widget to the {@link Viewer} widget. + * Rather than being called directly, this function is normally passed as + * a parameter to {@link Viewer#extend}, as shown in the example below. + * @exports viewerCesium3DTilesInspectorMixin + * + * @param {Viewer} viewer The viewer instance. + * + * @example + * var viewer = new Cesium.Viewer('cesiumContainer'); + * viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin); + */ + function viewerCesium3DTilesInspectorMixin(viewer) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object('viewer', viewer); + //>>includeEnd('debug'); + + var container = document.createElement('div'); + container.className = 'cesium-viewer-cesium3DTilesInspectorContainer'; + viewer.container.appendChild(container); + var cesium3DTilesInspector = new Cesium3DTilesInspector(container, viewer.scene); + + defineProperties(viewer, { + cesium3DTilesInspector : { + get : function() { + return cesium3DTilesInspector; + } + } + }); + } + + return viewerCesium3DTilesInspectorMixin; +}); diff --git a/Source/Widgets/Viewer/viewerCesiumInspectorMixin.js b/Source/Widgets/Viewer/viewerCesiumInspectorMixin.js index a997a2183569..6f93b85915f9 100644 --- a/Source/Widgets/Viewer/viewerCesiumInspectorMixin.js +++ b/Source/Widgets/Viewer/viewerCesiumInspectorMixin.js @@ -46,10 +46,6 @@ define([ } } }); - - viewer.scene.postRender.addEventListener(function() { - viewer.cesiumInspector.viewModel.update(); - }); } return viewerCesiumInspectorMixin; diff --git a/Source/Widgets/shared.css b/Source/Widgets/shared.css index 1bf709836175..19e2db63ede1 100644 --- a/Source/Widgets/shared.css +++ b/Source/Widgets/shared.css @@ -79,8 +79,6 @@ } .cesium-toolbar-button { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; width: 32px; height: 32px; diff --git a/Source/Widgets/widgets.css b/Source/Widgets/widgets.css index f291135b644a..b6584d2e5cfd 100644 --- a/Source/Widgets/widgets.css +++ b/Source/Widgets/widgets.css @@ -3,6 +3,7 @@ @import url(./BaseLayerPicker/BaseLayerPicker.css); @import url(./CesiumWidget/CesiumWidget.css); @import url(./CesiumInspector/CesiumInspector.css); +@import url(./Cesium3DTilesInspector/Cesium3DTilesInspector.css); @import url(./FullscreenButton/FullscreenButton.css); @import url(./VRButton/VRButton.css); @import url(./Geocoder/Geocoder.css); diff --git a/Source/Workers/createPointGeometry.js b/Source/Workers/createPointGeometry.js deleted file mode 100644 index 0b5884e97a85..000000000000 --- a/Source/Workers/createPointGeometry.js +++ /dev/null @@ -1,9 +0,0 @@ -/*global define*/ -define([ - '../Core/PointGeometry' - ], function( - PointGeometry) { - 'use strict'; - - return PointGeometry.createGeometry; -}); diff --git a/Source/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js b/Source/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js new file mode 100644 index 000000000000..64c7220dbe15 --- /dev/null +++ b/Source/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js @@ -0,0 +1,473 @@ +/*global define*/ +define([ + '../Core/AxisAlignedBoundingBox', + '../Core/BoundingSphere', + '../Core/Cartesian2', + '../Core/Cartesian3', + '../Core/Cartographic', + '../Core/defaultValue', + '../Core/defined', + '../Core/Ellipsoid', + '../Core/EllipsoidalOccluder', + '../Core/Math', + '../Core/Matrix4', + '../Core/OrientedBoundingBox', + '../Core/Rectangle', + '../Core/RuntimeError', + '../Core/TerrainEncoding', + '../Core/Transforms', + '../Core/WebMercatorProjection', + './createTaskProcessorWorker' +], function( + AxisAlignedBoundingBox, + BoundingSphere, + Cartesian2, + Cartesian3, + Cartographic, + defaultValue, + defined, + Ellipsoid, + EllipsoidalOccluder, + CesiumMath, + Matrix4, + OrientedBoundingBox, + Rectangle, + RuntimeError, + TerrainEncoding, + Transforms, + WebMercatorProjection, + createTaskProcessorWorker) { + 'use strict'; + + var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT; + var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT; + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT; + var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT; + + function indexOfEpsilon(arr, elem, elemType) { + elemType = defaultValue(elemType, CesiumMath); + var count = arr.length; + for (var i = 0; i < count; ++i) { + if (elemType.equalsEpsilon(arr[i], elem, CesiumMath.EPSILON12)) { + return i; + } + } + + return -1; + } + + function createVerticesFromGoogleEarthEnterpriseBuffer(parameters, transferableObjects) { + parameters.ellipsoid = Ellipsoid.clone(parameters.ellipsoid); + parameters.rectangle = Rectangle.clone(parameters.rectangle); + + var statistics = processBuffer(parameters.buffer, parameters.relativeToCenter, parameters.ellipsoid, + parameters.rectangle, parameters.nativeRectangle, parameters.exaggeration, parameters.skirtHeight, + parameters.includeWebMercatorT, parameters.negativeAltitudeExponentBias, parameters.negativeElevationThreshold); + var vertices = statistics.vertices; + transferableObjects.push(vertices.buffer); + var indices = statistics.indices; + transferableObjects.push(indices.buffer); + + return { + vertices : vertices.buffer, + indices : indices.buffer, + numberOfAttributes : statistics.encoding.getStride(), + minimumHeight : statistics.minimumHeight, + maximumHeight : statistics.maximumHeight, + boundingSphere3D : statistics.boundingSphere3D, + orientedBoundingBox : statistics.orientedBoundingBox, + occludeePointInScaledSpace : statistics.occludeePointInScaledSpace, + encoding : statistics.encoding, + vertexCountWithoutSkirts : statistics.vertexCountWithoutSkirts, + skirtIndex : statistics.skirtIndex + }; + } + + var scratchCartographic = new Cartographic(); + var scratchCartesian = new Cartesian3(); + var minimumScratch = new Cartesian3(); + var maximumScratch = new Cartesian3(); + var matrix4Scratch = new Matrix4(); + + function processBuffer(buffer, relativeToCenter, ellipsoid, rectangle, nativeRectangle, exaggeration, skirtHeight, includeWebMercatorT, negativeAltitudeExponentBias, negativeElevationThreshold) { + var geographicWest; + var geographicSouth; + var geographicEast; + var geographicNorth; + var rectangleWidth, rectangleHeight; + + if (!defined(rectangle)) { + geographicWest = CesiumMath.toRadians(nativeRectangle.west); + geographicSouth = CesiumMath.toRadians(nativeRectangle.south); + geographicEast = CesiumMath.toRadians(nativeRectangle.east); + geographicNorth = CesiumMath.toRadians(nativeRectangle.north); + rectangleWidth = CesiumMath.toRadians(rectangle.width); + rectangleHeight = CesiumMath.toRadians(rectangle.height); + } else { + geographicWest = rectangle.west; + geographicSouth = rectangle.south; + geographicEast = rectangle.east; + geographicNorth = rectangle.north; + rectangleWidth = rectangle.width; + rectangleHeight = rectangle.height; + } + + // Keep track of quad borders so we can remove duplicates around the borders + var quadBorderLatitudes = [geographicSouth, geographicNorth]; + var quadBorderLongitudes = [geographicWest, geographicEast]; + + var fromENU = Transforms.eastNorthUpToFixedFrame(relativeToCenter, ellipsoid); + var toENU = Matrix4.inverseTransformation(fromENU, matrix4Scratch); + + var southMercatorY; + var oneOverMercatorHeight; + if (includeWebMercatorT) { + southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicSouth); + oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicNorth) - southMercatorY); + } + + var dv = new DataView(buffer); + + var minHeight = Number.POSITIVE_INFINITY; + var maxHeight = Number.NEGATIVE_INFINITY; + + var minimum = minimumScratch; + minimum.x = Number.POSITIVE_INFINITY; + minimum.y = Number.POSITIVE_INFINITY; + minimum.z = Number.POSITIVE_INFINITY; + + var maximum = maximumScratch; + maximum.x = Number.NEGATIVE_INFINITY; + maximum.y = Number.NEGATIVE_INFINITY; + maximum.z = Number.NEGATIVE_INFINITY; + + // Compute sizes + var offset = 0; + var size = 0; + var indicesSize = 0; + var quadSize; + for (var quad = 0; quad < 4; ++quad) { + var o = offset; + quadSize = dv.getUint32(o, true); + o += sizeOfUint32; + + var x = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0); + o += sizeOfDouble; + if (indexOfEpsilon(quadBorderLongitudes, x) === -1) { + quadBorderLongitudes.push(x); + } + + var y = CesiumMath.toRadians(dv.getFloat64(o, true) * 180.0); + o += sizeOfDouble; + if (indexOfEpsilon(quadBorderLatitudes, y) === -1) { + quadBorderLatitudes.push(y); + } + + o += 2 * sizeOfDouble; // stepX + stepY + + var c = dv.getInt32(o, true); // Read point count + o += sizeOfInt32; + size += c; + + c = dv.getInt32(o, true); // Read index count + indicesSize += c * 3; + + offset += quadSize + sizeOfUint32; // Jump to next quad + } + + // Quad Border points to remove duplicates + var quadBorderPoints = []; + var quadBorderIndices = []; + + // Create arrays + var positions = new Array(size); + var uvs = new Array(size); + var heights = new Array(size); + var webMercatorTs = includeWebMercatorT ? new Array(size) : []; + var indices = new Array(indicesSize); + + // Points are laid out in rows starting at SW, so storing border points as we + // come across them all points will be adjacent. + var westBorder = []; + var southBorder = []; + var eastBorder = []; + var northBorder = []; + + // Each tile is split into 4 parts + var pointOffset = 0; + var indicesOffset = 0; + offset = 0; + for (quad = 0; quad < 4; ++quad) { + quadSize = dv.getUint32(offset, true); + offset += sizeOfUint32; + var startQuad = offset; + + var originX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + offset += sizeOfDouble; + + var originY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + offset += sizeOfDouble; + + var stepX = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + var halfStepX = stepX * 0.5; + offset += sizeOfDouble; + + var stepY = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); + var halfStepY = stepY * 0.5; + offset += sizeOfDouble; + + var numPoints = dv.getInt32(offset, true); + offset += sizeOfInt32; + + var numFaces = dv.getInt32(offset, true); + offset += sizeOfInt32; + + //var level = dv.getInt32(offset, true); + offset += sizeOfInt32; + + // Keep track of quad indices to overall tile indices + var indicesMapping = new Array(numPoints); + for (var i = 0; i < numPoints; ++i) { + var longitude = originX + dv.getUint8(offset++) * stepX; + scratchCartographic.longitude = longitude; + var latitude = originY + dv.getUint8(offset++) * stepY; + scratchCartographic.latitude = latitude; + // Height is stored in units of (1/EarthRadius) or (1/6371010.0) + var height = dv.getFloat32(offset, true) * 6371010.0; + offset += sizeOfFloat; + + // In order to support old clients, negative altitude values are stored as + // height/-2^32. Old clients see the value as really close to 0 but new clients multiply + // by -2^32 to get the real negative altitude value. + if (height < negativeElevationThreshold) { + height *= negativeAltitudeExponentBias; + } + height *= exaggeration; + + scratchCartographic.height = height; + + // Is it along a quad border - if so check if already exists and use that index + if (indexOfEpsilon(quadBorderLongitudes, longitude) !== -1 || + indexOfEpsilon(quadBorderLatitudes, latitude) !== -1) { + var index = indexOfEpsilon(quadBorderPoints, scratchCartographic, Cartographic); + if (index === -1) { + quadBorderPoints.push(Cartographic.clone(scratchCartographic)); + quadBorderIndices.push(pointOffset); + } else { + indicesMapping[i] = quadBorderIndices[index]; + continue; + } + } + indicesMapping[i] = pointOffset; + + if (Math.abs(longitude - geographicWest) < halfStepX) { + westBorder.push({ + index : pointOffset, + cartographic : Cartographic.clone(scratchCartographic) + }); + } else if (Math.abs(longitude - geographicEast) < halfStepX) { + eastBorder.push({ + index : pointOffset, + cartographic : Cartographic.clone(scratchCartographic) + }); + } else if (Math.abs(latitude - geographicSouth) < halfStepY) { + southBorder.push({ + index : pointOffset, + cartographic : Cartographic.clone(scratchCartographic) + }); + } else if (Math.abs(latitude - geographicNorth) < halfStepY) { + northBorder.push({ + index : pointOffset, + cartographic : Cartographic.clone(scratchCartographic) + }); + } + + minHeight = Math.min(height, minHeight); + maxHeight = Math.max(height, maxHeight); + heights[pointOffset] = height; + + var pos = ellipsoid.cartographicToCartesian(scratchCartographic); + positions[pointOffset] = pos; + + if (includeWebMercatorT) { + webMercatorTs[pointOffset] = (WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) - southMercatorY) * oneOverMercatorHeight; + } + + Matrix4.multiplyByPoint(toENU, pos, scratchCartesian); + + Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum); + Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum); + + var u = (longitude - geographicWest) / (geographicEast - geographicWest); + u = CesiumMath.clamp(u, 0.0, 1.0); + var v = (latitude - geographicSouth) / (geographicNorth - geographicSouth); + v = CesiumMath.clamp(v, 0.0, 1.0); + + uvs[pointOffset] = new Cartesian2(u, v); + ++pointOffset; + } + + var facesElementCount = numFaces * 3; + for (i = 0; i < facesElementCount; ++i, ++indicesOffset) { + indices[indicesOffset] = indicesMapping[dv.getUint16(offset, true)]; + offset += sizeOfUint16; + } + + if (quadSize !== (offset - startQuad)) { + throw new RuntimeError('Invalid terrain tile.'); + } + } + + positions.length = pointOffset; + uvs.length = pointOffset; + heights.length = pointOffset; + if (includeWebMercatorT) { + webMercatorTs.length = pointOffset; + } + + var vertexCountWithoutSkirts = pointOffset; + var skirtIndex = indicesOffset; + + // Add skirt points + var skirtOptions = { + hMin : minHeight, + lastBorderPoint : undefined, + skirtHeight : skirtHeight, + toENU : toENU, + ellipsoid : ellipsoid, + minimum : minimum, + maximum : maximum + }; + + // Sort counter clockwise from NW corner + // Corner points are in the east/west arrays + westBorder.sort(function(a, b) { + return b.cartographic.latitude - a.cartographic.latitude; + }); + southBorder.sort(function(a, b) { + return a.cartographic.longitude - b.cartographic.longitude; + }); + eastBorder.sort(function(a, b) { + return a.cartographic.latitude - b.cartographic.latitude; + }); + northBorder.sort(function(a, b) { + return b.cartographic.longitude - a.cartographic.longitude; + }); + + var percentage = 0.00001; + addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions, + westBorder, -percentage * rectangleWidth, true, -percentage * rectangleHeight); + addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions, + southBorder, -percentage * rectangleHeight, false); + addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions, + eastBorder, percentage * rectangleWidth, true, percentage * rectangleHeight); + addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions, + northBorder, percentage * rectangleHeight, false); + + // Since the corner between the north and west sides is in the west array, generate the last + // two triangles between the last north vertex and the first west vertex + if (westBorder.length > 0 && northBorder.length > 0) { + var firstBorderIndex = westBorder[0].index; + var firstSkirtIndex = vertexCountWithoutSkirts; + var lastBorderIndex = northBorder[northBorder.length - 1].index; + var lastSkirtIndex = positions.length - 1; + + indices.push(lastBorderIndex, lastSkirtIndex, firstSkirtIndex, firstSkirtIndex, firstBorderIndex, lastBorderIndex); + } + + size = positions.length; // Get new size with skirt vertices + + var boundingSphere3D = BoundingSphere.fromPoints(positions); + var orientedBoundingBox; + if (defined(rectangle) && rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) { + // Here, rectangle.width < pi/2, and rectangle.height < pi + // (though it would still work with rectangle.width up to pi) + orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minHeight, maxHeight, ellipsoid); + } + + var occluder = new EllipsoidalOccluder(ellipsoid); + var occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions); + + var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter); + var encoding = new TerrainEncoding(aaBox, skirtOptions.hMin, maxHeight, fromENU, false, includeWebMercatorT); + var vertices = new Float32Array(size * encoding.getStride()); + + var bufferIndex = 0; + for (var j = 0; j < size; ++j) { + bufferIndex = encoding.encode(vertices, bufferIndex, positions[j], uvs[j], heights[j], undefined, webMercatorTs[j]); + } + + return { + vertices : vertices, + indices : new Uint16Array(indices), + maximumHeight : maxHeight, + minimumHeight : minHeight, + encoding : encoding, + boundingSphere3D : boundingSphere3D, + orientedBoundingBox : orientedBoundingBox, + occludeePointInScaledSpace : occludeePointInScaledSpace, + vertexCountWithoutSkirts : vertexCountWithoutSkirts, + skirtIndex : skirtIndex + }; + } + + function addSkirt(positions, heights, uvs, webMercatorTs, indices, skirtOptions, + borderPoints, fudgeFactor, eastOrWest, cornerFudge) { + var count = borderPoints.length; + for (var j = 0; j < count; ++j) { + var borderPoint = borderPoints[j]; + var borderCartographic = borderPoint.cartographic; + var borderIndex = borderPoint.index; + var currentIndex = positions.length; + + var longitude = borderCartographic.longitude; + var latitude = borderCartographic.latitude; + latitude = CesiumMath.clamp(latitude, -CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO); // Don't go over the poles + var height = borderCartographic.height - skirtOptions.skirtHeight; + skirtOptions.hMin = Math.min(skirtOptions.hMin, height); + + Cartographic.fromRadians(longitude, latitude, height, scratchCartographic); + + // Adjust sides to angle out + if (eastOrWest) { + scratchCartographic.longitude += fudgeFactor; + } + + // Adjust top or bottom to angle out + // Since corners are in the east/west arrays angle the first and last points as well + if (!eastOrWest) { + scratchCartographic.latitude += fudgeFactor; + } else if (j === (count - 1)) { + scratchCartographic.latitude += cornerFudge; + } else if (j === 0) { + scratchCartographic.latitude -= cornerFudge; + } + + var pos = skirtOptions.ellipsoid.cartographicToCartesian(scratchCartographic); + positions.push(pos); + heights.push(height); + uvs.push(Cartesian2.clone(uvs[borderIndex])); // Copy UVs from border point + if (webMercatorTs.length > 0) { + webMercatorTs.push(webMercatorTs[borderIndex]); + } + + Matrix4.multiplyByPoint(skirtOptions.toENU, pos, scratchCartesian); + + var minimum = skirtOptions.minimum; + var maximum = skirtOptions.maximum; + Cartesian3.minimumByComponent(scratchCartesian, minimum, minimum); + Cartesian3.maximumByComponent(scratchCartesian, maximum, maximum); + + var lastBorderPoint = skirtOptions.lastBorderPoint; + if (defined(lastBorderPoint)) { + var lastBorderIndex = lastBorderPoint.index; + indices.push(lastBorderIndex, currentIndex - 1, currentIndex, currentIndex, borderIndex, lastBorderIndex); + } + + skirtOptions.lastBorderPoint = borderPoint; + } + } + + return createTaskProcessorWorker(createVerticesFromGoogleEarthEnterpriseBuffer); +}); diff --git a/Source/Workers/decodeGoogleEarthEnterprisePacket.js b/Source/Workers/decodeGoogleEarthEnterprisePacket.js new file mode 100644 index 000000000000..2a91b6b28385 --- /dev/null +++ b/Source/Workers/decodeGoogleEarthEnterprisePacket.js @@ -0,0 +1,255 @@ +/*global define*/ +define([ + '../Core/decodeGoogleEarthEnterpriseData', + '../Core/RuntimeError', + '../Core/GoogleEarthEnterpriseTileInformation', + './createTaskProcessorWorker', + '../ThirdParty/pako_inflate' +], function( + decodeGoogleEarthEnterpriseData, + RuntimeError, + GoogleEarthEnterpriseTileInformation, + createTaskProcessorWorker, + pako) { + 'use strict'; + + // Datatype sizes + var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT; + var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT; + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + + var Types = { + METADATA : 0, + TERRAIN : 1, + DBROOT : 2 + }; + + Types.fromString = function(s) { + if (s === 'Metadata') { + return Types.METADATA; + } else if (s === 'Terrain') { + return Types.TERRAIN; + } else if (s === 'DbRoot') { + return Types.DBROOT; + } + }; + + function decodeGoogleEarthEnterprisePacket(parameters, transferableObjects) { + var type = Types.fromString(parameters.type); + var buffer = parameters.buffer; + decodeGoogleEarthEnterpriseData(parameters.key, buffer); + + var uncompressedTerrain = uncompressPacket(buffer); + buffer = uncompressedTerrain.buffer; + var length = uncompressedTerrain.length; + + switch (type) { + case Types.METADATA: + return processMetadata(buffer, length, parameters.quadKey); + case Types.TERRAIN: + return processTerrain(buffer, length, transferableObjects); + case Types.DBROOT: + transferableObjects.push(buffer); + return { + buffer : buffer + }; + } + + } + + var qtMagic = 32301; + + function processMetadata(buffer, totalSize, quadKey) { + var dv = new DataView(buffer); + var offset = 0; + var magic = dv.getUint32(offset, true); + offset += sizeOfUint32; + if (magic !== qtMagic) { + throw new RuntimeError('Invalid magic'); + } + + var dataTypeId = dv.getUint32(offset, true); + offset += sizeOfUint32; + if (dataTypeId !== 1) { + throw new RuntimeError('Invalid data type. Must be 1 for QuadTreePacket'); + } + + // Tile format version + var quadVersion = dv.getUint32(offset, true); + offset += sizeOfUint32; + if (quadVersion !== 2) { + throw new RuntimeError('Invalid QuadTreePacket version. Only version 2 is supported.'); + } + + var numInstances = dv.getInt32(offset, true); + offset += sizeOfInt32; + + var dataInstanceSize = dv.getInt32(offset, true); + offset += sizeOfInt32; + if (dataInstanceSize !== 32) { + throw new RuntimeError('Invalid instance size.'); + } + + var dataBufferOffset = dv.getInt32(offset, true); + offset += sizeOfInt32; + + var dataBufferSize = dv.getInt32(offset, true); + offset += sizeOfInt32; + + var metaBufferSize = dv.getInt32(offset, true); + offset += sizeOfInt32; + + // Offset from beginning of packet (instances + current offset) + if (dataBufferOffset !== (numInstances * dataInstanceSize + offset)) { + throw new RuntimeError('Invalid dataBufferOffset'); + } + + // Verify the packets is all there header + instances + dataBuffer + metaBuffer + if (dataBufferOffset + dataBufferSize + metaBufferSize !== totalSize) { + throw new RuntimeError('Invalid packet offsets'); + } + + // Read all the instances + var instances = []; + for (var i = 0; i < numInstances; ++i) { + var bitfield = dv.getUint8(offset); + ++offset; + + ++offset; // 2 byte align + + var cnodeVersion = dv.getUint16(offset, true); + offset += sizeOfUint16; + + var imageVersion = dv.getUint16(offset, true); + offset += sizeOfUint16; + + var terrainVersion = dv.getUint16(offset, true); + offset += sizeOfUint16; + + // Number of channels stored in the dataBuffer + offset += sizeOfUint16; + + offset += sizeOfUint16; // 4 byte align + + // Channel type offset into dataBuffer + offset += sizeOfInt32; + + // Channel version offset into dataBuffer + offset += sizeOfInt32; + + offset += 8; // Ignore image neighbors for now + + // Data providers + var imageProvider = dv.getUint8(offset++); + var terrainProvider = dv.getUint8(offset++); + offset += sizeOfUint16; // 4 byte align + + instances.push(new GoogleEarthEnterpriseTileInformation(bitfield, cnodeVersion, + imageVersion, terrainVersion, imageProvider, terrainProvider)); + } + + var tileInfo = []; + var index = 0; + + function populateTiles(parentKey, parent, level) { + var isLeaf = false; + if (level === 4) { + if (parent.hasSubtree()) { + return; // We have a subtree, so just return + } + + isLeaf = true; // No subtree, so set all children to null + } + for (var i = 0; i < 4; ++i) { + var childKey = parentKey + i.toString(); + if (isLeaf) { + // No subtree so set all children to null + tileInfo[childKey] = null; + } else if (level < 4) { + // We are still in the middle of the subtree, so add child + // only if their bits are set, otherwise set child to null. + if (!parent.hasChild(i)) { + tileInfo[childKey] = null; + } else { + if (index === numInstances) { + console.log('Incorrect number of instances'); + return; + } + + var instance = instances[index++]; + tileInfo[childKey] = instance; + populateTiles(childKey, instance, level + 1); + } + } + } + } + + var level = 0; + var root = instances[index++]; + if (quadKey === '') { + // Root tile has data at its root and one less level + ++level; + } else { + tileInfo[quadKey] = root; // This will only contain the child bitmask + } + + populateTiles(quadKey, root, level); + + return tileInfo; + } + + function processTerrain(buffer, totalSize, transferableObjects) { + var dv = new DataView(buffer); + + var offset = 0; + var terrainTiles = []; + while (offset < totalSize) { + // Each tile is split into 4 parts + var tileStart = offset; + for (var quad = 0; quad < 4; ++quad) { + var size = dv.getUint32(offset, true); + offset += sizeOfUint32; + offset += size; + } + var tile = buffer.slice(tileStart, offset); + transferableObjects.push(tile); + terrainTiles.push(tile); + } + + return terrainTiles; + } + + var compressedMagic = 0x7468dead; + var compressedMagicSwap = 0xadde6874; + + function uncompressPacket(data) { + // The layout of this decoded data is + // Magic Uint32 + // Size Uint32 + // [GZipped chunk of Size bytes] + + // Pullout magic and verify we have the correct data + var dv = new DataView(data); + var offset = 0; + var magic = dv.getUint32(offset, true); + offset += sizeOfUint32; + if (magic !== compressedMagic && magic !== compressedMagicSwap) { + throw new RuntimeError('Invalid magic'); + } + + // Get the size of the compressed buffer - the endianness depends on which magic was used + var size = dv.getUint32(offset, (magic === compressedMagic)); + offset += sizeOfUint32; + + var compressedPacket = new Uint8Array(data, offset); + var uncompressedPacket = pako.inflate(compressedPacket); + + if (uncompressedPacket.length !== size) { + throw new RuntimeError('Size of packet doesn\'t match header'); + } + + return uncompressedPacket; + } + + return createTaskProcessorWorker(decodeGoogleEarthEnterprisePacket); +}); diff --git a/Source/Workers/transcodeCRNToDXT.js b/Source/Workers/transcodeCRNToDXT.js index 5f781025bc68..4eaa40f2a7e8 100644 --- a/Source/Workers/transcodeCRNToDXT.js +++ b/Source/Workers/transcodeCRNToDXT.js @@ -110,7 +110,7 @@ define([ var dstSize = 0; var i; for (i = 0; i < levels; ++i) { - dstSize += PixelFormat.compressedTextureSize(format, width >> i, height >> i); + dstSize += PixelFormat.compressedTextureSizeInBytes(format, width >> i, height >> i); } // Allocate enough space on the emscripten heap to hold the decoded DXT data @@ -133,7 +133,7 @@ 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 length = PixelFormat.compressedTextureSize(format, width, height); + var length = PixelFormat.compressedTextureSizeInBytes(format, width, height); var level0DXTData = new Uint8Array(length); level0DXTData.set(dxtData, 0); diff --git a/Specs/.eslintrc.json b/Specs/.eslintrc.json new file mode 100644 index 000000000000..297a41b8f964 --- /dev/null +++ b/Specs/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "../.eslintrc.json", + "env": { + "jasmine": true + } +} diff --git a/Specs/Cesium3DTilesTester.js b/Specs/Cesium3DTilesTester.js new file mode 100644 index 000000000000..07bff2877fe1 --- /dev/null +++ b/Specs/Cesium3DTilesTester.js @@ -0,0 +1,348 @@ +/*global define*/ +define([ + 'Core/arrayFill', + 'Core/Color', + 'Core/defaultValue', + 'Core/defined', + 'Scene/Cesium3DTileContentFactory', + 'Scene/Cesium3DTileContentState', + 'Scene/Cesium3DTileset', + 'Scene/TileBoundingSphere', + 'Specs/pollToPromise' + ], function( + arrayFill, + Color, + defaultValue, + defined, + Cesium3DTileContentFactory, + Cesium3DTileContentState, + Cesium3DTileset, + TileBoundingSphere, + pollToPromise) { + 'use strict'; + + var mockTile = { + contentBoundingVolume : new TileBoundingSphere(), + _contentBoundingVolume : new TileBoundingSphere(), + _header : { + content : { + boundingVolume : { + sphere : [0.0, 0.0, 0.0, 1.0] + } + } + } + }; + + function Cesium3DTilesTester() { + } + + function padStringToByteAlignment(string, byteAlignment) { + var length = string.length; + var paddedLength = Math.ceil(length / byteAlignment) * byteAlignment; // Round up to the required alignment + var padding = paddedLength - length; + var whitespace = ''; + for (var i = 0; i < padding; ++i) { + whitespace += ' '; + } + return string + whitespace; + } + + Cesium3DTilesTester.expectRender = function(scene, tileset, callback) { + tileset.show = false; + expect(scene).toRender([0, 0, 0, 255]); + tileset.show = true; + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba).not.toEqual([0, 0, 0, 255]); + if (defined(callback)) { + callback(rgba); + } + }); + }; + + Cesium3DTilesTester.expectRenderBlank = function(scene, tileset) { + tileset.show = false; + expect(scene).toRender([0, 0, 0, 255]); + tileset.show = true; + expect(scene).toRender([0, 0, 0, 255]); + }; + + Cesium3DTilesTester.expectRenderTileset = function(scene, tileset) { + // Verify render before being picked + Cesium3DTilesTester.expectRender(scene, tileset); + + // Pick a feature + expect(scene).toPickAndCall(function(result) { + expect(result).toBeDefined(); + + // Change the color of the picked feature to yellow + result.color = Color.clone(Color.YELLOW, result.color); + + // Expect the pixel color to be some shade of yellow + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Turn show off and on + result.show = false; + Cesium3DTilesTester.expectRenderBlank(scene, tileset); + result.show = true; + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }; + + Cesium3DTilesTester.waitForTilesLoaded = function(scene, tileset) { + return pollToPromise(function() { + scene.renderForSpecs(); + return tileset.tilesLoaded; + }).then(function() { + return tileset; + }); + }; + + Cesium3DTilesTester.waitForReady = function(scene, tileset) { + return pollToPromise(function() { + scene.renderForSpecs(); + return tileset.ready; + }).then(function() { + return tileset; + }); + }; + + Cesium3DTilesTester.loadTileset = function(scene, url, options) { + options = defaultValue(options, {}); + options.url = url; + // Load all visible tiles + var tileset = scene.primitives.add(new Cesium3DTileset(options)); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); + }; + + Cesium3DTilesTester.loadTileExpectError = function(scene, arrayBuffer, type) { + var tileset = {}; + var url = ''; + expect(function() { + return Cesium3DTileContentFactory[type](tileset, mockTile, url, arrayBuffer, 0); + }).toThrowRuntimeError(); + }; + + Cesium3DTilesTester.loadTile = function(scene, arrayBuffer, type) { + var tileset = {}; + var url = ''; + var content = Cesium3DTileContentFactory[type](tileset, mockTile, url, arrayBuffer, 0); + content.update(tileset, scene.frameState); + return content; + }; + + // Use counter to prevent models from sharing the same cache key, + // this fixes tests that load a model with the same invalid url + var counter = 0; + Cesium3DTilesTester.rejectsReadyPromiseOnError = function(scene, arrayBuffer, type) { + var tileset = { + basePath : counter++ + }; + var url = ''; + var content = Cesium3DTileContentFactory[type](tileset, mockTile, url, arrayBuffer, 0); + content.update(tileset, scene.frameState); + + return content.readyPromise.then(function(content) { + fail('should not resolve'); + }).otherwise(function(error) { + expect(error).toBeDefined(); + }); + }; + + Cesium3DTilesTester.resolvesReadyPromise = function(scene, url) { + return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { + var content = tileset._root.content; + return content.readyPromise.then(function(content) { + expect(content).toBeDefined(); + }); + }); + }; + + Cesium3DTilesTester.tileDestroys = function(scene, url) { + return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { + var content = tileset._root.content; + expect(content.isDestroyed()).toEqual(false); + scene.primitives.remove(tileset); + expect(content.isDestroyed()).toEqual(true); + }); + }; + + Cesium3DTilesTester.generateBatchedTileBuffer = function(options) { + // Procedurally generate the tile array buffer for testing purposes + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var magic = defaultValue(options.magic, [98, 51, 100, 109]); + var version = defaultValue(options.version, 1); + var featuresLength = defaultValue(options.featuresLength, 1); + var featureTableJson = { + BATCH_LENGTH : featuresLength + }; + var featureTableJsonString = JSON.stringify(featureTableJson); + var featureTableJsonByteLength = featureTableJsonString.length; + + var headerByteLength = 28; + var byteLength = headerByteLength + featureTableJsonByteLength; + var buffer = new ArrayBuffer(byteLength); + var view = new DataView(buffer); + view.setUint8(0, magic[0]); + view.setUint8(1, magic[1]); + view.setUint8(2, magic[2]); + view.setUint8(3, magic[3]); + view.setUint32(4, version, true); // version + view.setUint32(8, byteLength, true); // byteLength + view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength + view.setUint32(16, 0, true); // featureTableBinaryByteLength + view.setUint32(20, 0, true); // batchTableJsonByteLength + view.setUint32(24, 0, true); // batchTableBinaryByteLength + + var i; + var byteOffset = headerByteLength; + for (i = 0; i < featureTableJsonByteLength; i++) { + view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); + byteOffset++; + } + + return buffer; + }; + + Cesium3DTilesTester.generateInstancedTileBuffer = function(options) { + // Procedurally generate the tile array buffer for testing purposes + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var magic = defaultValue(options.magic, [105, 51, 100, 109]); + var version = defaultValue(options.version, 1); + + var gltfFormat = defaultValue(options.gltfFormat, 1); + var gltfUri = defaultValue(options.gltfUri, ''); + var gltfUriByteLength = gltfUri.length; + + var featuresLength = defaultValue(options.featuresLength, 1); + var featureTableJson = { + INSTANCES_LENGTH : featuresLength, + POSITION : arrayFill(new Array(featuresLength * 3), 0) + }; + var featureTableJsonString = JSON.stringify(featureTableJson); + var featureTableJsonByteLength = featureTableJsonString.length; + + var headerByteLength = 32; + var uriByteLength = gltfUri.length; + var byteLength = headerByteLength + featureTableJsonByteLength + uriByteLength; + var buffer = new ArrayBuffer(byteLength); + var view = new DataView(buffer); + view.setUint8(0, magic[0]); + view.setUint8(1, magic[1]); + view.setUint8(2, magic[2]); + view.setUint8(3, magic[3]); + view.setUint32(4, version, true); // version + view.setUint32(8, byteLength, true); // byteLength + view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength + view.setUint32(16, 0, true); // featureTableBinaryByteLength + view.setUint32(20, 0, true); // batchTableJsonByteLength + view.setUint32(24, 0, true); // batchTableBinaryByteLength + view.setUint32(28, gltfFormat, true); // gltfFormat + + var i; + var byteOffset = headerByteLength; + for (i = 0; i < featureTableJsonByteLength; i++) { + view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); + byteOffset++; + } + for (i = 0; i < gltfUriByteLength; i++) { + view.setUint8(byteOffset, gltfUri.charCodeAt(i)); + byteOffset++; + } + return buffer; + }; + + Cesium3DTilesTester.generatePointCloudTileBuffer = function(options) { + // Procedurally generate the tile array buffer for testing purposes + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var magic = defaultValue(options.magic, [112, 110, 116, 115]); + var version = defaultValue(options.version, 1); + var featureTableJson = options.featureTableJson; + if (!defined(featureTableJson)) { + featureTableJson = { + POINTS_LENGTH : 1, + POSITIONS : { + byteOffset : 0 + } + }; + } + + var featureTableJsonString = JSON.stringify(featureTableJson); + featureTableJsonString = padStringToByteAlignment(featureTableJsonString, 4); + var featureTableJsonByteLength = defaultValue(options.featureTableJsonByteLength, featureTableJsonString.length); + + var featureTableBinary = new ArrayBuffer(12); // Enough space to hold 3 floats + var featureTableBinaryByteLength = featureTableBinary.byteLength; + + var headerByteLength = 28; + var byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength; + var buffer = new ArrayBuffer(byteLength); + var view = new DataView(buffer); + view.setUint8(0, magic[0]); + view.setUint8(1, magic[1]); + view.setUint8(2, magic[2]); + view.setUint8(3, magic[3]); + view.setUint32(4, version, true); // version + view.setUint32(8, byteLength, true); // byteLength + view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength + view.setUint32(16, featureTableBinaryByteLength, true); // featureTableBinaryByteLength + view.setUint32(20, 0, true); // batchTableJsonByteLength + view.setUint32(24, 0, true); // batchTableBinaryByteLength + + var i; + var byteOffset = headerByteLength; + for (i = 0; i < featureTableJsonByteLength; i++) { + view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); + byteOffset++; + } + for (i = 0; i < featureTableBinaryByteLength; i++) { + view.setUint8(byteOffset, featureTableBinary[i]); + byteOffset++; + } + return buffer; + }; + + Cesium3DTilesTester.generateCompositeTileBuffer = function(options) { + // Procedurally generate the tile array buffer for testing purposes + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var magic = defaultValue(options.magic, [99, 109, 112, 116]); + var version = defaultValue(options.version, 1); + var tiles = defaultValue(options.tiles, []); + var tilesLength = tiles.length; + + var i; + var tilesByteLength = 0; + for (i = 0; i < tilesLength; ++i) { + tilesByteLength += tiles[i].byteLength; + } + + var headerByteLength = 16; + var byteLength = headerByteLength + tilesByteLength; + var buffer = new ArrayBuffer(byteLength); + var uint8Array = new Uint8Array(buffer); + var view = new DataView(buffer); + view.setUint8(0, magic[0]); + view.setUint8(1, magic[1]); + view.setUint8(2, magic[2]); + view.setUint8(3, magic[3]); + view.setUint32(4, version, true); // version + view.setUint32(8, byteLength, true); // byteLength + view.setUint32(12, tilesLength, true); // tilesLength + + var byteOffset = headerByteLength; + for (i = 0; i < tilesLength; ++i) { + var tile = new Uint8Array(tiles[i]); + uint8Array.set(tile, byteOffset); + byteOffset += tile.byteLength; + } + + return buffer; + }; + + return Cesium3DTilesTester; +}); diff --git a/Specs/Core/BingMapsGeocoderServiceSpec.js b/Specs/Core/BingMapsGeocoderServiceSpec.js index 7543deb78d4c..26e7be7758b3 100644 --- a/Specs/Core/BingMapsGeocoderServiceSpec.js +++ b/Specs/Core/BingMapsGeocoderServiceSpec.js @@ -22,6 +22,10 @@ defineSuite([ scene.destroyForSpecs(); }); + afterAll(function() { + loadJsonp.loadAndExecuteScript = loadJsonp.defaultLoadAndExecuteScript; + }); + it('constructor throws without scene', function() { expect(function() { return new BingMapsGeocoderService(); @@ -30,15 +34,18 @@ defineSuite([ it('returns geocoder results', function (done) { var query = 'some query'; - jasmine.createSpy('testSpy', loadJsonp).and.returnValue({ + var data = { resourceSets: [{ resources : [{ name : 'a', bbox : [32.0, 3.0, 3.0, 4.0] }] }] - }); - service.geocode(query, function(err, results) { + }; + loadJsonp.loadAndExecuteScript = function(url, functionName, deferred) { + deferred.resolve(data); + }; + service.geocode(query).then(function(results) { expect(results.length).toEqual(1); expect(results[0].displayName).toEqual('a'); expect(results[0].destination).toBeInstanceOf(Rectangle); @@ -48,10 +55,13 @@ defineSuite([ it('returns no geocoder results if Bing has no results', function (done) { var query = 'some query'; - jasmine.createSpy('testSpy', loadJsonp).and.returnValue({ + var data = { resourceSets: [] - }); - service.geocode(query, function(err, results) { + }; + loadJsonp.loadAndExecuteScript = function(url, functionName, deferred) { + deferred.resolve(data); + }; + service.geocode(query).then(function(results) { expect(results.length).toEqual(0); done(); }); @@ -59,12 +69,15 @@ defineSuite([ it('returns no geocoder results if Bing has results but no resources', function (done) { var query = 'some query'; - jasmine.createSpy('testSpy', loadJsonp).and.returnValue({ + var data = { resourceSets: [{ resources: [] }] - }); - service.geocode(query, function(err, results) { + }; + loadJsonp.loadAndExecuteScript = function(url, functionName, deferred) { + deferred.resolve(data); + }; + service.geocode(query).then(function(results) { expect(results.length).toEqual(0); done(); }); diff --git a/Specs/Core/BoundingRectangleSpec.js b/Specs/Core/BoundingRectangleSpec.js index a97bc5a1225c..770c285351d3 100644 --- a/Specs/Core/BoundingRectangleSpec.js +++ b/Specs/Core/BoundingRectangleSpec.js @@ -111,7 +111,7 @@ defineSuite([ expect(rectangle.height).toEqual(0.0); }); - it('create a bounding rectangle from an rectangle', function() { + it('create a bounding rectangle from a rectangle', function() { var rectangle = Rectangle.MAX_VALUE; var projection = new GeographicProjection(Ellipsoid.UNIT_SPHERE); var expected = new BoundingRectangle(rectangle.west, rectangle.south, rectangle.east - rectangle.west, rectangle.north - rectangle.south); diff --git a/Specs/Core/BoundingSphereSpec.js b/Specs/Core/BoundingSphereSpec.js index 849ba66278c7..9d63d03bd730 100644 --- a/Specs/Core/BoundingSphereSpec.js +++ b/Specs/Core/BoundingSphereSpec.js @@ -487,17 +487,25 @@ defineSuite([ it('fromOrientedBoundingBox works with a result', function() { var box = OrientedBoundingBox.fromPoints(getPositions()); - var expected = new BoundingSphere(positionsCenter, positionsRadius); var sphere = new BoundingSphere(); BoundingSphere.fromOrientedBoundingBox(box, sphere); - expect(sphere).toEqual(expected); + expect(sphere.center).toEqual(positionsCenter); + expect(sphere.radius).toBeGreaterThan(1.5); + expect(sphere.radius).toBeLessThan(2.0); }); it('fromOrientedBoundingBox works without a result parameter', function() { var box = OrientedBoundingBox.fromPoints(getPositions()); - var expected = new BoundingSphere(positionsCenter, positionsRadius); var sphere = BoundingSphere.fromOrientedBoundingBox(box); - expect(sphere).toEqual(expected); + expect(sphere.center).toEqual(positionsCenter); + expect(sphere.radius).toBeGreaterThan(1.5); + expect(sphere.radius).toBeLessThan(2.0); + }); + + it('throws from fromOrientedBoundingBox with undefined orientedBoundingBox parameter', function() { + expect(function() { + BoundingSphere.fromOrientedBoundingBox(undefined); + }).toThrowDeveloperError(); }); it('intersectPlane with sphere on the positive side of a plane', function() { diff --git a/Specs/Core/CartographicGeocoderServiceSpec.js b/Specs/Core/CartographicGeocoderServiceSpec.js index 5ecf68dbca37..f0d31c766184 100644 --- a/Specs/Core/CartographicGeocoderServiceSpec.js +++ b/Specs/Core/CartographicGeocoderServiceSpec.js @@ -9,11 +9,27 @@ defineSuite([ var service = new CartographicGeocoderService(); + it('returns cartesian with matching coordinates for NS/EW input', function (done) { + var query = '35N 75W'; + service.geocode(query).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].destination).toEqual(Cartesian3.fromDegrees(-75.0, 35.0, 300.0)); + }); + }); + + it('returns cartesian with matching coordinates for EW/NS input', function (done) { + var query = '75W 35N'; + service.geocode(query).then(function(results) { + expect(results.length).toEqual(1); + expect(results[0].destination).toEqual(Cartesian3.fromDegrees(-75.0, 35.0, 300.0)); + }); + }); + it('returns cartesian with matching coordinates for long/lat/height input', function (done) { var query = ' 1.0, 2.0, 3.0 '; - service.geocode(query, function(err, results) { + service.geocode(query).then(function(results) { expect(results.length).toEqual(1); - expect(results[0]).toEqual(Cartesian3.fromDegrees(1.0, 2.0, 3.0)); + expect(results[0].destination).toEqual(Cartesian3.fromDegrees(1.0, 2.0, 3.0)); done(); }); }); @@ -21,16 +37,32 @@ defineSuite([ it('returns cartesian with matching coordinates for long/lat input', function (done) { var query = ' 1.0, 2.0 '; var defaultHeight = 300.0; - service.geocode(query, function(err, results) { + service.geocode(query).then(function(results) { expect(results.length).toEqual(1); - expect(results[0]).toEqual(Cartesian3.fromDegrees(1.0, 2.0, defaultHeight)); + expect(results[0].destination).toEqual(Cartesian3.fromDegrees(1.0, 2.0, defaultHeight)); + done(); + }); + }); + + it('returns empty array for input with only longitudinal coordinates', function (done) { + var query = ' 1e 1e '; + service.geocode(query).then(function(results) { + expect(results.length).toEqual(0); + done(); + }); + }); + + it('returns empty array for input with only one NSEW coordinate', function (done) { + var query = ' 1e 1 '; + service.geocode(query).then(function(results) { + expect(results.length).toEqual(0); done(); }); }); it('returns empty array for input with only one number', function (done) { var query = ' 2.0 '; - service.geocode(query, function(err, results) { + service.geocode(query).then(function(results) { expect(results.length).toEqual(0); done(); }); @@ -38,7 +70,7 @@ defineSuite([ it('returns empty array for with string', function (done) { var query = ' aoeu '; - service.geocode(query, function(err, results) { + service.geocode(query).then(function(results) { expect(results.length).toEqual(0); done(); }); diff --git a/Specs/Core/CesiumTerrainProviderSpec.js b/Specs/Core/CesiumTerrainProviderSpec.js index e479b6011e0d..04cb24bff555 100644 --- a/Specs/Core/CesiumTerrainProviderSpec.js +++ b/Specs/Core/CesiumTerrainProviderSpec.js @@ -9,6 +9,8 @@ defineSuite([ 'Core/loadWithXhr', 'Core/Math', 'Core/QuantizedMeshTerrainData', + 'Core/Request', + 'Core/RequestScheduler', 'Core/TerrainProvider', 'Specs/pollToPromise', 'ThirdParty/when' @@ -22,11 +24,17 @@ defineSuite([ loadWithXhr, CesiumMath, QuantizedMeshTerrainData, + Request, + RequestScheduler, TerrainProvider, pollToPromise, when) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadWithXhr.load = loadWithXhr.defaultLoad; }); @@ -84,6 +92,12 @@ defineSuite([ }); } + function createRequest() { + return new Request({ + throttleByServer : true + }); + } + it('conforms to TerrainProvider interface', function() { expect(CesiumTerrainProvider).toConformToInterface(TerrainProvider); }); @@ -403,7 +417,6 @@ defineSuite([ }); }); - it('uses the proxy if one is supplied', function() { var baseUrl = 'made/up/url'; @@ -576,15 +589,15 @@ defineSuite([ return pollToPromise(function() { return terrainProvider.ready; }).then(function() { - var promise = terrainProvider.requestTileGeometry(0, 0, 0); - expect(promise).toBeDefined(); - + var promise; var i; - for (i = 0; i < 10; ++i) { - promise = terrainProvider.requestTileGeometry(0, 0, 0); + for (i = 0; i < RequestScheduler.maximumRequestsPerServer; ++i) { + promise = terrainProvider.requestTileGeometry(0, 0, 0, createRequest()); } + RequestScheduler.update(); + expect(promise).toBeDefined(); - promise = terrainProvider.requestTileGeometry(0, 0, 0); + promise = terrainProvider.requestTileGeometry(0, 0, 0, createRequest()); expect(promise).toBeUndefined(); for (i = 0; i < deferreds.length; ++i) { diff --git a/Specs/Core/DoublyLinkedListSpec.js b/Specs/Core/DoublyLinkedListSpec.js new file mode 100644 index 000000000000..7ee82223d8b5 --- /dev/null +++ b/Specs/Core/DoublyLinkedListSpec.js @@ -0,0 +1,368 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/DoublyLinkedList' + ], function( + DoublyLinkedList) { + 'use strict'; + + it('constructs', function() { + var list = new DoublyLinkedList(); + expect(list.head).not.toBeDefined(); + expect(list.tail).not.toBeDefined(); + expect(list.length).toEqual(0); + }); + + it('adds items', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + + // node + // ^ ^ + // | | + // head tail + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node); + expect(list.length).toEqual(1); + + expect(node).toBeDefined(); + expect(node.item).toEqual(1); + expect(node.previous).not.toBeDefined(); + expect(node.next).not.toBeDefined(); + + var node2 = list.add(2); + + // node <-> node2 + // ^ ^ + // | | + // head tail + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node2); + expect(list.length).toEqual(2); + + expect(node2).toBeDefined(); + expect(node2.item).toEqual(2); + expect(node2.previous).toEqual(node); + expect(node2.next).not.toBeDefined(); + + expect(node.next).toEqual(node2); + + var node3 = list.add(3); + + // node <-> node2 <-> node3 + // ^ ^ + // | | + // head tail + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node3); + expect(list.length).toEqual(3); + + expect(node3).toBeDefined(); + expect(node3.item).toEqual(3); + expect(node3.previous).toEqual(node2); + expect(node3.next).not.toBeDefined(); + + expect(node2.next).toEqual(node3); + }); + + it('removes from a list with one item', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + + list.remove(node); + + expect(list.head).not.toBeDefined(); + expect(list.tail).not.toBeDefined(); + expect(list.length).toEqual(0); + }); + + it('removes head of list', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + + list.remove(node); + + expect(list.head).toEqual(node2); + expect(list.tail).toEqual(node2); + expect(list.length).toEqual(1); + }); + + it('removes tail of list', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + + list.remove(node2); + + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node); + expect(list.length).toEqual(1); + }); + + it('removes middle of list', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + + list.remove(node2); + + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node3); + expect(list.length).toEqual(2); + }); + + it('removes nothing', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + + list.remove(undefined); + + expect(list.head).toEqual(node); + expect(list.tail).toEqual(node); + expect(list.length).toEqual(1); + }); + + function expectOrder(list, nodes) { + // Assumes at least one node is in the list + var length = nodes.length; + + expect(list.length).toEqual(length); + + // Verify head and tail pointers + expect(list.head).toEqual(nodes[0]); + expect(list.tail).toEqual(nodes[length - 1]); + + // Verify that linked list has nodes in the expected order + var node = list.head; + for (var i = 0; i < length; ++i) { + var nextNode = (i === length - 1) ? undefined : nodes[i + 1]; + var previousNode = (i === 0) ? undefined : nodes[i - 1]; + + expect(node).toEqual(nodes[i]); + expect(node.next).toEqual(nextNode); + expect(node.previous).toEqual(previousNode); + + node = node.next; + } + } + + it('splices nextNode before node', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + var node5 = list.add(5); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 <-> node5 + // ^ ^ ^ ^ + // | | | | + // head nextNode node tail + + // After: + // + // node <-> node3 <-> node4 <-> node2 <-> node5 + // ^ ^ + // | | + // head tail + + // Move node2 after node4 + list.splice(node4, node2); + expectOrder(list, [node, node3, node4, node2, node5]); + }); + + it('splices nextNode after node', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + var node5 = list.add(5); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 <-> node5 + // ^ ^ ^ ^ + // | | | | + // head node nextNode tail + + // After: + // + // node <-> node2 <-> node4 <-> node3 <-> node5 + // ^ ^ + // | | + // head tail + + // Move node4 after node2 + list.splice(node2, node4); + expectOrder(list, [node, node2, node4, node3, node5]); + }); + + it('splices nextNode immediately before node', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ ^ + // | | | | + // head nextNode node tail + + // After: + // + // node <-> node3 <-> node2 <-> node4 + // ^ ^ + // | | + // head tail + + // Move node2 after node4 + list.splice(node3, node2); + expectOrder(list, [node, node3, node2, node4]); + }); + + it('splices nextNode immediately after node', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ ^ + // | | | | + // head node nextNode tail + + // After: does not change + + list.splice(node2, node3); + expectOrder(list, [node, node2, node3, node4]); + }); + + it('splices node === nextNode', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + + // Before: + // + // node <-> node2 <-> node3 + // ^ ^ ^ + // | | | + // head node/nextNode tail + + // After: does not change + + list.splice(node2, node2); + expectOrder(list, [node, node2, node3]); + }); + + it('splices when nextNode was tail', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ + // | | | + // head node tail/nextNode + + // After: + // + // node <-> node2 <-> node4 <-> node3 + // ^ ^ + // | | + // head tail + + list.splice(node2, node4); + expectOrder(list, [node, node2, node4, node3]); + }); + + it('splices when node was tail', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ + // | | | + // head nextNode tail/node + + // After: + // + // node <-> node3 <-> node4 <-> node2 + // ^ ^ + // | | + // head tail/node + + list.splice(node4, node2); + expectOrder(list, [node, node3, node4, node2]); + }); + + it('splices when nextNode was head', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ + // | | | + // head/nextNode node tail + + // After: + // + // node2 <-> node3 <-> node <-> node4 + // ^ ^ + // | | + // head tail + + list.splice(node3, node); + expectOrder(list, [node2, node3, node, node4]); + }); + + it('splices when node was head', function() { + var list = new DoublyLinkedList(); + var node = list.add(1); + var node2 = list.add(2); + var node3 = list.add(3); + var node4 = list.add(4); + + // Before: + // + // node <-> node2 <-> node3 <-> node4 + // ^ ^ ^ + // | | | + // head/node nextNode tail + + // After: + // + // node <-> node3 <-> node2 <-> node4 + // ^ ^ + // | | + // head tail + + list.splice(node, node3); + expectOrder(list, [node, node3, node2, node4]); + }); +}); diff --git a/Specs/Core/EllipsoidGeometrySpec.js b/Specs/Core/EllipsoidGeometrySpec.js index cf1f4a2a62a1..c53930d3d8ae 100644 --- a/Specs/Core/EllipsoidGeometrySpec.js +++ b/Specs/Core/EllipsoidGeometrySpec.js @@ -13,6 +13,22 @@ defineSuite([ createPackableSpecs) { 'use strict'; + it('constructor rounds floating-point slicePartitions', function() { + var m = new EllipsoidGeometry({ + slicePartitions: 3.5, + stackPartitions: 3 + }); + expect(m._slicePartitions).toEqual(4); + }); + + it('constructor rounds floating-point stackPartitions', function() { + var m = new EllipsoidGeometry({ + slicePartitions: 3, + stackPartitions: 3.5 + }); + expect(m._stackPartitions).toEqual(4); + }); + it('constructor throws with invalid slicePartitions', function() { expect(function() { return new EllipsoidGeometry({ diff --git a/Specs/Core/EllipsoidOutlineGeometrySpec.js b/Specs/Core/EllipsoidOutlineGeometrySpec.js index e527c5d14bb1..ffa41f6fe590 100644 --- a/Specs/Core/EllipsoidOutlineGeometrySpec.js +++ b/Specs/Core/EllipsoidOutlineGeometrySpec.js @@ -33,6 +33,33 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('constructor rounds floating-point slicePartitions', function() { + var m = new EllipsoidOutlineGeometry({ + slicePartitions: 3.5, + stackPartitions: 3, + subdivisions: 3 + }); + expect(m._slicePartitions).toEqual(4); + }); + + it('constructor rounds floating-point stackPartitions', function() { + var m = new EllipsoidOutlineGeometry({ + slicePartitions: 3, + stackPartitions: 3.5, + subdivisions: 3 + }); + expect(m._stackPartitions).toEqual(4); + }); + + it('constructor rounds floating-point subdivisions', function() { + var m = new EllipsoidOutlineGeometry({ + slicePartitions: 3, + stackPartitions: 3, + subdivisions: 3.5 + }); + expect(m._subdivisions).toEqual(4); + }); + it('computes positions', function() { var m = EllipsoidOutlineGeometry.createGeometry(new EllipsoidOutlineGeometry({ stackPartitions : 3, diff --git a/Specs/Core/GeometryPipelineSpec.js b/Specs/Core/GeometryPipelineSpec.js index 71470fd08ffe..0980b1c8048a 100644 --- a/Specs/Core/GeometryPipelineSpec.js +++ b/Specs/Core/GeometryPipelineSpec.js @@ -345,7 +345,7 @@ defineSuite([ } }); - geometry = GeometryPipeline.reorderForPreVertexCache(geometry); + GeometryPipeline.reorderForPreVertexCache(geometry); }).toThrowDeveloperError(); }); diff --git a/Specs/Core/GoogleEarthEnterpriseMetadataSpec.js b/Specs/Core/GoogleEarthEnterpriseMetadataSpec.js new file mode 100644 index 000000000000..d3b2237c40ac --- /dev/null +++ b/Specs/Core/GoogleEarthEnterpriseMetadataSpec.js @@ -0,0 +1,245 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/GoogleEarthEnterpriseMetadata', + 'Core/GoogleEarthEnterpriseTileInformation', + 'Core/decodeGoogleEarthEnterpriseData', + 'Core/DefaultProxy', + 'Core/defaultValue', + 'Core/loadWithXhr', + 'Core/Math', + 'Core/Request', + 'ThirdParty/when' +], function( + GoogleEarthEnterpriseMetadata, + GoogleEarthEnterpriseTileInformation, + decodeGoogleEarthEnterpriseData, + DefaultProxy, + defaultValue, + loadWithXhr, + CesiumMath, + Request, + when) { + 'use strict'; + + it('tileXYToQuadKey', function() { + expect(GoogleEarthEnterpriseMetadata.tileXYToQuadKey(1, 0, 0)).toEqual('2'); + expect(GoogleEarthEnterpriseMetadata.tileXYToQuadKey(1, 2, 1)).toEqual('02'); + expect(GoogleEarthEnterpriseMetadata.tileXYToQuadKey(3, 5, 2)).toEqual('021'); + expect(GoogleEarthEnterpriseMetadata.tileXYToQuadKey(4, 7, 2)).toEqual('100'); + }); + + it('quadKeyToTileXY', function() { + expect(GoogleEarthEnterpriseMetadata.quadKeyToTileXY('2')).toEqual({ + x : 1, + y : 0, + level : 0 + }); + expect(GoogleEarthEnterpriseMetadata.quadKeyToTileXY('02')).toEqual({ + x : 1, + y : 2, + level : 1 + }); + expect(GoogleEarthEnterpriseMetadata.quadKeyToTileXY('021')).toEqual({ + x : 3, + y : 5, + level : 2 + }); + expect(GoogleEarthEnterpriseMetadata.quadKeyToTileXY('100')).toEqual({ + x : 4, + y : 7, + level : 2 + }); + }); + + it('decode', function() { + CesiumMath.setRandomNumberSeed(123123); + var key = new Uint8Array(1025); + var data = new Uint8Array(1025); + for (var i = 0; i < 1025; ++i) { + key[i] = Math.floor(CesiumMath.nextRandomNumber() * 256); + data[i] = Math.floor(CesiumMath.nextRandomNumber() * 256); + } + + var keyBuffer = key.buffer.slice(0, 1024); // Key length should be divisible by 4 + var dataBuffer = data.buffer.slice(); + var a = new Uint8Array(dataBuffer); + decodeGoogleEarthEnterpriseData(keyBuffer, dataBuffer); + expect(a).not.toEqual(data); + + // For the algorithm encode/decode are the same + decodeGoogleEarthEnterpriseData(keyBuffer, dataBuffer); + + expect(a).toEqual(data); + }); + + it('decode requires key' , function() { + var data = new Uint8Array(3); + + expect(function() { + decodeGoogleEarthEnterpriseData(undefined, data.buffer); + }).toThrowDeveloperError(); + }); + + it('decode requires data' , function() { + var key = new Uint8Array(4); + + expect(function() { + decodeGoogleEarthEnterpriseData(key.buffer); + }).toThrowDeveloperError(); + }); + + it('decode throws if key length isn\'t greater than 0 and a multiple 4' , function() { + var key; + var data = new Uint8Array(3); + + key = new Uint8Array(0); + expect(function() { + decodeGoogleEarthEnterpriseData(key.buffer, data.buffer); + }).toThrowRuntimeError(); + + key = new Uint8Array(1); + expect(function() { + decodeGoogleEarthEnterpriseData(key.buffer, data.buffer); + }).toThrowRuntimeError(); + + key = new Uint8Array(2); + expect(function() { + decodeGoogleEarthEnterpriseData(key.buffer, data.buffer); + }).toThrowRuntimeError(); + + key = new Uint8Array(3); + expect(function() { + decodeGoogleEarthEnterpriseData(key.buffer, data.buffer); + }).toThrowRuntimeError(); + }); + + it('populateSubtree', function() { + var quad = '0123'; + var index = 0; + spyOn(GoogleEarthEnterpriseMetadata.prototype, 'getQuadTreePacket').and.callFake(function(quadKey, version, request) { + quadKey = defaultValue(quadKey, '') + index.toString(); + this._tileInfo[quadKey] = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + index = (index + 1) % 4; + + return when(); + }); + + var metadata = new GoogleEarthEnterpriseMetadata({ + url : 'http://test.server' + }); + var request = new Request({ + throttle : true + }); + return metadata.readyPromise + .then(function() { + var tileXY = GoogleEarthEnterpriseMetadata.quadKeyToTileXY(quad); + return metadata.populateSubtree(tileXY.x, tileXY.y, tileXY.level, request); + }) + .then(function() { + expect(GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket.calls.count()).toEqual(4); + expect(GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket).toHaveBeenCalledWith('', 1); + expect(GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket).toHaveBeenCalledWith('0', 1, request); + expect(GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket).toHaveBeenCalledWith('01', 1, request); + expect(GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket).toHaveBeenCalledWith('012', 1, request); + + var tileInfo = metadata._tileInfo; + expect(tileInfo['0']).toBeDefined(); + expect(tileInfo['01']).toBeDefined(); + expect(tileInfo['012']).toBeDefined(); + expect(tileInfo['0123']).toBeDefined(); + }); + }); + + it('resolves readyPromise', function() { + var baseurl = 'http://fake.fake.invalid/'; + + var req = 0; + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(responseType).toEqual('arraybuffer'); + if (req === 0) { + expect(url).toEqual(baseurl + 'dbRoot.v5?output=proto'); + deferred.reject(); // Reject dbRoot request and use defaults + } else { + expect(url).toEqual(baseurl + 'flatfile?q2-0-q.1'); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterprise/gee.metadata', responseType, method, data, headers, deferred); + } + ++req; + }); + + var provider = new GoogleEarthEnterpriseMetadata({ + url : baseurl + }); + + return provider.readyPromise.then(function(result) { + expect(result).toBe(true); + + expect(provider.imageryPresent).toBe(true); + expect(provider.protoImagery).toBeUndefined(); + expect(provider.terrainPresent).toBe(true); + expect(provider.negativeAltitudeThreshold).toBe(CesiumMath.EPSILON12); + expect(provider.negativeAltitudeExponentBias).toBe(32); + expect(provider.providers).toEqual({}); + + var tileInfo = provider._tileInfo['0']; + expect(tileInfo).toBeDefined(); + expect(tileInfo._bits).toEqual(0x40); + expect(tileInfo.cnodeVersion).toEqual(2); + expect(tileInfo.imageryVersion).toEqual(1); + expect(tileInfo.terrainVersion).toEqual(1); + expect(tileInfo.ancestorHasTerrain).toEqual(false); + expect(tileInfo.terrainState).toBeUndefined(); + }); + }); + + it('rejects readyPromise on error', function() { + var url = 'host.invalid/'; + var provider = new GoogleEarthEnterpriseMetadata({ + url : url + }); + + return provider.readyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(e) { + expect(e.message).toContain(url); + }); + }); + + it('routes requests through a proxy if one is specified', function() { + var proxy = new DefaultProxy('/proxy/'); + var baseurl = 'http://fake.fake.invalid/'; + + var req = 0; + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(responseType).toEqual('arraybuffer'); + if (req === 0) { + expect(url).toEqual(proxy.getURL(baseurl + 'dbRoot.v5?output=proto')); + deferred.reject(); // Reject dbRoot request and use defaults + } else { + expect(url).toEqual(proxy.getURL(baseurl + 'flatfile?q2-0-q.1')); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterprise/gee.metadata', responseType, method, data, headers, deferred); + } + ++req; + }); + + var provider = new GoogleEarthEnterpriseMetadata({ + url : baseurl, + proxy : proxy + }); + + expect(provider.url).toEqual(baseurl); + expect(provider.proxy).toEqual(proxy); + + return provider.readyPromise.then(function(result) { + expect(result).toBe(true); + + var tileInfo = provider._tileInfo['0']; + expect(tileInfo).toBeDefined(); + expect(tileInfo._bits).toEqual(0x40); + expect(tileInfo.cnodeVersion).toEqual(2); + expect(tileInfo.imageryVersion).toEqual(1); + expect(tileInfo.terrainVersion).toEqual(1); + expect(tileInfo.ancestorHasTerrain).toEqual(false); + expect(tileInfo.terrainState).toBeUndefined(); + }); + }); +}); diff --git a/Specs/Core/GoogleEarthEnterpriseTerrainDataSpec.js b/Specs/Core/GoogleEarthEnterpriseTerrainDataSpec.js new file mode 100644 index 000000000000..e0efa2abec7b --- /dev/null +++ b/Specs/Core/GoogleEarthEnterpriseTerrainDataSpec.js @@ -0,0 +1,505 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/GoogleEarthEnterpriseTerrainData', + 'Core/BoundingSphere', + 'Core/Cartesian3', + 'Core/Cartographic', + 'Core/Ellipsoid', + 'Core/GeographicTilingScheme', + 'Core/Math', + 'Core/Matrix4', + 'Core/Rectangle', + 'Core/TerrainData', + 'Core/TerrainMesh', + 'Core/Transforms', + 'ThirdParty/when' +], function( + GoogleEarthEnterpriseTerrainData, + BoundingSphere, + Cartesian3, + Cartographic, + Ellipsoid, + GeographicTilingScheme, + CesiumMath, + Matrix4, + Rectangle, + TerrainData, + TerrainMesh, + Transforms, + when) { + 'use strict'; + + var sizeOfUint8 = Uint8Array.BYTES_PER_ELEMENT; + var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT; + var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT; + var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT; + var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT; + var toEarthRadii = 1.0 / 6371010.0; + + function getBuffer(tilingScheme, x, y, level) { + var rectangle = tilingScheme.tileXYToRectangle(x, y, level); + var center = Rectangle.center(rectangle); + var southwest = Rectangle.southwest(rectangle); + var stepX = CesiumMath.toDegrees(rectangle.width / 2) / 180.0; + var stepY = CesiumMath.toDegrees(rectangle.height / 2) / 180.0; + + // 2 Uint8s: x and y values in units of step + var pointSize = 2 * sizeOfUint8 + sizeOfFloat; + + // 3 shorts + var faceSize = 3 * sizeOfUint16; + + // Doubles: OriginX, OriginY, SizeX, SizeY + // Int32s: numPoints, numFaces, level + // 4 corner points + // 2 face (3 shorts) + var quadSize = 4 * sizeOfDouble + 3 * sizeOfInt32 + 4 * pointSize + 2 * faceSize; + + // QuadSize + size of each quad + var totalSize = 4 * (quadSize + sizeOfUint32); + var buf = new ArrayBuffer(totalSize); + var dv = new DataView(buf); + + var altitudeStart = 0; + var offset = 0; + for (var i = 0; i < 4; ++i) { + altitudeStart = 0; + dv.setUint32(offset, quadSize, true); + offset += sizeOfUint32; + + // Origin + var xOrigin = southwest.longitude; + var yOrigin = southwest.latitude; + + if ((i & 2) !== 0) { // Top row + if ((i & 1) === 0) { // NE + xOrigin = center.longitude; + altitudeStart = 10; + } + yOrigin = center.latitude; + } else if ((i & 1) !== 0) { // SE + xOrigin = center.longitude; + altitudeStart = 10; + } + + dv.setFloat64(offset, CesiumMath.toDegrees(xOrigin) / 180.0, true); + offset += sizeOfDouble; + dv.setFloat64(offset, CesiumMath.toDegrees(yOrigin) / 180.0, true); + offset += sizeOfDouble; + + // Step - Each step is a degree + dv.setFloat64(offset, stepX, true); + offset += sizeOfDouble; + dv.setFloat64(offset, stepY, true); + offset += sizeOfDouble; + + // NumPoints + dv.setInt32(offset, 4, true); + offset += sizeOfInt32; + + // NumFaces + dv.setInt32(offset, 2, true); + offset += sizeOfInt32; + + // Level + dv.setInt32(offset, 0, true); + offset += sizeOfInt32; + + // Points + for (var j = 0; j < 4; ++j) { + var xPos = 0; + var yPos = 0; + var altitude = altitudeStart; + if (j & 1) { + ++xPos; + altitude += 10; + } + if (j & 2) { + ++yPos; + } + + dv.setUint8(offset++, xPos); + dv.setUint8(offset++, yPos); + dv.setFloat32(offset, altitude * toEarthRadii, true); + offset += sizeOfFloat; + } + + // Faces + var indices = [0, 1, 2, 1, 3, 2]; + for (j = 0; j < indices.length; ++j) { + dv.setUint16(offset, indices[j], true); + offset += sizeOfUint16; + } + } + + return buf; + } + + it('conforms to TerrainData interface', function() { + expect(GoogleEarthEnterpriseTerrainData).toConformToInterface(TerrainData); + }); + + describe('upsample', function() { + it('works for all four children of a simple quad', function() { + var maxShort = 32767; + var tilingScheme = new GeographicTilingScheme(); + var buffer = getBuffer(tilingScheme, 0, 0, 0); + var data = new GoogleEarthEnterpriseTerrainData({ + buffer : buffer, + childTileMask : 15, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + tilingScheme = new GeographicTilingScheme(); + var childRectangles = [ + tilingScheme.tileXYToRectangle(0, 0, 1), + tilingScheme.tileXYToRectangle(1, 0, 1), + tilingScheme.tileXYToRectangle(0, 1, 1), + tilingScheme.tileXYToRectangle(1, 1, 1) + ]; + + return when(data.createMesh(tilingScheme, 0, 0, 0, 1)).then(function() { + var swPromise = data.upsample(tilingScheme, 0, 0, 0, 0, 0, 1); + var sePromise = data.upsample(tilingScheme, 0, 0, 0, 1, 0, 1); + var nwPromise = data.upsample(tilingScheme, 0, 0, 0, 0, 1, 1); + var nePromise = data.upsample(tilingScheme, 0, 0, 0, 1, 1, 1); + return when.join(swPromise, sePromise, nwPromise, nePromise); + }).then(function(upsampleResults) { + expect(upsampleResults.length).toBe(4); + + for (var i = 0; i < upsampleResults.length; ++i) { + var upsampled = upsampleResults[i]; + expect(upsampled).toBeDefined(); + + var uBuffer = upsampled._uValues; + var vBuffer = upsampled._vValues; + var ib = upsampled._indices; + var heights = upsampled._heightValues; + + expect(uBuffer.length).toBe(4); + expect(vBuffer.length).toBe(4); + expect(heights.length).toBe(4); + expect(ib.length).toBe(6); + + var rectangle = childRectangles[i]; + var north = 0; + var south = 0; + var east = 0; + var west = 0; + var index, u, v; + for (var j = 0; j < ib.length; ++j) { + index = ib[j]; + u = (uBuffer[index] / maxShort) * rectangle.width + rectangle.west; + v = (vBuffer[index] / maxShort) * rectangle.height + rectangle.south; + if (CesiumMath.equalsEpsilon(u, rectangle.west, CesiumMath.EPSILON7)) { + ++west; + } else if (CesiumMath.equalsEpsilon(u, rectangle.east, CesiumMath.EPSILON7)) { + ++east; + } + + if (CesiumMath.equalsEpsilon(v, rectangle.south, CesiumMath.EPSILON7)) { + ++south; + } else if (CesiumMath.equalsEpsilon(v, rectangle.north, CesiumMath.EPSILON7)) { + ++north; + } + } + + // Each one is made up of 2 triangles + expect(north).toEqual(3); + expect(south).toEqual(3); + expect(east).toEqual(3); + expect(west).toEqual(3); + + // Each side of quad has 2 edge points + expect(upsampled._westIndices.length).toEqual(2); + expect(upsampled._southIndices.length).toEqual(2); + expect(upsampled._eastIndices.length).toEqual(2); + expect(upsampled._northIndices.length).toEqual(2); + } + }); + }); + }); + + describe('createMesh', function() { + var data; + var tilingScheme; + var buffer; + + beforeEach(function() { + tilingScheme = new GeographicTilingScheme(); + buffer = getBuffer(tilingScheme, 0, 0, 0); + data = new GoogleEarthEnterpriseTerrainData({ + buffer : buffer, + childTileMask : 15, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + }); + + it('requires tilingScheme', function() { + expect(function() { + data.createMesh(undefined, 0, 0, 0); + }).toThrowDeveloperError(); + }); + + it('requires x', function() { + expect(function() { + data.createMesh(tilingScheme, undefined, 0, 0); + }).toThrowDeveloperError(); + }); + + it('requires y', function() { + expect(function() { + data.createMesh(tilingScheme, 0, undefined, 0); + }).toThrowDeveloperError(); + }); + + it('requires level', function() { + expect(function() { + data.createMesh(tilingScheme, 0, 0, undefined); + }).toThrowDeveloperError(); + }); + + it('creates specified vertices plus skirt vertices', function() { + var rectangle = tilingScheme.tileXYToRectangle(0, 0, 0); + + var wgs84 = Ellipsoid.WGS84; + return data.createMesh(tilingScheme, 0, 0, 0).then(function(mesh) { + expect(mesh).toBeInstanceOf(TerrainMesh); + expect(mesh.vertices.length).toBe(17 * mesh.encoding.getStride()); // 9 regular + 8 skirt vertices + expect(mesh.indices.length).toBe(4 * 6 * 3); // 2 regular + 4 skirt triangles per quad + expect(mesh.minimumHeight).toBe(0); + expect(mesh.maximumHeight).toBeCloseTo(20, 5); + + var encoding = mesh.encoding; + var cartesian = new Cartesian3(); + var cartographic = new Cartographic(); + var count = mesh.vertices.length / mesh.encoding.getStride(); + for (var i = 0; i < count; ++i) { + var height = encoding.decodeHeight(mesh.vertices, i); + if (i < 9) { // Original vertices + expect(height).toBeBetween(0, 20); + + // Only test on original positions as the skirts angle outward + encoding.decodePosition(mesh.vertices, i, cartesian); + wgs84.cartesianToCartographic(cartesian, cartographic); + cartographic.longitude = CesiumMath.convertLongitudeRange(cartographic.longitude); + expect(Rectangle.contains(rectangle, cartographic)).toBe(true); + } else { // Skirts + expect(height).toBeBetween(-1000, -980); + } + } + }); + }); + + it('exaggerates mesh', function() { + return data.createMesh(tilingScheme, 0, 0, 0, 2).then(function(mesh) { + expect(mesh).toBeInstanceOf(TerrainMesh); + expect(mesh.vertices.length).toBe(17 * mesh.encoding.getStride()); // 9 regular + 8 skirt vertices + expect(mesh.indices.length).toBe(4 * 6 * 3); // 2 regular + 4 skirt triangles per quad + expect(mesh.minimumHeight).toBe(0); + expect(mesh.maximumHeight).toBeCloseTo(40, 5); + + var encoding = mesh.encoding; + var count = mesh.vertices.length / mesh.encoding.getStride(); + for (var i = 0; i < count; ++i) { + var height = encoding.decodeHeight(mesh.vertices, i); + if (i < 9) { // Original vertices + expect(height).toBeBetween(0, 40); + } else { // Skirts + expect(height).toBeBetween(-1000, -960); + } + } + }); + }); + }); + + describe('interpolateHeight', function() { + var tilingScheme; + var rectangle; + var mesh; + + beforeEach(function() { + tilingScheme = new GeographicTilingScheme(); + rectangle = tilingScheme.tileXYToRectangle(7, 6, 5); + var buffer = getBuffer(tilingScheme, 7, 6, 5); + mesh = new GoogleEarthEnterpriseTerrainData({ + buffer : buffer, + childTileMask : 15, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + }); + + it('clamps coordinates if given a position outside the mesh', function() { + expect(mesh.interpolateHeight(rectangle, 0.0, 0.0)).toBe(mesh.interpolateHeight(rectangle, rectangle.east, rectangle.south)); + }); + + it('returns a height interpolated from the correct triangle', function() { + // position in the northwest quadrant of the tile. + var longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.25; + var latitude = rectangle.south + (rectangle.north - rectangle.south) * 0.75; + + var result = mesh.interpolateHeight(rectangle, longitude, latitude); + expect(result).toBeBetween(0.0, 10.0); + + // position in the southeast quadrant of the tile. + longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.75; + latitude = rectangle.south + (rectangle.north - rectangle.south) * 0.25; + + result = mesh.interpolateHeight(rectangle, longitude, latitude); + expect(result).toBeBetween(10.0, 20.0); + + // position on the line between the southwest and northeast corners. + longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.5; + latitude = rectangle.south + (rectangle.north - rectangle.south) * 0.5; + + result = mesh.interpolateHeight(rectangle, longitude, latitude); + expect(result).toEqualEpsilon(10.0, 1e-6); + }); + }); + + describe('isChildAvailable', function() { + var data; + + beforeEach(function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 15, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + }); + + it('requires thisX', function() { + expect(function() { + data.isChildAvailable(undefined, 0, 0, 0); + }).toThrowDeveloperError(); + }); + + it('requires thisY', function() { + expect(function() { + data.isChildAvailable(0, undefined, 0, 0); + }).toThrowDeveloperError(); + }); + + it('requires childX', function() { + expect(function() { + data.isChildAvailable(0, 0, undefined, 0); + }).toThrowDeveloperError(); + }); + + it('requires childY', function() { + expect(function() { + data.isChildAvailable(0, 0, 0, undefined); + }).toThrowDeveloperError(); + }); + + it('returns true for all children when child mask is not explicitly specified', function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + expect(data.isChildAvailable(10, 20, 20, 40)).toBe(true); + expect(data.isChildAvailable(10, 20, 21, 40)).toBe(true); + expect(data.isChildAvailable(10, 20, 20, 41)).toBe(true); + expect(data.isChildAvailable(10, 20, 21, 41)).toBe(true); + }); + + it('works when only southwest child is available', function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 1, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 20, 41)).toBe(true); + expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false); + }); + + it('works when only southeast child is available', function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 2, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 41)).toBe(true); + }); + + it('works when only northeast child is available', function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 4, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 40)).toBe(true); + expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false); + }); + + it('works when only northwest child is available', function() { + data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 8, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + + expect(data.isChildAvailable(10, 20, 20, 40)).toBe(true); + expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false); + expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false); + expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false); + }); + }); + + it('requires buffer', function() { + expect(function() { + /*eslint-disable no-unused-vars*/ + var data = new GoogleEarthEnterpriseTerrainData({ + childTileMask : 8, + negativeAltitudeExponentBias : 32, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + /*eslint-enable no-unused-vars*/ + }).toThrowDeveloperError(); + }); + + it('requires negativeAltitudeExponentBias', function() { + expect(function() { + /*eslint-disable no-unused-vars*/ + var data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 8, + negativeElevationThreshold : CesiumMath.EPSILON12 + }); + /*eslint-enable no-unused-vars*/ + }).toThrowDeveloperError(); + }); + + it('requires negativeElevationThreshold', function() { + expect(function() { + /*eslint-disable no-unused-vars*/ + var data = new GoogleEarthEnterpriseTerrainData({ + buffer : new ArrayBuffer(1), + childTileMask : 8, + negativeAltitudeExponentBias : 32 + }); + /*eslint-enable no-unused-vars*/ + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js b/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js new file mode 100644 index 000000000000..9917d46c18be --- /dev/null +++ b/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js @@ -0,0 +1,378 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/GoogleEarthEnterpriseTerrainProvider', + 'Core/DefaultProxy', + 'Core/defaultValue', + 'Core/defined', + 'Core/Ellipsoid', + 'Core/GeographicTilingScheme', + 'Core/GoogleEarthEnterpriseMetadata', + 'Core/GoogleEarthEnterpriseTerrainData', + 'Core/GoogleEarthEnterpriseTileInformation', + 'Core/loadImage', + 'Core/loadWithXhr', + 'Core/Math', + 'Core/Request', + 'Core/RequestScheduler', + 'Core/TerrainProvider', + 'Specs/pollToPromise', + 'ThirdParty/when' +], function( + GoogleEarthEnterpriseTerrainProvider, + DefaultProxy, + defaultValue, + defined, + Ellipsoid, + GeographicTilingScheme, + GoogleEarthEnterpriseMetadata, + GoogleEarthEnterpriseTerrainData, + GoogleEarthEnterpriseTileInformation, + loadImage, + loadWithXhr, + CesiumMath, + Request, + RequestScheduler, + TerrainProvider, + pollToPromise, + when) { + 'use strict'; + + function installMockGetQuadTreePacket() { + spyOn(GoogleEarthEnterpriseMetadata.prototype, 'getQuadTreePacket').and.callFake(function(quadKey, version) { + quadKey = defaultValue(quadKey, ''); + var t = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + t.ancestorHasTerrain = true; + this._tileInfo[quadKey + '0'] = t; + + t = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + t.ancestorHasTerrain = true; + this._tileInfo[quadKey + '1'] = t; + + t = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + t.ancestorHasTerrain = true; + this._tileInfo[quadKey + '2'] = t; + + t = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + t.ancestorHasTerrain = true; + this._tileInfo[quadKey + '3'] = t; + + return when(); + }); + } + + var terrainProvider; + + function waitForTile(level, x, y, f) { + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return pollToPromise(function() { + return terrainProvider.ready && terrainProvider.getTileDataAvailable(x, y, level); + }).then(function() { + var promise = terrainProvider.requestTileGeometry(level, x, y); + + return when(promise, f, function(error) { + expect('requestTileGeometry').toBe('returning a tile.'); // test failure + }); + }); + } + + function createRequest() { + return new Request({ + throttleByServer : true + }); + } + + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + + afterEach(function() { + loadWithXhr.load = loadWithXhr.defaultLoad; + }); + + it('conforms to TerrainProvider interface', function() { + expect(GoogleEarthEnterpriseTerrainProvider).toConformToInterface(TerrainProvider); + }); + + it('constructor throws if url is not provided', function() { + expect(function() { + return new GoogleEarthEnterpriseTerrainProvider(); + }).toThrowDeveloperError(); + + expect(function() { + return new GoogleEarthEnterpriseTerrainProvider({}); + }).toThrowDeveloperError(); + }); + + it('resolves readyPromise', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return terrainProvider.readyPromise.then(function(result) { + expect(result).toBe(true); + expect(terrainProvider.ready).toBe(true); + }); + }); + + it('uses geographic tiling scheme by default', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + var tilingScheme = terrainProvider.tilingScheme; + expect(tilingScheme instanceof GeographicTilingScheme).toBe(true); + }); + }); + + it('can use a custom ellipsoid', function() { + installMockGetQuadTreePacket(); + + var ellipsoid = new Ellipsoid(1, 2, 3); + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url', + ellipsoid : ellipsoid + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + expect(terrainProvider.tilingScheme.ellipsoid).toEqual(ellipsoid); + }); + }); + + it('has error event', function() { + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + expect(terrainProvider.errorEvent).toBeDefined(); + expect(terrainProvider.errorEvent).toBe(terrainProvider.errorEvent); + }); + + it('returns reasonable geometric error for various levels', function() { + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + expect(terrainProvider.getLevelMaximumGeometricError(0)).toBeGreaterThan(0.0); + expect(terrainProvider.getLevelMaximumGeometricError(0)).toEqualEpsilon(terrainProvider.getLevelMaximumGeometricError(1) * 2.0, CesiumMath.EPSILON10); + expect(terrainProvider.getLevelMaximumGeometricError(1)).toEqualEpsilon(terrainProvider.getLevelMaximumGeometricError(2) * 2.0, CesiumMath.EPSILON10); + }); + + it('readyPromise rejects if there isn\'t terrain', function() { + installMockGetQuadTreePacket(); + + var metadata = new GoogleEarthEnterpriseMetadata({ + url : 'made/up/url' + }); + + metadata.terrainPresent = false; + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + metadata : metadata + }); + + return terrainProvider.readyPromise + .then(function() { + fail('Server does not have terrain, so we shouldn\'t resolve.'); + }) + .otherwise(function() { + expect(terrainProvider.ready).toBe(false); + }); + }); + + it('logo is undefined if credit is not provided', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + expect(terrainProvider.credit).toBeUndefined(); + }); + }); + + it('logo is defined if credit is provided', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url', + credit : 'thanks to our awesome made up contributors!' + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + expect(terrainProvider.credit).toBeDefined(); + }); + }); + + it('has a water mask is false', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + expect(terrainProvider.hasWaterMask).toBe(false); + }); + }); + + it('has vertex normals is false', function() { + installMockGetQuadTreePacket(); + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : 'made/up/url' + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + expect(terrainProvider.hasVertexNormals).toBe(false); + }); + }); + + describe('requestTileGeometry', function() { + it('uses the proxy if one is supplied', function() { + installMockGetQuadTreePacket(); + var baseUrl = 'made/up/url'; + + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + expect(url.indexOf('/proxy/?')).toBe(0); + + loadWithXhr.defaultLoad('Data/GoogleEarthEnterprise/gee.terrain', responseType, method, data, headers, deferred); + }; + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : baseUrl, + proxy : new DefaultProxy('/proxy/') + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + return terrainProvider.requestTileGeometry(0, 0, 0); + }); + }); + + it('provides GoogleEarthEnterpriseTerrainData', function() { + installMockGetQuadTreePacket(); + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + loadWithXhr.defaultLoad('Data/GoogleEarthEnterprise/gee.terrain', responseType, method, data, headers, deferred); + }; + + return waitForTile(0, 0, 0, function(loadedData) { + expect(loadedData).toBeInstanceOf(GoogleEarthEnterpriseTerrainData); + }); + }); + + it('returns undefined if too many requests are already in progress', function() { + installMockGetQuadTreePacket(); + var baseUrl = 'made/up/url'; + + var deferreds = []; + var loadRealTile = true; + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + if (url.indexOf('dbRoot.v5') !== -1) { + return deferred.reject(); // Just reject dbRoot file and use defaults. + } + + if (loadRealTile) { + loadRealTile = false; + return loadWithXhr.defaultLoad('Data/GoogleEarthEnterprise/gee.terrain', responseType, method, data, headers, deferred); + } + // Do nothing, so requests never complete + deferreds.push(deferred); + }; + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : baseUrl + }); + + var promises = []; + return pollToPromise(function() { + return terrainProvider.ready; + }) + .then(function() { + return pollToPromise(function() { + var b = true; + for (var i = 0; i < 10; ++i) { + b = b && terrainProvider.getTileDataAvailable(i, i, i); + } + return b && terrainProvider.getTileDataAvailable(1, 2, 3); + }); + }) + .then(function() { + var promise; + for (var i = 0; i < RequestScheduler.maximumRequestsPerServer; ++i) { + promise = terrainProvider.requestTileGeometry(i, i, i, createRequest()); + promises.push(promise); + } + RequestScheduler.update(); + expect(promise).toBeDefined(); + + return terrainProvider.requestTileGeometry(1, 2, 3, createRequest()); + }) + .then(function(terrainData) { + expect(terrainData).toBeUndefined(); + for (var i = 0; i < deferreds.length; ++i) { + deferreds[i].resolve(); + } + + // Parsing terrain will fail, so just eat the errors and request the tile again + return when.all(promises) + .otherwise(function() { + loadRealTile = true; + return terrainProvider.requestTileGeometry(1, 2, 3); + }); + }) + .then(function(terrainData) { + expect(terrainData).toBeDefined(); + }); + }); + + it('supports getTileDataAvailable()', function() { + installMockGetQuadTreePacket(); + var baseUrl = 'made/up/url'; + + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + loadWithXhr.defaultLoad('Data/CesiumTerrainTileJson/tile.terrain', responseType, method, data, headers, deferred); + }; + + terrainProvider = new GoogleEarthEnterpriseTerrainProvider({ + url : baseUrl + }); + + return pollToPromise(function() { + return terrainProvider.ready; + }).then(function() { + var tileInfo = terrainProvider._metadata._tileInfo; + var info = tileInfo[GoogleEarthEnterpriseMetadata.tileXYToQuadKey(0, 1, 0)]; + info._bits = 0x7F; // Remove terrain bit from 0,1,0 tile + info.terrainState = 1; // NONE + info.ancestorHasTerrain = true; + + expect(terrainProvider.getTileDataAvailable(0, 0, 0)).toBe(true); + expect(terrainProvider.getTileDataAvailable(0, 1, 0)).toBe(false); + expect(terrainProvider.getTileDataAvailable(1, 0, 0)).toBe(true); + expect(terrainProvider.getTileDataAvailable(1, 1, 0)).toBe(true); + expect(terrainProvider.getTileDataAvailable(0, 0, 2)).toBe(false); + }); + }); + }); +}); diff --git a/Specs/Core/HeapSpec.js b/Specs/Core/HeapSpec.js new file mode 100644 index 000000000000..4d41e7e48fb0 --- /dev/null +++ b/Specs/Core/HeapSpec.js @@ -0,0 +1,175 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/Heap' +], function( + Heap) { + 'use strict'; + + var length = 100; + + function checkHeap(heap, comparator) { + var array = heap.internalArray; + var pass = true; + var length = heap.length; + for (var i = 0; i < length; ++i) { + var left = 2 * (i + 1) - 1; + var right = 2 * (i + 1); + if (left < heap.length) { + pass = pass && (comparator(array[i], array[left]) <= 0); + } + if (right < heap.length) { + pass = pass && (comparator(array[i], array[right]) <= 0); + } + } + return pass; + } + + // min heap + function comparator(a, b) { + return a - b; + } + + it('maintains heap property on insert', function() { + var heap = new Heap({ + comparator : comparator + }); + var pass = true; + for (var i = 0; i < length; ++i) { + heap.insert(Math.random()); + pass = pass && checkHeap(heap, comparator); + } + + expect(pass).toBe(true); + }); + + it('maintains heap property on pop', function() { + var heap = new Heap({ + comparator : comparator + }); + var i; + for (i = 0; i < length; ++i) { + heap.insert(Math.random()); + } + var pass = true; + for (i = 0; i < length; ++i) { + heap.pop(); + pass = pass && checkHeap(heap, comparator); + } + expect(pass).toBe(true); + }); + + it('limited by maximum length', function() { + var heap = new Heap({ + comparator : comparator + }); + heap.maximumLength = length / 2; + var pass = true; + for (var i = 0; i < length; ++i) { + heap.insert(Math.random()); + pass = pass && checkHeap(heap, comparator); + } + expect(pass).toBe(true); + expect(heap.length <= heap.maximumLength).toBe(true); + // allowed one extra slot for swapping + expect(heap.internalArray.length).toBeLessThanOrEqualTo(heap.maximumLength + 1); + }); + + it('pops in sorted order', function() { + var heap = new Heap({ + comparator : comparator + }); + var i; + for (i = 0; i < length; ++i) { + heap.insert(Math.random()); + } + var curr = heap.pop(); + var pass = true; + for (i = 0; i < length - 1; ++i) { + var next = heap.pop(); + pass = pass && (comparator(curr, next) <= 0); + curr = next; + } + expect(pass).toBe(true); + }); + + it('insert returns the removed element when maximumLength is set', function() { + var heap = new Heap({ + comparator : comparator + }); + heap.maximumLength = length; + + var i; + var max = 0.0; + var min = 1.0; + var values = new Array(length); + for (i = 0; i < length; ++i) { + var value = Math.random(); + max = Math.max(max, value); + min = Math.min(min, value); + values[i] = value; + } + + // Push 99 values + for (i = 0; i < length - 1; ++i) { + heap.insert(values[i]); + } + + // Push 100th, nothing is removed so it returns undefined + var removed = heap.insert(values[length - 1]); + expect(removed).toBeUndefined(); + + // Insert value, an element is removed + removed = heap.insert(max - 0.1); + expect(removed).toBeDefined(); + + // If this value is the least priority it will be returned + removed = heap.insert(max + 0.1); + expect(removed).toBe(max + 0.1); + }); + + it('resort', function() { + function comparator(a, b) { + return a.distance - b.distance; + } + + var i; + var heap = new Heap({ + comparator : comparator + }); + for (i = 0; i < length; ++i) { + heap.insert({ + distance : i / (length - 1), + id : i + }); + } + + // Check that elements are initially sorted + var element; + var elements = []; + var currentId = 0; + while (heap.length > 0) { + element = heap.pop(); + elements.push(element); + expect(element.id).toBeGreaterThanOrEqualTo(currentId); + currentId = element.id; + } + + // Add back into heap + for (i = 0; i < length; ++i) { + heap.insert(elements[i]); + } + + // Invert priority + for (i = 0; i < length; ++i) { + elements[i].distance = 1.0 - elements[i].distance; + } + + // Resort and check the the elements are popped in the opposite order now + heap.resort(); + while (heap.length > 0) { + element = heap.pop(); + expect(element.id).toBeLessThanOrEqualTo(currentId); + currentId = element.id; + } + }); +}); diff --git a/Specs/Core/ManagedArraySpec.js b/Specs/Core/ManagedArraySpec.js new file mode 100644 index 000000000000..67d138fa540a --- /dev/null +++ b/Specs/Core/ManagedArraySpec.js @@ -0,0 +1,141 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/ManagedArray' +], function( + ManagedArray) { + 'use strict'; + + it('constructor has expected default values', function() { + var array = new ManagedArray(); + expect(array.length).toEqual(0); + }); + + it('constructor initializes length', function() { + var array = new ManagedArray(10); + expect(array.length).toEqual(10); + expect(array.values.length).toEqual(10); + }); + + it('can get and set values', function() { + var length = 10; + var array = new ManagedArray(length); + var i; + for (i = 0; i < length; ++i) { + array.set(i, i*i); + } + for (i = 0; i < length; ++i) { + expect(array.get(i)).toEqual(i*i); + expect(array.values[i]).toEqual(i*i); + } + }); + + it('get throws if index does not exist', function() { + var array = new ManagedArray(); + array.reserve(5); + expect(array.values.length).toEqual(5); + expect(function() { + array.get(5); + }).toThrowDeveloperError(); + }); + + it('set throws if index invalid', function() { + var array = new ManagedArray(); + array.resize(10); + expect(function() { + array.set(undefined, 5); + }).toThrowDeveloperError(); + }); + + it('set resizes array', function() { + var array = new ManagedArray(); + array.set(0, 'a'); + expect(array.length).toEqual(1); + array.set(5, 'b'); + expect(array.length).toEqual(6); + array.set(2, 'c'); + expect(array.length).toEqual(6); + }); + + it('can push values', function() { + var array = new ManagedArray(); + var length = 10; + for (var i = 0; i < length; ++i) { + var val = Math.random(); + array.push(val); + expect(array.length).toEqual(i+1); + expect(array.values.length).toEqual(i+1); + expect(array.get(i)).toEqual(val); + expect(array.values[i]).toEqual(val); + } + }); + + it('can pop values', function() { + var length = 10; + var array = new ManagedArray(length); + var i; + for (i = 0; i < length; ++i) { + array.set(i, Math.random()); + } + for (i = length - 1; i >= 0; --i) { + var val = array.get(i); + expect(array.pop()).toEqual(val); + expect(array.length).toEqual(i); + expect(array.values.length).toEqual(length); + } + }); + + it('reserve throws if length is less than 0', function() { + var array = new ManagedArray(); + expect(function() { + array.reserve(-1); + }).toThrowDeveloperError(); + }); + + it('reserve', function() { + var array = new ManagedArray(2); + array.reserve(10); + expect(array.values.length).toEqual(10); + expect(array.length).toEqual(2); + array.reserve(20); + expect(array.values.length).toEqual(20); + expect(array.length).toEqual(2); + array.reserve(5); + expect(array.values.length).toEqual(20); + expect(array.length).toEqual(2); + }); + + it('resize throws if length is less than 0', function() { + var array = new ManagedArray(); + expect(function() { + array.resize(-1); + }).toThrowDeveloperError(); + }); + + it('resize', function() { + var array = new ManagedArray(2); + array.resize(10); + expect(array.values.length).toEqual(10); + expect(array.length).toEqual(10); + array.resize(20); + expect(array.values.length).toEqual(20); + expect(array.length).toEqual(20); + array.resize(5); + expect(array.values.length).toEqual(20); + expect(array.length).toEqual(5); + }); + + it('trim', function() { + var array = new ManagedArray(2); + array.reserve(10); + expect(array.length).toEqual(2); + expect(array.values.length).toEqual(10); + array.trim(); + expect(array.values.length).toEqual(2); + array.trim(5); + expect(array.length).toEqual(2); + expect(array.values.length).toEqual(5); + array.trim(3); + expect(array.length).toEqual(2); + expect(array.values.length).toEqual(3); + }); +}); diff --git a/Specs/Core/Matrix3Spec.js b/Specs/Core/Matrix3Spec.js index df3c4c009bc3..f00b85ae612d 100644 --- a/Specs/Core/Matrix3Spec.js +++ b/Specs/Core/Matrix3Spec.js @@ -148,6 +148,21 @@ defineSuite([ expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); }); + it('fromHeadingPitchRoll computed correctly', function() { + // Expected generated via STK Components + var expected = new Matrix3( + 0.754406506735489, 0.418940943945763, 0.505330889696038, + 0.133022221559489, 0.656295369162553, -0.742685314912828, + -0.642787609686539, 0.627506871597133, 0.439385041770705); + + var headingPitchRoll = new HeadingPitchRoll(-CesiumMath.toRadians(10), -CesiumMath.toRadians(40), CesiumMath.toRadians(55)); + var result = new Matrix3(); + var returnedResult = Matrix3.fromHeadingPitchRoll(headingPitchRoll, result); + expect(result).toBe(returnedResult); + expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); + }); + + it('fromScale works without a result parameter', function() { var expected = new Matrix3( 7.0, 0.0, 0.0, diff --git a/Specs/Core/OccluderSpec.js b/Specs/Core/OccluderSpec.js index 7fbfa0a4a49d..fba1b77ebbcb 100644 --- a/Specs/Core/OccluderSpec.js +++ b/Specs/Core/OccluderSpec.js @@ -255,7 +255,7 @@ defineSuite([ expect(occluder.isBoundingSphereVisible(new BoundingSphere(result, 0.0))).toEqual(true); }); - it('compute occludee point from rectangle throws without an rectangle', function() { + it('compute occludee point from rectangle throws without a rectangle', function() { expect(function() { return Occluder.computeOccludeePointFromRectangle(); }).toThrowDeveloperError(); diff --git a/Specs/Core/OrientedBoundingBoxSpec.js b/Specs/Core/OrientedBoundingBoxSpec.js index 38f1a6855cb5..936939723b3e 100644 --- a/Specs/Core/OrientedBoundingBoxSpec.js +++ b/Specs/Core/OrientedBoundingBoxSpec.js @@ -343,9 +343,8 @@ defineSuite([ var d = -Cartesian3.dot(p0, n); if (Math.abs(d) > 0.0001 && Cartesian3.magnitudeSquared(n) > 0.0001) { return new Plane(n, d); - } else { - return undefined; } + return undefined; }; var pl; diff --git a/Specs/Core/PlaneSpec.js b/Specs/Core/PlaneSpec.js index 5b4a2a448a31..166aa8185ed2 100644 --- a/Specs/Core/PlaneSpec.js +++ b/Specs/Core/PlaneSpec.js @@ -23,6 +23,12 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('constructor throws if normal is not normalized', function() { + expect(function() { + return new Plane(new Cartesian3(1.0, 2.0, 3.0), 0.0); + }).toThrowDeveloperError(); + }); + it('constructor throws without a distance', function() { expect(function() { return new Plane(Cartesian3.UNIT_X, undefined); @@ -31,6 +37,7 @@ defineSuite([ it('constructs from a point and a normal', function() { var normal = new Cartesian3(1.0, 2.0, 3.0); + normal = Cartesian3.normalize(normal, normal); var point = new Cartesian3(4.0, 5.0, 6.0); var plane = Plane.fromPointNormal(point, normal); expect(plane.normal).toEqual(normal); @@ -39,6 +46,7 @@ defineSuite([ it('constructs from a point and a normal with result', function() { var normal = new Cartesian3(1.0, 2.0, 3.0); + normal = Cartesian3.normalize(normal, normal); var point = new Cartesian3(4.0, 5.0, 6.0); var plane = new Plane(Cartesian3.UNIT_X, 0.0); @@ -75,14 +83,28 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('fromPointNormal throws if normal is not normalized', function() { + expect(function() { + return Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.ZERO); + }).toThrowDeveloperError(); + }); + it('fromCartesian4 throws without coefficients', function() { expect(function() { return Plane.fromCartesian4(undefined); }).toThrowDeveloperError(); }); + it('fromCartesian4 throws if normal is not normalized', function() { + expect(function() { + return Plane.fromCartesian4(new Cartesian4(1.0, 2.0, 3.0, 4.0)); + }).toThrowDeveloperError(); + }); + it('gets the distance to a point', function() { - var plane = new Plane(new Cartesian3(1.0, 2.0, 3.0), 12.34); + var normal = new Cartesian3(1.0, 2.0, 3.0); + normal = Cartesian3.normalize(normal, normal); + var plane = new Plane(normal, 12.34); var point = new Cartesian3(4.0, 5.0, 6.0); expect(Plane.getPointDistance(plane, point)).toEqual(Cartesian3.dot(plane.normal, point) + plane.distance); diff --git a/Specs/Core/PointGeometrySpec.js b/Specs/Core/PointGeometrySpec.js deleted file mode 100644 index 079f6db8d640..000000000000 --- a/Specs/Core/PointGeometrySpec.js +++ /dev/null @@ -1,69 +0,0 @@ -/*global defineSuite*/ -defineSuite([ - 'Core/PointGeometry', - 'Core/BoundingSphere', - 'Core/Cartesian3' - ], function( - PointGeometry, - BoundingSphere, - Cartesian3) { - 'use strict'; - - var positionsTypedArray = new Float32Array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0]); - var colorsTypedArray = new Uint8Array([255, 0, 0, 0, 255, 0]); - var boundingSphere = BoundingSphere.fromVertices(positionsTypedArray); - var expectedCenter = new Cartesian3(0.0, 0.0, 0.5); - var expectedRadius = 0.5; - - it('throws without positionsTypedArray', function() { - expect(function() { - return new PointGeometry({ - colorsTypedArray : colorsTypedArray - }); - }).toThrowDeveloperError(); - }); - - it('throws without colorsTypedArray', function() { - expect(function() { - return new PointGeometry({ - positionsTypedArray : positionsTypedArray - }); - }).toThrowDeveloperError(); - }); - - it('creates with boundingSphere', function() { - var points = new PointGeometry({ - positionsTypedArray : positionsTypedArray, - colorsTypedArray : colorsTypedArray, - boundingSphere : boundingSphere - }); - - var geometry = PointGeometry.createGeometry(points); - expect(geometry.boundingSphere.center).toEqual(expectedCenter); - expect(geometry.boundingSphere.radius).toEqual(expectedRadius); - }); - - it('creates without boundingSphere', function() { - var points = new PointGeometry({ - positionsTypedArray : positionsTypedArray, - colorsTypedArray : colorsTypedArray - }); - - var geometry = PointGeometry.createGeometry(points); - expect(geometry.boundingSphere.center).toEqual(expectedCenter); - expect(geometry.boundingSphere.radius).toEqual(expectedRadius); - }); - - it('computes all vertex attributes', function() { - var points = new PointGeometry({ - positionsTypedArray : positionsTypedArray, - colorsTypedArray : colorsTypedArray, - boundingSphere : boundingSphere - }); - - var geometry = PointGeometry.createGeometry(points); - expect(geometry.attributes.position.values.length).toEqual(2 * 3); - expect(geometry.attributes.color.values.length).toEqual(2 * 3); - expect(geometry.indices).toBeUndefined(); - }); -}); diff --git a/Specs/Core/RequestSchedulerSpec.js b/Specs/Core/RequestSchedulerSpec.js new file mode 100644 index 000000000000..4dfff78ecff2 --- /dev/null +++ b/Specs/Core/RequestSchedulerSpec.js @@ -0,0 +1,704 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/RequestScheduler', + 'Core/Request', + 'Core/RequestState', + 'Core/RequestType', + 'ThirdParty/when' + ], function( + RequestScheduler, + Request, + RequestState, + RequestType, + when) { + 'use strict'; + + var originalMaximumRequests; + var originalMaximumRequestsPerServer; + var originalPriorityHeapLength; + + beforeAll(function() { + originalMaximumRequests = RequestScheduler.maximumRequests; + originalMaximumRequestsPerServer = RequestScheduler.maximumRequestsPerServer; + originalPriorityHeapLength = RequestScheduler.priorityHeapLength; + }); + + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + + afterEach(function() { + RequestScheduler.maximumRequests = originalMaximumRequests; + RequestScheduler.maximumRequestsPerServer = originalMaximumRequestsPerServer; + RequestScheduler.priorityHeapLength = originalPriorityHeapLength; + }); + + it('request throws when request is undefined', function() { + expect(function() { + RequestScheduler.request(); + }).toThrowDeveloperError(); + }); + + it('request throws when request.url is undefined', function() { + expect(function() { + RequestScheduler.request(new Request({ + requestFunction : function(url) { + return undefined; + } + })); + }).toThrowDeveloperError(); + }); + + it('request throws when request.requestFunction is undefined', function() { + expect(function() { + RequestScheduler.request(new Request({ + url : 'file/path' + })); + }).toThrowDeveloperError(); + }); + + it('getServer throws if url is undefined', function() { + expect(function() { + RequestScheduler.getServerKey(); + }).toThrowDeveloperError(); + }); + + it('getServer with https', function() { + var server = RequestScheduler.getServerKey('https://foo.com/1'); + expect(server).toEqual('foo.com:443'); + }); + + it('getServer with http', function() { + var server = RequestScheduler.getServerKey('http://foo.com/1'); + expect(server).toEqual('foo.com:80'); + }); + + it('honors maximumRequests', function() { + RequestScheduler.maximumRequests = 2; + var statistics = RequestScheduler.statistics; + + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + function createRequest() { + return new Request({ + url : 'http://foo.com/1', + requestFunction : requestFunction, + throttle : true + }); + } + + var promise1 = RequestScheduler.request(createRequest()); + var promise2 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + // Scheduler is full, promise3 will be undefined + var promise3 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(statistics.numberOfActiveRequests).toBe(2); + expect(promise1).toBeDefined(); + expect(promise2).toBeDefined(); + expect(promise3).not.toBeDefined(); + + // Scheduler now has an empty slot, promise4 goes through + deferreds[0].resolve(); + RequestScheduler.update(); + + expect(statistics.numberOfActiveRequests).toBe(1); + + var promise4 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(statistics.numberOfActiveRequests).toBe(2); + expect(promise4).toBeDefined(); + + // Scheduler is full, promise5 will be undefined + var promise5 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(statistics.numberOfActiveRequests).toBe(2); + expect(promise5).not.toBeDefined(); + + // maximumRequests increases, promise6 goes through + RequestScheduler.maximumRequests = 3; + var promise6 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(statistics.numberOfActiveRequests).toBe(3); + expect(promise6).toBeDefined(); + + var length = deferreds.length; + for (var i = 0; i < length; ++i) { + deferreds[i].resolve(); + } + }); + + it('honors maximumRequestsPerServer', function() { + RequestScheduler.maximumRequestsPerServer = 2; + + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + var url = 'http://foo.com/1'; + var server = RequestScheduler.getServerKey(url); + + function createRequest() { + return new Request({ + url : url, + requestFunction : requestFunction, + throttleByServer : true + }); + } + + var promise1 = RequestScheduler.request(createRequest()); + var promise2 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + // Scheduler is full, promise3 will be undefined + var promise3 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2); + expect(promise1).toBeDefined(); + expect(promise2).toBeDefined(); + expect(promise3).not.toBeDefined(); + + // Scheduler now has an empty slot, promise4 goes through + deferreds[0].resolve(); + RequestScheduler.update(); + + expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(1); + + var promise4 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2); + expect(promise4).toBeDefined(); + + // Scheduler is full, promise5 will be undefined + var promise5 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2); + expect(promise5).not.toBeDefined(); + + // maximumRequests increases, promise6 goes through + RequestScheduler.maximumRequestsPerServer = 3; + var promise6 = RequestScheduler.request(createRequest()); + RequestScheduler.update(); + + expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(3); + expect(promise6).toBeDefined(); + + var length = deferreds.length; + for (var i = 0; i < length; ++i) { + deferreds[i].resolve(); + } + }); + + it('honors priorityHeapLength', function() { + var deferreds = []; + var requests = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + function createRequest(priority) { + var request = new Request({ + url : 'http://foo.com/1', + requestFunction : requestFunction, + throttle : true, + priority : priority + }); + requests.push(request); + return request; + } + + RequestScheduler.priorityHeapLength = 1; + var firstRequest = createRequest(0.0); + var promise = RequestScheduler.request(firstRequest); + expect(promise).toBeDefined(); + promise = RequestScheduler.request(createRequest(1.0)); + expect(promise).toBeUndefined(); + + RequestScheduler.priorityHeapLength = 3; + promise = RequestScheduler.request(createRequest(2.0)); + promise = RequestScheduler.request(createRequest(3.0)); + expect(promise).toBeDefined(); + promise = RequestScheduler.request(createRequest(4.0)); + expect(promise).toBeUndefined(); + + // A request is cancelled to accommodate the new heap length + RequestScheduler.priorityHeapLength = 2; + expect(firstRequest.state).toBe(RequestState.CANCELLED); + + var length = deferreds.length; + for (var i = 0; i < length; ++i) { + deferreds[i].resolve(); + } + }); + + function testImmediateRequest(url, dataOrBlobUri) { + var statistics = RequestScheduler.statistics; + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + var request = new Request({ + url : url, + requestFunction : requestFunction + }); + + var promise = RequestScheduler.request(request); + expect(promise).toBeDefined(); + + if (dataOrBlobUri) { + expect(request.serverKey).toBeUndefined(); + expect(statistics.numberOfActiveRequests).toBe(0); + } else { + expect(statistics.numberOfActiveRequests).toBe(1); + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(1); + } + + deferreds[0].resolve(); + + return promise.then(function() { + expect(request.state).toBe(RequestState.RECEIVED); + expect(statistics.numberOfActiveRequests).toBe(0); + if (!dataOrBlobUri) { + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0); + } + }); + } + + it('data uri goes through immediately', function() { + var dataUri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'; + testImmediateRequest(dataUri, true); + }); + + it('blob uri goes through immediately', function() { + var uint8Array = new Uint8Array(4); + var blob = new Blob([uint8Array], { + type : 'application/octet-stream' + }); + + var blobUrl = window.URL.createObjectURL(blob); + testImmediateRequest(blobUrl, true); + }); + + it('request goes through immediately when throttle is false', function() { + var url = 'https://foo.com/1'; + testImmediateRequest(url, false); + }); + + it('makes a throttled request', function() { + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + var request = new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + expect(request.state).toBe(RequestState.UNISSUED); + + var promise = RequestScheduler.request(request); + expect(promise).toBeDefined(); + expect(request.state).toBe(RequestState.ISSUED); + + RequestScheduler.update(); + expect(request.state).toBe(RequestState.ACTIVE); + + deferreds[0].resolve(); + expect(request.state).toBe(RequestState.RECEIVED); + }); + + it('cancels an issued request', function() { + var statistics = RequestScheduler.statistics; + + function requestFunction() { + return when.resolve(); + } + + var request = new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + + var promise = RequestScheduler.request(request); + expect(request.state).toBe(RequestState.ISSUED); + + request.cancel(); + RequestScheduler.update(); + + expect(request.state).toBe(RequestState.CANCELLED); + expect(statistics.numberOfCancelledRequests).toBe(1); + expect(statistics.numberOfCancelledActiveRequests).toBe(0); + + return promise.then(function() { + fail('should not be called'); + }).otherwise(function(error) { + expect(request.state).toBe(RequestState.CANCELLED); + }); + }); + + it('cancels an active request', function() { + var statistics = RequestScheduler.statistics; + var cancelFunction = jasmine.createSpy('cancelFunction'); + + function requestFunction() { + return when.defer().promise; + } + + var request = new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction, + cancelFunction : cancelFunction + }); + + var promise = RequestScheduler.request(request); + RequestScheduler.update(); + expect(request.state).toBe(RequestState.ACTIVE); + + request.cancel(); + RequestScheduler.update(); + + expect(request.state).toBe(RequestState.CANCELLED); + expect(statistics.numberOfCancelledRequests).toBe(1); + expect(statistics.numberOfCancelledActiveRequests).toBe(1); + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0); + expect(cancelFunction).toHaveBeenCalled(); + + return promise.then(function() { + fail('should not be called'); + }).otherwise(function(error) { + expect(request.state).toBe(RequestState.CANCELLED); + }); + }); + + it('handles request failure', function() { + var statistics = RequestScheduler.statistics; + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + var request = new Request({ + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + + var promise = RequestScheduler.request(request); + expect(request.state).toBe(RequestState.ACTIVE); + expect(statistics.numberOfActiveRequests).toBe(1); + + deferreds[0].reject('Request failed'); + RequestScheduler.update(); + expect(statistics.numberOfActiveRequests).toBe(0); + + return promise.then(function() { + fail('should not be called'); + }).otherwise(function(error) { + expect(error).toBe('Request failed'); + }); + }); + + it('prioritizes requests', function() { + var currentPriority = 0.0; + + function getRequestFunction(priority) { + return function() { + expect(priority).toBeGreaterThan(currentPriority); + currentPriority = priority; + return when.resolve(); + }; + } + + function createRequest(priority) { + return new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : getRequestFunction(priority), + priority : priority + }); + } + + var length = RequestScheduler.priorityHeapLength; + for (var i = 0; i < length; ++i) { + var priority = Math.random(); + RequestScheduler.request(createRequest(priority)); + } + + RequestScheduler.update(); + expect(currentPriority).toBeGreaterThan(0.0); // Ensures that the expect in getRequestFunction is actually called + }); + + it('updates priority', function() { + var invertPriority = false; + + function getPriorityFunction(priority) { + return function() { + if (invertPriority) { + return 1.0 - priority; + } + return priority; + }; + } + + function requestFunction() { + return when.resolve(); + } + + function createRequest(priority) { + return new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction, + priorityFunction : getPriorityFunction(priority) + }); + } + + var i; + var request; + var length = RequestScheduler.priorityHeapLength; + for (i = 0; i < length; ++i) { + var priority = i / (length - 1); + request = createRequest(priority); + request.testId = i; + RequestScheduler.request(request); + } + + // Update priorities while not letting any requests go through + RequestScheduler.maximumRequests = 0; + RequestScheduler.update(); + + var requestHeap = RequestScheduler.requestHeap; + var requests = []; + var currentTestId = 0; + while (requestHeap.length > 0) { + request = requestHeap.pop(); + requests.push(request); + expect(request.testId).toBeGreaterThanOrEqualTo(currentTestId); + currentTestId = request.testId; + } + + for (i = 0; i < length; ++i) { + requestHeap.insert(requests[i]); + } + + invertPriority = true; + RequestScheduler.update(); + + while (requestHeap.length > 0) { + request = requestHeap.pop(); + expect(request.testId).toBeLessThanOrEqualTo(currentTestId); + currentTestId = request.testId; + } + }); + + it('handles low priority requests', function() { + function requestFunction() { + return when.resolve(); + } + + function createRequest(priority) { + return new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction, + priority : priority + }); + } + + var highPriority = 0.0; + var mediumPriority = 0.5; + var lowPriority = 1.0; + + var length = RequestScheduler.priorityHeapLength; + for (var i = 0; i < length; ++i) { + RequestScheduler.request(createRequest(mediumPriority)); + } + + // Heap is full so low priority request is not even issued + var promise = RequestScheduler.request(createRequest(lowPriority)); + expect(promise).toBeUndefined(); + expect(RequestScheduler.statistics.numberOfCancelledRequests).toBe(0); + + // Heap is full so high priority request bumps off lower priority request + promise = RequestScheduler.request(createRequest(highPriority)); + expect(promise).toBeDefined(); + expect(RequestScheduler.statistics.numberOfCancelledRequests).toBe(1); + }); + + it('unthrottled requests starve throttled requests', function() { + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + function createRequest(throttle) { + return new Request({ + url : 'http://foo.com/1', + requestFunction : requestFunction, + throttle : throttle + }); + } + + var throttledRequest = createRequest(true); + RequestScheduler.request(throttledRequest); + + for (var i = 0; i < RequestScheduler.maximumRequests; ++i) { + RequestScheduler.request(createRequest(false)); + } + RequestScheduler.update(); + + expect(throttledRequest.state).toBe(RequestState.ISSUED); + + // Resolve one of the unthrottled requests + deferreds[0].resolve(); + RequestScheduler.update(); + expect(throttledRequest.state).toBe(RequestState.ACTIVE); + + var length = deferreds.length; + for (var j = 0; j < length; ++j) { + deferreds[j].resolve(); + } + }); + + it('request throttled by server is cancelled', function() { + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + function createRequest(throttleByServer) { + return new Request({ + url : 'http://foo.com/1', + requestFunction : requestFunction, + throttleByServer : throttleByServer + }); + } + + for (var i = 0; i < RequestScheduler.maximumRequestsPerServer - 1; ++i) { + RequestScheduler.request(createRequest(false)); + } + + var throttledRequest = createRequest(true); + RequestScheduler.request(throttledRequest); + RequestScheduler.request(createRequest(false)); + + RequestScheduler.update(); + expect(throttledRequest.state).toBe(RequestState.CANCELLED); + + var length = deferreds.length; + for (var j = 0; j < length; ++j) { + deferreds[j].resolve(); + } + }); + + it('throttleRequests', function() { + RequestScheduler.maximumRequests = 0; + + function requestFunction() { + return when.resolve(); + } + + RequestScheduler.throttleRequests = true; + var request = new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + var promise = RequestScheduler.request(request); + expect(promise).toBeUndefined(); + + RequestScheduler.throttleRequests = false; + request = new Request({ + throttle : true, + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + promise = RequestScheduler.request(request); + expect(promise).toBeDefined(); + + RequestScheduler.throttleRequests = true; + }); + + it('debugShowStatistics', function() { + spyOn(console, 'log'); + RequestScheduler.debugShowStatistics = true; + + var deferreds = []; + + function requestFunction() { + var deferred = when.defer(); + deferreds.push(deferred); + return deferred.promise; + } + + function createRequest() { + return new Request({ + url : 'https://foo.com/1', + requestFunction : requestFunction + }); + } + + var requestToCancel = createRequest(); + RequestScheduler.request(createRequest()); + RequestScheduler.request(createRequest()); + RequestScheduler.request(requestToCancel); + RequestScheduler.update(); + + expect(console.log).toHaveBeenCalledWith('Number of attempted requests: 3'); + expect(console.log).toHaveBeenCalledWith('Number of active requests: 3'); + + deferreds[0].reject(); + requestToCancel.cancel(); + RequestScheduler.update(); + + expect(console.log).toHaveBeenCalledWith('Number of cancelled requests: 1'); + expect(console.log).toHaveBeenCalledWith('Number of cancelled active requests: 1'); + expect(console.log).toHaveBeenCalledWith('Number of failed requests: 1'); + + var length = deferreds.length; + for (var i = 0; i < length; ++i) { + deferreds[i].resolve(); + } + + RequestScheduler.debugShowStatistics = false; + }); +}); diff --git a/Specs/Core/VRTheWorldTerrainProviderSpec.js b/Specs/Core/VRTheWorldTerrainProviderSpec.js index f04ccee3a016..cf5858abc290 100644 --- a/Specs/Core/VRTheWorldTerrainProviderSpec.js +++ b/Specs/Core/VRTheWorldTerrainProviderSpec.js @@ -7,6 +7,8 @@ defineSuite([ 'Core/loadImage', 'Core/loadWithXhr', 'Core/Math', + 'Core/Request', + 'Core/RequestScheduler', 'Core/TerrainProvider', 'Specs/pollToPromise', 'ThirdParty/when' @@ -18,12 +20,15 @@ defineSuite([ loadImage, loadWithXhr, CesiumMath, + Request, + RequestScheduler, TerrainProvider, pollToPromise, when) { 'use strict'; beforeEach(function() { + RequestScheduler.clearForSpecs(); loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { setTimeout(function() { var parser = new DOMParser(); @@ -58,6 +63,12 @@ defineSuite([ loadWithXhr.load = loadWithXhr.defaultLoad; }); + function createRequest() { + return new Request({ + throttleByServer : true + }); + } + it('conforms to TerrainProvider interface', function() { expect(VRTheWorldTerrainProvider).toConformToInterface(TerrainProvider); }); @@ -273,15 +284,15 @@ defineSuite([ return pollToPromise(function() { return terrainProvider.ready; }).then(function() { - var promise = terrainProvider.requestTileGeometry(0, 0, 0); - expect(promise).toBeDefined(); - + var promise; var i; - for (i = 0; i < 10; ++i) { - promise = terrainProvider.requestTileGeometry(0, 0, 0); + for (i = 0; i < RequestScheduler.maximumRequestsPerServer; ++i) { + promise = terrainProvider.requestTileGeometry(0, 0, 0, createRequest()); } + RequestScheduler.update(); + expect(promise).toBeDefined(); - promise = terrainProvider.requestTileGeometry(0, 0, 0); + promise = terrainProvider.requestTileGeometry(0, 0, 0, createRequest()); expect(promise).toBeUndefined(); for (i = 0; i < deferreds.length; ++i) { diff --git a/Specs/Core/arrayFillSpec.js b/Specs/Core/arrayFillSpec.js new file mode 100644 index 000000000000..52705448db4c --- /dev/null +++ b/Specs/Core/arrayFillSpec.js @@ -0,0 +1,57 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/arrayFill' + ], function( + arrayFill) { + 'use strict'; + + var array; + + beforeEach(function() { + array = [0, 0, 0, 0]; + }); + + it('will fill an entire array', function() { + arrayFill(array, 1); + expect(array).toEqual([1, 1, 1, 1]); + }); + + it('will fill a portion of an array', function() { + arrayFill(array, 1, 1, 3); + expect(array).toEqual([0, 1, 1, 0]); + }); + + it('will wrap around negative values', function() { + arrayFill(array, 1, -2, -1); + expect(array).toEqual([0, 0, 1, 0]); + }); + + it('will fill until end if no end is provided', function() { + arrayFill(array, 1, 1); + expect(array).toEqual([0, 1, 1, 1]); + }); + + it('will throw an error if no array is provided', function() { + expect(function() { + arrayFill(undefined, 1, 0, 1); + }).toThrowDeveloperError('array is required.'); + }); + + it('will throw an error if no array is provided', function() { + expect(function() { + arrayFill(array, undefined, 0, 1); + }).toThrowDeveloperError('value is required.'); + }); + + it('will throw an error if given an invalid start index', function() { + expect(function() { + arrayFill(array, 1, array, 1); + }).toThrowDeveloperError('start must be a valid index.'); + }); + + it('will throw an error if given an invalid end index', function() { + expect(function() { + arrayFill(array, 1, 1, array); + }).toThrowDeveloperError('end must be a valid index.'); + }); +}); diff --git a/Specs/Core/isBlobUriSpec.js b/Specs/Core/isBlobUriSpec.js new file mode 100644 index 000000000000..d319b0a7bf57 --- /dev/null +++ b/Specs/Core/isBlobUriSpec.js @@ -0,0 +1,27 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/isBlobUri' + ], function( + isBlobUri) { + 'use strict'; + + it('Throws if url is undefined', function() { + expect(function() { + isBlobUri(undefined); + }).toThrowDeveloperError(); + }); + + it('Determines that a uri is not a blob uri', function() { + expect(isBlobUri('http://cesiumjs.org/')).toEqual(false); + }); + + it('Determines that a uri is a blob uri', function() { + var uint8Array = new Uint8Array(4); + var blob = new Blob([uint8Array], { + type : 'application/octet-stream' + }); + + var blobUrl = window.URL.createObjectURL(blob); + expect(isBlobUri(blobUrl)).toEqual(true); + }); +}); diff --git a/Specs/Core/isDataUriSpec.js b/Specs/Core/isDataUriSpec.js new file mode 100644 index 000000000000..b3864848630c --- /dev/null +++ b/Specs/Core/isDataUriSpec.js @@ -0,0 +1,22 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/isDataUri' + ], function( + isDataUri) { + 'use strict'; + + it('Throws if url is undefined', function() { + expect(function() { + isDataUri(undefined); + }).toThrowDeveloperError(); + }); + + it('Determines that a uri is not a data uri', function() { + expect(isDataUri('http://cesiumjs.org/')).toEqual(false); + }); + + it('Determines that a uri is a data uri', function() { + var uri = 'data:text/plain;base64,' + btoa('a data uri'); + expect(isDataUri(uri)).toEqual(true); + }); +}); diff --git a/Specs/Core/joinUrlsSpec.js b/Specs/Core/joinUrlsSpec.js index 65a243c9261c..94ee2a10b1b8 100644 --- a/Specs/Core/joinUrlsSpec.js +++ b/Specs/Core/joinUrlsSpec.js @@ -160,4 +160,13 @@ defineSuite([ var result = joinUrls('http://www.xyz.com/', 'MODULE'); expect(result).toEqual('http://www.xyz.com/MODULE'); }); + + it('does not join data uris', function() { + var dataUri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'; + var result = joinUrls(dataUri, relativePath); + expect(result).toEqual(dataUri); + + result = joinUrls(absolutePath, dataUri); + expect(result).toEqual(dataUri); + }); }); diff --git a/Specs/Core/loadArrayBufferSpec.js b/Specs/Core/loadArrayBufferSpec.js index b6c6ad825d75..11b5bdd54073 100644 --- a/Specs/Core/loadArrayBufferSpec.js +++ b/Specs/Core/loadArrayBufferSpec.js @@ -1,10 +1,14 @@ /*global defineSuite*/ defineSuite([ 'Core/loadArrayBuffer', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadArrayBuffer, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; var fakeXHR; @@ -135,4 +139,19 @@ defineSuite([ expect(rejectedError.statusCode).toEqual(404); expect(rejectedError.response).toEqual(error); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadArrayBuffer(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadBlobSpec.js b/Specs/Core/loadBlobSpec.js index 449fd2edc6a1..af7f40599888 100644 --- a/Specs/Core/loadBlobSpec.js +++ b/Specs/Core/loadBlobSpec.js @@ -1,10 +1,14 @@ /*global defineSuite*/ defineSuite([ 'Core/loadBlob', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadBlob, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; var fakeXHR; @@ -135,4 +139,19 @@ defineSuite([ expect(rejectedError.statusCode).toEqual(404); expect(rejectedError.response).toEqual(error); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadBlob(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadCRNSpec.js b/Specs/Core/loadCRNSpec.js index 7377bb260ba0..c184d0ab71ec 100644 --- a/Specs/Core/loadCRNSpec.js +++ b/Specs/Core/loadCRNSpec.js @@ -2,11 +2,15 @@ defineSuite([ 'Core/loadCRN', 'Core/PixelFormat', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadCRN, PixelFormat, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { '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]); @@ -145,4 +149,19 @@ defineSuite([ expect(rejectedError).toBeUndefined(); }); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadCRN(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadImageSpec.js b/Specs/Core/loadImageSpec.js index 2a31f4cb2bdb..9837b4e79414 100644 --- a/Specs/Core/loadImageSpec.js +++ b/Specs/Core/loadImageSpec.js @@ -1,9 +1,13 @@ /*global defineSuite*/ defineSuite([ 'Core/loadImage', + 'Core/Request', + 'Core/RequestScheduler', 'ThirdParty/when' ], function( loadImage, + Request, + RequestScheduler, when) { 'use strict'; @@ -114,4 +118,19 @@ defineSuite([ expect(failure).toEqual(true); expect(loadedImage).toBeUndefined(); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadImage(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadImageViaBlobSpec.js b/Specs/Core/loadImageViaBlobSpec.js index bfbe82e5b965..440a4b6c574e 100644 --- a/Specs/Core/loadImageViaBlobSpec.js +++ b/Specs/Core/loadImageViaBlobSpec.js @@ -1,9 +1,13 @@ /*global defineSuite*/ defineSuite([ 'Core/loadImageViaBlob', + 'Core/Request', + 'Core/RequestScheduler', 'ThirdParty/when' ], function( loadImageViaBlob, + Request, + RequestScheduler, when) { 'use strict'; @@ -78,4 +82,19 @@ defineSuite([ expect(failure).toEqual(true); expect(loadedImage).toBeUndefined(); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadImageViaBlob(testUrl, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadJsonSpec.js b/Specs/Core/loadJsonSpec.js index 123763021d57..ac2618220a30 100644 --- a/Specs/Core/loadJsonSpec.js +++ b/Specs/Core/loadJsonSpec.js @@ -1,10 +1,14 @@ /*global defineSuite*/ defineSuite([ 'Core/loadJson', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadJson, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; var fakeXHR; @@ -139,4 +143,19 @@ defineSuite([ expect(rejectedError.statusCode).toEqual(404); expect(rejectedError.response).toEqual(error); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadJson(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadJsonpSpec.js b/Specs/Core/loadJsonpSpec.js index 9b735b692178..7cc442fe470d 100644 --- a/Specs/Core/loadJsonpSpec.js +++ b/Specs/Core/loadJsonpSpec.js @@ -1,10 +1,14 @@ /*global defineSuite*/ defineSuite([ 'Core/loadJsonp', - 'Core/DefaultProxy' + 'Core/DefaultProxy', + 'Core/Request', + 'Core/RequestScheduler' ], function( loadJsonp, - DefaultProxy) { + DefaultProxy, + Request, + RequestScheduler) { 'use strict'; it('throws with no url', function() { @@ -66,4 +70,19 @@ defineSuite([ }); loadJsonp(testUrl, options); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadJsonp(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadKTXSpec.js b/Specs/Core/loadKTXSpec.js index 3718ca996ca4..387854c68e7c 100644 --- a/Specs/Core/loadKTXSpec.js +++ b/Specs/Core/loadKTXSpec.js @@ -2,12 +2,16 @@ defineSuite([ 'Core/loadKTX', 'Core/PixelFormat', + 'Core/Request', 'Core/RequestErrorEvent', + 'Core/RequestScheduler', 'Core/RuntimeError' ], function( loadKTX, PixelFormat, + Request, RequestErrorEvent, + RequestScheduler, RuntimeError) { 'use strict'; @@ -333,7 +337,7 @@ defineSuite([ expect(rejectedError.message).toEqual('3D textures are unsupported.'); }); - it('Texture arrays are unsupported', function() { + it('texture arrays are unsupported', function() { var reinterprestBuffer = new Uint32Array(validUncompressed.buffer); var invalidKTX = new Uint32Array(reinterprestBuffer); invalidKTX[12] = 15; @@ -353,7 +357,7 @@ defineSuite([ expect(rejectedError.message).toEqual('Texture arrays are unsupported.'); }); - it('Cubemaps are unsupported', function() { + it('cubemaps are unsupported', function() { var reinterprestBuffer = new Uint32Array(validUncompressed.buffer); var invalidKTX = new Uint32Array(reinterprestBuffer); invalidKTX[13] = 6; @@ -372,4 +376,19 @@ defineSuite([ expect(rejectedError instanceof RuntimeError).toEqual(true); expect(rejectedError.message).toEqual('Cubemaps are unsupported.'); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadKTX(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadTextSpec.js b/Specs/Core/loadTextSpec.js index 272c89ea71d9..62bc5620170f 100644 --- a/Specs/Core/loadTextSpec.js +++ b/Specs/Core/loadTextSpec.js @@ -1,11 +1,14 @@ -/* jshint jasmine: true*/ /*global defineSuite*/ defineSuite([ 'Core/loadText', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadText, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; var fakeXHR; @@ -136,4 +139,19 @@ defineSuite([ expect(rejectedError.statusCode).toEqual(404); expect(rejectedError.response).toEqual(error); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadText(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/loadWithXhrSpec.js b/Specs/Core/loadWithXhrSpec.js index 6b2b5a5742ec..84aa74f13f08 100644 --- a/Specs/Core/loadWithXhrSpec.js +++ b/Specs/Core/loadWithXhrSpec.js @@ -2,11 +2,15 @@ defineSuite([ 'Core/loadWithXhr', 'Core/loadImage', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadWithXhr, loadImage, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; it('throws with no url', function() { @@ -15,6 +19,24 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadWithXhr({ + url : testUrl, + request : request + }); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); + describe('data URI loading', function() { it('can load URI escaped text with default response type', function() { return loadWithXhr({ diff --git a/Specs/Core/loadXMLSpec.js b/Specs/Core/loadXMLSpec.js index 8c023a8df2b7..b79a58909b92 100644 --- a/Specs/Core/loadXMLSpec.js +++ b/Specs/Core/loadXMLSpec.js @@ -1,10 +1,14 @@ /*global defineSuite*/ defineSuite([ 'Core/loadXML', - 'Core/RequestErrorEvent' + 'Core/Request', + 'Core/RequestErrorEvent', + 'Core/RequestScheduler' ], function( loadXML, - RequestErrorEvent) { + Request, + RequestErrorEvent, + RequestScheduler) { 'use strict'; var fakeXHR; @@ -137,4 +141,19 @@ defineSuite([ expect(rejectedError.statusCode).toEqual(404); expect(rejectedError.response).toEqual(error); }); + + it('returns undefined if the request is throttled', function() { + var oldMaximumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; + + var request = new Request({ + throttle : true + }); + + var testUrl = 'http://example.invalid/testuri'; + var promise = loadXML(testUrl, undefined, request); + expect(promise).toBeUndefined(); + + RequestScheduler.maximumRequests = oldMaximumRequests; + }); }); diff --git a/Specs/Core/sampleTerrainMostDetailedSpec.js b/Specs/Core/sampleTerrainMostDetailedSpec.js index bdb7ea157b8b..36750c7a6a1e 100644 --- a/Specs/Core/sampleTerrainMostDetailedSpec.js +++ b/Specs/Core/sampleTerrainMostDetailedSpec.js @@ -10,7 +10,7 @@ defineSuite([ "use strict"; var terrainProvider = new CesiumTerrainProvider({ - url : '//assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); it('queries heights', function() { @@ -76,7 +76,7 @@ defineSuite([ it('works for a dodgy point right near the edge of a tile', function() { var stkWorldTerrain = new CesiumTerrainProvider({ - url : '//assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); var positions = [new Cartographic(0.33179290856829535, 0.7363107781851078)]; diff --git a/Specs/Core/sampleTerrainSpec.js b/Specs/Core/sampleTerrainSpec.js index 3ab1971a23f4..e839d08a57ef 100644 --- a/Specs/Core/sampleTerrainSpec.js +++ b/Specs/Core/sampleTerrainSpec.js @@ -10,7 +10,7 @@ defineSuite([ 'use strict'; var terrainProvider = new CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); it('queries heights', function() { @@ -95,7 +95,7 @@ defineSuite([ it('works for a dodgy point right near the edge of a tile', function() { var stkWorldTerrain = new CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world' + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles' }); var positions = [new Cartographic(0.33179290856829535, 0.7363107781851078)]; diff --git a/Specs/Core/throttleRequestByServerSpec.js b/Specs/Core/throttleRequestByServerSpec.js deleted file mode 100644 index a8457fa90930..000000000000 --- a/Specs/Core/throttleRequestByServerSpec.js +++ /dev/null @@ -1,59 +0,0 @@ -/*global defineSuite*/ -defineSuite([ - 'Core/throttleRequestByServer', - 'ThirdParty/when' - ], function( - throttleRequestByServer, - when) { - 'use strict'; - - var originalMaximumRequestsPerServer; - - beforeEach(function() { - originalMaximumRequestsPerServer = throttleRequestByServer.maximumRequestsPerServer; - }); - - afterEach(function() { - throttleRequestByServer.maximumRequestsPerServer = originalMaximumRequestsPerServer; - }); - - it('honors maximumRequestsPerServer', function() { - throttleRequestByServer.maximumRequestsPerServer = 2; - - var deferreds = []; - - function requestFunction(url) { - var deferred = when.defer(); - deferreds.push(deferred); - return deferred.promise; - } - - var promise1 = throttleRequestByServer('http://foo.com/1', requestFunction); - var promise2 = throttleRequestByServer('http://foo.com/2', requestFunction); - var promise3 = throttleRequestByServer('http://foo.com/3', requestFunction); - - expect(deferreds.length).toBe(2); - expect(promise1).toBeDefined(); - expect(promise2).toBeDefined(); - expect(promise3).not.toBeDefined(); - - deferreds[0].resolve(); - - var promise4 = throttleRequestByServer('http://foo.com/3', requestFunction); - expect(deferreds.length).toBe(3); - expect(promise4).toBeDefined(); - - var promise5 = throttleRequestByServer('http://foo.com/4', requestFunction); - expect(deferreds.length).toBe(3); - expect(promise5).not.toBeDefined(); - - throttleRequestByServer.maximumRequestsPerServer = 3; - var promise6 = throttleRequestByServer('http://foo.com/4', requestFunction); - expect(deferreds.length).toBe(4); - expect(promise6).toBeDefined(); - - deferreds[1].resolve(); - deferreds[2].resolve(); - deferreds[3].resolve(); - }); -}); diff --git a/Specs/Data/CZML/ValidationDocument.czml b/Specs/Data/CZML/ValidationDocument.czml index 2b7a3a76dca6..81c7c79c3368 100644 --- a/Specs/Data/CZML/ValidationDocument.czml +++ b/Specs/Data/CZML/ValidationDocument.czml @@ -1676,6 +1676,27 @@ } } }, + { + "id":"material_path_material_polylineDash", + "path":{ + "material":{ + "polylineDash":{ + "color":{ + "rgba":[ + 190,189,9,7 + ] + }, + "gapColor":{ + "rgba":[ + 170,88,12,24 + ] + }, + "dashLength":45848, + "dashPattern":13519 + } + } + } + }, { "id":"material_path_material_polylineGlow", "path":{ @@ -1806,6 +1827,34 @@ } } }, + { + "id":"constant_path_material_polylineDash_color", + "path":{ + "material":{ + "polylineDash":{ + "color":{ + "rgbaf":[ + 0.11372549019607843,0.3686274509803922,0.4117647058823529,0.7450980392156863 + ] + } + } + } + } + }, + { + "id":"constant_path_material_polylineDash_gapColor", + "path":{ + "material":{ + "polylineDash":{ + "gapColor":{ + "rgbaf":[ + 0.8313725490196079,0.3137254901960784,0.3411764705882353,0.7490196078431373 + ] + } + } + } + } + }, { "id":"constant_path_material_polylineGlow_color", "path":{ @@ -2137,6 +2186,27 @@ } } }, + { + "id":"material_polyline_material_polylineDash", + "polyline":{ + "material":{ + "polylineDash":{ + "color":{ + "rgba":[ + 22,214,57,141 + ] + }, + "gapColor":{ + "rgba":[ + 150,91,109,117 + ] + }, + "dashLength":60297, + "dashPattern":40430 + } + } + } + }, { "id":"material_polyline_material_polylineGlow", "polyline":{ @@ -2267,6 +2337,34 @@ } } }, + { + "id":"constant_polyline_material_polylineDash_color", + "polyline":{ + "material":{ + "polylineDash":{ + "color":{ + "rgbaf":[ + 0.4627450980392157,0.1843137254901961,0.32941176470588235,0 + ] + } + } + } + } + }, + { + "id":"constant_polyline_material_polylineDash_gapColor", + "polyline":{ + "material":{ + "polylineDash":{ + "gapColor":{ + "rgbaf":[ + 0.050980392156862744,0.03137254901960784,0.23921568627450981,0.4 + ] + } + } + } + } + }, { "id":"constant_polyline_material_polylineGlow_color", "polyline":{ @@ -6578,6 +6676,27 @@ } } }, + { + "id":"reference_path_material_polylineDash", + "path":{ + "material":{ + "polylineDash":{ + "color":{ + "reference":"material_path_material_polylineDash#path.material.color" + }, + "gapColor":{ + "reference":"material_path_material_polylineDash#path.material.gapColor" + }, + "dashLength":{ + "reference":"material_path_material_polylineDash#path.material.dashLength" + }, + "dashPattern":{ + "reference":"material_path_material_polylineDash#path.material.dashPattern" + } + } + } + } + }, { "id":"reference_path_material_polylineGlow", "path":{ @@ -6761,6 +6880,27 @@ } } }, + { + "id":"reference_polyline_material_polylineDash", + "polyline":{ + "material":{ + "polylineDash":{ + "color":{ + "reference":"material_polyline_material_polylineDash#polyline.material.color" + }, + "gapColor":{ + "reference":"material_polyline_material_polylineDash#polyline.material.gapColor" + }, + "dashLength":{ + "reference":"material_polyline_material_polylineDash#polyline.material.dashLength" + }, + "dashPattern":{ + "reference":"material_polyline_material_polylineDash#polyline.material.dashPattern" + } + } + } + } + }, { "id":"reference_polyline_material_polylineGlow", "polyline":{ @@ -10442,6 +10582,43 @@ } } }, + { + "id":"sampled_path_material_polylineDash", + "path":{ + "material":{ + "polylineDash":{ + "color":{ + "epoch":"2016-06-17T12:00:00Z", + "rgba":[ + 0,140,167,151,119, + 3600,65,100,228,104 + ] + }, + "gapColor":{ + "epoch":"2016-06-17T12:00:00Z", + "rgba":[ + 0,154,198,168,151, + 3600,16,23,0,42 + ] + }, + "dashLength":{ + "epoch":"2016-06-17T12:00:00Z", + "number":[ + 0,38294, + 3600,33057 + ] + }, + "dashPattern":{ + "epoch":"2016-06-17T12:00:00Z", + "number":[ + 0,58660, + 3600,3340 + ] + } + } + } + } + }, { "id":"sampled_path_material_polylineGlow", "path":{ @@ -10617,6 +10794,38 @@ } } }, + { + "id":"sampled_path_material_polylineDash_color", + "path":{ + "material":{ + "polylineDash":{ + "color":{ + "epoch":"2016-06-17T12:00:00Z", + "rgbaf":[ + 0,0.803921568627451,0.6784313725490196,0.17647058823529413,0.7098039215686275, + 3600,0.6745098039215687,0.09803921568627451,0.4470588235294118,0.803921568627451 + ] + } + } + } + } + }, + { + "id":"sampled_path_material_polylineDash_gapColor", + "path":{ + "material":{ + "polylineDash":{ + "gapColor":{ + "epoch":"2016-06-17T12:00:00Z", + "rgbaf":[ + 0,0.23137254901960785,0.7450980392156863,0.7725490196078432,0.9019607843137255, + 3600,0.7137254901960784,0.1803921568627451,0.3176470588235294,0.30980392156862746 + ] + } + } + } + } + }, { "id":"sampled_path_material_polylineGlow_color", "path":{ @@ -10979,6 +11188,43 @@ } } }, + { + "id":"sampled_polyline_material_polylineDash", + "polyline":{ + "material":{ + "polylineDash":{ + "color":{ + "epoch":"2016-06-17T12:00:00Z", + "rgba":[ + 0,77,159,238,158, + 3600,206,194,234,158 + ] + }, + "gapColor":{ + "epoch":"2016-06-17T12:00:00Z", + "rgba":[ + 0,232,145,15,164, + 3600,173,151,118,138 + ] + }, + "dashLength":{ + "epoch":"2016-06-17T12:00:00Z", + "number":[ + 0,41757, + 3600,10126 + ] + }, + "dashPattern":{ + "epoch":"2016-06-17T12:00:00Z", + "number":[ + 0,33948, + 3600,16892 + ] + } + } + } + } + }, { "id":"sampled_polyline_material_polylineGlow", "polyline":{ @@ -11154,6 +11400,38 @@ } } }, + { + "id":"sampled_polyline_material_polylineDash_color", + "polyline":{ + "material":{ + "polylineDash":{ + "color":{ + "epoch":"2016-06-17T12:00:00Z", + "rgbaf":[ + 0,0.5254901960784314,0.9921568627450981,0.9647058823529412,0.36470588235294116, + 3600,0.2784313725490196,0.13333333333333333,0.4470588235294118,0.19215686274509805 + ] + } + } + } + } + }, + { + "id":"sampled_polyline_material_polylineDash_gapColor", + "polyline":{ + "material":{ + "polylineDash":{ + "gapColor":{ + "epoch":"2016-06-17T12:00:00Z", + "rgbaf":[ + 0,0.4980392156862745,0.7764705882352941,0.803921568627451,0.6901960784313725, + 3600,0.3764705882352941,0.8980392156862745,0.16862745098039217,0.8980392156862745 + ] + } + } + } + } + }, { "id":"sampled_polyline_material_polylineGlow_color", "polyline":{ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColors/batchedColors.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedColors/batchedColors.b3dm new file mode 100644 index 000000000000..55c6d3bd55b2 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedColors/batchedColors.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColors/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedColors/tileset.json new file mode 100644 index 000000000000..44733c7f654c --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedColors/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196683129064435 + }, + "Latitude": { + "minimum": 0.698861998722264, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.929546581581235, + "maximum": 13.581844886764884 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedColors.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/batchedColorsMix.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/batchedColorsMix.b3dm new file mode 100644 index 000000000000..cb47684b88d2 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/batchedColorsMix.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/tileset.json new file mode 100644 index 000000000000..6864219c8e59 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsMix/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196683129064435 + }, + "Latitude": { + "minimum": 0.698861998722264, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.929546581581235, + "maximum": 13.581844886764884 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedColorsMix.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/batchedColorsTranslucent.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/batchedColorsTranslucent.b3dm new file mode 100644 index 000000000000..4a671a9c4ebe Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/batchedColorsTranslucent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/tileset.json new file mode 100644 index 000000000000..7346380cc8a8 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedColorsTranslucent/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196683129064435 + }, + "Latitude": { + "minimum": 0.698861998722264, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.929546581581235, + "maximum": 13.581844886764884 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedColorsTranslucent.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/batchedCompressedTextures.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/batchedCompressedTextures.b3dm new file mode 100644 index 000000000000..0957d8b497ca Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/batchedCompressedTextures.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/tileset.json new file mode 100644 index 000000000000..b7b37b18bacd --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedCompressedTextures/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedCompressedTextures.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/batchedDeprecated1.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/batchedDeprecated1.b3dm new file mode 100644 index 000000000000..35083a6eb6df Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/batchedDeprecated1.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json new file mode 100644 index 000000000000..d40f3a9aa03b --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedDeprecated1.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/batchedDeprecated2.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/batchedDeprecated2.b3dm new file mode 100644 index 000000000000..c47a552d8581 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/batchedDeprecated2.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/tileset.json new file mode 100644 index 000000000000..e104f9661a07 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedDeprecated2/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedDeprecated2.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/batchedExpiration.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/batchedExpiration.b3dm new file mode 100644 index 000000000000..b3a7e3cb36ce Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/batchedExpiration.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/tileset.json new file mode 100644 index 000000000000..9682054f143d --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedExpiration/tileset.json @@ -0,0 +1,44 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "expire": { + "duration": 5 + }, + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedExpiration.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/batchedGltfZUp.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/batchedGltfZUp.b3dm new file mode 100644 index 000000000000..88114c3d080b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/batchedGltfZUp.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/tileset.json new file mode 100644 index 000000000000..9d366cf2e0bb --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedGltfZUp/tileset.json @@ -0,0 +1,42 @@ +{ + "asset": { + "version": "0.0", + "gltfUpAxis": "Z" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedGltfZUp.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/batchedNoBatchIds.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/batchedNoBatchIds.b3dm new file mode 100644 index 000000000000..b55063113359 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/batchedNoBatchIds.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/tileset.json new file mode 100644 index 000000000000..e782cc06759e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedNoBatchIds/tileset.json @@ -0,0 +1,23 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedNoBatchIds.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm new file mode 100644 index 000000000000..d5636e9602d7 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/tileset.json new file mode 100644 index 000000000000..e1377db0ef66 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedTextured.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/batchedTranslucent.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/batchedTranslucent.b3dm new file mode 100644 index 000000000000..eb572f2bad4d Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/batchedTranslucent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/tileset.json new file mode 100644 index 000000000000..4909113dcd85 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucent/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedTranslucent.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/batchedTranslucentOpaqueMix.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/batchedTranslucentOpaqueMix.b3dm new file mode 100644 index 000000000000..70bb4eedc755 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/batchedTranslucentOpaqueMix.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/tileset.json new file mode 100644 index 000000000000..dd19decf470e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedTranslucentOpaqueMix.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/batchedWGS84.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/batchedWGS84.b3dm new file mode 100644 index 000000000000..0398cb823b4d Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/batchedWGS84.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/tileset.json new file mode 100644 index 000000000000..8a2fb4674e7c --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWGS84/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWGS84.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/batchedWithBatchTable.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/batchedWithBatchTable.b3dm new file mode 100644 index 000000000000..2d2bc50db10d Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/batchedWithBatchTable.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json new file mode 100644 index 000000000000..9d27e6783e42 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithBatchTable.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/batchedWithBatchTableBinary.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/batchedWithBatchTableBinary.b3dm new file mode 100644 index 000000000000..f2c6d4051eb6 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/batchedWithBatchTableBinary.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/tileset.json new file mode 100644 index 000000000000..d0b76fc4bb40 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithBatchTableBinary.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/batchedWithBoundingSphere.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/batchedWithBoundingSphere.b3dm new file mode 100644 index 000000000000..dc8a294dda64 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/batchedWithBoundingSphere.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/tileset.json new file mode 100644 index 000000000000..6e095bb737b2 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/tileset.json @@ -0,0 +1,39 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215011.9317263428, + -4736309.3434217675, + 4081612.0044800863, + 141.4214 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithBoundingSphere.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/batchedWithKHRMaterialsCommon.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/batchedWithKHRMaterialsCommon.b3dm new file mode 100644 index 000000000000..cca0b3919c67 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/batchedWithKHRMaterialsCommon.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/tileset.json new file mode 100644 index 000000000000..28e0ad85e8e9 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithKHRMaterialsCommon/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithKHRMaterialsCommon.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/batchedWithQuantization.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/batchedWithQuantization.b3dm new file mode 100644 index 000000000000..0df33684eabc Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/batchedWithQuantization.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/tileset.json new file mode 100644 index 000000000000..7e55f3ab9ece --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithQuantization/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithQuantization.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/batchedWithTransformBox.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/batchedWithTransformBox.b3dm new file mode 100644 index 000000000000..01b0e171c5c9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/batchedWithTransformBox.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json new file mode 100644 index 000000000000..ee1b0dcc8a70 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json @@ -0,0 +1,65 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 100, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithTransformBox.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/batchedWithTransformRegion.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/batchedWithTransformRegion.b3dm new file mode 100644 index 000000000000..01b0e171c5c9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/batchedWithTransformRegion.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/tileset.json new file mode 100644 index 000000000000..5d4f361d7c03 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/tileset.json @@ -0,0 +1,59 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithTransformRegion.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/batchedWithTransformSphere.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/batchedWithTransformSphere.b3dm new file mode 100644 index 000000000000..01b0e171c5c9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/batchedWithTransformSphere.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/tileset.json new file mode 100644 index 000000000000..d98b14ec7368 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/tileset.json @@ -0,0 +1,57 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 12.83180232718587 + } + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "sphere": [ + 0, + 0, + 10, + 141.4214 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithTransformSphere.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/batchedWithoutBatchTable.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/batchedWithoutBatchTable.b3dm new file mode 100644 index 000000000000..115b4f07e164 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/batchedWithoutBatchTable.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/tileset.json b/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/tileset.json new file mode 100644 index 000000000000..510bf187f33e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/tileset.json @@ -0,0 +1,23 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "batchedWithoutBatchTable.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Composite/Composite/composite.cmpt b/Specs/Data/Cesium3DTiles/Composite/Composite/composite.cmpt new file mode 100644 index 000000000000..acd029a236af Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Composite/Composite/composite.cmpt differ diff --git a/Specs/Data/Cesium3DTiles/Composite/Composite/tileset.json b/Specs/Data/Cesium3DTiles/Composite/Composite/tileset.json new file mode 100644 index 000000000000..eb6aa226c12a --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Composite/Composite/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "composite.cmpt" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/compositeOfComposite.cmpt b/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/compositeOfComposite.cmpt new file mode 100644 index 000000000000..2ae795d67ebf Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/compositeOfComposite.cmpt differ diff --git a/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/tileset.json b/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/tileset.json new file mode 100644 index 000000000000..d5465d72d802 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Composite/CompositeOfComposite/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3196972173766555, + "maximum": -1.3196718547473905 + }, + "Latitude": { + "minimum": 0.6988624606923348, + "maximum": 0.6988888301460953 + }, + "Height": { + "minimum": 6.2074098233133554, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "compositeOfComposite.cmpt" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm new file mode 100644 index 000000000000..1f7b54d4f418 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tileset.json b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tileset.json new file mode 100644 index 000000000000..ed31f390fa80 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tileset.json @@ -0,0 +1,47 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 50, + 0, + 0, + 0, + 50, + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "tile.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tile.b3dm b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tile.b3dm new file mode 100644 index 000000000000..a58039a483bf Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tile.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tileset.json b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tileset.json new file mode 100644 index 000000000000..ed31f390fa80 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyBinary/tileset.json @@ -0,0 +1,47 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 50, + 0, + 0, + 0, + 50, + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "tile.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tile.b3dm b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tile.b3dm new file mode 100644 index 000000000000..983d0e56dee0 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tile.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tileset.json b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tileset.json new file mode 100644 index 000000000000..ed31f390fa80 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyMultipleParents/tileset.json @@ -0,0 +1,47 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 50, + 0, + 0, + 0, + 50, + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "tile.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tile.b3dm b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tile.b3dm new file mode 100644 index 000000000000..362a919adabd Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tile.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tileset.json b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tileset.json new file mode 100644 index 000000000000..ed31f390fa80 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Hierarchy/BatchTableHierarchyNoParents/tileset.json @@ -0,0 +1,47 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 50, + 0, + 0, + 0, + 50, + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "tile.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/instancedCompressedTextures.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/instancedCompressedTextures.i3dm new file mode 100644 index 000000000000..7e1251532041 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/instancedCompressedTextures.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/tileset.json new file mode 100644 index 000000000000..090c238793f5 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedCompressedTextures.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/Box.glb b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/Box.glb new file mode 100644 index 000000000000..2bde3c4ee98e Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/Box.glb differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/instancedGltfExternal.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/instancedGltfExternal.i3dm new file mode 100644 index 000000000000..afcfc8739afa Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/instancedGltfExternal.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/tileset.json new file mode 100644 index 000000000000..f54857135d9a --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfExternal/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedGltfExternal.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/instancedGltfZUp.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/instancedGltfZUp.i3dm new file mode 100644 index 000000000000..c7d534542685 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/instancedGltfZUp.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/tileset.json new file mode 100644 index 000000000000..a60d5e450505 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedGltfZUp/tileset.json @@ -0,0 +1,30 @@ +{ + "asset": { + "version": "0.0", + "gltfUpAxis": "Z" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedGltfZUp.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/instancedOct32POrientation.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/instancedOct32POrientation.i3dm new file mode 100644 index 000000000000..87e5ee55f518 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/instancedOct32POrientation.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/tileset.json new file mode 100644 index 000000000000..c52f33490b81 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedOct32POrientation.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/instancedOrientation.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/instancedOrientation.i3dm new file mode 100644 index 000000000000..fc780b4cf18e Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/instancedOrientation.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json new file mode 100644 index 000000000000..29e3cb2a488e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedOrientation.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/instancedQuantized.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/instancedQuantized.i3dm new file mode 100644 index 000000000000..3d64093f7aae Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/instancedQuantized.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/tileset.json new file mode 100644 index 000000000000..0a1d5acc31e6 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantized/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedQuantized.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/instancedQuantizedOct32POrientation.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/instancedQuantizedOct32POrientation.i3dm new file mode 100644 index 000000000000..2e47d0e0455e Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/instancedQuantizedOct32POrientation.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/tileset.json new file mode 100644 index 000000000000..c41b07b335f2 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedQuantizedOct32POrientation.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/instancedRTC.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/instancedRTC.i3dm new file mode 100644 index 000000000000..6f5b6b99807d Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/instancedRTC.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/tileset.json new file mode 100644 index 000000000000..9c5d59deb4ce --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedRTC/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedRTC.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/instancedRedMaterial.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/instancedRedMaterial.i3dm new file mode 100644 index 000000000000..78f082bf121e Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/instancedRedMaterial.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/tileset.json new file mode 100644 index 000000000000..1543d7c05bc2 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedRedMaterial/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedRedMaterial.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/instancedScale.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/instancedScale.i3dm new file mode 100644 index 000000000000..22c2e0bc0223 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/instancedScale.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/tileset.json new file mode 100644 index 000000000000..611188681761 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedScale/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedScale.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/instancedScaleNonUniform.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/instancedScaleNonUniform.i3dm new file mode 100644 index 000000000000..1f48b103a413 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/instancedScaleNonUniform.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/tileset.json new file mode 100644 index 000000000000..03f2136ba036 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedScaleNonUniform.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/instancedTextured.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/instancedTextured.i3dm new file mode 100644 index 000000000000..e62725639bdf Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/instancedTextured.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/tileset.json new file mode 100644 index 000000000000..ae42528eb3b5 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedTextured/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedTextured.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/instancedWithBatchIds.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/instancedWithBatchIds.i3dm new file mode 100644 index 000000000000..7cd6274ff15f Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/instancedWithBatchIds.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/tileset.json new file mode 100644 index 000000000000..5214b77061f9 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedWithBatchIds.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/instancedWithBatchTable.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/instancedWithBatchTable.i3dm new file mode 100644 index 000000000000..4a641e7a8a64 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/instancedWithBatchTable.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json new file mode 100644 index 000000000000..51047647dad5 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedWithBatchTable.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/instancedWithBatchTableBinary.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/instancedWithBatchTableBinary.i3dm new file mode 100644 index 000000000000..4a641e7a8a64 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/instancedWithBatchTableBinary.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/tileset.json new file mode 100644 index 000000000000..77f564e44e67 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/tileset.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedWithBatchTableBinary.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/instancedWithTransform.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/instancedWithTransform.i3dm new file mode 100644 index 000000000000..8543f9d265fc Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/instancedWithTransform.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/tileset.json new file mode 100644 index 000000000000..9c550fbc3e82 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithTransform/tileset.json @@ -0,0 +1,53 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "Height": { + "minimum": 20, + "maximum": 20 + } + }, + "geometricError": 70, + "root": { + "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 + ], + "refine": "add", + "boundingVolume": { + "box": [ + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 15 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedWithTransform.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/instancedWithoutBatchTable.i3dm b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/instancedWithoutBatchTable.i3dm new file mode 100644 index 000000000000..77b74aab6c31 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/instancedWithoutBatchTable.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/tileset.json b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/tileset.json new file mode 100644 index 000000000000..23eae57d525b --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/tileset.json @@ -0,0 +1,23 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "refine": "add", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 30 + ] + }, + "geometricError": 0, + "content": { + "url": "instancedWithoutBatchTable.i3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/pointCloudBatched.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/pointCloudBatched.pnts new file mode 100644 index 000000000000..9f37709b1616 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/pointCloudBatched.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/tileset.json new file mode 100644 index 000000000000..7da76d80e07d --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudBatched/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudBatched.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/pointCloudConstantColor.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/pointCloudConstantColor.pnts new file mode 100644 index 000000000000..4f095df7e9da Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/pointCloudConstantColor.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/tileset.json new file mode 100644 index 000000000000..86f287b67821 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudConstantColor/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudConstantColor.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/pointCloudNoColor.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/pointCloudNoColor.pnts new file mode 100644 index 000000000000..53a38bcc6291 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/pointCloudNoColor.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/tileset.json new file mode 100644 index 000000000000..1ba137876a77 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNoColor/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudNoColor.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/pointCloudNormals.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/pointCloudNormals.pnts new file mode 100644 index 000000000000..af2e25ae9c71 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/pointCloudNormals.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/tileset.json new file mode 100644 index 000000000000..cbd112148fe4 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormals/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudNormals.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/pointCloudNormalsOctEncoded.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/pointCloudNormalsOctEncoded.pnts new file mode 100644 index 000000000000..f11dbc159f44 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/pointCloudNormalsOctEncoded.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/tileset.json new file mode 100644 index 000000000000..dda4b23c12b1 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudNormalsOctEncoded.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/pointCloudQuantized.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/pointCloudQuantized.pnts new file mode 100644 index 000000000000..a0494c1b9717 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/pointCloudQuantized.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/tileset.json new file mode 100644 index 000000000000..fd46571f8aec --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantized/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudQuantized.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/pointCloudQuantizedOctEncoded.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/pointCloudQuantizedOctEncoded.pnts new file mode 100644 index 000000000000..01566ef5c565 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/pointCloudQuantizedOctEncoded.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/tileset.json new file mode 100644 index 000000000000..21a1a017e1ed --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudQuantizedOctEncoded.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/pointCloudRGB.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/pointCloudRGB.pnts new file mode 100644 index 000000000000..455d3b84065b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/pointCloudRGB.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/tileset.json new file mode 100644 index 000000000000..166b270b98be --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudRGB.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/pointCloudRGB565.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/pointCloudRGB565.pnts new file mode 100644 index 000000000000..64e59eed46c2 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/pointCloudRGB565.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/tileset.json new file mode 100644 index 000000000000..e5862c523b4f --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGB565/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudRGB565.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/pointCloudRGBA.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/pointCloudRGBA.pnts new file mode 100644 index 000000000000..ac30c6af8c75 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/pointCloudRGBA.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/tileset.json new file mode 100644 index 000000000000..8aca46b846aa --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudRGBA/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudRGBA.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/pointCloudWGS84.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/pointCloudWGS84.pnts new file mode 100644 index 000000000000..db140097ba4c Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/pointCloudWGS84.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/tileset.json new file mode 100644 index 000000000000..e07802ddb35c --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWGS84/tileset.json @@ -0,0 +1,21 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "refine": "add", + "boundingVolume": { + "sphere": [ + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudWGS84.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/pointCloudWithPerPointProperties.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/pointCloudWithPerPointProperties.pnts new file mode 100644 index 000000000000..3b30ca452503 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/pointCloudWithPerPointProperties.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/tileset.json new file mode 100644 index 000000000000..903f7739add9 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties/tileset.json @@ -0,0 +1,39 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "transform": [ + 0.968635634376879, + 0.24848542777253735, + 0, + 0, + -0.15986460794399626, + 0.6231776137472074, + 0.7655670897127491, + 0, + 0.190232265775849, + -0.7415555636019701, + 0.6433560687121489, + 0, + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 1 + ], + "refine": "add", + "boundingVolume": { + "sphere": [ + 0, + 0, + 0, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudWithPerPointProperties.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/pointCloudWithTransform.pnts b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/pointCloudWithTransform.pnts new file mode 100644 index 000000000000..ae4664c63feb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/pointCloudWithTransform.pnts differ diff --git a/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/tileset.json b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/tileset.json new file mode 100644 index 000000000000..16d86434b967 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/PointCloud/PointCloudWithTransform/tileset.json @@ -0,0 +1,39 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 17.32, + "root": { + "transform": [ + 0.968635634376879, + 0.24848542777253735, + 0, + 0, + -0.15986460794399626, + 0.6231776137472074, + 0.7655670897127491, + 0, + 0.190232265775849, + -0.7415555636019701, + 0.6433560687121489, + 0, + 1215012.8828876738, + -4736313.051199594, + 4081605.22126042, + 1 + ], + "refine": "add", + "boundingVolume": { + "sphere": [ + 0, + 0, + 0, + 5 + ] + }, + "geometricError": 0, + "content": { + "url": "pointCloudWithTransform.pnts" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Style/style.json b/Specs/Data/Cesium3DTiles/Style/style.json new file mode 100644 index 000000000000..6bd92818892d --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Style/style.json @@ -0,0 +1,5 @@ +{ + "color" : "color('red')", + "show" : "${id} < 100", + "pointSize" : "${id} / 100" +} \ No newline at end of file diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json new file mode 100644 index 000000000000..b2ede34c3f38 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json @@ -0,0 +1,118 @@ +{ + "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": 70, + "refine": "add", + "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 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/Tileset/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/tileset.json new file mode 100644 index 000000000000..8d9760a740ba --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/tileset.json @@ -0,0 +1,104 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 13.992324123159051 + } + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json new file mode 100644 index 000000000000..c3f31982179f --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 70, + "refine": "add", + "content": { + "url": "tileset2.json" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset2.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset2.json new file mode 100644 index 000000000000..bb2342e10ee7 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset2.json @@ -0,0 +1,89 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 88 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "parent.b3dm" + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "tileset3/tileset3.json" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/tileset3.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/tileset3.json new file mode 100644 index 000000000000..9cb6e6d8927d --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset3/tileset3.json @@ -0,0 +1,23 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 0, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "refine": "add", + "content": { + "url": "ll.b3dm" + } + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/tileset.json new file mode 100644 index 000000000000..a267e65f5edc --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/tileset.json @@ -0,0 +1,159 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 70, + "refine": "add", + "content": { + "url": "parent.b3dm", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + } + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + }, + "geometricError": 70, + "refine": "replace", + "content": { + "url": "parent.b3dm" + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "refine": "add", + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "refine": "replace", + "content": { + "url": "ur.b3dm" + } + } + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "parent.b3dm" + }, + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "refine": "add", + "content": { + "url": "ul.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "refine": "replace", + "content": { + "url": "lr.b3dm" + } + } + ] + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/tileset.json new file mode 100644 index 000000000000..94d831ec611f --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/tileset.json @@ -0,0 +1,149 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 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.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + } + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement1/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/tileset.json new file mode 100644 index 000000000000..0f386d533338 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/tileset.json @@ -0,0 +1,117 @@ +{ + "asset": { + "version": "0.0" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3197180493677987, + "maximum": -1.3196513750495065 + }, + "Latitude": { + "minimum": 0.6988530761634713, + "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": 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.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 7, + "refine": "replace", + "children": [ + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + } + ] + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + } + ] + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement2/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/parent.b3dm new file mode 100644 index 000000000000..81db7f15757b Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset.json new file mode 100644 index 000000000000..21564b776b9b --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset.json @@ -0,0 +1,70 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 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.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "content": { + "url": "tileset2.json" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset2.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset2.json new file mode 100644 index 000000000000..30f0a8e37191 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/tileset2.json @@ -0,0 +1,86 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "replace", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacement3/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 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..f052e259f078 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..81db7f15757b 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..b625126b1b2e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/tileset.json @@ -0,0 +1,173 @@ +{ + "asset": { + "version": "0.0" + }, + "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..a386047150bb 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..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetReplacementWithViewerRequestVolume/ur.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ll.b3dm new file mode 100644 index 000000000000..b97c4367854c Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/lr.b3dm new file mode 100644 index 000000000000..641627aafc62 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/parent.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/parent.b3dm new file mode 100644 index 000000000000..8f1a47aee7e3 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/parent.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/subtree.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/subtree.json new file mode 100644 index 000000000000..1451c6355150 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/subtree.json @@ -0,0 +1,86 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/tileset.json new file mode 100644 index 000000000000..e9099172b26b --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/tileset.json @@ -0,0 +1,72 @@ +{ + "asset": { + "version": "0.0" + }, + "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": 70, + "refine": "add", + "content": { + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + }, + "url": "parent.b3dm" + }, + "children": [ + { + "expire": { + "duration": 5 + }, + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "content": { + "url": "subtree.json" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ul.b3dm new file mode 100644 index 000000000000..30c904d18b81 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/ur.b3dm new file mode 100644 index 000000000000..e4ea60dcf5c1 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/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..37068256bb46 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..9c37a679cf1c 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/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/buildings.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/buildings.b3dm new file mode 100644 index 000000000000..01b0e171c5c9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/buildings.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/instances.i3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/instances.i3dm new file mode 100644 index 000000000000..8543f9d265fc Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/instances.i3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/tileset.json new file mode 100644 index 000000000000..094dd679bdf7 --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithTransforms/tileset.json @@ -0,0 +1,89 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 70, + "root": { + "boundingVolume": { + "box": [ + 0, + 0, + 10, + 100, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 10 + ] + }, + "transform": [ + 0.9686356343768792, + 0.24848542777253735, + 0, + 0, + -0.15986460744966327, + 0.623177611820219, + 0.765567091384559, + 0, + 0.19023226619126932, + -0.7415555652213445, + 0.6433560667227647, + 0, + 1215011.9317263428, + -4736309.3434217675, + 4081602.0044800863, + 1 + ], + "geometricError": 70, + "refine": "add", + "content": { + "url": "buildings.b3dm" + }, + "children": [ + { + "boundingVolume": { + "box": [ + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 15 + ] + }, + "transform": [ + 0.35355339059327373, + 0.3535533905932738, + 0, + 0, + -0.3535533905932738, + 0.35355339059327373, + 0, + 0, + 0, + 0, + 0.5, + 0, + 0, + 0, + 5, + 1 + ], + "geometricError": 0, + "content": { + "url": "instances.i3dm" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ll.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ll.b3dm new file mode 100644 index 000000000000..dbd0d2142ca9 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ll.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/lr.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/lr.b3dm new file mode 100644 index 000000000000..f052e259f078 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/lr.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/points.pnts b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/points.pnts new file mode 100644 index 000000000000..84e78cf66c18 Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/points.pnts differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/tileset.json b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/tileset.json new file mode 100644 index 000000000000..067e2d15d12e --- /dev/null +++ b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/tileset.json @@ -0,0 +1,126 @@ +{ + "asset": { + "version": "0.0" + }, + "geometricError": 240, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 70, + "refine": "add", + "children": [ + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.31968, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ll.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.6988424218, + -1.3196390408203893, + 0.698874, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "lr.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.31968, + 0.698874, + -1.3196390408203893, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ur.b3dm" + } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 0, + "content": { + "url": "ul.b3dm" + } + }, + { + "transform": [ + 0.9686356343768793, + 0.24848542777253738, + 0, + 0, + -0.1598646089326599, + 0.6231776176011753, + 0.7655670863691378, + 0, + 0.19023226494501025, + -0.7415555603632288, + 0.643356072690908, + 0, + 1215014.7852103356, + -4736320.466755246, + 4081611.654821087, + 1 + ], + "viewerRequestVolume": { + "sphere": [ + 0, + 0, + 0, + 1000 + ] + }, + "boundingVolume": { + "sphere": [ + 0, + 0, + 0, + 10 + ] + }, + "geometricError": 0, + "content": { + "url": "points.pnts" + } + } + ] + } +} diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ul.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ul.b3dm new file mode 100644 index 000000000000..a386047150bb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ul.b3dm differ diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ur.b3dm b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ur.b3dm new file mode 100644 index 000000000000..24a6c3e4abdb Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/ur.b3dm differ diff --git a/Specs/Data/GoogleEarthEnterprise/gee.metadata b/Specs/Data/GoogleEarthEnterprise/gee.metadata new file mode 100644 index 000000000000..c224cecaa7b0 Binary files /dev/null and b/Specs/Data/GoogleEarthEnterprise/gee.metadata differ diff --git a/Specs/Data/GoogleEarthEnterprise/gee.terrain b/Specs/Data/GoogleEarthEnterprise/gee.terrain new file mode 100644 index 000000000000..d2880fec7816 Binary files /dev/null and b/Specs/Data/GoogleEarthEnterprise/gee.terrain differ diff --git a/Specs/Data/GoogleEarthImageryProvider/bad_channel.json b/Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_channel.json similarity index 100% rename from Specs/Data/GoogleEarthImageryProvider/bad_channel.json rename to Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_channel.json diff --git a/Specs/Data/GoogleEarthImageryProvider/bad_projection.json b/Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_projection.json similarity index 100% rename from Specs/Data/GoogleEarthImageryProvider/bad_projection.json rename to Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_projection.json diff --git a/Specs/Data/GoogleEarthImageryProvider/bad_version.json b/Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_version.json similarity index 100% rename from Specs/Data/GoogleEarthImageryProvider/bad_version.json rename to Specs/Data/GoogleEarthEnterpriseMapsProvider/bad_version.json diff --git a/Specs/Data/GoogleEarthImageryProvider/good.json b/Specs/Data/GoogleEarthEnterpriseMapsProvider/good.json similarity index 100% rename from Specs/Data/GoogleEarthImageryProvider/good.json rename to Specs/Data/GoogleEarthEnterpriseMapsProvider/good.json diff --git a/Specs/Data/Models/Box-Textured-BasePath/CesiumTexturedBoxTest.gltf b/Specs/Data/Models/Box-Textured-BasePath/CesiumTexturedBoxTest.gltf new file mode 100644 index 000000000000..4d4c3c178797 --- /dev/null +++ b/Specs/Data/Models/Box-Textured-BasePath/CesiumTexturedBoxTest.gltf @@ -0,0 +1,339 @@ +{ + "accessors": { + "accessor_21": { + "bufferView": "bufferView_29", + "byteOffset": 0, + "byteStride": 0, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + "accessor_23": { + "bufferView": "bufferView_30", + "byteOffset": 0, + "byteStride": 12, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + "accessor_25": { + "bufferView": "bufferView_30", + "byteOffset": 288, + "byteStride": 12, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + "accessor_27": { + "bufferView": "bufferView_30", + "byteOffset": 576, + "byteStride": 8, + "componentType": 5126, + "count": 24, + "max": [ + 6, + 1 + ], + "min": [ + 0, + 0 + ], + "type": "VEC2" + } + }, + "animations": {}, + "asset": { + "generator": "collada2gltf@", + "premultipliedAlpha": true, + "profile": { + "api": "WebGL", + "version": "1.0.2" + }, + "version": "1.0" + }, + "bufferViews": { + "bufferView_29": { + "buffer": "CesiumTexturedBoxTest", + "byteLength": 72, + "byteOffset": 0, + "target": 34963 + }, + "bufferView_30": { + "buffer": "CesiumTexturedBoxTest", + "byteLength": 768, + "byteOffset": 72, + "target": 34962 + } + }, + "buffers": { + "CesiumTexturedBoxTest": { + "byteLength": 840, + "type": "arraybuffer", + "uri": "CesiumTexturedBoxTest.bin" + } + }, + "images": { + "Image0001": { + "name": "Image0001", + "uri": "Cesium_Logo_Flat.jpg" + } + }, + "materials": { + "Effect-Texture": { + "name": "Texture", + "technique": "technique0", + "values": { + "diffuse": "texture_Image0001", + "shininess": 256, + "specular": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 1 + ] + } + } + }, + "meshes": { + "Geometry-mesh002": { + "name": "Mesh", + "primitives": [ + { + "attributes": { + "NORMAL": "accessor_25", + "POSITION": "accessor_23", + "TEXCOORD_0": "accessor_27" + }, + "indices": "accessor_21", + "material": "Effect-Texture", + "mode": 4 + } + ] + } + }, + "nodes": { + "Geometry-mesh002Node": { + "children": [], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ], + "meshes": [ + "Geometry-mesh002" + ], + "name": "Mesh" + }, + "groupLocator030Node": { + "children": [ + "txtrLocator026Node" + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ], + "name": "Texture_Group" + }, + "node_3": { + "children": [ + "Geometry-mesh002Node", + "groupLocator030Node" + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 0, + -1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "name": "Y_UP_Transform" + }, + "txtrLocator026Node": { + "children": [], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ], + "name": "Cesium_Logo_Flat__Image___Texture_" + } + }, + "programs": { + "program_0": { + "attributes": [ + "a_normal", + "a_position", + "a_texcoord0" + ], + "fragmentShader": "CesiumTexturedBoxTest0FS", + "vertexShader": "CesiumTexturedBoxTest0VS" + } + }, + "samplers": { + "sampler_0": { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + }, + "scene": "defaultScene", + "scenes": { + "defaultScene": { + "nodes": [ + "node_3" + ] + } + }, + "shaders": { + "CesiumTexturedBoxTest0FS": { + "type": 35632, + "uri": "CesiumTexturedBoxTest0FS.glsl" + }, + "CesiumTexturedBoxTest0VS": { + "type": 35633, + "uri": "CesiumTexturedBoxTest0VS.glsl" + } + }, + "skins": {}, + "techniques": { + "technique0": { + "attributes": { + "a_normal": "normal", + "a_position": "position", + "a_texcoord0": "texcoord0" + }, + "parameters": { + "diffuse": { + "type": 35678 + }, + "modelViewMatrix": { + "semantic": "MODELVIEW", + "type": 35676 + }, + "normal": { + "semantic": "NORMAL", + "type": 35665 + }, + "normalMatrix": { + "semantic": "MODELVIEWINVERSETRANSPOSE", + "type": 35675 + }, + "position": { + "semantic": "POSITION", + "type": 35665 + }, + "projectionMatrix": { + "semantic": "PROJECTION", + "type": 35676 + }, + "shininess": { + "type": 5126 + }, + "specular": { + "type": 35666 + }, + "texcoord0": { + "semantic": "TEXCOORD_0", + "type": 35664 + } + }, + "program": "program_0", + "states": { + "enable": [ + 2929, + 2884 + ] + }, + "uniforms": { + "u_diffuse": "diffuse", + "u_modelViewMatrix": "modelViewMatrix", + "u_normalMatrix": "normalMatrix", + "u_projectionMatrix": "projectionMatrix", + "u_shininess": "shininess", + "u_specular": "specular" + } + } + }, + "textures": { + "texture_Image0001": { + "format": 6408, + "internalFormat": 6408, + "sampler": "sampler_0", + "source": "Image0001", + "target": 3553, + "type": 5121 + } + } +} diff --git a/Specs/Data/Models/moving-box/moving-box.gltf b/Specs/Data/Models/moving-box/moving-box.gltf new file mode 100644 index 000000000000..68690184fcd3 --- /dev/null +++ b/Specs/Data/Models/moving-box/moving-box.gltf @@ -0,0 +1,324 @@ +{ + "accessors": { + "accessor_16": { + "bufferView": "bufferView_34", + "byteOffset": 0, + "byteStride": 0, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + "accessor_18": { + "bufferView": "bufferView_35", + "byteOffset": 0, + "byteStride": 12, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + "accessor_20": { + "bufferView": "bufferView_35", + "byteOffset": 288, + "byteStride": 12, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + "animAccessor_0": { + "bufferView": "bufferView_33", + "byteOffset": 0, + "componentType": 5126, + "count": 2, + "type": "SCALAR" + }, + "animAccessor_1": { + "bufferView": "bufferView_33", + "byteOffset": 8, + "componentType": 5126, + "count": 2, + "type": "VEC3" + } + }, + "animations": { + "animation_0": { + "channels": [ + { + "sampler": "animation_0_translation_sampler", + "target": { + "id": "Cube", + "path": "translation" + } + } + ], + "parameters": { + "TIME": "animAccessor_0", + "translation": "animAccessor_1" + }, + "samplers": { + "animation_0_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_1": { + "channels": [], + "parameters": { + "TIME": "animAccessor_0" + }, + "samplers": {} + }, + "animation_2": { + "channels": [], + "parameters": { + "TIME": "animAccessor_0" + }, + "samplers": {} + } + }, + "asset": { + "generator": "collada2gltf@", + "premultipliedAlpha": true, + "profile": { + "api": "WebGL", + "version": "1.0.2" + }, + "version": 1 + }, + "bufferViews": { + "bufferView_33": { + "buffer": "input", + "byteLength": 32, + "byteOffset": 0 + }, + "bufferView_34": { + "buffer": "input", + "byteLength": 72, + "byteOffset": 32, + "target": 34963 + }, + "bufferView_35": { + "buffer": "input", + "byteLength": 576, + "byteOffset": 104, + "target": 34962 + } + }, + "buffers": { + "input": { + "byteLength": 680, + "type": "arraybuffer", + "uri": "data:application/octet-stream;base64,maoqPT9VVT4AAAAAAAAAAAAAAAAAAAAA76dTQQAAAAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASAAAAAgATAAMABQAUAAYACAAVAAkACwAWAAwADgAXAA8AEQAAAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAvwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAgD8AAIC/AACAvwAAgL8AAIA/AACAvwAAgD8AAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAIA/AAAAAAAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8=" + } + }, + "materials": { + "Material_001-effect": { + "name": "Material_001", + "technique": "technique0", + "values": { + "ambient": [ + 0, + 0, + 0, + 1 + ], + "diffuse": [ + 0, + 1, + 0.0019188299775123596, + 1 + ], + "emission": [ + 0, + 0, + 0, + 1 + ], + "shininess": 50, + "specular": [ + 0.5, + 0.5, + 0.5, + 1 + ] + } + } + }, + "meshes": { + "Cube_001-mesh": { + "name": "Cube.001", + "primitives": [ + { + "attributes": { + "NORMAL": "accessor_20", + "POSITION": "accessor_18" + }, + "indices": "accessor_16", + "material": "Material_001-effect", + "mode": 4 + } + ] + } + }, + "nodes": { + "Cube": { + "children": [], + "meshes": [ + "Cube_001-mesh" + ], + "name": "Cube", + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 0, + 0, + 0 + ] + }, + "node_1": { + "children": [ + "Cube" + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 0, + -1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "name": "Y_UP_Transform" + } + }, + "programs": { + "program_0": { + "attributes": [ + "a_normal", + "a_position" + ], + "fragmentShader": "input0FS", + "vertexShader": "input0VS" + } + }, + "scene": "defaultScene", + "scenes": { + "defaultScene": { + "nodes": [ + "node_1" + ] + } + }, + "shaders": { + "input0FS": { + "type": 35632, + "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0Owp2YXJ5aW5nIHZlYzMgdl9ub3JtYWw7CnVuaWZvcm0gdmVjNCB1X2FtYmllbnQ7CnVuaWZvcm0gdmVjNCB1X2RpZmZ1c2U7CnVuaWZvcm0gdmVjNCB1X2VtaXNzaW9uOwp1bmlmb3JtIHZlYzQgdV9zcGVjdWxhcjsKdW5pZm9ybSBmbG9hdCB1X3NoaW5pbmVzczsKdm9pZCBtYWluKHZvaWQpIHsKdmVjMyBub3JtYWwgPSBub3JtYWxpemUodl9ub3JtYWwpOwp2ZWM0IGNvbG9yID0gdmVjNCgwLiwgMC4sIDAuLCAwLik7CnZlYzQgZGlmZnVzZSA9IHZlYzQoMC4sIDAuLCAwLiwgMS4pOwp2ZWM0IGVtaXNzaW9uOwp2ZWM0IGFtYmllbnQ7CnZlYzQgc3BlY3VsYXI7CmFtYmllbnQgPSB1X2FtYmllbnQ7CmRpZmZ1c2UgPSB1X2RpZmZ1c2U7CmVtaXNzaW9uID0gdV9lbWlzc2lvbjsKc3BlY3VsYXIgPSB1X3NwZWN1bGFyOwpkaWZmdXNlLnh5eiAqPSBtYXgoZG90KG5vcm1hbCx2ZWMzKDAuLDAuLDEuKSksIDAuKTsKY29sb3IueHl6ICs9IGRpZmZ1c2UueHl6Owpjb2xvci54eXogKz0gZW1pc3Npb24ueHl6Owpjb2xvciA9IHZlYzQoY29sb3IucmdiICogZGlmZnVzZS5hLCBkaWZmdXNlLmEpOwpnbF9GcmFnQ29sb3IgPSBjb2xvcjsKfQo=" + }, + "input0VS": { + "type": 35633, + "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0OwphdHRyaWJ1dGUgdmVjMyBhX3Bvc2l0aW9uOwphdHRyaWJ1dGUgdmVjMyBhX25vcm1hbDsKdmFyeWluZyB2ZWMzIHZfbm9ybWFsOwp1bmlmb3JtIG1hdDMgdV9ub3JtYWxNYXRyaXg7CnVuaWZvcm0gbWF0NCB1X21vZGVsVmlld01hdHJpeDsKdW5pZm9ybSBtYXQ0IHVfcHJvamVjdGlvbk1hdHJpeDsKdm9pZCBtYWluKHZvaWQpIHsKdmVjNCBwb3MgPSB1X21vZGVsVmlld01hdHJpeCAqIHZlYzQoYV9wb3NpdGlvbiwxLjApOwp2X25vcm1hbCA9IHVfbm9ybWFsTWF0cml4ICogYV9ub3JtYWw7CmdsX1Bvc2l0aW9uID0gdV9wcm9qZWN0aW9uTWF0cml4ICogcG9zOwp9Cg==" + } + }, + "skins": {}, + "techniques": { + "technique0": { + "attributes": { + "a_normal": "normal", + "a_position": "position" + }, + "parameters": { + "ambient": { + "type": 35666 + }, + "diffuse": { + "type": 35666 + }, + "emission": { + "type": 35666 + }, + "modelViewMatrix": { + "semantic": "MODELVIEW", + "type": 35676 + }, + "normal": { + "semantic": "NORMAL", + "type": 35665 + }, + "normalMatrix": { + "semantic": "MODELVIEWINVERSETRANSPOSE", + "type": 35675 + }, + "position": { + "semantic": "POSITION", + "type": 35665 + }, + "projectionMatrix": { + "semantic": "PROJECTION", + "type": 35676 + }, + "shininess": { + "type": 5126 + }, + "specular": { + "type": 35666 + } + }, + "program": "program_0", + "states": { + "enable": [ + 2929, + 2884 + ] + }, + "uniforms": { + "u_ambient": "ambient", + "u_diffuse": "diffuse", + "u_emission": "emission", + "u_modelViewMatrix": "modelViewMatrix", + "u_normalMatrix": "normalMatrix", + "u_projectionMatrix": "projectionMatrix", + "u_shininess": "shininess", + "u_specular": "specular" + } + } + } +} \ No newline at end of file diff --git a/Specs/DataSources/BillboardGraphicsSpec.js b/Specs/DataSources/BillboardGraphicsSpec.js index efdbb17cb80d..a8a9ef615111 100644 --- a/Specs/DataSources/BillboardGraphicsSpec.js +++ b/Specs/DataSources/BillboardGraphicsSpec.js @@ -42,7 +42,8 @@ defineSuite([ translucencyByDistance : new NearFarScalar(17, 18, 19, 20), pixelOffsetScaleByDistance : new NearFarScalar(21, 22, 23, 24), sizeInMeters : true, - distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0) + distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0), + disableDepthTestDistance : 10.0 }; var billboard = new BillboardGraphics(options); @@ -63,6 +64,7 @@ defineSuite([ expect(billboard.pixelOffsetScaleByDistance).toBeInstanceOf(ConstantProperty); expect(billboard.sizeInMeters).toBeInstanceOf(ConstantProperty); expect(billboard.distanceDisplayCondition).toBeInstanceOf(ConstantProperty); + expect(billboard.disableDepthTestDistance).toBeInstanceOf(ConstantProperty); expect(billboard.image.getValue()).toEqual(options.image); expect(billboard.rotation.getValue()).toEqual(options.rotation); @@ -81,6 +83,7 @@ defineSuite([ expect(billboard.pixelOffsetScaleByDistance.getValue()).toEqual(options.pixelOffsetScaleByDistance); expect(billboard.sizeInMeters.getValue()).toEqual(options.sizeInMeters); expect(billboard.distanceDisplayCondition.getValue()).toEqual(options.distanceDisplayCondition); + expect(billboard.disableDepthTestDistance.getValue()).toEqual(options.disableDepthTestDistance); }); it('merge assigns unassigned properties', function() { @@ -104,6 +107,7 @@ defineSuite([ source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.sizeInMeters = new ConstantProperty(true); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); + source.disableDepthTestDistance = 10.0; var target = new BillboardGraphics(); target.merge(source); @@ -127,6 +131,7 @@ defineSuite([ expect(target.pixelOffsetScaleByDistance).toBe(source.pixelOffsetScaleByDistance); expect(target.sizeInMeters).toBe(source.sizeInMeters); expect(target.distanceDisplayCondition).toBe(source.distanceDisplayCondition); + expect(target.disableDepthTestDistance).toBe(source.disableDepthTestDistance); }); it('merge does not assign assigned properties', function() { @@ -150,6 +155,7 @@ defineSuite([ source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.sizeInMeters = new ConstantProperty(true); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); + source.disableDepthTestDistance = new ConstantProperty(10.0); var image = new ConstantProperty(''); var imageSubRegion = new ConstantProperty(); @@ -170,6 +176,7 @@ defineSuite([ var pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar()); var sizeInMeters = new ConstantProperty(true); var distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); + var disableDepthTestDistance = new ConstantProperty(10.0); var target = new BillboardGraphics(); target.image = image; @@ -191,6 +198,7 @@ defineSuite([ target.pixelOffsetScaleByDistance = pixelOffsetScaleByDistance; target.sizeInMeters = sizeInMeters; target.distanceDisplayCondition = distanceDisplayCondition; + target.disableDepthTestDistance = disableDepthTestDistance; target.merge(source); @@ -213,6 +221,7 @@ defineSuite([ expect(target.pixelOffsetScaleByDistance).toBe(pixelOffsetScaleByDistance); expect(target.sizeInMeters).toBe(sizeInMeters); expect(target.distanceDisplayCondition).toBe(distanceDisplayCondition); + expect(target.disableDepthTestDistance).toBe(disableDepthTestDistance); }); it('clone works', function() { @@ -236,6 +245,7 @@ defineSuite([ source.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); source.sizeInMeters = new ConstantProperty(true); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); + source.disableDepthTestDistance = new ConstantProperty(10.0); var result = source.clone(); expect(result.image).toBe(source.image); @@ -257,6 +267,7 @@ defineSuite([ expect(result.pixelOffsetScaleByDistance).toBe(source.pixelOffsetScaleByDistance); expect(result.sizeInMeters).toBe(source.sizeInMeters); expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition); + expect(result.disableDepthTestDistance).toBe(source.disableDepthTestDistance); }); it('merge throws if source undefined', function() { diff --git a/Specs/DataSources/BillboardVisualizerSpec.js b/Specs/DataSources/BillboardVisualizerSpec.js index d802621455e7..ee7c1bba1817 100644 --- a/Specs/DataSources/BillboardVisualizerSpec.js +++ b/Specs/DataSources/BillboardVisualizerSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Color', + 'Core/defined', 'Core/DistanceDisplayCondition', 'Core/JulianDate', 'Core/NearFarScalar', @@ -27,6 +28,7 @@ defineSuite([ Cartesian2, Cartesian3, Color, + defined, DistanceDisplayCondition, JulianDate, NearFarScalar, @@ -62,7 +64,9 @@ defineSuite([ }); afterEach(function() { - visualizer = visualizer && visualizer.destroy(); + if (defined(visualizer)) { + visualizer = visualizer.destroy(); + } entityCluster.destroy(); }); @@ -163,6 +167,7 @@ defineSuite([ billboard.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); billboard.sizeInMeters = new ConstantProperty(true); billboard.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); + billboard.disableDepthTestDistance = new ConstantProperty(10.0); visualizer.update(time); @@ -191,6 +196,7 @@ defineSuite([ expect(bb.pixelOffsetScaleByDistance).toEqual(testObject.billboard.pixelOffsetScaleByDistance.getValue(time)); expect(bb.sizeInMeters).toEqual(testObject.billboard.sizeInMeters.getValue(time)); expect(bb.distanceDisplayCondition).toEqual(testObject.billboard.distanceDisplayCondition.getValue(time)); + expect(bb.disableDepthTestDistance).toEqual(testObject.billboard.disableDepthTestDistance.getValue(time)); expect(bb._imageSubRegion).toEqual(testObject.billboard.imageSubRegion.getValue(time)); billboard.show = new ConstantProperty(false); @@ -232,6 +238,7 @@ defineSuite([ billboard.pixelOffsetScaleByDistance = new ConstantProperty(new NearFarScalar(1.0, 0.0, 3.0e9, 0.0)); billboard.sizeInMeters = new ConstantProperty(true); billboard.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition(10.0, 100.0)); + billboard.disableDepthTestDistance = new ConstantProperty(10.0); visualizer.update(time); @@ -250,32 +257,33 @@ defineSuite([ visualizer.update(time); return !bb.show; }).then(function() { - billboard.show = new ConstantProperty(true); - - return pollToPromise(function() { - visualizer.update(time); - return bb.show; - }).then(function() { - expect(bb.position).toEqual(testObject.position.getValue(time)); - expect(bb.color).toEqual(testObject.billboard.color.getValue(time)); - expect(bb.eyeOffset).toEqual(testObject.billboard.eyeOffset.getValue(time)); - expect(bb.scale).toEqual(testObject.billboard.scale.getValue(time)); - expect(bb.rotation).toEqual(testObject.billboard.rotation.getValue(time)); - expect(bb.alignedAxis).toEqual(testObject.billboard.alignedAxis.getValue(time)); - expect(bb.heightReference).toEqual(testObject.billboard.heightReference.getValue(time)); - expect(bb.horizontalOrigin).toEqual(testObject.billboard.horizontalOrigin.getValue(time)); - expect(bb.verticalOrigin).toEqual(testObject.billboard.verticalOrigin.getValue(time)); - expect(bb.width).toEqual(testObject.billboard.width.getValue(time)); - expect(bb.height).toEqual(testObject.billboard.height.getValue(time)); - expect(bb.scaleByDistance).toEqual(testObject.billboard.scaleByDistance.getValue(time)); - expect(bb.translucencyByDistance).toEqual(testObject.billboard.translucencyByDistance.getValue(time)); - expect(bb.pixelOffsetScaleByDistance).toEqual(testObject.billboard.pixelOffsetScaleByDistance.getValue(time)); - expect(bb.sizeInMeters).toEqual(testObject.billboard.sizeInMeters.getValue(time)); - expect(bb.distanceDisplayCondition).toEqual(testObject.billboard.distanceDisplayCondition.getValue(time)); - expect(bb.image).toBeDefined(); - expect(bb._imageSubRegion).toEqual(testObject.billboard.imageSubRegion.getValue(time)); - }); - }); + billboard.show = new ConstantProperty(true); + + return pollToPromise(function() { + visualizer.update(time); + return bb.show; + }).then(function() { + expect(bb.position).toEqual(testObject.position.getValue(time)); + expect(bb.color).toEqual(testObject.billboard.color.getValue(time)); + expect(bb.eyeOffset).toEqual(testObject.billboard.eyeOffset.getValue(time)); + expect(bb.scale).toEqual(testObject.billboard.scale.getValue(time)); + expect(bb.rotation).toEqual(testObject.billboard.rotation.getValue(time)); + expect(bb.alignedAxis).toEqual(testObject.billboard.alignedAxis.getValue(time)); + expect(bb.heightReference).toEqual(testObject.billboard.heightReference.getValue(time)); + expect(bb.horizontalOrigin).toEqual(testObject.billboard.horizontalOrigin.getValue(time)); + expect(bb.verticalOrigin).toEqual(testObject.billboard.verticalOrigin.getValue(time)); + expect(bb.width).toEqual(testObject.billboard.width.getValue(time)); + expect(bb.height).toEqual(testObject.billboard.height.getValue(time)); + expect(bb.scaleByDistance).toEqual(testObject.billboard.scaleByDistance.getValue(time)); + expect(bb.translucencyByDistance).toEqual(testObject.billboard.translucencyByDistance.getValue(time)); + expect(bb.pixelOffsetScaleByDistance).toEqual(testObject.billboard.pixelOffsetScaleByDistance.getValue(time)); + expect(bb.sizeInMeters).toEqual(testObject.billboard.sizeInMeters.getValue(time)); + expect(bb.distanceDisplayCondition).toEqual(testObject.billboard.distanceDisplayCondition.getValue(time)); + expect(bb.disableDepthTestDistance).toEqual(testObject.billboard.disableDepthTestDistance.getValue(time)); + expect(bb.image).toBeDefined(); + expect(bb._imageSubRegion).toEqual(testObject.billboard.imageSubRegion.getValue(time)); + }); + }); }); }); diff --git a/Specs/DataSources/CzmlDataSourceSpec.js b/Specs/DataSources/CzmlDataSourceSpec.js index 10b25f3116fb..5bf2bc771748 100644 --- a/Specs/DataSources/CzmlDataSourceSpec.js +++ b/Specs/DataSources/CzmlDataSourceSpec.js @@ -15,6 +15,7 @@ defineSuite([ 'Core/Iso8601', 'Core/JulianDate', 'Core/loadJson', + 'Core/loadWithXhr', 'Core/Math', 'Core/NearFarScalar', 'Core/Quaternion', @@ -51,6 +52,7 @@ defineSuite([ Iso8601, JulianDate, loadJson, + loadWithXhr, CesiumMath, NearFarScalar, Quaternion, @@ -258,13 +260,13 @@ defineSuite([ it('process loads expected data', function() { var dataSource = new CzmlDataSource(); - dataSource.process(simple, simpleUrl); + dataSource.process(simple); expect(dataSource.entities.values.length).toEqual(10); }); it('process loads data on top of existing', function() { var dataSource = new CzmlDataSource(); - dataSource.process(simple, simpleUrl); + dataSource.process(simple); expect(dataSource.entities.values.length === 10); dataSource.process(vehicle, vehicleUrl); @@ -273,7 +275,7 @@ defineSuite([ it('load replaces data', function() { var dataSource = new CzmlDataSource(); - dataSource.process(simple, simpleUrl); + dataSource.process(simple); expect(dataSource.entities.values.length).toEqual(10); dataSource.load(vehicle, vehicleUrl); @@ -554,6 +556,52 @@ defineSuite([ expect(imageProperty.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toEqual(source + 'image2.png'); }); + it('appends query to all uri', function() { + var source = 'http://some.url.invalid/'; + var packet = { + billboard : { + image : [{ + interval : '2013-01-01T00:00:00Z/2013-01-01T01:00:00Z', + uri : 'image.png' + }, { + interval : '2013-01-01T01:00:00Z/2013-01-01T02:00:00Z', + uri : 'image2.png' + }] + } + }; + + var dataSource = new CzmlDataSource(); + dataSource.load(makePacket(packet), { + sourceUri : source, + query : { + token : 34570, + password : "Passw0rd" + } + }); + var entity = dataSource.entities.values[0]; + var imageProperty = entity.billboard.image; + expect(imageProperty.getValue(JulianDate.fromIso8601('2013-01-01T00:00:00Z'))).toEqual(source + 'image.png' + '?token=34570&password=Passw0rd'); + expect(imageProperty.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toEqual(source + 'image2.png' + '?token=34570&password=Passw0rd'); + }); + + it('appends query tokens to source URL', function() { + var dataSource = new CzmlDataSource(); + var requestNetworkLink = when.defer(); + + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + requestNetworkLink.resolve(url); + deferred.reject(); + }); + + dataSource.process(simpleUrl, { query : { + "token" : 30203, + "pass" : "passw0rd" + }}); + return requestNetworkLink.promise.then(function(url) { + expect(url).toEqual(simpleUrl + '?token=30203&pass=passw0rd'); + }); + }); + it('CZML adds data for constrained billboard.', function() { var billboardPacket = { billboard : { @@ -3278,6 +3326,31 @@ defineSuite([ expect(entity.polyline.material.color.getValue()).toEqual(new Color(0.1, 0.2, 0.3, 0.4)); }); + it('Polyline dash.', function() { + var packet = { + id : 'polylineDash', + polyline : { + material : { + polylineDash : { + color : { + rgbaf : [0.1, 0.2, 0.3, 0.4] + }, + dashLength: 16.0, + dashPattern: 7.0 + } + } + } + }; + + var dataSource = new CzmlDataSource(); + dataSource.load(makePacket(packet)); + + var entity = dataSource.entities.getById('polylineDash'); + expect(entity.polyline.material.color.getValue()).toEqual(new Color(0.1, 0.2, 0.3, 0.4)); + expect(entity.polyline.material.dashLength.getValue()).toEqual(16.0); + expect(entity.polyline.material.dashPattern.getValue()).toEqual(7.0); + }); + it('Processes extrapolation options', function() { var packet = { id : 'point', @@ -3359,7 +3432,6 @@ defineSuite([ it('checks validation document', function() { return CzmlDataSource.load('Data/CZML/ValidationDocument.czml').then(function(dataSource) { - /*jshint -W120 */ var e; var date; var documentStartDate = JulianDate.fromIso8601('2016-06-17T12:00:00Z'); @@ -3728,6 +3800,11 @@ defineSuite([ expect(e.path.material.outlineWidth.getValue(date)).toEqual(11017.0); expect(e = dataSource.entities.getById('material_path_material_polylineArrow')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqual(Color.fromBytes(166, 131, 155, 102)); + expect(e = dataSource.entities.getById('material_path_material_polylineDash')).toBeDefined(); + expect(e.path.material.color.getValue(date)).toEqual(Color.fromBytes(190, 189, 9, 7)); + expect(e.path.material.gapColor.getValue(date)).toEqual(Color.fromBytes(170, 88, 12, 24)); + expect(e.path.material.dashLength.getValue(date)).toEqual(45848.0); + expect(e.path.material.dashPattern.getValue(date)).toEqual(13519.0); expect(e = dataSource.entities.getById('material_path_material_polylineGlow')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqual(Color.fromBytes(72, 114, 200, 147)); expect(e.path.material.glowPower.getValue(date)).toEqual(42344.0); @@ -3754,6 +3831,10 @@ defineSuite([ expect(e.path.material.outlineColor.getValue(date)).toEqualEpsilon(new Color(0.266666666666667, 0.556862745098039, 0.352941176470588, 0.76078431372549), 1e-14); expect(e = dataSource.entities.getById('constant_path_material_polylineArrow_color')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqualEpsilon(new Color(0.627450980392157, 0.27843137254902, 0.972549019607843, 0.92156862745098), 1e-14); + expect(e = dataSource.entities.getById('constant_path_material_polylineDash_color')).toBeDefined(); + expect(e.path.material.color.getValue(date)).toEqualEpsilon(new Color(0.113725490196078, 0.368627450980392, 0.411764705882353, 0.745098039215686), 1e-14); + expect(e = dataSource.entities.getById('constant_path_material_polylineDash_gapColor')).toBeDefined(); + expect(e.path.material.gapColor.getValue(date)).toEqualEpsilon(new Color(0.831372549019608, 0.313725490196078, 0.341176470588235, 0.749019607843137), 1e-14); expect(e = dataSource.entities.getById('constant_path_material_polylineGlow_color')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqualEpsilon(new Color(0.584313725490196, 0.0156862745098039, 0.329411764705882, 0.270588235294118), 1e-14); expect(e = dataSource.entities.getById('constant_path_material_image_color')).toBeDefined(); @@ -3813,6 +3894,11 @@ defineSuite([ expect(e.polyline.material.outlineWidth.getValue(date)).toEqual(6879.0); expect(e = dataSource.entities.getById('material_polyline_material_polylineArrow')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqual(Color.fromBytes(82, 169, 80, 107)); + expect(e = dataSource.entities.getById('material_polyline_material_polylineDash')).toBeDefined(); + expect(e.polyline.material.color.getValue(date)).toEqual(Color.fromBytes(22, 214, 57, 141)); + expect(e.polyline.material.gapColor.getValue(date)).toEqual(Color.fromBytes(150, 91, 109, 117)); + expect(e.polyline.material.dashLength.getValue(date)).toEqual(60297.0); + expect(e.polyline.material.dashPattern.getValue(date)).toEqual(40430.0); expect(e = dataSource.entities.getById('material_polyline_material_polylineGlow')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqual(Color.fromBytes(59, 125, 181, 171)); expect(e.polyline.material.glowPower.getValue(date)).toEqual(41345.0); @@ -3839,6 +3925,10 @@ defineSuite([ expect(e.polyline.material.outlineColor.getValue(date)).toEqualEpsilon(new Color(0.815686274509804, 0.545098039215686, 0.529411764705882, 0.317647058823529), 1e-14); expect(e = dataSource.entities.getById('constant_polyline_material_polylineArrow_color')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqualEpsilon(new Color(0.831372549019608, 0.823529411764706, 0.631372549019608, 0.443137254901961), 1e-14); + expect(e = dataSource.entities.getById('constant_polyline_material_polylineDash_color')).toBeDefined(); + expect(e.polyline.material.color.getValue(date)).toEqualEpsilon(new Color(0.462745098039216, 0.184313725490196, 0.329411764705882, 0), 1e-14); + expect(e = dataSource.entities.getById('constant_polyline_material_polylineDash_gapColor')).toBeDefined(); + expect(e.polyline.material.gapColor.getValue(date)).toEqualEpsilon(new Color(0.0509803921568627, 0.0313725490196078, 0.23921568627451, 0.4), 1e-14); expect(e = dataSource.entities.getById('constant_polyline_material_polylineGlow_color')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqualEpsilon(new Color(0.411764705882353, 0.313725490196078, 0.858823529411765, 0.380392156862745), 1e-14); expect(e = dataSource.entities.getById('constant_polyline_material_image_color')).toBeDefined(); @@ -4363,6 +4453,11 @@ defineSuite([ expect(e.path.material.outlineWidth.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineOutline').path.material.outlineWidth.getValue(date)); expect(e = dataSource.entities.getById('reference_path_material_polylineArrow')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineArrow').path.material.color.getValue(date)); + expect(e = dataSource.entities.getById('reference_path_material_polylineDash')).toBeDefined(); + expect(e.path.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineDash').path.material.color.getValue(date)); + expect(e.path.material.gapColor.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineDash').path.material.gapColor.getValue(date)); + expect(e.path.material.dashLength.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineDash').path.material.dashLength.getValue(date)); + expect(e.path.material.dashPattern.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineDash').path.material.dashPattern.getValue(date)); expect(e = dataSource.entities.getById('reference_path_material_polylineGlow')).toBeDefined(); expect(e.path.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineGlow').path.material.color.getValue(date)); expect(e.path.material.glowPower.getValue(date)).toEqual(dataSource.entities.getById('material_path_material_polylineGlow').path.material.glowPower.getValue(date)); @@ -4406,6 +4501,11 @@ defineSuite([ expect(e.polyline.material.outlineWidth.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineOutline').polyline.material.outlineWidth.getValue(date)); expect(e = dataSource.entities.getById('reference_polyline_material_polylineArrow')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineArrow').polyline.material.color.getValue(date)); + expect(e = dataSource.entities.getById('reference_polyline_material_polylineDash')).toBeDefined(); + expect(e.polyline.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineDash').polyline.material.color.getValue(date)); + expect(e.polyline.material.gapColor.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineDash').polyline.material.gapColor.getValue(date)); + expect(e.polyline.material.dashLength.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineDash').polyline.material.dashLength.getValue(date)); + expect(e.polyline.material.dashPattern.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineDash').polyline.material.dashPattern.getValue(date)); expect(e = dataSource.entities.getById('reference_polyline_material_polylineGlow')).toBeDefined(); expect(e.polyline.material.color.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineGlow').polyline.material.color.getValue(date)); expect(e.polyline.material.glowPower.getValue(date)).toEqual(dataSource.entities.getById('material_polyline_material_polylineGlow').polyline.material.glowPower.getValue(date)); @@ -4978,6 +5078,15 @@ defineSuite([ expect(e = dataSource.entities.getById('sampled_path_material_polylineArrow')).toBeDefined(); expect(e.path.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(150, 221, 161, 136)); expect(e.path.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(19, 231, 68, 117)); + expect(e = dataSource.entities.getById('sampled_path_material_polylineDash')).toBeDefined(); + expect(e.path.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(140, 167, 151, 119)); + expect(e.path.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(65, 100, 228, 104)); + expect(e.path.material.gapColor.getValue(documentStartDate)).toEqual(Color.fromBytes(154, 198, 168, 151)); + expect(e.path.material.gapColor.getValue(documentStopDate)).toEqual(Color.fromBytes(16, 23, 0, 42)); + expect(e.path.material.dashLength.getValue(documentStartDate)).toEqual(38294.0); + expect(e.path.material.dashLength.getValue(documentStopDate)).toEqual(33057.0); + expect(e.path.material.dashPattern.getValue(documentStartDate)).toEqual(58660.0); + expect(e.path.material.dashPattern.getValue(documentStopDate)).toEqual(3340.0); expect(e = dataSource.entities.getById('sampled_path_material_polylineGlow')).toBeDefined(); expect(e.path.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(197, 117, 196, 254)); expect(e.path.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(18, 17, 195, 230)); @@ -5017,6 +5126,12 @@ defineSuite([ expect(e = dataSource.entities.getById('sampled_path_material_polylineArrow_color')).toBeDefined(); expect(e.path.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.0666666666666667, 0.972549019607843, 0.686274509803922, 0.325490196078431), 1e-14); expect(e.path.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.2, 0.482352941176471, 0.498039215686275, 0.219607843137255), 1e-14); + expect(e = dataSource.entities.getById('sampled_path_material_polylineDash_color')).toBeDefined(); + expect(e.path.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.803921568627451, 0.67843137254902, 0.176470588235294, 0.709803921568627), 1e-14); + expect(e.path.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.674509803921569, 0.0980392156862745, 0.447058823529412, 0.803921568627451), 1e-14); + expect(e = dataSource.entities.getById('sampled_path_material_polylineDash_gapColor')).toBeDefined(); + expect(e.path.material.gapColor.getValue(documentStartDate)).toEqualEpsilon(new Color(0.231372549019608, 0.745098039215686, 0.772549019607843, 0.901960784313726), 1e-14); + expect(e.path.material.gapColor.getValue(documentStopDate)).toEqualEpsilon(new Color(0.713725490196078, 0.180392156862745, 0.317647058823529, 0.309803921568627), 1e-14); expect(e = dataSource.entities.getById('sampled_path_material_polylineGlow_color')).toBeDefined(); expect(e.path.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.63921568627451, 0.2, 0.0196078431372549, 0.984313725490196), 1e-14); expect(e.path.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.376470588235294, 0.815686274509804, 0.933333333333333, 0.0235294117647059), 1e-14); @@ -5094,6 +5209,15 @@ defineSuite([ expect(e = dataSource.entities.getById('sampled_polyline_material_polylineArrow')).toBeDefined(); expect(e.polyline.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(141, 137, 252, 157)); expect(e.polyline.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(8, 236, 198, 57)); + expect(e = dataSource.entities.getById('sampled_polyline_material_polylineDash')).toBeDefined(); + expect(e.polyline.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(77, 159, 238, 158)); + expect(e.polyline.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(206, 194, 234, 158)); + expect(e.polyline.material.gapColor.getValue(documentStartDate)).toEqual(Color.fromBytes(232, 145, 15, 164)); + expect(e.polyline.material.gapColor.getValue(documentStopDate)).toEqual(Color.fromBytes(173, 151, 118, 138)); + expect(e.polyline.material.dashLength.getValue(documentStartDate)).toEqual(41757.0); + expect(e.polyline.material.dashLength.getValue(documentStopDate)).toEqual(10126.0); + expect(e.polyline.material.dashPattern.getValue(documentStartDate)).toEqual(33948.0); + expect(e.polyline.material.dashPattern.getValue(documentStopDate)).toEqual(16892.0); expect(e = dataSource.entities.getById('sampled_polyline_material_polylineGlow')).toBeDefined(); expect(e.polyline.material.color.getValue(documentStartDate)).toEqual(Color.fromBytes(174, 178, 78, 176)); expect(e.polyline.material.color.getValue(documentStopDate)).toEqual(Color.fromBytes(79, 191, 38, 195)); @@ -5133,6 +5257,12 @@ defineSuite([ expect(e = dataSource.entities.getById('sampled_polyline_material_polylineArrow_color')).toBeDefined(); expect(e.polyline.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.52156862745098, 0.725490196078431, 0.87843137254902, 0.823529411764706), 1e-14); expect(e.polyline.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.772549019607843, 0.862745098039216, 0.325490196078431, 0), 1e-14); + expect(e = dataSource.entities.getById('sampled_polyline_material_polylineDash_color')).toBeDefined(); + expect(e.polyline.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.525490196078431, 0.992156862745098, 0.964705882352941, 0.364705882352941), 1e-14); + expect(e.polyline.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.27843137254902, 0.133333333333333, 0.447058823529412, 0.192156862745098), 1e-14); + expect(e = dataSource.entities.getById('sampled_polyline_material_polylineDash_gapColor')).toBeDefined(); + expect(e.polyline.material.gapColor.getValue(documentStartDate)).toEqualEpsilon(new Color(0.498039215686275, 0.776470588235294, 0.803921568627451, 0.690196078431373), 1e-14); + expect(e.polyline.material.gapColor.getValue(documentStopDate)).toEqualEpsilon(new Color(0.376470588235294, 0.898039215686275, 0.168627450980392, 0.898039215686275), 1e-14); expect(e = dataSource.entities.getById('sampled_polyline_material_polylineGlow_color')).toBeDefined(); expect(e.polyline.material.color.getValue(documentStartDate)).toEqualEpsilon(new Color(0.705882352941177, 0.901960784313726, 0.0784313725490196, 0.356862745098039), 1e-14); expect(e.polyline.material.color.getValue(documentStopDate)).toEqualEpsilon(new Color(0.517647058823529, 0.207843137254902, 0.701960784313725, 0.105882352941176), 1e-14); diff --git a/Specs/DataSources/EntityClusterSpec.js b/Specs/DataSources/EntityClusterSpec.js index 466323b543a4..dd77ff1f4e14 100644 --- a/Specs/DataSources/EntityClusterSpec.js +++ b/Specs/DataSources/EntityClusterSpec.js @@ -175,6 +175,29 @@ defineSuite([ expect(cluster._clusterLabelCollection).not.toBeDefined(); }); + it('clusters billboards on first update', function() { + cluster = new EntityCluster(); + cluster._initialize(scene); + + var entity = new Entity(); + var billboard = cluster.getBillboard(entity); + billboard.id = entity; + billboard.image = createBillboardImage(); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + + entity = new Entity(); + billboard = cluster.getBillboard(entity); + billboard.id = entity; + billboard.image = createBillboardImage(); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + + cluster.enabled = true; + cluster.update(scene.frameState); + + expect(cluster._clusterLabelCollection).toBeDefined(); + expect(cluster._clusterLabelCollection.length).toEqual(1); + }); + it('clusters labels', function() { cluster = new EntityCluster(); cluster._initialize(scene); @@ -208,6 +231,29 @@ defineSuite([ expect(cluster._clusterLabelCollection).not.toBeDefined(); }); + it('clusters labels on first update', function() { + cluster = new EntityCluster(); + cluster._initialize(scene); + + var entity = new Entity(); + var label = cluster.getLabel(entity); + label.id = entity; + label.text = 'a'; + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + + entity = new Entity(); + label = cluster.getLabel(entity); + label.id = entity; + label.text = 'b'; + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + + cluster.enabled = true; + cluster.update(scene.frameState); + + expect(cluster._clusterLabelCollection).toBeDefined(); + expect(cluster._clusterLabelCollection.length).toEqual(1); + }); + it('clusters points', function() { cluster = new EntityCluster(); cluster._initialize(scene); @@ -241,6 +287,29 @@ defineSuite([ expect(cluster._clusterLabelCollection).not.toBeDefined(); }); + it('clusters points on first update', function() { + cluster = new EntityCluster(); + cluster._initialize(scene); + + var entity = new Entity(); + var point = cluster.getPoint(entity); + point.id = entity; + point.pixelSize = 1; + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + + entity = new Entity(); + point = cluster.getPoint(entity); + point.id = entity; + point.pixelSize = 1; + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + + cluster.enabled = true; + cluster.update(scene.frameState); + + expect(cluster._clusterLabelCollection).toBeDefined(); + expect(cluster._clusterLabelCollection.length).toEqual(1); + }); + it('clusters points that have labels', function() { cluster = new EntityCluster(); cluster._initialize(scene); diff --git a/Specs/DataSources/GeometryVisualizerSpec.js b/Specs/DataSources/GeometryVisualizerSpec.js index 285d295be647..bd3721b20839 100644 --- a/Specs/DataSources/GeometryVisualizerSpec.js +++ b/Specs/DataSources/GeometryVisualizerSpec.js @@ -22,6 +22,8 @@ defineSuite([ 'DataSources/StaticGeometryPerMaterialBatch', 'DataSources/StaticGroundGeometryColorBatch', 'DataSources/StaticOutlineGeometryBatch', + 'DataSources/PolylineGeometryUpdater', + 'DataSources/PolylineGraphics', 'Scene/GroundPrimitive', 'Scene/ShadowMode', 'Specs/createDynamicProperty', @@ -50,6 +52,8 @@ defineSuite([ StaticGeometryPerMaterialBatch, StaticGroundGeometryColorBatch, StaticOutlineGeometryBatch, + PolylineGeometryUpdater, + PolylineGraphics, GroundPrimitive, ShadowMode, createDynamicProperty, @@ -348,6 +352,174 @@ defineSuite([ return createAndRemoveGeometryWithShadows(ShadowMode.RECEIVE_ONLY); }); + it('Creates and removes static color material and static color depth fail material', function() { + var objects = new EntityCollection(); + var visualizer = new GeometryVisualizer(PolylineGeometryUpdater, scene, objects); + + var polyline = new PolylineGraphics(); + polyline.positions = new ConstantProperty([Cartesian3.fromDegrees(0.0, 0.0), Cartesian3.fromDegrees(0.0, 1.0)]); + polyline.material = new ColorMaterialProperty(); + polyline.depthFailMaterial = new ColorMaterialProperty(); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(1234, 5678, 9101112)); + entity.polyline = polyline; + objects.add(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + var isUpdated = visualizer.update(time); + scene.render(time); + return isUpdated; + }).then(function() { + var primitive = scene.primitives.get(0); + var attributes = primitive.getGeometryInstanceAttributes(entity); + expect(attributes).toBeDefined(); + expect(attributes.show).toEqual(ShowGeometryInstanceAttribute.toValue(true)); + expect(attributes.color).toEqual(ColorGeometryInstanceAttribute.toValue(Color.WHITE)); + expect(attributes.depthFailColor).toEqual(ColorGeometryInstanceAttribute.toValue(Color.WHITE)); + expect(primitive.appearance).toBeInstanceOf(PolylineGeometryUpdater.perInstanceColorAppearanceType); + expect(primitive.depthFailAppearance).toBeInstanceOf(PolylineGeometryUpdater.perInstanceColorAppearanceType); + + objects.remove(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + expect(visualizer.update(time)).toBe(true); + scene.render(time); + return scene.primitives.length === 0; + }).then(function(){ + visualizer.destroy(); + }); + }); + }); + + it('Creates and removes static color material and static depth fail material', function() { + var objects = new EntityCollection(); + var visualizer = new GeometryVisualizer(PolylineGeometryUpdater, scene, objects); + + var polyline = new PolylineGraphics(); + polyline.positions = new ConstantProperty([Cartesian3.fromDegrees(0.0, 0.0), Cartesian3.fromDegrees(0.0, 1.0)]); + polyline.material = new ColorMaterialProperty(); + polyline.depthFailMaterial = new GridMaterialProperty(); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(1234, 5678, 9101112)); + entity.polyline = polyline; + objects.add(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + var isUpdated = visualizer.update(time); + scene.render(time); + return isUpdated; + }).then(function() { + var primitive = scene.primitives.get(0); + var attributes = primitive.getGeometryInstanceAttributes(entity); + expect(attributes).toBeDefined(); + expect(attributes.show).toEqual(ShowGeometryInstanceAttribute.toValue(true)); + expect(attributes.color).toEqual(ColorGeometryInstanceAttribute.toValue(Color.WHITE)); + expect(attributes.depthFailColor).toBeUndefined(); + expect(primitive.appearance).toBeInstanceOf(PolylineGeometryUpdater.perInstanceColorAppearanceType); + expect(primitive.depthFailAppearance).toBeInstanceOf(PolylineGeometryUpdater.materialAppearanceType); + + objects.remove(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + expect(visualizer.update(time)).toBe(true); + scene.render(time); + return scene.primitives.length === 0; + }).then(function(){ + visualizer.destroy(); + }); + }); + }); + + it('Creates and removes static material and static depth fail material', function() { + var objects = new EntityCollection(); + var visualizer = new GeometryVisualizer(PolylineGeometryUpdater, scene, objects); + + var polyline = new PolylineGraphics(); + polyline.positions = new ConstantProperty([Cartesian3.fromDegrees(0.0, 0.0), Cartesian3.fromDegrees(0.0, 1.0)]); + polyline.material = new GridMaterialProperty(); + polyline.depthFailMaterial = new GridMaterialProperty(); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(1234, 5678, 9101112)); + entity.polyline = polyline; + objects.add(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + var isUpdated = visualizer.update(time); + scene.render(time); + return isUpdated; + }).then(function() { + var primitive = scene.primitives.get(0); + var attributes = primitive.getGeometryInstanceAttributes(entity); + expect(attributes).toBeDefined(); + expect(attributes.show).toEqual(ShowGeometryInstanceAttribute.toValue(true)); + expect(attributes.color).toBeUndefined(); + expect(attributes.depthFailColor).toBeUndefined(); + expect(primitive.appearance).toBeInstanceOf(PolylineGeometryUpdater.materialAppearanceType); + expect(primitive.depthFailAppearance).toBeInstanceOf(PolylineGeometryUpdater.materialAppearanceType); + + objects.remove(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + expect(visualizer.update(time)).toBe(true); + scene.render(time); + return scene.primitives.length === 0; + }).then(function(){ + visualizer.destroy(); + }); + }); + }); + + it('Creates and removes static material and static color depth fail material', function() { + var objects = new EntityCollection(); + var visualizer = new GeometryVisualizer(PolylineGeometryUpdater, scene, objects); + + var polyline = new PolylineGraphics(); + polyline.positions = new ConstantProperty([Cartesian3.fromDegrees(0.0, 0.0), Cartesian3.fromDegrees(0.0, 1.0)]); + polyline.material = new GridMaterialProperty(); + polyline.depthFailMaterial = new ColorMaterialProperty(); + + var entity = new Entity(); + entity.position = new ConstantPositionProperty(new Cartesian3(1234, 5678, 9101112)); + entity.polyline = polyline; + objects.add(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + var isUpdated = visualizer.update(time); + scene.render(time); + return isUpdated; + }).then(function() { + var primitive = scene.primitives.get(0); + var attributes = primitive.getGeometryInstanceAttributes(entity); + expect(attributes).toBeDefined(); + expect(attributes.show).toEqual(ShowGeometryInstanceAttribute.toValue(true)); + expect(attributes.color).toBeUndefined(); + expect(attributes.depthFailColor).toEqual(ColorGeometryInstanceAttribute.toValue(Color.WHITE)); + expect(primitive.appearance).toBeInstanceOf(PolylineGeometryUpdater.materialAppearanceType); + expect(primitive.depthFailAppearance).toBeInstanceOf(PolylineGeometryUpdater.perInstanceColorAppearanceType); + + objects.remove(entity); + + return pollToPromise(function() { + scene.initializeFrame(); + expect(visualizer.update(time)).toBe(true); + scene.render(time); + return scene.primitives.length === 0; + }).then(function(){ + visualizer.destroy(); + }); + }); + }); + it('Correctly handles geometry changing batches', function() { var objects = new EntityCollection(); var visualizer = new GeometryVisualizer(EllipseGeometryUpdater, scene, objects); @@ -528,12 +700,12 @@ defineSuite([ var entityCollection = new EntityCollection(); var visualizer = new GeometryVisualizer(EllipseGeometryUpdater, scene, entityCollection); expect(entityCollection.collectionChanged.numberOfListeners).toEqual(1); - visualizer = visualizer.destroy(); + visualizer.destroy(); expect(entityCollection.collectionChanged.numberOfListeners).toEqual(0); }); it('StaticGeometryPerMaterialBatch handles shared material being invalidated', function() { - var batch = new StaticGeometryPerMaterialBatch(scene.primitives, EllipseGeometryUpdater.materialAppearanceType, false, ShadowMode.DISABLED); + var batch = new StaticGeometryPerMaterialBatch(scene.primitives, EllipseGeometryUpdater.materialAppearanceType, undefined, false, ShadowMode.DISABLED); var ellipse = new EllipseGraphics(); ellipse.semiMajorAxis = new ConstantProperty(2); @@ -580,7 +752,7 @@ defineSuite([ }); it('StaticGeometryColorBatch updates color attribute after rebuilding primitive', function() { - var batch = new StaticGeometryColorBatch(scene.primitives, EllipseGeometryUpdater.materialAppearanceType, false, ShadowMode.DISABLED); + var batch = new StaticGeometryColorBatch(scene.primitives, EllipseGeometryUpdater.materialAppearanceType, undefined, false, ShadowMode.DISABLED); var entity = new Entity({ position : new Cartesian3(1234, 5678, 9101112), diff --git a/Specs/DataSources/KmlDataSourceSpec.js b/Specs/DataSources/KmlDataSourceSpec.js index dcba41357518..130ad93a6e0c 100644 --- a/Specs/DataSources/KmlDataSourceSpec.js +++ b/Specs/DataSources/KmlDataSourceSpec.js @@ -1555,6 +1555,32 @@ defineSuite([ }); }); + it('IconStyle: Sets billboard image with query', function() { + var kml = '\ + \ + \ + '; + + return KmlDataSource.load(parser.parseFromString(kml, "text/xml"), { + camera : options.camera, + canvas : options.canvas, + sourceUri : 'http://test.invalid', + query : { + "test": true + } + }).then(function(dataSource) { + var entities = dataSource.entities.values; + var billboard = entities[0].billboard; + expect(billboard.image.getValue()).toEqual('http://test.invalid/image.png?test=true'); + }); + }); + it('IconStyle: Sets billboard image with subregion', function() { var kml = '\ >includeStart('debug', pragmas.debug); + // Circular dependencies are only caught in debug builds. + it('throws if hierarchy has a circular dependency', function() { + // window0 -> door0 -> building0 -> window0 + var batchTableJson = { + HIERARCHY : { + instancesLength : 3, + classIds : [0, 1, 2], + parentIds : [1, 2, 0], + classes : [{ + name : 'window', + length : 1, + instances : { + window_name : ['window0'] + } + }, { + name : 'door', + length : 1, + instances : { + door_name : ['door0'] + } + }, { + name : 'building', + length : 1, + instances : { + building_name : ['building0'] + } + }] + } + }; + expect(function() { + return new Cesium3DTileBatchTable(mockTileset, 3, batchTableJson); + }).toThrowDeveloperError(); + }); + + it('throws if hierarchy has a circular dependency (2)', function() { + // window0 -> door0 -> building0 -> window1 -> door0 + var batchTableJson = { + HIERARCHY : { + instancesLength : 4, + classIds : [0, 1, 2, 0], + parentIds : [1, 2, 3, 1], + classes : [{ + name : 'window', + length : 2, + instances : { + window_name : ['window0', 'window1'] + } + }, { + name : 'door', + length : 1, + instances : { + door_name : ['door0'] + } + }, { + name : 'building', + length : 1, + instances : { + building_name : ['building0'] + } + }] + } + }; + expect(function() { + return new Cesium3DTileBatchTable(mockTileset, 4, batchTableJson); + }).toThrowDeveloperError(); + }); + //>>includeEnd('debug'); + + it('throws if an instance\'s parentId exceeds instancesLength', function() { + var batchTableJson = { + HIERARCHY : { + instancesLength : 2, + classIds : [0, 1], + parentIds : [1, 2], + classes : [{ + name : 'window', + length : 1, + instances : { + window_name : ['window0'] + } + }, { + name : 'door', + length : 1, + instances : { + door_name : ['door0'] + } + }] + } + }; + expect(function() { + return new Cesium3DTileBatchTable(mockTileset, 2, batchTableJson); + }).toThrowDeveloperError(); + }); + + it('destroys', function() { + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + var content = tileset._root.content; + var batchTable = content.batchTable; + expect(batchTable.isDestroyed()).toEqual(false); + scene.primitives.remove(tileset); + expect(batchTable.isDestroyed()).toEqual(true); + }); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/Cesium3DTileContentSpec.js b/Specs/Scene/Cesium3DTileContentSpec.js new file mode 100644 index 000000000000..28b8d03e909f --- /dev/null +++ b/Specs/Scene/Cesium3DTileContentSpec.js @@ -0,0 +1,68 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Cesium3DTileContent' + ], function( + Cesium3DTileContent) { + 'use strict'; + + it('throws', function() { + var content = new Cesium3DTileContent(); + expect(function() { + return content.featuresLength; + }).toThrowDeveloperError(); + expect(function() { + return content.pointsLength; + }).toThrowDeveloperError(); + expect(function() { + return content.trianglesLength; + }).toThrowDeveloperError(); + expect(function() { + return content.geometryByteLength; + }).toThrowDeveloperError(); + expect(function() { + return content.texturesByteLength; + }).toThrowDeveloperError(); + expect(function() { + return content.batchTableByteLength; + }).toThrowDeveloperError(); + expect(function() { + return content.innerContents; + }).toThrowDeveloperError(); + expect(function() { + return content.readyPromise; + }).toThrowDeveloperError(); + expect(function() { + return content.tileset; + }).toThrowDeveloperError(); + expect(function() { + return content.tile; + }).toThrowDeveloperError(); + expect(function() { + return content.url; + }).toThrowDeveloperError(); + expect(function() { + return content.batchTable; + }).toThrowDeveloperError(); + expect(function() { + return content.hasProperty(0, 'height'); + }).toThrowDeveloperError(); + expect(function() { + return content.getFeature(0); + }).toThrowDeveloperError(); + expect(function() { + content.applyDebugSettings(); + }).toThrowDeveloperError(); + expect(function() { + content.applyStyle(); + }).toThrowDeveloperError(); + expect(function() { + content.update(); + }).toThrowDeveloperError(); + expect(function() { + content.isDestroyed(); + }).toThrowDeveloperError(); + expect(function() { + content.destroy(); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/Scene/Cesium3DTileFeatureTableSpec.js b/Specs/Scene/Cesium3DTileFeatureTableSpec.js new file mode 100644 index 000000000000..200ba7a5b546 --- /dev/null +++ b/Specs/Scene/Cesium3DTileFeatureTableSpec.js @@ -0,0 +1,37 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Cesium3DTileFeatureTable', + 'Core/ComponentDatatype' + ], function( + Cesium3DTileFeatureTable, + ComponentDatatype) { + 'use strict'; + + it('loads from JSON', function() { + var featureTable = new Cesium3DTileFeatureTable({ + TEST : [0, 1, 2, 3, 4, 5] + }); + featureTable.featuresLength = 3; + var all = featureTable.getGlobalProperty('TEST', ComponentDatatype.UNSIGNED_BYTE); + expect(all).toEqual([0, 1, 2, 3, 4, 5]); + var feature = featureTable.getProperty('TEST', ComponentDatatype.UNSIGNED_BYTE, 2, 1, new Array(2)); + expect(feature).toEqual([2, 3]); + var properties = featureTable.getPropertyArray('TEST', ComponentDatatype.UNSIGNED_BYTE, 2); + expect(properties).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('loads from binary', function() { + var featureTable = new Cesium3DTileFeatureTable({ + TEST : { + byteOffset : 4 + } + }, new Uint8Array([0, 0, 0, 0, 0, 1, 2, 3, 4, 5])); + featureTable.featuresLength = 3; + var all = featureTable.getGlobalProperty('TEST', ComponentDatatype.UNSIGNED_BYTE, 6); + expect(all).toEqual([0, 1, 2, 3, 4, 5]); + var feature = featureTable.getProperty('TEST', ComponentDatatype.UNSIGNED_BYTE, 2, 1, new Array(2)); + expect(feature).toEqual([2, 3]); + var properties = featureTable.getPropertyArray('TEST', ComponentDatatype.UNSIGNED_BYTE, 2); + expect(properties).toEqual([0, 1, 2, 3, 4, 5]); + }); +}); diff --git a/Specs/Scene/Cesium3DTileSpec.js b/Specs/Scene/Cesium3DTileSpec.js new file mode 100644 index 000000000000..743495dd99bc --- /dev/null +++ b/Specs/Scene/Cesium3DTileSpec.js @@ -0,0 +1,341 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Cesium3DTile', + 'Core/Cartesian3', + 'Core/clone', + 'Core/defined', + 'Core/HeadingPitchRoll', + 'Core/loadWithXhr', + 'Core/Math', + 'Core/Matrix3', + 'Core/Matrix4', + 'Core/Rectangle', + 'Core/SphereOutlineGeometry', + 'Core/Transforms', + 'Scene/Cesium3DTileRefine', + 'Scene/TileBoundingRegion', + 'Scene/TileOrientedBoundingBox', + 'Specs/createScene' + ], function( + Cesium3DTile, + Cartesian3, + clone, + defined, + HeadingPitchRoll, + loadWithXhr, + CesiumMath, + Matrix3, + Matrix4, + Rectangle, + SphereOutlineGeometry, + Transforms, + Cesium3DTileRefine, + TileBoundingRegion, + TileOrientedBoundingBox, + createScene) { + 'use strict'; + + var tileWithBoundingSphere = { + geometricError : 1, + refine : 'REPLACE', + children : [], + boundingVolume : { + sphere: [0.0, 0.0, 0.0, 5.0] + } + }; + + var tileWithContentBoundingSphere = { + geometricError : 1, + refine : 'REPLACE', + content : { + url : '0/0.b3dm', + boundingVolume : { + sphere: [0.0, 0.0, 1.0, 5.0] + } + }, + children : [], + boundingVolume : { + sphere: [0.0, 0.0, 1.0, 5.0] + } + }; + + var tileWithBoundingRegion = { + geometricError : 1, + refine : 'REPLACE', + children : [], + boundingVolume: { + region : [-1.2, -1.2, 0.0, 0.0, -30, -34] + } + }; + + var tileWithContentBoundingRegion = { + geometricError : 1, + refine : 'REPLACE', + children : [], + content : { + url : '0/0.b3dm', + boundingVolume : { + region : [-1.2, -1.2, 0, 0, -30, -34] + } + }, + boundingVolume: { + region : [-1.2, -1.2, 0, 0, -30, -34] + } + }; + + var tileWithBoundingBox = { + geometricError : 1, + refine : 'REPLACE', + children : [], + boundingVolume: { + box : [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] + } + }; + + var tileWithContentBoundingBox = { + geometricError : 1, + refine : 'REPLACE', + children : [], + content : { + url : '0/0.b3dm', + boundingVolume : { + box : [0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0] + } + }, + boundingVolume: { + box : [0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0] + } + }; + + var tileWithViewerRequestVolume = { + geometricError : 1, + refine : 'REPLACE', + children : [], + boundingVolume: { + box : [0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0] + }, + viewerRequestVolume : { + box : [0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0] + } + }; + + var mockTileset = { + debugShowBoundingVolume : true, + debugShowViewerRequestVolume : true, + modelMatrix : Matrix4.IDENTITY + }; + + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + function getTileTransform(longitude, latitude) { + var transformCenter = Cartesian3.fromRadians(longitude, latitude, 0.0); + var hpr = new HeadingPitchRoll(); + var transformMatrix = Transforms.headingPitchRollToFixedFrame(transformCenter, hpr); + return Matrix4.pack(transformMatrix, new Array(16)); + } + + it('destroys', function() { + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingSphere, undefined); + expect(tile.isDestroyed()).toEqual(false); + tile.destroy(); + expect(tile.isDestroyed()).toEqual(true); + }); + + it('throws if geometricError is undefined', function() { + var tileWithoutGeometricError = clone(tileWithBoundingSphere, true); + delete tileWithoutGeometricError.geometricError; + expect(function() { + return new Cesium3DTile(mockTileset, '/some_url', tileWithoutGeometricError, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if boundingVolume is undefined', function() { + var tileWithoutBoundingVolume = clone(tileWithBoundingSphere, true); + delete tileWithoutBoundingVolume.boundingVolume; + expect(function() { + return new Cesium3DTile(mockTileset, '/some_url', tileWithoutBoundingVolume, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if boundingVolume does not contain a sphere, region, or box', function() { + var tileWithoutBoundingVolume = clone(tileWithBoundingSphere, true); + delete tileWithoutBoundingVolume.boundingVolume.sphere; + expect(function() { + return new Cesium3DTile(mockTileset, '/some_url', tileWithoutBoundingVolume, undefined); + }).toThrowRuntimeError(); + }); + + describe('bounding volumes', function() { + it('can have a bounding sphere', function() { + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingSphere, undefined); + var radius = tileWithBoundingSphere.boundingVolume.sphere[3]; + expect(tile.contentBoundingVolume).toBeDefined(); + expect(tile.contentBoundingVolume.boundingVolume.radius).toEqual(radius); + expect(tile.contentBoundingVolume.boundingVolume.center).toEqual(Cartesian3.ZERO); + }); + + it('can have a content bounding sphere', function() { + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithContentBoundingSphere, undefined); + var radius = tileWithContentBoundingSphere.content.boundingVolume.sphere[3]; + expect(tile.contentBoundingVolume).toBeDefined(); + expect(tile.contentBoundingVolume.boundingVolume.radius).toEqual(radius); + expect(tile.contentBoundingVolume.boundingVolume.center).toEqual(new Cartesian3(0.0, 0.0, 1.0)); + }); + + it('can have a bounding region', function() { + var box = tileWithBoundingRegion.boundingVolume.region; + var rectangle = new Rectangle(box[0], box[1], box[2], box[3]); + var minimumHeight = tileWithBoundingRegion.boundingVolume.region[4]; + var maximumHeight = tileWithBoundingRegion.boundingVolume.region[5]; + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingRegion, undefined); + var tbr = new TileBoundingRegion({rectangle: rectangle, minimumHeight: minimumHeight, maximumHeight: maximumHeight}); + expect(tile.contentBoundingVolume).toBeDefined(); + expect(tile.contentBoundingVolume).toEqual(tbr); + }); + + it('can have a content bounding region', function() { + var region = tileWithContentBoundingRegion.content.boundingVolume.region; + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithContentBoundingRegion, undefined); + expect(tile._contentBoundingVolume).toBeDefined(); + var tbb = new TileBoundingRegion({ + rectangle: new Rectangle(region[0], region[1], region[2], region[3]), + minimumHeight: region[4], + maximumHeight: region[5] + }); + expect(tile._contentBoundingVolume).toEqual(tbb); + }); + + it('can have an oriented bounding box', function() { + var box = tileWithBoundingBox.boundingVolume.box; + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingBox, undefined); + expect(tile.contentBoundingVolume).toBeDefined(); + var center = new Cartesian3(box[0], box[1], box[2]); + var halfAxes = Matrix3.fromArray(box, 3); + var obb = new TileOrientedBoundingBox(center, halfAxes); + expect(tile.contentBoundingVolume).toEqual(obb); + }); + + it('can have a content oriented bounding box', function() { + var box = tileWithContentBoundingBox.boundingVolume.box; + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithContentBoundingBox, undefined); + expect(tile.contentBoundingVolume).toBeDefined(); + var center = new Cartesian3(box[0], box[1], box[2]); + var halfAxes = Matrix3.fromArray(box, 3); + var obb = new TileOrientedBoundingBox(center, halfAxes); + expect(tile.contentBoundingVolume).toEqual(obb); + }); + + it('tile transform affects bounding sphere', function() { + var header = clone(tileWithContentBoundingSphere, true); + header.transform = getTileTransform(centerLongitude, centerLatitude); + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + var boundingSphere = tile._boundingVolume.boundingVolume; + var contentBoundingSphere = tile._contentBoundingVolume.boundingVolume; + + var boundingVolumeCenter = Cartesian3.fromRadians(centerLongitude, centerLatitude, 1.0); + expect(boundingSphere.center).toEqualEpsilon(boundingVolumeCenter, CesiumMath.EPSILON4); + expect(boundingSphere.radius).toEqual(5.0); // No change + + expect(contentBoundingSphere.center).toEqualEpsilon(boundingVolumeCenter, CesiumMath.EPSILON4); + expect(contentBoundingSphere.radius).toEqual(5.0); // No change + }); + + it('tile transform affects oriented bounding box', function() { + var header = clone(tileWithContentBoundingBox, true); + header.transform = getTileTransform(centerLongitude, centerLatitude); + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + var boundingBox = tile._boundingVolume.boundingVolume; + var contentBoundingBox = tile._contentBoundingVolume.boundingVolume; + + var boundingVolumeCenter = Cartesian3.fromRadians(centerLongitude, centerLatitude, 1.0); + expect(boundingBox.center).toEqualEpsilon(boundingVolumeCenter, CesiumMath.EPSILON7); + expect(contentBoundingBox.center).toEqualEpsilon(boundingVolumeCenter, CesiumMath.EPSILON7); + }); + + it('tile transform does not affect bounding region', function() { + var header = clone(tileWithContentBoundingRegion, true); + header.transform = getTileTransform(centerLongitude, centerLatitude); + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + var boundingRegion = tile._boundingVolume; + var contentBoundingRegion = tile._contentBoundingVolume; + + var region = header.boundingVolume.region; + var rectangle = Rectangle.unpack(region); + expect(boundingRegion.rectangle).toEqual(rectangle); + expect(contentBoundingRegion.rectangle).toEqual(rectangle); + }); + + it('tile transform affects viewer request volume', function() { + var header = clone(tileWithViewerRequestVolume, true); + header.transform = getTileTransform(centerLongitude, centerLatitude); + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + var requestVolume = tile._viewerRequestVolume.boundingVolume; + var requestVolumeCenter = Cartesian3.fromRadians(centerLongitude, centerLatitude, 1.0); + expect(requestVolume.center).toEqualEpsilon(requestVolumeCenter, CesiumMath.EPSILON7); + }); + + it('tile transform changes', function() { + var mockTileset = { + modelMatrix : Matrix4.IDENTITY + }; + var header = clone(tileWithBoundingSphere, true); + header.transform = getTileTransform(centerLongitude, centerLatitude); + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + var boundingSphere = tile._boundingVolume.boundingVolume; + + // Check the original transform + var boundingVolumeCenter = Cartesian3.fromRadians(centerLongitude, centerLatitude); + expect(boundingSphere.center).toEqualEpsilon(boundingVolumeCenter, CesiumMath.EPSILON7); + + // Change the transform + var newLongitude = -1.012; + var newLatitude = 0.698874; + tile.transform = getTileTransform(newLongitude, newLatitude); + tile.updateTransform(); + + // Check the new transform + var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude); + expect(boundingSphere.center).toEqualEpsilon(newCenter, CesiumMath.EPSILON7); + }); + + it('logs deprecation warning if refine is lowercase', function() { + spyOn(Cesium3DTile, '_deprecationWarning'); + var header = clone(tileWithBoundingSphere, true); + header.refine = 'replace'; + var tile = new Cesium3DTile(mockTileset, '/some_url', header, undefined); + expect(tile.refine).toBe(Cesium3DTileRefine.REPLACE); + expect(Cesium3DTile._deprecationWarning).toHaveBeenCalled(); + }); + }); + + describe('debug bounding volumes', function() { + it('can be a bounding region', function() { + var scene = createScene(); + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingRegion, undefined); + tile.update(mockTileset, scene.frameState); + expect(tile._debugBoundingVolume).toBeDefined(); + }); + + it('can be an oriented bounding box', function() { + var scene = createScene(); + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingBox, undefined); + tile.update(mockTileset, scene.frameState); + expect(tile._debugBoundingVolume).toBeDefined(); + }); + + it('can be a bounding sphere', function() { + var scene = createScene(); + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithBoundingSphere, undefined); + tile.update(mockTileset, scene.frameState); + expect(tile._debugBoundingVolume).toBeDefined(); + }); + + it('creates debug bounding volume for viewer request volume', function() { + var scene = createScene(); + var tile = new Cesium3DTile(mockTileset, '/some_url', tileWithViewerRequestVolume, undefined); + tile.update(mockTileset, scene.frameState); + expect(tile._debugViewerRequestVolume).toBeDefined(); + }); + }); +}, 'WebGL'); diff --git a/Specs/Scene/Cesium3DTileStyleSpec.js b/Specs/Scene/Cesium3DTileStyleSpec.js new file mode 100644 index 000000000000..f8437620960a --- /dev/null +++ b/Specs/Scene/Cesium3DTileStyleSpec.js @@ -0,0 +1,498 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Cesium3DTileStyle', + 'Core/Color', + 'Scene/ConditionsExpression', + 'Scene/Expression' + ], function( + Cesium3DTileStyle, + Color, + ConditionsExpression, + Expression) { + 'use strict'; + + var frameState = {}; + + function MockFeature() { + this._properties = {}; + } + + MockFeature.prototype.addProperty = function(name, value) { + this._properties[name] = value; + }; + + MockFeature.prototype.getProperty = function(name) { + return this._properties[name]; + }; + + var feature1 = new MockFeature(); + feature1.addProperty('ZipCode', '19341'); + feature1.addProperty('County', 'Chester'); + feature1.addProperty('YearBuilt', 1979); + feature1.addProperty('Temperature', 78); + feature1.addProperty('red', 38); + feature1.addProperty('green', 255); + feature1.addProperty('blue', 82); + feature1.addProperty('volume', 128); + feature1.addProperty('Height', 100); + feature1.addProperty('Width', 20); + feature1.addProperty('Depth', 20); + feature1.addProperty('id', 11); + feature1.addProperty('name', 'Hello'); + + var feature2 = new MockFeature(); + feature2.addProperty('ZipCode', '19342'); + feature2.addProperty('County', 'Delaware'); + feature2.addProperty('YearBuilt', 1979); + feature2.addProperty('Temperature', 92); + feature2.addProperty('red', 255); + feature2.addProperty('green', 30); + feature2.addProperty('blue', 30); + feature2.addProperty('volume', 50); + feature2.addProperty('Height', 38); + feature2.addProperty('id', 12); + + var styleUrl = './Data/Cesium3DTiles/Style/style.json'; + + it('rejects readyPromise with undefined url', function() { + var tileStyle = new Cesium3DTileStyle('invalid.json'); + + return tileStyle.readyPromise.then(function(style) { + fail('should not resolve'); + }).otherwise(function(error) { + expect(tileStyle.ready).toEqual(false); + expect(error.statusCode).toEqual(404); + }); + }); + + it('loads style from uri', function() { + var tileStyle = new Cesium3DTileStyle(styleUrl); + + return tileStyle.readyPromise.then(function(style) { + expect(style.style).toEqual({ + show : '${id} < 100', + color : "color('red')", + pointSize : '${id} / 100' + }); + expect(style.show).toEqual(new Expression('${id} < 100')); + expect(style.color).toEqual(new Expression("color('red')")); + expect(style.pointSize).toEqual(new Expression('${id} / 100')); + expect(tileStyle.ready).toEqual(true); + }).otherwise(function() { + fail('should load style.json'); + }); + }); + + it('sets show value to default expression', function() { + var style = new Cesium3DTileStyle({}); + expect(style.show).toEqual(new Expression('true')); + + style = new Cesium3DTileStyle(); + expect(style.show).toEqual(new Expression('true')); + }); + + it('sets color value to default expression', function() { + var style = new Cesium3DTileStyle({}); + expect(style.color).toEqual(new Expression('color("#ffffff")')); + + style = new Cesium3DTileStyle(); + expect(style.color).toEqual(new Expression('color("#ffffff")')); + }); + + it('sets pointSize value to default expression', function() { + var style = new Cesium3DTileStyle({}); + expect(style.pointSize).toEqual(new Expression('1')); + + style = new Cesium3DTileStyle(); + expect(style.pointSize).toEqual(new Expression('1')); + }); + + it('sets show value to expression', function() { + var style = new Cesium3DTileStyle({ + show : 'true' + }); + expect(style.show).toEqual(new Expression('true')); + + style = new Cesium3DTileStyle({ + show : 'false' + }); + expect(style.show).toEqual(new Expression('false')); + + style = new Cesium3DTileStyle({ + show : '${height} * 10 >= 1000' + }); + expect(style.show).toEqual(new Expression('${height} * 10 >= 1000')); + + style = new Cesium3DTileStyle({ + show : true + }); + expect(style.show).toEqual(new Expression('true')); + + style = new Cesium3DTileStyle({ + show : false + }); + expect(style.show).toEqual(new Expression('false')); + }); + + it('sets show value to conditional', function() { + var jsonExp = { + conditions : [ + ['${height} > 2', 'false'], + ['true', 'true'] + ] + }; + + var style = new Cesium3DTileStyle({ + show : jsonExp + }); + expect(style.show).toEqual(new ConditionsExpression(jsonExp)); + }); + + it('sets show to undefined if not a string, boolean, or conditional', function() { + var style = new Cesium3DTileStyle({ + show : 1 + }); + expect(style.show).toEqual(undefined); + }); + + it('sets color value to expression', function() { + var style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(style.color).toEqual(new Expression('color("red")')); + + style = new Cesium3DTileStyle({ + color : 'rgba(30, 30, 30, 0.5)' + }); + expect(style.color).toEqual(new Expression('rgba(30, 30, 30, 0.5)')); + + style = new Cesium3DTileStyle({ + color : '(${height} * 10 >= 1000) ? rgba(0.0, 0.0, 1.0, 0.5) : color("blue")' + }); + expect(style.color).toEqual(new Expression('(${height} * 10 >= 1000) ? rgba(0.0, 0.0, 1.0, 0.5) : color("blue")')); + }); + + it('sets color value to conditional', function() { + var jsonExp = { + conditions : [ + ['${height} > 2', 'color("cyan")'], + ['true', 'color("blue")'] + ] + }; + + var style = new Cesium3DTileStyle({ + color : jsonExp + }); + expect(style.color).toEqual(new ConditionsExpression(jsonExp)); + }); + + it('sets color to undefined if not a string or conditional', function() { + var style = new Cesium3DTileStyle({ + color : 1 + }); + expect(style.color).toEqual(undefined); + }); + + it('sets pointSize value to expression', function() { + var style = new Cesium3DTileStyle({ + pointSize : '2' + }); + expect(style.pointSize).toEqual(new Expression('2')); + + style = new Cesium3DTileStyle({ + pointSize : '${height} / 10' + }); + expect(style.pointSize).toEqual(new Expression('${height} / 10')); + + style = new Cesium3DTileStyle({ + pointSize : 2 + }); + expect(style.pointSize).toEqual(new Expression('2')); + }); + + it('sets pointSize value to conditional', function() { + var jsonExp = { + conditions : [ + ['${height} > 2', '1.0'], + ['true', '2.0'] + ] + }; + + var style = new Cesium3DTileStyle({ + pointSize : jsonExp + }); + expect(style.pointSize).toEqual(new ConditionsExpression(jsonExp)); + }); + + it('sets pointSize to undefined if not a number, string, or conditional', function() { + var style = new Cesium3DTileStyle({ + pointSize : true + }); + expect(style.pointSize).toEqual(undefined); + }); + + it('throws on accessing style if not ready', function() { + var style = new Cesium3DTileStyle({}); + style._ready = false; + + expect(function() { + return style.style; + }).toThrowDeveloperError(); + }); + + it('throws on accessing color if not ready', function() { + var style = new Cesium3DTileStyle({}); + style._ready = false; + + expect(function() { + return style.color; + }).toThrowDeveloperError(); + }); + + it('throws on accessing show if not ready', function() { + var style = new Cesium3DTileStyle({}); + style._ready = false; + + expect(function() { + return style.show; + }).toThrowDeveloperError(); + }); + + it('throws on accessing pointSize if not ready', function() { + var style = new Cesium3DTileStyle({}); + style._ready = false; + + expect(function() { + return style.pointSize; + }).toThrowDeveloperError(); + }); + + it('sets meta properties', function() { + var style = new Cesium3DTileStyle({ + meta : { + description : '"Hello, ${name}"' + } + }); + expect(style.meta.description.evaluate(frameState, feature1)).toEqual("Hello, Hello"); + + style = new Cesium3DTileStyle({ + meta : { + featureColor : 'rgb(${red}, ${green}, ${blue})', + volume : '${Height} * ${Width} * ${Depth}' + } + }); + expect(style.meta.featureColor.evaluateColor(frameState, feature1)).toEqual(Color.fromBytes(38, 255, 82)); + expect(style.meta.volume.evaluate(frameState, feature1)).toEqual(20 * 20 * 100); + }); + + it('default meta has no properties', function() { + var style = new Cesium3DTileStyle({}); + expect(style.meta).toEqual({}); + + style = new Cesium3DTileStyle({ + meta: {} + }); + expect(style.meta).toEqual({}); + }); + + it('default meta has no properties', function() { + var style = new Cesium3DTileStyle({}); + expect(style.meta).toEqual({}); + + style = new Cesium3DTileStyle({ + meta: {} + }); + expect(style.meta).toEqual({}); + }); + + it('throws on accessing meta if not ready', function() { + var style = new Cesium3DTileStyle({}); + style._ready = false; + + expect(function() { + return style.meta; + }).toThrowDeveloperError(); + }); + + // Tests for examples from the style spec + + it('applies default style', function() { + var style = new Cesium3DTileStyle({ + "show" : "true", + "color" : "color('#ffffff')", + "pointSize" : "1.0" + }); + + expect(style.show.evaluate(frameState, undefined)).toEqual(true); + expect(style.color.evaluateColor(frameState, undefined)).toEqual(Color.WHITE); + expect(style.pointSize.evaluate(frameState, undefined)).toEqual(1.0); + }); + + it('applies show style with variable', function() { + var style = new Cesium3DTileStyle({ + "show" : "${ZipCode} === '19341'" + }); + + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.show.evaluate(frameState, feature2)).toEqual(false); + expect(style.color.evaluateColor(frameState, undefined)).toEqual(Color.WHITE); + }); + + it('applies show style with regexp and variables', function() { + var style = new Cesium3DTileStyle({ + "show" : "(regExp('^Chest').test(${County})) && (${YearBuilt} >= 1970)" + }); + + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.show.evaluate(frameState, feature2)).toEqual(false); + expect(style.color.evaluateColor(frameState, undefined)).toEqual(Color.WHITE); + }); + + it('applies show style with conditional', function() { + var style = new Cesium3DTileStyle({ + "show" : { + "conditions" : [ + ["(${Height} >= 100.0)", "false"], + ["(${Height} >= 70.0)", "true"], + ["(${Height} >= 50.0)", "false"], + ["(${Height} >= 30.0)", "true"], + ["(${Height} >= 10.0)", "false"], + ["(${Height} >= 1.0)", "true"] + ] + } + }); + expect(style.show.evaluate(frameState, feature1)).toEqual(false); + expect(style.show.evaluate(frameState, feature2)).toEqual(true); + }); + + it('applies color style variables', function() { + var style = new Cesium3DTileStyle({ + "color" : "(${Temperature} > 90) ? color('red') : color('white')" + }); + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.WHITE); + expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.RED); + }); + + it('applies color style with new color', function() { + var style = new Cesium3DTileStyle({ + "color" : "rgba(${red}, ${green}, ${blue}, (${volume} > 100 ? 0.5 : 1.0))" + }); + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.color.evaluateColor(frameState, feature1)).toEqual(new Color(38/255, 255/255, 82/255, 0.5)); + expect(style.color.evaluateColor(frameState, feature2)).toEqual(new Color(255/255, 30/255, 30/255, 1.0)); + }); + + it('applies color style that maps id to color', function() { + var style = new Cesium3DTileStyle({ + "defines" : { + "id" : "regExp('^1(\\d)').exec(String(${id}))" + }, + "color" : { + "conditions" : [ + ["${id} === '1'", "color('#FF0000')"], + ["${id} === '2'", "color('#00FF00')"], + ["true", "color('#FFFFFF')"] + ] + } + }); + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.RED); + expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.LIME); + }); + + it('applies color style with conditional', function() { + var style = new Cesium3DTileStyle({ + "color" : { + "conditions" : [ + ["(${Height} >= 100.0)", "color('#0000FF')"], + ["(${Height} >= 70.0)", "color('#00FFFF')"], + ["(${Height} >= 50.0)", "color('#00FF00')"], + ["(${Height} >= 30.0)", "color('#FFFF00')"], + ["(${Height} >= 10.0)", "color('#FF0000')"], + ["(${Height} >= 1.0)", "color('#FF00FF')"] + ] + } + }); + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.BLUE); + expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.YELLOW); + }); + + it('applies pointSize style with variable', function() { + var style = new Cesium3DTileStyle({ + "pointSize" : "${Temperature} / 10.0" + }); + + expect(style.pointSize.evaluate(frameState, feature1)).toEqual(7.8); + expect(style.pointSize.evaluate(frameState, feature2)).toEqual(9.2); + }); + + it('applies pointSize style with regexp and variables', function() { + var style = new Cesium3DTileStyle({ + "pointSize" : "(regExp('^Chest').test(${County})) ? 2.0 : 1.0" + }); + + expect(style.pointSize.evaluate(frameState, feature1)).toEqual(2.0); + expect(style.pointSize.evaluate(frameState, feature2)).toEqual(1.0); + }); + + it('applies pointSize style with conditional', function() { + var style = new Cesium3DTileStyle({ + "pointSize" : { + "conditions" : [ + ["(${Height} >= 100.0)", "6"], + ["(${Height} >= 70.0)", "5"], + ["(${Height} >= 50.0)", "4"], + ["(${Height} >= 30.0)", "3"], + ["(${Height} >= 10.0)", "2"], + ["(${Height} >= 1.0)", "1"] + ] + } + }); + expect(style.pointSize.evaluate(frameState, feature1)).toEqual(6); + expect(style.pointSize.evaluate(frameState, feature2)).toEqual(3); + }); + + it('applies with defines', function() { + var style = new Cesium3DTileStyle({ + "defines" : { + "halfHeight" : "${Height} / 2", + "quarterHeight" : "${Height} / 4", + "halfVolume" : "${volume} / 2" + }, + "color" : { + "conditions" : [ + ["(${halfHeight} >= 25.0)", "color('red')"], + ["(${Height} >= 1.0)", "color('blue')"] + ] + }, + "show" : "(${quarterHeight} >= 20.0)", + "pointSize" : "${halfVolume} + ${halfHeight}", + "meta" : { + "description" : "'Half height is ' + ${halfHeight}" + } + }); + + expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.RED); + expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.BLUE); + expect(style.show.evaluate(frameState, feature1)).toEqual(true); + expect(style.show.evaluate(frameState, feature2)).toEqual(false); + expect(style.pointSize.evaluate(frameState, feature1)).toEqual(114); + expect(style.pointSize.evaluate(frameState, feature2)).toEqual(44); + expect(style.meta.description.evaluate(frameState, feature1)).toEqual('Half height is 50'); + expect(style.meta.description.evaluate(frameState, feature2)).toEqual('Half height is 19'); + }); + + it('return undefined shader functions when the style is empty', function() { + // The default color style is white, the default show style is true, and the default pointSize is 1.0, + // but the generated generated shader functions should just be undefined. We don't want all the points to be white. + var style = new Cesium3DTileStyle({}); + var colorFunction = style.getColorShaderFunction('getColor', '', {}); + var showFunction = style.getShowShaderFunction('getShow', '', {}); + var pointSizeFunction = style.getPointSizeShaderFunction('getPointSize', '', {}); + expect(colorFunction).toBeUndefined(); + expect(showFunction).toBeUndefined(); + expect(pointSizeFunction).toBeUndefined(); + }); +}); diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js new file mode 100644 index 000000000000..d4fb9300048d --- /dev/null +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -0,0 +1,2719 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Cesium3DTileset', + 'Core/Cartesian3', + 'Core/Color', + 'Core/defaultValue', + 'Core/defined', + 'Core/getStringFromTypedArray', + 'Core/HeadingPitchRange', + 'Core/JulianDate', + 'Core/loadWithXhr', + 'Core/Math', + 'Core/Matrix4', + 'Core/PrimitiveType', + 'Core/RequestScheduler', + 'Renderer/ClearCommand', + 'Renderer/ContextLimits', + 'Scene/Cesium3DTile', + 'Scene/Cesium3DTileColorBlendMode', + 'Scene/Cesium3DTileContentState', + 'Scene/Cesium3DTileOptimizations', + 'Scene/Cesium3DTileRefine', + 'Scene/Cesium3DTileStyle', + 'Scene/CullFace', + 'Scene/CullingVolume', + 'Scene/PerspectiveFrustum', + 'Specs/Cesium3DTilesTester', + 'Specs/createScene', + 'Specs/pollToPromise', + 'ThirdParty/when' + ], function( + Cesium3DTileset, + Cartesian3, + Color, + defaultValue, + defined, + getStringFromTypedArray, + HeadingPitchRange, + JulianDate, + loadWithXhr, + CesiumMath, + Matrix4, + PrimitiveType, + RequestScheduler, + ClearCommand, + ContextLimits, + Cesium3DTile, + Cesium3DTileColorBlendMode, + Cesium3DTileContentState, + Cesium3DTileOptimizations, + Cesium3DTileRefine, + Cesium3DTileStyle, + CullFace, + CullingVolume, + PerspectiveFrustum, + Cesium3DTilesTester, + createScene, + pollToPromise, + when) { + 'use strict'; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + // Parent tile with content and four child tiles with content + var tilesetUrl = './Data/Cesium3DTiles/Tilesets/Tileset/'; + + // Parent tile with no content and four child tiles with content + var tilesetEmptyRootUrl = './Data/Cesium3DTiles/Tilesets/TilesetEmptyRoot/'; + + var tilesetReplacement1Url = './Data/Cesium3DTiles/Tilesets/TilesetReplacement1/'; + var tilesetReplacement2Url = './Data/Cesium3DTiles/Tilesets/TilesetReplacement2/'; + var tilesetReplacement3Url = './Data/Cesium3DTiles/Tilesets/TilesetReplacement3/'; + + // 3 level tree with mix of additive and replacement refinement + var tilesetRefinementMix = './Data/Cesium3DTiles/Tilesets/TilesetRefinementMix/'; + + // tileset.json : root content points to tiles2.json + // tiles2.json: root with b3dm content, three children with b3dm content, one child points to tiles3.json + // tiles3.json: root with b3dm content + var tilesetOfTilesetsUrl = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/'; + + 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/'; + var withTransformRegionUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/'; + var withBoundingSphereUrl = './Data/Cesium3DTiles/Batched/BatchedWithBoundingSphere/'; + + var compositeUrl = './Data/Cesium3DTiles/Composite/Composite/'; + var instancedUrl = './Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/'; + var instancedRedMaterialUrl = './Data/Cesium3DTiles/Instanced/InstancedRedMaterial'; + + // 1 tile where each feature is a different source color + var colorsUrl = './Data/Cesium3DTiles/Batched/BatchedColors/'; + + // 1 tile where each feature has a reddish texture + var texturedUrl = './Data/Cesium3DTiles/Batched/BatchedTextured/'; + + // 1 tile with translucent features + var translucentUrl = './Data/Cesium3DTiles/Batched/BatchedTranslucent/'; + + // 1 tile with opaque and translucent features + var translucentOpaqueMixUrl = './Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/'; + + // Root tile is transformed from local space to wgs84, child tile is rotated, scaled, and translated locally + var tilesetWithTransformsUrl = './Data/Cesium3DTiles/Tilesets/TilesetWithTransforms'; + + // 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 tilesetWithExternalResourcesUrl = './Data/Cesium3DTiles/Tilesets/TilesetWithExternalResources'; + var tilesetSubtreeExpirationUrl = './Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration'; + var tilesetSubtreeUrl = './Data/Cesium3DTiles/Tilesets/TilesetSubtreeExpiration/subtree.json'; + var batchedExpirationUrl = './Data/Cesium3DTiles/Batched/BatchedExpiration'; + var batchedColorsB3dmUrl = './Data/Cesium3DTiles/Batched/BatchedColors/batchedColors.b3dm'; + + var styleUrl = './Data/Cesium3DTiles/Style/style.json'; + + var pointCloudUrl = './Data/Cesium3DTiles/PointCloud/PointCloudRGB'; + var pointCloudBatchedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudBatched'; + + beforeAll(function() { + scene = createScene(); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + beforeEach(function() { + RequestScheduler.clearForSpecs(); + scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + + viewAllTiles(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + function setZoom(distance) { + // Bird's eye view + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, distance)); + } + + function viewAllTiles() { + setZoom(15.0); + } + + function viewRootOnly() { + setZoom(100.0); + } + + function viewNothing() { + setZoom(200.0); + } + + function viewSky() { + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude, 100); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, 1.57, 10.0)); + } + + function viewBottomLeft() { + viewAllTiles(); + scene.camera.moveLeft(200.0); + scene.camera.moveDown(200.0); + } + + function viewInstances() { + setZoom(30.0); + } + + function viewPointCloud() { + setZoom(5.0); + } + + it('throws with undefined url', function() { + expect(function() { + return new Cesium3DTileset(); + }).toThrowDeveloperError(); + }); + + it('rejects readyPromise with invalid tileset.json', function() { + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + deferred.reject(); + }); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : 'invalid.json' + })); + return tileset.readyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(error) { + expect(tileset.ready).toEqual(false); + }); + }); + + it('rejects readyPromise with invalid tileset version', function() { + var tilesetJson = { + asset : { + version : 2.0 + } + }; + + var uri = 'data:text/plain;base64,' + btoa(JSON.stringify(tilesetJson)); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : uri + })); + return tileset.readyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(error) { + expect(tileset.ready).toEqual(false); + }); + }); + + it('url and tilesetUrl set up correctly given tileset.json path', function() { + var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json'; + var tileset = new Cesium3DTileset({ + url : path + }); + expect(tileset.url).toEqual(path); + expect(tileset._tilesetUrl).toEqual(path); + }); + + it('url and tilesetUrl set up correctly given directory without trailing slash', function() { + var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets'; + var tileset = new Cesium3DTileset({ + url : path + }); + expect(tileset.url).toEqual(path); + expect(tileset._tilesetUrl).toEqual(path + '/tileset.json'); + }); + + it('url and tilesetUrl set up correctly given directory with trailing slash', function() { + var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/'; + var tileset = new Cesium3DTileset({ + url : path + }); + expect(tileset.url).toEqual(path); + expect(tileset._tilesetUrl).toEqual(path + 'tileset.json'); + }); + + it('url and tilesetUrl set up correctly given path with query string', function() { + var path = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets'; + var param = '?param1=1¶m2=2'; + var tileset = new Cesium3DTileset({ + url : path + param + }); + expect(tileset.url).toEqual(path + param); + expect(tileset._tilesetUrl).toEqual(path + '/tileset.json' + param); + }); + + it('resolves readyPromise', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + return tileset.readyPromise.then(function(tileset) { + expect(tileset.ready).toEqual(true); + }); + }); + }); + + it('loads tileset.json', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var asset = tileset.asset; + expect(asset).toBeDefined(); + expect(asset.version).toEqual('0.0'); + expect(asset.tilesetVersion).toEqual('1.2.3'); + + var properties = tileset.properties; + expect(properties).toBeDefined(); + expect(properties.id).toBeDefined(); + expect(properties.id.minimum).toEqual(0); + expect(properties.id.maximum).toEqual(9); + + expect(tileset._geometricError).toEqual(240.0); + expect(tileset._root).toBeDefined(); + expect(tileset.url).toEqual(tilesetUrl); + }); + }); + + it('passes version in query string to tiles', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + expect(tileset._root.content._url).toEqual(tilesetUrl + 'parent.b3dm?v=1.2.3'); + }); + }); + + 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, tilesetWithExternalResourcesUrl + 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(tilesetWithExternalResourcesUrl) >= 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 + }); + expect(function() { + return tileset.asset; + }).toThrowDeveloperError(); + }); + + it('throws when getting properties and tileset is not ready', function() { + var tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + expect(function() { + return tileset.properties; + }).toThrowDeveloperError(); + }); + + it('requests tile with invalid magic', function() { + var invalidMagicBuffer = Cesium3DTilesTester.generateBatchedTileBuffer({ + magic : [120, 120, 120, 120] + }); + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + return tileset.readyPromise.then(function(tileset) { + // Start spying after the tileset json has been loaded + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + deferred.resolve(invalidMagicBuffer); + }); + scene.renderForSpecs(); // Request root + var root = tileset._root; + return root.contentReadyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(error) { + expect(error.message).toBe('Invalid tile content.'); + expect(root._contentState).toEqual(Cesium3DTileContentState.FAILED); + }); + }); + }); + + it('handles failed tile requests', function() { + viewRootOnly(); + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + return tileset.readyPromise.then(function(tileset) { + // Start spying after the tileset json has been loaded + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + deferred.reject(); + }); + scene.renderForSpecs(); // Request root + var root = tileset._root; + return root.contentReadyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(error) { + expect(root._contentState).toEqual(Cesium3DTileContentState.FAILED); + }); + }); + }); + + it('renders tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + }); + }); + + it('renders tileset in CV', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + scene.morphToColumbusView(0.0); + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + }); + }); + + it('renders tileset in 2D', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + scene.morphTo2D(0.0); + tileset.maximumScreenSpaceError = 3; + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(10); + }); + }); + + it('does not render during morph', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var commandList = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commandList.length).toBeGreaterThan(0); + scene.morphToColumbusView(1.0); + scene.renderForSpecs(); + expect(commandList.length).toBe(0); + }); + + }); + + it('renders tileset with empty root tile', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetEmptyRootUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(4); // Empty tile doesn't issue a command + }); + }); + + it('verify statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + + // Verify initial values + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + expect(statistics.numberOfPendingRequests).toEqual(0); + expect(statistics.numberOfTilesProcessing).toEqual(0); + + return Cesium3DTilesTester.waitForReady(scene, tileset).then(function() { + // Check that root and children are requested + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(0); + expect(statistics.numberOfPendingRequests).toEqual(5); + expect(statistics.numberOfTilesProcessing).toEqual(0); + + // Wait for all tiles to load and check that they are all visited and rendered + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfPendingRequests).toEqual(0); + expect(statistics.numberOfTilesProcessing).toEqual(0); + }); + }); + }); + + function checkPointAndFeatureCounts(tileset, features, points, triangles) { + var statistics = tileset._statistics; + + expect(statistics.numberOfFeaturesSelected).toEqual(0); + expect(statistics.numberOfFeaturesLoaded).toEqual(0); + expect(statistics.numberOfPointsSelected).toEqual(0); + expect(statistics.numberOfPointsLoaded).toEqual(0); + expect(statistics.numberOfTrianglesSelected).toEqual(0); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfFeaturesSelected).toEqual(features); + expect(statistics.numberOfFeaturesLoaded).toEqual(features); + expect(statistics.numberOfPointsSelected).toEqual(points); + expect(statistics.numberOfPointsLoaded).toEqual(points); + expect(statistics.numberOfTrianglesSelected).toEqual(triangles); + + viewNothing(); + scene.renderForSpecs(); + + expect(statistics.numberOfFeaturesSelected).toEqual(0); + expect(statistics.numberOfFeaturesLoaded).toEqual(features); + expect(statistics.numberOfPointsSelected).toEqual(0); + expect(statistics.numberOfPointsLoaded).toEqual(points); + expect(statistics.numberOfTrianglesSelected).toEqual(0); + + tileset.trimLoadedTiles(); + scene.renderForSpecs(); + + expect(statistics.numberOfFeaturesSelected).toEqual(0); + expect(statistics.numberOfFeaturesLoaded).toEqual(0); + expect(statistics.numberOfPointsSelected).toEqual(0); + expect(statistics.numberOfPointsLoaded).toEqual(0); + expect(statistics.numberOfTrianglesSelected).toEqual(0); + }); + } + + it('verify batched features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : withBatchTableUrl + })); + + return checkPointAndFeatureCounts(tileset, 10, 0, 120); + }); + + it('verify no batch table features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : noBatchIdsUrl + })); + + return checkPointAndFeatureCounts(tileset, 0, 0, 120); + }); + + it('verify instanced features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : instancedRedMaterialUrl + })); + + return checkPointAndFeatureCounts(tileset, 25, 0, 12); + }); + + it('verify composite features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : compositeUrl + })); + + return checkPointAndFeatureCounts(tileset, 35, 0, 132); + }); + + it('verify tileset of tilesets features statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetOfTilesetsUrl + })); + + return checkPointAndFeatureCounts(tileset, 50, 0, 600); + }); + + it('verify points statistics', function() { + viewPointCloud(); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : pointCloudUrl + })); + + return checkPointAndFeatureCounts(tileset, 0, 1000, 0); + }); + + it('verify triangle statistics', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetEmptyRootUrl + })); + + return checkPointAndFeatureCounts(tileset, 40, 0, 480); + }); + + it('verify batched points statistics', function() { + viewPointCloud(); + + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : pointCloudBatchedUrl + })); + + return checkPointAndFeatureCounts(tileset, 8, 1000, 0); + }); + + it('verify memory usage statistics', function() { + // Calculations in Batched3DModel3DTilesContentSpec + var singleTileGeometryMemory = 8880; + var singleTileTextureMemory = 0; + var singleTileBatchTextureMemory = 40; + var singleTilePickTextureMemory = 40; + var tilesLength = 5; + + viewNothing(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + + // No tiles loaded + expect(statistics.geometryByteLength).toEqual(0); + expect(statistics.texturesByteLength).toEqual(0); + expect(statistics.batchTableByteLength).toEqual(0); + + viewRootOnly(); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + // Root tile loaded + expect(statistics.geometryByteLength).toEqual(singleTileGeometryMemory); + expect(statistics.texturesByteLength).toEqual(singleTileTextureMemory); + expect(statistics.batchTableByteLength).toEqual(0); + + viewAllTiles(); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + // All tiles loaded + expect(statistics.geometryByteLength).toEqual(singleTileGeometryMemory * tilesLength); + expect(statistics.texturesByteLength).toEqual(singleTileTextureMemory * tilesLength); + expect(statistics.batchTableByteLength).toEqual(0); + + // One feature colored, the batch table memory is now higher + tileset._root.content.getFeature(0).color = Color.RED; + scene.renderForSpecs(); + expect(statistics.geometryByteLength).toEqual(singleTileGeometryMemory * tilesLength); + expect(statistics.texturesByteLength).toEqual(singleTileTextureMemory * tilesLength); + expect(statistics.batchTableByteLength).toEqual(singleTileBatchTextureMemory); + + // All tiles picked, the texture memory is now higher + scene.pickForSpecs(); + expect(statistics.geometryByteLength).toEqual(singleTileGeometryMemory * tilesLength); + expect(statistics.texturesByteLength).toEqual(singleTileTextureMemory * tilesLength); + expect(statistics.batchTableByteLength).toEqual(singleTileBatchTextureMemory + singleTilePickTextureMemory * tilesLength); + + // Tiles are still in memory when zoomed out + viewNothing(); + scene.renderForSpecs(); + expect(statistics.geometryByteLength).toEqual(singleTileGeometryMemory * tilesLength); + expect(statistics.texturesByteLength).toEqual(singleTileTextureMemory * tilesLength); + expect(statistics.batchTableByteLength).toEqual(singleTileBatchTextureMemory + singleTilePickTextureMemory * tilesLength); + + // Trim loaded tiles, expect the memory statistics to be 0 + tileset.trimLoadedTiles(); + scene.renderForSpecs(); + expect(statistics.geometryByteLength).toEqual(0); + expect(statistics.texturesByteLength).toEqual(0); + expect(statistics.batchTableByteLength).toEqual(0); + }); + }); + }); + }); + + it('verify memory usage statistics for shared resources', function() { + // Six tiles total: + // * Two b3dm tiles - no shared resources + // * Two i3dm tiles with embedded glTF - no shared resources + // * Two i3dm tiles with external glTF - shared resources + // Expect to see some saving with memory usage since two of the tiles share resources + // All tiles reference the same external texture but texture caching is not supported yet + // TODO : tweak test when #5051 is in + + var b3dmGeometryMemory = 840; // Only one box in the tile, unlike most other test tiles + var i3dmGeometryMemory = 840; + + // Texture is 211x211 RGBA bytes, but upsampled to 256x256 because the wrap mode is REPEAT + var texturesByteLength = 262144; + + var expectedGeometryMemory = b3dmGeometryMemory * 2 + i3dmGeometryMemory * 3; + var expectedTextureMemory = texturesByteLength * 5; + + return Cesium3DTilesTester.loadTileset(scene, tilesetWithExternalResourcesUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.geometryByteLength).toBe(expectedGeometryMemory); + expect(statistics.texturesByteLength).toBe(expectedTextureMemory); + }); + }); + + it('does not process tileset when screen space error is not met', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + + // Set zoom far enough away to not meet sse + viewNothing(); + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + }); + }); + + it('does not select tiles when outside of view frustum', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + + viewSky(); + + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + expect(tileset._root.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).toEqual(CullingVolume.MASK_OUTSIDE); + }); + }); + + it('culls with content box', function() { + // Root tile has a content box that is half the extents of its box + // Expect to cull root tile and three child tiles + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + + viewBottomLeft(); + scene.renderForSpecs(); + expect(statistics.visited).toEqual(2); // Visits root, but does not render it + expect(statistics.numberOfCommands).toEqual(1); + expect(tileset._selectedTiles[0]).not.toBe(tileset._root); + + // Set contents box to undefined, and now root won't be culled + tileset._root._contentBoundingVolume = undefined; + scene.renderForSpecs(); + expect(statistics.visited).toEqual(2); + expect(statistics.numberOfCommands).toEqual(2); + }); + }); + + function findTileByUrl(tiles, url) { + var length = tiles.length; + for (var i = 0; i < length; ++i) { + if (tiles[i].content._url.indexOf(url) >= 0) { + return tiles[i]; + } + } + return undefined; + } + + it('selects children in front to back order', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + // After moving the camera left by 1.0 and down by 0.5, the distance from the camera should be in the order: + // 1. lower left + // 2. upper left + // 3. lower right + // 4. upper right + + scene.camera.moveLeft(1.0); + scene.camera.moveDown(0.5); + scene.renderForSpecs(); + + var root = tileset._root; + var llTile = findTileByUrl(root.children, 'll.b3dm'); + var lrTile = findTileByUrl(root.children, 'lr.b3dm'); + var urTile = findTileByUrl(root.children, 'ur.b3dm'); + var ulTile = findTileByUrl(root.children, 'ul.b3dm'); + + var selectedTiles = tileset._selectedTiles; + expect(selectedTiles[0]).toBe(root); + expect(selectedTiles[1]).toBe(llTile); + expect(selectedTiles[2]).toBe(ulTile); + expect(selectedTiles[3]).toBe(lrTile); + expect(selectedTiles[4]).toBe(urTile); + }); + }); + + function testDynamicScreenSpaceError(url, distance) { + return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { + var statistics = tileset._statistics; + + // Horizon view, only root is visible + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, distance)); + + // Set dynamic SSE to false (default) + tileset.dynamicScreenSpaceError = false; + scene.renderForSpecs(); + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(1); + + // Set dynamic SSE to true, now the root is not rendered + tileset.dynamicScreenSpaceError = true; + tileset.dynamicScreenSpaceErrorDensity = 1.0; + tileset.dynamicScreenSpaceErrorFactor = 10.0; + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + }); + } + + function numberOfChildrenWithoutContent(tile) { + var children = tile.children; + var length = children.length; + var count = 0; + for (var i = 0; i < length; ++i) { + var child = children[i]; + if (!child.contentReady) { + ++count; + } + } + return count; + } + + // Adjust distances for each test because the dynamic SSE takes the + // bounding volume height into account, which differs for each bounding volume. + it('uses dynamic screen space error for tileset with region', function() { + return testDynamicScreenSpaceError(withTransformRegionUrl, 103.0); + }); + + it('uses dynamic screen space error for tileset with bounding sphere', function() { + return testDynamicScreenSpaceError(withBoundingSphereUrl, 137.0); + }); + + it('uses dynamic screen space error for local tileset with box', function() { + return testDynamicScreenSpaceError(withTransformBoxUrl, 103.0); + }); + + it('uses dynamic screen space error for local tileset with sphere', function() { + return testDynamicScreenSpaceError(withTransformSphereUrl, 144.0); + }); + + it('additive refinement - selects root when sse is met', function() { + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + // Meets screen space error, only root tile is rendered + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(1); + }); + }); + + it('additive refinement - selects all tiles when sse is not met', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + // Does not meet screen space error, all tiles are visible + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + }); + }); + + it('additive refinement - use parent\'s geometric error on child\'s box for early refinement', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); + expect(statistics.numberOfCommands).toEqual(5); + + // Both right tiles don't meet the SSE anymore + scene.camera.moveLeft(50.0); + scene.renderForSpecs(); + expect(statistics.visited).toEqual(3); + expect(statistics.numberOfCommands).toEqual(3); + }); + }); + + it('additive refinement - selects tile when inside viewer request volume', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetWithViewerRequestVolumeUrl).then(function(tileset) { + var statistics = tileset._statistics; + // Force root tile to always not meet SSE since this is just checking the request volume + tileset.maximumScreenSpaceError = 0.0; + + // Renders all 5 tiles + setZoom(20.0); + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(5); + + // No longer renders the tile with a request volume + setZoom(1500.0); + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(4); + }); + }); + + it('replacement refinement - selects root when sse is met', function() { + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset._root.refine = Cesium3DTileRefine.REPLACE; + + // Meets screen space error, only root tile is rendered + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(1); + }); + }); + + it('replacement refinement - selects children when sse is not met', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset._root.refine = Cesium3DTileRefine.REPLACE; + + // Does not meet screen space error, child tiles replace root tile + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(5); // Visits root, but does not render it + expect(statistics.numberOfCommands).toEqual(4); + }); + }); + + it('replacement refinement - selects root when sse is not met and children are not ready', function() { + // Set view so that only root tile is loaded initially + viewRootOnly(); + + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var root = tileset._root; + root.refine = Cesium3DTileRefine.REPLACE; + + // Set zoom to start loading child tiles + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + // LOD skipping visits all visible + expect(statistics.visited).toEqual(5); + // no stencil clear command because only the root tile + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfPendingRequests).toEqual(4); + expect(numberOfChildrenWithoutContent(root)).toEqual(4); + }); + }); + + it('replacement refinement - selects tile when inside viewer request volume', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetWithViewerRequestVolumeUrl).then(function(tileset) { + var statistics = tileset._statistics; + + var root = tileset._root; + root.refine = Cesium3DTileRefine.REPLACE; + // Force root tile to always not meet SSE since this is just checking the request volume + tileset.maximumScreenSpaceError = 0.0; + + // Renders all 5 tiles + setZoom(20.0); + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(5); + expect(root.selected).toBe(false); + + // No longer renders the tile with a request volume + setZoom(1500.0); + root.hasRenderableContent = true; // mock content + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(4); + expect(root.selected).toBe(true); // one child is no longer selected. root is chosen instead + }); + }); + + it('replacement refinement - selects root when sse is not met and subtree is not refinable (1)', function() { + // No children have content, but all grandchildren have content + // + // C + // E E + // C C C C + // + + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement1Url).then(function(tileset) { + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + var root = tileset._root; + + return when.join(root.children[0].contentReadyPromise, root.children[1].contentReadyPromise).then(function() { + // Even though root's children are loaded, the grandchildren need to be loaded before it becomes refinable + expect(numberOfChildrenWithoutContent(root)).toEqual(0); // Children are loaded + expect(statistics.numberOfCommands).toEqual(1); // No stencil or backface commands; no mixed content + expect(statistics.numberOfPendingRequests).toEqual(4); // Loading grandchildren + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(4); // Render children + }); + }); + }); + }); + + it('replacement refinement - selects root when sse is not met and subtree is not refinable (2)', function() { + // Check that the root is refinable once its child is loaded + // + // C + // E + // C E + // C (smaller geometric error) + // + + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement2Url).then(function(tileset) { + var statistics = tileset._statistics; + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(1); + + setZoom(5.0); // Zoom into the last tile, when it is ready the root is refinable + scene.renderForSpecs(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(2); // Renders two content tiles + }); + }); + }); + }); + + it('replacement refinement - selects root when sse is not met and subtree is not refinable (3)', function() { + // Check that the root is refinable once its child is loaded + // + // C + // T (external tileset ref) + // E (root of external tileset) + // C C C C + // + + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + var statistics = tileset._statistics; + var root = tileset._root; + expect(statistics.numberOfCommands).toEqual(1); + + viewAllTiles(); + scene.renderForSpecs(); + return root.children[0].contentReadyPromise.then(function() { + // The external tileset json is loaded, but the external tileset isn't. + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(1); // root + expect(statistics.numberOfPendingRequests).toEqual(4); // Loading child content tiles + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(root.selected).toEqual(false); + expect(statistics.numberOfCommands).toEqual(4); // Render child content tiles + }); + }); + }); + }); + + it('replacement and additive refinement', function() { + // A + // A R (not rendered) + // R A R A + // + return Cesium3DTilesTester.loadTileset(scene, tilesetRefinementMix).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(7); + expect(statistics.numberOfCommands).toEqual(6); + }); + }); + + describe('children bound union optimization', function() { + it('does not select visible tiles with invisible children', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude, 22.0); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, 1.57, 1.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.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); + }); + }); + + 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]; + + childRoot.geometricError = 0; // child root should meet SSE and children should not be drawn + scene.renderForSpecs(); + // wait for load because geometric error has changed + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + 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); + }); + }); + }); + + it('does select visibile tiles with visible children failing request volumes', function() { + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacementWithViewerRequestVolumeUrl).then(function(tileset) { + var root = tileset._root; + var childRoot = root.children[0]; + + 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) { + var root = tileset._root; + var childRoot = root.children[0]; + childRoot.geometricError = 0; + + // wait for load because geometric error has changed + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + 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); + }); + }); + }); + }); + + it('loads tileset with external tileset.json', function() { + // Set view so that no tiles are loaded initially + viewNothing(); + + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + // Root points to an external tileset.json and has no children until it is requested + var root = tileset._root; + expect(root.children.length).toEqual(0); + + // Set view so that root's content is requested + viewRootOnly(); + scene.renderForSpecs(); + return root.contentReadyPromise.then(function() { + expect(root.hasTilesetContent).toEqual(true); + + // Root has one child now, the root of the external tileset + expect(root.children.length).toEqual(1); + + // Check that headers are equal + var subtreeRoot = root.children[0]; + expect(root.geometricError).toEqual(subtreeRoot.geometricError); + expect(root.refine).toEqual(subtreeRoot.refine); + expect(root.contentBoundingVolume.boundingVolume).toEqual(subtreeRoot.contentBoundingVolume.boundingVolume); + + // Check that subtree root has 4 children + expect(subtreeRoot.hasTilesetContent).toEqual(false); + expect(subtreeRoot.children.length).toEqual(4); + }); + }); + }); + + it('preserves query string with external tileset.json', function() { + // Set view so that no tiles are loaded initially + viewNothing(); + + //Spy on loadWithXhr so we can verify requested urls + spyOn(loadWithXhr, 'load').and.callThrough(); + + var queryParams = '?a=1&b=boy'; + var expectedUrl = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset.json' + queryParams; + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl + queryParams).then(function(tileset) { + //Make sure tileset.json was requested with query parameters + expect(loadWithXhr.load.calls.argsFor(0)[0]).toEqual(expectedUrl); + + loadWithXhr.load.calls.reset(); + + // Set view so that root's content is requested + viewRootOnly(); + scene.renderForSpecs(); + + return tileset._root.contentReadyPromise; + }).then(function() { + //Make sure tileset2.json was requested with query parameters and version + var queryParamsWithVersion = queryParams + '&v=0.0'; + expectedUrl = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/tileset2.json' + queryParamsWithVersion; + expect(loadWithXhr.load.calls.argsFor(0)[0]).toEqual(expectedUrl); + }); + }); + + it('renders tileset with external tileset.json', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(7); // Visits two tiles with tileset content, five tiles with b3dm content + expect(statistics.numberOfCommands).toEqual(5); // Render the five tiles with b3dm content + }); + }); + + it('set tile color', function() { + return Cesium3DTilesTester.loadTileset(scene, noBatchIdsUrl).then(function(tileset) { + // Get initial color + var color; + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + color = rgba; + }); + + // Check for color + tileset._root.color = Color.RED; + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba).not.toEqual(color); + }); + }); + }); + + it('debugFreezeFrame', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(1); + + tileset.debugFreezeFrame = true; + viewAllTiles(); + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); // selectTiles returns early, so no tiles are visited + expect(statistics.numberOfCommands).toEqual(1); // root tile is still in selectedTiles list + }); + }); + + function checkDebugColorizeTiles(url) { + return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { + // Get initial color + var color; + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + color = rgba; + }); + + // Check for debug color + tileset.debugColorizeTiles = true; + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba).not.toEqual(color); + }); + + // Check for original color + tileset.debugColorizeTiles = false; + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba).toEqual(color); + }); + }); + } + + it('debugColorizeTiles for b3dm with batch table', function() { + return checkDebugColorizeTiles(withBatchTableUrl); + }); + + it('debugColorizeTiles for b3dm without batch table', function() { + return checkDebugColorizeTiles(noBatchIdsUrl); + }); + + it('debugColorizeTiles for i3dm', function() { + viewInstances(); + return checkDebugColorizeTiles(instancedUrl); + }); + + it('debugColorizeTiles for cmpt', function() { + return checkDebugColorizeTiles(compositeUrl); + }); + + it('debugColorizeTiles for pnts with batch table', function() { + viewPointCloud(); + return checkDebugColorizeTiles(pointCloudBatchedUrl); + }); + + it('debugColorizeTiles for pnts without batch table', function() { + viewPointCloud(); + return checkDebugColorizeTiles(pointCloudUrl); + }); + + it('debugWireframe', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + tileset.debugWireframe = true; + scene.renderForSpecs(); + var commands = scene.frameState.commandList; + var length = commands.length; + var i; + for (i = 0; i < length; ++i) { + expect(commands[i].primitiveType).toEqual(PrimitiveType.LINES); + } + + tileset.debugWireframe = false; + scene.renderForSpecs(); + commands = scene.frameState.commandList; + for (i = 0; i < length; ++i) { + expect(commands[i].primitiveType).toEqual(PrimitiveType.TRIANGLES); + } + }); + }); + + it('debugShowBoundingVolume', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + tileset.debugShowBoundingVolume = true; + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(2); // Tile command + bounding volume command + + tileset.debugShowBoundingVolume = false; + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(1); + }); + }); + + it('debugShowContentBoundingVolume', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + tileset.debugShowContentBoundingVolume = true; + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(2); // Tile command + bounding volume command + + tileset.debugShowContentBoundingVolume = false; + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(1); + }); + }); + + it('debugShowViewerRequestVolume', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetWithViewerRequestVolumeUrl).then(function(tileset) { + tileset.debugShowViewerRequestVolume = true; + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.visited).toEqual(6); // 1 empty root tile + 4 b3dm tiles + 1 pnts tile + expect(statistics.numberOfCommands).toEqual(6); // 5 tile commands + viewer request volume command + + tileset.debugShowViewerRequestVolume = false; + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(5); + }); + }); + + it('show tile debug labels with regions', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + expect(tileset._tileDebugLabels.length).toEqual(5); + + var root = tileset._root; + expect(tileset._tileDebugLabels._labels[0].text).toEqual('Geometric error: ' + root.geometricError); + expect(tileset._tileDebugLabels._labels[1].text).toEqual('Geometric error: ' + root.children[0].geometricError); + expect(tileset._tileDebugLabels._labels[2].text).toEqual('Geometric error: ' + root.children[1].geometricError); + expect(tileset._tileDebugLabels._labels[3].text).toEqual('Geometric error: ' + root.children[2].geometricError); + expect(tileset._tileDebugLabels._labels[4].text).toEqual('Geometric error: ' + root.children[3].geometricError); + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show tile debug labels with boxes', function() { + // tilesetWithTransformsUrl has bounding boxes + return Cesium3DTilesTester.loadTileset(scene, tilesetWithTransformsUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + expect(tileset._tileDebugLabels.length).toEqual(2); + + var root = tileset._root; + expect(tileset._tileDebugLabels._labels[0].text).toEqual('Geometric error: ' + root.geometricError); + expect(tileset._tileDebugLabels._labels[1].text).toEqual('Geometric error: ' + root.children[0].geometricError); + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show tile debug labels with bounding spheres', function() { + // tilesetWithViewerRequestVolumeUrl has bounding sphere + return Cesium3DTilesTester.loadTileset(scene, tilesetWithViewerRequestVolumeUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + scene.renderForSpecs(); + + var length = tileset._selectedTiles.length; + expect(tileset._tileDebugLabels).toBeDefined(); + expect(tileset._tileDebugLabels.length).toEqual(length); + + for (var i = 0; i < length; ++i) { + expect(tileset._tileDebugLabels._labels[i].text).toEqual('Geometric error: ' + tileset._selectedTiles[i].geometricError); + } + + tileset.debugShowGeometricError = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show tile debug labels with rendering statistics', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowRenderingStatistics = true; + viewRootOnly(); + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + expect(tileset._tileDebugLabels.length).toEqual(1); + + var content = tileset._root.content; + var expected = 'Commands: ' + tileset._root.commandsLength + '\n' + + 'Triangles: ' + content.trianglesLength + '\n' + + 'Features: ' + content.featuresLength; + + expect(tileset._tileDebugLabels._labels[0].text).toEqual(expected); + + tileset.debugShowRenderingStatistics = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show tile debug labels with memory usage', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowMemoryUsage = true; + viewRootOnly(); + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + expect(tileset._tileDebugLabels.length).toEqual(1); + + var expected = 'Texture Memory: 0\n' + + 'Geometry Memory: 0.008'; + + expect(tileset._tileDebugLabels._labels[0].text).toEqual(expected); + + tileset.debugShowMemoryUsage = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show tile debug labels with all statistics', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + tileset.debugShowRenderingStatistics = true; + tileset.debugShowMemoryUsage = true; + viewRootOnly(); + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + + var expected = 'Geometric error: 70\n' + + 'Commands: 1\n' + + 'Triangles: 120\n' + + 'Features: 10\n' + + 'Texture Memory: 0\n' + + 'Geometry Memory: 0.008'; + expect(tileset._tileDebugLabels._labels[0].text).toEqual(expected); + + tileset.debugShowGeometricError = false; + tileset.debugShowRenderingStatistics = false; + tileset.debugShowMemoryUsage = false; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).not.toBeDefined(); + }); + }); + + it('show only picked tile debug label with all stats', function() { + // tilesetUrl has bounding regions + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.debugShowGeometricError = true; + tileset.debugShowRenderingStatistics = true; + tileset.debugShowMemoryUsage = true; + tileset.debugPickedTileLabelOnly = true; + + var scratchPosition = new Cartesian3(1.0, 1.0, 1.0); + tileset.debugPickedTile = tileset._root; + tileset.debugPickPosition = scratchPosition; + + scene.renderForSpecs(); + expect(tileset._tileDebugLabels).toBeDefined(); + + var expected = 'Geometric error: 70\n' + + 'Commands: 1\n' + + 'Triangles: 120\n' + + 'Features: 10\n' + + 'Texture Memory: 0\n' + + 'Geometry Memory: 0.008'; + expect(tileset._tileDebugLabels.get(0).text).toEqual(expected); + expect(tileset._tileDebugLabels.get(0).position).toEqual(scratchPosition); + + tileset.debugPickedTile = undefined; + scene.renderForSpecs(); + expect(tileset._tileDebugLabels.length).toEqual(0); + }); + }); + + it('does not request tiles when picking', function() { + viewNothing(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + scene.pickForSpecs(); + expect(tileset._statistics.numberOfPendingRequests).toEqual(0); + scene.renderForSpecs(); + expect(tileset._statistics.numberOfPendingRequests).toEqual(1); + }); + }); + + it('does not process tiles when picking', function() { + var spy = spyOn(Cesium3DTile.prototype, 'process').and.callThrough(); + + viewNothing(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewRootOnly(); + scene.renderForSpecs(); // Request root + expect(tileset._statistics.numberOfPendingRequests).toEqual(1); + return tileset._root.contentReadyToProcessPromise.then(function() { + scene.pickForSpecs(); + expect(spy).not.toHaveBeenCalled(); + scene.renderForSpecs(); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + it('does not request tiles when the request scheduler is full', function() { + viewRootOnly(); // Root tiles are loaded initially + var options = { + skipLevelOfDetail : false + }; + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl, options).then(function(tileset) { + // Try to load 4 children. Only 3 requests will go through, 1 will be attempted. + var oldMaximumRequestsPerServer = RequestScheduler.maximumRequestsPerServer; + RequestScheduler.maximumRequestsPerServer = 3; + + viewAllTiles(); + scene.renderForSpecs(); + + expect(tileset._statistics.numberOfPendingRequests).toEqual(3); + expect(tileset._statistics.numberOfAttemptedRequests).toEqual(1); + + RequestScheduler.maximumRequestsPerServer = oldMaximumRequestsPerServer; + }); + }); + + it('load progress events are raised', function() { + // [numberOfPendingRequests, numberOfTilesProcessing] + var results = [ + [1, 0], + [0, 1], + [0, 0] + ]; + var spyUpdate = jasmine.createSpy('listener'); + + viewNothing(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.loadProgress.addEventListener(spyUpdate); + viewRootOnly(); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(spyUpdate.calls.count()).toEqual(3); + expect(spyUpdate.calls.allArgs()).toEqual(results); + }); + }); + }); + + it('tilesLoaded', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + expect(tileset.tilesLoaded).toBe(false); + tileset.readyPromise.then(function() { + expect(tileset.tilesLoaded).toBe(false); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(tileset.tilesLoaded).toBe(true); + }); + }); + }); + + it('all tiles loaded event is raised', function() { + // Called first when only the root is visible and it becomes loaded, and then again when + // the rest of the tileset is visible and all tiles are loaded. + var spyUpdate = jasmine.createSpy('listener'); + viewRootOnly(); + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + tileset.allTilesLoaded.addEventListener(spyUpdate); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + viewAllTiles(); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(spyUpdate.calls.count()).toEqual(2); + }); + }); + }); + + it('tile visible event is raised', function() { + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var spyUpdate = jasmine.createSpy('listener'); + tileset.tileVisible.addEventListener(spyUpdate); + scene.renderForSpecs(); + expect(tileset._root.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(spyUpdate.calls.count()).toEqual(1); + expect(spyUpdate.calls.argsFor(0)[0]).toBe(tileset._root); + }); + }); + + it('destroys', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var root = tileset._root; + expect(tileset.isDestroyed()).toEqual(false); + scene.primitives.remove(tileset); + expect(tileset.isDestroyed()).toEqual(true); + + // Check that all tiles are destroyed + expect(root.isDestroyed()).toEqual(true); + expect(root.children[0].isDestroyed()).toEqual(true); + expect(root.children[1].isDestroyed()).toEqual(true); + expect(root.children[2].isDestroyed()).toEqual(true); + expect(root.children[3].isDestroyed()).toEqual(true); + }); + }); + + it('destroys before external tileset.json finishes loading', function() { + viewNothing(); + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var root = tileset._root; + + viewRootOnly(); + scene.renderForSpecs(); // Request external tileset.json + + var statistics = tileset._statistics; + expect(statistics.numberOfPendingRequests).toEqual(1); + scene.primitives.remove(tileset); + + return root.contentReadyPromise.then(function(root) { + fail('should not resolve'); + }).otherwise(function(error) { + // Expect the root to not have added any children from the external tileset.json + expect(root.children.length).toEqual(0); + }); + }); + }); + + it('destroys before tile finishes loading', function() { + viewRootOnly(); + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + return tileset.readyPromise.then(function(tileset) { + var root = tileset._root; + scene.renderForSpecs(); // Request root + scene.primitives.remove(tileset); + + return root.contentReadyPromise.then(function(content) { + fail('should not resolve'); + }).otherwise(function(error) { + expect(root._contentState).toBe(Cesium3DTileContentState.FAILED); + }); + }); + }); + + /////////////////////////////////////////////////////////////////////////// + // Styling tests + + it('applies show style to a tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + var hideStyle = new Cesium3DTileStyle({show : 'false'}); + tileset.style = hideStyle; + expect(tileset.style).toBe(hideStyle); + expect(scene).toRender([0, 0, 0, 255]); + + tileset.style = new Cesium3DTileStyle({show : 'true'}); + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + it('applies style with complex show expression to a tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + // Each feature in the b3dm file has an id property from 0 to 9 + // ${id} >= 10 will always evaluate to false + tileset.style = new Cesium3DTileStyle({show : '${id} >= 50 * 2'}); + expect(scene).toRender([0, 0, 0, 255]); + + // ${id} < 10 will always evaluate to true + tileset.style = new Cesium3DTileStyle({show : '${id} < 200 / 2'}); + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + it('applies show style to a tileset with a composite tile', function() { + return Cesium3DTilesTester.loadTileset(scene, compositeUrl).then(function(tileset) { + tileset.style = new Cesium3DTileStyle({show : 'false'}); + expect(scene).toRender([0, 0, 0, 255]); + + tileset.style = new Cesium3DTileStyle({show : 'true'}); + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + function expectColorStyle(tileset) { + var color; + expect(scene).toRenderAndCall(function(rgba) { + color = rgba; + }); + + tileset.style = new Cesium3DTileStyle({color : 'color("blue")'}); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[3]).toEqual(255); + }); + + // set color to transparent + tileset.style = new Cesium3DTileStyle({color : 'color("blue", 0.0)'}); + expect(scene).toRender([0, 0, 0, 255]); + + tileset.style = new Cesium3DTileStyle({color : 'color("cyan")'}); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[3]).toEqual(255); + }); + + // Remove style + tileset.style = undefined; + expect(scene).toRender(color); + } + + it('applies color style to a tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + expectColorStyle(tileset); + }); + }); + + it('applies color style to a tileset with translucent tiles', function() { + return Cesium3DTilesTester.loadTileset(scene, translucentUrl).then(function(tileset) { + expectColorStyle(tileset); + }); + }); + + it('applies color style to a tileset with translucent and opaque tiles', function() { + return Cesium3DTilesTester.loadTileset(scene, translucentOpaqueMixUrl).then(function(tileset) { + expectColorStyle(tileset); + }); + }); + + it('applies style when feature properties change', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + // Initially, all feature ids are less than 10 + tileset.style = new Cesium3DTileStyle({show : '${id} < 10'}); + expect(scene).notToRender([0, 0, 0, 255]); + + // Change feature ids so the show expression will evaluate to false + var content = tileset._root.content; + var length = content.featuresLength; + var i; + var feature; + for (i = 0; i < length; ++i) { + feature = content.getFeature(i); + feature.setProperty('id', feature.getProperty('id') + 10); + } + expect(scene).toRender([0, 0, 0, 255]); + + // Change ids back + for (i = 0; i < length; ++i) { + feature = content.getFeature(i); + feature.setProperty('id', feature.getProperty('id') - 10); + } + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + it('applies style with complex color expression to a tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + // Each feature in the b3dm file has an id property from 0 to 9 + // ${id} >= 10 will always evaluate to false + tileset.style = new Cesium3DTileStyle({color : '(${id} >= 50 * 2) ? color("red") : color("blue")'}); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[3]).toEqual(255); + }); + + // ${id} < 10 will always evaluate to true + tileset.style = new Cesium3DTileStyle({color : '(${id} < 50 * 2) ? color("red") : color("blue")'}); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + }); + }); + + it('applies conditional color style to a tileset', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + // ${id} < 10 will always evaluate to true + tileset.style = new Cesium3DTileStyle({ + color : { + conditions : [ + ['${id} < 10', 'color("red")'], + ['true', 'color("blue")'] + ] + } + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // ${id}>= 10 will always evaluate to false + tileset.style = new Cesium3DTileStyle({ + color : { + conditions : [ + ['${id} >= 10', 'color("red")'], + ['true', 'color("blue")'] + ] + } + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[3]).toEqual(255); + }); + }); + }); + + it('loads style from uri', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + // ${id} < 10 will always evaluate to true + tileset.style = new Cesium3DTileStyle(styleUrl); + return tileset.style.readyPromise.then(function(style) { + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + }).otherwise(function(error) { + expect(error).not.toBeDefined(); + }); + }); + }); + + it('applies custom style to a tileset', function() { + var style = new Cesium3DTileStyle(); + style.show = { + evaluate : function(frameState, feature) { + return this._value; + }, + _value : false + }; + style.color = { + evaluateColor : function(frameState, feature, result) { + return Color.clone(Color.WHITE, result); + } + }; + + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + tileset.style = style; + expect(tileset.style).toBe(style); + expect(scene).toRender([0, 0, 0, 255]); + + style.show._value = true; + tileset.makeStyleDirty(); + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + function testColorBlendMode(url) { + return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { + // Check that the feature is red + var sourceRed; + expect(scene).toRenderAndCall(function(rgba) { + sourceRed = rgba[0]; + }); + + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Use HIGHLIGHT blending + tileset.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT; + + // Style with dark yellow. Expect the red channel to be darker than before. + tileset.style = new Cesium3DTileStyle({ + color : 'rgb(128, 128, 0)' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(sourceRed); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Style with yellow + alpha. Expect the red channel to be darker than before. + tileset.style = new Cesium3DTileStyle({ + color : 'rgba(255, 255, 0, 0.5)' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(sourceRed); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Use REPLACE blending + tileset.colorBlendMode = Cesium3DTileColorBlendMode.REPLACE; + + // Style with dark yellow. Expect the red and green channels to be roughly dark yellow. + tileset.style = new Cesium3DTileStyle({ + color : 'rgb(128, 128, 0)' + }); + var replaceRed; + var replaceGreen; + expect(scene).toRenderAndCall(function(rgba) { + replaceRed = rgba[0]; + replaceGreen = rgba[1]; + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[1]).toBeLessThan(255); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Style with yellow + alpha. Expect the red and green channels to be a shade of yellow. + tileset.style = new Cesium3DTileStyle({ + color : 'rgba(255, 255, 0, 0.5)' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[1]).toBeLessThan(255); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Use MIX blending + tileset.colorBlendMode = Cesium3DTileColorBlendMode.MIX; + tileset.colorBlendAmount = 0.5; + + // Style with dark yellow. Expect color to be a mix of the source and style colors. + tileset.style = new Cesium3DTileStyle({ + color : 'rgb(128, 128, 0)' + }); + var mixRed; + var mixGreen; + expect(scene).toRenderAndCall(function(rgba) { + mixRed = rgba[0]; + mixGreen = rgba[1]; + expect(rgba[0]).toBeGreaterThan(replaceRed); + expect(rgba[0]).toBeLessThan(sourceRed); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[1]).toBeLessThan(replaceGreen); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Set colorBlendAmount to 0.25. Expect color to be closer to the source color. + tileset.colorBlendAmount = 0.25; + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(mixRed); + expect(rgba[0]).toBeLessThan(sourceRed); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[1]).toBeLessThan(mixGreen); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Set colorBlendAmount to 0.0. Expect color to equal the source color + tileset.colorBlendAmount = 0.0; + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(sourceRed); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Set colorBlendAmount to 1.0. Expect color to equal the style color + tileset.colorBlendAmount = 1.0; + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toEqual(replaceRed); + expect(rgba[1]).toEqual(replaceGreen); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Style with yellow + alpha. Expect color to be a mix of the source and style colors. + tileset.colorBlendAmount = 0.5; + tileset.style = new Cesium3DTileStyle({ + color : 'rgba(255, 255, 0, 0.5)' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + }); + } + + it('sets colorBlendMode', function() { + return testColorBlendMode(colorsUrl); + }); + + it('sets colorBlendMode when vertex texture fetch is not supported', function() { + // Disable VTF + var maximumVertexTextureImageUnits = ContextLimits.maximumVertexTextureImageUnits; + ContextLimits._maximumVertexTextureImageUnits = 0; + return testColorBlendMode(colorsUrl).then(function() { + // Re-enable VTF + ContextLimits._maximumVertexTextureImageUnits = maximumVertexTextureImageUnits; + }); + }); + + it('sets colorBlendMode for textured tileset', function() { + return testColorBlendMode(texturedUrl); + }); + + it('sets colorBlendMode for instanced tileset', function() { + viewInstances(); + return testColorBlendMode(instancedRedMaterialUrl); + }); + + /////////////////////////////////////////////////////////////////////////// + // Cache replacement tests + + it('Unload all cached tiles not required to meet SSE using maximumMemoryUsage', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.maximumMemoryUsage = 0; + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + expect(tileset.totalMemoryUsageInBytes).toEqual(44400); // Specific to this tileset + + // Zoom out so only root tile is needed to meet SSE. This unloads + // the four children since the maximum memory usage is zero. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(1); + expect(tileset.totalMemoryUsageInBytes).toEqual(8880); // Specific to this tileset + + // Zoom back in so all four children are re-requested. + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + expect(tileset.totalMemoryUsageInBytes).toEqual(44400); // Specific to this tileset + }); + }); + }); + + it('Unload some cached tiles not required to meet SSE using maximumMemoryUsage', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.maximumMemoryUsage = 0.03; // Just enough memory to allow 3 tiles to remain + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + + // Zoom out so only root tile is needed to meet SSE. This unloads + // two of the four children so three tiles are still loaded (the + // root and two children) since the maximum memory usage is sufficient. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(3); + + // Zoom back in so the two children are re-requested. + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + }); + }); + }); + + it('Unloads cached tiles outside of the view frustum using maximumMemoryUsage', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.maximumMemoryUsage = 0; + + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + + viewSky(); + + // All tiles are unloaded + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(0); + expect(statistics.numberOfTilesWithContentReady).toEqual(0); + + // Reset camera so all tiles are reloaded + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + }); + }); + }); + + it('Unloads cached tiles in a tileset with external tileset.json using maximumMemoryUsage', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var statistics = tileset._statistics; + var replacementList = tileset._replacementList; + + tileset.maximumMemoryUsage = 0.025; + + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + expect(replacementList.length - 1).toEqual(5); // Only tiles with content are on the replacement list. -1 for sentinel. + + // Zoom out so only root tile is needed to meet SSE. This unloads + // all tiles except the root and one of the b3dm children + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(2); + expect(replacementList.length - 1).toEqual(2); + + // Reset camera so all tiles are reloaded + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + + expect(replacementList.length - 1).toEqual(5); + }); + }); + }); + + it('Unloads cached tiles in a tileset with empty tiles using maximumMemoryUsage', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetEmptyRootUrl).then(function(tileset) { + var statistics = tileset._statistics; + + tileset.maximumMemoryUsage = 0.025; + + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(4); + expect(statistics.numberOfTilesWithContentReady).toEqual(4); // 4 children with b3dm content (does not include empty root) + + viewSky(); + + // Unload tiles to meet cache size + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toEqual(0); + expect(statistics.numberOfTilesWithContentReady).toEqual(2); // 2 children with b3dm content (does not include empty root) + + // Reset camera so all tiles are reloaded + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(4); + expect(statistics.numberOfTilesWithContentReady).toEqual(4); + }); + }); + }); + + it('Unload cached tiles when a tileset uses replacement refinement using maximumMemoryUsage', function() { + // No children have content, but all grandchildren have content + // + // C + // E E + // C C C C + // + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement1Url).then(function(tileset) { + tileset.maximumMemoryUsage = 0; // Only root needs to be visible + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(4); // 4 grandchildren. Root is replaced. + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Root + four grandchildren (does not include empty children) + + // Zoom out so only root tile is needed to meet SSE. This unloads + // all grandchildren since the max number of loaded tiles is one. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(1); + + // Zoom back in so the four children are re-requested. + viewAllTiles(); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function() { + expect(statistics.numberOfCommands).toEqual(4); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + }); + }); + }); + + it('Explicitly unloads cached tiles with trimLoadedTiles', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.maximumMemoryUsage = 0.05; + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + + // Zoom out so only root tile is needed to meet SSE. The children + // are not unloaded since max number of loaded tiles is five. + viewRootOnly(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + + tileset.trimLoadedTiles(); + scene.renderForSpecs(); + + expect(statistics.numberOfCommands).toEqual(1); + expect(statistics.numberOfTilesWithContentReady).toEqual(1); + }); + }); + + it('tileUnload event is raised', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + tileset.maximumMemoryUsage = 0; + + // Render parent and four children (using additive refinement) + viewAllTiles(); + scene.renderForSpecs(); + + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(5); + expect(statistics.numberOfTilesWithContentReady).toEqual(5); // Five loaded tiles + + // Zoom out so only root tile is needed to meet SSE. All the + // children are unloaded since max number of loaded tiles is one. + viewRootOnly(); + var spyUpdate = jasmine.createSpy('listener'); + tileset.tileUnload.addEventListener(spyUpdate); + scene.renderForSpecs(); + + expect(tileset._root.visibility(scene.frameState, CullingVolume.MASK_INDETERMINATE)).not.toEqual(CullingVolume.MASK_OUTSIDE); + expect(spyUpdate.calls.count()).toEqual(4); + expect(spyUpdate.calls.argsFor(0)[0]).toBe(tileset._root.children[0]); + expect(spyUpdate.calls.argsFor(1)[0]).toBe(tileset._root.children[1]); + expect(spyUpdate.calls.argsFor(2)[0]).toBe(tileset._root.children[2]); + expect(spyUpdate.calls.argsFor(3)[0]).toBe(tileset._root.children[3]); + }); + }); + + it('maximumMemoryUsage throws when negative', function() { + var tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + expect(function() { + tileset.maximumMemoryUsage = -1; + }).toThrowDeveloperError(); + }); + + it('maximumScreenSpaceError throws when negative', function() { + var tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + expect(function() { + tileset.maximumScreenSpaceError = -1; + }).toThrowDeveloperError(); + }); + + it('propagates tile transform down the tree', function() { + var b3dmCommands = 1; + var i3dmCommands = scene.context.instancedArrays ? 1 : 25; // When instancing is not supported there is one command per instance + var totalCommands = b3dmCommands + i3dmCommands; + return Cesium3DTilesTester.loadTileset(scene, tilesetWithTransformsUrl).then(function(tileset) { + var statistics = tileset._statistics; + var root = tileset._root; + var rootTransform = Matrix4.unpack(root._header.transform); + + var child = root.children[0]; + var childTransform = Matrix4.unpack(child._header.transform); + var computedTransform = Matrix4.multiply(rootTransform, childTransform, new Matrix4()); + + expect(statistics.numberOfCommands).toBe(totalCommands); + expect(root.computedTransform).toEqual(rootTransform); + expect(child.computedTransform).toEqual(computedTransform); + + // Set the tileset's modelMatrix + var tilesetTransform = Matrix4.fromTranslation(new Cartesian3(0.0, 1.0, 0.0)); + tileset.modelMatrix = tilesetTransform; + computedTransform = Matrix4.multiply(tilesetTransform, computedTransform, computedTransform); + scene.renderForSpecs(); + expect(child.computedTransform).toEqual(computedTransform); + + // Set the modelMatrix somewhere off screen + tileset.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0.0, 100000.0, 0.0)); + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toBe(0); + + // Now bring it back + tileset.modelMatrix = Matrix4.IDENTITY; + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toBe(totalCommands); + + // Do the same steps for a tile transform + child.transform = Matrix4.fromTranslation(new Cartesian3(0.0, 100000.0, 0.0)); + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toBe(1); + child.transform = Matrix4.IDENTITY; + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toBe(totalCommands); + }); + }); + + it('does not mark tileset as refining when tiles have selection depth 0', function() { + viewRootOnly(); + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + viewAllTiles(); + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(statistics.numberOfTilesWithContentReady).toEqual(1); + expect(tileset._selectedTiles[0]._selectionDepth).toEqual(0); + expect(tileset._hasMixedContent).toBe(false); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + expect(tileset._hasMixedContent).toBe(false); + }); + }); + }); + + it('marks tileset as mixed when tiles have nonzero selection depth', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + var statistics = tileset._statistics; + + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + statistics.numberOfTilesWithContentReady -= 3; + + scene.renderForSpecs(); + + expect(tileset._hasMixedContent).toBe(true); + expect(statistics.numberOfTilesWithContentReady).toEqual(2); + expect(tileset._root.children[0].children[0].children[3]._selectionDepth).toEqual(1); + expect(tileset._root._selectionDepth).toEqual(0); + + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + expect(statistics.numberOfTilesWithContentReady).toEqual(5); + expect(tileset._hasMixedContent).toBe(false); + }); + }); + }); + + it('adds stencil clear command first when unresolved', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + + scene.renderForSpecs(); + var commandList = scene.frameState.commandList; + expect(commandList[0] instanceof ClearCommand).toBe(true); + expect(commandList[0].stencil).toBe(0); + }); + }); + + it('creates duplicate backface commands', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + + var statistics = tileset._statistics; + var root = tileset._root; + + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + + scene.renderForSpecs(); + + // 2 for root tile, 2 for child, 1 for stencil clear + expect(statistics.numberOfCommands).toEqual(5); + expect(root.selected).toBe(true); + expect(root._finalResolution).toBe(false); + expect(root.children[0].children[0].children[3].selected).toBe(true); + expect(root.children[0].children[0].children[3]._finalResolution).toBe(true); + expect(tileset._hasMixedContent).toBe(true); + + var commandList = scene.frameState.commandList; + var rs = commandList[1].renderState; + expect(rs.cull.enabled).toBe(true); + expect(rs.cull.face).toBe(CullFace.FRONT); + }); + }); + + it('does not create duplicate backface commands if no selected descendants', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + var statistics = tileset._statistics; + var root = tileset._root; + + tileset._root.children[0].children[0].children[0].unloadContent(); + tileset._root.children[0].children[0].children[1].unloadContent(); + tileset._root.children[0].children[0].children[2].unloadContent(); + tileset._root.children[0].children[0].children[3].unloadContent(); + + scene.renderForSpecs(); + + // 2 for root tile, 1 for child, 1 for stencil clear + expect(statistics.numberOfCommands).toEqual(1); + expect(root.selected).toBe(true); + expect(root._finalResolution).toBe(true); + expect(root.children[0].children[0].children[0].selected).toBe(false); + expect(root.children[0].children[0].children[1].selected).toBe(false); + expect(root.children[0].children[0].children[2].selected).toBe(false); + expect(root.children[0].children[0].children[3].selected).toBe(false); + expect(tileset._hasMixedContent).toBe(false); + }); + }); + + it('does not add commands or stencil clear command with no selected tiles', function() { + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetUrl + })); + scene.renderForSpecs(); + var statistics = tileset._statistics; + expect(tileset._selectedTiles.length).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + }); + + it('does not add stencil clear command or backface commands when fully resolved', function() { + viewAllTiles(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.numberOfCommands).toEqual(tileset._selectedTiles.length); + + var commandList = scene.frameState.commandList; + var length = commandList.length; + for (var i = 0; i < length; ++i) { + var command = commandList[i]; + expect(command instanceof ClearCommand).toBe(false); + expect(command.renderState.cull.face).not.toBe(CullFace.FRONT); + } + }); + }); + + it('loadSiblings', function() { + viewBottomLeft(); + return Cesium3DTilesTester.loadTileset(scene, tilesetReplacement3Url, { + loadSiblings : false, + baseScreenSpaceError: 1000000000 + }).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.numberOfTilesWithContentReady).toBe(2); + tileset.loadSiblings = true; + scene.renderForSpecs(); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + expect(statistics.numberOfTilesWithContentReady).toBe(5); + }); + }); + }); + + it('immediatelyLoadDesiredLevelOfDetail', function() { + viewBottomLeft(); + var tileset = scene.primitives.add(new Cesium3DTileset({ + url : tilesetOfTilesetsUrl, + immediatelyLoadDesiredLevelOfDetail : true + })); + return Cesium3DTilesTester.waitForReady(scene, tileset).then(function(tileset) { + scene.renderForSpecs(); + return tileset._root.contentReadyPromise.then(function() { + tileset._root.refine = Cesium3DTileRefine.REPLACE; + tileset._root.children[0].refine = Cesium3DTileRefine.REPLACE; + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then(function(tileset) { + var statistics = tileset._statistics; + expect(statistics.numberOfTilesWithContentReady).toBe(1); + }); + }); + }); + }); + + it('selects children if no ancestors available', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var statistics = tileset._statistics; + var parent = tileset._root.children[0]; + var child = parent.children[3].children[0]; + parent.refine = Cesium3DTileRefine.REPLACE; + parent.unloadContent(); + + viewBottomLeft(); + scene.renderForSpecs(); + + expect(child.contentReady).toBe(true); + expect(parent.contentReady).toBe(false); + expect(child.selected).toBe(true); + expect(parent.selected).toBe(false); + expect(statistics.numberOfCommands).toEqual(1); + }); + }); + + it('tile expires', function() { + return Cesium3DTilesTester.loadTileset(scene, batchedExpirationUrl).then(function(tileset) { + // Intercept the request and load content that produces more draw commands, to simulate fetching new content after the original expires + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + loadWithXhr.defaultLoad(batchedColorsB3dmUrl, responseType, method, data, headers, deferred, overrideMimeType); + }); + var tile = tileset._root; + var statistics = tileset._statistics; + var expiredContent; + + // Check that expireDuration and expireDate are correctly set + var expireDate = JulianDate.addSeconds(JulianDate.now(), 5.0, new JulianDate()); + expect(JulianDate.secondsDifference(tile.expireDate, expireDate)).toEqualEpsilon(0.0, CesiumMath.EPSILON1); + expect(tile.expireDuration).toBe(5.0); + expect(tile.contentExpired).toBe(false); + expect(tile.contentReady).toBe(true); + expect(tile.contentAvailable).toBe(true); + expect(tile._expiredContent).toBeUndefined(); + + // Check statistics + expect(statistics.numberOfCommands).toBe(1); + expect(statistics.numberOfTilesTotal).toBe(1); + + // Trigger expiration to happen next frame + tile.expireDate = JulianDate.addSeconds(JulianDate.now(), -1.0, new JulianDate()); + + // Stays in the expired state until the request goes through + var originalMaxmimumRequests = RequestScheduler.maximumRequests; + RequestScheduler.maximumRequests = 0; // Artificially limit Request Scheduler so the request won't go through + scene.renderForSpecs(); + RequestScheduler.maximumRequests = originalMaxmimumRequests; + expiredContent = tile._expiredContent; + expect(tile.contentExpired).toBe(true); + expect(tile.contentAvailable).toBe(true); // Expired content now exists + expect(expiredContent).toBeDefined(); + + // Expired content renders while new content loads in + expect(statistics.numberOfCommands).toBe(1); + expect(statistics.numberOfTilesTotal).toBe(1); + + // Request goes through, now in the LOADING state + scene.renderForSpecs(); + expect(tile.contentExpired).toBe(false); + expect(tile.contentReady).toBe(false); + expect(tile.contentAvailable).toBe(true); + expect(tile._contentState).toBe(Cesium3DTileContentState.LOADING); + expect(tile._expiredContent).toBeDefined(); // Still holds onto expired content until the content state is READY + + // Check that url contains a query param with the timestamp + var url = loadWithXhr.load.calls.first().args[0]; + expect(url.indexOf('expired=') >= 0).toBe(true); + + // statistics are still the same + expect(statistics.numberOfCommands).toBe(1); + expect(statistics.numberOfTilesTotal).toBe(1); + + return pollToPromise(function() { + expect(statistics.numberOfCommands).toBe(1); // Still renders expired content + scene.renderForSpecs(); + return tile.contentReady; + }).then(function() { + scene.renderForSpecs(); + + // Expired content is destroyed + expect(tile._expiredContent).toBeUndefined(); + expect(expiredContent.isDestroyed()).toBe(true); + + // statistics for new content + expect(statistics.numberOfCommands).toBe(10); + expect(statistics.numberOfTilesTotal).toBe(1); + }); + }); + }); + + function modifySubtreeBuffer(arrayBuffer) { + var uint8Array = new Uint8Array(arrayBuffer); + var jsonString = getStringFromTypedArray(uint8Array); + var json = JSON.parse(jsonString); + json.root.children.splice(0, 1); + + jsonString = JSON.stringify(json); + var length = jsonString.length; + uint8Array = new Uint8Array(length); + for (var i = 0; i < length; i++) { + uint8Array[i] = jsonString.charCodeAt(i); + } + return uint8Array.buffer; + } + + it('tile with tileset content expires', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetSubtreeExpirationUrl).then(function(tileset) { + // Intercept the request and load a subtree with one less child. Still want to make an actual request to simulate + // real use cases instead of immediately returning a pre-created array buffer. + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + var newDeferred = when.defer(); + loadWithXhr.defaultLoad(tilesetSubtreeUrl, responseType, method, data, headers, newDeferred, overrideMimeType); + newDeferred.promise.then(function(arrayBuffer) { + deferred.resolve(modifySubtreeBuffer(arrayBuffer)); + }); + }); + + var subtreeRoot = tileset._root.children[0]; + var subtreeChildren = subtreeRoot.children[0].children; + var childrenLength = subtreeChildren.length; + var statistics = tileset._statistics; + + // Check statistics + expect(statistics.numberOfCommands).toBe(5); + expect(statistics.numberOfTilesTotal).toBe(7); + expect(statistics.numberOfTilesWithContentReady).toBe(5); + + // Trigger expiration to happen next frame + subtreeRoot.expireDate = JulianDate.addSeconds(JulianDate.now(), -1.0, new JulianDate()); + + // Listen to tile unload events + var spyUpdate = jasmine.createSpy('listener'); + tileset.tileUnload.addEventListener(spyUpdate); + + // Tiles in the subtree are removed from the cache and destroyed. + scene.renderForSpecs(); // Becomes expired + scene.renderForSpecs(); // Makes request + expect(subtreeRoot.children).toEqual([]); + for (var i = 0; i < childrenLength; ++i) { + expect(subtreeChildren[0].isDestroyed()).toBe(true); + } + expect(spyUpdate.calls.count()).toEqual(4); + + // Remove the spy so new tiles load in normally + loadWithXhr.load = loadWithXhr.defaultLoad; + + // Wait for the new tileset content to come in with one less leaf + return pollToPromise(function() { + scene.renderForSpecs(); + return subtreeRoot.contentReady && tileset.tilesLoaded; + }).then(function() { + scene.renderForSpecs(); + expect(statistics.numberOfCommands).toBe(4); + expect(statistics.numberOfTilesTotal).toBe(6); + expect(statistics.numberOfTilesWithContentReady).toBe(4); + }); + }); + }); + + it('tile expires and request fails', function() { + return Cesium3DTilesTester.loadTileset(scene, batchedExpirationUrl).then(function(tileset) { + spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { + deferred.reject(); + }); + var tile = tileset._root; + var statistics = tileset._statistics; + + // Trigger expiration to happen next frame + tile.expireDate = JulianDate.addSeconds(JulianDate.now(), -1.0, new JulianDate()); + + // After update the tile is expired + scene.renderForSpecs(); + + // Make request (it will fail) + scene.renderForSpecs(); + + // Render scene + scene.renderForSpecs(); + expect(tile._contentState).toBe(Cesium3DTileContentState.FAILED); + expect(statistics.numberOfCommands).toBe(0); + expect(statistics.numberOfTilesTotal).toBe(1); + }); + }); + + it('tile expiration date', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function(tileset) { + var tile = tileset._root; + + // Trigger expiration to happen next frame + tile.expireDate = JulianDate.addSeconds(JulianDate.now(), -1.0, new JulianDate()); + + // Stays in the expired state until the request goes through + scene.renderForSpecs(); + expect(tile.contentExpired).toBe(true); + + return pollToPromise(function() { + scene.renderForSpecs(); + return tile.contentReady; + }).then(function() { + scene.renderForSpecs(); + expect(tile._expiredContent).toBeUndefined(); + expect(tile.expireDate).toBeUndefined(); + }); + }); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/Composite3DTileContentSpec.js b/Specs/Scene/Composite3DTileContentSpec.js new file mode 100644 index 000000000000..6c0111372c8a --- /dev/null +++ b/Specs/Scene/Composite3DTileContentSpec.js @@ -0,0 +1,129 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Composite3DTileContent', + 'Core/Cartesian3', + 'Core/Color', + 'Core/HeadingPitchRange', + 'Specs/Cesium3DTilesTester', + 'Specs/createScene' + ], function( + Composite3DTileContent, + Cartesian3, + Color, + HeadingPitchRange, + Cesium3DTilesTester, + createScene) { + 'use strict'; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + var compositeUrl = './Data/Cesium3DTiles/Composite/Composite/'; + var compositeOfComposite = './Data/Cesium3DTiles/Composite/CompositeOfComposite/'; + + beforeAll(function() { + scene = createScene(); + // One item in each data set is always located in the center, so point the camera there + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 30.0)); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + function expectRenderComposite(tileset) { + expect(scene).toPickAndCall(function(result) { + // Pick a building + var pickedBuilding = result; + expect(pickedBuilding).toBeDefined(); + + // Change the color of the picked building to yellow + pickedBuilding.color = Color.clone(Color.YELLOW, pickedBuilding.color); + + // Expect the pixel color to be some shade of yellow + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Both a building and instance are located at the center, hide the building and pick the instance + pickedBuilding.show = false; + + var pickedInstance; + expect(scene).toPickAndCall(function(result) { + pickedInstance = result; + expect(pickedInstance).toBeDefined(); + expect(pickedInstance).not.toEqual(pickedBuilding); + }); + + // Change the color of the picked instance to green + pickedInstance.color = Color.clone(Color.GREEN, pickedInstance.color); + + // Expect the pixel color to be some shade of green + Cesium3DTilesTester.expectRender(scene, tileset, function(rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + + // Hide the instance, and expect the render to be blank + pickedInstance.show = false; + Cesium3DTilesTester.expectRenderBlank(scene, tileset); + }); + } + + it('throws with invalid version', function() { + var arrayBuffer = Cesium3DTilesTester.generateCompositeTileBuffer({ + version : 2 + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'cmpt'); + }); + + it('throws with invalid inner tile content type', function() { + var arrayBuffer = Cesium3DTilesTester.generateCompositeTileBuffer({ + tiles : [Cesium3DTilesTester.generateInstancedTileBuffer({ + magic : [120, 120, 120, 120] + })] + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'cmpt'); + }); + + it('resolves readyPromise', function() { + return Cesium3DTilesTester.resolvesReadyPromise(scene, compositeUrl); + }); + + it('rejects readyPromise on error', function() { + // Try loading a composite tile with an instanced tile that has an invalid url. + // Expect promise to be rejected in Model, ModelInstanceCollection, + // Instanced3DModel3DTileContent, and Composite3DTileContent. + var arrayBuffer = Cesium3DTilesTester.generateCompositeTileBuffer({ + tiles : [Cesium3DTilesTester.generateInstancedTileBuffer({ + gltfFormat : 0, + gltfUri : 'invalid' + })] + }); + return Cesium3DTilesTester.rejectsReadyPromiseOnError(scene, arrayBuffer, 'cmpt'); + }); + + it('renders composite', function() { + return Cesium3DTilesTester.loadTileset(scene, compositeUrl).then(expectRenderComposite); + }); + + it('renders composite of composite', function() { + return Cesium3DTilesTester.loadTileset(scene, compositeOfComposite).then(expectRenderComposite); + }); + + it('destroys', function() { + return Cesium3DTilesTester.tileDestroys(scene, compositeUrl); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/ConditionsExpressionSpec.js b/Specs/Scene/ConditionsExpressionSpec.js new file mode 100644 index 000000000000..db321cc8444c --- /dev/null +++ b/Specs/Scene/ConditionsExpressionSpec.js @@ -0,0 +1,123 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/ConditionsExpression', + 'Core/Cartesian4', + 'Core/Color' + ], function( + ConditionsExpression, + Cartesian4, + Color) { + 'use strict'; + + var frameState = {}; + + function MockFeature(value) { + this._value = value; + } + + MockFeature.prototype.getProperty = function() { + return this._value; + }; + + var jsonExp = { + conditions : [ + ['${Height} > 100', 'color("blue")'], + ['${Height} > 50', 'color("red")'], + ['true', 'color("lime")'] + ] + }; + + var defines = { + halfHeight: '${Height}/2', + quarterHeight: '${Height}/4' + }; + + var jsonExpWithDefines = { + conditions : [ + ['${halfHeight} > 50 && ${halfHeight} < 100', 'color("blue")'], + ['${quarterHeight} > 50 && ${quarterHeight} < 52', 'color("red")'], + ['true', 'color("lime")'] + ] + }; + + it('constructs', function() { + var expression = new ConditionsExpression(jsonExp); + expect(expression.conditionsExpression).toEqual(jsonExp); + }); + + it('evaluates conditional', function() { + var expression = new ConditionsExpression(jsonExp); + expect(expression.evaluateColor(frameState, new MockFeature(101))).toEqual(Color.BLUE); + expect(expression.evaluateColor(frameState, new MockFeature(52))).toEqual(Color.RED); + expect(expression.evaluateColor(frameState, new MockFeature(3))).toEqual(Color.LIME); + }); + + it('evaluates conditional with defines', function() { + var expression = new ConditionsExpression(jsonExpWithDefines, defines); + expect(expression.evaluateColor(frameState, new MockFeature(101))).toEqual(Color.BLUE); + expect(expression.evaluateColor(frameState, new MockFeature(52))).toEqual(Color.LIME); + expect(expression.evaluateColor(frameState, new MockFeature(3))).toEqual(Color.LIME); + }); + + it('evaluate takes result argument', function() { + var result = new Cartesian4(); + var expression = new ConditionsExpression(jsonExpWithDefines, defines, result); + var value = expression.evaluate(frameState, new MockFeature(101), result); + expect(value).toEqual(new Cartesian4(0.0, 0.0, 1.0, 1.0)); + expect(value).toBe(result); + }); + + it('evaluate takes a color result argument', function() { + var result = new Color(); + var expression = new ConditionsExpression(jsonExpWithDefines, defines, result); + var value = expression.evaluate(frameState, new MockFeature(101), result); + expect(value).toEqual(Color.BLUE); + expect(value).toBe(result); + }); + + it('constructs and evaluates empty conditional', function() { + var expression = new ConditionsExpression({ + "conditions" : [] + }); + expect(expression._conditions).toEqual([]); + expect(expression.evaluate(frameState, new MockFeature(101))).toEqual(undefined); + expect(expression.evaluate(frameState, new MockFeature(52))).toEqual(undefined); + expect(expression.evaluate(frameState, new MockFeature(3))).toEqual(undefined); + }); + + it('constructs and evaluates empty', function() { + var expression = new ConditionsExpression([]); + expect(expression._conditions).toEqual(undefined); + expect(expression.evaluate(frameState, new MockFeature(101))).toEqual(undefined); + expect(expression.evaluate(frameState, new MockFeature(52))).toEqual(undefined); + expect(expression.evaluate(frameState, new MockFeature(3))).toEqual(undefined); + }); + + it('gets shader function', function() { + var expression = new ConditionsExpression(jsonExp); + var shaderFunction = expression.getShaderFunction('getColor', '', {}, 'vec4'); + var expected = 'vec4 getColor() \n' + + '{ \n' + + ' if ((Height > 100.0)) \n' + + ' { \n' + + ' return vec4(vec3(0.0, 0.0, 1.0), 1.0); \n' + + ' } \n' + + ' else if ((Height > 50.0)) \n' + + ' { \n' + + ' return vec4(vec3(1.0, 0.0, 0.0), 1.0); \n' + + ' } \n' + + ' else if (true) \n' + + ' { \n' + + ' return vec4(vec3(0.0, 1.0, 0.0), 1.0); \n' + + ' } \n' + + ' return vec4(1.0); \n' + + '} \n'; + expect(shaderFunction).toEqual(expected); + }); + + it('return undefined shader function when there are no conditions', function() { + var expression = new ConditionsExpression([]); + var shaderFunction = expression.getShaderFunction('getColor', '', {}, 'vec4'); + expect(shaderFunction).toBeUndefined(); + }); +}); diff --git a/Specs/Scene/Empty3DTileContentSpec.js b/Specs/Scene/Empty3DTileContentSpec.js new file mode 100644 index 000000000000..86dbf32b9438 --- /dev/null +++ b/Specs/Scene/Empty3DTileContentSpec.js @@ -0,0 +1,32 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Empty3DTileContent' + ], function( + Empty3DTileContent) { + 'use strict'; + + it('destroys', function() { + var content = new Empty3DTileContent(); + expect(content.isDestroyed()).toEqual(false); + content.destroy(); + expect(content.isDestroyed()).toEqual(true); + }); + + it('gets properties', function() { + var mockTileset = {}; + var mockTile = {}; + var content = new Empty3DTileContent(mockTileset, mockTile); + expect(content.featuresLength).toBe(0); + expect(content.pointsLength).toBe(0); + expect(content.trianglesLength).toBe(0); + expect(content.geometryByteLength).toBe(0); + expect(content.texturesByteLength).toBe(0); + expect(content.batchTableByteLength).toBe(0); + expect(content.innerContents).toBeUndefined(); + expect(content.readyPromise).toBeUndefined(); + expect(content.tileset).toBe(mockTileset); + expect(content.tile).toBe(mockTile); + expect(content.url).toBeUndefined(); + expect(content.batchTable).toBeUndefined(); + }); +}); diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js new file mode 100644 index 000000000000..e67410f727cf --- /dev/null +++ b/Specs/Scene/ExpressionSpec.js @@ -0,0 +1,3634 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Expression', + 'Core/Cartesian2', + 'Core/Cartesian3', + 'Core/Cartesian4', + 'Core/Color', + 'Core/Math', + 'Scene/ExpressionNodeType' + ], function( + Expression, + Cartesian2, + Cartesian3, + Cartesian4, + Color, + CesiumMath, + ExpressionNodeType) { + 'use strict'; + + var frameState = {}; + + function MockFeature() { + this._properties = {}; + this._className = undefined; + this._inheritedClassName = undefined; + this.content = { + tileset : { + timeSinceLoad : 0.0 + } + }; + } + + MockFeature.prototype.addProperty = function(name, value) { + this._properties[name] = value; + }; + + MockFeature.prototype.getProperty = function(name) { + return this._properties[name]; + }; + + MockFeature.prototype.setClass = function(className) { + this._className = className; + }; + + MockFeature.prototype.setInheritedClass = function(className) { + this._inheritedClassName = className; + }; + + MockFeature.prototype.isExactClass = function(className) { + return this._className === className; + }; + + MockFeature.prototype.isClass = function(className) { + return (this._className === className) || (this._inheritedClassName === className); + }; + + MockFeature.prototype.getExactClassName = function() { + return this._className; + }; + + it('parses backslashes', function() { + var expression = new Expression('"\\he\\\\\\ll\\\\o"'); + expect(expression.evaluate(frameState, undefined)).toEqual('\\he\\\\\\ll\\\\o'); + }); + + it('evaluates variable', function() { + var feature = new MockFeature(); + feature.addProperty('height', 10); + feature.addProperty('width', 5); + feature.addProperty('string', 'hello'); + feature.addProperty('boolean', true); + feature.addProperty('vector', Cartesian3.UNIT_X); + feature.addProperty('null', null); + feature.addProperty('undefined', undefined); + + var expression = new Expression('${height}'); + expect(expression.evaluate(frameState, feature)).toEqual(10); + + expression = new Expression('\'${height}\''); + expect(expression.evaluate(frameState, feature)).toEqual('10'); + + expression = new Expression('${height}/${width}'); + expect(expression.evaluate(frameState, feature)).toEqual(2); + + expression = new Expression('${string}'); + expect(expression.evaluate(frameState, feature)).toEqual('hello'); + + expression = new Expression('\'replace ${string}\''); + expect(expression.evaluate(frameState, feature)).toEqual('replace hello'); + + expression = new Expression('\'replace ${string} multiple ${height}\''); + expect(expression.evaluate(frameState, feature)).toEqual('replace hello multiple 10'); + + expression = new Expression('"replace ${string}"'); + expect(expression.evaluate(frameState, feature)).toEqual('replace hello'); + + expression = new Expression('\'replace ${string\''); + expect(expression.evaluate(frameState, feature)).toEqual('replace ${string'); + + expression = new Expression('${boolean}'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + + expression = new Expression('\'${boolean}\''); + expect(expression.evaluate(frameState, feature)).toEqual('true'); + + expression = new Expression('${vector}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian3.UNIT_X); + + expression = new Expression('\'${vector}\''); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian3.UNIT_X.toString()); + + expression = new Expression('${null}'); + expect(expression.evaluate(frameState, feature)).toEqual(null); + + expression = new Expression('\'${null}\''); + expect(expression.evaluate(frameState, feature)).toEqual(''); + + expression = new Expression('${undefined}'); + expect(expression.evaluate(frameState, feature)).toEqual(undefined); + + expression = new Expression('\'${undefined}\''); + expect(expression.evaluate(frameState, feature)).toEqual(''); + + expect(function() { + return new Expression('${height'); + }).toThrowRuntimeError(); + }); + + it('evaluates with defines', function() { + var defines = { + halfHeight: '${Height}/2' + }; + var feature = new MockFeature(); + feature.addProperty('Height', 10); + + var expression = new Expression('${halfHeight}', defines); + expect(expression.evaluate(frameState, feature)).toEqual(5); + }); + + it('evaluates with defines, honoring order of operations', function() { + var defines = { + value: '1 + 2' + }; + var expression = new Expression('5.0 * ${value}', defines); + expect(expression.evaluate(frameState, undefined)).toEqual(15); + }); + + it('evaluate takes result argument', function() { + var expression = new Expression('vec3(1.0)'); + var result = new Cartesian3(); + var value = expression.evaluate(frameState, undefined, result); + expect(value).toEqual(new Cartesian3(1.0, 1.0, 1.0)); + expect(value).toBe(result); + }); + + it('evaluate takes a color result argument', function() { + var expression = new Expression('color("red")'); + var result = new Color(); + var value = expression.evaluate(frameState, undefined, result); + expect(value).toEqual(Color.RED); + expect(value).toBe(result); + }); + + it('gets expressions', function() { + var expressionString = "(regExp('^Chest').test(${County})) && (${YearBuilt} >= 1970)"; + var expression = new Expression(expressionString); + expect(expression.expression).toEqual(expressionString); + }); + + it('throws on invalid expressions', function() { + expect(function() { + return new Expression(false); + }).toThrowDeveloperError(); + + expect(function() { + return new Expression(''); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('this'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2; 3;'); + }).toThrowRuntimeError(); + }); + + it('throws on unknown characters', function() { + expect(function() { + return new Expression('#'); + }).toThrowRuntimeError(); + }); + + it('throws on unmatched parenthesis', function() { + expect(function() { + return new Expression('((true)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('(true))'); + }).toThrowRuntimeError(); + }); + + it('throws on unknown identifiers', function() { + expect(function() { + return new Expression('flse'); + }).toThrowRuntimeError(); + }); + + it('throws on unknown function calls', function() { + expect(function() { + return new Expression('unknown()'); + }).toThrowRuntimeError(); + }); + + it('throws on unknown member function calls', function() { + expect(function() { + return new Expression('regExp().unknown()'); + }).toThrowRuntimeError(); + }); + + it('throws with unsupported operators', function() { + expect(function() { + return new Expression('~1'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2 | 3'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2 & 3'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2 << 3'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2 >> 3'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('2 >>> 3'); + }).toThrowRuntimeError(); + }); + + it('evaluates literal null', function() { + var expression = new Expression('null'); + expect(expression.evaluate(frameState, undefined)).toEqual(null); + }); + + it('evaluates literal undefined', function() { + var expression = new Expression('undefined'); + expect(expression.evaluate(frameState, undefined)).toEqual(undefined); + }); + + it('evaluates literal boolean', function() { + var expression = new Expression('true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('false'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('converts to literal boolean', function() { + var expression = new Expression('Boolean()'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('Boolean(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('Boolean("true")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('evaluates literal number', function() { + var expression = new Expression('1'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('0'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('NaN'); + expect(expression.evaluate(frameState, undefined)).toEqual(NaN); + + expression = new Expression('Infinity'); + expect(expression.evaluate(frameState, undefined)).toEqual(Infinity); + }); + + it('evaluates math constants', function() { + var expression = new Expression('Math.PI'); + expect(expression.evaluate(frameState, undefined)).toEqual(Math.PI); + + expression = new Expression('Math.E'); + expect(expression.evaluate(frameState, undefined)).toEqual(Math.E); + }); + + it('converts to literal number', function() { + var expression = new Expression('Number()'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('Number("1")'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('Number(true)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + }); + + it('evaluates literal string', function() { + var expression = new Expression('\'hello\''); + expect(expression.evaluate(frameState, undefined)).toEqual('hello'); + + expression = new Expression('\'Cesium\''); + expect(expression.evaluate(frameState, undefined)).toEqual('Cesium'); + + expression = new Expression('"Cesium"'); + expect(expression.evaluate(frameState, undefined)).toEqual('Cesium'); + }); + + it('converts to literal string', function() { + var expression = new Expression('String()'); + expect(expression.evaluate(frameState, undefined)).toEqual(''); + + expression = new Expression('String(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual('1'); + + expression = new Expression('String(true)'); + expect(expression.evaluate(frameState, undefined)).toEqual('true'); + }); + + it('evaluates literal color', function() { + var expression = new Expression('color(\'#ffffff\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(\'#00FFFF\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.CYAN)); + + expression = new Expression('color(\'#fff\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(\'#0FF\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.CYAN)); + + expression = new Expression('color(\'white\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(\'cyan\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.CYAN)); + + expression = new Expression('color(\'white\', 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromAlpha(Color.WHITE, 0.5))); + + expression = new Expression('rgb(255, 255, 255)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('rgb(100, 255, 190)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromBytes(100, 255, 190))); + + expression = new Expression('hsl(0, 0, 1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('hsl(1.0, 0.6, 0.7)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromHsl(1.0, 0.6, 0.7))); + + expression = new Expression('rgba(255, 255, 255, 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromAlpha(Color.WHITE, 0.5))); + + expression = new Expression('rgba(100, 255, 190, 0.25)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromBytes(100, 255, 190, 0.25 * 255))); + + expression = new Expression('hsla(0, 0, 1, 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(new Color(1.0, 1.0, 1.0, 0.5))); + + expression = new Expression('hsla(1.0, 0.6, 0.7, 0.75)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.fromHsl(1.0, 0.6, 0.7, 0.75))); + + expression = new Expression('color()'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + }); + + it('evaluates literal color with result parameter', function() { + var color = new Color(); + + var expression = new Expression('color(\'#0000ff\')'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.BLUE); + expect(color).toEqual(Color.BLUE); + + expression = new Expression('color(\'#f00\')'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.RED); + expect(color).toEqual(Color.RED); + + expression = new Expression('color(\'cyan\')'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.CYAN); + expect(color).toEqual(Color.CYAN); + + expression = new Expression('color(\'white\', 0.5)'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(new Color(1.0, 1.0, 1.0, 0.5)); + expect(color).toEqual(new Color(1.0, 1.0, 1.0, 0.5)); + + expression = new Expression('rgb(0, 0, 0)'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.BLACK); + expect(color).toEqual(Color.BLACK); + + expression = new Expression('hsl(0, 0, 1)'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.WHITE); + expect(color).toEqual(Color.WHITE); + + expression = new Expression('rgba(255, 0, 255, 0.5)'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(new Color(1.0, 0, 1.0, 0.5)); + expect(color).toEqual(new Color(1.0, 0, 1.0, 0.5)); + + expression = new Expression('hsla(0, 0, 1, 0.5)'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(new Color(1.0, 1.0, 1.0, 0.5)); + expect(color).toEqual(new Color(1.0, 1.0, 1.0, 0.5)); + + expression = new Expression('color()'); + expect(expression.evaluate(frameState, undefined, color)).toEqual(Color.WHITE); + expect(color).toEqual(Color.WHITE); + }); + + it('evaluates color with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('hex6', '#ffffff'); + feature.addProperty('hex3', '#fff'); + feature.addProperty('keyword', 'white'); + feature.addProperty('alpha', 0.2); + + var expression = new Expression('color(${hex6})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(${hex3})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(${keyword})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('color(${keyword}, ${alpha} + 0.6)'); + expect(expression.evaluate(frameState, feature).x).toEqual(1.0); + expect(expression.evaluate(frameState, feature).y).toEqual(1.0); + expect(expression.evaluate(frameState, feature).z).toEqual(1.0); + expect(expression.evaluate(frameState, feature).w).toEqual(0.8); + }); + + it('evaluates rgb with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('red', 100); + feature.addProperty('green', 200); + feature.addProperty('blue', 255); + + var expression = new Expression('rgb(${red}, ${green}, ${blue})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(100, 200, 255))); + + expression = new Expression('rgb(${red}/2, ${green}/2, ${blue})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(50, 100, 255))); + }); + + it('evaluates hsl with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('h', 0.0); + feature.addProperty('s', 0.0); + feature.addProperty('l', 1.0); + + var expression = new Expression('hsl(${h}, ${s}, ${l})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('hsl(${h} + 0.2, ${s} + 1.0, ${l} - 0.5)'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromHsl(0.2, 1.0, 0.5))); + }); + + it('evaluates rgba with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('red', 100); + feature.addProperty('green', 200); + feature.addProperty('blue', 255); + feature.addProperty('a', 0.3); + + var expression = new Expression('rgba(${red}, ${green}, ${blue}, ${a})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(100, 200, 255, 0.3*255))); + + expression = new Expression('rgba(${red}/2, ${green}/2, ${blue}, ${a} * 2)'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(50, 100, 255, 0.6*255))); + }); + + it('evaluates hsla with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('h', 0.0); + feature.addProperty('s', 0.0); + feature.addProperty('l', 1.0); + feature.addProperty('a', 1.0); + + var expression = new Expression('hsla(${h}, ${s}, ${l}, ${a})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('hsla(${h} + 0.2, ${s} + 1.0, ${l} - 0.5, ${a} / 4)'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromHsl(0.2, 1.0, 0.5, 0.25))); + }); + + it('evaluates rgba with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('red', 100); + feature.addProperty('green', 200); + feature.addProperty('blue', 255); + feature.addProperty('alpha', 0.5); + + var expression = new Expression('rgba(${red}, ${green}, ${blue}, ${alpha})'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(100, 200, 255, 0.5 * 255))); + + expression = new Expression('rgba(${red}/2, ${green}/2, ${blue}, ${alpha} + 0.1)'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.fromColor(Color.fromBytes(50, 100, 255, 0.6 * 255))); + }); + + it('color constructors throw with wrong number of arguments', function() { + expect(function() { + return new Expression('rgb(255, 255)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('hsl(1, 1)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('rgba(255, 255, 255)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('hsla(1, 1, 1)'); + }).toThrowRuntimeError(); + }); + + it('evaluates color properties (r, g, b, a)', function() { + var expression = new Expression('color(\'#ffffff\').r'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0).g'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan").b'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5).a'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties (x, y, z, w)', function() { + var expression = new Expression('color(\'#ffffff\').x'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0).y'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan").z'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5).w'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties ([0], [1], [2]. [3])', function() { + var expression = new Expression('color(\'#ffffff\')[0]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)[1]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")[2]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)[3]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties (["r"], ["g"], ["b"], ["a"])', function() { + var expression = new Expression('color(\'#ffffff\')["r"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)["g"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")["b"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)["a"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties (["x"], ["y"], ["z"], ["w"])', function() { + var expression = new Expression('color(\'#ffffff\')["x"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)["y"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")["z"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)["w"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates vec2', function() { + var expression = new Expression('vec2(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2.0, 2.0)); + + expression = new Expression('vec2(3.0, 4.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec2(3.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec3(3.0, 4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + }); + + it('throws if vec2 has invalid number of arguments', function() { + var expression = new Expression('vec2()'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec2(3.0, 4.0, 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec2(vec2(3.0, 4.0), 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if vec2 has invalid argument', function() { + var expression = new Expression('vec2("1")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates vec3', function() { + var expression = new Expression('vec3(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2.0, 2.0, 2.0)); + + expression = new Expression('vec3(3.0, 4.0, 5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec2(3.0, 4.0), 5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(3.0, vec2(4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec3(3.0, 4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + }); + + it ('throws if vec3 has invalid number of arguments', function() { + var expression = new Expression('vec3()'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec3(3.0, 4.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec3(3.0, 4.0, 5.0, 6.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec3(vec2(3.0, 4.0), vec2(5.0, 6.0))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0), 1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if vec3 has invalid argument', function() { + var expression = new Expression('vec3(1.0, "1.0", 2.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates vec4', function() { + var expression = new Expression('vec4(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 2.0, 2.0)); + + expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(vec2(3.0, 4.0), 5.0, 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(3.0, vec2(4.0, 5.0), 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(3.0, 4.0, vec2(5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(vec3(3.0, 4.0, 5.0), 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(3.0, vec3(4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + }); + + it ('throws if vec4 has invalid number of arguments', function() { + var expression = new Expression('vec4()'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec4(3.0, 4.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec4(3.0, 4.0, 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0, 7.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec4(vec3(3.0, 4.0, 5.0))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if vec4 has invalid argument', function() { + var expression = new Expression('vec4(1.0, "2.0", 3.0, 4.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates vector with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('height', 2); + feature.addProperty('width', 4); + feature.addProperty('depth', 3); + feature.addProperty('scale', 1); + + var expression = new Expression('vec4(${height}, ${width}, ${depth}, ${scale})'); + expect(expression.evaluate(frameState, feature)).toEqual(new Cartesian4(2.0, 4.0, 3.0, 1.0)); + }); + + it('evaluates expression with multiple nested vectors', function() { + var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4(1.0).w, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 1.0, 5.0)); + }); + + it('evaluates vector properties (x, y, z, w)', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).x'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).y'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).z'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).w'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties (r, g, b, a)', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).r'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).g'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).b'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).a'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties ([0], [1], [2], [3])', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[0]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[1]'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[2]'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[3]'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties (["x"], ["y"], ["z"]. ["w"])', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["x"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["y"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["z"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["w"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties (["r"], ["g"], ["b"]. ["a"])', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["r"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["g"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["b"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["a"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates unary not', function() { + var expression = new Expression('!true'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('!!true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('throws if unary not takes invalid argument', function() { + var expression = new Expression('!"true"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates unary negative', function() { + var expression = new Expression('-5'); + expect(expression.evaluate(frameState, undefined)).toEqual(-5); + + expression = new Expression('-(-5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(5); + }); + + it('throws if unary negative takes invalid argument', function() { + var expression = new Expression('-"56"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates unary positive', function() { + var expression = new Expression('+5'); + expect(expression.evaluate(frameState, undefined)).toEqual(5); + }); + + it('throws if unary positive takes invalid argument', function() { + var expression = new Expression('+"56"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary addition', function() { + var expression = new Expression('1 + 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(3); + + expression = new Expression('1 + 2 + 3 + 4'); + expect(expression.evaluate(frameState, undefined)).toEqual(10); + }); + + it('evaluates binary addition with strings', function() { + var expression = new Expression('1 + "10"'); + expect(expression.evaluate(frameState, undefined)).toEqual('110'); + + expression = new Expression('"10" + 1'); + expect(expression.evaluate(frameState, undefined)).toEqual('101'); + + expression = new Expression('"name_" + "building"'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_building'); + + expression = new Expression('"name_" + true'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_true'); + + expression = new Expression('"name_" + null'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_null'); + + expression = new Expression('"name_" + undefined'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_undefined'); + + expression = new Expression('"name_" + vec2(1.1)'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_(1.1, 1.1)'); + + expression = new Expression('"name_" + vec3(1.1)'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_(1.1, 1.1, 1.1)'); + + expression = new Expression('"name_" + vec4(1.1)'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_(1.1, 1.1, 1.1, 1.1)'); + + expression = new Expression('"name_" + regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual('name_/a/'); + }); + + it('throws if binary addition takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) + vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 + vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary subtraction', function() { + var expression = new Expression('2 - 1'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('4 - 3 - 2 - 1'); + expect(expression.evaluate(frameState, undefined)).toEqual(-2); + }); + + it('throws if binary subtraction takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) - vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 - vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('"name1" - "name2"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary multiplication', function() { + var expression = new Expression('1 * 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(2); + + expression = new Expression('1 * 2 * 3 * 4'); + expect(expression.evaluate(frameState, undefined)).toEqual(24); + }); + + it('throws if binary multiplication takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) * vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec2(1.0) * "name"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary division', function() { + var expression = new Expression('2 / 1'); + expect(expression.evaluate(frameState, undefined)).toEqual(2); + + expression = new Expression('1/2'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + + expression = new Expression('24 / -4 / 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(-3); + }); + + it('throws if binary division takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) / vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec2(1.0) / "2.0"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 / vec4(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary modulus', function() { + var expression = new Expression('2 % 1'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('6 % 4 % 3'); + expect(expression.evaluate(frameState, undefined)).toEqual(2); + }); + + it('throws if binary modulus takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) % vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('vec2(1.0) % "2.0"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 % vec4(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary equals strict', function() { + var expression = new Expression('\'hello\' === \'hello\''); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('1 === 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('false === true === false'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('1 === "1"'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('evaluates binary not equals strict', function() { + var expression = new Expression('\'hello\' !== \'hello\''); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('1 !== 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('false !== true !== false'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('1 !== "1"'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('evaluates binary less than', function() { + var expression = new Expression('2 < 3'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('2 < 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('3 < 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('throws if binary less than takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) < vec2(2.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 < vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('true < false'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('color(\'blue\') < 10'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary less than or equals', function() { + var expression = new Expression('2 <= 3'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('2 <= 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('3 <= 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('throws if binary less than or equals takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) <= vec2(2.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 <= vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 <= "5"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('true <= false'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('color(\'blue\') <= 10'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary greater than', function() { + var expression = new Expression('2 > 3'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('2 > 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('3 > 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('throws if binary greater than takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) > vec2(2.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 > vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 > "5"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('true > false'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('color(\'blue\') > 10'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates binary greater than or equals', function() { + var expression = new Expression('2 >= 3'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('2 >= 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('3 >= 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('throws if binary greater than or equals takes invalid arguments', function() { + var expression = new Expression('vec2(1.0) >= vec2(2.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 >= vec3(1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1.0 >= "5"'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('true >= false'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('color(\'blue\') >= 10'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates logical and', function() { + var expression = new Expression('false && false'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('false && true'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('true && true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('2 && color(\'red\')'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws with invalid and operands', function() { + var expression = new Expression('2 && true'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('true && color(\'red\')'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates logical or', function() { + var expression = new Expression('false || false'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('false || true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('true || true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('throws with invalid or operands', function() { + var expression = new Expression('2 || false'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('false || color(\'red\')'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates color operations', function() { + var expression = new Expression('+rgba(255, 0, 0, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.RED)); + + expression = new Expression('rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.MAGENTA)); + + expression = new Expression('rgba(0, 255, 255, 1.0) - rgba(0, 255, 0, 0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.BLUE)); + + expression = new Expression('rgba(255, 255, 255, 1.0) * rgba(255, 0, 0, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.RED)); + + expression = new Expression('rgba(255, 255, 0, 1.0) * 1.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.YELLOW)); + + expression = new Expression('1 * rgba(255, 255, 0, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.YELLOW)); + + expression = new Expression('rgba(255, 255, 255, 1.0) / rgba(255, 255, 255, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(Color.WHITE)); + + expression = new Expression('rgba(255, 255, 255, 1.0) / 2'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(new Color(0.5, 0.5, 0.5, 0.5))); + + expression = new Expression('rgba(255, 255, 255, 1.0) % rgba(255, 255, 255, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Cartesian4.fromColor(new Color(0, 0, 0, 0))); + + expression = new Expression('color(\'green\') === color(\'green\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('color(\'green\') !== color(\'green\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('evaluates vector operations', function() { + var expression = new Expression('+vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1, 2)); + + expression = new Expression('+vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1, 2, 3)); + + expression = new Expression('+vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 4)); + + expression = new Expression('-vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-1, -2)); + + expression = new Expression('-vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-1, -2, -3)); + + expression = new Expression('-vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, -4)); + + expression = new Expression('vec2(1, 2) + vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(4, 6)); + + expression = new Expression('vec3(1, 2, 3) + vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(4, 6, 8)); + + expression = new Expression('vec4(1, 2, 3, 4) + vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 8, 10)); + + expression = new Expression('vec2(1, 2) - vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-2, -2)); + + expression = new Expression('vec3(1, 2, 3) - vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-2, -2, -2)); + + expression = new Expression('vec4(1, 2, 3, 4) - vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -2, -2)); + + expression = new Expression('vec2(1, 2) * vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 8)); + + expression = new Expression('vec2(1, 2) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6)); + + expression = new Expression('3.0 * vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6)); + + expression = new Expression('vec3(1, 2, 3) * vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 8, 15)); + + expression = new Expression('vec3(1, 2, 3) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9)); + + expression = new Expression('3.0 * vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9)); + + expression = new Expression('vec4(1, 2, 3, 4) * vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 8, 15, 24)); + + expression = new Expression('vec4(1, 2, 3, 4) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12)); + + expression = new Expression('3.0 * vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12)); + + expression = new Expression('vec2(1, 2) / vec2(2, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 0.4)); + + expression = new Expression('vec2(1, 2) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 1.0)); + + expression = new Expression('vec3(1, 2, 3) / vec3(2, 5, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 0.4, 1.0)); + + expression = new Expression('vec3(1, 2, 3) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 1.0, 1.5)); + + expression = new Expression('vec4(1, 2, 3, 4) / vec4(2, 5, 3, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 2.0)); + + expression = new Expression('vec4(1, 2, 3, 4) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 1.0, 1.5, 2.0)); + + expression = new Expression('vec2(2, 3) % vec2(3, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2, 0)); + + expression = new Expression('vec3(2, 3, 4) % vec3(3, 3, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2, 0, 1)); + + expression = new Expression('vec4(2, 3, 4, 5) % vec4(3, 3, 3, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2, 0, 1, 1)); + + expression = new Expression('vec2(1, 2) === vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec3(1, 2, 3) === vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec4(1, 2, 3, 4) === vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec2(1, 2) !== vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('vec3(1, 2, 3) !== vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('vec4(1, 2, 3, 4) !== vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('evaluates color toString function', function() { + var expression = new Expression('color("red").toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 0, 0, 1)'); + + expression = new Expression('rgba(0, 0, 255, 0.5).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(0, 0, 1, 0.5)'); + }); + + it('evaluates vector toString function', function() { + var feature = new MockFeature(); + feature.addProperty('property', new Cartesian4(1, 2, 3, 4)); + + var expression = new Expression('vec2(1, 2).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2)'); + + expression = new Expression('vec3(1, 2, 3).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3)'); + + expression = new Expression('vec4(1, 2, 3, 4).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3, 4)'); + + expression = new Expression('${property}.toString()'); + expect(expression.evaluate(frameState, feature)).toEqual('(1, 2, 3, 4)'); + }); + + it('evaluates isNaN function', function() { + var expression = new Expression('isNaN()'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isNaN(NaN)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isNaN(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isNaN(Infinity)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isNaN(null)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isNaN(true)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isNaN("hello")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isNaN(color("white"))'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + }); + + it('evaluates isFinite function', function() { + var expression = new Expression('isFinite()'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isFinite(NaN)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isFinite(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isFinite(Infinity)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isFinite(null)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isFinite(true)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('isFinite("hello")'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('isFinite(color("white"))'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('evaluates isExactClass function', function() { + var feature = new MockFeature(); + feature.setClass('door'); + + var expression = new Expression('isExactClass("door")'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + + expression = new Expression('isExactClass("roof")'); + expect(expression.evaluate(frameState, feature)).toEqual(false); + }); + + it('throws if isExactClass takes an invalid number of arguments', function() { + expect(function() { + return new Expression('isExactClass()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('isExactClass("door", "roof")'); + }).toThrowRuntimeError(); + }); + + it('evaluates isClass function', function() { + var feature = new MockFeature(); + + feature.setClass('door'); + feature.setInheritedClass('building'); + + var expression = new Expression('isClass("door") && isClass("building")'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + }); + + it('throws if isClass takes an invalid number of arguments', function() { + expect(function() { + return new Expression('isClass()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('isClass("door", "building")'); + }).toThrowRuntimeError(); + }); + + it('evaluates getExactClassName function', function() { + var feature = new MockFeature(); + feature.setClass('door'); + var expression = new Expression('getExactClassName()'); + expect(expression.evaluate(frameState, feature)).toEqual('door'); + }); + + it('throws if getExactClassName takes an invalid number of arguments', function() { + expect(function() { + return new Expression('getExactClassName("door")'); + }).toThrowRuntimeError(); + }); + + it('throws if built-in unary function is given an invalid argument', function() { + // Argument must be a number or vector + var expression = new Expression('abs("-1")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates abs function', function() { + var expression = new Expression('abs(-1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('abs(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('abs(vec2(-1.0, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, 1.0)); + + expression = new Expression('abs(vec3(-1.0, 1.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1.0, 1.0, 0.0)); + + expression = new Expression('abs(vec4(-1.0, 1.0, 0.0, -1.2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 1.0, 0.0, 1.2)); + }); + + it('throws if abs function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('abs()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('abs(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates cos function', function() { + var expression = new Expression('cos(0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('cos(vec2(0, Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(1.0, -1.0), CesiumMath.EPSILON7); + + expression = new Expression('cos(vec3(0, Math.PI, -Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(1.0, -1.0, -1.0), CesiumMath.EPSILON7); + + expression = new Expression('cos(vec4(0, Math.PI, -Math.PI, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(1.0, -1.0, -1.0, 1.0), CesiumMath.EPSILON7); + }); + + it('throws if cos function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('cos()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('cos(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates sin function', function() { + var expression = new Expression('sin(0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('sin(vec2(0, Math.PI/2))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(0.0, 1.0), CesiumMath.EPSILON7); + + expression = new Expression('sin(vec3(0, Math.PI/2, -Math.PI/2))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(0.0, 1.0, -1.0), CesiumMath.EPSILON7); + + expression = new Expression('sin(vec4(0, Math.PI/2, -Math.PI/2, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(0.0, 1.0, -1.0, 0.0), CesiumMath.EPSILON7); + }); + + it('throws if sin function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('sin()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('sin(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates tan function', function() { + var expression = new Expression('tan(0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('tan(vec2(0, Math.PI/4))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(0.0, 1.0), CesiumMath.EPSILON7); + + expression = new Expression('tan(vec3(0, Math.PI/4, Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(0.0, 1.0, 0.0), CesiumMath.EPSILON7); + + expression = new Expression('tan(vec4(0, Math.PI/4, Math.PI, -Math.PI/4))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(0.0, 1.0, 0.0, -1.0), CesiumMath.EPSILON7); + }); + + it('throws if tan function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('tan()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('tan(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates acos function', function() { + var expression = new Expression('acos(1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('acos(vec2(1, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(0.0, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + + expression = new Expression('acos(vec3(1, 0, 1))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(0.0, CesiumMath.PI_OVER_TWO, 0.0, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + + expression = new Expression('acos(vec4(1, 0, 1, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(0.0, CesiumMath.PI_OVER_TWO, 0.0, CesiumMath.PI_OVER_TWO, 0.0), CesiumMath.EPSILON7); + }); + + it('throws if acos function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('acos()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('acos(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates asin function', function() { + var expression = new Expression('asin(0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('asin(vec2(0, 1))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(0.0, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + + expression = new Expression('asin(vec3(0, 1, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(0.0, CesiumMath.PI_OVER_TWO, 0.0, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + + expression = new Expression('asin(vec4(0, 1, 0, 1))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(0.0, CesiumMath.PI_OVER_TWO, 0.0, CesiumMath.PI_OVER_TWO, 0.0), CesiumMath.EPSILON7); + }); + + it('throws if asin function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('asin()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('asin(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates atan function', function() { + var expression = new Expression('atan(0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('atan(vec2(0, 1))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(0.0, CesiumMath.PI_OVER_FOUR), CesiumMath.EPSILON7); + + expression = new Expression('atan(vec3(0, 1, 0))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(0.0, CesiumMath.PI_OVER_FOUR, 0.0, CesiumMath.PI_OVER_FOUR), CesiumMath.EPSILON7); + + expression = new Expression('atan(vec4(0, 1, 0, 1))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(0.0, CesiumMath.PI_OVER_FOUR, 0.0, CesiumMath.PI_OVER_FOUR, 0.0), CesiumMath.EPSILON7); + }); + + it('throws if atan function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('atan()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('atan(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates radians function', function() { + var expression = new Expression('radians(180)'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(Math.PI, CesiumMath.EPSILON10); + + expression = new Expression('radians(vec2(180, 90))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(Math.PI, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + + expression = new Expression('radians(vec3(180, 90, 180))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(Math.PI, CesiumMath.PI_OVER_TWO, Math.PI), CesiumMath.EPSILON7); + + expression = new Expression('radians(vec4(180, 90, 180, 90))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(Math.PI, CesiumMath.PI_OVER_TWO, Math.PI, CesiumMath.PI_OVER_TWO), CesiumMath.EPSILON7); + }); + + it('throws if radians function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('radians()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('radians(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates degrees function', function() { + var expression = new Expression('degrees(2 * Math.PI)'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(360, CesiumMath.EPSILON10); + + expression = new Expression('degrees(vec2(2 * Math.PI, Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian2(360, 180), CesiumMath.EPSILON7); + + expression = new Expression('degrees(vec3(2 * Math.PI, Math.PI, 2 * Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(360, 180, 360), CesiumMath.EPSILON7); + + expression = new Expression('degrees(vec4(2 * Math.PI, Math.PI, 2 * Math.PI, Math.PI))'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian4(360, 180, 360, 180), CesiumMath.EPSILON7); + }); + + it('throws if degrees function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('degrees()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('degrees(1, 2)'); + }); + }); + + it('evaluates sqrt function', function() { + var expression = new Expression('sqrt(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('sqrt(4.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('sqrt(-1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(NaN); + + expression = new Expression('sqrt(vec2(1.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, 2.0)); + + expression = new Expression('sqrt(vec3(1.0, 4.0, 9.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1.0, 2.0, 3.0)); + + expression = new Expression('sqrt(vec4(1.0, 4.0, 9.0, 16.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 4.0)); + }); + + it('throws if sqrt function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('sqrt()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('sqrt(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates sign function', function() { + var expression = new Expression('sign(5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('sign(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('sign(-5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(-1.0); + + expression = new Expression('sign(vec2(5.0, -5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, -1.0)); + + expression = new Expression('sign(vec3(5.0, -5.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1.0, -1.0, 0.0)); + + expression = new Expression('sign(vec4(5.0, -5.0, 0.0, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, -1.0, 0.0, 1.0)); + }); + + it('throws if sign function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('sign()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('sign(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates floor function', function() { + var expression = new Expression('floor(5.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(5.0); + + expression = new Expression('floor(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('floor(-1.2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(-2.0); + + expression = new Expression('floor(vec2(5.5, -1.2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(5.0, -2.0)); + + expression = new Expression('floor(vec3(5.5, -1.2, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(5.0, -2.0, 0.0)); + + expression = new Expression('floor(vec4(5.5, -1.2, 0.0, -2.9))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(5.0, -2.0, 0.0, -3.0)); + }); + + it('throws if floor function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('floor()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('floor(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates ceil function', function() { + var expression = new Expression('ceil(5.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(6.0); + + expression = new Expression('ceil(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('ceil(-1.2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(-1.0); + + expression = new Expression('ceil(vec2(5.5, -1.2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(6.0, -1.0)); + + expression = new Expression('ceil(vec3(5.5, -1.2, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(6.0, -1.0, 0.0)); + + expression = new Expression('ceil(vec4(5.5, -1.2, 0.0, -2.9))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(6.0, -1.0, 0.0, -2.0)); + }); + + it('throws if ceil function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('ceil()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('ceil(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates round function', function() { + var expression = new Expression('round(5.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(6); + + expression = new Expression('round(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0); + + expression = new Expression('round(1.2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('round(vec2(5.5, -1.2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(6.0, -1.0)); + + expression = new Expression('round(vec3(5.5, -1.2, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(6.0, -1.0, 0.0)); + + expression = new Expression('round(vec4(5.5, -1.2, 0.0, -2.9))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(6.0, -1.0, 0.0, -3.0)); + }); + + it('throws if round function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('round()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('round(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates exp function', function() { + var expression = new Expression('exp(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Math.E); + + expression = new Expression('exp(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('exp(vec2(1.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(Math.E, 1.0)); + + expression = new Expression('exp(vec3(1.0, 0.0, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(Math.E, 1.0, Math.E)); + + expression = new Expression('exp(vec4(1.0, 0.0, 1.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(Math.E, 1.0, Math.E, 1.0)); + }); + + it('throws if exp function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('exp()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('exp(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates exp2 function', function() { + var expression = new Expression('exp2(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('exp2(0.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('exp2(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + + expression = new Expression('exp2(vec2(1.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2.0, 1.0)); + + expression = new Expression('exp2(vec3(1.0, 0.0, 2.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2.0, 1.0, 4.0)); + + expression = new Expression('exp2(vec4(1.0, 0.0, 2.0, 3.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 1.0, 4.0, 8.0)); + }); + + it('throws if exp2 function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('exp2()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('exp2(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates log function', function() { + var expression = new Expression('log(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('log(10.0)'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(2.302585092994046, CesiumMath.EPSILON7); + + expression = new Expression('log(vec2(1.0, Math.E))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.0, 1.0)); + + expression = new Expression('log(vec3(1.0, Math.E, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.0, 1.0, 0.0)); + + expression = new Expression('log(vec4(1.0, Math.E, 1.0, Math.E))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 1.0, 0.0, 1.0)); + }); + + it('throws if log function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('log()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('log(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates log2 function', function() { + var expression = new Expression('log2(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('log2(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('log2(4.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('log2(vec2(1.0, 2.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.0, 1.0)); + + expression = new Expression('log2(vec3(1.0, 2.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.0, 1.0, 2.0)); + + expression = new Expression('log2(vec4(1.0, 2.0, 4.0, 8.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 1.0, 2.0, 3.0)); + }); + + it('throws if log2 function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('log2()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('log2(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates fract function', function() { + var expression = new Expression('fract(1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('fract(2.25)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.25); + + expression = new Expression('fract(-2.25)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.75); + + expression = new Expression('fract(vec2(1.0, 2.25))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.0, 0.25)); + + expression = new Expression('fract(vec3(1.0, 2.25, -2.25))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.0, 0.25, 0.75)); + + expression = new Expression('fract(vec4(1.0, 2.25, -2.25, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.25, 0.75, 0.0)); + }); + + it('throws if fract function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('fract()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('fract(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates length function', function() { + var expression = new Expression('length(-3.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('length(vec2(-3.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(5.0); + + expression = new Expression('length(vec3(2.0, 3.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(7.0); + + expression = new Expression('length(vec4(2.0, 4.0, 7.0, 10.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(13.0); + }); + + it('throws if length function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('length()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('length(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates normalize function', function() { + var expression = new Expression('normalize(5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('normalize(vec2(3.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.6, 0.8)); + + expression = new Expression('normalize(vec3(2.0, 3.0, -4.0))'); + var length = Math.sqrt(2 * 2 + 3 * 3 + 4 * 4); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(new Cartesian3(2.0 / length, 3.0 / length, -4.0 / length), CesiumMath.EPSILON10); + + expression = new Expression('normalize(vec4(-2.0, 3.0, -4.0, 5.0))'); + length = Math.sqrt(2 * 2 + 3 * 3 + 4 * 4 + 5 * 5); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2.0 / length, 3.0 / length, -4.0 / length, 5.0/length), CesiumMath.EPSILON10); + }); + + it('throws if normalize function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('fract()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('fract(1, 2)'); + }).toThrowRuntimeError(); + }); + + it('evaluates clamp function', function() { + var expression = new Expression('clamp(50.0, 0.0, 100.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(50.0); + + expression = new Expression('clamp(50.0, 0.0, 25.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(25.0); + + expression = new Expression('clamp(50.0, 75.0, 100.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(75.0); + + expression = new Expression('clamp(vec2(50.0,50.0), vec2(0.0,75.0), 100.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(50.0, 75.0)); + + expression = new Expression('clamp(vec2(50.0,50.0), vec2(0.0,75.0), vec2(25.0,100.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(25.0, 75.0)); + + expression = new Expression('clamp(vec3(50.0, 50.0, 50.0), vec3(0.0, 0.0, 75.0), vec3(100.0, 25.0, 100.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(50.0, 25.0, 75.0)); + + expression = new Expression('clamp(vec4(50.0, 50.0, 50.0, 100.0), vec4(0.0, 0.0, 75.0, 75.0), vec4(100.0, 25.0, 100.0, 85.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(50.0, 25.0, 75.0, 85.0)); + }); + + it('throws if clamp function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('clamp()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('clamp(1)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('clamp(1, 2)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('clamp(1, 2, 3, 4)'); + }).toThrowRuntimeError(); + }); + + it('throws if clamp function takes mismatching types', function() { + var expression = new Expression('clamp(0.0,vec2(0,1),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('clamp(vec2(0,1),vec3(0,1,2),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('clamp(vec2(0,1),vec2(0,1), vec3(1,2,3))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates mix function', function() { + var expression = new Expression('mix(0.0, 2.0, 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('mix(vec2(0.0,1.0), vec2(2.0,3.0), 0.5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, 2.0)); + + expression = new Expression('mix(vec2(0.0,1.0), vec2(2.0,3.0), vec2(0.5,4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, 9.0)); + + expression = new Expression('mix(vec3(0.0,1.0,2.0), vec3(2.0,3.0,4.0), vec3(0.5,4.0,5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1.0, 9.0, 12.0)); + + expression = new Expression('mix(vec4(0.0,1.0,2.0,1.5), vec4(2.0,3.0,4.0,2.5), vec4(0.5,4.0,5.0,3.5))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 9.0, 12.0, 5.0)); + }); + + it('throws if mix function takes mismatching types', function() { + var expression = new Expression('mix(0.0,vec2(0,1),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('mix(vec2(0,1),vec3(0,1,2),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('mix(vec2(0,1),vec2(0,1), vec3(1,2,3))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if mix function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('mix()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('mix(1)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('mix(1, 2)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('mix(1, 2, 3, 4)'); + }).toThrowRuntimeError(); + }); + + it('evaluates atan2 function', function() { + var expression = new Expression('atan2(0,1)'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(0.0, CesiumMath.EPSILON10); + + expression = new Expression('atan2(1,0)'); + expect(expression.evaluate(frameState, undefined)).toEqualEpsilon(0.5 * Math.PI, CesiumMath.EPSILON10); + + expression = new Expression('atan2(vec2(0,1),vec2(1,0))'); + expect(expression.evaluate(frameState, undefined)) + .toEqualEpsilon(new Cartesian2(0.0, 0.5 * Math.PI), CesiumMath.EPSILON10); + + expression = new Expression('atan2(vec3(0,1,0.5),vec3(1,0,0.5))'); + expect(expression.evaluate(frameState, undefined)) + .toEqualEpsilon(new Cartesian3(0.0, 0.5 * Math.PI, 0.25 * Math.PI), CesiumMath.EPSILON10); + + expression = new Expression('atan2(vec4(0,1,0.5,1),vec4(1,0,0.5,0))'); + expect(expression.evaluate(frameState, undefined)) + .toEqualEpsilon(new Cartesian4(0.0, 0.5 * Math.PI, 0.25 * Math.PI, 0.5 * Math.PI), CesiumMath.EPSILON10); + }); + + it('throws if atan2 function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('atan2(0.0)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('atan2(1, 2, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if atan2 function takes mismatching types', function() { + var expression = new Expression('atan2(0.0,vec2(0,1))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('atan2(vec2(0,1),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('atan2(vec2(0,1),vec3(0,1,2))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates pow function', function() { + var expression = new Expression('pow(5,0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('pow(4,2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(16.0); + + expression = new Expression('pow(vec2(5,4),vec2(0,2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1.0, 16.0)); + + expression = new Expression('pow(vec3(5,4,3),vec3(0,2,3))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1.0, 16.0, 27.0)); + + expression = new Expression('pow(vec4(5,4,3,2),vec4(0,2,3,5))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 16.0, 27.0, 32.0)); + }); + + it('throws if pow function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('pow(0.0)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('pow(1, 2, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if pow function takes mismatching types', function() { + var expression = new Expression('pow(0.0, vec2(0,1))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('pow(vec2(0,1),0.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('pow(vec2(0,1),vec3(0,1,2))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates min function', function() { + var expression = new Expression('min(0,1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('min(-1,0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(-1.0); + + expression = new Expression('min(vec2(-1,1),0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-1.0, 0)); + + expression = new Expression('min(vec2(-1,2),vec2(0,1))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-1.0, 1.0)); + + expression = new Expression('min(vec3(-1,2,1),vec3(0,1,2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-1.0, 1.0, 1.0)); + + expression = new Expression('min(vec4(-1,2,1,4),vec4(0,1,2,3))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1.0, 1.0, 1.0, 3.0)); + }); + + it('throws if min function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('min(0.0)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('min(1, 2, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if min function takes mismatching types', function() { + var expression = new Expression('min(0.0, vec2(0,1))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('min(vec2(0,1),vec3(0,1,2))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates max function', function() { + var expression = new Expression('max(0,1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('max(-1,0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.0); + + expression = new Expression('max(vec2(-1,1),0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0, 1.0)); + + expression = new Expression('max(vec2(-1,2),vec2(0,1))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0, 2.0)); + + expression = new Expression('max(vec3(-1,2,1),vec3(0,1,2))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0, 2.0, 2.0)); + + expression = new Expression('max(vec4(-1,2,1,4),vec4(0,1,2,3))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0, 2.0, 2.0, 4.0)); + }); + + it('throws if max function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('max(0.0)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('max(1, 2, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if max function takes mismatching types', function() { + var expression = new Expression('max(0.0, vec2(0,1))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('max(vec2(0,1),vec3(0,1,2))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates the distance function', function() { + var expression = new Expression('distance(0, 1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('distance(vec2(1.0, 0.0), vec2(0.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('distance(vec3(3.0, 2.0, 1.0), vec3(1.0, 0.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('distance(vec4(5.0, 5.0, 5.0, 5.0), vec4(0.0, 0.0, 0.0, 0.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(10.0); + }); + + it('throws if distance function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('distance(0.0)'); + }) .toThrowRuntimeError(); + + expect(function() { + return new Expression('distance(1, 3, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if distance function takes mismatching types of arguments', function() { + expect(function() { + return new Expression('distance(1, vec2(3.0, 2.0)').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('distance(vec4(5.0, 2.0, 3.0, 1.0), vec3(4.0, 4.0, 4.0))').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates the dot function', function() { + var expression = new Expression('dot(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('dot(vec2(1.0, 1.0), vec2(2.0, 2.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + + expression = new Expression('dot(vec3(1.0, 2.0, 3.0), vec3(2.0, 2.0, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(9.0); + + expression = new Expression('dot(vec4(5.0, 5.0, 2.0, 3.0), vec4(1.0, 2.0, 1.0, 1.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(20.0); + }); + + it('throws if dot function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('dot(0.0)'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('dot(1, 3, 0)'); + }).toThrowRuntimeError(); + }); + + it('throws if dot function takes mismatching types of arguments', function() { + expect(function() { + return new Expression('dot(1, vec2(3.0, 2.0)').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('dot(vec4(5.0, 2.0, 3.0, 1.0), vec3(4.0, 4.0, 4.0))').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates the cross function', function() { + var expression = new Expression('cross(vec3(1.0, 1.0, 1.0), vec3(2.0, 2.0, 2.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.0, 0.0, 0.0)); + + expression = new Expression('cross(vec3(-1.0, -1.0, -1.0), vec3(0.0, -2.0, -5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, -5.0, 2.0)); + + expression = new Expression('cross(vec3(5.0, -2.0, 1.0), vec3(-2.0, -6.0, -8.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(22.0, 38.0, -34.0)); + }); + + it('throws if cross function takes an invalid number of arguments', function() { + expect(function() { + return new Expression('cross(vec3(0.0, 0.0, 0.0))'); + }) .toThrowRuntimeError(); + + expect(function() { + return new Expression('cross(vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0), vec3(2.0, 2.0, 2.0))'); + }).toThrowRuntimeError(); + }); + + it('throws if cross function does not take vec3 arguments', function() { + expect(function() { + return new Expression('cross(vec2(1.0, 2.0), vec2(3.0, 2.0)').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('cross(vec4(5.0, 2.0, 3.0, 1.0), vec3(4.0, 4.0, 4.0))').evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates ternary conditional', function() { + var expression = new Expression('true ? "first" : "second"'); + expect(expression.evaluate(frameState, undefined)).toEqual('first'); + + expression = new Expression('false ? "first" : "second"'); + expect(expression.evaluate(frameState, undefined)).toEqual('second'); + + expression = new Expression('(!(1 + 2 > 3)) ? (2 > 1 ? 1 + 1 : 0) : (2 > 1 ? -1 + -1 : 0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(2); + }); + + it('evaluates member expression with dot', function() { + var feature = new MockFeature(); + feature.addProperty('height', 10); + feature.addProperty('width', 5); + feature.addProperty('string', 'hello'); + feature.addProperty('boolean', true); + feature.addProperty('vector', Cartesian4.UNIT_X); + feature.addProperty('vector.x', 'something else'); + feature.addProperty('feature.vector', Cartesian4.UNIT_Y); + feature.addProperty('feature', { + vector : Cartesian4.UNIT_Z + }); + feature.addProperty('null', null); + feature.addProperty('undefined', undefined); + feature.addProperty('address', { + "street" : "Example Street", + "city" : "Example City" + }); + + var expression = new Expression('${vector.x}'); + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + + expression = new Expression('${vector.z}'); + expect(expression.evaluate(frameState, feature)).toEqual(0.0); + + expression = new Expression('${height.z}'); + expect(expression.evaluate(frameState, feature)).toEqual(undefined); + + expression = new Expression('${undefined.z}'); + expect(expression.evaluate(frameState, feature)).toEqual(undefined); + + expression = new Expression('${feature}'); + expect(expression.evaluate(frameState, feature)).toEqual({ + vector : Cartesian4.UNIT_Z + }); + + expression = new Expression('${feature.vector}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_X); + + expression = new Expression('${feature.feature.vector}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Z); + + expression = new Expression('${feature.vector.x}'); + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + + expression = new Expression('${address.street}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example Street"); + + expression = new Expression('${address.city}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example City"); + }); + + it('evaluates member expression with brackets', function() { + var feature = new MockFeature(); + feature.addProperty('height', 10); + feature.addProperty('width', 5); + feature.addProperty('string', 'hello'); + feature.addProperty('boolean', true); + feature.addProperty('vector', Cartesian4.UNIT_X); + feature.addProperty('vector.x', 'something else'); + feature.addProperty('feature.vector', Cartesian4.UNIT_Y); + feature.addProperty('feature', { + vector : Cartesian4.UNIT_Z + }); + feature.addProperty('null', null); + feature.addProperty('undefined', undefined); + feature.addProperty('address.street', "Other Street"); + feature.addProperty('address', { + "street" : "Example Street", + "city" : "Example City" + }); + + var expression = new Expression('${vector["x"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + + expression = new Expression('${vector["z"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(0.0); + + expression = new Expression('${height["z"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(undefined); + + expression = new Expression('${undefined["z"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(undefined); + + expression = new Expression('${feature["vector"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_X); + + expression = new Expression('${feature.vector["x"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + + expression = new Expression('${feature["vector"].x}'); + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + + expression = new Expression('${feature["vector.x"]}'); + expect(expression.evaluate(frameState, feature)).toEqual('something else'); + + expression = new Expression('${feature.feature["vector"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Z); + + expression = new Expression('${feature["feature.vector"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Y); + + expression = new Expression('${address.street}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example Street"); + + expression = new Expression('${feature.address.street}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example Street"); + + expression = new Expression('${feature["address"].street}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example Street"); + + expression = new Expression('${feature["address.street"]}'); + expect(expression.evaluate(frameState, feature)).toEqual("Other Street"); + + expression = new Expression('${address["street"]}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example Street"); + + expression = new Expression('${address["city"]}'); + expect(expression.evaluate(frameState, feature)).toEqual("Example City"); + }); + + it('member expressions throw without variable notation', function() { + expect(function() { + return new Expression('color.r'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('color["r"]'); + }).toThrowRuntimeError(); + }); + + it('member expression throws with variable property', function() { + var feature = new MockFeature(); + feature.addProperty('vector', Cartesian4.UNIT_X); + feature.addProperty('vectorName', 'UNIT_X'); + + expect(function() { + return new Expression('${vector[${vectorName}]}'); + }).toThrowRuntimeError(); + }); + + it('evaluates feature property', function() { + var feature = new MockFeature(); + feature.addProperty('feature', { + vector : Cartesian4.UNIT_X + }); + + var expression = new Expression('${feature}'); + expect(expression.evaluate(frameState, feature)).toEqual({ + vector : Cartesian4.UNIT_X + }); + + expression = new Expression('${feature} === ${feature.feature}'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + }); + + it('constructs regex', function() { + var feature = new MockFeature(); + feature.addProperty('pattern', "[abc]"); + + var expression = new Expression('regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual(/a/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.LITERAL_REGEX); + + expression = new Expression('regExp("\\w")'); + expect(expression.evaluate(frameState, undefined)).toEqual(/\w/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.LITERAL_REGEX); + + expression = new Expression('regExp(1 + 1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(/2/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.REGEX); + + expression = new Expression('regExp(true)'); + expect(expression.evaluate(frameState, undefined)).toEqual(/true/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.LITERAL_REGEX); + + expression = new Expression('regExp()'); + expect(expression.evaluate(frameState, undefined)).toEqual(/(?:)/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.LITERAL_REGEX); + + expression = new Expression('regExp(${pattern})'); + expect(expression.evaluate(frameState, feature)).toEqual(/[abc]/); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.REGEX); + }); + + it ('constructs regex with flags', function() { + var expression = new Expression('regExp("a", "i")'); + expect(expression.evaluate(frameState, undefined)).toEqual(/a/i); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.LITERAL_REGEX); + + expression = new Expression('regExp("a", "m" + "g")'); + expect(expression.evaluate(frameState, undefined)).toEqual(/a/mg); + expect(expression._runtimeAst._type).toEqual(ExpressionNodeType.REGEX); + }); + + it('throws if regex constructor has invalid pattern', function() { + var expression = new Expression('regExp("(?<=\\s)" + ".")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('regExp("(?<=\\s)")'); + }).toThrowRuntimeError(); + }); + + it('throws if regex constructor has invalid flags', function() { + var expression = new Expression('regExp("a" + "b", "q")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('regExp("a", "q")'); + }).toThrowRuntimeError(); + }); + + it('evaluates regex test function', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('regExp("a").test("abc")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('regExp("a").test("bcd")'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('regExp("quick\\s(brown).+?(jumps)", "ig").test("The Quick Brown Fox Jumps Over The Lazy Dog")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('regExp("a").test()'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('regExp(${property}).test(${property})'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + }); + + it('throws if regex test function has invalid arguments', function() { + var expression = new Expression('regExp("1").test(1)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('regExp("a").test(regExp("b"))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates regex exec function', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + feature.addProperty('Name', 'Building 1'); + + var expression = new Expression('regExp("a(.)", "i").exec("Abc")'); + expect(expression.evaluate(frameState, undefined)).toEqual('b'); + + expression = new Expression('regExp("a(.)").exec("qbc")'); + expect(expression.evaluate(frameState, undefined)).toEqual(null); + + expression = new Expression('regExp("a(.)").exec()'); + expect(expression.evaluate(frameState, undefined)).toEqual(null); + + expression = new Expression('regExp("quick\\s(b.*n).+?(jumps)", "ig").exec("The Quick Brown Fox Jumps Over The Lazy Dog")'); + expect(expression.evaluate(frameState, undefined)).toEqual('Brown'); + + expression = new Expression('regExp("(" + ${property} + ")").exec(${property})'); + expect(expression.evaluate(frameState, feature)).toEqual('abc'); + + expression = new Expression('regExp("Building\\s(\\d)").exec(${Name})'); + expect(expression.evaluate(frameState, feature)).toEqual('1'); + }); + + it('throws if regex exec function has invalid arguments', function() { + var expression = new Expression('regExp("1").exec(1)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('regExp("a").exec(regExp("b"))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates regex match operator', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('regExp("a") =~ "abc"'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('"abc" =~ regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('regExp("a") =~ "bcd"'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('"bcd" =~ regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('regExp("quick\\s(brown).+?(jumps)", "ig") =~ "The Quick Brown Fox Jumps Over The Lazy Dog"'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('regExp(${property}) =~ ${property}'); + expect(expression.evaluate(frameState, feature)).toEqual(true); + }); + + it('throws if regex match operator has invalid arguments', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('regExp("a") =~ 1'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 =~ regExp("a")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 =~ 1'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('evaluates regex not match operator', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('regExp("a") !~ "abc"'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('"abc" !~ regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('regExp("a") !~ "bcd"'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('"bcd" !~ regExp("a")'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('regExp("quick\\s(brown).+?(jumps)", "ig") !~ "The Quick Brown Fox Jumps Over The Lazy Dog"'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('regExp(${property}) !~ ${property}'); + expect(expression.evaluate(frameState, feature)).toEqual(false); + }); + + it('throws if regex not match operator has invalid arguments', function() { + var expression = new Expression('regExp("a") !~ 1'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 !~ regExp("a")'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + + expression = new Expression('1 !~ 1'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowRuntimeError(); + }); + + it('throws if test is not called with a RegExp', function() { + expect(function() { + return new Expression('color("blue").test()'); + }).toThrowRuntimeError(); + + expect(function() { + return new Expression('"blue".test()'); + }).toThrowRuntimeError(); + }); + + it('evaluates regExp toString function', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('regExp().toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('/(?:)/'); + + expression = new Expression('regExp("\\d\\s\\d", "ig").toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('/\\d\\s\\d/gi'); + + expression = new Expression('regExp(${property}).toString()'); + expect(expression.evaluate(frameState, feature)).toEqual('/abc/'); + }); + + it('throws when using toString on other type', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'abc'); + + var expression = new Expression('${property}.toString()'); + expect(function() { + return expression.evaluate(frameState, feature); + }).toThrowRuntimeError(); + }); + + it('evaluates array expression', function() { + var feature = new MockFeature(); + feature.addProperty('property', 'value'); + feature.addProperty('array', [Cartesian4.UNIT_X, Cartesian4.UNIT_Y, Cartesian4.UNIT_Z]); + feature.addProperty('complicatedArray', [{ + 'subproperty' : Cartesian4.UNIT_X, + 'anotherproperty' : Cartesian4.UNIT_Y + }, { + 'subproperty' : Cartesian4.UNIT_Z, + 'anotherproperty' : Cartesian4.UNIT_W + }]); + feature.addProperty('temperatures', { + "scale" : "fahrenheit", + "values" : [70, 80, 90] + }); + + var expression = new Expression('[1, 2, 3]'); + expect(expression.evaluate(frameState, undefined)).toEqual([1, 2, 3]); + + expression = new Expression('[1+2, "hello", 2 < 3, color("blue"), ${property}]'); + expect(expression.evaluate(frameState, feature)).toEqual([3, 'hello', true, Cartesian4.fromColor(Color.BLUE), 'value']); + + expression = new Expression('${array[1]}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Y); + + expression = new Expression('${complicatedArray[1].subproperty}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Z); + + expression = new Expression('${complicatedArray[0]["anotherproperty"]}'); + expect(expression.evaluate(frameState, feature)).toEqual(Cartesian4.UNIT_Y); + + expression = new Expression('${temperatures["scale"]}'); + expect(expression.evaluate(frameState, feature)).toEqual('fahrenheit'); + + expression = new Expression('${temperatures.values[0]}'); + expect(expression.evaluate(frameState, feature)).toEqual(70); + + expression = new Expression('${temperatures["values"][0]}'); + expect(expression.evaluate(frameState, feature)).toEqual(70); + }); + + it('evaluates tiles3d_tileset_time expression', function() { + var feature = new MockFeature(); + var expression = new Expression('${tiles3d_tileset_time}'); + expect(expression.evaluate(frameState, feature)).toEqual(0.0); + feature.content.tileset.timeSinceLoad = 1.0; + expect(expression.evaluate(frameState, feature)).toEqual(1.0); + }); + + it('gets shader function', function() { + var expression = new Expression('true'); + var shaderFunction = expression.getShaderFunction('getShow', '', {}, 'bool'); + var expected = 'bool getShow() \n' + + '{ \n' + + ' return true; \n' + + '} \n'; + expect(shaderFunction).toEqual(expected); + }); + + it('gets shader expression for variable', function() { + var expression = new Expression('${property}'); + var shaderExpression = expression.getShaderExpression('prefix_', {}); + var expected = 'prefix_property'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for unary not', function() { + var expression = new Expression('!true'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '!true'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for unary negative', function() { + var expression = new Expression('-5.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '-5.0'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for unary positive', function() { + var expression = new Expression('+5.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '+5.0'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for converting to literal boolean', function() { + var expression = new Expression('Boolean(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'bool(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for converting to literal number', function() { + var expression = new Expression('Number(true)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'float(true)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary addition', function() { + var expression = new Expression('1.0 + 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 + 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary subtraction', function() { + var expression = new Expression('1.0 - 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 - 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary multiplication', function() { + var expression = new Expression('1.0 * 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 * 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary division', function() { + var expression = new Expression('1.0 / 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 / 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary modulus', function() { + var expression = new Expression('1.0 % 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'mod(1.0, 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary equals strict', function() { + var expression = new Expression('1.0 === 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 == 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary not equals strict', function() { + var expression = new Expression('1.0 !== 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 != 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary less than', function() { + var expression = new Expression('1.0 < 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 < 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary less than or equals', function() { + var expression = new Expression('1.0 <= 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 <= 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary greater than', function() { + var expression = new Expression('1.0 > 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 > 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for binary greater than or equals', function() { + var expression = new Expression('1.0 >= 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 >= 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for logical and', function() { + var expression = new Expression('true && false'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(true && false)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for logical or', function() { + var expression = new Expression('true || false'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(true || false)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for ternary conditional', function() { + var expression = new Expression('true ? 1.0 : 2.0'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(true ? 1.0 : 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for array indexing', function() { + var expression = new Expression('${property[0]}'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'property[0]'; + expect(shaderExpression).toEqual(expected); + + expression = new Expression('${property[4 / 2]}'); + shaderExpression = expression.getShaderExpression('', {}); + expected = 'property[int((4.0 / 2.0))]'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for array', function() { + var expression = new Expression('[1.0, 2.0]'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'vec2(1.0, 2.0)'; + expect(shaderExpression).toEqual(expected); + + expression = new Expression('[1.0, 2.0, 3.0]'); + shaderExpression = expression.getShaderExpression('', {}); + expected = 'vec3(1.0, 2.0, 3.0)'; + expect(shaderExpression).toEqual(expected); + + expression = new Expression('[1.0, 2.0, 3.0, 4.0]'); + shaderExpression = expression.getShaderExpression('', {}); + expected = 'vec4(1.0, 2.0, 3.0, 4.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('throws when getting shader expression for array of invalid length', function() { + var expression = new Expression('[]'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + + expression = new Expression('[1.0]'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + + expression = new Expression('[1.0, 2.0, 3.0, 4.0, 5.0]'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('gets shader expression for boolean', function() { + var expression = new Expression('true || false'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(true || false)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for integer', function() { + var expression = new Expression('1'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '1.0'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for float', function() { + var expression = new Expression('1.02'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '1.02'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for color', function() { + var shaderState = {translucent : false}; + var expression = new Expression('color()'); + var shaderExpression = expression.getShaderExpression('', shaderState); + var expected = 'vec4(1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('color("red")'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(vec3(1.0, 0.0, 0.0), 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('color("#FFF")'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(vec3(1.0, 1.0, 1.0), 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('color("#FF0000")'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(vec3(1.0, 0.0, 0.0), 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('color("rgb(255, 0, 0)")'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(vec3(1.0, 0.0, 0.0), 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('color("red", 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(vec3(1.0, 0.0, 0.0), 0.5)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(true); + + shaderState = {translucent : false}; + expression = new Expression('rgb(255, 0, 0)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(1.0, 0.0, 0.0, 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('rgb(255, ${property}, 0)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(255.0 / 255.0, property / 255.0, 0.0 / 255.0, 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('rgba(255, 0, 0, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(1.0, 0.0, 0.0, 0.5)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(true); + + shaderState = {translucent : false}; + expression = new Expression('rgba(255, ${property}, 0, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(255.0 / 255.0, property / 255.0, 0.0 / 255.0, 0.5)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(true); + + shaderState = {translucent : false}; + expression = new Expression('hsl(1.0, 0.5, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(0.75, 0.25, 0.25, 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('hsla(1.0, 0.5, 0.5, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(0.75, 0.25, 0.25, 0.5)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(true); + + shaderState = {translucent : false}; + expression = new Expression('hsl(1.0, ${property}, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(czm_HSLToRGB(vec3(1.0, property, 0.5)), 1.0)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(false); + + shaderState = {translucent : false}; + expression = new Expression('hsla(1.0, ${property}, 0.5, 0.5)'); + shaderExpression = expression.getShaderExpression('', shaderState); + expected = 'vec4(czm_HSLToRGB(vec3(1.0, property, 0.5)), 0.5)'; + expect(shaderExpression).toEqual(expected); + expect(shaderState.translucent).toBe(true); + }); + + it('gets shader expression for color components', function() { + // .r, .g, .b, .a + var expression = new Expression('color().r + color().g + color().b + color().a'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])'; + expect(shaderExpression).toEqual(expected); + + // .x, .y, .z, .w + expression = new Expression('color().x + color().y + color().z + color().w'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + + // [0], [1], [2], [3] + expression = new Expression('color()[0] + color()[1] + color()[2] + color()[3]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for vector', function() { + var expression = new Expression('vec4(1, 2, 3, 4)'); + var shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('vec4(1.0, 2.0, 3.0, 4.0)'); + + expression = new Expression('vec4(1) + vec4(2)'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('(vec4(1.0) + vec4(2.0))'); + + expression = new Expression('vec4(1, ${property}, vec2(1, 2).x, 0)'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('vec4(1.0, property, vec2(1.0, 2.0)[0], 0.0)'); + + expression = new Expression('vec4(vec3(2), 1.0)'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('vec4(vec3(2.0), 1.0)'); + }); + + it('gets shader expression for vector components', function() { + // .x, .y, .z, .w + var expression = new Expression('vec4(1).x + vec4(1).y + vec4(1).z + vec4(1).w'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])'; + expect(shaderExpression).toEqual(expected); + + // [0], [1], [2], [3] + expression = new Expression('vec4(1)[0] + vec4(1)[1] + vec4(1)[2] + vec4(1)[3]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for tiles3d_tileset_time', function() { + var expression = new Expression('${tiles3d_tileset_time}'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'u_tilesetTime'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for abs', function() { + var expression = new Expression('abs(-1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'abs(-1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for cos', function() { + var expression = new Expression('cos(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'cos(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for sin', function() { + var expression = new Expression('sin(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'sin(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for tan', function() { + var expression = new Expression('tan(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'tan(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for acos', function() { + var expression = new Expression('acos(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'acos(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for asin', function() { + var expression = new Expression('asin(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'asin(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for atan', function() { + var expression = new Expression('atan(0.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'atan(0.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for sqrt', function() { + var expression = new Expression('sqrt(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'sqrt(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for sign', function() { + var expression = new Expression('sign(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'sign(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for floor', function() { + var expression = new Expression('floor(1.5)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'floor(1.5)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for ceil', function() { + var expression = new Expression('ceil(1.2)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'ceil(1.2)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for round', function() { + var expression = new Expression('round(1.2)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'floor(1.2 + 0.5)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for exp', function() { + var expression = new Expression('exp(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'exp(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for exp2', function() { + var expression = new Expression('exp2(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'exp2(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for log', function() { + var expression = new Expression('log(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'log(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for log2', function() { + var expression = new Expression('log2(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'log2(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for fract', function() { + var expression = new Expression('fract(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'fract(1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for clamp', function() { + var expression = new Expression('clamp(50.0, 0.0, 100.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'clamp(50.0, 0.0, 100.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for mix', function() { + var expression = new Expression('mix(0.0, 2.0, 0.5)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'mix(0.0, 2.0, 0.5)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for atan2', function() { + var expression = new Expression('atan2(0.0,1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'atan(0.0, 1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for pow', function() { + var expression = new Expression('pow(2.0,2.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'pow(2.0, 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for min', function() { + var expression = new Expression('min(3.0,5.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'min(3.0, 5.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for max', function() { + var expression = new Expression('max(3.0,5.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'max(3.0, 5.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for length', function() { + var expression = new Expression('length(3.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'length(3.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for normalize', function() { + var expression = new Expression('normalize(3.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'normalize(3.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for distance', function() { + var expression = new Expression('distance(0.0, 1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'distance(0.0, 1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for dot', function() { + var expression = new Expression('dot(1.0, 2.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'dot(1.0, 2.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for cross', function() { + var expression = new Expression('cross(vec3(1.0, 1.0, 1.0), vec3(2.0, 2.0, 2.0))'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'cross(vec3(1.0, 1.0, 1.0), vec3(2.0, 2.0, 2.0))'; + expect(shaderExpression).toEqual(expected); + }); + + it('throws when getting shader expression for regex', function() { + var expression = new Expression('regExp("a").test("abc")'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + + expression = new Expression('regExp("a(.)", "i").exec("Abc")'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + + expression = new Expression('regExp("a") =~ "abc"'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + + expression = new Expression('regExp("a") !~ "abc"'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for member expression with dot', function() { + var expression = new Expression('${property.name}'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for string member expression with brackets', function() { + var expression = new Expression('${property["name"]}'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for String', function() { + var expression = new Expression('String(1.0)'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for toString', function() { + var expression = new Expression('color("red").toString()'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for literal string', function() { + var expression = new Expression('"name"'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for variable in string', function() { + var expression = new Expression('"${property}"'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for literal undefined', function() { + var expression = new Expression('undefined'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for literal null', function() { + var expression = new Expression('null'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for isNaN', function() { + var expression = new Expression('isNaN(1.0)'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for isFinite', function() { + var expression = new Expression('isFinite(1.0)'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for isExactClass', function() { + var expression = new Expression('isExactClass("door")'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for isClass', function() { + var expression = new Expression('isClass("door")'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); + + it('throws when getting shader expression for getExactClassName', function() { + var expression = new Expression('getExactClassName()'); + expect(function() { + return expression.getShaderExpression('', {}); + }).toThrowRuntimeError(); + }); +}); diff --git a/Specs/Scene/FrameRateMonitorSpec.js b/Specs/Scene/FrameRateMonitorSpec.js index d85faff842aa..b54508373748 100644 --- a/Specs/Scene/FrameRateMonitorSpec.js +++ b/Specs/Scene/FrameRateMonitorSpec.js @@ -29,10 +29,11 @@ defineSuite([ }); function spinWait(milliseconds) { - /*jshint noempty: false*/ var endTime = getTimestamp() + milliseconds; + /*eslint-disable no-empty*/ while (getTimestamp() < endTime) { } + /*eslint-enable no-empty*/ } it('throws when constructed without a scene', function() { diff --git a/Specs/Scene/GlobeSurfaceTileProviderSpec.js b/Specs/Scene/GlobeSurfaceTileProviderSpec.js index 9c21718503ad..a647c761e2ac 100644 --- a/Specs/Scene/GlobeSurfaceTileProviderSpec.js +++ b/Specs/Scene/GlobeSurfaceTileProviderSpec.js @@ -378,6 +378,7 @@ defineSuite([ var oldFog = scene.fog; scene.fog = new Fog(); switchViewMode(SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84)); + scene.camera.lookUp(1.2); // Horizon-view return updateUntilDone(scene.globe).then(function() { expect(scene).notToRender([0, 0, 0, 255]); @@ -400,6 +401,7 @@ defineSuite([ var oldFog = scene.fog; scene.fog = new Fog(); switchViewMode(SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84)); + scene.camera.lookUp(1.2); // Horizon-view return updateUntilDone(scene.globe).then(function() { expect(scene).notToRender([0, 0, 0, 255]); @@ -605,7 +607,7 @@ defineSuite([ var terrainCredit = new Credit('terrain credit'); scene.terrainProvider = new CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', credit : terrainCredit }); diff --git a/Specs/Scene/GlobeSurfaceTileSpec.js b/Specs/Scene/GlobeSurfaceTileSpec.js index 1078a639c616..6a9c75861187 100644 --- a/Specs/Scene/GlobeSurfaceTileSpec.js +++ b/Specs/Scene/GlobeSurfaceTileSpec.js @@ -9,7 +9,7 @@ defineSuite([ 'Core/GeographicTilingScheme', 'Core/Ray', 'Core/Rectangle', - 'Core/WebMercatorTilingScheme', + 'Core/RequestScheduler', 'Scene/Imagery', 'Scene/ImageryLayer', 'Scene/ImageryLayerCollection', @@ -31,7 +31,7 @@ defineSuite([ GeographicTilingScheme, Ray, Rectangle, - WebMercatorTilingScheme, + RequestScheduler, Imagery, ImageryLayer, ImageryLayerCollection, @@ -97,7 +97,7 @@ defineSuite([ }); beforeEach(function() { - tilingScheme = new WebMercatorTilingScheme(); + tilingScheme = new GeographicTilingScheme(); alwaysDeferTerrainProvider.tilingScheme = tilingScheme; alwaysFailTerrainProvider.tilingScheme = tilingScheme; rootTiles = QuadtreeTile.createLevelZeroTiles(tilingScheme); @@ -145,6 +145,7 @@ defineSuite([ it('once a root tile is loaded, its children get both loadedTerrain and upsampledTerrain', function() { return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return rootTile.state === QuadtreeTileLoadState.DONE; }).then(function() { var children = rootTile.children; @@ -159,6 +160,7 @@ defineSuite([ it('loaded terrainData is copied to the tile once it is available', function() { return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return rootTile.data.loadedTerrain.state >= TerrainState.RECEIVED; }).then(function() { expect(rootTile.data.terrainData).toBeDefined(); @@ -283,6 +285,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, alwaysDeferTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return rootTile.renderable && childTile.renderable; }).then(function() { expect(childTile.data.waterMaskTexture).toBeDefined(); @@ -292,6 +295,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, realTerrainProvider, imageryLayerCollection, vertexArraysToDestroy); + RequestScheduler.update(); return childTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(childTile.data.waterMaskTexture).toBeDefined(); @@ -308,6 +312,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, alwaysFailTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return rootTile.renderable && childTile.renderable; }).then(function() { expect(childTile.data.loadedTerrain).toBeUndefined(); @@ -322,6 +327,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, alwaysDeferTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return defined(rootTile.data.terrainData) && defined(rootTile.data.terrainData._mesh) && defined(childTile.data.terrainData); }).then(function() { @@ -330,6 +336,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(grandchildTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return grandchildTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(grandchildTile.data.loadedTerrain).toBeUndefined(); @@ -339,6 +346,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, realTerrainProvider, imageryLayerCollection, vertexArraysToDestroy); + RequestScheduler.update(); return childTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(grandchildTile.state).toBe(QuadtreeTileLoadState.DONE); @@ -359,6 +367,7 @@ defineSuite([ GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, alwaysDeferTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(grandchildTile, scene.frameState, alwaysDeferTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return defined(rootTile.data.terrainData) && defined(rootTile.data.terrainData._mesh) && defined(childTile.data.terrainData) && defined(childTile.data.terrainData._mesh) && defined(grandchildTile.data.terrainData); @@ -368,6 +377,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(greatGrandchildTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return greatGrandchildTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(greatGrandchildTile.data.loadedTerrain).toBeUndefined(); @@ -378,6 +388,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, realTerrainProvider, imageryLayerCollection, vertexArraysToBeDestroyed); GlobeSurfaceTile.processStateMachine(grandchildTile, scene.frameState, alwaysDeferTerrainProvider, imageryLayerCollection, vertexArraysToBeDestroyed); + RequestScheduler.update(); return childTile.state === QuadtreeTileLoadState.DONE && !defined(grandchildTile.data.upsampledTerrain); }).then(function() { @@ -396,6 +407,7 @@ defineSuite([ return pollToPromise(function() { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, realTerrainProvider, imageryLayerCollection, []); GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, alwaysFailTerrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return rootTile.state >= QuadtreeTileLoadState.DONE && childTile.state >= QuadtreeTileLoadState.DONE; }).then(function() { @@ -432,10 +444,9 @@ defineSuite([ if (rootTile.state !== QuadtreeTileLoadState.DONE) { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, allWaterTerrainProvider, imageryLayerCollection, []); return false; - } else { - GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, allWaterTerrainProvider, imageryLayerCollection, []); - return childTile.state === QuadtreeTileLoadState.DONE; } + GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, allWaterTerrainProvider, imageryLayerCollection, []); + return childTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(childTile.data.waterMaskTexture).toBeDefined(); expect(childTile.data.waterMaskTexture).toBe(rootTile.data.waterMaskTexture); @@ -470,10 +481,9 @@ defineSuite([ if (rootTile.state !== QuadtreeTileLoadState.DONE) { GlobeSurfaceTile.processStateMachine(rootTile, scene.frameState, allLandTerrainProvider, imageryLayerCollection, []); return false; - } else { - GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, allLandTerrainProvider, imageryLayerCollection, []); - return childTile.state === QuadtreeTileLoadState.DONE; } + GlobeSurfaceTile.processStateMachine(childTile, scene.frameState, allLandTerrainProvider, imageryLayerCollection, []); + return childTile.state === QuadtreeTileLoadState.DONE; }).then(function() { expect(childTile.data.waterMaskTexture).toBeUndefined(); }); @@ -521,7 +531,7 @@ defineSuite([ it('gets correct results even when the mesh includes normals', function() { var terrainProvider = new CesiumTerrainProvider({ - url : 'https://assets.agi.com/stk-terrain/world', + url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles', requestVertexNormals : true }); @@ -540,6 +550,7 @@ defineSuite([ } GlobeSurfaceTile.processStateMachine(tile, scene.frameState, terrainProvider, imageryLayerCollection, []); + RequestScheduler.update(); return tile.state === QuadtreeTileLoadState.DONE; }).then(function() { var ray = new Ray( diff --git a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js new file mode 100644 index 000000000000..00eec62d9ac6 --- /dev/null +++ b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js @@ -0,0 +1,302 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/GoogleEarthEnterpriseImageryProvider', + 'Core/decodeGoogleEarthEnterpriseData', + 'Core/DefaultProxy', + 'Core/defaultValue', + 'Core/defined', + 'Core/GeographicTilingScheme', + 'Core/GoogleEarthEnterpriseMetadata', + 'Core/GoogleEarthEnterpriseTileInformation', + 'Core/loadImage', + 'Core/loadWithXhr', + 'Core/Rectangle', + 'Core/RequestScheduler', + 'Scene/DiscardMissingTileImagePolicy', + 'Scene/Imagery', + 'Scene/ImageryLayer', + 'Scene/ImageryProvider', + 'Scene/ImageryState', + 'Specs/pollToPromise', + 'ThirdParty/when' +], function( + GoogleEarthEnterpriseImageryProvider, + decodeGoogleEarthEnterpriseData, + DefaultProxy, + defaultValue, + defined, + GeographicTilingScheme, + GoogleEarthEnterpriseMetadata, + GoogleEarthEnterpriseTileInformation, + loadImage, + loadWithXhr, + Rectangle, + RequestScheduler, + DiscardMissingTileImagePolicy, + Imagery, + ImageryLayer, + ImageryProvider, + ImageryState, + pollToPromise, + when) { + 'use strict'; + + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + + beforeAll(function() { + decodeGoogleEarthEnterpriseData.passThroughDataForTesting = true; + }); + + afterAll(function() { + decodeGoogleEarthEnterpriseData.passThroughDataForTesting = false; + }); + + var imageryProvider; + afterEach(function() { + loadImage.createImage = loadImage.defaultCreateImage; + loadWithXhr.load = loadWithXhr.defaultLoad; + }); + + it('conforms to ImageryProvider interface', function() { + expect(GoogleEarthEnterpriseImageryProvider).toConformToInterface(ImageryProvider); + }); + + it('constructor throws when url is not specified', function() { + function constructWithoutServer() { + return new GoogleEarthEnterpriseImageryProvider({}); + } + + expect(constructWithoutServer).toThrowDeveloperError(); + }); + + function installMockGetQuadTreePacket() { + spyOn(GoogleEarthEnterpriseMetadata.prototype, 'getQuadTreePacket').and.callFake(function(quadKey, version) { + quadKey = defaultValue(quadKey, ''); + this._tileInfo[quadKey + '0'] = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + this._tileInfo[quadKey + '1'] = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + this._tileInfo[quadKey + '2'] = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + this._tileInfo[quadKey + '3'] = new GoogleEarthEnterpriseTileInformation(0xFF, 1, 1, 1); + + return when(); + }); + } + + function installFakeImageRequest(expectedUrl) { + loadImage.createImage = function(url, crossOrigin, deferred) { + if (/^blob:/.test(url)) { + // load blob url normally + loadImage.defaultCreateImage(url, crossOrigin, deferred); + } else { + if (defined(expectedUrl)) { + expect(url).toEqual(expectedUrl); + } + // Just return any old image. + loadImage.defaultCreateImage('Data/Images/Red16x16.png', crossOrigin, deferred); + } + }; + + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + if (defined(expectedUrl)) { + expect(url).toEqual(expectedUrl); + } + + // Just return any old image. + loadWithXhr.defaultLoad('Data/Images/Red16x16.png', responseType, method, data, headers, deferred); + }; + } + + it('resolves readyPromise', function() { + installMockGetQuadTreePacket(); + var url = 'http://fake.fake.invalid'; + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + return imageryProvider.readyPromise.then(function(result) { + expect(result).toBe(true); + expect(imageryProvider.ready).toBe(true); + }); + }); + + it('rejects readyPromise on error', function() { + var url = 'host.invalid'; + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + return imageryProvider.readyPromise.then(function() { + fail('should not resolve'); + }).otherwise(function(e) { + expect(imageryProvider.ready).toBe(false); + expect(e.message).toContain(url); + }); + }); + + it('readyPromise rejects if there isn\'t imagery', function() { + installMockGetQuadTreePacket(); + + var metadata = new GoogleEarthEnterpriseMetadata({ + url : 'made/up/url' + }); + + metadata.imageryPresent = false; + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + metadata : metadata + }); + + return imageryProvider.readyPromise + .then(function() { + fail('Server does not have imagery, so we shouldn\'t resolve.'); + }) + .otherwise(function() { + expect(imageryProvider.ready).toBe(false); + }); + }); + + it('returns false for hasAlphaChannel', function() { + installMockGetQuadTreePacket(); + var url = 'http://fake.fake.invalid'; + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + return pollToPromise(function() { + return imageryProvider.ready; + }).then(function() { + expect(typeof imageryProvider.hasAlphaChannel).toBe('boolean'); + expect(imageryProvider.hasAlphaChannel).toBe(false); + }); + }); + + it('can provide a root tile', function() { + installMockGetQuadTreePacket(); + var url = 'http://fake.fake.invalid/'; + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + expect(imageryProvider.url).toEqual(url); + + return pollToPromise(function() { + return imageryProvider.ready; + }).then(function() { + expect(imageryProvider.tileWidth).toEqual(256); + expect(imageryProvider.tileHeight).toEqual(256); + expect(imageryProvider.maximumLevel).toEqual(23); + expect(imageryProvider.tilingScheme).toBeInstanceOf(GeographicTilingScheme); + // Defaults to custom tile policy + expect(imageryProvider.tileDiscardPolicy).not.toBeInstanceOf(DiscardMissingTileImagePolicy); + expect(imageryProvider.rectangle).toEqual(new Rectangle(-Math.PI, -Math.PI, Math.PI, Math.PI)); + expect(imageryProvider.credit).toBeUndefined(); + + installFakeImageRequest('http://fake.fake.invalid/flatfile?f1-03-i.1'); + + return imageryProvider.requestImage(0, 0, 0).then(function(image) { + expect(image).toBeInstanceOf(Image); + }); + }); + }); + + it('routes requests through a proxy if one is specified', function() { + installMockGetQuadTreePacket(); + var url = 'http://foo.bar.invalid/'; + + var proxy = new DefaultProxy('/proxy/'); + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url, + proxy : proxy + }); + + expect(imageryProvider.url).toEqual(url); + expect(imageryProvider.proxy).toEqual(proxy); + + return pollToPromise(function() { + return imageryProvider.ready; + }).then(function() { + installFakeImageRequest(proxy.getURL('http://foo.bar.invalid/flatfile?f1-03-i.1')); + + return imageryProvider.requestImage(0, 0, 0).then(function(image) { + expect(image).toBeInstanceOf(Image); + }); + }); + }); + + it('raises error on invalid url', function() { + var url = 'host.invalid'; + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + var errorEventRaised = false; + imageryProvider.errorEvent.addEventListener(function(error) { + expect(error.message).toContain(url); + errorEventRaised = true; + }); + + return pollToPromise(function() { + return imageryProvider.ready || errorEventRaised; + }).then(function() { + expect(imageryProvider.ready).toEqual(false); + expect(errorEventRaised).toEqual(true); + }); + }); + + it('raises error event when image cannot be loaded', function() { + installMockGetQuadTreePacket(); + var url = 'http://foo.bar.invalid'; + + imageryProvider = new GoogleEarthEnterpriseImageryProvider({ + url : url + }); + + var layer = new ImageryLayer(imageryProvider); + + var tries = 0; + imageryProvider.errorEvent.addEventListener(function(error) { + expect(error.timesRetried).toEqual(tries); + ++tries; + if (tries < 3) { + error.retry = true; + } + setTimeout(function() { + RequestScheduler.update(); + }, 1); + }); + + loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + if (tries === 2) { + // Succeed after 2 tries + loadWithXhr.defaultLoad('Data/Images/Red16x16.png', responseType, method, data, headers, deferred); + } else { + // fail + setTimeout(function() { + deferred.reject(); + }, 1); + } + }; + + return pollToPromise(function() { + return imageryProvider.ready; + }).then(function() { + var imagery = new Imagery(layer, 0, 0, 0); + imagery.addReference(); + layer._requestImagery(imagery); + RequestScheduler.update(); + + return pollToPromise(function() { + return imagery.state === ImageryState.RECEIVED; + }).then(function() { + expect(imagery.image).toBeInstanceOf(Image); + expect(tries).toEqual(2); + imagery.releaseReference(); + }); + }); + }); +}); diff --git a/Specs/Scene/GoogleEarthImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js similarity index 78% rename from Specs/Scene/GoogleEarthImageryProviderSpec.js rename to Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 7df3ce2e74ea..7fa6649ef479 100644 --- a/Specs/Scene/GoogleEarthImageryProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -1,11 +1,12 @@ /*global defineSuite*/ defineSuite([ - 'Scene/GoogleEarthImageryProvider', + 'Scene/GoogleEarthEnterpriseMapsProvider', 'Core/DefaultProxy', 'Core/GeographicTilingScheme', 'Core/loadImage', 'Core/loadWithXhr', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorTilingScheme', 'Scene/Imagery', 'Scene/ImageryLayer', @@ -13,18 +14,19 @@ defineSuite([ 'Scene/ImageryState', 'Specs/pollToPromise' ], function( - GoogleEarthImageryProvider, - DefaultProxy, - GeographicTilingScheme, - loadImage, - loadWithXhr, - Rectangle, - WebMercatorTilingScheme, - Imagery, - ImageryLayer, - ImageryProvider, - ImageryState, - pollToPromise) { + GoogleEarthEnterpriseMapsProvider, + DefaultProxy, + GeographicTilingScheme, + loadImage, + loadWithXhr, + Rectangle, + RequestScheduler, + WebMercatorTilingScheme, + Imagery, + ImageryLayer, + ImageryProvider, + ImageryState, + pollToPromise) { 'use strict'; afterEach(function() { @@ -33,24 +35,26 @@ defineSuite([ }); it('conforms to ImageryProvider interface', function() { - expect(GoogleEarthImageryProvider).toConformToInterface(ImageryProvider); + expect(GoogleEarthEnterpriseMapsProvider).toConformToInterface(ImageryProvider); }); it('constructor throws when url is not specified', function() { function constructWithoutServer() { - return new GoogleEarthImageryProvider({ + return new GoogleEarthEnterpriseMapsProvider({ channel : 1234 }); } + expect(constructWithoutServer).toThrowDeveloperError(); }); it('constructor throws when channel is not specified', function() { function constructWithoutChannel() { - return new GoogleEarthImageryProvider({ + return new GoogleEarthEnterpriseMapsProvider({ url : 'http://invalid.localhost' }); } + expect(constructWithoutChannel).toThrowDeveloperError(); }); @@ -60,16 +64,16 @@ defineSuite([ var channel = 1234; loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/good.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/good.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : channel, path : path }); - return provider.readyPromise.then(function (result) { + return provider.readyPromise.then(function(result) { expect(result).toBe(true); expect(provider.ready).toBe(true); }); @@ -77,14 +81,14 @@ defineSuite([ it('rejects readyPromise on error', function() { var url = 'invalid.localhost'; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : 1234 }); - return provider.readyPromise.then(function () { + return provider.readyPromise.then(function() { fail('should not resolve'); - }).otherwise(function (e) { + }).otherwise(function(e) { expect(provider.ready).toBe(false); expect(e.message).toContain(url); }); @@ -96,10 +100,10 @@ defineSuite([ var channel = 1234; loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/good.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/good.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : channel, path : path @@ -119,10 +123,10 @@ defineSuite([ var version = 1; loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/good.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/good.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : channel, path : path @@ -178,32 +182,32 @@ defineSuite([ loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { return deferred.resolve('{\n' + - 'isAuthenticated: true,\n' + - 'layers: [\n' + - ' {\n' + - ' icon: "icons/773_l.png",\n' + - ' id: 1234,\n' + - ' initialState: true,\n' + - ' label: "Imagery",\n' + - ' lookAt: "none",\n' + - ' requestType: "ImageryMaps",\n' + - ' version: 1\n' + - ' },{\n' + - ' icon: "icons/773_l.png",\n' + - ' id: 1007,\n' + - ' initialState: true,\n' + - ' label: "Labels",\n' + - ' lookAt: "none",\n' + - ' requestType: "VectorMapsRaster",\n' + - ' version: 8\n' + - ' }\n' + - '],\n' + - 'serverUrl: "https://example.invalid",\n' + - 'useGoogleLayers: false\n' + - '}'); + 'isAuthenticated: true,\n' + + 'layers: [\n' + + ' {\n' + + ' icon: "icons/773_l.png",\n' + + ' id: 1234,\n' + + ' initialState: true,\n' + + ' label: "Imagery",\n' + + ' lookAt: "none",\n' + + ' requestType: "ImageryMaps",\n' + + ' version: 1\n' + + ' },{\n' + + ' icon: "icons/773_l.png",\n' + + ' id: 1007,\n' + + ' initialState: true,\n' + + ' label: "Labels",\n' + + ' lookAt: "none",\n' + + ' requestType: "VectorMapsRaster",\n' + + ' version: 8\n' + + ' }\n' + + '],\n' + + 'serverUrl: "https://example.invalid",\n' + + 'useGoogleLayers: false\n' + + '}'); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : channel }); @@ -224,10 +228,10 @@ defineSuite([ var proxy = new DefaultProxy('/proxy/'); loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/good.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/good.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : 1234, proxy : proxy @@ -267,7 +271,7 @@ defineSuite([ it('raises error on invalid url', function() { var url = 'invalid.localhost'; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : url, channel : 1234 }); @@ -288,10 +292,10 @@ defineSuite([ it('raises error event when image cannot be loaded', function() { loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/good.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/good.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'example.invalid', channel : 1234 }); @@ -305,6 +309,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -340,6 +347,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -368,7 +376,7 @@ defineSuite([ })); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://example.invalid', channel : 1234 }); @@ -399,7 +407,7 @@ defineSuite([ })); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://example.invalid', channel : 1234 }); @@ -430,7 +438,7 @@ defineSuite([ })); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://example.invalid', channel : 1234 }); @@ -445,10 +453,10 @@ defineSuite([ it('raises error when channel cannot be found', function() { loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/bad_channel.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/bad_channel.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://invalid.localhost', channel : 1235 }); @@ -469,10 +477,10 @@ defineSuite([ it('raises error when channel version cannot be found', function() { loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/bad_version.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/bad_version.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://invalid.localhost', channel : 1234 }); @@ -493,10 +501,10 @@ defineSuite([ it('raises error when unsupported projection is specified', function() { loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - loadWithXhr.defaultLoad('Data/GoogleEarthImageryProvider/bad_projection.json', responseType, method, data, headers, deferred); + loadWithXhr.defaultLoad('Data/GoogleEarthEnterpriseMapsProvider/bad_projection.json', responseType, method, data, headers, deferred); }; - var provider = new GoogleEarthImageryProvider({ + var provider = new GoogleEarthEnterpriseMapsProvider({ url : 'http://invalid.localhost', channel : 1234 }); diff --git a/Specs/Scene/ImageryLayerCollectionSpec.js b/Specs/Scene/ImageryLayerCollectionSpec.js index 7749c1f490ff..d3118a0d3d1e 100644 --- a/Specs/Scene/ImageryLayerCollectionSpec.js +++ b/Specs/Scene/ImageryLayerCollectionSpec.js @@ -544,9 +544,8 @@ defineSuite([ // At level 1, only the northwest quadrant has a valid tile. if (level !== 1 || (x === 0 && y === 0)) { return ImageryProvider.loadImage(this, 'Data/Images/Blue.png'); - } else { - return when.reject(); } + return when.reject(); } }; diff --git a/Specs/Scene/ImageryLayerSpec.js b/Specs/Scene/ImageryLayerSpec.js index 4c9ec1fcbcbb..151b91d5d0ca 100644 --- a/Specs/Scene/ImageryLayerSpec.js +++ b/Specs/Scene/ImageryLayerSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/loadJsonp', 'Core/loadWithXhr', 'Core/Rectangle', + 'Core/RequestScheduler', 'Renderer/ComputeEngine', 'Scene/ArcGisMapServerImageryProvider', 'Scene/BingMapsImageryProvider', @@ -28,6 +29,7 @@ defineSuite([ loadJsonp, loadWithXhr, Rectangle, + RequestScheduler, ComputeEngine, ArcGisMapServerImageryProvider, BingMapsImageryProvider, @@ -104,6 +106,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -166,6 +169,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -202,6 +206,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 1, 3); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -237,6 +242,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 1, 3); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -292,6 +298,7 @@ defineSuite([ var imagery = new Imagery(layer, 4400, 2686, 13); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -327,6 +334,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; @@ -386,6 +394,7 @@ defineSuite([ return provider.ready; }).then(function() { imageryLayer._requestImagery(new Imagery(imageryLayer, 0, 0, 0)); + RequestScheduler.update(); return pollToPromise(function() { return errorRaised; diff --git a/Specs/Scene/Instanced3DModel3DTileContentSpec.js b/Specs/Scene/Instanced3DModel3DTileContentSpec.js new file mode 100644 index 000000000000..7ff687f5e473 --- /dev/null +++ b/Specs/Scene/Instanced3DModel3DTileContentSpec.js @@ -0,0 +1,313 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Instanced3DModel3DTileContent', + 'Core/Cartesian3', + 'Core/Color', + 'Core/HeadingPitchRange', + 'Core/HeadingPitchRoll', + 'Core/Transforms', + 'Scene/Cesium3DTileContentState', + 'Scene/TileBoundingSphere', + 'Specs/Cesium3DTilesTester', + 'Specs/createScene' + ], function( + Instanced3DModel3DTileContent, + Cartesian3, + Color, + HeadingPitchRange, + HeadingPitchRoll, + Transforms, + Cesium3DTileContentState, + TileBoundingSphere, + Cesium3DTilesTester, + createScene) { + 'use strict'; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + var gltfExternalUrl = './Data/Cesium3DTiles/Instanced/InstancedGltfExternal/'; + var withBatchTableUrl = './Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/'; + var withBatchTableBinaryUrl = './Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/'; + var withoutBatchTableUrl = './Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/'; + var orientationUrl = './Data/Cesium3DTiles/Instanced/InstancedOrientation/'; + var oct16POrientationUrl = './Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/'; + var scaleUrl = './Data/Cesium3DTiles/Instanced/InstancedScale/'; + var scaleNonUniformUrl = './Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/'; + var rtcUrl = './Data/Cesium3DTiles/Instanced/InstancedRTC'; + var quantizedUrl = './Data/Cesium3DTiles/Instanced/InstancedQuantized/'; + var quantizedOct32POrientationUrl = './Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/'; + var withTransformUrl = './Data/Cesium3DTiles/Instanced/InstancedWithTransform/'; + var withBatchIdsUrl = './Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/'; + var texturedUrl = './Data/Cesium3DTiles/Instanced/InstancedTextured/'; + var compressedTexturesUrl = './Data/Cesium3DTiles/Instanced/InstancedCompressedTextures/'; + var gltfZUpUrl = './Data/Cesium3DTiles/Instanced/InstancedGltfZUp'; + + function setCamera(longitude, latitude) { + // One instance is located at the center, point the camera there + var center = Cartesian3.fromRadians(longitude, latitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 27.0)); + } + + beforeAll(function() { + scene = createScene(); + }); + + beforeEach(function() { + scene.morphTo3D(0.0); + setCamera(centerLongitude, centerLatitude); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + it('throws with invalid format', function() { + var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ + gltfFormat : 2 + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'i3dm'); + }); + + it('throws with invalid version', function() { + var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ + version : 2 + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'i3dm'); + }); + + it('throws with empty gltf', function() { + // Expect to throw DeveloperError in Model due to invalid gltf magic + var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer(); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'i3dm'); + }); + + it('resolves readyPromise', function() { + return Cesium3DTilesTester.resolvesReadyPromise(scene, withoutBatchTableUrl); + }); + + it('rejects readyPromise on error', function() { + // Try loading a tile with an invalid url. + // Expect promise to be rejected in Model, then in ModelInstanceCollection, and + // finally in Instanced3DModel3DTileContent. + var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ + gltfFormat : 0, + gltfUri : 'not-a-real-path' + }); + return Cesium3DTilesTester.rejectsReadyPromiseOnError(scene, arrayBuffer, 'i3dm'); + }); + + it('renders with external gltf', function() { + return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with batch table', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with batch table binary', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableBinaryUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders without batch table', function() { + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined orientation', function() { + return Cesium3DTilesTester.loadTileset(scene, orientationUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined Oct32P encoded orientation', function() { + return Cesium3DTilesTester.loadTileset(scene, oct16POrientationUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined scale', function() { + return Cesium3DTilesTester.loadTileset(scene, scaleUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined non-uniform scale', function() { + return Cesium3DTilesTester.loadTileset(scene, scaleNonUniformUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with RTC_CENTER semantic', function() { + return Cesium3DTilesTester.loadTileset(scene, rtcUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined quantized position', function() { + return Cesium3DTilesTester.loadTileset(scene, quantizedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with feature defined quantized position and Oct32P encoded orientation', function() { + return Cesium3DTilesTester.loadTileset(scene, quantizedOct32POrientationUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with batch ids', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchIdsUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with a gltf z-up axis', function() { + return Cesium3DTilesTester.loadTileset(scene, gltfZUpUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with tile transform', function() { + return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + + var newLongitude = -1.31962; + var newLatitude = 0.698874; + var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude, 10.0); + var newTransform = Transforms.headingPitchRollToFixedFrame(newCenter, new HeadingPitchRoll()); + + // Update tile transform + tileset._root.transform = newTransform; + + // Move the camera to the new location + setCamera(newLongitude, newLatitude); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with textures', function() { + return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with compressed textures', function() { + return Cesium3DTilesTester.loadTileset(scene, compressedTexturesUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders in 2D', function() { + return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + tileset.maximumScreenSpaceError = 2.0; + scene.morphTo2D(0.0); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders in 2D with tile transform', function() { + return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + tileset.maximumScreenSpaceError = 2.0; + scene.morphTo2D(0.0); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders in CV', function() { + return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + scene.morphToColumbusView(0.0); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders in CV with tile transform', function() { + return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + scene.morphToColumbusView(0.0); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('throws when calling getFeature with invalid index', function() { + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { + var content = tileset._root.content; + expect(function(){ + content.getFeature(-1); + }).toThrowDeveloperError(); + expect(function(){ + content.getFeature(10000); + }).toThrowDeveloperError(); + expect(function(){ + content.getFeature(); + }).toThrowDeveloperError(); + }); + }); + + it('gets memory usage', function() { + return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function(tileset) { + var content = tileset._root.content; + + // Box model - 32 ushort indices and 24 vertices per building, 8 float components (position, normal, uv) per vertex. + // (24 * 8 * 4) + (36 * 2) = 840 + var geometryByteLength = 840; + + // Texture is 211x211 RGBA bytes, but upsampled to 256x256 because the wrap mode is REPEAT + var texturesByteLength = 262144; + + // One RGBA byte pixel per feature + var batchTexturesByteLength = content.featuresLength * 4; + var pickTexturesByteLength = content.featuresLength * 4; + + // Features have not been picked or colored yet, so the batch table contribution is 0. + expect(content.geometryByteLength).toEqual(geometryByteLength); + expect(content.texturesByteLength).toEqual(texturesByteLength); + expect(content.batchTableByteLength).toEqual(0); + + // Color a feature and expect the texture memory to increase + content.getFeature(0).color = Color.RED; + scene.renderForSpecs(); + expect(content.geometryByteLength).toEqual(geometryByteLength); + expect(content.texturesByteLength).toEqual(texturesByteLength); + expect(content.batchTableByteLength).toEqual(batchTexturesByteLength); + + // Pick the tile and expect the texture memory to increase + scene.pickForSpecs(); + expect(content.geometryByteLength).toEqual(geometryByteLength); + expect(content.texturesByteLength).toEqual(texturesByteLength); + expect(content.batchTableByteLength).toEqual(batchTexturesByteLength + pickTexturesByteLength); + }); + }); + + it('destroys', function() { + return Cesium3DTilesTester.tileDestroys(scene, withoutBatchTableUrl); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/JobSchedulerSpec.js b/Specs/Scene/JobSchedulerSpec.js new file mode 100644 index 000000000000..21d607d788d5 --- /dev/null +++ b/Specs/Scene/JobSchedulerSpec.js @@ -0,0 +1,195 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/JobScheduler', + 'Scene/JobType' + ], function( + JobScheduler, + JobType) { + 'use strict'; + + var originalGetTimestamp; + + beforeAll(function() { + originalGetTimestamp = JobScheduler.getTimestamp; + + var time = 0.0; + JobScheduler.getTimestamp = function() { + return time++; + }; + }); + + afterAll(function() { + JobScheduler.getTimestamp = originalGetTimestamp; + }); + + /////////////////////////////////////////////////////////////////////////// + + var MockJob = function() { + this.executed = false; + }; + + MockJob.prototype.execute = function() { + this.executed = true; + }; + + /////////////////////////////////////////////////////////////////////////// + + it('constructs with defaults', function() { + var js = new JobScheduler(); + expect(js.totalBudget).toEqual(50.0); + + var budgets = js._budgets; + expect(budgets.length).toEqual(JobType.NUMBER_OF_JOB_TYPES); + expect(budgets[JobType.TEXTURE].total).toEqual(10.0); + expect(budgets[JobType.PROGRAM].total).toEqual(10.0); + expect(budgets[JobType.BUFFER].total).toEqual(30.0); + }); + + it('executes a job', function() { + var js = new JobScheduler([2.0, // JobType.TEXTURE + 0.0, // JobType.PROGRAM + 0.0]); // JobType.BUFFER + + var job = new MockJob(); + var executed = js.execute(job, JobType.TEXTURE); + + expect(executed).toEqual(true); + expect(job.executed).toEqual(true); + expect(js._totalUsedThisFrame).toEqual(1.0); + expect(js._budgets[JobType.TEXTURE].total).toEqual(2.0); + expect(js._budgets[JobType.TEXTURE].usedThisFrame).toEqual(1.0); + }); + + it('disableThisFrame does not execute a job', function() { + var js = new JobScheduler([2.0, 0.0, 0.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + + js.disableThisFrame(); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + }); + + it('executes different job types', function() { + var js = new JobScheduler([1.0, 1.0, 1.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(true); + + expect(js._totalUsedThisFrame).toEqual(3.0); + var budgets = js._budgets; + expect(budgets[JobType.TEXTURE].usedThisFrame).toEqual(1.0); + expect(budgets[JobType.PROGRAM].usedThisFrame).toEqual(1.0); + expect(budgets[JobType.BUFFER].usedThisFrame).toEqual(1.0); + }); + + it('executes a second job', function() { + var js = new JobScheduler([2.0, 0.0, 0.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js._totalUsedThisFrame).toEqual(2.0); + expect(js._budgets[JobType.TEXTURE].usedThisFrame).toEqual(2.0); + }); + + it('does not execute second job (exceeds total time)', function() { + var js = new JobScheduler([1.0, 0.0, 0.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + expect(js._budgets[JobType.TEXTURE].starvedThisFrame).toEqual(true); + }); + + it('executes a second job (TEXTURE steals PROGRAM budget)', function() { + var js = new JobScheduler([1.0, 1.0, 0.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js._totalUsedThisFrame).toEqual(2.0); + + var budgets = js._budgets; + expect(budgets[JobType.TEXTURE].usedThisFrame).toEqual(1.0); + expect(budgets[JobType.TEXTURE].starvedThisFrame).toEqual(true); + expect(budgets[JobType.PROGRAM].usedThisFrame).toEqual(0.0); + expect(budgets[JobType.PROGRAM].stolenFromMeThisFrame).toEqual(1.0); + expect(budgets[JobType.PROGRAM].starvedThisFrame).toEqual(false); + + // There are no budgets left to steal from + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); // Allowed once per frame + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); + expect(budgets[JobType.PROGRAM].starvedThisFrame).toEqual(true); + }); + + it('does not steal in the same frame', function() { + var js = new JobScheduler([1.0, 1.0, 1.0]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(true); + + // Exhaust budget for all job types in the first frame + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(false); + + // In this next frame, no job type can steal from another since + // they all exhausted their budgets in the previous frame + js.resetBudgets(); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); + + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(true); + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(false); + }); + + it('does not steal from starving job types over multiple frames', function() { + var js = new JobScheduler([1.0, 1.0, 0.0]); + + // Exhaust in first frame + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); // Stolen from PROGRAM + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + + js.resetBudgets(); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); // Can't steal from TEXTURE + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + + js.resetBudgets(); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); // Can't steal from TEXTURE yet + + js.resetBudgets(); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); // Can steal from TEXTURE since it wasn't exhausted last frame + }); + + it('Allows progress on all job types once per frame', function() { + var js = new JobScheduler([1.0, 1.0, 1.0]); + + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); // Steal from PROGRAM + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); // Steal from BUFFER + + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(false); + + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); // Still gets to make progress once this frame + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(false); + + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(true); // Still gets to make progress once this frame + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(false); + }); + + it('Long job still allows progress on other job types once per frame', function() { + // Job duration is always 1.0 in the tests so shorten budget + var js = new JobScheduler([0.5, 0.2, 0.2]); + expect(js.execute(new MockJob(), JobType.TEXTURE)).toEqual(true); // Goes over total budget + expect(js.execute(new MockJob(), JobType.PROGRAM)).toEqual(true); // Still gets to make progress once this frame + expect(js.execute(new MockJob(), JobType.BUFFER)).toEqual(true); // Still gets to make progress once this frame + }); + + it('constructor throws when budgets.length is not JobType.NUMBER_OF_JOB_TYPES', function() { + expect(function() { + return new JobScheduler([1.0]); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 5a0830cf6ef6..61b348fe3dc5 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -100,6 +100,7 @@ defineSuite([ expect(label.pixelOffsetScaleByDistance).not.toBeDefined(); expect(label.scaleByDistance).not.toBeDefined(); expect(label.distanceDisplayCondition).not.toBeDefined(); + expect(label.disableDepthTestDistance).toEqual(0.0); }); it('can add a label with specified values', function() { @@ -134,6 +135,7 @@ defineSuite([ 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 disableDepthTestDistance = 10.0; var label = labels.add({ show : show, position : position, @@ -155,7 +157,8 @@ defineSuite([ translucencyByDistance : translucency, pixelOffsetScaleByDistance : pixelOffsetScale, scaleByDistance : scaleByDistance, - distanceDisplayCondition : distanceDisplayCondition + distanceDisplayCondition : distanceDisplayCondition, + disableDepthTestDistance : disableDepthTestDistance }); expect(label.show).toEqual(show); @@ -179,6 +182,7 @@ defineSuite([ expect(label.pixelOffsetScaleByDistance).toEqual(pixelOffsetScale); expect(label.scaleByDistance).toEqual(scaleByDistance); expect(label.distanceDisplayCondition).toEqual(distanceDisplayCondition); + expect(label.disableDepthTestDistance).toEqual(disableDepthTestDistance); }); it('can specify font using units other than pixels', function() { @@ -675,6 +679,43 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('renders with disableDepthTestDistance', function() { + var l = labels.add({ + position : new Cartesian3(-1.0, 0.0, 0.0), + text : solidBox, + fillColor : Color.LIME, + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER + }); + labels.add({ + position : Cartesian3.ZERO, + text : solidBox, + fillColor : Color.BLUE, + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER + }); + + expect(scene).toRender([0, 0, 255, 255]); + + l.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toRender([0, 255, 0, 255]); + }); + + it('throws with new label with disableDepthTestDistance less than 0.0', function() { + expect(function() { + labels.add({ + disableDepthTestDistance : -1.0 + }); + }).toThrowDeveloperError(); + }); + + it('throws with disableDepthTestDistance set less than 0.0', function() { + var l = labels.add(); + expect(function() { + l.disableDepthTestDistance = -1.0; + }).toThrowDeveloperError(); + }); + it('can pick a label', function() { var label = labels.add({ position : Cartesian3.ZERO, diff --git a/Specs/Scene/MapboxImageryProviderSpec.js b/Specs/Scene/MapboxImageryProviderSpec.js index 60e382b7b092..50b681bd4412 100644 --- a/Specs/Scene/MapboxImageryProviderSpec.js +++ b/Specs/Scene/MapboxImageryProviderSpec.js @@ -5,6 +5,7 @@ defineSuite([ 'Core/loadImage', 'Core/Math', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorTilingScheme', 'Scene/Imagery', 'Scene/ImageryLayer', @@ -17,6 +18,7 @@ defineSuite([ loadImage, CesiumMath, Rectangle, + RequestScheduler, WebMercatorTilingScheme, Imagery, ImageryLayer, @@ -25,6 +27,10 @@ defineSuite([ pollToPromise) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; }); @@ -250,6 +256,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -270,6 +279,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Scene/MaterialSpec.js b/Specs/Scene/MaterialSpec.js index d6bfaf4f4008..0730cdd41d26 100644 --- a/Specs/Scene/MaterialSpec.js +++ b/Specs/Scene/MaterialSpec.js @@ -198,6 +198,10 @@ defineSuite([ verifyPolylineMaterial('PolylineArrow'); }); + it('draws PolylineDash built-in material', function() { + verifyPolylineMaterial('PolylineDash'); + }); + it('draws PolylineGlow built-in material', function() { verifyPolylineMaterial('PolylineGlow'); }); diff --git a/Specs/Scene/ModelInstanceCollectionSpec.js b/Specs/Scene/ModelInstanceCollectionSpec.js new file mode 100644 index 000000000000..72b7b5e23caf --- /dev/null +++ b/Specs/Scene/ModelInstanceCollectionSpec.js @@ -0,0 +1,698 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/ModelInstanceCollection', + 'Core/BoundingSphere', + 'Core/Cartesian3', + 'Core/defaultValue', + 'Core/defined', + 'Core/HeadingPitchRange', + 'Core/HeadingPitchRoll', + 'Core/JulianDate', + 'Core/Math', + 'Core/Matrix4', + 'Core/PrimitiveType', + 'Core/Transforms', + 'Scene/Model', + 'Scene/ModelAnimationLoop', + 'Scene/SceneMode', + 'Scene/ShadowMode', + 'Specs/createScene', + 'Specs/pollToPromise', + 'ThirdParty/when' + ], function( + ModelInstanceCollection, + BoundingSphere, + Cartesian3, + defaultValue, + defined, + HeadingPitchRange, + HeadingPitchRoll, + JulianDate, + CesiumMath, + Matrix4, + PrimitiveType, + Transforms, + Model, + ModelAnimationLoop, + SceneMode, + ShadowMode, + createScene, + pollToPromise, + when) { + 'use strict'; + + var boxUrl = './Data/Models/Box/CesiumBoxTest.gltf'; + var cesiumAirUrl = './Data/Models/CesiumAir/Cesium_Air.gltf'; + var riggedFigureUrl = './Data/Models/rigged-figure-test/rigged-figure-test.gltf'; + var movingBoxUrl = './Data/Models/moving-box/moving-box.gltf'; + + var boxGltf; + var cesiumAirGltf; + var riggedFigureGltf; + var movingBoxGltf; + + var boxRadius; + + var scene; + + beforeAll(function() { + scene = createScene(); + + var modelPromises = []; + modelPromises.push(loadModel(boxUrl).then(function(model) { + boxGltf = model.gltf; + boxRadius = model.boundingSphere.radius; + scene.primitives.remove(model); + })); + modelPromises.push(loadModel(cesiumAirUrl).then(function(model) { + cesiumAirGltf = model.gltf; + scene.primitives.remove(model); + })); + modelPromises.push(loadModel(riggedFigureUrl).then(function(model) { + riggedFigureGltf = model.gltf; + scene.primitives.remove(model); + })); + modelPromises.push(loadModel(movingBoxUrl).then(function(model) { + movingBoxGltf = model.gltf; + scene.primitives.remove(model); + })); + + return when.all(modelPromises); + }); + + beforeEach(function() { + scene.morphTo3D(0.0); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + function loadModel(url) { + var model = scene.primitives.add(Model.fromGltf({ + url : url + })); + + return pollToPromise(function() { + // Render scene to progressively load the model + scene.renderForSpecs(); + return model.ready; + }).then(function() { + return model; + }); + } + + function loadCollection(options) { + var collection = scene.primitives.add(new ModelInstanceCollection(options)); + + return pollToPromise(function() { + // Render scene to progressively load the model + scene.renderForSpecs(); + return collection.ready; + }).then(function() { + zoomTo(collection, 0); + return collection; + }); + } + + function createInstances(count, heightOffset) { + heightOffset = defaultValue(heightOffset, 0.0); + + var spacing = 20.0; + var centerLongitude = -123.0744619; + var centerLatitude = 44.0503706; + var height = 5000.0 + heightOffset; + + var instances = []; + for (var i = 0; i < count; ++i) { + var instanceHeight = height + spacing * i; + var position = Cartesian3.fromDegrees(centerLongitude, centerLatitude, instanceHeight); + var heading = Math.PI/2.0; + var pitch = 0.0; + var roll = 0.0; + var hpr = new HeadingPitchRoll(heading, pitch, roll); + var modelMatrix = Transforms.headingPitchRollToFixedFrame(position, hpr); + instances.push({ + modelMatrix : modelMatrix + }); + } + + return instances; + } + + function getBoundingSphere(instances, modelRadius) { + var length = instances.length; + var points = new Array(length); + for (var i = 0; i < length; ++i) { + var translation = new Cartesian3(); + Matrix4.getTranslation(instances[i].modelMatrix, translation); + points[i] = translation; + } + var boundingSphere = new BoundingSphere(); + BoundingSphere.fromPoints(points, boundingSphere); + boundingSphere.radius += modelRadius; + return boundingSphere; + } + + var centerScratch = new Cartesian3(); + + function zoomTo(collection, instance) { + var center = Matrix4.getTranslation(collection._instances[instance].modelMatrix, centerScratch); + var camera = scene.camera; + camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, 10.0)); + } + + function expectRender(collection, expectColor, time) { + expectColor = defaultValue(expectColor, true); + + collection.show = false; + expect(scene).toRender([0, 0, 0, 255]); + collection.show = true; + + // Verify each instance + var length = collection.length; + for (var i = 0; i < length; ++i) { + zoomTo(collection, i); + if (expectColor) { + expect({ + scene : scene, + time : time + }).notToRender([0, 0, 0, 255]); + } else { + expect({ + scene : scene, + time : time + }).toRender([0, 0, 0, 255]); + } + } + } + + function verifyPickedInstance(collection, instanceId) { + return function(result) { + expect(result.primitive).toBe(collection); + expect(result.modelMatrix).toBeDefined(); + expect(result.instanceId).toBe(instanceId); + expect(result.model).toBe(collection._model); + }; + } + + function expectPick(collection) { + collection.show = false; + expect(scene).notToPick(); + collection.show = true; + + // Verify each instance + var length = collection.length; + for (var i = 0; i < length; ++i) { + zoomTo(collection, i); + expect(scene).toPickAndCall(verifyPickedInstance(collection, i)); + } + } + + it('throws if neither options.gltf nor options.url are provided', function() { + expect(function() { + return new ModelInstanceCollection(); + }).toThrowDeveloperError(); + }); + + it('throws when both options.gltf and options.url are provided', function() { + expect(function() { + return new ModelInstanceCollection({ + url : boxUrl, + gltf : boxGltf + }); + }).toThrowDeveloperError(); + }); + + it('sets properties', function() { + return loadCollection({ + url : boxUrl, + instances : createInstances(4) + }).then(function(collection) { + expect(collection.ready).toEqual(true); + expect(collection.show).toEqual(true); + expect(collection.allowPicking).toEqual(true); + expect(collection.length).toEqual(4); + expect(collection.debugShowBoundingVolume).toEqual(false); + expect(collection.debugWireframe).toEqual(false); + expect(collection._dynamic).toEqual(false); + expect(collection._cull).toEqual(true); + expect(collection._model).toBeDefined(); + expect(collection._model.ready).toEqual(true); + + if (collection._instancingSupported) { + expect(collection._model.cacheKey).toEqual(boxUrl + '#instanced'); + } + }); + }); + + it('renders from url', function() { + return loadCollection({ + url : boxUrl, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('renders from gltf', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('resolves readyPromise', function() { + var collection = scene.primitives.add(new ModelInstanceCollection({ + gltf : boxGltf, + instances : createInstances(4) + })); + + scene.renderForSpecs(); + scene.renderForSpecs(); + + return collection.readyPromise.then(function(collection) { + expect(collection.ready).toEqual(true); + }); + }); + + it('rejects readyPromise on error', function() { + // Expect promise to be rejected in Model, then in ModelInstanceCollection. + var collection = scene.primitives.add(new ModelInstanceCollection({ + url : 'invalid.gltf', + instances : createInstances(4) + })); + + collection.update(scene.frameState); + + return collection.readyPromise.then(function(collection) { + fail('should not resolve'); + }).otherwise(function(error) { + expect(collection.ready).toEqual(false); + }); + }); + + it('renders one instance', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(1) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('renders zero instances', function() { + var collection = scene.primitives.add(new ModelInstanceCollection({ + gltf : boxGltf, + instances : createInstances(0) + })); + + // Collection never reaches the ready state due to returning early + for (var i = 0; i < 10; ++i) { + expectRender(collection, false); + expect(collection.ready).toBe(false); + } + }); + + it('renders 100 instances', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(100) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('renders cesiumAir', function() { + return loadCollection({ + gltf : cesiumAirGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('renders rigged figure', function() { + return loadCollection({ + gltf : riggedFigureGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('renders when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('renders when dynamic is true', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4), + dynamic : true + }).then(function(collection) { + expectRender(collection); + }); + }); + + it('verify bounding volume', function() { + var instances = createInstances(4); + return loadCollection({ + gltf : boxGltf, + instances : instances + }).then(function(collection) { + var boundingSphere = getBoundingSphere(instances, boxRadius); + expect(collection._boundingSphere.center).toEqual(boundingSphere.center); + expect(collection._boundingSphere.radius).toEqual(boundingSphere.radius); + }); + }); + + it('renders bounding volume', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + collection.debugShowBoundingVolume = true; + expectRender(collection); + }); + }); + + it('renders in wireframe', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + collection.debugWireframe = true; + scene.renderForSpecs(); + expect(collection._drawCommands[0].primitiveType).toEqual(PrimitiveType.LINES); + }); + }); + + it('renders with animations', function() { + // Test that all instances are being animated. + // The moving box is in view on frame 1 and out of view by frame 5. + return loadCollection({ + gltf : movingBoxGltf, + instances : createInstances(4) + }).then(function(collection) { + collection.activeAnimations.addAll(); + + // Render when animation is in view + var time = JulianDate.now(); + expectRender(collection, true, time); + + // Render when animation is out of view + time = JulianDate.addSeconds(time, 0.1, new JulianDate()); + expectRender(collection, false, time); + }); + }); + + it('renders with animations when instancing is disabled', function() { + // Instance transforms are updated differently when instancing is disabled + + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : movingBoxGltf, + instances : createInstances(4) + }).then(function(collection) { + collection.activeAnimations.addAll(); + + // Render when animation is in view + var time = JulianDate.now(); + expectRender(collection, true, time); + + // Render when animation is out of view + time = JulianDate.addSeconds(time, 0.1, new JulianDate()); + expectRender(collection, false, time); + + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('renders two model instance collections that use the same cache key', function() { + var collections = []; + var promises = []; + + promises.push(loadCollection({ + url : boxUrl, + instances : createInstances(2) + }).then(function(collection) { + collections.push(collection); + })); + + promises.push(loadCollection({ + url : boxUrl, + instances : createInstances(2, 1000.0) + }).then(function(collection) { + collections.push(collection); + })); + + return when.all(promises).then(function() { + var resourcesFirst = collections[0]._model._rendererResources; + var resourcesSecond = collections[1]._model._rendererResources; + var name; + + expect(collections[0]._model.cacheKey).toEqual(collections[1]._model.cacheKey); + zoomTo(collections[0], 0); + expectRender(collections[0]); + zoomTo(collections[1], 0); + expectRender(collections[1]); + + // Check that buffers are equal + for (name in resourcesFirst.buffers) { + if (resourcesFirst.buffers.hasOwnProperty(name)) { + expect(resourcesFirst.buffers[name]).toEqual(resourcesSecond.buffers[name]); + } + } + + // Check that programs are equal + for (name in resourcesFirst.programs) { + if (resourcesFirst.programs.hasOwnProperty(name)) { + expect(resourcesFirst.programs[name]).toEqual(resourcesSecond.programs[name]); + } + } + + if (collections[0]._instancingSupported) { + // Check that vertex arrays are different, since each collection has a unique vertex buffer for instanced attributes. + for (name in resourcesFirst.vertexArrays) { + if (resourcesFirst.vertexArrays.hasOwnProperty(name)) { + expect(resourcesFirst.vertexArrays[name]).not.toEqual(resourcesSecond.vertexArrays[name]); + } + } + } + }); + }); + + it('culls when out of view and cull is true', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4), + cull : true + }).then(function(collection) { + scene.renderForSpecs(); + expect(scene._frustumCommandsList.length).not.toEqual(0); + scene.camera.lookAt(new Cartesian3(100000.0, 0.0, 0.0), new HeadingPitchRange(0.0, 0.0, 10.0)); + scene.renderForSpecs(); + expect(scene._frustumCommandsList.length).toEqual(0); + }); + }); + + it('does not cull when out of view and cull is false', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4), + cull : false + }).then(function(collection) { + scene.renderForSpecs(); + expect(scene._frustumCommandsList.length).not.toEqual(0); + scene.camera.lookAt(new Cartesian3(100000.0, 0.0, 0.0), new HeadingPitchRange(0.0, 0.0, 10.0)); + scene.renderForSpecs(); + expect(scene._frustumCommandsList.length).not.toEqual(0); + }); + }); + + it('shadows', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + scene.renderForSpecs(); + expect(collection._shadows).toBe(ShadowMode.ENABLED); + var drawCommand = collection._drawCommands[0]; + expect(drawCommand.castShadows).toBe(true); + expect(drawCommand.receiveShadows).toBe(true); + collection.shadows = ShadowMode.DISABLED; + scene.renderForSpecs(); + expect(drawCommand.castShadows).toBe(false); + expect(drawCommand.receiveShadows).toBe(false); + }); + }); + + it('picks', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectPick(collection); + }); + }); + + it('picks when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectPick(collection); + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('moves instance', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expect(scene).toPickAndCall(function(result) { + var originalMatrix = result.modelMatrix; + result.modelMatrix = Matrix4.IDENTITY; + expect(scene).notToPick(); + result.modelMatrix = originalMatrix; + expect(scene).toPickPrimitive(collection); + }); + }); + }); + + it('moves instance when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expect(scene).toPickAndCall(function(result) { + var originalMatrix = result.modelMatrix; + var originalRadius = collection._boundingSphere.radius; + result.modelMatrix = Matrix4.IDENTITY; + expect(scene).notToPick(); + expect(collection._boundingSphere.radius).toBeGreaterThan(originalRadius); + result.modelMatrix = originalMatrix; + expect(scene).toPickPrimitive(collection); + }); + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('renders in 2D', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + scene.morphTo2D(0.0); + expectRender(collection); + }); + }); + + it('renders in 2D when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + scene.morphTo2D(0.0); + expectRender(collection); + + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('renders in CV', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + scene.morphToColumbusView(0.0); + expectRender(collection); + }); + }); + + it('renders in CV when instancing is disabled', function() { + // Disable extension + var instancedArrays = scene.context._instancedArrays; + scene.context._instancedArrays = undefined; + + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expectRender(collection); + scene.morphToColumbusView(0.0); + expectRender(collection); + + // Re-enable extension + scene.context._instancedArrays = instancedArrays; + }); + }); + + it('does not render during morph', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4), + cull : false + }).then(function() { + var commandList = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commandList.length).toBeGreaterThan(0); + scene.morphToColumbusView(1.0); + scene.renderForSpecs(); + expect(commandList.length).toBe(0); + }); + }); + + it('destroys', function() { + return loadCollection({ + gltf : boxGltf, + instances : createInstances(4) + }).then(function(collection) { + expect(collection.isDestroyed()).toEqual(false); + scene.primitives.remove(collection); + expect(collection.isDestroyed()).toEqual(true); + }); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index abb959703b97..baea33fd3adf 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -80,6 +80,7 @@ defineSuite([ var boxNoIndicesUrl = './Data/Models/Box-NoIndices/box-noindices.gltf'; var texturedBoxUrl = './Data/Models/Box-Textured/CesiumTexturedBoxTest.gltf'; var texturedBoxSeparateUrl = './Data/Models/Box-Textured-Separate/CesiumTexturedBoxTest.gltf'; + var texturedBoxBasePathUrl = './Data/Models/Box-Textured-BasePath/CesiumTexturedBoxTest.gltf'; var texturedBoxKTXUrl = './Data/Models/Box-Textured-KTX/CesiumTexturedBoxTest.gltf'; var texturedBoxKTXBinaryUrl = './Data/Models/Box-Textured-KTX-Binary/CesiumTexturedBoxTest.glb'; var texturedBoxKTXEmbeddedUrl = './Data/Models/Box-Textured-KTX-Embedded/CesiumTexturedBoxTest.gltf'; @@ -262,6 +263,27 @@ defineSuite([ expect(texturedBoxModel.colorBlendAmount).toEqual(0.5); }); + it('preserves query string in url', function() { + var params = '?param1=1¶m2=2'; + var url = texturedBoxUrl + params; + var model = Model.fromGltf({ + url: url + }); + expect(model._basePath).toEndWith(params); + expect(model._baseUri).toEndWith(params); + }); + + it('fromGltf takes a base path', function() { + var url = texturedBoxBasePathUrl; + var basePath = './Data/Models/Box-Textured-Separate/'; + var model = Model.fromGltf({ + url: url, + basePath: basePath + }); + expect(model._basePath).toEndWith(basePath); + expect(model._cacheKey).toEndWith(basePath); + }); + it('renders', function() { verifyRender(texturedBoxModel); }); @@ -328,6 +350,23 @@ defineSuite([ }); }); + it('does not render during morph', function() { + var commandList = scene.frameState.commandList; + var model = texturedBoxModel; + model.show = true; + model.cull = false; + expect(model.ready).toBe(true); + + scene.renderForSpecs(); + expect(commandList.length).toBeGreaterThan(0); + + scene.morphTo2D(1.0); + scene.renderForSpecs(); + expect(commandList.length).toBe(0); + scene.completeMorph(); + model.show = false; + }); + it('Renders x-up model', function() { return loadJson(boxEcefUrl).then(function(gltf) { // Model data is z-up. Edit the transform to be z-up to x-up. @@ -400,6 +439,24 @@ defineSuite([ }); }); + it('rejects readyPromise on error', function() { + var invalidGltf = clone(texturedBoxModel.gltf, true); + invalidGltf.shaders.CesiumTexturedBoxTest0FS.uri = 'invalid.glsl'; + + var model = primitives.add(new Model({ + gltf : invalidGltf + })); + + scene.renderForSpecs(); + + return model.readyPromise.then(function(model) { + fail('should not resolve'); + }).otherwise(function(error) { + expect(model.ready).toEqual(false); + primitives.remove(model); + }); + }); + it('renders from glTF', function() { // Simulate using procedural glTF as opposed to loading it from a file return loadModelJson(texturedBoxModel.gltf).then(function(model) { @@ -1906,8 +1963,7 @@ defineSuite([ } Matrix4.multiplyByMatrix3(m.modelMatrix, rotate, m.modelMatrix); - /* jshint loopfunc: true */ - expect(scene).toRenderAndCall(function(rgba) { + expect(scene).toRenderAndCall(function(rgba) { //eslint-disable-line no-loop-func expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba).not.toEqual(oldPixelColor); oldPixelColor = rgba; @@ -2302,6 +2358,36 @@ defineSuite([ }); }); + it('gets triangle count', function() { + expect(texturedBoxModel.trianglesLength).toBe(12); + expect(cesiumAirModel.trianglesLength).toBe(5984); + }); + + it('gets memory usage', function() { + // Texture is originally 211*211 but is scaled up to 256*256 to support its minification filter and then is mipmapped + var expectedTextureMemory = Math.floor(256*256*4*(4/3)); + var expectedGeometryMemory = 840; + var options = { + cacheKey : 'memory-usage-test', + incrementallyLoadTextures : false + }; + return loadModel(texturedBoxUrl, options).then(function(model) { + // The first model owns the resources + expect(model.geometryByteLength).toBe(expectedGeometryMemory); + expect(model.texturesByteLength).toBe(expectedTextureMemory); + expect(model.cachedGeometryByteLength).toBe(0); + expect(model.cachedTexturesByteLength).toBe(0); + + return loadModel(texturedBoxUrl, options).then(function(model) { + // The second model is sharing the resources, so its memory usage is reported as 0 + expect(model.geometryByteLength).toBe(0); + expect(model.texturesByteLength).toBe(0); + expect(model.cachedGeometryByteLength).toBe(expectedGeometryMemory); + expect(model.cachedTexturesByteLength).toBe(expectedTextureMemory); + }); + }); + }); + describe('height referenced model', function() { function createMockGlobe() { var globe = { diff --git a/Specs/Scene/PerspectiveOffCenterFrustumSpec.js b/Specs/Scene/PerspectiveOffCenterFrustumSpec.js index 6cd2b4d653e2..535d919f63e5 100644 --- a/Specs/Scene/PerspectiveOffCenterFrustumSpec.js +++ b/Specs/Scene/PerspectiveOffCenterFrustumSpec.js @@ -64,40 +64,40 @@ defineSuite([ var leftPlane = planes[0]; var x = 1.0 / Math.sqrt(2.0); var expectedResult = new Cartesian4(x, 0.0, -x, 0.0); - expect(leftPlane).toEqual(expectedResult); + expect(leftPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get frustum right plane', function() { var rightPlane = planes[1]; var x = 1.0 / Math.sqrt(2.0); var expectedResult = new Cartesian4(-x, 0.0, -x, 0.0); - expect(rightPlane).toEqual(expectedResult); + expect(rightPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get frustum bottom plane', function() { var bottomPlane = planes[2]; var x = 1.0 / Math.sqrt(2.0); var expectedResult = new Cartesian4(0.0, x, -x, 0.0); - expect(bottomPlane).toEqual(expectedResult); + expect(bottomPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get frustum top plane', function() { var topPlane = planes[3]; var x = 1.0 / Math.sqrt(2.0); var expectedResult = new Cartesian4(0.0, -x, -x, 0.0); - expect(topPlane).toEqual(expectedResult); + expect(topPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get frustum near plane', function() { var nearPlane = planes[4]; var expectedResult = new Cartesian4(0.0, 0.0, -1.0, -1.0); - expect(nearPlane).toEqual(expectedResult); + expect(nearPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get frustum far plane', function() { var farPlane = planes[5]; var expectedResult = new Cartesian4(0.0, 0.0, 1.0, 2.0); - expect(farPlane).toEqual(expectedResult); + expect(farPlane).toEqualEpsilon(expectedResult, CesiumMath.EPSILON15); }); it('get perspective projection matrix', function() { diff --git a/Specs/Scene/PointAppearanceSpec.js b/Specs/Scene/PointAppearanceSpec.js deleted file mode 100644 index 0563a14aa576..000000000000 --- a/Specs/Scene/PointAppearanceSpec.js +++ /dev/null @@ -1,139 +0,0 @@ -/*global defineSuite*/ -defineSuite([ - 'Scene/PointAppearance', - 'Core/BoundingSphere', - 'Core/Cartesian3', - 'Core/Color', - 'Core/GeometryInstance', - 'Core/PointGeometry', - 'Core/VertexFormat', - 'Scene/Appearance', - 'Scene/Primitive', - 'Specs/createScene' - ], function( - PointAppearance, - BoundingSphere, - Cartesian3, - Color, - GeometryInstance, - PointGeometry, - VertexFormat, - Appearance, - Primitive, - createScene) { - 'use strict'; - - var scene; - var primitive; - - beforeAll(function() { - scene = createScene(); - scene.frameState.scene3DOnly = true; - - var camera = scene.camera; - camera.position = new Cartesian3(10.0, 0.0, 0.0); - camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()); - camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); - - var positionsTypedArray = new Float64Array([0.0, 0.0, 0.0, -1.0, 0.0, 0.0]); - var colorsTypedArray = new Uint8Array([255, 0, 0, 0, 255, 0]); - var boundingSphere = BoundingSphere.fromVertices(positionsTypedArray); - - var instance = new GeometryInstance({ - geometry : new PointGeometry({ - positionsTypedArray : positionsTypedArray, - colorsTypedArray : colorsTypedArray, - boundingSphere : boundingSphere - }) - }); - - primitive = new Primitive({ - geometryInstances : instance, - appearance : new PointAppearance(), - asynchronous : false, - allowPicking : false, - cull : false - }); - - scene.primitives.add(primitive); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); - - it('default constructor', function() { - var a = new PointAppearance(); - - expect(a.vertexShaderSource).toBeDefined(); - expect(a.fragmentShaderSource).toBeDefined(); - expect(a.renderState).toEqual(Appearance.getDefaultRenderState(false, false)); - expect(a.material).not.toBeDefined(); - expect(a.translucent).toEqual(false); - expect(a.closed).toEqual(false); - expect(a.vertexFormat).toEqual(VertexFormat.POSITION_AND_COLOR); - expect(a.uniforms.highlightColor).toEqual(new Color()); - expect(a.uniforms.pointSize).toEqual(2.0); - }); - - it('renders', function() { - primitive.show = false; - expect(scene).toRender([0, 0, 0, 255]); - primitive.show = true; - expect(scene).toRender([255, 0, 0, 255]); - primitive.show = false; - }); - - it('renders with translucency', function() { - primitive.appearance.translucent = true; - primitive.appearance.uniforms.highlightColor.alpha = 0.5; - - primitive.show = false; - expect(scene).toRender([0, 0, 0, 255]); - - primitive.show = true; - expect(scene).toRenderAndCall(function(rgba) { - expect(rgba[0]).toBeGreaterThan(0); - expect(rgba[0]).toBeLessThan(255); - expect(rgba[1]).toBeGreaterThan(0); - expect(rgba[1]).toBeLessThan(255); - expect(rgba[2]).toEqual(0); - expect(rgba[3]).toEqual(255); - }); - primitive.show = false; - }); - - it('renders relative to center', function() { - // Camera is positioned at (10, 0, 0) and looking down -x axis. Without rtc the first point would be behind the camera. - var positionsTypedArray = new Float32Array([20.0, 0.0, 0.0, -20.0, 0.0, 0.0]); - var colorsTypedArray = new Uint8Array([255, 0, 0, 0, 255, 0]); - var boundingSphere = new BoundingSphere(new Cartesian3(-15.0, 0.0, 0.0), 20.0); - - var instance = new GeometryInstance({ - geometry : new PointGeometry({ - positionsTypedArray : positionsTypedArray, - colorsTypedArray : colorsTypedArray, - boundingSphere : boundingSphere - }) - }); - - var primitive = new Primitive({ - geometryInstances : instance, - appearance : new PointAppearance(), - asynchronous : false, - allowPicking : false, - cull : false, - rtcCenter : boundingSphere.center - }); - - scene.primitives.add(primitive); - - primitive.show = false; - expect(scene).toRender([0, 0, 0, 255]); - primitive.show = true; - expect(scene).toRender([255, 0, 0, 255]); - - scene.primitives.remove(primitive); - }); - -}, 'WebGL'); diff --git a/Specs/Scene/PointCloud3DTileContentSpec.js b/Specs/Scene/PointCloud3DTileContentSpec.js new file mode 100644 index 000000000000..ba0648fb55fc --- /dev/null +++ b/Specs/Scene/PointCloud3DTileContentSpec.js @@ -0,0 +1,693 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/PointCloud3DTileContent', + 'Core/Cartesian3', + 'Core/Color', + 'Core/ComponentDatatype', + 'Core/defined', + 'Core/HeadingPitchRange', + 'Core/HeadingPitchRoll', + 'Core/Math', + 'Core/Transforms', + 'Scene/Cesium3DTileStyle', + 'Scene/Expression', + 'Scene/PerspectiveFrustum', + 'Specs/Cesium3DTilesTester', + 'Specs/createScene', + 'ThirdParty/when' + ], function( + PointCloud3DTileContent, + Cartesian3, + Color, + ComponentDatatype, + defined, + HeadingPitchRange, + HeadingPitchRoll, + CesiumMath, + Transforms, + Cesium3DTileStyle, + Expression, + PerspectiveFrustum, + Cesium3DTilesTester, + createScene, + when) { + 'use strict'; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + var pointCloudRGBUrl = './Data/Cesium3DTiles/PointCloud/PointCloudRGB'; + var pointCloudRGBAUrl = './Data/Cesium3DTiles/PointCloud/PointCloudRGBA'; + var pointCloudRGB565Url = './Data/Cesium3DTiles/PointCloud/PointCloudRGB565'; + var pointCloudNoColorUrl = './Data/Cesium3DTiles/PointCloud/PointCloudNoColor'; + var pointCloudConstantColorUrl = './Data/Cesium3DTiles/PointCloud/PointCloudConstantColor'; + var pointCloudNormalsUrl = './Data/Cesium3DTiles/PointCloud/PointCloudNormals'; + var pointCloudNormalsOctEncodedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudNormalsOctEncoded'; + var pointCloudQuantizedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudQuantized'; + var pointCloudQuantizedOctEncodedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudQuantizedOctEncoded'; + var pointCloudWGS84Url = './Data/Cesium3DTiles/PointCloud/PointCloudWGS84'; + var pointCloudBatchedUrl = './Data/Cesium3DTiles/PointCloud/PointCloudBatched'; + var pointCloudWithPerPointPropertiesUrl = './Data/Cesium3DTiles/PointCloud/PointCloudWithPerPointProperties'; + var pointCloudWithTransformUrl = './Data/Cesium3DTiles/PointCloud/PointCloudWithTransform'; + + function setCamera(longitude, latitude) { + // Point the camera to the center of the tile + var center = Cartesian3.fromRadians(longitude, latitude, 5.0); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 5.0)); + } + + beforeAll(function() { + scene = createScene(); + scene.frameState.passes.render = true; + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + beforeEach(function() { + scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + + setCamera(centerLongitude, centerLatitude); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + it('throws with invalid version', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + version: 2 + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if featureTableJsonByteLength is 0', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJsonByteLength : 0 + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if the feature table does not contain POINTS_LENGTH', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POSITION : { + byteOffset : 0 + } + } + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if the feature table does not contain POSITION or POSITION_QUANTIZED', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POINTS_LENGTH : 1 + } + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if the positions are quantized and the feature table does not contain QUANTIZED_VOLUME_SCALE', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POINTS_LENGTH : 1, + POSITION_QUANTIZED : { + byteOffset : 0 + }, + QUANTIZED_VOLUME_OFFSET : [0.0, 0.0, 0.0] + } + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if the positions are quantized and the feature table does not contain QUANTIZED_VOLUME_OFFSET', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POINTS_LENGTH : 1, + POSITION_QUANTIZED : { + byteOffset : 0 + }, + QUANTIZED_VOLUME_SCALE : [1.0, 1.0, 1.0] + } + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('throws if the BATCH_ID semantic is defined but BATCHES_LENGTH is not', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POINTS_LENGTH : 2, + POSITION : [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], + BATCH_ID : [0, 1] + } + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'pnts'); + }); + + it('BATCH_ID semantic uses componentType of UNSIGNED_SHORT by default', function() { + var arrayBuffer = Cesium3DTilesTester.generatePointCloudTileBuffer({ + featureTableJson : { + POINTS_LENGTH : 2, + POSITION : [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], + BATCH_ID : [0, 1], + BATCH_LENGTH : 2 + } + }); + var content = Cesium3DTilesTester.loadTile(scene, arrayBuffer, 'pnts'); + expect(content._drawCommand._vertexArray._attributes[1].componentDatatype).toEqual(ComponentDatatype.UNSIGNED_SHORT); + }); + + it('resolves readyPromise', function() { + return Cesium3DTilesTester.resolvesReadyPromise(scene, pointCloudRGBUrl); + }); + + it('renders point cloud with rgb colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with rgba colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBAUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with rgb565 colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGB565Url).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with no colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudNoColorUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with constant colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudConstantColorUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudNormalsUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with oct encoded normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudNormalsOctEncodedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with quantized positions', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudQuantizedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with quantized positions and oct-encoded normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudQuantizedOctEncodedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud that are not defined relative to center', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWGS84Url).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with batch table', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with per-point properties', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithPerPointPropertiesUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders point cloud with tile transform', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithTransformUrl).then(function(tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + + var newLongitude = -1.31962; + var newLatitude = 0.698874; + var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude, 5.0); + var newHPR = new HeadingPitchRoll(); + var newTransform = Transforms.headingPitchRollToFixedFrame(newCenter, newHPR); + + // Update tile transform + tileset._root.transform = newTransform; + + // Move the camera to the new location + setCamera(newLongitude, newLatitude); + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders with debug color', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + var color; + expect(scene).toRenderAndCall(function(rgba) { + color = rgba; + }); + tileset.debugColorizeTiles = true; + expect(scene).notToRender(color); + tileset.debugColorizeTiles = false; + expect(scene).toRender(color); + }); + }); + + it('renders in CV', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + scene.morphToColumbusView(0.0); + setCamera(centerLongitude, centerLatitude); + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('renders in 2D', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + scene.morphTo2D(0.0); + setCamera(centerLongitude, centerLatitude); + tileset.maximumScreenSpaceError = 3; + Cesium3DTilesTester.expectRender(scene, tileset); + }); + }); + + it('picks', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + var content = tileset._root.content; + tileset.show = false; + expect(scene).toPickPrimitive(undefined); + tileset.show = true; + expect(scene).toPickAndCall(function(result) { + expect(result).toBeDefined(); + expect(result.primitive).toBe(tileset); + expect(result.content).toBe(content); + }); + }); + }); + + it('picks based on batchId', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + // Get the original color + var color; + expect(scene).toRenderAndCall(function(rgba) { + color = rgba; + }); + + // Change the color of the picked feature to yellow + expect(scene).toPickAndCall(function(first) { + expect(first).toBeDefined(); + + first.color = Color.clone(Color.YELLOW, first.color); + + // Expect the pixel color to be some shade of yellow + expect(scene).notToRender(color); + + // Turn show off. Expect a different feature to get picked. + first.show = false; + expect(scene).toPickAndCall(function(second) { + expect(second).toBeDefined(); + expect(second).not.toBe(first); + }); + }); + }); + }); + + it('point cloud without batch table works', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + var content = tileset._root.content; + expect(content.featuresLength).toBe(0); + expect(content.innerContents).toBeUndefined(); + expect(content.hasProperty(0, 'name')).toBe(false); + expect(content.getFeature(0)).toBeUndefined(); + }); + }); + + it('batched point cloud works', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + var content = tileset._root.content; + expect(content.featuresLength).toBe(8); + expect(content.innerContents).toBeUndefined(); + expect(content.hasProperty(0, 'name')).toBe(true); + expect(content.getFeature(0)).toBeDefined(); + }); + }); + + it('point cloud with per-point properties work', function() { + // When the batch table contains per-point properties, aka no batching, then a Cesium3DTileBatchTable is not + // created. There is no per-point show/color/pickId because the overhead is too high. Instead points are styled + // based on their properties, and these are not accessible from the API. + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithPerPointPropertiesUrl).then(function(tileset) { + var content = tileset._root.content; + expect(content.featuresLength).toBe(0); + expect(content.innerContents).toBeUndefined(); + expect(content.hasProperty(0, 'name')).toBe(false); + expect(content.getFeature(0)).toBeUndefined(); + }); + }); + + it('throws when calling getFeature with invalid index', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + var content = tileset._root.content; + expect(function(){ + content.getFeature(-1); + }).toThrowDeveloperError(); + expect(function(){ + content.getFeature(1000); + }).toThrowDeveloperError(); + expect(function(){ + content.getFeature(); + }).toThrowDeveloperError(); + }); + }); + + it('Supports back face culling when there are per-point normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + var content = tileset._root.content; + + // Get the number of picked sections with back face culling on + var pickedCountCulling = 0; + var pickedCount = 0; + var picked; + + expect(scene).toPickAndCall(function(result) { + // Set culling to true + content.backFaceCulling = true; + + expect(scene).toPickAndCall(function(result) { + picked = result; + }); + + /* jshint loopfunc: true */ + while (defined(picked)) { + picked.show = false; + expect(scene).toPickAndCall(function(result) { //eslint-disable-line no-loop-func + picked = result; + }); + ++pickedCountCulling; + } + + // Set the shows back to true + var length = content.featuresLength; + for (var i = 0; i < length; ++i) { + var feature = content.getFeature(i); + feature.show = true; + } + + // Set culling to false + content.backFaceCulling = false; + + expect(scene).toPickAndCall(function(result) { + picked = result; + }); + + /* jshint loopfunc: true */ + while (defined(picked)) { + picked.show = false; + expect(scene).toPickAndCall(function(result) { //eslint-disable-line no-loop-func + picked = result; + }); + ++pickedCount; + } + + expect(pickedCount).toBeGreaterThan(pickedCountCulling); + }); + }); + }); + + it('applies shader style', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithPerPointPropertiesUrl).then(function(tileset) { + var content = tileset._root.content; + + // Solid red color + tileset.style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(scene).toRender([255, 0, 0, 255]); + expect(content._styleTranslucent).toBe(false); + + // Applies translucency + tileset.style = new Cesium3DTileStyle({ + color : 'rgba(255, 0, 0, 0.005)' + }); + expect(scene).toRenderAndCall(function(rgba) { + // Pixel is a darker red + expect(rgba[0]).toBeLessThan(255); + expect(rgba[1]).toBe(0); + expect(rgba[2]).toBe(0); + expect(rgba[3]).toBe(255); + expect(content._styleTranslucent).toBe(true); + }); + + // Style with property + tileset.style = new Cesium3DTileStyle({ + color : 'color() * ${temperature}' + }); + expect(scene).toRenderAndCall(function(rgba) { + // Pixel color is some shade of gray + expect(rgba[0]).toBe(rgba[1]); + expect(rgba[0]).toBe(rgba[2]); + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + }); + + // When no conditions are met the default color is white + tileset.style = new Cesium3DTileStyle({ + color : { + conditions : [ + ['${secondaryColor}[0] > 1.0', 'color("red")'] // This condition will not be met + ] + } + }); + expect(scene).toRender([255, 255, 255, 255]); + + // Apply style with conditions + tileset.style = new Cesium3DTileStyle({ + color : { + conditions : [ + ['${temperature} < 0.1', 'color("#000099")'], + ['${temperature} < 0.2', 'color("#00cc99", 1.0)'], + ['${temperature} < 0.3', 'color("#66ff33", 0.5)'], + ['${temperature} < 0.4', 'rgba(255, 255, 0, 0.1)'], + ['${temperature} < 0.5', 'rgb(255, 128, 0)'], + ['${temperature} < 0.6', 'color("red")'], + ['${temperature} < 0.7', 'color("rgb(255, 102, 102)")'], + ['${temperature} < 0.8', 'hsl(0.875, 1.0, 0.6)'], + ['${temperature} < 0.9', 'hsla(0.83, 1.0, 0.5, 0.1)'], + ['true', 'color("#FFFFFF", 1.0)'] + ] + } + }); + expect(scene).notToRender([0, 0, 0, 255]); + + // Apply show style + tileset.style = new Cesium3DTileStyle({ + show : true + }); + expect(scene).notToRender([0, 0, 0, 255]); + + // Apply show style that hides all points + tileset.style = new Cesium3DTileStyle({ + show : false + }); + expect(scene).toRender([0, 0, 0, 255]); + + // Apply show style with property + tileset.style = new Cesium3DTileStyle({ + show : '${temperature} > 0.1' + }); + expect(scene).notToRender([0, 0, 0, 255]); + tileset.style = new Cesium3DTileStyle({ + show : '${temperature} > 1.0' + }); + expect(scene).toRender([0, 0, 0, 255]); + + // Apply style with point cloud semantics + tileset.style = new Cesium3DTileStyle({ + color : '${COLOR} / 2.0', + show : '${POSITION}[0] > 0.5' + }); + expect(scene).notToRender([0, 0, 0, 255]); + + // Apply pointSize style + tileset.style = new Cesium3DTileStyle({ + pointSize : 5.0 + }); + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + it('rebuilds shader style when expression changes', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithPerPointPropertiesUrl).then(function(tileset) { + // Solid red color + tileset.style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(scene).toRender([255, 0, 0, 255]); + + tileset.style.color = new Expression('color("lime")'); + tileset.makeStyleDirty(); + expect(scene).toRender([0, 255, 0, 255]); + }); + }); + + it('applies shader style to point cloud with normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudQuantizedOctEncodedUrl).then(function(tileset) { + tileset.style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + }); + }); + }); + + it('applies shader style to point cloud with normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudQuantizedOctEncodedUrl).then(function(tileset) { + tileset.style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + }); + }); + }); + + it('applies shader style to point cloud without colors', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudNoColorUrl).then(function(tileset) { + tileset.style = new Cesium3DTileStyle({ + color : 'color("red")' + }); + expect(scene).toRender([255, 0, 0, 255]); + }); + }); + + it('throws if style references the NORMAL semantic but the point cloud does not have per-point normals', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + var content = tileset._root.content; + expect(function() { + content.applyStyle(scene.frameState, new Cesium3DTileStyle({ + color : '${NORMAL}[0] > 0.5' + })); + }).toThrowRuntimeError(); + }); + }); + + it('throws when shader style reference a non-existent property', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudWithPerPointPropertiesUrl).then(function(tileset) { + var content = tileset._root.content; + expect(function() { + content.applyStyle(scene.frameState, new Cesium3DTileStyle({ + color : 'color() * ${non_existent_property}' + })); + }).toThrowRuntimeError(); + }); + }); + + it('does not apply shader style if the point cloud has a batch table', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + var content = tileset._root.content; + var shaderProgram = content._drawCommand.shaderProgram; + tileset.style = new Cesium3DTileStyle({ + color:'color("red")' + }); + expect(content._drawCommand.shaderProgram).toBe(shaderProgram); + + // Point cloud is styled through the batch table + expect(scene).notToRender([0, 0, 0, 255]); + }); + }); + + it('throws when shader style is invalid', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl).then(function(tileset) { + var content = tileset._root.content; + expect(function() { + content.applyStyle(scene.frameState, new Cesium3DTileStyle({ + show : '1 < "2"' + })); + }).toThrowRuntimeError(); + }); + }); + + it('gets memory usage', function() { + var promises = [ + Cesium3DTilesTester.loadTileset(scene, pointCloudNoColorUrl), + Cesium3DTilesTester.loadTileset(scene, pointCloudRGBUrl), + Cesium3DTilesTester.loadTileset(scene, pointCloudNormalsUrl), + Cesium3DTilesTester.loadTileset(scene, pointCloudQuantizedOctEncodedUrl) + ]; + + // 1000 points + var expectedGeometryMemory = [ + 1000 * 12, // 3 floats (xyz) + 1000 * 15, // 3 floats (xyz), 3 bytes (rgb) + 1000 * 27, // 3 floats (xyz), 3 bytes (rgb), 3 floats (normal) + 1000 * 11 // 3 shorts (quantized xyz), 3 bytes (rgb), 2 bytes (oct-encoded normal) + ]; + + return when.all(promises).then(function(tilesets) { + var length = tilesets.length; + for (var i = 0; i < length; ++i) { + var content = tilesets[i]._root.content; + expect(content.geometryByteLength).toEqual(expectedGeometryMemory[i]); + expect(content.texturesByteLength).toEqual(0); + } + }); + }); + + it('gets memory usage for batch point cloud', function() { + return Cesium3DTilesTester.loadTileset(scene, pointCloudBatchedUrl).then(function(tileset) { + var content = tileset._root.content; + + // Point cloud consists of positions, colors, normals, and batchIds + // 3 floats (xyz), 3 floats (normal), 1 byte (batchId) + var pointCloudGeometryMemory = 1000 * 25; + + // One RGBA byte pixel per feature + var batchTexturesByteLength = content.featuresLength * 4; + var pickTexturesByteLength = content.featuresLength * 4; + + // Features have not been picked or colored yet, so the batch table contribution is 0. + expect(content.geometryByteLength).toEqual(pointCloudGeometryMemory); + expect(content.texturesByteLength).toEqual(0); + expect(content.batchTableByteLength).toEqual(0); + + // Color a feature and expect the texture memory to increase + content.getFeature(0).color = Color.RED; + scene.renderForSpecs(); + expect(content.geometryByteLength).toEqual(pointCloudGeometryMemory); + expect(content.texturesByteLength).toEqual(0); + expect(content.batchTableByteLength).toEqual(batchTexturesByteLength); + + // Pick the tile and expect the texture memory to increase + scene.pickForSpecs(); + expect(content.geometryByteLength).toEqual(pointCloudGeometryMemory); + expect(content.texturesByteLength).toEqual(0); + expect(content.batchTableByteLength).toEqual(batchTexturesByteLength + pickTexturesByteLength); + }); + }); + + it('destroys', function() { + return Cesium3DTilesTester.tileDestroys(scene, pointCloudRGBUrl); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/PointPrimitiveCollectionSpec.js b/Specs/Scene/PointPrimitiveCollectionSpec.js index 3f00c48dddfc..b706146371c5 100644 --- a/Specs/Scene/PointPrimitiveCollectionSpec.js +++ b/Specs/Scene/PointPrimitiveCollectionSpec.js @@ -73,6 +73,7 @@ defineSuite([ expect(p.scaleByDistance).not.toBeDefined(); expect(p.translucencyByDistance).not.toBeDefined(); expect(p.distanceDisplayCondition).not.toBeDefined(); + expect(p.disableDepthTestDistance).toEqual(0.0); expect(p.id).not.toBeDefined(); }); @@ -103,6 +104,7 @@ defineSuite([ scaleByDistance : new NearFarScalar(1.0, 3.0, 1.0e6, 0.0), translucencyByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0), distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0), + disableDepthTestDistance : 10.0, id : 'id' }); @@ -121,6 +123,7 @@ defineSuite([ expect(p.scaleByDistance).toEqual(new NearFarScalar(1.0, 3.0, 1.0e6, 0.0)); expect(p.translucencyByDistance).toEqual(new NearFarScalar(1.0, 1.0, 1.0e6, 0.0)); expect(p.distanceDisplayCondition).toEqual(new DistanceDisplayCondition(10.0, 100.0)); + expect(p.disableDepthTestDistance).toEqual(10.0); expect(p.id).toEqual('id'); }); @@ -135,6 +138,7 @@ defineSuite([ p.scaleByDistance = new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0); p.translucencyByDistance = new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0); p.distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0); + p.disableDepthTestDistance = 10.0; expect(p.show).toEqual(false); expect(p.position).toEqual(new Cartesian3(1.0, 2.0, 3.0)); @@ -151,6 +155,7 @@ defineSuite([ expect(p.scaleByDistance).toEqual(new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)); expect(p.translucencyByDistance).toEqual(new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0)); expect(p.distanceDisplayCondition).toEqual(new DistanceDisplayCondition(10.0, 100.0)); + expect(p.disableDepthTestDistance).toEqual(10.0); }); it('is not destroyed', function() { @@ -309,6 +314,39 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('renders with disableDepthTestDistance', function() { + var p = pointPrimitives.add({ + position : new Cartesian3(-1.0, 0.0, 0.0), + pixelSize : 10.0, + color : Color.LIME + }); + pointPrimitives.add({ + position : Cartesian3.ZERO, + pixelSize : 10.0, + color : Color.BLUE + }); + + expect(scene).toRender([0 , 0, 255, 255]); + + p.disableDepthTestDistance = Number.POSITIVE_INFINITY; + expect(scene).toRender([0, 255, 0, 255]); + }); + + it('throws with new point primitive with invalid disableDepthTestDistance (< 0.0)', function() { + expect(function() { + pointPrimitives.add({ + disableDepthTestDistance : -1.0 + }); + }).toThrowDeveloperError(); + }); + + it('throws with disableDepthTestDistance set less than 0.0', function() { + var p = pointPrimitives.add(); + expect(function() { + p.disableDepthTestDistance = -1.0; + }).toThrowDeveloperError(); + }); + it('set a removed pointPrimitive property', function() { var p = pointPrimitives.add(); pointPrimitives.remove(p); diff --git a/Specs/Scene/PolylineCollectionSpec.js b/Specs/Scene/PolylineCollectionSpec.js index 3988ef54366c..0daf02c53669 100644 --- a/Specs/Scene/PolylineCollectionSpec.js +++ b/Specs/Scene/PolylineCollectionSpec.js @@ -7,6 +7,7 @@ defineSuite([ 'Core/DistanceDisplayCondition', 'Core/HeadingPitchRange', 'Core/Math', + 'Core/Matrix4', 'Scene/Camera', 'Scene/Material', 'Scene/SceneMode', @@ -19,6 +20,7 @@ defineSuite([ DistanceDisplayCondition, HeadingPitchRange, CesiumMath, + Matrix4, Camera, Material, SceneMode, @@ -1261,6 +1263,42 @@ defineSuite([ expect(scene).toRender([0, 0, 0, 255]); }); + it('renders with a distance display condition after creation', function() { + var near = 100.0; + var far = 10000.0; + + var line = polylines.add({ + positions : [{ + x : 10.0, + y : -10.0, + z : 0.0 + }, { + x : 10.0, + y : 10.0, + z : 0.0 + }], + width : 7 + }); + + scene.primitives.add(polylines); + scene.renderForSpecs(); + + line.distanceDisplayCondition = new DistanceDisplayCondition(near, far); + + var boundingSphere = line._boundingVolumeWC; + var center = boundingSphere.center; + var radius = boundingSphere.radius; + + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + near - 10.0)); + expect(scene).toRender([0, 0, 0, 255]); + + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + near + 1.0)); + expect(scene).notToRender([0, 0, 0, 255]); + + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + far + 10.0)); + expect(scene).toRender([0, 0, 0, 255]); + }); + it('changes polyline position size recreates vertex arrays', function() { var positions = []; for(var i = 0; i < 20; ++i){ @@ -1335,6 +1373,26 @@ defineSuite([ }); + it('renders with model matrix', function() { + polylines.add({ + positions : [{ + x : 0.0, + y : 0.0, + z : 0.0 + }, { + x : 0.0, + y : 1.0, + z : 0.0 + }] + }); + + expect(scene).toRender([0, 0, 0, 255]); + scene.primitives.add(polylines); + expect(scene).toRender([0, 0, 0, 255]); + polylines.modelMatrix = Matrix4.fromUniformScale(1000000.0, polylines.modelMatrix); + expect(scene).notToRender([0, 0, 0, 255]); + }); + it('is picked', function() { var p = polylines.add({ positions : [{ diff --git a/Specs/Scene/PrimitiveSpec.js b/Specs/Scene/PrimitiveSpec.js index ca981609044b..e80109ec8d9d 100644 --- a/Specs/Scene/PrimitiveSpec.js +++ b/Specs/Scene/PrimitiveSpec.js @@ -156,6 +156,7 @@ defineSuite([ primitive = new Primitive(); expect(primitive.geometryInstances).not.toBeDefined(); expect(primitive.appearance).not.toBeDefined(); + expect(primitive.depthFailAppearance).not.toBeDefined(); expect(primitive.modelMatrix).toEqual(Matrix4.IDENTITY); expect(primitive.show).toEqual(true); expect(primitive.vertexCacheOptimize).toEqual(false); @@ -171,11 +172,13 @@ defineSuite([ it('Constructs with options', function() { var geometryInstances = {}; var appearance = {}; + var depthFailAppearance = {}; var modelMatrix = Matrix4.fromUniformScale(5.0); primitive = new Primitive({ geometryInstances : geometryInstances, appearance : appearance, + depthFailAppearance : depthFailAppearance, modelMatrix : modelMatrix, show : false, vertexCacheOptimize : true, @@ -190,6 +193,7 @@ defineSuite([ expect(primitive.geometryInstances).toEqual(geometryInstances); expect(primitive.appearance).toEqual(appearance); + expect(primitive.depthFailAppearance).toEqual(depthFailAppearance); expect(primitive.modelMatrix).toEqual(modelMatrix); expect(primitive.show).toEqual(false); expect(primitive.vertexCacheOptimize).toEqual(true); @@ -433,6 +437,117 @@ defineSuite([ scene._camera = camera; }); + it('renders with depth fail appearance', function() { + var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); + var translation = Cartesian3.multiplyByScalar(Cartesian3.normalize(ellipsoid.cartographicToCartesian(Rectangle.center(rect)), new Cartesian3()), 100.0, new Cartesian3()); + var rectInstance = new GeometryInstance({ + geometry : new RectangleGeometry({ + vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT, + ellipsoid : ellipsoid, + rectangle : rect + }), + modelMatrix : Matrix4.fromTranslation(translation, new Matrix4()), + id : 'rect', + attributes : { + color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0) + } + }); + var p0 = new Primitive({ + geometryInstances : rectInstance, + appearance : new PerInstanceColorAppearance({ + translucent : false + }), + asynchronous : false + }); + + var rectInstance2 = new GeometryInstance({ + geometry : new RectangleGeometry({ + vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT, + ellipsoid : ellipsoid, + rectangle : rect + }), + id : 'rect2', + attributes : { + color : new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), + depthFailColor : new ColorGeometryInstanceAttribute(1.0, 0.0, 1.0, 1.0) + } + }); + var p1 = new Primitive({ + geometryInstances : rectInstance2, + appearance : new PerInstanceColorAppearance({ + translucent : false + }), + depthFailAppearance : new PerInstanceColorAppearance({ + translucent : false + }), + asynchronous : false + }); + + scene.primitives.add(p0); + scene.primitives.add(p1); + scene.camera.setView({ destination : rect }); + scene.renderForSpecs(); + + expect(scene).toRender([255, 0, 255, 255]); + }); + + it('pick with depth fail appearance', function() { + var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); + var translation = Cartesian3.multiplyByScalar(Cartesian3.normalize(ellipsoid.cartographicToCartesian(Rectangle.center(rect)), new Cartesian3()), 100.0, new Cartesian3()); + var rectInstance = new GeometryInstance({ + geometry : new RectangleGeometry({ + vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT, + ellipsoid : ellipsoid, + rectangle : rect + }), + modelMatrix : Matrix4.fromTranslation(translation, new Matrix4()), + id : 'rect', + attributes : { + color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0) + } + }); + var p0 = new Primitive({ + geometryInstances : rectInstance, + appearance : new PerInstanceColorAppearance({ + translucent : false + }), + asynchronous : false + }); + + var rectInstance2 = new GeometryInstance({ + geometry : new RectangleGeometry({ + vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT, + ellipsoid : ellipsoid, + rectangle : rect + }), + id : 'rect2', + attributes : { + color : new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), + depthFailColor : new ColorGeometryInstanceAttribute(1.0, 0.0, 1.0, 1.0) + } + }); + var p1 = new Primitive({ + geometryInstances : rectInstance2, + appearance : new PerInstanceColorAppearance({ + translucent : false + }), + depthFailAppearance : new PerInstanceColorAppearance({ + translucent : false + }), + asynchronous : false + }); + + scene.primitives.add(p0); + scene.primitives.add(p1); + scene.camera.setView({ destination : rect }); + scene.renderForSpecs(); + + expect(scene).toPickAndCall(function(result) { + expect(result.primitive).toEqual(p1); + expect(result.id).toEqual('rect2'); + }); + }); + it('RTC throws with more than one instance', function() { expect(function() { return new Primitive({ diff --git a/Specs/Scene/SceneSpec.js b/Specs/Scene/SceneSpec.js index 03beddb71f27..9f3c1fd1cfe8 100644 --- a/Specs/Scene/SceneSpec.js +++ b/Specs/Scene/SceneSpec.js @@ -33,6 +33,7 @@ defineSuite([ 'Scene/Scene', 'Scene/ScreenSpaceCameraController', 'Scene/TweenCollection', + 'Scene/SceneTransforms', 'Specs/createCanvas', 'Specs/createScene', 'Specs/equals', @@ -72,6 +73,7 @@ defineSuite([ Scene, ScreenSpaceCameraController, TweenCollection, + SceneTransforms, createCanvas, createScene, equals, @@ -382,23 +384,6 @@ defineSuite([ }); }); - it('renders fast path with no translucent primitives', function() { - var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); - - var rectanglePrimitive = createRectangle(rectangle, 1000.0); - rectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); - - var primitives = scene.primitives; - primitives.add(rectanglePrimitive); - - scene.camera.setView({ destination : rectangle }); - expect(scene).toRenderAndCall(function(rgba) { - expect(rgba[0]).not.toEqual(0); - expect(rgba[1]).toEqual(0); - expect(rgba[2]).toEqual(0); - }); - }); - it('renders with OIT and without FXAA', function() { var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); @@ -838,6 +823,41 @@ defineSuite([ }); }); + it('pickPosition caches results per frame',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); + spyOn(SceneTransforms, 'transformWindowToDrawingBuffer').and.callThrough(); + + expect(scene).toRenderAndCall(function() { + scene.pickPosition(windowPosition); + expect(SceneTransforms.transformWindowToDrawingBuffer).toHaveBeenCalled(); + + scene.pickPosition(windowPosition); + expect(SceneTransforms.transformWindowToDrawingBuffer.calls.count()).toEqual(1); + + 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() { + scene.pickPosition(windowPosition); + expect(SceneTransforms.transformWindowToDrawingBuffer.calls.count()).toEqual(2); + + scene.pickPosition(windowPosition); + expect(SceneTransforms.transformWindowToDrawingBuffer.calls.count()).toEqual(2); + }); + }); + it('pickPosition throws without windowPosition', function() { expect(function() { scene.pickPosition(); @@ -1078,4 +1098,28 @@ defineSuite([ } s.destroyForSpecs(); }); + + it('throws when minimumDisableDepthTestDistance is set less than 0.0', function() { + expect(function() { + scene.minimumDisableDepthTestDistance = -1.0; + }).toThrowDeveloperError(); + }); + + it('converts to canvas coordinates',function(){ + var mockPosition = new Cartesian3(); + spyOn(SceneTransforms, 'wgs84ToWindowCoordinates'); + scene.cartesianToCanvasCoordinates(mockPosition); + + expect(SceneTransforms.wgs84ToWindowCoordinates).toHaveBeenCalledWith(scene, mockPosition, undefined); + }); + + it('converts to canvas coordinates and return it in a variable',function(){ + var result = new Cartesian2(); + var mockPosition = new Cartesian3(); + spyOn(SceneTransforms, 'wgs84ToWindowCoordinates'); + scene.cartesianToCanvasCoordinates(mockPosition, result); + + expect(SceneTransforms.wgs84ToWindowCoordinates).toHaveBeenCalledWith(scene, mockPosition, result); + }); + }, 'WebGL'); diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index 7033e26fce2c..bca7e6a9cfdf 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -494,9 +494,7 @@ defineSuite([ // Move the camera into the shadowed area scene.camera.moveRight(0.2); - var shadowedColor; renderAndCall(function(rgba) { - shadowedColor = rgba; expect(rgba).not.toEqual(backgroundColor); expect(rgba).not.toEqual(unshadowedColor); }); @@ -712,7 +710,6 @@ defineSuite([ ]; for (var i = 0; i < 6; ++i) { - /* jshint loopfunc: true */ var box = scene.primitives.add(Model.fromGltf({ url : boxUrl, modelMatrix : Transforms.headingPitchRollToFixedFrame(origins[i], new HeadingPitchRoll()), @@ -725,14 +722,14 @@ defineSuite([ // Render without shadows scene.shadowMap.enabled = false; var unshadowedColor; - renderAndCall(function(rgba) { + renderAndCall(function(rgba) { //eslint-disable-line no-loop-func unshadowedColor = rgba; expect(rgba).not.toEqual(backgroundColor); }); // Render with shadows scene.shadowMap.enabled = true; - renderAndCall(function(rgba) { + renderAndCall(function(rgba) { //eslint-disable-line no-loop-func expect(rgba).not.toEqual(backgroundColor); expect(rgba).not.toEqual(unshadowedColor); }); diff --git a/Specs/Scene/SingleTileImageryProviderSpec.js b/Specs/Scene/SingleTileImageryProviderSpec.js index b4d27a76a8cb..609acb717392 100644 --- a/Specs/Scene/SingleTileImageryProviderSpec.js +++ b/Specs/Scene/SingleTileImageryProviderSpec.js @@ -166,13 +166,13 @@ defineSuite([ it('routes requests through a proxy if one is specified', function() { var imageUrl = 'Data/Images/Red16x16.png'; + var proxy = new DefaultProxy('/proxy/'); spyOn(loadImage, 'createImage').and.callFake(function(url, crossOrigin, deferred) { expect(url.indexOf(proxy.getURL('Data/Images/Red16x16.png'))).toEqual(0); loadImage.defaultCreateImage(url, crossOrigin, deferred); }); - var proxy = new DefaultProxy('/proxy/'); var provider = new SingleTileImageryProvider({ url : imageUrl, proxy : proxy diff --git a/Specs/Scene/StyleExpressionSpec.js b/Specs/Scene/StyleExpressionSpec.js new file mode 100644 index 000000000000..c48a33b53ccd --- /dev/null +++ b/Specs/Scene/StyleExpressionSpec.js @@ -0,0 +1,29 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/StyleExpression' + ], function( + StyleExpression) { + 'use strict'; + + var frameState = {}; + + function MockFeature() { + } + + MockFeature.prototype.getProperty = function(name) { + return undefined; + }; + + it('throws', function() { + var expression = new StyleExpression(); + var feature = new MockFeature(); + + expect(function() { + return expression.evaluate(frameState, feature); + }).toThrowDeveloperError(); + + expect(function() { + return expression.evaluateColor(frameState, feature); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/Scene/SunSpec.js b/Specs/Scene/SunSpec.js index 6d8d3515a9c4..fafe8d9691f2 100644 --- a/Specs/Scene/SunSpec.js +++ b/Specs/Scene/SunSpec.js @@ -87,7 +87,7 @@ defineSuite([ viewSun(scene.camera, scene.context.uniformState); scene.frameState.passes.render = false; - var command = scene.sun.update(scene); + var command = scene.sun.update(scene.frameState, scene._passState); expect(command).not.toBeDefined(); }); diff --git a/Specs/Scene/TileBoundingBoxSpec.js b/Specs/Scene/TileBoundingRegionSpec.js similarity index 57% rename from Specs/Scene/TileBoundingBoxSpec.js rename to Specs/Scene/TileBoundingRegionSpec.js index 0eb35f667f5b..b2fe4de76a83 100644 --- a/Specs/Scene/TileBoundingBoxSpec.js +++ b/Specs/Scene/TileBoundingRegionSpec.js @@ -1,28 +1,39 @@ /*global defineSuite*/ defineSuite([ - 'Scene/TileBoundingBox', + 'Scene/TileBoundingRegion', 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Cartographic', + 'Core/Color', 'Core/Ellipsoid', 'Core/GeographicTilingScheme', + 'Core/Intersect', 'Core/Math', + 'Core/Plane', 'Core/Rectangle', 'Scene/SceneMode', 'Specs/createFrameState' ], function( - TileBoundingBox, + TileBoundingRegion, Cartesian2, Cartesian3, Cartographic, + Color, Ellipsoid, GeographicTilingScheme, + Intersect, CesiumMath, + Plane, Rectangle, SceneMode, createFrameState) { 'use strict'; + var boundingVolumeRegion = [0.0, 0.0, 1.0, 1.0, 0, 1]; + var regionBox = boundingVolumeRegion.slice(0, 4); + var rectangle = new Rectangle(regionBox[0], regionBox[1], regionBox[2], regionBox[3]); + var tileBoundingRegion = new TileBoundingRegion({maximumHeight: boundingVolumeRegion[5], minimumHeight: boundingVolumeRegion[4], rectangle: rectangle}); + var frameState; var camera; @@ -32,11 +43,49 @@ defineSuite([ }); it('throws when options.rectangle is undefined', function() { - expect(function(){ - return new TileBoundingBox(); + expect(function() { + return new TileBoundingRegion(); }).toThrowDeveloperError(); }); + it('can be instantiated with rectangle and heights', function() { + var minimumHeight = boundingVolumeRegion[4]; + var maximumHeight = boundingVolumeRegion[5]; + var tbr = new TileBoundingRegion({maximumHeight: maximumHeight, minimumHeight: minimumHeight, rectangle: rectangle}); + expect(tbr).toBeDefined(); + expect(tbr.boundingVolume).toBeDefined(); + expect(tbr.boundingSphere).toBeDefined(); + expect(tbr.rectangle).toEqual(rectangle); + expect(tbr.minimumHeight).toEqual(minimumHeight); + expect(tbr.maximumHeight).toEqual(maximumHeight); + }); + + it('can be instantiated with only a rectangle', function() { + var tbr = new TileBoundingRegion({rectangle: rectangle}); + expect(tbr).toBeDefined(); + expect(tbr.boundingVolume).toBeDefined(); + expect(tbr.boundingSphere).toBeDefined(); + expect(tbr.rectangle).toEqual(rectangle); + expect(tbr.minimumHeight).toBeDefined(); + expect(tbr.maximumHeight).toBeDefined(); + }); + + it('distanceToCamera throws when frameState is undefined', function() { + expect(function() { + return tileBoundingRegion.distanceToCamera(); + }).toThrowDeveloperError(); + }); + + it('distance to camera is 0 when camera is inside bounding region', function() { + camera.position = Cartesian3.fromRadians(regionBox[0] + CesiumMath.EPSILON6, regionBox[1], 0); + expect(tileBoundingRegion.distanceToCamera(frameState)).toEqual(0.0); + }); + + it('distance to camera is correct when camera is outside bounding region', function() { + camera.position = Cartesian3.fromRadians(regionBox[0], regionBox[1], 2.0); + expect(tileBoundingRegion.distanceToCamera(frameState)).toEqualEpsilon(1.0, CesiumMath.EPSILON6); + }); + it('distanceToCamera', function() { var offset = 0.0001; var west = -0.001; @@ -44,7 +93,7 @@ defineSuite([ var east = 0.001; var north = 0.001; - var tile = new TileBoundingBox({ + var tile = new TileBoundingRegion({ rectangle : new Rectangle(west, south, east, north), minimumHeight : 0.0, maximumHeight : 10.0 @@ -81,7 +130,7 @@ defineSuite([ cameraPositionCartographic.south -= CesiumMath.EPSILON8; - var tile = new TileBoundingBox({ + var tile = new TileBoundingRegion({ rectangle : rectangle, minimumHeight : 0.0, maximumHeight : 10.0 @@ -92,7 +141,7 @@ defineSuite([ expect(tile.distanceToCamera(frameState)).toBeLessThan(CesiumMath.EPSILON8 * ellipsoid.maximumRadius); }); - it('distanceToCamera close to north plane at the southern hemisphere', function() { + it('distanceToCamera close to north plane at the southern hemisphere', function() { var ellipsoid = Ellipsoid.WGS84; var tilingScheme = new GeographicTilingScheme({ellipsoid : ellipsoid}); @@ -102,7 +151,7 @@ defineSuite([ cameraPositionCartographic.north += CesiumMath.EPSILON8; - var tile = new TileBoundingBox({ + var tile = new TileBoundingRegion({ rectangle : rectangle, minimumHeight : 0.0, maximumHeight : 10.0 @@ -122,7 +171,7 @@ defineSuite([ var east = 0.001; var north = 0.001; - var tile = new TileBoundingBox({ + var tile = new TileBoundingRegion({ rectangle : new Rectangle(west, south, east, north), minimumHeight : 0.0, maximumHeight : 10.0 @@ -144,4 +193,32 @@ defineSuite([ camera.position = Cartesian3.fromRadians(position3D.longitude, position3D.latitude); expect(tile.distanceToCamera(frameState)).toEqualEpsilon(expectedDistance, 10.0); }); + + it('createDebugVolume throws when color is undefined', function() { + expect(function() { + return tileBoundingRegion.createDebugVolume(); + }).toThrowDeveloperError(); + }); + + it('can create a debug volume', function() { + var debugVolume = tileBoundingRegion.createDebugVolume(Color.BLUE); + expect(debugVolume).toBeDefined(); + }); + + it('intersectPlane throws when plane is undefined', function() { + expect(function() { + return tileBoundingRegion.intersectPlane(); + }).toThrowDeveloperError(); + }); + + it('intersects plane', function() { + var normal = new Cartesian3(); + Cartesian3.normalize(Cartesian3.fromRadians(0.0, 0.0, 1.0), normal); + var distanceFromCenter = Cartesian3.distance( + new Cartesian3(0.0, 0.0, 0.0), + Cartesian3.fromRadians(0.0, 0.0, 0.0) + ); + var plane = new Plane(normal, -distanceFromCenter); + expect(tileBoundingRegion.intersectPlane(plane)).toEqual(Intersect.INTERSECTING); + }); }); diff --git a/Specs/Scene/TileBoundingSphereSpec.js b/Specs/Scene/TileBoundingSphereSpec.js new file mode 100644 index 000000000000..bdf9482b7c4c --- /dev/null +++ b/Specs/Scene/TileBoundingSphereSpec.js @@ -0,0 +1,73 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/TileBoundingSphere', + 'Core/Cartesian3', + 'Core/Color', + 'Core/Intersect', + 'Core/Math', + 'Core/Plane', + 'Specs/createFrameState' + ], function( + TileBoundingSphere, + Cartesian3, + Color, + Intersect, + CesiumMath, + Plane, + createFrameState) { + 'use strict'; + + var tileBoundingSphere = new TileBoundingSphere(new Cartesian3(0.0, 0.0, 0.0), 1.0); + var frameState = createFrameState(); + + it('can be instantiated with center and radius', function() { + var center = new Cartesian3(0.0, 0.0, 0.0); + var radius = 1.0; + var tbs = new TileBoundingSphere(center, radius); + expect(tbs).toBeDefined(); + expect(tbs.boundingVolume).toBeDefined(); + expect(tbs.boundingSphere).toBeDefined(); + expect(tbs.center).toEqual(center); + expect(tbs.radius).toEqual(radius); + }); + + it('createDebugVolume throws when color is undefined', function() { + expect(function() { + return tileBoundingSphere.createDebugVolume(); + }).toThrowDeveloperError(); + }); + + it('can create a debug volume', function() { + var debugVolume = tileBoundingSphere.createDebugVolume(Color.BLUE); + expect(debugVolume).toBeDefined(); + }); + + it('distanceToCamera throws when frameState is undefined', function() { + expect(function() { + return tileBoundingSphere.distanceToCamera(); + }).toThrowDeveloperError(); + }); + + it('distance to camera is 0 when camera is inside bounding sphere', function() { + frameState.camera.position = new Cartesian3(0.0, 0.0, 0.0); + expect(tileBoundingSphere.distanceToCamera(frameState)).toEqual(0.0); + }); + + it('distance to camera is correct when camera is outside bounding region', function() { + frameState.camera.position = new Cartesian3(0.0, 2.0, 0.0); + expect(tileBoundingSphere.distanceToCamera(frameState)).toEqual(1.0); + }); + + it('intersectPlane throws when plane is undefined', function() { + expect(function() { + return tileBoundingSphere.intersectPlane(); + }).toThrowDeveloperError(); + }); + + it('intersects plane', function() { + var normal = new Cartesian3(0.0, 0.0, 1.0); + var plane = new Plane(normal, CesiumMath.EPSILON6); + expect(tileBoundingSphere.intersectPlane(plane)).toEqual(Intersect.INTERSECTING); + }); + +}); diff --git a/Specs/Scene/TileBoundingVolumeSpec.js b/Specs/Scene/TileBoundingVolumeSpec.js new file mode 100644 index 000000000000..cf64783bb482 --- /dev/null +++ b/Specs/Scene/TileBoundingVolumeSpec.js @@ -0,0 +1,20 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/TileBoundingVolume' + ], function( + TileBoundingVolume) { + 'use strict'; + + it('throws', function() { + var boundingVolume = new TileBoundingVolume(); + expect(function() { + boundingVolume.createDebugVolume(); + }).toThrowDeveloperError(); + expect(function() { + boundingVolume.distanceToCamera(); + }).toThrowDeveloperError(); + expect(function() { + boundingVolume.intersectPlane(); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/Scene/TileOrientedBoundingBoxSpec.js b/Specs/Scene/TileOrientedBoundingBoxSpec.js new file mode 100644 index 000000000000..e7f8bb799746 --- /dev/null +++ b/Specs/Scene/TileOrientedBoundingBoxSpec.js @@ -0,0 +1,109 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/TileOrientedBoundingBox', + 'Core/Cartesian3', + 'Core/Color', + 'Core/Intersect', + 'Core/Math', + 'Core/Matrix3', + 'Core/Plane', + 'Specs/createFrameState' + ], function( + TileOrientedBoundingBox, + Cartesian3, + Color, + Intersect, + CesiumMath, + Matrix3, + Plane, + createFrameState) { + 'use strict'; + + var center = new Cartesian3(0.0, 0.0, 0.0); + var halfAxes = Matrix3.fromScale(new Cartesian3(0.5, 0.5, 0.5), new Matrix3()); + var tileBoundingVolume = new TileOrientedBoundingBox(center, halfAxes); + + var frameState = createFrameState(); + + it('can be instantiated with center and half-axes', function() { + expect(tileBoundingVolume.boundingVolume.center).toEqual(center); + expect(tileBoundingVolume.boundingVolume.halfAxes).toEqual(halfAxes); + expect(tileBoundingVolume.boundingSphere.center).toEqual(center); + expect(tileBoundingVolume.boundingSphere.radius).toBeGreaterThan(0.5); + expect(tileBoundingVolume.boundingSphere.radius).toBeLessThan(1.0); + }); + + it('createDebugVolume throws when color is undefined', function() { + expect(function() { + return tileBoundingVolume.createDebugVolume(); + }).toThrowDeveloperError(); + }); + + it('can create debug volume', function() { + expect(tileBoundingVolume.createDebugVolume(Color.BLUE)).toBeDefined(); + }); + + it('distanceToCamera throws when frameState is undefined', function() { + expect(function() { + return tileBoundingVolume.distanceToCamera(); + }).toThrowDeveloperError(); + }); + + it('has distance 0 to camera if camera is inside', function() { + frameState.camera.position = new Cartesian3(0.0, 0.0, 0.0); + expect(tileBoundingVolume.distanceToCamera(frameState)).toEqual(0.0); + + frameState.camera.position = new Cartesian3(-0.5, -0.5, -0.5); + expect(tileBoundingVolume.distanceToCamera(frameState)).toEqual(0.0); + frameState.camera.position = new Cartesian3(0.5, 0.5, 0.5); + expect(tileBoundingVolume.distanceToCamera(frameState)).toEqual(0.0); + }); + + it('has correct distance to camera if camera is slightly outside box', function() { + var eps6 = CesiumMath.EPSILON6; + frameState.camera.position = new Cartesian3(0.5 + eps6, 0.5, 0.5); + expect(tileBoundingVolume.distanceToCamera(frameState)).not.toEqual(0.0); + frameState.camera.position = new Cartesian3(-0.5, -0.5, -0.5 - eps6); + expect(tileBoundingVolume.distanceToCamera(frameState)).not.toEqual(0.0); + frameState.camera.position = new Cartesian3(100.5, 100.5, 100.5); + expect(tileBoundingVolume.distanceToCamera(frameState)).toEqual(Math.sqrt(30000.0)); + }); + + it('has correct distance to camera for large distances', function() { + frameState.camera.position = new Cartesian3(2170456.713380141, -36351235.19646463, 28403328.27058654); + expect(tileBoundingVolume.distanceToCamera(frameState)).toEqualEpsilon(46183029.05370139, CesiumMath.EPSILON6); + }); + + it('intersectPlane throws when plane is undefined', function() { + expect(function() { + return tileBoundingVolume.intersectPlane(); + }).toThrowDeveloperError(); + }); + + it('intersects plane', function() { + var plane = new Plane(Cartesian3.UNIT_X, 0.0); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INTERSECTING); + plane = new Plane(Cartesian3.UNIT_X, 0.5 - CesiumMath.EPSILON6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INTERSECTING); + plane = new Plane(Cartesian3.UNIT_X, -0.5 + CesiumMath.EPSILON6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INTERSECTING); + }); + + it('does not intersect plane', function() { + var eps6 = CesiumMath.EPSILON6; + var plane = new Plane(Cartesian3.UNIT_X, 0.5 + eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INSIDE); + plane = new Plane(Cartesian3.UNIT_Y, 0.5 + eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INSIDE); + plane = new Plane(Cartesian3.UNIT_Z, 0.5 + eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.INSIDE); + + plane = new Plane(Cartesian3.UNIT_X, -0.5 - eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.OUTSIDE); + plane = new Plane(Cartesian3.UNIT_Y, -0.5 - eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.OUTSIDE); + plane = new Plane(Cartesian3.UNIT_Z, -0.5 - eps6); + expect(tileBoundingVolume.intersectPlane(plane)).toEqual(Intersect.OUTSIDE); + }); + +}); diff --git a/Specs/Scene/Tileset3DTileContentSpec.js b/Specs/Scene/Tileset3DTileContentSpec.js new file mode 100644 index 000000000000..36b536d93db9 --- /dev/null +++ b/Specs/Scene/Tileset3DTileContentSpec.js @@ -0,0 +1,69 @@ +/*global defineSuite*/ +defineSuite([ + 'Scene/Tileset3DTileContent', + 'Core/Cartesian3', + 'Core/HeadingPitchRange', + 'Scene/Cesium3DTileContentState', + 'Specs/Cesium3DTilesTester', + 'Specs/createScene' + ], function( + Tileset3DTileContent, + Cartesian3, + HeadingPitchRange, + Cesium3DTileContentState, + Cesium3DTilesTester, + createScene) { + 'use strict'; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + var tilesetOfTilesetsUrl = './Data/Cesium3DTiles/Tilesets/TilesetOfTilesets/'; + + beforeAll(function() { + scene = createScene(); + + // Point the camera at the center and far enough way to only load the root tile + var center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 100.0)); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + it('resolves readyPromise', function() { + return Cesium3DTilesTester.resolvesReadyPromise(scene, tilesetOfTilesetsUrl); + }); + + it('destroys', function() { + return Cesium3DTilesTester.tileDestroys(scene, tilesetOfTilesetsUrl); + }); + + it('gets properties', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var tile = tileset._root; + var content = tile.content; + expect(content.featuresLength).toBe(0); + expect(content.pointsLength).toBe(0); + expect(content.trianglesLength).toBe(0); + expect(content.geometryByteLength).toBe(0); + expect(content.texturesByteLength).toBe(0); + expect(content.batchTableByteLength).toBe(0); + expect(content.innerContents).toBeUndefined(); + expect(content.readyPromise).toBeDefined(); + expect(content.tileset).toBe(tileset); + expect(content.tile).toBe(tile); + expect(content.url).toBeDefined(); + expect(content.batchTable).toBeUndefined(); + expect(content.hasProperty(0, 'name')).toBe(false); + expect(content.getFeature(0)).toBeUndefined(); + }); + }); + +}, 'WebGL'); diff --git a/Specs/Scene/UrlTemplateImageryProviderSpec.js b/Specs/Scene/UrlTemplateImageryProviderSpec.js index 41b3af874c21..f3fbe8d77e9e 100644 --- a/Specs/Scene/UrlTemplateImageryProviderSpec.js +++ b/Specs/Scene/UrlTemplateImageryProviderSpec.js @@ -7,6 +7,7 @@ defineSuite([ 'Core/loadImage', 'Core/Math', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorProjection', 'Core/WebMercatorTilingScheme', 'Scene/GetFeatureInfoFormat', @@ -24,6 +25,7 @@ defineSuite([ loadImage, CesiumMath, Rectangle, + RequestScheduler, WebMercatorProjection, WebMercatorTilingScheme, GetFeatureInfoFormat, @@ -35,6 +37,10 @@ defineSuite([ when) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; }); @@ -204,6 +210,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -224,6 +233,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Scene/WebMapServiceImageryProviderSpec.js b/Specs/Scene/WebMapServiceImageryProviderSpec.js index d55572ed361e..3f729fd64690 100644 --- a/Specs/Scene/WebMapServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapServiceImageryProviderSpec.js @@ -10,6 +10,7 @@ defineSuite([ 'Core/Math', 'Core/queryToObject', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorTilingScheme', 'Scene/GetFeatureInfoFormat', 'Scene/Imagery', @@ -30,6 +31,7 @@ defineSuite([ CesiumMath, queryToObject, Rectangle, + RequestScheduler, WebMercatorTilingScheme, GetFeatureInfoFormat, Imagery, @@ -41,6 +43,10 @@ defineSuite([ Uri) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; loadWithXhr.load = loadWithXhr.defaultLoad; @@ -133,9 +139,10 @@ defineSuite([ deferred.resolve(true); }); - provider.requestImage(0, 0, 0); + return provider.requestImage(0, 0, 0).then(function(image) { + expect(loadImage.createImage).toHaveBeenCalled(); + }); - expect(loadImage.createImage).toHaveBeenCalled(); }); }); @@ -182,9 +189,9 @@ defineSuite([ deferred.resolve(true); }); - provider.requestImage(0, 0, 0); - - expect(loadImage.createImage).toHaveBeenCalled(); + return provider.requestImage(0, 0, 0).then(function(image) { + expect(loadImage.createImage).toHaveBeenCalled(); + }); }); }); @@ -200,16 +207,15 @@ defineSuite([ spyOn(loadImage, 'createImage').and.callFake(function(url, crossOrigin, deferred) { var questionMarkCount = url.match(/\?/g).length; expect(questionMarkCount).toEqual(1); - expect(url).not.toContain('&&'); // Don't need to actually load image, but satisfy the request. deferred.resolve(true); }); - provider.requestImage(0, 0, 0); - - expect(loadImage.createImage).toHaveBeenCalled(); + return provider.requestImage(0, 0, 0).then(function(image) { + expect(loadImage.createImage).toHaveBeenCalled(); + }); }); }); @@ -566,11 +572,14 @@ defineSuite([ expect(params.format).toEqual('foo'); expect(params.format).not.toEqual('image/jpeg'); - }); - provider.requestImage(0, 0, 0); + // Just return any old image. + loadImage.defaultCreateImage('Data/Images/Red16x16.png', crossOrigin, deferred); + }); - expect(loadImage.createImage).toHaveBeenCalled(); + return provider.requestImage(0, 0, 0).then(function(image) { + expect(loadImage.createImage).toHaveBeenCalled(); + }); }); }); @@ -700,6 +709,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -717,6 +729,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js index 078a18542a65..11398eba7f48 100644 --- a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/GeographicTilingScheme', 'Core/loadImage', 'Core/queryToObject', + 'Core/RequestScheduler', 'Core/WebMercatorTilingScheme', 'Scene/Imagery', 'Scene/ImageryLayer', @@ -20,6 +21,7 @@ defineSuite([ GeographicTilingScheme, loadImage, queryToObject, + RequestScheduler, WebMercatorTilingScheme, Imagery, ImageryLayer, @@ -29,6 +31,10 @@ defineSuite([ Uri) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; }); @@ -361,6 +367,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -381,6 +390,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js index 77800b4d2983..041b175ca4cf 100644 --- a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js +++ b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js @@ -5,6 +5,7 @@ defineSuite([ 'Core/loadImage', 'Core/Math', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorTilingScheme', 'Scene/Imagery', 'Scene/ImageryLayer', @@ -17,6 +18,7 @@ defineSuite([ loadImage, CesiumMath, Rectangle, + RequestScheduler, WebMercatorTilingScheme, Imagery, ImageryLayer, @@ -25,6 +27,10 @@ defineSuite([ pollToPromise) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; }); @@ -223,6 +229,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -243,6 +252,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Scene/createTileMapServiceImageryProviderSpec.js b/Specs/Scene/createTileMapServiceImageryProviderSpec.js index cacd63f62166..fb1cfc523695 100644 --- a/Specs/Scene/createTileMapServiceImageryProviderSpec.js +++ b/Specs/Scene/createTileMapServiceImageryProviderSpec.js @@ -11,6 +11,7 @@ defineSuite([ 'Core/loadWithXhr', 'Core/Math', 'Core/Rectangle', + 'Core/RequestScheduler', 'Core/WebMercatorProjection', 'Core/WebMercatorTilingScheme', 'Scene/Imagery', @@ -31,6 +32,7 @@ defineSuite([ loadWithXhr, CesiumMath, Rectangle, + RequestScheduler, WebMercatorProjection, WebMercatorTilingScheme, Imagery, @@ -41,6 +43,10 @@ defineSuite([ when) { 'use strict'; + beforeEach(function() { + RequestScheduler.clearForSpecs(); + }); + afterEach(function() { loadImage.createImage = loadImage.defaultCreateImage; loadWithXhr.load = loadWithXhr.defaultLoad; @@ -236,7 +242,7 @@ defineSuite([ }); it('routes resource request through a proxy if one is specified', function() { - /*jshint unused: false*/ + /*eslint-disable no-unused-vars*/ var proxy = new DefaultProxy('/proxy/'); var requestMetadata = when.defer(); spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { @@ -252,10 +258,11 @@ defineSuite([ return requestMetadata.promise.then(function(url) { expect(url.indexOf(proxy.getURL('server.invalid'))).toEqual(0); }); + /*eslint-enable no-unused-vars*/ }); it('resource request takes a query string', function() { - /*jshint unused: false*/ + /*eslint-disable no-unused-vars*/ var requestMetadata = when.defer(); spyOn(loadWithXhr, 'load').and.callFake(function(url, responseType, method, data, headers, deferred, overrideMimeType) { requestMetadata.resolve(url); @@ -269,6 +276,7 @@ defineSuite([ return requestMetadata.promise.then(function(url) { expect(/\?query=1$/.test(url)).toEqual(true); }); + /*eslint-enable no-unused-vars*/ }); it('routes tile requests through a proxy if one is specified', function() { @@ -358,6 +366,9 @@ defineSuite([ if (tries < 3) { error.retry = true; } + setTimeout(function() { + RequestScheduler.update(); + }, 1); }); loadImage.createImage = function(url, crossOrigin, deferred) { @@ -378,6 +389,7 @@ defineSuite([ var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); + RequestScheduler.update(); return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; diff --git a/Specs/Widgets/Animation/AnimationSpec.js b/Specs/Widgets/Animation/AnimationSpec.js index de4fc687367d..6c1ed5b46be9 100644 --- a/Specs/Widgets/Animation/AnimationSpec.js +++ b/Specs/Widgets/Animation/AnimationSpec.js @@ -1,19 +1,56 @@ /*global defineSuite*/ defineSuite([ 'Widgets/Animation/Animation', + 'Core/defined', 'Widgets/Animation/AnimationViewModel', - 'Widgets/ClockViewModel' + 'Widgets/ClockViewModel', + 'Specs/pollToPromise' ], function( Animation, + defined, AnimationViewModel, - ClockViewModel) { + ClockViewModel, + pollToPromise) { 'use strict'; - it('sanity check', function() { + var container; + var animation; + afterEach(function() { + if (defined(animation)) { + animation = animation.destroy(); + } + if (defined(container) && defined(container.parentNode)) { + container.parentNode.removeChild(container); + } + }); + + it('Can create and destroy', function() { + var clockViewModel = new ClockViewModel(); + var animationViewModel = new AnimationViewModel(clockViewModel); + animation = new Animation(document.body, animationViewModel); + }); + + it('Can create with container not in the DOM', function() { + container = document.createElement('div'); + var clockViewModel = new ClockViewModel(); + var animationViewModel = new AnimationViewModel(clockViewModel); + var animation = new Animation(container, animationViewModel); + + //Verify applyThemeChanges is called when we add the container to the DOM. + spyOn(animation, 'applyThemeChanges').and.callThrough(); + document.body.appendChild(container); + + //This is done via polling because we can't control when the DOM decides to + //fire the Mutation event. + return pollToPromise(function() { + return animation.applyThemeChanges.calls.count() === 1; + }); + }); + + it('Can destroy without container ever being in the DOM', function() { + container = document.createElement('div'); var clockViewModel = new ClockViewModel(); var animationViewModel = new AnimationViewModel(clockViewModel); - var animation = new Animation(document.body, animationViewModel); - animation.applyThemeChanges(); - animation.destroy(); + animation = new Animation(container, animationViewModel); }); }); diff --git a/Specs/Widgets/Animation/AnimationViewModelSpec.js b/Specs/Widgets/Animation/AnimationViewModelSpec.js index 1d7b3a9b61fc..ce1aa3ca85e3 100644 --- a/Specs/Widgets/Animation/AnimationViewModelSpec.js +++ b/Specs/Widgets/Animation/AnimationViewModelSpec.js @@ -611,7 +611,7 @@ defineSuite([ var originalTicks = [0.0, 1.0, 2.0]; animationViewModel.setShuttleRingTicks(originalTicks); - var ticks = animationViewModel.getShuttleRingTicks(ticks); + var ticks = animationViewModel.getShuttleRingTicks(); ticks.push(99); ticks[0] = -99; expect(animationViewModel.getShuttleRingTicks()).toEqual(originalTicks); diff --git a/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorSpec.js b/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorSpec.js new file mode 100644 index 000000000000..bb9a7ecc70b1 --- /dev/null +++ b/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorSpec.js @@ -0,0 +1,95 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector', + 'Core/Ellipsoid', + 'Scene/Cesium3DTileset', + 'Scene/Globe', + 'Specs/createScene' + ], function( + Cesium3DTilesInspector, + Ellipsoid, + Cesium3DTileset, + Globe, + createScene) { + 'use strict'; + + // Parent tile with content and four child tiles with content + var tilesetUrl = './Data/Cesium3DTiles/Tilesets/Tileset/'; + + var scene; + beforeAll(function() { + scene = createScene(); + var ellipsoid = Ellipsoid.UNIT_SPHERE; + scene.globe = new Globe(ellipsoid); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + it('can create and destroy', function() { + var container = document.createElement('div'); + container.id = 'testContainer'; + document.body.appendChild(container); + + var widget = new Cesium3DTilesInspector('testContainer', scene); + expect(widget.container).toBe(container); + expect(widget.viewModel._scene).toBe(scene); + expect(widget.isDestroyed()).toEqual(false); + widget.destroy(); + expect(widget.isDestroyed()).toEqual(true); + + document.body.removeChild(container); + }); + + it('constructor throws with no element', function() { + expect(function() { + return new Cesium3DTilesInspector(); + }).toThrowDeveloperError(); + }); + + it('constructor throws with string element that does not exist', function() { + expect(function() { + return new Cesium3DTilesInspector('does not exist', scene); + }).toThrowDeveloperError(); + }); + + it('constructor throws with no scene', function() { + expect(function() { + return new Cesium3DTilesInspector(document.body); + }).toThrowDeveloperError(); + }); + + describe('logging', function() { + var widget; + var container; + + beforeAll(function() { + container = document.createElement('div'); + container.id = 'testContainer'; + document.body.appendChild(container); + widget = new Cesium3DTilesInspector('testContainer', scene); + + var viewModel = widget.viewModel; + viewModel.tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + return viewModel.tileset.readyPromise; + }); + + afterAll(function() { + widget.destroy(); + document.body.removeChild(container); + }); + + it('shows performance', function() { + var viewModel = widget.viewModel; + viewModel.performance = true; + expect(viewModel._performanceDisplay._container.className.indexOf('cesium-cesiumInspector-show') !== -1).toBe(true); + expect(viewModel._performanceDisplay._container.className.indexOf('cesium-cesiumInspector-hide') === -1).toBe(true); + viewModel.performance = false; + expect(viewModel._performanceDisplay._container.className.indexOf('cesium-cesiumInspector-show') === -1).toBe(true); + expect(viewModel._performanceDisplay._container.className.indexOf('cesium-cesiumInspector-hide') !== -1).toBe(true); + }); + }); +}, 'WebGL'); diff --git a/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js b/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js new file mode 100644 index 000000000000..53f77c679ed0 --- /dev/null +++ b/Specs/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js @@ -0,0 +1,265 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel', + 'Scene/Cesium3DTileset', + 'Scene/Cesium3DTileStyle', + 'Core/defined', + 'Core/Math', + 'Scene/Globe', + 'Specs/createScene', + 'ThirdParty/when' + ], function( + Cesium3DTilesInspectorViewModel, + Cesium3DTileset, + Cesium3DTileStyle, + defined, + CesiumMath, + Globe, + createScene, + when) { + 'use strict'; + + // Parent tile with content and four child tiles with content + var tilesetUrl = './Data/Cesium3DTiles/Tilesets/Tileset/'; + + var scene; + var viewModel; + var performanceContainer = document.createElement('div'); + + beforeAll(function() { + scene = createScene(); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + beforeEach(function() { + scene.globe = new Globe(); + scene.initializeFrame(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + it('can create and destroy', function() { + var viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + expect(viewModel._scene).toBe(scene); + expect(viewModel.isDestroyed()).toEqual(false); + viewModel.destroy(); + expect(viewModel.isDestroyed()).toEqual(true); + }); + + it('throws if scene is undefined', function() { + expect(function() { + return new Cesium3DTilesInspectorViewModel(); + }).toThrowDeveloperError(); + }); + + it('throws if performanceContainer is undefined', function() { + expect(function() { + return new Cesium3DTilesInspectorViewModel(scene); + }).toThrowDeveloperError(); + }); + + describe('tileset options', function() { + it('show properties', function() { + viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + var tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + viewModel.tileset = tileset; + var done = when.defer(); + tileset.readyPromise.then(function() { + expect(viewModel.properties.indexOf('id') !== -1).toBe(true); + expect(viewModel.properties.indexOf('Longitude') !== -1).toBe(true); + expect(viewModel.properties.indexOf('Latitude') !== -1).toBe(true); + expect(viewModel.properties.indexOf('Height') !== -1).toBe(true); + viewModel.destroy(); + done.resolve(); + }); + return done; + }); + }); + + describe('display options', function() { + beforeAll(function() { + viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + var tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + viewModel.tileset = tileset; + return tileset.readyPromise; + }); + + afterAll(function() { + viewModel.destroy(); + }); + + it('colorize', function() { + viewModel.colorize = true; + expect(viewModel.tileset.debugColorizeTiles).toBe(true); + viewModel.colorize = false; + expect(viewModel.tileset.debugColorizeTiles).toBe(false); + }); + + it('wireframe', function() { + viewModel.wireframe = true; + expect(viewModel.tileset.debugWireframe).toBe(true); + viewModel.wireframe = false; + expect(viewModel.tileset.debugWireframe).toBe(false); + }); + + it('showBoundingVolumes', function() { + viewModel.showBoundingVolumes = true; + expect(viewModel.tileset.debugShowBoundingVolume).toBe(true); + viewModel.showBoundingVolumes = false; + expect(viewModel.tileset.debugShowBoundingVolume).toBe(false); + }); + + it('showContentVolumes', function() { + viewModel.showContentBoundingVolumes = true; + expect(viewModel.tileset.debugShowContentBoundingVolume).toBe(true); + viewModel.showContentBoundingVolumes = false; + expect(viewModel.tileset.debugShowContentBoundingVolume).toBe(false); + }); + + it('showRequestVolumes', function() { + viewModel.showRequestVolumes = true; + expect(viewModel.tileset.debugShowViewerRequestVolume).toBe(true); + viewModel.showRequestVolumes = false; + expect(viewModel.tileset.debugShowViewerRequestVolume).toBe(false); + }); + + it('showOnlyPickedTileDebugLabel', function() { + viewModel.showOnlyPickedTileDebugLabel = true; + expect(viewModel.tileset.debugPickedTileLabelOnly).toBe(true); + viewModel.showOnlyPickedTileDebugLabel = false; + expect(viewModel.tileset.debugPickedTileLabelOnly).toBe(false); + }); + + it('showGeometricError', function() { + viewModel.showGeometricError = true; + expect(viewModel.tileset.debugShowGeometricError).toBe(true); + viewModel.showGeometricError = false; + expect(viewModel.tileset.debugShowGeometricError).toBe(false); + }); + + it('showRenderingStatistics', function() { + viewModel.showRenderingStatistics = true; + expect(viewModel.tileset.debugShowRenderingStatistics).toBe(true); + viewModel.showRenderingStatistics = false; + expect(viewModel.tileset.debugShowRenderingStatistics).toBe(false); + }); + + it('showMemoryUsage', function() { + viewModel.showMemoryUsage = true; + expect(viewModel.tileset.debugShowMemoryUsage).toBe(true); + viewModel.showMemoryUsage = false; + expect(viewModel.tileset.debugShowMemoryUsage).toBe(false); + }); + }); + + describe('update options', function() { + beforeAll(function() { + viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + viewModel.tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + return viewModel.tileset.readyPromise; + }); + + afterAll(function() { + viewModel.destroy(); + }); + + it('freeze frame', function() { + viewModel.freezeFrame = false; + expect(viewModel.tileset.debugFreezeFrame).toBe(false); + viewModel.freezeFrame = true; + expect(viewModel.tileset.debugFreezeFrame).toBe(true); + }); + + it('maximum screen space error', function() { + viewModel.dynamicScreenSpaceError = false; + viewModel.maximumScreenSpaceError = 10; + expect(viewModel.tileset.dynamicScreenSpaceError).toBe(false); + expect(viewModel.tileset.maximumScreenSpaceError).toBe(10); + }); + + it('dynamic screen space error', function() { + viewModel.dynamicScreenSpaceError = true; + viewModel.dynamicScreenSpaceErrorFactor = 2; + viewModel.dynamicScreenSpaceErrorDensity = 0.1; + expect(viewModel.tileset.dynamicScreenSpaceError).toBe(true); + expect(viewModel.tileset.dynamicScreenSpaceErrorFactor).toBe(2); + expect(viewModel.tileset.dynamicScreenSpaceErrorDensity).toBe(0.1); + }); + }); + + describe('style options', function() { + var style; + + beforeAll(function() { + style = new Cesium3DTileStyle({ + color : { + conditions : [ + ["${Height} >= 83", "color('purple', 0.5)"], + ["${Height} >= 80", "color('red')"], + ["${Height} >= 70", "color('orange')"], + ["${Height} >= 12", "color('yellow')"], + ["${Height} >= 7", "color('lime')"], + ["${Height} >= 1", "color('cyan')"], + ["true", "color('blue')"] + ] + }, + meta : { + description : "'Building id ${id} has height ${Height}.'" + } + }); + + viewModel = new Cesium3DTilesInspectorViewModel(scene, performanceContainer); + viewModel.tileset = new Cesium3DTileset({ + url : tilesetUrl + }); + + return viewModel.tileset.readyPromise; + }); + + afterAll(function() { + viewModel.destroy(); + }); + + it('loads tileset style', function() { + viewModel.tileset.style = style; + viewModel._update(); + expect(JSON.stringify(style.style)).toBe(JSON.stringify(JSON.parse(viewModel.styleString))); + }); + + it('does not throw on invalid syntax', function() { + expect(function() { + viewModel.styleString = 'invalid'; + }).not.toThrowError(); + }); + + it('recompiles style', function() { + viewModel._style = undefined; + viewModel.tileset.style = style; + viewModel._update(); + var s = JSON.parse(viewModel.styleString); + s.color = "color('red')"; + viewModel.styleString = JSON.stringify(s); + viewModel.compileStyle(); + viewModel._update(); + expect(viewModel.tileset.style.style.color).toBe("color('red')"); + expect(viewModel.tileset.style.style.meta.description).toBe("'Building id ${id} has height ${Height}.'"); + }); + + it('does not throw on invalid value', function() { + expect(function() { + viewModel.styleString = '{ "color": "color(1)" }'; + }).not.toThrowError(); + }); + }); +}, 'WebGL'); diff --git a/Specs/Widgets/PerformanceWatchdog/PerformanceWatchdogViewModelSpec.js b/Specs/Widgets/PerformanceWatchdog/PerformanceWatchdogViewModelSpec.js index b4b01dbe7066..7d9188779e76 100644 --- a/Specs/Widgets/PerformanceWatchdog/PerformanceWatchdogViewModelSpec.js +++ b/Specs/Widgets/PerformanceWatchdog/PerformanceWatchdogViewModelSpec.js @@ -33,10 +33,11 @@ defineSuite([ }); function spinWait(milliseconds) { - /*jshint noempty: false*/ + /*eslint-disable no-empty*/ var endTime = getTimestamp() + milliseconds; while (getTimestamp() < endTime) { } + /*eslint-enable no-empty*/ } it('throws when constructed without a scene', function() { diff --git a/Specs/Widgets/Viewer/ViewerSpec.js b/Specs/Widgets/Viewer/ViewerSpec.js index 9b91922c4b87..970e659b8c40 100644 --- a/Specs/Widgets/Viewer/ViewerSpec.js +++ b/Specs/Widgets/Viewer/ViewerSpec.js @@ -26,6 +26,7 @@ defineSuite([ 'Widgets/BaseLayerPicker/BaseLayerPicker', 'Widgets/BaseLayerPicker/ProviderViewModel', 'Widgets/CesiumWidget/CesiumWidget', + 'Widgets/ClockViewModel', 'Widgets/FullscreenButton/FullscreenButton', 'Widgets/Geocoder/Geocoder', 'Widgets/HomeButton/HomeButton', @@ -60,6 +61,7 @@ defineSuite([ BaseLayerPicker, ProviderViewModel, CesiumWidget, + ClockViewModel, FullscreenButton, Geocoder, HomeButton, @@ -114,6 +116,8 @@ defineSuite([ expect(viewer.baseLayerPicker).toBeInstanceOf(BaseLayerPicker); expect(viewer.navigationHelpButton).toBeInstanceOf(NavigationHelpButton); expect(viewer.animation).toBeInstanceOf(Animation); + expect(viewer.clockViewModel).toBeInstanceOf(ClockViewModel); + expect(viewer.animation.viewModel.clockViewModel).toBe(viewer.clockViewModel); expect(viewer.timeline).toBeInstanceOf(Timeline); expect(viewer.fullscreenButton).toBeInstanceOf(FullscreenButton); expect(viewer.selectionIndicator).toBeInstanceOf(SelectionIndicator); @@ -130,6 +134,16 @@ defineSuite([ expect(viewer.isDestroyed()).toEqual(true); }); + it('can specify custom clockViewModel', function() { + var clockViewModel = new ClockViewModel(); + viewer = createViewer(container, {clockViewModel : clockViewModel}); + expect(viewer.clockViewModel).toBe(clockViewModel); + expect(viewer.animation.viewModel.clockViewModel).toBe(viewer.clockViewModel); + viewer.destroy(); + expect(clockViewModel.isDestroyed()).toBe(false); + clockViewModel.destroy(); + }); + it('renders without errors', function() { viewer = createViewer(container); spyOn(viewer.scene.renderError, 'raiseEvent'); diff --git a/Specs/createFrameState.js b/Specs/createFrameState.js index 003291b39bc1..418201a8cc13 100644 --- a/Specs/createFrameState.js +++ b/Specs/createFrameState.js @@ -5,19 +5,21 @@ define([ 'Core/JulianDate', 'Scene/Camera', 'Scene/CreditDisplay', - 'Scene/FrameState' + 'Scene/FrameState', + 'Scene/JobScheduler' ], function( defaultValue, GeographicProjection, JulianDate, Camera, CreditDisplay, - FrameState) { + FrameState, + JobScheduler) { 'use strict'; function createFrameState(context, camera, frameNumber, time) { // Mock frame-state for testing. - var frameState = new FrameState(context, new CreditDisplay(document.createElement('div'))); + var frameState = new FrameState(context, new CreditDisplay(document.createElement('div')), new JobScheduler()); var projection = new GeographicProjection(); frameState.mapProjection = projection; @@ -37,6 +39,8 @@ define([ frameState.passes.render = true; frameState.passes.pick = false; + frameState.minimumDisableDepthTestDistance = 0.0; + return frameState; } diff --git a/Specs/createScene.js b/Specs/createScene.js index eae9dcb7d60f..ecceb37b4b44 100644 --- a/Specs/createScene.js +++ b/Specs/createScene.js @@ -1,5 +1,6 @@ /*global define*/ define([ + 'Core/Cartesian2', 'Core/clone', 'Core/defaultValue', 'Core/defined', @@ -7,6 +8,7 @@ define([ 'Specs/createCanvas', 'Specs/getWebGLStub' ], function( + Cartesian2, clone, defaultValue, defined, @@ -57,6 +59,10 @@ define([ this.render(time); }; + scene.pickForSpecs = function() { + this.pick(new Cartesian2(0, 0)); + }; + scene.rethrowRenderErrors = defaultValue(options.rethrowRenderErrors, true); return scene; diff --git a/Specs/getWebGLStub.js b/Specs/getWebGLStub.js index 00f5608433ce..83f60f3e0868 100644 --- a/Specs/getWebGLStub.js +++ b/Specs/getWebGLStub.js @@ -189,7 +189,7 @@ define([ antialias : defaultValue(options.antialias, true), premultipliedAlpha : defaultValue(options.premultipliedAlpha, true), preserveDrawingBuffer : defaultValue(options.preserveDrawingBuffer, false), - preferLowPowerToHighPerformance : defaultValue(options.preferLowPowerToHighPerformance, false), + powerPreference : defaultValue(options.powerPreference, false), failIfMajorPerformanceCaveat : defaultValue(options.failIfMajorPerformanceCaveat, false) }; diff --git a/Specs/karma-main.js b/Specs/karma-main.js index 69575ab18f1a..46b04fc0451b 100644 --- a/Specs/karma-main.js +++ b/Specs/karma-main.js @@ -56,7 +56,6 @@ 'Specs/customizeJasmine' ], function( customizeJasmine) { - customizeJasmine(jasmine.getEnv(), included, excluded, webglValidation, webglStub, release); var specFiles = Object.keys(__karma__.files).filter(function(file) { diff --git a/Specs/karma.conf.js b/Specs/karma.conf.js index 4c12124fdd5b..374f114dded8 100644 --- a/Specs/karma.conf.js +++ b/Specs/karma.conf.js @@ -1,4 +1,4 @@ -/*jshint node:true*/ +/*eslint-env node*/ "use strict"; module.exports = function(config) { diff --git a/Specs/pick.js b/Specs/pick.js index 3af52acd40bd..711798051a7d 100644 --- a/Specs/pick.js +++ b/Specs/pick.js @@ -6,7 +6,8 @@ define([ 'Renderer/ClearCommand', 'Renderer/Pass', 'Scene/CreditDisplay', - 'Scene/FrameState' + 'Scene/FrameState', + 'Scene/JobScheduler' ], function( BoundingRectangle, Color, @@ -14,7 +15,8 @@ define([ ClearCommand, Pass, CreditDisplay, - FrameState) { + FrameState, + JobScheduler) { 'use strict'; function executeCommands(context, passState, commands) { @@ -34,7 +36,7 @@ define([ var passState = pickFramebuffer.begin(rectangle); var oldPasses = frameState.passes; - frameState.passes = (new FrameState(new CreditDisplay(document.createElement('div')))).passes; + frameState.passes = (new FrameState(new CreditDisplay(document.createElement('div')), new JobScheduler())).passes; frameState.passes.pick = true; primitives.update(frameState); diff --git a/Tools/eslint-config-cesium/.eslintrc.json b/Tools/eslint-config-cesium/.eslintrc.json new file mode 100644 index 000000000000..eb0e708be522 --- /dev/null +++ b/Tools/eslint-config-cesium/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node.js" +} diff --git a/Tools/eslint-config-cesium/CHANGES.md b/Tools/eslint-config-cesium/CHANGES.md new file mode 100644 index 000000000000..f1fd366c54ad --- /dev/null +++ b/Tools/eslint-config-cesium/CHANGES.md @@ -0,0 +1,16 @@ +Change Log +========== + +### 2.0.0 + +* Enable [no-floating-decimal](http://eslint.org/docs/rules/no-floating-decimal). +* Enable [no-use-before-define](http://eslint.org/docs/rules/no-use-before-define). +* Enable [no-else-return](http://eslint.org/docs/rules/no-else-return). +* Enable [no-alert](http://eslint.org/docs/rules/no-alert). +* Enable [no-loop-func](http://eslint.org/docs/rules/no-loop-func). +* Enable [no-undef-init](http://eslint.org/docs/rules/no-undef-init). +* Enable [no-implicit-globals](http://eslint.org/docs/rules/no-implicit-globals). + +### 1.0.0 - 2017-06-12 + +* Initial release. diff --git a/Tools/eslint-config-cesium/README.md b/Tools/eslint-config-cesium/README.md new file mode 100644 index 000000000000..16ded425aaae --- /dev/null +++ b/Tools/eslint-config-cesium/README.md @@ -0,0 +1,29 @@ +The official [shareable ESLint config](http://eslint.org/docs/developer-guide/shareable-configs) for the [Cesium](https://cesiumjs.org/) ecosystem. + +## Usage + +--- + +We export three ESLint configurations. + +### eslint-config-cesium + +This config contains basic Cesium syntax and style config, from which `browser` and `node` extend. Extends `eslint:recommended` with additional rules. + +### eslint-config-cesium/browser + +For use in [`AMD`](http://requirejs.org/docs/whyamd.html) modules and browser code. + +### eslint-config-cesium/node + +For use in `node` packages. + +--- + +To use any of these configs, + +1. `npm install eslint-config-cesium --save-dev` + + If using the `browser` config: `npm install eslint-plugin-html --save-dev` + +2. Add `"extends": "cesium"` to your `.eslintrc.*` files diff --git a/Tools/eslint-config-cesium/browser.js b/Tools/eslint-config-cesium/browser.js new file mode 100644 index 000000000000..c6300a148bef --- /dev/null +++ b/Tools/eslint-config-cesium/browser.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + extends: './index.js', + env: { + amd: true, + browser: true + }, + plugins: [ + 'html' + ], + rules: { + 'no-implicit-globals': 'error' + } +}; diff --git a/Tools/eslint-config-cesium/index.js b/Tools/eslint-config-cesium/index.js new file mode 100644 index 000000000000..eb577bb9c260 --- /dev/null +++ b/Tools/eslint-config-cesium/index.js @@ -0,0 +1,43 @@ +'use strict'; + +module.exports = { + extends: 'eslint:recommended', + globals: { + DataView: false, + ArrayBuffer: false, + Float32Array: false, + Float64Array: false, + Int16Array: false, + Int32Array: false, + Int8Array: false, + Uint16Array: false, + Uint32Array: false, + Uint8Array: false, + Uint8ClampedArray: false + }, + rules: { + curly: 'error', + eqeqeq: 'error', + 'guard-for-in': 'error', + 'new-cap': ['error', {properties: false}], + 'no-alert': 'error', + 'no-caller': 'error', + 'no-console': 'off', + 'no-else-return': 'error', + 'no-empty': 'error', + 'no-extend-native': 'error', + 'no-extra-boolean-cast': 'off', + 'no-floating-decimal': 'error', + 'no-irregular-whitespace': 'error', + 'no-loop-func': 'error', + 'no-new': 'error', + 'no-undef': 'error', + 'no-undef-init': 'error', + 'no-unused-vars': ['error', {vars: 'all', args: 'all'}], + 'no-useless-escape': 'off', + 'no-use-before-define': ['error', 'nofunc'], + semi: 'error', + strict: 'error', + 'wrap-iife': ['error', 'any'] + } +}; diff --git a/Tools/eslint-config-cesium/node.js b/Tools/eslint-config-cesium/node.js new file mode 100644 index 000000000000..2686d2251d8e --- /dev/null +++ b/Tools/eslint-config-cesium/node.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + extends: './index.js', + env: { + node: true + } +}; diff --git a/Tools/eslint-config-cesium/package.json b/Tools/eslint-config-cesium/package.json new file mode 100644 index 000000000000..afa7027fbe87 --- /dev/null +++ b/Tools/eslint-config-cesium/package.json @@ -0,0 +1,25 @@ +{ + "name": "eslint-config-cesium", + "version": "1.0.0", + "description": "ESLint shareable configs for Cesium", + "homepage": "http://cesiumjs.org", + "license": "Apache-2.0", + "author": { + "name": "Analytical Graphics, Inc.", + "url": "http://www.agi.com" + }, + "contributors": [ + { + "name": "Cesium community", + "url": "https://github.com/AnalyticalGraphicsInc/cesium/blob/master/CONTRIBUTORS.md" + } + ], + "keywords": [ + "eslint", + "cesium" + ], + "peerDependencies": { + "eslint": ">= 4", + "eslint-plugin-html": ">= 3" + } +} diff --git a/gulpfile.js b/gulpfile.js index 45a2f61da48c..99c16b6feebf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,4 +1,5 @@ /*jslint node: true, latedef: nofunc*/ +/*eslint-env node*/ 'use strict'; var fs = require('fs'); @@ -11,7 +12,6 @@ var readline = require('readline'); var request = require('request'); var globby = require('globby'); -var jshint = require('gulp-jshint'); var gulpTap = require('gulp-tap'); var rimraf = require('rimraf'); var glslStripComments = require('glsl-strip-comments'); @@ -22,7 +22,6 @@ var gulpInsert = require('gulp-insert'); var gulpZip = require('gulp-zip'); var gulpRename = require('gulp-rename'); var gulpReplace = require('gulp-replace'); -var os = require('os'); var Promise = require('bluebird'); var requirejs = require('requirejs'); var karma = require('karma').Server; @@ -66,21 +65,11 @@ var buildFiles = ['Specs/**/*.js', '!Specs/SpecList.js', 'Source/Shaders/**/*.glsl']; -var jsHintFiles = ['Source/**/*.js', - '!Source/Shaders/**', - '!Source/ThirdParty/**', - '!Source/Workers/cesiumWorkerBootstrapper.js', - 'Apps/**/*.js', - 'Apps/Sandcastle/gallery/*.html', - '!Apps/Sandcastle/ThirdParty/**', - 'Specs/**/*.js', - 'Tools/buildTasks/**/*.js', - 'gulpfile.js']; - var filesToClean = ['Source/Cesium.js', 'Build', 'Instrumented', 'Source/Shaders/**/*.js', + 'Source/ThirdParty/Shaders/*.js', 'Specs/SpecList.js', 'Apps/Sandcastle/jsHintOptions.js', 'Apps/Sandcastle/gallery/gallery-index.js', @@ -232,27 +221,6 @@ gulp.task('instrumentForCoverage', ['build'], function(done) { }); }); -gulp.task('jsHint', ['build'], function() { - var stream = gulp.src(jsHintFiles) - .pipe(jshint.extract('auto')) - .pipe(jshint()) - .pipe(jshint.reporter('jshint-stylish')); - - if (yargs.argv.failTaskOnError) { - stream = stream.pipe(jshint.reporter('fail')); - } - return stream; -}); - -gulp.task('jsHint-watch', function() { - gulp.watch(jsHintFiles).on('change', function(event) { - gulp.src(event.path) - .pipe(jshint.extract('auto')) - .pipe(jshint()) - .pipe(jshint.reporter('jshint-stylish')); - }); -}); - gulp.task('makeZipFile', ['release'], function() { //For now we regenerate the JS glsl to force it to be unminified in the release zip //See https://github.com/AnalyticalGraphicsInc/cesium/pull/3106#discussion_r42793558 for discussion. @@ -409,7 +377,7 @@ function deployCesium(bucketName, uploadDirectory, cacheControl, done) { var mimeLookup = getMimeType(blobName); var contentType = mimeLookup.type; var compress = mimeLookup.compress; - var contentEncoding = compress ? 'gzip' : undefined; + var contentEncoding = compress || mimeLookup.isCompressed ? 'gzip' : undefined; var etag; totalFiles++; @@ -529,21 +497,22 @@ function deployCesium(bucketName, uploadDirectory, cacheControl, done) { function getMimeType(filename) { var ext = path.extname(filename); if (ext === '.bin' || ext === '.terrain') { - return { type : 'application/octet-stream', compress : true }; + return {type : 'application/octet-stream', compress : true, isCompressed : false}; } else if (ext === '.md' || ext === '.glsl') { - return { type : 'text/plain', compress : true }; + return {type : 'text/plain', compress : true, isCompressed : false}; } else if (ext === '.czml' || ext === '.geojson' || ext === '.json') { - return { type : 'application/json', compress : true }; + return {type : 'application/json', compress : true, isCompressed : false}; } else if (ext === '.js') { - return { type : 'application/javascript', compress : true }; + return {type : 'application/javascript', compress : true, isCompressed : false}; } else if (ext === '.svg') { - return { type : 'image/svg+xml', compress : true }; + return {type : 'image/svg+xml', compress : true, isCompressed : false}; } else if (ext === '.woff') { - return { type : 'application/font-woff', compress : false }; + return {type : 'application/font-woff', compress : false, isCompressed : false}; } var mimeType = mime.lookup(filename); - return {type : mimeType, compress : compressible(mimeType)}; + var compress = compressible(mimeType); + return {type : mimeType, compress : compress, isCompressed : false}; } // get all files currently in bucket asynchronously @@ -675,8 +644,7 @@ gulp.task('generateStubs', ['build'], function(done) { var contents = '\ /*global define,Cesium*/\n\ (function() {\n\ -\'use strict\';\n\ -/*jshint sub:true*/\n'; +\'use strict\';\n'; var modulePathMappings = []; globby.sync(sourceFiles).forEach(function(file) { @@ -776,9 +744,8 @@ gulp.task('sortRequires', function() { return -1; } else if (aName > bName) { return 1; - } else { - return 0; } + return 0; }); if (preserveFirst) { @@ -957,7 +924,7 @@ function glslToJavaScript(minify, minifyStateFilePath) { // we still are using from the set, then delete any files remaining in the set. var leftOverJsFiles = {}; - globby.sync(['Source/Shaders/**/*.js']).forEach(function(file) { + globby.sync(['Source/Shaders/**/*.js', 'Source/ThirdParty/Shaders/*.js']).forEach(function(file) { leftOverJsFiles[path.normalize(file)] = true; }); @@ -965,7 +932,7 @@ function glslToJavaScript(minify, minifyStateFilePath) { var builtinConstants = []; var builtinStructs = []; - var glslFiles = globby.sync(['Source/Shaders/**/*.glsl']); + var glslFiles = globby.sync(['Source/Shaders/**/*.glsl', 'Source/ThirdParty/Shaders/*.glsl']); glslFiles.forEach(function(glslFile) { glslFile = path.normalize(glslFile); var baseName = path.basename(glslFile, '.glsl'); @@ -1093,7 +1060,6 @@ function createCesiumJs() { /*global define*/\n\ define([' + moduleIds.join(', ') + '], function(' + parameters.join(', ') + ') {\n\ \'use strict\';\n\ - /*jshint sub:true*/\n\ var Cesium = {\n\ VERSION : "' + version + '",\n\ _shaders : {}\n\ @@ -1113,7 +1079,7 @@ function createSpecList() { specs.push("'" + filePathToModuleId(file) + "'"); }); - var contents = '/*jshint unused: false*/\nvar specs = [' + specs.join(',') + '];'; + var contents = '/*eslint-disable no-unused-vars*/\n/*eslint-disable no-implicit-globals*/\nvar specs = [' + specs.join(',') + '];'; fs.writeFileSync(path.join('Specs', 'SpecList.js'), contents); } @@ -1152,9 +1118,8 @@ function createGalleryList() { return -1; } else if (a.name > b.name) { return 1; - } else { - return 0; } + return 0; }); var helloWorldIndex = Math.max(demoObjects.indexOf(helloWorld), 0); @@ -1173,7 +1138,7 @@ var gallery_demos = [' + demoJSONs.join(', ') + '];'; } function createJsHintOptions() { - var primary = JSON.parse(fs.readFileSync('.jshintrc', 'utf8')); + var primary = JSON.parse(fs.readFileSync(path.join('Apps', '.jshintrc'), 'utf8')); var gallery = JSON.parse(fs.readFileSync(path.join('Apps', 'Sandcastle', '.jshintrc'), 'utf8')); primary.jasmine = false; primary.predef = gallery.predef; diff --git a/index.js b/index.js index b4de7a383d76..06588baa35eb 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -/*jshint node:true*/ +/*eslint-env node*/ 'use strict'; var path = require('path'); diff --git a/index.release.html b/index.release.html index 570b0585f98a..4c1f2a881061 100644 --- a/index.release.html +++ b/index.release.html @@ -106,7 +106,7 @@