blob: ef7d76c82b4e097af95fb00d390ef99cde7a9ca9 [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.ApogyAddonsSensorsFOVPackage;
import org.eclipse.apogy.addons.sensors.fov.ConicalFieldOfView;
import org.eclipse.apogy.addons.sensors.fov.DistanceRange;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.AbstractFieldOfViewImageProjectorControl;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.ConicalFieldOfViewImageProjectorControl;
import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.JME3FovUtilities;
import org.eclipse.apogy.addons.sensors.fov.ui.scene_objects.ConicalFieldOfViewSceneObject;
import org.eclipse.apogy.common.topology.addons.primitives.ui.jme3.JME3PrimitivesUtilities;
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.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.scene.Geometry;
import com.jme3.scene.Mesh;
public class ConicalFieldOfViewJME3Object extends AbstractFieldOfViewJME3Object<ConicalFieldOfView>
implements ConicalFieldOfViewSceneObject {
private static final Logger Logger = LoggerFactory.getLogger(ConicalFieldOfViewJME3Object.class);
private float previousAxisLength = 1.0f;
private boolean axisVisible = true;
public static float DEFAULT_ANGLE_INCREMENT = (float) Math.toRadians(10.0);
private Adapter adapter;
private final AssetManager assetManager;
private Geometry frontHemisphereGeometry = null;
private Geometry rearHemisphereGeometry = null;
private Geometry truncatedConeGeometry = null;
private Geometry axisGeometry = null;
private ConicalFieldOfViewImageProjectorControl conicalFieldOfViewImageProjectorControl;
public ConicalFieldOfViewJME3Object(ConicalFieldOfView 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);
// Updates the geometry.
// updateGeometry();
requestUpdate();
// Listens for change on the ConicalFieldOfView.
node.eAdapters().add(getAdapter());
node.getRange().eAdapters().add(getAdapter());
}
@Override
public void updateGeometry(float tpf) {
// Removes previous geometries if applicable.
if (this.truncatedConeGeometry != null)
getFovNode().detachChild(this.truncatedConeGeometry);
if (this.frontHemisphereGeometry != null)
getFovNode().detachChild(this.frontHemisphereGeometry);
if (this.rearHemisphereGeometry != null)
getFovNode().detachChild(this.rearHemisphereGeometry);
// Creates new mesh.
float apexAngle = (float) getTopologyNode().getFieldOfViewAngle();
float minRadius = (float) getTopologyNode().getRange().getMinimumDistance();
float maxRadius = (float) getTopologyNode().getRange().getMaximumDistance();
Mesh truncatedConeMesh = JME3FovUtilities.createTruncatedCone(apexAngle, minRadius, maxRadius, 36);
this.truncatedConeGeometry = new Geometry("Sides", truncatedConeMesh);
this.truncatedConeGeometry.setMaterial(createMaterial());
Mesh frontHemisphereMesh = JME3PrimitivesUtilities.createSphericalCap(maxRadius,
(float) (Math.toRadians(90.0) - apexAngle / 2.0), (float) Math.toRadians(90.0), 36);
this.frontHemisphereGeometry = new Geometry("Front", frontHemisphereMesh);
this.frontHemisphereGeometry.setMaterial(createMaterial());
Mesh rearHemisphereMesh = JME3PrimitivesUtilities.createSphericalCap(minRadius,
(float) (Math.toRadians(90.0) - apexAngle / 2.0), (float) Math.toRadians(90.0), 36);
this.rearHemisphereGeometry = new Geometry("Rear", rearHemisphereMesh);
this.rearHemisphereGeometry.setMaterial(createMaterial());
// Attaches new geometries.
getFovNode().attachChild(this.truncatedConeGeometry);
getFovNode().attachChild(this.frontHemisphereGeometry);
getFovNode().attachChild(this.rearHemisphereGeometry);
// Sets the presentation mode.
internalSetPresentationMode(this.meshPresentationMode);
getAttachmentNode().addControl(getConicalFieldOfViewImageProjectorControl());
}
@Override
public void dispose() {
if (getTopologyNode() != null) {
getTopologyNode().eAdapters().remove(getAdapter());
if (getTopologyNode().getRange() != null) {
getTopologyNode().getRange().eAdapters().remove(getAdapter());
}
}
if (this.conicalFieldOfViewImageProjectorControl != null) {
this.conicalFieldOfViewImageProjectorControl.dispose();
this.conicalFieldOfViewImageProjectorControl = null;
}
super.dispose();
}
@Override
public List<Geometry> getGeometries() {
List<Geometry> geometries = new ArrayList<Geometry>();
geometries.add(this.frontHemisphereGeometry);
geometries.add(this.rearHemisphereGeometry);
geometries.add(this.truncatedConeGeometry);
if (this.axisGeometry != null)
geometries.add(this.axisGeometry);
return geometries;
}
@Override
public void setColor(RGB rgb) {
Logger.info("Set Color <" + rgb + ")");
super.setColor(rgb);
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
if (ConicalFieldOfViewJME3Object.this.frontHemisphereGeometry != null) {
Material mat = createMaterial();
mat.setColor("Diffuse", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Ambient", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Specular", ConicalFieldOfViewJME3Object.this.fovColor.clone());
ConicalFieldOfViewJME3Object.this.frontHemisphereGeometry.setMaterial(mat);
}
if (ConicalFieldOfViewJME3Object.this.rearHemisphereGeometry != null) {
Material mat = createMaterial();
mat.setColor("Diffuse", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Ambient", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Specular", ConicalFieldOfViewJME3Object.this.fovColor.clone());
ConicalFieldOfViewJME3Object.this.rearHemisphereGeometry.setMaterial(mat);
}
if (ConicalFieldOfViewJME3Object.this.truncatedConeGeometry != null) {
Material mat = createMaterial();
mat.setColor("Diffuse", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Ambient", ConicalFieldOfViewJME3Object.this.fovColor.clone());
mat.setColor("Specular", ConicalFieldOfViewJME3Object.this.fovColor.clone());
ConicalFieldOfViewJME3Object.this.truncatedConeGeometry.setMaterial(mat);
}
} catch (Throwable t) {
Logger.error("Failed to set color to <" + rgb + ">.", t);
}
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 {
getConicalFieldOfViewImageProjectorControl().setEnabled(showProjection);
return null;
}
});
this.showProjection = showProjection;
}
@Override
public void setShowOutlineOnly(boolean showOutlineOnly) {
}
@Override
public void setPresentationMode(MeshPresentationMode mode) {
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
internalSetPresentationMode(mode);
return null;
}
});
}
@Override
public AbstractFieldOfViewImageProjectorControl<ConicalFieldOfView> getAbstractFieldOfViewImageProjectorControl() {
return getConicalFieldOfViewImageProjectorControl();
}
@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 (ConicalFieldOfViewJME3Object.this.axisVisible) {
getAttachmentNode().attachChild(ConicalFieldOfViewJME3Object.this.axisGeometry);
} else {
getAttachmentNode().detachChild(ConicalFieldOfViewJME3Object.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) / ConicalFieldOfViewJME3Object.this.previousAxisLength;
// Scales existing axis.
if (ConicalFieldOfViewJME3Object.this.axisGeometry != null)
ConicalFieldOfViewJME3Object.this.axisGeometry.scale(scale);
ConicalFieldOfViewJME3Object.this.previousAxisLength = (float) length;
} catch (Throwable t) {
Logger.error("Failed to setAxisLength(" + length + ").", t);
}
return null;
}
});
}
// private void updateGeometry()
// {
// // Updates the geometry.
// Job job = new Job("ConicalFieldOfViewJME3Object : Updating Geometry.")
// {
// @Override
// protected IStatus run(IProgressMonitor monitor)
// {
// jme3Application.enqueue(new Callable<Object>()
// {
// @Override
// public Object call() throws Exception
// {
// // Removes previous geometries if applicable.
// if(truncatedConeGeometry != null) getFovNode().detachChild(truncatedConeGeometry);
// if(frontHemisphereGeometry != null) getFovNode().detachChild(frontHemisphereGeometry);
// if(rearHemisphereGeometry != null) getFovNode().detachChild(rearHemisphereGeometry);
//
// // Creates new mesh.
// float apexAngle = (float) getTopologyNode().getFieldOfViewAngle();
// float minRadius = (float) getTopologyNode().getRange().getMinimumDistance();
// float maxRadius = (float) getTopologyNode().getRange().getMaximumDistance();
//
// Mesh truncatedConeMesh = JME3FovUtilities.createTruncatedCone(apexAngle, minRadius, maxRadius, 36);
// truncatedConeGeometry = new Geometry("Sides", truncatedConeMesh);
// truncatedConeGeometry.setMaterial(createMaterial());
//
//
// Mesh frontHemisphereMesh = JME3PrimitivesUtilities.createSphericalCap(maxRadius, (float) (Math.toRadians(90.0) - apexAngle / 2.0), (float) Math.toRadians(90.0), 36);
// frontHemisphereGeometry = new Geometry("Front", frontHemisphereMesh);
// frontHemisphereGeometry.setMaterial(createMaterial());
//
// Mesh rearHemisphereMesh = JME3PrimitivesUtilities.createSphericalCap(minRadius, (float) (Math.toRadians(90.0) - apexAngle / 2.0), (float) Math.toRadians(90.0), 36);
// rearHemisphereGeometry = new Geometry("Rear", rearHemisphereMesh);
// rearHemisphereGeometry.setMaterial(createMaterial());
//
// // Attaches new geometries.
// getFovNode().attachChild(truncatedConeGeometry);
// getFovNode().attachChild(frontHemisphereGeometry);
// getFovNode().attachChild(rearHemisphereGeometry);
//
// // Sets the presentation mode.
// internalSetPresentationMode(meshPresentationMode);
//
// getAttachmentNode().addControl(getConicalFieldOfViewImageProjectorControl());
//
// return null;
// }
// });
// return Status.OK_STATUS;
// }
// };
// job.schedule();
//
// }
private void internalSetPresentationMode(MeshPresentationMode mode) {
switch (mode.getValue()) {
case MeshPresentationMode.SURFACE_VALUE:
if (this.truncatedConeGeometry != null) {
this.truncatedConeGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.truncatedConeGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
if (this.rearHemisphereGeometry != null) {
this.rearHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.rearHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
if (this.frontHemisphereGeometry != null) {
this.frontHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.frontHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
break;
case MeshPresentationMode.WIREFRAME_VALUE:
if (this.truncatedConeGeometry != null) {
this.truncatedConeGeometry.getMaterial().getAdditionalRenderState().setWireframe(true);
this.truncatedConeGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
if (this.rearHemisphereGeometry != null) {
this.rearHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(true);
this.rearHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
if (this.frontHemisphereGeometry != null) {
this.frontHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(true);
this.frontHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Triangles);
}
break;
case MeshPresentationMode.POINTS_VALUE:
if (this.truncatedConeGeometry != null) {
this.truncatedConeGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.truncatedConeGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Points);
}
if (this.rearHemisphereGeometry != null) {
this.rearHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.rearHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Points);
}
if (this.frontHemisphereGeometry != null) {
this.frontHemisphereGeometry.getMaterial().getAdditionalRenderState().setWireframe(false);
this.frontHemisphereGeometry.getMesh().setMode(com.jme3.scene.Mesh.Mode.Points);
}
break;
default:
break;
}
}
private Material createMaterial() {
Material mat = new Material(this.assetManager, "Common/MatDefs/Light/Lighting.j3md");
if (this.fovColor != null) {
mat.setColor("Diffuse", this.fovColor.clone());
mat.setColor("Ambient", this.fovColor.clone());
mat.setColor("Specular", this.fovColor.clone());
}
mat.setFloat("Shininess", 64f);
mat.setBoolean("UseMaterialColors", true);
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
return mat;
}
private Adapter getAdapter() {
if (this.adapter == null) {
this.adapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification notification) {
try {
if (notification.getNotifier() instanceof ConicalFieldOfView) {
int featureId = notification.getFeatureID(ConicalFieldOfView.class);
switch (featureId) {
case ApogyAddonsSensorsFOVPackage.CONICAL_FIELD_OF_VIEW__FIELD_OF_VIEW_ANGLE:
// updateGeometry();
requestUpdate();
// Updates the FOV Settings of the projector.
getConicalFieldOfViewImageProjectorControl().updateProjectorFOVSettings();
break;
case ApogyAddonsSensorsFOVPackage.CONICAL_FIELD_OF_VIEW__RANGE:
if (notification.getOldValue() instanceof DistanceRange) {
DistanceRange oldValue = (DistanceRange) notification.getOldValue();
oldValue.eAdapters().remove(getAdapter());
}
if (notification.getNewValue() instanceof DistanceRange) {
DistanceRange newValue = (DistanceRange) notification.getNewValue();
newValue.eAdapters().add(getAdapter());
}
// Updates the geometry.
// updateGeometry();
requestUpdate();
default:
break;
}
} else if (notification.getNotifier() instanceof DistanceRange) {
int featureId = notification.getFeatureID(DistanceRange.class);
switch (featureId) {
case ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MAXIMUM_DISTANCE:
case ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MINIMUM_DISTANCE:
// Updates the geometry.
// updateGeometry();
requestUpdate();
break;
default:
break;
}
}
} catch (Exception e) {
Logger.error("Error during update of ConicalFieldOfView geometry.", e);
}
}
};
}
return this.adapter;
}
private ConicalFieldOfViewImageProjectorControl getConicalFieldOfViewImageProjectorControl() {
if (this.conicalFieldOfViewImageProjectorControl == null) {
this.conicalFieldOfViewImageProjectorControl = new ConicalFieldOfViewImageProjectorControl(
this.jme3Application, getTopologyNode());
}
return this.conicalFieldOfViewImageProjectorControl;
}
}