MBED port of the Physacs library

Committer:
jstephens78
Date:
Mon Dec 05 19:27:37 2022 +0000
Revision:
1:ebc0214989c0
Parent:
0:e39efa4f4f58
Update README, add MBED example

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jstephens78 0:e39efa4f4f58 1 <img src="https://github.com/victorfisac/Physac/blob/master/icon/physac_256x256.png">
jstephens78 0:e39efa4f4f58 2
jstephens78 1:ebc0214989c0 3 # Physac-MBED
jstephens78 1:ebc0214989c0 4 This is a port of the Physac physics library to the MBED platform.
jstephens78 1:ebc0214989c0 5
jstephens78 1:ebc0214989c0 6 ## Resource Usage
jstephens78 1:ebc0214989c0 7 The following outlines are based on defaults settings described under
jstephens78 1:ebc0214989c0 8 [##Modifications], and can be used to gauge resource demands of using Physac-MBED:
jstephens78 1:ebc0214989c0 9 - Each `PhysicsBody` requires 172 bytes of memory. Unless calling PhysicsShatter,
jstephens78 1:ebc0214989c0 10 these are always generated by user.
jstephens78 1:ebc0214989c0 11 - Each `PhysicsManifold` requires 56 bytes. These are generated internally during
jstephens78 1:ebc0214989c0 12 collisions to represent how the PhysicsBodys are interacting.
jstephens78 1:ebc0214989c0 13
jstephens78 1:ebc0214989c0 14 ## Modifications
jstephens78 1:ebc0214989c0 15 The original Physac library was altered in the following ways to get running and
jstephens78 1:ebc0214989c0 16 to reduce footprint:
jstephens78 1:ebc0214989c0 17 - `PHYSAC_MAX_BODIES` was set to 16
jstephens78 1:ebc0214989c0 18 - `PHYSAC_MAX_MANIFOLDS` was set to 96
jstephens78 1:ebc0214989c0 19 - `PHYSAC_MAX_VERTICES` was set to 4
jstephens78 1:ebc0214989c0 20 - `PHYSAC_CIRCLE_VERTICES` was set to 12
jstephens78 1:ebc0214989c0 21
jstephens78 1:ebc0214989c0 22 Physac-MBED can represent only collision shapes which are triangles, rectangles,
jstephens78 1:ebc0214989c0 23 or circles in its out-of-the-box configuration.
jstephens78 1:ebc0214989c0 24
jstephens78 1:ebc0214989c0 25 ## Using Physac
jstephens78 1:ebc0214989c0 26 A simple demo using Physac-MBED to simulate objects and render them to a uLCD
jstephens78 1:ebc0214989c0 27 display:
jstephens78 1:ebc0214989c0 28 ```c++
jstephens78 1:ebc0214989c0 29 #include "mbed.h"
jstephens78 1:ebc0214989c0 30 #include "uLCD_4DGL.h"
jstephens78 1:ebc0214989c0 31
jstephens78 1:ebc0214989c0 32 #define PHYSAC_NO_THREADS
jstephens78 1:ebc0214989c0 33 #define PHYSAC_STANDALONE
jstephens78 1:ebc0214989c0 34 #define PHYSAC_IMPLEMENTATION
jstephens78 1:ebc0214989c0 35 #define _STDBOOL_H
jstephens78 1:ebc0214989c0 36 #include "physac.h"
jstephens78 1:ebc0214989c0 37
jstephens78 1:ebc0214989c0 38 uLCD_4DGL uLCD(p9,p10,p30);
jstephens78 1:ebc0214989c0 39
jstephens78 1:ebc0214989c0 40 int main(void)
jstephens78 1:ebc0214989c0 41 {
jstephens78 1:ebc0214989c0 42 uLCD.baudrate(BAUD_3000000);
jstephens78 1:ebc0214989c0 43 wait(.05);
jstephens78 1:ebc0214989c0 44
jstephens78 1:ebc0214989c0 45 int screenWidth = 128;
jstephens78 1:ebc0214989c0 46 int screenHeight = 128;
jstephens78 1:ebc0214989c0 47 PhysicsBody floor = CreatePhysicsBodyRectangle((Vector2) {
jstephens78 1:ebc0214989c0 48 screenWidth/2, screenHeight - 4
jstephens78 1:ebc0214989c0 49 }, screenWidth - 2, 4, 100);
jstephens78 1:ebc0214989c0 50 floor->enabled = false; // make object static
jstephens78 1:ebc0214989c0 51
jstephens78 1:ebc0214989c0 52 // Defines which objects to create in the demo, and their initial properties
jstephens78 1:ebc0214989c0 53 // { x, y, density, rot, v_x, v_y }
jstephens78 1:ebc0214989c0 54 const size_t objs_n = 5;
jstephens78 1:ebc0214989c0 55 size_t objs_rect_n = 4;
jstephens78 1:ebc0214989c0 56 float objs_init[objs_n][6] = {
jstephens78 1:ebc0214989c0 57 { 48, 0, 10, PHYSAC_PI / 4, 0, 0 },
jstephens78 1:ebc0214989c0 58 { 48, 64, 10, 0, 0, 0 },
jstephens78 1:ebc0214989c0 59 { 92, 0, 10, 0, 0, 0 },
jstephens78 1:ebc0214989c0 60 { 80, 48, 10, PHYSAC_PI / 4, 0, 0 },
jstephens78 1:ebc0214989c0 61 { 256, 0, 30, 0, -.75, -.5 },
jstephens78 1:ebc0214989c0 62 };
jstephens78 1:ebc0214989c0 63 PhysicsBody objs[objs_n];
jstephens78 1:ebc0214989c0 64
jstephens78 1:ebc0214989c0 65 // Create demo objects
jstephens78 1:ebc0214989c0 66 for (int i = 0; i < objs_n; i++) {
jstephens78 1:ebc0214989c0 67 if (i < objs_rect_n) {
jstephens78 1:ebc0214989c0 68 objs[i] = CreatePhysicsBodyRectangle((Vector2) {
jstephens78 1:ebc0214989c0 69 objs_init[i][0], objs_init[i][1]
jstephens78 1:ebc0214989c0 70 }, 24, 24, objs_init[i][2]);
jstephens78 1:ebc0214989c0 71 } else {
jstephens78 1:ebc0214989c0 72 objs[i] = CreatePhysicsBodyCircle((Vector2) {
jstephens78 1:ebc0214989c0 73 objs_init[i][0], objs_init[i][1]
jstephens78 1:ebc0214989c0 74 }, 12, objs_init[i][2]);
jstephens78 1:ebc0214989c0 75 }
jstephens78 1:ebc0214989c0 76 SetPhysicsBodyRotation(objs[i], objs_init[i][3]);
jstephens78 1:ebc0214989c0 77 objs[i]->velocity.x = objs_init[i][4];
jstephens78 1:ebc0214989c0 78 objs[i]->velocity.y = objs_init[i][5];
jstephens78 1:ebc0214989c0 79 }
jstephens78 1:ebc0214989c0 80
jstephens78 1:ebc0214989c0 81 // Simulation Loop
jstephens78 1:ebc0214989c0 82 Timer timer;
jstephens78 1:ebc0214989c0 83 timer.start();
jstephens78 1:ebc0214989c0 84
jstephens78 1:ebc0214989c0 85 // Time per step in ms
jstephens78 1:ebc0214989c0 86 deltaTime = 2.5;
jstephens78 1:ebc0214989c0 87
jstephens78 1:ebc0214989c0 88 while (true) {
jstephens78 1:ebc0214989c0 89 float dt = timer.read() * 1000;
jstephens78 1:ebc0214989c0 90 timer.reset();
jstephens78 1:ebc0214989c0 91
jstephens78 1:ebc0214989c0 92 // In this demo, rendering takes orders of magnitudes longer than
jstephens78 1:ebc0214989c0 93 // physics takes to compute. So we run several steps per frame.
jstephens78 1:ebc0214989c0 94 for (int i = 0; i < 8; i++) {
jstephens78 1:ebc0214989c0 95 PhysicsStep();
jstephens78 1:ebc0214989c0 96 }
jstephens78 1:ebc0214989c0 97 float phys_time = timer.read() * 1000;
jstephens78 1:ebc0214989c0 98
jstephens78 1:ebc0214989c0 99 // Draw physics bodies
jstephens78 1:ebc0214989c0 100 uLCD.cls();
jstephens78 1:ebc0214989c0 101 int bodiesCount = GetPhysicsBodiesCount();
jstephens78 1:ebc0214989c0 102 for (int i = 0; i < bodiesCount; i++) {
jstephens78 1:ebc0214989c0 103 PhysicsBody body = GetPhysicsBody(i);
jstephens78 1:ebc0214989c0 104
jstephens78 1:ebc0214989c0 105 if (body != NULL) {
jstephens78 1:ebc0214989c0 106 int vertexCount = GetPhysicsShapeVerticesCount(i);
jstephens78 1:ebc0214989c0 107 for (int j = 0; j < vertexCount; j++) {
jstephens78 1:ebc0214989c0 108 // Get physics bodies shape vertices to draw lines
jstephens78 1:ebc0214989c0 109 // Note: GetPhysicsShapeVertex() already calculates rotation transformations
jstephens78 1:ebc0214989c0 110 Vector2 vertexA = GetPhysicsShapeVertex(body, j);
jstephens78 1:ebc0214989c0 111
jstephens78 1:ebc0214989c0 112 int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape
jstephens78 1:ebc0214989c0 113 Vector2 vertexB = GetPhysicsShapeVertex(body, jj);
jstephens78 1:ebc0214989c0 114
jstephens78 1:ebc0214989c0 115 uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, GREEN); // Draw a line between two vertex positions
jstephens78 1:ebc0214989c0 116 }
jstephens78 1:ebc0214989c0 117 }
jstephens78 1:ebc0214989c0 118 }
jstephens78 1:ebc0214989c0 119 float render_time = timer.read()*1000 - phys_time;
jstephens78 1:ebc0214989c0 120
jstephens78 1:ebc0214989c0 121 printf("[%2.2f] Phys: %4.4f Render: %4.4f\r\n",
jstephens78 1:ebc0214989c0 122 dt,
jstephens78 1:ebc0214989c0 123 phys_time,
jstephens78 1:ebc0214989c0 124 render_time);
jstephens78 1:ebc0214989c0 125 }
jstephens78 1:ebc0214989c0 126
jstephens78 1:ebc0214989c0 127 ClosePhysics(); // Unitialize physics
jstephens78 1:ebc0214989c0 128 }
jstephens78 1:ebc0214989c0 129 ```
jstephens78 1:ebc0214989c0 130
jstephens78 1:ebc0214989c0 131 ### Use in multi-file projects:
jstephens78 1:ebc0214989c0 132 **Note**: the `PHYSAC_IMPLEMENTATION` macro will cause the definitions of all
jstephens78 1:ebc0214989c0 133 the internal functions of Physac to be included. This should **only be done once,
jstephens78 1:ebc0214989c0 134 from .cpp file**. For example:
jstephens78 1:ebc0214989c0 135 ```c++
jstephens78 1:ebc0214989c0 136 // myheader.h
jstephens78 1:ebc0214989c0 137 #define PHYSAC_NO_THREADS
jstephens78 1:ebc0214989c0 138 #define PHYSAC_STANDALONE
jstephens78 1:ebc0214989c0 139 #define _STDBOOL_H
jstephens78 1:ebc0214989c0 140 #include "physac.h" // this includes only the declarations
jstephens78 1:ebc0214989c0 141
jstephens78 1:ebc0214989c0 142 ------
jstephens78 1:ebc0214989c0 143
jstephens78 1:ebc0214989c0 144 // myfile.cpp
jstephens78 1:ebc0214989c0 145 #include "myheader.h"
jstephens78 1:ebc0214989c0 146
jstephens78 1:ebc0214989c0 147 #define PHYSAC_IMPLEMENTATION
jstephens78 1:ebc0214989c0 148 #include "physac.h" // this includes the definitions
jstephens78 1:ebc0214989c0 149 ```
jstephens78 1:ebc0214989c0 150
jstephens78 1:ebc0214989c0 151 ## Future work
jstephens78 1:ebc0214989c0 152 Possible future improvements to this port:
jstephens78 1:ebc0214989c0 153
jstephens78 1:ebc0214989c0 154 1. Re-work allocation scheme so users can pre-allocate arrays of bodies and
jstephens78 1:ebc0214989c0 155 manifolds
jstephens78 1:ebc0214989c0 156
jstephens78 1:ebc0214989c0 157 2. Implement a partial collision reporting scheme.
jstephens78 1:ebc0214989c0 158
jstephens78 1:ebc0214989c0 159 Presently in `PhysicsStep()`, all bodies are iterated, a manifold is generated
jstephens78 1:ebc0214989c0 160 for every collision, and *then* manifolds are iteratively resolved. This is
jstephens78 1:ebc0214989c0 161 necessary for "correct" resolution of collisions in sequence with velocity,
jstephens78 1:ebc0214989c0 162 but this requires a lot of working memory.
jstephens78 1:ebc0214989c0 163
jstephens78 1:ebc0214989c0 164 A possible trade-off would be to iterate bodies only until a certain number
jstephens78 1:ebc0214989c0 165 of manifolds are generated, then updating those bodies. This would reduce the
jstephens78 1:ebc0214989c0 166 resource demands for large scenes, at the expense of accuracy.
jstephens78 1:ebc0214989c0 167
jstephens78 1:ebc0214989c0 168
jstephens78 0:e39efa4f4f58 169 # Physac
jstephens78 0:e39efa4f4f58 170
jstephens78 0:e39efa4f4f58 171 Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop to simluate physics.
jstephens78 0:e39efa4f4f58 172 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.
jstephens78 0:e39efa4f4f58 173
jstephens78 0:e39efa4f4f58 174 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.
jstephens78 0:e39efa4f4f58 175
jstephens78 0:e39efa4f4f58 176 Physac API
jstephens78 0:e39efa4f4f58 177 -----
jstephens78 0:e39efa4f4f58 178
jstephens78 0:e39efa4f4f58 179 The PhysicsBody struct contains all dynamics information and collision shape. The user should use the following structure components:
jstephens78 0:e39efa4f4f58 180 ```c
jstephens78 0:e39efa4f4f58 181 typedef struct *PhysicsBody {
jstephens78 0:e39efa4f4f58 182 unsigned int id;
jstephens78 0:e39efa4f4f58 183 bool enabled; // Enabled dynamics state (collisions are calculated anyway)
jstephens78 0:e39efa4f4f58 184 Vector2 position; // Physics body shape pivot
jstephens78 0:e39efa4f4f58 185 Vector2 velocity; // Current linear velocity applied to position
jstephens78 0:e39efa4f4f58 186 Vector2 force; // Current linear force (reset to 0 every step)
jstephens78 0:e39efa4f4f58 187 float angularVelocity; // Current angular velocity applied to orient
jstephens78 0:e39efa4f4f58 188 float torque; // Current angular force (reset to 0 every step)
jstephens78 0:e39efa4f4f58 189 float orient; // Rotation in radians
jstephens78 1:ebc0214989c0 190 float staticFriction; // Friction when the body has no movement (0 to 1)
jstephens78 0:e39efa4f4f58 191 float dynamicFriction; // Friction when the body has movement (0 to 1)
jstephens78 0:e39efa4f4f58 192 float restitution; // Restitution coefficient of the body (0 to 1)
jstephens78 0:e39efa4f4f58 193 bool useGravity; // Apply gravity force to dynamics
jstephens78 0:e39efa4f4f58 194 bool isGrounded; // Physics grounded on other body state
jstephens78 0:e39efa4f4f58 195 bool freezeOrient; // Physics rotation constraint
jstephens78 0:e39efa4f4f58 196 PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals)
jstephens78 0:e39efa4f4f58 197 } *PhysicsBody;
jstephens78 0:e39efa4f4f58 198 ```
jstephens78 1:ebc0214989c0 199
jstephens78 0:e39efa4f4f58 200 The header contains a few customizable define values. I set the values that gived me the best results.
jstephens78 0:e39efa4f4f58 201 ```c
jstephens78 0:e39efa4f4f58 202 #define PHYSAC_MAX_BODIES 64
jstephens78 0:e39efa4f4f58 203 #define PHYSAC_MAX_MANIFOLDS 4096
jstephens78 0:e39efa4f4f58 204 #define PHYSAC_MAX_VERTICES 24
jstephens78 0:e39efa4f4f58 205 #define PHYSAC_CIRCLE_VERTICES 24
jstephens78 0:e39efa4f4f58 206
jstephens78 0:e39efa4f4f58 207 #define PHYSAC_COLLISION_ITERATIONS 100
jstephens78 0:e39efa4f4f58 208 #define PHYSAC_PENETRATION_ALLOWANCE 0.05f
jstephens78 0:e39efa4f4f58 209 #define PHYSAC_PENETRATION_CORRECTION 0.4f
jstephens78 0:e39efa4f4f58 210 ```
jstephens78 0:e39efa4f4f58 211
jstephens78 0:e39efa4f4f58 212 Physac contains defines for memory management functions (malloc, free) to bring the user the opportunity to implement its own memory functions:
jstephens78 0:e39efa4f4f58 213
jstephens78 0:e39efa4f4f58 214 ```c
jstephens78 0:e39efa4f4f58 215 #define PHYSAC_MALLOC(size) malloc(size)
jstephens78 0:e39efa4f4f58 216 #define PHYSAC_FREE(ptr) free(ptr)
jstephens78 0:e39efa4f4f58 217 ```
jstephens78 0:e39efa4f4f58 218
jstephens78 0:e39efa4f4f58 219 The Physac API functions availables for the user are the following:
jstephens78 0:e39efa4f4f58 220
jstephens78 0:e39efa4f4f58 221 ```c
jstephens78 0:e39efa4f4f58 222 // Initializes physics values, pointers and creates physics loop thread
jstephens78 0:e39efa4f4f58 223 void InitPhysics(void);
jstephens78 0:e39efa4f4f58 224
jstephens78 0:e39efa4f4f58 225 // Returns true if physics thread is currently enabled
jstephens78 0:e39efa4f4f58 226 bool IsPhysicsEnabled(void);
jstephens78 0:e39efa4f4f58 227
jstephens78 0:e39efa4f4f58 228 // Sets physics global gravity force
jstephens78 0:e39efa4f4f58 229 void SetPhysicsGravity(float x, float y);
jstephens78 0:e39efa4f4f58 230
jstephens78 0:e39efa4f4f58 231 // Creates a new circle physics body with generic parameters
jstephens78 0:e39efa4f4f58 232 PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density);
jstephens78 0:e39efa4f4f58 233
jstephens78 0:e39efa4f4f58 234 // Creates a new rectangle physics body with generic parameters
jstephens78 0:e39efa4f4f58 235 PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density);
jstephens78 0:e39efa4f4f58 236
jstephens78 0:e39efa4f4f58 237 // Creates a new polygon physics body with generic parameters
jstephens78 0:e39efa4f4f58 238 PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density);
jstephens78 0:e39efa4f4f58 239
jstephens78 0:e39efa4f4f58 240 // Adds a force to a physics body
jstephens78 0:e39efa4f4f58 241 void PhysicsAddForce(PhysicsBody body, Vector2 force);
jstephens78 0:e39efa4f4f58 242
jstephens78 0:e39efa4f4f58 243 // Adds a angular force to a physics body
jstephens78 0:e39efa4f4f58 244 void PhysicsAddTorque(PhysicsBody body, float amount);
jstephens78 0:e39efa4f4f58 245
jstephens78 0:e39efa4f4f58 246 // Shatters a polygon shape physics body to little physics bodies with explosion force
jstephens78 0:e39efa4f4f58 247 void PhysicsShatter(PhysicsBody body, Vector2 position, float force);
jstephens78 0:e39efa4f4f58 248
jstephens78 0:e39efa4f4f58 249 // Returns the current amount of created physics bodies
jstephens78 0:e39efa4f4f58 250 int GetPhysicsBodiesCount(void);
jstephens78 0:e39efa4f4f58 251
jstephens78 0:e39efa4f4f58 252 // Returns a physics body of the bodies pool at a specific index
jstephens78 0:e39efa4f4f58 253 PhysicsBody GetPhysicsBody(int index);
jstephens78 0:e39efa4f4f58 254
jstephens78 0:e39efa4f4f58 255 // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
jstephens78 0:e39efa4f4f58 256 int GetPhysicsShapeType(int index);
jstephens78 0:e39efa4f4f58 257
jstephens78 0:e39efa4f4f58 258 // Returns the amount of vertices of a physics body shape
jstephens78 0:e39efa4f4f58 259 int GetPhysicsShapeVerticesCount(int index);
jstephens78 0:e39efa4f4f58 260
jstephens78 0:e39efa4f4f58 261 // Returns transformed position of a body shape (body position + vertex transformed position)
jstephens78 0:e39efa4f4f58 262 Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex);
jstephens78 0:e39efa4f4f58 263
jstephens78 0:e39efa4f4f58 264 // Sets physics body shape transform based on radians parameter
jstephens78 0:e39efa4f4f58 265 void SetPhysicsBodyRotation(PhysicsBody body, float radians);
jstephens78 0:e39efa4f4f58 266
jstephens78 0:e39efa4f4f58 267 // Unitializes and destroy a physics body
jstephens78 0:e39efa4f4f58 268 void DestroyPhysicsBody(PhysicsBody body);
jstephens78 0:e39efa4f4f58 269
jstephens78 0:e39efa4f4f58 270 // Unitializes physics pointers and closes physics loop thread
jstephens78 0:e39efa4f4f58 271 void ClosePhysics(void);
jstephens78 0:e39efa4f4f58 272 ```
jstephens78 0:e39efa4f4f58 273 _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)._
jstephens78 0:e39efa4f4f58 274
jstephens78 0:e39efa4f4f58 275 Dependencies
jstephens78 0:e39efa4f4f58 276 -----
jstephens78 0:e39efa4f4f58 277
jstephens78 0:e39efa4f4f58 278 Physac uses the following C libraries for memory management, math operations and some debug features:
jstephens78 0:e39efa4f4f58 279
jstephens78 0:e39efa4f4f58 280 * stdlib.h - Memory allocation [malloc(), free(), srand(), rand()].
jstephens78 0:e39efa4f4f58 281 * stdio.h - Message logging (only if PHYSAC_DEBUG is defined) [printf()].
jstephens78 1:ebc0214989c0 282 * math.h - Math operations functions [cos(), sin(), fabs(), sqrtf()].