From 73989a49817225f11f547d270598e93745bf7df0 Mon Sep 17 00:00:00 2001 From: Crydsch Cube Date: Tue, 14 Feb 2023 17:47:21 +0100 Subject: [PATCH] WIP rcamera redesign vector (#2563) * core functionality CAMERA_FREE * fix example * add remaining camera modes * add view bobbing * view bobbing * catch curser in SetCameraMode * adjust examples * fix compilation on linux * fix example text_draw_3d * actually fix text_draw_3d * Updated camera API * Improve Vector3RotateByAxisAngle() function * remove camera.mode dependency from low-level functions * remove camera.mode from struct * fixes after rebase * adjust examples for new UpdateCamera function * adjust example models_loading_m3d --------- Co-authored-by: Ray --- examples/core/core_3d_camera_first_person.c | 92 ++- examples/core/core_3d_camera_free.c | 4 +- examples/core/core_3d_picking.c | 15 +- examples/core/core_vr_simulator.c | 6 +- examples/core/core_world_screen.c | 7 +- examples/models/models_animation.c | 5 +- examples/models/models_billboard.c | 6 +- examples/models/models_cubicmap.c | 5 +- examples/models/models_first_person_maze.c | 5 +- examples/models/models_heightmap.c | 5 +- examples/models/models_loading.c | 5 +- examples/models/models_loading_gltf.c | 6 +- examples/models/models_loading_m3d.c | 5 +- examples/models/models_loading_vox.c | 5 +- examples/models/models_mesh_generation.c | 5 +- examples/models/models_mesh_picking.c | 14 +- examples/models/models_rlgl_solar_system.c | 5 +- examples/models/models_skybox.c | 5 +- examples/shaders/shaders_basic_lighting.c | 5 +- examples/shaders/shaders_custom_uniform.c | 6 +- examples/shaders/shaders_fog.c | 5 +- examples/shaders/shaders_mesh_instancing.c | 6 +- examples/shaders/shaders_model_shader.c | 5 +- examples/shaders/shaders_postprocessing.c | 6 +- examples/shaders/shaders_raymarching.c | 5 +- examples/shaders/shaders_simple_mask.c | 3 +- examples/text/text_draw_3d.c | 9 +- src/raylib.h | 8 +- src/rcamera.h | 648 ++++++++------------ src/rcamera_old.h | 567 +++++++++++++++++ 30 files changed, 996 insertions(+), 477 deletions(-) create mode 100644 src/rcamera_old.h diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index a7aa5f49..d98a002f 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -12,6 +12,7 @@ ********************************************************************************************/ #include "raylib.h" +#include "rcamera.h" #define MAX_COLUMNS 20 @@ -29,11 +30,14 @@ int main(void) // Define the camera to look into our 3d world (position, target, up vector) Camera camera = { 0 }; - camera.position = (Vector3){ 4.0f, 2.0f, 4.0f }; - camera.target = (Vector3){ 0.0f, 1.8f, 0.0f }; + camera.position = (Vector3){ 0.0f, 2.0f, 4.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 60.0f; camera.projection = CAMERA_PERSPECTIVE; + camera.swingCounter = 1; // Enable view bobbing + + int cameraMode = CAMERA_FIRST_PERSON; // Generates some random columns float heights[MAX_COLUMNS] = { 0 }; @@ -47,8 +51,7 @@ int main(void) colors[i] = (Color){ GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255 }; } - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set a first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -57,7 +60,51 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + // Switch camera mode + if (IsKeyPressed(KEY_ONE)) { + cameraMode = CAMERA_FREE; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_TWO)) { + cameraMode = CAMERA_FIRST_PERSON; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_THREE)) { + cameraMode = CAMERA_THIRD_PERSON; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_FOUR)) { + cameraMode = CAMERA_ORBITAL; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + + // Switch camera projection + if (IsKeyPressed(KEY_P)) { + if (camera.projection == CAMERA_PERSPECTIVE) { + // Create isometric view + cameraMode = CAMERA_THIRD_PERSON; + // Note: The target distance is related to the render distance in the orthographic projection + camera.position = (Vector3){ 0.0f, 2.0f, -100.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.projection = CAMERA_ORTHOGRAPHIC; + camera.fovy = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC + CameraYaw(&camera, -135 * DEG2RAD, true); + CameraPitch(&camera, -45 * DEG2RAD, true, true, false); + } + else if (camera.projection == CAMERA_ORTHOGRAPHIC) + { + // Reset to default view + cameraMode = CAMERA_THIRD_PERSON; + camera.position = (Vector3){ 0.0f, 2.0f, 10.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.projection = CAMERA_PERSPECTIVE; + camera.fovy = 60.0f; + } + } + + UpdateCamera(&camera, cameraMode); // Update camera //---------------------------------------------------------------------------------- // Draw @@ -80,14 +127,39 @@ int main(void) DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, MAROON); } + // Draw player cube + if (cameraMode == CAMERA_THIRD_PERSON) + { + DrawCube(camera.target, 0.5f, 0.5f, 0.5f, PURPLE); + DrawCubeWires(camera.target, 0.5f, 0.5f, 0.5f, DARKPURPLE); + } + EndMode3D(); - DrawRectangle( 10, 10, 220, 70, Fade(SKYBLUE, 0.5f)); - DrawRectangleLines( 10, 10, 220, 70, BLUE); + // Draw info boxes + DrawRectangle(5, 5, 330, 100, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(5, 5, 330, 100, BLUE); - DrawText("First person camera default controls:", 20, 20, 10, BLACK); - DrawText("- Move with keys: W, A, S, D", 40, 40, 10, DARKGRAY); - DrawText("- Mouse move to look around", 40, 60, 10, DARKGRAY); + DrawText("Camera controls:", 15, 15, 10, BLACK); + DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK); + DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK); + DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, BLACK); + DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, BLACK); + DrawText("- Camera projection key: P", 15, 90, 10, BLACK); + + DrawRectangle(600, 5, 195, 100, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(600, 5, 195, 100, BLUE); + + DrawText("Camera status:", 610, 15, 10, BLACK); + DrawText(TextFormat("- Mode: %s", (cameraMode == CAMERA_FREE) ? "FREE" : + (cameraMode == CAMERA_FIRST_PERSON) ? "FIRST_PERSON" : + (cameraMode == CAMERA_THIRD_PERSON) ? "THIRD_PERSON" : + (cameraMode == CAMERA_ORBITAL) ? "ORBITAL" : "CUSTOM"), 610, 30, 10, BLACK); + DrawText(TextFormat("- Projection: %s", (camera.projection == CAMERA_PERSPECTIVE) ? "PERSPECTIVE" : + (camera.projection == CAMERA_ORTHOGRAPHIC) ? "ORTHOGRAPHIC" : "CUSTOM"), 610, 45, 10, BLACK); + DrawText(TextFormat("- Position: (%06.3f, %06.3f, %06.3f)", camera.position.x, camera.position.y, camera.position.z), 610, 60, 10, BLACK); + DrawText(TextFormat("- Target: (%06.3f, %06.3f, %06.3f)", camera.target.x, camera.target.y, camera.target.z), 610, 75, 10, BLACK); + DrawText(TextFormat("- Up: (%06.3f, %06.3f, %06.3f)", camera.up.x, camera.up.y, camera.up.z), 610, 90, 10, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index af7ab8d7..887a1df3 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -35,8 +35,6 @@ int main(void) Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -45,7 +43,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FREE); if (IsKeyDown('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //---------------------------------------------------------------------------------- diff --git a/examples/core/core_3d_picking.c b/examples/core/core_3d_picking.c index 8c0df966..0cf56f5f 100644 --- a/examples/core/core_3d_picking.c +++ b/examples/core/core_3d_picking.c @@ -40,7 +40,7 @@ int main(void) RayCollision collision = { 0 }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + EnableCursor(); // Disable camera controls SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -50,7 +50,14 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Update camera + + // Toggle camera controls + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) + { + if (IsCursorHidden()) EnableCursor(); + else DisableCursor(); + } if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { @@ -93,10 +100,12 @@ int main(void) EndMode3D(); - DrawText("Try selecting the box with mouse!", 240, 10, 20, DARKGRAY); + DrawText("Try clicking on the box with your mouse!", 240, 10, 20, DARKGRAY); if (collision.hit) DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30)) / 2, (int)(screenHeight * 0.1f), 30, GREEN); + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, GRAY); + DrawFPS(10, 10); EndDrawing(); diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 5914897b..3024b785 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -96,11 +96,11 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector camera.fovy = 60.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera type + camera.swingCounter = 1; // Enable view bobbing Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(90); // Set our game to run at 90 frames-per-second //-------------------------------------------------------------------------------------- @@ -109,7 +109,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index d86adf87..f96690ba 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -23,7 +23,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - core world screen"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -36,8 +36,7 @@ int main(void) Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; Vector2 cubeScreenPosition = { 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -46,7 +45,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Calculate cube screen space position (with a little offset to be in top) cubeScreenPosition = GetWorldToScreen((Vector3){cubePosition.x, cubePosition.y + 2.5f, cubePosition.z}, camera); diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index b9216b21..a36f3fe5 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -54,8 +54,7 @@ int main(void) ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount); int animFrameCounter = 0; - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -64,7 +63,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Play animation when spacebar is held down if (IsKeyDown(KEY_SPACE)) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index ce313949..6d16cf01 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -48,14 +48,13 @@ int main(void) // Here we choose to rotate around the image center // NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture Vector2 rotateOrigin = { 0.0f }; - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode // Distance is needed for the correct billboard draw order // Larger distance (further away from the camera) should be drawn prior to smaller distance. float distanceStatic; float distanceRotating; - float rotation = 0.0f; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -64,7 +63,8 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); + rotation += 0.4f; distanceStatic = Vector3Distance(camera.position, billPositionStatic); distanceRotating = Vector3Distance(camera.position, billPositionRotating); diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index 4953a99f..83bfab68 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -42,8 +42,7 @@ int main(void) UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -52,7 +51,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index b5e35b58..3f6a935c 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -45,8 +45,7 @@ int main(void) Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -57,7 +56,7 @@ int main(void) //---------------------------------------------------------------------------------- Vector3 oldCamPos = camera.position; // Store old camera position - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Check player collision (we simplify to 2D collision detection) Vector2 playerPos = { camera.position.x, camera.position.z }; diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index a5d17b02..25dc004d 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -39,8 +39,7 @@ int main(void) UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -49,7 +48,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 7bdf1772..56dad408 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -57,10 +57,9 @@ int main(void) // NOTE: bounds are calculated from the original size of the model, // if model is scaled on drawing, bounds must be also scaled - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - bool selected = false; // Selected object flag + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -69,7 +68,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Load new models/textures on drag&drop if (IsFileDropped()) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 92b517a1..0c907071 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -51,8 +51,6 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -61,6 +59,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Select current animation if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; @@ -69,9 +68,6 @@ int main(void) ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; UpdateModelAnimation(model, anim, animCurrentFrame); - - // Update camera - UpdateCamera(&camera); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 52dc9522..c1a4af07 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -53,8 +53,7 @@ int main(void) int animFrameCounter = 0, animId = 0; ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -63,7 +62,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); if (animsCount) { diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 49477cd9..6a38fe89 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -69,8 +69,7 @@ int main(void) int currentModel = 0; - SetCameraMode(camera, CAMERA_ORBITAL); // Set a orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -79,7 +78,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); // Cycle between models on mouse click if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES; diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index b4ff018c..8e9fdf90 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -68,8 +68,7 @@ int main(void) int currentModel = 0; - SetCameraMode(camera, CAMERA_ORBITAL); // Set a orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -78,7 +77,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 52f9974d..1118168c 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -64,8 +64,7 @@ int main(void) Vector3 sp = (Vector3){ -30.0f, 5.0f, 5.0f }; float sr = 4.0f; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - + EnableCursor(); // Disable camera controls SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -73,7 +72,14 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Update camera + + // Toggle camera controls + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) + { + if (IsCursorHidden()) EnableCursor(); + else DisableCursor(); + } // Display information about closest hit RayCollision collision = { 0 }; @@ -219,7 +225,7 @@ int main(void) DrawText(TextFormat("Barycenter: %3.2f %3.2f %3.2f", bary.x, bary.y, bary.z), 10, ypos + 45, 10, BLACK); } - DrawText("Use Mouse to Move Camera", 10, 430, 10, GRAY); + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, GRAY); DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index 903752f7..24f8b9a5 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -49,8 +49,6 @@ int main(void) camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - SetCameraMode(camera, CAMERA_FREE); - float rotationSpeed = 0.2f; // General system rotation speed float earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees @@ -58,6 +56,7 @@ int main(void) float moonRotation = 0.0f; // Rotation of moon around itself float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,7 +65,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); earthRotation += (5.0f*rotationSpeed); earthOrbitRotation += (365/360.0f*(5.0f*rotationSpeed)*rotationSpeed); diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 1b992333..d12cc557 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -87,8 +87,7 @@ int main(void) UnloadImage(img); } - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set a first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -97,7 +96,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Load new cubemap texture on drag&drop if (IsFileDropped()) diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 1b29b852..702177f1 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -80,8 +80,7 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, shader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, shader); - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -90,7 +89,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index 75f6112e..c617a6d1 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -67,9 +67,7 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - // Setup orbital camera - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -78,7 +76,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); Vector2 mousePosition = GetMousePosition(); diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index ac84a018..c4b619ed 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -84,8 +84,7 @@ int main(void) // Using just 1 point lights CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader); - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -94,7 +93,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsKeyDown(KEY_UP)) { diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index f4e6b40b..0c2f9e1d 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -94,9 +94,7 @@ int main(void) Material matDefault = LoadMaterialDefault(); matDefault.maps[MATERIAL_MAP_DIFFUSE].color = BLUE; - // Set an orbital camera mode - SetCameraMode(camera, CAMERA_ORBITAL); - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -105,7 +103,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); // Update the light shader with the camera view position float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index de3cb5f8..069d0ee2 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -60,8 +60,7 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetCameraMode(camera, CAMERA_FREE); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -70,7 +69,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index d0555f21..688ca909 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -107,9 +107,7 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - // Setup orbital camera - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -118,7 +116,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsKeyPressed(KEY_RIGHT)) currentShader++; else if (IsKeyPressed(KEY_LEFT)) currentShader--; diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 9b8bc30c..5cc9af05 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -41,8 +41,6 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 65.0f; // Camera field-of-view Y - SetCameraMode(camera, CAMERA_FREE); // Set camera mode - // Load raymarching shader // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/raymarching.fs", GLSL_VERSION)); @@ -58,6 +56,7 @@ int main(void) float runTime = 0.0f; + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,7 +65,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z }; diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index ce3fc8bb..49470294 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -85,6 +85,7 @@ int main(void) int framesCounter = 0; Vector3 rotation = { 0 }; // Model rotation angles + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -93,7 +94,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); framesCounter++; rotation.x += 0.01f; diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 127eb7c6..c90ea665 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -98,11 +98,12 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera mode type - SetCameraMode(camera, CAMERA_ORBITAL); + int camera_mode = CAMERA_ORBITAL; Vector3 cubePosition = { 0.0f, 1.0f, 0.0f }; Vector3 cubeSize = { 2.0f, 2.0f, 2.0f }; + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second // Use the default font @@ -141,7 +142,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, camera_mode); // Handle font files dropped if (IsFileDropped()) @@ -181,12 +182,12 @@ int main(void) if (spin) { camera.position = (Vector3){ -10.0f, 15.0f, -10.0f }; // Camera position - SetCameraMode(camera, CAMERA_ORBITAL); + camera_mode = CAMERA_ORBITAL; } else { camera.position = (Vector3){ 10.0f, 10.0f, -10.0f }; // Camera position - SetCameraMode(camera, CAMERA_FREE); + camera_mode = CAMERA_FREE; } } diff --git a/src/raylib.h b/src/raylib.h index e31a514a..650b5b29 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1156,13 +1156,7 @@ RLAPI float GetGesturePinchAngle(void); // Get gesture pinch ang //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode - -RLAPI void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -RLAPI void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -RLAPI void SetCameraSmoothZoomControl(int keySmoothZoom); // Set camera smooth zoom key to combine with mouse (free camera) -RLAPI void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) +RLAPI void UpdateCamera(Camera3D *camera, int mode); // Update camera position for selected mode //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) diff --git a/src/rcamera.h b/src/rcamera.h index 0caad666..60ac642a 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -2,8 +2,6 @@ * * rcamera - Basic camera system for multiple camera modes * -* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) -* * CONFIGURATION: * * #define CAMERA_IMPLEMENTATION @@ -17,6 +15,7 @@ * * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, update and maintenance +* Christoph Wagner: Redesign (2022) * Marc Palau: Initial implementation (2014) * * @@ -44,15 +43,26 @@ #ifndef RCAMERA_H #define RCAMERA_H +// The only dependency // TODO review standalone mode +#include "raymath.h" + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- //... +#if defined(CAMERA_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 //---------------------------------------------------------------------------------- // Types and Structures Definition // NOTE: Below types are required for CAMERA_STANDALONE usage //---------------------------------------------------------------------------------- +// TODO review #if defined(CAMERA_STANDALONE) // Vector2 type typedef struct Vector2 { @@ -76,8 +86,6 @@ int projection; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; - typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - // Camera system modes typedef enum { CAMERA_CUSTOM = 0, @@ -97,7 +105,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -//... + //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -107,17 +115,19 @@ extern "C" { // Prevents name mangling of functions #endif -#if defined(CAMERA_STANDALONE) -void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -void UpdateCamera(Camera *camera); // Update camera position for selected mode - -void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraMoveControls(int keyFront, int keyBack, - int keyRight, int keyLeft, - int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) -#endif +Vector3 GetCameraForward(Camera3D* camera); +Vector3 GetCameraUp(Camera3D* camera); +Vector3 GetCameraRight(Camera3D* camera); +void CameraMoveForward(Camera3D* camera, float distance, bool moveInWorldPlane); +void CameraMoveUp(Camera3D* camera, float distance); +void CameraMoveRight(Camera3D* camera, float distance, bool moveInWorldPlane); +void CameraZoom(Camera3D* camera, float delta); +void CameraYaw(Camera3D* camera, float angle, bool rotateAroundTarget); +void CameraPitch(Camera3D* camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); +void CameraRoll(Camera3D* camera, float angle); +void CameraViewBobbing(Camera3D* camera); +Matrix GetCameraViewMatrix(Camera3D* camera); +Matrix GetCameraProjectionMatrix(Camera3D* camera, float aspect); #if defined(__cplusplus) } @@ -134,424 +144,310 @@ void SetCameraMoveControls(int keyFront, int keyBack, #if defined(CAMERA_IMPLEMENTATION) -#include // Required for: sinf(), cosf(), sqrtf() - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846 -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif + +#define CAMERA_MOVE_SPEED 0.09f +#define CAMERA_ROTATION_SPEED 0.03f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.5f // TODO: it should be independent of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f -// FREE_CAMERA -#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f -#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f -#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f -#define CAMERA_FREE_MIN_CLAMP 85.0f -#define CAMERA_FREE_MAX_CLAMP -85.0f -#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f -#define CAMERA_FREE_PANNING_DIVIDER 5.1f +#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame -// ORBITAL_CAMERA -#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second -// FIRST_PERSON -//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f -#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f -#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f - -// When walking, y-position of the player moves up-down at step frequency (swinging) but -// also the body slightly tilts left-right on every step, when all the body weight is left over one foot (tilting) -#define CAMERA_FIRST_PERSON_STEP_FREQUENCY 1.8f // Step frequency when walking (steps per second) -#define CAMERA_FIRST_PERSON_SWINGING_DELTA 0.03f // Maximum up-down swinging distance when walking -#define CAMERA_FIRST_PERSON_TILTING_DELTA 0.005f // Maximum left-right tilting distance when walking - -// THIRD_PERSON -//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f -#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f -#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f -#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } +#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f +#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f +#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f // PLAYER (used by camera) -#define PLAYER_MOVEMENT_SENSITIVITY 2.0f +#define PLAYER_MOVEMENT_SENSITIVITY 20.0f //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -// Camera move modes (first person and third person cameras) -typedef enum { - MOVE_FRONT = 0, - MOVE_BACK, - MOVE_RIGHT, - MOVE_LEFT, - MOVE_UP, - MOVE_DOWN -} CameraMove; -// Camera global state context data [56 bytes] -typedef struct { - unsigned int mode; // Current camera mode - float targetDistance; // Camera distance from position to target - float playerEyesPosition; // Player eyes position from ground (in meters) - Vector2 angle; // Camera angle in plane XZ - - // Camera movement control keys - int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) - int smoothZoomControl; // Smooth zoom control key - int altControl; // Alternative control key - int panControl; // Pan view control key -} CameraData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static CameraData CAMERA = { // Global CAMERA state context - .mode = 0, - .targetDistance = 0, - .playerEyesPosition = 1.85f, - .angle = { 0 }, - .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, - .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL - .altControl = 342, // raylib: KEY_LEFT_ALT - .panControl = 2 // raylib: MOUSE_BUTTON_MIDDLE -}; + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) -// NOTE: Camera controls depend on some raylib input functions -static void EnableCursor() {} // Unlock cursor -static void DisableCursor() {} // Lock cursor -static int IsKeyDown(int key) { return 0; } - -static int IsMouseButtonDown(int button) { return 0;} -static float GetMouseWheelMove() { return 0.0f; } -static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- -// Select camera mode (multiple camera modes available) -void SetCameraMode(Camera camera, int mode) +// Returns the cameras forward vector (normalized) +Vector3 GetCameraForward(Camera3D *camera) { - Vector3 v1 = camera.position; - Vector3 v2 = camera.target; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - - CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance to target - - // Camera angle calculation - CAMERA.angle.x = atan2f(dx, dz); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) - CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW) - - CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position - - // Lock cursor for first person and third person cameras - if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); - else EnableCursor(); - - CAMERA.mode = mode; + return Vector3Normalize(Vector3Subtract(camera->target, camera->position)); } -// Update camera depending on selected mode -// NOTE: Camera controls depend on some raylib functions: -// System: EnableCursor(), DisableCursor() -// Mouse: IsMouseButtonDown(), GetMousePosition(), GetMouseWheelMove() -// Keys: IsKeyDown() -void UpdateCamera(Camera *camera) +// Returns the cameras up vector (normalized) +// Note: The up vector might not be perpendicular to the forward vector +Vector3 GetCameraUp(Camera3D *camera) { - static float swingCounter = 0.0f; // Used for 1st person swinging movement + return Vector3Normalize(camera->up); +} - // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) +// Returns the cameras right vector (normalized) +Vector3 GetCameraRight(Camera3D *camera) +{ + Vector3 forward = GetCameraForward(camera); + Vector3 up = GetCameraUp(camera); + return Vector3CrossProduct(forward, up); +} - // Mouse movement detection - Vector2 mousePositionDelta = GetMouseDelta(); - float mouseWheelMove = GetMouseWheelMove(); +// Moves the camera in its forward direction +void CameraMoveForward(Camera3D *camera, float distance, bool moveInWorldPlane) +{ + Vector3 forward = GetCameraForward(camera); - // Keys input detection - // TODO: Input detection is raylib-dependant, it could be moved outside the module - bool keyPan = IsMouseButtonDown(CAMERA.panControl); - bool keyAlt = IsKeyDown(CAMERA.altControl); - bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl); - bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]), - IsKeyDown(CAMERA.moveControl[MOVE_BACK]), - IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]), - IsKeyDown(CAMERA.moveControl[MOVE_LEFT]), - IsKeyDown(CAMERA.moveControl[MOVE_UP]), - IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; - - // Support for multiple automatic camera modes - // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually - switch (CAMERA.mode) + if (moveInWorldPlane) { - case CAMERA_FREE: // Camera free controls, using standard 3d-content-creation scheme - { - // Camera zoom - if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; - } + // Project vector onto world plane + forward.y = 0; + forward = Vector3Normalize(forward); + } - // Camera looking down - else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + // Scale by distance + forward = Vector3Scale(forward, distance); - // if (camera->target.y < 0) camera->target.y = -0.001; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } - // Camera looking up - else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + // Move position and target + camera->position = Vector3Add(camera->position, forward); + camera->target = Vector3Add(camera->target, forward); +} - // if (camera->target.y > 0) camera->target.y = 0.001; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } +// Moves the camera in its up direction +void CameraMoveUp(Camera3D *camera, float distance) +{ + Vector3 up = GetCameraUp(camera); + + // Scale by distance + up = Vector3Scale(up, distance); - // Input keys checks - if (keyPan) - { - if (keyAlt) // Alternative key behaviour - { - if (szoomKey) - { - // Camera smooth zoom - CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY); - } - else - { - // Camera rotation - CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY; - CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY; + // Move position and target + camera->position = Vector3Add(camera->position, up); + camera->target = Vector3Add(camera->target, up); +} - // Angle clamp - if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD; - } - } - else - { - // Camera panning - camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - } - } +// Moves the camera target in its current right direction +void CameraMoveRight(Camera3D *camera, float distance, bool moveInWorldPlane) +{ + Vector3 right = GetCameraRight(camera); - // Update camera position with changes - camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y; - camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + if (moveInWorldPlane) + { + // Project vector onto world plane + right.y = 0; + right = Vector3Normalize(right); + } - } break; - case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed - { - CAMERA.angle.x += CAMERA_ORBITAL_SPEED*GetFrameTime(); // Camera orbit angle - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom + // Scale by distance + right = Vector3Scale(right, distance); - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + // Move position and target + camera->position = Vector3Add(camera->position, right); + camera->target = Vector3Add(camera->target, right); +} - // Update camera position with changes - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; +// Moves the camera position closer/farther to/from the camera target +void CameraZoom(Camera3D *camera, float delta) +{ + float distance = Vector3Distance(camera->position, camera->target); - } break; - case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable - { - // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' - camera->position.y = CAMERA.playerEyesPosition; + // Apply delta + distance += delta; - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); + // Distance must be greater than 0 + if (distance < 0) distance = 0.001f; - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); + // Set new distance by moving the position along the forward vector + Vector3 forward = GetCameraForward(camera); + camera->position = Vector3Add(camera->target, Vector3Scale(forward, -distance)); +} - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); +// Rotates the camera around its up vector +// Yaw is "looking left and right" +// If rotateAroundTarget is false, the camera rotates around its position +// Note: angle must be provided in radians +void CameraYaw(Camera3D *camera, float angle, bool rotateAroundTarget) +{ + // Rotation axis + Vector3 up = GetCameraUp(camera); - // Camera orientation calculation - CAMERA.angle.x -= mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime(); - CAMERA.angle.y -= mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime(); + // View vector + Vector3 target_position = Vector3Subtract(camera->target, camera->position); - // Angle clamp - if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; + // Rotate view vector around up axis + target_position = Vector3RotateByAxisAngle(target_position, up, angle); - // Calculate translation matrix - Matrix matTranslation = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER), - 0.0f, 0.0f, 0.0f, 1.0f }; - - // Calculate rotation matrix - Matrix matRotation = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - float cosz = cosf(0.0f); - float sinz = sinf(0.0f); - float cosy = cosf(-(PI*2 - CAMERA.angle.x)); - float siny = sinf(-(PI*2 - CAMERA.angle.x)); - float cosx = cosf(-(PI*2 - CAMERA.angle.y)); - float sinx = sinf(-(PI*2 - CAMERA.angle.y)); - - matRotation.m0 = cosz*cosy; - matRotation.m4 = (cosz*siny*sinx) - (sinz*cosx); - matRotation.m8 = (cosz*siny*cosx) + (sinz*sinx); - matRotation.m1 = sinz*cosy; - matRotation.m5 = (sinz*siny*sinx) + (cosz*cosx); - matRotation.m9 = (sinz*siny*cosx) - (cosz*sinx); - matRotation.m2 = -siny; - matRotation.m6 = cosy*sinx; - matRotation.m10= cosy*cosx; - - // Multiply translation and rotation matrices - Matrix matTransform = { 0 }; - matTransform.m0 = matTranslation.m0*matRotation.m0 + matTranslation.m1*matRotation.m4 + matTranslation.m2*matRotation.m8 + matTranslation.m3*matRotation.m12; - matTransform.m1 = matTranslation.m0*matRotation.m1 + matTranslation.m1*matRotation.m5 + matTranslation.m2*matRotation.m9 + matTranslation.m3*matRotation.m13; - matTransform.m2 = matTranslation.m0*matRotation.m2 + matTranslation.m1*matRotation.m6 + matTranslation.m2*matRotation.m10 + matTranslation.m3*matRotation.m14; - matTransform.m3 = matTranslation.m0*matRotation.m3 + matTranslation.m1*matRotation.m7 + matTranslation.m2*matRotation.m11 + matTranslation.m3*matRotation.m15; - matTransform.m4 = matTranslation.m4*matRotation.m0 + matTranslation.m5*matRotation.m4 + matTranslation.m6*matRotation.m8 + matTranslation.m7*matRotation.m12; - matTransform.m5 = matTranslation.m4*matRotation.m1 + matTranslation.m5*matRotation.m5 + matTranslation.m6*matRotation.m9 + matTranslation.m7*matRotation.m13; - matTransform.m6 = matTranslation.m4*matRotation.m2 + matTranslation.m5*matRotation.m6 + matTranslation.m6*matRotation.m10 + matTranslation.m7*matRotation.m14; - matTransform.m7 = matTranslation.m4*matRotation.m3 + matTranslation.m5*matRotation.m7 + matTranslation.m6*matRotation.m11 + matTranslation.m7*matRotation.m15; - matTransform.m8 = matTranslation.m8*matRotation.m0 + matTranslation.m9*matRotation.m4 + matTranslation.m10*matRotation.m8 + matTranslation.m11*matRotation.m12; - matTransform.m9 = matTranslation.m8*matRotation.m1 + matTranslation.m9*matRotation.m5 + matTranslation.m10*matRotation.m9 + matTranslation.m11*matRotation.m13; - matTransform.m10 = matTranslation.m8*matRotation.m2 + matTranslation.m9*matRotation.m6 + matTranslation.m10*matRotation.m10 + matTranslation.m11*matRotation.m14; - matTransform.m11 = matTranslation.m8*matRotation.m3 + matTranslation.m9*matRotation.m7 + matTranslation.m10*matRotation.m11 + matTranslation.m11*matRotation.m15; - matTransform.m12 = matTranslation.m12*matRotation.m0 + matTranslation.m13*matRotation.m4 + matTranslation.m14*matRotation.m8 + matTranslation.m15*matRotation.m12; - matTransform.m13 = matTranslation.m12*matRotation.m1 + matTranslation.m13*matRotation.m5 + matTranslation.m14*matRotation.m9 + matTranslation.m15*matRotation.m13; - matTransform.m14 = matTranslation.m12*matRotation.m2 + matTranslation.m13*matRotation.m6 + matTranslation.m14*matRotation.m10 + matTranslation.m15*matRotation.m14; - matTransform.m15 = matTranslation.m12*matRotation.m3 + matTranslation.m13*matRotation.m7 + matTranslation.m14*matRotation.m11 + matTranslation.m15*matRotation.m15; - - camera->target.x = camera->position.x - matTransform.m12; - camera->target.y = camera->position.y - matTransform.m13; - camera->target.z = camera->position.z - matTransform.m14; - - // Camera swinging (y-movement), only when walking (some key pressed) - for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter += GetFrameTime(); break; } - camera->position.y -= sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_SWINGING_DELTA; - - // Camera waiving (xz-movement), only when walking (some key pressed) - camera->up.x = sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA; - camera->up.z = -sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA; - - } break; - case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable - { - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - // Camera orientation calculation - CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); - CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - - // Angle clamp - if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; - - // Camera zoom - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - - if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; - - } break; - case CAMERA_CUSTOM: break; - default: break; + if (rotateAroundTarget) + { + // Move position relative to target + camera->position = Vector3Subtract(camera->target, target_position); + } + else // rotate around camera.position + { + // Move target relative to position + camera->target = Vector3Add(camera->position, target_position); } } -// Set camera pan key to combine with mouse movement (free camera) -void SetCameraPanControl(int keyPan) { CAMERA.panControl = keyPan; } - -// Set camera alt key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt) { CAMERA.altControl = keyAlt; } - -// Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; } - -// Set camera move controls (1st person and 3rd person cameras) -void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown) +// Rotates the camera around its right vector +// Pitch is "looking up and down" +// lockView prevents camera overrotation (aka "somersaults") +// If rotateAroundTarget is false, the camera rotates around its position +// rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) +// Note: angle must be provided in radians +void CameraPitch(Camera3D *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { - CAMERA.moveControl[MOVE_FRONT] = keyFront; - CAMERA.moveControl[MOVE_BACK] = keyBack; - CAMERA.moveControl[MOVE_RIGHT] = keyRight; - CAMERA.moveControl[MOVE_LEFT] = keyLeft; - CAMERA.moveControl[MOVE_UP] = keyUp; - CAMERA.moveControl[MOVE_DOWN] = keyDown; + // Up direction + Vector3 up = GetCameraUp(camera); + + // View vector + Vector3 target_position = Vector3Subtract(camera->target, camera->position); + + if (lockView) + { + // In these camera modes we clamp the Pitch angle + // to allow only viewing straight up or down. + + // Clamp view up + float max_angle_up = Vector3Angle(up, target_position); + max_angle_up -= 0.001f; // avoid numerical errors + if (angle > max_angle_up) angle = max_angle_up; + + // Clamp view down + float max_angle_down = Vector3Angle(Vector3Negate(up), target_position); + max_angle_down *= -1.0f; // downwards angle is negative + max_angle_down += 0.001f; // avoid numerical errors + if (angle < max_angle_down) angle = max_angle_down; + } + + // Rotation axis + Vector3 right = GetCameraRight(camera); + + // Rotate view vector around right axis + target_position = Vector3RotateByAxisAngle(target_position, right, angle); + + if (rotateAroundTarget) + { + // Move position relative to target + camera->position = Vector3Subtract(camera->target, target_position); + } + else // rotate around camera.position + { + // Move target relative to position + camera->target = Vector3Add(camera->position, target_position); + } + + if (rotateUp) + { + // Rotate up direction around right axis + camera->up = Vector3RotateByAxisAngle(camera->up, right, angle); + } } +// Rotates the camera around its forward vector +// Roll is "turning your head sideways to the left or right" +// Note: angle must be provided in radians +void CameraRoll(Camera3D *camera, float angle) +{ + // Rotation axis + Vector3 forward = GetCameraForward(camera); + + // Rotate up direction around forward axis + camera->up = Vector3RotateByAxisAngle(camera->up, forward, angle); +} + +// Moves camera slightly to simulate a walking motion +// Note: Only active if camera->swingCounter > 0 +void CameraViewBobbing(Camera3D *camera) +{ + if (camera->swingCounter > 0) + { + // NOTE: We delay the target movement relative to the position movement to create a little pitch with each step. + camera->position.y = camera->position.y - 0.25f * sinf((camera->swingCounter + 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; + camera->target.y = camera->target.y - 0.25f * sinf((camera->swingCounter - 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; + + // Update counter for next frame + camera->swingCounter %= 2147483647 /* INT_MAX */; // Counter must be positive + camera->swingCounter++; + } +} + +// Returns the camera view matrix +Matrix GetCameraViewMatrix(Camera3D *camera) +{ + return MatrixLookAt(camera->position, camera->target, camera->up); +} + +// Returns the camera projection matrix +Matrix GetCameraProjectionMatrix(Camera3D *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(); +} + + +#ifndef CAMERA_STANDALONE +// Update camera position for selected mode +// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM +void UpdateCamera(Camera3D *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_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; + bool rotateUp = mode == CAMERA_FREE; + + // Camera movement + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + + // Camera rotation + if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); + if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); + + CameraYaw(camera, mousePositionDelta.x * -CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, mousePositionDelta.y * -CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + // Zoom target distance + CameraZoom(camera, -GetMouseWheelMove()); + if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraZoom(camera, 2.0f); + if (IsKeyPressed(KEY_KP_ADD)) CameraZoom(camera, -2.0f); + + + // Apply view bobbing when moving around (per default only active in CAMERA_FIRST_PERSON) + if (mode == CAMERA_FIRST_PERSON && (IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))) CameraViewBobbing(camera); +} +#endif // !CAMERA_STANDALONE + #endif // CAMERA_IMPLEMENTATION diff --git a/src/rcamera_old.h b/src/rcamera_old.h new file mode 100644 index 00000000..08c37669 --- /dev/null +++ b/src/rcamera_old.h @@ -0,0 +1,567 @@ +/******************************************************************************************* +* +* rcamera - Basic camera system for multiple camera modes +* +* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) +* +* CONFIGURATION: +* +* #define CAMERA_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define CAMERA_STANDALONE +* If defined, the library can be used as standalone as a camera system but some +* functions must be redefined to manage inputs accordingly. +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, update and maintenance +* Marc Palau: Initial implementation (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RCAMERA_H +#define RCAMERA_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Below types are required for CAMERA_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(CAMERA_STANDALONE) + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Camera type, defines a camera position/orientation in 3d space + typedef struct Camera3D { + Vector3 position; // Camera position + Vector3 target; // Camera target it looks-at + Vector3 up; // Camera up vector (rotation over its axis) + float fovy; // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic + int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC + } Camera3D; + + typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + + // Camera system modes + typedef enum { + CAMERA_CUSTOM = 0, + CAMERA_FREE, + CAMERA_ORBITAL, + CAMERA_FIRST_PERSON, + CAMERA_THIRD_PERSON + } CameraMode; + + // Camera projection modes + typedef enum { + CAMERA_PERSPECTIVE = 0, + CAMERA_ORTHOGRAPHIC + } CameraProjection; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +#if defined(CAMERA_STANDALONE) +void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) +void UpdateCamera(Camera *camera); // Update camera position for selected mode + +void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) +void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) +void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) +void SetCameraMoveControls(int keyFront, int keyBack, + int keyRight, int keyLeft, + int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) +#endif + +#ifdef __cplusplus +} +#endif + +#endif // CAMERA_H + + +/*********************************************************************************** +* +* CAMERA IMPLEMENTATION +* +************************************************************************************/ + +#if defined(CAMERA_IMPLEMENTATION) + +#include // Required for: sinf(), cosf(), sqrtf() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846 +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Camera mouse movement sensitivity +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f +#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f + +// FREE_CAMERA +#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f +#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f +#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f +#define CAMERA_FREE_MIN_CLAMP 85.0f +#define CAMERA_FREE_MAX_CLAMP -85.0f +#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f +#define CAMERA_FREE_PANNING_DIVIDER 5.1f + +// ORBITAL_CAMERA +#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame + +// FIRST_PERSON +//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f +#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f +#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f +#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f + +#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f +#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f +#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f + +// THIRD_PERSON +//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f +#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f +#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f +#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f +#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } + +// PLAYER (used by camera) +#define PLAYER_MOVEMENT_SENSITIVITY 20.0f + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Camera move modes (first person and third person cameras) +typedef enum { + MOVE_FRONT = 0, + MOVE_BACK, + MOVE_RIGHT, + MOVE_LEFT, + MOVE_UP, + MOVE_DOWN +} CameraMove; + +// Camera global state context data [56 bytes] +typedef struct { + unsigned int mode; // Current camera mode + float targetDistance; // Camera distance from position to target + float playerEyesPosition; // Player eyes position from ground (in meters) + Vector2 angle; // Camera angle in plane XZ + Vector2 previousMousePosition; // Previous mouse position + + // Camera movement control keys + int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) + int smoothZoomControl; // Smooth zoom control key + int altControl; // Alternative control key + int panControl; // Pan view control key +} CameraData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static CameraData CAMERA = { // Global CAMERA state context + .mode = 0, + .targetDistance = 0, + .playerEyesPosition = 1.85f, + .angle = { 0 }, + .previousMousePosition = { 0 }, + .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, + .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL + .altControl = 342, // raylib: KEY_LEFT_ALT + .panControl = 2 // raylib: MOUSE_BUTTON_MIDDLE +}; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(CAMERA_STANDALONE) +// NOTE: Camera controls depend on some raylib input functions +static void EnableCursor() {} // Unlock cursor +static void DisableCursor() {} // Lock cursor + +static int IsKeyDown(int key) { return 0; } + +static int IsMouseButtonDown(int button) { return 0;} +static float GetMouseWheelMove() { return 0.0f; } +static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; } +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Select camera mode (multiple camera modes available) +void SetCameraMode(Camera camera, int mode) +{ + Vector3 v1 = camera.position; + Vector3 v2 = camera.target; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + + CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance to target + + // Camera angle calculation + CAMERA.angle.x = atan2f(dx, dz); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) + CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW) + + CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position + + CAMERA.previousMousePosition = GetMousePosition(); // Init mouse position + + // Lock cursor for first person and third person cameras + if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); + else EnableCursor(); + + CAMERA.mode = mode; +} + +// Update camera depending on selected mode +// NOTE: Camera controls depend on some raylib functions: +// System: EnableCursor(), DisableCursor() +// Mouse: IsMouseButtonDown(), GetMousePosition(), GetMouseWheelMove() +// Keys: IsKeyDown() +void UpdateCamera(Camera *camera) +{ + static int swingCounter = 0; // Used for 1st person swinging movement + + // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) + + // Mouse movement detection + Vector2 mousePositionDelta = { 0.0f, 0.0f }; + Vector2 mousePosition = GetMousePosition(); + float mouseWheelMove = GetMouseWheelMove(); + + // Keys input detection + // TODO: Input detection is raylib-dependant, it could be moved outside the module + bool keyPan = IsMouseButtonDown(CAMERA.panControl); + bool keyAlt = IsKeyDown(CAMERA.altControl); + bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl); + bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]), + IsKeyDown(CAMERA.moveControl[MOVE_BACK]), + IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]), + IsKeyDown(CAMERA.moveControl[MOVE_LEFT]), + IsKeyDown(CAMERA.moveControl[MOVE_UP]), + IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; + + if (CAMERA.mode != CAMERA_CUSTOM) + { + mousePositionDelta.x = mousePosition.x - CAMERA.previousMousePosition.x; + mousePositionDelta.y = mousePosition.y - CAMERA.previousMousePosition.y; + + CAMERA.previousMousePosition = mousePosition; + } + + // Support for multiple automatic camera modes + // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually + switch (CAMERA.mode) + { + case CAMERA_FREE: // Camera free controls, using standard 3d-content-creation scheme + { + // Camera zoom + if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; + } + + // Camera looking down + else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + + // if (camera->target.y < 0) camera->target.y = -0.001; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; + } + // Camera looking up + else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + + // if (camera->target.y > 0) camera->target.y = 0.001; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; + } + + // Input keys checks + if (keyPan) + { + if (keyAlt) // Alternative key behaviour + { + if (szoomKey) + { + // Camera smooth zoom + CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY); + } + else + { + // Camera rotation + CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY; + CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY; + + // Angle clamp + if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD; + } + } + else + { + // Camera panning + camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + } + } + + // Update camera position with changes + camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y; + camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed + { + CAMERA.angle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom + + // Camera distance clamp + if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + + // Update camera position with changes + camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable + { + camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - + sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + + cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - + sinf(CAMERA.angle.y)*direction[MOVE_BACK] + + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + + sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - + sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + // Camera orientation calculation + CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); + CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); + + // Angle clamp + if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; + + // Calculate translation matrix + Matrix matTranslation = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER), + 0.0f, 0.0f, 0.0f, 1.0f }; + + // Calculate rotation matrix + Matrix matRotation = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + float cosz = cosf(0.0f); + float sinz = sinf(0.0f); + float cosy = cosf(-(PI*2 - CAMERA.angle.x)); + float siny = sinf(-(PI*2 - CAMERA.angle.x)); + float cosx = cosf(-(PI*2 - CAMERA.angle.y)); + float sinx = sinf(-(PI*2 - CAMERA.angle.y)); + + matRotation.m0 = cosz*cosy; + matRotation.m4 = (cosz*siny*sinx) - (sinz*cosx); + matRotation.m8 = (cosz*siny*cosx) + (sinz*sinx); + matRotation.m1 = sinz*cosy; + matRotation.m5 = (sinz*siny*sinx) + (cosz*cosx); + matRotation.m9 = (sinz*siny*cosx) - (cosz*sinx); + matRotation.m2 = -siny; + matRotation.m6 = cosy*sinx; + matRotation.m10= cosy*cosx; + + // Multiply translation and rotation matrices + Matrix matTransform = { 0 }; + matTransform.m0 = matTranslation.m0*matRotation.m0 + matTranslation.m1*matRotation.m4 + matTranslation.m2*matRotation.m8 + matTranslation.m3*matRotation.m12; + matTransform.m1 = matTranslation.m0*matRotation.m1 + matTranslation.m1*matRotation.m5 + matTranslation.m2*matRotation.m9 + matTranslation.m3*matRotation.m13; + matTransform.m2 = matTranslation.m0*matRotation.m2 + matTranslation.m1*matRotation.m6 + matTranslation.m2*matRotation.m10 + matTranslation.m3*matRotation.m14; + matTransform.m3 = matTranslation.m0*matRotation.m3 + matTranslation.m1*matRotation.m7 + matTranslation.m2*matRotation.m11 + matTranslation.m3*matRotation.m15; + matTransform.m4 = matTranslation.m4*matRotation.m0 + matTranslation.m5*matRotation.m4 + matTranslation.m6*matRotation.m8 + matTranslation.m7*matRotation.m12; + matTransform.m5 = matTranslation.m4*matRotation.m1 + matTranslation.m5*matRotation.m5 + matTranslation.m6*matRotation.m9 + matTranslation.m7*matRotation.m13; + matTransform.m6 = matTranslation.m4*matRotation.m2 + matTranslation.m5*matRotation.m6 + matTranslation.m6*matRotation.m10 + matTranslation.m7*matRotation.m14; + matTransform.m7 = matTranslation.m4*matRotation.m3 + matTranslation.m5*matRotation.m7 + matTranslation.m6*matRotation.m11 + matTranslation.m7*matRotation.m15; + matTransform.m8 = matTranslation.m8*matRotation.m0 + matTranslation.m9*matRotation.m4 + matTranslation.m10*matRotation.m8 + matTranslation.m11*matRotation.m12; + matTransform.m9 = matTranslation.m8*matRotation.m1 + matTranslation.m9*matRotation.m5 + matTranslation.m10*matRotation.m9 + matTranslation.m11*matRotation.m13; + matTransform.m10 = matTranslation.m8*matRotation.m2 + matTranslation.m9*matRotation.m6 + matTranslation.m10*matRotation.m10 + matTranslation.m11*matRotation.m14; + matTransform.m11 = matTranslation.m8*matRotation.m3 + matTranslation.m9*matRotation.m7 + matTranslation.m10*matRotation.m11 + matTranslation.m11*matRotation.m15; + matTransform.m12 = matTranslation.m12*matRotation.m0 + matTranslation.m13*matRotation.m4 + matTranslation.m14*matRotation.m8 + matTranslation.m15*matRotation.m12; + matTransform.m13 = matTranslation.m12*matRotation.m1 + matTranslation.m13*matRotation.m5 + matTranslation.m14*matRotation.m9 + matTranslation.m15*matRotation.m13; + matTransform.m14 = matTranslation.m12*matRotation.m2 + matTranslation.m13*matRotation.m6 + matTranslation.m14*matRotation.m10 + matTranslation.m15*matRotation.m14; + matTransform.m15 = matTranslation.m12*matRotation.m3 + matTranslation.m13*matRotation.m7 + matTranslation.m14*matRotation.m11 + matTranslation.m15*matRotation.m15; + + camera->target.x = camera->position.x - matTransform.m12; + camera->target.y = camera->position.y - matTransform.m13; + camera->target.z = camera->position.z - matTransform.m14; + + // If movement detected (some key pressed), increase swinging + for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter++; break; } + + // Camera position update + // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' + camera->position.y = CAMERA.playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER; + + camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; + + } break; + case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable + { + camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - + sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + + cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - + sinf(CAMERA.angle.y)*direction[MOVE_BACK] + + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + + sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - + sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + // Camera orientation calculation + CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); + CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); + + // Angle clamp + if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; + + // Camera zoom + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + + camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + + if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + + camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_CUSTOM: break; + default: break; + } +} + +// Set camera pan key to combine with mouse movement (free camera) +void SetCameraPanControl(int keyPan) { CAMERA.panControl = keyPan; } + +// Set camera alt key to combine with mouse movement (free camera) +void SetCameraAltControl(int keyAlt) { CAMERA.altControl = keyAlt; } + +// Set camera smooth zoom key to combine with mouse (free camera) +void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; } + +// Set camera move controls (1st person and 3rd person cameras) +void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown) +{ + CAMERA.moveControl[MOVE_FRONT] = keyFront; + CAMERA.moveControl[MOVE_BACK] = keyBack; + CAMERA.moveControl[MOVE_RIGHT] = keyRight; + CAMERA.moveControl[MOVE_LEFT] = keyLeft; + CAMERA.moveControl[MOVE_UP] = keyUp; + CAMERA.moveControl[MOVE_DOWN] = keyDown; +} + +#endif // CAMERA_IMPLEMENTATION