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