X-Git-Url: http://www2.svjatoslav.eu/gitweb/?p=sixth-3d.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Feu%2Fsvjatoslav%2Fsixth%2Fe3d%2Fgui%2Fhumaninput%2FHIDInputTracker.java;fp=src%2Fmain%2Fjava%2Feu%2Fsvjatoslav%2Fsixth%2Fe3d%2Fgui%2Fhumaninput%2FHIDInputTracker.java;h=6015ee75957fcbae7baa113be9f5cce852a5dd73;hp=0000000000000000000000000000000000000000;hb=70ee733b25c56bed539b89ff5507ae0af842d68a;hpb=afdebf8f4f532ed5b456040c6c481f3cfbf8d236 diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/HIDInputTracker.java b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/HIDInputTracker.java new file mode 100755 index 0000000..6015ee7 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/HIDInputTracker.java @@ -0,0 +1,261 @@ +/* + * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 3 of the GNU Lesser General Public License + * or later as published by the Free Software Foundation. + * + */ + +package eu.svjatoslav.sixth.e3d.gui.humaninput; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.ViewRenderListener; + +import javax.swing.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HIDInputTracker implements + MouseMotionListener, KeyListener, MouseListener, MouseWheelListener, ViewRenderListener { + + /** + *
+     * Key is keyboard key code.
+     * Value is system milliseconds when key was pressed.
+     *
+     * So by reading the map one can determine currently pressed keys as well as duration.
+     * 
+ */ + private final Map pressedKeysToPressedTimeMap = new HashMap<>(); + private final List detectedMouseClicks = new ArrayList<>(); + private final List detectedKeyEvents = new ArrayList<>(); + private int wheelMovedDirection = 0; + private Point2D mouseDraggedDirection = new Point2D(); + private Point2D oldMouseCoordinatesWhenDragging; + private ViewPanel viewPanel; + private Point2D currentMouseLocation; + private boolean mouseMoved; + private boolean mouseWithinWindow = false; + + public HIDInputTracker(final ViewPanel viewPanel) { + this.viewPanel = viewPanel; + bind(viewPanel); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) { + boolean viewUpdateNeeded = handleKeyboardEvents(); + viewUpdateNeeded |= handleMouseClicksAndHover(viewPanel); + viewUpdateNeeded |= handleMouseDragging(); + viewUpdateNeeded |= handleMouseVerticalScrolling(); + return viewUpdateNeeded; + } + + private void bind(final JPanel panel) { + panel.addMouseMotionListener(this); + + panel.addKeyListener(this); + + panel.addMouseListener(this); + + panel.addMouseWheelListener(this); + } + + /** + * @return true if view needs to be repainted. + */ + private boolean handleKeyboardEvents() { + final UserInputHandler currentFocusOwner = viewPanel.getKeyboardFocusStack().getCurrentFocusOwner(); + ArrayList unprocessedKeyboardEvents = getUnprocessedKeyboardEvents(); + + return currentFocusOwner != null + && forwardKeyboardEventsToFocusOwner(currentFocusOwner, unprocessedKeyboardEvents); + } + + private ArrayList getUnprocessedKeyboardEvents() { + synchronized (detectedKeyEvents) { + ArrayList result = new ArrayList<>(detectedKeyEvents); + detectedKeyEvents.clear(); + return result; + } + } + + /** + * @return true if view update is needed. + */ + private boolean forwardKeyboardEventsToFocusOwner( + UserInputHandler currentFocusOwner, ArrayList keyEvents) { + boolean viewUpdateNeeded = false; + + for (KeyEvent keyEvent : keyEvents) + viewUpdateNeeded |= processKeyEvent(currentFocusOwner, keyEvent); + + return viewUpdateNeeded; + } + + private boolean processKeyEvent(UserInputHandler currentFocusOwner, KeyEvent keyEvent) { + switch (keyEvent.getID()) { + case KeyEvent.KEY_PRESSED: + return currentFocusOwner.keyPressed(keyEvent, viewPanel); + + case KeyEvent.KEY_RELEASED: + return currentFocusOwner.keyReleased(keyEvent, viewPanel); + } + return false; + } + + /** + * @return true if view needs to be repainted. + */ + private synchronized boolean handleMouseClicksAndHover(final ViewPanel viewPanel) { + MouseClick unprocessedMouseClick = findUnprocessedMouseClick(); + + if (unprocessedMouseClick != null) { + viewPanel.getRenderingContext().mouseClick = unprocessedMouseClick; + return false; + } else + return handleMouseHovering(viewPanel); + } + + private MouseClick findUnprocessedMouseClick() { + synchronized (detectedMouseClicks) { + if (detectedMouseClicks.isEmpty()) + return null; + + return detectedMouseClicks.remove(0); + } + } + + private boolean handleMouseHovering(ViewPanel viewPanel) { + if (currentMouseLocation != null) + // mouse click with button 0 amounts to mouse hovering event + viewPanel.getRenderingContext().mouseClick = new MouseClick(currentMouseLocation, 0); + + if (mouseMoved) { + mouseMoved = false; + return true; + } else + return false; + } + + boolean isKeyPressed(final int keyCode) { + return pressedKeysToPressedTimeMap.containsKey(keyCode); + } + + @Override + public void keyPressed(final KeyEvent evt) { + synchronized (detectedKeyEvents) { + pressedKeysToPressedTimeMap.put(evt.getKeyCode(), System.currentTimeMillis()); + detectedKeyEvents.add(evt); + } + } + + @Override + public void keyReleased(final KeyEvent evt) { + synchronized (detectedKeyEvents) { + pressedKeysToPressedTimeMap.remove(evt.getKeyCode()); + detectedKeyEvents.add(evt); + } + } + + @Override + public void keyTyped(final KeyEvent e) { + } + + @Override + public void mouseClicked(final MouseEvent e) { + synchronized (detectedMouseClicks) { + detectedMouseClicks.add(new MouseClick(e.getX(), e.getY(), e.getButton())); + } + } + + @Override + public void mouseDragged(final java.awt.event.MouseEvent evt) { + final Point2D mouseLocation = new Point2D(evt.getX(), evt.getY()); + + if (oldMouseCoordinatesWhenDragging == null) { + oldMouseCoordinatesWhenDragging = mouseLocation; + return; + } + + mouseDraggedDirection.add(mouseLocation.clone().subtract(oldMouseCoordinatesWhenDragging)); + + oldMouseCoordinatesWhenDragging = mouseLocation; + } + + @Override + public void mouseEntered(final MouseEvent e) { + mouseWithinWindow = true; + } + + @Override + public synchronized void mouseExited(final MouseEvent e) { + mouseWithinWindow = false; + currentMouseLocation = null; + } + + @Override + public synchronized void mouseMoved(final MouseEvent e) { + currentMouseLocation = new Point2D(e.getX(), e.getY()); + mouseMoved = true; + } + + @Override + public void mousePressed(final MouseEvent e) { + } + + @Override + public void mouseReleased(final java.awt.event.MouseEvent evt) { + oldMouseCoordinatesWhenDragging = null; + } + + @Override + public void mouseWheelMoved(final java.awt.event.MouseWheelEvent evt) { + wheelMovedDirection += evt.getWheelRotation(); + } + + /** + * @return true if view needs to be repainted. + */ + private boolean handleMouseVerticalScrolling() { + final Avatar avatar = viewPanel.getAvatar(); + final double actualAcceleration = 50 * avatar.avatarAcceleration * (1 + (avatar.getMovementSpeed() / 10)); + avatar.getMovementVector().y += (wheelMovedDirection * actualAcceleration); + avatar.enforceSpeedLimit(); + boolean repaintNeeded = wheelMovedDirection != 0; + wheelMovedDirection = 0; + return repaintNeeded; + } + + /** + * @return true if view needs to be repainted. + */ + private boolean handleMouseDragging() { + // TODO: need to detect whether user moved mouse or touch screen + + final Avatar avatar = viewPanel.getAvatar(); + // for mouse + avatar.setAngleXZ(avatar.getAngleXZ() - ((float) mouseDraggedDirection.x / 50)); + avatar.setAngleYZ(avatar.getAngleYZ() - ((float) mouseDraggedDirection.y / 50)); + + // for touch screen + // avatar.setAngleXZ(avatar.getAngleXZ() + ((float) + // mouseDraggedDirection.x / 50)); + // avatar.setAngleYZ(avatar.getAngleYZ() + ((float) + // mouseDraggedDirection.y / 50)); + + boolean viewUpdateNeeded = !mouseDraggedDirection.isZero(); + mouseDraggedDirection.zero(); + return viewUpdateNeeded; + } + +}