MBED port of the Physacs library
README.md
- Committer:
- jstephens78
- Date:
- 22 months ago
- Revision:
- 1:ebc0214989c0
- Parent:
- 0:e39efa4f4f58
File content as of revision 1:ebc0214989c0:
<img src="https://github.com/victorfisac/Physac/blob/master/icon/physac_256x256.png"> # Physac-MBED This is a port of the Physac physics library to the MBED platform. ## Resource Usage The following outlines are based on defaults settings described under [##Modifications], and can be used to gauge resource demands of using Physac-MBED: - Each `PhysicsBody` requires 172 bytes of memory. Unless calling PhysicsShatter, these are always generated by user. - Each `PhysicsManifold` requires 56 bytes. These are generated internally during collisions to represent how the PhysicsBodys are interacting. ## Modifications The original Physac library was altered in the following ways to get running and to reduce footprint: - `PHYSAC_MAX_BODIES` was set to 16 - `PHYSAC_MAX_MANIFOLDS` was set to 96 - `PHYSAC_MAX_VERTICES` was set to 4 - `PHYSAC_CIRCLE_VERTICES` was set to 12 Physac-MBED can represent only collision shapes which are triangles, rectangles, or circles in its out-of-the-box configuration. ## Using Physac A simple demo using Physac-MBED to simulate objects and render them to a uLCD display: ```c++ #include "mbed.h" #include "uLCD_4DGL.h" #define PHYSAC_NO_THREADS #define PHYSAC_STANDALONE #define PHYSAC_IMPLEMENTATION #define _STDBOOL_H #include "physac.h" uLCD_4DGL uLCD(p9,p10,p30); int main(void) { uLCD.baudrate(BAUD_3000000); wait(.05); int screenWidth = 128; int screenHeight = 128; PhysicsBody floor = CreatePhysicsBodyRectangle((Vector2) { screenWidth/2, screenHeight - 4 }, screenWidth - 2, 4, 100); floor->enabled = false; // make object static // Defines which objects to create in the demo, and their initial properties // { x, y, density, rot, v_x, v_y } const size_t objs_n = 5; size_t objs_rect_n = 4; float objs_init[objs_n][6] = { { 48, 0, 10, PHYSAC_PI / 4, 0, 0 }, { 48, 64, 10, 0, 0, 0 }, { 92, 0, 10, 0, 0, 0 }, { 80, 48, 10, PHYSAC_PI / 4, 0, 0 }, { 256, 0, 30, 0, -.75, -.5 }, }; PhysicsBody objs[objs_n]; // Create demo objects for (int i = 0; i < objs_n; i++) { if (i < objs_rect_n) { objs[i] = CreatePhysicsBodyRectangle((Vector2) { objs_init[i][0], objs_init[i][1] }, 24, 24, objs_init[i][2]); } else { objs[i] = CreatePhysicsBodyCircle((Vector2) { objs_init[i][0], objs_init[i][1] }, 12, objs_init[i][2]); } SetPhysicsBodyRotation(objs[i], objs_init[i][3]); objs[i]->velocity.x = objs_init[i][4]; objs[i]->velocity.y = objs_init[i][5]; } // Simulation Loop Timer timer; timer.start(); // Time per step in ms deltaTime = 2.5; while (true) { float dt = timer.read() * 1000; timer.reset(); // In this demo, rendering takes orders of magnitudes longer than // physics takes to compute. So we run several steps per frame. for (int i = 0; i < 8; i++) { PhysicsStep(); } float phys_time = timer.read() * 1000; // Draw physics bodies uLCD.cls(); int bodiesCount = GetPhysicsBodiesCount(); for (int i = 0; i < bodiesCount; i++) { PhysicsBody body = GetPhysicsBody(i); if (body != NULL) { int vertexCount = GetPhysicsShapeVerticesCount(i); for (int j = 0; j < vertexCount; j++) { // Get physics bodies shape vertices to draw lines // Note: GetPhysicsShapeVertex() already calculates rotation transformations Vector2 vertexA = GetPhysicsShapeVertex(body, j); int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape Vector2 vertexB = GetPhysicsShapeVertex(body, jj); uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, GREEN); // Draw a line between two vertex positions } } } float render_time = timer.read()*1000 - phys_time; printf("[%2.2f] Phys: %4.4f Render: %4.4f\r\n", dt, phys_time, render_time); } ClosePhysics(); // Unitialize physics } ``` ### Use in multi-file projects: **Note**: the `PHYSAC_IMPLEMENTATION` macro will cause the definitions of all the internal functions of Physac to be included. This should **only be done once, from .cpp file**. For example: ```c++ // myheader.h #define PHYSAC_NO_THREADS #define PHYSAC_STANDALONE #define _STDBOOL_H #include "physac.h" // this includes only the declarations ------ // myfile.cpp #include "myheader.h" #define PHYSAC_IMPLEMENTATION #include "physac.h" // this includes the definitions ``` ## Future work Possible future improvements to this port: 1. Re-work allocation scheme so users can pre-allocate arrays of bodies and manifolds 2. Implement a partial collision reporting scheme. Presently in `PhysicsStep()`, all bodies are iterated, a manifold is generated for every collision, and *then* manifolds are iteratively resolved. This is necessary for "correct" resolution of collisions in sequence with velocity, but this requires a lot of working memory. A possible trade-off would be to iterate bodies only until a certain number of manifolds are generated, then updating those bodies. This would reduce the resource demands for large scenes, at the expense of accuracy. # Physac 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. The header file includes some tweakable define values to fit the results that the user wants with a minimal bad results. Most of those values are commented with a little explanation about their uses. Physac API ----- The PhysicsBody struct contains all dynamics information and collision shape. The user should use the following structure components: ```c typedef struct *PhysicsBody { unsigned int id; 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 staticFriction; // Friction when the body has no 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) } *PhysicsBody; ``` The header contains a few customizable define values. I set the values that gived me the best results. ```c #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 ``` Physac contains defines for memory management functions (malloc, free) to bring the user the opportunity to implement its own memory functions: ```c #define PHYSAC_MALLOC(size) malloc(size) #define PHYSAC_FREE(ptr) free(ptr) ``` The Physac API functions availables for the user are the following: ```c // Initializes physics values, pointers and creates physics loop thread void InitPhysics(void); // Returns true if physics thread is currently enabled bool IsPhysicsEnabled(void); // Sets physics global gravity force void SetPhysicsGravity(float x, float y); // Creates a new circle physics body with generic parameters PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new rectangle physics body with generic parameters PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new polygon physics body with generic parameters PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Adds a force to a physics body void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a angular force to a physics body void PhysicsAddTorque(PhysicsBody body, float amount); // Shatters a polygon shape physics body to little physics bodies with explosion force void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Returns the current amount of created physics bodies int GetPhysicsBodiesCount(void); // Returns a physics body of the bodies pool at a specific index PhysicsBody GetPhysicsBody(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) int GetPhysicsShapeType(int index); // Returns the amount of vertices of a physics body shape int GetPhysicsShapeVerticesCount(int index); // Returns transformed position of a body shape (body position + vertex transformed position) Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Sets physics body shape transform based on radians parameter void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Unitializes and destroy a physics body void DestroyPhysicsBody(PhysicsBody body); // Unitializes physics pointers and closes physics loop thread void ClosePhysics(void); ``` _Note: InitPhysics() needs to be called at program start and ClosePhysics() before the program ends. Closing and initializing Physac during the program flow doesn't affect or produces any error (useful as a 'reset' to destroy any created body by user in runtime)._ Dependencies ----- Physac uses the following C libraries for memory management, math operations and some debug features: * stdlib.h - Memory allocation [malloc(), free(), srand(), rand()]. * stdio.h - Message logging (only if PHYSAC_DEBUG is defined) [printf()]. * math.h - Math operations functions [cos(), sin(), fabs(), sqrtf()].