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