| 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.DistanceRange; |
| import org.eclipse.apogy.addons.sensors.fov.RectangularFrustrumFieldOfView; |
| import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.AbstractFieldOfViewImageProjectorControl; |
| import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.JME3FovUtilities; |
| import org.eclipse.apogy.addons.sensors.fov.ui.jme3.utils.RectangularFrustrumFieldOfViewImageProjectorControl; |
| import org.eclipse.apogy.addons.sensors.fov.ui.scene_objects.RectangularFrustrumFieldOfViewSceneObject; |
| 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.asset.plugins.FileLocator; |
| 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 RectangularFrustumFieldOfViewJME3Object |
| extends AbstractFieldOfViewJME3Object<RectangularFrustrumFieldOfView> |
| implements RectangularFrustrumFieldOfViewSceneObject { |
| private static final Logger Logger = LoggerFactory.getLogger(RectangularFrustumFieldOfViewJME3Object.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 MeshPresentationMode meshPresentationMode = MeshPresentationMode.WIREFRAME; |
| |
| private Adapter fovAdapter; |
| private Adapter rangeAdapter; |
| |
| private AssetManager assetManager; |
| private Geometry fovGeometry = null; |
| private Geometry axisGeometry = null; |
| |
| // Projective textures |
| private RectangularFrustrumFieldOfViewImageProjectorControl rectangularFrustrumFieldOfViewImageProjectorControl; |
| |
| public RectangularFrustumFieldOfViewJME3Object(RectangularFrustrumFieldOfView node, |
| JME3RenderEngineDelegate jme3RenderEngineDelegate) { |
| super(node, jme3RenderEngineDelegate); |
| |
| this.assetManager = this.jme3Application.getAssetManager(); |
| this.assetManager.registerLocator("/", FileLocator.class); |
| |
| // Creates the 3DAxis. |
| this.axisGeometry = JME3Utilities.createAxis3D(1.0f, this.assetManager); |
| |
| this.jme3Application.enqueue(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| // updateGeometry(); |
| requestUpdate(); |
| |
| // Listens for change on the RectangularFrustrumFieldOfView. |
| node.eAdapters().add(getFOVAdapter()); |
| if (node.getRange() != null) { |
| node.getRange().eAdapters().add(getRangeAdapter()); |
| } |
| |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public void updateGeometry(float tpf) { |
| // Removes previous geometry if applicable. |
| if (this.fovGeometry != null) { |
| getFovNode().detachChild(this.fovGeometry); |
| } |
| |
| int numberOfDivision = 5; |
| |
| if (getTopologyNode().getHorizontalFieldOfViewAngle() > 5 * DEFAULT_ANGLE_INCREMENT) { |
| numberOfDivision = (int) Math |
| .round(getTopologyNode().getHorizontalFieldOfViewAngle() / DEFAULT_ANGLE_INCREMENT); |
| } else { |
| numberOfDivision = 5; |
| } |
| |
| Mesh mesh = JME3FovUtilities.createRectangularFrustum(getTopologyNode(), numberOfDivision, numberOfDivision); |
| |
| // Creates new geometry. |
| if (getTopologyNode().getNodeId() != null) |
| this.fovGeometry = new Geometry(getTopologyNode().getNodeId(), mesh); |
| else |
| this.fovGeometry = new Geometry("?", mesh); |
| this.fovGeometry.setMaterial(createMaterial()); |
| |
| // Attaches new geometry. |
| getFovNode().attachChild(this.fovGeometry); |
| |
| // Sets the presentation mode. |
| internalSetPresentationMode(this.meshPresentationMode); |
| |
| // Attaches the image projector to the root node so that image projector |
| // continues to work even if the node is invisible. |
| getAttachmentNode().addControl(getRectangularFrustrumFieldOfViewImageProjectorControl()); |
| } |
| |
| @Override |
| public void dispose() { |
| if (getTopologyNode() != null) { |
| getTopologyNode().eAdapters().remove(getFOVAdapter()); |
| |
| if (getTopologyNode().getRange() != null) { |
| getTopologyNode().getRange().eAdapters().remove(getRangeAdapter()); |
| } |
| } |
| |
| if (this.rectangularFrustrumFieldOfViewImageProjectorControl != null) { |
| this.rectangularFrustrumFieldOfViewImageProjectorControl.dispose(); |
| this.rectangularFrustrumFieldOfViewImageProjectorControl = null; |
| } |
| |
| 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) { |
| Logger.info("Set Color <" + rgb + ")."); |
| super.setColor(rgb); |
| try { |
| if (this.fovGeometry != null) { |
| final Material mat = createMaterial(); |
| mat.setColor("Diffuse", this.fovColor); |
| mat.setColor("Ambient", this.fovColor); |
| mat.setColor("Specular", this.fovColor); |
| |
| this.jme3Application.enqueue(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| RectangularFrustumFieldOfViewJME3Object.this.fovGeometry.setMaterial(mat); |
| return null; |
| } |
| }); |
| } |
| } catch (Throwable t) { |
| Logger.error("Failed to set color to <" + rgb + ">.", t); |
| } |
| } |
| |
| @Override |
| public void setPresentationMode(MeshPresentationMode mode) { |
| Logger.info("setPresentationMode(" + mode + ")."); |
| |
| this.meshPresentationMode = 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(final boolean showProjection) { |
| Logger.info("setShowProjection(" + showProjection + ")"); |
| |
| // Set the image projector enablement. |
| this.jme3Application.enqueue(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| getRectangularFrustrumFieldOfViewImageProjectorControl().setEnabled(showProjection); |
| return null; |
| } |
| }); |
| |
| this.showProjection = showProjection; |
| } |
| |
| @Override |
| public void setShowOutlineOnly(boolean showOutlineOnly) { |
| } |
| |
| @Override |
| public AbstractFieldOfViewImageProjectorControl<RectangularFrustrumFieldOfView> getAbstractFieldOfViewImageProjectorControl() { |
| return getRectangularFrustrumFieldOfViewImageProjectorControl(); |
| } |
| |
| @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 (RectangularFrustumFieldOfViewJME3Object.this.axisVisible) { |
| getAttachmentNode().attachChild(RectangularFrustumFieldOfViewJME3Object.this.axisGeometry); |
| } else { |
| getAttachmentNode().detachChild(RectangularFrustumFieldOfViewJME3Object.this.axisGeometry); |
| } |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public void setAxisLength(double length) { |
| if (length > 0) { |
| 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) |
| / RectangularFrustumFieldOfViewJME3Object.this.previousAxisLength; |
| |
| // Scales existing axis. |
| if (RectangularFrustumFieldOfViewJME3Object.this.axisGeometry != null) |
| RectangularFrustumFieldOfViewJME3Object.this.axisGeometry.scale(scale); |
| |
| RectangularFrustumFieldOfViewJME3Object.this.previousAxisLength = (float) length; |
| } catch (Throwable t) { |
| Logger.error("Failed to setAxisLength(" + length + ").", t); |
| } |
| |
| return null; |
| } |
| }); |
| } else { |
| Logger.error("Setting axis length to <" + length + "> failed : Length must be greater than zero."); |
| } |
| } |
| |
| private void internalSetPresentationMode(MeshPresentationMode mode) { |
| if (this.fovGeometry != null && this.fovGeometry.getMesh() != 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; |
| } |
| } |
| } |
| |
| // 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); |
| // } |
| // |
| // int numberOfDivision = 5; |
| // |
| // if(getTopologyNode().getHorizontalFieldOfViewAngle() > 5*DEFAULT_ANGLE_INCREMENT) |
| // { |
| // numberOfDivision = (int) Math.round(getTopologyNode().getHorizontalFieldOfViewAngle() / DEFAULT_ANGLE_INCREMENT); |
| // } |
| // else |
| // { |
| // numberOfDivision = 5; |
| // } |
| // |
| // |
| // Mesh mesh = JME3FovUtilities.createRectangularFrustum(getTopologyNode(), numberOfDivision, numberOfDivision); |
| // |
| // // Creates new geometry. |
| // if(getTopologyNode().getNodeId() != null) fovGeometry = new Geometry(getTopologyNode().getNodeId(), mesh); |
| // else fovGeometry = new Geometry("?", mesh); |
| // fovGeometry.setMaterial(createMaterial()); |
| // |
| // // Attaches new geometry. |
| // getFovNode().attachChild(fovGeometry); |
| // |
| // // Sets the presentation mode. |
| // internalSetPresentationMode(meshPresentationMode); |
| // |
| // // Attaches the image projector to the root node so that image projector continues to work even if the node is invisible. |
| // getAttachmentNode().addControl(getRectangularFrustrumFieldOfViewImageProjectorControl()); |
| // |
| // 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 getFOVAdapter() { |
| if (this.fovAdapter == null) { |
| this.fovAdapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification notification) { |
| if (notification.getNotifier() instanceof RectangularFrustrumFieldOfView) { |
| int featureId = notification.getFeatureID(RectangularFrustrumFieldOfView.class); |
| |
| if (featureId == ApogyAddonsSensorsFOVPackage.RECTANGULAR_FRUSTRUM_FIELD_OF_VIEW__HORIZONTAL_FIELD_OF_VIEW_ANGLE |
| || featureId == ApogyAddonsSensorsFOVPackage.RECTANGULAR_FRUSTRUM_FIELD_OF_VIEW__VERTICAL_FIELD_OF_VIEW_ANGLE) { |
| // updateGeometry(); |
| requestUpdate(); |
| |
| // Updates the FOV Settings of the projector. |
| getRectangularFrustrumFieldOfViewImageProjectorControl().updateProjectorFOVSettings(); |
| } else if (featureId == ApogyAddonsSensorsFOVPackage.RECTANGULAR_FRUSTRUM_FIELD_OF_VIEW__RANGE) { |
| if (notification.getOldValue() instanceof DistanceRange) { |
| DistanceRange oldDistanceRange = (DistanceRange) notification.getOldValue(); |
| oldDistanceRange.eAdapters().remove(getRangeAdapter()); |
| } |
| |
| // updateGeometry(); |
| requestUpdate(); |
| |
| if (notification.getNewValue() instanceof DistanceRange) { |
| DistanceRange newDistanceRange = (DistanceRange) notification.getNewValue(); |
| |
| // Add adapter to the new range. |
| newDistanceRange.eAdapters().add(getRangeAdapter()); |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| return this.fovAdapter; |
| } |
| |
| private Adapter getRangeAdapter() { |
| if (this.rangeAdapter == null) { |
| this.rangeAdapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification notification) { |
| if (notification.getNotifier() instanceof DistanceRange) { |
| int featureId = notification.getFeatureID(DistanceRange.class); |
| if (featureId == ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MAXIMUM_DISTANCE |
| || featureId == ApogyAddonsSensorsFOVPackage.DISTANCE_RANGE__MAXIMUM_DISTANCE) { |
| // updateGeometry(); |
| requestUpdate(); |
| } |
| } |
| |
| } |
| }; |
| } |
| |
| return this.rangeAdapter; |
| } |
| |
| private RectangularFrustrumFieldOfViewImageProjectorControl getRectangularFrustrumFieldOfViewImageProjectorControl() { |
| if (this.rectangularFrustrumFieldOfViewImageProjectorControl == null) { |
| this.rectangularFrustrumFieldOfViewImageProjectorControl = new RectangularFrustrumFieldOfViewImageProjectorControl( |
| this.jme3Application, getTopologyNode()); |
| } |
| |
| return this.rectangularFrustrumFieldOfViewImageProjectorControl; |
| } |
| } |