From 4190689ab9b0c8535499b9f5a673c643e714c841 Mon Sep 17 00:00:00 2001 From: Bensuperpc Date: Sat, 28 May 2022 20:08:32 +0200 Subject: [PATCH 1/5] Update raylib to latest and remove old functions Signed-off-by: Bensuperpc --- examples/CMakeLists.txt | 2 +- include/AudioStream.hpp | 7 ++++++- include/Matrix.hpp | 4 ---- include/Model.hpp | 7 ------- include/Ray.hpp | 7 ------- include/RayCollision.hpp | 7 ------- 6 files changed, 7 insertions(+), 27 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e5a22fa2..088b7fd8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,7 +15,7 @@ if (NOT raylib_FOUND) FetchContent_Declare( raylib GIT_REPOSITORY https://github.com/raysan5/raylib.git - GIT_TAG 4.0.0 + GIT_TAG 1f806b555dd1ecb7e77797ed0de57d62a6bdd6f0 ) FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? diff --git a/include/AudioStream.hpp b/include/AudioStream.hpp index c280c830..84070ca3 100644 --- a/include/AudioStream.hpp +++ b/include/AudioStream.hpp @@ -16,9 +16,10 @@ class AudioStream : public ::AudioStream { } AudioStream(rAudioBuffer* buffer = nullptr, + rAudioProcessor *processor = nullptr, unsigned int sampleRate = 0, unsigned int sampleSize = 0, - unsigned int channels = 0) : ::AudioStream{buffer, sampleRate, sampleSize, channels} { + unsigned int channels = 0) : ::AudioStream{buffer, processor, sampleRate, sampleSize, channels} { // Nothing. } @@ -39,6 +40,7 @@ class AudioStream : public ::AudioStream { set(other); other.buffer = nullptr; + other.processor = nullptr; other.sampleRate = 0; other.sampleSize = 0; other.channels = 0; @@ -49,6 +51,7 @@ class AudioStream : public ::AudioStream { } GETTERSETTER(rAudioBuffer *, Buffer, buffer) + GETTERSETTER(rAudioProcessor *, Processor, processor) GETTERSETTER(unsigned int, SampleRate, sampleRate) GETTERSETTER(unsigned int, SampleSize, sampleSize) GETTERSETTER(unsigned int, Channels, channels) @@ -69,6 +72,7 @@ class AudioStream : public ::AudioStream { set(other); other.buffer = nullptr; + other.processor = nullptr; other.sampleRate = 0; other.sampleSize = 0; other.channels = 0; @@ -180,6 +184,7 @@ class AudioStream : public ::AudioStream { private: inline void set(const ::AudioStream& stream) { buffer = stream.buffer; + processor = stream.processor; sampleRate = stream.sampleRate; sampleSize = stream.sampleSize; channels = stream.channels; diff --git a/include/Matrix.hpp b/include/Matrix.hpp index 203bedc9..3ff5c590 100644 --- a/include/Matrix.hpp +++ b/include/Matrix.hpp @@ -94,10 +94,6 @@ class Matrix : public ::Matrix { return ::MatrixInvert(*this); } - inline Matrix Normalize() const { - return ::MatrixNormalize(*this); - } - static Matrix Identity() { return ::MatrixIdentity(); } diff --git a/include/Model.hpp b/include/Model.hpp index 9b26fba0..619a0dde 100644 --- a/include/Model.hpp +++ b/include/Model.hpp @@ -111,13 +111,6 @@ class Model : public ::Model { return *this; } - /** - * Get collision info between ray and model - */ - inline RayCollision GetCollision(const ::Ray& ray) const { - return ::GetRayCollisionModel(ray, *this); - } - /** * Update model animation pose */ diff --git a/include/Ray.hpp b/include/Ray.hpp index 7bf5cbe4..1cd6a187 100644 --- a/include/Ray.hpp +++ b/include/Ray.hpp @@ -60,13 +60,6 @@ class Ray : public ::Ray { return GetRayCollisionBox(*this, box).hit; } - /** - * Get collision info between ray and model - */ - inline RayCollision GetCollision(const ::Model& model) const { - return GetRayCollisionModel(*this, model); - } - /** * Get collision information between ray and mesh */ diff --git a/include/RayCollision.hpp b/include/RayCollision.hpp index 2cda9ac1..49d26424 100644 --- a/include/RayCollision.hpp +++ b/include/RayCollision.hpp @@ -26,13 +26,6 @@ class RayCollision : public ::RayCollision { set(::GetRayCollisionMesh(ray, mesh, transform)); } - /** - * Get collision info between ray and model - */ - RayCollision(const ::Ray& ray, const ::Model& model) { - set(::GetRayCollisionModel(ray, model)); - } - /** * Get collision info between ray and triangle */ From f4f2d5850b888fc10a7d5320e3857c1946a74cea Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Fri, 12 Aug 2022 12:35:15 -0400 Subject: [PATCH 2/5] Update to raylib 4.2 --- examples/CMakeLists.txt | 2 +- examples/core/core_drop_files.cpp | 2 +- examples/physics/physac.h | 2142 +++++++++++++++++++++++++++ examples/physics/physics_demo.cpp | 11 +- examples/text/text_font_filters.cpp | 2 +- include/Functions.hpp | 18 +- include/Mesh.hpp | 8 - include/Physics.hpp | 54 +- include/RayCollision.hpp | 7 - include/Sound.hpp | 2 +- include/physac.hpp | 15 - projects/CMake/CMakeLists.txt | 2 +- tests/raylib_cpp_test.cpp | 4 +- 13 files changed, 2190 insertions(+), 79 deletions(-) create mode 100644 examples/physics/physac.h delete mode 100644 include/physac.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 088b7fd8..55277c80 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,7 +15,7 @@ if (NOT raylib_FOUND) FetchContent_Declare( raylib GIT_REPOSITORY https://github.com/raysan5/raylib.git - GIT_TAG 1f806b555dd1ecb7e77797ed0de57d62a6bdd6f0 + GIT_TAG 4.2.0 ) FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? diff --git a/examples/core/core_drop_files.cpp b/examples/core/core_drop_files.cpp index a954bc1a..55d1f86c 100644 --- a/examples/core/core_drop_files.cpp +++ b/examples/core/core_drop_files.cpp @@ -30,7 +30,7 @@ int main() { while (!window.ShouldClose()) { // Detect window close button or ESC key // Update //---------------------------------------------------------------------------------- - droppedFiles = raylib::GetDroppedFiles(); + droppedFiles = raylib::LoadDroppedFiles(); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/physics/physac.h b/examples/physics/physac.h new file mode 100644 index 00000000..47b38a78 --- /dev/null +++ b/examples/physics/physac.h @@ -0,0 +1,2142 @@ +/********************************************************************************************** +* +* Physac v1.1 - 2D Physics library for videogames +* +* DESCRIPTION: +* +* Physac is a small 2D physics library written in pure C. The engine uses a fixed time-step thread loop +* to simluate physics. A physics step contains the following phases: get collision information, +* apply dynamics, collision solving and position correction. It uses a very simple struct for physic +* bodies with a position vector to be used in any 3D rendering API. +* +* CONFIGURATION: +* +* #define PHYSAC_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 PHYSAC_STATIC (defined by default) +* The generated implementation will stay private inside implementation file and all +* internal symbols and functions will only be visible inside that file. +* +* #define PHYSAC_NO_THREADS +* The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). +* It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. +* +* #define PHYSAC_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details). +* +* #define PHYSAC_DEBUG +* Traces log messages when creating and destroying physics bodies and detects errors in physics +* calculations and reference exceptions; it is useful for debug purposes +* +* #define PHYSAC_MALLOC() +* #define PHYSAC_FREE() +* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. +* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. +* +* +* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. +* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) +* +* Use the following code to compile: +* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lpthread -lopengl32 -lgdi32 -lwinmm -std=c99 +* +* VERY THANKS TO: +* - raysan5: helped with library design +* - ficoos: added support for Linux +* - R8D8: added support for Linux +* - jubalh: fixed implementation of time calculations +* - a3f: fixed implementation of time calculations +* - define-private-public: added support for OSX +* - pamarcos: fixed implementation of physics steps +* - noshbar: fixed some memory leaks +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2016-2020 Victor Fisac (github: @victorfisac) +* +* 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. +* +**********************************************************************************************/ + +#if !defined(PHYSAC_H) +#define PHYSAC_H + +// #define PHYSAC_STATIC +// #define PHYSAC_NO_THREADS +// #define PHYSAC_STANDALONE +// #define PHYSAC_DEBUG + +#if defined(PHYSAC_STATIC) + #define PHYSACDEF static // Functions just visible to module including this file +#else + #if defined(__cplusplus) + #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define PHYSACDEF extern // Functions visible from other files + #endif +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define PHYSAC_MAX_BODIES 64 +#define PHYSAC_MAX_MANIFOLDS 4096 +#define PHYSAC_MAX_VERTICES 24 +#define PHYSAC_CIRCLE_VERTICES 24 + +#define PHYSAC_COLLISION_ITERATIONS 100 +#define PHYSAC_PENETRATION_ALLOWANCE 0.05f +#define PHYSAC_PENETRATION_CORRECTION 0.4f + +#define PHYSAC_PI 3.14159265358979323846 +#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) + +#define PHYSAC_MALLOC(size) malloc(size) +#define PHYSAC_FREE(ptr) free(ptr) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Below types are required for PHYSAC_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(PHYSAC_STANDALONE) + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Boolean type + #if !defined(_STDBOOL_H) + typedef enum { false, true } bool; + #define _STDBOOL_H + #endif +#endif + +typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; + +// Previously defined to be used in PhysicsShape struct as circular dependencies +typedef struct PhysicsBodyData *PhysicsBody; + +// Mat2 type (used for polygon shape rotation matrix) +typedef struct Mat2 { + float m00; + float m01; + float m10; + float m11; +} Mat2; + +typedef struct PolygonData { + unsigned int vertexCount; // Current used vertex and normals count + Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors + Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors +} PolygonData; + +typedef struct PhysicsShape { + PhysicsShapeType type; // Physics shape type (circle or polygon) + PhysicsBody body; // Shape physics body reference + float radius; // Circle shape radius (used for circle shapes) + Mat2 transform; // Vertices transform matrix 2x2 + PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) +} PhysicsShape; + +typedef struct PhysicsBodyData { + unsigned int id; // Reference unique identifier + bool enabled; // Enabled dynamics state (collisions are calculated anyway) + Vector2 position; // Physics body shape pivot + Vector2 velocity; // Current linear velocity applied to position + Vector2 force; // Current linear force (reset to 0 every step) + float angularVelocity; // Current angular velocity applied to orient + float torque; // Current angular force (reset to 0 every step) + float orient; // Rotation in radians + float inertia; // Moment of inertia + float inverseInertia; // Inverse value of inertia + float mass; // Physics body mass + float inverseMass; // Inverse value of mass + float staticFriction; // Friction when the body has not movement (0 to 1) + float dynamicFriction; // Friction when the body has movement (0 to 1) + float restitution; // Restitution coefficient of the body (0 to 1) + bool useGravity; // Apply gravity force to dynamics + bool isGrounded; // Physics grounded on other body state + bool freezeOrient; // Physics rotation constraint + PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) +} PhysicsBodyData; + +typedef struct PhysicsManifoldData { + unsigned int id; // Reference unique identifier + PhysicsBody bodyA; // Manifold first physics body reference + PhysicsBody bodyB; // Manifold second physics body reference + float penetration; // Depth of penetration from collision + Vector2 normal; // Normal direction vector from 'a' to 'b' + Vector2 contacts[2]; // Points of contact during collision + unsigned int contactsCount; // Current collision number of contacts + float restitution; // Mixed restitution during collision + float dynamicFriction; // Mixed dynamic friction during collision + float staticFriction; // Mixed static friction during collision +} PhysicsManifoldData, *PhysicsManifold; + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread +PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop +PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default +PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled +PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force +PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters +PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body +PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body +PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force +PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies +PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index +PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) +PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape +PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) +PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter +PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body +PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread + +#if defined(__cplusplus) +} +#endif + +#endif // PHYSAC_H + +/*********************************************************************************** +* +* PHYSAC IMPLEMENTATION +* +************************************************************************************/ + +#if defined(PHYSAC_IMPLEMENTATION) + +#if !defined(PHYSAC_NO_THREADS) + #include // Required for: pthread_t, pthread_create() +#endif + +#if defined(PHYSAC_DEBUG) + #include // Required for: printf() +#endif + +#include // Required for: malloc(), free(), srand(), rand() +#include // Required for: cosf(), sinf(), fabs(), sqrtf() +#include // Required for: uint64_t + +#if !defined(PHYSAC_STANDALONE) + #include "raymath.h" // Required for: Vector2Add(), Vector2Subtract() +#endif + +// Time management functionality +#include // Required for: time(), clock_gettime() +#if defined(_WIN32) + // Functions required to query time on Windows + #if defined(__cplusplus) + extern "C" { // Prevents name mangling of functions + #endif + int __stdcall QueryPerformanceCounter(unsigned long long int* lpPerformanceCount); + int __stdcall QueryPerformanceFrequency(unsigned long long int* lpFrequency); + #if defined(__cplusplus) + } + #endif +#elif defined(__linux__) + #if _POSIX_C_SOURCE < 199309L + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #endif + #include // Required for: timespec +#elif defined(__APPLE__) // macOS also defines __MACH__ + #include // Required for: mach_absolute_time() +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define min(a,b) (((a)<(b))?(a):(b)) +#define max(a,b) (((a)>(b))?(a):(b)) +#define PHYSAC_FLT_MAX 3.402823466e+38f +#define PHYSAC_EPSILON 0.000001f +#define PHYSAC_K 1.0f/3.0f +#define PHYSAC_VECTOR_ZERO (Vector2){ 0.0f, 0.0f } + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +#if !defined(PHYSAC_NO_THREADS) +static pthread_t physicsThreadId; // Physics thread id +#endif +static unsigned int usedMemory = 0; // Total allocated dynamic memory +static bool physicsThreadEnabled = false; // Physics thread enabled state +static double baseTime = 0.0; // Offset time for MONOTONIC clock +static double startTime = 0.0; // Start time in milliseconds +static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds +static double currentTime = 0.0; // Current time in milliseconds +static uint64_t frequency = 0; // Hi-res clock frequency + +static double accumulator = 0.0; // Physics time step delta time accumulator +static unsigned int stepsCount = 0; // Total physics steps processed +static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force +static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array +static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter +static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array +static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization +static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot +static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions +static void *PhysicsLoop(void *arg); // Physics loop thread function +static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections) +static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization +static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision +static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold +static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies +static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies +static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies +static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies +static void SolveDifferentShapes(PhysicsManifold manifold, PhysicsBody bodyA, PhysicsBody bodyB); // Solve collision between two different types of shapes +static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies +static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity +static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions +static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions +static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces +static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information +static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration +static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face +static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces +static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range +static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points + +static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer +static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds +static double GetCurrentTime(void); // Get current time measure in milliseconds + +// Math functions +static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value +static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors +static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector +static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors +static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors +static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector +#if defined(PHYSAC_STANDALONE) +static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors +static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors +#endif + +static Mat2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value +static void Mat2Set(Mat2 *matrix, float radians); // Set values from radians to a created matrix 2x2 +static inline Mat2 Mat2Transpose(Mat2 matrix); // Returns the transpose of a given matrix 2x2 +static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2 + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +// Initializes physics values, pointers and creates physics loop thread +PHYSACDEF void InitPhysics(void) +{ + #if !defined(PHYSAC_NO_THREADS) + // NOTE: if defined, user will need to create a thread for PhysicsThread function manually + // Create physics thread using POSIXS thread libraries + pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL); + #endif + + // Initialize high resolution timer + InitTimer(); + + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] physics module initialized successfully\n"); + #endif + + accumulator = 0.0; +} + +// Returns true if physics thread is currently enabled +PHYSACDEF bool IsPhysicsEnabled(void) +{ + return physicsThreadEnabled; +} + +// Sets physics global gravity force +PHYSACDEF void SetPhysicsGravity(float x, float y) +{ + gravityForce.x = x; + gravityForce.y = y; +} + +// Creates a new circle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) +{ + PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + usedMemory += sizeof(PhysicsBodyData); + + int newId = FindAvailableBodyIndex(); + if (newId != -1) + { + // Initialize new body with generic values + newBody->id = newId; + newBody->enabled = true; + newBody->position = pos; + newBody->velocity = PHYSAC_VECTOR_ZERO; + newBody->force = PHYSAC_VECTOR_ZERO; + newBody->angularVelocity = 0.0f; + newBody->torque = 0.0f; + newBody->orient = 0.0f; + newBody->shape.type = PHYSICS_CIRCLE; + newBody->shape.body = newBody; + newBody->shape.radius = radius; + newBody->shape.transform = Mat2Radians(0.0f); + newBody->shape.vertexData = (PolygonData) { 0 }; + + newBody->mass = PHYSAC_PI*radius*radius*density; + newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); + newBody->inertia = newBody->mass*radius*radius; + newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); + newBody->staticFriction = 0.4f; + newBody->dynamicFriction = 0.2f; + newBody->restitution = 0.0f; + newBody->useGravity = true; + newBody->isGrounded = false; + newBody->freezeOrient = false; + + // Add new body to bodies pointers array and update bodies count + bodies[physicsBodiesCount] = newBody; + physicsBodiesCount++; + + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); + #endif + + return newBody; +} + +// Creates a new rectangle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) +{ + PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + usedMemory += sizeof(PhysicsBodyData); + + int newId = FindAvailableBodyIndex(); + if (newId != -1) + { + // Initialize new body with generic values + newBody->id = newId; + newBody->enabled = true; + newBody->position = pos; + newBody->velocity = (Vector2){ 0.0f }; + newBody->force = (Vector2){ 0.0f }; + newBody->angularVelocity = 0.0f; + newBody->torque = 0.0f; + newBody->orient = 0.0f; + newBody->shape.type = PHYSICS_POLYGON; + newBody->shape.body = newBody; + newBody->shape.radius = 0.0f; + newBody->shape.transform = Mat2Radians(0.0f); + newBody->shape.vertexData = CreateRectanglePolygon(pos, (Vector2){ width, height }); + + // Calculate centroid and moment of inertia + Vector2 center = { 0.0f, 0.0f }; + float area = 0.0f; + float inertia = 0.0f; + + for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 p1 = newBody->shape.vertexData.positions[i]; + int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 p2 = newBody->shape.vertexData.positions[nextIndex]; + + float D = MathCrossVector2(p1, p2); + float triangleArea = D/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); + center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); + + float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; + float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; + inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) + // Note: this is not really necessary + for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + { + newBody->shape.vertexData.positions[i].x -= center.x; + newBody->shape.vertexData.positions[i].y -= center.y; + } + + newBody->mass = density*area; + newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); + newBody->inertia = density*inertia; + newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); + newBody->staticFriction = 0.4f; + newBody->dynamicFriction = 0.2f; + newBody->restitution = 0.0f; + newBody->useGravity = true; + newBody->isGrounded = false; + newBody->freezeOrient = false; + + // Add new body to bodies pointers array and update bodies count + bodies[physicsBodiesCount] = newBody; + physicsBodiesCount++; + + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); + #endif + + return newBody; +} + +// Creates a new polygon physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) +{ + PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + usedMemory += sizeof(PhysicsBodyData); + + int newId = FindAvailableBodyIndex(); + if (newId != -1) + { + // Initialize new body with generic values + newBody->id = newId; + newBody->enabled = true; + newBody->position = pos; + newBody->velocity = PHYSAC_VECTOR_ZERO; + newBody->force = PHYSAC_VECTOR_ZERO; + newBody->angularVelocity = 0.0f; + newBody->torque = 0.0f; + newBody->orient = 0.0f; + newBody->shape.type = PHYSICS_POLYGON; + newBody->shape.body = newBody; + newBody->shape.transform = Mat2Radians(0.0f); + newBody->shape.vertexData = CreateRandomPolygon(radius, sides); + + // Calculate centroid and moment of inertia + Vector2 center = { 0.0f, 0.0f }; + float area = 0.0f; + float inertia = 0.0f; + + for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 position1 = newBody->shape.vertexData.positions[i]; + int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 position2 = newBody->shape.vertexData.positions[nextIndex]; + + float cross = MathCrossVector2(position1, position2); + float triangleArea = cross/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(position1.x + position2.x); + center.y += triangleArea*PHYSAC_K*(position1.y + position2.y); + + float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x; + float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y; + inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) + // Note: this is not really necessary + for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) + { + newBody->shape.vertexData.positions[i].x -= center.x; + newBody->shape.vertexData.positions[i].y -= center.y; + } + + newBody->mass = density*area; + newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); + newBody->inertia = density*inertia; + newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); + newBody->staticFriction = 0.4f; + newBody->dynamicFriction = 0.2f; + newBody->restitution = 0.0f; + newBody->useGravity = true; + newBody->isGrounded = false; + newBody->freezeOrient = false; + + // Add new body to bodies pointers array and update bodies count + bodies[physicsBodiesCount] = newBody; + physicsBodiesCount++; + + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); + #endif + + return newBody; +} + +// Adds a force to a physics body +PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) +{ + if (body != NULL) + body->force = Vector2Add(body->force, force); +} + +// Adds an angular force to a physics body +PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) +{ + if (body != NULL) + body->torque += amount; +} + +// Shatters a polygon shape physics body to little physics bodies with explosion force +PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) +{ + if (body != NULL) + { + if (body->shape.type == PHYSICS_POLYGON) + { + PolygonData vertexData = body->shape.vertexData; + bool collision = false; + + for (int i = 0; i < vertexData.vertexCount; i++) + { + Vector2 positionA = body->position; + Vector2 positionB = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[i])); + int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); + Vector2 positionC = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[nextIndex])); + + // Check collision between each triangle + float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ + ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); + + float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/ + ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); + + float gamma = 1.0f - alpha - beta; + + if ((alpha > 0.0f) && (beta > 0.0f) && (gamma > 0.0f)) + { + collision = true; + break; + } + } + + if (collision) + { + int count = vertexData.vertexCount; + Vector2 bodyPos = body->position; + Vector2 *vertices = (Vector2*)PHYSAC_MALLOC(sizeof(Vector2) * count); + Mat2 trans = body->shape.transform; + + for (int i = 0; i < count; i++) + vertices[i] = vertexData.positions[i]; + + // Destroy shattered physics body + DestroyPhysicsBody(body); + + for (int i = 0; i < count; i++) + { + int nextIndex = (((i + 1) < count) ? (i + 1) : 0); + Vector2 center = TriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); + center = Vector2Add(bodyPos, center); + Vector2 offset = Vector2Subtract(center, bodyPos); + + PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values + + PolygonData newData = { 0 }; + newData.vertexCount = 3; + + newData.positions[0] = Vector2Subtract(vertices[i], offset); + newData.positions[1] = Vector2Subtract(vertices[nextIndex], offset); + newData.positions[2] = Vector2Subtract(position, center); + + // Separate vertices to avoid unnecessary physics collisions + newData.positions[0].x *= 0.95f; + newData.positions[0].y *= 0.95f; + newData.positions[1].x *= 0.95f; + newData.positions[1].y *= 0.95f; + newData.positions[2].x *= 0.95f; + newData.positions[2].y *= 0.95f; + + // Calculate polygon faces normals + for (int j = 0; j < newData.vertexCount; j++) + { + int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0); + Vector2 face = Vector2Subtract(newData.positions[nextVertex], newData.positions[j]); + + newData.normals[j] = (Vector2){ face.y, -face.x }; + MathNormalize(&newData.normals[j]); + } + + // Apply computed vertex data to new physics body shape + newBody->shape.vertexData = newData; + newBody->shape.transform = trans; + + // Calculate centroid and moment of inertia + center = PHYSAC_VECTOR_ZERO; + float area = 0.0f; + float inertia = 0.0f; + + for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 p1 = newBody->shape.vertexData.positions[j]; + int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0); + Vector2 p2 = newBody->shape.vertexData.positions[nextVertex]; + + float D = MathCrossVector2(p1, p2); + float triangleArea = D/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); + center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); + + float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; + float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; + inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + newBody->mass = area; + newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); + newBody->inertia = inertia; + newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); + + // Calculate explosion force direction + Vector2 pointA = newBody->position; + Vector2 pointB = Vector2Subtract(newData.positions[1], newData.positions[0]); + pointB.x /= 2.0f; + pointB.y /= 2.0f; + Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.positions[0], pointB)), newBody->position); + MathNormalize(&forceDirection); + forceDirection.x *= force; + forceDirection.y *= force; + + // Apply force to new physics body + PhysicsAddForce(newBody, forceDirection); + } + + PHYSAC_FREE(vertices); + } + } + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error when trying to shatter a null reference physics body"); + #endif +} + +// Returns the current amount of created physics bodies +PHYSACDEF int GetPhysicsBodiesCount(void) +{ + return physicsBodiesCount; +} + +// Returns a physics body of the bodies pool at a specific index +PHYSACDEF PhysicsBody GetPhysicsBody(int index) +{ + if (index < physicsBodiesCount) + { + if (bodies[index] == NULL) + { + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] error when trying to get a null reference physics body"); + #endif + } + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] physics body index is out of bounds"); + #endif + + return bodies[index]; +} + +// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) +PHYSACDEF int GetPhysicsShapeType(int index) +{ + int result = -1; + + if (index < physicsBodiesCount) + { + if (bodies[index] != NULL) + result = bodies[index]->shape.type; + + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error when trying to get a null reference physics body"); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] physics body index is out of bounds"); + #endif + + return result; +} + +// Returns the amount of vertices of a physics body shape +PHYSACDEF int GetPhysicsShapeVerticesCount(int index) +{ + int result = 0; + + if (index < physicsBodiesCount) + { + if (bodies[index] != NULL) + { + switch (bodies[index]->shape.type) + { + case PHYSICS_CIRCLE: result = PHYSAC_CIRCLE_VERTICES; break; + case PHYSICS_POLYGON: result = bodies[index]->shape.vertexData.vertexCount; break; + default: break; + } + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error when trying to get a null reference physics body"); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] physics body index is out of bounds"); + #endif + + return result; +} + +// Returns transformed position of a body shape (body position + vertex transformed position) +PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) +{ + Vector2 position = { 0.0f, 0.0f }; + + if (body != NULL) + { + switch (body->shape.type) + { + case PHYSICS_CIRCLE: + { + position.x = body->position.x + cosf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + position.y = body->position.y + sinf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + } break; + case PHYSICS_POLYGON: + { + PolygonData vertexData = body->shape.vertexData; + position = Vector2Add(body->position, Mat2MultiplyVector2(body->shape.transform, vertexData.positions[vertex])); + } break; + default: break; + } + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error when trying to get a null reference physics body"); + #endif + + return position; +} + +// Sets physics body shape transform based on radians parameter +PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) +{ + if (body != NULL) + { + body->orient = radians; + + if (body->shape.type == PHYSICS_POLYGON) + body->shape.transform = Mat2Radians(radians); + } +} + +// Unitializes and destroys a physics body +PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) +{ + if (body != NULL) + { + int id = body->id; + int index = -1; + + for (int i = 0; i < physicsBodiesCount; i++) + { + if (bodies[i]->id == id) + { + index = i; + break; + } + } + + if (index == -1) + { + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] Not possible to find body id %i in pointers array\n", id); + #endif + return; + } + + // Free body allocated memory + PHYSAC_FREE(body); + usedMemory -= sizeof(PhysicsBodyData); + bodies[index] = NULL; + + // Reorder physics bodies pointers array and its catched index + for (int i = index; i < physicsBodiesCount; i++) + { + if ((i + 1) < physicsBodiesCount) + bodies[i] = bodies[i + 1]; + } + + // Update physics bodies count + physicsBodiesCount--; + + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] destroyed physics body id %i\n", id); + #endif + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error trying to destroy a null referenced body\n"); + #endif +} + +// Unitializes physics pointers and exits physics loop thread +PHYSACDEF void ClosePhysics(void) +{ + // Exit physics loop thread + physicsThreadEnabled = false; + + #if !defined(PHYSAC_NO_THREADS) + pthread_join(physicsThreadId, NULL); + #endif + + // Unitialize physics manifolds dynamic memory allocations + for (int i = physicsManifoldsCount - 1; i >= 0; i--) + DestroyPhysicsManifold(contacts[i]); + + // Unitialize physics bodies dynamic memory allocations + for (int i = physicsBodiesCount - 1; i >= 0; i--) + DestroyPhysicsBody(bodies[i]); + + #if defined(PHYSAC_DEBUG) + if (physicsBodiesCount > 0 || usedMemory != 0) + printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); + else if (physicsManifoldsCount > 0 || usedMemory != 0) + printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); + else + printf("[PHYSAC] physics module closed successfully\n"); + #endif +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- +// Finds a valid index for a new physics body initialization +static int FindAvailableBodyIndex() +{ + int index = -1; + for (int i = 0; i < PHYSAC_MAX_BODIES; i++) + { + int currentId = i; + + // Check if current id already exist in other physics body + for (int k = 0; k < physicsBodiesCount; k++) + { + if (bodies[k]->id == currentId) + { + currentId++; + break; + } + } + + // If it is not used, use it as new physics body id + if (currentId == i) + { + index = i; + break; + } + } + + return index; +} + +// Creates a random polygon shape with max vertex distance from polygon pivot +static PolygonData CreateRandomPolygon(float radius, int sides) +{ + PolygonData data = { 0 }; + data.vertexCount = sides; + + // Calculate polygon vertices positions + for (int i = 0; i < data.vertexCount; i++) + { + data.positions[i].x = cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + data.positions[i].y = sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + } + + // Calculate polygon faces normals + for (int i = 0; i < data.vertexCount; i++) + { + int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); + Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = (Vector2){ face.y, -face.x }; + MathNormalize(&data.normals[i]); + } + + return data; +} + +// Creates a rectangle polygon shape based on a min and max positions +static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size) +{ + PolygonData data = { 0 }; + data.vertexCount = 4; + + // Calculate polygon vertices positions + data.positions[0] = (Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; + data.positions[1] = (Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; + data.positions[2] = (Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; + data.positions[3] = (Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; + + // Calculate polygon faces normals + for (int i = 0; i < data.vertexCount; i++) + { + int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); + Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = (Vector2){ face.y, -face.x }; + MathNormalize(&data.normals[i]); + } + + return data; +} + +// Physics loop thread function +static void *PhysicsLoop(void *arg) +{ + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] physics thread created successfully\n"); + #endif + + // Initialize physics loop thread values + physicsThreadEnabled = true; + + // Physics update loop + while (physicsThreadEnabled) + { + RunPhysicsStep(); + } + + return NULL; +} + +// Physics steps calculations (dynamics, collisions and position corrections) +static void PhysicsStep(void) +{ + // Update current steps count + stepsCount++; + + // Clear previous generated collisions information + for (int i = physicsManifoldsCount - 1; i >= 0; i--) + { + PhysicsManifold manifold = contacts[i]; + + if (manifold != NULL) + DestroyPhysicsManifold(manifold); + } + + // Reset physics bodies grounded state + for (int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + body->isGrounded = false; + } + + // Generate new collision information + for (int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody bodyA = bodies[i]; + + if (bodyA != NULL) + { + for (int j = i + 1; j < physicsBodiesCount; j++) + { + PhysicsBody bodyB = bodies[j]; + + if (bodyB != NULL) + { + if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) + continue; + + PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); + SolvePhysicsManifold(manifold); + + if (manifold->contactsCount > 0) + { + // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot + PhysicsManifold newManifold = CreatePhysicsManifold(bodyA, bodyB); + newManifold->penetration = manifold->penetration; + newManifold->normal = manifold->normal; + newManifold->contacts[0] = manifold->contacts[0]; + newManifold->contacts[1] = manifold->contacts[1]; + newManifold->contactsCount = manifold->contactsCount; + newManifold->restitution = manifold->restitution; + newManifold->dynamicFriction = manifold->dynamicFriction; + newManifold->staticFriction = manifold->staticFriction; + } + } + } + } + } + + // Integrate forces to physics bodies + for (int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + + if (body != NULL) + IntegratePhysicsForces(body); + } + + // Initialize physics manifolds to solve collisions + for (int i = 0; i < physicsManifoldsCount; i++) + { + PhysicsManifold manifold = contacts[i]; + + if (manifold != NULL) + InitializePhysicsManifolds(manifold); + } + + // Integrate physics collisions impulses to solve collisions + for (int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) + { + for (int j = 0; j < physicsManifoldsCount; j++) + { + PhysicsManifold manifold = contacts[i]; + + if (manifold != NULL) + IntegratePhysicsImpulses(manifold); + } + } + + // Integrate velocity to physics bodies + for (int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + + if (body != NULL) + IntegratePhysicsVelocity(body); + } + + // Correct physics bodies positions based on manifolds collision information + for (int i = 0; i < physicsManifoldsCount; i++) + { + PhysicsManifold manifold = contacts[i]; + + if (manifold != NULL) + CorrectPhysicsPositions(manifold); + } + + // Clear physics bodies forces + for (int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + + if (body != NULL) + { + body->force = PHYSAC_VECTOR_ZERO; + body->torque = 0.0f; + } + } +} + +// Wrapper to ensure PhysicsStep is run with at a fixed time step +PHYSACDEF void RunPhysicsStep(void) +{ + // Calculate current time + currentTime = GetCurrentTime(); + + // Calculate current delta time + const double delta = currentTime - startTime; + + // Store the time elapsed since the last frame began + accumulator += delta; + + // Fixed time stepping loop + while (accumulator >= deltaTime) + { + PhysicsStep(); + accumulator -= deltaTime; + } + + // Record the starting of this frame + startTime = currentTime; +} + +PHYSACDEF void SetPhysicsTimeStep(double delta) +{ + deltaTime = delta; +} + +// Finds a valid index for a new manifold initialization +static int FindAvailableManifoldIndex() +{ + int index = -1; + for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++) + { + int currentId = i; + + // Check if current id already exist in other physics body + for (int k = 0; k < physicsManifoldsCount; k++) + { + if (contacts[k]->id == currentId) + { + currentId++; + break; + } + } + + // If it is not used, use it as new physics body id + if (currentId == i) + { + index = i; + break; + } + } + + return index; +} + +// Creates a new physics manifold to solve collision +static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) +{ + PhysicsManifold newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); + usedMemory += sizeof(PhysicsManifoldData); + + int newId = FindAvailableManifoldIndex(); + if (newId != -1) + { + // Initialize new manifold with generic values + newManifold->id = newId; + newManifold->bodyA = a; + newManifold->bodyB = b; + newManifold->penetration = 0; + newManifold->normal = PHYSAC_VECTOR_ZERO; + newManifold->contacts[0] = PHYSAC_VECTOR_ZERO; + newManifold->contacts[1] = PHYSAC_VECTOR_ZERO; + newManifold->contactsCount = 0; + newManifold->restitution = 0.0f; + newManifold->dynamicFriction = 0.0f; + newManifold->staticFriction = 0.0f; + + // Add new body to bodies pointers array and update bodies count + contacts[physicsManifoldsCount] = newManifold; + physicsManifoldsCount++; + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] new physics manifold creation failed because there is any available id to use\n"); + #endif + + return newManifold; +} + +// Unitializes and destroys a physics manifold +static void DestroyPhysicsManifold(PhysicsManifold manifold) +{ + if (manifold != NULL) + { + int id = manifold->id; + int index = -1; + + for (int i = 0; i < physicsManifoldsCount; i++) + { + if (contacts[i]->id == id) + { + index = i; + break; + } + } + + if (index == -1) + { + #if defined(PHYSAC_DEBUG) + printf("[PHYSAC] Not possible to manifold id %i in pointers array\n", id); + #endif + return; + } + + // Free manifold allocated memory + PHYSAC_FREE(manifold); + usedMemory -= sizeof(PhysicsManifoldData); + contacts[index] = NULL; + + // Reorder physics manifolds pointers array and its catched index + for (int i = index; i < physicsManifoldsCount; i++) + { + if ((i + 1) < physicsManifoldsCount) + contacts[i] = contacts[i + 1]; + } + + // Update physics manifolds count + physicsManifoldsCount--; + } + #if defined(PHYSAC_DEBUG) + else + printf("[PHYSAC] error trying to destroy a null referenced manifold\n"); + #endif +} + +// Solves a created physics manifold between two physics bodies +static void SolvePhysicsManifold(PhysicsManifold manifold) +{ + switch (manifold->bodyA->shape.type) + { + case PHYSICS_CIRCLE: + { + switch (manifold->bodyB->shape.type) + { + case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break; + case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break; + default: break; + } + } break; + case PHYSICS_POLYGON: + { + switch (manifold->bodyB->shape.type) + { + case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break; + case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break; + default: break; + } + } break; + default: break; + } + + // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds + if (!manifold->bodyB->isGrounded) + manifold->bodyB->isGrounded = (manifold->normal.y < 0); +} + +// Solves collision between two circle shape physics bodies +static void SolveCircleToCircle(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + // Calculate translational vector, which is normal + Vector2 normal = Vector2Subtract(bodyB->position, bodyA->position); + + float distSqr = MathLenSqr(normal); + float radius = bodyA->shape.radius + bodyB->shape.radius; + + // Check if circles are not in contact + if (distSqr >= radius*radius) + { + manifold->contactsCount = 0; + return; + } + + float distance = sqrtf(distSqr); + manifold->contactsCount = 1; + + if (distance == 0.0f) + { + manifold->penetration = bodyA->shape.radius; + manifold->normal = (Vector2){ 1.0f, 0.0f }; + manifold->contacts[0] = bodyA->position; + } + else + { + manifold->penetration = radius - distance; + manifold->normal = (Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathNormalize() due to sqrt is already performed + manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + } + + // Update physics body grounded state if normal direction is down + if (!bodyA->isGrounded) + bodyA->isGrounded = (manifold->normal.y < 0); +} + +// Solves collision between a circle to a polygon shape physics bodies +static void SolveCircleToPolygon(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + SolveDifferentShapes(manifold, bodyA, bodyB); +} + +// Solves collision between a circle to a polygon shape physics bodies +static void SolvePolygonToCircle(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + SolveDifferentShapes(manifold, bodyB, bodyA); + + manifold->normal.x *= -1.0f; + manifold->normal.y *= -1.0f; +} + +// Solve collision between two different types of shapes +static void SolveDifferentShapes(PhysicsManifold manifold, PhysicsBody bodyA, PhysicsBody bodyB) +{ + manifold->contactsCount = 0; + + // Transform circle center to polygon transform space + Vector2 center = bodyA->position; + center = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.transform), Vector2Subtract(center, bodyB->position)); + + // Find edge with minimum penetration + // It is the same concept as using support points in SolvePolygonToPolygon + float separation = -PHYSAC_FLT_MAX; + int faceNormal = 0; + PolygonData vertexData = bodyB->shape.vertexData; + + for (int i = 0; i < vertexData.vertexCount; i++) + { + float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(center, vertexData.positions[i])); + + if (currentSeparation > bodyA->shape.radius) + return; + + if (currentSeparation > separation) + { + separation = currentSeparation; + faceNormal = i; + } + } + + // Grab face's vertices + Vector2 v1 = vertexData.positions[faceNormal]; + int nextIndex = (((faceNormal + 1) < vertexData.vertexCount) ? (faceNormal + 1) : 0); + Vector2 v2 = vertexData.positions[nextIndex]; + + // Check to see if center is within polygon + if (separation < PHYSAC_EPSILON) + { + manifold->contactsCount = 1; + Vector2 normal = Mat2MultiplyVector2(bodyB->shape.transform, vertexData.normals[faceNormal]); + manifold->normal = (Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + manifold->penetration = bodyA->shape.radius; + return; + } + + // Determine which voronoi region of the edge center of circle lies within + float dot1 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1)); + float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2)); + manifold->penetration = bodyA->shape.radius - separation; + + if (dot1 <= 0.0f) // Closest to v1 + { + if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) + return; + + manifold->contactsCount = 1; + Vector2 normal = Vector2Subtract(v1, center); + normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); + MathNormalize(&normal); + manifold->normal = normal; + v1 = Mat2MultiplyVector2(bodyB->shape.transform, v1); + v1 = Vector2Add(v1, bodyB->position); + manifold->contacts[0] = v1; + } + else if (dot2 <= 0.0f) // Closest to v2 + { + if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) + return; + + manifold->contactsCount = 1; + Vector2 normal = Vector2Subtract(v2, center); + v2 = Mat2MultiplyVector2(bodyB->shape.transform, v2); + v2 = Vector2Add(v2, bodyB->position); + manifold->contacts[0] = v2; + normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); + MathNormalize(&normal); + manifold->normal = normal; + } + else // Closest to face + { + Vector2 normal = vertexData.normals[faceNormal]; + + if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) + return; + + normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); + manifold->normal = (Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + manifold->contactsCount = 1; + } +} + +// Solves collision between two polygons shape physics bodies +static void SolvePolygonToPolygon(PhysicsManifold manifold) +{ + if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) + return; + + PhysicsShape bodyA = manifold->bodyA->shape; + PhysicsShape bodyB = manifold->bodyB->shape; + manifold->contactsCount = 0; + + // Check for separating axis with A shape's face planes + int faceA = 0; + float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB); + + if (penetrationA >= 0.0f) + return; + + // Check for separating axis with B shape's face planes + int faceB = 0; + float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA); + + if (penetrationB >= 0.0f) + return; + + int referenceIndex = 0; + bool flip = false; // Always point from A shape to B shape + + PhysicsShape refPoly; // Reference + PhysicsShape incPoly; // Incident + + // Determine which shape contains reference face + if (BiasGreaterThan(penetrationA, penetrationB)) + { + refPoly = bodyA; + incPoly = bodyB; + referenceIndex = faceA; + } + else + { + refPoly = bodyB; + incPoly = bodyA; + referenceIndex = faceB; + flip = true; + } + + // World space incident face + Vector2 incidentFace[2]; + FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); + + // Setup reference face vertices + PolygonData refData = refPoly.vertexData; + Vector2 v1 = refData.positions[referenceIndex]; + referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0); + Vector2 v2 = refData.positions[referenceIndex]; + + // Transform vertices to world space + v1 = Mat2MultiplyVector2(refPoly.transform, v1); + v1 = Vector2Add(v1, refPoly.body->position); + v2 = Mat2MultiplyVector2(refPoly.transform, v2); + v2 = Vector2Add(v2, refPoly.body->position); + + // Calculate reference face side normal in world space + Vector2 sidePlaneNormal = Vector2Subtract(v2, v1); + MathNormalize(&sidePlaneNormal); + + // Orthogonalize + Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; + float refC = MathDot(refFaceNormal, v1); + float negSide = MathDot(sidePlaneNormal, v1)*-1; + float posSide = MathDot(sidePlaneNormal, v2); + + // Clip incident face to reference face side planes (due to floating point error, possible to not have required points + if (Clip((Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, negSide, &incidentFace[0], &incidentFace[1]) < 2) + return; + + if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) + return; + + // Flip normal if required + manifold->normal = (flip ? (Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); + + // Keep points behind reference face + int currentPoint = 0; // Clipped points behind reference face + float separation = MathDot(refFaceNormal, incidentFace[0]) - refC; + + if (separation <= 0.0f) + { + manifold->contacts[currentPoint] = incidentFace[0]; + manifold->penetration = -separation; + currentPoint++; + } + else + manifold->penetration = 0.0f; + + separation = MathDot(refFaceNormal, incidentFace[1]) - refC; + + if (separation <= 0.0f) + { + manifold->contacts[currentPoint] = incidentFace[1]; + manifold->penetration += -separation; + currentPoint++; + + // Calculate total penetration average + manifold->penetration /= currentPoint; + } + + manifold->contactsCount = currentPoint; +} + +// Integrates physics forces into velocity +static void IntegratePhysicsForces(PhysicsBody body) +{ + if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) + return; + + body->velocity.x += (body->force.x*body->inverseMass)*(deltaTime/2.0); + body->velocity.y += (body->force.y*body->inverseMass)*(deltaTime/2.0); + + if (body->useGravity) + { + body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); + body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); + } + + if (!body->freezeOrient) + body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0); +} + +// Initializes physics manifolds to solve collisions +static void InitializePhysicsManifolds(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + // Calculate average restitution, static and dynamic friction + manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution); + manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction); + manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction); + + for (int i = 0; i < manifold->contactsCount; i++) + { + // Caculate radius from center of mass to contact + Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); + + Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA); + Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB); + + Vector2 radiusV = { 0.0f, 0.0f }; + radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; + radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y; + + // Determine if we should perform a resting collision or not; + // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution + if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) + manifold->restitution = 0; + } +} + +// Integrates physics collisions impulses to solve collisions +static void IntegratePhysicsImpulses(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + // Early out and positional correct if both objects have infinite mass + if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON) + { + bodyA->velocity = PHYSAC_VECTOR_ZERO; + bodyB->velocity = PHYSAC_VECTOR_ZERO; + return; + } + + for (int i = 0; i < manifold->contactsCount; i++) + { + // Calculate radius from center of mass to contact + Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); + + // Calculate relative velocity + Vector2 radiusV = { 0.0f, 0.0f }; + radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; + radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; + + // Relative velocity along the normal + float contactVelocity = MathDot(radiusV, manifold->normal); + + // Do not resolve if velocities are separating + if (contactVelocity > 0.0f) + return; + + float raCrossN = MathCrossVector2(radiusA, manifold->normal); + float rbCrossN = MathCrossVector2(radiusB, manifold->normal); + + float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; + + // Calculate impulse scalar value + float impulse = -(1.0f + manifold->restitution)*contactVelocity; + impulse /= inverseMassSum; + impulse /= (float)manifold->contactsCount; + + // Apply impulse to each physics body + Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse }; + + if (bodyA->enabled) + { + bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); + bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); + + if (!bodyA->freezeOrient) + bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -impulseV.x, -impulseV.y }); + } + + if (bodyB->enabled) + { + bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); + bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); + + if (!bodyB->freezeOrient) + bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, impulseV); + } + + // Apply friction impulse to each physics body + radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; + radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; + + Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) }; + MathNormalize(&tangent); + + // Calculate impulse tangent magnitude + float impulseTangent = -MathDot(radiusV, tangent); + impulseTangent /= inverseMassSum; + impulseTangent /= (float)manifold->contactsCount; + + float absImpulseTangent = fabs(impulseTangent); + + // Don't apply tiny friction impulses + if (absImpulseTangent <= PHYSAC_EPSILON) + return; + + // Apply coulumb's law + Vector2 tangentImpulse = { 0.0f, 0.0f }; + if (absImpulseTangent < impulse*manifold->staticFriction) + tangentImpulse = (Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; + else + tangentImpulse = (Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; + + // Apply friction impulse + if (bodyA->enabled) + { + bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); + bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); + + if (!bodyA->freezeOrient) + bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -tangentImpulse.x, -tangentImpulse.y }); + } + + if (bodyB->enabled) + { + bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); + bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); + + if (!bodyB->freezeOrient) + bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, tangentImpulse); + } + } +} + +// Integrates physics velocity into position and forces +static void IntegratePhysicsVelocity(PhysicsBody body) +{ + if ((body == NULL) ||!body->enabled) + return; + + body->position.x += body->velocity.x*deltaTime; + body->position.y += body->velocity.y*deltaTime; + + if (!body->freezeOrient) + body->orient += body->angularVelocity*deltaTime; + + Mat2Set(&body->shape.transform, body->orient); + + IntegratePhysicsForces(body); +} + +// Corrects physics bodies positions based on manifolds collision information +static void CorrectPhysicsPositions(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) + return; + + Vector2 correction = { 0.0f, 0.0f }; + correction.x = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; + correction.y = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; + + if (bodyA->enabled) + { + bodyA->position.x -= correction.x*bodyA->inverseMass; + bodyA->position.y -= correction.y*bodyA->inverseMass; + } + + if (bodyB->enabled) + { + bodyB->position.x += correction.x*bodyB->inverseMass; + bodyB->position.y += correction.y*bodyB->inverseMass; + } +} + +// Returns the extreme point along a direction within a polygon +static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) +{ + float bestProjection = -PHYSAC_FLT_MAX; + Vector2 bestVertex = { 0.0f, 0.0f }; + PolygonData data = shape.vertexData; + + for (int i = 0; i < data.vertexCount; i++) + { + Vector2 vertex = data.positions[i]; + float projection = MathDot(vertex, dir); + + if (projection > bestProjection) + { + bestVertex = vertex; + bestProjection = projection; + } + } + + return bestVertex; +} + +// Finds polygon shapes axis least penetration +static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB) +{ + float bestDistance = -PHYSAC_FLT_MAX; + int bestIndex = 0; + + PolygonData dataA = shapeA.vertexData; + + for (int i = 0; i < dataA.vertexCount; i++) + { + // Retrieve a face normal from A shape + Vector2 normal = dataA.normals[i]; + Vector2 transNormal = Mat2MultiplyVector2(shapeA.transform, normal); + + // Transform face normal into B shape's model space + Mat2 buT = Mat2Transpose(shapeB.transform); + normal = Mat2MultiplyVector2(buT, transNormal); + + // Retrieve support point from B shape along -n + Vector2 support = GetSupport(shapeB, (Vector2){ -normal.x, -normal.y }); + + // Retrieve vertex on face from A shape, transform into B shape's model space + Vector2 vertex = dataA.positions[i]; + vertex = Mat2MultiplyVector2(shapeA.transform, vertex); + vertex = Vector2Add(vertex, shapeA.body->position); + vertex = Vector2Subtract(vertex, shapeB.body->position); + vertex = Mat2MultiplyVector2(buT, vertex); + + // Compute penetration distance in B shape's model space + float distance = MathDot(normal, Vector2Subtract(support, vertex)); + + // Store greatest distance + if (distance > bestDistance) + { + bestDistance = distance; + bestIndex = i; + } + } + + *faceIndex = bestIndex; + return bestDistance; +} + +// Finds two polygon shapes incident face +static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) +{ + PolygonData refData = ref.vertexData; + PolygonData incData = inc.vertexData; + + Vector2 referenceNormal = refData.normals[index]; + + // Calculate normal in incident's frame of reference + referenceNormal = Mat2MultiplyVector2(ref.transform, referenceNormal); // To world space + referenceNormal = Mat2MultiplyVector2(Mat2Transpose(inc.transform), referenceNormal); // To incident's model space + + // Find most anti-normal face on polygon + int incidentFace = 0; + float minDot = PHYSAC_FLT_MAX; + + for (int i = 0; i < incData.vertexCount; i++) + { + float dot = MathDot(referenceNormal, incData.normals[i]); + + if (dot < minDot) + { + minDot = dot; + incidentFace = i; + } + } + + // Assign face vertices for incident face + *v0 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); + *v0 = Vector2Add(*v0, inc.body->position); + incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0); + *v1 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); + *v1 = Vector2Add(*v1, inc.body->position); +} + +// Calculates clipping based on a normal and two faces +static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) +{ + int sp = 0; + Vector2 out[2] = { *faceA, *faceB }; + + // Retrieve distances from each endpoint to the line + float distanceA = MathDot(normal, *faceA) - clip; + float distanceB = MathDot(normal, *faceB) - clip; + + // If negative (behind plane) + if (distanceA <= 0.0f) + out[sp++] = *faceA; + + if (distanceB <= 0.0f) + out[sp++] = *faceB; + + // If the points are on different sides of the plane + if ((distanceA*distanceB) < 0.0f) + { + // Push intersection point + float alpha = distanceA/(distanceA - distanceB); + out[sp] = *faceA; + Vector2 delta = Vector2Subtract(*faceB, *faceA); + delta.x *= alpha; + delta.y *= alpha; + out[sp] = Vector2Add(out[sp], delta); + sp++; + } + + // Assign the new converted values + *faceA = out[0]; + *faceB = out[1]; + + return sp; +} + +// Check if values are between bias range +static bool BiasGreaterThan(float valueA, float valueB) +{ + return (valueA >= (valueB*0.95f + valueA*0.01f)); +} + +// Returns the barycenter of a triangle given by 3 points +static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) +{ + Vector2 result = { 0.0f, 0.0f }; + + result.x = (v1.x + v2.x + v3.x)/3; + result.y = (v1.y + v2.y + v3.y)/3; + + return result; +} + +// Initializes hi-resolution MONOTONIC timer +static void InitTimer(void) +{ + srand(time(NULL)); // Initialize random seed + + #if defined(_WIN32) + QueryPerformanceFrequency((unsigned long long int *) &frequency); + #endif + + #if defined(__linux__) + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) + frequency = 1000000000; + #endif + + #if defined(__APPLE__) + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + frequency = (timebase.denom*1e9)/timebase.numer; + #endif + + baseTime = GetTimeCount(); // Get MONOTONIC clock time offset + startTime = GetCurrentTime(); // Get current time +} + +// Get hi-res MONOTONIC time measure in seconds +static uint64_t GetTimeCount(void) +{ + uint64_t value = 0; + + #if defined(_WIN32) + QueryPerformanceCounter((unsigned long long int *) &value); + #endif + + #if defined(__linux__) + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + value = (uint64_t)now.tv_sec*(uint64_t)1000000000 + (uint64_t)now.tv_nsec; + #endif + + #if defined(__APPLE__) + value = mach_absolute_time(); + #endif + + return value; +} + +// Get current time in milliseconds +static double GetCurrentTime(void) +{ + return (double)(GetTimeCount() - baseTime)/frequency*1000; +} + +// Returns the cross product of a vector and a value +static inline Vector2 MathCross(float value, Vector2 vector) +{ + return (Vector2){ -value*vector.y, value*vector.x }; +} + +// Returns the cross product of two vectors +static inline float MathCrossVector2(Vector2 v1, Vector2 v2) +{ + return (v1.x*v2.y - v1.y*v2.x); +} + +// Returns the len square root of a vector +static inline float MathLenSqr(Vector2 vector) +{ + return (vector.x*vector.x + vector.y*vector.y); +} + +// Returns the dot product of two vectors +static inline float MathDot(Vector2 v1, Vector2 v2) +{ + return (v1.x*v2.x + v1.y*v2.y); +} + +// Returns the square root of distance between two vectors +static inline float DistSqr(Vector2 v1, Vector2 v2) +{ + Vector2 dir = Vector2Subtract(v1, v2); + return MathDot(dir, dir); +} + +// Returns the normalized values of a vector +static void MathNormalize(Vector2 *vector) +{ + float length, ilength; + + Vector2 aux = *vector; + length = sqrtf(aux.x*aux.x + aux.y*aux.y); + + if (length == 0) + length = 1.0f; + + ilength = 1.0f/length; + + vector->x *= ilength; + vector->y *= ilength; +} + +#if defined(PHYSAC_STANDALONE) +// Returns the sum of two given vectors +static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2) +{ + return (Vector2){ v1.x + v2.x, v1.y + v2.y }; +} + +// Returns the subtract of two given vectors +static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) +{ + return (Vector2){ v1.x - v2.x, v1.y - v2.y }; +} +#endif + +// Creates a matrix 2x2 from a given radians value +static Mat2 Mat2Radians(float radians) +{ + float c = cosf(radians); + float s = sinf(radians); + + return (Mat2){ c, -s, s, c }; +} + +// Set values from radians to a created matrix 2x2 +static void Mat2Set(Mat2 *matrix, float radians) +{ + float cos = cosf(radians); + float sin = sinf(radians); + + matrix->m00 = cos; + matrix->m01 = -sin; + matrix->m10 = sin; + matrix->m11 = cos; +} + +// Returns the transpose of a given matrix 2x2 +static inline Mat2 Mat2Transpose(Mat2 matrix) +{ + return (Mat2){ matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; +} + +// Multiplies a vector by a matrix 2x2 +static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector) +{ + return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; +} + +#endif // PHYSAC_IMPLEMENTATION diff --git a/examples/physics/physics_demo.cpp b/examples/physics/physics_demo.cpp index b95adf2d..e2c98b84 100644 --- a/examples/physics/physics_demo.cpp +++ b/examples/physics/physics_demo.cpp @@ -19,6 +19,7 @@ #define PHYSAC_IMPLEMENTATION #define PHYSAC_NO_THREADS +#include "physac.h" #include "Physics.hpp" int main(void) @@ -55,7 +56,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- // Delay initialization of variables due to physics reset async - physics.Update(); + physics.RunStep(); if (needsReset) { @@ -68,13 +69,6 @@ int main(void) needsReset = false; } - // Reset physics input - if (IsKeyPressed('R')) - { - physics.Reset(); - needsReset = true; - } - // Physics body creation inputs if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) physics.CreateBodyPolygon(GetMousePosition(), GetRandomValue(20, 80), GetRandomValue(3, 8), 10); else if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) physics.CreateBodyCircle(GetMousePosition(), GetRandomValue(10, 45), 10); @@ -121,7 +115,6 @@ int main(void) raylib::DrawText("Left mouse button to create a polygon", 10, 10, 10, WHITE); raylib::DrawText("Right mouse button to create a circle", 10, 25, 10, WHITE); - raylib::DrawText("Press 'R' to reset example", 10, 40, 10, WHITE); raylib::DrawText("Physac", logoX, logoY, 30, WHITE); raylib::DrawText("Powered by", logoX + 50, logoY - 7, 10, WHITE); diff --git a/examples/text/text_font_filters.cpp b/examples/text/text_font_filters.cpp index f7586d9b..f103790e 100644 --- a/examples/text/text_font_filters.cpp +++ b/examples/text/text_font_filters.cpp @@ -74,7 +74,7 @@ int main(void) else if (IsKeyDown(KEY_RIGHT)) fontPosition.x += 10; // Load a dropped TTF file dynamically (at current fontSize) - for (const auto& file : raylib::GetDroppedFiles()) { + for (const auto& file : raylib::LoadDroppedFiles()) { if (raylib::IsFileExtension(file, ".ttf")) { msg.font = font = raylib::Font(file, font.GetBaseSize()); } diff --git a/include/Functions.hpp b/include/Functions.hpp index fe472f64..8fa7b3f7 100644 --- a/include/Functions.hpp +++ b/include/Functions.hpp @@ -152,11 +152,10 @@ RLCPPAPI inline std::string GetWorkingDirectory() { * Get filenames in a directory path */ [[maybe_unused]] -RLCPPAPI std::vector GetDirectoryFiles(const std::string& dirPath) { - int count; - char** files = ::GetDirectoryFiles(dirPath.c_str(), &count); - std::vector output(files, files + count); - ::ClearDirectoryFiles(); +RLCPPAPI std::vector LoadDirectoryFiles(const std::string& dirPath) { + FilePathList files = ::LoadDirectoryFiles(dirPath.c_str()); + std::vector output(files.paths, files.paths + files.count); + ::UnloadDirectoryFiles(files); return output; } @@ -171,14 +170,13 @@ RLCPPAPI inline bool ChangeDirectory(const std::string& dir) { * Get dropped files names */ [[maybe_unused]] -RLCPPAPI std::vector GetDroppedFiles() { +RLCPPAPI std::vector LoadDroppedFiles() { if (!::IsFileDropped()) { return std::vector(); } - int count; - char** files = ::GetDroppedFiles(&count); - std::vector output(files, files + count); - ::ClearDroppedFiles(); + FilePathList files = ::LoadDroppedFiles(); + std::vector output(files.paths, files.paths + files.count); + ::UnloadDroppedFiles(files); return output; } diff --git a/include/Mesh.hpp b/include/Mesh.hpp index cb56f89f..bdf2520c 100644 --- a/include/Mesh.hpp +++ b/include/Mesh.hpp @@ -260,14 +260,6 @@ class Mesh : public ::Mesh { return *this; } - /** - * Compute mesh binormals (aka bitangent) - */ - inline Mesh& GenBinormals() { - ::GenMeshBinormals(this); - return *this; - } - /** * Load model from generated mesh */ diff --git a/include/Physics.hpp b/include/Physics.hpp index 15739b33..656ee346 100644 --- a/include/Physics.hpp +++ b/include/Physics.hpp @@ -2,12 +2,16 @@ #define RAYLIB_CPP_INCLUDE_PHYSICS_HPP_ #include "./raylib.hpp" -#include "./physac.hpp" #include "./Vector2.hpp" namespace raylib { /** * 2D Physics library for videogames + * + * This requires you to deploy your own version of physac.h over at: + * https://github.com/victorfisac/Physac + * + * @see https://github.com/victorfisac/Physac */ class Physics { public: @@ -34,18 +38,8 @@ class Physics { return *this; } - inline Physics& Update() { - ::UpdatePhysics(); - return *this; - } - - inline Physics& Reset() { - ::ResetPhysics(); - return *this; - } - - inline Physics& Close() { - ::ClosePhysics(); + inline Physics& RunStep() { + ::RunPhysicsStep(); return *this; } @@ -54,6 +48,10 @@ class Physics { return *this; } + inline bool IsEnabled() { + return ::IsPhysicsEnabled(); + } + inline Physics& SetGravity(float x, float y) { ::SetPhysicsGravity(x, y); return *this; @@ -71,11 +69,6 @@ class Physics { return ::CreatePhysicsBodyPolygon(pos, radius, sides, density); } - inline Physics& DestroyBody(PhysicsBody body) { - ::DestroyPhysicsBody(body); - return *this; - } - inline Physics& AddForce(PhysicsBody body, Vector2 force) { ::PhysicsAddForce(body, force); return *this; @@ -91,11 +84,6 @@ class Physics { return *this; } - inline Physics& SetBodyRotation(PhysicsBody body, float radians) { - ::SetPhysicsBodyRotation(body, radians); - return *this; - } - inline int GetBodiesCount() const { return ::GetPhysicsBodiesCount(); } @@ -115,6 +103,26 @@ class Physics { inline Vector2 GetShapeVertex(PhysicsBody body, int vertex) const { return ::GetPhysicsShapeVertex(body, vertex); } + + inline Physics& GetBodyRotation(PhysicsBody body, float radians) { + ::SetPhysicsBodyRotation(body, radians); + return *this; + } + + inline Physics& SetBodyRotation(PhysicsBody body, float radians) { + ::SetPhysicsBodyRotation(body, radians); + return *this; + } + + inline Physics& DestroyBody(PhysicsBody body) { + ::DestroyPhysicsBody(body); + return *this; + } + + inline Physics& Close() { + ::ClosePhysics(); + return *this; + } }; } // namespace raylib using RPhysics = raylib::Physics; diff --git a/include/RayCollision.hpp b/include/RayCollision.hpp index ba80f296..cf037e6d 100644 --- a/include/RayCollision.hpp +++ b/include/RayCollision.hpp @@ -33,13 +33,6 @@ class RayCollision : public ::RayCollision { set(::GetRayCollisionMesh(ray, mesh, transform)); } - /** - * Get collision info between ray and model - */ - RayCollision(const ::Ray& ray, const ::Model& model) { - set(::GetRayCollisionModel(ray, model)); - } - /** * Get collision info between ray and quad */ diff --git a/include/Sound.hpp b/include/Sound.hpp index c5270694..35157704 100644 --- a/include/Sound.hpp +++ b/include/Sound.hpp @@ -33,8 +33,8 @@ class Sound : public ::Sound { Sound(Sound&& other) { set(other); + other.stream = { 0, 0, 0, 0, 0 }; other.frameCount = 0; - other.stream = { 0, 0, 0, 0 }; } /** diff --git a/include/physac.hpp b/include/physac.hpp deleted file mode 100644 index 8adbea8f..00000000 --- a/include/physac.hpp +++ /dev/null @@ -1,15 +0,0 @@ -/** - * C++ header to wrap physac.h. - */ -#ifndef RAYLIB_CPP_INCLUDE_PHYSAC_HPP_ -#define RAYLIB_CPP_INCLUDE_PHYSAC_HPP_ - -#ifdef __cplusplus -extern "C" { -#endif -#include "extras/physac.h" // NOLINT -#ifdef __cplusplus -} -#endif - -#endif // RAYLIB_CPP_INCLUDE_PHYSAC_HPP_ diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index e4d599f5..08195a30 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -8,7 +8,7 @@ if (NOT raylib_FOUND) FetchContent_Declare( raylib GIT_REPOSITORY https://github.com/raysan5/raylib.git - GIT_TAG 4.0.0 + GIT_TAG 4.2.0 ) FetchContent_MakeAvailable(raylib) endif() diff --git a/tests/raylib_cpp_test.cpp b/tests/raylib_cpp_test.cpp index 915f6d0f..8b67f7ab 100644 --- a/tests/raylib_cpp_test.cpp +++ b/tests/raylib_cpp_test.cpp @@ -67,9 +67,9 @@ int main(int argc, char *argv[]) { AssertEqual(image.GetHeight(), 50); } - // raylib::GetDirectoryFiles() + // raylib::LoadDirectoryFiles() { - std::vector files = raylib::GetDirectoryFiles(::GetWorkingDirectory()); + std::vector files = raylib::LoadDirectoryFiles(::GetWorkingDirectory()); Assert(files.size() > 3); } From 95d52073805397be285e2b4ca0d5760b6f663199 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 20 Aug 2022 16:01:59 -0400 Subject: [PATCH 3/5] Update to raylib 4.2 --- clib.json | 1 + include/AudioDevice.hpp | 13 ++-- include/AudioStream.hpp | 49 ++++++++++++--- include/BoundingBox.hpp | 12 +--- include/CMakeLists.txt | 1 + include/Camera2D.hpp | 1 + include/Camera3D.hpp | 4 +- include/Color.hpp | 15 +++-- include/Font.hpp | 70 ++++++++++++++------- include/Gamepad.hpp | 5 ++ include/Image.hpp | 117 +++++++++++++++++++++--------------- include/Material.hpp | 5 ++ include/Matrix.hpp | 11 +++- include/Mesh.hpp | 59 ++++++++++++------ include/Model.hpp | 31 ++++++---- include/ModelAnimation.hpp | 7 ++- include/Mouse.hpp | 34 +++++++++++ include/Music.hpp | 60 ++++++++++-------- include/Physics.hpp | 1 + include/Ray.hpp | 41 ++++++++----- include/RayCollision.hpp | 1 + include/RaylibException.hpp | 1 + include/Rectangle.hpp | 1 + include/RenderTexture.hpp | 1 + include/Shader.hpp | 7 +++ include/Sound.hpp | 47 ++++++++++----- include/Text.hpp | 1 + include/Texture.hpp | 56 +++++++++++++---- include/Touch.hpp | 51 ++++++++++++++++ include/Vector2.hpp | 24 +++++--- include/Vector3.hpp | 1 + include/Vector4.hpp | 1 + include/VrStereoConfig.hpp | 31 +++++----- include/Wave.hpp | 47 ++++++++------- include/Window.hpp | 53 ++++++++++++++-- include/raylib-cpp.hpp | 1 + 36 files changed, 614 insertions(+), 247 deletions(-) create mode 100644 include/Touch.hpp diff --git a/clib.json b/clib.json index f61d4ca4..7c23867e 100644 --- a/clib.json +++ b/clib.json @@ -53,6 +53,7 @@ "include/Sound.hpp", "include/Text.hpp", "include/Texture.hpp", + "include/Touch.hpp", "include/Vector2.hpp", "include/Vector3.hpp", "include/Vector4.hpp", diff --git a/include/AudioDevice.hpp b/include/AudioDevice.hpp index eec3edb7..f8eb1fef 100644 --- a/include/AudioDevice.hpp +++ b/include/AudioDevice.hpp @@ -20,9 +20,7 @@ class AudioDevice { */ AudioDevice(bool lateInit = false) { if (!lateInit) { - if (!Init()) { - throw RaylibException("Failed to initialize AudioDevice"); - } + Init(); } } @@ -35,10 +33,14 @@ class AudioDevice { /** * Initialize audio device and context. + * + * @throws raylib::RaylibException Throws if the AudioDevice failed to initialize. */ - inline bool Init() { + inline void Init() { ::InitAudioDevice(); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to initialize AudioDevice"); + } } /** @@ -66,6 +68,7 @@ class AudioDevice { } }; } // namespace raylib + using RAudioDevice = raylib::AudioDevice; #endif // RAYLIB_CPP_INCLUDE_AUDIODEVICE_HPP_ diff --git a/include/AudioStream.hpp b/include/AudioStream.hpp index 5846e841..4a47d4f2 100644 --- a/include/AudioStream.hpp +++ b/include/AudioStream.hpp @@ -28,10 +28,8 @@ class AudioStream : public ::AudioStream { * * @throws raylib::RaylibException Throws if the AudioStream failed to load. */ - AudioStream(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) { - if (!Load(SampleRate, SampleSize, Channels)) { - throw RaylibException("Failed to create AudioStream"); - } + AudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels = 2) { + Load(sampleRate, sampleSize, channels); } AudioStream(const AudioStream&) = delete; @@ -144,7 +142,7 @@ class AudioStream : public ::AudioStream { /** * Set volume for audio stream (1.0 is max level) */ - inline AudioStream& SetVolume(float volume) { + inline AudioStream& SetVolume(float volume = 1.0f) { ::SetAudioStreamVolume(*this, volume); return *this; } @@ -157,6 +155,14 @@ class AudioStream : public ::AudioStream { return *this; } + /** + * Set pan for audio stream (0.5 is centered) + */ + inline AudioStream& SetPan(float pan = 0.5f) { + ::SetAudioStreamPitch(*this, pan); + return *this; + } + /** * Default size for new audio streams */ @@ -164,6 +170,27 @@ class AudioStream : public ::AudioStream { ::SetAudioStreamBufferSizeDefault(size); } + /** + * Audio thread callback to request new data + */ + inline void SetCallback(::AudioCallback callback) { + ::SetAudioStreamCallback(*this, callback); + } + + /** + * Attach audio stream processor to stream + */ + inline void AttachProcessor(::AudioCallback processor) { + ::SetAudioStreamCallback(*this, processor); + } + + /** + * Detach audio stream processor from stream + */ + inline void DetachProcessor(::AudioCallback processor) { + ::SetAudioStreamCallback(*this, processor); + } + /** * Retrieve whether or not the audio stream is ready. */ @@ -172,13 +199,16 @@ class AudioStream : public ::AudioStream { } /** - * Init audio stream (to stream raw audio pcm data) + * Load audio stream (to stream raw audio pcm data) * - * @return True or false depending on if the audio stream initialized properly. + * @throws raylib::RaylibException Throws if the AudioStream failed to load. */ - bool Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) { + void Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) { + Unload(); set(::LoadAudioStream(SampleRate, SampleSize, Channels)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load audio stream"); + } } private: @@ -191,6 +221,7 @@ class AudioStream : public ::AudioStream { } }; } // namespace raylib + using RAudioStream = raylib::AudioStream; #endif // RAYLIB_CPP_INCLUDE_AUDIOSTREAM_HPP_ diff --git a/include/BoundingBox.hpp b/include/BoundingBox.hpp index 20214437..ef56d188 100644 --- a/include/BoundingBox.hpp +++ b/include/BoundingBox.hpp @@ -10,13 +10,6 @@ namespace raylib { */ class BoundingBox : public ::BoundingBox { public: - /* - * Set the bounding box to default: min (0, 0, 0) and max (0, 0, 0). - */ - BoundingBox() : ::BoundingBox{::Vector3{0, 0, 0}, ::Vector3{ 0, 0, 0 }} { - // Nothing. - } - /* * Copy a bounding box from another bounding box. */ @@ -31,7 +24,7 @@ class BoundingBox : public ::BoundingBox { set(::GetMeshBoundingBox(mesh)); } - BoundingBox(::Vector3 minMax) : ::BoundingBox{minMax, minMax} {} + BoundingBox(::Vector3 minMax = ::Vector3{0.0f, 0.0f, 0.0f}) : ::BoundingBox{minMax, minMax} {} BoundingBox(::Vector3 min, ::Vector3 max) : ::BoundingBox{min, max} {} GETTERSETTER(::Vector3, Min, min) @@ -46,7 +39,7 @@ class BoundingBox : public ::BoundingBox { * Draw a bounding box with wires */ inline BoundingBox& Draw(::Color color = {255, 255, 255, 255}) { - DrawBoundingBox(*this, color); + ::DrawBoundingBox(*this, color); return *this; } @@ -89,6 +82,7 @@ class BoundingBox : public ::BoundingBox { } }; } // namespace raylib + using RBoundingBox = raylib::BoundingBox; #endif // RAYLIB_CPP_INCLUDE_BOUNDINGBOX_HPP_ diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index e0cf7675..d520d5fd 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -37,6 +37,7 @@ install(FILES Sound.hpp Text.hpp Texture.hpp + Touch.hpp Vector2.hpp Vector3.hpp Vector4.hpp diff --git a/include/Camera2D.hpp b/include/Camera2D.hpp index edcd0e9f..64678d83 100644 --- a/include/Camera2D.hpp +++ b/include/Camera2D.hpp @@ -69,6 +69,7 @@ class Camera2D : public ::Camera2D { } }; } // namespace raylib + using RCamera2D = raylib::Camera2D; #endif // RAYLIB_CPP_INCLUDE_CAMERA2D_HPP_ diff --git a/include/Camera3D.hpp b/include/Camera3D.hpp index fd52f4e5..26b71f68 100644 --- a/include/Camera3D.hpp +++ b/include/Camera3D.hpp @@ -60,7 +60,7 @@ class Camera3D : public ::Camera3D { } /** - * Get transform matrix for camera + * Get camera transform matrix (view matrix) */ inline Matrix GetMatrix() const { return ::GetCameraMatrix(*this); @@ -159,7 +159,9 @@ class Camera3D : public ::Camera3D { }; typedef Camera3D Camera; + } // namespace raylib + using RCamera = raylib::Camera; using RCamera3D = raylib::Camera3D; diff --git a/include/Color.hpp b/include/Color.hpp index b8e65091..02fd6e2c 100644 --- a/include/Color.hpp +++ b/include/Color.hpp @@ -49,11 +49,8 @@ class Color : public ::Color { set(::GetColor(hexValue)); } - /** - * Returns Color from normalized values [0..1] - */ - Color(::Vector4 normalized) { - set(::ColorFromNormalized(normalized)); + Color(void *srcPtr, int format) { + set(::GetPixelColor(srcPtr, format)); } /** @@ -84,6 +81,13 @@ class Color : public ::Color { return ::ColorNormalize(*this); } + /** + * Returns Color from normalized values [0..1] + */ + Color(::Vector4 normalized) { + set(::ColorFromNormalized(normalized)); + } + /** * Returns HSV values for a Color */ @@ -254,6 +258,7 @@ class Color : public ::Color { }; } // namespace raylib + using RColor = raylib::Color; #endif // RAYLIB_CPP_INCLUDE_COLOR_HPP_ diff --git a/include/Font.hpp b/include/Font.hpp index 87a5dd1e..5a1810bf 100644 --- a/include/Font.hpp +++ b/include/Font.hpp @@ -22,6 +22,9 @@ class Font : public ::Font { // Nothing. } + /** + * Retrieves the default Font. + */ Font() { set(::GetFontDefault()); } @@ -38,9 +41,7 @@ class Font : public ::Font { * @throws raylib::RaylibException Throws if the given font failed to initialize. */ Font(const std::string& fileName) { - if (!Load(fileName)) { - throw RaylibException("Failed to load Font from file"); - } + Load(fileName); } /** @@ -53,9 +54,7 @@ class Font : public ::Font { * @see ::LoadFontEx */ Font(const std::string& fileName, int fontSize, int* fontChars = 0, int charCount = 0) { - if (!Load(fileName, fontSize, fontChars, charCount)) { - throw RaylibException("Failed to load font from font with extras"); - } + Load(fileName, fontSize, fontChars, charCount); } /** @@ -68,9 +67,7 @@ class Font : public ::Font { * @see ::LoadFontFromImage() */ Font(const ::Image& image, ::Color key, int firstChar) { - if (!Load(image, key, firstChar)) { - throw RaylibException("Failed to load Texture from Image"); - } + Load(image, key, firstChar); } /** @@ -82,9 +79,7 @@ class Font : public ::Font { */ Font(const std::string& fileType, const unsigned char* fileData, int dataSize, int fontSize, int *fontChars, int charsCount) { - if (!Load(fileType, fileData, dataSize, fontSize, fontChars, charsCount)) { - throw RaylibException("Failed to load Texture from file data"); - } + Load(fileType, fileData, dataSize, fontSize, fontChars, charsCount); } Font(const Font&) = delete; @@ -146,13 +141,15 @@ class Font : public ::Font { * * @param fileName The filename of the font to load. * - * @return True of false depending on if the font loaded successfully. + * @throws raylib::RaylibException Throws if the given font failed to initialize. * * @see ::LoadFont() */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadFont(fileName.c_str())); - return baseSize > 0; + if (!IsReady()) { + throw new RaylibException("Failed to load Font with from file: " + fileName); + } } /** @@ -161,24 +158,37 @@ class Font : public ::Font { * @param fileName The filename of the font to load. * @param fontSize The desired size of the font. * - * @return True of false depending on if the font loaded successfully. + * @throws raylib::RaylibException Throws if the given font failed to initialize. * * @see ::LoadFontEx() */ - bool Load(const std::string& fileName, int fontSize, int* fontChars, int charCount) { + void Load(const std::string& fileName, int fontSize, int* fontChars, int charCount) { set(::LoadFontEx(fileName.c_str(), fontSize, fontChars, charCount)); - return baseSize > 0; + if (!IsReady()) { + throw new RaylibException("Failed to load Font with from file with font size: " + fileName); + } } - bool Load(const ::Image& image, ::Color key, int firstChar) { + void Load(const ::Image& image, ::Color key, int firstChar) { set(::LoadFontFromImage(image, key, firstChar)); - return baseSize > 0; + if (!IsReady()) { + throw new RaylibException("Failed to load Font with from image"); + } } - bool Load(const std::string& fileType, const unsigned char* fileData, int dataSize, int fontSize, + void Load(const std::string& fileType, const unsigned char* fileData, int dataSize, int fontSize, int *fontChars, int charsCount) { set(::LoadFontFromMemory(fileType.c_str(), fileData, dataSize, fontSize, fontChars, charsCount)); + if (!IsReady()) { + throw new RaylibException("Failed to load Font " + fileType + " with from file data"); + } + } + + /** + * Returns if the font is ready to be used. + */ + bool IsReady() { return baseSize > 0; } @@ -191,6 +201,15 @@ class Font : public ::Font { return *this; } + /** + * Draw text using font and additional parameters. + */ + inline Font& DrawText(const std::string& text, int posX, int posY, float fontSize, + float spacing, ::Color tint = WHITE) { + ::DrawTextEx(*this, text.c_str(), {static_cast(posX), static_cast(posY)}, fontSize, spacing, tint); + return *this; + } + inline Font& DrawText( const std::string& text, ::Vector2 position, @@ -214,6 +233,14 @@ class Font : public ::Font { return *this; } + /** + * Draw multiple character (codepoint) + */ + inline Font& DrawText(const int *codepoints, int count, ::Vector2 position, float fontSize, float spacing, ::Color tint = { 255, 255, 255, 255 }) { + ::DrawTextCodepoints(*this, codepoints, count, position, fontSize, spacing, tint); + return *this; + } + /** * Measure string size for Font */ @@ -247,6 +274,7 @@ class Font : public ::Font { } }; } // namespace raylib + using RFont = raylib::Font; #endif // RAYLIB_CPP_INCLUDE_FONT_HPP_ diff --git a/include/Gamepad.hpp b/include/Gamepad.hpp index 5ac975fe..b65e9268 100644 --- a/include/Gamepad.hpp +++ b/include/Gamepad.hpp @@ -108,12 +108,17 @@ class Gamepad { return ::GetGamepadAxisMovement(number, axis); } + inline int SetMappings(const std::string& mappings) { + return SetGamepadMappings(mappings.c_str()); + } + private: inline void set(int gamepadNumber) { number = gamepadNumber; } }; } // namespace raylib + using RGamepad = raylib::Gamepad; #endif // RAYLIB_CPP_INCLUDE_GAMEPAD_HPP_ diff --git a/include/Image.hpp b/include/Image.hpp index ab9770ab..c68edf76 100644 --- a/include/Image.hpp +++ b/include/Image.hpp @@ -6,6 +6,7 @@ #include "./raylib.hpp" #include "./raylib-cpp-utils.hpp" #include "./RaylibException.hpp" +#include "./Color.hpp" namespace raylib { /** @@ -35,9 +36,7 @@ class Image : public ::Image { * @see Load() */ Image(const std::string& fileName) { - if (!Load(fileName)) { - throw RaylibException(TextFormat("Failed to load Image from file: %s", fileName.c_str())); - } + Load(fileName); } /** @@ -47,10 +46,8 @@ class Image : public ::Image { * * @see LoadRaw() */ - Image(const std::string& fileName, int width, int height, int format, int headerSize) { - if (!Load(fileName, width, height, format, headerSize)) { - throw RaylibException(TextFormat("Failed to load Image from file: %s", fileName.c_str())); - } + Image(const std::string& fileName, int width, int height, int format, int headerSize = 0) { + Load(fileName, width, height, format, headerSize); } /** @@ -61,9 +58,7 @@ class Image : public ::Image { * @see LoadAnim() */ Image(const std::string& fileName, int* frames) { - if (!Load(fileName, frames)) { - throw RaylibException(TextFormat("Failed to load Image from animation: %s", fileName.c_str())); - } + Load(fileName, frames); } /** @@ -72,9 +67,7 @@ class Image : public ::Image { * @throws raylib::RaylibException Thrown if the image failed to load from the file. */ Image(const std::string& fileType, const unsigned char* fileData, int dataSize) { - if (!Load(fileType, fileData, dataSize)) { - throw RaylibException("Failed to load Image from memory"); - } + Load(fileType, fileData, dataSize); } /** @@ -83,9 +76,7 @@ class Image : public ::Image { * @throws raylib::RaylibException Thrown if the image failed to load from the file. */ Image(const ::Texture2D& texture) { - if (!Load(texture)) { - throw RaylibException("Failed to load Image from Texture"); - } + Load(texture); } Image(int width, int height, ::Color color = {255, 255, 255, 255}) { @@ -223,64 +214,74 @@ class Image : public ::Image { /** * Load image from file into CPU memory (RAM) * - * @return Whether or not the image was loaded successfully. + * @throws raylib::RaylibException Thrown if the image failed to load from the file. * * @see ::LoadImage() */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadImage(fileName.c_str())); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Image from file: " + fileName); + } } /** * Load image from RAW file data. * - * @return Whether or not the raw image data was loaded successfully. + * @throws raylib::RaylibException Thrown if the image failed to load from the file. * * @see ::LoadImageRaw() */ - bool Load(const std::string& fileName, int width, int height, int format, int headerSize) { + void Load(const std::string& fileName, int width, int height, int format, int headerSize) { set(::LoadImageRaw(fileName.c_str(), width, height, format, headerSize)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Image from file: " + fileName); + } } /** * Load image sequence from file (frames appended to image.data). * - * @return Whether or not the image animation was loaded successfully. + * @throws raylib::RaylibException Thrown if the image animation to load from the file. * * @see ::LoadImageAnim() */ - bool Load(const std::string& fileName, int* frames) { + void Load(const std::string& fileName, int* frames) { set(::LoadImageAnim(fileName.c_str(), frames)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Image from file: " + fileName); + } } /** * Load image from memory buffer, fileType refers to extension: i.e. "png". * - * @return Whether or not the image data was loaded successfully. + * @throws raylib::RaylibException Thrown if the image animation to load from the file. * * @see ::LoadImageFromMemory() */ - bool Load( + void Load( const std::string& fileType, const unsigned char *fileData, int dataSize) { set(::LoadImageFromMemory(fileType.c_str(), fileData, dataSize)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Image data with file type: " + fileType); + } } /** * Load an image from the given file. * - * @return True or false depending on whether or not the image was loaded from the texture. + * @throws raylib::RaylibException Thrown if the image animation to load from the file. * * @see ::LoadImageFromTexture() */ - bool Load(const ::Texture2D& texture) { + void Load(const ::Texture2D& texture) { set(::LoadImageFromTexture(texture)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Image from texture."); + } } /** @@ -295,17 +296,24 @@ class Image : public ::Image { /** * Export image data to file, returns true on success + * + * @throws raylib::RaylibException Thrown if the image failed to load from the file. */ - inline bool Export(const std::string& fileName) const { - // TODO(RobLoach): Switch to an invalid loading exception on false. - return ::ExportImage(*this, fileName.c_str()); + inline void Export(const std::string& fileName) const { + if (!::ExportImage(*this, fileName.c_str())) { + throw RaylibException(TextFormat("Failed to export Image to file: %s", fileName.c_str())); + } } /** * Export image as code file defining an array of bytes, returns true on success + * + * @throws raylib::RaylibException Thrown if the image failed to load from the file. */ - inline bool ExportAsCode(const std::string& fileName) const { - return ::ExportImageAsCode(*this, fileName.c_str()); + inline void ExportAsCode(const std::string& fileName) const { + if (!::ExportImageAsCode(*this, fileName.c_str())) { + throw RaylibException(TextFormat("Failed to export Image code to file: %s", fileName.c_str())); + } } GETTERSETTER(void*, Data, data) @@ -335,6 +343,14 @@ class Image : public ::Image { return ::ImageFromImage(*this, rec); } + /** + * Convert image data to desired format + */ + inline Image& Format(int newFormat) { + ::ImageFormat(this, newFormat); + return *this; + } + /** * Convert image to POT (power-of-two) */ @@ -344,10 +360,10 @@ class Image : public ::Image { } /** - * Convert image data to desired format + * Crop an image to area defined by a rectangle */ - inline Image& Format(int newFormat) { - ::ImageFormat(this, newFormat); + inline Image& Crop(::Rectangle crop) { + ::ImageCrop(this, crop); return *this; } @@ -383,14 +399,6 @@ class Image : public ::Image { return *this; } - /** - * Crop an image to area defined by a rectangle - */ - inline Image& Crop(::Rectangle crop) { - ::ImageCrop(this, crop); - return *this; - } - /** * Crop an image to a new given width and height. */ @@ -553,6 +561,20 @@ class Image : public ::Image { return ::GetImageAlphaBorder(*this, threshold); } + /** + * Get image pixel color at (x, y) position + */ + inline raylib::Color GetColor(int x = 0, int y = 0) const { + return ::GetImageColor(*this, x, y); + } + + /** + * Get image pixel color at vector position + */ + inline raylib::Color GetColor(::Vector2 position) const { + return ::GetImageColor(*this, static_cast(position.x), static_cast(position.y)); + } + /** * Clear image background with given color */ @@ -728,6 +750,7 @@ class Image : public ::Image { } }; } // namespace raylib + using RImage = raylib::Image; #endif // RAYLIB_CPP_INCLUDE_IMAGE_HPP_ diff --git a/include/Material.hpp b/include/Material.hpp index dc1b6b6e..e0a8cc8b 100644 --- a/include/Material.hpp +++ b/include/Material.hpp @@ -31,6 +31,10 @@ class Material : public ::Material { other.maps = nullptr; other.shader = {}; + other.params[0] = 0.0f; + other.params[1] = 0.0f; + other.params[2] = 0.0f; + other.params[3] = 0.0f; } ~Material() { @@ -118,6 +122,7 @@ class Material : public ::Material { } }; } // namespace raylib + using RMaterial = raylib::Material; #endif // RAYLIB_CPP_INCLUDE_MATERIAL_HPP_ diff --git a/include/Matrix.hpp b/include/Matrix.hpp index ed73d8e2..6767b874 100644 --- a/include/Matrix.hpp +++ b/include/Matrix.hpp @@ -179,11 +179,19 @@ class Matrix : public ::Matrix { /** * Set shader uniform value (matrix 4x4) */ - inline Matrix& SetShaderValue(::Shader shader, int uniformLoc) { + inline Matrix& SetShaderValue(const ::Shader& shader, int uniformLoc) { ::SetShaderValueMatrix(shader, uniformLoc, *this); return *this; } + inline static Matrix GetCamera(const ::Camera& camera) { + return ::GetCameraMatrix(camera); + } + + inline static Matrix GetCamera(const ::Camera2D& camera) { + return ::GetCameraMatrix2D(camera); + } + #endif private: @@ -207,6 +215,7 @@ class Matrix : public ::Matrix { } }; } // namespace raylib + using RMatrix = raylib::Matrix; #endif // RAYLIB_CPP_INCLUDE_MATRIX_HPP_ diff --git a/include/Mesh.hpp b/include/Mesh.hpp index bdf2520c..b0773829 100644 --- a/include/Mesh.hpp +++ b/include/Mesh.hpp @@ -19,22 +19,36 @@ class Mesh : public ::Mesh { set(mesh); } - Mesh(int vertexCount, int triangleCount) : ::Mesh{ + Mesh(int vertexCount = 0, + int triangleCount = 0, + float *vertices = nullptr, + float *texcoords = nullptr, + float *texcoords2 = nullptr, + float *normals = nullptr, + float *tangents = nullptr, + unsigned char *colors = nullptr, + unsigned short *indices = nullptr, + float *animVertices = nullptr, + float *animNormals = nullptr, + unsigned char *boneIds = nullptr, + float *boneWeights = nullptr, + unsigned int vaoId = 0, + unsigned int *vboId = nullptr) : ::Mesh{ vertexCount, triangleCount, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - 0, - nullptr + vertices, + texcoords, + texcoords2, + normals, + tangents, + colors, + indices, + animVertices, + animNormals, + boneIds, + boneWeights, + vaoId, + vboId } {} /** @@ -110,6 +124,13 @@ class Mesh : public ::Mesh { return ::GenMeshCylinder(radius, height, slices); } + /** + * Generate cone/pyramid mesh + */ + static ::Mesh Cone(float radius, float height, int slices) { + return ::GenMeshCone(radius, height, slices); + } + /** * Generate torus mesh */ @@ -222,10 +243,13 @@ class Mesh : public ::Mesh { /** * Export mesh data to file + * + * @throws raylib::RaylibException Throws if failed to export the Mesh. */ - inline bool Export(const std::string& fileName) { - // TODO(RobLoach): Switch to an exception when failed. - return ExportMesh(*this, fileName.c_str()); + inline void Export(const std::string& fileName) { + if (!::ExportMesh(*this, fileName.c_str())) { + throw new RaylibException("Failed to export the Mesh"); + } } /** @@ -294,6 +318,7 @@ class Mesh : public ::Mesh { } }; } // namespace raylib + using RMesh = raylib::Mesh; #endif // RAYLIB_CPP_INCLUDE_MESH_HPP_ diff --git a/include/Model.hpp b/include/Model.hpp index 6889b1a1..45b08232 100644 --- a/include/Model.hpp +++ b/include/Model.hpp @@ -27,6 +27,8 @@ class Model : public ::Model { /* * Load a model from a file. + * + * @throws raylib::RaylibException Throws if failed to load the Modal. */ Model(const std::string& fileName) { Load(fileName); @@ -34,6 +36,8 @@ class Model : public ::Model { /* * Load a model from a mesh. + * + * @throws raylib::RaylibException Throws if failed to load the Modal. */ Model(const ::Mesh& mesh) { Load(mesh); @@ -48,12 +52,13 @@ class Model : public ::Model { Model(Model&& other) { set(other); - other.materials = nullptr; + other.meshCount = 0; other.materialCount = 0; other.meshes = nullptr; - other.meshCount = 0; - other.bones = nullptr; + other.materials = nullptr; + other.meshMaterial = nullptr; other.boneCount = 0; + other.bones = nullptr; other.bindPose = nullptr; } @@ -82,12 +87,13 @@ class Model : public ::Model { Unload(); set(other); - other.bones = nullptr; - other.boneCount = 0; - other.materials = nullptr; + other.meshCount = 0; other.materialCount = 0; other.meshes = nullptr; - other.meshCount = 0; + other.materials = nullptr; + other.meshMaterial = nullptr; + other.boneCount = 0; + other.bones = nullptr; other.bindPose = nullptr; return *this; @@ -205,27 +211,25 @@ class Model : public ::Model { /** * Loads a Model from the given file. * - * @return True of false depending on whether or not the model was successfully loaded. + * @throws raylib::RaylibException Throws if failed to load the Modal. */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadModel(fileName.c_str())); if (!IsReady()) { throw RaylibException("Failed to load Model from " + fileName); } - return IsReady(); } /** * Loads a Model from the given Mesh. * - * @return True of false depending on whether or not the model was successfully loaded. + * @throws raylib::RaylibException Throws if failed to load the Modal. */ - bool Load(const ::Mesh& mesh) { + void Load(const ::Mesh& mesh) { set(::LoadModelFromMesh(mesh)); if (!IsReady()) { throw RaylibException("Failed to load Model from Mesh"); } - return IsReady(); } private: @@ -245,6 +249,7 @@ class Model : public ::Model { }; } // namespace raylib + using RModel = raylib::Model; #endif // RAYLIB_CPP_INCLUDE_MODEL_HPP_ diff --git a/include/ModelAnimation.hpp b/include/ModelAnimation.hpp index 7b57a9dc..a3a45554 100644 --- a/include/ModelAnimation.hpp +++ b/include/ModelAnimation.hpp @@ -24,8 +24,8 @@ class ModelAnimation : public ::ModelAnimation { set(other); other.boneCount = 0; - other.bones = nullptr; other.frameCount = 0; + other.bones = nullptr; other.framePoses = nullptr; } @@ -67,8 +67,8 @@ class ModelAnimation : public ::ModelAnimation { set(other); other.boneCount = 0; - other.bones = nullptr; other.frameCount = 0; + other.bones = nullptr; other.framePoses = nullptr; return *this; @@ -99,12 +99,13 @@ class ModelAnimation : public ::ModelAnimation { private: void set(const ::ModelAnimation& model) { boneCount = model.boneCount; - bones = model.bones; frameCount = model.frameCount; + bones = model.bones; framePoses = model.framePoses; } }; } // namespace raylib + using RModelAnimation = raylib::ModelAnimation; #endif // RAYLIB_CPP_INCLUDE_MODELANIMATION_HPP_ diff --git a/include/Mouse.hpp b/include/Mouse.hpp index fbd27791..c0708bbc 100644 --- a/include/Mouse.hpp +++ b/include/Mouse.hpp @@ -63,6 +63,13 @@ class Mouse { ::SetMousePosition(static_cast(position.x), static_cast(position.y)); } + /** + * Get mouse delta between frames + */ + static inline Vector2 GetDelta() { + return ::GetMouseDelta(); + } + static inline void SetOffset(int offsetX = 0, int offsetY = 0) { ::SetMouseOffset(offsetX, offsetY); } @@ -79,10 +86,22 @@ class Mouse { ::SetMouseScale(scale.x, scale.y); } + /** + * Get mouse wheel movement for X or Y, whichever is larger + */ static inline float GetWheelMove() { return ::GetMouseWheelMove(); } + /** + * Get mouse wheel movement for both X and Y + * + * @see ::GetMouseWheelMoveV() + */ + static inline Vector2 GetWheelMoveV() { + return GetMouseWheelMoveV(); + } + /** * Sets the current mouse cursor icon. * @@ -112,8 +131,23 @@ class Mouse { static inline Vector2 GetTouchPosition(int index) { return ::GetTouchPosition(index); } + + /** + * Get a ray trace from mouse position + */ + static inline Ray GetRay(::Vector2 mousePosition, const ::Camera& camera) { + return ::GetMouseRay(mousePosition, camera); + } + + /** + * Get a ray trace from mouse position + */ + static inline Ray GetRay(const ::Camera& camera) { + return ::GetMouseRay(::GetMousePosition(), camera); + } }; } // namespace raylib + using RMouse = raylib::Mouse; #endif // RAYLIB_CPP_INCLUDE_MOUSE_HPP_ diff --git a/include/Music.hpp b/include/Music.hpp index e7e630d4..c528a9fd 100644 --- a/include/Music.hpp +++ b/include/Music.hpp @@ -13,16 +13,11 @@ namespace raylib { */ class Music : public ::Music { public: - /** - * Default Music constructor to build an empty Music object. - */ - Music() { - ctxType = 0; - ctxData = nullptr; - looping = false; - frameCount = 0; - stream.buffer = nullptr; - } + Music(::AudioStream stream = {nullptr, nullptr, 0, 0, 0}, + unsigned int frameCount = 0, + bool looping = false, + int ctxType = 0, + void *ctxData = nullptr) : ::Music{stream, frameCount, looping, ctxType, ctxData} {} Music(const ::Music& music) { set(music); @@ -34,9 +29,7 @@ class Music : public ::Music { * @throws raylib::RaylibException Throws if the music failed to load. */ Music(const std::string& fileName) { - if (!Load(fileName)) { - throw RaylibException(TextFormat("Failed to load Music from file: %s", fileName.c_str())); - } + Load(fileName); } /** @@ -45,9 +38,7 @@ class Music : public ::Music { * @throws raylib::RaylibException Throws if the music failed to load. */ Music(const std::string& fileType, unsigned char* data, int dataSize) { - if (!Load(fileType, data, dataSize)) { - throw RaylibException(TextFormat("Failed to load Music from %s file", fileType.c_str())); - } + Load(fileType, data, dataSize); } Music(const Music&) = delete; @@ -55,11 +46,11 @@ class Music : public ::Music { Music(Music&& other) { set(other); + other.stream = {}; + other.frameCount = 0; + other.looping = false; other.ctxType = 0; other.ctxData = nullptr; - other.looping = false; - other.frameCount = 0; - other.stream = {}; } /** @@ -177,6 +168,14 @@ class Music : public ::Music { return *this; } + /** + * Set pan for a music (0.5 is center) + */ + inline Music& SetPan(float pan = 0.5f) { + ::SetMusicPan(*this, pan); + return *this; + } + /** * Get music time length (in seconds) */ @@ -193,18 +192,26 @@ class Music : public ::Music { /** * Load music stream from file + * + * @throws raylib::RaylibException Throws if the music failed to load. */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadMusicStream(fileName.c_str())); - return IsReady(); + if (!IsReady()) { + throw RaylibException(TextFormat("Failed to load Music from file: %s", fileName.c_str())); + } } /** * Load music stream from memory + * + * @throws raylib::RaylibException Throws if the music failed to load. */ - bool Load(const std::string& fileType, unsigned char* data, int dataSize) { + void Load(const std::string& fileType, unsigned char* data, int dataSize) { set(::LoadMusicStreamFromMemory(fileType.c_str(), data, dataSize)); - return IsReady(); + if (!IsReady()) { + throw RaylibException(TextFormat("Failed to load Music from %s file dat", fileType.c_str())); + } } /** @@ -218,14 +225,15 @@ class Music : public ::Music { private: void set(const ::Music& music) { + stream = music.stream; + frameCount = music.frameCount; + looping = music.looping; ctxType = music.ctxType; ctxData = music.ctxData; - looping = music.looping; - frameCount = music.frameCount; - stream = music.stream; } }; } // namespace raylib + using RMusic = raylib::Music; #endif // RAYLIB_CPP_INCLUDE_MUSIC_HPP_ diff --git a/include/Physics.hpp b/include/Physics.hpp index 656ee346..2a660a1e 100644 --- a/include/Physics.hpp +++ b/include/Physics.hpp @@ -125,6 +125,7 @@ class Physics { } }; } // namespace raylib + using RPhysics = raylib::Physics; #endif // RAYLIB_CPP_INCLUDE_PHYSICS_HPP_ diff --git a/include/Ray.hpp b/include/Ray.hpp index 23b53334..46911ae3 100644 --- a/include/Ray.hpp +++ b/include/Ray.hpp @@ -15,11 +15,12 @@ class Ray : public ::Ray { set(ray); } - Ray(::Vector3 position, ::Vector3 direction = {0.0f, 0.0f, 0.0f}) : ::Ray{position, direction} { + Ray(::Vector3 position = {0.0f, 0.0f, 0.0f}, ::Vector3 direction = {0.0f, 0.0f, 0.0f}) : + ::Ray{position, direction} { // Nothing. } - Ray(::Vector2 mousePosition, ::Camera camera) { + Ray(::Vector2 mousePosition, const ::Camera& camera) { set(::GetMouseRay(mousePosition, camera)); } @@ -39,46 +40,53 @@ class Ray : public ::Ray { return *this; } - /** - * Detect collision between ray and sphere - */ - inline bool CheckCollisionSphere(::Vector3 center, float radius) const { - return GetRayCollisionSphere(*this, center, radius).hit; - } - /** * Get collision information between ray and sphere */ inline RayCollision GetCollision(::Vector3 center, float radius) const { - return GetRayCollisionSphere(*this, center, radius); + return ::GetRayCollisionSphere(*this, center, radius); } /** * Detect collision between ray and box */ - inline bool CheckCollision(const ::BoundingBox& box) const { - return GetRayCollisionBox(*this, box).hit; + inline RayCollision GetCollision(const ::BoundingBox& box) const { + return ::GetRayCollisionBox(*this, box); } /** * Get collision information between ray and mesh */ inline RayCollision GetCollision(const ::Mesh& mesh, const ::Matrix& transform) const { - return GetRayCollisionMesh(*this, mesh, transform); + return ::GetRayCollisionMesh(*this, mesh, transform); } /** * Get collision info between ray and triangle */ inline RayCollision GetCollision(::Vector3 p1, ::Vector3 p2, ::Vector3 p3) const { - return GetRayCollisionTriangle(*this, p1, p2, p3); + return ::GetRayCollisionTriangle(*this, p1, p2, p3); } /** * Get collision info between ray and quad */ - inline RayCollision GetCollision(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) const { - return GetRayCollisionQuad(*this, p1, p2, p3, p4); + inline RayCollision GetCollision(::Vector3 p1, ::Vector3 p2, ::Vector3 p3, ::Vector3 p4) const { + return ::GetRayCollisionQuad(*this, p1, p2, p3, p4); + } + + /** + * Get a ray trace from mouse position + */ + inline static Ray GetMouse(::Vector2 mousePosition, const ::Camera& camera) { + return ::GetMouseRay(mousePosition, camera); + } + + /** + * Get a ray trace from mouse position + */ + inline static Ray GetMouse(const ::Camera& camera) { + return ::GetMouseRay(::GetMousePosition(), camera); } private: @@ -88,6 +96,7 @@ class Ray : public ::Ray { } }; } // namespace raylib + using RRay = raylib::Ray; #endif // RAYLIB_CPP_INCLUDE_RAY_HPP_ diff --git a/include/RayCollision.hpp b/include/RayCollision.hpp index cf037e6d..9821d228 100644 --- a/include/RayCollision.hpp +++ b/include/RayCollision.hpp @@ -73,6 +73,7 @@ class RayCollision : public ::RayCollision { } }; } // namespace raylib + using RRayCollision = raylib::RayCollision; #endif // RAYLIB_CPP_INCLUDE_RAYCOLLISION_HPP_ diff --git a/include/RaylibException.hpp b/include/RaylibException.hpp index f1160427..1adb5a78 100644 --- a/include/RaylibException.hpp +++ b/include/RaylibException.hpp @@ -32,6 +32,7 @@ class RaylibException : public std::runtime_error { }; } // namespace raylib + using RRaylibException = raylib::RaylibException; #endif // RAYLIB_CPP_INCLUDE_RAYLIBEXCEPTION_HPP_ diff --git a/include/Rectangle.hpp b/include/Rectangle.hpp index 490576bb..80fb8a4b 100644 --- a/include/Rectangle.hpp +++ b/include/Rectangle.hpp @@ -167,6 +167,7 @@ class Rectangle : public ::Rectangle { } }; } // namespace raylib + using RRectangle = raylib::Rectangle; #endif // RAYLIB_CPP_INCLUDE_RECTANGLE_HPP_ diff --git a/include/RenderTexture.hpp b/include/RenderTexture.hpp index 4f17158c..8d1fe11b 100644 --- a/include/RenderTexture.hpp +++ b/include/RenderTexture.hpp @@ -114,6 +114,7 @@ class RenderTexture : public ::RenderTexture { }; typedef RenderTexture RenderTexture2D; } // namespace raylib + using RRenderTexture = raylib::RenderTexture; using RRenderTexture2D = raylib::RenderTexture2D; diff --git a/include/Shader.hpp b/include/Shader.hpp index 87d88c8b..f2bb6d22 100644 --- a/include/Shader.hpp +++ b/include/Shader.hpp @@ -78,10 +78,16 @@ class Shader : public ::Shader { return *this; } + /** + * Unload shader from GPU memory (VRAM) + */ ~Shader() { Unload(); } + /** + * Unload shader from GPU memory (VRAM) + */ void Unload() { if (locs != nullptr) { ::UnloadShader(*this); @@ -176,6 +182,7 @@ class Shader : public ::Shader { } }; } // namespace raylib + using RShader = raylib::Shader; #endif // RAYLIB_CPP_INCLUDE_SHADER_HPP_ diff --git a/include/Sound.hpp b/include/Sound.hpp index 35157704..f01abed1 100644 --- a/include/Sound.hpp +++ b/include/Sound.hpp @@ -22,8 +22,8 @@ class Sound : public ::Sound { Sound& operator=(const Sound&) = delete; Sound() { + stream = { nullptr, nullptr, 0, 0, 0 }; frameCount = 0; - stream.buffer = nullptr; } Sound(::AudioStream stream, unsigned int frameCount) : ::Sound{stream, frameCount} { @@ -33,7 +33,7 @@ class Sound : public ::Sound { Sound(Sound&& other) { set(other); - other.stream = { 0, 0, 0, 0, 0 }; + other.stream = { nullptr, nullptr, 0, 0, 0 }; other.frameCount = 0; } @@ -43,9 +43,7 @@ class Sound : public ::Sound { * @throws raylib::RaylibException Throws if the Sound failed to load. */ Sound(const std::string& fileName) { - if (!Load(fileName)) { - throw RaylibException(TextFormat("Failed to load Sound from file: %s", fileName.c_str())); - } + Load(fileName); } /** @@ -54,9 +52,7 @@ class Sound : public ::Sound { * @throws raylib::RaylibException Throws if the Sound failed to load. */ Sound(const ::Wave& wave) { - if (!Load(wave)) { - throw RaylibException("Failed to load Sound from Wave"); - } + Load(wave); } ~Sound() { @@ -74,7 +70,7 @@ class Sound : public ::Sound { Unload(); set(other); other.frameCount = 0; - other.stream = { 0, 0, 0, 0 }; + other.stream = { nullptr, nullptr, 0, 0, 0 }; return *this; } @@ -173,25 +169,38 @@ class Sound : public ::Sound { return *this; } + /** + * Set pan for a sound (0.5 is center) + */ + inline Sound& SetPan(float pan = 0.5f) { + ::SetSoundPan(*this, pan); + return *this; + } + /** * Load a sound from the given file. * - * @return True or false depending on loading worked. + * @throws raylib::RaylibException Throws if the Sound failed to load. */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadSound(fileName.c_str())); - return IsReady(); + if (!IsReady()) { + throw new RaylibException("Failed to load Sound from file"); + } } /** * Loads the given Wave object into the Sound. + * + * @throws raylib::RaylibException Throws if the Sound failed to load. */ - bool Load(const ::Wave& wave) { + void Load(const ::Wave& wave) { set(::LoadSoundFromWave(wave)); - return IsReady(); + if (!IsReady()) { + throw new RaylibException("Failed to load Wave"); + } } - /** * Retrieve whether or not the Sound buffer is loaded. * @@ -201,6 +210,13 @@ class Sound : public ::Sound { return stream.buffer != nullptr; } + /** + * Get number of sounds playing in the multichannel + */ + int GetPlaying() { + return ::GetSoundsPlaying(); + } + private: void set(const ::Sound& sound) { frameCount = sound.frameCount; @@ -208,6 +224,7 @@ class Sound : public ::Sound { } }; } // namespace raylib + using RSound = raylib::Sound; #endif // RAYLIB_CPP_INCLUDE_SOUND_HPP_ diff --git a/include/Text.hpp b/include/Text.hpp index 1090c8f8..335d688d 100644 --- a/include/Text.hpp +++ b/include/Text.hpp @@ -206,6 +206,7 @@ class Text { } }; } // namespace raylib + using RText = raylib::Text; #endif // RAYLIB_CPP_INCLUDE_TEXT_HPP_ diff --git a/include/Texture.hpp b/include/Texture.hpp index 383b8543..ee9a7d08 100644 --- a/include/Texture.hpp +++ b/include/Texture.hpp @@ -25,8 +25,10 @@ class Texture : public ::Texture { /** * Move/Create a texture structure manually. */ - Texture(unsigned int id, int width, int height, - int mipmaps = 1, int format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + Texture(unsigned int id, + int width, int height, + int mipmaps = 1, + int format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) : ::Texture{id, width, height, mipmaps, format} { // Nothing. } @@ -242,6 +244,18 @@ class Texture : public ::Texture { return *this; } + inline Texture& Draw(::Vector3 position, float width, float height, float length, + ::Color tint = {255, 255, 255, 255}) { + ::DrawCubeTexture(*this, position, width, height, length, tint); + return *this; + } + + inline Texture& DrawTiled(::Rectangle sourceRec, ::Rectangle destRec, ::Vector2 origin = {0, 0}, + float rotation = 0, float scale = 1, Color tint = {255, 255, 255, 255}) { + ::DrawTextureTiled(*this, sourceRec, destRec, origin, rotation, scale, tint); + return *this; + } + inline Texture& Draw(::Rectangle sourceRec, ::Rectangle destRec, ::Vector2 origin = {0, 0}, float rotation = 0, ::Color tint = {255, 255, 255, 255}) { ::DrawTexturePro(*this, sourceRec, destRec, origin, rotation, tint); @@ -254,22 +268,41 @@ class Texture : public ::Texture { return *this; } - inline Texture& Draw(::Vector3 position, float width, float height, float length, + inline Texture& DrawPoly(::Vector2 center, ::Vector2 *points, + ::Vector2 *texcoords, int pointsCount, ::Color tint = {255, 255, 255, 255}) { - ::DrawCubeTexture(*this, position, width, height, length, tint); + ::DrawTexturePoly(*this, center, points, texcoords, pointsCount, tint); return *this; } - inline Texture& DrawTiled(::Rectangle sourceRec, ::Rectangle destRec, ::Vector2 origin = {0, 0}, - float rotation = 0, float scale = 1, Color tint = {255, 255, 255, 255}) { - ::DrawTextureTiled(*this, sourceRec, destRec, origin, rotation, scale, tint); + /** + * Draw a billboard texture + */ + inline Texture& DrawBillboard(const ::Camera& camera, + ::Vector3 position, float size, + ::Color tint = {255, 255, 255, 255}) { + ::DrawBillboard(camera, *this, position, size, tint); return *this; } - inline Texture& DrawPoly(Vector2 center, Vector2 *points, - Vector2 *texcoords, int pointsCount, - Color tint = {255, 255, 255, 255}) { - ::DrawTexturePoly(*this, center, points, texcoords, pointsCount, tint); + /** + * Draw a billboard texture defined by source + */ + inline Texture& DrawBillboard(const ::Camera& camera, + ::Rectangle source, ::Vector3 position, ::Vector2 size, + ::Color tint = {255, 255, 255, 255}) { + DrawBillboardRec(camera, *this, source, position, size, tint); + return *this; + } + + /** + * Draw a billboard texture defined by source and rotation + */ + inline Texture& DrawBillboard(const ::Camera& camera, + ::Rectangle source, Vector3 position, + ::Vector3 up, Vector2 size, Vector2 origin, float rotation, + ::Color tint = {255, 255, 255, 255}) { + DrawBillboardPro(camera, *this, source, position, up, size, origin, rotation, tint); return *this; } @@ -326,6 +359,7 @@ typedef Texture Texture2D; typedef Texture TextureCubemap; } // namespace raylib + using RTexture = raylib::Texture; using RTexture2D = raylib::Texture2D; using RTextureCubemap = raylib::TextureCubemap; diff --git a/include/Touch.hpp b/include/Touch.hpp new file mode 100644 index 00000000..9a7a4ba2 --- /dev/null +++ b/include/Touch.hpp @@ -0,0 +1,51 @@ +#ifndef RAYLIB_CPP_INCLUDE_TOUCH_HPP_ +#define RAYLIB_CPP_INCLUDE_TOUCH_HPP_ + +#include "./raylib.hpp" + +namespace raylib { +/** + * Input-related functions: touch + */ +class Touch { + public: + /** + * Get touch position X for touch point 0 (relative to screen size) + */ + inline static int GetX() { + return ::GetTouchX(); + } + + /** + * Get touch position Y for touch point 0 (relative to screen size) + */ + inline static int GetY() { + return ::GetTouchY(); + } + + /** + * Get touch position XY for a touch point index (relative to screen size) + */ + inline static Vector2 GetPosition(int index) { + return ::GetTouchPosition(index); + } + + /** + * Get touch point identifier for given index + */ + inline static int GetPointId(int index) { + return ::GetTouchPointId(index); + } + + /** + * Get number of touch points + */ + inline static int GetPointCount() { + return ::GetTouchPointCount(); + } +}; +} // namespace raylib + +using RTouch = raylib::Touch; + +#endif // RAYLIB_CPP_INCLUDE_MOUSE_HPP_ diff --git a/include/Vector2.hpp b/include/Vector2.hpp index f28a31f2..fe954aba 100644 --- a/include/Vector2.hpp +++ b/include/Vector2.hpp @@ -288,22 +288,22 @@ class Vector2 : public ::Vector2 { } #endif - inline Vector2& DrawPixel(::Color color) { + inline Vector2& DrawPixel(::Color color = {0, 0, 0, 255}) { ::DrawPixelV(*this, color); return *this; } - inline Vector2& DrawLine(::Vector2 endPos, ::Color color) { + inline Vector2& DrawLine(::Vector2 endPos, ::Color color = {0, 0, 0, 255}) { ::DrawLineV(*this, endPos, color); return *this; } - inline Vector2& DrawLine(::Vector2 endPos, float thick, ::Color color) { + inline Vector2& DrawLine(::Vector2 endPos, float thick, ::Color color = {0, 0, 0, 255}) { ::DrawLineEx(*this, endPos, thick, color); return *this; } - inline Vector2& DrawLineBezier(::Vector2 endPos, float thick, ::Color color) { + inline Vector2& DrawLineBezier(::Vector2 endPos, float thick, ::Color color = {0, 0, 0, 255}) { ::DrawLineBezier(*this, endPos, thick, color); return *this; } @@ -315,7 +315,7 @@ class Vector2 : public ::Vector2 { ::Vector2 endPos, ::Vector2 controlPos, float thick, - ::Color color) { + ::Color color = {0, 0, 0, 255}) { ::DrawLineBezierQuad(*this, endPos, controlPos, thick, color); return *this; } @@ -323,17 +323,17 @@ class Vector2 : public ::Vector2 { /** * Draw a color-filled circle (Vector version) */ - inline Vector2& DrawCircle(float radius, ::Color color) { + inline Vector2& DrawCircle(float radius, ::Color color = {0, 0, 0, 255}) { ::DrawCircleV(*this, radius, color); return *this; } - inline Vector2& DrawRectangle(::Vector2 size, ::Color color) { + inline Vector2& DrawRectangle(::Vector2 size, ::Color color = {0, 0, 0, 255}) { ::DrawRectangleV(*this, size, color); return *this; } - inline Vector2& DrawPoly(int sides, float radius, float rotation, ::Color color) { + inline Vector2& DrawPoly(int sides, float radius, float rotation, ::Color color = {0, 0, 0, 255}) { ::DrawPoly(*this, sides, radius, rotation, color); return *this; } @@ -383,6 +383,13 @@ class Vector2 : public ::Vector2 { return ::CheckCollisionLines(*this, endPos1, startPos2, endPos2, collisionPoint); } + /** + * Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] + */ + inline bool CheckCollisionPointLine(::Vector2 p1, ::Vector2 p2, int threshold = 1) { + return ::CheckCollisionPointLine(*this, p1, p2, threshold); + } + private: void set(const ::Vector2& vec) { x = vec.x; @@ -391,6 +398,7 @@ class Vector2 : public ::Vector2 { }; } // namespace raylib + using RVector2 = raylib::Vector2; #endif // RAYLIB_CPP_INCLUDE_VECTOR2_HPP_ diff --git a/include/Vector3.hpp b/include/Vector3.hpp index 5e8b9d8b..7ee3fd33 100644 --- a/include/Vector3.hpp +++ b/include/Vector3.hpp @@ -362,6 +362,7 @@ class Vector3 : public ::Vector3 { } }; } // namespace raylib + using RVector3 = raylib::Vector3; #endif // RAYLIB_CPP_INCLUDE_VECTOR3_HPP_ diff --git a/include/Vector4.hpp b/include/Vector4.hpp index 2fcb68fb..57e3cb24 100644 --- a/include/Vector4.hpp +++ b/include/Vector4.hpp @@ -165,6 +165,7 @@ class Vector4 : public ::Vector4 { // Alias the Vector4 as Quaternion. typedef Vector4 Quaternion; } // namespace raylib + using RVector4 = raylib::Vector4; using RQuaternion = raylib::Quaternion; diff --git a/include/VrStereoConfig.hpp b/include/VrStereoConfig.hpp index e158675c..e163d892 100644 --- a/include/VrStereoConfig.hpp +++ b/include/VrStereoConfig.hpp @@ -11,13 +11,13 @@ namespace raylib { class VrStereoConfig : public ::VrStereoConfig { public: VrStereoConfig(const ::VrDeviceInfo& info) { - Init(info); + Load(info); } /** * Load VR stereo config for VR simulator device parameters */ - inline void Init(const ::VrDeviceInfo& info) { + inline void Load(const ::VrDeviceInfo& info) { set(LoadVrStereoConfig(info)); } @@ -39,7 +39,7 @@ class VrStereoConfig : public ::VrStereoConfig { /** * End stereo rendering */ - inline VrStereoConfig& EndDrawing() { + inline VrStereoConfig& EndMode() { ::EndVrStereoMode(); return *this; } @@ -54,24 +54,25 @@ class VrStereoConfig : public ::VrStereoConfig { private: void set(const ::VrStereoConfig& config) { projection[0] = config.projection[0]; - viewOffset[1] = config.viewOffset[1]; - projection[0] = config.projection[0]; + projection[1] = config.projection[1]; + viewOffset[0] = config.viewOffset[0]; viewOffset[1] = config.viewOffset[1]; leftLensCenter[0] = config.leftLensCenter[0]; leftLensCenter[1] = config.leftLensCenter[1]; - rightLensCenter[0] = config.leftLensCenter[0]; - rightLensCenter[1] = config.leftLensCenter[1]; - leftScreenCenter[0] = config.leftLensCenter[0]; - leftScreenCenter[1] = config.leftLensCenter[1]; - rightScreenCenter[0] = config.leftLensCenter[0]; - rightScreenCenter[1] = config.leftLensCenter[1]; - scale[0] = config.leftLensCenter[0]; - scale[1] = config.leftLensCenter[1]; - scaleIn[0] = config.leftLensCenter[0]; - scaleIn[1] = config.leftLensCenter[1]; + rightLensCenter[0] = config.rightLensCenter[0]; + rightLensCenter[1] = config.rightLensCenter[1]; + leftScreenCenter[0] = config.leftScreenCenter[0]; + leftScreenCenter[1] = config.leftScreenCenter[1]; + rightScreenCenter[0] = config.rightScreenCenter[0]; + rightScreenCenter[1] = config.rightScreenCenter[1]; + scale[0] = config.scale[0]; + scale[1] = config.scale[1]; + scaleIn[0] = config.scaleIn[0]; + scaleIn[1] = config.scaleIn[1]; } }; } // namespace raylib + using RVrStereoConfig = raylib::VrStereoConfig; #endif // RAYLIB_CPP_INCLUDE_VRSTEREOCONFIG_HPP_ diff --git a/include/Wave.hpp b/include/Wave.hpp index be5d052b..bd3e2766 100644 --- a/include/Wave.hpp +++ b/include/Wave.hpp @@ -28,20 +28,20 @@ class Wave : public ::Wave { /** * Load wave data from file + * + * @throws raylib::RaylibException Throws if the Wave failed to load. */ Wave(const std::string& fileName) { - if (!Load(fileName)) { - throw RaylibException(TextFormat("Failed to load Wave from file: %s", fileName.c_str())); - } + Load(fileName); } /** * Load wave from memory buffer, fileType refers to extension: i.e. "wav" + * + * @throws raylib::RaylibException Throws if the Wave failed to load. */ Wave(const std::string& fileType, const unsigned char *fileData, int dataSize) { - if (!Load(fileType, fileData, dataSize)) { - throw RaylibException("Failed to load Wave from memory"); - } + Load(fileType, fileData, dataSize); } Wave(const Wave& other) { @@ -104,14 +104,6 @@ class Wave : public ::Wave { return *this; } - /** - * Convert wave data to desired format - */ - inline Wave& Format(int SampleRate, int SampleSize, int Channels = 2) { - ::WaveFormat(this, SampleRate, SampleSize, Channels); - return *this; - } - /** * Copy a wave to a new wave */ @@ -127,6 +119,14 @@ class Wave : public ::Wave { return *this; } + /** + * Convert wave data to desired format + */ + inline Wave& Format(int SampleRate, int SampleSize, int Channels = 2) { + ::WaveFormat(this, SampleRate, SampleSize, Channels); + return *this; + } + /** * Load samples data from wave as a floats array */ @@ -137,7 +137,7 @@ class Wave : public ::Wave { /** * Unload samples data loaded with LoadWaveSamples() */ - inline void UnloadSamples(float *samples) { + inline static void UnloadSamples(float *samples) { ::UnloadWaveSamples(samples); } @@ -184,21 +184,25 @@ class Wave : public ::Wave { /** * Load wave data from file. * - * @return True or false depending on if the Wave data was loaded properly. + * @throws raylib::RaylibException Throws if the Wave failed to load. */ - bool Load(const std::string& fileName) { + void Load(const std::string& fileName) { set(::LoadWave(fileName.c_str())); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Wave from file: " + fileName); + } } /** * Load wave from memory buffer, fileType refers to extension: i.e. "wav" * - * @return True or false depending on if the Wave data was loaded properly. + * @throws raylib::RaylibException Throws if the Wave failed to load. */ - bool Load(const std::string& fileType, const unsigned char *fileData, int dataSize) { + void Load(const std::string& fileType, const unsigned char *fileData, int dataSize) { set(::LoadWaveFromMemory(fileType.c_str(), fileData, dataSize)); - return IsReady(); + if (!IsReady()) { + throw RaylibException("Failed to load Wave from file data of type: " + fileType); + } } /** @@ -221,6 +225,7 @@ class Wave : public ::Wave { }; } // namespace raylib + using RWave = raylib::Wave; #endif // RAYLIB_CPP_INCLUDE_WAVE_HPP_ diff --git a/include/Window.hpp b/include/Window.hpp index fe2998a8..7acf4282 100644 --- a/include/Window.hpp +++ b/include/Window.hpp @@ -5,6 +5,7 @@ #include "./raylib.hpp" #include "./RaylibException.hpp" +#include "./Vector2.hpp" namespace raylib { /** @@ -12,14 +13,21 @@ namespace raylib { */ class Window { public: + /** + * Build a Window object, but defer the initialization. Ensure you call Init() manually. + * + * @see Init() + */ Window() { - // need to create it manually with Init(). + // Nothing. } /** * Initialize window and OpenGL context. + * + * @throws raylib::RaylibException Thrown if the window failed to initiate. */ - Window(int width, int height, const std::string& title) { + Window(int width, int height, const std::string& title = "raylib") { Init(width, height, title); } @@ -256,6 +264,14 @@ class Window { return *this; } + /** + * Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) + */ + inline Window& SetOpacity(float opacity) { + ::SetWindowOpacity(opacity); + return *this; + } + /** * Set window dimensions */ @@ -307,20 +323,48 @@ class Window { return ::GetScreenHeight(); } + /** + * Get current render width (it considers HiDPI) + */ + inline int GetRenderWidth() const { + return ::GetRenderWidth(); + } + + /** + * Get current render height (it considers HiDPI) + */ + inline int GetRenderHeight() const { + return ::GetRenderHeight(); + } + /** * Get window position XY on monitor */ - inline ::Vector2 GetPosition() const { + inline Vector2 GetPosition() const { return ::GetWindowPosition(); } /** * Get window scale DPI factor */ - inline ::Vector2 GetScaleDPI() const { + inline Vector2 GetScaleDPI() const { return ::GetWindowScaleDPI(); } + /** + * Set clipboard text content + */ + inline void SetClipboardText(const std::string& text) { + SetClipboardText(text.c_str()); + } + + /** + * Get clipboard text content + */ + inline const std::string& GetClipboardText() { + return GetClipboardText(); + } + /** * Set target FPS (maximum) */ @@ -359,6 +403,7 @@ class Window { } }; } // namespace raylib + using RWindow = raylib::Window; #endif // RAYLIB_CPP_INCLUDE_WINDOW_HPP_ diff --git a/include/raylib-cpp.hpp b/include/raylib-cpp.hpp index 74301f98..bb221c2c 100644 --- a/include/raylib-cpp.hpp +++ b/include/raylib-cpp.hpp @@ -57,6 +57,7 @@ #include "./Sound.hpp" #include "./Text.hpp" #include "./Texture.hpp" +#include "./Touch.hpp" #include "./Vector2.hpp" #include "./Vector3.hpp" #include "./Vector4.hpp" From d19cbccebb0196945693131c7ee3c8a47c5c96ff Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 20 Aug 2022 16:12:40 -0400 Subject: [PATCH 4/5] Remove physac C++ wrapper --- clib.json | 1 - examples/CMakeLists.txt | 2 +- examples/physics/physac.h | 2142 ----------------------------- examples/physics/physics_demo.cpp | 134 -- include/CMakeLists.txt | 1 - include/Functions.hpp | 3 - include/Physics.hpp | 131 -- 7 files changed, 1 insertion(+), 2413 deletions(-) delete mode 100644 examples/physics/physac.h delete mode 100644 examples/physics/physics_demo.cpp delete mode 100644 include/Physics.hpp diff --git a/clib.json b/clib.json index 7c23867e..e59f0b71 100644 --- a/clib.json +++ b/clib.json @@ -39,7 +39,6 @@ "include/Mouse.hpp", "include/Music.hpp", "include/physac.hpp", - "include/Physics.hpp", "include/Ray.hpp", "include/RayCollision.hpp", "include/RaylibException.hpp", diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 55277c80..bc1c36a4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # Get the sources together -set(example_dirs audio core models physics shaders shapes text textures) +set(example_dirs audio core models shaders shapes text textures) set(example_sources) set(example_resources) diff --git a/examples/physics/physac.h b/examples/physics/physac.h deleted file mode 100644 index 47b38a78..00000000 --- a/examples/physics/physac.h +++ /dev/null @@ -1,2142 +0,0 @@ -/********************************************************************************************** -* -* Physac v1.1 - 2D Physics library for videogames -* -* DESCRIPTION: -* -* Physac is a small 2D physics library written in pure C. The engine uses a fixed time-step thread loop -* to simluate physics. A physics step contains the following phases: get collision information, -* apply dynamics, collision solving and position correction. It uses a very simple struct for physic -* bodies with a position vector to be used in any 3D rendering API. -* -* CONFIGURATION: -* -* #define PHYSAC_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 PHYSAC_STATIC (defined by default) -* The generated implementation will stay private inside implementation file and all -* internal symbols and functions will only be visible inside that file. -* -* #define PHYSAC_NO_THREADS -* The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). -* It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. -* -* #define PHYSAC_STANDALONE -* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined -* internally in the library and input management and drawing functions must be provided by -* the user (check library implementation for further details). -* -* #define PHYSAC_DEBUG -* Traces log messages when creating and destroying physics bodies and detects errors in physics -* calculations and reference exceptions; it is useful for debug purposes -* -* #define PHYSAC_MALLOC() -* #define PHYSAC_FREE() -* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. -* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. -* -* -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) -* -* Use the following code to compile: -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lpthread -lopengl32 -lgdi32 -lwinmm -std=c99 -* -* VERY THANKS TO: -* - raysan5: helped with library design -* - ficoos: added support for Linux -* - R8D8: added support for Linux -* - jubalh: fixed implementation of time calculations -* - a3f: fixed implementation of time calculations -* - define-private-public: added support for OSX -* - pamarcos: fixed implementation of physics steps -* - noshbar: fixed some memory leaks -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2016-2020 Victor Fisac (github: @victorfisac) -* -* 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. -* -**********************************************************************************************/ - -#if !defined(PHYSAC_H) -#define PHYSAC_H - -// #define PHYSAC_STATIC -// #define PHYSAC_NO_THREADS -// #define PHYSAC_STANDALONE -// #define PHYSAC_DEBUG - -#if defined(PHYSAC_STATIC) - #define PHYSACDEF static // Functions just visible to module including this file -#else - #if defined(__cplusplus) - #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define PHYSACDEF extern // Functions visible from other files - #endif -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define PHYSAC_MAX_BODIES 64 -#define PHYSAC_MAX_MANIFOLDS 4096 -#define PHYSAC_MAX_VERTICES 24 -#define PHYSAC_CIRCLE_VERTICES 24 - -#define PHYSAC_COLLISION_ITERATIONS 100 -#define PHYSAC_PENETRATION_ALLOWANCE 0.05f -#define PHYSAC_PENETRATION_CORRECTION 0.4f - -#define PHYSAC_PI 3.14159265358979323846 -#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) - -#define PHYSAC_MALLOC(size) malloc(size) -#define PHYSAC_FREE(ptr) free(ptr) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -// NOTE: Below types are required for PHYSAC_STANDALONE usage -//---------------------------------------------------------------------------------- -#if defined(PHYSAC_STANDALONE) - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; - - // Boolean type - #if !defined(_STDBOOL_H) - typedef enum { false, true } bool; - #define _STDBOOL_H - #endif -#endif - -typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; - -// Previously defined to be used in PhysicsShape struct as circular dependencies -typedef struct PhysicsBodyData *PhysicsBody; - -// Mat2 type (used for polygon shape rotation matrix) -typedef struct Mat2 { - float m00; - float m01; - float m10; - float m11; -} Mat2; - -typedef struct PolygonData { - unsigned int vertexCount; // Current used vertex and normals count - Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors - Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors -} PolygonData; - -typedef struct PhysicsShape { - PhysicsShapeType type; // Physics shape type (circle or polygon) - PhysicsBody body; // Shape physics body reference - float radius; // Circle shape radius (used for circle shapes) - Mat2 transform; // Vertices transform matrix 2x2 - PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) -} PhysicsShape; - -typedef struct PhysicsBodyData { - unsigned int id; // Reference unique identifier - bool enabled; // Enabled dynamics state (collisions are calculated anyway) - Vector2 position; // Physics body shape pivot - Vector2 velocity; // Current linear velocity applied to position - Vector2 force; // Current linear force (reset to 0 every step) - float angularVelocity; // Current angular velocity applied to orient - float torque; // Current angular force (reset to 0 every step) - float orient; // Rotation in radians - float inertia; // Moment of inertia - float inverseInertia; // Inverse value of inertia - float mass; // Physics body mass - float inverseMass; // Inverse value of mass - float staticFriction; // Friction when the body has not movement (0 to 1) - float dynamicFriction; // Friction when the body has movement (0 to 1) - float restitution; // Restitution coefficient of the body (0 to 1) - bool useGravity; // Apply gravity force to dynamics - bool isGrounded; // Physics grounded on other body state - bool freezeOrient; // Physics rotation constraint - PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) -} PhysicsBodyData; - -typedef struct PhysicsManifoldData { - unsigned int id; // Reference unique identifier - PhysicsBody bodyA; // Manifold first physics body reference - PhysicsBody bodyB; // Manifold second physics body reference - float penetration; // Depth of penetration from collision - Vector2 normal; // Normal direction vector from 'a' to 'b' - Vector2 contacts[2]; // Points of contact during collision - unsigned int contactsCount; // Current collision number of contacts - float restitution; // Mixed restitution during collision - float dynamicFriction; // Mixed dynamic friction during collision - float staticFriction; // Mixed static friction during collision -} PhysicsManifoldData, *PhysicsManifold; - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop -PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default -PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled -PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force -PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body -PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body -PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies -PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index -PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape -PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body -PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread - -#if defined(__cplusplus) -} -#endif - -#endif // PHYSAC_H - -/*********************************************************************************** -* -* PHYSAC IMPLEMENTATION -* -************************************************************************************/ - -#if defined(PHYSAC_IMPLEMENTATION) - -#if !defined(PHYSAC_NO_THREADS) - #include // Required for: pthread_t, pthread_create() -#endif - -#if defined(PHYSAC_DEBUG) - #include // Required for: printf() -#endif - -#include // Required for: malloc(), free(), srand(), rand() -#include // Required for: cosf(), sinf(), fabs(), sqrtf() -#include // Required for: uint64_t - -#if !defined(PHYSAC_STANDALONE) - #include "raymath.h" // Required for: Vector2Add(), Vector2Subtract() -#endif - -// Time management functionality -#include // Required for: time(), clock_gettime() -#if defined(_WIN32) - // Functions required to query time on Windows - #if defined(__cplusplus) - extern "C" { // Prevents name mangling of functions - #endif - int __stdcall QueryPerformanceCounter(unsigned long long int* lpPerformanceCount); - int __stdcall QueryPerformanceFrequency(unsigned long long int* lpFrequency); - #if defined(__cplusplus) - } - #endif -#elif defined(__linux__) - #if _POSIX_C_SOURCE < 199309L - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. - #endif - #include // Required for: timespec -#elif defined(__APPLE__) // macOS also defines __MACH__ - #include // Required for: mach_absolute_time() -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define min(a,b) (((a)<(b))?(a):(b)) -#define max(a,b) (((a)>(b))?(a):(b)) -#define PHYSAC_FLT_MAX 3.402823466e+38f -#define PHYSAC_EPSILON 0.000001f -#define PHYSAC_K 1.0f/3.0f -#define PHYSAC_VECTOR_ZERO (Vector2){ 0.0f, 0.0f } - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -#if !defined(PHYSAC_NO_THREADS) -static pthread_t physicsThreadId; // Physics thread id -#endif -static unsigned int usedMemory = 0; // Total allocated dynamic memory -static bool physicsThreadEnabled = false; // Physics thread enabled state -static double baseTime = 0.0; // Offset time for MONOTONIC clock -static double startTime = 0.0; // Start time in milliseconds -static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds -static double currentTime = 0.0; // Current time in milliseconds -static uint64_t frequency = 0; // Hi-res clock frequency - -static double accumulator = 0.0; // Physics time step delta time accumulator -static unsigned int stepsCount = 0; // Total physics steps processed -static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force -static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array -static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter -static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array -static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter - -//---------------------------------------------------------------------------------- -// Module Internal Functions Declaration -//---------------------------------------------------------------------------------- -static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization -static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions -static void *PhysicsLoop(void *arg); // Physics loop thread function -static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections) -static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization -static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision -static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold -static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies -static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies -static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies -static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies -static void SolveDifferentShapes(PhysicsManifold manifold, PhysicsBody bodyA, PhysicsBody bodyB); // Solve collision between two different types of shapes -static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies -static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity -static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions -static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions -static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces -static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration -static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces -static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range -static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points - -static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds -static double GetCurrentTime(void); // Get current time measure in milliseconds - -// Math functions -static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value -static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors -static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector -static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors -static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors -static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector -#if defined(PHYSAC_STANDALONE) -static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors -static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors -#endif - -static Mat2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value -static void Mat2Set(Mat2 *matrix, float radians); // Set values from radians to a created matrix 2x2 -static inline Mat2 Mat2Transpose(Mat2 matrix); // Returns the transpose of a given matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2 - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- -// Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void InitPhysics(void) -{ - #if !defined(PHYSAC_NO_THREADS) - // NOTE: if defined, user will need to create a thread for PhysicsThread function manually - // Create physics thread using POSIXS thread libraries - pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL); - #endif - - // Initialize high resolution timer - InitTimer(); - - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] physics module initialized successfully\n"); - #endif - - accumulator = 0.0; -} - -// Returns true if physics thread is currently enabled -PHYSACDEF bool IsPhysicsEnabled(void) -{ - return physicsThreadEnabled; -} - -// Sets physics global gravity force -PHYSACDEF void SetPhysicsGravity(float x, float y) -{ - gravityForce.x = x; - gravityForce.y = y; -} - -// Creates a new circle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) -{ - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int newId = FindAvailableBodyIndex(); - if (newId != -1) - { - // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = PHYSAC_VECTOR_ZERO; - newBody->force = PHYSAC_VECTOR_ZERO; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_CIRCLE; - newBody->shape.body = newBody; - newBody->shape.radius = radius; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = (PolygonData) { 0 }; - - newBody->mass = PHYSAC_PI*radius*radius*density; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = newBody->mass*radius*radius; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; - physicsBodiesCount++; - - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif - - return newBody; -} - -// Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) -{ - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int newId = FindAvailableBodyIndex(); - if (newId != -1) - { - // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = (Vector2){ 0.0f }; - newBody->force = (Vector2){ 0.0f }; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.radius = 0.0f; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRectanglePolygon(pos, (Vector2){ width, height }); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextIndex]; - - float D = MathCrossVector2(p1, p2); - float triangleArea = D/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); - center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); - - float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; - float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; - inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) - // Note: this is not really necessary - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; - } - - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; - physicsBodiesCount++; - - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif - - return newBody; -} - -// Creates a new polygon physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) -{ - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int newId = FindAvailableBodyIndex(); - if (newId != -1) - { - // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = PHYSAC_VECTOR_ZERO; - newBody->force = PHYSAC_VECTOR_ZERO; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRandomPolygon(radius, sides); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 position1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 position2 = newBody->shape.vertexData.positions[nextIndex]; - - float cross = MathCrossVector2(position1, position2); - float triangleArea = cross/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(position1.x + position2.x); - center.y += triangleArea*PHYSAC_K*(position1.y + position2.y); - - float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x; - float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y; - inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) - // Note: this is not really necessary - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; - } - - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; - physicsBodiesCount++; - - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif - - return newBody; -} - -// Adds a force to a physics body -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) -{ - if (body != NULL) - body->force = Vector2Add(body->force, force); -} - -// Adds an angular force to a physics body -PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) -{ - if (body != NULL) - body->torque += amount; -} - -// Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) -{ - if (body != NULL) - { - if (body->shape.type == PHYSICS_POLYGON) - { - PolygonData vertexData = body->shape.vertexData; - bool collision = false; - - for (int i = 0; i < vertexData.vertexCount; i++) - { - Vector2 positionA = body->position; - Vector2 positionB = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[i])); - int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); - Vector2 positionC = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[nextIndex])); - - // Check collision between each triangle - float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ - ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); - - float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/ - ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); - - float gamma = 1.0f - alpha - beta; - - if ((alpha > 0.0f) && (beta > 0.0f) && (gamma > 0.0f)) - { - collision = true; - break; - } - } - - if (collision) - { - int count = vertexData.vertexCount; - Vector2 bodyPos = body->position; - Vector2 *vertices = (Vector2*)PHYSAC_MALLOC(sizeof(Vector2) * count); - Mat2 trans = body->shape.transform; - - for (int i = 0; i < count; i++) - vertices[i] = vertexData.positions[i]; - - // Destroy shattered physics body - DestroyPhysicsBody(body); - - for (int i = 0; i < count; i++) - { - int nextIndex = (((i + 1) < count) ? (i + 1) : 0); - Vector2 center = TriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); - center = Vector2Add(bodyPos, center); - Vector2 offset = Vector2Subtract(center, bodyPos); - - PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values - - PolygonData newData = { 0 }; - newData.vertexCount = 3; - - newData.positions[0] = Vector2Subtract(vertices[i], offset); - newData.positions[1] = Vector2Subtract(vertices[nextIndex], offset); - newData.positions[2] = Vector2Subtract(position, center); - - // Separate vertices to avoid unnecessary physics collisions - newData.positions[0].x *= 0.95f; - newData.positions[0].y *= 0.95f; - newData.positions[1].x *= 0.95f; - newData.positions[1].y *= 0.95f; - newData.positions[2].x *= 0.95f; - newData.positions[2].y *= 0.95f; - - // Calculate polygon faces normals - for (int j = 0; j < newData.vertexCount; j++) - { - int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0); - Vector2 face = Vector2Subtract(newData.positions[nextVertex], newData.positions[j]); - - newData.normals[j] = (Vector2){ face.y, -face.x }; - MathNormalize(&newData.normals[j]); - } - - // Apply computed vertex data to new physics body shape - newBody->shape.vertexData = newData; - newBody->shape.transform = trans; - - // Calculate centroid and moment of inertia - center = PHYSAC_VECTOR_ZERO; - float area = 0.0f; - float inertia = 0.0f; - - for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[j]; - int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextVertex]; - - float D = MathCrossVector2(p1, p2); - float triangleArea = D/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); - center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); - - float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; - float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; - inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - newBody->mass = area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - - // Calculate explosion force direction - Vector2 pointA = newBody->position; - Vector2 pointB = Vector2Subtract(newData.positions[1], newData.positions[0]); - pointB.x /= 2.0f; - pointB.y /= 2.0f; - Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.positions[0], pointB)), newBody->position); - MathNormalize(&forceDirection); - forceDirection.x *= force; - forceDirection.y *= force; - - // Apply force to new physics body - PhysicsAddForce(newBody, forceDirection); - } - - PHYSAC_FREE(vertices); - } - } - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error when trying to shatter a null reference physics body"); - #endif -} - -// Returns the current amount of created physics bodies -PHYSACDEF int GetPhysicsBodiesCount(void) -{ - return physicsBodiesCount; -} - -// Returns a physics body of the bodies pool at a specific index -PHYSACDEF PhysicsBody GetPhysicsBody(int index) -{ - if (index < physicsBodiesCount) - { - if (bodies[index] == NULL) - { - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] physics body index is out of bounds"); - #endif - - return bodies[index]; -} - -// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeType(int index) -{ - int result = -1; - - if (index < physicsBodiesCount) - { - if (bodies[index] != NULL) - result = bodies[index]->shape.type; - - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] physics body index is out of bounds"); - #endif - - return result; -} - -// Returns the amount of vertices of a physics body shape -PHYSACDEF int GetPhysicsShapeVerticesCount(int index) -{ - int result = 0; - - if (index < physicsBodiesCount) - { - if (bodies[index] != NULL) - { - switch (bodies[index]->shape.type) - { - case PHYSICS_CIRCLE: result = PHYSAC_CIRCLE_VERTICES; break; - case PHYSICS_POLYGON: result = bodies[index]->shape.vertexData.vertexCount; break; - default: break; - } - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] physics body index is out of bounds"); - #endif - - return result; -} - -// Returns transformed position of a body shape (body position + vertex transformed position) -PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) -{ - Vector2 position = { 0.0f, 0.0f }; - - if (body != NULL) - { - switch (body->shape.type) - { - case PHYSICS_CIRCLE: - { - position.x = body->position.x + cosf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - position.y = body->position.y + sinf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - } break; - case PHYSICS_POLYGON: - { - PolygonData vertexData = body->shape.vertexData; - position = Vector2Add(body->position, Mat2MultiplyVector2(body->shape.transform, vertexData.positions[vertex])); - } break; - default: break; - } - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error when trying to get a null reference physics body"); - #endif - - return position; -} - -// Sets physics body shape transform based on radians parameter -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) -{ - if (body != NULL) - { - body->orient = radians; - - if (body->shape.type == PHYSICS_POLYGON) - body->shape.transform = Mat2Radians(radians); - } -} - -// Unitializes and destroys a physics body -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) -{ - if (body != NULL) - { - int id = body->id; - int index = -1; - - for (int i = 0; i < physicsBodiesCount; i++) - { - if (bodies[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) - { - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] Not possible to find body id %i in pointers array\n", id); - #endif - return; - } - - // Free body allocated memory - PHYSAC_FREE(body); - usedMemory -= sizeof(PhysicsBodyData); - bodies[index] = NULL; - - // Reorder physics bodies pointers array and its catched index - for (int i = index; i < physicsBodiesCount; i++) - { - if ((i + 1) < physicsBodiesCount) - bodies[i] = bodies[i + 1]; - } - - // Update physics bodies count - physicsBodiesCount--; - - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] destroyed physics body id %i\n", id); - #endif - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error trying to destroy a null referenced body\n"); - #endif -} - -// Unitializes physics pointers and exits physics loop thread -PHYSACDEF void ClosePhysics(void) -{ - // Exit physics loop thread - physicsThreadEnabled = false; - - #if !defined(PHYSAC_NO_THREADS) - pthread_join(physicsThreadId, NULL); - #endif - - // Unitialize physics manifolds dynamic memory allocations - for (int i = physicsManifoldsCount - 1; i >= 0; i--) - DestroyPhysicsManifold(contacts[i]); - - // Unitialize physics bodies dynamic memory allocations - for (int i = physicsBodiesCount - 1; i >= 0; i--) - DestroyPhysicsBody(bodies[i]); - - #if defined(PHYSAC_DEBUG) - if (physicsBodiesCount > 0 || usedMemory != 0) - printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); - else if (physicsManifoldsCount > 0 || usedMemory != 0) - printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); - else - printf("[PHYSAC] physics module closed successfully\n"); - #endif -} - -//---------------------------------------------------------------------------------- -// Module Internal Functions Definition -//---------------------------------------------------------------------------------- -// Finds a valid index for a new physics body initialization -static int FindAvailableBodyIndex() -{ - int index = -1; - for (int i = 0; i < PHYSAC_MAX_BODIES; i++) - { - int currentId = i; - - // Check if current id already exist in other physics body - for (int k = 0; k < physicsBodiesCount; k++) - { - if (bodies[k]->id == currentId) - { - currentId++; - break; - } - } - - // If it is not used, use it as new physics body id - if (currentId == i) - { - index = i; - break; - } - } - - return index; -} - -// Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRandomPolygon(float radius, int sides) -{ - PolygonData data = { 0 }; - data.vertexCount = sides; - - // Calculate polygon vertices positions - for (int i = 0; i < data.vertexCount; i++) - { - data.positions[i].x = cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - data.positions[i].y = sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - } - - // Calculate polygon faces normals - for (int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); - } - - return data; -} - -// Creates a rectangle polygon shape based on a min and max positions -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size) -{ - PolygonData data = { 0 }; - data.vertexCount = 4; - - // Calculate polygon vertices positions - data.positions[0] = (Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; - data.positions[1] = (Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; - data.positions[2] = (Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; - data.positions[3] = (Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; - - // Calculate polygon faces normals - for (int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); - } - - return data; -} - -// Physics loop thread function -static void *PhysicsLoop(void *arg) -{ - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] physics thread created successfully\n"); - #endif - - // Initialize physics loop thread values - physicsThreadEnabled = true; - - // Physics update loop - while (physicsThreadEnabled) - { - RunPhysicsStep(); - } - - return NULL; -} - -// Physics steps calculations (dynamics, collisions and position corrections) -static void PhysicsStep(void) -{ - // Update current steps count - stepsCount++; - - // Clear previous generated collisions information - for (int i = physicsManifoldsCount - 1; i >= 0; i--) - { - PhysicsManifold manifold = contacts[i]; - - if (manifold != NULL) - DestroyPhysicsManifold(manifold); - } - - // Reset physics bodies grounded state - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - body->isGrounded = false; - } - - // Generate new collision information - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody bodyA = bodies[i]; - - if (bodyA != NULL) - { - for (int j = i + 1; j < physicsBodiesCount; j++) - { - PhysicsBody bodyB = bodies[j]; - - if (bodyB != NULL) - { - if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) - continue; - - PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); - SolvePhysicsManifold(manifold); - - if (manifold->contactsCount > 0) - { - // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot - PhysicsManifold newManifold = CreatePhysicsManifold(bodyA, bodyB); - newManifold->penetration = manifold->penetration; - newManifold->normal = manifold->normal; - newManifold->contacts[0] = manifold->contacts[0]; - newManifold->contacts[1] = manifold->contacts[1]; - newManifold->contactsCount = manifold->contactsCount; - newManifold->restitution = manifold->restitution; - newManifold->dynamicFriction = manifold->dynamicFriction; - newManifold->staticFriction = manifold->staticFriction; - } - } - } - } - } - - // Integrate forces to physics bodies - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - - if (body != NULL) - IntegratePhysicsForces(body); - } - - // Initialize physics manifolds to solve collisions - for (int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - - if (manifold != NULL) - InitializePhysicsManifolds(manifold); - } - - // Integrate physics collisions impulses to solve collisions - for (int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) - { - for (int j = 0; j < physicsManifoldsCount; j++) - { - PhysicsManifold manifold = contacts[i]; - - if (manifold != NULL) - IntegratePhysicsImpulses(manifold); - } - } - - // Integrate velocity to physics bodies - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - - if (body != NULL) - IntegratePhysicsVelocity(body); - } - - // Correct physics bodies positions based on manifolds collision information - for (int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - - if (manifold != NULL) - CorrectPhysicsPositions(manifold); - } - - // Clear physics bodies forces - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - - if (body != NULL) - { - body->force = PHYSAC_VECTOR_ZERO; - body->torque = 0.0f; - } - } -} - -// Wrapper to ensure PhysicsStep is run with at a fixed time step -PHYSACDEF void RunPhysicsStep(void) -{ - // Calculate current time - currentTime = GetCurrentTime(); - - // Calculate current delta time - const double delta = currentTime - startTime; - - // Store the time elapsed since the last frame began - accumulator += delta; - - // Fixed time stepping loop - while (accumulator >= deltaTime) - { - PhysicsStep(); - accumulator -= deltaTime; - } - - // Record the starting of this frame - startTime = currentTime; -} - -PHYSACDEF void SetPhysicsTimeStep(double delta) -{ - deltaTime = delta; -} - -// Finds a valid index for a new manifold initialization -static int FindAvailableManifoldIndex() -{ - int index = -1; - for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++) - { - int currentId = i; - - // Check if current id already exist in other physics body - for (int k = 0; k < physicsManifoldsCount; k++) - { - if (contacts[k]->id == currentId) - { - currentId++; - break; - } - } - - // If it is not used, use it as new physics body id - if (currentId == i) - { - index = i; - break; - } - } - - return index; -} - -// Creates a new physics manifold to solve collision -static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) -{ - PhysicsManifold newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); - usedMemory += sizeof(PhysicsManifoldData); - - int newId = FindAvailableManifoldIndex(); - if (newId != -1) - { - // Initialize new manifold with generic values - newManifold->id = newId; - newManifold->bodyA = a; - newManifold->bodyB = b; - newManifold->penetration = 0; - newManifold->normal = PHYSAC_VECTOR_ZERO; - newManifold->contacts[0] = PHYSAC_VECTOR_ZERO; - newManifold->contacts[1] = PHYSAC_VECTOR_ZERO; - newManifold->contactsCount = 0; - newManifold->restitution = 0.0f; - newManifold->dynamicFriction = 0.0f; - newManifold->staticFriction = 0.0f; - - // Add new body to bodies pointers array and update bodies count - contacts[physicsManifoldsCount] = newManifold; - physicsManifoldsCount++; - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] new physics manifold creation failed because there is any available id to use\n"); - #endif - - return newManifold; -} - -// Unitializes and destroys a physics manifold -static void DestroyPhysicsManifold(PhysicsManifold manifold) -{ - if (manifold != NULL) - { - int id = manifold->id; - int index = -1; - - for (int i = 0; i < physicsManifoldsCount; i++) - { - if (contacts[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) - { - #if defined(PHYSAC_DEBUG) - printf("[PHYSAC] Not possible to manifold id %i in pointers array\n", id); - #endif - return; - } - - // Free manifold allocated memory - PHYSAC_FREE(manifold); - usedMemory -= sizeof(PhysicsManifoldData); - contacts[index] = NULL; - - // Reorder physics manifolds pointers array and its catched index - for (int i = index; i < physicsManifoldsCount; i++) - { - if ((i + 1) < physicsManifoldsCount) - contacts[i] = contacts[i + 1]; - } - - // Update physics manifolds count - physicsManifoldsCount--; - } - #if defined(PHYSAC_DEBUG) - else - printf("[PHYSAC] error trying to destroy a null referenced manifold\n"); - #endif -} - -// Solves a created physics manifold between two physics bodies -static void SolvePhysicsManifold(PhysicsManifold manifold) -{ - switch (manifold->bodyA->shape.type) - { - case PHYSICS_CIRCLE: - { - switch (manifold->bodyB->shape.type) - { - case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break; - case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break; - default: break; - } - } break; - case PHYSICS_POLYGON: - { - switch (manifold->bodyB->shape.type) - { - case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break; - case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break; - default: break; - } - } break; - default: break; - } - - // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds - if (!manifold->bodyB->isGrounded) - manifold->bodyB->isGrounded = (manifold->normal.y < 0); -} - -// Solves collision between two circle shape physics bodies -static void SolveCircleToCircle(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - // Calculate translational vector, which is normal - Vector2 normal = Vector2Subtract(bodyB->position, bodyA->position); - - float distSqr = MathLenSqr(normal); - float radius = bodyA->shape.radius + bodyB->shape.radius; - - // Check if circles are not in contact - if (distSqr >= radius*radius) - { - manifold->contactsCount = 0; - return; - } - - float distance = sqrtf(distSqr); - manifold->contactsCount = 1; - - if (distance == 0.0f) - { - manifold->penetration = bodyA->shape.radius; - manifold->normal = (Vector2){ 1.0f, 0.0f }; - manifold->contacts[0] = bodyA->position; - } - else - { - manifold->penetration = radius - distance; - manifold->normal = (Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathNormalize() due to sqrt is already performed - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - } - - // Update physics body grounded state if normal direction is down - if (!bodyA->isGrounded) - bodyA->isGrounded = (manifold->normal.y < 0); -} - -// Solves collision between a circle to a polygon shape physics bodies -static void SolveCircleToPolygon(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - SolveDifferentShapes(manifold, bodyA, bodyB); -} - -// Solves collision between a circle to a polygon shape physics bodies -static void SolvePolygonToCircle(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - SolveDifferentShapes(manifold, bodyB, bodyA); - - manifold->normal.x *= -1.0f; - manifold->normal.y *= -1.0f; -} - -// Solve collision between two different types of shapes -static void SolveDifferentShapes(PhysicsManifold manifold, PhysicsBody bodyA, PhysicsBody bodyB) -{ - manifold->contactsCount = 0; - - // Transform circle center to polygon transform space - Vector2 center = bodyA->position; - center = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.transform), Vector2Subtract(center, bodyB->position)); - - // Find edge with minimum penetration - // It is the same concept as using support points in SolvePolygonToPolygon - float separation = -PHYSAC_FLT_MAX; - int faceNormal = 0; - PolygonData vertexData = bodyB->shape.vertexData; - - for (int i = 0; i < vertexData.vertexCount; i++) - { - float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(center, vertexData.positions[i])); - - if (currentSeparation > bodyA->shape.radius) - return; - - if (currentSeparation > separation) - { - separation = currentSeparation; - faceNormal = i; - } - } - - // Grab face's vertices - Vector2 v1 = vertexData.positions[faceNormal]; - int nextIndex = (((faceNormal + 1) < vertexData.vertexCount) ? (faceNormal + 1) : 0); - Vector2 v2 = vertexData.positions[nextIndex]; - - // Check to see if center is within polygon - if (separation < PHYSAC_EPSILON) - { - manifold->contactsCount = 1; - Vector2 normal = Mat2MultiplyVector2(bodyB->shape.transform, vertexData.normals[faceNormal]); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - manifold->penetration = bodyA->shape.radius; - return; - } - - // Determine which voronoi region of the edge center of circle lies within - float dot1 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1)); - float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2)); - manifold->penetration = bodyA->shape.radius - separation; - - if (dot1 <= 0.0f) // Closest to v1 - { - if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) - return; - - manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v1, center); - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); - manifold->normal = normal; - v1 = Mat2MultiplyVector2(bodyB->shape.transform, v1); - v1 = Vector2Add(v1, bodyB->position); - manifold->contacts[0] = v1; - } - else if (dot2 <= 0.0f) // Closest to v2 - { - if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) - return; - - manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v2, center); - v2 = Mat2MultiplyVector2(bodyB->shape.transform, v2); - v2 = Vector2Add(v2, bodyB->position); - manifold->contacts[0] = v2; - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); - manifold->normal = normal; - } - else // Closest to face - { - Vector2 normal = vertexData.normals[faceNormal]; - - if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) - return; - - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - manifold->contactsCount = 1; - } -} - -// Solves collision between two polygons shape physics bodies -static void SolvePolygonToPolygon(PhysicsManifold manifold) -{ - if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) - return; - - PhysicsShape bodyA = manifold->bodyA->shape; - PhysicsShape bodyB = manifold->bodyB->shape; - manifold->contactsCount = 0; - - // Check for separating axis with A shape's face planes - int faceA = 0; - float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB); - - if (penetrationA >= 0.0f) - return; - - // Check for separating axis with B shape's face planes - int faceB = 0; - float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA); - - if (penetrationB >= 0.0f) - return; - - int referenceIndex = 0; - bool flip = false; // Always point from A shape to B shape - - PhysicsShape refPoly; // Reference - PhysicsShape incPoly; // Incident - - // Determine which shape contains reference face - if (BiasGreaterThan(penetrationA, penetrationB)) - { - refPoly = bodyA; - incPoly = bodyB; - referenceIndex = faceA; - } - else - { - refPoly = bodyB; - incPoly = bodyA; - referenceIndex = faceB; - flip = true; - } - - // World space incident face - Vector2 incidentFace[2]; - FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); - - // Setup reference face vertices - PolygonData refData = refPoly.vertexData; - Vector2 v1 = refData.positions[referenceIndex]; - referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0); - Vector2 v2 = refData.positions[referenceIndex]; - - // Transform vertices to world space - v1 = Mat2MultiplyVector2(refPoly.transform, v1); - v1 = Vector2Add(v1, refPoly.body->position); - v2 = Mat2MultiplyVector2(refPoly.transform, v2); - v2 = Vector2Add(v2, refPoly.body->position); - - // Calculate reference face side normal in world space - Vector2 sidePlaneNormal = Vector2Subtract(v2, v1); - MathNormalize(&sidePlaneNormal); - - // Orthogonalize - Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; - float refC = MathDot(refFaceNormal, v1); - float negSide = MathDot(sidePlaneNormal, v1)*-1; - float posSide = MathDot(sidePlaneNormal, v2); - - // Clip incident face to reference face side planes (due to floating point error, possible to not have required points - if (Clip((Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, negSide, &incidentFace[0], &incidentFace[1]) < 2) - return; - - if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) - return; - - // Flip normal if required - manifold->normal = (flip ? (Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); - - // Keep points behind reference face - int currentPoint = 0; // Clipped points behind reference face - float separation = MathDot(refFaceNormal, incidentFace[0]) - refC; - - if (separation <= 0.0f) - { - manifold->contacts[currentPoint] = incidentFace[0]; - manifold->penetration = -separation; - currentPoint++; - } - else - manifold->penetration = 0.0f; - - separation = MathDot(refFaceNormal, incidentFace[1]) - refC; - - if (separation <= 0.0f) - { - manifold->contacts[currentPoint] = incidentFace[1]; - manifold->penetration += -separation; - currentPoint++; - - // Calculate total penetration average - manifold->penetration /= currentPoint; - } - - manifold->contactsCount = currentPoint; -} - -// Integrates physics forces into velocity -static void IntegratePhysicsForces(PhysicsBody body) -{ - if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) - return; - - body->velocity.x += (body->force.x*body->inverseMass)*(deltaTime/2.0); - body->velocity.y += (body->force.y*body->inverseMass)*(deltaTime/2.0); - - if (body->useGravity) - { - body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); - body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); - } - - if (!body->freezeOrient) - body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0); -} - -// Initializes physics manifolds to solve collisions -static void InitializePhysicsManifolds(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - // Calculate average restitution, static and dynamic friction - manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution); - manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction); - manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction); - - for (int i = 0; i < manifold->contactsCount; i++) - { - // Caculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); - - Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA); - Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB); - - Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; - radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y; - - // Determine if we should perform a resting collision or not; - // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) - manifold->restitution = 0; - } -} - -// Integrates physics collisions impulses to solve collisions -static void IntegratePhysicsImpulses(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - // Early out and positional correct if both objects have infinite mass - if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON) - { - bodyA->velocity = PHYSAC_VECTOR_ZERO; - bodyB->velocity = PHYSAC_VECTOR_ZERO; - return; - } - - for (int i = 0; i < manifold->contactsCount; i++) - { - // Calculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); - - // Calculate relative velocity - Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; - - // Relative velocity along the normal - float contactVelocity = MathDot(radiusV, manifold->normal); - - // Do not resolve if velocities are separating - if (contactVelocity > 0.0f) - return; - - float raCrossN = MathCrossVector2(radiusA, manifold->normal); - float rbCrossN = MathCrossVector2(radiusB, manifold->normal); - - float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; - - // Calculate impulse scalar value - float impulse = -(1.0f + manifold->restitution)*contactVelocity; - impulse /= inverseMassSum; - impulse /= (float)manifold->contactsCount; - - // Apply impulse to each physics body - Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse }; - - if (bodyA->enabled) - { - bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); - bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); - - if (!bodyA->freezeOrient) - bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -impulseV.x, -impulseV.y }); - } - - if (bodyB->enabled) - { - bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); - bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); - - if (!bodyB->freezeOrient) - bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, impulseV); - } - - // Apply friction impulse to each physics body - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; - - Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) }; - MathNormalize(&tangent); - - // Calculate impulse tangent magnitude - float impulseTangent = -MathDot(radiusV, tangent); - impulseTangent /= inverseMassSum; - impulseTangent /= (float)manifold->contactsCount; - - float absImpulseTangent = fabs(impulseTangent); - - // Don't apply tiny friction impulses - if (absImpulseTangent <= PHYSAC_EPSILON) - return; - - // Apply coulumb's law - Vector2 tangentImpulse = { 0.0f, 0.0f }; - if (absImpulseTangent < impulse*manifold->staticFriction) - tangentImpulse = (Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; - else - tangentImpulse = (Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; - - // Apply friction impulse - if (bodyA->enabled) - { - bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); - bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); - - if (!bodyA->freezeOrient) - bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -tangentImpulse.x, -tangentImpulse.y }); - } - - if (bodyB->enabled) - { - bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); - bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); - - if (!bodyB->freezeOrient) - bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, tangentImpulse); - } - } -} - -// Integrates physics velocity into position and forces -static void IntegratePhysicsVelocity(PhysicsBody body) -{ - if ((body == NULL) ||!body->enabled) - return; - - body->position.x += body->velocity.x*deltaTime; - body->position.y += body->velocity.y*deltaTime; - - if (!body->freezeOrient) - body->orient += body->angularVelocity*deltaTime; - - Mat2Set(&body->shape.transform, body->orient); - - IntegratePhysicsForces(body); -} - -// Corrects physics bodies positions based on manifolds collision information -static void CorrectPhysicsPositions(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) - return; - - Vector2 correction = { 0.0f, 0.0f }; - correction.x = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; - correction.y = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; - - if (bodyA->enabled) - { - bodyA->position.x -= correction.x*bodyA->inverseMass; - bodyA->position.y -= correction.y*bodyA->inverseMass; - } - - if (bodyB->enabled) - { - bodyB->position.x += correction.x*bodyB->inverseMass; - bodyB->position.y += correction.y*bodyB->inverseMass; - } -} - -// Returns the extreme point along a direction within a polygon -static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) -{ - float bestProjection = -PHYSAC_FLT_MAX; - Vector2 bestVertex = { 0.0f, 0.0f }; - PolygonData data = shape.vertexData; - - for (int i = 0; i < data.vertexCount; i++) - { - Vector2 vertex = data.positions[i]; - float projection = MathDot(vertex, dir); - - if (projection > bestProjection) - { - bestVertex = vertex; - bestProjection = projection; - } - } - - return bestVertex; -} - -// Finds polygon shapes axis least penetration -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB) -{ - float bestDistance = -PHYSAC_FLT_MAX; - int bestIndex = 0; - - PolygonData dataA = shapeA.vertexData; - - for (int i = 0; i < dataA.vertexCount; i++) - { - // Retrieve a face normal from A shape - Vector2 normal = dataA.normals[i]; - Vector2 transNormal = Mat2MultiplyVector2(shapeA.transform, normal); - - // Transform face normal into B shape's model space - Mat2 buT = Mat2Transpose(shapeB.transform); - normal = Mat2MultiplyVector2(buT, transNormal); - - // Retrieve support point from B shape along -n - Vector2 support = GetSupport(shapeB, (Vector2){ -normal.x, -normal.y }); - - // Retrieve vertex on face from A shape, transform into B shape's model space - Vector2 vertex = dataA.positions[i]; - vertex = Mat2MultiplyVector2(shapeA.transform, vertex); - vertex = Vector2Add(vertex, shapeA.body->position); - vertex = Vector2Subtract(vertex, shapeB.body->position); - vertex = Mat2MultiplyVector2(buT, vertex); - - // Compute penetration distance in B shape's model space - float distance = MathDot(normal, Vector2Subtract(support, vertex)); - - // Store greatest distance - if (distance > bestDistance) - { - bestDistance = distance; - bestIndex = i; - } - } - - *faceIndex = bestIndex; - return bestDistance; -} - -// Finds two polygon shapes incident face -static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) -{ - PolygonData refData = ref.vertexData; - PolygonData incData = inc.vertexData; - - Vector2 referenceNormal = refData.normals[index]; - - // Calculate normal in incident's frame of reference - referenceNormal = Mat2MultiplyVector2(ref.transform, referenceNormal); // To world space - referenceNormal = Mat2MultiplyVector2(Mat2Transpose(inc.transform), referenceNormal); // To incident's model space - - // Find most anti-normal face on polygon - int incidentFace = 0; - float minDot = PHYSAC_FLT_MAX; - - for (int i = 0; i < incData.vertexCount; i++) - { - float dot = MathDot(referenceNormal, incData.normals[i]); - - if (dot < minDot) - { - minDot = dot; - incidentFace = i; - } - } - - // Assign face vertices for incident face - *v0 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v0 = Vector2Add(*v0, inc.body->position); - incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0); - *v1 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v1 = Vector2Add(*v1, inc.body->position); -} - -// Calculates clipping based on a normal and two faces -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) -{ - int sp = 0; - Vector2 out[2] = { *faceA, *faceB }; - - // Retrieve distances from each endpoint to the line - float distanceA = MathDot(normal, *faceA) - clip; - float distanceB = MathDot(normal, *faceB) - clip; - - // If negative (behind plane) - if (distanceA <= 0.0f) - out[sp++] = *faceA; - - if (distanceB <= 0.0f) - out[sp++] = *faceB; - - // If the points are on different sides of the plane - if ((distanceA*distanceB) < 0.0f) - { - // Push intersection point - float alpha = distanceA/(distanceA - distanceB); - out[sp] = *faceA; - Vector2 delta = Vector2Subtract(*faceB, *faceA); - delta.x *= alpha; - delta.y *= alpha; - out[sp] = Vector2Add(out[sp], delta); - sp++; - } - - // Assign the new converted values - *faceA = out[0]; - *faceB = out[1]; - - return sp; -} - -// Check if values are between bias range -static bool BiasGreaterThan(float valueA, float valueB) -{ - return (valueA >= (valueB*0.95f + valueA*0.01f)); -} - -// Returns the barycenter of a triangle given by 3 points -static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) -{ - Vector2 result = { 0.0f, 0.0f }; - - result.x = (v1.x + v2.x + v3.x)/3; - result.y = (v1.y + v2.y + v3.y)/3; - - return result; -} - -// Initializes hi-resolution MONOTONIC timer -static void InitTimer(void) -{ - srand(time(NULL)); // Initialize random seed - - #if defined(_WIN32) - QueryPerformanceFrequency((unsigned long long int *) &frequency); - #endif - - #if defined(__linux__) - struct timespec now; - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) - frequency = 1000000000; - #endif - - #if defined(__APPLE__) - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - frequency = (timebase.denom*1e9)/timebase.numer; - #endif - - baseTime = GetTimeCount(); // Get MONOTONIC clock time offset - startTime = GetCurrentTime(); // Get current time -} - -// Get hi-res MONOTONIC time measure in seconds -static uint64_t GetTimeCount(void) -{ - uint64_t value = 0; - - #if defined(_WIN32) - QueryPerformanceCounter((unsigned long long int *) &value); - #endif - - #if defined(__linux__) - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - value = (uint64_t)now.tv_sec*(uint64_t)1000000000 + (uint64_t)now.tv_nsec; - #endif - - #if defined(__APPLE__) - value = mach_absolute_time(); - #endif - - return value; -} - -// Get current time in milliseconds -static double GetCurrentTime(void) -{ - return (double)(GetTimeCount() - baseTime)/frequency*1000; -} - -// Returns the cross product of a vector and a value -static inline Vector2 MathCross(float value, Vector2 vector) -{ - return (Vector2){ -value*vector.y, value*vector.x }; -} - -// Returns the cross product of two vectors -static inline float MathCrossVector2(Vector2 v1, Vector2 v2) -{ - return (v1.x*v2.y - v1.y*v2.x); -} - -// Returns the len square root of a vector -static inline float MathLenSqr(Vector2 vector) -{ - return (vector.x*vector.x + vector.y*vector.y); -} - -// Returns the dot product of two vectors -static inline float MathDot(Vector2 v1, Vector2 v2) -{ - return (v1.x*v2.x + v1.y*v2.y); -} - -// Returns the square root of distance between two vectors -static inline float DistSqr(Vector2 v1, Vector2 v2) -{ - Vector2 dir = Vector2Subtract(v1, v2); - return MathDot(dir, dir); -} - -// Returns the normalized values of a vector -static void MathNormalize(Vector2 *vector) -{ - float length, ilength; - - Vector2 aux = *vector; - length = sqrtf(aux.x*aux.x + aux.y*aux.y); - - if (length == 0) - length = 1.0f; - - ilength = 1.0f/length; - - vector->x *= ilength; - vector->y *= ilength; -} - -#if defined(PHYSAC_STANDALONE) -// Returns the sum of two given vectors -static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - return (Vector2){ v1.x + v2.x, v1.y + v2.y }; -} - -// Returns the subtract of two given vectors -static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - return (Vector2){ v1.x - v2.x, v1.y - v2.y }; -} -#endif - -// Creates a matrix 2x2 from a given radians value -static Mat2 Mat2Radians(float radians) -{ - float c = cosf(radians); - float s = sinf(radians); - - return (Mat2){ c, -s, s, c }; -} - -// Set values from radians to a created matrix 2x2 -static void Mat2Set(Mat2 *matrix, float radians) -{ - float cos = cosf(radians); - float sin = sinf(radians); - - matrix->m00 = cos; - matrix->m01 = -sin; - matrix->m10 = sin; - matrix->m11 = cos; -} - -// Returns the transpose of a given matrix 2x2 -static inline Mat2 Mat2Transpose(Mat2 matrix) -{ - return (Mat2){ matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; -} - -// Multiplies a vector by a matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector) -{ - return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; -} - -#endif // PHYSAC_IMPLEMENTATION diff --git a/examples/physics/physics_demo.cpp b/examples/physics/physics_demo.cpp deleted file mode 100644 index e2c98b84..00000000 --- a/examples/physics/physics_demo.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************************* -* -* Physac - Physics demo -* -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) -* -* Use the following line to compile: -* -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static / -* -lraylib -lpthread -lglfw3 -lopengl32 -lgdi32 -lopenal32 -lwinmm / -* -std=c99 -Wl,--subsystem,windows -Wl,-allow-multiple-definition -* -* Copyright (c) 2016-2018 Victor Fisac -* -********************************************************************************************/ - -#include "raylib-cpp.hpp" - -#define PHYSAC_IMPLEMENTATION -#define PHYSAC_NO_THREADS -#include "physac.h" -#include "Physics.hpp" - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - SetConfigFlags(FLAG_MSAA_4X_HINT); - raylib::Window window(screenWidth, screenHeight, "Physac [raylib-cpp] - Physics demo"); - - // Physac logo drawing position - int logoX = screenWidth - MeasureText("Physac", 30) - 10; - int logoY = 15; - bool needsReset = false; - - // Initialize physics and default physics bodies - raylib::Physics physics; - - // Create floor rectangle physics body - PhysicsBody floor = physics.CreateBodyRectangle(Vector2{screenWidth/2, screenHeight}, 500, 100, 10); - floor->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions) - - // Create obstacle circle physics body - PhysicsBody circle = physics.CreateBodyCircle(Vector2{screenWidth/2, screenHeight/2}, 45, 10); - circle->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions) - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!window.ShouldClose()) { // Detect window close button or ESC key - // Update - //---------------------------------------------------------------------------------- - // Delay initialization of variables due to physics reset async - physics.RunStep(); - - if (needsReset) - { - floor = physics.CreateBodyRectangle(Vector2{ screenWidth/2, screenHeight }, 500, 100, 10); - floor->enabled = false; - - circle = physics.CreateBodyCircle(Vector2{ screenWidth/2, screenHeight/2 }, 45, 10); - circle->enabled = false; - - needsReset = false; - } - - // Physics body creation inputs - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) physics.CreateBodyPolygon(GetMousePosition(), GetRandomValue(20, 80), GetRandomValue(3, 8), 10); - else if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) physics.CreateBodyCircle(GetMousePosition(), GetRandomValue(10, 45), 10); - - // Destroy falling physics bodies - int bodiesCount = physics.GetBodiesCount(); - for (int i = bodiesCount - 1; i >= 0; i--) - { - PhysicsBody body = physics.GetBody(i); - if (body != NULL && (body->position.y > screenHeight*2)) physics.DestroyBody(body); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - { - window.ClearBackground(BLACK); - - DrawFPS(screenWidth - 90, screenHeight - 30); - - // Draw created physics bodies - bodiesCount = physics.GetBodiesCount(); - for (int i = 0; i < bodiesCount; i++) - { - PhysicsBody body = physics.GetBody(i); - - if (body != NULL) - { - int vertexCount = physics.GetShapeVerticesCount(i); - for (int j = 0; j < vertexCount; j++) - { - // Get physics bodies shape vertices to draw lines - // Note: GetPhysicsShapeVertex() already calculates rotation transformations - Vector2 vertexA = physics.GetShapeVertex(body, j); - - int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape - Vector2 vertexB = physics.GetShapeVertex(body, jj); - - DrawLineV(vertexA, vertexB, GREEN); // Draw a line between two vertex positions - } - } - } - - raylib::DrawText("Left mouse button to create a polygon", 10, 10, 10, WHITE); - raylib::DrawText("Right mouse button to create a circle", 10, 25, 10, WHITE); - - raylib::DrawText("Physac", logoX, logoY, 30, WHITE); - raylib::DrawText("Powered by", logoX + 50, logoY - 7, 10, WHITE); - } - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - // ClosePhysics(); // Unitializing physics handled through the garbage collector. - - // CloseWindow(); // Close window and OpenGL context is handled. - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index d520d5fd..8d8b1126 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -23,7 +23,6 @@ install(FILES Mouse.hpp Music.hpp physac.hpp - Physics.hpp Ray.hpp RayCollision.hpp RaylibException.hpp diff --git a/include/Functions.hpp b/include/Functions.hpp index 8fa7b3f7..adbc18bc 100644 --- a/include/Functions.hpp +++ b/include/Functions.hpp @@ -70,7 +70,6 @@ RLCPPAPI inline std::string GetGamepadName(int gamepad) { /** * Load text data from file (read) */ -[[maybe_unused]] RLCPPAPI std::string LoadFileText(const std::string& fileName) { char* text = ::LoadFileText(fileName.c_str()); std::string output(text); @@ -151,7 +150,6 @@ RLCPPAPI inline std::string GetWorkingDirectory() { /** * Get filenames in a directory path */ -[[maybe_unused]] RLCPPAPI std::vector LoadDirectoryFiles(const std::string& dirPath) { FilePathList files = ::LoadDirectoryFiles(dirPath.c_str()); std::vector output(files.paths, files.paths + files.count); @@ -169,7 +167,6 @@ RLCPPAPI inline bool ChangeDirectory(const std::string& dir) { /** * Get dropped files names */ -[[maybe_unused]] RLCPPAPI std::vector LoadDroppedFiles() { if (!::IsFileDropped()) { return std::vector(); diff --git a/include/Physics.hpp b/include/Physics.hpp deleted file mode 100644 index 2a660a1e..00000000 --- a/include/Physics.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef RAYLIB_CPP_INCLUDE_PHYSICS_HPP_ -#define RAYLIB_CPP_INCLUDE_PHYSICS_HPP_ - -#include "./raylib.hpp" -#include "./Vector2.hpp" - -namespace raylib { -/** - * 2D Physics library for videogames - * - * This requires you to deploy your own version of physac.h over at: - * https://github.com/victorfisac/Physac - * - * @see https://github.com/victorfisac/Physac - */ -class Physics { - public: - Physics() { - Init(); - } - - Physics(float gravityY) { - Init(); - SetGravity(0, gravityY); - } - - Physics(float gravityX, float gravityY) { - Init(); - SetGravity(gravityX, gravityY); - } - - ~Physics() { - Close(); - } - - inline Physics& Init() { - ::InitPhysics(); - return *this; - } - - inline Physics& RunStep() { - ::RunPhysicsStep(); - return *this; - } - - inline Physics& SetTimeStep(double delta) { - ::SetPhysicsTimeStep(delta); - return *this; - } - - inline bool IsEnabled() { - return ::IsPhysicsEnabled(); - } - - inline Physics& SetGravity(float x, float y) { - ::SetPhysicsGravity(x, y); - return *this; - } - - inline PhysicsBody CreateBodyCircle(Vector2 pos, float radius, float density) { - return ::CreatePhysicsBodyCircle(pos, radius, density); - } - - inline PhysicsBody CreateBodyRectangle(Vector2 pos, float width, float height, float density) { - return ::CreatePhysicsBodyRectangle(pos, width, height, density); - } - - inline PhysicsBody CreateBodyPolygon(Vector2 pos, float radius, int sides, float density) { - return ::CreatePhysicsBodyPolygon(pos, radius, sides, density); - } - - inline Physics& AddForce(PhysicsBody body, Vector2 force) { - ::PhysicsAddForce(body, force); - return *this; - } - - inline Physics& AddTorque(PhysicsBody body, float amount) { - ::PhysicsAddTorque(body, amount); - return *this; - } - - inline Physics& Shatter(PhysicsBody body, Vector2 position, float force) { - ::PhysicsShatter(body, position, force); - return *this; - } - - inline int GetBodiesCount() const { - return ::GetPhysicsBodiesCount(); - } - - inline PhysicsBody GetBody(int index) const { - return ::GetPhysicsBody(index); - } - - inline int GetShapeType(int index) const { - return ::GetPhysicsShapeType(index); - } - - inline int GetShapeVerticesCount(int index) const { - return ::GetPhysicsShapeVerticesCount(index); - } - - inline Vector2 GetShapeVertex(PhysicsBody body, int vertex) const { - return ::GetPhysicsShapeVertex(body, vertex); - } - - inline Physics& GetBodyRotation(PhysicsBody body, float radians) { - ::SetPhysicsBodyRotation(body, radians); - return *this; - } - - inline Physics& SetBodyRotation(PhysicsBody body, float radians) { - ::SetPhysicsBodyRotation(body, radians); - return *this; - } - - inline Physics& DestroyBody(PhysicsBody body) { - ::DestroyPhysicsBody(body); - return *this; - } - - inline Physics& Close() { - ::ClosePhysics(); - return *this; - } -}; -} // namespace raylib - -using RPhysics = raylib::Physics; - -#endif // RAYLIB_CPP_INCLUDE_PHYSICS_HPP_ From 9814b6605d5af07b578cbc811b36eaf17d6c5b2f Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 20 Aug 2022 16:26:44 -0400 Subject: [PATCH 5/5] v4.2.5 --- CMakeLists.txt | 2 +- clib.json | 2 +- docs/_audio_device_8hpp_source.html | 73 +- docs/_audio_stream_8hpp_source.html | 286 ++--- docs/_bounding_box_8hpp_source.html | 104 +- docs/_camera2_d_8hpp_source.html | 7 +- docs/_camera3_d_8hpp_source.html | 16 +- docs/_color_8hpp_source.html | 379 +++---- docs/_font_8hpp_source.html | 383 +++---- docs/_functions_8hpp_source.html | 481 +++++---- docs/_gamepad_8hpp_source.html | 23 +- docs/_image_8hpp_source.html | 981 +++++++++--------- docs/_material_8hpp_source.html | 161 +-- docs/_matrix_8hpp_source.html | 166 +-- docs/_mesh_8hpp_source.html | 446 ++++---- docs/_model_8hpp_source.html | 377 ++++--- docs/_model_animation_8hpp_source.html | 19 +- docs/_mouse_8hpp_source.html | 111 +- docs/_music_8hpp_source.html | 344 +++--- docs/_ray_8hpp_source.html | 135 +-- docs/_ray_collision_8hpp_source.html | 68 +- docs/_raylib_exception_8hpp_source.html | 7 +- docs/_rectangle_8hpp_source.html | 7 +- docs/_render_texture_8hpp_source.html | 9 +- docs/_shader_8hpp_source.html | 145 +-- docs/_sound_8hpp_source.html | 269 ++--- docs/_text_8hpp_source.html | 13 +- docs/_texture_8hpp_source.html | 534 +++++----- docs/_touch_8hpp_source.html | 122 +++ docs/_vector2_8hpp_source.html | 50 +- docs/_vector3_8hpp_source.html | 7 +- docs/_vector4_8hpp_source.html | 9 +- docs/_vr_stereo_config_8hpp_source.html | 41 +- docs/_wave_8hpp_source.html | 109 +- docs/_window_8hpp_source.html | 463 +++++---- docs/annotated.html | 20 +- docs/classes.html | 15 +- .../classraylib_1_1_audio_device-members.html | 2 +- docs/classraylib_1_1_audio_device.html | 48 +- .../classraylib_1_1_audio_stream-members.html | 48 +- docs/classraylib_1_1_audio_stream.html | 161 ++- .../classraylib_1_1_bounding_box-members.html | 29 +- docs/classraylib_1_1_bounding_box.html | 14 +- docs/classraylib_1_1_camera3_d.html | 2 +- docs/classraylib_1_1_color-members.html | 107 +- docs/classraylib_1_1_color.html | 19 +- docs/classraylib_1_1_font-members.html | 41 +- docs/classraylib_1_1_font.html | 116 ++- docs/classraylib_1_1_gamepad-members.html | 3 +- docs/classraylib_1_1_gamepad.html | 3 + docs/classraylib_1_1_image-members.html | 18 +- docs/classraylib_1_1_image.html | 289 ++++-- docs/classraylib_1_1_material.html | 8 +- docs/classraylib_1_1_matrix-members.html | 79 +- docs/classraylib_1_1_matrix.html | 17 +- docs/classraylib_1_1_mesh-members.html | 75 +- docs/classraylib_1_1_mesh.html | 114 +- docs/classraylib_1_1_model-members.html | 69 +- docs/classraylib_1_1_model.html | 86 +- docs/classraylib_1_1_mouse-members.html | 14 +- docs/classraylib_1_1_mouse.html | 50 +- docs/classraylib_1_1_music-members.html | 21 +- docs/classraylib_1_1_music.html | 151 ++- docs/classraylib_1_1_ray-members.html | 12 +- docs/classraylib_1_1_ray.html | 56 +- ...classraylib_1_1_ray_collision-members.html | 15 +- docs/classraylib_1_1_ray_collision.html | 20 +- docs/classraylib_1_1_shader-members.html | 4 +- docs/classraylib_1_1_shader.html | 19 +- docs/classraylib_1_1_sound-members.html | 26 +- docs/classraylib_1_1_sound.html | 96 +- docs/classraylib_1_1_text.html | 8 +- docs/classraylib_1_1_texture-members.html | 89 +- docs/classraylib_1_1_texture.html | 54 +- docs/classraylib_1_1_touch-members.html | 89 ++ docs/classraylib_1_1_touch.html | 115 ++ docs/classraylib_1_1_vector2-members.html | 99 +- docs/classraylib_1_1_vector2.html | 56 +- ...ssraylib_1_1_vr_stereo_config-members.html | 4 +- docs/classraylib_1_1_vr_stereo_config.html | 16 +- docs/classraylib_1_1_wave-members.html | 6 +- docs/classraylib_1_1_wave.html | 115 +- docs/classraylib_1_1_window-members.html | 73 +- docs/classraylib_1_1_window.html | 140 ++- docs/functions.html | 5 +- docs/functions_c.html | 12 +- docs/functions_d.html | 8 +- docs/functions_e.html | 10 +- docs/functions_f.html | 2 +- docs/functions_func.html | 5 +- docs/functions_func_c.html | 10 +- docs/functions_func_d.html | 8 +- docs/functions_func_e.html | 10 +- docs/functions_func_f.html | 2 +- docs/functions_func_g.html | 52 +- docs/functions_func_i.html | 6 +- docs/functions_func_l.html | 15 +- docs/functions_func_m.html | 2 +- docs/functions_func_s.html | 23 +- docs/functions_func_u.html | 3 +- docs/functions_func_w.html | 2 +- docs/functions_func_~.html | 3 + docs/functions_g.html | 52 +- docs/functions_i.html | 6 +- docs/functions_l.html | 15 +- docs/functions_m.html | 2 +- docs/functions_s.html | 21 +- docs/functions_u.html | 3 +- docs/functions_w.html | 2 +- docs/functions_~.html | 3 + docs/hierarchy.html | 18 +- docs/index.html | 2 +- docs/namespacemembers.html | 12 +- docs/namespacemembers_func.html | 12 +- docs/namespaceraylib.html | 22 +- docs/namespaces.html | 20 +- docs/raylib-cpp_8hpp_source.html | 25 +- docs/search/all_0.js | 5 +- docs/search/all_1.js | 6 +- docs/search/all_10.js | 262 ++--- docs/search/all_11.js | 51 +- docs/search/all_12.js | 20 +- docs/search/all_13.js | 8 +- docs/search/all_14.js | 6 +- docs/search/all_15.js | 2 +- docs/search/all_16.js | 11 +- docs/search/all_2.js | 53 +- docs/search/all_3.js | 35 +- docs/search/all_4.js | 12 +- docs/search/all_5.js | 21 +- docs/search/all_6.js | 184 ++-- docs/search/all_7.js | 4 +- docs/search/all_8.js | 46 +- docs/search/all_9.js | 2 +- docs/search/all_a.js | 40 +- docs/search/all_b.js | 30 +- docs/search/all_c.js | 4 +- docs/search/all_d.js | 42 +- docs/search/all_e.js | 11 +- docs/search/all_f.js | 32 +- docs/search/classes_0.js | 4 +- docs/search/classes_1.js | 2 +- docs/search/classes_2.js | 6 +- docs/search/classes_3.js | 2 +- docs/search/classes_4.js | 2 +- docs/search/classes_5.js | 2 +- docs/search/classes_6.js | 14 +- docs/search/classes_7.js | 6 +- docs/search/classes_8.js | 7 +- docs/search/classes_9.js | 5 +- docs/search/classes_a.js | 6 +- docs/search/classes_b.js | 6 +- docs/search/functions_0.js | 21 +- docs/search/functions_1.js | 6 +- docs/search/functions_10.js | 258 ++--- docs/search/functions_11.js | 46 +- docs/search/functions_12.js | 20 +- docs/search/functions_13.js | 6 +- docs/search/functions_14.js | 2 +- docs/search/functions_15.js | 11 +- docs/search/functions_2.js | 47 +- docs/search/functions_3.js | 35 +- docs/search/functions_4.js | 12 +- docs/search/functions_5.js | 16 +- docs/search/functions_6.js | 284 ++--- docs/search/functions_7.js | 4 +- docs/search/functions_8.js | 46 +- docs/search/functions_9.js | 2 +- docs/search/functions_a.js | 40 +- docs/search/functions_b.js | 22 +- docs/search/functions_c.js | 4 +- docs/search/functions_d.js | 42 +- docs/search/functions_e.js | 10 +- docs/search/functions_f.js | 24 +- docs/search/namespaces_0.js | 2 +- docs/search/pages_0.js | 2 +- docs/search/searchdata.js | 2 +- docs/search/variables_0.js | 2 +- docs/search/variables_1.js | 4 +- docs/search/variables_2.js | 2 +- docs/search/variables_3.js | 2 +- examples/core/core_drop_files.cpp | 4 +- examples/models/models_billboard.cpp | 2 +- include/Font.hpp | 19 +- include/Mesh.hpp | 32 - include/Touch.hpp | 2 +- projects/CMake/CMakeLists.txt | 2 +- 187 files changed, 6415 insertions(+), 5042 deletions(-) create mode 100644 docs/_touch_8hpp_source.html create mode 100644 docs/classraylib_1_1_touch-members.html create mode 100644 docs/classraylib_1_1_touch.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 2850a647..82683907 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.11) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") project (raylib_cpp - VERSION 4.2.4 + VERSION 4.2.5 DESCRIPTION "raylib-cpp C++ Object Oriented Wrapper for raylib" HOMEPAGE_URL "https://github.com/robloach/raylib-cpp" LANGUAGES C CXX) diff --git a/clib.json b/clib.json index e59f0b71..43686af0 100644 --- a/clib.json +++ b/clib.json @@ -1,6 +1,6 @@ { "name": "raylib-cpp", - "version": "4.2.2", + "version": "4.2.5", "repo": "RobLoach/raylib-cpp", "description": "raylib-cpp: C++ Object-Oriented Wrapper for raylib", "homepage": "https://github.com/robloach/raylib-cpp", diff --git a/docs/_audio_device_8hpp_source.html b/docs/_audio_device_8hpp_source.html index 4f5c0848..a753b363 100644 --- a/docs/_audio_device_8hpp_source.html +++ b/docs/_audio_device_8hpp_source.html @@ -84,45 +84,46 @@
13  public:
21  AudioDevice(bool lateInit = false) {
22  if (!lateInit) {
-
23  if (!Init()) {
-
24  throw RaylibException("Failed to initialize AudioDevice");
-
25  }
-
26  }
-
27  }
-
28 
- -
33  Close();
-
34  }
-
35 
-
39  inline bool Init() {
+
23  Init();
+
24  }
+
25  }
+
26 
+ +
31  Close();
+
32  }
+
33 
+
39  inline void Init() {
40  ::InitAudioDevice();
-
41  return IsReady();
-
42  }
-
43 
-
47  inline void Close() {
-
48  ::CloseAudioDevice();
-
49  }
-
50 
-
54  inline bool IsReady() const {
-
55  return ::IsAudioDeviceReady();
-
56  }
-
57 
-
63  inline AudioDevice& SetVolume(float volume) {
-
64  ::SetMasterVolume(volume);
-
65  return *this;
-
66  }
-
67 };
-
68 } // namespace raylib
- -
70 
-
71 #endif // RAYLIB_CPP_INCLUDE_AUDIODEVICE_HPP_
+
41  if (!IsReady()) {
+
42  throw RaylibException("Failed to initialize AudioDevice");
+
43  }
+
44  }
+
45 
+
49  inline void Close() {
+
50  ::CloseAudioDevice();
+
51  }
+
52 
+
56  inline bool IsReady() const {
+
57  return ::IsAudioDeviceReady();
+
58  }
+
59 
+
65  inline AudioDevice& SetVolume(float volume) {
+
66  ::SetMasterVolume(volume);
+
67  return *this;
+
68  }
+
69 };
+
70 } // namespace raylib
+
71 
+ +
73 
+
74 #endif // RAYLIB_CPP_INCLUDE_AUDIODEVICE_HPP_
Audio device management functions.
Definition: AudioDevice.hpp:12
-
void Close()
Close the audio device and context.
Definition: AudioDevice.hpp:47
-
bool IsReady() const
Check if audio device has been initialized successfully.
Definition: AudioDevice.hpp:54
-
bool Init()
Initialize audio device and context.
Definition: AudioDevice.hpp:39
-
~AudioDevice()
Close the audio device and context.
Definition: AudioDevice.hpp:32
+
void Close()
Close the audio device and context.
Definition: AudioDevice.hpp:49
+
bool IsReady() const
Check if audio device has been initialized successfully.
Definition: AudioDevice.hpp:56
+
void Init()
Initialize audio device and context.
Definition: AudioDevice.hpp:39
+
~AudioDevice()
Close the audio device and context.
Definition: AudioDevice.hpp:30
AudioDevice(bool lateInit=false)
Initialize audio device and context.
Definition: AudioDevice.hpp:21
-
AudioDevice & SetVolume(float volume)
Set master volume (listener).
Definition: AudioDevice.hpp:63
+
AudioDevice & SetVolume(float volume)
Set master volume (listener).
Definition: AudioDevice.hpp:65
Exception used for most raylib-related exceptions.
All raylib-cpp classes and functions appear in the raylib namespace.
Definition: AudioDevice.hpp:8
diff --git a/docs/_audio_stream_8hpp_source.html b/docs/_audio_stream_8hpp_source.html index 2db013e8..0080354d 100644 --- a/docs/_audio_stream_8hpp_source.html +++ b/docs/_audio_stream_8hpp_source.html @@ -87,24 +87,24 @@
16  }
17 
18  AudioStream(rAudioBuffer* buffer = nullptr,
-
19  unsigned int sampleRate = 0,
-
20  unsigned int sampleSize = 0,
-
21  unsigned int channels = 0) : ::AudioStream{buffer, sampleRate, sampleSize, channels} {
-
22  // Nothing.
-
23  }
-
24 
-
30  AudioStream(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) {
-
31  if (!Load(SampleRate, SampleSize, Channels)) {
-
32  throw RaylibException("Failed to create AudioStream");
-
33  }
-
34  }
-
35 
-
36  AudioStream(const AudioStream&) = delete;
-
37 
-
38  AudioStream(AudioStream&& other) {
-
39  set(other);
-
40 
-
41  other.buffer = nullptr;
+
19  rAudioProcessor *processor = nullptr,
+
20  unsigned int sampleRate = 0,
+
21  unsigned int sampleSize = 0,
+
22  unsigned int channels = 0) : ::AudioStream{buffer, processor, sampleRate, sampleSize, channels} {
+
23  // Nothing.
+
24  }
+
25 
+
31  AudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels = 2) {
+
32  Load(sampleRate, sampleSize, channels);
+
33  }
+
34 
+
35  AudioStream(const AudioStream&) = delete;
+
36 
+
37  AudioStream(AudioStream&& other) {
+
38  set(other);
+
39 
+
40  other.buffer = nullptr;
+
41  other.processor = nullptr;
42  other.sampleRate = 0;
43  other.sampleSize = 0;
44  other.channels = 0;
@@ -115,120 +115,148 @@
49  }
50 
51  GETTERSETTER(rAudioBuffer *, Buffer, buffer)
-
52  GETTERSETTER(unsigned int, SampleRate, sampleRate)
-
53  GETTERSETTER(unsigned int, SampleSize, sampleSize)
-
54  GETTERSETTER(unsigned int, Channels, channels)
-
55 
-
56  AudioStream& operator=(const ::AudioStream& stream) {
-
57  set(stream);
-
58  return *this;
-
59  }
-
60 
-
61  AudioStream& operator=(const AudioStream&) = delete;
-
62 
-
63  AudioStream& operator=(AudioStream&& other) noexcept {
-
64  if (this == &other) {
-
65  return *this;
-
66  }
-
67 
-
68  Unload();
-
69  set(other);
-
70 
-
71  other.buffer = nullptr;
-
72  other.sampleRate = 0;
-
73  other.sampleSize = 0;
-
74  other.channels = 0;
-
75 
-
76  return *this;
-
77  }
-
78 
-
82  inline AudioStream& Update(const void *data, int samplesCount) {
-
83  ::UpdateAudioStream(*this, data, samplesCount);
-
84  return *this;
-
85  }
-
86 
-
90  inline void Unload() {
-
91  ::UnloadAudioStream(*this);
-
92  }
-
93 
-
97  inline bool IsProcessed() const {
-
98  return ::IsAudioStreamProcessed(*this);
-
99  }
-
100 
-
104  inline AudioStream& Play() {
-
105  ::PlayAudioStream(*this);
-
106  return *this;
-
107  }
-
108 
-
112  inline AudioStream& Pause() {
-
113  ::PauseAudioStream(*this);
-
114  return *this;
-
115  }
-
116 
-
120  inline AudioStream& Resume() {
-
121  ::ResumeAudioStream(*this);
-
122  return *this;
-
123  }
-
124 
-
128  inline bool IsPlaying() const {
-
129  return ::IsAudioStreamPlaying(*this);
-
130  }
-
131 
-
135  inline AudioStream& Stop() {
-
136  ::StopAudioStream(*this);
-
137  return *this;
-
138  }
-
139 
-
143  inline AudioStream& SetVolume(float volume) {
-
144  ::SetAudioStreamVolume(*this, volume);
-
145  return *this;
-
146  }
-
147 
-
151  inline AudioStream& SetPitch(float pitch) {
-
152  ::SetAudioStreamPitch(*this, pitch);
-
153  return *this;
-
154  }
-
155 
-
159  inline static void SetBufferSizeDefault(int size) {
-
160  ::SetAudioStreamBufferSizeDefault(size);
-
161  }
-
162 
-
166  bool IsReady() {
-
167  return channels > 0;
-
168  }
-
169 
-
175  bool Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) {
-
176  set(::LoadAudioStream(SampleRate, SampleSize, Channels));
-
177  return IsReady();
+
52  GETTERSETTER(rAudioProcessor *, Processor, processor)
+
53  GETTERSETTER(unsigned int, SampleRate, sampleRate)
+
54  GETTERSETTER(unsigned int, SampleSize, sampleSize)
+
55  GETTERSETTER(unsigned int, Channels, channels)
+
56 
+
57  AudioStream& operator=(const ::AudioStream& stream) {
+
58  set(stream);
+
59  return *this;
+
60  }
+
61 
+
62  AudioStream& operator=(const AudioStream&) = delete;
+
63 
+
64  AudioStream& operator=(AudioStream&& other) noexcept {
+
65  if (this == &other) {
+
66  return *this;
+
67  }
+
68 
+
69  Unload();
+
70  set(other);
+
71 
+
72  other.buffer = nullptr;
+
73  other.processor = nullptr;
+
74  other.sampleRate = 0;
+
75  other.sampleSize = 0;
+
76  other.channels = 0;
+
77 
+
78  return *this;
+
79  }
+
80 
+
84  inline AudioStream& Update(const void *data, int samplesCount) {
+
85  ::UpdateAudioStream(*this, data, samplesCount);
+
86  return *this;
+
87  }
+
88 
+
92  inline void Unload() {
+
93  ::UnloadAudioStream(*this);
+
94  }
+
95 
+
99  inline bool IsProcessed() const {
+
100  return ::IsAudioStreamProcessed(*this);
+
101  }
+
102 
+
106  inline AudioStream& Play() {
+
107  ::PlayAudioStream(*this);
+
108  return *this;
+
109  }
+
110 
+
114  inline AudioStream& Pause() {
+
115  ::PauseAudioStream(*this);
+
116  return *this;
+
117  }
+
118 
+
122  inline AudioStream& Resume() {
+
123  ::ResumeAudioStream(*this);
+
124  return *this;
+
125  }
+
126 
+
130  inline bool IsPlaying() const {
+
131  return ::IsAudioStreamPlaying(*this);
+
132  }
+
133 
+
137  inline AudioStream& Stop() {
+
138  ::StopAudioStream(*this);
+
139  return *this;
+
140  }
+
141 
+
145  inline AudioStream& SetVolume(float volume = 1.0f) {
+
146  ::SetAudioStreamVolume(*this, volume);
+
147  return *this;
+
148  }
+
149 
+
153  inline AudioStream& SetPitch(float pitch) {
+
154  ::SetAudioStreamPitch(*this, pitch);
+
155  return *this;
+
156  }
+
157 
+
161  inline AudioStream& SetPan(float pan = 0.5f) {
+
162  ::SetAudioStreamPitch(*this, pan);
+
163  return *this;
+
164  }
+
165 
+
169  inline static void SetBufferSizeDefault(int size) {
+
170  ::SetAudioStreamBufferSizeDefault(size);
+
171  }
+
172 
+
176  inline void SetCallback(::AudioCallback callback) {
+
177  ::SetAudioStreamCallback(*this, callback);
178  }
179 
-
180  private:
-
181  void set(const ::AudioStream& stream) {
-
182  buffer = stream.buffer;
-
183  sampleRate = stream.sampleRate;
-
184  sampleSize = stream.sampleSize;
-
185  channels = stream.channels;
-
186  }
-
187 };
-
188 } // namespace raylib
- -
190 
-
191 #endif // RAYLIB_CPP_INCLUDE_AUDIOSTREAM_HPP_
+
183  inline void AttachProcessor(::AudioCallback processor) {
+
184  ::SetAudioStreamCallback(*this, processor);
+
185  }
+
186 
+
190  inline void DetachProcessor(::AudioCallback processor) {
+
191  ::SetAudioStreamCallback(*this, processor);
+
192  }
+
193 
+
197  bool IsReady() {
+
198  return channels > 0;
+
199  }
+
200 
+
206  void Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels = 2) {
+
207  Unload();
+
208  set(::LoadAudioStream(SampleRate, SampleSize, Channels));
+
209  if (!IsReady()) {
+
210  throw RaylibException("Failed to load audio stream");
+
211  }
+
212  }
+
213 
+
214  private:
+
215  void set(const ::AudioStream& stream) {
+
216  buffer = stream.buffer;
+
217  processor = stream.processor;
+
218  sampleRate = stream.sampleRate;
+
219  sampleSize = stream.sampleSize;
+
220  channels = stream.channels;
+
221  }
+
222 };
+
223 } // namespace raylib
+
224 
+ +
226 
+
227 #endif // RAYLIB_CPP_INCLUDE_AUDIOSTREAM_HPP_
AudioStream management functions.
Definition: AudioStream.hpp:12
-
bool IsProcessed() const
Check if any audio stream buffers requires refill.
Definition: AudioStream.hpp:97
-
AudioStream & Stop()
Stop audio stream.
-
AudioStream & SetPitch(float pitch)
Set pitch for audio stream (1.0 is base level)
-
bool IsPlaying() const
Check if audio stream is playing.
-
AudioStream & Play()
Play audio stream.
-
AudioStream(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels=2)
Init audio stream (to stream raw audio pcm data)
Definition: AudioStream.hpp:30
-
AudioStream & SetVolume(float volume)
Set volume for audio stream (1.0 is max level)
-
void Unload()
Unload audio stream and free memory.
Definition: AudioStream.hpp:90
-
static void SetBufferSizeDefault(int size)
Default size for new audio streams.
-
AudioStream & Pause()
Pause audio stream.
-
AudioStream & Resume()
Resume audio stream.
-
AudioStream & Update(const void *data, int samplesCount)
Update audio stream buffers with data.
Definition: AudioStream.hpp:82
-
bool Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels=2)
Init audio stream (to stream raw audio pcm data)
-
bool IsReady()
Retrieve whether or not the audio stream is ready.
+
void SetCallback(::AudioCallback callback)
Audio thread callback to request new data.
+
bool IsProcessed() const
Check if any audio stream buffers requires refill.
Definition: AudioStream.hpp:99
+
AudioStream & Stop()
Stop audio stream.
+
AudioStream & SetPitch(float pitch)
Set pitch for audio stream (1.0 is base level)
+
bool IsPlaying() const
Check if audio stream is playing.
+
void AttachProcessor(::AudioCallback processor)
Attach audio stream processor to stream.
+
AudioStream & SetVolume(float volume=1.0f)
Set volume for audio stream (1.0 is max level)
+
AudioStream & Play()
Play audio stream.
+
void Load(unsigned int SampleRate, unsigned int SampleSize, unsigned int Channels=2)
Load audio stream (to stream raw audio pcm data)
+
void Unload()
Unload audio stream and free memory.
Definition: AudioStream.hpp:92
+
static void SetBufferSizeDefault(int size)
Default size for new audio streams.
+
AudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels=2)
Init audio stream (to stream raw audio pcm data)
Definition: AudioStream.hpp:31
+
AudioStream & Pause()
Pause audio stream.
+
AudioStream & Resume()
Resume audio stream.
+
AudioStream & SetPan(float pan=0.5f)
Set pan for audio stream (0.5 is centered)
+
AudioStream & Update(const void *data, int samplesCount)
Update audio stream buffers with data.
Definition: AudioStream.hpp:84
+
bool IsReady()
Retrieve whether or not the audio stream is ready.
+
void DetachProcessor(::AudioCallback processor)
Detach audio stream processor from stream.
Exception used for most raylib-related exceptions.
All raylib-cpp classes and functions appear in the raylib namespace.
Definition: AudioDevice.hpp:8
diff --git a/docs/_bounding_box_8hpp_source.html b/docs/_bounding_box_8hpp_source.html index 0289e0d8..9dd684ed 100644 --- a/docs/_bounding_box_8hpp_source.html +++ b/docs/_bounding_box_8hpp_source.html @@ -82,76 +82,70 @@
11 class BoundingBox : public ::BoundingBox {
12  public:
13  /*
-
14  * Set the bounding box to default: min (0, 0, 0) and max (0, 0, 0).
+
14  * Copy a bounding box from another bounding box.
15  */
-
16  BoundingBox() : ::BoundingBox{::Vector3{0, 0, 0}, ::Vector3{ 0, 0, 0 }} {
+
16  BoundingBox(const ::BoundingBox& box) : ::BoundingBox{box.min, box.max} {
17  // Nothing.
18  }
19 
-
20  /*
-
21  * Copy a bounding box from another bounding box.
-
22  */
-
23  BoundingBox(const ::BoundingBox& box) : ::BoundingBox{box.min, box.max} {
-
24  // Nothing.
+
23  BoundingBox(const ::Mesh& mesh) {
+
24  set(::GetMeshBoundingBox(mesh));
25  }
26 
-
30  BoundingBox(const ::Mesh& mesh) {
-
31  set(::GetMeshBoundingBox(mesh));
-
32  }
-
33 
-
34  BoundingBox(::Vector3 minMax) : ::BoundingBox{minMax, minMax} {}
-
35  BoundingBox(::Vector3 min, ::Vector3 max) : ::BoundingBox{min, max} {}
-
36 
-
37  GETTERSETTER(::Vector3, Min, min)
-
38  GETTERSETTER(::Vector3, Max, max)
-
39 
-
40  BoundingBox& operator=(const ::BoundingBox& box) {
-
41  set(box);
-
42  return *this;
-
43  }
-
44 
-
48  inline BoundingBox& Draw(::Color color = {255, 255, 255, 255}) {
-
49  DrawBoundingBox(*this, color);
-
50  return *this;
+
27  BoundingBox(::Vector3 minMax = ::Vector3{0.0f, 0.0f, 0.0f}) : ::BoundingBox{minMax, minMax} {}
+
28  BoundingBox(::Vector3 min, ::Vector3 max) : ::BoundingBox{min, max} {}
+
29 
+
30  GETTERSETTER(::Vector3, Min, min)
+
31  GETTERSETTER(::Vector3, Max, max)
+
32 
+
33  BoundingBox& operator=(const ::BoundingBox& box) {
+
34  set(box);
+
35  return *this;
+
36  }
+
37 
+
41  inline BoundingBox& Draw(::Color color = {255, 255, 255, 255}) {
+
42  ::DrawBoundingBox(*this, color);
+
43  return *this;
+
44  }
+
45 
+
49  inline bool CheckCollision(const ::BoundingBox& box2) const {
+
50  return CheckCollisionBoxes(*this, box2);
51  }
52 
-
56  inline bool CheckCollision(const ::BoundingBox& box2) const {
-
57  return CheckCollisionBoxes(*this, box2);
+
56  inline bool CheckCollision(::Vector3 center, float radius) const {
+
57  return CheckCollisionBoxSphere(*this, center, radius);
58  }
59 
-
63  inline bool CheckCollision(::Vector3 center, float radius) const {
-
64  return CheckCollisionBoxSphere(*this, center, radius);
+
63  inline bool CheckCollision(const ::Ray& ray) const {
+
64  return GetRayCollisionBox(ray, *this).hit;
65  }
66 
-
70  inline bool CheckCollision(const ::Ray& ray) const {
-
71  return GetRayCollisionBox(ray, *this).hit;
+
70  inline RayCollision GetCollision(const ::Ray& ray) const {
+
71  return GetRayCollisionBox(ray, *this);
72  }
73 
-
77  inline RayCollision GetCollision(const ::Ray& ray) const {
-
78  return GetRayCollisionBox(ray, *this);
-
79  }
-
80 
-
81  private:
-
82  void set(const ::BoundingBox& box) {
-
83  min = box.min;
-
84  max = box.max;
-
85  }
-
86  void set(const ::Vector3& _min, const ::Vector3& _max) {
-
87  min = _min;
-
88  max = _max;
-
89  }
-
90 };
-
91 } // namespace raylib
- -
93 
-
94 #endif // RAYLIB_CPP_INCLUDE_BOUNDINGBOX_HPP_
+
74  private:
+
75  void set(const ::BoundingBox& box) {
+
76  min = box.min;
+
77  max = box.max;
+
78  }
+
79  void set(const ::Vector3& _min, const ::Vector3& _max) {
+
80  min = _min;
+
81  max = _max;
+
82  }
+
83 };
+
84 } // namespace raylib
+
85 
+ +
87 
+
88 #endif // RAYLIB_CPP_INCLUDE_BOUNDINGBOX_HPP_
Bounding box type.
Definition: BoundingBox.hpp:11
-
bool CheckCollision(::Vector3 center, float radius) const
Detect collision between box and sphere.
Definition: BoundingBox.hpp:63
-
RayCollision GetCollision(const ::Ray &ray) const
Get collision information between ray and bounding box.
Definition: BoundingBox.hpp:77
-
BoundingBox(const ::Mesh &mesh)
Compute mesh bounding box limits.
Definition: BoundingBox.hpp:30
-
BoundingBox & Draw(::Color color={255, 255, 255, 255})
Draw a bounding box with wires.
Definition: BoundingBox.hpp:48
-
bool CheckCollision(const ::BoundingBox &box2) const
Detect collision between two boxes.
Definition: BoundingBox.hpp:56
-
bool CheckCollision(const ::Ray &ray) const
Detect collision between ray and bounding box.
Definition: BoundingBox.hpp:70
+
bool CheckCollision(::Vector3 center, float radius) const
Detect collision between box and sphere.
Definition: BoundingBox.hpp:56
+
RayCollision GetCollision(const ::Ray &ray) const
Get collision information between ray and bounding box.
Definition: BoundingBox.hpp:70
+
BoundingBox(const ::Mesh &mesh)
Compute mesh bounding box limits.
Definition: BoundingBox.hpp:23
+
BoundingBox & Draw(::Color color={255, 255, 255, 255})
Draw a bounding box with wires.
Definition: BoundingBox.hpp:41
+
bool CheckCollision(const ::BoundingBox &box2) const
Detect collision between two boxes.
Definition: BoundingBox.hpp:49
+
bool CheckCollision(const ::Ray &ray) const
Detect collision between ray and bounding box.
Definition: BoundingBox.hpp:63
Color type, RGBA (32bit)
Definition: Color.hpp:14
Raycast hit information.
Vector3 type.
Definition: Vector3.hpp:16
diff --git a/docs/_camera2_d_8hpp_source.html b/docs/_camera2_d_8hpp_source.html index 3ca1a877..3f81d1c5 100644 --- a/docs/_camera2_d_8hpp_source.html +++ b/docs/_camera2_d_8hpp_source.html @@ -131,9 +131,10 @@
69  }
70 };
71 } // namespace raylib
- -
73 
-
74 #endif // RAYLIB_CPP_INCLUDE_CAMERA2D_HPP_
+
72 
+ +
74 
+
75 #endif // RAYLIB_CPP_INCLUDE_CAMERA2D_HPP_
Camera2D type, defines a 2d camera.
Definition: Camera2D.hpp:12
Vector2 GetScreenToWorld(::Vector2 position) const
Returns the world space position for a 2d camera screen space position.
Definition: Camera2D.hpp:59
Matrix GetMatrix() const
Returns camera 2d transform matrix.
Definition: Camera2D.hpp:45
diff --git a/docs/_camera3_d_8hpp_source.html b/docs/_camera3_d_8hpp_source.html index 5e98bc89..a0852f6d 100644 --- a/docs/_camera3_d_8hpp_source.html +++ b/docs/_camera3_d_8hpp_source.html @@ -185,14 +185,16 @@
159 };
160 
161 typedef Camera3D Camera;
-
162 } // namespace raylib
-
163 using RCamera = raylib::Camera;
- -
165 
-
166 #endif // RAYLIB_CPP_INCLUDE_CAMERA3D_HPP_
+
162 
+
163 } // namespace raylib
+
164 
+
165 using RCamera = raylib::Camera;
+ +
167 
+
168 #endif // RAYLIB_CPP_INCLUDE_CAMERA3D_HPP_
Camera type, defines a camera position/orientation in 3d space.
Definition: Camera3D.hpp:12
Camera3D & BeginMode()
Initializes 3D mode with custom camera (3D)
Definition: Camera3D.hpp:49
-
Matrix GetMatrix() const
Get transform matrix for camera.
Definition: Camera3D.hpp:65
+
Matrix GetMatrix() const
Get camera transform matrix (view matrix)
Definition: Camera3D.hpp:65
Camera3D & DrawBillboard(const ::Texture2D &texture, ::Rectangle sourceRec, ::Vector3 center, ::Vector2 size, ::Color tint={255, 255, 255, 255})
Draw a billboard texture defined by source.
Definition: Camera3D.hpp:141
Camera3D & DrawBillboard(const ::Texture2D &texture, ::Vector3 center, float size, ::Color tint={255, 255, 255, 255})
Draw a billboard texture.
Definition: Camera3D.hpp:129
Vector2 GetWorldToScreen(::Vector3 position) const
Returns the screen space position for a 3d world space position.
Definition: Camera3D.hpp:122
@@ -211,7 +213,7 @@
Vector2 type.
Definition: Vector2.hpp:16
Vector3 type.
Definition: Vector3.hpp:16
All raylib-cpp classes and functions appear in the raylib namespace.
Definition: AudioDevice.hpp:8
-
static void UpdateCamera(const ::Camera &camera)
Update camera depending on selected mode.
Definition: Functions.hpp:202
+
static void UpdateCamera(const ::Camera &camera)
Update camera depending on selected mode.
Definition: Functions.hpp:197