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