/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package automenta.spacenet.space.control.pointer; import automenta.spacenet.run.ArdorSpacetime; import automenta.spacenet.space.Repeat; import automenta.spacenet.space.Spacetime; import automenta.spacenet.space.control.Draggable; import automenta.spacenet.space.control.Pressable; import automenta.spacenet.space.control.Tangible; import automenta.spacenet.space.control.Touchable; import automenta.spacenet.space.control.Zoomable; import automenta.spacenet.space.control.camera.ArdorCamera; import com.ardor3d.framework.Canvas; import com.ardor3d.framework.NativeCanvas; import com.ardor3d.input.MouseButton; import com.ardor3d.input.MouseManager; import com.ardor3d.input.logical.InputTrigger; import com.ardor3d.input.logical.LogicalLayer; import com.ardor3d.input.logical.MouseButtonPressedCondition; import com.ardor3d.input.logical.MouseButtonReleasedCondition; import com.ardor3d.input.logical.MouseMovedCondition; import com.ardor3d.input.logical.TriggerAction; import com.ardor3d.input.logical.TwoInputStates; import com.ardor3d.intersection.PickData; import com.ardor3d.intersection.PickResults; import com.ardor3d.intersection.PickingUtil; import com.ardor3d.intersection.PrimitivePickResults; import com.ardor3d.math.Matrix3; import com.ardor3d.math.Ray3; import com.ardor3d.math.Vector2; import com.ardor3d.math.Vector3; import com.ardor3d.scenegraph.Mesh; import com.ardor3d.scenegraph.Node; import com.ardor3d.scenegraph.Spatial; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; /** * * * @author seh */ public class DefaultPointer extends Repeat implements Pointer { private double firstPersonRotateSpeed = 64.0; private double zoomOutSpeed = 2.0; private final Matrix3 tempMatrix = new Matrix3(); private final Vector3 tempVectorA = new Vector3(); private PrimitivePickResults pickResults; private Touchable currentTouch; private Tangible currentTangible; private Pressable currentPress; private PickData tangiblePick; private final Spacetime spacetime; private Draggable currentDraggable, beingDragged; Vector2 pixelPos = new Vector2(); final Ray3 pickRay = new Ray3(); Vector2 pixelPosDragStart = new Vector2(); final Ray3 rayDrag = new Ray3(); final Ray3 rayDragStart = new Ray3(); final Ray3 rayDragStop = new Ray3(); Vector2 pixelPosDragStop = new Vector2(); private Mesh pickedMesh; private boolean middlePressed = false; Vector2 midPressStartPixelPos; List<PickData> pdList = new LinkedList(); Vector3 rcUp = new Vector3(); Vector3 rcLeft = new Vector3(); Vector3 rcDir = new Vector3(); private boolean leftPressed = false; private boolean rightPressed = false; private Mesh pickedMeshWhenRightPressed; private double rightZoomTime; private double autoZoomPressTimeThreshold = 0.2; //in seconds protected void setRay(Vector2 pixelPos, Ray3 targetRay) { getCanvas().getCanvasRenderer().getCamera().getPickRay(pixelPos, false, targetRay); } public Ray3 getPickRay() { return pickRay; } protected void updatePointer(double dt) { setRay(pixelPos, pickRay); updatePick(pickRay); if (beingDragged != null) { dragged(); } else if (currentDraggable != null) { //System.out.println(" drag dist: " + pixelPosDragStart.distance(pixelPos) + " @ " + pixelPos + " <- " + pixelPosDragStart); double dist = pixelPosDragStart.distance(pixelPos); if (dist > getDragThreshold()) { dragStart(); } else { pixelPosDragStart.set(pixelPos); } } else { pixelPosDragStart.set(pixelPos); } if (middlePressed) { if (midPressStartPixelPos == null) { midPressStartPixelPos = new Vector2(pixelPos); } double dx = pixelPos.getX() - midPressStartPixelPos.getX(); double dy = pixelPos.getY() - midPressStartPixelPos.getY(); int pixelInnerRadius = Math.min(getSpacetime().getVideo().getCanvasRenderer().getCamera().getWidth(), getSpacetime().getVideo().getCanvasRenderer().getCamera().getHeight()); dx /= (double) pixelInnerRadius; dy /= (double) pixelInnerRadius; //midPressStartPixelPos.set(pixelPos); rotateCam(dx, dy, dt); } else { midPressStartPixelPos = null; } if (rightPressed) { rightZoomTime+=dt; if (middlePressed) { if (pickedMeshWhenRightPressed == null) { getSpacetime().getCamera().zoomForward(-zoomOutSpeed * dt); } else { getSpacetime().getCamera().zoomForward(zoomOutSpeed * dt); } } else { getSpacetime().getCamera().zoomForward(-zoomOutSpeed * dt); } } else { rightZoomTime = 0; } } protected void dragStart() { setRay(pixelPos, rayDragStart); beingDragged = currentDraggable; beingDragged.onDragStart(rayDragStart); } protected void dragged() { setRay(pixelPos, rayDrag); beingDragged.onDragging(rayDrag); } protected void dragStop() { pixelPosDragStop.set(pixelPos); setRay(pixelPosDragStop, rayDragStop); beingDragged.onDragStop(rayDragStop); } protected double getDragThreshold() { return 1.01; } protected void leftPressed() { leftPressed = true; // if (getMouseManager().isSetGrabbedSupported()) { // getMouseManager().setGrabbed(GrabbedState.GRABBED); // } if (currentTangible instanceof Pressable) { currentPress = (Pressable) currentTangible; ((Pressable) currentTangible).onPressStart(tangiblePick); } if (currentTangible instanceof Draggable) { if (currentDraggable != currentTangible) { currentDraggable = (Draggable) currentTangible; //System.out.println("start drag " + pixelPos); pixelPosDragStart.set(pixelPos); } } } protected void leftReleased() { leftPressed = false; // if (getMouseManager().isSetGrabbedSupported()) { // getMouseManager().setGrabbed(GrabbedState.NOT_GRABBED); // } if (currentPress != null) { currentPress.onPressStop(tangiblePick); currentPress = null; } currentDraggable = null; if (beingDragged != null) { dragStop(); } beingDragged = null; } public Spacetime getSpacetime() { return spacetime; } protected void middlePressed() { this.middlePressed = true; } protected void middleReleased() { this.middlePressed = false; } protected void rightPressed() { this.rightPressed = true; this.pickedMeshWhenRightPressed = pickedMesh; } protected void rightReleased() { this.rightPressed = false; this.pickedMeshWhenRightPressed = null; if (rightZoomTime < autoZoomPressTimeThreshold) { if (!middlePressed) { if (currentTangible != null) { Zoomable z = getZoomable(currentTangible); if (z != null) { getSpacetime().getCamera().zoomTo(z, currentTangible, pickedMesh); } } } } } public DefaultPointer(Spacetime spacetime) { super(); this.spacetime = spacetime; // Set up a reusable pick results pickResults = new PrimitivePickResults(); pickResults.setCheckDistance(true); // _logicalLayer.registerTrigger(new InputTrigger(new MouseButtonClickedCondition(MouseButton.RIGHT), // new TriggerAction() { // // public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { // // final Vector2 pos = Vector2.fetchTempInstance().set( // inputStates.getCurrent().getMouseState().getX(), // inputStates.getCurrent().getMouseState().getY()); // final Ray3 pickRay = new Ray3(); // _canvas.getCanvasRenderer().getCamera().getPickRay(pos, false, pickRay); // Vector2.releaseTempInstance(pos); // doPick(pickRay); // } // })); // // final Predicate<TwoInputStates> clickLeftOrRight = Predicates.or(new MouseButtonClickedCondition( // MouseButton.LEFT), new MouseButtonClickedCondition(MouseButton.RIGHT)); // // _logicalLayer.registerTrigger(new InputTrigger(clickLeftOrRight, new TriggerAction() { // // public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { // System.err.println("clicked: " + inputStates.getCurrent().getMouseState().getClickCounts()); // } // })); spacetime.addCondition(new InputTrigger(new MouseButtonPressedCondition(MouseButton.LEFT), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { leftPressed(); } })); spacetime.addCondition(new InputTrigger(new MouseButtonReleasedCondition(MouseButton.LEFT), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { leftReleased(); } })); spacetime.addCondition(new InputTrigger(new MouseButtonPressedCondition(MouseButton.RIGHT), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { rightPressed(); } })); spacetime.addCondition(new InputTrigger(new MouseButtonPressedCondition(MouseButton.MIDDLE), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { middlePressed(); } })); spacetime.addCondition(new InputTrigger(new MouseButtonReleasedCondition(MouseButton.MIDDLE), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { middleReleased(); } })); spacetime.addCondition(new InputTrigger(new MouseButtonReleasedCondition(MouseButton.RIGHT), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) { rightReleased(); } })); spacetime.addCondition(new InputTrigger(new MouseMovedCondition(), new TriggerAction() { public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { pixelPos.set(inputStates.getCurrent().getMouseState().getX(), inputStates.getCurrent().getMouseState().getY()); //moved(); } })); } @Override protected void update(double t, double dt, Spatial s) { updatePointer(dt); } public Node getRoot() { return spacetime.getRoot(); } public NativeCanvas getCanvas() { return spacetime.getVideo(); } public LogicalLayer getLogicalLayer() { return spacetime.getInputLogic(); } public MouseManager getMouseManager() { return spacetime.getMouseManager(); } private void print(List<PickData> pr) { System.out.println("pick results: " + pr.size()); for (PickData pd : pr) { System.out.println(" " + pd.getClosestDistance() + " : " + pd.getTargetMesh() + " " + isTangible(pd.getTargetMesh())); } } protected void processPicks(final PrimitivePickResults pickResults) { pdList.clear(); for (int i = 0; i < pickResults.getNumber(); i++) { pdList.add(pickResults.getPickData(i)); } Collections.sort(pdList, new Comparator<PickData>() { @Override public int compare(PickData a, PickData b) { double ad = a.getClosestDistance(); double bd = b.getClosestDistance(); if (ad == bd) { return 0; } if (ad < bd) { return -1; } return 1; } }); //print(pdList); setTangible(null, null); pickedMesh = null; boolean touched = false; for (PickData pd : pdList) { if (isTangible(pd.getTargetMesh())) { final PickData pick = pd; pickedMesh = pd.getTargetMesh(); final Tangible topLevel = getTangible(pick.getTargetMesh()); setTangible(topLevel, pick); if (topLevel instanceof Touchable) { setPicked((Touchable) topLevel, pick); touched = true; return; } } } if (!touched) setPicked(null, null); } private void setTangible(Tangible t, PickData pick) { this.currentTangible = t; this.tangiblePick = pick; } private void setPicked(Touchable p, PickData pick) { // System.out.println("picked currentTouch=" + currentTouch + " , touchable=" + p); if (this.currentTouch == p) { if (p != null) { currentTouch.onTouching(pick); } } else { if (this.currentTouch != null) { currentTouch.onTouchStop(); } this.currentTouch = p; if (currentTouch != null) { p.onTouchStart(pick); } } } public static <C> C getParent(final Spatial target, Class<? extends C> c) { if (c.isInstance(target)) { return (C) target; } if (target.getParent() == null) { return null; } else { return getParent(target.getParent(), c); } } public static Tangible getTangible(final Spatial target) { Tangible t = getParent(target, Tangible.class); if (t == null) { return null; } if (t.isTangible()) { return t; } return null; } public static Zoomable getZoomable(final Object target) { Zoomable z = getParent((Spatial) target, Zoomable.class); if (z == null) { return null; } if (z.isZoomable()) { return z; } return null; } public PickResults updatePick(Ray3 pickRay) { pickResults.clear(); pickResults.setCheckDistance(true); PickingUtil.findPick(getRoot(), pickRay, pickResults); processPicks(pickResults); return pickResults; } protected void rotateCam(double dx, double dy, double dt) { dx = -dx; dy = -dy; //System.out.println("rotateCam: " + dx + " " + dy); ArdorCamera camera = getSpacetime().getCamera(); rcUp.set(camera.getCurrentUp()); rcLeft.set(camera.getCurrentLeft()); rcDir.set(camera.getCurrentDirection()); tempMatrix.fromAngleNormalAxis(firstPersonRotateSpeed * dx * dt, rcUp); tempMatrix.applyPost(rcLeft, tempVectorA); rcLeft.set(tempVectorA); tempMatrix.applyPost(rcDir, tempVectorA); rcDir.set(tempVectorA); tempMatrix.applyPost(rcUp, tempVectorA); rcUp.set(tempVectorA); tempMatrix.fromAngleNormalAxis(firstPersonRotateSpeed * dy * dt, rcLeft); tempMatrix.applyPost(rcDir, tempVectorA); rcDir.set(tempVectorA); rcDir.normalizeLocal(); camera.getTargetTarget().set(camera.getTargetPosition()).addLocal(rcDir); //camera.getTargetUp().set(up); } // protected void rotateCam(double dx, double dy, double dt) { // //TODO make camera.setTargetToCurrent() unnecessary - it produces a jerky movement // // dx = -dx; // dy = -dy; // // System.out.println("rotateCam: " + dx + " " + dy); // // ArdorCamera camera = getSpacetime().getCamera(); // // if (dx != 0) { // tempMatrix.fromAngleNormalAxis(firstPersonRotateSpeed * dx, _upAxis != null ? _upAxis : camera.getCurrentUp()); // tempMatrix.applyPost(camera.getCurrentLeft(), tempVectorA); // //camera.gettar setLeft(_workerStoreA); // tempMatrix.applyPost(camera.getCurrentDirection(), tempVectorA); // camera.getTargetTarget().set(camera.getTargetPosition()).addLocal(tempVectorA); // tempMatrix.applyPost(camera.getCurrentUp(), tempVectorA); // camera.getTargetUp().set(tempVectorA); // camera.setTargetToCurrent(); // } // // if (dy != 0) { // tempMatrix.fromAngleNormalAxis(firstPersonRotateSpeed * dy, camera.getCurrentLeft()); // tempMatrix.applyPost(camera.getCurrentLeft(), tempVectorA); // //camera.setLeft(_workerStoreA); // tempMatrix.applyPost(camera.getCurrentDirection(), tempVectorA); // camera.getTargetTarget().set(camera.getTargetPosition()).addLocal(tempVectorA); // tempMatrix.applyPost(camera.getCurrentUp(), tempVectorA); // camera.getTargetUp().set(tempVectorA); // camera.setTargetToCurrent(); // } // // } private boolean isTangible(Mesh targetMesh) { return getTangible(targetMesh) != null; } @Override public String toString() { //TODO add more pointer stats return ("pickedMesh=" + pickedMesh + ", currentTouch=" + currentTouch); } public Node getCurrentTouched() { if (currentTouch instanceof Node) { return ((Node) currentTouch); } return null; } public boolean isPressed(int button) { //TODO use a list of Button Boolean's if (button == 0) { return leftPressed; } return false; } }