X-Git-Url: https://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2Fthreedee%2FJava3DWindow.java;h=579245db62eb2d07a08d005529f234ea13fa4bb4;hp=e3531f08d6acb3f4914e632c6236a0ef2121fd9d;hb=92dad5df664287acb51728e9ea599f150765d34a;hpb=54b9d8bc8f0025ccf97a67d9dd217ef1f9cf082f diff --git a/tim/prune/threedee/Java3DWindow.java b/tim/prune/threedee/Java3DWindow.java index e3531f0..579245d 100644 --- a/tim/prune/threedee/Java3DWindow.java +++ b/tim/prune/threedee/Java3DWindow.java @@ -1,7 +1,7 @@ package tim.prune.threedee; -import java.awt.FlowLayout; import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.Font; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; @@ -18,12 +18,16 @@ import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Font3D; import javax.media.j3d.FontExtrusion; +import javax.media.j3d.GeometryArray; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.Material; import javax.media.j3d.PointLight; +import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Text3D; +import javax.media.j3d.Texture; +import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.swing.JButton; @@ -34,19 +38,28 @@ import javax.vecmath.Color3f; import javax.vecmath.Matrix3d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; +import javax.vecmath.TexCoord2f; import javax.vecmath.Vector3d; +import tim.prune.DataStatus; +import tim.prune.FunctionLibrary; +import tim.prune.I18nManager; +import tim.prune.data.Track; +import tim.prune.function.Export3dFunction; +import tim.prune.function.srtm.LookupSrtmFunction; +import tim.prune.gui.map.MapSourceLibrary; +import tim.prune.save.GroutedImage; +import tim.prune.save.MapGrouter; + import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.geometry.Cylinder; +import com.sun.j3d.utils.geometry.GeometryInfo; +import com.sun.j3d.utils.geometry.NormalGenerator; import com.sun.j3d.utils.geometry.Sphere; +import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; -import tim.prune.FunctionLibrary; -import tim.prune.I18nManager; -import tim.prune.data.Altitude; -import tim.prune.data.Track; - /** * Class to hold main window for java3d view of data @@ -58,7 +71,11 @@ public class Java3DWindow implements ThreeDWindow private JFrame _frame = null; private ThreeDModel _model = null; private OrbitBehavior _orbit = null; - private int _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP; + private double _altFactor = -1.0; + private ImageDefinition _imageDefinition = null; + private GroutedImage _baseImage = null; + private TerrainDefinition _terrainDefinition = null; + private DataStatus _dataStatus = null; /** only prompt about big track size once */ private static boolean TRACK_SIZE_WARNING_GIVEN = false; @@ -68,6 +85,7 @@ public class Java3DWindow implements ThreeDWindow private static final double INITIAL_X_ROTATION = 15.0; private static final String CARDINALS_FONT = "Arial"; private static final int MAX_TRACK_SIZE = 2500; // threshold for warning + private static final double MODEL_SCALE_FACTOR = 20.0; /** @@ -89,24 +107,52 @@ public class Java3DWindow implements ThreeDWindow _track = inTrack; } + /** + * @param inFactor altitude factor to use + */ + public void setAltitudeFactor(double inFactor) + { + _altFactor = inFactor; + } /** - * Show the window + * Set the parameters for the base image and do the grouting already + * (setTrack should already be called by now) */ - public void show() throws ThreeDException + public void setBaseImageParameters(ImageDefinition inDefinition) { - // Get the altitude cap to use - String altitudeUnits = getAltitudeUnitsLabel(_track); - Object altCapString = JOptionPane.showInputDialog(_parentFrame, - I18nManager.getText("dialog.3d.altitudecap") + " (" + altitudeUnits + ")", - I18nManager.getText("dialog.3d.title"), - JOptionPane.QUESTION_MESSAGE, null, null, "" + _altitudeCap); - if (altCapString == null) return; - try + _imageDefinition = inDefinition; + if (inDefinition != null && inDefinition.getUseImage()) { - _altitudeCap = Integer.parseInt(altCapString.toString()); + _baseImage = new MapGrouter().createMapImage(_track, MapSourceLibrary.getSource(inDefinition.getSourceIndex()), + inDefinition.getZoom()); } - catch (Exception e) {} // Ignore parse errors + else _baseImage = null; + } + + /** + * Set the terrain parameters + */ + public void setTerrainParameters(TerrainDefinition inDefinition) + { + _terrainDefinition = inDefinition; + } + + /** + * Set the current data status + */ + public void setDataStatus(DataStatus inStatus) + { + _dataStatus = inStatus; + } + + /** + * Show the window + */ + public void show() throws ThreeDException + { + // Make sure altitude exaggeration is positive + if (_altFactor < 0.0) {_altFactor = 1.0;} // Set up the graphics config GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); @@ -128,17 +174,16 @@ public class Java3DWindow implements ThreeDWindow Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")}; if (_track.getNumPoints() > MAX_TRACK_SIZE && !TRACK_SIZE_WARNING_GIVEN) { - if (JOptionPane.showOptionDialog(_frame, - I18nManager.getText("dialog.exportpov.warningtracksize"), - I18nManager.getText("function.exportpov"), JOptionPane.OK_CANCEL_OPTION, + if (JOptionPane.showOptionDialog(_parentFrame, + I18nManager.getText("dialog.3d.warningtracksize"), + I18nManager.getText("function.show3d"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) == JOptionPane.OK_OPTION) { // opted to continue, don't show warning again TRACK_SIZE_WARNING_GIVEN = true; } - else - { + else { // opted to cancel - show warning again next time return; } @@ -156,8 +201,7 @@ public class Java3DWindow implements ThreeDWindow u.getViewingPlatform().setNominalViewingTransform(); // Add behaviour to rotate using mouse - _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL | - OrbitBehavior.STOP_ZOOM); + _orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL | OrbitBehavior.STOP_ZOOM); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0); _orbit.setSchedulingBounds(bounds); u.getViewingPlatform().setViewPlatformBehavior(_orbit); @@ -172,40 +216,24 @@ public class Java3DWindow implements ThreeDWindow // Make panel for render, close buttons JPanel panel = new JPanel(); panel.setLayout(new FlowLayout(FlowLayout.RIGHT)); - // Add callback button for render - JButton renderButton = new JButton(I18nManager.getText("function.exportpov")); - renderButton.addActionListener(new ActionListener() - { - /** Render button pressed */ + // Add button for exporting pov + JButton povButton = new JButton(I18nManager.getText("function.exportpov")); + povButton.addActionListener(new ActionListener() { + /** Export pov button pressed */ public void actionPerformed(ActionEvent e) { - if (_orbit != null) - { - callbackRender(); + if (_orbit != null) { + callbackRender(FunctionLibrary.FUNCTION_POVEXPORT); } }}); - panel.add(renderButton); - // Display coordinates of lat/long lines of 3d graph in separate dialog - JButton showLinesButton = new JButton(I18nManager.getText("button.showlines")); - showLinesButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - double[] latLines = _model.getLatitudeLines(); - double[] lonLines = _model.getLongitudeLines(); - LineDialog dialog = new LineDialog(_frame, latLines, lonLines); - dialog.showDialog(); - } - }); - panel.add(showLinesButton); + panel.add(povButton); // Close button JButton closeButton = new JButton(I18nManager.getText("button.close")); closeButton.addActionListener(new ActionListener() { /** Close button pressed - clean up */ - public void actionPerformed(ActionEvent e) - { - _frame.dispose(); - _frame = null; + public void actionPerformed(ActionEvent e) { + dispose(); _orbit = null; } }); @@ -215,16 +243,14 @@ public class Java3DWindow implements ThreeDWindow _frame.pack(); // Add a listener to clean up when window closed _frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) - { + public void windowClosing(WindowEvent e) { dispose(); } }); // show frame _frame.setVisible(true); - if (_frame.getState() == JFrame.ICONIFIED) - { + if (_frame.getState() == JFrame.ICONIFIED) { _frame.setState(JFrame.NORMAL); } } @@ -274,20 +300,87 @@ public class Java3DWindow implements ThreeDWindow Box plane = null; planeAppearance = new Appearance(); planeAppearance.setMaterial(new Material(new Color3f(0.1f, 0.2f, 0.2f), - new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.3f, 0.4f, 0.4f), - new Color3f(0.3f, 0.3f, 0.3f), 0.0f)); + new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.3f, 0.4f, 0.4f), + new Color3f(0.3f, 0.3f, 0.3f), 0.0f)); plane = new Box(10f, 0.04f, 10f, planeAppearance); objTrans.addChild(plane); + // Image on top of base plane, if specified + final boolean showTerrain = _terrainDefinition != null && _terrainDefinition.getUseTerrain(); + if (_baseImage != null && !showTerrain) + { + QuadArray baseSquare = new QuadArray (4, QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2); + baseSquare.setCoordinate(0, new Point3f(-10f, 0.05f, -10f)); + baseSquare.setCoordinate(1, new Point3f(-10f, 0.05f, 10f)); + baseSquare.setCoordinate(2, new Point3f( 10f, 0.05f, 10f)); + baseSquare.setCoordinate(3, new Point3f( 10f, 0.05f, -10f)); + // and set anchor points for the texture + baseSquare.setTextureCoordinate(0, 0, new TexCoord2f(0.0f, 1.0f)); + baseSquare.setTextureCoordinate(0, 1, new TexCoord2f(0.0f, 0.0f)); + baseSquare.setTextureCoordinate(0, 2, new TexCoord2f(1.0f, 0.0f)); + baseSquare.setTextureCoordinate(0, 3, new TexCoord2f(1.0f, 1.0f)); + // Set appearance including image + Appearance baseAppearance = new Appearance(); + Texture mapImage = new TextureLoader(_baseImage.getImage(), _frame).getTexture(); + baseAppearance.setTexture(mapImage); + objTrans.addChild(new Shape3D(baseSquare, baseAppearance)); + } + + // Create model containing track information + _model = new ThreeDModel(_track); + _model.setAltitudeFactor(_altFactor); + + if (showTerrain) + { + TerrainHelper terrainHelper = new TerrainHelper(_terrainDefinition.getGridSize()); + // See if there's a previously saved terrain track we can reuse + Track terrainTrack = TerrainCache.getTerrainTrack(_dataStatus, _terrainDefinition); + if (terrainTrack == null) + { + // Construct the terrain track according to these extents and the grid size + terrainTrack = terrainHelper.createGridTrack(_track); + // Get the altitudes from SRTM for all the points in the track + LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM; + srtmLookup.begin(terrainTrack); + while (srtmLookup.isRunning()) + { + try { + Thread.sleep(750); // just polling in a wait loop isn't ideal but simple + } + catch (InterruptedException e) {} + } + + // Fix the voids + terrainHelper.fixVoids(terrainTrack); + + // Store this back in the cache, maybe we'll need it again + TerrainCache.storeTerrainTrack(terrainTrack, _dataStatus, _terrainDefinition); + } + // else System.out.println("Yay - reusing the cached track!"); + + // Give the terrain definition to the _model as well + _model.setTerrain(terrainTrack); + _model.scale(); + + objTrans.addChild(createTerrain(_model, terrainHelper, _baseImage)); + } + else + { + // No terrain, so just scale the model as it is + _model.scale(); + } + // N, S, E, W GeneralPath bevelPath = new GeneralPath(); bevelPath.moveTo(0.0f, 0.0f); - for (int i=0; i<91; i+= 5) + for (int i=0; i<91; i+= 5) { bevelPath.lineTo((float) (0.1 - 0.1 * Math.cos(Math.toRadians(i))), (float) (0.1 * Math.sin(Math.toRadians(i)))); - for (int i=90; i>0; i-=5) + } + for (int i=90; i>0; i-=5) { bevelPath.lineTo((float) (0.3 + 0.1 * Math.cos(Math.toRadians(i))), (float) (0.1 * Math.sin(Math.toRadians(i)))); + } Font3D compassFont = new Font3D( new Font(CARDINALS_FONT, Font.PLAIN, 1), new FontExtrusion(bevelPath)); @@ -296,39 +389,27 @@ public class Java3DWindow implements ThreeDWindow objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.w"), new Point3f(-11f, 0f, 0f), compassFont)); objTrans.addChild(createCompassPoint(I18nManager.getText("cardinal.e"), new Point3f(10f, 0f, 0f), compassFont)); - // create and scale model - _model = new ThreeDModel(_track); - _model.setAltitudeCap(_altitudeCap); - _model.scale(); - - // Lat/Long lines - objTrans.addChild(createLatLongs(_model)); - // Add points to model objTrans.addChild(createDataPoints(_model)); // Create lights - BoundingSphere bounds = - new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0); + BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0); AmbientLight aLgt = new AmbientLight(new Color3f(1.0f, 1.0f, 1.0f)); aLgt.setInfluencingBounds(bounds); objTrans.addChild(aLgt); PointLight pLgt = new PointLight(new Color3f(1.0f, 1.0f, 1.0f), - new Point3f(0f, 0f, 2f), - new Point3f(0.25f, 0.05f, 0.0f) ); + new Point3f(0f, 0f, 2f), new Point3f(0.25f, 0.05f, 0.0f) ); pLgt.setInfluencingBounds(bounds); objTrans.addChild(pLgt); PointLight pl2 = new PointLight(new Color3f(0.8f, 0.9f, 0.4f), - new Point3f(6f, 1f, 6f), - new Point3f(0.2f, 0.1f, 0.05f) ); + new Point3f(6f, 1f, 6f), new Point3f(0.2f, 0.1f, 0.05f) ); pl2.setInfluencingBounds(bounds); objTrans.addChild(pl2); PointLight pl3 = new PointLight(new Color3f(0.7f, 0.7f, 0.7f), - new Point3f(0.0f, 12f, -2f), - new Point3f(0.1f, 0.1f, 0.0f) ); + new Point3f(0.0f, 12f, -2f), new Point3f(0.1f, 0.1f, 0.0f) ); pl3.setInfluencingBounds(bounds); objTrans.addChild(pl3); @@ -350,8 +431,8 @@ public class Java3DWindow implements ThreeDWindow { Text3D txt = new Text3D(inFont, inText, inLocn, Text3D.ALIGN_FIRST, Text3D.PATH_RIGHT); Material mat = new Material(new Color3f(0.5f, 0.5f, 0.55f), - new Color3f(0.05f, 0.05f, 0.1f), new Color3f(0.3f, 0.4f, 0.5f), - new Color3f(0.4f, 0.5f, 0.7f), 70.0f); + new Color3f(0.05f, 0.05f, 0.1f), new Color3f(0.3f, 0.4f, 0.5f), + new Color3f(0.4f, 0.5f, 0.7f), 70.0f); mat.setLightingEnable(true); Appearance app = new Appearance(); app.setMaterial(mat); @@ -360,70 +441,6 @@ public class Java3DWindow implements ThreeDWindow } - /** - * Create all the latitude and longitude lines on the base plane - * @param inModel model containing data - * @return Group object containing cylinders for lat and long lines - */ - private static Group createLatLongs(ThreeDModel inModel) - { - Group group = new Group(); - int numlines = inModel.getLatitudeLines().length; - for (int i=0; i= 5) mat.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f)); + else if (inHeightCode == 2) mat.setDiffuseColor(new Color3f(0.7f, 0.8f, 0.2f)); + else if (inHeightCode == 3) mat.setDiffuseColor(new Color3f(0.3f, 0.6f, 0.4f)); + else if (inHeightCode == 4) mat.setDiffuseColor(new Color3f(0.1f, 0.9f, 0.9f)); + else if (inHeightCode >= 5) mat.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f)); // return object return mat; } @@ -531,15 +557,14 @@ public class Java3DWindow implements ThreeDWindow // Also create rod for ball to sit on Cylinder rod = new Cylinder(0.1f, (float) inPosition.y); Material rodMat = new Material(new Color3f(0.2f, 0.2f, 0.2f), - new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.2f, 0.2f, 0.2f), - new Color3f(0.05f, 0.05f, 0.05f), 0.4f); + new Color3f(0.0f, 0.0f, 0.0f), new Color3f(0.2f, 0.2f, 0.2f), + new Color3f(0.05f, 0.05f, 0.05f), 0.4f); rodMat.setLightingEnable(true); Appearance rodApp = new Appearance(); rodApp.setMaterial(rodMat); rod.setAppearance(rodApp); Transform3D rodShift = new Transform3D(); - rodShift.setTranslation(new Vector3d(inPosition.x, - inPosition.y/2.0, inPosition.z)); + rodShift.setTranslation(new Vector3d(inPosition.x, inPosition.y/2.0, inPosition.z)); TransformGroup rodShiftTrans = new TransformGroup(rodShift); rodShiftTrans.addChild(rod); group.addChild(rodShiftTrans); @@ -547,11 +572,67 @@ public class Java3DWindow implements ThreeDWindow return group; } + /** + * Create a java3d Shape for the terrain + * @param inModel threedModel + * @param inHelper terrain helper + * @param inBaseImage base image for shape, or null for no image + * @return Shape3D object + */ + private static Shape3D createTerrain(ThreeDModel inModel, TerrainHelper inHelper, GroutedImage inBaseImage) + { + final int numNodes = inHelper.getGridSize(); + final int RESULT_SIZE = numNodes * (numNodes * 2 - 2); + int[] stripData = inHelper.getStripLengths(); + + // Get the scaled terrainTrack coordinates (or just heights) from the model + final int nSquared = numNodes * numNodes; + Point3d[] rawPoints = new Point3d[nSquared]; + for (int i=0; i