h-raylib-4.6.0.1: src/Raylib/Util/Camera.hs
{-# OPTIONS -Wall #-}
module Raylib.Util.Camera
( getCameraForward,
getCameraUp,
getCameraRight,
cameraMove,
cameraMoveForward,
cameraMoveUp,
cameraMoveRight,
cameraRotate,
cameraYaw,
cameraPitch,
cameraRoll,
getCameraViewMatrix,
getCameraProjectionMatrix,
)
where
import Raylib.Types (Camera, Camera3D (..), CameraProjection (CameraOrthographic, CameraPerspective), Matrix, Vector3 (..))
import Raylib.Util.Math (Vector (..), clamp, deg2Rad, matrixLookAt, matrixOrtho, matrixPerspective, vector3Angle, vector3CrossProduct, vector3RotateByAxisAngle)
-- | The camera's forward vector (normalized)
getCameraForward :: Camera -> Vector3
getCameraForward cam = vectorNormalize $ camera3D'target cam |-| camera3D'position cam
-- | The camera's up vector (normalized)
getCameraUp :: Camera -> Vector3
getCameraUp cam = vectorNormalize $ camera3D'up cam
-- | The camera's right vector (normalized)
getCameraRight :: Camera -> Vector3
getCameraRight cam = vector3CrossProduct (getCameraForward cam) (getCameraUp cam)
-- | Move the camera by a specific vector
cameraMove :: Camera -> Vector3 -> Camera
cameraMove cam dir =
cam {camera3D'position = camera3D'position cam |+| dir, camera3D'target = camera3D'target cam |+| dir}
-- | Move the camera in its forward direction
cameraMoveForward ::
Camera ->
-- | Distance to move
Float ->
-- | Move in world plane (i.e. no vertical movement if enabled)
Bool ->
Camera
cameraMoveForward cam distance moveInWorldPlane =
cameraMove cam (forward |* distance)
where
forward = if moveInWorldPlane then camForward {vector3'y = 0} else camForward
camForward = getCameraForward cam
-- | Move the camera in its up direction
cameraMoveUp ::
Camera ->
-- | Distance to move
Float ->
Camera
cameraMoveUp cam distance =
cameraMove cam (up |* distance)
where
up = getCameraUp cam
-- | Move the camera in its right direction
cameraMoveRight ::
Camera ->
-- | Distance to move
Float ->
-- | Move in world plane (i.e. no vertical movement if enabled)
Bool ->
Camera
cameraMoveRight cam distance moveInWorldPlane =
cameraMove cam (right |* distance)
where
right = if moveInWorldPlane then camRight {vector3'y = 0} else camRight
camRight = getCameraRight cam
-- | Rotate the camera using an axis and angle
cameraRotate ::
Camera ->
-- | Axis of rotation
Vector3 ->
-- | Angle to rotate by
Float ->
-- | Rotate around target (if false, the camera rotates around its position)
Bool ->
Camera
cameraRotate cam axis angle rotateAroundTarget =
cam
{ camera3D'position = if rotateAroundTarget then target |-| viewRot else pos,
camera3D'target = if rotateAroundTarget then target else pos |+| viewRot
}
where
viewVec = target |-| pos
viewRot = vector3RotateByAxisAngle viewVec axis angle
pos = camera3D'position cam
target = camera3D'target cam
-- | Rotate the camera around its up vector.
-- Yaw is "looking left and right".
cameraYaw ::
Camera ->
-- | Angle in radians
Float ->
-- | Rotate around target (if false, the camera rotates around its position)
Bool ->
Camera
cameraYaw cam angle rotateAroundTarget =
cam
{ camera3D'position = if rotateAroundTarget then target |-| viewRot else pos,
camera3D'target = if rotateAroundTarget then target else pos |+| viewRot
}
where
viewVec = target |-| pos
viewRot = vector3RotateByAxisAngle viewVec (getCameraUp cam) angle
pos = camera3D'position cam
target = camera3D'target cam
-- | Rotate the camera around its right vector.
-- Pitch is "looking up and down".
cameraPitch ::
Camera ->
-- | Angle in radians
Float ->
-- | Lock view (prevents camera overrotation, aka "somersaults")
Bool ->
-- | Rotate around target (if false, the camera rotates around its position)
Bool ->
-- | Rotate the camera's up vector to match the new pitch
Bool ->
Camera
cameraPitch cam angle lockView rotateAroundTarget rotateUp =
cam
{ camera3D'position = if rotateAroundTarget then target |-| viewRot else pos,
camera3D'target = if rotateAroundTarget then target else pos |+| viewRot,
camera3D'up = if not rotateUp then up else vector3RotateByAxisAngle up right angle'
}
where
angle' = if not lockView then angle else clamp angle maxAngleDown maxAngleUp
maxAngleUp = vector3Angle up viewVec - 0.001
maxAngleDown = (- vector3Angle (additiveInverse up) viewVec) - 0.001
viewVec = target |-| pos
viewRot = vector3RotateByAxisAngle viewVec right angle'
pos = camera3D'position cam
target = camera3D'target cam
up = getCameraUp cam
right = getCameraRight cam
-- | Rotates the camera around its forward vector.
-- Roll is "turning your head sideways to the left or right".
cameraRoll ::
Camera ->
-- | Angle in radians
Float ->
Camera
cameraRoll cam angle =
cam
{ camera3D'up = vector3RotateByAxisAngle up forward angle
}
where
forward = getCameraForward cam
up = getCameraUp cam
-- | View matrix from camera
getCameraViewMatrix :: Camera -> Matrix
getCameraViewMatrix cam = matrixLookAt (camera3D'position cam) (camera3D'target cam) (camera3D'up cam)
-- | Projection matrix from camera
getCameraProjectionMatrix ::
Camera ->
-- | Aspect ratio
Float ->
-- | Near clipping plane distance (recommended: 0.01)
Float ->
-- | Far clipping plane distance (recommended: 1000)
Float ->
Matrix
getCameraProjectionMatrix cam aspect near far =
case camera3D'projection cam of
CameraPerspective -> matrixPerspective (camera3D'fovy cam * deg2Rad) aspect near far
CameraOrthographic -> matrixOrtho (- right) right (- top) top near far
where
top = camera3D'fovy cam / 2
right = top * aspect