Skip to content

Instantly share code, notes, and snippets.

@Pikachuxxxx
Last active April 22, 2024 12:32
Show Gist options
  • Save Pikachuxxxx/0dda4b70bf71b794b08923df34961844 to your computer and use it in GitHub Desktop.
Save Pikachuxxxx/0dda4b70bf71b794b08923df34961844 to your computer and use it in GitHub Desktop.
A Function to Detect Collisions Between a Ray and a Rectangle and Dynamic Rectangle and a Rectangle in Raylib Environment
#include <stdio.h>
#include <raylib.h>
/*
* define this macro RAY_COLLISION_CALCULATION_DEBUG_STATS
* if you want to enable the ray collision calculations printed as debug data
*/
// helper functions
void SwapFloatValue(float *a, float *b){
float t = *a;
*a = *b;
*b = t;
}
void SwapIntValues(int *a, int *b){
register int t = *a;
*a = *b;
*b = t;
}
int maxI(int num1, int num2)
{
return (num1 > num2 ) ? num1 : num2;
}
int minI(int num1, int num2)
{
return (num1 > num2 ) ? num2 : num1;
}
float maxF(float num1, float num2)
{
return (num1 > num2 ) ? num1 : num2;
}
float minF(float num1, float num2)
{
return (num1 > num2 ) ? num2 : num1;
}
int absI(int num){
return num < 0 ? -num : num;
}
float absF(float num){
return num < 0 ? -num : num;
}
/*
DESCRIPTION : A function to detect Collisions between a Ray and a Rectangle.
- Also provides additonal data such as the contact point and contact normal and the probale collison points
- PARAMETER ray_origin : A Vector2 defining the origin/start point of the Ray
- PARAMETER ray_dir : A Vector2 defining the direction of the Ray
- PARAMETER targetRect : The target rectangle against which we are checking the collision
- PARAMETER contact_point(Pointer) : A Vector2 that stores the contact point if the collision occurs with the rectangle
- PARAMETER contact_normal(Pointer) : A Vector2 that stores the contact normal of the rectangle surface if the collision occurs with the rectangle
- PARAMETER near_contact_time(Pointer) : A float that stores the parametric contact time of the rectangle surface if the collision occurs with the rectangle
- PARAMETER probableContactPoints : An array of Vector2's that stores the probale near and far contact points with the ray and rectanlge if the there is a chance of collision
RETURNS : A bool indicating wether the collision has occured or not.
- Also updates the contact_point, contact_normal and the probableContactPoints pointers
*/
bool RayVsRect2D(const Vector2 ray_origin, const Vector2 ray_dir, const Rectangle targetRect, Vector2* contact_point,Vector2* contact_normal, float* near_contact_time, Vector2 probableContactPoints[]){
/*
* The t in the P(t) = P + D.t
* Where t is the parametric variable to plot the near collison point using the parametric line equation (P(t) = P + D.t)
* Where P is the Position Vector of the Ray and D is the Direciton Vector of the Ray
*/
float t_hit_near;
/*
* Calculate intersection points with rectangle bounding axes
* Parametric 't' for Near (X,Y) and Far (X,Y)
*/
float delta_t1_X = targetRect.x - ray_origin.x;
float t_hit_near_X = (delta_t1_X / ray_dir.x);
float delta_t1_Y = targetRect.y - ray_origin.y;
float t_hit_near_Y = (delta_t1_Y / ray_dir.y);
float delta_t2_X = targetRect.x + targetRect.width - ray_origin.x;
float t_hit_far_X = (delta_t2_X / ray_dir.x);
float delta_t2_Y = targetRect.y + targetRect.height - ray_origin.y;
float t_hit_far_Y = (delta_t2_Y / ray_dir.y);
/*
* Sort the distances to maintain Affine uniformity
* i.e. sort the near and far axes of the rectangle in the appropritate order from the POV of ray_origin
*/
if (t_hit_near_X > t_hit_far_X) SwapFloatValue(&t_hit_near_X, &t_hit_far_X);
if (t_hit_near_Y > t_hit_far_Y) SwapFloatValue(&t_hit_near_Y, &t_hit_far_Y);
// As there is no chance of collision i.e the paramteric line cannot pass throguh the rectangle the probable points are empty
probableContactPoints[0] = (Vector2){0, 0};
probableContactPoints[1] = (Vector2){0, 0};
/*
* Check the order of the near and far points
* if they satisfy the below condition the line will pass through the rectanle (It didn't yet)
* if not return out of the function as it will not pass through
*/
if(!(t_hit_near_X < t_hit_far_Y && t_hit_near_Y < t_hit_far_X)) return false;
/*
* If the parametric line passes through the rectangle calculate the parametric 't'
* the 't' should be such that it must lie on both the Line/Ray and the Rectangle
* Use the condition below to calculate the 'tnear' and 'tfar' that gives the near and far collison parametric t
*/
*near_contact_time = maxF(t_hit_near_X, t_hit_near_Y);
float t_hit_far = minF(t_hit_far_X, t_hit_far_Y);
// Use the parametric t values calculated above and subsitute them in the parametric equation to get the near and far contact points
float Hit_Near_X_Position = ray_origin.x + (ray_dir.x * (*near_contact_time));
float Hit_Near_Y_Position = ray_origin.y + (ray_dir.y * (*near_contact_time));
float Hit_Far_X_Position = ray_origin.x + (ray_dir.x * t_hit_far);
float Hit_Far_Y_Position = ray_origin.y + (ray_dir.y * t_hit_far);
// Debugging the calculations
#ifdef RAY_COLLISION_CALCULATION_DEBUG_STATS
printf("The delta_t1_X is : %f | t_hit_near_X is : %f | ray_dir.x is : %f | rayOrigin.x is : %f | Hit_Near_X_Position is : %f \n", delta_t1_X, t_hit_near_X, ray_dir.x, ray_origin.x, Hit_Near_X_Position);
printf("The delta_t1_Y is : %f | t_hit_near_Y is : %f | ray_dir.y is : %f | rayOrigin.y is : %f | Hit_Near_Y_Position is : %f \n", delta_t1_Y, t_hit_near_Y, ray_dir.y, ray_origin.y, Hit_Near_Y_Position);
printf("t_hit_near is : %f \n", *near_contact_time);
printf("The delta_t2_X is : %f | t_hit_far_X is : %f | ray_dir.x is : %f | rayOrigin.x is : %f | Hit_Far_X_Position is : %f \n", delta_t2_X, t_hit_far_X, ray_dir.x, ray_origin.x, Hit_Far_X_Position);
printf("The delta_t2_Y is : %f | t_hit_far_Y is : %f | ray_dir.y is : %f | rayOrigin.y is : %f | Hit_Far_Y_Position is : %f \n", delta_t2_Y, t_hit_far_Y, ray_dir.y, ray_origin.y, Hit_Far_Y_Position);
printf("t_hit_far is : %f \n", t_hit_far);
#endif
// Generate Vectors using the near and far collision points
Vector2 Near_Hit_Vector = (Vector2){Hit_Near_X_Position, Hit_Near_Y_Position};
Vector2 Far_Hit_Vector = (Vector2){Hit_Far_X_Position, Hit_Far_Y_Position};
// Since we are sure that the line will pass through the rectanle upadte the probable near and far points
probableContactPoints[0] = Near_Hit_Vector;
probableContactPoints[1] = Far_Hit_Vector;
/*
* Check wether the parametric 't' values are withing certain bounds to guarantee that the probable collision has actually occured
* i.e. If the below conditions are true only then the Ray has passed through the Rectangle
* if not it still can pass but it did not yet
*/
if(t_hit_far < 0 || t_hit_near > 1) return false;
// Now Update the actual contact point
*contact_point = (Vector2){Hit_Near_X_Position, Hit_Near_Y_Position};
// Update the contact normal of the Ray with the Rectangle surface
if(t_hit_near_X > t_hit_near_Y){
if(ray_dir.x < 0) *contact_normal = (Vector2){1, 0};
else *contact_normal = (Vector2){-1, 0};
}
else if(t_hit_near_X < t_hit_near_Y){
if(ray_dir.y < 0) *contact_normal = (Vector2){0, 1};
else *contact_normal = (Vector2){0, -1};
}
// Since the contact has definitely occured return true
return true;
}
/*
DESCRIPTION : A function to detect Collisions between a Dynamic Rectangle and a static Rectangle.
FIXME : Using this function causes contact_point to be detected on the expanded_rectangle, please resolve it properly if you need it on the target itself
- PARAMETER sourceRect : The dynamic rectangle which collides with the static rectnalges
- PARAMETER sourceRectVelocity : The Velecoty Vector of the source rectangle
- PARAMETER targetRect : The target rectangle against which we check for collisions with the source rectangle
- PARAMETER contact_point(Pointer) : A Vector2 that stores the contact point if the collision occurs with the rectangle
- PARAMETER contact_normal(Pointer) : A Vector2 that stores the contact normal of the rectangle surface if the collision occurs with the rectangle
- PARAMETER near_contact_time(Pointer) : A float that stores the parametric contact time of the rectangle surface if the collision occurs with the rectangle
- PARAMETER probableContactPoints : An array of Vector2's that stores the probale near and far contact points with the ray and rectanlge if the there is a chance of collision
RETURNS : A bool indicating wether the collision has occured or not.
- Also updates the contact_point, contact_normal and the probableContactPoints pointers
*/
bool DynamicRectVsRect(const Rectangle sourceRect, const Vector2 sourceRectVelocity, const Rectangle targetRect, Vector2* contact_point, Vector2* contact_normal, float* near_contact_time, Vector2 probableContactPoints[]){
// We assume that the soruce rectanle is always dynamic while it is in collison
if(sourceRectVelocity.x == 0 && sourceRectVelocity.y == 0){ return false; }
/*
* Using a clever trick by expanding the target rect by dimension of source rect
* to detect collison and correct overlapping using the contact_time and subtrating the relative Velocity
*/
Rectangle expanded_target;
expanded_target.x = targetRect.x - (sourceRect.width/2);
expanded_target.y = targetRect.y - (sourceRect.height/2);
expanded_target.width = targetRect.width + sourceRect.width;
expanded_target.height = targetRect.height + sourceRect.height;
// Now check the collion against the source rectangle velocity vector ray and the expanded rectangle to avoid any unresolvable Collisions
Vector2 ray_or = (Vector2){sourceRect.x + (sourceRect.width/2), sourceRect.y + (sourceRect.height/2)};
Vector2 ray_di = (Vector2){sourceRectVelocity.x, sourceRectVelocity.y};
if(RayVsRect2D(ray_or, ray_di, expanded_target, contact_point, contact_normal, near_contact_time, probableContactPoints)){
return true;
}
// If the collision doesn't occur return false
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment