/******************************************************************************* * Copyright 2015 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.mygdx.game.utilities; import com.badlogic.gdx.math.*; import com.badlogic.gdx.math.collision.BoundingBox; import com.badlogic.gdx.math.collision.Ray; import com.mygdx.game.settings.GameSettings; /** * @author jsjolund */ public class CameraController { private final GhostCamera camera; // World data private final Plane worldGroundPlane = new Plane(Vector3.Y, 0); private final Vector3 worldDragCurrent = new Vector3(); private final Vector3 worldDragLast = new Vector3(); private final Vector3 worldGroundTarget = new Vector3(); // Temporary private final Ray ray = new Ray(); private final Vector3 tmp1 = new Vector3(); private final Vector3 tmp2 = new Vector3(); private final Quaternion deltaRotation = new Quaternion(); private float zoom = GameSettings.CAMERA_MAX_ZOOM; private BoundingBox worldBoundingBox; private Matrix4 followTarget; public CameraController(GhostCamera camera) { this.camera = camera; } public void setWorldBoundingBox(BoundingBox worldBoundingBox) { this.worldBoundingBox = new BoundingBox(worldBoundingBox); Vector3 min = new Vector3(); Vector3 max = new Vector3(); // Set height of bounding box to zero (y dimension) this.worldBoundingBox.getMax(max).y = 0; this.worldBoundingBox.getMin(min).y = 0; this.worldBoundingBox.set(min, max); ray.set(camera.targetPosition, camera.targetDirection); if (!Intersector.intersectRayBounds(ray, this.worldBoundingBox, worldGroundTarget)) { // TODO: What happens if the center of camera is not aimed at bounding box? // Probably move the camera until it is... } } public void setFollowTarget(Matrix4 followTarget) { this.followTarget = followTarget; } public void processDragPan(Ray dragCurrentRay, Ray lastDragProcessedRay) { followTarget = null; // TODO: // Can probably be optimized, but simply storing worldDragLast.set(worldDragCurrent) // caused jitter for some reason. Intersector.intersectRayPlane(dragCurrentRay, worldGroundPlane, worldDragCurrent); Intersector.intersectRayPlane(lastDragProcessedRay, worldGroundPlane, worldDragLast); tmp1.set(worldDragLast).sub(worldDragCurrent); tmp1.y = 0; ray.origin.set(camera.targetPosition).add(tmp1); ray.direction.set(camera.targetDirection); if (Intersector.intersectRayBoundsFast(ray, worldBoundingBox)) { camera.targetPosition.add(tmp1); worldGroundTarget.add(tmp1); } } public void processDragRotation(Vector2 cursorDelta) { tmp1.set(camera.targetDirection).crs(camera.targetUp).nor(); deltaRotation.setEulerAngles(cursorDelta.x, cursorDelta.y * tmp1.x, cursorDelta.y * tmp1.z); camera.rotateAround(worldGroundTarget, deltaRotation); } public void processZoom(float amount) { zoom += GameSettings.CAMERA_ZOOM_STEP * amount; zoom = MathUtils.clamp(zoom, GameSettings.CAMERA_MIN_ZOOM, GameSettings.CAMERA_MAX_ZOOM); camera.targetPosition.set(camera.targetDirection).nor().scl(-zoom).add(worldGroundTarget); } public void processKeyboardPan(Vector2 keysMoveDirection, float deltaTime) { followTarget = null; tmp1.set(camera.targetDirection).crs(camera.targetUp).scl(keysMoveDirection.x); tmp1.add(tmp2.set(camera.targetDirection).scl(keysMoveDirection.y)); tmp1.y = 0; tmp1.nor().scl(deltaTime * GameSettings.CAMERA_MAX_PAN_VELOCITY); ray.origin.set(camera.targetPosition).add(tmp1); ray.direction.set(camera.targetDirection); if (Intersector.intersectRayBoundsFast(ray, worldBoundingBox)) { camera.targetPosition.add(tmp1); worldGroundTarget.add(tmp1); } } public void processTouchDownLeft(Ray ray) { Intersector.intersectRayPlane(ray, worldGroundPlane, worldDragCurrent); worldDragLast.set(worldDragCurrent); } public void processTouchDownRight() { // ray.set(camera.position, camera.direction); // Intersector.intersectRayPlane(ray, worldGroundPlane, worldGroundTarget); } public void update() { if (followTarget != null) { camera.targetPosition.add(tmp1.set(followTarget.getTranslation(tmp2)).sub(worldGroundTarget)); worldGroundTarget.set(tmp2); } } }