| /******************************************************************************* |
| * 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, |
| * Regent L'Archeveque, |
| * Sebastien Gemme - initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-1.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.apogy.common.topology.addons.primitives.ui.jme3.scene_objects; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.Callable; |
| |
| import javax.vecmath.Matrix4d; |
| import javax.vecmath.Point3d; |
| |
| import org.eclipse.apogy.common.topology.ApogyCommonTopologyFacade; |
| import org.eclipse.apogy.common.topology.Node; |
| import org.eclipse.apogy.common.topology.addons.primitives.ApogyCommonTopologyAddonsPrimitivesPackage; |
| import org.eclipse.apogy.common.topology.addons.primitives.PickVector; |
| import org.eclipse.apogy.common.topology.addons.primitives.Vector; |
| import org.eclipse.apogy.common.topology.addons.primitives.ui.VectorSceneObject; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3Application; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3Utilities; |
| import org.eclipse.apogy.common.topology.ui.jme3.scene_objects.DefaultJME3SceneObject; |
| 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.collision.CollisionResult; |
| import com.jme3.collision.CollisionResults; |
| import com.jme3.material.Material; |
| import com.jme3.math.ColorRGBA; |
| import com.jme3.math.Quaternion; |
| import com.jme3.math.Ray; |
| import com.jme3.math.Vector3f; |
| import com.jme3.scene.Geometry; |
| import com.jme3.scene.Mesh; |
| import com.jme3.util.BufferUtils; |
| |
| public class PickVectorJME3SceneObject extends DefaultJME3SceneObject<PickVector> implements VectorSceneObject { |
| private static final Logger Logger = LoggerFactory.getLogger(PickVectorJME3SceneObject.class); |
| |
| private Adapter adapter = null; |
| |
| private ColorRGBA vectorColor = ColorRGBA.White; |
| private final AssetManager assetManager; |
| private Geometry vectorGeometry = null; |
| |
| public PickVectorJME3SceneObject(PickVector topologyNode, JME3RenderEngineDelegate jme3RenderEngineDelegate) { |
| super(topologyNode, jme3RenderEngineDelegate); |
| |
| this.assetManager = this.jme3Application.getAssetManager(); |
| |
| this.vectorGeometry = createVectorGeometry(); |
| getAttachmentNode().attachChild(this.vectorGeometry); |
| |
| // Listens for changes on the Vector. |
| getTopologyNode().eAdapters().add(getAdapter()); |
| } |
| |
| @Override |
| public void updateGeometry(float tpf) { |
| if (this.vectorGeometry != null) |
| getAttachmentNode().detachChild(this.vectorGeometry); |
| |
| // Creates the vector. |
| this.vectorGeometry = createVectorGeometry(); |
| |
| // Attach the new vector. |
| getAttachmentNode().attachChild(this.vectorGeometry); |
| |
| try { |
| // Finds intersection. |
| updateIntersection(); |
| |
| } catch (Throwable t) { |
| Logger.error(t.getMessage(), t); |
| } |
| } |
| |
| @Override |
| public List<Geometry> getGeometries() { |
| List<Geometry> geometries = new ArrayList<Geometry>(); |
| geometries.add(this.vectorGeometry); |
| return geometries; |
| } |
| |
| @Override |
| public void setColor(RGB rgb) { |
| this.vectorColor = JME3Utilities.convertToColorRGBA(rgb); |
| this.jme3Application.enqueue(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| try { |
| if (PickVectorJME3SceneObject.this.vectorGeometry != null) { |
| Material mat = createVectorMaterial(); |
| PickVectorJME3SceneObject.this.vectorGeometry.setMaterial(mat); |
| } |
| } catch (Throwable t) { |
| Logger.error("Failed to set color to <" + rgb + ">.", t); |
| } |
| |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| Logger.info("Setting visibility to <" + visible + ">."); |
| super.setVisible(visible); |
| } |
| |
| @Override |
| public void dispose() { |
| if (getTopologyNode() != null) { |
| getTopologyNode().eAdapters().remove(getAdapter()); |
| } |
| super.dispose(); |
| } |
| |
| @Override |
| public int getLineWidth() { |
| return 0; |
| } |
| |
| @Override |
| public void setLineWidth(int lineWidth) { |
| } |
| |
| @Override |
| public void initialize() { |
| requestUpdate(); |
| } |
| |
| private Geometry createVectorGeometry() { |
| // Create the mesh |
| Mesh mesh = createVectorMesh(); |
| |
| // Create the Material |
| Material material = createVectorMaterial(); |
| |
| // Create Geometry. |
| Geometry geometry = new Geometry("Vector Body", mesh); |
| geometry.setMaterial(material); |
| |
| return geometry; |
| } |
| |
| private Mesh createVectorMesh() { |
| List<Vector3f> verticesList = new ArrayList<Vector3f>(); |
| List<Integer> indexesList = new ArrayList<Integer>(); |
| |
| Vector3f p0 = new Vector3f(0, 0, 0); |
| Vector3f p1 = new Vector3f((float) getTopologyNode().getCoordinates().getX(), |
| (float) getTopologyNode().getCoordinates().getY(), (float) getTopologyNode().getCoordinates().getZ()); |
| verticesList.add(p0); |
| verticesList.add(p1); |
| indexesList.add(verticesList.indexOf(p0)); |
| indexesList.add(verticesList.indexOf(p1)); |
| |
| Mesh mesh = new Mesh(); |
| mesh.setMode(Mesh.Mode.Lines); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, |
| BufferUtils.createFloatBuffer(JME3Utilities.convertToFloatArray(verticesList))); |
| mesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 2, |
| BufferUtils.createIntBuffer(JME3Utilities.convertToIntArray(indexesList))); |
| mesh.updateBound(); |
| mesh.updateCounts(); |
| |
| return mesh; |
| } |
| |
| private Material createVectorMaterial() { |
| Material material = new Material(this.assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
| material.setColor("Color", this.vectorColor.clone()); |
| return material; |
| } |
| |
| private JME3Application getJME3TopologyViewer() { |
| if (getApplication() instanceof JME3Application) { |
| return (JME3Application) getApplication(); |
| } else { |
| return null; |
| } |
| } |
| |
| private void updateIntersection() { |
| this.jme3Application.enqueue(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| float intersectionDistance = Float.NaN; |
| Node intersectedNode = null; |
| Point3d relativeIntersectionPosition = null; |
| Point3d absoluteIntersectionPosition = null; |
| |
| CollisionResults results = new CollisionResults(); |
| Vector3f origin = getAttachmentNode().getWorldTranslation(); |
| |
| Vector3f direction = new Vector3f((float) getTopologyNode().getCoordinates().getX(), |
| (float) getTopologyNode().getCoordinates().getY(), |
| (float) getTopologyNode().getCoordinates().getZ()); |
| direction = direction.normalize(); |
| |
| // Rotate the relative direction. |
| Quaternion rotation = getAttachmentNode().getWorldRotation(); |
| direction = rotation.mult(direction); |
| Ray ray = new Ray(origin, direction); |
| |
| // Collect intersections between ray and all nodes in results list. |
| getJME3TopologyViewer().getRootNode().collideWith(ray, results); |
| |
| if (results.size() > 0) { |
| try { |
| int i = 0; |
| while (i < results.size()) { |
| CollisionResult collisionResult = results.getCollision(i); |
| Geometry geometry = collisionResult.getGeometry(); |
| |
| // Transform the pick position in the node frame. |
| Node node = PickVectorJME3SceneObject.this.jme3RenderEngineDelegate |
| .getTopologyNode(geometry); |
| |
| // Check if the node type is to be included in intersection. |
| if (getTopologyNode().isNodeIncludedInIntersection(node)) { |
| Vector3f contact = collisionResult.getContactPoint(); |
| float currentDistance = origin.distance(contact); |
| |
| if (Float.isNaN(intersectionDistance) || currentDistance < intersectionDistance) { |
| intersectionDistance = currentDistance; |
| intersectedNode = node; |
| absoluteIntersectionPosition = new Point3d( |
| JME3Utilities.convertToJavaxVector3f(contact)); |
| |
| // Transform the pick position in the node frame. |
| Matrix4d m = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(node); |
| m.invert(); |
| Point3d relativePosition = new Point3d(absoluteIntersectionPosition); |
| m.transform(relativePosition); |
| relativeIntersectionPosition = new Point3d(relativePosition); |
| } |
| } |
| i++; |
| } |
| } catch (Throwable t) { |
| } |
| } |
| |
| // Updates the intersection data. |
| getTopologyNode().setIntersectedNode(intersectedNode); |
| getTopologyNode().setAbsoluteIntersectionPosition(absoluteIntersectionPosition); |
| getTopologyNode().setRelativeIntersectionPosition(relativeIntersectionPosition); |
| getTopologyNode().setIntersectionDistance(intersectionDistance); |
| |
| return null; |
| } |
| }); |
| } |
| |
| private Adapter getAdapter() { |
| if (this.adapter == null) { |
| this.adapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification msg) { |
| if (msg.getNotifier() instanceof Vector) { |
| int featureId = msg.getFeatureID(Vector.class); |
| switch (featureId) { |
| case ApogyCommonTopologyAddonsPrimitivesPackage.VECTOR__COORDINATES: |
| requestUpdate(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| }; |
| } |
| return this.adapter; |
| } |
| } |