]> git.lizzy.rs Git - nothing.git/blob - src/game/level/rigid_bodies.c
Merge pull request #646 from tsoding/639
[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 struct RigidBodies
13 {
14     Lt *lt;
15     size_t capacity;
16     size_t count;
17
18     Vec *positions;
19     Vec *velocities;
20     Vec *movements;
21     Vec *sizes;
22     Color *colors;
23     bool *grounded;
24     Vec *forces;
25 };
26
27 static const Vec opposing_rect_side_forces[RECT_SIDE_N] = {
28     { .x = 1.0f,  .y =  0.0f  },  /* RECT_SIDE_LEFT = 0, */
29     { .x = -1.0f, .y =  0.0f  },  /* RECT_SIDE_RIGHT, */
30     { .x = 0.0f,  .y =  1.0f, },  /* RECT_SIDE_TOP, */
31     { .x = 0.0f,  .y = -1.0f, }   /* RECT_SIDE_BOTTOM, */
32 };
33
34 static Vec opposing_force_by_sides(int sides[RECT_SIDE_N])
35 {
36     Vec opposing_force = {
37         .x = 0.0f,
38         .y = 0.0f
39     };
40
41     for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
42         if (sides[side]) {
43             vec_add(
44                 &opposing_force,
45                 opposing_rect_side_forces[side]);
46         }
47     }
48
49     return opposing_force;
50 }
51
52 RigidBodies *create_rigid_bodies(size_t capacity)
53 {
54     Lt *lt = create_lt();
55     if (lt == NULL) {
56         return NULL;
57     }
58
59     RigidBodies *rigid_bodies = PUSH_LT(lt, nth_calloc(1, sizeof(RigidBodies)), free);
60     if (rigid_bodies == NULL) {
61         RETURN_LT(lt, NULL);
62     }
63     rigid_bodies->lt = lt;
64
65     rigid_bodies->capacity = capacity;
66     rigid_bodies->count = 0;
67
68     rigid_bodies->positions = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
69     if (rigid_bodies->positions == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     rigid_bodies->velocities = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
74     if (rigid_bodies->velocities == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     rigid_bodies->movements = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
79     if (rigid_bodies->movements == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     rigid_bodies->sizes = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
84     if (rigid_bodies->sizes == NULL) {
85         RETURN_LT(lt, NULL);
86     }
87
88     rigid_bodies->colors = PUSH_LT(lt, nth_calloc(capacity, sizeof(Color)), free);
89     if (rigid_bodies->colors == NULL) {
90         RETURN_LT(lt, NULL);
91     }
92
93     rigid_bodies->grounded = PUSH_LT(lt, nth_calloc(capacity, sizeof(bool)), free);
94     if (rigid_bodies->grounded == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     rigid_bodies->forces = PUSH_LT(lt, nth_calloc(capacity, sizeof(Vec)), free);
99     if (rigid_bodies->forces == NULL) {
100         RETURN_LT(lt, NULL);
101     }
102
103     return rigid_bodies;
104 }
105
106 void destroy_rigid_bodies(RigidBodies *rigid_bodies)
107 {
108     trace_assert(rigid_bodies);
109     RETURN_LT0(rigid_bodies->lt);
110 }
111
112 int rigid_bodies_collide_with_itself(RigidBodies *rigid_bodies)
113 {
114     trace_assert(rigid_bodies);
115
116     /* TODO(#647): rigid_bodies_collide_with_itself is not implemented */
117
118     return 0;
119 }
120
121 int rigid_bodies_collide_with_platforms(
122     RigidBodies *rigid_bodies,
123     const Platforms *platforms)
124 {
125     trace_assert(rigid_bodies);
126     trace_assert(platforms);
127
128     int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
129
130     for (size_t i = 0; i < rigid_bodies->count; ++i) {
131         memset(sides, 0, sizeof(int) * RECT_SIDE_N);
132
133         platforms_touches_rect_sides(platforms, rigid_bodies_hitbox(rigid_bodies, i), sides);
134
135         if (sides[RECT_SIDE_BOTTOM]) {
136             rigid_bodies->grounded[i] = true;
137         }
138
139         Vec opforce_direction = opposing_force_by_sides(sides);
140
141         /* It's an opposing force that we apply to platforms. But
142          * since platforms are impenetrable, it's not needed
143          * here. Plus we are trying to apply that force to the rigid
144          * body itself, because I'm dumb. */
145         /*
146         rigid_bodies_apply_force(
147             rigid_bodies, i,
148             vec_scala_mult(
149                 vec_neg(vec_norm(opforce_direction)),
150                 vec_length(
151                     vec_sum(rigid_bodies->velocities[i],
152                             rigid_bodies->movements[i])) * 8.0f));
153         */
154
155         if (fabs(opforce_direction.x) > 1e-6 && (opforce_direction.x < 0.0f) != ((rigid_bodies->velocities[i].x + rigid_bodies->movements[i].x) < 0.0f)) {
156             rigid_bodies->velocities[i].x = 0.0f;
157             rigid_bodies->movements[i].x = 0.0f;
158         }
159
160         if (fabs(opforce_direction.y) > 1e-6 && (opforce_direction.y < 0.0f) != ((rigid_bodies->velocities[i].y + rigid_bodies->movements[i].y) < 0.0f)) {
161             rigid_bodies->velocities[i].y = 0.0f;
162             rigid_bodies->movements[i].y = 0.0f;
163
164             if (vec_length(rigid_bodies->velocities[i]) > 1e-6) {
165                 rigid_bodies_apply_force(
166                     rigid_bodies, i,
167                     vec_scala_mult(
168                         vec_neg(rigid_bodies->velocities[i]),
169                         16.0f));
170             }
171         }
172
173         for (int j = 0; j < 1000 && vec_length(opforce_direction) > 1e-6; ++j) {
174             rigid_bodies->positions[i] = vec_sum(
175                 rigid_bodies->positions[i],
176                 vec_scala_mult(
177                     opforce_direction,
178                     1e-2f));
179
180             memset(sides, 0, sizeof(int) * RECT_SIDE_N);
181             platforms_touches_rect_sides(platforms, rigid_bodies_hitbox(rigid_bodies, i), sides);
182             opforce_direction = opposing_force_by_sides(sides);
183         }
184     }
185
186     return 0;
187 }
188
189 int rigid_bodies_update(RigidBodies *rigid_bodies,
190                         float delta_time)
191 {
192     trace_assert(rigid_bodies);
193
194     memset(rigid_bodies->grounded, 0,
195            sizeof(bool) * rigid_bodies->count);
196
197     for (size_t i = 0; i < rigid_bodies->count; ++i) {
198         rigid_bodies->velocities[i] = vec_sum(
199             rigid_bodies->velocities[i],
200             vec_scala_mult(
201                 rigid_bodies->forces[i],
202                 delta_time));
203     }
204
205     for (size_t i = 0; i < rigid_bodies->count; ++i) {
206         rigid_bodies->positions[i] = vec_sum(
207             rigid_bodies->positions[i],
208             vec_scala_mult(
209                 vec_sum(
210                     rigid_bodies->velocities[i],
211                     rigid_bodies->movements[i]),
212                 delta_time));
213     }
214
215     memset(rigid_bodies->forces, 0,
216            sizeof(Vec) * rigid_bodies->count);
217
218     return 0;
219 }
220
221 int rigid_bodies_render(RigidBodies *rigid_bodies,
222                         Camera *camera)
223 {
224     trace_assert(rigid_bodies);
225     trace_assert(camera);
226
227     for (size_t i = 0; i < rigid_bodies->count; ++i) {
228         if (camera_fill_rect(
229                 camera,
230                 rect_from_vecs(
231                     rigid_bodies->positions[i],
232                     rigid_bodies->sizes[i]),
233                 rigid_bodies->colors[i]) < 0) {
234             return -1;
235         }
236     }
237
238     return 0;
239 }
240
241 RigidBodyId rigid_bodies_add(RigidBodies *rigid_bodies,
242                              Rect rect,
243                              Color color)
244 {
245     trace_assert(rigid_bodies);
246     trace_assert(rigid_bodies->count < rigid_bodies->capacity);
247
248     RigidBodyId id = rigid_bodies->count++;
249     rigid_bodies->positions[id] = vec(rect.x, rect.y);
250     rigid_bodies->sizes[id] = vec(rect.w, rect.h);
251     rigid_bodies->colors[id] = color;
252
253     return id;
254 }
255
256 Rect rigid_bodies_hitbox(const RigidBodies *rigid_bodies,
257                          RigidBodyId id)
258 {
259     trace_assert(rigid_bodies);
260     trace_assert(id < rigid_bodies->count);
261
262     return rect_from_vecs(rigid_bodies->positions[id],
263                           rigid_bodies->sizes[id]);
264 }
265
266 void rigid_bodies_move(RigidBodies *rigid_bodies,
267                        RigidBodyId id,
268                        Vec movement)
269 {
270     trace_assert(rigid_bodies);
271     trace_assert(id < rigid_bodies->count);
272
273     rigid_bodies->movements[id] = movement;
274 }
275
276 int rigid_bodies_touches_ground(const RigidBodies *rigid_bodies,
277                                 RigidBodyId id)
278 {
279     trace_assert(rigid_bodies);
280     trace_assert(id < rigid_bodies->count);
281
282     return rigid_bodies->grounded[id];
283 }
284
285 void rigid_bodies_apply_omniforce(RigidBodies *rigid_bodies,
286                                   Vec force)
287 {
288     for (size_t i = 0; i < rigid_bodies->count; ++i) {
289         rigid_bodies_apply_force(rigid_bodies, i, force);
290     }
291 }
292
293 void rigid_bodies_apply_force(RigidBodies * rigid_bodies,
294                               RigidBodyId id,
295                               Vec force)
296 {
297     trace_assert(rigid_bodies);
298     trace_assert(id < rigid_bodies->count);
299
300     rigid_bodies->forces[id] = vec_sum(rigid_bodies->forces[id], force);
301 }
302
303 void rigid_bodies_transform_velocity(RigidBodies *rigid_bodies,
304                                      RigidBodyId id,
305                                      mat3x3 trans_mat)
306 {
307     trace_assert(rigid_bodies);
308     trace_assert(id < rigid_bodies->count);
309
310     rigid_bodies->velocities[id] = point_mat3x3_product(
311         rigid_bodies->velocities[id],
312         trans_mat);
313 }
314
315 void rigid_bodies_teleport_to(RigidBodies *rigid_bodies,
316                               RigidBodyId id,
317                               Vec position)
318 {
319     trace_assert(rigid_bodies);
320     trace_assert(id < rigid_bodies->count);
321
322     rigid_bodies->positions[id] = position;
323 }
324
325 void rigid_bodies_damper(RigidBodies *rigid_bodies,
326                          RigidBodyId id,
327                          Vec v)
328 {
329     trace_assert(rigid_bodies);
330     trace_assert(id < rigid_bodies->count);
331
332     rigid_bodies_apply_force(
333         rigid_bodies, id,
334         vec(
335             rigid_bodies->velocities[id].x * v.x,
336             rigid_bodies->velocities[id].y * v.y));
337 }