blob: 94e924714b53466bcf971711879723488775df75 [file] [log] [blame]
package org.eclipse.apogy.addons.sensors.fov.ui.jme3.scene_objects;
/*******************************************************************************
* 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 - initial API and implementation
* Regent L'Archeveque
* SPDX-License-Identifier: EPL-1.0
*******************************************************************************/
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.eclipse.apogy.addons.sensors.fov.AngularSpan;
import org.eclipse.apogy.addons.sensors.fov.ApogyAddonsSensorsFOVPackage;
import org.eclipse.apogy.addons.sensors.fov.CircularSectorFieldOfView;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.AbstractFieldOfViewImageProjectorControl;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.CircularSectorFieldOfViewImageProjectorControl;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.JME3FovUtilities;
import org.eclipse.apogy.addons.sensors.fov.ui.scene_objects.CircularSectorFieldOfViewSceneObject;
import org.eclipse.apogy.common.topology.ui.MeshPresentationMode;
import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate;
import org.eclipse.apogy.common.topology.ui.jme3.JME3Utilities;
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.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
public class CircularSectorFieldOfViewJME3Object extends AbstractFieldOfViewJME3Object<CircularSectorFieldOfView>
implements CircularSectorFieldOfViewSceneObject {
private static final Logger Logger = LoggerFactory.getLogger(CircularSectorFieldOfViewJME3Object.class);
public static ColorRGBA DEFAULT_CIRCULAR_SECTOR_FOV_COLOR = new ColorRGBA(0f, 1f, 0.0f, 1.0f);
public static float DEFAULT_ANGLE_INCREMENT = (float) Math.toRadians(10.0);
private float previousAxisLength = 1.0f;
private boolean axisVisible = true;
private float angleIncrement = DEFAULT_ANGLE_INCREMENT;
private Adapter angularSpanAdapter;
private Adapter rangeAdapter;
private AssetManager assetManager;
private Geometry fovGeometry = null;
private Geometry axisGeometry = null;
// Projective textures
private CircularSectorFieldOfViewImageProjectorControl circularSectorFieldOfViewImageProjectorControl;
public CircularSectorFieldOfViewJME3Object(CircularSectorFieldOfView node,
JME3RenderEngineDelegate jme3RenderEngineDelegate) {
super(node, jme3RenderEngineDelegate);
if (node == null || jme3RenderEngineDelegate == null) {
throw new IllegalArgumentException();
}
this.assetManager = this.jme3Application.getAssetManager();
// Creates the 3DAxis.
this.axisGeometry = JME3Utilities.createAxis3D(1.0f, this.assetManager);
Job job = new Job("CircularSectorFieldOfViewJME3Object : Updating Geometry.") {
@Override
protected IStatus run(IProgressMonitor monitor) {
// Updates the geometry.
// updateGeometry();
requestUpdate();
// Listens for change on the CircularSectorFieldOfView.
node.getAngularSpan().eAdapters().add(getAngularSpanAdapter());
node.getRange().eAdapters().add(getRangeAdapter());
return Status.OK_STATUS;
}
};
job.schedule();
}
@Override
public void updateGeometry(float tpf) {
// Removes previous geometry if applicable.
if (this.fovGeometry != null) {
getFovNode().detachChild(this.fovGeometry);
}
// Creates new mesh.
double minAngle = getTopologyNode().getAngularSpan().getMinimumAngle();
double maxAngle = getTopologyNode().getAngularSpan().getMaximumAngle();
double minRadius = getTopologyNode().getRange().getMinimumDistance();
double maxRadius = getTopologyNode().getRange().getMaximumDistance();
int numberOfDivision = (int) Math
.round(getTopologyNode().getAngularSpan().getSpanningAngle() / getAngleIncrement());
Mesh mesh = JME3FovUtilities.createCircularSector(minAngle, maxAngle, minRadius, maxRadius, numberOfDivision,
true);
// Creates new geometry.
if (getTopologyNode().getNodeId() != null)
this.fovGeometry = new Geometry("", mesh);
else
this.fovGeometry = new Geometry("?", mesh);
this.fovGeometry.setMaterial(createMaterial());
// Attaches new geometry.
getFovNode().attachChild(this.fovGeometry);
// Updates Presentation mode.
internalSetPresentationMode(this.meshPresentationMode);
// Attache image projector control.
getAttachmentNode().addControl(getCircularSectorFieldOfViewImageProjectorControl());
}
@Override
public void dispose() {
if (getTopologyNode() != null) {
if (getTopologyNode().getAngularSpan() != null) {
getTopologyNode().getAngularSpan().eAdapters().remove(getAngularSpanAdapter());
}
if (getTopologyNode().getRange() != null) {
getTopologyNode().getRange().eAdapters().remove(getRangeAdapter());
}
}
super.dispose();
}
@Override
public List<Geometry> getGeometries() {
List<Geometry> geometries = new ArrayList<Geometry>();
geometries.add(this.fovGeometry);
if (this.axisGeometry != null)
geometries.add(this.axisGeometry);
return geometries;
}
@Override
public void setColor(RGB rgb) {
super.setColor(rgb);
try {
if (this.fovGeometry != null) {
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
Material mat = createMaterial();
mat.setColor("Diffuse", CircularSectorFieldOfViewJME3Object.this.fovColor);
mat.setColor("Ambient", CircularSectorFieldOfViewJME3Object.this.fovColor);
mat.setColor("Specular", CircularSectorFieldOfViewJME3Object.this.fovColor);
CircularSectorFieldOfViewJME3Object.this.fovGeometry.setMaterial(mat);
return null;
}
});
}
} catch (Throwable t) {
Logger.error("Failed to set color to <" + rgb + ">.", t);
}
}
@Override
public float getAngleIncrement() {
return this.angleIncrement;
}
@Override
public void setAngleIncrement(float angleIncrement) {
if (angleIncrement <= 0) {
throw new IllegalArgumentException("Angle increment must be greater than zero !");
} else {
this.angleIncrement = angleIncrement;
// updateGeometry();
requestUpdate();
}
}
@Override
public MeshPresentationMode getPresentationMode() {
return this.meshPresentationMode;
}
@Override
public void setPresentationMode(final MeshPresentationMode mode) {
Logger.info("Setting Presentation Mode to <" + mode + ">.");
if (this.fovGeometry != null && this.fovGeometry.getMaterial() != null) {
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
internalSetPresentationMode(mode);
return null;
}
});
}
}
@Override
public void setShowProjection(boolean showProjection) {
Logger.info("setShowProjection(" + showProjection + ")");
// Set the image projector enablement.
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
getCircularSectorFieldOfViewImageProjectorControl().setEnabled(showProjection);
return null;
}
});
this.showProjection = showProjection;
}
@Override
public float getTransparency() {
return this.transparency;
}
@Override
public void setShowOutlineOnly(boolean showOutlineOnly) {
}
@Override
public AbstractFieldOfViewImageProjectorControl<CircularSectorFieldOfView> getAbstractFieldOfViewImageProjectorControl() {
return getCircularSectorFieldOfViewImageProjectorControl();
}
@Override
public void setAxisVisible(final boolean visible) {
Logger.info("Setting axis visible to <" + visible + ">.");
this.axisVisible = visible;
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
if (CircularSectorFieldOfViewJME3Object.this.axisVisible) {
getAttachmentNode().attachChild(CircularSectorFieldOfViewJME3Object.this.axisGeometry);
} else {
getAttachmentNode().detachChild(CircularSectorFieldOfViewJME3Object.this.axisGeometry);
}
return null;
}
});
}
@Override
public void setAxisLength(double length) {
Logger.info("Setting axis length to <" + length + ">.");
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
float scale = (float) Math.abs(length)
/ CircularSectorFieldOfViewJME3Object.this.previousAxisLength;
// Scales existing axis.
if (CircularSectorFieldOfViewJME3Object.this.axisGeometry != null)
CircularSectorFieldOfViewJME3Object.this.axisGeometry.scale(scale);
CircularSectorFieldOfViewJME3Object.this.previousAxisLength = (float) length;
} catch (Throwable t) {
Logger.error("Failed to setAxisLength(" + length + ").", t);
}
return null;
}
});
}
private void internalSetPresentationMode(final MeshPresentationMode mode) {
if (this.fovGeometry != null) {
Mesh mesh = this.fovGeometry.getMesh();
switch (mode.getValue()) {
case MeshPresentationMode.SURFACE_VALUE:
this.fovGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
if (mesh != null)
mesh.setMode(com.jme3.scene.Mesh.Mode.Triangles);
break;
case MeshPresentationMode.WIREFRAME_VALUE:
this.fovGeometry.getMaterial().getAdditionalRenderState().setWireframe(true);
if (mesh != null)
mesh.setMode(com.jme3.scene.Mesh.Mode.Triangles);
break;
case MeshPresentationMode.POINTS_VALUE:
this.fovGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
if (mesh != null)
mesh.setMode(com.jme3.scene.Mesh.Mode.Points);
break;
default:
break;
}
}
this.meshPresentationMode = mode;
}
// private void updateGeometry()
// {
// jme3Application.enqueue(new Callable<Object>()
// {
// @Override
// public Object call() throws Exception
// {
// // Removes previous geometry if applicable.
// if(fovGeometry != null)
// {
// getFovNode().detachChild(fovGeometry);
// }
//
// // Creates new mesh.
// double minAngle = getTopologyNode().getAngularSpan().getMinimumAngle();
// double maxAngle = getTopologyNode().getAngularSpan().getMaximumAngle();
// double minRadius = getTopologyNode().getRange().getMinimumDistance();
// double maxRadius = getTopologyNode().getRange().getMaximumDistance();
//
// int numberOfDivision = (int) Math.round(getTopologyNode().getAngularSpan().getSpanningAngle() / getAngleIncrement());
// Mesh mesh = JME3FovUtilities.createCircularSector(minAngle, maxAngle, minRadius, maxRadius, numberOfDivision, true);
//
// // Creates new geometry.
// if(getTopologyNode().getNodeId() != null) fovGeometry = new Geometry("", mesh);
// else fovGeometry = new Geometry("?", mesh);
// fovGeometry.setMaterial(createMaterial());
//
// // Attaches new geometry.
// getFovNode().attachChild(fovGeometry);
//
// // Updates Presentation mode.
// internalSetPresentationMode(meshPresentationMode);
//
// // Attache image projector control.
// getAttachmentNode().addControl(getCircularSectorFieldOfViewImageProjectorControl());
//
// return null;
// }
// });
// }
private Material createMaterial() {
Material mat = new Material(this.assetManager, "Common/MatDefs/Light/Lighting.j3md");
if (getColor() != null) {
mat.setColor("Diffuse", JME3Utilities.convertToColorRGBA(getColor()));
mat.setColor("Ambient", JME3Utilities.convertToColorRGBA(getColor()));
mat.setColor("Specular", JME3Utilities.convertToColorRGBA(getColor()));
} else {
mat.setColor("Diffuse", DEFAULT_CIRCULAR_SECTOR_FOV_COLOR.clone());
mat.setColor("Ambient", DEFAULT_CIRCULAR_SECTOR_FOV_COLOR.clone());
mat.setColor("Specular", DEFAULT_CIRCULAR_SECTOR_FOV_COLOR.clone());
}
mat.setFloat("Shininess", 64f);
mat.setBoolean("UseMaterialColors", true);
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
return mat;
}
private Adapter getAngularSpanAdapter() {
if (this.angularSpanAdapter == null) {
this.angularSpanAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification notification) {
if (notification.getNotifier() instanceof AngularSpan) {
int featureId = notification.getFeatureID(AngularSpan.class);
switch (featureId) {
case ApogyAddonsSensorsFOVPackage.ANGULAR_SPAN__MAXIMUM_ANGLE:
case ApogyAddonsSensorsFOVPackage.ANGULAR_SPAN__MINIMUM_ANGLE:
// Updates Geometry.
// updateGeometry();
requestUpdate();
// Updates the FOV Settings of the projector.
getCircularSectorFieldOfViewImageProjectorControl().updateProjectorFOVSettings();
break;
default:
break;
}
}
}
};
}
return this.angularSpanAdapter;
}
private Adapter getRangeAdapter() {
if (this.rangeAdapter == null) {
this.rangeAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification notification) {
if (notification.getFeatureID(
CircularSectorFieldOfView.class) == ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MAXIMUM_DISTANCE) {
// updateGeometry();
requestUpdate();
// Updates the FOV Settings of the projector.
getCircularSectorFieldOfViewImageProjectorControl().updateProjectorFOVSettings();
} else if (notification.getFeatureID(
CircularSectorFieldOfView.class) == ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MINIMUM_DISTANCE) {
// updateGeometry();
requestUpdate();
// Updates the FOV Settings of the projector.
getCircularSectorFieldOfViewImageProjectorControl().updateProjectorFOVSettings();
}
}
};
}
return this.rangeAdapter;
}
private CircularSectorFieldOfViewImageProjectorControl getCircularSectorFieldOfViewImageProjectorControl() {
if (this.circularSectorFieldOfViewImageProjectorControl == null) {
this.circularSectorFieldOfViewImageProjectorControl = new CircularSectorFieldOfViewImageProjectorControl(
this.jme3Application, getTopologyNode());
}
return this.circularSectorFieldOfViewImageProjectorControl;
}
}