| /******************************************************************************* |
| * 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: |
| * Pierre Allard, |
| * Regent L'Archeveque - initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-1.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.apogy.core.environment.surface.ui.jme3.scene_objects; |
| |
| import java.awt.Color; |
| import java.awt.Font; |
| import java.awt.image.BufferedImage; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.apogy.common.images.AbstractEImage; |
| import org.eclipse.apogy.common.images.EImagesUtilities; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3Utilities; |
| import org.eclipse.apogy.common.topology.ui.jme3.scene_objects.AbstractTransformationJME3SceneObject; |
| import org.eclipse.apogy.core.ApogyCorePackage; |
| import org.eclipse.apogy.core.FeatureOfInterest; |
| import org.eclipse.apogy.core.FeatureOfInterestNode; |
| import org.eclipse.apogy.core.environment.surface.ui.scene_objects.FeatureOfInterestSceneObject; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.swt.graphics.RGB; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.jme3.asset.AssetManager; |
| import com.jme3.material.Material; |
| import com.jme3.math.ColorRGBA; |
| import com.jme3.math.Vector2f; |
| import com.jme3.math.Vector3f; |
| 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.control.BillboardControl; |
| import com.jme3.util.BufferUtils; |
| |
| public class FeatureOfInterestNodeJME3Object extends AbstractTransformationJME3SceneObject<FeatureOfInterestNode> |
| implements FeatureOfInterestSceneObject { |
| private static final Logger Logger = LoggerFactory.getLogger(FeatureOfInterestNodeJME3Object.class); |
| |
| public static final float DEFAULT_FLAG_POLE_HEIGHT = 1.0f; |
| public static final int DEFAULT_FONT_SIZE = 16; |
| |
| public static final float FLAG_SIZE_PER_PIXEL = 0.005f; |
| public static final ColorRGBA FLAG_POLE_COLOR = ColorRGBA.Cyan; |
| public static final ColorRGBA FLAG_BACKGROUND_COLOR = ColorRGBA.Black; |
| public static final ColorRGBA FLAG_TEXT_COLOR = ColorRGBA.Cyan; |
| |
| private float flagPoleHeight = DEFAULT_FLAG_POLE_HEIGHT; |
| private int fontSize = DEFAULT_FONT_SIZE; |
| |
| private Adapter featureOfInterestNodeAdapter = null; |
| private FeatureOfInterestNode featureOfInterestNode = null; |
| |
| private AssetManager assetManager; |
| |
| private Node pole = null; |
| private Node flag = null; |
| private Geometry poleGeometry = null; |
| private Geometry flagGeometry = null; |
| |
| private BillboardControl billboardControl; |
| |
| private ColorRGBA flagPoleColor = FLAG_POLE_COLOR; |
| private ColorRGBA flagTextColor = FLAG_TEXT_COLOR; |
| private final ColorRGBA flagBackgroundColor = FLAG_BACKGROUND_COLOR; |
| |
| public FeatureOfInterestNodeJME3Object(FeatureOfInterestNode node, |
| JME3RenderEngineDelegate jme3RenderEngineDelegate) { |
| super(node, jme3RenderEngineDelegate); |
| |
| this.assetManager = this.jme3Application.getAssetManager(); |
| |
| this.featureOfInterestNode = node; |
| |
| // Listens to the FeatureOfInterestNode |
| this.featureOfInterestNode.eAdapters().add(getFeatureOfInterestNodeAdapter()); |
| if (this.featureOfInterestNode.getFeatureOfInterest() != null) { |
| this.featureOfInterestNode.getFeatureOfInterest().eAdapters().add(getFeatureOfInterestNodeAdapter()); |
| } |
| |
| Job job = new Job("FeatureOfInterestNodeJME3Object : Updating Geometry") { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| requestUpdate(); |
| setAxisVisible(false); |
| return Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| } |
| |
| @Override |
| public List<Geometry> getGeometries() { |
| List<Geometry> geometries = new ArrayList<Geometry>(); |
| |
| if (this.poleGeometry != null) |
| geometries.add(this.poleGeometry); |
| if (this.flagGeometry != null) |
| geometries.add(this.flagGeometry); |
| |
| geometries.addAll(super.getGeometries()); |
| return geometries; |
| } |
| |
| @Override |
| public void updateGeometry(float tpf) { |
| super.updateGeometry(tpf); |
| |
| if (this.pole != null) |
| getAttachmentNode().detachChild(this.pole); |
| if (this.flag != null) |
| getAttachmentNode().detachChild(this.flag); |
| |
| this.poleGeometry = null; |
| this.flagGeometry = null; |
| |
| getBillboardControl().setSpatial(null); |
| |
| // Creates the pole. |
| this.pole = createPole(this.flagPoleHeight); |
| getAttachmentNode().attachChild(this.pole); |
| |
| // Creates the flag |
| this.flag = createFlag(getFlagText(), this.fontSize); |
| this.pole.attachChild(this.flag); |
| } |
| |
| @Override |
| public void setColor(RGB rgb) { |
| if (rgb != null) { |
| Logger.info("setColor(" + rgb + ")"); |
| this.flagPoleColor = JME3Utilities.convertToColorRGBA(rgb); |
| this.flagTextColor = JME3Utilities.convertToColorRGBA(rgb); |
| |
| requestUpdate(); |
| } else { |
| Logger.error("Cannot set color to null."); |
| } |
| } |
| |
| @Override |
| public void setFlagPoleHeight(float poleHeight) { |
| if (poleHeight > 0) { |
| Logger.info("Setting flag pole height <" + poleHeight + ">."); |
| this.flagPoleHeight = poleHeight; |
| requestUpdate(); |
| } else { |
| Logger.error("Cannot set flag pole height."); |
| } |
| } |
| |
| @Override |
| public void setFlagVisible(boolean visible) { |
| setVisible(visible); |
| } |
| |
| @Override |
| public void setFontSize(int fontSize) { |
| if (fontSize > 0) { |
| Logger.info("Setting the font size to <" + fontSize + ">."); |
| this.fontSize = fontSize; |
| requestUpdate(); |
| } else { |
| Logger.info("Cannot set the font size to <" + fontSize + ">."); |
| } |
| } |
| |
| private Node createPole(float poleHeight) { |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| Vector3f p0 = new Vector3f(0, 0, 0); |
| Vector3f p1 = new Vector3f(0, 0, poleHeight); |
| verticesList.add(p0); |
| verticesList.add(p1); |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| 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(); |
| |
| this.poleGeometry = new Geometry("Pole", mesh); |
| |
| Material mat = new Material(this.assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| mat.setColor("Color", this.flagPoleColor.clone()); |
| this.poleGeometry.setMaterial(mat); |
| |
| Node node = new Node("Pole."); |
| node.setShadowMode(ShadowMode.Off); |
| node.attachChild(this.poleGeometry); |
| |
| return node; |
| } |
| |
| private String getFlagText() { |
| String text = "?"; |
| |
| if (getTopologyNode() != null) { |
| FeatureOfInterest foi = getTopologyNode().getFeatureOfInterest(); |
| |
| if (foi != null) { |
| if (foi.getName() != null && foi.getName().length() > 0) { |
| text = foi.getName(); |
| } |
| } |
| } |
| |
| return text; |
| } |
| |
| private Node createFlag(String text, int fontSize) { |
| Node node = new Node("Flag"); |
| node.setShadowMode(ShadowMode.Off); |
| |
| Node flagAttachmentPoint = new Node("Flag Attachment Point"); |
| flagAttachmentPoint.setLocalTranslation(0, 0, this.flagPoleHeight); |
| node.attachChild(flagAttachmentPoint); |
| |
| // First, create the image that will hold the text. |
| Font font = new Font("Serif", Font.BOLD, fontSize); |
| BufferedImage bufferedImage = createTextImage(text, font, 2); |
| |
| // Based on the image size, create the flag geometry. |
| float flagWidth = FLAG_SIZE_PER_PIXEL * bufferedImage.getWidth(); |
| float flagHeight = FLAG_SIZE_PER_PIXEL * bufferedImage.getHeight(); |
| Mesh flagMesh = createFlagMesh(flagWidth, flagHeight); |
| |
| this.flagGeometry = new Geometry("Flag Geometry", flagMesh); |
| Material mat = JME3Utilities.createMaterial(bufferedImage, this.assetManager); |
| this.flagGeometry.setMaterial(mat); |
| |
| flagAttachmentPoint.addControl(getBillboardControl()); |
| flagAttachmentPoint.attachChild(this.flagGeometry); |
| |
| return node; |
| } |
| |
| private Mesh createFlagMesh(float flagWidth, float flagHeight) { |
| Vector3f[] vertices = new Vector3f[4]; |
| vertices[0] = new Vector3f(0, 0, 0); |
| vertices[1] = new Vector3f(flagWidth, 0, 0); |
| vertices[2] = new Vector3f(0, flagHeight, 0); |
| vertices[3] = new Vector3f(flagWidth, flagHeight, 0); |
| |
| int[] indexes = { 2, 0, 1, 1, 3, 2 }; |
| |
| Vector2f[] texCoord = new Vector2f[4]; |
| texCoord[0] = new Vector2f(0, 0); |
| texCoord[1] = new Vector2f(1, 0); |
| texCoord[2] = new Vector2f(0, 1); |
| texCoord[3] = new Vector2f(1, 1); |
| |
| Mesh mesh = new Mesh(); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord)); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(indexes)); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| return mesh; |
| } |
| |
| private BillboardControl getBillboardControl() { |
| if (this.billboardControl == null) { |
| this.billboardControl = new BillboardControl(); |
| this.billboardControl.setAlignment(BillboardControl.Alignment.Screen); |
| } |
| |
| return this.billboardControl; |
| } |
| |
| private Adapter getFeatureOfInterestNodeAdapter() { |
| if (this.featureOfInterestNodeAdapter == null) { |
| this.featureOfInterestNodeAdapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification msg) { |
| if (msg.getNotifier() instanceof FeatureOfInterestNode) { |
| int featureId = msg.getFeatureID(FeatureOfInterestNode.class); |
| if (featureId == ApogyCorePackage.FEATURE_OF_INTEREST_NODE__POSITION |
| || featureId == ApogyCorePackage.FEATURE_OF_INTEREST_NODE__ROTATION_MATRIX) { |
| requestUpdate(); |
| } else if (featureId == ApogyCorePackage.FEATURE_OF_INTEREST_NODE__FEATURE_OF_INTEREST) { |
| if (msg.getOldValue() instanceof FeatureOfInterest) { |
| FeatureOfInterest oldFeatureOfInterest = (FeatureOfInterest) msg.getOldValue(); |
| oldFeatureOfInterest.eAdapters().remove(getFeatureOfInterestNodeAdapter()); |
| } |
| |
| if (msg.getNewValue() instanceof FeatureOfInterest) { |
| FeatureOfInterest newFeatureOfInterest = (FeatureOfInterest) msg.getNewValue(); |
| newFeatureOfInterest.eAdapters().add(getFeatureOfInterestNodeAdapter()); |
| requestUpdate(); |
| } |
| } |
| } else if (msg.getNotifier() instanceof FeatureOfInterest) { |
| int featureId = msg.getFeatureID(FeatureOfInterest.class); |
| if (featureId == ApogyCorePackage.FEATURE_OF_INTEREST__NAME) { |
| requestUpdate(); |
| } |
| } |
| } |
| }; |
| } |
| |
| return this.featureOfInterestNodeAdapter; |
| } |
| |
| private BufferedImage createTextImage(String text, Font font, int borderWidth) { |
| Color textColor = JME3Utilities.convertToAWTColor(this.flagTextColor); |
| Color backgroundColor = JME3Utilities.convertToAWTColor(this.flagBackgroundColor); |
| AbstractEImage original = EImagesUtilities.INSTANCE.createTextImage(text, font, textColor, backgroundColor, |
| borderWidth); |
| |
| int[] borderColor = JME3Utilities.convertToColorIntRGBA(this.flagPoleColor); |
| AbstractEImage imageWithBorder = EImagesUtilities.INSTANCE.addBorder(original, borderWidth, borderColor[0], |
| borderColor[1], borderColor[2]); |
| |
| return imageWithBorder.asBufferedImage(); |
| } |
| } |