Skip to content

Commit bd6466a

Browse files
authored
Merge pull request #307 from Esri/control-the-camera-sample
Orbit the camera around an object sample
2 parents 422f3d5 + 911e77f commit bd6466a

File tree

5 files changed

+502
-0
lines changed

5 files changed

+502
-0
lines changed
590 KB
Loading
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright 2019 Esri.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.esri.samples.scene.orbit_the_camera_around_an_object;
18+
19+
import java.io.File;
20+
21+
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment;
22+
import com.esri.arcgisruntime.mapping.view.DrawStatus;
23+
import com.esri.arcgisruntime.mapping.view.DrawStatusChangedEvent;
24+
import com.esri.arcgisruntime.mapping.view.DrawStatusChangedListener;
25+
import javafx.fxml.FXML;
26+
import javafx.scene.control.CheckBox;
27+
import javafx.scene.control.Slider;
28+
29+
import com.esri.arcgisruntime.geometry.Point;
30+
import com.esri.arcgisruntime.geometry.SpatialReferences;
31+
import com.esri.arcgisruntime.mapping.ArcGISScene;
32+
import com.esri.arcgisruntime.mapping.ArcGISTiledElevationSource;
33+
import com.esri.arcgisruntime.mapping.Basemap;
34+
import com.esri.arcgisruntime.mapping.Surface;
35+
import com.esri.arcgisruntime.mapping.view.Graphic;
36+
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
37+
import com.esri.arcgisruntime.mapping.view.LayerSceneProperties;
38+
import com.esri.arcgisruntime.mapping.view.OrbitGeoElementCameraController;
39+
import com.esri.arcgisruntime.mapping.view.SceneView;
40+
import com.esri.arcgisruntime.symbology.ModelSceneSymbol;
41+
import com.esri.arcgisruntime.symbology.SimpleRenderer;
42+
43+
public class OrbitTheCameraAroundAnObjectController {
44+
45+
@FXML
46+
private SceneView sceneView;
47+
@FXML
48+
private CheckBox allowDistanceInteractionCheckBox;
49+
@FXML
50+
private Slider cameraHeadingSlider;
51+
@FXML
52+
private Slider planePitchSlider;
53+
54+
private OrbitGeoElementCameraController orbitCameraController;
55+
56+
57+
public void initialize() {
58+
59+
try {
60+
61+
// create a scene and add a basemap to it
62+
ArcGISScene scene = new ArcGISScene();
63+
scene.setBasemap(Basemap.createImagery());
64+
sceneView.setArcGISScene(scene);
65+
66+
// add a base surface with elevation data
67+
Surface surface = new Surface();
68+
ArcGISTiledElevationSource elevationSource = new ArcGISTiledElevationSource("http://elevation3d.arcgis" +
69+
".com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer");
70+
surface.getElevationSources().add(elevationSource);
71+
scene.setBaseSurface(surface);
72+
73+
// create a graphics overlay for the scene
74+
GraphicsOverlay sceneGraphicsOverlay = new GraphicsOverlay();
75+
sceneGraphicsOverlay.getSceneProperties().setSurfacePlacement(LayerSceneProperties.SurfacePlacement.RELATIVE);
76+
sceneView.getGraphicsOverlays().add(sceneGraphicsOverlay);
77+
78+
// create a renderer to control the plane's orientation by its attributes
79+
SimpleRenderer renderer = new SimpleRenderer();
80+
renderer.getSceneProperties().setHeadingExpression("[HEADING]");
81+
renderer.getSceneProperties().setPitchExpression("[PITCH]");
82+
renderer.getSceneProperties().setRollExpression("[ROLL]");
83+
sceneGraphicsOverlay.setRenderer(renderer);
84+
85+
// create a graphic of a plane model
86+
String modelURI = new File("./samples-data/bristol/Collada/Bristol.dae").getAbsolutePath();
87+
ModelSceneSymbol plane3DSymbol = new ModelSceneSymbol(modelURI, 1.0);
88+
plane3DSymbol.loadAsync();
89+
// position the plane over a runway
90+
Graphic plane = new Graphic(new Point(6.637, 45.399, 100, SpatialReferences.getWgs84()), plane3DSymbol);
91+
// initialize the plane's heading to line up with the runway
92+
plane.getAttributes().put("HEADING", 45.0);
93+
sceneGraphicsOverlay.getGraphics().add(plane);
94+
95+
// control the plane's pitch with a slider
96+
planePitchSlider.valueProperty().addListener(o -> plane.getAttributes().put("PITCH", planePitchSlider.getValue()));
97+
98+
// listener for the view to stop loading and to add the camera controller
99+
DrawStatusChangedListener listener = new DrawStatusChangedListener() {
100+
101+
@Override
102+
public void drawStatusChanged(DrawStatusChangedEvent drawStatusChangedEvent) {
103+
if (drawStatusChangedEvent.getDrawStatus() == DrawStatus.COMPLETED) {
104+
105+
// create an orbit geoelement camera controller with the plane as the target
106+
orbitCameraController = new OrbitGeoElementCameraController(plane, 50.0);
107+
108+
// restrict the camera's heading to stay behind the plane
109+
orbitCameraController.setMinCameraHeadingOffset(-45);
110+
orbitCameraController.setMaxCameraHeadingOffset(45);
111+
112+
// restrict the camera's pitch so it doesn't go completely vertical or collide with the ground
113+
orbitCameraController.setMinCameraPitchOffset(10);
114+
orbitCameraController.setMaxCameraPitchOffset(100);
115+
116+
// restrict the camera to stay between 10 and 1000 meters from the plane
117+
orbitCameraController.setMinCameraDistance(10);
118+
orbitCameraController.setMaxCameraDistance(100);
119+
120+
// position the plane a third from the bottom of the screen
121+
orbitCameraController.setTargetVerticalScreenFactor(0.33f);
122+
123+
// don't pitch the camera when the plane pitches
124+
orbitCameraController.setAutoPitchEnabled(false);
125+
126+
// set the orbit camera controller to the scene view
127+
sceneView.setCameraController(orbitCameraController);
128+
129+
// set the camera's heading using a slider
130+
cameraHeadingSlider.valueProperty().addListener(o -> orbitCameraController.setCameraHeadingOffset(cameraHeadingSlider.getValue()));
131+
132+
// update camera heading slider position whilst interacting with the camera heading
133+
sceneView.addViewpointChangedListener( event -> cameraHeadingSlider.setValue(orbitCameraController.getCameraHeadingOffset()));
134+
135+
// stop listening for the view to load
136+
sceneView.removeDrawStatusChangedListener(this);
137+
}
138+
}
139+
};
140+
141+
sceneView.addDrawStatusChangedListener(listener);
142+
143+
} catch (Exception e) {
144+
// on any exception, print the stack trace
145+
e.printStackTrace();
146+
}
147+
}
148+
149+
/**
150+
* Animates the camera to a cockpit view. The camera's target is offset to the cockpit (instead of the plane's
151+
* center). The camera is moved onto the target position to create a swivelling camera effect. Auto pitch is
152+
* enabled so the camera pitches when the plane pitches.
153+
*/
154+
@FXML
155+
private void handleCockpitViewButtonClicked() {
156+
// disable camera distance interaction checkbox
157+
allowDistanceInteractionCheckBox.setDisable(true);
158+
159+
// allow the camera to get closer to the target
160+
orbitCameraController.setMinCameraDistance(0);
161+
162+
// pitch the camera when the plane pitches
163+
orbitCameraController.setAutoPitchEnabled(true);
164+
165+
// animate the camera target to the cockpit instead of the center of the plane
166+
orbitCameraController.setTargetOffsetsAsync(0, -2, 1.1, 1);
167+
168+
// animate the camera so that it is 0.01m from the target (cockpit), facing forward (0 deg heading), and aligned
169+
// with the horizon (90 deg pitch)
170+
orbitCameraController.moveCameraAsync(0 - orbitCameraController.getCameraDistance(),
171+
0 - orbitCameraController.getCameraHeadingOffset(), 90 - orbitCameraController.getCameraPitchOffset(), 1).addDoneListener(() -> {
172+
// once the camera is in the cockpit, only allow the camera's heading to change
173+
orbitCameraController.setMinCameraPitchOffset(90);
174+
orbitCameraController.setMaxCameraPitchOffset(90);
175+
});
176+
}
177+
178+
/**
179+
* Configures the camera controller for a "follow" view. The camera targets the center of the plane with a default
180+
* position directly behind and slightly above the plane. Auto pitch is disabled so the camera does not pitch when
181+
* the plane pitches.
182+
*/
183+
@FXML
184+
private void handleCenterViewButtonClicked() {
185+
allowDistanceInteractionCheckBox.setDisable(false);
186+
187+
orbitCameraController.setAutoPitchEnabled(false);
188+
orbitCameraController.setTargetOffsetX(0);
189+
orbitCameraController.setTargetOffsetY(0);
190+
orbitCameraController.setTargetOffsetZ(0);
191+
orbitCameraController.setCameraHeadingOffset(0);
192+
orbitCameraController.setCameraPitchOffset(45);
193+
orbitCameraController.setMinCameraPitchOffset(10);
194+
orbitCameraController.setMaxCameraPitchOffset(100);
195+
orbitCameraController.setMinCameraDistance(10.0);
196+
orbitCameraController.setCameraDistance(50.0);
197+
}
198+
199+
/**
200+
* Toggle interactive distance. When distance interaction is disabled, the user cannot zoom in with the mouse.
201+
*/
202+
@FXML
203+
private void handleDistanceInteractionCheckBoxToggle() {
204+
orbitCameraController.setCameraDistanceInteractive(allowDistanceInteractionCheckBox.isSelected());
205+
}
206+
207+
/**
208+
* Disposes of application resources.
209+
*/
210+
void terminate() {
211+
212+
// release resources when the application closes
213+
if (sceneView != null) {
214+
sceneView.dispose();
215+
}
216+
}
217+
218+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2019 Esri.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.esri.samples.scene.orbit_the_camera_around_an_object;
18+
19+
import javafx.application.Application;
20+
import javafx.fxml.FXMLLoader;
21+
import javafx.scene.Parent;
22+
import javafx.scene.Scene;
23+
import javafx.stage.Stage;
24+
25+
import java.io.IOException;
26+
27+
public class OrbitTheCameraAroundAnObjectSample extends Application {
28+
29+
private static OrbitTheCameraAroundAnObjectController controller;
30+
31+
32+
@Override
33+
public void start(Stage stage) throws IOException {
34+
35+
// set up the scene
36+
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/orbit_the_camera_around_an_object.fxml"));
37+
Parent root = loader.load();
38+
controller = loader.getController();
39+
Scene scene = new Scene(root);
40+
41+
// set up the stage
42+
stage.setTitle("Orbit the Camera Around an Object Sample");
43+
stage.setWidth(800);
44+
stage.setHeight(700);
45+
stage.setScene(scene);
46+
stage.show();
47+
}
48+
49+
50+
/**
51+
* Stops and releases all resources used in application.
52+
*/
53+
@Override
54+
public void stop() {
55+
controller.terminate();
56+
}
57+
58+
/**
59+
* Opens and runs application.
60+
*
61+
* @param args arguments passed to this application
62+
*/
63+
public static void main(String[] args) {
64+
65+
Application.launch(args);
66+
}
67+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<h1>Orbit the camera around an object</h1>
2+
3+
<p>Fix the camera to point at and rotate around a target object.</p>
4+
5+
<p><img src="OrbitTheCameraAroundAnObject.png"/></p>
6+
7+
<h2>Use case</h2>
8+
9+
<p>The orbit geoelement camera controller provides control over the following camera behaviors:</p>
10+
11+
<ul>
12+
<li>automatically track the target</li>
13+
14+
15+
<li>stay near the target by setting a minimum and maximum distance offset</li>
16+
17+
<li>restrict where you can rotate around the target</li>
18+
19+
<li>automatically rotate the camera when the target's heading and pitch changes</li>
20+
21+
<li>disable user interactions for rotating the camera</li>
22+
23+
<li>animate camera movement over a specified duration</li>
24+
25+
<li>control the vertical positioning of the target on the screen</li>
26+
27+
<li>set a target offset (e.g.to orbit around the tail of the plane) instead of defaulting to orbiting the center of the object</li>
28+
</ul>
29+
30+
<h2>How to use the sample</h2>
31+
32+
<p> The sample loads with the camera orbiting an aeroplane model. The camera is preset with a restricted camera heading and pitch, and a limited minimum and maximum camera distance set from the plane. The position of the plane on the screen is also set just below center.</p>
33+
34+
35+
<p> Use the "Camera Heading" slider to adjust the camera heading. Select the "Allow camera distance interaction" checkbox to allow zooming in and out with the mouse/keyboard: when the checkbox is deselected the user will be unable to adjust with the camera distance.</p>
36+
37+
<p> Use the "Plane Pitch" slider to adjust the plane's pitch. When not in Cockpit view, the plane's pitch will change independently to that of the camera pitch.</p>
38+
39+
<p>Use the "Cockpit view" button to offset and fix the camera into the cockpit of the aeroplane. Use the "Plane pitch" slider to control the pitch of aeroplane: the camera will follow the pitch of the plane in this mode. In this view adjusting the camera distance is disabled. Hit the "Center view" button to exit cockpit view mode and fix the camera controller on the center of the plane.</p>
40+
41+
<h2>How it works</h2>
42+
43+
<ol>
44+
<li>Instantiate an <code>OrbitGeoElementCameraController</code>, with <code>GeoElement</code> and camera distance as parameters.</li>
45+
<li>Use <code>sceneView.setCameraController(OrbitCameraController)</code> to set the camera to the scene view.</li>
46+
<li>Set the heading, pitch and distance camera properties with:
47+
<ul>
48+
<li><code>orbitCameraController.setCameraHeadingOffset(double)</code></li> <li><code>orbitCameraController.setCameraPitchOffset(double)</code></li>
49+
<li><code>orbitCameraController.setCameraDistance(double)</code></li>
50+
</ul></li>
51+
<li>Set the minimum and maximum angle of heading and pitch, and minimum and maximum distance for the camera with:
52+
<ul>
53+
<li><code>orbitCameraController.setMin</code> or <code>setMaxCameraHeadingOffset(double)</code>.</li>
54+
<li><code>orbitCameraController.setMin</code> or <code>setMaxCameraPitchOffset(double)</code>.</li>
55+
<li><code>orbitCameraController.setMin</code> or <code>setMaxCameraDistance(double)</code>.</li>
56+
</ul></li>
57+
<li>Set the distance from which the camera is offset from the plane with:
58+
<ul>
59+
<li><code>orbitCameraController.setTargetOffsetsAsync(x, y, z, duration)</code></li>
60+
<li><code>orbitCameraController.setTargetOffsetX(double)</code></li>
61+
<li><code>orbitCameraController.setTargetOffsetY(double)</code></li>
62+
<li><code>orbitCameraController.setTargetOffsetZ(double)</code></li>
63+
</ul></li>
64+
<li>Set the vertical screen factor to determine where the plane appears in the scene:
65+
<ul>
66+
<li><code>orbitCameraController.setTargetVerticalScreenFactor(float)</code></li>
67+
</ul></li>
68+
69+
<li>Animate the camera to the cockpit using <code>orbitCameraController.setTargetOffsetsAsync(x, y, z, duration)</code>
70+
</li>
71+
72+
<li>Set if the camera distance will adjust when zooming or panning using mouse or keyboard (default is true):
73+
<ul>
74+
<li><code>orbitCameraController.setCameraDistanceInteractive(boolean)</code></li>
75+
</ul></li>
76+
<li>Set if the camera will follow the pitch of the plane (default is true):
77+
<ul>
78+
<li><code>orbitCameraController.setAutoPitchEnabled(boolean)</code></li>
79+
</ul></li>
80+
</ol>
81+
82+
<h2>Relevant API</h2>
83+
84+
<ul>
85+
<li>OrbitGeoElementCameraController</li>
86+
</ul>
87+
88+
<h2>Tags</h2>
89+
90+
OrbitGeoElementCameraController, Camera, SceneView, 3D
91+
92+

0 commit comments

Comments
 (0)