From c10c49e44f31ddac4b544ddf8c973774afd288c6 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 11 Jun 2016 18:35:46 +0200 Subject: Convert physac module from static steps to fixed time steps Old physics update system used a static number of steps to calculate physics (450 for desktop and 64 for android). It was too much and it was limited by target frame time... Now physics update runs in a secondary thread using a fixed delta time value to update steps. Collisions are perfectly detected and resolved and performance has been improved so much. --- src/physac.h | 601 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 296 insertions(+), 305 deletions(-) diff --git a/src/physac.h b/src/physac.h index 6a90dc29..dd2b1628 100644 --- a/src/physac.h +++ b/src/physac.h @@ -146,7 +146,7 @@ typedef struct PhysicBodyData { // Module Functions Declaration //---------------------------------------------------------------------------------- PHYSACDEF void InitPhysics(Vector2 gravity); // Initializes pointers array (just pointers, fixed size) -PHYSACDEF void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection +PHYSACDEF void UpdatePhysics(double deltaTime); // Update physic objects, calculating physic behaviours and collisions detection PHYSACDEF void ClosePhysics(); // Unitialize all physic objects and empty the objects pool PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale); // Create a new physic body dinamically, initialize it and add to pool @@ -182,7 +182,7 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform); // Defines and Macros //---------------------------------------------------------------------------------- #define MAX_PHYSIC_BODIES 256 // Maximum available physic bodies slots in bodies pool -#define PHYSICS_STEPS 64 // Physics update steps per frame for improved collision-detection +#define PHYSICS_TIMESTEP 0.016666 // Physics fixed time step (1/fps) #define PHYSICS_ACCURACY 0.0001f // Velocity subtract operations round filter (friction) #define PHYSICS_ERRORPERCENT 0.001f // Collision resolve position fix @@ -218,376 +218,367 @@ PHYSACDEF void InitPhysics(Vector2 gravity) } // Update physic objects, calculating physic behaviours and collisions detection -PHYSACDEF void UpdatePhysics() +PHYSACDEF void UpdatePhysics(double deltaTime) { - // Reset all physic objects is grounded state - for (int i = 0; i < physicBodiesCount; i++) physicBodies[i]->rigidbody.isGrounded = false; - - for (int steps = 0; steps < PHYSICS_STEPS; steps++) + for (int i = 0; i < physicBodiesCount; i++) { - for (int i = 0; i < physicBodiesCount; i++) + if (physicBodies[i]->enabled) { - if (physicBodies[i]->enabled) + // Update physic behaviour + if (physicBodies[i]->rigidbody.enabled) { - // Update physic behaviour - if (physicBodies[i]->rigidbody.enabled) + // Apply friction to acceleration in X axis + if (physicBodies[i]->rigidbody.acceleration.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x -= physicBodies[i]->rigidbody.friction*deltaTime; + else if (physicBodies[i]->rigidbody.acceleration.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x += physicBodies[i]->rigidbody.friction*deltaTime; + else physicBodies[i]->rigidbody.acceleration.x = 0.0f; + + // Apply friction to acceleration in Y axis + if (physicBodies[i]->rigidbody.acceleration.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y -= physicBodies[i]->rigidbody.friction*deltaTime; + else if (physicBodies[i]->rigidbody.acceleration.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y += physicBodies[i]->rigidbody.friction*deltaTime; + else physicBodies[i]->rigidbody.acceleration.y = 0.0f; + + // Apply friction to velocity in X axis + if (physicBodies[i]->rigidbody.velocity.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x -= physicBodies[i]->rigidbody.friction*deltaTime; + else if (physicBodies[i]->rigidbody.velocity.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.friction*deltaTime; + else physicBodies[i]->rigidbody.velocity.x = 0.0f; + + // Apply friction to velocity in Y axis + if (physicBodies[i]->rigidbody.velocity.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y -= physicBodies[i]->rigidbody.friction*deltaTime; + else if (physicBodies[i]->rigidbody.velocity.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.friction*deltaTime; + else physicBodies[i]->rigidbody.velocity.y = 0.0f; + + // Apply gravity to velocity + if (physicBodies[i]->rigidbody.applyGravity) { - // Apply friction to acceleration in X axis - if (physicBodies[i]->rigidbody.acceleration.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x -= physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else if (physicBodies[i]->rigidbody.acceleration.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x += physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else physicBodies[i]->rigidbody.acceleration.x = 0.0f; - - // Apply friction to acceleration in Y axis - if (physicBodies[i]->rigidbody.acceleration.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y -= physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else if (physicBodies[i]->rigidbody.acceleration.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y += physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else physicBodies[i]->rigidbody.acceleration.y = 0.0f; - - // Apply friction to velocity in X axis - if (physicBodies[i]->rigidbody.velocity.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x -= physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else if (physicBodies[i]->rigidbody.velocity.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else physicBodies[i]->rigidbody.velocity.x = 0.0f; - - // Apply friction to velocity in Y axis - if (physicBodies[i]->rigidbody.velocity.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y -= physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else if (physicBodies[i]->rigidbody.velocity.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.friction/PHYSICS_STEPS; - else physicBodies[i]->rigidbody.velocity.y = 0.0f; - - // Apply gravity to velocity - if (physicBodies[i]->rigidbody.applyGravity) - { - physicBodies[i]->rigidbody.velocity.x += gravityForce.x/PHYSICS_STEPS; - physicBodies[i]->rigidbody.velocity.y += gravityForce.y/PHYSICS_STEPS; - } - - // Apply acceleration to velocity - physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.acceleration.x/PHYSICS_STEPS; - physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.acceleration.y/PHYSICS_STEPS; - - // Apply velocity to position - physicBodies[i]->transform.position.x += physicBodies[i]->rigidbody.velocity.x/PHYSICS_STEPS; - physicBodies[i]->transform.position.y -= physicBodies[i]->rigidbody.velocity.y/PHYSICS_STEPS; + physicBodies[i]->rigidbody.velocity.x += gravityForce.x*deltaTime; + physicBodies[i]->rigidbody.velocity.y += gravityForce.y*deltaTime; } - // Update collision detection - if (physicBodies[i]->collider.enabled) + // Apply acceleration to velocity + physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.acceleration.x*deltaTime; + physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.acceleration.y*deltaTime; + + // Apply velocity to position + physicBodies[i]->transform.position.x += physicBodies[i]->rigidbody.velocity.x*deltaTime; + physicBodies[i]->transform.position.y -= physicBodies[i]->rigidbody.velocity.y*deltaTime; + } + + // Update collision detection + if (physicBodies[i]->collider.enabled) + { + // Update collider bounds + physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform); + + // Check collision with other colliders + for (int k = 0; k < physicBodiesCount; k++) { - // Update collider bounds - physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform); - - // Check collision with other colliders - for (int k = 0; k < physicBodiesCount; k++) + if (physicBodies[k]->collider.enabled && i != k) { - if (physicBodies[k]->collider.enabled && i != k) + // Resolve physic collision + // NOTE: collision resolve is generic for all directions and conditions (no axis separated cases behaviours) + // and it is separated in rigidbody attributes resolve (velocity changes by impulse) and position correction (position overlap) + + // 1. Calculate collision normal + // ------------------------------------------------------------------------------------------------------------------------------------- + + // Define collision contact normal, direction and penetration depth + Vector2 contactNormal = { 0.0f, 0.0f }; + Vector2 direction = { 0.0f, 0.0f }; + float penetrationDepth = 0.0f; + + switch (physicBodies[i]->collider.type) { - // Resolve physic collision - // NOTE: collision resolve is generic for all directions and conditions (no axis separated cases behaviours) - // and it is separated in rigidbody attributes resolve (velocity changes by impulse) and position correction (position overlap) - - // 1. Calculate collision normal - // ------------------------------------------------------------------------------------------------------------------------------------- - - // Define collision contact normal, direction and penetration depth - Vector2 contactNormal = { 0.0f, 0.0f }; - Vector2 direction = { 0.0f, 0.0f }; - float penetrationDepth = 0.0f; - - switch (physicBodies[i]->collider.type) + case COLLIDER_RECTANGLE: { - case COLLIDER_RECTANGLE: + switch (physicBodies[k]->collider.type) { - switch (physicBodies[k]->collider.type) + case COLLIDER_RECTANGLE: { - case COLLIDER_RECTANGLE: + // Check if colliders are overlapped + if (CheckCollisionRecs(physicBodies[i]->collider.bounds, physicBodies[k]->collider.bounds)) { - // Check if colliders are overlapped - if (CheckCollisionRecs(physicBodies[i]->collider.bounds, physicBodies[k]->collider.bounds)) + // Calculate direction vector from i to k + direction.x = (physicBodies[k]->transform.position.x + physicBodies[k]->transform.scale.x/2) - (physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2); + direction.y = (physicBodies[k]->transform.position.y + physicBodies[k]->transform.scale.y/2) - (physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2); + + // Define overlapping and penetration attributes + Vector2 overlap; + + // Calculate overlap on X axis + overlap.x = (physicBodies[i]->transform.scale.x + physicBodies[k]->transform.scale.x)/2 - abs(direction.x); + + // SAT test on X axis + if (overlap.x > 0.0f) { - // Calculate direction vector from i to k - direction.x = (physicBodies[k]->transform.position.x + physicBodies[k]->transform.scale.x/2) - (physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2); - direction.y = (physicBodies[k]->transform.position.y + physicBodies[k]->transform.scale.y/2) - (physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2); - - // Define overlapping and penetration attributes - Vector2 overlap; - - // Calculate overlap on X axis - overlap.x = (physicBodies[i]->transform.scale.x + physicBodies[k]->transform.scale.x)/2 - abs(direction.x); + // Calculate overlap on Y axis + overlap.y = (physicBodies[i]->transform.scale.y + physicBodies[k]->transform.scale.y)/2 - abs(direction.y); - // SAT test on X axis - if (overlap.x > 0.0f) + // SAT test on Y axis + if (overlap.y > 0.0f) { - // Calculate overlap on Y axis - overlap.y = (physicBodies[i]->transform.scale.y + physicBodies[k]->transform.scale.y)/2 - abs(direction.y); - - // SAT test on Y axis - if (overlap.y > 0.0f) + // Find out which axis is axis of least penetration + if (overlap.y > overlap.x) + { + // Point towards k knowing that direction points from i to k + if (direction.x < 0.0f) contactNormal = (Vector2){ -1.0f, 0.0f }; + else contactNormal = (Vector2){ 1.0f, 0.0f }; + + // Update penetration depth for position correction + penetrationDepth = overlap.x; + } + else { - // Find out which axis is axis of least penetration - if (overlap.y > overlap.x) - { - // Point towards k knowing that direction points from i to k - if (direction.x < 0.0f) contactNormal = (Vector2){ -1.0f, 0.0f }; - else contactNormal = (Vector2){ 1.0f, 0.0f }; - - // Update penetration depth for position correction - penetrationDepth = overlap.x; - } - else - { - // Point towards k knowing that direction points from i to k - if (direction.y < 0.0f) contactNormal = (Vector2){ 0.0f, 1.0f }; - else contactNormal = (Vector2){ 0.0f, -1.0f }; - - // Update penetration depth for position correction - penetrationDepth = overlap.y; - } + // Point towards k knowing that direction points from i to k + if (direction.y < 0.0f) contactNormal = (Vector2){ 0.0f, 1.0f }; + else contactNormal = (Vector2){ 0.0f, -1.0f }; + + // Update penetration depth for position correction + penetrationDepth = overlap.y; } } } - } break; - case COLLIDER_CIRCLE: + } + } break; + case COLLIDER_CIRCLE: + { + if (CheckCollisionCircleRec(physicBodies[k]->transform.position, physicBodies[k]->collider.radius, physicBodies[i]->collider.bounds)) { - if (CheckCollisionCircleRec(physicBodies[k]->transform.position, physicBodies[k]->collider.radius, physicBodies[i]->collider.bounds)) + // Calculate direction vector between circles + direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2; + direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2; + + // Calculate closest point on rectangle to circle + Vector2 closestPoint = { 0.0f, 0.0f }; + if (direction.x > 0.0f) closestPoint.x = physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width; + else closestPoint.x = physicBodies[i]->collider.bounds.x; + + if (direction.y > 0.0f) closestPoint.y = physicBodies[i]->collider.bounds.y + physicBodies[i]->collider.bounds.height; + else closestPoint.y = physicBodies[i]->collider.bounds.y; + + // Check if the closest point is inside the circle + if (CheckCollisionPointCircle(closestPoint, physicBodies[k]->transform.position, physicBodies[k]->collider.radius)) { - // Calculate direction vector between circles - direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2; - direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2; - - // Calculate closest point on rectangle to circle - Vector2 closestPoint = { 0.0f, 0.0f }; - if (direction.x > 0.0f) closestPoint.x = physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width; - else closestPoint.x = physicBodies[i]->collider.bounds.x; + // Recalculate direction based on closest point position + direction.x = physicBodies[k]->transform.position.x - closestPoint.x; + direction.y = physicBodies[k]->transform.position.y - closestPoint.y; + float distance = Vector2Length(direction); - if (direction.y > 0.0f) closestPoint.y = physicBodies[i]->collider.bounds.y + physicBodies[i]->collider.bounds.height; - else closestPoint.y = physicBodies[i]->collider.bounds.y; + // Calculate final contact normal + contactNormal.x = direction.x/distance; + contactNormal.y = -direction.y/distance; - // Check if the closest point is inside the circle - if (CheckCollisionPointCircle(closestPoint, physicBodies[k]->transform.position, physicBodies[k]->collider.radius)) + // Calculate penetration depth + penetrationDepth = physicBodies[k]->collider.radius - distance; + } + else + { + if (abs(direction.y) < abs(direction.x)) { - // Recalculate direction based on closest point position - direction.x = physicBodies[k]->transform.position.x - closestPoint.x; - direction.y = physicBodies[k]->transform.position.y - closestPoint.y; - float distance = Vector2Length(direction); - // Calculate final contact normal - contactNormal.x = direction.x/distance; - contactNormal.y = -direction.y/distance; - - // Calculate penetration depth - penetrationDepth = physicBodies[k]->collider.radius - distance; + if (direction.y > 0.0f) + { + contactNormal = (Vector2){ 0.0f, -1.0f }; + penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y - physicBodies[k]->collider.radius); + } + else + { + contactNormal = (Vector2){ 0.0f, 1.0f }; + penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y + physicBodies[k]->collider.radius); + } } else { - if (abs(direction.y) < abs(direction.x)) + // Calculate final contact normal + if (direction.x > 0.0f) { - // Calculate final contact normal - if (direction.y > 0.0f) - { - contactNormal = (Vector2){ 0.0f, -1.0f }; - penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y - physicBodies[k]->collider.radius); - } - else - { - contactNormal = (Vector2){ 0.0f, 1.0f }; - penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y + physicBodies[k]->collider.radius); - } + contactNormal = (Vector2){ 1.0f, 0.0f }; + penetrationDepth = fabs(physicBodies[k]->transform.position.x + physicBodies[k]->collider.radius - physicBodies[i]->collider.bounds.x); } - else + else { - // Calculate final contact normal - if (direction.x > 0.0f) - { - contactNormal = (Vector2){ 1.0f, 0.0f }; - penetrationDepth = fabs(physicBodies[k]->transform.position.x + physicBodies[k]->collider.radius - physicBodies[i]->collider.bounds.x); - } - else - { - contactNormal = (Vector2){ -1.0f, 0.0f }; - penetrationDepth = fabs(physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width - physicBodies[k]->transform.position.x - physicBodies[k]->collider.radius); - } + contactNormal = (Vector2){ -1.0f, 0.0f }; + penetrationDepth = fabs(physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width - physicBodies[k]->transform.position.x - physicBodies[k]->collider.radius); } } } - } break; - } - } break; - case COLLIDER_CIRCLE: + } + } break; + } + } break; + case COLLIDER_CIRCLE: + { + switch (physicBodies[k]->collider.type) { - switch (physicBodies[k]->collider.type) + case COLLIDER_RECTANGLE: { - case COLLIDER_RECTANGLE: + if (CheckCollisionCircleRec(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->collider.bounds)) { - if (CheckCollisionCircleRec(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->collider.bounds)) + // Calculate direction vector between circles + direction.x = physicBodies[k]->transform.position.x + physicBodies[i]->transform.scale.x/2 - physicBodies[i]->transform.position.x; + direction.y = physicBodies[k]->transform.position.y + physicBodies[i]->transform.scale.y/2 - physicBodies[i]->transform.position.y; + + // Calculate closest point on rectangle to circle + Vector2 closestPoint = { 0.0f, 0.0f }; + if (direction.x > 0.0f) closestPoint.x = physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width; + else closestPoint.x = physicBodies[k]->collider.bounds.x; + + if (direction.y > 0.0f) closestPoint.y = physicBodies[k]->collider.bounds.y + physicBodies[k]->collider.bounds.height; + else closestPoint.y = physicBodies[k]->collider.bounds.y; + + // Check if the closest point is inside the circle + if (CheckCollisionPointCircle(closestPoint, physicBodies[i]->transform.position, physicBodies[i]->collider.radius)) { - // Calculate direction vector between circles - direction.x = physicBodies[k]->transform.position.x + physicBodies[i]->transform.scale.x/2 - physicBodies[i]->transform.position.x; - direction.y = physicBodies[k]->transform.position.y + physicBodies[i]->transform.scale.y/2 - physicBodies[i]->transform.position.y; - - // Calculate closest point on rectangle to circle - Vector2 closestPoint = { 0.0f, 0.0f }; - if (direction.x > 0.0f) closestPoint.x = physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width; - else closestPoint.x = physicBodies[k]->collider.bounds.x; + // Recalculate direction based on closest point position + direction.x = physicBodies[i]->transform.position.x - closestPoint.x; + direction.y = physicBodies[i]->transform.position.y - closestPoint.y; + float distance = Vector2Length(direction); - if (direction.y > 0.0f) closestPoint.y = physicBodies[k]->collider.bounds.y + physicBodies[k]->collider.bounds.height; - else closestPoint.y = physicBodies[k]->collider.bounds.y; + // Calculate final contact normal + contactNormal.x = direction.x/distance; + contactNormal.y = -direction.y/distance; - // Check if the closest point is inside the circle - if (CheckCollisionPointCircle(closestPoint, physicBodies[i]->transform.position, physicBodies[i]->collider.radius)) + // Calculate penetration depth + penetrationDepth = physicBodies[k]->collider.radius - distance; + } + else + { + if (abs(direction.y) < abs(direction.x)) { - // Recalculate direction based on closest point position - direction.x = physicBodies[i]->transform.position.x - closestPoint.x; - direction.y = physicBodies[i]->transform.position.y - closestPoint.y; - float distance = Vector2Length(direction); - // Calculate final contact normal - contactNormal.x = direction.x/distance; - contactNormal.y = -direction.y/distance; - - // Calculate penetration depth - penetrationDepth = physicBodies[k]->collider.radius - distance; + if (direction.y > 0.0f) + { + contactNormal = (Vector2){ 0.0f, -1.0f }; + penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y - physicBodies[i]->collider.radius); + } + else + { + contactNormal = (Vector2){ 0.0f, 1.0f }; + penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y + physicBodies[i]->collider.radius); + } } else { - if (abs(direction.y) < abs(direction.x)) + // Calculate final contact normal and penetration depth + if (direction.x > 0.0f) { - // Calculate final contact normal - if (direction.y > 0.0f) - { - contactNormal = (Vector2){ 0.0f, -1.0f }; - penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y - physicBodies[i]->collider.radius); - } - else - { - contactNormal = (Vector2){ 0.0f, 1.0f }; - penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y + physicBodies[i]->collider.radius); - } + contactNormal = (Vector2){ 1.0f, 0.0f }; + penetrationDepth = fabs(physicBodies[i]->transform.position.x + physicBodies[i]->collider.radius - physicBodies[k]->collider.bounds.x); } - else + else { - // Calculate final contact normal and penetration depth - if (direction.x > 0.0f) - { - contactNormal = (Vector2){ 1.0f, 0.0f }; - penetrationDepth = fabs(physicBodies[i]->transform.position.x + physicBodies[i]->collider.radius - physicBodies[k]->collider.bounds.x); - } - else - { - contactNormal = (Vector2){ -1.0f, 0.0f }; - penetrationDepth = fabs(physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width - physicBodies[i]->transform.position.x - physicBodies[i]->collider.radius); - } + contactNormal = (Vector2){ -1.0f, 0.0f }; + penetrationDepth = fabs(physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width - physicBodies[i]->transform.position.x - physicBodies[i]->collider.radius); } } } - } break; - case COLLIDER_CIRCLE: + } + } break; + case COLLIDER_CIRCLE: + { + // Check if colliders are overlapped + if (CheckCollisionCircles(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->transform.position, physicBodies[k]->collider.radius)) { - // Check if colliders are overlapped - if (CheckCollisionCircles(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->transform.position, physicBodies[k]->collider.radius)) - { - // Calculate direction vector between circles - direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x; - direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y; - - // Calculate distance between circles - float distance = Vector2Length(direction); - - // Check if circles are not completely overlapped - if (distance != 0.0f) - { - // Calculate contact normal direction (Y axis needs to be flipped) - contactNormal.x = direction.x/distance; - contactNormal.y = -direction.y/distance; - } - else contactNormal = (Vector2){ 1.0f, 0.0f }; // Choose random (but consistent) values + // Calculate direction vector between circles + direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x; + direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y; + + // Calculate distance between circles + float distance = Vector2Length(direction); + + // Check if circles are not completely overlapped + if (distance != 0.0f) + { + // Calculate contact normal direction (Y axis needs to be flipped) + contactNormal.x = direction.x/distance; + contactNormal.y = -direction.y/distance; } - } break; - default: break; - } - } break; - default: break; - } + else contactNormal = (Vector2){ 1.0f, 0.0f }; // Choose random (but consistent) values + } + } break; + default: break; + } + } break; + default: break; + } + + // Update rigidbody grounded state + if (physicBodies[i]->rigidbody.enabled) physicBodies[i]->rigidbody.isGrounded = (contactNormal.y < 0.0f); + + // 2. Calculate collision impulse + // ------------------------------------------------------------------------------------------------------------------------------------- + + // Calculate relative velocity + Vector2 relVelocity = { 0.0f, 0.0f }; + relVelocity.x = physicBodies[k]->rigidbody.velocity.x - physicBodies[i]->rigidbody.velocity.x; + relVelocity.y = physicBodies[k]->rigidbody.velocity.y - physicBodies[i]->rigidbody.velocity.y; + + // Calculate relative velocity in terms of the normal direction + float velAlongNormal = Vector2DotProduct(relVelocity, contactNormal); + + // Dot not resolve if velocities are separating + if (velAlongNormal <= 0.0f) + { + // Calculate minimum bounciness value from both objects + float e = fminf(physicBodies[i]->rigidbody.bounciness, physicBodies[k]->rigidbody.bounciness); - // Update rigidbody grounded state - if (physicBodies[i]->rigidbody.enabled) - { - if (contactNormal.y < 0.0f) physicBodies[i]->rigidbody.isGrounded = true; - } + // Calculate impulse scalar value + float j = -(1.0f + e)*velAlongNormal; + j /= 1.0f/physicBodies[i]->rigidbody.mass + 1.0f/physicBodies[k]->rigidbody.mass; - // 2. Calculate collision impulse - // ------------------------------------------------------------------------------------------------------------------------------------- + // Calculate final impulse vector + Vector2 impulse = { j*contactNormal.x, j*contactNormal.y }; - // Calculate relative velocity - Vector2 relVelocity = { 0.0f, 0.0f }; - relVelocity.x = physicBodies[k]->rigidbody.velocity.x - physicBodies[i]->rigidbody.velocity.x; - relVelocity.y = physicBodies[k]->rigidbody.velocity.y - physicBodies[i]->rigidbody.velocity.y; - - // Calculate relative velocity in terms of the normal direction - float velAlongNormal = Vector2DotProduct(relVelocity, contactNormal); - - // Dot not resolve if velocities are separating - if (velAlongNormal <= 0.0f) + // Calculate collision mass ration + float massSum = physicBodies[i]->rigidbody.mass + physicBodies[k]->rigidbody.mass; + float ratio = 0.0f; + + // Apply impulse to current rigidbodies velocities if they are enabled + if (physicBodies[i]->rigidbody.enabled) { - // Calculate minimum bounciness value from both objects - float e = fminf(physicBodies[i]->rigidbody.bounciness, physicBodies[k]->rigidbody.bounciness); - - // Calculate impulse scalar value - float j = -(1.0f + e)*velAlongNormal; - j /= 1.0f/physicBodies[i]->rigidbody.mass + 1.0f/physicBodies[k]->rigidbody.mass; + // Calculate inverted mass ration + ratio = physicBodies[i]->rigidbody.mass/massSum; - // Calculate final impulse vector - Vector2 impulse = { j*contactNormal.x, j*contactNormal.y }; + // Apply impulse direction to velocity + physicBodies[i]->rigidbody.velocity.x -= impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); + physicBodies[i]->rigidbody.velocity.y -= impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); + } + + if (physicBodies[k]->rigidbody.enabled) + { + // Calculate inverted mass ration + ratio = physicBodies[k]->rigidbody.mass/massSum; - // Calculate collision mass ration - float massSum = physicBodies[i]->rigidbody.mass + physicBodies[k]->rigidbody.mass; - float ratio = 0.0f; + // Apply impulse direction to velocity + physicBodies[k]->rigidbody.velocity.x += impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); + physicBodies[k]->rigidbody.velocity.y += impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); + } + + // 3. Correct colliders overlaping (transform position) + // --------------------------------------------------------------------------------------------------------------------------------- + + // Calculate transform position penetration correction + Vector2 posCorrection; + posCorrection.x = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.x; + posCorrection.y = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.y; + + // Fix transform positions + if (physicBodies[i]->rigidbody.enabled) + { + // Fix physic objects transform position + physicBodies[i]->transform.position.x -= 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.x; + physicBodies[i]->transform.position.y += 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.y; - // Apply impulse to current rigidbodies velocities if they are enabled - if (physicBodies[i]->rigidbody.enabled) - { - // Calculate inverted mass ration - ratio = physicBodies[i]->rigidbody.mass/massSum; - - // Apply impulse direction to velocity - physicBodies[i]->rigidbody.velocity.x -= impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); - physicBodies[i]->rigidbody.velocity.y -= impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); - } + // Update collider bounds + physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform); - if (physicBodies[k]->rigidbody.enabled) + if (physicBodies[k]->rigidbody.enabled) { - // Calculate inverted mass ration - ratio = physicBodies[k]->rigidbody.mass/massSum; - - // Apply impulse direction to velocity - physicBodies[k]->rigidbody.velocity.x += impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); - physicBodies[k]->rigidbody.velocity.y += impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness); - } - - // 3. Correct colliders overlaping (transform position) - // --------------------------------------------------------------------------------------------------------------------------------- - - // Calculate transform position penetration correction - Vector2 posCorrection; - posCorrection.x = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.x; - posCorrection.y = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.y; - - // Fix transform positions - if (physicBodies[i]->rigidbody.enabled) - { // Fix physic objects transform position - physicBodies[i]->transform.position.x -= 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.x; - physicBodies[i]->transform.position.y += 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.y; + physicBodies[k]->transform.position.x += 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.x; + physicBodies[k]->transform.position.y -= 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.y; // Update collider bounds - physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform); - - if (physicBodies[k]->rigidbody.enabled) - { - // Fix physic objects transform position - physicBodies[k]->transform.position.x += 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.x; - physicBodies[k]->transform.position.y -= 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.y; - - // Update collider bounds - physicBodies[k]->collider.bounds = TransformToRectangle(physicBodies[k]->transform); - } + physicBodies[k]->collider.bounds = TransformToRectangle(physicBodies[k]->transform); } } } -- cgit v1.2.3 From 4c43a407888d516b38191b5df76e373dae6ec58e Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 11 Jun 2016 19:11:30 +0200 Subject: Update physac examples with fixed timestep method --- examples/physics_basic_rigidbody.c | 32 ++++++++++++++++++++++++++++++-- examples/physics_forces.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 8870c55b..24570426 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -13,10 +13,13 @@ #define PHYSAC_IMPLEMENTATION #include "physac.h" +#include #define MOVE_VELOCITY 5 #define JUMP_VELOCITY 30 +void* PhysicsThread(void *arg); + int main() { // Initialization @@ -53,6 +56,10 @@ int main() // Create pplatform physic object PhysicBody platform = CreatePhysicBody((Vector2){ screenWidth/2, screenHeight*0.7f }, 0.0f, (Vector2){ screenWidth*0.25f, 20 }); + // Create physics thread + pthread_t tid; + pthread_create(&tid, NULL, &PhysicsThread, NULL); + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -61,10 +68,9 @@ int main() { // Update //---------------------------------------------------------------------------------- - UpdatePhysics(); // Update all created physic objects // Check rectangle movement inputs - if (IsKeyDown('W') && rectangle->rigidbody.isGrounded) rectangle->rigidbody.velocity.y = JUMP_VELOCITY; + if (IsKeyPressed('W')) rectangle->rigidbody.velocity.y = JUMP_VELOCITY; if (IsKeyDown('A')) rectangle->rigidbody.velocity.x = -MOVE_VELOCITY; else if (IsKeyDown('D')) rectangle->rigidbody.velocity.x = MOVE_VELOCITY; @@ -117,10 +123,32 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- + pthread_cancel(tid); // Destroy physics thread + ClosePhysics(); // Unitialize physics (including all loaded objects) CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; +} + +void* PhysicsThread(void *arg) +{ + // Initialize time variables + double currentTime = GetTime(); + double previousTime = currentTime; + + // Physics update loop + while (!WindowShouldClose()) + { + currentTime = GetTime(); + double deltaTime = (double)(currentTime - previousTime); + previousTime = currentTime; + + // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) + UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); + } + + return NULL; } \ No newline at end of file diff --git a/examples/physics_forces.c b/examples/physics_forces.c index 3e90a21d..397c2331 100644 --- a/examples/physics_forces.c +++ b/examples/physics_forces.c @@ -13,12 +13,15 @@ #define PHYSAC_IMPLEMENTATION #include "physac.h" +#include #define FORCE_AMOUNT 5.0f #define FORCE_RADIUS 150 #define LINE_LENGTH 75 #define TRIANGLE_LENGTH 12 +void* PhysicsThread(void *arg); + int main() { // Initialization @@ -61,6 +64,10 @@ int main() PhysicBody topWall = CreatePhysicBody((Vector2){ screenWidth/2, -25 }, 0.0f, (Vector2){ screenWidth, 50 }); PhysicBody bottomWall = CreatePhysicBody((Vector2){ screenWidth/2, screenHeight + 25 }, 0.0f, (Vector2){ screenWidth, 50 }); + // Create physics thread + pthread_t tid; + pthread_create(&tid, NULL, &PhysicsThread, NULL); + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -69,7 +76,6 @@ int main() { // Update //---------------------------------------------------------------------------------- - UpdatePhysics(); // Update all created physic objects // Update mouse position value mousePosition = GetMousePosition(); @@ -174,10 +180,32 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- + pthread_cancel(tid); // Destroy physics thread + ClosePhysics(); // Unitialize physics module CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; +} + +void* PhysicsThread(void *arg) +{ + // Initialize time variables + double currentTime = GetTime(); + double previousTime = currentTime; + + // Physics update loop + while (!WindowShouldClose()) + { + currentTime = GetTime(); + double deltaTime = (double)(currentTime - previousTime); + previousTime = currentTime; + + // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) + UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); + } + + return NULL; } \ No newline at end of file -- cgit v1.2.3 From 7999bbafa8c5333b69edb7881f64986f3e3e3d45 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 11 Jun 2016 19:14:25 +0200 Subject: Make GetTime() public to be used externally --- src/core.c | 3 +-- src/raylib.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index 0c1e0454..00b2e82f 100644 --- a/src/core.c +++ b/src/core.c @@ -290,7 +290,6 @@ static void InitDisplay(int width, int height); // Initialize display de static void InitGraphics(void); // Initialize OpenGL graphics static void SetupFramebufferSize(int displayWidth, int displayHeight); static void InitTimer(void); // Initialize timer -static double GetTime(void); // Returns time since InitTimer() was run static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events @@ -2039,7 +2038,7 @@ static void InitTimer(void) } // Get current time measure (in seconds) since InitTimer() -static double GetTime(void) +double GetTime(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetTime(); diff --git a/src/raylib.h b/src/raylib.h index bfcb9bf5..bfed533b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -582,6 +582,7 @@ Matrix GetCameraMatrix(Camera camera); // Returns camera tr void SetTargetFPS(int fps); // Set target FPS (maximum) float GetFPS(void); // Returns current FPS float GetFrameTime(void); // Returns time in seconds for one frame +double GetTime(void); // Returns time since InitTimer() was run internally Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value int GetHexValue(Color color); // Returns hexadecimal value for a Color -- cgit v1.2.3 From 16609d6702f60ae714741888837a80756628400c Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sun, 12 Jun 2016 22:04:51 +0200 Subject: Revert "Make GetTime() public to be used externally" This reverts commit 7999bbafa8c5333b69edb7881f64986f3e3e3d45. --- src/core.c | 3 ++- src/raylib.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index 899f0162..122453e3 100644 --- a/src/core.c +++ b/src/core.c @@ -290,6 +290,7 @@ static void InitDisplay(int width, int height); // Initialize display de static void InitGraphics(void); // Initialize OpenGL graphics static void SetupFramebufferSize(int displayWidth, int displayHeight); static void InitTimer(void); // Initialize timer +static double GetTime(void); // Returns time since InitTimer() was run static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events @@ -2030,7 +2031,7 @@ static void InitTimer(void) } // Get current time measure (in seconds) since InitTimer() -double GetTime(void) +static double GetTime(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetTime(); diff --git a/src/raylib.h b/src/raylib.h index bfed533b..bfcb9bf5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -582,7 +582,6 @@ Matrix GetCameraMatrix(Camera camera); // Returns camera tr void SetTargetFPS(int fps); // Set target FPS (maximum) float GetFPS(void); // Returns current FPS float GetFrameTime(void); // Returns time in seconds for one frame -double GetTime(void); // Returns time since InitTimer() was run internally Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value int GetHexValue(Color color); // Returns hexadecimal value for a Color -- cgit v1.2.3 From 5625c11e9982838498722c33d832289f3e79fa6e Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sun, 12 Jun 2016 22:07:06 +0200 Subject: Added internal hi-resolution timer to physac... ... and now physac thread creation is done in InitPhysics() and it is destroyed in ClosePhysics(). User just need to call these functions to use physac module. --- src/physac.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/physac.h b/src/physac.h index dd2b1628..ea8801c3 100644 --- a/src/physac.h +++ b/src/physac.h @@ -177,6 +177,18 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform); #endif #include // Required for: cos(), sin(), abs(), fminf() +#include // Required for typedef unsigned long long int uint64_t, used by hi-res timer +#include // Required for: pthread_create() +#include "utils.h" // Required for: TraceLog() + +#if defined(PLATFORM_DESKTOP) + // Functions required to query time on Windows + int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); + int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); +#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + #include // Required for: timespec + #include // Required for: clock_gettime() +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -195,6 +207,9 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform); //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- +static bool physicsThreadEnabled = false; // Physics calculations thread exit control +static uint64_t baseTime; // Base time measure for hi-res timer +static double currentTime, previousTime; // Used to track timmings static PhysicBody physicBodies[MAX_PHYSIC_BODIES]; // Physic bodies pool static int physicBodiesCount; // Counts current enabled physic bodies static Vector2 gravityForce; // Gravity force @@ -202,6 +217,9 @@ static Vector2 gravityForce; // Gravity f //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static void* PhysicsThread(void *arg); // Physics calculations thread function +static void InitTimer(void); // Initialize hi-resolution timer +static double GetCurrentTime(void); // Time measure returned are microseconds static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2 static float Vector2Length(Vector2 v); // Returns the length of a Vector2 @@ -215,6 +233,10 @@ PHYSACDEF void InitPhysics(Vector2 gravity) // Initialize physics variables physicBodiesCount = 0; gravityForce = gravity; + + // Create physics thread + pthread_t tid; + pthread_create(&tid, NULL, &PhysicsThread, NULL); } // Update physic objects, calculating physic behaviours and collisions detection @@ -592,6 +614,9 @@ PHYSACDEF void UpdatePhysics(double deltaTime) // Unitialize all physic objects and empty the objects pool PHYSACDEF void ClosePhysics() { + // Exit physics thread loop + physicsThreadEnabled = false; + // Free all dynamic memory allocations for (int i = 0; i < physicBodiesCount; i++) PHYSAC_FREE(physicBodies[i]); @@ -710,6 +735,65 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform) //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +// Physics calculations thread function +static void* PhysicsThread(void *arg) +{ + // Initialize thread loop state + physicsThreadEnabled = true; + + // Initialize hi-resolution timer + InitTimer(); + + // Physics update loop + while (physicsThreadEnabled) + { + currentTime = GetCurrentTime(); + double deltaTime = (double)(currentTime - previousTime); + previousTime = currentTime; + + // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) + UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); + } + + return NULL; +} + +// Initialize hi-resolution timer +static void InitTimer(void) +{ +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec now; + + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success + { + baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; + } + else TraceLog(WARNING, "No hi-resolution timer available"); +#endif + + previousTime = GetCurrentTime(); // Get time as double +} + +// Time measure returned are microseconds +static double GetCurrentTime(void) +{ +#if defined(PLATFORM_DESKTOP) + unsigned long long int clockFrequency, currentTime; + + QueryPerformanceFrequency(&clockFrequency); + QueryPerformanceCounter(¤tTime); + + return (double)(currentTime/clockFrequency); +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t time = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; + + return (double)(time - baseTime)*1e-9; +#endif +} // Returns the dot product of two Vector2 static float Vector2DotProduct(Vector2 v1, Vector2 v2) -- cgit v1.2.3 From 6a2bbae5216e66e5581d697998efe135ad826c50 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sun, 12 Jun 2016 22:07:36 +0200 Subject: Updated physics examples with new module changes --- examples/physics_basic_rigidbody.c | 32 +------------------------------- examples/physics_forces.c | 33 +-------------------------------- 2 files changed, 2 insertions(+), 63 deletions(-) diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 24570426..811ab982 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -13,12 +13,10 @@ #define PHYSAC_IMPLEMENTATION #include "physac.h" -#include #define MOVE_VELOCITY 5 #define JUMP_VELOCITY 30 -void* PhysicsThread(void *arg); int main() { @@ -28,7 +26,6 @@ int main() int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [physac] example - basic rigidbody"); - InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module // Debug variables @@ -56,10 +53,6 @@ int main() // Create pplatform physic object PhysicBody platform = CreatePhysicBody((Vector2){ screenWidth/2, screenHeight*0.7f }, 0.0f, (Vector2){ screenWidth*0.25f, 20 }); - // Create physics thread - pthread_t tid; - pthread_create(&tid, NULL, &PhysicsThread, NULL); - SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -122,33 +115,10 @@ int main() } // De-Initialization - //-------------------------------------------------------------------------------------- - pthread_cancel(tid); // Destroy physics thread - + //-------------------------------------------------------------------------------------- ClosePhysics(); // Unitialize physics (including all loaded objects) - CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; -} - -void* PhysicsThread(void *arg) -{ - // Initialize time variables - double currentTime = GetTime(); - double previousTime = currentTime; - - // Physics update loop - while (!WindowShouldClose()) - { - currentTime = GetTime(); - double deltaTime = (double)(currentTime - previousTime); - previousTime = currentTime; - - // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) - UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); - } - - return NULL; } \ No newline at end of file diff --git a/examples/physics_forces.c b/examples/physics_forces.c index 397c2331..28566753 100644 --- a/examples/physics_forces.c +++ b/examples/physics_forces.c @@ -13,15 +13,12 @@ #define PHYSAC_IMPLEMENTATION #include "physac.h" -#include #define FORCE_AMOUNT 5.0f #define FORCE_RADIUS 150 #define LINE_LENGTH 75 #define TRIANGLE_LENGTH 12 -void* PhysicsThread(void *arg); - int main() { // Initialization @@ -30,7 +27,6 @@ int main() int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [physac] example - forces"); - InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module // Global variables @@ -64,10 +60,6 @@ int main() PhysicBody topWall = CreatePhysicBody((Vector2){ screenWidth/2, -25 }, 0.0f, (Vector2){ screenWidth, 50 }); PhysicBody bottomWall = CreatePhysicBody((Vector2){ screenWidth/2, screenHeight + 25 }, 0.0f, (Vector2){ screenWidth, 50 }); - // Create physics thread - pthread_t tid; - pthread_create(&tid, NULL, &PhysicsThread, NULL); - SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -179,33 +171,10 @@ int main() } // De-Initialization - //-------------------------------------------------------------------------------------- - pthread_cancel(tid); // Destroy physics thread - + //-------------------------------------------------------------------------------------- ClosePhysics(); // Unitialize physics module - CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; -} - -void* PhysicsThread(void *arg) -{ - // Initialize time variables - double currentTime = GetTime(); - double previousTime = currentTime; - - // Physics update loop - while (!WindowShouldClose()) - { - currentTime = GetTime(); - double deltaTime = (double)(currentTime - previousTime); - previousTime = currentTime; - - // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) - UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); - } - - return NULL; } \ No newline at end of file -- cgit v1.2.3 From 54537e8f0b57df2f3f15d8e46309672f46e4775a Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:23:46 +0200 Subject: Fixed bug in delta time calculation... and added PHYSAC_NO_THREADS define. Improved physac example drawing frames per second in screen. --- examples/physics_basic_rigidbody.c | 2 ++ examples/physics_forces.c | 4 +++- src/physac.h | 26 ++++++++++++++++---------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 811ab982..5223f46a 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -110,6 +110,8 @@ int main() // Draw help message DrawText("Use WASD to move rectangle and ARROWS to move square", screenWidth/2 - MeasureText("Use WASD to move rectangle and ARROWS to move square", 20)/2, screenHeight*0.075f, 20, LIGHTGRAY); + DrawFPS(10, 10); + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/physics_forces.c b/examples/physics_forces.c index 28566753..87510552 100644 --- a/examples/physics_forces.c +++ b/examples/physics_forces.c @@ -164,7 +164,9 @@ int main() // Draw help messages DrawText("Use LEFT MOUSE BUTTON to apply a force", screenWidth/2 - MeasureText("Use LEFT MOUSE BUTTON to apply a force", 20)/2, screenHeight*0.075f, 20, LIGHTGRAY); - DrawText("Use R to reset objects position", screenWidth/2 - MeasureText("Use R to reset objects position", 20)/2, screenHeight*0.875f, 20, GRAY); + DrawText("Use R to reset objects position", screenWidth/2 - MeasureText("Use R to reset objects position", 20)/2, screenHeight*0.875f, 20, GRAY); + + DrawFPS(10, 10); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/physac.h b/src/physac.h index ea8801c3..5ce3970e 100644 --- a/src/physac.h +++ b/src/physac.h @@ -178,8 +178,10 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform); #include // Required for: cos(), sin(), abs(), fminf() #include // Required for typedef unsigned long long int uint64_t, used by hi-res timer -#include // Required for: pthread_create() -#include "utils.h" // Required for: TraceLog() + +#ifndef PHYSAC_NO_THREADS + #include // Required for: pthread_create() +#endif #if defined(PLATFORM_DESKTOP) // Functions required to query time on Windows @@ -234,9 +236,11 @@ PHYSACDEF void InitPhysics(Vector2 gravity) physicBodiesCount = 0; gravityForce = gravity; - // Create physics thread - pthread_t tid; - pthread_create(&tid, NULL, &PhysicsThread, NULL); + #ifndef PHYSAC_NO_THREADS // NOTE: if defined, user will need to create a thread for PhysicsThread function manually + // Create physics thread + pthread_t tid; + pthread_create(&tid, NULL, &PhysicsThread, NULL); + #endif } // Update physic objects, calculating physic behaviours and collisions detection @@ -768,7 +772,6 @@ static void InitTimer(void) { baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; } - else TraceLog(WARNING, "No hi-resolution timer available"); #endif previousTime = GetCurrentTime(); // Get time as double @@ -777,22 +780,25 @@ static void InitTimer(void) // Time measure returned are microseconds static double GetCurrentTime(void) { + double time; + #if defined(PLATFORM_DESKTOP) unsigned long long int clockFrequency, currentTime; QueryPerformanceFrequency(&clockFrequency); QueryPerformanceCounter(¤tTime); - - return (double)(currentTime/clockFrequency); + #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - uint64_t time = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; + uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; - return (double)(time - baseTime)*1e-9; + time = (double)(temp - baseTime)*1e-9; #endif + + return time; } // Returns the dot product of two Vector2 -- cgit v1.2.3 From 5a1cbb2842f6801f7a86086e87f4821fd0b09229 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:25:08 +0200 Subject: Fix current time value --- src/physac.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/physac.h b/src/physac.h index 5ce3970e..4f9b736f 100644 --- a/src/physac.h +++ b/src/physac.h @@ -788,6 +788,7 @@ static double GetCurrentTime(void) QueryPerformanceFrequency(&clockFrequency); QueryPerformanceCounter(¤tTime); + time = (double)((double)currentTime/(double)clockFrequency); #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) -- cgit v1.2.3 From 1a8fbe5cf0b982cf74434f1ba4654fced71a0450 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:31:48 +0200 Subject: Add pthread external library to source... and add instructions in physac examples to run it successful. --- .gitignore | 3 ++- examples/physics_basic_rigidbody.c | 4 ++++ examples/physics_forces.c | 5 +++++ src/external/pthread/pthreadGC2.dll | Bin 0 -> 119888 bytes 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/external/pthread/pthreadGC2.dll diff --git a/.gitignore b/.gitignore index b221a37b..cf9cdfe1 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,5 @@ src/libraylib.bc # external libraries DLLs !src/external/glfw3/lib/win32/glfw3.dll !src/external/openal_soft/lib/win32/OpenAL32.dll -!src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll \ No newline at end of file +!src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll +!src/external/pthread/pthreadGC2.dll \ No newline at end of file diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 5223f46a..b85f7543 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -5,6 +5,10 @@ * This example has been created using raylib 1.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * +* +* Compile example using: +* cmd /c IF NOT EXIST pthreadGC2.dll copy C:\raylib\raylib\src\external\pthread\pthreadGC2.dll $(CURRENT_DIRECTORY) /Y +* * Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/physics_forces.c b/examples/physics_forces.c index 87510552..7de85483 100644 --- a/examples/physics_forces.c +++ b/examples/physics_forces.c @@ -5,6 +5,11 @@ * This example has been created using raylib 1.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * +* NOTE: This example requires raylib module [rlgl] +* +* Compile example using: +* cmd /c IF NOT EXIST pthreadGC2.dll copy C:\raylib\raylib\src\external\pthread\pthreadGC2.dll $(CURRENT_DIRECTORY) /Y +* * Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/src/external/pthread/pthreadGC2.dll b/src/external/pthread/pthreadGC2.dll new file mode 100644 index 00000000..67b9289d Binary files /dev/null and b/src/external/pthread/pthreadGC2.dll differ -- cgit v1.2.3 From 4e84ded7ef3b165081e08a83d95bf54387a413ca Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:38:49 +0200 Subject: Fixed spacing and set UpdatePhysics() function as static... and remove static from PhysicsThread(). --- examples/physics_basic_rigidbody.c | 8 +- examples/physics_forces.c | 2 +- src/physac.h | 418 ++++++++++++++++++------------------- 3 files changed, 213 insertions(+), 215 deletions(-) diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index b85f7543..084bfb0e 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -21,7 +21,6 @@ #define MOVE_VELOCITY 5 #define JUMP_VELOCITY 30 - int main() { // Initialization @@ -30,7 +29,7 @@ int main() int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [physac] example - basic rigidbody"); - InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module + // InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module // Debug variables bool isDebug = false; @@ -64,8 +63,7 @@ int main() while (!WindowShouldClose()) // Detect window close button or ESC key { // Update - //---------------------------------------------------------------------------------- - + //---------------------------------------------------------------------------------- // Check rectangle movement inputs if (IsKeyPressed('W')) rectangle->rigidbody.velocity.y = JUMP_VELOCITY; if (IsKeyDown('A')) rectangle->rigidbody.velocity.x = -MOVE_VELOCITY; @@ -121,7 +119,7 @@ int main() } // De-Initialization - //-------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------- ClosePhysics(); // Unitialize physics (including all loaded objects) CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/physics_forces.c b/examples/physics_forces.c index 7de85483..efe8e240 100644 --- a/examples/physics_forces.c +++ b/examples/physics_forces.c @@ -178,7 +178,7 @@ int main() } // De-Initialization - //-------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------- ClosePhysics(); // Unitialize physics module CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/src/physac.h b/src/physac.h index 4f9b736f..b8cc8f15 100644 --- a/src/physac.h +++ b/src/physac.h @@ -146,7 +146,7 @@ typedef struct PhysicBodyData { // Module Functions Declaration //---------------------------------------------------------------------------------- PHYSACDEF void InitPhysics(Vector2 gravity); // Initializes pointers array (just pointers, fixed size) -PHYSACDEF void UpdatePhysics(double deltaTime); // Update physic objects, calculating physic behaviours and collisions detection +PHYSACDEF void* PhysicsThread(void *arg); // Physics calculations thread function PHYSACDEF void ClosePhysics(); // Unitialize all physic objects and empty the objects pool PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale); // Create a new physic body dinamically, initialize it and add to pool @@ -219,7 +219,7 @@ static Vector2 gravityForce; // Gravity f //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static void* PhysicsThread(void *arg); // Physics calculations thread function +static void UpdatePhysics(double deltaTime); // Update physic objects, calculating physic behaviours and collisions detection static void InitTimer(void); // Initialize hi-resolution timer static double GetCurrentTime(void); // Time measure returned are microseconds static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2 @@ -243,8 +243,214 @@ PHYSACDEF void InitPhysics(Vector2 gravity) #endif } +// Unitialize all physic objects and empty the objects pool +PHYSACDEF void ClosePhysics() +{ + // Exit physics thread loop + physicsThreadEnabled = false; + + // Free all dynamic memory allocations + for (int i = 0; i < physicBodiesCount; i++) PHYSAC_FREE(physicBodies[i]); + + // Reset enabled physic objects count + physicBodiesCount = 0; +} + +// Create a new physic body dinamically, initialize it and add to pool +PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale) +{ + // Allocate dynamic memory + PhysicBody obj = (PhysicBody)PHYSAC_MALLOC(sizeof(PhysicBodyData)); + + // Initialize physic body values with generic values + obj->id = physicBodiesCount; + obj->enabled = true; + + obj->transform = (Transform){ (Vector2){ position.x - scale.x/2, position.y - scale.y/2 }, rotation, scale }; + + obj->rigidbody.enabled = false; + obj->rigidbody.mass = 1.0f; + obj->rigidbody.acceleration = (Vector2){ 0.0f, 0.0f }; + obj->rigidbody.velocity = (Vector2){ 0.0f, 0.0f }; + obj->rigidbody.applyGravity = false; + obj->rigidbody.isGrounded = false; + obj->rigidbody.friction = 0.0f; + obj->rigidbody.bounciness = 0.0f; + + obj->collider.enabled = true; + obj->collider.type = COLLIDER_RECTANGLE; + obj->collider.bounds = TransformToRectangle(obj->transform); + obj->collider.radius = 0.0f; + + // Add new physic body to the pointers array + physicBodies[physicBodiesCount] = obj; + + // Increase enabled physic bodies count + physicBodiesCount++; + + return obj; +} + +// Destroy a specific physic body and take it out of the list +PHYSACDEF void DestroyPhysicBody(PhysicBody pbody) +{ + // Free dynamic memory allocation + PHYSAC_FREE(physicBodies[pbody->id]); + + // Remove *obj from the pointers array + for (int i = pbody->id; i < physicBodiesCount; i++) + { + // Resort all the following pointers of the array + if ((i + 1) < physicBodiesCount) + { + physicBodies[i] = physicBodies[i + 1]; + physicBodies[i]->id = physicBodies[i + 1]->id; + } + else PHYSAC_FREE(physicBodies[i]); + } + + // Decrease enabled physic bodies count + physicBodiesCount--; +} + +// Apply directional force to a physic body +PHYSACDEF void ApplyForce(PhysicBody pbody, Vector2 force) +{ + if (pbody->rigidbody.enabled) + { + pbody->rigidbody.velocity.x += force.x/pbody->rigidbody.mass; + pbody->rigidbody.velocity.y += force.y/pbody->rigidbody.mass; + } +} + +// Apply radial force to all physic objects in range +PHYSACDEF void ApplyForceAtPosition(Vector2 position, float force, float radius) +{ + for (int i = 0; i < physicBodiesCount; i++) + { + if (physicBodies[i]->rigidbody.enabled) + { + // Calculate direction and distance between force and physic body position + Vector2 distance = (Vector2){ physicBodies[i]->transform.position.x - position.x, physicBodies[i]->transform.position.y - position.y }; + + if (physicBodies[i]->collider.type == COLLIDER_RECTANGLE) + { + distance.x += physicBodies[i]->transform.scale.x/2; + distance.y += physicBodies[i]->transform.scale.y/2; + } + + float distanceLength = Vector2Length(distance); + + // Check if physic body is in force range + if (distanceLength <= radius) + { + // Normalize force direction + distance.x /= distanceLength; + distance.y /= -distanceLength; + + // Calculate final force + Vector2 finalForce = { distance.x*force, distance.y*force }; + + // Apply force to the physic body + ApplyForce(physicBodies[i], finalForce); + } + } + } +} + +// Convert Transform data type to Rectangle (position and scale) +PHYSACDEF Rectangle TransformToRectangle(Transform transform) +{ + return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y}; +} + +// Physics calculations thread function +PHYSACDEF void* PhysicsThread(void *arg) +{ + // Initialize thread loop state + physicsThreadEnabled = true; + + // Initialize hi-resolution timer + InitTimer(); + + // Physics update loop + while (physicsThreadEnabled) + { + currentTime = GetCurrentTime(); + double deltaTime = (double)(currentTime - previousTime); + previousTime = currentTime; + + // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) + UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); + } + + return NULL; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +// Initialize hi-resolution timer +static void InitTimer(void) +{ +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec now; + + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success + { + baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; + } +#endif + + previousTime = GetCurrentTime(); // Get time as double +} + +// Time measure returned are microseconds +static double GetCurrentTime(void) +{ + double time; + +#if defined(PLATFORM_DESKTOP) + unsigned long long int clockFrequency, currentTime; + + QueryPerformanceFrequency(&clockFrequency); + QueryPerformanceCounter(¤tTime); + + time = (double)((double)currentTime/(double)clockFrequency); +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; + + time = (double)(temp - baseTime)*1e-9; +#endif + + return time; +} + +// Returns the dot product of two Vector2 +static float Vector2DotProduct(Vector2 v1, Vector2 v2) +{ + float result; + + result = v1.x*v2.x + v1.y*v2.y; + + return result; +} + +static float Vector2Length(Vector2 v) +{ + float result; + + result = sqrt(v.x*v.x + v.y*v.y); + + return result; +} + // Update physic objects, calculating physic behaviours and collisions detection -PHYSACDEF void UpdatePhysics(double deltaTime) +static void UpdatePhysics(double deltaTime) { for (int i = 0; i < physicBodiesCount; i++) { @@ -615,210 +821,4 @@ PHYSACDEF void UpdatePhysics(double deltaTime) } } -// Unitialize all physic objects and empty the objects pool -PHYSACDEF void ClosePhysics() -{ - // Exit physics thread loop - physicsThreadEnabled = false; - - // Free all dynamic memory allocations - for (int i = 0; i < physicBodiesCount; i++) PHYSAC_FREE(physicBodies[i]); - - // Reset enabled physic objects count - physicBodiesCount = 0; -} - -// Create a new physic body dinamically, initialize it and add to pool -PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale) -{ - // Allocate dynamic memory - PhysicBody obj = (PhysicBody)PHYSAC_MALLOC(sizeof(PhysicBodyData)); - - // Initialize physic body values with generic values - obj->id = physicBodiesCount; - obj->enabled = true; - - obj->transform = (Transform){ (Vector2){ position.x - scale.x/2, position.y - scale.y/2 }, rotation, scale }; - - obj->rigidbody.enabled = false; - obj->rigidbody.mass = 1.0f; - obj->rigidbody.acceleration = (Vector2){ 0.0f, 0.0f }; - obj->rigidbody.velocity = (Vector2){ 0.0f, 0.0f }; - obj->rigidbody.applyGravity = false; - obj->rigidbody.isGrounded = false; - obj->rigidbody.friction = 0.0f; - obj->rigidbody.bounciness = 0.0f; - - obj->collider.enabled = true; - obj->collider.type = COLLIDER_RECTANGLE; - obj->collider.bounds = TransformToRectangle(obj->transform); - obj->collider.radius = 0.0f; - - // Add new physic body to the pointers array - physicBodies[physicBodiesCount] = obj; - - // Increase enabled physic bodies count - physicBodiesCount++; - - return obj; -} - -// Destroy a specific physic body and take it out of the list -PHYSACDEF void DestroyPhysicBody(PhysicBody pbody) -{ - // Free dynamic memory allocation - PHYSAC_FREE(physicBodies[pbody->id]); - - // Remove *obj from the pointers array - for (int i = pbody->id; i < physicBodiesCount; i++) - { - // Resort all the following pointers of the array - if ((i + 1) < physicBodiesCount) - { - physicBodies[i] = physicBodies[i + 1]; - physicBodies[i]->id = physicBodies[i + 1]->id; - } - else PHYSAC_FREE(physicBodies[i]); - } - - // Decrease enabled physic bodies count - physicBodiesCount--; -} - -// Apply directional force to a physic body -PHYSACDEF void ApplyForce(PhysicBody pbody, Vector2 force) -{ - if (pbody->rigidbody.enabled) - { - pbody->rigidbody.velocity.x += force.x/pbody->rigidbody.mass; - pbody->rigidbody.velocity.y += force.y/pbody->rigidbody.mass; - } -} - -// Apply radial force to all physic objects in range -PHYSACDEF void ApplyForceAtPosition(Vector2 position, float force, float radius) -{ - for (int i = 0; i < physicBodiesCount; i++) - { - if (physicBodies[i]->rigidbody.enabled) - { - // Calculate direction and distance between force and physic body position - Vector2 distance = (Vector2){ physicBodies[i]->transform.position.x - position.x, physicBodies[i]->transform.position.y - position.y }; - - if (physicBodies[i]->collider.type == COLLIDER_RECTANGLE) - { - distance.x += physicBodies[i]->transform.scale.x/2; - distance.y += physicBodies[i]->transform.scale.y/2; - } - - float distanceLength = Vector2Length(distance); - - // Check if physic body is in force range - if (distanceLength <= radius) - { - // Normalize force direction - distance.x /= distanceLength; - distance.y /= -distanceLength; - - // Calculate final force - Vector2 finalForce = { distance.x*force, distance.y*force }; - - // Apply force to the physic body - ApplyForce(physicBodies[i], finalForce); - } - } - } -} - -// Convert Transform data type to Rectangle (position and scale) -PHYSACDEF Rectangle TransformToRectangle(Transform transform) -{ - return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y}; -} - -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- -// Physics calculations thread function -static void* PhysicsThread(void *arg) -{ - // Initialize thread loop state - physicsThreadEnabled = true; - - // Initialize hi-resolution timer - InitTimer(); - - // Physics update loop - while (physicsThreadEnabled) - { - currentTime = GetCurrentTime(); - double deltaTime = (double)(currentTime - previousTime); - previousTime = currentTime; - - // Delta time value needs to be inverse multiplied by physics time step value (1/target fps) - UpdatePhysics(deltaTime/PHYSICS_TIMESTEP); - } - - return NULL; -} - -// Initialize hi-resolution timer -static void InitTimer(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) - struct timespec now; - - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success - { - baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; - } -#endif - - previousTime = GetCurrentTime(); // Get time as double -} - -// Time measure returned are microseconds -static double GetCurrentTime(void) -{ - double time; - -#if defined(PLATFORM_DESKTOP) - unsigned long long int clockFrequency, currentTime; - - QueryPerformanceFrequency(&clockFrequency); - QueryPerformanceCounter(¤tTime); - - time = (double)((double)currentTime/(double)clockFrequency); -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; - - time = (double)(temp - baseTime)*1e-9; -#endif - - return time; -} - -// Returns the dot product of two Vector2 -static float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result; - - result = v1.x*v2.x + v1.y*v2.y; - - return result; -} - -static float Vector2Length(Vector2 v) -{ - float result; - - result = sqrt(v.x*v.x + v.y*v.y); - - return result; -} - #endif // PHYSAC_IMPLEMENTATION \ No newline at end of file -- cgit v1.2.3 From 1879a8129e786e859cc2984e294ef9c22663f923 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:40:12 +0200 Subject: Fixed little bug in physac example --- examples/physics_basic_rigidbody.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 084bfb0e..75720c97 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -29,7 +29,7 @@ int main() int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [physac] example - basic rigidbody"); - // InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module + InitPhysics((Vector2){ 0.0f, -9.81f/2 }); // Initialize physics module // Debug variables bool isDebug = false; -- cgit v1.2.3 From 1b0996fb0bcf68e2a14bc6260c6f2c5366ab033f Mon Sep 17 00:00:00 2001 From: victorfisac Date: Tue, 14 Jun 2016 20:54:20 +0200 Subject: Updated physac header documentation --- src/physac.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/physac.h b/src/physac.h index b8cc8f15..dd4c4126 100644 --- a/src/physac.h +++ b/src/physac.h @@ -15,6 +15,10 @@ * The generated implementation will stay private inside implementation file and all * internal symbols and functions will only be visible inside that file. * +* #define PHYSAC_NO_THREADS +* The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). +* It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. +* * #define PHYSAC_STANDALONE * Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined * internally in the library and input management and drawing functions must be provided by @@ -27,12 +31,16 @@ * * LIMITATIONS: * -* // TODO. +* - There is a limit of 256 physic objects. +* - Physics behaviour can be unexpected using bounciness or friction values out of 0.0f - 1.0f range. +* - The module is limited to 2D axis oriented physics. +* - Physics colliders must be rectangle or circle shapes (there is not a custom polygon collider type). * * VERSIONS: * -* 1.0 (09-Jun-2016) Module names review and converted to header-only. -* 0.9 (23-Mar-2016) Complete module redesign, steps-based for better physics resolution. +* 1.0 (14-Jun-2016) New module defines and fixed some delta time calculation bugs. +* 0.9 (09-Jun-2016) Module names review and converted to header-only. +* 0.8 (23-Mar-2016) Complete module redesign, steps-based for better physics resolution. * 0.3 (13-Feb-2016) Reviewed to add PhysicObjects pool. * 0.2 (03-Jan-2016) Improved physics calculations. * 0.1 (30-Dec-2015) Initial release. -- cgit v1.2.3