From 59f6c7d1ee8620db03bc449d982bda38547fbaf0 Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 7 Oct 2023 18:42:38 +0200 Subject: [PATCH] moved shape overlap code to collision.c --- src/collision.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ src/shape.c | 63 ----------------------------------- src/shape.h | 2 -- 3 files changed, 88 insertions(+), 65 deletions(-) create mode 100644 src/collision.c diff --git a/src/collision.c b/src/collision.c new file mode 100644 index 0000000..d690b1f --- /dev/null +++ b/src/collision.c @@ -0,0 +1,88 @@ +#include "collision.h" +#include "player.h" + +// ===================================================== +// Shape overlap test using the separating axis theorem +// ===================================================== + +typedef struct Range {float min; float max; } Range; + +static +Range _shape_get_range_on_axis(PhysicsEntity self, Vector axis) { + Vector point = shape_get_point_transformed(self.tc->get_shape(self.data), 0, *self.transformable->get_transform(self.data)); + float dot = vdotf(axis, point); + Range range = {dot, dot}; + + for(size_t point_index = 1; point_index < shape_get_points_count(self.tc->get_shape(self.data)); ++point_index) { + point = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self.transformable->get_transform(self.data)); + dot = vdotf(axis, point); + if(dot < range.min) + range.min = fminf(dot, range.min); + if(dot > range.max) + range.max = fmaxf(dot, range.max); + } + + return range; +} + +static +Vector _shape_overlap_on_axis(PhysicsEntity self, PhysicsEntity other, Vector axis) { + Range a_range = _shape_get_range_on_axis(self, axis); + Range b_range = _shape_get_range_on_axis(other, axis); + + if(a_range.min <= b_range.max && b_range.min <= b_range.max) + return vmulff(axis, fminf(b_range.max - b_range.min, b_range.min - a_range.max)); + else + return ZeroVector; +} + +static +Collision _shape_get_overlap_internal(PhysicsEntity self, PhysicsEntity other) { + // The shortest escape vector found so far + Vector shortest_escape = InfinityVector; + // the squared length of the shortest escape vector found so far + float shortest_sqrmag = INFINITY; + // the first index of the points on the edge + size_t shortest_escape_edge = 0; + // the number of points in the shape of self + size_t self_point_count = shape_get_points_count(self.tc->get_shape(self.data)); + + for(size_t point_index = 0; point_index < self_point_count; ++point_index) { + // the next point on the line + size_t next_index = (point_index + 1) % self_point_count; + // get the two points defining the collision edge + Vector a = shape_get_point_transformed(self.tc->get_shape(self.data), point_index, *self.transformable->get_transform(self.data)); + Vector b = shape_get_point_transformed(self.tc->get_shape(self.data), next_index, *self.transformable->get_transform(self.data)); + // the direction of the line + Vector diff = vsubf(b, a); + + // the smallest escape vector on this axis + Vector escape = _shape_overlap_on_axis(self, other, vnormalizedf(diff)); + float sqr_mag = vsqrmagnitudef(escape); + if(sqr_mag < shortest_sqrmag) { + shortest_sqrmag = sqr_mag; + shortest_escape = escape; + shortest_escape_edge = point_index; + } + } + + RigidBody* rba = self.tc->get_rigidbody(self.data); + RigidBody* rbb = self.tc->get_rigidbody(self.data); + + return (Collision) { + .edge_left = shortest_escape_edge, + .edge_right = (1 + shortest_escape_edge) % self_point_count, + .normal = vnormalizedf(shortest_escape), + .velocity = vsubf(rigidbody_get_velocity(rba), rigidbody_get_velocity(rbb)), + .other = other, + .separation_force = shortest_escape + }; +} + +Collision get_collision(PhysicsEntity a, PhysicsEntity b) { + Collision self_first = _shape_get_overlap_internal(a, b); + Collision other_first = _shape_get_overlap_internal(b, a); + return vsqrmagnitudef(self_first.separation_force) < vsqrmagnitudef(other_first.separation_force) + ? self_first + : other_first; +} \ No newline at end of file diff --git a/src/shape.c b/src/shape.c index c6cafbf..d030bb3 100644 --- a/src/shape.c +++ b/src/shape.c @@ -173,66 +173,3 @@ Vector shape_get_median_point(Shape* self) { int shape_is_convex(Shape* self) { return self->is_convex; } - -// ===================================================== -// Shape overlap test using the separating axis theorem -// ===================================================== - -typedef struct Range {float min; float max; } Range; - -static -Range _shape_get_range_on_axis(Shape* self, Transform self_transform, Vector axis) { - Vector point = shape_get_point_transformed(self, 0, self_transform); - float dot = vdotf(axis, point); - Range range = {dot, dot}; - - for(size_t point_index = 1; point_index < self->points_len; ++point_index) { - point = shape_get_point_transformed(self, point_index, self_transform); - dot = vdotf(axis, point); - if(dot < range.min) - range.min = fminf(dot, range.min); - if(dot > range.max) - range.max = fmaxf(dot, range.max); - } - - return range; -} - -static -Vector _shape_overlap_on_axis(Shape* self, Transform self_transform, Shape* other, Transform other_transform, Vector axis) { - Range a_range = _shape_get_range_on_axis(self, self_transform, axis); - Range b_range = _shape_get_range_on_axis(other, other_transform, axis); - - if(a_range.min <= b_range.max && b_range.min <= b_range.max) - return vmulff(axis, fminf(b_range.max - b_range.min, b_range.min - a_range.max)); - else - return ZeroVector; -} - -static -Vector _shape_get_overlap_internal(Shape* self, Transform self_transform, Shape* other, Transform other_transform) { - Vector shortest_escape = InfinityVector; - float shortest_sqrmag = INFINITY; - - for(size_t point_index = 0; point_index < self->points_len; ++point_index) { - size_t next_index = (point_index + 1) % self->points_len; - Vector a = shape_get_point_transformed(self, point_index, self_transform); - Vector b = shape_get_point_transformed(self, next_index, other_transform); - Vector diff = vsubf(b, a); - - Vector escape = _shape_overlap_on_axis(self, self_transform, other, other_transform, vnormalizedf(diff)); - float sqr_mag = vsqrmagnitudef(escape); - if(sqr_mag < shortest_sqrmag) { - shortest_sqrmag = sqr_mag; - shortest_escape = escape; - } - } - - return shortest_escape; -} - -Vector shape_get_overlap(Shape* self, Transform self_transform, Shape* other, Transform other_transform) { - Vector self_first = _shape_get_overlap_internal(self, self_transform, other, other_transform); - Vector other_first = _shape_get_overlap_internal(other, other_transform, self, self_transform); - return vsqrmagnitudef(self_first) < vsqrmagnitudef(other_first) ? self_first : other_first; -} diff --git a/src/shape.h b/src/shape.h index 8844f4e..a20a53c 100644 --- a/src/shape.h +++ b/src/shape.h @@ -25,6 +25,4 @@ extern Vector shape_remove_point(Shape* self, size_t at); extern Vector shape_get_median_point(Shape* self); extern int shape_is_convex(Shape* self); -extern Vector shape_get_overlap(Shape* self, Transform self_transform, Shape* other, Transform other_transform); - #endif // !_fencer_shape_h