/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. ******************************************************************************/ package com.badlogic.gdx.graphics; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Graphics; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Frustum; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.collision.Ray; /** * Base class for {@link OrthographicCamera} and {@link PerspectiveCamera}. * * @author mzechner */ public abstract class Camera { /** the position of the camera **/ public final Vector3 position = new Vector3(); /** the unit length direction vector of the camera **/ public final Vector3 direction = new Vector3(0, 0, -1); /** the unit length up vector of the camera **/ public final Vector3 up = new Vector3(0, 1, 0); /** the projection matrix **/ public final Matrix4 projection = new Matrix4(); /** the view matrix **/ public final Matrix4 view = new Matrix4(); /** the combined projection and view matrix **/ public final Matrix4 combined = new Matrix4(); /** the inverse combined projection and view matrix **/ public final Matrix4 invProjectionView = new Matrix4(); /** the near clipping plane distance, has to be positive **/ public float near = 1; /** the far clipping plane distance, has to be positive **/ public float far = 100; /** the viewport width **/ public float viewportWidth = 0; /** the viewport height **/ public float viewportHeight = 0; /** the frustum **/ public final Frustum frustum = new Frustum(); private final Matrix4 tmpMat = new Matrix4(); private final Vector3 tmpVec = new Vector3(); /** * Recalculates the projection and view matrix of this camera and the {@link Frustum} planes. Use this after you've * manipulated any of the attributes of the camera. */ public abstract void update(); /** * Recalculates the projection and view matrix of this camera and the {@link Frustum} planes if * <code>updateFrustum</code> is true. Use this after you've manipulated any of the attributes of the camera. */ public abstract void update(boolean updateFrustum); /** * Sets the current projection and model-view matrix of this camera. Only works with {@link GL10} and {@link GL11} * of course. The parameter is there to remind you that it does not work with GL20. Make sure to call * {@link #update()} before calling this method so all matrices are up to date. * * @param gl * the GL10 or GL11 instance. */ public void apply(GL10 gl) { gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadMatrixf(projection.val, 0); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadMatrixf(view.val, 0); } /** * Recalculates the direction of the camera to look at the point (x, y, z). * * @param x * the x-coordinate of the point to look at * @param y * the x-coordinate of the point to look at * @param z * the x-coordinate of the point to look at */ public void lookAt(float x, float y, float z) { direction.set(x, y, z).sub(position).nor(); } /** * Normalizes the up vector by first calculating the right vector via a cross product between direction and up, and * then recalculating the up vector via a cross product between right and direction. */ final Vector3 right = new Vector3(); public void normalizeUp() { right.set(direction).crs(up).nor(); up.set(right).crs(direction).nor(); } /** * Rotates the direction and up vector of this camera by the given angle around the given axis. The direction and up * vector will not be orthogonalized. * * @param angle * the angle * @param axisX * the x-component of the axis * @param axisY * the y-component of the axis * @param axisZ * the z-component of the axis */ public void rotate(float angle, float axisX, float axisY, float axisZ) { rotate(tmpVec.set(axisX, axisY, axisZ), angle); } /** * Rotates the direction and up vector of this camera by the given angle around the given axis. The direction and up * vector will not be orthogonalized. * * @param axis * @param angle * the angle */ public void rotate(Vector3 axis, float angle) { tmpMat.setToRotation(axis, angle); direction.mul(tmpMat).nor(); up.mul(tmpMat).nor(); } /** * Rotates the direction and up vector of this camera by the given angle around the given axis, with the axis * attached to given point. The direction and up vector will not be orthogonalized. * * @param point * @param axis * @param angle * the angle */ public void rotateAround(Vector3 point, Vector3 axis, float angle) { tmpVec.set(point); tmpVec.sub(position); translate(tmpVec); rotate(axis, angle); tmpVec.rotate(axis, angle); translate(-tmpVec.x, -tmpVec.y, -tmpVec.z); } /** * Moves the camera by the given amount on each axis. * * @param x * the displacement on the x-axis * @param y * the displacement on the y-axis * @param z * the displacement on the z-axis */ public void translate(float x, float y, float z) { position.add(x, y, z); } /** * Moves the camera by the given vector. * * @param vec * the displacement vector */ public void translate(Vector3 vec) { position.add(vec); } /** * Function to translate a point given in window (or window) coordinates to world space. It's the same as * {@link GLU#gluUnProject(float, float, float, float[], int, float[], int, int[], int, float[], int)} but does not * rely on OpenGL. The x- and y-coordinate of vec are assumed to be in window coordinates (origin is the top left * corner, y pointing down, x pointing to the right) as reported by the touch methods in {@link Input}. A * z-coordinate of 0 will return a 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 viewport position and dimensions in the coordinate system expected * by {@link GLCommon#glViewport(int, int, int, int)}, with the origin in the bottom left corner of the screen. * * @param vec * the point in window coordinates (origin top left) * @param viewportX * the coordinate of the top left corner of the viewport in glViewport coordinates (origin bottom left) * @param viewportY * the coordinate of the top left corner of the viewport in glViewport coordinates (origin bottom left) * @param viewportWidth * the width of the viewport in pixels * @param viewportHeight * the height of the viewport in pixels */ public void unproject(Vector3 vec, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { float x = vec.x, y = vec.y; x = x - viewportX; y = Gdx.graphics.getHeight() - y - 1; y = y - viewportY; vec.x = (2 * x) / viewportWidth - 1; vec.y = (2 * y) / viewportHeight - 1; vec.z = 2 * vec.z - 1; vec.prj(invProjectionView); } /** * Function to translate a point given in window (or window) coordinates to world space. It's the same as * {@link GLU#gluUnProject(float, float, float, float[], int, float[], int, int[], int, float[], int)} but does not * rely on OpenGL. The viewport is assumed to span the whole screen and is fetched from {@link Graphics#getWidth()} * and {@link Graphics#getHeight()}. The x- and y-coordinate of vec are assumed to be in window coordinates (origin * is the top left corner, y pointing down, x pointing to the right) as reported by the touch methods in * {@link Input}. A z-coordinate of 0 will return a point on the near plane, a z-coordinate of 1 will return a point * on the far plane. * * @param vec * the point in window coordinates */ public void unproject(Vector3 vec) { unproject(vec, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } /** * Projects the {@link Vector3} given in object/world space to window coordinates. It's the same as * {@link GLU#gluProject(float, float, float, float[], int, float[], int, int[], int, float[], int)} with one small * deviation: The viewport is assumed to span the whole screen. The window coordinate system has its origin in the * <b>bottom</b> left, with the y-axis pointing <b>upwards</b> and the x-axis pointing to the right. This makes it * easily useable in conjunction with {@link SpriteBatch} and similar classes. * * @param vec * the position in object/world space. */ public void project(Vector3 vec) { project(vec, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } /** * Projects the {@link Vector3} given in object/world space to window coordinates. It's the same as * {@link GLU#gluProject(float, float, float, float[], int, float[], int, int[], int, float[], int)} with one small * deviation: The viewport is assumed to span the whole screen. The window coordinate system has its origin in the * <b>bottom</b> left, with the y-axis pointing <b>upwards</b> and the x-axis pointing to the right. This makes it * easily useable in conjunction with {@link SpriteBatch} and similar classes. This method allows you to specify the * viewport position and dimensions in the coordinate system expected by * {@link GLCommon#glViewport(int, int, int, int)}, with the origin in the bottom left corner of the screen. * * @param vec * the point in object/world space * @param viewportX * the coordinate of the top left corner of the viewport in glViewport coordinates (origin bottom left) * @param viewportY * the coordinate of the top left corner of the viewport in glViewport coordinates (origin bottom left) * @param viewportWidth * the width of the viewport in pixels * @param viewportHeight * the height of the viewport in pixels */ public void project(Vector3 vec, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { vec.prj(combined); vec.x = viewportWidth * (vec.x + 1) / 2 + viewportX; vec.y = viewportHeight * (vec.y + 1) / 2 + viewportY; vec.z = (vec.z + 1) / 2; } final Ray ray = new Ray(new Vector3(), new Vector3()); /** * Creates a picking {@link Ray} from the coordinates given in window coordinates. It is assumed that the viewport * spans the whole screen. The window coordinates origin is assumed to be in the top left corner, its y-axis * pointing down, the x-axis pointing to the right. The returned instance is not a new instance but an internal * member only accessible via this function. * * @param x * the x-coordinate in window coordinates. * @param y * the y-coordinate in window coordinates. * @return the picking Ray. */ public Ray getPickRay(float x, float y, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { unproject(ray.origin.set(x, y, 0), viewportX, viewportY, viewportWidth, viewportHeight); unproject(ray.direction.set(x, y, 1), viewportX, viewportY, viewportWidth, viewportHeight); ray.direction.sub(ray.origin).nor(); return ray; } /** * Creates a picking {@link Ray} from the coordinates given in window coordinates. It is assumed that the viewport * spans the whole screen. The window coordinates origin is assumed to be in the top left corner, its y-axis * pointing down, the x-axis pointing to the right. The returned instance is not a new instance but an internal * member only accessible via this function. * * @param x * the x-coordinate in window coordinates. * @param y * the y-coordinate in window coordinates. * @return the picking Ray. */ public Ray getPickRay(float x, float y) { return getPickRay(x, y, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } }