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