]> git.lizzy.rs Git - nothing.git/blob - src/game/camera.c
Add TODO(#982)
[nothing.git] / src / game / camera.c
1 #include <SDL.h>
2 #include "system/stacktrace.h"
3 #include <math.h>
4 #include <stdbool.h>
5
6 #include "camera.h"
7 #include "sdl/renderer.h"
8 #include "system/nth_alloc.h"
9 #include "system/log.h"
10
11 #define RATIO_X 16.0f
12 #define RATIO_Y 9.0f
13
14 struct Camera {
15     bool debug_mode;
16     bool blackwhite_mode;
17     Point position;
18     float scale;
19     SDL_Renderer *renderer;
20     Sprite_font *font;
21 };
22
23 static Vec effective_ratio(const SDL_Rect *view_port);
24 static Vec effective_scale(const SDL_Rect *view_port);
25 static Triangle camera_triangle(const Camera *camera,
26                                 const Triangle t);
27
28 Camera *create_camera(SDL_Renderer *renderer,
29                       Sprite_font *font)
30 {
31     trace_assert(renderer);
32     trace_assert(font);
33
34     Camera *camera = nth_calloc(1, sizeof(Camera));
35
36     if (camera == NULL) {
37         return NULL;
38     }
39
40     camera->position = vec(0.0f, 0.0f);
41     camera->scale = 1.0f;
42     camera->debug_mode = 0;
43     camera->blackwhite_mode = 0;
44     camera->renderer = renderer;
45     camera->font = font;
46
47     return camera;
48 }
49
50 void destroy_camera(Camera *camera)
51 {
52     trace_assert(camera);
53
54     free(camera);
55 }
56
57 int camera_fill_rect(Camera *camera,
58                      Rect rect,
59                      Color color)
60 {
61     trace_assert(camera);
62
63     const SDL_Rect sdl_rect = rect_for_sdl(
64         camera_rect(camera, rect));
65
66     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
67
68     if (camera->debug_mode) {
69         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a / 2) < 0) {
70             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
71             return -1;
72         }
73     } else {
74         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
75             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
76             return -1;
77         }
78     }
79
80     if (SDL_RenderFillRect(camera->renderer, &sdl_rect) < 0) {
81         log_fail("SDL_RenderFillRect: %s\n", SDL_GetError());
82         return -1;
83     }
84
85     return 0;
86 }
87
88 int camera_draw_rect(Camera *camera,
89                      Rect rect,
90                      Color color)
91 {
92     trace_assert(camera);
93
94     const SDL_Rect sdl_rect = rect_for_sdl(
95         camera_rect(camera, rect));
96
97     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
98
99     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
100         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
101         return -1;
102     }
103
104     if (SDL_RenderDrawRect(camera->renderer, &sdl_rect) < 0) {
105         log_fail("SDL_RenderDrawRect: %s\n", SDL_GetError());
106         return -1;
107     }
108
109     return 0;
110 }
111
112 int camera_draw_rect_screen(Camera *camera,
113                             Rect rect,
114                             Color color)
115 {
116     trace_assert(camera);
117
118     const SDL_Rect sdl_rect = rect_for_sdl(rect);
119     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
120
121     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
122         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
123         return -1;
124     }
125
126     if (SDL_RenderDrawRect(camera->renderer, &sdl_rect) < 0) {
127         log_fail("SDL_RenderDrawRect: %s\n", SDL_GetError());
128         return -1;
129     }
130
131     return 0;
132 }
133
134 int camera_draw_triangle(Camera *camera,
135                          Triangle t,
136                          Color color)
137 {
138     trace_assert(camera);
139
140     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
141
142     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
143         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
144         return -1;
145     }
146
147     if (draw_triangle(camera->renderer, camera_triangle(camera, t)) < 0) {
148         return -1;
149     }
150
151     return 0;
152 }
153
154 int camera_fill_triangle(Camera *camera,
155                          Triangle t,
156                          Color color)
157 {
158     trace_assert(camera);
159
160     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
161
162     if (camera->debug_mode) {
163         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a / 2) < 0) {
164             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
165             return -1;
166         }
167     } else {
168         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
169             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
170             return -1;
171         }
172     }
173
174     if (fill_triangle(camera->renderer, camera_triangle(camera, t)) < 0) {
175         return -1;
176     }
177
178     return 0;
179 }
180
181 int camera_render_text(Camera *camera,
182                        const char *text,
183                        Vec size,
184                        Color c,
185                        Vec position)
186 {
187     SDL_Rect view_port;
188     SDL_RenderGetViewport(camera->renderer, &view_port);
189
190     const Vec scale = effective_scale(&view_port);
191     const Vec screen_position = camera_point(camera, position);
192
193     if (sprite_font_render_text(
194             camera->font,
195             camera->renderer,
196             screen_position,
197             vec(size.x * scale.x * camera->scale, size.y * scale.y * camera->scale),
198             camera->blackwhite_mode ? color_desaturate(c) : c,
199             text) < 0) {
200         return -1;
201     }
202
203     return 0;
204 }
205
206 int camera_render_debug_text(Camera *camera,
207                              const char *text,
208                              Vec position)
209 {
210     trace_assert(camera);
211     trace_assert(text);
212
213     if (!camera->debug_mode) {
214         return 0;
215     }
216
217     if (camera_render_text(
218             camera,
219             text,
220             vec(2.0f, 2.0f),
221             rgba(0.0f, 0.0f, 0.0f, 1.0f),
222             position) < 0) {
223         return -1;
224     }
225
226     return 0;
227 }
228
229 int camera_clear_background(Camera *camera,
230                             Color color)
231 {
232     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
233
234     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
235         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
236         return -1;
237     }
238
239     if (SDL_RenderClear(camera->renderer) < 0) {
240         log_fail("SDL_RenderClear: %s\n", SDL_GetError());
241         return -1;
242     }
243
244     return 0;
245 }
246
247 void camera_center_at(Camera *camera, Point position)
248 {
249     trace_assert(camera);
250     camera->position = position;
251 }
252
253 void camera_scale(Camera *camera, float scale)
254 {
255     trace_assert(camera);
256     camera->scale = fmaxf(0.1f, scale);
257 }
258
259 void camera_toggle_debug_mode(Camera *camera)
260 {
261     trace_assert(camera);
262     camera->debug_mode = !camera->debug_mode;
263 }
264
265 void camera_disable_debug_mode(Camera *camera)
266 {
267     trace_assert(camera);
268     camera->debug_mode = 0;
269 }
270
271 void camera_toggle_blackwhite_mode(Camera *camera)
272 {
273     trace_assert(camera);
274     camera->blackwhite_mode = !camera->blackwhite_mode;
275 }
276
277 int camera_is_point_visible(const Camera *camera, Point p)
278 {
279     SDL_Rect view_port;
280     SDL_RenderGetViewport(camera->renderer, &view_port);
281
282     return rect_contains_point(
283         rect_from_sdl(&view_port),
284         camera_point(camera, p));
285 }
286
287 Rect camera_view_port(const Camera *camera)
288 {
289     trace_assert(camera);
290
291     SDL_Rect view_port;
292     SDL_RenderGetViewport(camera->renderer, &view_port);
293
294     const Vec s = effective_scale(&view_port);
295     const float w = (float) view_port.w * s.x;
296     const float h = (float) view_port.h * s.y;
297
298     return rect(camera->position.x - w * 0.5f,
299                 camera->position.y - h * 0.5f,
300                 w, h);
301 }
302
303 Rect camera_view_port_screen(const Camera *camera)
304 {
305     trace_assert(camera);
306
307     SDL_Rect view_port;
308     SDL_RenderGetViewport(camera->renderer, &view_port);
309
310     return rect_from_sdl(&view_port);
311 }
312
313 int camera_is_text_visible(const Camera *camera,
314                            Vec size,
315                            Vec position,
316                            const char *text)
317 {
318     trace_assert(camera);
319     trace_assert(text);
320
321     SDL_Rect view_port;
322     SDL_RenderGetViewport(camera->renderer, &view_port);
323
324     return rects_overlap(
325         camera_rect(
326             camera,
327             sprite_font_boundary_box(
328                 camera->font,
329                 position,
330                 size,
331                 text)),
332         rect_from_sdl(&view_port));
333 }
334
335 /* ---------- Private Function ---------- */
336
337 static Vec effective_ratio(const SDL_Rect *view_port)
338 {
339     if ((float) view_port->w / RATIO_X > (float) view_port->h / RATIO_Y) {
340         return vec(RATIO_X, (float) view_port->h / ((float) view_port->w / RATIO_X));
341     } else {
342         return vec((float) view_port->w / ((float) view_port->h / RATIO_Y), RATIO_Y);
343     }
344 }
345
346 static Vec effective_scale(const SDL_Rect *view_port)
347 {
348     return vec_entry_div(
349         vec((float) view_port->w, (float) view_port->h),
350         vec_scala_mult(effective_ratio(view_port), 50.0f));
351 }
352
353 Vec camera_point(const Camera *camera, const Vec p)
354 {
355     SDL_Rect view_port;
356     SDL_RenderGetViewport(camera->renderer, &view_port);
357
358     return vec_sum(
359         vec_scala_mult(
360             vec_entry_mult(
361                 vec_sum(p, vec_neg(camera->position)),
362                 effective_scale(&view_port)),
363             camera->scale),
364         vec((float) view_port.w * 0.5f,
365             (float) view_port.h * 0.5f));
366 }
367
368 static Triangle camera_triangle(const Camera *camera,
369                                 const Triangle t)
370 {
371     return triangle(
372         camera_point(camera, t.p1),
373         camera_point(camera, t.p2),
374         camera_point(camera, t.p3));
375 }
376
377 Rect camera_rect(const Camera *camera, const Rect rect)
378 {
379     return rect_from_points(
380         camera_point(
381             camera,
382             vec(rect.x, rect.y)),
383         camera_point(
384             camera,
385             vec(rect.x + rect.w, rect.y + rect.h)));
386 }
387
388 int camera_render_debug_rect(Camera *camera,
389                              Rect rect,
390                              Color c)
391 {
392     trace_assert(camera);
393
394     if (!camera->debug_mode) {
395         return 0;
396     }
397
398     if (camera_fill_rect(camera, rect, c) < 0) {
399         return -1;
400     }
401
402     return 0;
403 }
404
405 Vec camera_map_screen(const Camera *camera,
406                       Sint32 x, Sint32 y)
407 {
408     trace_assert(camera);
409
410     SDL_Rect view_port;
411     SDL_RenderGetViewport(camera->renderer, &view_port);
412
413     Vec es = effective_scale(&view_port);
414     es.x = 1.0f / es.x;
415     es.y = 1.0f / es.y;
416
417     const Vec p = vec((float) x, (float) y);
418
419     return vec_sum(
420         vec_entry_mult(
421             vec_scala_mult(
422                 vec_sum(
423                     p,
424                     vec((float) view_port.w * -0.5f,
425                         (float) view_port.h * -0.5f)),
426                 1.0f / camera->scale),
427             es),
428         camera->position);
429 }
430
431 int camera_fill_rect_screen(Camera *camera,
432                             Rect rect,
433                             Color color)
434 {
435     trace_assert(camera);
436
437     const SDL_Rect sdl_rect = rect_for_sdl(rect);
438     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
439
440     if (camera->debug_mode) {
441         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a / 2) < 0) {
442             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
443             return -1;
444         }
445     } else {
446         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
447             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
448             return -1;
449         }
450     }
451
452     if (SDL_RenderFillRect(camera->renderer, &sdl_rect) < 0) {
453         log_fail("SDL_RenderFillRect: %s\n", SDL_GetError());
454         return -1;
455     }
456
457     return 0;
458
459 }
460
461 int camera_render_text_screen(Camera *camera,
462                               const char *text,
463                               Vec size,
464                               Color color,
465                               Vec position)
466 {
467     trace_assert(camera);
468     trace_assert(text);
469
470     return sprite_font_render_text(
471         camera->font,
472         camera->renderer,
473         position,
474         size,
475         color,
476         text);
477 }
478
479 int camera_draw_thicc_rect_screen(Camera *camera,
480                                   Rect rect,
481                                   Color color,
482                                   float thiccness)
483 {
484     trace_assert(camera);
485
486     // Top
487     if (camera_fill_rect_screen(
488             camera,
489             horizontal_thicc_line(
490                rect.x,
491                rect.x + rect.w,
492                rect.y,
493                thiccness),
494             color) < 0) {
495         return -1;
496     }
497
498     // Bottom
499     if (camera_fill_rect_screen(
500             camera,
501             horizontal_thicc_line(
502                 rect.x,
503                 rect.x + rect.w,
504                 rect.y + rect.h,
505                 thiccness),
506             color) < 0) {
507         return -1;
508     }
509
510     // Left
511     if (camera_fill_rect_screen(
512             camera,
513             vertical_thicc_line(
514                 rect.y,
515                 rect.y + rect.h,
516                 rect.x,
517                 thiccness),
518             color) < 0) {
519         return -1;
520     }
521
522     // Right
523     if (camera_fill_rect_screen(
524             camera,
525             vertical_thicc_line(
526                 rect.y,
527                 rect.y + rect.h,
528                 rect.x + rect.w,
529                 thiccness),
530             color) < 0) {
531         return -1;
532     }
533
534     return 0;
535 }
536
537 const Sprite_font *camera_font(const Camera *camera)
538 {
539     return camera->font;
540 }