1 package tim.prune.threedee;
3 import java.awt.BorderLayout;
4 import java.awt.FlowLayout;
6 import java.awt.GraphicsConfiguration;
7 import java.awt.GraphicsEnvironment;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.awt.event.WindowAdapter;
11 import java.awt.event.WindowEvent;
12 import java.awt.geom.GeneralPath;
14 import javax.media.j3d.AmbientLight;
15 import javax.media.j3d.Appearance;
16 import javax.media.j3d.BoundingSphere;
17 import javax.media.j3d.BranchGroup;
18 import javax.media.j3d.Canvas3D;
19 import javax.media.j3d.Font3D;
20 import javax.media.j3d.FontExtrusion;
21 import javax.media.j3d.GeometryArray;
22 import javax.media.j3d.GraphicsConfigTemplate3D;
23 import javax.media.j3d.Group;
24 import javax.media.j3d.Material;
25 import javax.media.j3d.PointLight;
26 import javax.media.j3d.QuadArray;
27 import javax.media.j3d.Shape3D;
28 import javax.media.j3d.Text3D;
29 import javax.media.j3d.Texture;
30 import javax.media.j3d.Transform3D;
31 import javax.media.j3d.TransformGroup;
32 import javax.media.j3d.TriangleStripArray;
33 import javax.swing.JButton;
34 import javax.swing.JFrame;
35 import javax.swing.JOptionPane;
36 import javax.swing.JPanel;
37 import javax.vecmath.Color3f;
38 import javax.vecmath.Matrix3d;
39 import javax.vecmath.Point3d;
40 import javax.vecmath.Point3f;
41 import javax.vecmath.TexCoord2f;
42 import javax.vecmath.Vector3d;
44 import tim.prune.FunctionLibrary;
45 import tim.prune.I18nManager;
46 import tim.prune.data.Track;
47 import tim.prune.function.Export3dFunction;
48 import tim.prune.function.srtm.LookupSrtmFunction;
49 import tim.prune.gui.map.MapSourceLibrary;
50 import tim.prune.save.GroutedImage;
51 import tim.prune.save.MapGrouter;
53 import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
54 import com.sun.j3d.utils.geometry.Box;
55 import com.sun.j3d.utils.geometry.Cylinder;
56 import com.sun.j3d.utils.geometry.Sphere;
57 import com.sun.j3d.utils.image.TextureLoader;
58 import com.sun.j3d.utils.universe.SimpleUniverse;
62 * Class to hold main window for java3d view of data
64 public class Java3DWindow implements ThreeDWindow
66 private Track _track = null;
67 private JFrame _parentFrame = null;
68 private JFrame _frame = null;
69 private ThreeDModel _model = null;
70 private OrbitBehavior _orbit = null;
71 private double _altFactor = -1.0;
72 private ImageDefinition _imageDefinition = null;
73 private GroutedImage _baseImage = null;
74 private TerrainDefinition _terrainDefinition = null;
76 /** only prompt about big track size once */
77 private static boolean TRACK_SIZE_WARNING_GIVEN = false;
80 private static final double INITIAL_Y_ROTATION = -25.0;
81 private static final double INITIAL_X_ROTATION = 15.0;
82 private static final String CARDINALS_FONT = "Arial";
83 private static final int MAX_TRACK_SIZE = 2500; // threshold for warning
84 private static final double MODEL_SCALE_FACTOR = 20.0;
89 * @param inFrame parent frame
91 public Java3DWindow(JFrame inFrame)
93 _parentFrame = inFrame;
98 * Set the track object
99 * @param inTrack Track object
101 public void setTrack(Track inTrack)
107 * @param inFactor altitude factor to use
109 public void setAltitudeFactor(double inFactor)
111 _altFactor = inFactor;
115 * Set the parameters for the base image and do the grouting already
116 * (setTrack should already be called by now)
118 public void setBaseImageParameters(ImageDefinition inDefinition)
120 _imageDefinition = inDefinition;
121 if (inDefinition != null && inDefinition.getUseImage())
123 _baseImage = new MapGrouter().createMapImage(_track, MapSourceLibrary.getSource(inDefinition.getSourceIndex()),
124 inDefinition.getZoom());
126 else _baseImage = null;
130 * Set the terrain parameters
132 public void setTerrainParameters(TerrainDefinition inDefinition)
134 _terrainDefinition = inDefinition;
140 public void show() throws ThreeDException
142 // Make sure altitude exaggeration is positive
143 if (_altFactor < 0.0) {_altFactor = 1.0;}
145 // Set up the graphics config
146 GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
149 // Config shouldn't be null, but we can try to create a new one as a workaround
150 GraphicsConfigTemplate3D gc = new GraphicsConfigTemplate3D();
152 config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getBestConfiguration(gc);
157 // Second attempt also failed, going to have to give up here.
158 throw new ThreeDException("Couldn't create graphics config");
161 // Check number of points in model isn't too big, and suggest compression
162 Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")};
163 if (_track.getNumPoints() > MAX_TRACK_SIZE && !TRACK_SIZE_WARNING_GIVEN)
165 if (JOptionPane.showOptionDialog(_parentFrame,
166 I18nManager.getText("dialog.3d.warningtracksize"),
167 I18nManager.getText("function.show3d"), JOptionPane.OK_CANCEL_OPTION,
168 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
169 == JOptionPane.OK_OPTION)
171 // opted to continue, don't show warning again
172 TRACK_SIZE_WARNING_GIVEN = true;
175 // opted to cancel - show warning again next time
180 Canvas3D canvas = new Canvas3D(config);
181 canvas.setSize(400, 300);
183 // Create the scene and attach it to the virtual universe
184 BranchGroup scene = createSceneGraph();
185 SimpleUniverse u = new SimpleUniverse(canvas);
187 // This will move the ViewPlatform back a bit so the
188 // objects in the scene can be viewed.
189 u.getViewingPlatform().setNominalViewingTransform();
191 // Add behaviour to rotate using mouse
192 _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL | OrbitBehavior.STOP_ZOOM);
193 BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
194 _orbit.setSchedulingBounds(bounds);
195 u.getViewingPlatform().setViewPlatformBehavior(_orbit);
196 u.addBranchGraph(scene);
198 // Don't reuse _frame object from last time, because data and/or scale might be different
199 // Need to regenerate everything
200 _frame = new JFrame(I18nManager.getText("dialog.3d.title"));
201 _frame.getContentPane().setLayout(new BorderLayout());
202 _frame.getContentPane().add(canvas, BorderLayout.CENTER);
203 _frame.setIconImage(_parentFrame.getIconImage());
204 // Make panel for render, close buttons
205 JPanel panel = new JPanel();
206 panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
207 // Add button for exporting pov
208 JButton povButton = new JButton(I18nManager.getText("function.exportpov"));
209 povButton.addActionListener(new ActionListener() {
210 /** Export pov button pressed */
211 public void actionPerformed(ActionEvent e)
213 if (_orbit != null) {
214 callbackRender(FunctionLibrary.FUNCTION_POVEXPORT);
217 panel.add(povButton);
218 // Add button for exporting svg
219 JButton svgButton = new JButton(I18nManager.getText("function.exportsvg"));
220 svgButton.addActionListener(new ActionListener() {
221 public void actionPerformed(ActionEvent e)
223 if (_orbit != null) {
224 callbackRender(FunctionLibrary.FUNCTION_SVGEXPORT);
227 panel.add(svgButton);
230 JButton closeButton = new JButton(I18nManager.getText("button.close"));
231 closeButton.addActionListener(new ActionListener()
233 /** Close button pressed - clean up */
234 public void actionPerformed(ActionEvent e) {
239 panel.add(closeButton);
240 _frame.getContentPane().add(panel, BorderLayout.SOUTH);
241 _frame.setSize(500, 350);
243 // Add a listener to clean up when window closed
244 _frame.addWindowListener(new WindowAdapter() {
245 public void windowClosing(WindowEvent e) {
251 _frame.setVisible(true);
252 if (_frame.getState() == JFrame.ICONIFIED) {
253 _frame.setState(JFrame.NORMAL);
258 * Dispose of the frame and its resources
260 public void dispose()
262 if (_frame != null) {
269 * Create the whole scenery from the given track
270 * @return all objects in the scene
272 private BranchGroup createSceneGraph()
274 // Create the root of the branch graph
275 BranchGroup objRoot = new BranchGroup();
277 // Create the transform group node and initialize it.
278 // Enable the TRANSFORM_WRITE capability so it can be spun by the mouse
279 TransformGroup objTrans = new TransformGroup();
280 objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
282 // Create a translation
283 Transform3D shiftz = new Transform3D();
284 shiftz.setScale(0.055);
285 TransformGroup shiftTrans = new TransformGroup(shiftz);
287 objRoot.addChild(shiftTrans);
288 Transform3D rotTrans = new Transform3D();
289 rotTrans.rotY(Math.toRadians(INITIAL_Y_ROTATION));
290 Transform3D rot2 = new Transform3D();
291 rot2.rotX(Math.toRadians(INITIAL_X_ROTATION));
292 TransformGroup tg2 = new TransformGroup(rot2);
293 objTrans.setTransform(rotTrans);
294 shiftTrans.addChild(tg2);
295 tg2.addChild(objTrans);
298 Appearance planeAppearance = null;
300 planeAppearance = new Appearance();
301 planeAppearance.setMaterial(new Material(new Color3f(0.1f, 0.2f, 0.2f),
302 new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.3f, 0.4f, 0.4f),
303 new Color3f(0.3f, 0.3f, 0.3f), 0.0f));
304 plane = new Box(10f, 0.04f, 10f, planeAppearance);
305 objTrans.addChild(plane);
307 // Image on top of base plane, if specified
308 final boolean showTerrain = _terrainDefinition != null && _terrainDefinition.getUseTerrain();
309 if (_baseImage != null && !showTerrain)
311 QuadArray baseSquare = new QuadArray (4, QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2);
312 baseSquare.setCoordinate(0, new Point3f(-10f, 0.05f, -10f));
313 baseSquare.setCoordinate(1, new Point3f(-10f, 0.05f, 10f));
314 baseSquare.setCoordinate(2, new Point3f( 10f, 0.05f, 10f));
315 baseSquare.setCoordinate(3, new Point3f( 10f, 0.05f, -10f));
316 // and set anchor points for the texture
317 baseSquare.setTextureCoordinate(0, 0, new TexCoord2f(0.0f, 1.0f));
318 baseSquare.setTextureCoordinate(0, 1, new TexCoord2f(0.0f, 0.0f));
319 baseSquare.setTextureCoordinate(0, 2, new TexCoord2f(1.0f, 0.0f));
320 baseSquare.setTextureCoordinate(0, 3, new TexCoord2f(1.0f, 1.0f));
321 // Set appearance including image
322 Appearance baseAppearance = new Appearance();
323 Texture mapImage = new TextureLoader(_baseImage.getImage(), _frame).getTexture();
324 baseAppearance.setTexture(mapImage);
325 objTrans.addChild(new Shape3D(baseSquare, baseAppearance));
328 // Create model containing track information
329 _model = new ThreeDModel(_track);
330 _model.setAltitudeFactor(_altFactor);
334 // TODO: Is it maybe possible to cache the last terrainTrack?
335 // (if the dataTrack and the resolution haven't changed)
336 // Construct the terrain track according to these extents and the grid size
337 TerrainHelper terrainHelper = new TerrainHelper(_terrainDefinition.getGridSize());
338 Track terrainTrack = terrainHelper.createGridTrack(_track);
339 // Get the altitudes from SRTM for all the points in the track
340 LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM;
341 srtmLookup.begin(terrainTrack);
342 while (srtmLookup.isRunning())
345 Thread.sleep(750); // just polling in a wait loop isn't ideal but simple
347 catch (InterruptedException e) {}
351 terrainHelper.fixVoids(terrainTrack);
353 // Give the terrain definition to the _model as well
354 _model.setTerrain(terrainTrack);
357 objTrans.addChild(createTerrain(_model, terrainHelper, _baseImage));
361 // No terrain, so just scale the model as it is
366 GeneralPath bevelPath = new GeneralPath();
367 bevelPath.moveTo(0.0f, 0.0f);
368 for (int i=0; i<91; i+= 5) {
369 bevelPath.lineTo((float) (0.1 - 0.1 * Math.cos(Math.toRadians(i))),
370 (float) (0.1 * Math.sin(Math.toRadians(i))));
372 for (int i=90; i>0; i-=5) {
373 bevelPath.lineTo((float) (0.3 + 0.1 * Math.cos(Math.toRadians(i))),
374 (float) (0.1 * Math.sin(Math.toRadians(i))));
376 Font3D compassFont = new Font3D(
377 new Font(CARDINALS_FONT, Font.PLAIN, 1),
378 new FontExtrusion(bevelPath));
379 objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.n"), new Point3f(0f, 0f, -10f), compassFont));
380 objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.s"), new Point3f(0f, 0f, 10f), compassFont));
381 objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.w"), new Point3f(-11f, 0f, 0f), compassFont));
382 objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.e"), new Point3f(10f, 0f, 0f), compassFont));
384 // Add points to model
385 objTrans.addChild(createDataPoints(_model));
388 BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
389 AmbientLight aLgt = new AmbientLight(new Color3f(1.0f, 1.0f, 1.0f));
390 aLgt.setInfluencingBounds(bounds);
391 objTrans.addChild(aLgt);
393 PointLight pLgt = new PointLight(new Color3f(1.0f, 1.0f, 1.0f),
394 new Point3f(0f, 0f, 2f), new Point3f(0.25f, 0.05f, 0.0f) );
395 pLgt.setInfluencingBounds(bounds);
396 objTrans.addChild(pLgt);
398 PointLight pl2 = new PointLight(new Color3f(0.8f, 0.9f, 0.4f),
399 new Point3f(6f, 1f, 6f), new Point3f(0.2f, 0.1f, 0.05f) );
400 pl2.setInfluencingBounds(bounds);
401 objTrans.addChild(pl2);
403 PointLight pl3 = new PointLight(new Color3f(0.7f, 0.7f, 0.7f),
404 new Point3f(0.0f, 12f, -2f), new Point3f(0.1f, 0.1f, 0.0f) );
405 pl3.setInfluencingBounds(bounds);
406 objTrans.addChild(pl3);
408 // Have Java 3D perform optimizations on this scene graph.
416 * Create a text object for compass point, N S E or W
417 * @param text text to display
418 * @param locn position at which to display
419 * @param font 3d font to use
420 * @return Shape3D object
422 private Shape3D createCompassPoint(String inText, Point3f inLocn, Font3D inFont)
424 Text3D txt = new Text3D(inFont, inText, inLocn, Text3D.ALIGN_FIRST, Text3D.PATH_RIGHT);
425 Material mat = new Material(new Color3f(0.5f, 0.5f, 0.55f),
426 new Color3f(0.05f, 0.05f, 0.1f), new Color3f(0.3f, 0.4f, 0.5f),
427 new Color3f(0.4f, 0.5f, 0.7f), 70.0f);
428 mat.setLightingEnable(true);
429 Appearance app = new Appearance();
430 app.setMaterial(mat);
431 Shape3D shape = new Shape3D(txt, app);
437 * Make a Group of the data points to be added
438 * @param inModel model containing data
439 * @return Group object containing spheres, rods etc
441 private static Group createDataPoints(ThreeDModel inModel)
443 // Add points to model
444 Group group = new Group();
445 int numPoints = inModel.getNumPoints();
446 for (int i=0; i<numPoints; i++)
448 byte pointType = inModel.getPointType(i);
449 if (pointType == ThreeDModel.POINT_TYPE_WAYPOINT)
452 // Note that x, y and z are horiz, altitude, -vert
453 group.addChild(createWaypoint(new Point3d(
454 inModel.getScaledHorizValue(i) * MODEL_SCALE_FACTOR,
455 inModel.getScaledAltValue(i) * MODEL_SCALE_FACTOR,
456 -inModel.getScaledVertValue(i) * MODEL_SCALE_FACTOR)));
460 // Add colour-coded track point
461 // Note that x, y and z are horiz, altitude, -vert
462 group.addChild(createTrackpoint(new Point3d(
463 inModel.getScaledHorizValue(i) * MODEL_SCALE_FACTOR,
464 inModel.getScaledAltValue(i) * MODEL_SCALE_FACTOR,
465 -inModel.getScaledVertValue(i) * MODEL_SCALE_FACTOR), inModel.getPointHeightCode(i)));
473 * Create a waypoint sphere
474 * @param inPointPos position of point
475 * @return Group object containing sphere
477 private static Group createWaypoint(Point3d inPointPos)
479 Material mat = getWaypointMaterial();
480 // MAYBE: sort symbol scaling
481 Sphere dot = new Sphere(0.35f); // * symbolScaling / 100f);
482 return createBall(inPointPos, dot, mat);
487 * @return a new Material object to define waypoint colour / shine etc
489 private static Material getWaypointMaterial()
491 return new Material(new Color3f(0.1f, 0.1f, 0.4f),
492 new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.0f, 0.2f, 0.7f),
493 new Color3f(1.0f, 0.6f, 0.6f), 40.0f);
498 * @return track point object
500 private static Group createTrackpoint(Point3d inPointPos, byte inHeightCode)
502 Material mat = getTrackpointMaterial(inHeightCode);
503 // MAYBE: sort symbol scaling
504 Sphere dot = new Sphere(0.2f);
505 return createBall(inPointPos, dot, mat);
510 * @return Material object for track points with the appropriate colour for the height
512 private static Material getTrackpointMaterial(byte inHeightCode)
514 // create default material
515 Material mat = new Material(new Color3f(0.3f, 0.2f, 0.1f),
516 new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.0f, 0.6f, 0.0f),
517 new Color3f(1.0f, 0.6f, 0.6f), 70.0f);
518 // change colour according to height code
519 if (inHeightCode == 1) mat.setDiffuseColor(new Color3f(0.4f, 0.9f, 0.2f));
520 else if (inHeightCode == 2) mat.setDiffuseColor(new Color3f(0.7f, 0.8f, 0.2f));
521 else if (inHeightCode == 3) mat.setDiffuseColor(new Color3f(0.3f, 0.6f, 0.4f));
522 else if (inHeightCode == 4) mat.setDiffuseColor(new Color3f(0.1f, 0.9f, 0.9f));
523 else if (inHeightCode >= 5) mat.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f));
530 * Create a ball at the given point
531 * @param inPosition scaled position of point
532 * @param inSphere sphere object
533 * @param inMaterial material object
534 * @return Group containing sphere
536 private static Group createBall(Point3d inPosition, Sphere inSphere, Material inMaterial)
538 Group group = new Group();
539 // Create ball and add to group
540 Transform3D ballShift = new Transform3D();
541 ballShift.setTranslation(new Vector3d(inPosition));
542 TransformGroup ballShiftTrans = new TransformGroup(ballShift);
543 inMaterial.setLightingEnable(true);
544 Appearance ballApp = new Appearance();
545 ballApp.setMaterial(inMaterial);
546 inSphere.setAppearance(ballApp);
547 ballShiftTrans.addChild(inSphere);
548 group.addChild(ballShiftTrans);
549 // Also create rod for ball to sit on
550 Cylinder rod = new Cylinder(0.1f, (float) inPosition.y);
551 Material rodMat = new Material(new Color3f(0.2f, 0.2f, 0.2f),
552 new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.2f, 0.2f, 0.2f),
553 new Color3f(0.05f, 0.05f, 0.05f), 0.4f);
554 rodMat.setLightingEnable(true);
555 Appearance rodApp = new Appearance();
556 rodApp.setMaterial(rodMat);
557 rod.setAppearance(rodApp);
558 Transform3D rodShift = new Transform3D();
559 rodShift.setTranslation(new Vector3d(inPosition.x, inPosition.y/2.0, inPosition.z));
560 TransformGroup rodShiftTrans = new TransformGroup(rodShift);
561 rodShiftTrans.addChild(rod);
562 group.addChild(rodShiftTrans);
568 * Create a java3d Shape for the terrain
569 * @param inModel threedModel
570 * @param inHelper terrain helper
571 * @param inBaseImage base image for shape, or null for no image
572 * @return Shape3D object
574 private static Shape3D createTerrain(ThreeDModel inModel, TerrainHelper inHelper, GroutedImage inBaseImage)
576 final int numNodes = inHelper.getGridSize();
577 final int RESULT_SIZE = numNodes * (numNodes * 2 - 2);
578 final int GEOMETRY_COLOURING_TYPE = (inBaseImage == null ? GeometryArray.COLOR_3 : GeometryArray.TEXTURE_COORDINATE_2);
580 int[] stripData = inHelper.getStripLengths();
581 TriangleStripArray tsa = new TriangleStripArray(RESULT_SIZE, GeometryArray.COORDINATES | GEOMETRY_COLOURING_TYPE,
583 // Get the scaled terrainTrack coordinates (or just heights) from the model
584 final int nSquared = numNodes * numNodes;
585 Point3d[] rawPoints = new Point3d[nSquared];
586 for (int i=0; i<nSquared; i++)
588 double height = inModel.getScaledTerrainValue(i) * MODEL_SCALE_FACTOR;
589 rawPoints[i] = new Point3d(inModel.getScaledTerrainHorizValue(i) * MODEL_SCALE_FACTOR,
590 Math.max(height, 0.05), // make sure it's above the box
591 -inModel.getScaledTerrainVertValue(i) * MODEL_SCALE_FACTOR);
593 tsa.setCoordinates(0, inHelper.getTerrainCoordinates(rawPoints));
595 Appearance tAppearance = new Appearance();
596 if (inBaseImage != null)
598 tsa.setTextureCoordinates(0, 0, inHelper.getTextureCoordinates());
599 Texture mapImage = new TextureLoader(inBaseImage.getImage()).getTexture();
600 tAppearance.setTexture(mapImage);
604 Color3f[] colours = new Color3f[RESULT_SIZE];
605 Color3f terrainColour = new Color3f(0.1f, 0.2f, 0.2f);
606 for (int i=0; i<RESULT_SIZE; i++) {colours[i] = terrainColour;}
607 tsa.setColors(0, colours);
609 return new Shape3D(tsa, tAppearance);
613 * Calculate the angles and call them back to the app
614 * @param inFunction function to call (either pov or svg)
616 private void callbackRender(Export3dFunction inFunction)
618 Transform3D trans3d = new Transform3D();
619 _orbit.getViewingPlatform().getViewPlatformTransform().getTransform(trans3d);
620 Matrix3d matrix = new Matrix3d();
622 Point3d point = new Point3d(0.0, 0.0, 1.0);
623 matrix.transform(point);
624 // Set up initial rotations
625 Transform3D firstTran = new Transform3D();
626 firstTran.rotY(Math.toRadians(-INITIAL_Y_ROTATION));
627 Transform3D secondTran = new Transform3D();
628 secondTran.rotX(Math.toRadians(-INITIAL_X_ROTATION));
629 // Apply inverse rotations in reverse order to the test point
630 Point3d result = new Point3d();
631 secondTran.transform(point, result);
632 firstTran.transform(result);
634 // Give the settings to the rendering function
635 inFunction.setCameraCoordinates(result.x, result.y, result.z);
636 inFunction.setAltitudeExaggeration(_altFactor);
637 inFunction.setTerrainDefinition(_terrainDefinition); // ignored by svg, used by pov
638 inFunction.setImageDefinition(_imageDefinition); // ignored by svg, used by pov