blob: 2c9ccb9e4e78f9457b2c7f9be699d8ef5179515f [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,
<<<<<<< HEAD
* Sebastien Gemme
*
=======
* Sebastien Gemme - initial API and implementation
*
>>>>>>> refs/heads/eclipse_pa
* SPDX-License-Identifier: EPL-1.0
*
*******************************************************************************/
package org.eclipse.apogy.common.topology.ui.jme3.internal;
import java.util.Iterator;
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.ui.ApogyCommonTopologyUIFacade;
import org.eclipse.apogy.common.topology.ui.GraphicsContext;
import org.eclipse.apogy.common.topology.ui.NodeSelection;
import org.eclipse.apogy.common.topology.ui.TopologyPresentationSet;
import org.eclipse.apogy.common.topology.ui.jme3.JME3Application;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.system.AppSettings;
public abstract class MousePickListener implements ActionListener {
private static final Logger Logger = LoggerFactory.getLogger(MousePickListener.class);
public static final String PICK_ENABLED_EVENT = "KeyboardP";
public static final String PICK_EVENT = "LeftMouseButtonPickEvent";
private final String[] inputs = new String[] { PICK_ENABLED_EVENT, PICK_EVENT };
private Camera camera = null;
private final InputManager inputManager;
private final JME3Application jme3Application;
private BitmapText hudText;
private BitmapFont guiFont;
private boolean keyBoardInputRegistered = false;
private boolean mouseInputRegistered = false;
private boolean pickEnabled = false;
private boolean busy = false;
public MousePickListener(Camera camera, JME3Application jme3Application, InputManager inputManager) {
this.camera = camera;
this.jme3Application = jme3Application;
this.inputManager = inputManager;
registerWithInput();
}
/**
* Method called when a selection is made. Should be overloaded.
*
* @param event The SelectionChangedEvent.
*/
protected abstract void fireSelectionChanged(final SelectionChangedEvent event);
protected abstract void pickingEnabled(boolean enable);
@Override
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals(PICK_EVENT) && keyPressed) {
if (this.busy) {
Logger.info("Busy !");
} else if (this.pickEnabled) {
try {
this.busy = true;
CollisionResults results = new CollisionResults();
// Convert screen click to 3d position
Vector2f click2d = this.inputManager.getCursorPosition();
Vector3f click3d = this.camera.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = this.camera.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f)
.subtractLocal(click3d).normalizeLocal();
// Aim the ray from the clicked spot forwards.
Ray ray = new Ray(click3d, dir);
// Collect intersections between ray and all nodes in
// results list.
this.jme3Application.getRootNode().collideWith(ray, results);
if (results.size() > 0) {
CollisionResult collisionResult = null;
Vector3f contactNormal = null;
Geometry geometry = null;
Node node = null;
Iterator<CollisionResult> it = results.iterator();
while (node == null && it.hasNext()) {
collisionResult = it.next();
contactNormal = collisionResult.getContactNormal();
geometry = collisionResult.getGeometry();
node = this.jme3Application.getJMERenderEngineDelegate().getTopologyNode(geometry);
}
if (node != null) {
// Transform the pick position in the node frame.
Matrix4d m = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(node);
m.invert();
Point3d relativePosition = new Point3d(collisionResult.getContactPoint().x,
collisionResult.getContactPoint().y, collisionResult.getContactPoint().z);
m.transform(relativePosition);
// Converts the normal
javax.vecmath.Vector3f contactNormalVector = new javax.vecmath.Vector3f(contactNormal.x,
contactNormal.y, contactNormal.z);
m.transform(contactNormalVector);
TopologyPresentationSet topologyPresentationSet = null;
if (this.jme3Application.getJMERenderEngineDelegate().getTopologyViewer()
.getInput() instanceof GraphicsContext) {
GraphicsContext graphicsContext = (GraphicsContext) this.jme3Application
.getJMERenderEngineDelegate().getTopologyViewer().getInput();
topologyPresentationSet = graphicsContext.getTopologyPresentationSet();
}
final NodeSelection nodeSelection = ApogyCommonTopologyUIFacade.INSTANCE
.createNodeSelection(topologyPresentationSet, node, relativePosition,
contactNormalVector);
Logger.debug("--------------------------------------------------------------");
Logger.debug(" PICK SELECTION");
Logger.debug(nodeSelection.toString());
Logger.debug("--------------------------------------------------------------");
Logger.debug("--------------------------------------------------------------");
Logger.debug(" PICK SELECTION");
Logger.debug("Selected Node : " + nodeSelection.getSelectedNode());
Logger.debug("Relative Position : " + nodeSelection.getRelativeIntersectionPoint());
Logger.debug("Absolute Position : " + nodeSelection.getAbsoluteIntersectionPoint());
Logger.debug("Relative Normal : " + nodeSelection.getRelativeIntersectionNormal());
Logger.debug("Absolute Normal : " + nodeSelection.getAbsoluteIntersectionNormal());
Logger.debug("--------------------------------------------------------------");
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
if (MousePickListener.this.jme3Application
.getJMERenderEngineDelegate() != null) {
StructuredSelection selection = new StructuredSelection(nodeSelection);
SelectionChangedEvent event = new SelectionChangedEvent(
MousePickListener.this.jme3Application.getJMERenderEngineDelegate()
.getTopologyViewer(),
selection);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
try {
fireSelectionChanged(event);
} catch (Exception e) {
Logger.error(e.getMessage(), e);
}
MousePickListener.this.busy = false;
}
});
}
} catch (Throwable t) {
}
return null;
}
});
// Runnable runnable = new Runnable() {
// @Override
// public void run() {
// try {
// if (jme3Application.getJMERenderEngineDelegate() != null) {
// StructuredSelection selection = new StructuredSelection(nodeSelection);
//
// SelectionChangedEvent event = new SelectionChangedEvent(
// jme3Application.getJMERenderEngineDelegate().getTopologyViewer(),
// selection);
//
// fireSelectionChanged(event);
// }
// } catch (Throwable t) {
//
// }
//
// busy = false;
// }
// };
//
// Display.getDefault().asyncExec(runnable);
} else {
this.busy = false;
}
} else {
this.busy = false;
}
} catch (Exception e) {
this.busy = false;
}
}
} else if (name.equals(PICK_ENABLED_EVENT)) {
// Toggles pick enabled.
setPickEnabled(!this.pickEnabled);
}
}
public void setEnabled(boolean enabled) {
if (enabled) {
if (!this.keyBoardInputRegistered) {
registerWithInput();
}
Logger.info("Pick is enabled.");
} else {
if (this.keyBoardInputRegistered) {
unregisterInput();
}
Logger.info("Pick is disabled.");
}
}
public void setPickEnabled(boolean enabled) {
pickingEnabled(enabled);
if (enabled) {
if (!this.mouseInputRegistered) {
registerMouseInput();
}
} else {
if (this.mouseInputRegistered) {
unregisterMouseInput();
}
}
// Updates the GUI.
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
if (enabled) {
if (!MousePickListener.this.jme3Application.getGuiNode().getChildren().contains(getHudText())) {
updateHudText();
MousePickListener.this.jme3Application.getGuiNode().attachChild(getHudText());
}
} else {
if (MousePickListener.this.jme3Application.getGuiNode().getChildren().contains(getHudText())) {
MousePickListener.this.jme3Application.getGuiNode().detachChild(getHudText());
}
}
return null;
}
});
this.pickEnabled = enabled;
}
protected BitmapText getHudText() {
if (this.hudText == null) {
this.hudText = new BitmapText(getGuiFont(), false);
this.hudText.setSize(this.guiFont.getCharSet().getRenderedSize()); // font
// size
this.hudText.setColor(ColorRGBA.Yellow); // font color
this.hudText.setText("Picking Enabled");
updateHudText();
}
return this.hudText;
}
protected void updateHudText() {
BitmapText text = getHudText();
AppSettings settings = this.jme3Application.getAppSettings();
float x = 0;
float y = this.hudText.getLineHeight();
Logger.debug(settings.getWidth() + " x " + settings.getHeight());
text.setLocalTranslation(x, y, 0); // position
text.setQueueBucket(Bucket.Gui);
}
protected BitmapFont getGuiFont() {
if (this.guiFont == null) {
this.guiFont = this.jme3Application.getAssetManager().loadFont("Interface/Fonts/Default.fnt");
}
return this.guiFont;
}
protected void registerMouseInput() {
if (!this.inputManager.hasMapping(PICK_EVENT)) {
this.inputManager.addMapping(PICK_EVENT, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
this.inputManager.addListener(this, new String[] { PICK_EVENT });
}
this.mouseInputRegistered = true;
}
protected void unregisterMouseInput() {
if (this.inputManager.hasMapping(PICK_EVENT)) {
this.inputManager.deleteMapping(PICK_EVENT);
}
this.mouseInputRegistered = false;
}
/**
* Registers inputs with the input manager.
*
* @param inputManager
*/
private void registerWithInput() {
if (!this.inputManager.hasMapping(PICK_ENABLED_EVENT)) {
this.inputManager.addMapping(PICK_ENABLED_EVENT, new KeyTrigger(KeyInput.KEY_P));
this.inputManager.addListener(this, new String[] { PICK_ENABLED_EVENT });
}
this.keyBoardInputRegistered = true;
}
private void unregisterInput() {
for (int i = 0; i < this.inputs.length; i++) {
if (!this.inputManager.hasMapping(this.inputs[i])) {
this.inputManager.deleteMapping(this.inputs[i]);
}
}
this.keyBoardInputRegistered = false;
}
}