12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988 |
- /**********************************************************************************************
- *
- * Physac v1.1 - 2D Physics library for videogames
- *
- * DESCRIPTION:
- *
- * Physac is a small 2D physics engine 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_DEBUG
- * Show debug traces log messages about physic bodies creation/destruction, physic system errors,
- * some calculations results and NULL reference exceptions
- *
- * #define PHYSAC_DEFINE_VECTOR2_TYPE
- * Forces library to define struct Vector2 data type (float x; float y)
- *
- * #define PHYSAC_AVOID_TIMMING_SYSTEM
- * Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps,
- * it allows just running UpdatePhysics() automatically on a separate thread at a desired time step.
- * In case physics steps update needs to be controlled by user with a custom timming mechanism,
- * just define this flag and the internal timming mechanism will be avoided, in that case,
- * timming libraries are neither required by the module.
- *
- * #define PHYSAC_MALLOC()
- * #define PHYSAC_CALLOC()
- * #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.
- *
- * COMPILATION:
- *
- * Use the following code to compile with GCC:
- * gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lopengl32 -lgdi32 -lwinmm -std=c99
- *
- * VERSIONS HISTORY:
- * 1.1 (20-Jan-2021) @raysan5: Library general revision
- * Removed threading system (up to the user)
- * Support MSVC C++ compilation using CLITERAL()
- * Review DEBUG mechanism for TRACELOG() and all TRACELOG() messages
- * Review internal variables/functions naming for consistency
- * Allow option to avoid internal timming system, to allow app manage the steps
- * 1.0 (12-Jun-2017) First release of the library
- *
- *
- * LICENSE: zlib/libpng
- *
- * Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5)
- *
- * This software is provided "as-is", without any express or implied warranty. In no event
- * will the authors be held liable for any damages arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose, including commercial
- * applications, and to alter it and redistribute it freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not claim that you
- * wrote the original software. If you use this software in a product, an acknowledgment
- * in the product documentation would be appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
- * as being the original software.
- *
- * 3. This notice may not be removed or altered from any source distribution.
- *
- **********************************************************************************************/
- #if !defined(PHYSAC_H)
- #define PHYSAC_H
- #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
- // Allow custom memory allocators
- #ifndef PHYSAC_MALLOC
- #define PHYSAC_MALLOC(size) malloc(size)
- #endif
- #ifndef PHYSAC_CALLOC
- #define PHYSAC_CALLOC(size, n) calloc(size, n)
- #endif
- #ifndef PHYSAC_FREE
- #define PHYSAC_FREE(ptr) free(ptr)
- #endif
- //----------------------------------------------------------------------------------
- // Defines and Macros
- //----------------------------------------------------------------------------------
- #define PHYSAC_MAX_BODIES 64 // Maximum number of physic bodies supported
- #define PHYSAC_MAX_MANIFOLDS 4096 // Maximum number of physic bodies interactions (64x64)
- #define PHYSAC_MAX_VERTICES 24 // Maximum number of vertex for polygons shapes
- #define PHYSAC_DEFAULT_CIRCLE_VERTICES 24 // Default number of vertices for circle shapes
- #define PHYSAC_COLLISION_ITERATIONS 100
- #define PHYSAC_PENETRATION_ALLOWANCE 0.05f
- #define PHYSAC_PENETRATION_CORRECTION 0.4f
- #define PHYSAC_PI 3.14159265358979323846f
- #define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f)
- //----------------------------------------------------------------------------------
- // Data Types Structure Definition
- //----------------------------------------------------------------------------------
- #if defined(__STDC__) && __STDC_VERSION__ >= 199901L
- #include <stdbool.h>
- #endif
- typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsShapeType;
- // Previously defined to be used in PhysicsShape struct as circular dependencies
- typedef struct PhysicsBodyData *PhysicsBody;
- #if defined(PHYSAC_DEFINE_VECTOR2_TYPE)
- // Vector2 type
- typedef struct Vector2 {
- float x;
- float y;
- } Vector2;
- #endif
- // Matrix2x2 type (used for polygon shape rotation matrix)
- typedef struct Matrix2x2 {
- float m00;
- float m01;
- float m10;
- float m11;
- } Matrix2x2;
- typedef struct PhysicsVertexData {
- unsigned int vertexCount; // Vertex count (positions and normals)
- Vector2 positions[PHYSAC_MAX_VERTICES]; // Vertex positions vectors
- Vector2 normals[PHYSAC_MAX_VERTICES]; // Vertex normals vectors
- } PhysicsVertexData;
- typedef struct PhysicsShape {
- PhysicsShapeType type; // Shape type (circle or polygon)
- PhysicsBody body; // Shape physics body data pointer
- PhysicsVertexData vertexData; // Shape vertices data (used for polygon shapes)
- float radius; // Shape radius (used for circle shapes)
- Matrix2x2 transform; // Vertices transform matrix 2x2
- } PhysicsShape;
- typedef struct PhysicsBodyData {
- unsigned int id; // 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, transform)
- } PhysicsBodyData;
- typedef struct PhysicsManifoldData {
- unsigned int id; // 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
- //----------------------------------------------------------------------------------
- // Physics system management
- PHYSACDEF void InitPhysics(void); // Initializes physics system
- PHYSACDEF void UpdatePhysics(void); // Update physics system
- PHYSACDEF void ResetPhysics(void); // Reset physics system (global variables)
- PHYSACDEF void ClosePhysics(void); // Close physics system and unload used memory
- PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default
- PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force
- // Physic body creation/destroy
- 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 DestroyPhysicsBody(PhysicsBody body); // Destroy a physics body
- // Physic body forces
- 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 void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter
- // Query physics info
- PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index
- PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies
- 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)
- #if defined(__cplusplus)
- }
- #endif
- #endif // PHYSAC_H
- /***********************************************************************************
- *
- * PHYSAC IMPLEMENTATION
- *
- ************************************************************************************/
- #if defined(PHYSAC_IMPLEMENTATION)
- // Support TRACELOG macros
- #if defined(PHYSAC_DEBUG)
- #include <stdio.h> // Required for: printf()
- #define TRACELOG(...) printf(__VA_ARGS__)
- #else
- #define TRACELOG(...) (void)0;
- #endif
- #include <stdlib.h> // Required for: malloc(), calloc(), free()
- #include <math.h> // Required for: cosf(), sinf(), fabs(), sqrtf()
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- // Time management functionality
- #include <time.h> // Required for: time(), clock_gettime()
- #if defined(_WIN32)
- // Functions required to query time on Windows
- int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
- int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
- #endif
- #if defined(__linux__) || defined(__FreeBSD__)
- #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 <sys/time.h> // Required for: timespec
- #endif
- #if defined(__APPLE__) // macOS also defines __MACH__
- #include <mach/mach_time.h> // Required for: mach_absolute_time()
- #endif
- #endif
- // NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
- // Plain structures in C++ (without constructors) can be initialized from { } initializers.
- #if defined(__cplusplus)
- #define CLITERAL(type) type
- #else
- #define CLITERAL(type) (type)
- #endif
- //----------------------------------------------------------------------------------
- // Defines and Macros
- //----------------------------------------------------------------------------------
- #define PHYSAC_MIN(a,b) (((a)<(b))?(a):(b))
- #define PHYSAC_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 CLITERAL(Vector2){ 0.0f, 0.0f }
- //----------------------------------------------------------------------------------
- // Global Variables Definition
- //----------------------------------------------------------------------------------
- static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time in milliseconds used for physics steps
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- // Time measure variables
- static double baseClockTicks = 0.0; // Offset clock ticks for MONOTONIC clock
- static unsigned long long int frequency = 0; // Hi-res clock frequency
- static double startTime = 0.0; // Start time in milliseconds
- static double currentTime = 0.0; // Current time in milliseconds
- #endif
- // Physics system configuration
- 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
- static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force
- // Utilities variables
- static unsigned int usedMemory = 0; // Total allocated dynamic memory
- //----------------------------------------------------------------------------------
- // Module Internal Functions Declaration
- //----------------------------------------------------------------------------------
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- // Timming measure functions
- static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer
- static unsigned long long int GetClockTicks(void); // Get hi-res MONOTONIC time measure in mseconds
- static double GetCurrentTime(void); // Get current time measure in milliseconds
- #endif
- static void UpdatePhysicsStep(void); // Update physics step (dynamics, collisions and position corrections)
- static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization
- static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization
- static PhysicsVertexData CreateDefaultPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot
- static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions
- static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions
- 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 SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies
- static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity
- static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces
- static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions
- static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information
- static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face
- static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration
- // Math required functions
- static Vector2 MathVector2Product(Vector2 vector, float value); // Returns the product of a vector and a value
- static float MathVector2CrossProduct(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors
- static float MathVector2SqrLen(Vector2 vector); // Returns the len square root of a vector
- static float MathVector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors
- static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors
- static void MathVector2Normalize(Vector2 *vector); // Returns the normalized values of a vector
- static Vector2 MathVector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors
- static Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors
- static Matrix2x2 MathMatFromRadians(float radians); // Returns a matrix 2x2 from a given radians value
- static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2
- static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector); // Returns product between matrix 2x2 and vector
- static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip); // Returns clipping value based on a normal and two faces
- static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points
- //----------------------------------------------------------------------------------
- // Module Functions Definition
- //----------------------------------------------------------------------------------
- // Initializes physics values, pointers and creates physics loop thread
- PHYSACDEF void InitPhysics(void)
- {
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- // Initialize high resolution timer
- InitTimer();
- #endif
- TRACELOG("[PHYSAC] Physics module initialized successfully\n");
- }
- // 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 body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density);
- return body;
- }
- // Creates a new rectangle physics body with generic parameters
- PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density)
- {
- // NOTE: Make sure body data is initialized to 0
- PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1);
- usedMemory += sizeof(PhysicsBodyData);
- int id = FindAvailableBodyIndex();
- if (id != -1)
- {
- // Initialize new body with generic values
- body->id = id;
- body->enabled = true;
- body->position = pos;
- body->shape.type = PHYSICS_POLYGON;
- body->shape.body = body;
- body->shape.transform = MathMatFromRadians(0.0f);
- body->shape.vertexData = CreateRectanglePolygon(pos, CLITERAL(Vector2){ width, height });
- // Calculate centroid and moment of inertia
- Vector2 center = { 0.0f, 0.0f };
- float area = 0.0f;
- float inertia = 0.0f;
- for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
- {
- // Triangle vertices, third vertex implied as (0, 0)
- Vector2 p1 = body->shape.vertexData.positions[i];
- unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0);
- Vector2 p2 = body->shape.vertexData.positions[nextIndex];
- float D = MathVector2CrossProduct(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 (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
- {
- body->shape.vertexData.positions[i].x -= center.x;
- body->shape.vertexData.positions[i].y -= center.y;
- }
- body->mass = density*area;
- body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
- body->inertia = density*inertia;
- body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
- body->staticFriction = 0.4f;
- body->dynamicFriction = 0.2f;
- body->restitution = 0.0f;
- body->useGravity = true;
- body->isGrounded = false;
- body->freezeOrient = false;
- // Add new body to bodies pointers array and update bodies count
- bodies[physicsBodiesCount] = body;
- physicsBodiesCount++;
- TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id);
- }
- else TRACELOG("[PHYSAC] Physic body could not be created, PHYSAC_MAX_BODIES reached\n");
- return body;
- }
- // Creates a new polygon physics body with generic parameters
- PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density)
- {
- PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData));
- usedMemory += sizeof(PhysicsBodyData);
- int id = FindAvailableBodyIndex();
- if (id != -1)
- {
- // Initialize new body with generic values
- body->id = id;
- body->enabled = true;
- body->position = pos;
- body->velocity = PHYSAC_VECTOR_ZERO;
- body->force = PHYSAC_VECTOR_ZERO;
- body->angularVelocity = 0.0f;
- body->torque = 0.0f;
- body->orient = 0.0f;
- body->shape.type = PHYSICS_POLYGON;
- body->shape.body = body;
- body->shape.transform = MathMatFromRadians(0.0f);
- body->shape.vertexData = CreateDefaultPolygon(radius, sides);
- // Calculate centroid and moment of inertia
- Vector2 center = { 0.0f, 0.0f };
- float area = 0.0f;
- float inertia = 0.0f;
- for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
- {
- // Triangle vertices, third vertex implied as (0, 0)
- Vector2 position1 = body->shape.vertexData.positions[i];
- unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0);
- Vector2 position2 = body->shape.vertexData.positions[nextIndex];
- float cross = MathVector2CrossProduct(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 (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
- {
- body->shape.vertexData.positions[i].x -= center.x;
- body->shape.vertexData.positions[i].y -= center.y;
- }
- body->mass = density*area;
- body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
- body->inertia = density*inertia;
- body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
- body->staticFriction = 0.4f;
- body->dynamicFriction = 0.2f;
- body->restitution = 0.0f;
- body->useGravity = true;
- body->isGrounded = false;
- body->freezeOrient = false;
- // Add new body to bodies pointers array and update bodies count
- bodies[physicsBodiesCount] = body;
- physicsBodiesCount++;
- TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id);
- }
- else TRACELOG("[PHYSAC] Physics body could not be created, PHYSAC_MAX_BODIES reached\n");
- return body;
- }
- // Adds a force to a physics body
- PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force)
- {
- if (body != NULL) body->force = MathVector2Add(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)
- {
- PhysicsVertexData vertexData = body->shape.vertexData;
- bool collision = false;
- for (unsigned int i = 0; i < vertexData.vertexCount; i++)
- {
- Vector2 positionA = body->position;
- Vector2 positionB = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[i]));
- unsigned int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0);
- Vector2 positionC = MathMatVector2Product(body->shape.transform, MathVector2Add(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);
- Matrix2x2 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 = MathTriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO);
- center = MathVector2Add(bodyPos, center);
- Vector2 offset = MathVector2Subtract(center, bodyPos);
- PhysicsBody body = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values
- PhysicsVertexData vertexData = { 0 };
- vertexData.vertexCount = 3;
- vertexData.positions[0] = MathVector2Subtract(vertices[i], offset);
- vertexData.positions[1] = MathVector2Subtract(vertices[nextIndex], offset);
- vertexData.positions[2] = MathVector2Subtract(position, center);
- // Separate vertices to avoid unnecessary physics collisions
- vertexData.positions[0].x *= 0.95f;
- vertexData.positions[0].y *= 0.95f;
- vertexData.positions[1].x *= 0.95f;
- vertexData.positions[1].y *= 0.95f;
- vertexData.positions[2].x *= 0.95f;
- vertexData.positions[2].y *= 0.95f;
- // Calculate polygon faces normals
- for (unsigned int j = 0; j < vertexData.vertexCount; j++)
- {
- unsigned int nextVertex = (((j + 1) < vertexData.vertexCount) ? (j + 1) : 0);
- Vector2 face = MathVector2Subtract(vertexData.positions[nextVertex], vertexData.positions[j]);
- vertexData.normals[j] = CLITERAL(Vector2){ face.y, -face.x };
- MathVector2Normalize(&vertexData.normals[j]);
- }
- // Apply computed vertex data to new physics body shape
- body->shape.vertexData = vertexData;
- body->shape.transform = trans;
- // Calculate centroid and moment of inertia
- center = PHYSAC_VECTOR_ZERO;
- float area = 0.0f;
- float inertia = 0.0f;
- for (unsigned int j = 0; j < body->shape.vertexData.vertexCount; j++)
- {
- // Triangle vertices, third vertex implied as (0, 0)
- Vector2 p1 = body->shape.vertexData.positions[j];
- unsigned int nextVertex = (((j + 1) < body->shape.vertexData.vertexCount) ? (j + 1) : 0);
- Vector2 p2 = body->shape.vertexData.positions[nextVertex];
- float D = MathVector2CrossProduct(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;
- body->mass = area;
- body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
- body->inertia = inertia;
- body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
- // Calculate explosion force direction
- Vector2 pointA = body->position;
- Vector2 pointB = MathVector2Subtract(vertexData.positions[1], vertexData.positions[0]);
- pointB.x /= 2.0f;
- pointB.y /= 2.0f;
- Vector2 forceDirection = MathVector2Subtract(MathVector2Add(pointA, MathVector2Add(vertexData.positions[0], pointB)), body->position);
- MathVector2Normalize(&forceDirection);
- forceDirection.x *= force;
- forceDirection.y *= force;
- // Apply force to new physics body
- PhysicsAddForce(body, forceDirection);
- }
- PHYSAC_FREE(vertices);
- }
- }
- }
- else TRACELOG("[PHYSAC] WARNING: PhysicsShatter: NULL physic body\n");
- }
- // 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)
- {
- PhysicsBody body = NULL;
- if (index < (int)physicsBodiesCount)
- {
- body = bodies[index];
- if (body == NULL) TRACELOG("[PHYSAC] WARNING: GetPhysicsBody: NULL physic body\n");
- }
- else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
- return body;
- }
- // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
- PHYSACDEF int GetPhysicsShapeType(int index)
- {
- int result = -1;
- if (index < (int)physicsBodiesCount)
- {
- PhysicsBody body = bodies[index];
- if (body != NULL) result = body->shape.type;
- else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeType: NULL physic body\n");
- }
- else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
- return result;
- }
- // Returns the amount of vertices of a physics body shape
- PHYSACDEF int GetPhysicsShapeVerticesCount(int index)
- {
- int result = 0;
- if (index < (int)physicsBodiesCount)
- {
- PhysicsBody body = bodies[index];
- if (body != NULL)
- {
- switch (body->shape.type)
- {
- case PHYSICS_CIRCLE: result = PHYSAC_DEFAULT_CIRCLE_VERTICES; break;
- case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break;
- default: break;
- }
- }
- else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVerticesCount: NULL physic body\n");
- }
- else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
- 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_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
- position.y = body->position.y + sinf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
- } break;
- case PHYSICS_POLYGON:
- {
- PhysicsVertexData vertexData = body->shape.vertexData;
- position = MathVector2Add(body->position, MathMatVector2Product(body->shape.transform, vertexData.positions[vertex]));
- } break;
- default: break;
- }
- }
- else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVertex: NULL physic body\n");
- 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 = MathMatFromRadians(radians);
- }
- }
- // Unitializes and destroys a physics body
- PHYSACDEF void DestroyPhysicsBody(PhysicsBody body)
- {
- if (body != NULL)
- {
- int id = body->id;
- int index = -1;
- for (unsigned int i = 0; i < physicsBodiesCount; i++)
- {
- if (bodies[i]->id == id)
- {
- index = i;
- break;
- }
- }
- if (index == -1)
- {
- TRACELOG("[PHYSAC] WARNING: Requested body (id: %i) can not be found\n", id);
- return; // Prevent access to index -1
- }
- // Free body allocated memory
- PHYSAC_FREE(body);
- usedMemory -= sizeof(PhysicsBodyData);
- bodies[index] = NULL;
- // Reorder physics bodies pointers array and its catched index
- for (unsigned int i = index; i < physicsBodiesCount; i++)
- {
- if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1];
- }
- // Update physics bodies count
- physicsBodiesCount--;
- TRACELOG("[PHYSAC] Physic body destroyed successfully (id: %i)\n", id);
- }
- else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsBody: NULL physic body\n");
- }
- // Destroys created physics bodies and manifolds and resets global values
- PHYSACDEF void ResetPhysics(void)
- {
- if (physicsBodiesCount > 0)
- {
- // Unitialize physics bodies dynamic memory allocations
- for (unsigned int i = physicsBodiesCount - 1; i >= 0; i--)
- {
- PhysicsBody body = bodies[i];
- if (body != NULL)
- {
- PHYSAC_FREE(body);
- bodies[i] = NULL;
- usedMemory -= sizeof(PhysicsBodyData);
- }
- }
- physicsBodiesCount = 0;
- }
- if (physicsManifoldsCount > 0)
- {
- // Unitialize physics manifolds dynamic memory allocations
- for (unsigned int i = physicsManifoldsCount - 1; i >= 0; i--)
- {
- PhysicsManifold manifold = contacts[i];
- if (manifold != NULL)
- {
- PHYSAC_FREE(manifold);
- contacts[i] = NULL;
- usedMemory -= sizeof(PhysicsManifoldData);
- }
- }
- physicsManifoldsCount = 0;
- }
- TRACELOG("[PHYSAC] Physics module reseted successfully\n");
- }
- // Unitializes physics pointers and exits physics loop thread
- PHYSACDEF void ClosePhysics(void)
- {
- // Unitialize physics manifolds dynamic memory allocations
- if (physicsManifoldsCount > 0)
- {
- for (unsigned int i = physicsManifoldsCount - 1; i >= 0; i--)
- DestroyPhysicsManifold(contacts[i]);
- }
-
- // Unitialize physics bodies dynamic memory allocations
- if (physicsBodiesCount > 0)
- {
- for (unsigned int i = physicsBodiesCount - 1; i >= 0; i--)
- DestroyPhysicsBody(bodies[i]);
- }
- // Trace log info
- if ((physicsBodiesCount > 0) || (usedMemory != 0))
- {
- TRACELOG("[PHYSAC] WARNING: Physics module closed with unallocated bodies (BODIES: %i, MEMORY: %i bytes)\n", physicsBodiesCount, usedMemory);
- }
- else if ((physicsManifoldsCount > 0) || (usedMemory != 0))
- {
- TRACELOG("[PHYSAC] WARNING: Pysics module closed with unallocated manifolds (MANIFOLDS: %i, MEMORY: %i bytes)\n", physicsManifoldsCount, usedMemory);
- }
- else TRACELOG("[PHYSAC] Physics module closed successfully\n");
- }
- //----------------------------------------------------------------------------------
- // 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 (unsigned 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 == (int)i)
- {
- index = (int)i;
- break;
- }
- }
- return index;
- }
- // Creates a default polygon shape with max vertex distance from polygon pivot
- static PhysicsVertexData CreateDefaultPolygon(float radius, int sides)
- {
- PhysicsVertexData data = { 0 };
- data.vertexCount = sides;
- // Calculate polygon vertices positions
- for (unsigned int i = 0; i < data.vertexCount; i++)
- {
- data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius;
- data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius;
- }
- // Calculate polygon faces normals
- for (int i = 0; i < (int)data.vertexCount; i++)
- {
- int nextIndex = (((i + 1) < sides) ? (i + 1) : 0);
- Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]);
- data.normals[i] = CLITERAL(Vector2){ face.y, -face.x };
- MathVector2Normalize(&data.normals[i]);
- }
- return data;
- }
- // Creates a rectangle polygon shape based on a min and max positions
- static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size)
- {
- PhysicsVertexData data = { 0 };
- data.vertexCount = 4;
- // Calculate polygon vertices positions
- data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 };
- data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 };
- data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 };
- data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 };
- // Calculate polygon faces normals
- for (unsigned int i = 0; i < data.vertexCount; i++)
- {
- int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0);
- Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]);
- data.normals[i] = CLITERAL(Vector2){ face.y, -face.x };
- MathVector2Normalize(&data.normals[i]);
- }
- return data;
- }
- // Update physics step (dynamics, collisions and position corrections)
- void UpdatePhysicsStep(void)
- {
- // Clear previous generated collisions information
- for (int i = (int)physicsManifoldsCount - 1; i >= 0; i--)
- {
- PhysicsManifold manifold = contacts[i];
- if (manifold != NULL) DestroyPhysicsManifold(manifold);
- }
- // Reset physics bodies grounded state
- for (unsigned int i = 0; i < physicsBodiesCount; i++)
- {
- PhysicsBody body = bodies[i];
- body->isGrounded = false;
- }
-
- // Generate new collision information
- for (unsigned int i = 0; i < physicsBodiesCount; i++)
- {
- PhysicsBody bodyA = bodies[i];
- if (bodyA != NULL)
- {
- for (unsigned 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 manifold = CreatePhysicsManifold(bodyA, bodyB);
- manifold->penetration = manifold->penetration;
- manifold->normal = manifold->normal;
- manifold->contacts[0] = manifold->contacts[0];
- manifold->contacts[1] = manifold->contacts[1];
- manifold->contactsCount = manifold->contactsCount;
- manifold->restitution = manifold->restitution;
- manifold->dynamicFriction = manifold->dynamicFriction;
- manifold->staticFriction = manifold->staticFriction;
- }
- }
- }
- }
- }
- // Integrate forces to physics bodies
- for (unsigned int i = 0; i < physicsBodiesCount; i++)
- {
- PhysicsBody body = bodies[i];
- if (body != NULL) IntegratePhysicsForces(body);
- }
- // Initialize physics manifolds to solve collisions
- for (unsigned int i = 0; i < physicsManifoldsCount; i++)
- {
- PhysicsManifold manifold = contacts[i];
- if (manifold != NULL) InitializePhysicsManifolds(manifold);
- }
- // Integrate physics collisions impulses to solve collisions
- for (unsigned int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++)
- {
- for (unsigned int j = 0; j < physicsManifoldsCount; j++)
- {
- PhysicsManifold manifold = contacts[i];
- if (manifold != NULL) IntegratePhysicsImpulses(manifold);
- }
- }
- // Integrate velocity to physics bodies
- for (unsigned 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 (unsigned int i = 0; i < physicsManifoldsCount; i++)
- {
- PhysicsManifold manifold = contacts[i];
- if (manifold != NULL) CorrectPhysicsPositions(manifold);
- }
- // Clear physics bodies forces
- for (unsigned int i = 0; i < physicsBodiesCount; i++)
- {
- PhysicsBody body = bodies[i];
- if (body != NULL)
- {
- body->force = PHYSAC_VECTOR_ZERO;
- body->torque = 0.0f;
- }
- }
- }
- // Update physics system
- // Physics steps are launched at a fixed time step if enabled
- PHYSACDEF void UpdatePhysics(void)
- {
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- static double deltaTimeAccumulator = 0.0;
- // Calculate current time (ms)
- currentTime = GetCurrentTime();
- // Calculate current delta time (ms)
- const double delta = currentTime - startTime;
- // Store the time elapsed since the last frame began
- deltaTimeAccumulator += delta;
- // Fixed time stepping loop
- while (deltaTimeAccumulator >= deltaTime)
- {
- UpdatePhysicsStep();
- deltaTimeAccumulator -= deltaTime;
- }
- // Record the starting of this frame
- startTime = currentTime;
- #else
- UpdatePhysicsStep();
- #endif
- }
- 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 (unsigned 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 manifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData));
- usedMemory += sizeof(PhysicsManifoldData);
- int id = FindAvailableManifoldIndex();
- if (id != -1)
- {
- // Initialize new manifold with generic values
- manifold->id = id;
- manifold->bodyA = a;
- manifold->bodyB = b;
- manifold->penetration = 0;
- manifold->normal = PHYSAC_VECTOR_ZERO;
- manifold->contacts[0] = PHYSAC_VECTOR_ZERO;
- manifold->contacts[1] = PHYSAC_VECTOR_ZERO;
- manifold->contactsCount = 0;
- manifold->restitution = 0.0f;
- manifold->dynamicFriction = 0.0f;
- manifold->staticFriction = 0.0f;
- // Add new body to bodies pointers array and update bodies count
- contacts[physicsManifoldsCount] = manifold;
- physicsManifoldsCount++;
- }
- else TRACELOG("[PHYSAC] Physic manifold could not be created, PHYSAC_MAX_MANIFOLDS reached\n");
- return manifold;
- }
- // Unitializes and destroys a physics manifold
- static void DestroyPhysicsManifold(PhysicsManifold manifold)
- {
- if (manifold != NULL)
- {
- int id = manifold->id;
- int index = -1;
- for (unsigned int i = 0; i < physicsManifoldsCount; i++)
- {
- if (contacts[i]->id == id)
- {
- index = i;
- break;
- }
- }
- if (index == -1) return; // Prevent access to index -1
- // Free manifold allocated memory
- PHYSAC_FREE(manifold);
- usedMemory -= sizeof(PhysicsManifoldData);
- contacts[index] = NULL;
- // Reorder physics manifolds pointers array and its catched index
- for (unsigned int i = index; i < physicsManifoldsCount; i++)
- {
- if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1];
- }
- // Update physics manifolds count
- physicsManifoldsCount--;
- }
- else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsManifold: NULL physic manifold\n");
- }
- // 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 = MathVector2Subtract(bodyB->position, bodyA->position);
- float distSqr = MathVector2SqrLen(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 = CLITERAL(Vector2){ 1.0f, 0.0f };
- manifold->contacts[0] = bodyA->position;
- }
- else
- {
- manifold->penetration = radius - distance;
- manifold->normal = CLITERAL(Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathVector2Normalize() due to sqrt is already performed
- manifold->contacts[0] = CLITERAL(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;
- manifold->contactsCount = 0;
- // Transform circle center to polygon transform space
- Vector2 center = bodyA->position;
- center = MathMatVector2Product(MathMatTranspose(bodyB->shape.transform), MathVector2Subtract(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;
- PhysicsVertexData vertexData = bodyB->shape.vertexData;
- for (unsigned int i = 0; i < vertexData.vertexCount; i++)
- {
- float currentSeparation = MathVector2DotProduct(vertexData.normals[i], MathVector2Subtract(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) < (int)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 = MathMatVector2Product(bodyB->shape.transform, vertexData.normals[faceNormal]);
- manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y };
- manifold->contacts[0] = CLITERAL(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 = MathVector2DotProduct(MathVector2Subtract(center, v1), MathVector2Subtract(v2, v1));
- float dot2 = MathVector2DotProduct(MathVector2Subtract(center, v2), MathVector2Subtract(v1, v2));
- manifold->penetration = bodyA->shape.radius - separation;
- if (dot1 <= 0.0f) // Closest to v1
- {
- if (MathVector2SqrDistance(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return;
- manifold->contactsCount = 1;
- Vector2 normal = MathVector2Subtract(v1, center);
- normal = MathMatVector2Product(bodyB->shape.transform, normal);
- MathVector2Normalize(&normal);
- manifold->normal = normal;
- v1 = MathMatVector2Product(bodyB->shape.transform, v1);
- v1 = MathVector2Add(v1, bodyB->position);
- manifold->contacts[0] = v1;
- }
- else if (dot2 <= 0.0f) // Closest to v2
- {
- if (MathVector2SqrDistance(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return;
- manifold->contactsCount = 1;
- Vector2 normal = MathVector2Subtract(v2, center);
- v2 = MathMatVector2Product(bodyB->shape.transform, v2);
- v2 = MathVector2Add(v2, bodyB->position);
- manifold->contacts[0] = v2;
- normal = MathMatVector2Product(bodyB->shape.transform, normal);
- MathVector2Normalize(&normal);
- manifold->normal = normal;
- }
- else // Closest to face
- {
- Vector2 normal = vertexData.normals[faceNormal];
- if (MathVector2DotProduct(MathVector2Subtract(center, v1), normal) > bodyA->shape.radius) return;
- normal = MathMatVector2Product(bodyB->shape.transform, normal);
- manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y };
- manifold->contacts[0] = CLITERAL(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 a polygon to a circle shape physics bodies
- static void SolvePolygonToCircle(PhysicsManifold manifold)
- {
- PhysicsBody bodyA = manifold->bodyA;
- PhysicsBody bodyB = manifold->bodyB;
- if ((bodyA == NULL) || (bodyB == NULL)) return;
- manifold->bodyA = bodyB;
- manifold->bodyB = bodyA;
- SolveCircleToPolygon(manifold);
- manifold->normal.x *= -1.0f;
- manifold->normal.y *= -1.0f;
- }
- // 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
- // Checking bias range for penetration
- if (penetrationA >= (penetrationB*0.95f + penetrationA*0.01f))
- {
- 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
- PhysicsVertexData refData = refPoly.vertexData;
- Vector2 v1 = refData.positions[referenceIndex];
- referenceIndex = (((referenceIndex + 1) < (int)refData.vertexCount) ? (referenceIndex + 1) : 0);
- Vector2 v2 = refData.positions[referenceIndex];
- // Transform vertices to world space
- v1 = MathMatVector2Product(refPoly.transform, v1);
- v1 = MathVector2Add(v1, refPoly.body->position);
- v2 = MathMatVector2Product(refPoly.transform, v2);
- v2 = MathVector2Add(v2, refPoly.body->position);
- // Calculate reference face side normal in world space
- Vector2 sidePlaneNormal = MathVector2Subtract(v2, v1);
- MathVector2Normalize(&sidePlaneNormal);
- // Orthogonalize
- Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x };
- float refC = MathVector2DotProduct(refFaceNormal, v1);
- float negSide = MathVector2DotProduct(sidePlaneNormal, v1)*-1;
- float posSide = MathVector2DotProduct(sidePlaneNormal, v2);
- // MathVector2Clip incident face to reference face side planes (due to floating point error, possible to not have required points
- if (MathVector2Clip(CLITERAL(Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, &incidentFace[0], &incidentFace[1], negSide) < 2) return;
- if (MathVector2Clip(sidePlaneNormal, &incidentFace[0], &incidentFace[1], posSide) < 2) return;
- // Flip normal if required
- manifold->normal = (flip ? CLITERAL(Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal);
- // Keep points behind reference face
- int currentPoint = 0; // MathVector2Clipped points behind reference face
- float separation = MathVector2DotProduct(refFaceNormal, incidentFace[0]) - refC;
- if (separation <= 0.0f)
- {
- manifold->contacts[currentPoint] = incidentFace[0];
- manifold->penetration = -separation;
- currentPoint++;
- }
- else manifold->penetration = 0.0f;
- separation = MathVector2DotProduct(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 += (float)((body->force.x*body->inverseMass)*(deltaTime/2.0));
- body->velocity.y += (float)((body->force.y*body->inverseMass)*(deltaTime/2.0));
- if (body->useGravity)
- {
- body->velocity.x += (float)(gravityForce.x*(deltaTime/1000/2.0));
- body->velocity.y += (float)(gravityForce.y*(deltaTime/1000/2.0));
- }
- if (!body->freezeOrient) body->angularVelocity += (float)(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 (unsigned int i = 0; i < manifold->contactsCount; i++)
- {
- // Caculate radius from center of mass to contact
- Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position);
- Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position);
- Vector2 crossA = MathVector2Product(radiusA, bodyA->angularVelocity);
- Vector2 crossB = MathVector2Product(radiusB, bodyB->angularVelocity);
- 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 (MathVector2SqrLen(radiusV) < (MathVector2SqrLen(CLITERAL(Vector2){ (float)(gravityForce.x*deltaTime/1000), (float)(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 (unsigned int i = 0; i < manifold->contactsCount; i++)
- {
- // Calculate radius from center of mass to contact
- Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position);
- Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position);
- // Calculate relative velocity
- Vector2 radiusV = { 0.0f, 0.0f };
- radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x;
- radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y;
- // Relative velocity along the normal
- float contactVelocity = MathVector2DotProduct(radiusV, manifold->normal);
- // Do not resolve if velocities are separating
- if (contactVelocity > 0.0f) return;
- float raCrossN = MathVector2CrossProduct(radiusA, manifold->normal);
- float rbCrossN = MathVector2CrossProduct(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*MathVector2CrossProduct(radiusA, CLITERAL(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*MathVector2CrossProduct(radiusB, impulseV);
- }
- // Apply friction impulse to each physics body
- radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x;
- radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y;
- Vector2 tangent = { radiusV.x - (manifold->normal.x*MathVector2DotProduct(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathVector2DotProduct(radiusV, manifold->normal)) };
- MathVector2Normalize(&tangent);
- // Calculate impulse tangent magnitude
- float impulseTangent = -MathVector2DotProduct(radiusV, tangent);
- impulseTangent /= inverseMassSum;
- impulseTangent /= (float)manifold->contactsCount;
- float absImpulseTangent = (float)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 = CLITERAL(Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent };
- else tangentImpulse = CLITERAL(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*MathVector2CrossProduct(radiusA, CLITERAL(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*MathVector2CrossProduct(radiusB, tangentImpulse);
- }
- }
- }
- // Integrates physics velocity into position and forces
- static void IntegratePhysicsVelocity(PhysicsBody body)
- {
- if ((body == NULL) ||!body->enabled) return;
- body->position.x += (float)(body->velocity.x*deltaTime);
- body->position.y += (float)(body->velocity.y*deltaTime);
- if (!body->freezeOrient) body->orient += (float)(body->angularVelocity*deltaTime);
- body->shape.transform = MathMatFromRadians(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 = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION;
- correction.y = (PHYSAC_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 };
- PhysicsVertexData data = shape.vertexData;
- for (unsigned int i = 0; i < data.vertexCount; i++)
- {
- Vector2 vertex = data.positions[i];
- float projection = MathVector2DotProduct(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;
- PhysicsVertexData dataA = shapeA.vertexData;
- //PhysicsVertexData dataB = shapeB.vertexData;
- for (unsigned int i = 0; i < dataA.vertexCount; i++)
- {
- // Retrieve a face normal from A shape
- Vector2 normal = dataA.normals[i];
- Vector2 transNormal = MathMatVector2Product(shapeA.transform, normal);
- // Transform face normal into B shape's model space
- Matrix2x2 buT = MathMatTranspose(shapeB.transform);
- normal = MathMatVector2Product(buT, transNormal);
- // Retrieve support point from B shape along -n
- Vector2 support = GetSupport(shapeB, CLITERAL(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 = MathMatVector2Product(shapeA.transform, vertex);
- vertex = MathVector2Add(vertex, shapeA.body->position);
- vertex = MathVector2Subtract(vertex, shapeB.body->position);
- vertex = MathMatVector2Product(buT, vertex);
- // Compute penetration distance in B shape's model space
- float distance = MathVector2DotProduct(normal, MathVector2Subtract(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)
- {
- PhysicsVertexData refData = ref.vertexData;
- PhysicsVertexData incData = inc.vertexData;
- Vector2 referenceNormal = refData.normals[index];
- // Calculate normal in incident's frame of reference
- referenceNormal = MathMatVector2Product(ref.transform, referenceNormal); // To world space
- referenceNormal = MathMatVector2Product(MathMatTranspose(inc.transform), referenceNormal); // To incident's model space
- // Find most anti-normal face on polygon
- int incidentFace = 0;
- float minDot = PHYSAC_FLT_MAX;
- for (unsigned int i = 0; i < incData.vertexCount; i++)
- {
- float dot = MathVector2DotProduct(referenceNormal, incData.normals[i]);
- if (dot < minDot)
- {
- minDot = dot;
- incidentFace = i;
- }
- }
- // Assign face vertices for incident face
- *v0 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]);
- *v0 = MathVector2Add(*v0, inc.body->position);
- incidentFace = (((incidentFace + 1) < (int)incData.vertexCount) ? (incidentFace + 1) : 0);
- *v1 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]);
- *v1 = MathVector2Add(*v1, inc.body->position);
- }
- // Returns clipping value based on a normal and two faces
- static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip)
- {
- int sp = 0;
- Vector2 out[2] = { *faceA, *faceB };
- // Retrieve distances from each endpoint to the line
- float distanceA = MathVector2DotProduct(normal, *faceA) - clip;
- float distanceB = MathVector2DotProduct(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 = MathVector2Subtract(*faceB, *faceA);
- delta.x *= alpha;
- delta.y *= alpha;
- out[sp] = MathVector2Add(out[sp], delta);
- sp++;
- }
- // Assign the new converted values
- *faceA = out[0];
- *faceB = out[1];
- return sp;
- }
- // Returns the barycenter of a triangle given by 3 points
- static Vector2 MathTriangleBarycenter(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;
- }
- #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
- // Initializes hi-resolution MONOTONIC timer
- static void InitTimer(void)
- {
- #if defined(_WIN32)
- QueryPerformanceFrequency((unsigned long long int *) &frequency);
- #endif
- #if defined(__EMSCRIPTEN__) || 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
- baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset
- startTime = GetCurrentTime(); // Get current time in milliseconds
- }
- // Get hi-res MONOTONIC time measure in clock ticks
- static unsigned long long int GetClockTicks(void)
- {
- unsigned long long int value = 0;
- #if defined(_WIN32)
- QueryPerformanceCounter((unsigned long long int *) &value);
- #endif
- #if defined(__linux__)
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)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)(GetClockTicks() - baseClockTicks)/frequency*1000;
- }
- #endif // !PHYSAC_AVOID_TIMMING_SYSTEM
- // Returns the cross product of a vector and a value
- static inline Vector2 MathVector2Product(Vector2 vector, float value)
- {
- Vector2 result = { -value*vector.y, value*vector.x };
- return result;
- }
- // Returns the cross product of two vectors
- static inline float MathVector2CrossProduct(Vector2 v1, Vector2 v2)
- {
- return (v1.x*v2.y - v1.y*v2.x);
- }
- // Returns the len square root of a vector
- static inline float MathVector2SqrLen(Vector2 vector)
- {
- return (vector.x*vector.x + vector.y*vector.y);
- }
- // Returns the dot product of two vectors
- static inline float MathVector2DotProduct(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 MathVector2SqrDistance(Vector2 v1, Vector2 v2)
- {
- Vector2 dir = MathVector2Subtract(v1, v2);
- return MathVector2DotProduct(dir, dir);
- }
- // Returns the normalized values of a vector
- static void MathVector2Normalize(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;
- }
- // Returns the sum of two given vectors
- static inline Vector2 MathVector2Add(Vector2 v1, Vector2 v2)
- {
- Vector2 result = { v1.x + v2.x, v1.y + v2.y };
- return result;
- }
- // Returns the subtract of two given vectors
- static inline Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2)
- {
- Vector2 result = { v1.x - v2.x, v1.y - v2.y };
- return result;
- }
- // Creates a matrix 2x2 from a given radians value
- static Matrix2x2 MathMatFromRadians(float radians)
- {
- float cos = cosf(radians);
- float sin = sinf(radians);
- Matrix2x2 result = { cos, -sin, sin, cos };
- return result;
- }
- // Returns the transpose of a given matrix 2x2
- static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix)
- {
- Matrix2x2 result = { matrix.m00, matrix.m10, matrix.m01, matrix.m11 };
- return result;
- }
- // Multiplies a vector by a matrix 2x2
- static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector)
- {
- Vector2 result = { matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y };
- return result;
- }
- #endif // PHYSAC_IMPLEMENTATION
|