Home | History | Annotate | Download | only in utils
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.graphics.g3d.utils;
     18 
     19 import com.badlogic.gdx.Gdx;
     20 import com.badlogic.gdx.Input.Keys;
     21 import com.badlogic.gdx.InputAdapter;
     22 import com.badlogic.gdx.Input.Buttons;
     23 import com.badlogic.gdx.graphics.Camera;
     24 import com.badlogic.gdx.input.GestureDetector;
     25 import com.badlogic.gdx.math.MathUtils;
     26 import com.badlogic.gdx.math.Vector2;
     27 import com.badlogic.gdx.math.Vector3;
     28 
     29 public class CameraInputController extends GestureDetector {
     30 	/** The button for rotating the camera. */
     31 	public int rotateButton = Buttons.LEFT;
     32 	/** The angle to rotate when moved the full width or height of the screen. */
     33 	public float rotateAngle = 360f;
     34 	/** The button for translating the camera along the up/right plane */
     35 	public int translateButton = Buttons.RIGHT;
     36 	/** The units to translate the camera when moved the full width or height of the screen. */
     37 	public float translateUnits = 10f; // FIXME auto calculate this based on the target
     38 	/** The button for translating the camera along the direction axis */
     39 	public int forwardButton = Buttons.MIDDLE;
     40 	/** The key which must be pressed to activate rotate, translate and forward or 0 to always activate. */
     41 	public int activateKey = 0;
     42 	/** Indicates if the activateKey is currently being pressed. */
     43 	protected boolean activatePressed;
     44 	/** Whether scrolling requires the activeKey to be pressed (false) or always allow scrolling (true). */
     45 	public boolean alwaysScroll = true;
     46 	/** The weight for each scrolled amount. */
     47 	public float scrollFactor = -0.1f;
     48 	/** World units per screen size */
     49 	public float pinchZoomFactor = 10f;
     50 	/** Whether to update the camera after it has been changed. */
     51 	public boolean autoUpdate = true;
     52 	/** The target to rotate around. */
     53 	public Vector3 target = new Vector3();
     54 	/** Whether to update the target on translation */
     55 	public boolean translateTarget = true;
     56 	/** Whether to update the target on forward */
     57 	public boolean forwardTarget = true;
     58 	/** Whether to update the target on scroll */
     59 	public boolean scrollTarget = false;
     60 	public int forwardKey = Keys.W;
     61 	protected boolean forwardPressed;
     62 	public int backwardKey = Keys.S;
     63 	protected boolean backwardPressed;
     64 	public int rotateRightKey = Keys.A;
     65 	protected boolean rotateRightPressed;
     66 	public int rotateLeftKey = Keys.D;
     67 	protected boolean rotateLeftPressed;
     68 	/** The camera. */
     69 	public Camera camera;
     70 	/** The current (first) button being pressed. */
     71 	protected int button = -1;
     72 
     73 	private float startX, startY;
     74 	private final Vector3 tmpV1 = new Vector3();
     75 	private final Vector3 tmpV2 = new Vector3();
     76 
     77 	protected static class CameraGestureListener extends GestureAdapter {
     78 		public CameraInputController controller;
     79 		private float previousZoom;
     80 
     81 		@Override
     82 		public boolean touchDown (float x, float y, int pointer, int button) {
     83 			previousZoom = 0;
     84 			return false;
     85 		}
     86 
     87 		@Override
     88 		public boolean tap (float x, float y, int count, int button) {
     89 			return false;
     90 		}
     91 
     92 		@Override
     93 		public boolean longPress (float x, float y) {
     94 			return false;
     95 		}
     96 
     97 		@Override
     98 		public boolean fling (float velocityX, float velocityY, int button) {
     99 			return false;
    100 		}
    101 
    102 		@Override
    103 		public boolean pan (float x, float y, float deltaX, float deltaY) {
    104 			return false;
    105 		}
    106 
    107 		@Override
    108 		public boolean zoom (float initialDistance, float distance) {
    109 			float newZoom = distance - initialDistance;
    110 			float amount = newZoom - previousZoom;
    111 			previousZoom = newZoom;
    112 			float w = Gdx.graphics.getWidth(), h = Gdx.graphics.getHeight();
    113 			return controller.pinchZoom(amount / ((w > h) ? h : w));
    114 		}
    115 
    116 		@Override
    117 		public boolean pinch (Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
    118 			return false;
    119 		}
    120 	};
    121 
    122 	protected final CameraGestureListener gestureListener;
    123 
    124 	protected CameraInputController (final CameraGestureListener gestureListener, final Camera camera) {
    125 		super(gestureListener);
    126 		this.gestureListener = gestureListener;
    127 		this.gestureListener.controller = this;
    128 		this.camera = camera;
    129 	}
    130 
    131 	public CameraInputController (final Camera camera) {
    132 		this(new CameraGestureListener(), camera);
    133 	}
    134 
    135 	public void update () {
    136 		if (rotateRightPressed || rotateLeftPressed || forwardPressed || backwardPressed) {
    137 			final float delta = Gdx.graphics.getDeltaTime();
    138 			if (rotateRightPressed) camera.rotate(camera.up, -delta * rotateAngle);
    139 			if (rotateLeftPressed) camera.rotate(camera.up, delta * rotateAngle);
    140 			if (forwardPressed) {
    141 				camera.translate(tmpV1.set(camera.direction).scl(delta * translateUnits));
    142 				if (forwardTarget) target.add(tmpV1);
    143 			}
    144 			if (backwardPressed) {
    145 				camera.translate(tmpV1.set(camera.direction).scl(-delta * translateUnits));
    146 				if (forwardTarget) target.add(tmpV1);
    147 			}
    148 			if (autoUpdate) camera.update();
    149 		}
    150 	}
    151 
    152 	private int touched;
    153 	private boolean multiTouch;
    154 
    155 	@Override
    156 	public boolean touchDown (int screenX, int screenY, int pointer, int button) {
    157 		touched |= (1 << pointer);
    158 		multiTouch = !MathUtils.isPowerOfTwo(touched);
    159 		if (multiTouch)
    160 			this.button = -1;
    161 		else if (this.button < 0 && (activateKey == 0 || activatePressed)) {
    162 			startX = screenX;
    163 			startY = screenY;
    164 			this.button = button;
    165 		}
    166 		return super.touchDown(screenX, screenY, pointer, button) || (activateKey == 0 || activatePressed);
    167 	}
    168 
    169 	@Override
    170 	public boolean touchUp (int screenX, int screenY, int pointer, int button) {
    171 		touched &= -1 ^ (1 << pointer);
    172 		multiTouch = !MathUtils.isPowerOfTwo(touched);
    173 		if (button == this.button) this.button = -1;
    174 		return super.touchUp(screenX, screenY, pointer, button) || activatePressed;
    175 	}
    176 
    177 	protected boolean process (float deltaX, float deltaY, int button) {
    178 		if (button == rotateButton) {
    179 			tmpV1.set(camera.direction).crs(camera.up).y = 0f;
    180 			camera.rotateAround(target, tmpV1.nor(), deltaY * rotateAngle);
    181 			camera.rotateAround(target, Vector3.Y, deltaX * -rotateAngle);
    182 		} else if (button == translateButton) {
    183 			camera.translate(tmpV1.set(camera.direction).crs(camera.up).nor().scl(-deltaX * translateUnits));
    184 			camera.translate(tmpV2.set(camera.up).scl(-deltaY * translateUnits));
    185 			if (translateTarget) target.add(tmpV1).add(tmpV2);
    186 		} else if (button == forwardButton) {
    187 			camera.translate(tmpV1.set(camera.direction).scl(deltaY * translateUnits));
    188 			if (forwardTarget) target.add(tmpV1);
    189 		}
    190 		if (autoUpdate) camera.update();
    191 		return true;
    192 	}
    193 
    194 	@Override
    195 	public boolean touchDragged (int screenX, int screenY, int pointer) {
    196 		boolean result = super.touchDragged(screenX, screenY, pointer);
    197 		if (result || this.button < 0) return result;
    198 		final float deltaX = (screenX - startX) / Gdx.graphics.getWidth();
    199 		final float deltaY = (startY - screenY) / Gdx.graphics.getHeight();
    200 		startX = screenX;
    201 		startY = screenY;
    202 		return process(deltaX, deltaY, button);
    203 	}
    204 
    205 	@Override
    206 	public boolean scrolled (int amount) {
    207 		return zoom(amount * scrollFactor * translateUnits);
    208 	}
    209 
    210 	public boolean zoom (float amount) {
    211 		if (!alwaysScroll && activateKey != 0 && !activatePressed) return false;
    212 		camera.translate(tmpV1.set(camera.direction).scl(amount));
    213 		if (scrollTarget) target.add(tmpV1);
    214 		if (autoUpdate) camera.update();
    215 		return true;
    216 	}
    217 
    218 	protected boolean pinchZoom (float amount) {
    219 		return zoom(pinchZoomFactor * amount);
    220 	}
    221 
    222 	@Override
    223 	public boolean keyDown (int keycode) {
    224 		if (keycode == activateKey) activatePressed = true;
    225 		if (keycode == forwardKey)
    226 			forwardPressed = true;
    227 		else if (keycode == backwardKey)
    228 			backwardPressed = true;
    229 		else if (keycode == rotateRightKey)
    230 			rotateRightPressed = true;
    231 		else if (keycode == rotateLeftKey) rotateLeftPressed = true;
    232 		return false;
    233 	}
    234 
    235 	@Override
    236 	public boolean keyUp (int keycode) {
    237 		if (keycode == activateKey) {
    238 			activatePressed = false;
    239 			button = -1;
    240 		}
    241 		if (keycode == forwardKey)
    242 			forwardPressed = false;
    243 		else if (keycode == backwardKey)
    244 			backwardPressed = false;
    245 		else if (keycode == rotateRightKey)
    246 			rotateRightPressed = false;
    247 		else if (keycode == rotateLeftKey) rotateLeftPressed = false;
    248 		return false;
    249 	}
    250 }
    251