blob: 782883e1e88db26623aaf8fe0d91de95744a2026 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}