diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/widgets/options/ViewerOptions.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/widgets/options/ViewerOptions.java index 151a87077cde5438f2c8b132fb81acb7cc05cab6..584bf9ac57e1e65b9b0b700200f68559482049c6 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/widgets/options/ViewerOptions.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/widgets/options/ViewerOptions.java @@ -176,11 +176,15 @@ public class ViewerOptions { */ @JsProperty public SkyBox skyBox; + @JsProperty(name = "skyBox") + public boolean enableSkyBox; /** * Blue sky, and the glow around the Earth's limb. Set to false to turn it off. */ @JsProperty public SkyAtmosphere skyAtmosphere; + @JsProperty(name = "skyAtmosphere") + public boolean enableSkyAtmosphere; /** * The element or id to be placed into fullscreen mode when the full screen * button is pressed. Default: document.body @@ -241,6 +245,8 @@ public class ViewerOptions { */ @JsProperty public Globe globe; + @JsProperty(name = "globe") + public boolean enableGlobe; /** * If true and the configuration supports it, use order independent * translucency. Default: true diff --git a/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/config/InjectorModule.java b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/config/InjectorModule.java index 302d38e6945ba1a37e42f9fb47863a9ec81cc5f9..8146c88fc5d4f36b4c3a172b60389b7d90bab75d 100644 --- a/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/config/InjectorModule.java +++ b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/config/InjectorModule.java @@ -121,5 +121,6 @@ public class InjectorModule extends AbstractGinModule { bind(CustomShaders3DTiles.class).asEagerSingleton(); bind(CustomShadersModels.class).asEagerSingleton(); bind(Tiles3DNextCDBYemen.class).asEagerSingleton(); + bind(Tiles3DNextS2Globe.class).asEagerSingleton(); } } diff --git a/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextS2Globe.java b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextS2Globe.java new file mode 100644 index 0000000000000000000000000000000000000000..e16fb068ee48281c0c1ea38e631a61efd39ac4dc --- /dev/null +++ b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextS2Globe.java @@ -0,0 +1,228 @@ +/* + * Copyright 2021 iserge, Gis4Fun. + * + * 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. + */ + +package org.cleanlogic.cesiumjs4gwt.showcase.examples; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.ui.*; +import org.cesiumjs.cs.Cesium; +import org.cesiumjs.cs.core.*; +import org.cesiumjs.cs.core.enums.ScreenSpaceEventType; +import org.cesiumjs.cs.core.events.MouseMoveEvent; +import org.cesiumjs.cs.js.JsObject; +import org.cesiumjs.cs.scene.Cesium3DTileFeature; +import org.cesiumjs.cs.scene.Cesium3DTileStyle; +import org.cesiumjs.cs.scene.Cesium3DTileset; +import org.cesiumjs.cs.scene.Scene; +import org.cesiumjs.cs.scene.experimental.CustomShader; +import org.cesiumjs.cs.scene.experimental.enums.UniformType; +import org.cesiumjs.cs.scene.experimental.options.CustomShaderOptions; +import org.cesiumjs.cs.scene.options.CameraFlyToOptions; +import org.cesiumjs.cs.widgets.Viewer; +import org.cesiumjs.cs.widgets.ViewerPanel; +import org.cesiumjs.cs.widgets.options.ViewerOptions; +import org.cleanlogic.cesiumjs4gwt.showcase.basic.AbstractExample; +import org.cleanlogic.cesiumjs4gwt.showcase.components.store.ShowcaseExampleStore; + +import javax.inject.Inject; + +public class Tiles3DNextS2Globe extends AbstractExample { + private Viewer viewer; + private boolean enablePicking = true; + @Inject + public Tiles3DNextS2Globe(ShowcaseExampleStore store) { + super("3D Tiles Next S2 Globe", + "Load a global scale 3D Tiles Next tileset that uses 3DTILES_bounding_volume_S2", + new String[]{"Showcase", "Cesium", "3d", "Viewer"}, store, "1.87.1"); + } + + @Override + public void buildPanel() { + // One World Terrain Base Globe provided by Maxar + Cesium.ExperimentalFeatures.enableModelExperimental = true; + + ViewerOptions options = new ViewerOptions(); + options.enableGlobe = false; + ViewerPanel csVPanel = new ViewerPanel(options); + viewer = csVPanel.getViewer(); + + Scene scene = csVPanel.getViewer().scene(); + + viewer.camera.flyTo(new CameraFlyToOptions() + .setDuration(0) + .setDestination(new Cartesian3(762079.3157173397, -28363749.882652905, 19814354.842565004)) + .setOrientation(new Cartesian3(-0.022007098944236157, 0.819079900508189, -0.5732571885110), + new Cartesian3(-0.015396759850986286, 0.5730503851893346, 0.8193754913471885)) + .setEasingFunction(EasingFunction.QUADRATIC_IN_OUT())); + + // MAXAR OWT WFF 1.2 Base Globe + Cesium3DTileset tileset = (Cesium3DTileset) scene.primitives().add(Cesium3DTileset.create(IonResource.fromAssetId(666330))); + tileset.maximumScreenSpaceError = 4; + + // --- Style --- + + JsObject jsStyle = JsObject.create(); + jsStyle.setProperty("defines", JsObject.create()); + jsStyle.getJsObject("defines").setProperty("LandCoverColor", "rgb(${color}[0], ${color}[1], ${color}[2])"); + jsStyle.setProperty("color", "${LandCoverColor} === vec4(1.0) ? rgb(254, 254, 254) : ${LandCoverColor}"); + Cesium3DTileStyle style = new Cesium3DTileStyle(jsStyle); + Cesium.log(style); + + // --- Custom Shader --- + + CustomShader customShader = new CustomShader(new CustomShaderOptions() + .addUniform("u_time", UniformType.FLOAT(), 0) + .setFragmentShaderText(String.join("\n", new String[] { + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", + "{", + " // NOTE: this is exposing internal details of the shader.", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + " // Use cartesian coordinates but scale to be roughly [-1, 1]", + " vec3 positionWC = fsInput.attributes.positionWC / 6.3e6;", + " if (featureId == 60.0)", + " {", + " // Something like FM synthesis to make irregularly spaced waves", + " float wave = sin(14.0 * positionWC.z - u_time);", + " wave = 0.5 + 0.5 * sin(10.0 * wave * positionWC.z - u_time);", + " // mix in an over-saturated version of the diffuse to make shimmering bands of color", + " material.diffuse = mix(material.diffuse, material.diffuse * 3.0, wave);", + " }", + "}", + }))); + + final double startTime = performanceNow(); + scene.postUpdate().addEventListener((Event.Listener) o -> customShaderUpdate(customShader, startTime)); + + // --- Picking --- + + ScreenSpaceEventHandler handler = new ScreenSpaceEventHandler(scene.canvas()); + + DivElement metadataOverlay = RootPanel.get().getElement().getOwnerDocument().createDivElement(); + metadataOverlay.addClassName("backdrop"); + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + metadataOverlay.getStyle().setPosition(Style.Position.ABSOLUTE); + metadataOverlay.getStyle().setBottom(0., Style.Unit.PX); + metadataOverlay.getStyle().setLeft(0., Style.Unit.PX); +// metadataOverlay.style["pointer-events"] = "none"; + metadataOverlay.getStyle().setPadding(4.0, Style.Unit.PX); + metadataOverlay.getStyle().setBackgroundColor("#303030"); + metadataOverlay.getStyle().setWhiteSpace(Style.WhiteSpace.PRE_LINE); + metadataOverlay.getStyle().setFontSize(16., Style.Unit.PX); + csVPanel.getViewer().container().appendChild(metadataOverlay); + + handler.setInputAction(event -> { + MouseMoveEvent movement = (MouseMoveEvent) event; + if (enablePicking) { + PickedObject feature = scene.pick(movement.endPosition); + if (feature instanceof Cesium3DTileFeature) { + metadataOverlay.getStyle().setDisplay(Style.Display.BLOCK); + metadataOverlay.getStyle().setBottom(viewer.canvas().getClientHeight() - movement.endPosition.y, Style.Unit.PX); + metadataOverlay.getStyle().setLeft(movement.endPosition.x, Style.Unit.PX); + + StringBuilder tableHtmlScratch = new StringBuilder(""); + + String[] propertyNames = ((Cesium3DTileFeature) feature).getPropertyNames(); + int length = propertyNames.length; + for (String propertyName : propertyNames) { + JsObject propertyValue = ((Cesium3DTileFeature) feature).getProperty(propertyName); + tableHtmlScratch.append(""); + } + tableHtmlScratch.append("
PropertyValue
").append(propertyName).append("").append(propertyValue).append("
"); + metadataOverlay.setInnerHTML(tableHtmlScratch.toString()); + } else { + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + } + } + }, ScreenSpaceEventType.MOUSE_MOVE()); + + ListBox listBox = new ListBox(); + listBox.addItem("Globe View"); + listBox.addItem("Show S2 Bounding Volumes"); + listBox.addItem("Apply Style"); + listBox.addItem("Apply Custom Shader"); + listBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "Globe View": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = false; + tileset.style = null; + break; + } + case "Show S2 Bounding Volumes": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = true; + tileset.style = null; + break; + } + case "Apply Style": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = false; + tileset.style = style; + break; + } + case "Apply Custom Shader": { + tileset.customShader = customShader; + tileset.debugShowBoundingVolume = false; + tileset.style = null; + break; + } + default: break; + } + }); + + CheckBox enablePickingCBox = new CheckBox("Enable picking"); + enablePickingCBox.getElement().getStyle().setColor("white"); + enablePickingCBox.setWidth("100px"); + enablePickingCBox.setValue(this.enablePicking); + enablePickingCBox.addValueChangeHandler(event -> { + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + enablePicking = event.getValue(); + }); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(listBox); + hPanel.add(enablePickingCBox); + + AbsolutePanel aPanel = new AbsolutePanel(); + aPanel.add(csVPanel); + aPanel.add(hPanel, 20, 20); + + contentPanel.add(new HTML( + "

Load a global scale 3D Tiles Next tileset that uses 3DTILES_bounding_volume_S2..

")); + contentPanel.add(aPanel); + + initWidget(contentPanel); + } + + @Override + public String[] getSourceCodeURLs() { + String[] sourceCodeURLs = new String[1]; + sourceCodeURLs[0] = GWT.getModuleBaseURL() + "examples/" + "Tiles3DNextS2Globe.txt"; + return sourceCodeURLs; + } + + private native double performanceNow() /*-{ + return performance.now(); + }-*/; + + private void customShaderUpdate(CustomShader customShader, double startTime) { + double elapsedTimeSeconds = (performanceNow() - startTime) / 1000.; + customShader.setUniform("u_time", elapsedTimeSeconds); + } +} diff --git a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next S2 Globe.jpg b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next S2 Globe.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46f7ea9d2d0e8dd2115595dfdcb553195eb3a7be Binary files /dev/null and b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next S2 Globe.jpg differ diff --git a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextCDBYemen.txt b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextCDBYemen.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..eae65552d5c74d45f892661ea93fa69ae5892b95 100644 --- a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextCDBYemen.txt +++ b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextCDBYemen.txt @@ -0,0 +1,367 @@ +package org.cleanlogic.cesiumjs4gwt.showcase.examples; + +import com.google.gwt.core.client.*; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.ui.*; +import org.cesiumjs.cs.Cesium; +import org.cesiumjs.cs.core.*; +import org.cesiumjs.cs.core.Math; +import org.cesiumjs.cs.core.enums.ScreenSpaceEventType; +import org.cesiumjs.cs.core.events.MouseClickEvent; +import org.cesiumjs.cs.core.events.MouseMoveEvent; +import org.cesiumjs.cs.datasources.properties.ConstantProperty; +import org.cesiumjs.cs.js.JsObject; +import org.cesiumjs.cs.scene.*; +import org.cesiumjs.cs.scene.options.CameraFlyToOptions; +import org.cesiumjs.cs.widgets.Viewer; +import org.cesiumjs.cs.widgets.ViewerPanel; +import org.cleanlogic.cesiumjs4gwt.showcase.basic.AbstractExample; +import org.cleanlogic.cesiumjs4gwt.showcase.components.store.ShowcaseExampleStore; + +import javax.inject.Inject; + +public class Tiles3DNextCDBYemen extends AbstractExample { + private Viewer viewer; + + private Cesium3DTileFeature highlightedFeature = null; + private Color highlightedOriginalColor = new Color(); + + private Cesium3DTileFeature selectedFeature = null; + private Color selectedOriginalColor = new Color(); + + private boolean enablePicking = true; + + @Inject + public Tiles3DNextCDBYemen(ShowcaseExampleStore store) { + super("3D Tiles Next CDB Yemen", + "Load a 3D Tile Next tileset converted from CDB", + new String[]{"Showcase", "Cesium", "3d", "Viewer", "experimental"}, store, "1.87.1"); + } + + @Override + public void buildPanel() { + Cesium.ExperimentalFeatures.enableModelExperimental = true; + + ViewerPanel csVPanel = new ViewerPanel(); + viewer = csVPanel.getViewer(); + + viewer.clock().currentTime = JulianDate.fromIso8601("2021-11-09T07:27:37.016064475348684937Z"); + Scene scene = viewer.scene(); + scene.light.intensity = 7.0; + + // 3D Tiles Next converted from CDB of Aden, Yemen (CDB provided by Presagis) + Cesium3DTileset terrainTileset = (Cesium3DTileset) scene.primitives().add(Cesium3DTileset.create(IonResource.fromAssetId(667809))); + + Cesium3DTileset buildingsTileset = (Cesium3DTileset) scene.primitives().add(Cesium3DTileset.create(IonResource.fromAssetId(666747))); + buildingsTileset.maximumScreenSpaceError = 12; + + CameraTransform tileset = new CameraTransform(); + tileset.destination = new Cartesian3(4397999.822774582, 4404502.67774069, 1397782.4709840622); + tileset.direction = new Cartesian3(-0.29335588497705106, -0.6066709587467911, 0.7388454997917905); + tileset.up = new Cartesian3(0.6240972421637774, 0.46391380837591956, 0.6287182283994301); + + CameraTransform airport = new CameraTransform(); + airport.destination = new Cartesian3(4394719.151490939, 4402317.401942875, 1406608.6602404779); + airport.direction = new Cartesian3(0.4146699515908668, -0.8887814163588482, 0.1952342828060377); + airport.up = new Cartesian3(0.8415067525520951, 0.4561872920946922, 0.28941240460723); + + CameraTransform crater = new CameraTransform(); + crater.destination = new Cartesian3(4398179.160380196, 4402518.469409466, 1399161.7612076725); + crater.direction = new Cartesian3(-0.2800903637088597, -0.6348021519070498, 0.7201219452923355); + crater.up = new Cartesian3(0.6319189548885261, 0.4427783126727723, 0.6361020360596605); + + CameraTransform port = new CameraTransform(); + port.destination = new Cartesian3(4399698.85724341, 4399019.639078034, 1405153.7766045567); + port.direction = new Cartesian3(-0.5651458936543287, 0.17696574231117793, -0.8057873447342694); + port.up = new Cartesian3(0.4886488937394081, 0.8587605935024302, -0.15411846642958343); + + buildingsTileset.readyPromise().then(t -> { + Cartesian3 center = Cartesian3.fromDegrees(45.04192, 12.753525, 2000); + double heading = Math.toRadians(50.0); + double pitch = Math.toRadians(-20.0); + double range = 5000.0; + + flyCameraTo(tileset, 0.); + }); + + // --- Style --- + + String[][] conditions = new String[][] { + {"${HGT} !== undefined && ${HGT} < 5", "color('#f5fd2d')"}, + {"${HGT} !== undefined && ${HGT} >= 5 && ${HGT} < 10", "color('#d3a34a')"}, + {"${HGT} !== undefined && ${HGT} >= 10 && ${HGT} < 15", "color('#947e75')"}, + {"${HGT} !== undefined && ${HGT} >= 15 && ${HGT} < 20", "color('#565a9f')"}, + {"${HGT} !== undefined && ${HGT} > 20", "color('#223bc3')"}, + {"true", "color('white')"}, + }; + JsObject style = JsObject.create(); + style.setProperty("color", JsObject.create()); + style.getJsObject("color").setProperty("conditions", stringArrayToJsArrayString(conditions)); + Cesium3DTileStyle buildingStyle = new Cesium3DTileStyle(style); + + conditions = new String[][] { + {"${name} === 'OCEAN'", "color('#436d9d')"}, + {"${name} === 'LAKE'", "color('#3987c9')"}, + {"${name} === 'CALCAREOUS'", "color('#BBB6B1')"}, + {"${name} === 'GRASS'", "color('#567d46')"}, + {"${name} === 'FOREST'", "color('green')"}, + {"${name} === 'CITY'", "color('lightgray')"}, + {"${name} === 'ASPHALTROAD'", "color('#434343')"}, + {"${name} === 'ASPHALT'", "color('#463d39')"}, + {"${name} === 'CONCRETE'", "color('#b9b4ab')"}, + {"${name} === 'DRYGROUND'", "color('#9B7653')"}, + {"${name} === 'WETGROUND'", "color('#5a4332')"}, + {"${name} === 'SAND'", "color('gold')"}, + {"true", "color('#9B7653')"}, + }; + style = JsObject.create(); + style.setProperty("color", JsObject.create()); + style.getJsObject("color").setProperty("conditions", stringArrayToJsArrayString(conditions)); + Cesium3DTileStyle terrainStyle = new Cesium3DTileStyle(style); + + // --- Picking --- + + ScreenSpaceEventHandler handler = new ScreenSpaceEventHandler(scene.canvas()); + + DivElement metadataOverlay = RootPanel.get().getElement().getOwnerDocument().createDivElement(); + metadataOverlay.addClassName("backdrop"); + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + metadataOverlay.getStyle().setPosition(Style.Position.ABSOLUTE); + metadataOverlay.getStyle().setBottom(0., Style.Unit.PX); + metadataOverlay.getStyle().setLeft(0., Style.Unit.PX); +// nameOverlay.style["pointer-events"] = "none"; + metadataOverlay.getStyle().setPadding(4.0, Style.Unit.PX); + metadataOverlay.getStyle().setBackgroundColor("#303030"); + metadataOverlay.getStyle().setWhiteSpace(Style.WhiteSpace.PRE_LINE); + metadataOverlay.getStyle().setFontSize(16., Style.Unit.PX); + metadataOverlay.getStyle().setBorderStyle(Style.BorderStyle.SOLID); + + csVPanel.getViewer().container().appendChild(metadataOverlay); + + handler.setInputAction(event -> { + MouseMoveEvent movement = (MouseMoveEvent) event; + if (enablePicking) { + // If a feature was previously highlighted, undo the highlight + if (Cesium.defined(highlightedFeature)) { + highlightedFeature.color = highlightedOriginalColor; + highlightedFeature = null; + } + + PickedObject feature = scene.pick(movement.endPosition); + boolean featurePicked = feature instanceof Cesium3DTileFeature; + + boolean isTerrainFeature = featurePicked && ((Cesium3DTileFeature) feature).hasProperty("substrates"); + boolean isBuildingFeature = featurePicked && ((Cesium3DTileFeature) feature).hasProperty("HGT"); + + if (isTerrainFeature) { + metadataOverlay.getStyle().setDisplay(Style.Display.BLOCK); + metadataOverlay.getStyle().setBottom(viewer.canvas().getClientHeight() - movement.endPosition.y, Style.Unit.PX); + metadataOverlay.getStyle().setLeft(movement.endPosition.x, Style.Unit.PX); + + StringBuilder tableHtmlScratch = + new StringBuilder(""); + + JsArrayString materialsScratch = (JsArrayString) (Object) ((Cesium3DTileFeature) feature).getProperty("substrates"); + JsArrayString weightsScratch = (JsArrayString) (Object) ((Cesium3DTileFeature) feature).getProperty("weights"); + tableHtmlScratch.append(""); + for (int i = 0; i < materialsScratch.length(); i++) { + tableHtmlScratch.append(""); + } + tableHtmlScratch.append("
Material:" + + ((Cesium3DTileFeature) feature).getProperty("name") + + "
Substrates
") + .append(materialsScratch.get(i).substring(3)) + .append("") + .append(weightsScratch.get(i)) + .append("%
"); + metadataOverlay.setInnerHTML(tableHtmlScratch.toString()); + } else { + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + } + + if (isBuildingFeature) { + // Highlight the feature if it's not already selected. + if (feature != selectedFeature) { + highlightedFeature = (Cesium3DTileFeature) feature; + Color.clone(((Cesium3DTileFeature) feature).color, highlightedOriginalColor); + ((Cesium3DTileFeature) feature).color = Color.MAGENTA(); + } + } + } + }, ScreenSpaceEventType.MOUSE_MOVE()); + + handler.setInputAction(event -> { + MouseClickEvent movement = (MouseClickEvent) event; + // If a feature was previously selected, undo the highlight + if (Cesium.defined(selectedFeature)) { + selectedFeature.color = selectedOriginalColor; + selectedFeature = null; + } + + PickedObject feature = scene.pick(movement.position); + boolean featurePicked = feature instanceof Cesium3DTileFeature; + boolean isBuildingFeature = featurePicked && ((Cesium3DTileFeature) feature).hasProperty("HGT"); + + if (isBuildingFeature) { + // Select the feature if it's not already selected + if (selectedFeature == feature) { + return; + } + selectedFeature = (Cesium3DTileFeature) feature; + + // Save the selected feature's original color + if (feature == highlightedFeature) { + Color.clone(highlightedOriginalColor, selectedOriginalColor); + highlightedFeature = null; + } else { + Color.clone(((Cesium3DTileFeature) feature).color, selectedOriginalColor); + } + ((Cesium3DTileFeature) feature).color = Color.LIME(); + + StringBuilder tableHtmlScratch = new StringBuilder(""); + tableHtmlScratch.append(""); + JsObject metadataClass = JsObject.getObject(feature, "content").getJsObject("batchTable").getJsObject("_propertyTable").getJsObject("class"); + String[] propertyNames = ((Cesium3DTileFeature) feature).getPropertyNames(); + int length = propertyNames.length; + for (int i = 0; i < length; ++i) { + String propertyName = propertyNames[i]; + + // Skip these properties, since they are always empty. + if (propertyName.equals("APID") || propertyName.equals("FACC") || propertyName.equals("RWID")) { + continue; + } + + JsObject propertyValue = ((Cesium3DTileFeature) feature).getProperty(propertyName); + JsObject properties = metadataClass.getJsObject("properties"); + JsObject property = properties.getJsObject(propertyName); + + tableHtmlScratch.append(""); + } + tableHtmlScratch.append("
Property NameIDTypeValue
" + + property.getString("name") + + "ℹ️" + + property.getString("id") + + "" + + property.getString("componentType") + + "" + + propertyValue + + "
"); + viewer.selectedEntity.description = new ConstantProperty(tableHtmlScratch.toString()); + } + }, ScreenSpaceEventType.LEFT_CLICK()); + + ListBox tilesetBox = new ListBox(); + tilesetBox.addItem("Overview"); + tilesetBox.addItem("Airport"); + tilesetBox.addItem("Crater"); + tilesetBox.addItem("Port"); + tilesetBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "Overview": flyCameraTo(tileset, 2.); break; + case "Airport": flyCameraTo(airport, 2.); break; + case "Crater": flyCameraTo(crater, 2.); break; + case "Port": flyCameraTo(port, 2.); break; + default: break; + } + }); + + ListBox styleBox = new ListBox(); + styleBox.addItem("No style"); + styleBox.addItem("Style buildings based on height"); + styleBox.addItem("Style terrain based on materials"); + styleBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "No style": { + resetHighlight(); + buildingsTileset.style = null; + terrainTileset.style = null; + break; + } + case "Style buildings based on height": { + resetHighlight(); + buildingsTileset.style = buildingStyle; + terrainTileset.style = null; + break; + } + case "Style terrain based on materials": { + buildingsTileset.style = null; + terrainTileset.style = terrainStyle; + break; + } + default: break; + } + }); + + CheckBox enablePickingCBox = new CheckBox("Enable picking"); + enablePickingCBox.getElement().getStyle().setColor("white"); + enablePickingCBox.setWidth("100px"); + enablePickingCBox.setValue(this.enablePicking); + enablePickingCBox.addValueChangeHandler(event -> enablePicking = event.getValue()); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(tilesetBox); + hPanel.add(styleBox); + hPanel.add(enablePickingCBox); + + AbsolutePanel aPanel = new AbsolutePanel(); + aPanel.add(csVPanel); + aPanel.add(hPanel, 20, 20); + + contentPanel.add(new HTML( + "

Load a 3D Tile Next tileset converted from CDB.

")); + contentPanel.add(aPanel); + + initWidget(contentPanel); + } + + @Override + public String[] getSourceCodeURLs() { + String[] sourceCodeURLs = new String[1]; + sourceCodeURLs[0] = GWT.getModuleBaseURL() + "examples/" + "Tiles3DNextCDBYemen.txt"; + return sourceCodeURLs; + } + + private static class CameraTransform { + protected Cartesian3 destination; + protected Cartesian3 direction; + protected Cartesian3 up; + } + + private void flyCameraTo(CameraTransform cameraTransform, double duration) { + // Fly to a nice overview of the city. + viewer.camera.flyTo(new CameraFlyToOptions() + .setDuration(duration) + .setDestination(cameraTransform.destination) + .setOrientation(cameraTransform.direction, cameraTransform.up) + .setEasingFunction(EasingFunction.QUADRATIC_IN_OUT())); + } + + private void resetHighlight() { + if (selectedFeature != null) { + selectedFeature.color = selectedOriginalColor; + selectedFeature = null; + } + if (highlightedFeature != null) { + highlightedFeature.color = highlightedOriginalColor; + highlightedFeature = null; + } + } + + private JsArray stringArrayToJsArrayString(String[][] array) { + JsArray result = JavaScriptObject.createArray().cast(); + + for (int i = 0; i < array.length; i++) { + result.push(JsArrayString.createArray().cast()); + for (int j = 0; j < array[i].length; j++) { + result.get(i).push(array[i][j]); + } + } + + return result; + } +} diff --git a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextS2Globe.txt b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextS2Globe.txt new file mode 100644 index 0000000000000000000000000000000000000000..f7e3dde0e7637bf14e7460abfef9dca33934ff0c --- /dev/null +++ b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextS2Globe.txt @@ -0,0 +1,212 @@ +package org.cleanlogic.cesiumjs4gwt.showcase.examples; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.ui.*; +import org.cesiumjs.cs.Cesium; +import org.cesiumjs.cs.core.*; +import org.cesiumjs.cs.core.enums.ScreenSpaceEventType; +import org.cesiumjs.cs.core.events.MouseMoveEvent; +import org.cesiumjs.cs.js.JsObject; +import org.cesiumjs.cs.scene.Cesium3DTileFeature; +import org.cesiumjs.cs.scene.Cesium3DTileStyle; +import org.cesiumjs.cs.scene.Cesium3DTileset; +import org.cesiumjs.cs.scene.Scene; +import org.cesiumjs.cs.scene.experimental.CustomShader; +import org.cesiumjs.cs.scene.experimental.enums.UniformType; +import org.cesiumjs.cs.scene.experimental.options.CustomShaderOptions; +import org.cesiumjs.cs.scene.options.CameraFlyToOptions; +import org.cesiumjs.cs.widgets.Viewer; +import org.cesiumjs.cs.widgets.ViewerPanel; +import org.cesiumjs.cs.widgets.options.ViewerOptions; +import org.cleanlogic.cesiumjs4gwt.showcase.basic.AbstractExample; +import org.cleanlogic.cesiumjs4gwt.showcase.components.store.ShowcaseExampleStore; + +import javax.inject.Inject; + +public class Tiles3DNextS2Globe extends AbstractExample { + private Viewer viewer; + private boolean enablePicking = true; + @Inject + public Tiles3DNextS2Globe(ShowcaseExampleStore store) { + super("3D Tiles Next S2 Globe", + "Load a global scale 3D Tiles Next tileset that uses 3DTILES_bounding_volume_S2", + new String[]{"Showcase", "Cesium", "3d", "Viewer"}, store, "1.87.1"); + } + + @Override + public void buildPanel() { + // One World Terrain Base Globe provided by Maxar + Cesium.ExperimentalFeatures.enableModelExperimental = true; + + ViewerOptions options = new ViewerOptions(); + options.enableGlobe = false; + ViewerPanel csVPanel = new ViewerPanel(options); + viewer = csVPanel.getViewer(); + + Scene scene = csVPanel.getViewer().scene(); + + viewer.camera.flyTo(new CameraFlyToOptions() + .setDuration(0) + .setDestination(new Cartesian3(762079.3157173397, -28363749.882652905, 19814354.842565004)) + .setOrientation(new Cartesian3(-0.022007098944236157, 0.819079900508189, -0.5732571885110), + new Cartesian3(-0.015396759850986286, 0.5730503851893346, 0.8193754913471885)) + .setEasingFunction(EasingFunction.QUADRATIC_IN_OUT())); + + // MAXAR OWT WFF 1.2 Base Globe + Cesium3DTileset tileset = (Cesium3DTileset) scene.primitives().add(Cesium3DTileset.create(IonResource.fromAssetId(666330))); + tileset.maximumScreenSpaceError = 4; + + // --- Style --- + + JsObject jsStyle = JsObject.create(); + jsStyle.setProperty("defines", JsObject.create()); + jsStyle.getJsObject("defines").setProperty("LandCoverColor", "rgb(${color}[0], ${color}[1], ${color}[2])"); + jsStyle.setProperty("color", "${LandCoverColor} === vec4(1.0) ? rgb(254, 254, 254) : ${LandCoverColor}"); + Cesium3DTileStyle style = new Cesium3DTileStyle(jsStyle); + Cesium.log(style); + + // --- Custom Shader --- + + CustomShader customShader = new CustomShader(new CustomShaderOptions() + .addUniform("u_time", UniformType.FLOAT(), 0) + .setFragmentShaderText(String.join("\n", new String[] { + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", + "{", + " // NOTE: this is exposing internal details of the shader.", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + " // Use cartesian coordinates but scale to be roughly [-1, 1]", + " vec3 positionWC = fsInput.attributes.positionWC / 6.3e6;", + " if (featureId == 60.0)", + " {", + " // Something like FM synthesis to make irregularly spaced waves", + " float wave = sin(14.0 * positionWC.z - u_time);", + " wave = 0.5 + 0.5 * sin(10.0 * wave * positionWC.z - u_time);", + " // mix in an over-saturated version of the diffuse to make shimmering bands of color", + " material.diffuse = mix(material.diffuse, material.diffuse * 3.0, wave);", + " }", + "}", + }))); + + final double startTime = performanceNow(); + scene.postUpdate().addEventListener((Event.Listener) o -> customShaderUpdate(customShader, startTime)); + + // --- Picking --- + + ScreenSpaceEventHandler handler = new ScreenSpaceEventHandler(scene.canvas()); + + DivElement metadataOverlay = RootPanel.get().getElement().getOwnerDocument().createDivElement(); + metadataOverlay.addClassName("backdrop"); + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + metadataOverlay.getStyle().setPosition(Style.Position.ABSOLUTE); + metadataOverlay.getStyle().setBottom(0., Style.Unit.PX); + metadataOverlay.getStyle().setLeft(0., Style.Unit.PX); +// metadataOverlay.style["pointer-events"] = "none"; + metadataOverlay.getStyle().setPadding(4.0, Style.Unit.PX); + metadataOverlay.getStyle().setBackgroundColor("#303030"); + metadataOverlay.getStyle().setWhiteSpace(Style.WhiteSpace.PRE_LINE); + metadataOverlay.getStyle().setFontSize(16., Style.Unit.PX); + csVPanel.getViewer().container().appendChild(metadataOverlay); + + handler.setInputAction(event -> { + MouseMoveEvent movement = (MouseMoveEvent) event; + if (enablePicking) { + PickedObject feature = scene.pick(movement.endPosition); + if (feature instanceof Cesium3DTileFeature) { + metadataOverlay.getStyle().setDisplay(Style.Display.BLOCK); + metadataOverlay.getStyle().setBottom(viewer.canvas().getClientHeight() - movement.endPosition.y, Style.Unit.PX); + metadataOverlay.getStyle().setLeft(movement.endPosition.x, Style.Unit.PX); + + StringBuilder tableHtmlScratch = new StringBuilder(""); + + String[] propertyNames = ((Cesium3DTileFeature) feature).getPropertyNames(); + int length = propertyNames.length; + for (String propertyName : propertyNames) { + JsObject propertyValue = ((Cesium3DTileFeature) feature).getProperty(propertyName); + tableHtmlScratch.append(""); + } + tableHtmlScratch.append("
PropertyValue
").append(propertyName).append("").append(propertyValue).append("
"); + metadataOverlay.setInnerHTML(tableHtmlScratch.toString()); + } else { + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + } + } + }, ScreenSpaceEventType.MOUSE_MOVE()); + + ListBox listBox = new ListBox(); + listBox.addItem("Globe View"); + listBox.addItem("Show S2 Bounding Volumes"); + listBox.addItem("Apply Style"); + listBox.addItem("Apply Custom Shader"); + listBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "Globe View": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = false; + tileset.style = null; + break; + } + case "Show S2 Bounding Volumes": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = true; + tileset.style = null; + break; + } + case "Apply Style": { + tileset.customShader = null; + tileset.debugShowBoundingVolume = false; + tileset.style = style; + break; + } + case "Apply Custom Shader": { + tileset.customShader = customShader; + tileset.debugShowBoundingVolume = false; + tileset.style = null; + break; + } + default: break; + } + }); + + CheckBox enablePickingCBox = new CheckBox("Enable picking"); + enablePickingCBox.getElement().getStyle().setColor("white"); + enablePickingCBox.setWidth("100px"); + enablePickingCBox.setValue(this.enablePicking); + enablePickingCBox.addValueChangeHandler(event -> { + metadataOverlay.getStyle().setDisplay(Style.Display.NONE); + enablePicking = event.getValue(); + }); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(listBox); + hPanel.add(enablePickingCBox); + + AbsolutePanel aPanel = new AbsolutePanel(); + aPanel.add(csVPanel); + aPanel.add(hPanel, 20, 20); + + contentPanel.add(new HTML( + "

Load a global scale 3D Tiles Next tileset that uses 3DTILES_bounding_volume_S2..

")); + contentPanel.add(aPanel); + + initWidget(contentPanel); + } + + @Override + public String[] getSourceCodeURLs() { + String[] sourceCodeURLs = new String[1]; + sourceCodeURLs[0] = GWT.getModuleBaseURL() + "examples/" + "Tiles3DNextS2Globe.txt"; + return sourceCodeURLs; + } + + private native double performanceNow() /*-{ + return performance.now(); + }-*/; + + private void customShaderUpdate(CustomShader customShader, double startTime) { + double elapsedTimeSeconds = (performanceNow() - startTime) / 1000.; + customShader.setUniform("u_time", elapsedTimeSeconds); + } +}