| /******************************************************************************* |
| * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| <<<<<<< HEAD |
| * Pierre Allard - initial API and implementation |
| * |
| ======= |
| * Pierre Allard - initial API and implementation |
| * |
| >>>>>>> refs/heads/eclipse_pa |
| * SPDX-License-Identifier: EPL-1.0 |
| *******************************************************************************/ |
| package org.eclipse.apogy.core.environment.earth.surface.ui.jme3; |
| |
| import java.awt.image.BufferedImage; |
| import java.net.URL; |
| import java.text.DecimalFormat; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.media.j3d.LineAttributes; |
| import javax.media.j3d.Transform3D; |
| import javax.vecmath.Matrix4d; |
| import javax.vecmath.Vector3d; |
| |
| import org.eclipse.apogy.common.ApogyCommonOSGiUtilities; |
| import org.eclipse.apogy.common.EclipseUtils; |
| import org.eclipse.apogy.common.images.AbstractEImage; |
| import org.eclipse.apogy.common.images.ApogyCommonImagesFactory; |
| import org.eclipse.apogy.common.images.EImage; |
| import org.eclipse.apogy.common.images.EImagesUtilities; |
| import org.eclipse.apogy.common.images.URLEImage; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3Utilities; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.jme3.asset.AssetManager; |
| import com.jme3.asset.plugins.FileLocator; |
| import com.jme3.bounding.BoundingSphere; |
| import com.jme3.font.BitmapFont; |
| import com.jme3.font.BitmapText; |
| import com.jme3.font.Rectangle; |
| import com.jme3.material.Material; |
| import com.jme3.math.Matrix3f; |
| import com.jme3.math.Vector3f; |
| import com.jme3.renderer.queue.RenderQueue; |
| import com.jme3.renderer.queue.RenderQueue.Bucket; |
| import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
| import com.jme3.scene.Geometry; |
| import com.jme3.scene.Mesh; |
| import com.jme3.scene.Node; |
| import com.jme3.scene.Spatial; |
| import com.jme3.scene.shape.Sphere; |
| import com.jme3.texture.Image; |
| import com.jme3.texture.Texture; |
| import com.jme3.texture.Texture2D; |
| import com.jme3.texture.TextureCubeMap; |
| import com.jme3.texture.plugins.AWTLoader; |
| import com.jme3.util.BufferUtils; |
| |
| public class EnvironmentUIJME3Utilities { |
| private static final Logger Logger = LoggerFactory.getLogger(EnvironmentUIJME3Utilities.class); |
| |
| private static DecimalFormat decimalFormat = new DecimalFormat("0.0"); |
| private static Map<Double, String> labelMap; |
| |
| /** |
| * Creates a Mesh representing a grid in the XY plane. |
| * |
| * @param gridSize The size of the grid squares. |
| * @param planeSize The overall size of the grid. |
| * @return The Mesh. |
| */ |
| public static Mesh createGrid(float gridSize, float planeSize) { |
| float planeHalfSize = planeSize / 2.0f; |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| // Lines in the Y direction |
| float x = 0; |
| while (x <= planeHalfSize) { |
| Vector3f p1 = new Vector3f(x, -planeHalfSize, 0); |
| Vector3f p2 = new Vector3f(x, planeHalfSize, 0); |
| |
| verticesList.add(p1); |
| verticesList.add(p2); |
| |
| indexesList.add(verticesList.indexOf(p1)); |
| indexesList.add(verticesList.indexOf(p2)); |
| |
| x += gridSize; |
| } |
| |
| x = 0.0f; |
| while (x >= -planeHalfSize) { |
| Vector3f p1 = new Vector3f(x, -planeHalfSize, 0); |
| Vector3f p2 = new Vector3f(x, planeHalfSize, 0); |
| |
| verticesList.add(p1); |
| verticesList.add(p2); |
| |
| indexesList.add(verticesList.indexOf(p1)); |
| indexesList.add(verticesList.indexOf(p2)); |
| |
| x -= gridSize; |
| } |
| |
| // Lines in the X direction |
| float y = 0; |
| while (y <= planeHalfSize) { |
| Vector3f p1 = new Vector3f(-planeHalfSize, y, 0); |
| Vector3f p2 = new Vector3f(planeHalfSize, y, 0); |
| |
| verticesList.add(p1); |
| verticesList.add(p2); |
| |
| indexesList.add(verticesList.indexOf(p1)); |
| indexesList.add(verticesList.indexOf(p2)); |
| |
| y += gridSize; |
| } |
| |
| y = 0; |
| while (y >= -planeHalfSize) { |
| Vector3f p1 = new Vector3f(-planeHalfSize, y, 0); |
| Vector3f p2 = new Vector3f(planeHalfSize, y, 0); |
| |
| verticesList.add(p1); |
| verticesList.add(p2); |
| |
| indexesList.add(verticesList.indexOf(p1)); |
| indexesList.add(verticesList.indexOf(p2)); |
| |
| y -= gridSize; |
| } |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| return mesh; |
| } |
| |
| /** |
| * Creates a square plane in the XY plane centered at the origin. |
| * |
| * @param planeSize The overall size of the plane. |
| * @return The plane mesh. |
| */ |
| public static Mesh createPlane(float planeSize) { |
| float planeHalfSize = planeSize / 2.0f; |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| Vector3f p0 = new Vector3f(-planeHalfSize, -planeHalfSize, 0); |
| Vector3f p1 = new Vector3f(planeHalfSize, -planeHalfSize, 0); |
| Vector3f p2 = new Vector3f(planeHalfSize, planeHalfSize, 0); |
| Vector3f p3 = new Vector3f(-planeHalfSize, planeHalfSize, 0); |
| |
| verticesList.add(p0); |
| verticesList.add(p1); |
| verticesList.add(p2); |
| verticesList.add(p3); |
| |
| indexesList.add(new Integer(0)); |
| indexesList.add(new Integer(1)); |
| indexesList.add(new Integer(2)); |
| |
| indexesList.add(new Integer(0)); |
| indexesList.add(new Integer(2)); |
| indexesList.add(new Integer(3)); |
| |
| Mesh mesh = new Mesh(); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| return mesh; |
| } |
| |
| /** |
| * Creates a node that displays the azimuth |
| * |
| * @return |
| */ |
| public static Node createAzimuthDisplay(final AssetManager assetManager) { |
| Node node = new Node("Worksite Azimuth Display."); |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| // Create the major ticks. |
| float angle = 0.0f; |
| while (angle < 360) { |
| float x = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(angle))); |
| float y = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(angle))); |
| float z0 = 0; |
| float z1 = EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAJOR_TICKS_HEIGHT; |
| |
| Vector3f p0 = new Vector3f(x, y, z0); |
| Vector3f p1 = new Vector3f(x, y, z1); |
| |
| // Adds line |
| verticesList.add(p0); |
| verticesList.add(p1); |
| |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| // Adds text. |
| node.attachChild(createLabel(assetManager, getAzimuthLabelText(360 - angle), x, y, z1 * 1.1f, |
| Math.toRadians(angle), 0, EarthSurfaceEnvironmentJMEConstants.MAJOR_TICKS_FONT_SIZE)); |
| |
| angle += EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAJOR_TICK_ANGLE_INTERVAL_IN_DEG; |
| } |
| |
| // Create the mid ticks. |
| angle = 45; |
| while (angle < 360) { |
| if (Math.IEEEremainder(angle, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAJOR_TICK_ANGLE_INTERVAL_IN_DEG) != 0) { |
| float x = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(angle))); |
| float y = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(angle))); |
| float z0 = 0; |
| float z1 = EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MIDDLE_TICKS_HEIGHT; |
| |
| Vector3f p0 = new Vector3f(x, y, z0); |
| Vector3f p1 = new Vector3f(x, y, z1); |
| |
| // Adds line |
| verticesList.add(p0); |
| verticesList.add(p1); |
| |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| // Adds text. |
| node.attachChild(createLabel(assetManager, getAzimuthLabelText(360 - angle), x, y, z1 * 3f, |
| Math.toRadians(angle), 0, EarthSurfaceEnvironmentJMEConstants.MIDDLE_TICKS_FONT_SIZE)); |
| } |
| angle += EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAJOR_TICK_ANGLE_INTERVAL_IN_DEG; |
| } |
| |
| // Create the minor ticks. |
| angle = 0; |
| while (angle < 360) { |
| if ((Math.IEEEremainder(angle, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAJOR_TICK_ANGLE_INTERVAL_IN_DEG) != 0) |
| && (Math.IEEEremainder(angle, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MID_TICK_ANGLE_INTERVAL_IN_DEG) != 0)) { |
| float x = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(angle))); |
| float y = (float) (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(angle))); |
| float z0 = 0; |
| float z1 = (EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MINOR_TICKS_HEIGHT); |
| |
| Vector3f p0 = new Vector3f(x, y, z0); |
| Vector3f p1 = new Vector3f(x, y, z1); |
| |
| // Adds line |
| verticesList.add(p0); |
| verticesList.add(p1); |
| |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| // Adds text. |
| node.attachChild(createLabel(assetManager, getAzimuthLabelText(360 - angle), x, y, z1 * 3f, |
| Math.toRadians(angle), 0, EarthSurfaceEnvironmentJMEConstants.MINOR_TICKS_FONT_SIZE)); |
| } |
| angle += EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MINOR_TICK_ANGLE_INTERVAL_IN_DEG; |
| } |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| Geometry azimuthGeometry = new Geometry("Azimuth lines", mesh); |
| |
| Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| mat.setColor("Color", EarthSurfaceEnvironmentJMEConstants.DEFAULT_AZIMUTH_LINES_COLOR.clone()); |
| azimuthGeometry.setMaterial(mat); |
| azimuthGeometry.setShadowMode(ShadowMode.Off); |
| |
| node.attachChild(azimuthGeometry); |
| |
| return node; |
| } |
| |
| /** |
| * Creates the elevation circles display. |
| * |
| * @return The transformGroup containing the geometries. |
| */ |
| public static Node createElevationCirclesDisplay(final AssetManager assetManager) { |
| Node node = new Node("Elevation Circles Display."); |
| |
| // Adds the cross at the zenith |
| Node zenithCross = createZenithCross(assetManager, 1.0f); |
| node.attachChild(zenithCross); |
| |
| // Creates the elevation lines. |
| double elevationAngleInDeg = EarthSurfaceEnvironmentJMEConstants.ELEVATION_LINES_ANGLE_INTERVAL_IN_DEG; |
| while (elevationAngleInDeg < 90) { |
| Node elevationCircle = createElevationCircle(assetManager, elevationAngleInDeg, |
| EarthSurfaceEnvironmentJMEConstants.ELEVATION_AZIMUTH_INTERVAL_IN_DEG, |
| EarthSurfaceEnvironmentJMEConstants.ELEVATION_LABEL_AZIMUTH_INTERVAL_IN_DEG, 0.5f, |
| LineAttributes.PATTERN_DASH); |
| elevationAngleInDeg += EarthSurfaceEnvironmentJMEConstants.ELEVATION_LINES_ANGLE_INTERVAL_IN_DEG; |
| node.attachChild(elevationCircle); |
| } |
| return node; |
| } |
| |
| /** |
| * Generate a line approximating a circle for a specified elevation angle. |
| * |
| * @param elevationAngleInDeg The elevation angle (above horizon), in |
| * degrees. |
| * @param azimuthAngleIncrementInDeg The azimuth increment to be used to sweep a |
| * circle, in degrees. |
| * @param labelAzimuthIntervalInDeg The azimuth interval of the elevation |
| * labels. |
| * @param lineWidth The line width. |
| * @param linePattern The line pattern to use. See |
| * LineAttributes. |
| * @return The line. |
| */ |
| public static Node createElevationCircle(final AssetManager assetManager, final double elevationAngleInDeg, |
| final double azimuthAngleIncrementInDeg, final double labelAzimuthIntervalInDeg, final float lineWidth, |
| int linePattern) { |
| Node node = new Node("Elevation Circle"); |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| // Increment the azimuth angle an generate points along the swept circle. |
| |
| double azimuthAngleInDeg = 0.0; |
| while (azimuthAngleInDeg < 360) { |
| float d = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(elevationAngleInDeg))); |
| |
| float x0 = (float) (d * Math.cos(Math.toRadians(azimuthAngleInDeg))); |
| float x1 = (float) (d * Math.cos(Math.toRadians( |
| azimuthAngleInDeg + EarthSurfaceEnvironmentJMEConstants.ELEVATION_AZIMUTH_INTERVAL_IN_DEG))); |
| float y0 = (float) (d * Math.sin(Math.toRadians(azimuthAngleInDeg))); |
| float y1 = (float) (d * Math.sin(Math.toRadians( |
| azimuthAngleInDeg + EarthSurfaceEnvironmentJMEConstants.ELEVATION_AZIMUTH_INTERVAL_IN_DEG))); |
| float z = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(elevationAngleInDeg))); |
| |
| // Generate a line between the two points. |
| Vector3f p0 = new Vector3f(x0, y0, z); |
| Vector3f p1 = new Vector3f(x1, y1, z); |
| |
| verticesList.add(p0); |
| verticesList.add(p1); |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| azimuthAngleInDeg += azimuthAngleIncrementInDeg; |
| } |
| |
| // Adds the elevation labels offseted in azimuth relative to azimuth circles. |
| float labelAzimuthAngleInDeg = EarthSurfaceEnvironmentJMEConstants.AZIMUTH_LINES_ANGLE_INTERVAL_IN_DEG / 2.0f; |
| String labelText = getElevationLabelText(elevationAngleInDeg); |
| while (labelAzimuthAngleInDeg < 360) { |
| float d = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(elevationAngleInDeg))); |
| float x = (float) (d * Math.cos(Math.toRadians(labelAzimuthAngleInDeg))); |
| float y = (float) (d * Math.sin(Math.toRadians(labelAzimuthAngleInDeg))); |
| float z = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(elevationAngleInDeg))); |
| |
| // Add Text |
| node.attachChild(createLabel(assetManager, labelText, x, y, z, Math.toRadians(labelAzimuthAngleInDeg), |
| Math.toRadians(elevationAngleInDeg), EarthSurfaceEnvironmentJMEConstants.MINOR_TICKS_FONT_SIZE)); |
| |
| labelAzimuthAngleInDeg += labelAzimuthIntervalInDeg; |
| } |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| Geometry azimuthGeometry = new Geometry("Elevation Circle", mesh); |
| |
| Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| mat.setColor("Color", EarthSurfaceEnvironmentJMEConstants.DEFAULT_AZIMUTH_LINES_COLOR.clone()); |
| azimuthGeometry.setMaterial(mat); |
| |
| node.attachChild(azimuthGeometry); |
| |
| return node; |
| |
| } |
| |
| public static Node createAzimuthCirclesDisplay(final AssetManager assetManager) { |
| Node node = new Node("Azimuth Display Circles"); |
| |
| // Adds the cross at the zenith |
| Node zenithCross = createZenithCross(assetManager, 1.0f); |
| node.attachChild(zenithCross); |
| |
| // Creates the elevation lines. |
| double azimuthAngleInDeg = EarthSurfaceEnvironmentJMEConstants.AZIMUTH_LINES_ANGLE_INTERVAL_IN_DEG; |
| while (azimuthAngleInDeg <= 360) { |
| Node azimuthCircle = createAzimuthCircle(assetManager, azimuthAngleInDeg, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_ELEVATION_INTERVAL_IN_DEG, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MIN_ELEVATION_ANGLE_IN_DEG, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_MAX_ELEVATION_ANGLE_IN_DEG, |
| EarthSurfaceEnvironmentJMEConstants.AZIMUTH_LABEL_AZIMUTH_INTERVAL_IN_DEG, 0.5f, |
| LineAttributes.PATTERN_DASH); |
| azimuthAngleInDeg += EarthSurfaceEnvironmentJMEConstants.AZIMUTH_LINES_ANGLE_INTERVAL_IN_DEG; |
| node.attachChild(azimuthCircle); |
| } |
| |
| return node; |
| } |
| |
| public static Node createAzimuthCircle(final AssetManager assetManager, final double azimuthAngleInDeg, |
| final double elevationAngleIncrementInDeg, final double minimumElevationAngleInDeg, |
| final double maximumElevationAngleInDeg, final double labelElevationIntervalInDeg, final float lineWidth, |
| int linePattern) { |
| Node node = new Node("Azimuth Circle"); |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| // Increment the elevation angle an generate points along the swept circle. |
| double elevationAngleInDeg = minimumElevationAngleInDeg; |
| while (elevationAngleInDeg < maximumElevationAngleInDeg) { |
| double elevationAngleInDegHigh = elevationAngleInDeg + elevationAngleIncrementInDeg; |
| if (elevationAngleInDegHigh > maximumElevationAngleInDeg) { |
| elevationAngleInDegHigh = maximumElevationAngleInDeg; |
| } |
| |
| float d0 = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(elevationAngleInDeg))); |
| float d1 = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.cos(Math.toRadians(elevationAngleInDegHigh))); |
| |
| float x0 = (float) (d0 * Math.cos(Math.toRadians(azimuthAngleInDeg))); |
| float x1 = (float) (d1 * Math.cos(Math.toRadians(azimuthAngleInDeg))); |
| float y0 = (float) (d0 * Math.sin(Math.toRadians(azimuthAngleInDeg))); |
| float y1 = (float) (d1 * Math.sin(Math.toRadians(azimuthAngleInDeg))); |
| float z0 = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(elevationAngleInDeg))); |
| float z1 = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS |
| * Math.sin(Math.toRadians(elevationAngleInDegHigh))); |
| |
| // Generate a line between the two points. |
| Vector3f p0 = new Vector3f(x0, y0, z0); |
| Vector3f p1 = new Vector3f(x1, y1, z1); |
| |
| verticesList.add(p0); |
| verticesList.add(p1); |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| elevationAngleInDeg += elevationAngleIncrementInDeg; |
| } |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| Geometry azimuthGeometry = new Geometry("Azimuth lines", mesh); |
| |
| Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| mat.setColor("Color", EarthSurfaceEnvironmentJMEConstants.DEFAULT_AZIMUTH_LINES_COLOR.clone()); |
| azimuthGeometry.setMaterial(mat); |
| azimuthGeometry.setShadowMode(ShadowMode.Off); |
| |
| node.attachChild(azimuthGeometry); |
| |
| return node; |
| } |
| |
| /** |
| * Creates a Sky Box using six images found in a specified location. |
| * |
| * @param assetManager The AssetManager to use. |
| * @param texturesLocation The location of the textures. The location should |
| * containg the following files : west.png, east.png, |
| * north.png, south.png, up.png, down.png. |
| * @return The Spatial that represents the Sky. |
| * @throws Exception If the spatial cannot be initialized. |
| */ |
| public static Spatial createSky(AssetManager assetManager, String texturesLocation) throws Exception { |
| Logger.info("Creating Sky Box using images found in location <" + texturesLocation + ">."); |
| |
| // TODO : Verifies this will work on Windows. |
| String urlString = "platform:plugin/" + ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(EnvironmentUIJME3Utilities.class) + "/assets"; |
| URL url = org.eclipse.core.runtime.FileLocator.toFileURL(new URL(urlString)); |
| assetManager.registerLocator(url.getPath(), FileLocator.class); |
| |
| // Ensures the texturesLocation ends with a forward slash. |
| String assetLocation = texturesLocation; |
| if (!assetLocation.endsWith("/")) { |
| assetLocation += "/"; |
| } |
| |
| // Loads the texture for each sides of the box. |
| Texture west = assetManager.loadTexture(assetLocation + "west.png"); |
| Texture east = assetManager.loadTexture(assetLocation + "east.png"); |
| Texture north = assetManager.loadTexture(assetLocation + "north.png"); |
| Texture south = assetManager.loadTexture(assetLocation + "south.png"); |
| Texture up = assetManager.loadTexture(assetLocation + "up.png"); |
| Texture down = assetManager.loadTexture(assetLocation + "down.png"); |
| |
| // Creates the sky box. |
| return createSky(assetManager, west, east, north, south, up, down); |
| } |
| |
| /** |
| * Creates a Sky Box using six images used for each side of the box. |
| * |
| * @param assetManager The AssetManager to use. |
| * @param west Texture used for the west side of the box. |
| * @param east Texture used for the east side of the box. |
| * @param north Texture used for the north side of the box. |
| * @param south Texture used for the south side of the box. |
| * @param up Texture used for the up side of the box. |
| * @param down Texture used for the down side of the box. |
| * @return The Spatial that represents the Sky. |
| * @throws If the spatial cannot be initialized. |
| */ |
| public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, |
| Texture up, Texture down) throws Exception { |
| Image westImg = west.getImage(); |
| Image eastImg = east.getImage(); |
| Image northImg = north.getImage(); |
| Image southImg = south.getImage(); |
| Image upImg = up.getImage(); |
| Image downImg = down.getImage(); |
| |
| Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null); |
| |
| cubeImage.addData(westImg.getData(0)); |
| cubeImage.addData(eastImg.getData(0)); |
| cubeImage.addData(downImg.getData(0)); |
| cubeImage.addData(upImg.getData(0)); |
| cubeImage.addData(southImg.getData(0)); |
| cubeImage.addData(northImg.getData(0)); |
| |
| TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); |
| |
| try { |
| // TODO : Verifies this will work on Windows. |
| // FIXME: Implement in ApogyCommonOSGiUtilities. |
| String urlString = "platform:plugin/" + ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(EnvironmentUIJME3Utilities.class) + "/assets"; |
| URL url = org.eclipse.core.runtime.FileLocator.toFileURL(new URL(urlString)); |
| assetManager.registerLocator(url.getPath(), FileLocator.class); |
| } catch (Exception e) { |
| Logger.error(e.getMessage(), e); |
| } |
| |
| final Sphere sphereMesh = new Sphere(10, 10, 10, false, true); |
| |
| Geometry sky = new Geometry("Sky", sphereMesh); |
| sky.setQueueBucket(Bucket.Sky); |
| sky.setCullHint(Spatial.CullHint.Never); |
| sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO)); |
| |
| Material skyMat = new Material(assetManager, "MatDefs/EarthSky.j3md"); |
| skyMat.setVector3("NormalScale", Vector3f.UNIT_XYZ); |
| skyMat.setVector3("SunPosition", new Vector3f()); |
| skyMat.setFloat("Alpha", 1.0f); |
| skyMat.setTransparent(true); |
| |
| // FIXME Implement in ApogyCommonOSGi platformURL. |
| String urlString = "platform:plugin/" + ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(EnvironmentUIJME3Utilities.class) + "/assets/Textures/sunglow.png"; |
| URL url = EclipseUtils.resolveURL(ApogyCommonOSGiUtilities.INSTANCE.getBundle(EnvironmentUIJME3Utilities.class), urlString); |
| skyMat.setTexture("SunGlowTexture", EnvironmentUIJME3Utilities.createTexture2D(url, 1.0f)); |
| |
| cubeMap.setMagFilter(Texture.MagFilter.Bilinear); |
| cubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
| cubeMap.setAnisotropicFilter(0); |
| cubeMap.setWrap(Texture.WrapMode.EdgeClamp); |
| skyMat.setTexture("Texture", cubeMap); |
| skyMat.setTransparent(true); |
| |
| sky.setMaterial(skyMat); |
| |
| // Rotate sky to align with worksite. |
| Matrix3f skyRot = new Matrix3f(); |
| skyRot.fromAngleAxis((float) Math.toRadians(-90), new Vector3f(1, 0, 0)); |
| sky.setLocalRotation(skyRot); |
| |
| return sky; |
| } |
| |
| /** |
| * Creates a Texture2D by loading an image from a specified URL. |
| * |
| * @param url The URL to the image. |
| * @param alpha The alpha to apply to the image (0.0 = fully transparent, 1.0 |
| * fully opaque). |
| * @return The Texture2D. |
| * @throws An exception if the image could not be loaded. |
| */ |
| public static Texture2D createTexture2D(URL url, float alpha) throws Exception { |
| Texture2D texture2D = null; |
| |
| URLEImage tmp = ApogyCommonImagesFactory.eINSTANCE.createURLEImage(); |
| tmp.setUrl(url.toString()); |
| |
| BufferedImage image = tmp.asBufferedImage(); |
| AWTLoader awtLoader = new AWTLoader(); |
| |
| if (alpha != 1.0) { |
| EImage originalImage = ApogyCommonImagesFactory.eINSTANCE.createEImage(); |
| originalImage.setImageContent(image); |
| AbstractEImage alphaImage = EImagesUtilities.INSTANCE.applyAlpha(originalImage, alpha); |
| |
| texture2D = new Texture2D(awtLoader.load(alphaImage.asBufferedImage(), false)); |
| } else { |
| texture2D = new Texture2D(awtLoader.load(image, false)); |
| } |
| |
| return texture2D; |
| } |
| |
| /** |
| * Creates a Texture2D by loading an image from a specified URL. |
| * |
| * @param url The URL to the image. |
| * @param alpha The alpha to apply to the image (0.0 = fully transparent, 1.0 |
| * fully opaque). |
| * @return The Texture2D. |
| * @throws An exception if the image could not be loaded. |
| */ |
| public static TextureCubeMap createTextureCubeMap(URL url, float alpha) throws Exception { |
| TextureCubeMap texture2D = null; |
| |
| URLEImage tmp = ApogyCommonImagesFactory.eINSTANCE.createURLEImage(); |
| tmp.setUrl(url.toString()); |
| |
| BufferedImage image = tmp.asBufferedImage(); |
| AWTLoader awtLoader = new AWTLoader(); |
| |
| if (alpha != 1.0) { |
| EImage originalImage = ApogyCommonImagesFactory.eINSTANCE.createEImage(); |
| originalImage.setImageContent(image); |
| AbstractEImage alphaImage = EImagesUtilities.INSTANCE.applyAlpha(originalImage, alpha); |
| |
| texture2D = new TextureCubeMap(awtLoader.load(alphaImage.asBufferedImage(), false)); |
| } else { |
| texture2D = new TextureCubeMap(awtLoader.load(image, false)); |
| } |
| |
| return texture2D; |
| } |
| |
| /** |
| * Creates a cross at the zenith. |
| * |
| * @param crossAngularSpanInDeg The angular span of the cross members, in |
| * degrees. |
| * @return The line array representing the cross. |
| */ |
| private static Node createZenithCross(final AssetManager assetManager, float crossAngularSpanInDeg) { |
| Node node = new Node("Zenith Cross."); |
| |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| float elevation = (float) (Math.toRadians(90 - (crossAngularSpanInDeg / 2.0f))); |
| float delta = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS * Math.cos(elevation)); |
| float z = (float) (EarthSurfaceEnvironmentJMEConstants.ELEVATION_DISPLAY_RADIUS * Math.sin(elevation)); |
| |
| Vector3f p0 = new Vector3f(delta, 0, z); |
| Vector3f p1 = new Vector3f(-delta, 0, z); |
| verticesList.add(p0); |
| verticesList.add(p1); |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| Vector3f p2 = new Vector3f(0, delta, z); |
| Vector3f p3 = new Vector3f(0, -delta, z); |
| |
| verticesList.add(p2); |
| verticesList.add(p3); |
| indexesList.add(verticesList.indexOf(p2)); |
| indexesList.add(verticesList.indexOf(p3)); |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| Geometry zenithCrossGeometry = new Geometry("Zenith Cross", mesh); |
| |
| Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| mat.setColor("Color", EarthSurfaceEnvironmentJMEConstants.DEFAULT_AZIMUTH_LINES_COLOR.clone()); |
| zenithCrossGeometry.setMaterial(mat); |
| zenithCrossGeometry.setShadowMode(ShadowMode.Off); |
| |
| node.attachChild(zenithCrossGeometry); |
| |
| return node; |
| } |
| |
| private static String getAzimuthLabelText(double angle) { |
| String label = getLabelMap().get(new Double(angle)); |
| if (label == null) { |
| label = new String(decimalFormat.format(angle) + EarthSurfaceEnvironmentJMEConstants.DEGREE_STRING); |
| } |
| return label; |
| } |
| |
| private static String getElevationLabelText(double angle) { |
| return new String(decimalFormat.format(angle) + EarthSurfaceEnvironmentJMEConstants.DEGREE_STRING); |
| } |
| |
| private static Map<Double, String> getLabelMap() { |
| if (labelMap == null) { |
| labelMap = new HashMap<Double, String>(); |
| labelMap.put(new Double(0), "N"); |
| labelMap.put(new Double(360), "N"); |
| labelMap.put(new Double(45), "NE"); |
| labelMap.put(new Double(90), "E"); |
| labelMap.put(new Double(135), "SE"); |
| labelMap.put(new Double(180), "S"); |
| labelMap.put(new Double(225), "SW"); |
| labelMap.put(new Double(270), "W"); |
| labelMap.put(new Double(315), "NW"); |
| } |
| |
| return labelMap; |
| } |
| |
| private static Node createLabel(final AssetManager assetManager, String text, double x, double y, double z, |
| double azimuthAngle, double elevationAngle, int fontSize) { |
| Node root = new Node(); |
| |
| float textWidth = text.length() * fontSize * 0.45f; |
| |
| // Adds the text. |
| BitmapFont bitmapFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); |
| |
| BitmapText bitmapText = bitmapFont.createLabel(text); |
| bitmapText.setSize(fontSize); |
| bitmapText.setText(text); // crosshairs |
| bitmapText.setColor(EarthSurfaceEnvironmentJMEConstants.DEFAULT_AZIMUTH_LINES_COLOR.clone()); |
| bitmapText.setBox(new Rectangle(-textWidth / 2.0f, 0, textWidth, 100)); |
| bitmapText.setQueueBucket(RenderQueue.Bucket.Transparent); |
| |
| // TODO : Make text background transparent. |
| // bitmapText.setMaterial(arg0); |
| |
| // Translate the text to the required position |
| Transform3D translation = new Transform3D(); |
| translation.setTranslation(new Vector3d(x, y, z)); |
| |
| // Rotates the text to make it vertical. |
| Transform3D t1 = new Transform3D(); |
| t1.rotX(Math.toRadians(90)); |
| |
| // Rotate the text in azimuth. |
| Transform3D t2 = new Transform3D(); |
| t2.rotY(azimuthAngle - Math.toRadians(90)); |
| |
| // Rotate the text in elevation. |
| Transform3D t3 = new Transform3D(); |
| t3.rotX(elevationAngle); |
| |
| // Stacks the transforms |
| Transform3D t = new Transform3D(); |
| t.mul(translation, t1); |
| t.mul(t2); |
| t.mul(t3); |
| |
| Matrix4d transformMatrix = new Matrix4d(); |
| t.get(transformMatrix); |
| root.setLocalTransform(JME3Utilities.createTransform(transformMatrix)); |
| |
| root.attachChild(bitmapText); |
| |
| return root; |
| } |
| } |