diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/enums/Axis.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/enums/Axis.java index 8aaba6a077dbe6f4289326660ded576e757e2646..c132b28e9f31957867542b8914798c04767c5ef3 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/enums/Axis.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/enums/Axis.java @@ -28,15 +28,15 @@ public class Axis { * Denotes the x-axis. */ @JsProperty(name = "X") - public native Number X(); + public static native Number X(); /** * Denotes the y-axis. */ @JsProperty(name = "Y") - public native Number Y(); + public static native Number Y(); /** * Denotes the z-axis. */ @JsProperty(name = "Z") - public native Number Z(); + public static native Number Z(); } diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/CustomShader.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/CustomShader.java index c0dc5a4c84fc11bfdf08ef0e2cf2623a95619702..5ce7ca9f4f9d5c792c10fe1e8725a4d56a793ba4 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/CustomShader.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/CustomShader.java @@ -16,9 +16,7 @@ package org.cesiumjs.cs.scene.experimental; -import jsinterop.annotations.JsConstructor; -import jsinterop.annotations.JsMethod; -import jsinterop.annotations.JsType; +import jsinterop.annotations.*; import org.cesiumjs.cs.core.*; import org.cesiumjs.cs.scene.experimental.options.CustomShaderOptions; @@ -27,7 +25,6 @@ import org.cesiumjs.cs.scene.experimental.options.CustomShaderOptions; */ @JsType(isNative = true, namespace = "Cesium", name = "CustomShader") public class CustomShader { - @JsConstructor public CustomShader(CustomShaderOptions options) {} diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/enums/UniformType.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/enums/UniformType.java index df6df0adcc4179b679a88c08c66354d006ea8d8e..00b8fa4f5380843e60ed48714fbe4f3887eedd18 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/enums/UniformType.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/enums/UniformType.java @@ -29,83 +29,83 @@ public class UniformType { * A single floating point value. */ @JsProperty(name = "FLOAT") - public native String FLOAT(); + public static native String FLOAT(); /** * A vector of 2 floating point values. */ @JsProperty(name = "VEC2") - public native String VEC2(); + public static native String VEC2(); /** * A vector of 3 floating point values. */ @JsProperty(name = "VEC3") - public native String VEC3(); + public static native String VEC3(); /** * A vector of 4 floating point values. */ @JsProperty(name = "VEC4") - public native String VEC4(); + public static native String VEC4(); /** * A single integer value */ @JsProperty(name = "INT") - public native String INT(); + public static native String INT(); /** * A vector of 2 integer values. */ @JsProperty(name = "INT_VEC2") - public native String INT_VEC2(); + public static native String INT_VEC2(); /** * A vector of 3 integer values. */ @JsProperty(name = "INT_VEC3") - public native String INT_VEC3(); + public static native String INT_VEC3(); /** * A vector of 4 integer values. */ @JsProperty(name = "INT_VEC4") - public native String INT_VEC4(); + public static native String INT_VEC4(); /** * A single boolean value. */ @JsProperty(name = "BOOL") - public native String BOOL(); + public static native String BOOL(); /** * A vector of 2 boolean values. */ @JsProperty(name = "BOOL_VEC2") - public native String BOOL_VEC2(); + public static native String BOOL_VEC2(); /** * A vector of 3 boolean values. */ @JsProperty(name = "BOOL_VEC3") - public native String BOOL_VEC3(); + public static native String BOOL_VEC3(); /** * A vector of 4 boolean values. */ @JsProperty(name = "BOOL_VEC4") - public native String BOOL_VEC4(); + public static native String BOOL_VEC4(); /** * A 2x2 matrix of floating point values. */ @JsProperty(name = "MAT2") - public native String MAT2(); + public static native String MAT2(); /** * A 3x3 matrix of floating point values. */ @JsProperty(name = "MAT3") - public native String MAT3(); + public static native String MAT3(); /** * A 3x3 matrix of floating point values. */ @JsProperty(name = "MAT4") - public native String MAT4(); + public static native String MAT4(); /** * A 2D sampled texture. */ @JsProperty(name = "SAMPLER_2D") - public native String SAMPLER_2D(); + public static native String SAMPLER_2D(); @JsProperty(name = "SAMPLER_CUBE") - public native String SAMPLER_CUBE(); + public static native String SAMPLER_CUBE(); } diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/options/CustomShaderOptions.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/options/CustomShaderOptions.java index 0ee17c0850ceeb683604079103c0d8f5b4d76280..d5f46eabb48a6179fb7083de7bb6bfd6afbfc98b 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/options/CustomShaderOptions.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/experimental/options/CustomShaderOptions.java @@ -16,10 +16,12 @@ package org.cesiumjs.cs.scene.experimental.options; -import jsinterop.annotations.JsConstructor; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; +import jsinterop.annotations.*; +import org.cesiumjs.cs.Cesium; +import org.cesiumjs.cs.js.JsObject; +import org.cesiumjs.cs.scene.experimental.CustomShader; +import org.cesiumjs.cs.scene.experimental.enums.CustomShaderMode; +import org.cesiumjs.cs.scene.experimental.enums.LightingModel; @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") public class CustomShaderOptions { @@ -40,8 +42,20 @@ public class CustomShaderOptions { */ @JsProperty public boolean isTranslucent; -// uniforms Object. optional A dictionary for user-defined uniforms. The key is the uniform name that will appear in the GLSL code. The value is an object that describes the uniform type and initial value -// varyings Object. optional A dictionary for declaring additional GLSL varyings used in the shader. The key is the varying name that will appear in the GLSL code. The value is the data type of the varying. For each varying, the declaration will be added to the top of the shader automatically. The caller is responsible for assigning a value in the vertex shader and using the value in the fragment shader. + /** + * A dictionary for user-defined uniforms. The key is the uniform name that will appear in the GLSL code. + * The value is an object that describes the uniform type and initial value + */ + @JsProperty + public JsObject uniforms; + /** + * A dictionary for declaring additional GLSL varyings used in the shader. The key is the varying name that will + * appear in the GLSL code. The value is the data type of the varying. For each varying, the declaration will be + * added to the top of the shader automatically. The caller is responsible for assigning a value in the vertex + * shader and using the value in the fragment shader. + */ + @JsProperty + public JsObject varyings; /** * The custom vertex shader as a string of GLSL code. It must include a GLSL function called vertexMain. See the * example for the expected signature. If not specified, the custom vertex shader step will be skipped in @@ -59,4 +73,58 @@ public class CustomShaderOptions { @JsConstructor public CustomShaderOptions() {} + + @JsOverlay + public final CustomShaderOptions setMode(String mode) { + this.mode = mode; + return this; + } + + @JsOverlay + public final CustomShaderOptions setLightingModel(Number lightingModel) { + this.lightingModel = lightingModel; + return this; + } + + @JsOverlay + public final CustomShaderOptions setTranslucent(boolean isTranslucent) { + this.isTranslucent = isTranslucent; + return this; + } + + @JsOverlay + public final CustomShaderOptions addUniform(String name, String type, Object value) { + if (this.uniforms == JsObject.undefined()) { + this.uniforms = JsObject.create(); + } + JsObject uniform = JsObject.create(); + uniform.setProperty("type", type); + uniform.setProperty("value", value); + + JsObject.setProperty(uniforms, name, uniform); + + return this; + } + + @JsOverlay + public final CustomShaderOptions addVarying(String name, String type) { + if (this.varyings == JsObject.undefined()) { + this.varyings = JsObject.create(); + } + JsObject.setProperty(this.varyings, name, type); + + return this; + } + + @JsOverlay + public final CustomShaderOptions setVertexShaderText(String vertexShaderText) { + this.vertexShaderText = vertexShaderText; + return this; + } + + @JsOverlay + public final CustomShaderOptions setFragmentShaderText(String fragmentShaderText) { + this.fragmentShaderText = fragmentShaderText; + return this; + } } diff --git a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/options/CameraFlyToOptions.java b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/options/CameraFlyToOptions.java index 01dbeb7b169d99b4710c1d0b5284b4759af0c333..9cf2fd3ee5331b50c3ac1eda12a8dc9d2d8ae305 100644 --- a/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/options/CameraFlyToOptions.java +++ b/cesiumjs4gwt-main/src/main/java/org/cesiumjs/cs/scene/options/CameraFlyToOptions.java @@ -16,10 +16,7 @@ package org.cesiumjs.cs.scene.options; -import jsinterop.annotations.JsConstructor; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; +import jsinterop.annotations.*; import org.cesiumjs.cs.core.*; import org.cesiumjs.cs.scene.Camera; @@ -116,4 +113,88 @@ public class CameraFlyToOptions { @JsConstructor public CameraFlyToOptions() { } + + @JsOverlay + public final CameraFlyToOptions setDestination(Cartesian3 destination) { + this.destinationPos = destination; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setDestination(Rectangle destination) { + this.destinationRec = destination; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setOrientation(HeadingPitchRoll orientation) { + this.orientation = orientation; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setDuration(double duration) { + this.duration = duration; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setComplete(Camera.FlightCompleteCallback complete) { + this.complete = complete; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setCancel(Camera.FlightCancelledCallback cancel) { + this.cancel = cancel; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setEndTransform(Matrix4 endTransform) { + this.endTransform = endTransform; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setMaximumHeight(double maximumHeight) { + this.maximumHeight = maximumHeight; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setPitchAdjustHeight(double pitchAdjustHeight) { + this.pitchAdjustHeight = pitchAdjustHeight; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setFlyOverLongitude(double flyOverLongitude) { + this.flyOverLongitude = flyOverLongitude; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setFlyOverLongitudeWeight(double flyOverLongitudeWeight) { + this.flyOverLongitudeWeight = flyOverLongitudeWeight; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setEasingFunction(EasingFunction easingFunction) { + this.easingFunction = easingFunction; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setEasingFunctionCallback(EasingFunction.Callback easingFunctionCallback) { + this.easingFunctionCallback = easingFunctionCallback; + return this; + } + + @JsOverlay + public final CameraFlyToOptions setOffset(HeadingPitchRange offset) { + this.offset = offset; + return this; + } } 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 43d1da42609781a03d471877e0ab58b08052e95c..e7ce024ff469cafc407227b96831f41b1674c78b 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 @@ -117,6 +117,7 @@ public class InjectorModule extends AbstractGinModule { bind(Clouds.class).asEagerSingleton(); bind(CloudParameters.class).asEagerSingleton(); + bind(Tiles3DNextPhotogrammetryClassification.class).asEagerSingleton(); bind(CustomShaders3DTiles.class).asEagerSingleton(); } } diff --git a/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextPhotogrammetryClassification.java b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextPhotogrammetryClassification.java new file mode 100644 index 0000000000000000000000000000000000000000..563f54c0d949ff43d6715edbeb8a7594113a8b61 --- /dev/null +++ b/cesiumjs4gwt-showcase/src/main/java/org/cleanlogic/cesiumjs4gwt/showcase/examples/Tiles3DNextPhotogrammetryClassification.java @@ -0,0 +1,319 @@ +/* + * 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.MouseClickEvent; +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.enums.Cesium3DTileColorBlendMode; +import org.cesiumjs.cs.scene.experimental.CustomShader; +import org.cesiumjs.cs.scene.experimental.enums.LightingModel; +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.ViewerPanel; +import org.cesiumjs.cs.widgets.options.ViewerOptions; +import org.cesiumjs.cs.core.HeadingPitchRoll; +import org.cleanlogic.cesiumjs4gwt.showcase.basic.AbstractExample; +import org.cleanlogic.cesiumjs4gwt.showcase.components.store.ShowcaseExampleStore; + +import javax.inject.Inject; + +public class Tiles3DNextPhotogrammetryClassification extends AbstractExample { + private Cesium3DTileset tileset; + private CustomShader unlitShader; + private Cesium3DTileStyle classificationStyle; + private CustomShader translucentWindowsShader; + private CustomShader materialShader; + private CustomShader selectFeatureShader; + private boolean enablePicking = true; + + @Inject + public Tiles3DNextPhotogrammetryClassification(ShowcaseExampleStore store) { + super("3D Tiles Next Photogrammetry Classification", + "Load a photogrammetry dataset with feature ID textures from EXT_mesh_features", + new String[]{"Showcase", "Cesium", "3d", "Viewer", "experimental"}, store, "1.87.1"); + } + + @Override + public void buildPanel() { + Cesium.ExperimentalFeatures.enableModelExperimental = true; + ViewerOptions options = new ViewerOptions(); + options.terrainProvider = Cesium.createWorldTerrain(); + options.infoBox = false; + options.orderIndependentTranslucency = false; + ViewerPanel csVPanel = new ViewerPanel(options); + + + csVPanel.getViewer().clock().currentTime = JulianDate.fromIso8601("2021-11-09T20:27:37.016064475348684937Z"); + + Scene scene = csVPanel.getViewer().scene(); + + tileset = Cesium3DTileset.create(IonResource.fromAssetId(666297)); + + Cartesian3 translation = new Cartesian3(-1.398521324920626, 0.7823052871729486, 0.7015244410592609); + tileset.modelMatrix = Matrix4.fromTranslation(translation); + + tileset.maximumScreenSpaceError = 8.0; + scene.pickTranslucentDepth = true; + scene.light.intensity = 7.0; + + scene.primitives().add(tileset); + csVPanel.getViewer().zoomTo(tileset); + + // Fly to a nice overview of the city. + csVPanel.getViewer().camera.flyTo(new CameraFlyToOptions() + .setDestination(new Cartesian3(-2703640.80485846, -4261161.990345464, 3887439.511104276)) + .setOrientation(new HeadingPitchRoll(0.22426651143535548, -0.2624145362506527, 0.000006972977223185239))); + + // Styles ============================================================================= + + classificationStyle = new Cesium3DTileStyle(); + JsObject.setProperty(classificationStyle, "color", "color(${color})"); + + // Shaders ============================================================================ + + // Dummy shader that sets the UNLIT lighting mode. For use with the classification style + String emptyFragmentShader = "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {}"; + unlitShader = new CustomShader(new CustomShaderOptions() + .setLightingModel(LightingModel.UNLIT()).setFragmentShaderText(emptyFragmentShader)); + + translucentWindowsShader = new CustomShader(new CustomShaderOptions().setLightingModel(LightingModel.UNLIT()) + .setTranslucent(true).setFragmentShaderText(String.join("\n", new String[] { + "const float WINDOW = 0.0;", + "const float SKYLIGHT = 4.0;", + "const float TOTAL_FEATURES = 12.0;", + "", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (featureId == WINDOW || featureId == SKYLIGHT) {", + " material.alpha = 0.4;", + " material.roughness = 0.1;", + " }", + "}", + }))); + + materialShader = new CustomShader(new CustomShaderOptions().setLightingModel(LightingModel.PBR()) + .setTranslucent(true).setFragmentShaderText(String.join("\n", new String[] { + "const float WINDOW = 0.0;", + "const float FRAME = 1.0;", + "const float WALL = 2.0;", + "const float ROOF = 3.0;", + "const float SKYLIGHT = 4.0;", + "const float AIR_CONDITIONER_WHITE = 5.0;", + "const float AIR_CONDITIONER_BLACK = 6.0;", + "const float AIR_CONDITIONER_TALL = 7.0;", + "const float CLOCK = 8.0;", + "const float PILLARS = 9.0;", + "const float STREET_LIGHT = 10.0;", + "const float TRAFFIC_LIGHT = 11.0;", + "", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (featureId == CLOCK) {", + " // Shiny brass", + " material.specular = vec3(0.98, 0.90, 0.59);", + " material.roughness = 0.3;", + " } else if (", + " featureId == STREET_LIGHT ||", + " featureId == AIR_CONDITIONER_BLACK ||", + " featureId == AIR_CONDITIONER_WHITE ||", + " featureId == AIR_CONDITIONER_TALL ||", + " featureId == ROOF", + " ) {", + " // dull aluminum", + " material.specular = vec3(0.91, 0.92, 0.92);", + " material.roughness = 0.5;", + " } else if (featureId == WINDOW || featureId == SKYLIGHT) {", + " // make translucent, but also set an orange emissive color so it looks like", + " // it's lit from inside", + " material.emissive = vec3(1.0, 0.3, 0.0);", + " material.alpha = 0.5;", + " } else if (featureId == WALL || featureId == FRAME || featureId == PILLARS) {", + " // paint the walls and pillars white to contrast the brass clock", + " material.diffuse = mix(material.diffuse, vec3(1.0), 0.8);", + " material.roughness = 0.9;", + " } else {", + " // brighten everything else", + " material.diffuse += 0.05;", + " material.roughness = 0.9;", + " }", + "}", + }))); + + Number NOTHING_SELECTED = 12; + selectFeatureShader = new CustomShader(new CustomShaderOptions() + .setLightingModel(LightingModel.PBR()) + .addUniform("u_selectedFeature", UniformType.FLOAT(), NOTHING_SELECTED) + .setFragmentShaderText(String.join("\n", new String[] { + "const float NOTHING_SELECTED = 12.0;", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (u_selectedFeature < NOTHING_SELECTED && featureId == u_selectedFeature) {", + " material.specular = vec3(1.00, 0.85, 0.57);", + " material.roughness = 0.3;", + " }", + "}", + }))); + + tileset.style = classificationStyle; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.MIX(); + + DivElement nameOverlay = RootPanel.get().getElement().getOwnerDocument().createDivElement(); + nameOverlay.addClassName("backdrop"); + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + nameOverlay.getStyle().setPosition(Style.Position.ABSOLUTE); + nameOverlay.getStyle().setBottom(0., Style.Unit.PX); + nameOverlay.getStyle().setLeft(0., Style.Unit.PX); +// nameOverlay.style["pointer-events"] = "none"; + nameOverlay.getStyle().setPadding(4.0, Style.Unit.PX); + nameOverlay.getStyle().setBackgroundColor("black"); + nameOverlay.getStyle().setWhiteSpace(Style.WhiteSpace.PRE_LINE); + nameOverlay.getStyle().setFontSize(12., Style.Unit.PX); + csVPanel.getViewer().container().appendChild(nameOverlay); + + this.enablePicking = true; + + csVPanel.getViewer().screenSpaceEventHandler().setInputAction(event -> { + MouseMoveEvent movement = (MouseMoveEvent) event; + if (enablePicking) { + PickedObject pickedObject = scene.pick(movement.endPosition); + if (pickedObject instanceof Cesium3DTileFeature) { + nameOverlay.getStyle().setDisplay(Style.Display.BLOCK); + nameOverlay.getStyle().setBottom(csVPanel.getViewer().canvas().getClientHeight() - movement.endPosition.y, Style.Unit.PX); + nameOverlay.getStyle().setLeft(movement.endPosition.x, Style.Unit.PX); + String message = "Component: " + ((Cesium3DTileFeature) pickedObject).getProperty("component") + + "\nFeature ID: " + ((Cesium3DTileFeature) pickedObject).getProperty("_batchId"); + nameOverlay.setInnerText(message); + } else { + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + } + } else { + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + } + }, ScreenSpaceEventType.MOUSE_MOVE()); + + csVPanel.getViewer().screenSpaceEventHandler().setInputAction(event -> { + MouseClickEvent movement = (MouseClickEvent) event; + if (enablePicking) { + PickedObject pickedObject = scene.pick(movement.position); + Object batchId = JsObject.undefined(); + if (pickedObject != null) { + batchId = JsObject.getObject(pickedObject, "_batchId"); + } + Cesium.log(batchId); + if (Cesium.defined(pickedObject) && Cesium.defined(batchId)) { + selectFeatureShader.setUniform("u_selectedFeature", (Number) batchId); + } else { + selectFeatureShader.setUniform("u_selectedFeature", NOTHING_SELECTED); + } + } + }, ScreenSpaceEventType.LEFT_CLICK()); + + CheckBox enablePickingCBox = new CheckBox("Enable picking"); + enablePickingCBox.getElement().getStyle().setColor("white"); + enablePickingCBox.setWidth("100px"); + enablePickingCBox.setValue(this.enablePicking); + enablePickingCBox.addValueChangeHandler(event -> enablePicking = event.getValue()); + + ListBox listBox = new ListBox(); + listBox.addItem("Photogrammetry"); + listBox.addItem("Show Classification"); + listBox.addItem("Translucent Windows"); + listBox.addItem("Stylized PBR Materials"); + listBox.addItem("Golden Touch"); + listBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "Photogrammetry": defaults(); break; + case "Show Classification": showClassification(); break; + case "Translucent Windows": translucentWindows(); break; + case "Stylized PBR Materials": pbrMaterials(); break; + case "Golden Touch": goldenTouch(); break; + default: break; + } + }); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(enablePickingCBox); + hPanel.add(listBox); + + AbsolutePanel aPanel = new AbsolutePanel(); + aPanel.add(csVPanel); + aPanel.add(hPanel, 20, 20); + + contentPanel.add(new HTML( + "

Load a photogrammetry dataset with feature ID textures from EXT_mesh_features.

")); + contentPanel.add(aPanel); + + initWidget(contentPanel); + + defaults(); + } + + @Override + public String[] getSourceCodeURLs() { + String[] sourceCodeURLs = new String[1]; + sourceCodeURLs[0] = GWT.getModuleBaseURL() + "examples/" + "Tiles3DNextPhotogrammetryClassification.txt"; + return sourceCodeURLs; + } + + private void defaults() { + tileset.style = (Cesium3DTileStyle) JsObject.undefined(); + tileset.customShader = unlitShader; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT(); + tileset.colorBlendAmount = 0.5; + } + + private void showClassification() { + defaults(); + tileset.style = classificationStyle; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.MIX(); + } + + private void translucentWindows() { + defaults(); + tileset.customShader = translucentWindowsShader; + } + + private void pbrMaterials() { + defaults(); + tileset.customShader = materialShader; + } + + private void goldenTouch() { + defaults(); + tileset.customShader = selectFeatureShader; + } +} diff --git a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next Photogrammetry Classification.jpg b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next Photogrammetry Classification.jpg new file mode 100644 index 0000000000000000000000000000000000000000..559f54e42076a2a639d04b35250c5a61200ff96a Binary files /dev/null and b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/3D Tiles Next Photogrammetry Classification.jpg differ diff --git a/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextPhotogrammetryClassification.txt b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextPhotogrammetryClassification.txt new file mode 100644 index 0000000000000000000000000000000000000000..cfe31a58d9228d2fbc0368c278f2ac38372767ff --- /dev/null +++ b/cesiumjs4gwt-showcase/src/main/resources/org/cleanlogic/cesiumjs4gwt/public/examples/Tiles3DNextPhotogrammetryClassification.txt @@ -0,0 +1,303 @@ +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.MouseClickEvent; +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.enums.Cesium3DTileColorBlendMode; +import org.cesiumjs.cs.scene.experimental.CustomShader; +import org.cesiumjs.cs.scene.experimental.enums.LightingModel; +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.ViewerPanel; +import org.cesiumjs.cs.widgets.options.ViewerOptions; +import org.cesiumjs.cs.core.HeadingPitchRoll; +import org.cleanlogic.cesiumjs4gwt.showcase.basic.AbstractExample; +import org.cleanlogic.cesiumjs4gwt.showcase.components.store.ShowcaseExampleStore; + +import javax.inject.Inject; + +public class Tiles3DNextPhotogrammetryClassification extends AbstractExample { + private Cesium3DTileset tileset; + private CustomShader unlitShader; + private Cesium3DTileStyle classificationStyle; + private CustomShader translucentWindowsShader; + private CustomShader materialShader; + private CustomShader selectFeatureShader; + private boolean enablePicking = true; + + @Inject + public Tiles3DNextPhotogrammetryClassification(ShowcaseExampleStore store) { + super("3D Tiles Next Photogrammetry Classification", + "Load a photogrammetry dataset with feature ID textures from EXT_mesh_features", + new String[]{"Showcase", "Cesium", "3d", "Viewer", "experimental"}, store, "1.87.1"); + } + + @Override + public void buildPanel() { + Cesium.ExperimentalFeatures.enableModelExperimental = true; + ViewerOptions options = new ViewerOptions(); + options.terrainProvider = Cesium.createWorldTerrain(); + options.infoBox = false; + options.orderIndependentTranslucency = false; + ViewerPanel csVPanel = new ViewerPanel(options); + + + csVPanel.getViewer().clock().currentTime = JulianDate.fromIso8601("2021-11-09T20:27:37.016064475348684937Z"); + + Scene scene = csVPanel.getViewer().scene(); + + tileset = Cesium3DTileset.create(IonResource.fromAssetId(666297)); + + Cartesian3 translation = new Cartesian3(-1.398521324920626, 0.7823052871729486, 0.7015244410592609); + tileset.modelMatrix = Matrix4.fromTranslation(translation); + + tileset.maximumScreenSpaceError = 8.0; + scene.pickTranslucentDepth = true; + scene.light.intensity = 7.0; + + scene.primitives().add(tileset); + csVPanel.getViewer().zoomTo(tileset); + + // Fly to a nice overview of the city. + csVPanel.getViewer().camera.flyTo(new CameraFlyToOptions() + .setDestination(new Cartesian3(-2703640.80485846, -4261161.990345464, 3887439.511104276)) + .setOrientation(new HeadingPitchRoll(0.22426651143535548, -0.2624145362506527, 0.000006972977223185239))); + + // Styles ============================================================================= + + classificationStyle = new Cesium3DTileStyle(); + JsObject.setProperty(classificationStyle, "color", "color(${color})"); + + // Shaders ============================================================================ + + // Dummy shader that sets the UNLIT lighting mode. For use with the classification style + String emptyFragmentShader = "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {}"; + unlitShader = new CustomShader(new CustomShaderOptions() + .setLightingModel(LightingModel.UNLIT()).setFragmentShaderText(emptyFragmentShader)); + + translucentWindowsShader = new CustomShader(new CustomShaderOptions().setLightingModel(LightingModel.UNLIT()) + .setTranslucent(true).setFragmentShaderText(String.join("\n", new String[] { + "const float WINDOW = 0.0;", + "const float SKYLIGHT = 4.0;", + "const float TOTAL_FEATURES = 12.0;", + "", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (featureId == WINDOW || featureId == SKYLIGHT) {", + " material.alpha = 0.4;", + " material.roughness = 0.1;", + " }", + "}", + }))); + + materialShader = new CustomShader(new CustomShaderOptions().setLightingModel(LightingModel.PBR()) + .setTranslucent(true).setFragmentShaderText(String.join("\n", new String[] { + "const float WINDOW = 0.0;", + "const float FRAME = 1.0;", + "const float WALL = 2.0;", + "const float ROOF = 3.0;", + "const float SKYLIGHT = 4.0;", + "const float AIR_CONDITIONER_WHITE = 5.0;", + "const float AIR_CONDITIONER_BLACK = 6.0;", + "const float AIR_CONDITIONER_TALL = 7.0;", + "const float CLOCK = 8.0;", + "const float PILLARS = 9.0;", + "const float STREET_LIGHT = 10.0;", + "const float TRAFFIC_LIGHT = 11.0;", + "", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (featureId == CLOCK) {", + " // Shiny brass", + " material.specular = vec3(0.98, 0.90, 0.59);", + " material.roughness = 0.3;", + " } else if (", + " featureId == STREET_LIGHT ||", + " featureId == AIR_CONDITIONER_BLACK ||", + " featureId == AIR_CONDITIONER_WHITE ||", + " featureId == AIR_CONDITIONER_TALL ||", + " featureId == ROOF", + " ) {", + " // dull aluminum", + " material.specular = vec3(0.91, 0.92, 0.92);", + " material.roughness = 0.5;", + " } else if (featureId == WINDOW || featureId == SKYLIGHT) {", + " // make translucent, but also set an orange emissive color so it looks like", + " // it's lit from inside", + " material.emissive = vec3(1.0, 0.3, 0.0);", + " material.alpha = 0.5;", + " } else if (featureId == WALL || featureId == FRAME || featureId == PILLARS) {", + " // paint the walls and pillars white to contrast the brass clock", + " material.diffuse = mix(material.diffuse, vec3(1.0), 0.8);", + " material.roughness = 0.9;", + " } else {", + " // brighten everything else", + " material.diffuse += 0.05;", + " material.roughness = 0.9;", + " }", + "}", + }))); + + Number NOTHING_SELECTED = 12; + selectFeatureShader = new CustomShader(new CustomShaderOptions() + .setLightingModel(LightingModel.PBR()) + .addUniform("u_selectedFeature", UniformType.FLOAT(), NOTHING_SELECTED) + .setFragmentShaderText(String.join("\n", new String[] { + "const float NOTHING_SELECTED = 12.0;", + "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {", + " // NOTE: This is exposing internal details of the shader. It would be better if this was added to fsInput somewhere...", + " float featureId = floor(texture2D(FEATURE_ID_TEXTURE, FEATURE_ID_TEXCOORD).FEATURE_ID_CHANNEL * 255.0 + 0.5);", + "", + " if (u_selectedFeature < NOTHING_SELECTED && featureId == u_selectedFeature) {", + " material.specular = vec3(1.00, 0.85, 0.57);", + " material.roughness = 0.3;", + " }", + "}", + }))); + + tileset.style = classificationStyle; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.MIX(); + + DivElement nameOverlay = RootPanel.get().getElement().getOwnerDocument().createDivElement(); + nameOverlay.addClassName("backdrop"); + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + nameOverlay.getStyle().setPosition(Style.Position.ABSOLUTE); + nameOverlay.getStyle().setBottom(0., Style.Unit.PX); + nameOverlay.getStyle().setLeft(0., Style.Unit.PX); +// nameOverlay.style["pointer-events"] = "none"; + nameOverlay.getStyle().setPadding(4.0, Style.Unit.PX); + nameOverlay.getStyle().setBackgroundColor("black"); + nameOverlay.getStyle().setWhiteSpace(Style.WhiteSpace.PRE_LINE); + nameOverlay.getStyle().setFontSize(12., Style.Unit.PX); + csVPanel.getViewer().container().appendChild(nameOverlay); + + this.enablePicking = true; + + csVPanel.getViewer().screenSpaceEventHandler().setInputAction(event -> { + MouseMoveEvent movement = (MouseMoveEvent) event; + if (enablePicking) { + PickedObject pickedObject = scene.pick(movement.endPosition); + if (pickedObject instanceof Cesium3DTileFeature) { + nameOverlay.getStyle().setDisplay(Style.Display.BLOCK); + nameOverlay.getStyle().setBottom(csVPanel.getViewer().canvas().getClientHeight() - movement.endPosition.y, Style.Unit.PX); + nameOverlay.getStyle().setLeft(movement.endPosition.x, Style.Unit.PX); + String message = "Component: " + ((Cesium3DTileFeature) pickedObject).getProperty("component") + + "\nFeature ID: " + ((Cesium3DTileFeature) pickedObject).getProperty("_batchId"); + nameOverlay.setInnerText(message); + } else { + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + } + } else { + nameOverlay.getStyle().setDisplay(Style.Display.NONE); + } + }, ScreenSpaceEventType.MOUSE_MOVE()); + + csVPanel.getViewer().screenSpaceEventHandler().setInputAction(event -> { + MouseClickEvent movement = (MouseClickEvent) event; + if (enablePicking) { + PickedObject pickedObject = scene.pick(movement.position); + Object batchId = JsObject.undefined(); + if (pickedObject != null) { + batchId = JsObject.getObject(pickedObject, "_batchId"); + } + Cesium.log(batchId); + if (Cesium.defined(pickedObject) && Cesium.defined(batchId)) { + selectFeatureShader.setUniform("u_selectedFeature", (Number) batchId); + } else { + selectFeatureShader.setUniform("u_selectedFeature", NOTHING_SELECTED); + } + } + }, ScreenSpaceEventType.LEFT_CLICK()); + + CheckBox enablePickingCBox = new CheckBox("Enable picking"); + enablePickingCBox.getElement().getStyle().setColor("white"); + enablePickingCBox.setWidth("100px"); + enablePickingCBox.setValue(this.enablePicking); + enablePickingCBox.addValueChangeHandler(event -> enablePicking = event.getValue()); + + ListBox listBox = new ListBox(); + listBox.addItem("Photogrammetry"); + listBox.addItem("Show Classification"); + listBox.addItem("Translucent Windows"); + listBox.addItem("Stylized PBR Materials"); + listBox.addItem("Golden Touch"); + listBox.addChangeHandler(event -> { + String value = ((ListBox) event.getSource()).getSelectedItemText(); + switch (value) { + case "Photogrammetry": defaults(); break; + case "Show Classification": showClassification(); break; + case "Translucent Windows": translucentWindows(); break; + case "Stylized PBR Materials": pbrMaterials(); break; + case "Golden Touch": goldenTouch(); break; + default: break; + } + }); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(enablePickingCBox); + hPanel.add(listBox); + + AbsolutePanel aPanel = new AbsolutePanel(); + aPanel.add(csVPanel); + aPanel.add(hPanel, 20, 20); + + contentPanel.add(new HTML( + "

Load a photogrammetry dataset with feature ID textures from EXT_mesh_features.

")); + contentPanel.add(aPanel); + + initWidget(contentPanel); + + defaults(); + } + + @Override + public String[] getSourceCodeURLs() { + String[] sourceCodeURLs = new String[1]; + sourceCodeURLs[0] = GWT.getModuleBaseURL() + "examples/" + "Tiles3DNextPhotogrammetryClassification.txt"; + return sourceCodeURLs; + } + + private void defaults() { + tileset.style = (Cesium3DTileStyle) JsObject.undefined(); + tileset.customShader = unlitShader; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.HIGHLIGHT(); + tileset.colorBlendAmount = 0.5; + } + + private void showClassification() { + defaults(); + tileset.style = classificationStyle; + tileset.colorBlendMode = Cesium3DTileColorBlendMode.MIX(); + } + + private void translucentWindows() { + defaults(); + tileset.customShader = translucentWindowsShader; + } + + private void pbrMaterials() { + defaults(); + tileset.customShader = materialShader; + } + + private void goldenTouch() { + defaults(); + tileset.customShader = selectFeatureShader; + } +}