blob: c3af6dc3c02e0ee2742e2798fd3bdea50f7da0d6 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}