]> git.lizzy.rs Git - nothing.git/blob - src/game/level/rigid_bodies.c
Merge pull request #663 from tsoding/656
[nothing.git] / src / game / level / rigid_bodies.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3
4 #include "game/camera.h"
5 #include "game/level/platforms.h"
6 #include "system/lt.h"
7 #include "system/nth_alloc.h"
8 #include "system/stacktrace.h"
9
10 #include "./rigid_bodies.h"
11
12 #define ID_SIZE 100
13
14 struct RigidBodies
15 {
16     Lt *lt;
17     size_t capacity;
18     size_t count;
19
20     char *id;
21     Rect *bodies;
22     Vec *velocities;
23     Vec *movements;
24     Color *colors;
25     bool *grounded;
26     Vec *forces;
27 };
28
29 static const Vec opposing_rect_side_forces[RECT_SIDE_N] = {
30     { .x = 1.0f,  .y =  0.0f  },  /* RECT_SIDE_LEFT = 0, */
31     { .x = -1.0f, .y =  0.0f  },  /* RECT_SIDE_RIGHT, */
32     { .x = 0.0f,  .y =  1.0f, },  /* RECT_SIDE_TOP, */
33     { .x = 0.0f,  .y = -1.0f, }   /* RECT_SIDE_BOTTOM, */
34 };
35
36 static Vec opposing_force_by_sides(int sides[RECT_SIDE_N])
37 {
38     Vec opposing_force = {
39         .x = 0.0f,
40         .y = 0.0f
41     };
42
43     for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
44         if (sides[side]) {
45             vec_add(
46                 &opposing_force,
47                 opposing_rect_side_forces[side]);
48         }
49     }
50
51     return opposing_force;
52 }
53
54 RigidBodies *create_rigid_bodies(size_t capacity)
55 {
56     Lt *lt = create_lt();
57     if (lt == NULL) {
58         return NULL;
59     }
60
61     RigidBodies *rigid_bodies = PUSH_LT(lt, nth_calloc(1, sizeof(RigidBodies)), free);
62     if (rigid_bodies == NULL) {
63         RETURN_LT(lt, NULL);
64     }
65     rigid_bodies->lt = lt;
66
67     rigid_bodies->capacity = capacity;
68     rigid_bodies->count = 0;
69
70     rigid_bodies->id = PUSH_LT(
71         lt,
72         nth_calloc(ID_SIZE, sizeof(char)),
73         free);
74     if (rigid_bodies->id == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     rigid_bodies->bodies = PUSH_LT(lt, nth_calloc(capacity, sizeof(Rect)), free);
79     if (rigid_bodies->bodies == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     rigid_bodies->velocities = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
84     if (rigid_bodies->velocities == NULL) {
85         RETURN_LT(lt, NULL);
86     }
87
88     rigid_bodies->movements = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
89     if (rigid_bodies->movements == NULL) {
90         RETURN_LT(lt, NULL);
91     }
92
93     rigid_bodies->colors = PUSH_LT(lt, nth_calloc(capacity, sizeof(Color)), free);
94     if (rigid_bodies->colors == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     rigid_bodies->grounded = PUSH_LT(lt, nth_calloc(capacity, sizeof(bool)), free);
99     if (rigid_bodies->grounded == NULL) {
100         RETURN_LT(lt, NULL);
101     }
102
103     rigid_bodies->forces = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
104     if (rigid_bodies->forces == NULL) {
105         RETURN_LT(lt, NULL);
106     }
107
108     return rigid_bodies;
109 }
110
111 void destroy_rigid_bodies(RigidBodies *rigid_bodies)
112 {
113     trace_assert(rigid_bodies);
114     RETURN_LT0(rigid_bodies->lt);
115 }
116
117 static int rigid_bodies_collide_with_itself(RigidBodies *rigid_bodies)
118 {
119     trace_assert(rigid_bodies);
120
121     if (rigid_bodies->count == 0) {
122         return 0;
123     }
124
125     for (size_t i1 = 0; i1 < rigid_bodies->count - 1; ++i1) {
126         for (size_t i2 = i1 + 1; i2 < rigid_bodies->count; ++i2) {
127             // TODO(#653): Rigid Bodies perform too many conversions between rect and two vecs representation
128             //   Maybe it's just better to represent the bodies as rects all the time?
129             // TODO(#654): Rigid Bodies don't exchange forces with each other
130             if (!rects_overlap(rigid_bodies->bodies[i1], rigid_bodies->bodies[i2])) {
131                 continue;
132             }
133
134             rect_impulse(&rigid_bodies->bodies[i1], &rigid_bodies->bodies[i2]);
135         }
136     }
137
138     return 0;
139 }
140
141 static int rigid_bodies_collide_with_platforms(
142     RigidBodies *rigid_bodies,
143     const Platforms *platforms)
144 {
145     trace_assert(rigid_bodies);
146     trace_assert(platforms);
147
148     int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
149
150     for (size_t i = 0; i < rigid_bodies->count; ++i) {
151         memset(sides, 0, sizeof(int) * RECT_SIDE_N);
152
153         platforms_touches_rect_sides(platforms, rigid_bodies->bodies[i], sides);
154
155         if (sides[RECT_SIDE_BOTTOM]) {
156             rigid_bodies->grounded[i] = true;
157         }
158
159         /* TODO(#655): Opposing force notion in Rigid Bodies seems redundant */
160         Vec opforce_direction = opposing_force_by_sides(sides);
161
162         if (fabs(opforce_direction.x) > 1e-6 && (opforce_direction.x < 0.0f) != ((rigid_bodies->velocities[i].x + rigid_bodies->movements[i].x) < 0.0f)) {
163             rigid_bodies->velocities[i].x = 0.0f;
164             rigid_bodies->movements[i].x = 0.0f;
165         }
166
167         if (fabs(opforce_direction.y) > 1e-6 && (opforce_direction.y < 0.0f) != ((rigid_bodies->velocities[i].y + rigid_bodies->movements[i].y) < 0.0f)) {
168             rigid_bodies->velocities[i].y = 0.0f;
169             rigid_bodies->movements[i].y = 0.0f;
170
171             if (vec_length(rigid_bodies->velocities[i]) > 1e-6) {
172                 rigid_bodies_apply_force(
173                     rigid_bodies, i,
174                     vec_scala_mult(
175                         vec_neg(rigid_bodies->velocities[i]),
176                         16.0f));
177             }
178         }
179
180         rigid_bodies->bodies[i] = platforms_snap_rect(platforms, rigid_bodies->bodies[i]);
181     }
182
183     return 0;
184 }
185
186 int rigid_bodies_collide(RigidBodies *rigid_bodies,
187                          const Platforms *platforms)
188 {
189     if (rigid_bodies_collide_with_itself(rigid_bodies) < 0) {
190         return -1;
191     }
192
193     if (rigid_bodies_collide_with_platforms(rigid_bodies, platforms) < 0) {
194         return -1;
195     }
196
197     return 0;
198 }
199
200 int rigid_bodies_update(RigidBodies *rigid_bodies,
201                         float delta_time)
202 {
203     trace_assert(rigid_bodies);
204
205     memset(rigid_bodies->grounded, 0,
206            sizeof(bool) * rigid_bodies->count);
207
208     for (size_t i = 0; i < rigid_bodies->count; ++i) {
209         rigid_bodies->velocities[i] = vec_sum(
210             rigid_bodies->velocities[i],
211             vec_scala_mult(
212                 rigid_bodies->forces[i],
213                 delta_time));
214     }
215
216     for (size_t i = 0; i < rigid_bodies->count; ++i) {
217         Vec position = vec(rigid_bodies->bodies[i].x,
218                            rigid_bodies->bodies[i].y);
219
220         position = vec_sum(
221             position,
222             vec_scala_mult(
223                 vec_sum(
224                     rigid_bodies->velocities[i],
225                     rigid_bodies->movements[i]),
226                 delta_time));
227
228         rigid_bodies->bodies[i].x = position.x;
229         rigid_bodies->bodies[i].y = position.y;
230     }
231
232     memset(rigid_bodies->forces, 0,
233            sizeof(Vec) * rigid_bodies->count);
234
235     return 0;
236 }
237
238 int rigid_bodies_render(RigidBodies *rigid_bodies,
239                         Camera *camera)
240 {
241     trace_assert(rigid_bodies);
242     trace_assert(camera);
243
244     for (size_t i = 0; i < rigid_bodies->count; ++i) {
245         if (camera_fill_rect(
246                 camera,
247                 rigid_bodies->bodies[i],
248                 rigid_bodies->colors[i]) < 0) {
249             return -1;
250         }
251
252         snprintf(rigid_bodies->id, ID_SIZE, "%ld", i);
253
254         if (camera_render_debug_text(
255                 camera,
256                 rigid_bodies->id,
257                 vec(rigid_bodies->bodies[i].x,
258                     rigid_bodies->bodies[i].y)) < 0) {
259             return -1;
260         }
261     }
262
263     return 0;
264 }
265
266 RigidBodyId rigid_bodies_add(RigidBodies *rigid_bodies,
267                              Rect rect,
268                              Color color)
269 {
270     trace_assert(rigid_bodies);
271     trace_assert(rigid_bodies->count < rigid_bodies->capacity);
272
273     RigidBodyId id = rigid_bodies->count++;
274     rigid_bodies->bodies[id] = rect;
275     rigid_bodies->colors[id] = color;
276
277     return id;
278 }
279
280 Rect rigid_bodies_hitbox(const RigidBodies *rigid_bodies,
281                          RigidBodyId id)
282 {
283     trace_assert(rigid_bodies);
284     trace_assert(id < rigid_bodies->count);
285
286     return rigid_bodies->bodies[id];
287 }
288
289 void rigid_bodies_move(RigidBodies *rigid_bodies,
290                        RigidBodyId id,
291                        Vec movement)
292 {
293     trace_assert(rigid_bodies);
294     trace_assert(id < rigid_bodies->count);
295
296     rigid_bodies->movements[id] = movement;
297 }
298
299 int rigid_bodies_touches_ground(const RigidBodies *rigid_bodies,
300                                 RigidBodyId id)
301 {
302     trace_assert(rigid_bodies);
303     trace_assert(id < rigid_bodies->count);
304
305     return rigid_bodies->grounded[id];
306 }
307
308 void rigid_bodies_apply_omniforce(RigidBodies *rigid_bodies,
309                                   Vec force)
310 {
311     for (size_t i = 0; i < rigid_bodies->count; ++i) {
312         rigid_bodies_apply_force(rigid_bodies, i, force);
313     }
314 }
315
316 void rigid_bodies_apply_force(RigidBodies * rigid_bodies,
317                               RigidBodyId id,
318                               Vec force)
319 {
320     trace_assert(rigid_bodies);
321     trace_assert(id < rigid_bodies->count);
322
323     rigid_bodies->forces[id] = vec_sum(rigid_bodies->forces[id], force);
324 }
325
326 void rigid_bodies_transform_velocity(RigidBodies *rigid_bodies,
327                                      RigidBodyId id,
328                                      mat3x3 trans_mat)
329 {
330     trace_assert(rigid_bodies);
331     trace_assert(id < rigid_bodies->count);
332
333     rigid_bodies->velocities[id] = point_mat3x3_product(
334         rigid_bodies->velocities[id],
335         trans_mat);
336 }
337
338 void rigid_bodies_teleport_to(RigidBodies *rigid_bodies,
339                               RigidBodyId id,
340                               Vec position)
341 {
342     trace_assert(rigid_bodies);
343     trace_assert(id < rigid_bodies->count);
344
345     rigid_bodies->bodies[id].x = position.x;
346     rigid_bodies->bodies[id].y = position.y;
347 }
348
349 void rigid_bodies_damper(RigidBodies *rigid_bodies,
350                          RigidBodyId id,
351                          Vec v)
352 {
353     trace_assert(rigid_bodies);
354     trace_assert(id < rigid_bodies->count);
355
356     rigid_bodies_apply_force(
357         rigid_bodies, id,
358         vec(
359             rigid_bodies->velocities[id].x * v.x,
360             rigid_bodies->velocities[id].y * v.y));
361 }