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