package electrosphere.controls; import org.joml.Quaterniond; import org.joml.Vector3d; import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.client.entity.crosshair.Crosshair; import electrosphere.collision.CollisionEngine; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.ui.events.MouseEvent; import electrosphere.util.math.SpatialMathUtils; /** * Handler for camera-related events and controls */ public class CameraHandler { //The first person camera perspective public static final int CAMERA_PERSPECTIVE_FIRST = 1; //The third person camera perspective public static final int CAMERA_PERSPECTIVE_THIRD = 3; //the horizontal mouse sensitivity float mouseSensitivityHorizontal = .1f; //the vertical mouse sensitivity float mouseSensitivityVertical = .08f; //the speed of the freecam float cameraSpeed; //the current yaw float yaw = 150; //the current pitch float pitch = 50; //the camera's rotation vector Vector3d cameraRotationVector = new Vector3d(); //the radial offset of the camera Vector3d radialOffset = new Vector3d(0,1,0); //if set to true, the camera will track the player's entity boolean trackPlayerEntity = true; //sets whether the camera handler should update the player's camera or not boolean update = true; /** * Handles a mouse event * @param event The mouse event */ public void handleMouseEvent(MouseEvent event){ if(Globals.controlHandler != null && !Globals.controlHandler.isMouseVisible()){ yaw = yaw + event.getDeltaX() * mouseSensitivityHorizontal; pitch = pitch + event.getDeltaY() * mouseSensitivityVertical; if (pitch >= 89.9f) { pitch = 89.9f; } if (pitch <= -89.9f) { pitch = -89.9f; } } this.updateGlobalCamera(); } /** * Updates the radial offset * @param offset the radial offset */ public void updateRadialOffset(Vector3d offset){ radialOffset = offset; } /** * Updates the global camera */ public void updateGlobalCamera(){ Globals.profiler.beginCpuSample("updateGlobalCamera"); if(update){ if(Globals.playerCamera != null){ cameraSpeed = 2.5f * (float)Globals.timekeeper.getMostRecentRawFrametime(); if(Crosshair.getCrosshairActive()){ Vector3d characterPos = EntityUtils.getPosition(Globals.playerEntity); Vector3d targetPos = Crosshair.getTargetPosition(); Vector3d diffed = new Vector3d(targetPos).sub(characterPos).mul(-1).normalize(); cameraRotationVector.set((float)diffed.x, 0.5f, (float)diffed.z).normalize(); yaw = (float)Math.toDegrees(Math.atan2(diffed.z, diffed.x)); CameraEntityUtils.setCameraPitch(Globals.playerCamera, pitch); CameraEntityUtils.setCameraYaw(Globals.playerCamera, yaw); } else { CameraEntityUtils.setCameraPitch(Globals.playerCamera, pitch); CameraEntityUtils.setCameraYaw(Globals.playerCamera, yaw); Quaterniond pitchQuat = new Quaterniond().fromAxisAngleDeg(SpatialMathUtils.getLeftVector(), -pitch); Quaterniond yawQuat = new Quaterniond().fromAxisAngleDeg(SpatialMathUtils.getUpVector(), -yaw); cameraRotationVector = pitchQuat.transform(SpatialMathUtils.getOriginVector()); cameraRotationVector = yawQuat.transform(cameraRotationVector); cameraRotationVector.normalize(); } if(trackPlayerEntity && Globals.playerEntity != null){ Vector3d entityPos = EntityUtils.getPosition(Globals.playerEntity); CameraEntityUtils.setCameraCenter(Globals.playerCamera, new Vector3d(entityPos).add(CameraEntityUtils.getOrbitalCameraRadialOffset(Globals.playerCamera))); } //update view matrix offset float xFactor = (float)Math.cos(yaw / 180.0f * Math.PI); float yFactor = (float)Math.sin(yaw / 180.0f * Math.PI); Vector3d radialOffset = CameraEntityUtils.getOrbitalCameraRadialOffset(Globals.playerCamera); Vector3d trueOffset = new Vector3d(radialOffset).mul(xFactor,1.0f,yFactor); CameraEntityUtils.setOrbitalCameraRadialOffset(Globals.playerCamera, trueOffset); cameraRotationVector.mul(CameraEntityUtils.getOrbitalCameraDistance(Globals.playerCamera)); CameraEntityUtils.setCameraEye(Globals.playerCamera, cameraRotationVector); //tell the server that we changed where we're looking, if we're in first person int perspectiveVal = CameraHandler.CAMERA_PERSPECTIVE_FIRST; if(Globals.controlHandler.cameraIsThirdPerson()){ perspectiveVal = CameraHandler.CAMERA_PERSPECTIVE_THIRD; } if(Globals.cameraHandler.getTrackPlayerEntity() && Globals.playerEntity != null){ Globals.clientConnection.queueOutgoingMessage( EntityMessage.constructupdateEntityViewDirMessage( Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId()), Globals.timekeeper.getNumberOfSimFramesElapsed(), perspectiveVal, yaw, pitch ) ); } //the view matrix Globals.viewMatrix = CameraEntityUtils.getCameraViewMatrix(Globals.playerCamera); //update the cursor on client side this.updatePlayerCursor(); } } Globals.profiler.endCpuSample(); } /** * Updates the position of the player's in world cursor */ private void updatePlayerCursor(){ CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); Entity camera = Globals.playerCamera; if( collisionEngine != null && camera != null && Globals.playerCursor != null ){ Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera)); Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera)); Vector3d cursorPos = collisionEngine.rayCastPosition(centerPos, new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE); if(cursorPos == null){ cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE)); } EntityUtils.getPosition(Globals.playerCursor).set(cursorPos); //clamp block cursor to nearest voxel cursorPos.set(PlayerCursor.clampPositionToNearestBlock(cursorPos)); EntityUtils.getPosition(Globals.playerBlockCursor).set(cursorPos); } } /** * Gets the yaw of the camera handler * @return the yaw */ public float getYaw(){ return yaw; } /** * Sets the yaw of the camera handler * @param yaw The yaw */ public void setYaw(double yaw){ this.yaw = (float)yaw; } /** * Gets the pitch of the camera handler * @return the pitch */ public float getPitch(){ return pitch; } /** * Sets the pitch of the camera handler * @param pitch The pitch */ public void setPitch(double pitch){ this.pitch = (float)pitch; } //set player tracking public void setTrackPlayerEntity(boolean track){ trackPlayerEntity = track; } //get trackPlayerEntity public boolean getTrackPlayerEntity(){ return trackPlayerEntity; } /** * Sets whether the camera should update with player input or not * @param update true to update with input, false otherwise */ public void setUpdate(boolean update){ this.update = update; } }