]> git.lizzy.rs Git - nothing.git/blob - src/game/level/rigid_bodies.c
Merge pull request #684 from tsoding/631
[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 #include "system/line_stream.h"
10 #include "system/str.h"
11 #include "system/log.h"
12
13 #include "./rigid_bodies.h"
14
15 #define ID_SIZE 100
16 #define RIGID_BODIES_MAX_ID_SIZE 36
17
18 struct RigidBodies
19 {
20     Lt *lt;
21     // TODO(#685): capacity is not needed in RigidBodies
22     size_t capacity;
23     size_t count;
24
25     Rect *bodies;
26     Vec *velocities;
27     Vec *movements;
28     Color *colors;
29     bool *grounded;
30     Vec *forces;
31 };
32
33 RigidBodies *create_rigid_bodies(size_t capacity)
34 {
35     Lt *lt = create_lt();
36     if (lt == NULL) {
37         return NULL;
38     }
39
40     RigidBodies *rigid_bodies = PUSH_LT(lt, nth_calloc(1, sizeof(RigidBodies)), free);
41     if (rigid_bodies == NULL) {
42         RETURN_LT(lt, NULL);
43     }
44     rigid_bodies->lt = lt;
45
46     rigid_bodies->capacity = capacity;
47     rigid_bodies->count = 0;
48
49     rigid_bodies->bodies = PUSH_LT(lt, nth_calloc(capacity, sizeof(Rect)), free);
50     if (rigid_bodies->bodies == NULL) {
51         RETURN_LT(lt, NULL);
52     }
53
54     rigid_bodies->velocities = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
55     if (rigid_bodies->velocities == NULL) {
56         RETURN_LT(lt, NULL);
57     }
58
59     rigid_bodies->movements = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
60     if (rigid_bodies->movements == NULL) {
61         RETURN_LT(lt, NULL);
62     }
63
64     rigid_bodies->colors = PUSH_LT(lt, nth_calloc(capacity, sizeof(Color)), free);
65     if (rigid_bodies->colors == NULL) {
66         RETURN_LT(lt, NULL);
67     }
68
69     rigid_bodies->grounded = PUSH_LT(lt, nth_calloc(capacity, sizeof(bool)), free);
70     if (rigid_bodies->grounded == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     rigid_bodies->forces = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
75     if (rigid_bodies->forces == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     return rigid_bodies;
80 }
81
82 void destroy_rigid_bodies(RigidBodies *rigid_bodies)
83 {
84     trace_assert(rigid_bodies);
85     RETURN_LT0(rigid_bodies->lt);
86 }
87
88 static int rigid_bodies_collide_with_itself(RigidBodies *rigid_bodies)
89 {
90     trace_assert(rigid_bodies);
91
92     if (rigid_bodies->count == 0) {
93         return 0;
94     }
95
96     for (size_t i1 = 0; i1 < rigid_bodies->count - 1; ++i1) {
97         for (size_t i2 = i1 + 1; i2 < rigid_bodies->count; ++i2) {
98             if (!rects_overlap(rigid_bodies->bodies[i1], rigid_bodies->bodies[i2])) {
99                 continue;
100             }
101
102             rect_impulse(&rigid_bodies->bodies[i1], &rigid_bodies->bodies[i2]);
103
104             const Vec v1 = rigid_bodies->velocities[i1];
105             const Vec v2 = rigid_bodies->velocities[i2];
106             rigid_bodies->velocities[i1] = v2;
107             rigid_bodies->velocities[i2] = v1;
108         }
109     }
110
111     return 0;
112 }
113
114 static int rigid_bodies_collide_with_platforms(
115     RigidBodies *rigid_bodies,
116     const Platforms *platforms)
117 {
118     trace_assert(rigid_bodies);
119     trace_assert(platforms);
120
121     int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
122
123     for (size_t i = 0; i < rigid_bodies->count; ++i) {
124         memset(sides, 0, sizeof(int) * RECT_SIDE_N);
125
126         platforms_touches_rect_sides(platforms, rigid_bodies->bodies[i], sides);
127
128         if (sides[RECT_SIDE_BOTTOM]) {
129             rigid_bodies->grounded[i] = true;
130         }
131
132         Vec v = platforms_snap_rect(platforms, &rigid_bodies->bodies[i]);
133         rigid_bodies->velocities[i] = vec_entry_mult(rigid_bodies->velocities[i], v);
134         rigid_bodies->movements[i] = vec_entry_mult(rigid_bodies->movements[i], v);
135         rigid_bodies_damper(rigid_bodies, i, vec_entry_mult(v, vec(-16.0f, 0.0f)));
136     }
137
138     return 0;
139 }
140
141 int rigid_bodies_collide(RigidBodies *rigid_bodies,
142                          const Platforms *platforms)
143 {
144     // TODO(#683): RigidBodies should collide only the bodies that were updated on after a previous collision
145     memset(rigid_bodies->grounded, 0, sizeof(bool) * rigid_bodies->count);
146
147     if (rigid_bodies_collide_with_itself(rigid_bodies) < 0) {
148         return -1;
149     }
150
151     if (rigid_bodies_collide_with_platforms(rigid_bodies, platforms) < 0) {
152         return -1;
153     }
154
155     return 0;
156 }
157
158 int rigid_bodies_update(RigidBodies *rigid_bodies,
159                         RigidBodyId id,
160                         float delta_time)
161 {
162     trace_assert(rigid_bodies);
163
164     rigid_bodies->velocities[id] = vec_sum(
165             rigid_bodies->velocities[id],
166             vec_scala_mult(
167                 rigid_bodies->forces[id],
168                 delta_time));
169
170     Vec position = vec(rigid_bodies->bodies[id].x,
171                        rigid_bodies->bodies[id].y);
172
173     position = vec_sum(
174         position,
175         vec_scala_mult(
176             vec_sum(
177                 rigid_bodies->velocities[id],
178                 rigid_bodies->movements[id]),
179             delta_time));
180
181     rigid_bodies->bodies[id].x = position.x;
182     rigid_bodies->bodies[id].y = position.y;
183
184     rigid_bodies->forces[id] = vec(0.0f, 0.0f);
185
186     return 0;
187 }
188
189 int rigid_bodies_render(RigidBodies *rigid_bodies,
190                         RigidBodyId id,
191                         Camera *camera)
192 {
193     trace_assert(rigid_bodies);
194     trace_assert(camera);
195
196     char text_buffer[256];
197
198     if (camera_fill_rect(
199             camera,
200             rigid_bodies->bodies[id],
201             rigid_bodies->colors[id]) < 0) {
202         return -1;
203     }
204
205     snprintf(text_buffer, ID_SIZE, "id: %ld", id);
206
207     if (camera_render_debug_text(
208             camera,
209             text_buffer,
210             vec(rigid_bodies->bodies[id].x,
211                 rigid_bodies->bodies[id].y)) < 0) {
212         return -1;
213     }
214
215     snprintf(text_buffer, 256, "pos:(%f, %f)",
216              rigid_bodies->bodies[id].x,
217              rigid_bodies->bodies[id].y);
218     if (camera_render_debug_text(
219             camera,
220             text_buffer,
221             vec(rigid_bodies->bodies[id].x,
222                 rigid_bodies->bodies[id].y + FONT_CHAR_HEIGHT * 2.0f))) {
223         return -1;
224     }
225
226     return 0;
227 }
228
229 RigidBodyId rigid_bodies_add(RigidBodies *rigid_bodies,
230                              Rect rect,
231                              Color color)
232 {
233     trace_assert(rigid_bodies);
234     trace_assert(rigid_bodies->count < rigid_bodies->capacity);
235
236     RigidBodyId id = rigid_bodies->count++;
237     rigid_bodies->bodies[id] = rect;
238     rigid_bodies->colors[id] = color;
239
240     return id;
241 }
242
243 RigidBodyId rigid_bodies_add_from_line_stream(RigidBodies *rigid_bodies,
244                                               LineStream *line_stream)
245 {
246     trace_assert(rigid_bodies);
247     trace_assert(line_stream);
248
249     char color[7];
250     Rect rect;
251     // TODO(#686): id should be part of boxes
252     char id[RIGID_BODIES_MAX_ID_SIZE];
253
254     if (sscanf(line_stream_next(line_stream),
255                "%" STRINGIFY(RIGID_BODIES_MAX_ID_SIZE) "s%f%f%f%f%6s\n",
256                id,
257                &rect.x, &rect.y,
258                &rect.w, &rect.h,
259                color) < 0) {
260         log_fail("Could not read rigid rect\n");
261         // TODO(#687): rigid_bodies_add_from_line_stream cannot indicate an error properly
262         return 0;
263     }
264
265     return rigid_bodies_add(rigid_bodies, rect, hexstr(color));
266 }
267
268 Rect rigid_bodies_hitbox(const RigidBodies *rigid_bodies,
269                          RigidBodyId id)
270 {
271     trace_assert(rigid_bodies);
272     trace_assert(id < rigid_bodies->count);
273
274     return rigid_bodies->bodies[id];
275 }
276
277 void rigid_bodies_move(RigidBodies *rigid_bodies,
278                        RigidBodyId id,
279                        Vec movement)
280 {
281     trace_assert(rigid_bodies);
282     trace_assert(id < rigid_bodies->count);
283
284     rigid_bodies->movements[id] = movement;
285 }
286
287 int rigid_bodies_touches_ground(const RigidBodies *rigid_bodies,
288                                 RigidBodyId id)
289 {
290     trace_assert(rigid_bodies);
291     trace_assert(id < rigid_bodies->count);
292
293     return rigid_bodies->grounded[id];
294 }
295
296 void rigid_bodies_apply_omniforce(RigidBodies *rigid_bodies,
297                                   Vec force)
298 {
299     for (size_t i = 0; i < rigid_bodies->count; ++i) {
300         rigid_bodies_apply_force(rigid_bodies, i, force);
301     }
302 }
303
304 void rigid_bodies_apply_force(RigidBodies * rigid_bodies,
305                               RigidBodyId id,
306                               Vec force)
307 {
308     trace_assert(rigid_bodies);
309     trace_assert(id < rigid_bodies->count);
310
311     rigid_bodies->forces[id] = vec_sum(rigid_bodies->forces[id], force);
312 }
313
314 void rigid_bodies_transform_velocity(RigidBodies *rigid_bodies,
315                                      RigidBodyId id,
316                                      mat3x3 trans_mat)
317 {
318     trace_assert(rigid_bodies);
319     trace_assert(id < rigid_bodies->count);
320
321     rigid_bodies->velocities[id] = point_mat3x3_product(
322         rigid_bodies->velocities[id],
323         trans_mat);
324 }
325
326 void rigid_bodies_teleport_to(RigidBodies *rigid_bodies,
327                               RigidBodyId id,
328                               Vec position)
329 {
330     trace_assert(rigid_bodies);
331     trace_assert(id < rigid_bodies->count);
332
333     rigid_bodies->bodies[id].x = position.x;
334     rigid_bodies->bodies[id].y = position.y;
335 }
336
337 void rigid_bodies_damper(RigidBodies *rigid_bodies,
338                          RigidBodyId id,
339                          Vec v)
340 {
341     trace_assert(rigid_bodies);
342     trace_assert(id < rigid_bodies->count);
343
344     rigid_bodies_apply_force(
345         rigid_bodies, id,
346         vec(
347             rigid_bodies->velocities[id].x * v.x,
348             rigid_bodies->velocities[id].y * v.y));
349 }