code: /*
   * Copyright (c) 2021 Callum Newlands
   *
   *     This program is free software: you can redistribute it and/or modify
   *     it under the terms of the GNU General Public License as published by
   *     the Free Software Foundation, either version 3 of the License, or
   *     (at your option) any later version.
   *
   *     The additional term of 7.b applies: Requiring preservation of specified
   *     reasonable legal notices or author attributions in that material or in the
   *     Appropriate Legal Notices displayed by works containing it
   *
   *     This program is distributed in the hope that it will be useful,
   *     but WITHOUT ANY WARRANTY; without even the implied warranty of
   *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *     GNU General Public License for more details.
   *
   *     You should have received a copy of the GNU General Public License
   *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  package rendering;
  
  import lombok.Getter;
  import org.joml.Matrix4f;
  import org.joml.Vector3f;
  import params.ParameterLoader;
  import params.Parameters;
  import utils.VectorUtils;
  
  @Getter
  public class Camera {
  
  	private static final Vector3f GLOBAL_UP = new Vector3f(0.0f, 1.0f, 0.0f);
  	private static final float MOVEMENT_SPEED = 7.0f;
  	private static final float MOUSE_SENSITIVITY = 0.1f;
  	private static final float MAX_PITCH = 89.0f;
  	private final Parameters parameters = ParameterLoader.getParameters();
  	private float yaw;
  	private float pitch;
  	private Vector3f position;
  	private Vector3f direction;
  	private Vector3f front;
  	private Vector3f right;
  	private Vector3f up;
  
  	public void setYaw(float yaw) {
  		this.yaw = yaw;
  	}
  
  	public void setPitch(float pitch) {
  		this.pitch = pitch;
  	}
  
  	public void setPosition(Vector3f position) {
  		this.position = position;
  	}
  
  	public void setDirection(Vector3f direction) {
  		this.direction = direction;
  	}
  
  	public Camera(final Vector3f position, final float yaw, final float pitch) {
  		this.yaw = yaw;
  		this.pitch = pitch;
  		this.position = position;
  		this.front = new Vector3f();
  		this.direction = new Vector3f();
  		updateVectorsFromAngles();
  	}
  
  	public Camera(final Vector3f position, final Vector3f direction) {
  		this.direction = direction.normalize();
  		this.pitch = (float) Math.toDegrees(Math.asin(this.direction.y));
  		this.yaw = (float) Math.toDegrees(Math.atan2(this.direction.z, this.direction.x));
  		this.position = position;
  		this.front = new Vector3f(this.direction.x, 0.0f, this.direction.z);
  		updateVectorsFromAngles();
  	}
  
  	public void updateVectorsFromAngles() {
  		this.direction.x = (float) Math.cos(Math.toRadians(yaw)) * (float) Math.cos(Math.toRadians(pitch));
  		this.direction.y = (float) Math.sin(Math.toRadians(pitch));
  		this.direction.z = (float) Math.sin(Math.toRadians(yaw)) * (float) Math.cos(Math.toRadians(pitch));
  		this.direction.normalize();
  		this.front = new Vector3f(direction.x, 0.0f, direction.z);
  		this.front.normalize();
  		this.right = VectorUtils.cross(front, GLOBAL_UP).normalize();
  		this.up = VectorUtils.cross(right, front).normalize();
  	}
  
  	public Matrix4f getViewMatrix() {
  		Vector3f target = VectorUtils.add(position, direction);
  		return (new Matrix4f()).lookAt(position, target, up);
  	}
  
  	public void move(final MovementDirection direction, final float deltaTime) {
  
  		final float velocity = MOVEMENT_SPEED * deltaTime;
  
  		switch (direction) {
  			case FORWARD:
  				position = VectorUtils.add(position, VectorUtils.multiply(velocity, front));
  				break;
  			case BACKWARD:
  				position = VectorUtils.subtract(position, VectorUtils.multiply(velocity, front));
  				break;
  			case LEFT:
  				position = VectorUtils.subtract(position, VectorUtils.multiply(velocity, right));
  				break;
  			case RIGHT:
  				position = VectorUtils.add(position, VectorUtils.multiply(velocity, right));
  				break;
  			case UP:
  				if (parameters.camera.verticalMovement) {
  					position = VectorUtils.add(position, VectorUtils.multiply(velocity, up));
  				}
  				break;
  			case DOWN:
  				if (parameters.camera.verticalMovement) {
  					position = VectorUtils.subtract(position, VectorUtils.multiply(velocity, up));
  				}
  				break;
  			default:
  		}
  	}
  
  	public void processMouseMovement(final float xOffset, final float yOffset) {
  		this.yaw += xOffset * MOUSE_SENSITIVITY;
  		this.pitch += yOffset * MOUSE_SENSITIVITY;
  
  		if (pitch > MAX_PITCH) {
  			pitch = MAX_PITCH;
  		} else if (pitch < -MAX_PITCH) {
  			pitch = -MAX_PITCH;
  		}
  
  		updateVectorsFromAngles();
  	}
  
  	public enum MovementDirection {
  		FORWARD,
  		BACKWARD,
  		LEFT,
  		RIGHT,
  		UP,
  		DOWN
  	}
  
  }
  