From b25cdf7f7dcfef070ba4eef27995532ed7d86c04 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 4 Feb 2015 20:23:43 +0100 Subject: [PATCH] Added support for multiple camera modes --- src/core.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/raylib.h | 7 +- 2 files changed, 286 insertions(+), 18 deletions(-) diff --git a/src/core.c b/src/core.c index efff6206..869b7e95 100644 --- a/src/core.c +++ b/src/core.c @@ -94,6 +94,45 @@ //---------------------------------------------------------------------------------- #define MAX_TOUCH_POINTS 256 +// CAMERA_GENERIC +#define CAMERA_SCROLL_SENSITIVITY 1.5 + +// FREE_CAMERA +#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 +#define FREE_CAMERA_DISTANCE_CLAMP 0.3 +#define FREE_CAMERA_MIN_CLAMP 85 +#define FREE_CAMERA_MAX_CLAMP -85 +#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 +#define FREE_CAMERA_PANNING_DIVIDER 5.1 + +// ORBITAL_CAMERA +#define ORBITAL_CAMERA_SPEED 0.01 + +// FIRST_PERSON +#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003 +#define FIRST_PERSON_FOCUS_DISTANCE 25 +#define FIRST_PERSON_MIN_CLAMP 5 +#define FIRST_PERSON_MAX_CLAMP -85 + +#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0 +#define FIRST_PERSON_STEP_DIVIDER 30.0 +#define FIRST_PERSON_WAVING_DIVIDER 200.0 + +#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85 + +// THIRD_PERSON +#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003 +#define THIRD_PERSON_DISTANCE_CLAMP 1.2 +#define THIRD_PERSON_MIN_CLAMP 5 +#define THIRD_PERSON_MAX_CLAMP -85 +#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 } + +// PLAYER (used by camera) +#define PLAYER_WIDTH 0.4 +#define PLAYER_HEIGHT 0.9 +#define PLAYER_DEPTH 0.4 +#define PLAYER_MOVEMENT_DIVIDER 20.0 + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -206,9 +245,19 @@ static double targetTime = 0.0; // Desired time for one frame, if 0 static char configFlags = 0; static bool showLogo = false; -static bool customCamera = true; -//static int cameraMode = CUSTOM; // FREE, FIRST_PERSON, THIRD_PERSON - +// Camera variables +static int cameraMode = CAMERA_CUSTOM; +static Camera currentCamera; +static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}}; +static Vector2 cameraAngle = { 0, 0 }; +static float cameraTargetDistance = 5; +static Vector2 cameraMousePosition = { 0, 0 }; +static Vector2 cameraMouseVariation = { 0, 0 }; +static int cameraMovementCounter = 0; +static bool cameraUseGravity = true; +static Vector3 cameraPosition = { 2, 0, 2 }; // Player + +// Shaders variables static bool enabledPostpro = false; static unsigned int fboShader = 0; @@ -261,6 +310,8 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event); // static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands #endif +static void ProcessCamera(Camera *camera); + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- @@ -479,6 +530,9 @@ void BeginDrawing(void) updateTime = currentTime - previousTime; previousTime = currentTime; + // Calculate camera + if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera); + if (enabledPostpro) rlEnableFBO(); rlClearScreenBuffers(); @@ -543,15 +597,11 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (MODELVIEW) // Setup Camera view - if (customCamera) - { - Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up); - rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) - } - else - { - // TODO: Add support for multiple automatic camera modes - } + if (cameraMode == CAMERA_CUSTOM) currentCamera = camera; + else currentCamera = internalCamera; + + Matrix matLookAt = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) } // Ends 3D mode and returns to default 2D orthographic mode @@ -636,7 +686,7 @@ Color Fade(Color color, float alpha) // Enable some window configurations (SetWindowFlags()?) // TODO: Review function name and usage -void SetupFlags(char flags) +void SetConfigFlags(char flags) { configFlags = flags; @@ -650,6 +700,11 @@ void ShowLogo(void) showLogo = true; } +void SetCameraMode(int mode) +{ + cameraMode = mode; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- @@ -2019,13 +2074,13 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) if (widthRatio <= heightRatio) { renderWidth = displayWidth; - renderHeight = (int)((float)screenHeight*widthRatio); + renderHeight = (int)round((float)screenHeight*widthRatio); renderOffsetX = 0; renderOffsetY = (displayHeight - renderHeight); } else { - renderWidth = (int)((float)screenWidth*heightRatio); + renderWidth = (int)round((float)screenWidth*heightRatio); renderHeight = displayHeight; renderOffsetX = (displayWidth - renderWidth); renderOffsetY = 0; @@ -2055,13 +2110,13 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) if (displayRatio <= screenRatio) { renderWidth = screenWidth; - renderHeight = (int)((float)screenWidth/displayRatio); + renderHeight = (int)round((float)screenWidth/displayRatio); renderOffsetX = 0; renderOffsetY = (renderHeight - screenHeight); } else { - renderWidth = (int)((float)screenHeight*displayRatio); + renderWidth = (int)round((float)screenHeight*displayRatio); renderHeight = screenHeight; renderOffsetX = (renderWidth - screenWidth); renderOffsetY = 0; @@ -2202,3 +2257,211 @@ static void LogoAnimation(void) showLogo = false; // Prevent for repeating when reloading window (Android) } +// Process desired camera mode and controls +static void ProcessCamera(Camera *camera) +{ + // Mouse movement detection + if (fullscreen) + { + if (GetMousePosition().x < 100) SetMousePosition((Vector2){ screenWidth - 100, GetMousePosition().y}); + else if (GetMousePosition().y < 100) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - 100}); + else if (GetMousePosition().x > screenWidth - 100) SetMousePosition((Vector2) { 100, GetMousePosition().y}); + else if (GetMousePosition().y > screenHeight - 100) SetMousePosition((Vector2){ GetMousePosition().x, 100}); + else + { + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + } + else + { + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + + cameraMousePosition = GetMousePosition(); + + // Support for multiple automatic camera modes + switch (cameraMode) + { + case CAMERA_FREE: + { + // Pass to orbiting camera + if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_CLAMP; + + if (IsKeyDown(KEY_LEFT_ALT)) + { + if (IsKeyDown(KEY_LEFT_CONTROL)) + { + // Camera smooth zoom + if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY); + } + // Camera orientation calculation + else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD; + } + } + // Paning + else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) + { + camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + } + + // Focus to center + if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_ORBITAL: + { + // Pass to free camera + if (IsKeyPressed('O')) cameraMode = CAMERA_FREE; + + cameraAngle.x += ORBITAL_CAMERA_SPEED; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Focus to center + if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_FIRST_PERSON: + case CAMERA_THIRD_PERSON: + { + bool isMoving = false; + + // Keyboard inputs + if (IsKeyDown('W')) + { + cameraPosition.x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown('S')) + { + cameraPosition.x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown('A')) + { + cameraPosition.x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown('D')) + { + cameraPosition.x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown('E')) + { + if (!cameraUseGravity) cameraPosition.y += 1 / PLAYER_MOVEMENT_DIVIDER; + } + else if (IsKeyDown('Q')) + { + if (!cameraUseGravity) cameraPosition.y -= 1 / PLAYER_MOVEMENT_DIVIDER; + } + + if (cameraMode == CAMERA_THIRD_PERSON) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -THIRD_PERSON_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -THIRD_PERSON_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Camera is always looking at player + camera->target.x = cameraPosition.x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); + camera->target.y = cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; + camera->target.z = cameraPosition.z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + } + else + { + if (isMoving) cameraMovementCounter++; + + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -FIRST_PERSON_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -FIRST_PERSON_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera is always looking at player + camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + + camera->position.x = cameraPosition.x; + camera->position.y = (cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; + camera->position.z = cameraPosition.z; + + camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + } + } break; + default: break; + } +} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 6ee78226..63a2b21f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -252,6 +252,9 @@ typedef struct Camera { Vector3 up; } Camera; +// Camera modes +typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; + // Vertex data definning a mesh typedef struct VertexData { int vertexCount; @@ -337,7 +340,8 @@ int GetHexValue(Color color); // Returns hexadecim int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -void SetupFlags(char flags); // Enable some window configurations +void SetCameraMode(int mode); // Multiple camera modes available +void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) void InitPostShader(void); @@ -489,6 +493,7 @@ unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); +//void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition); //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio)