/* * Copyright 2017 MovingBlocks * * 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 org.terasology.rendering.cameras; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import org.terasology.config.RenderingConfig; import org.terasology.math.MatrixUtils; import org.terasology.math.TeraMath; import org.terasology.math.geom.Matrix4f; import org.terasology.math.geom.Vector3f; import org.terasology.rendering.nui.layers.mainMenu.videoSettings.CameraSetting; import org.terasology.world.WorldProvider; import java.util.Deque; import java.util.LinkedList; import static org.lwjgl.opengl.GL11.GL_PROJECTION; import static org.lwjgl.opengl.GL11.glMatrixMode; /** * Simple default camera. */ public class PerspectiveCamera extends SubmersibleCamera { // Values used for smoothing private Deque<Vector3f> previousPositions = new LinkedList<>(); private Deque<Vector3f> previousViewingDirections = new LinkedList<>(); private float multiplier = 0.9f; private PerspectiveCameraSettings cameraSettings; private float bobbingRotationOffsetFactor; private float bobbingVerticalOffsetFactor; private float cachedBobbingRotationOffsetFactor; private float cachedBobbingVerticalOffsetFactor; private Vector3f tempRightVector = new Vector3f(); public PerspectiveCamera(WorldProvider worldProvider, RenderingConfig renderingConfig) { super(worldProvider, renderingConfig); this.cameraSettings = renderingConfig.getCameraSettings(); } @Override public boolean isBobbingAllowed() { return true; } @Override public void loadProjectionMatrix() { glMatrixMode(GL_PROJECTION); GL11.glLoadMatrix(MatrixUtils.matrixToFloatBuffer(getProjectionMatrix())); glMatrixMode(GL11.GL_MODELVIEW); } @Override public void loadModelViewMatrix() { glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadMatrix(MatrixUtils.matrixToFloatBuffer(getViewMatrix())); } @Override public void loadNormalizedModelViewMatrix() { glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadMatrix(MatrixUtils.matrixToFloatBuffer(getNormViewMatrix())); } @Override public void update(float deltaT) { applyCinematicEffect(); super.update(deltaT); updateMatrices(); } private void applyCinematicEffect() { previousPositions.addFirst(new Vector3f(position)); previousViewingDirections.addFirst(new Vector3f(viewingDirection)); CameraSetting cameraSetting = cameraSettings.getCameraSetting(); while (previousPositions.size() > cameraSetting.getSmoothingFrames()) { previousPositions.removeLast(); previousViewingDirections.removeLast(); } position.set(calculateVector(previousPositions)); viewingDirection.set(calculateVector(previousViewingDirections)); } private Vector3f calculateVector(Deque<Vector3f> vectors) { int i = 0; float x = 0; float y = 0; float z = 0; float factorMult = 0; for (Vector3f vector : vectors) { double factor = Math.pow(multiplier, i); factorMult += factor; x += vector.x * factor; y += vector.y * factor; z += vector.z * factor; i++; } return new Vector3f(x / factorMult, y / factorMult, z / factorMult); } @Override public void updateMatrices() { updateMatrices(activeFov); } @Override public void updateMatrices(float fov) { // Nothing to do... if (cachedPosition.equals(getPosition()) && cachedViewigDirection.equals(getViewingDirection()) && cachedBobbingRotationOffsetFactor == bobbingRotationOffsetFactor && cachedBobbingVerticalOffsetFactor == bobbingVerticalOffsetFactor && cachedFov == fov && cachedZFar == getzFar() && cachedZNear == getzNear() && cachedReflectionHeight == getReflectionHeight()) { return; } tempRightVector.cross(viewingDirection, up); tempRightVector.scale(bobbingRotationOffsetFactor); projectionMatrix = createPerspectiveProjectionMatrix(fov, getzNear(), getzFar()); viewMatrix = MatrixUtils.createViewMatrix(0f, bobbingVerticalOffsetFactor * 2.0f, 0f, viewingDirection.x, viewingDirection.y + bobbingVerticalOffsetFactor * 2.0f, viewingDirection.z, up.x + tempRightVector.x, up.y + tempRightVector.y, up.z + tempRightVector.z); normViewMatrix = MatrixUtils.createViewMatrix(0f, 0f, 0f, viewingDirection.x, viewingDirection.y, viewingDirection.z, up.x + tempRightVector.x, up.y + tempRightVector.y, up.z + tempRightVector.z); reflectionMatrix.setRow(0, 1.0f, 0.0f, 0.0f, 0.0f); reflectionMatrix.setRow(1, 0.0f, -1.0f, 0.0f, 2f * (-position.y + getReflectionHeight())); reflectionMatrix.setRow(2, 0.0f, 0.0f, 1.0f, 0.0f); reflectionMatrix.setRow(3, 0.0f, 0.0f, 0.0f, 1.0f); viewMatrixReflected.mul(viewMatrix, reflectionMatrix); reflectionMatrix.setRow(1, 0.0f, -1.0f, 0.0f, 0.0f); normViewMatrixReflected.mul(normViewMatrix, reflectionMatrix); viewProjectionMatrix = MatrixUtils.calcViewProjectionMatrix(viewMatrix, projectionMatrix); inverseProjectionMatrix.invert(projectionMatrix); inverseViewProjectionMatrix.invert(viewProjectionMatrix); // Used for dirty checks cachedPosition.set(getPosition()); cachedViewigDirection.set(getViewingDirection()); cachedBobbingVerticalOffsetFactor = bobbingVerticalOffsetFactor; cachedBobbingRotationOffsetFactor = bobbingRotationOffsetFactor; cachedFov = fov; cachedZNear = getzNear(); cachedZFar = getzFar(); cachedReflectionHeight = getReflectionHeight(); updateFrustum(); } public void setBobbingRotationOffsetFactor(float f) { bobbingRotationOffsetFactor = f; } public void setBobbingVerticalOffsetFactor(float f) { bobbingVerticalOffsetFactor = f; } // TODO: Move the dependency on LWJGL (Display) elsewhere private static Matrix4f createPerspectiveProjectionMatrix(float fov, float zNear, float zFar) { float aspectRatio = (float) Display.getWidth() / Display.getHeight(); float fovY = (float) (2 * Math.atan2(Math.tan(0.5 * fov * TeraMath.DEG_TO_RAD), aspectRatio)); return MatrixUtils.createPerspectiveProjectionMatrix(fovY, aspectRatio, zNear, zFar); } }