forked from siddharthroy12/sr_resolve
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsr_resolve.h
254 lines (202 loc) · 7 KB
/
sr_resolve.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// A simple AABB Collision resolver that will work for most games
// Author - siddharthroy12/@siddharthroy12
// Modified by PikoStudios for use in the Muzzle Framework
#ifndef SR_RESOLVE_H
#define SR_RESOLVE_H
#include <math.h>
#include <stdio.h>
#include <stdbool.h>
#include "core/vector.h"
#include "shapes/Rectangle.h"
typedef struct sr_ray2 {
vec2 position;
vec2 direction;
} sr_ray2;
typedef struct sr_sort_pair {
int index;
float time;
} sr_sort_pair;
#define MZ_COLLISION_MAX(x, y) (x > y ? x : y)
#define MZ_COLLISION_MIN(x, y) (x > y ? y : x)
void swap_float(float *vec1, float *vec2);
float sr_vec2_length(vec2 v);
static vec2 sr_vec2_scale(vec2 v, float scale);
static vec2 sr_vec2_divide(vec2 v1, vec2 v2);
static vec2 sr_vec2_multiply(vec2 v1, vec2 v2);
static vec2 sr_vec2_normalize(vec2 v);
static vec2 sr_vector2_sub(vec2 v1, vec2 v2);
static vec2 sr_vector2_add(vec2 v1, vec2 v2);
static bool sr_check_rec_vs_rec_collision(rectangle rec1, rectangle rec2);
static bool sr_check_ray_vs_rec_collision(const sr_ray2 ray, const rectangle target, vec2 *contact_point, vec2 *contact_normal, float *t_hit_near);
static bool sr_dynamic_rect_vs_rect(const rectangle in, const rectangle target, vec2 vel, vec2 *contact_point, vec2 *contact_normal, float *contact_time, float delta);
void sr_sort_indexes(sr_sort_pair *times, int length);
static void sr_move_and_slide(rectangle *obstacles, const int obstacles_length, vec2 hitbox, vec2 *vel, vec2 *pos , float delta);
#ifdef MZ_SR_RESOLVE_IMPLEMENTION
void swap_float(float *vec1, float *vec2) {
float temp = *vec1;
*vec1 = *vec2;
*vec2 = temp;
}
// Get the length of a vector
float sr_vec2_length(vec2 v) {
float result = sqrtf((v.x*v.x) + (v.y*v.y));
return result;
}
// Scale a vector by a given float value
static vec2 sr_vec2_scale(vec2 v, float scale) {
return (vec2){ v.x*scale, v.y*scale };
}
// Divide two vectors
static vec2 sr_vec2_divide(vec2 v1, vec2 v2) {
return (vec2){ v1.x/v2.x, v1.y/v2.y };
}
// Multiply two vectors
static vec2 sr_vec2_multiply(vec2 v1, vec2 v2) {
return (vec2){ v1.x*v2.x, v1.y*v2.y };
}
// Normalize a vector
static vec2 sr_vec2_normalize(vec2 v) {
return sr_vec2_scale(v, 1 / sr_vec2_length(v));
}
// Substract two vectors
static vec2 sr_vector2_sub(vec2 v1, vec2 v2) {
return (vec2){ v1.x - v2.x, v1.y - v2.y };
}
// Add two vectors
static vec2 sr_vector2_add(vec2 v1, vec2 v2) {
return (vec2){ v1.x + v2.x, v1.y + v2.y };
}
// Check collision between two rectangles
static bool sr_check_rec_vs_rec_collision(rectangle rec1, rectangle rec2) {
if (
(rec1.x < (rec2.x + rec2.width) && (rec1.x + rec1.width) > rec2.x) &&
(rec1.y < (rec2.y + rec2.height) && (rec1.y + rec1.height) > rec2.y)
) {
return true;
} else {
return false;
}
}
// Check collision between a ray and a rectangle
static bool sr_check_ray_vs_rec_collision(const sr_ray2 ray, const rectangle target, vec2 *contact_point, vec2 *contact_normal, float *t_hit_near) {
// Cache division
vec2 indiv = sr_vec2_divide((vec2){1.0f, 1.0f}, ray.direction);
// Calculate intersections with rectangle bounding axes
vec2 t_near = sr_vec2_multiply(
sr_vector2_sub((vec2){ target.x, target.y }, ray.position),
indiv
);
vec2 t_far = sr_vec2_multiply(
sr_vector2_sub(
sr_vector2_add((vec2){target.x, target.y}, (vec2){target.width, target.height}),
ray.position
),
indiv
);
// Check for nan
if (isnanf(t_far.y) || isnanf(t_far.x)) return false;
if (isnanf(t_near.y) || isnanf(t_near.x)) return false;
// Sort distances
if (t_near.x > t_far.x) swap_float(&t_near.x, &t_far.x);
if (t_near.y > t_far.y) swap_float(&t_near.y, &t_far.y);
// Early rejection
if (t_near.x > t_far.y || t_near.y > t_far.x) return false;
// Closest 'time' will be the first contact
*t_hit_near = MZ_COLLISION_MAX(t_near.x, t_near.y);
// Furthest 'time' is contact on opposite side of target
float t_hit_far = MZ_COLLISION_MIN(t_far.x, t_far.y);
// Reject if ray direction is pointing away from object
if (t_hit_far < 0) return false;
// Contact point of collision from parametric line equation
*contact_point = sr_vector2_add(ray.position, sr_vec2_scale(ray.direction, *t_hit_near));
if (t_near.x > t_near.y)
if (ray.direction.x < 0)
*contact_normal = (vec2){ 1, 0 };
else
*contact_normal = (vec2){ -1, 0 };
else if (t_near.x < t_near.y)
if (ray.direction.y < 0)
*contact_normal = (vec2){ 0, 1 };
else
*contact_normal = (vec2){ 0, -1 };
return true;
}
static bool sr_dynamic_rect_vs_rect(const rectangle in, const rectangle target, vec2 vel, vec2 *contact_point, vec2 *contact_normal, float *contact_time, float delta) {
// Check if dynamic rectangle is actually moving - we assume rectangles are NOT in collision to start
if (vel.x == 0 && vel.y == 0) return false;
// Expand target rectangle by source dimensions
rectangle expanded_target;
expanded_target.x = target.x - (in.width/2);
expanded_target.y = target.y - (in.height/2);
expanded_target.width = target.width + in.width;
expanded_target.height = target.height + in.height;
if (sr_check_ray_vs_rec_collision(
(sr_ray2){
(vec2){
in.x + (in.width/2),
in.y + (in.height/2)
},
sr_vec2_scale(vel, delta)
},
expanded_target,
contact_point,
contact_normal,
contact_time
)) {
if (*contact_time <= 1.0f && *contact_time >= -1.0f) return true;
}
return false;
}
void sr_sort_indexes(sr_sort_pair *times, int length) {
sr_sort_pair key;
int i, j;
for (i = 1; i < length; i++) {
key = times[i];
j = i - 1;
while(j >= 0 && times[j].time > key.time) {
times[j+1] = times[j];
j = j -1;
}
times[j + 1] = key;
}
// for (int i = 0; i < length; ++i) {
// for (int j = i + 1; j < length; ++j) {
// if (times[i].time > times[j].time) {
// key = times[i];
// times[i] = times[j];
// times[j] = key;
// }
// }
// }
}
static void sr_move_and_slide(rectangle *obstacles, const int obstacles_length, vec2 hitbox, vec2 *vel, vec2 *pos , float delta) {
sr_sort_pair times[obstacles_length];
vec2 cp, cn;
float time = 0;
rectangle hitbox_rec = {
pos->x - (hitbox.x/2),
pos->y - (hitbox.y/2),
hitbox.x,
hitbox.y
};
for (int i = 0; i < obstacles_length; i++) {
sr_dynamic_rect_vs_rect(hitbox_rec, obstacles[i], *vel, &cp, &cn, &time, delta);
times[i].index = i;
times[i].time = time;
}
sr_sort_indexes(times, obstacles_length);
for (int i = 0; i < obstacles_length; i++) {
if (sr_dynamic_rect_vs_rect(hitbox_rec, obstacles[times[i].index], *vel, &cp, &cn, &time, delta)) {
pos->x = cp.x;
pos->y = cp.y;
if (fabs(cn.x)) {
vel->x = 0;
}
if (fabs(cn.y)) {
vel->y = 0;
}
}
}
}
#endif // IMPLEMENTION
#endif // HEADER GUARD