#ifndef RCAMERA_H
#define RCAMERA_H
#if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED)
#if defined(__TINYC__)
#define __declspec(x) __attribute__((x))
#endif
#define RLAPI __declspec(dllexport)
#elif defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport)
#endif
#endif
#ifndef RLAPI
#define RLAPI
#endif
#if defined(RCAMERA_STANDALONE)
#define CAMERA_CULL_DISTANCE_NEAR 0.01
#define CAMERA_CULL_DISTANCE_FAR 1000.0
#else
#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR
#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR
#endif
#if defined(RCAMERA_STANDALONE)
typedef struct Vector2 {
float x; float y; } Vector2;
typedef struct Vector3 {
float x; float y; float z; } Vector3;
typedef struct Matrix {
float m0, m4, m8, m12; float m1, m5, m9, m13; float m2, m6, m10, m14; float m3, m7, m11, m15; } Matrix;
typedef struct Camera3D {
Vector3 position; Vector3 target; Vector3 up; float fovy; int projection; } Camera3D;
typedef Camera3D Camera;
typedef enum {
CAMERA_PERSPECTIVE = 0, CAMERA_ORTHOGRAPHIC } CameraProjection;
typedef enum {
CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode;
#endif
#if defined(__cplusplus)
extern "C" { #endif
RLAPI Vector3 GetCameraForward(Camera *camera);
RLAPI Vector3 GetCameraUp(Camera *camera);
RLAPI Vector3 GetCameraRight(Camera *camera);
RLAPI void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane);
RLAPI void CameraMoveUp(Camera *camera, float distance);
RLAPI void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane);
RLAPI void CameraMoveToTarget(Camera *camera, float delta);
RLAPI void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget);
RLAPI void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp);
RLAPI void CameraRoll(Camera *camera, float angle);
RLAPI Matrix GetCameraViewMatrix(Camera *camera);
RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
#if defined(__cplusplus)
}
#endif
#endif
#if defined(RCAMERA_IMPLEMENTATION)
#include "raymath.h"
#define CAMERA_MOVE_SPEED 5.4f
#define CAMERA_ROTATION_SPEED 0.03f
#define CAMERA_PAN_SPEED 0.2f
#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f
#define CAMERA_ORBITAL_SPEED 0.5f
Vector3 GetCameraForward(Camera *camera)
{
return Vector3Normalize(Vector3Subtract(camera->target, camera->position));
}
Vector3 GetCameraUp(Camera *camera)
{
return Vector3Normalize(camera->up);
}
Vector3 GetCameraRight(Camera *camera)
{
Vector3 forward = GetCameraForward(camera);
Vector3 up = GetCameraUp(camera);
return Vector3Normalize(Vector3CrossProduct(forward, up));
}
void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane)
{
Vector3 forward = GetCameraForward(camera);
if (moveInWorldPlane)
{
forward.y = 0;
forward = Vector3Normalize(forward);
}
forward = Vector3Scale(forward, distance);
camera->position = Vector3Add(camera->position, forward);
camera->target = Vector3Add(camera->target, forward);
}
void CameraMoveUp(Camera *camera, float distance)
{
Vector3 up = GetCameraUp(camera);
up = Vector3Scale(up, distance);
camera->position = Vector3Add(camera->position, up);
camera->target = Vector3Add(camera->target, up);
}
void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane)
{
Vector3 right = GetCameraRight(camera);
if (moveInWorldPlane)
{
right.y = 0;
right = Vector3Normalize(right);
}
right = Vector3Scale(right, distance);
camera->position = Vector3Add(camera->position, right);
camera->target = Vector3Add(camera->target, right);
}
void CameraMoveToTarget(Camera *camera, float delta)
{
float distance = Vector3Distance(camera->position, camera->target);
distance += delta;
if (distance <= 0) distance = 0.001f;
Vector3 forward = GetCameraForward(camera);
camera->position = Vector3Add(camera->target, Vector3Scale(forward, -distance));
}
void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget)
{
Vector3 up = GetCameraUp(camera);
Vector3 targetPosition = Vector3Subtract(camera->target, camera->position);
targetPosition = Vector3RotateByAxisAngle(targetPosition, up, angle);
if (rotateAroundTarget)
{
camera->position = Vector3Subtract(camera->target, targetPosition);
}
else {
camera->target = Vector3Add(camera->position, targetPosition);
}
}
void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp)
{
Vector3 up = GetCameraUp(camera);
Vector3 targetPosition = Vector3Subtract(camera->target, camera->position);
if (lockView)
{
float maxAngleUp = Vector3Angle(up, targetPosition);
maxAngleUp -= 0.001f; if (angle > maxAngleUp) angle = maxAngleUp;
float maxAngleDown = Vector3Angle(Vector3Negate(up), targetPosition);
maxAngleDown *= -1.0f; maxAngleDown += 0.001f; if (angle < maxAngleDown) angle = maxAngleDown;
}
Vector3 right = GetCameraRight(camera);
targetPosition = Vector3RotateByAxisAngle(targetPosition, right, angle);
if (rotateAroundTarget)
{
camera->position = Vector3Subtract(camera->target, targetPosition);
}
else {
camera->target = Vector3Add(camera->position, targetPosition);
}
if (rotateUp)
{
camera->up = Vector3RotateByAxisAngle(camera->up, right, angle);
}
}
void CameraRoll(Camera *camera, float angle)
{
Vector3 forward = GetCameraForward(camera);
camera->up = Vector3RotateByAxisAngle(camera->up, forward, angle);
}
Matrix GetCameraViewMatrix(Camera *camera)
{
return MatrixLookAt(camera->position, camera->target, camera->up);
}
Matrix GetCameraProjectionMatrix(Camera *camera, float aspect)
{
if (camera->projection == CAMERA_PERSPECTIVE)
{
return MatrixPerspective(camera->fovy*DEG2RAD, aspect, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR);
}
else if (camera->projection == CAMERA_ORTHOGRAPHIC)
{
double top = camera->fovy/2.0;
double right = top*aspect;
return MatrixOrtho(-right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR);
}
return MatrixIdentity();
}
#if !defined(RCAMERA_STANDALONE)
void UpdateCamera(Camera *camera, int mode)
{
Vector2 mousePositionDelta = GetMouseDelta();
bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON));
bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool lockView = ((mode == CAMERA_FREE) || (mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool rotateUp = false;
float cameraMoveSpeed = CAMERA_MOVE_SPEED*GetFrameTime();
float cameraRotationSpeed = CAMERA_ROTATION_SPEED*GetFrameTime();
float cameraPanSpeed = CAMERA_PAN_SPEED*GetFrameTime();
float cameraOrbitalSpeed = CAMERA_ORBITAL_SPEED*GetFrameTime();
if (mode == CAMERA_CUSTOM) {}
else if (mode == CAMERA_ORBITAL)
{
Matrix rotation = MatrixRotate(GetCameraUp(camera), cameraOrbitalSpeed);
Vector3 view = Vector3Subtract(camera->position, camera->target);
view = Vector3Transform(view, rotation);
camera->position = Vector3Add(camera->target, view);
}
else
{
if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -cameraRotationSpeed, lockView, rotateAroundTarget, rotateUp);
if (IsKeyDown(KEY_UP)) CameraPitch(camera, cameraRotationSpeed, lockView, rotateAroundTarget, rotateUp);
if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -cameraRotationSpeed, rotateAroundTarget);
if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, cameraRotationSpeed, rotateAroundTarget);
if (IsKeyDown(KEY_Q)) CameraRoll(camera, -cameraRotationSpeed);
if (IsKeyDown(KEY_E)) CameraRoll(camera, cameraRotationSpeed);
if ((mode == CAMERA_FREE) && (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)))
{
const Vector2 mouseDelta = GetMouseDelta();
if (mouseDelta.x > 0.0f) CameraMoveRight(camera, cameraPanSpeed, moveInWorldPlane);
if (mouseDelta.x < 0.0f) CameraMoveRight(camera, -cameraPanSpeed, moveInWorldPlane);
if (mouseDelta.y > 0.0f) CameraMoveUp(camera, -cameraPanSpeed);
if (mouseDelta.y < 0.0f) CameraMoveUp(camera, cameraPanSpeed);
}
else
{
CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);
CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp);
}
if (IsKeyDown(KEY_W)) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
if (IsKeyDown(KEY_D)) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
if (IsGamepadAvailable(0))
{
CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);
CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
}
if (mode == CAMERA_FREE)
{
if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, cameraMoveSpeed);
if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -cameraMoveSpeed);
}
}
if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL) || (mode == CAMERA_FREE))
{
CameraMoveToTarget(camera, -GetMouseWheelMove());
if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraMoveToTarget(camera, 2.0f);
if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f);
}
}
#endif
void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom)
{
bool lockView = true;
bool rotateAroundTarget = false;
bool rotateUp = false;
bool moveInWorldPlane = true;
CameraPitch(camera, -rotation.y*DEG2RAD, lockView, rotateAroundTarget, rotateUp);
CameraYaw(camera, -rotation.x*DEG2RAD, rotateAroundTarget);
CameraRoll(camera, rotation.z*DEG2RAD);
CameraMoveForward(camera, movement.x, moveInWorldPlane);
CameraMoveRight(camera, movement.y, moveInWorldPlane);
CameraMoveUp(camera, movement.z);
CameraMoveToTarget(camera, zoom);
}
#endif