diff --git a/examples/lighting_blinn_phong.c b/examples/lighting_blinn_phong.c index beb2c4da..46f95148 100644 --- a/examples/lighting_blinn_phong.c +++ b/examples/lighting_blinn_phong.c @@ -1,11 +1,21 @@ /******************************************************************************************* * -* raylib - Phong lighting shader example +* raylib [lighting] example - Basic Phong lighting * -* This example has been created using raylib v1.3.0 (www.raylib.com) +* Welcome to raylib! +* +* To test examples, just press F6 and execute raylib_compile_execute script +* Note that compiled executable is placed in the same folder as .c file +* +* You can find all basic examples on C:\raylib\raylib\examples folder or +* raylib official webpage: www.raylib.com +* +* Enjoy using raylib. :) +* +* This example has been created using raylib 1.3 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2015 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* Copyright (c) 2015 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -22,7 +32,7 @@ int main() const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "raylib [lighting] example - basic blinn-phong lighting"); + InitWindow(screenWidth, screenHeight, "raylib [lighting] example - blinn phong lighting"); SetTargetFPS(60); // Camera initialization diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c new file mode 100644 index 00000000..2f3fffbc --- /dev/null +++ b/examples/physics_basic_rigidbody.c @@ -0,0 +1,144 @@ +/******************************************************************************************* +* +* raylib [physics] example - Basic rigidbody +* +* Welcome to raylib! +* +* To test examples, just press F6 and execute raylib_compile_execute script +* Note that compiled executable is placed in the same folder as .c file +* +* You can find all basic examples on C:\raylib\raylib\examples folder or +* raylib official webpage: www.raylib.com +* +* Enjoy using raylib. :) +* +* This example has been created using raylib 1.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define OBJECT_SIZE 50 +#define PLAYER_INDEX 0 + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [physics] example - basic rigidbody"); + SetTargetFPS(60); // Enable v-sync + InitPhysics(); // Initialize internal physics values (max rigidbodies/colliders available: 1024) + + // Physics initialization + Physics worldPhysics = {true, false, (Vector2){0, -9.81f}}; + + // Set internal physics settings + SetPhysics(worldPhysics); + + // Object initialization + Transform player = (Transform){(Vector2){(screenWidth - OBJECT_SIZE) / 2, (screenHeight - OBJECT_SIZE) / 2}, 0.0f, (Vector2){OBJECT_SIZE, OBJECT_SIZE}}; + AddCollider(PLAYER_INDEX, (Collider){true, RectangleCollider, (Rectangle){player.position.x, player.position.y, player.scale.x, player.scale.y}, 0}); + AddRigidbody(PLAYER_INDEX, (Rigidbody){true, 1.0f, (Vector2){0, 0}, (Vector2){0, 0}, false, false, true, 0.5f, 1.0f}); + + // Floor initialization + // NOTE: floor doesn't need a rigidbody because it's a static physic object, just a collider to collide with other dynamic colliders (with rigidbody) + Transform floor = (Transform){(Vector2){0, screenHeight * 0.8f}, 0.0f, (Vector2){screenWidth, screenHeight * 0.2f}}; + AddCollider(PLAYER_INDEX + 1, (Collider){true, RectangleCollider, (Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, 0}); + + // Object properties initialization + float moveSpeed = 6.0f; + float jumpForce = 4.5f; + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + // Update object physics + // NOTE: all physics detections and reactions are calculated in ApplyPhysics() function (You will live happier :D) + ApplyPhysics(PLAYER_INDEX, &player.position); + + // Check jump button input + if(IsKeyDown(KEY_SPACE) && GetRigidbody(PLAYER_INDEX).isGrounded) + { + // Reset object Y velocity to avoid double jumping cases but keep the same X velocity that it already has + SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){GetRigidbody(PLAYER_INDEX).velocity.x, 0}); + + // Add jumping force in Y axis + AddRigidbodyForce(PLAYER_INDEX, (Vector2){0, jumpForce}); + } + + // Check movement buttons input + if(IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) + { + // Set rigidbody velocity in X based on moveSpeed value and apply the same Y velocity that it already has + SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){moveSpeed, GetRigidbody(PLAYER_INDEX).velocity.y}); + } + else if(IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) + { + // Set rigidbody velocity in X based on moveSpeed negative value and apply the same Y velocity that it already has + SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){-moveSpeed, GetRigidbody(PLAYER_INDEX).velocity.y}); + } + + // Check debug mode toggle button input + if(IsKeyPressed(KEY_P)) + { + // Update program physics value + worldPhysics.debug = !worldPhysics.debug; + + // Update internal physics value + SetPhysics(worldPhysics); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw information + DrawText("Use LEFT / RIGHT to MOVE and SPACE to JUMP", (screenWidth - MeasureText("Use LEFT / RIGHT to MOVE and SPACE to JUMP", 20)) / 2, screenHeight * 0.20f, 20, LIGHTGRAY); + DrawText("Use P to switch DEBUG MODE", (screenWidth - MeasureText("Use P to switch DEBUG MODE", 20)) / 2, screenHeight * 0.3f, 20, LIGHTGRAY); + + // Check if debug mode is enabled + if(worldPhysics.debug) + { + // Draw every internal physics stored collider if it is active + for(int i = 0; i < 2; i++) + { + if(GetCollider(i).enabled) + { + DrawRectangleLines(GetCollider(i).bounds.x, GetCollider(i).bounds.y, GetCollider(i).bounds.width, GetCollider(i).bounds.height, GREEN); + } + } + + } + else + { + // Draw player + DrawRectangleRec((Rectangle){player.position.x, player.position.y, player.scale.x, player.scale.y}, GRAY); + + // Draw floor + DrawRectangleRec((Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, BLACK); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/physics_basic_rigidbody.png b/examples/physics_basic_rigidbody.png new file mode 100644 index 00000000..3d691637 Binary files /dev/null and b/examples/physics_basic_rigidbody.png differ diff --git a/src/libraylib.a b/src/libraylib.a new file mode 100644 index 00000000..5ba9eda7 Binary files /dev/null and b/src/libraylib.a differ diff --git a/src/lighting.c b/src/lighting.c index 98e1696a..5cf2a2ec 100644 --- a/src/lighting.c +++ b/src/lighting.c @@ -42,7 +42,7 @@ //... //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Declarations //---------------------------------------------------------------------------------- // Lights functions diff --git a/src/lighting.h b/src/lighting.h index 67a65d45..a35113c3 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -61,7 +61,7 @@ typedef struct Material { } Material; //---------------------------------------------------------------------------------- -// Module Functions Declaration +// Module Functions Definitions // NOTE: light and material structs uses float pointers instead of vectors to be compatible with SetShaderValue() //---------------------------------------------------------------------------------- // Lights functions diff --git a/src/physics.c b/src/physics.c new file mode 100644 index 00000000..90a8b2e9 --- /dev/null +++ b/src/physics.c @@ -0,0 +1,272 @@ +/********************************************************************************************** +* +* raylib physics engine module - Basic functions to apply physics to 2D objects +* +* Copyright (c) 2015 Victor Fisac and Ramon Santamaria +* +* 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. +* +**********************************************************************************************/ + +//#define PHYSICS_STANDALONE // NOTE: To use the physics module as standalone lib, just uncomment this line + +#if defined(PHYSICS_STANDALONE) + #include "physics.h" +#else + #include "raylib.h" +#endif + +#include +#include + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MAX_ELEMENTS 1024 // Stored rigidbodies and colliders array length +#define DECIMAL_FIX 0.01f // Decimal margin for collision checks (avoid rigidbodies shake) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static Physics physics; +static Collider colliders[MAX_ELEMENTS]; +static Rigidbody rigidbodies[MAX_ELEMENTS]; +static bool collisionChecker = false; + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +void InitPhysics() +{ + for (int i = 0; i < MAX_ELEMENTS; i++) + { + rigidbodies[i].enabled = false; + rigidbodies[i].mass = 0.0f; + rigidbodies[i].velocity = (Vector2){0, 0}; + rigidbodies[i].acceleration = (Vector2){0, 0}; + rigidbodies[i].isGrounded = false; + rigidbodies[i].isContact = false; + rigidbodies[i].friction = 0.0f; + + colliders[i].enabled = false; + colliders[i].bounds = (Rectangle){0, 0, 0, 0}; + colliders[i].radius = 0; + } +} + +void SetPhysics(Physics settings) +{ + physics = settings; + + // To get good results, gravity needs to be 1:10 from original parameter + physics.gravity = (Vector2){physics.gravity.x / 10, physics.gravity.y / 10}; +} + +void AddCollider(int index, Collider collider) +{ + colliders[index] = collider; +} + +void AddRigidbody(int index, Rigidbody rigidbody) +{ + rigidbodies[index] = rigidbody; +} + +void ApplyPhysics(int index, Vector2 *position) +{ + if (rigidbodies[index].enabled) + { + // Apply gravity + rigidbodies[index].velocity.y += rigidbodies[index].acceleration.y; + rigidbodies[index].velocity.x += rigidbodies[index].acceleration.x; + + rigidbodies[index].velocity.y += physics.gravity.y; + rigidbodies[index].velocity.x += physics.gravity.x; + + // Apply friction to velocity + if (rigidbodies[index].isGrounded) + { + if (rigidbodies[index].velocity.x > DECIMAL_FIX) + { + rigidbodies[index].velocity.x -= rigidbodies[index].friction; + } + else if (rigidbodies[index].velocity.x < -DECIMAL_FIX) + { + rigidbodies[index].velocity.x += rigidbodies[index].friction; + } + else + { + rigidbodies[index].velocity.x = 0; + } + } + + if (rigidbodies[index].velocity.y > DECIMAL_FIX) + { + rigidbodies[index].velocity.y -= rigidbodies[index].friction; + } + else if (rigidbodies[index].velocity.y < -DECIMAL_FIX) + { + rigidbodies[index].velocity.y += rigidbodies[index].friction; + } + else + { + rigidbodies[index].velocity.y = 0; + } + + // Apply friction to acceleration + if (rigidbodies[index].isGrounded) + { + if (rigidbodies[index].acceleration.x > DECIMAL_FIX) + { + rigidbodies[index].acceleration.x -= rigidbodies[index].friction; + } + else if (rigidbodies[index].acceleration.x < -DECIMAL_FIX) + { + rigidbodies[index].acceleration.x += rigidbodies[index].friction; + } + else + { + rigidbodies[index].acceleration.x = 0; + } + } + + if (rigidbodies[index].acceleration.y > DECIMAL_FIX) + { + rigidbodies[index].acceleration.y -= rigidbodies[index].friction; + } + else if (rigidbodies[index].acceleration.y < -DECIMAL_FIX) + { + rigidbodies[index].acceleration.y += rigidbodies[index].friction; + } + else + { + rigidbodies[index].acceleration.y = 0; + } + + // Update position vector + position->x += rigidbodies[index].velocity.x; + position->y -= rigidbodies[index].velocity.y; + + // Update collider bounds + colliders[index].bounds.x = position->x; + colliders[index].bounds.y = position->y; + + // Check collision with other colliders + collisionChecker = false; + rigidbodies[index].isContact = false; + for (int j = 0; j < MAX_ELEMENTS; j++) + { + if (index != j) + { + if (colliders[index].enabled && colliders[j].enabled) + { + if (colliders[index].type == RectangleCollider) + { + if (colliders[j].type == RectangleCollider) + { + if (CheckCollisionRecs(colliders[index].bounds, colliders[j].bounds)) + { + collisionChecker = true; + + if ((colliders[index].bounds.y + colliders[index].bounds.height <= colliders[j].bounds.y) == false) + { + rigidbodies[index].isContact = true; + } + } + } + else + { + if (CheckCollisionCircleRec((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, colliders[index].bounds)) + { + collisionChecker = true; + } + } + } + else + { + if (colliders[j].type == RectangleCollider) + { + if (CheckCollisionCircleRec((Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius, colliders[j].bounds)) + { + collisionChecker = true; + } + } + else + { + if (CheckCollisionCircles((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, (Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius)) + { + collisionChecker = true; + } + } + } + } + } + } + + // Update grounded rigidbody state + rigidbodies[index].isGrounded = collisionChecker; + + // Set grounded state if needed (fix overlap and set y velocity) + if (collisionChecker && rigidbodies[index].velocity.y != 0) + { + position->y += rigidbodies[index].velocity.y; + rigidbodies[index].velocity.y = -rigidbodies[index].velocity.y * rigidbodies[index].bounciness; + } + + if (rigidbodies[index].isContact) + { + position->x -= rigidbodies[index].velocity.x; + rigidbodies[index].velocity.x = rigidbodies[index].velocity.x; + } + } +} + +void SetRigidbodyEnabled(int index, bool state) +{ + rigidbodies[index].enabled = state; +} + +void SetRigidbodyVelocity(int index, Vector2 velocity) +{ + rigidbodies[index].velocity.x = velocity.x; + rigidbodies[index].velocity.y = velocity.y; +} + +void AddRigidbodyForce(int index, Vector2 force) +{ + rigidbodies[index].acceleration.x = force.x * rigidbodies[index].mass; + rigidbodies[index].acceleration.y = force.y * rigidbodies[index].mass; +} + +void SetColliderEnabled(int index, bool state) +{ + colliders[index].enabled = state; +} + +Collider GetCollider(int index) +{ + return colliders[index]; +} + +Rigidbody GetRigidbody(int index) +{ + return rigidbodies[index]; +} \ No newline at end of file diff --git a/src/physics.h b/src/physics.h new file mode 100644 index 00000000..211f31a2 --- /dev/null +++ b/src/physics.h @@ -0,0 +1,99 @@ +/********************************************************************************************** +* +* raylib physics engine module - Basic functions to apply physics to 2D objects +* +* Copyright (c) 2015 Victor Fisac and Ramon Santamaria +* +* 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 PHYSICS_H +#define PHYSICS_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum { RectangleCollider, CircleCollider } ColliderType; + +// Physics struct +typedef struct Physics { + bool enabled; + bool debug; // Should be used by programmer for testing purposes + Vector2 gravity; +} Physics; + +// Transform struct +typedef struct Transform { + Vector2 position; + float rotation; + Vector2 scale; +} Transform; + +// Rigidbody struct +typedef struct Rigidbody { + bool enabled; + float mass; + Vector2 acceleration; + Vector2 velocity; + bool isGrounded; + bool isContact; // Avoid freeze player when touching floor + bool applyGravity; + float friction; // 0.0f to 1.0f + float bounciness; // 0.0f to 1.0f +} Rigidbody; + +// Collider struct +typedef struct Collider { + bool enabled; + ColliderType type; + Rectangle bounds; // Just used for RectangleCollider type + int radius; // Just used for CircleCollider type +} Collider; + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +void InitPhysics(); // Initialize all internal physics values +void SetPhysics(Physics settings); // Set physics settings values using Physics data type to overwrite internal physics settings + +void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot +void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot + +void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter +void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody +void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value) +void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value) + +void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider + +Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter +Collider GetCollider(int index); // Returns the internal collider data defined by index parameter + +#ifdef __cplusplus +} +#endif + +#endif // PHYSICS_H \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 04ece42e..4eefa3ea 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -433,6 +433,44 @@ typedef enum { // Camera system modes typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; +// Collider types +typedef enum { RectangleCollider, CircleCollider } ColliderType; + +// Physics struct +typedef struct Physics { + bool enabled; + bool debug; // Should be used by programmer for testing purposes + Vector2 gravity; +} Physics; + +// Transform struct +typedef struct Transform { + Vector2 position; + float rotation; + Vector2 scale; +} Transform; + +// Rigidbody struct +typedef struct Rigidbody { + bool enabled; + float mass; + Vector2 acceleration; + Vector2 velocity; + bool isGrounded; + bool isContact; // Avoid freeze player when touching floor + bool applyGravity; + float friction; // 0.0f to 1.0f + float bounciness; // 0.0f to 1.0f +} Rigidbody; + +// Collider struct +typedef struct Collider { + bool enabled; + ColliderType type; + Rectangle bounds; // Just used for RectangleCollider type + int radius; // Just used for CircleCollider type +} Collider; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -742,6 +780,25 @@ void SetMaterialSpecularColor(Material *material, Vector3 color); // Set m void SetMaterialGlossiness(Material *material, float glossiness); // Set material glossiness value (recommended values: 0 - 100) void SetMaterialNormalDepth(Material *material, float depth); // Set normal map depth (B component from RGB type map scalar multiplier) +//---------------------------------------------------------------------------------- +// Physics System Functions (engine-module: physics) +//---------------------------------------------------------------------------------- +void InitPhysics(); // Initialize all internal physics values +void SetPhysics(Physics settings); // Set physics settings values using Physics data type to overwrite internal physics settings + +void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot +void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot + +void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter +void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody +void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value) +void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value) + +void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider + +Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter +Collider GetCollider(int index); // Returns the internal collider data defined by index parameter + //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) //------------------------------------------------------------------------------------