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