| 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; |
| } |
| } |