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; 18 19 import com.badlogic.gdx.Gdx; 20 import com.badlogic.gdx.Graphics; 21 import com.badlogic.gdx.Input; 22 import com.badlogic.gdx.graphics.g2d.Batch; 23 import com.badlogic.gdx.math.Frustum; 24 import com.badlogic.gdx.math.Matrix4; 25 import com.badlogic.gdx.math.Quaternion; 26 import com.badlogic.gdx.math.Vector3; 27 import com.badlogic.gdx.math.collision.Ray; 28 29 /** Base class for {@link OrthographicCamera} and {@link PerspectiveCamera}. 30 * @author mzechner */ 31 public abstract class Camera { 32 /** the position of the camera **/ 33 public final Vector3 position = new Vector3(); 34 /** the unit length direction vector of the camera **/ 35 public final Vector3 direction = new Vector3(0, 0, -1); 36 /** the unit length up vector of the camera **/ 37 public final Vector3 up = new Vector3(0, 1, 0); 38 39 /** the projection matrix **/ 40 public final Matrix4 projection = new Matrix4(); 41 /** the view matrix **/ 42 public final Matrix4 view = new Matrix4(); 43 /** the combined projection and view matrix **/ 44 public final Matrix4 combined = new Matrix4(); 45 /** the inverse combined projection and view matrix **/ 46 public final Matrix4 invProjectionView = new Matrix4(); 47 48 /** the near clipping plane distance, has to be positive **/ 49 public float near = 1; 50 /** the far clipping plane distance, has to be positive **/ 51 public float far = 100; 52 53 /** the viewport width **/ 54 public float viewportWidth = 0; 55 /** the viewport height **/ 56 public float viewportHeight = 0; 57 58 /** the frustum **/ 59 public final Frustum frustum = new Frustum(); 60 61 private final Vector3 tmpVec = new Vector3(); 62 private final Ray ray = new Ray(new Vector3(), new Vector3()); 63 64 /** Recalculates the projection and view matrix of this camera and the {@link Frustum} planes. Use this after you've manipulated 65 * any of the attributes of the camera. */ update()66 public abstract void update (); 67 68 /** Recalculates the projection and view matrix of this camera and the {@link Frustum} planes if <code>updateFrustum</code> is 69 * true. Use this after you've manipulated any of the attributes of the camera. */ update(boolean updateFrustum)70 public abstract void update (boolean updateFrustum); 71 72 /** Recalculates the direction of the camera to look at the point (x, y, z). This function assumes the up vector is normalized. 73 * @param x the x-coordinate of the point to look at 74 * @param y the y-coordinate of the point to look at 75 * @param z the z-coordinate of the point to look at */ lookAt(float x, float y, float z)76 public void lookAt (float x, float y, float z) { 77 tmpVec.set(x, y, z).sub(position).nor(); 78 if (!tmpVec.isZero()) { 79 float dot = tmpVec.dot(up); // up and direction must ALWAYS be orthonormal vectors 80 if (Math.abs(dot - 1) < 0.000000001f) { 81 // Collinear 82 up.set(direction).scl(-1); 83 } else if (Math.abs(dot + 1) < 0.000000001f) { 84 // Collinear opposite 85 up.set(direction); 86 } 87 direction.set(tmpVec); 88 normalizeUp(); 89 } 90 } 91 92 /** Recalculates the direction of the camera to look at the point (x, y, z). 93 * @param target the point to look at */ lookAt(Vector3 target)94 public void lookAt (Vector3 target) { 95 lookAt(target.x, target.y, target.z); 96 } 97 98 /** Normalizes the up vector by first calculating the right vector via a cross product between direction and up, and then 99 * recalculating the up vector via a cross product between right and direction. */ normalizeUp()100 public void normalizeUp () { 101 tmpVec.set(direction).crs(up).nor(); 102 up.set(tmpVec).crs(direction).nor(); 103 } 104 105 /** Rotates the direction and up vector of this camera by the given angle around the given axis. The direction and up vector 106 * will not be orthogonalized. 107 * 108 * @param angle the angle 109 * @param axisX the x-component of the axis 110 * @param axisY the y-component of the axis 111 * @param axisZ the z-component of the axis */ rotate(float angle, float axisX, float axisY, float axisZ)112 public void rotate (float angle, float axisX, float axisY, float axisZ) { 113 direction.rotate(angle, axisX, axisY, axisZ); 114 up.rotate(angle, axisX, axisY, axisZ); 115 } 116 117 /** Rotates the direction and up vector of this camera by the given angle around the given axis. The direction and up vector 118 * will not be orthogonalized. 119 * 120 * @param axis the axis to rotate around 121 * @param angle the angle */ rotate(Vector3 axis, float angle)122 public void rotate (Vector3 axis, float angle) { 123 direction.rotate(axis, angle); 124 up.rotate(axis, angle); 125 } 126 127 /** Rotates the direction and up vector of this camera by the given rotation matrix. The direction and up vector will not be 128 * orthogonalized. 129 * 130 * @param transform The rotation matrix */ rotate(final Matrix4 transform)131 public void rotate (final Matrix4 transform) { 132 direction.rot(transform); 133 up.rot(transform); 134 } 135 136 /** Rotates the direction and up vector of this camera by the given {@link Quaternion}. The direction and up vector will not be 137 * orthogonalized. 138 * 139 * @param quat The quaternion */ rotate(final Quaternion quat)140 public void rotate (final Quaternion quat) { 141 quat.transform(direction); 142 quat.transform(up); 143 } 144 145 /** Rotates the direction and up vector of this camera by the given angle around the given axis, with the axis attached to given 146 * point. The direction and up vector will not be orthogonalized. 147 * 148 * @param point the point to attach the axis to 149 * @param axis the axis to rotate around 150 * @param angle the angle */ rotateAround(Vector3 point, Vector3 axis, float angle)151 public void rotateAround (Vector3 point, Vector3 axis, float angle) { 152 tmpVec.set(point); 153 tmpVec.sub(position); 154 translate(tmpVec); 155 rotate(axis, angle); 156 tmpVec.rotate(axis, angle); 157 translate(-tmpVec.x, -tmpVec.y, -tmpVec.z); 158 } 159 160 /** Transform the position, direction and up vector by the given matrix 161 * 162 * @param transform The transform matrix */ transform(final Matrix4 transform)163 public void transform (final Matrix4 transform) { 164 position.mul(transform); 165 rotate(transform); 166 } 167 168 /** Moves the camera by the given amount on each axis. 169 * @param x the displacement on the x-axis 170 * @param y the displacement on the y-axis 171 * @param z the displacement on the z-axis */ translate(float x, float y, float z)172 public void translate (float x, float y, float z) { 173 position.add(x, y, z); 174 } 175 176 /** Moves the camera by the given vector. 177 * @param vec the displacement vector */ translate(Vector3 vec)178 public void translate (Vector3 vec) { 179 position.add(vec); 180 } 181 182 /** Function to translate a point given in screen coordinates to world space. It's the same as GLU gluUnProject, but does not 183 * rely on OpenGL. The x- and y-coordinate of vec are assumed to be in screen coordinates (origin is the top left corner, y 184 * pointing down, x pointing to the right) as reported by the touch methods in {@link Input}. A z-coordinate of 0 will return a 185 * point on the near plane, a z-coordinate of 1 will return a point on the far plane. This method allows you to specify the 186 * viewport position and dimensions in the coordinate system expected by {@link GL20#glViewport(int, int, int, int)}, with the 187 * origin in the bottom left corner of the screen. 188 * @param screenCoords the point in screen coordinates (origin top left) 189 * @param viewportX the coordinate of the bottom left corner of the viewport in glViewport coordinates. 190 * @param viewportY the coordinate of the bottom left corner of the viewport in glViewport coordinates. 191 * @param viewportWidth the width of the viewport in pixels 192 * @param viewportHeight the height of the viewport in pixels */ unproject(Vector3 screenCoords, float viewportX, float viewportY, float viewportWidth, float viewportHeight)193 public Vector3 unproject (Vector3 screenCoords, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { 194 float x = screenCoords.x, y = screenCoords.y; 195 x = x - viewportX; 196 y = Gdx.graphics.getHeight() - y - 1; 197 y = y - viewportY; 198 screenCoords.x = (2 * x) / viewportWidth - 1; 199 screenCoords.y = (2 * y) / viewportHeight - 1; 200 screenCoords.z = 2 * screenCoords.z - 1; 201 screenCoords.prj(invProjectionView); 202 return screenCoords; 203 } 204 205 /** Function to translate a point given in screen coordinates to world space. It's the same as GLU gluUnProject but does not 206 * rely on OpenGL. The viewport is assumed to span the whole screen and is fetched from {@link Graphics#getWidth()} and 207 * {@link Graphics#getHeight()}. The x- and y-coordinate of vec are assumed to be in screen coordinates (origin is the top left 208 * corner, y pointing down, x pointing to the right) as reported by the touch methods in {@link Input}. A z-coordinate of 0 209 * will return a point on the near plane, a z-coordinate of 1 will return a point on the far plane. 210 * @param screenCoords the point in screen coordinates */ unproject(Vector3 screenCoords)211 public Vector3 unproject (Vector3 screenCoords) { 212 unproject(screenCoords, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 213 return screenCoords; 214 } 215 216 /** Projects the {@link Vector3} given in world space to screen coordinates. It's the same as GLU gluProject with one small 217 * deviation: The viewport is assumed to span the whole screen. The screen coordinate system has its origin in the 218 * <b>bottom</b> left, with the y-axis pointing <b>upwards</b> and the x-axis pointing to the right. This makes it easily 219 * useable in conjunction with {@link Batch} and similar classes. */ project(Vector3 worldCoords)220 public Vector3 project (Vector3 worldCoords) { 221 project(worldCoords, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 222 return worldCoords; 223 } 224 225 /** Projects the {@link Vector3} given in world space to screen coordinates. It's the same as GLU gluProject with one small 226 * deviation: The viewport is assumed to span the whole screen. The screen coordinate system has its origin in the 227 * <b>bottom</b> left, with the y-axis pointing <b>upwards</b> and the x-axis pointing to the right. This makes it easily 228 * useable in conjunction with {@link Batch} and similar classes. This method allows you to specify the viewport position and 229 * dimensions in the coordinate system expected by {@link GL20#glViewport(int, int, int, int)}, with the origin in the bottom 230 * left corner of the screen. 231 * @param viewportX the coordinate of the bottom left corner of the viewport in glViewport coordinates. 232 * @param viewportY the coordinate of the bottom left corner of the viewport in glViewport coordinates. 233 * @param viewportWidth the width of the viewport in pixels 234 * @param viewportHeight the height of the viewport in pixels */ project(Vector3 worldCoords, float viewportX, float viewportY, float viewportWidth, float viewportHeight)235 public Vector3 project (Vector3 worldCoords, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { 236 worldCoords.prj(combined); 237 worldCoords.x = viewportWidth * (worldCoords.x + 1) / 2 + viewportX; 238 worldCoords.y = viewportHeight * (worldCoords.y + 1) / 2 + viewportY; 239 worldCoords.z = (worldCoords.z + 1) / 2; 240 return worldCoords; 241 } 242 243 /** Creates a picking {@link Ray} from the coordinates given in screen coordinates. It is assumed that the viewport spans the 244 * whole screen. The screen coordinates origin is assumed to be in the top left corner, its y-axis pointing down, the x-axis 245 * pointing to the right. The returned instance is not a new instance but an internal member only accessible via this function. 246 * @param viewportX the coordinate of the bottom left corner of the viewport in glViewport coordinates. 247 * @param viewportY the coordinate of the bottom left corner of the viewport in glViewport coordinates. 248 * @param viewportWidth the width of the viewport in pixels 249 * @param viewportHeight the height of the viewport in pixels 250 * @return the picking Ray. */ getPickRay(float screenX, float screenY, float viewportX, float viewportY, float viewportWidth, float viewportHeight)251 public Ray getPickRay (float screenX, float screenY, float viewportX, float viewportY, float viewportWidth, 252 float viewportHeight) { 253 unproject(ray.origin.set(screenX, screenY, 0), viewportX, viewportY, viewportWidth, viewportHeight); 254 unproject(ray.direction.set(screenX, screenY, 1), viewportX, viewportY, viewportWidth, viewportHeight); 255 ray.direction.sub(ray.origin).nor(); 256 return ray; 257 } 258 259 /** Creates a picking {@link Ray} from the coordinates given in screen coordinates. It is assumed that the viewport spans the 260 * whole screen. The screen coordinates origin is assumed to be in the top left corner, its y-axis pointing down, the x-axis 261 * pointing to the right. The returned instance is not a new instance but an internal member only accessible via this function. 262 * @return the picking Ray. */ getPickRay(float screenX, float screenY)263 public Ray getPickRay (float screenX, float screenY) { 264 return getPickRay(screenX, screenY, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 265 } 266 } 267