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