]> git.lizzy.rs Git - nothing.git/blob - src/game/camera.c
(#572) Introduce quasiquote special form
[nothing.git] / src / game / camera.c
1 #include <SDL2/SDL.h>
2 #include <assert.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     SDL_Renderer *renderer;
19     Sprite_font *font;
20 };
21
22 static Vec effective_ratio(const SDL_Rect *view_port);
23 static Vec effective_scale(const SDL_Rect *view_port);
24 static Vec camera_point(const Camera *camera,
25                         const SDL_Rect *view_port,
26                         const Vec p);
27 static Rect camera_rect(const Camera *camera,
28                         const SDL_Rect *view_port,
29                         const Rect rect);
30 static Triangle camera_triangle(const Camera *camera,
31                                   const SDL_Rect *view_port,
32                                   const Triangle t);
33
34 Camera *create_camera(SDL_Renderer *renderer,
35                       Sprite_font *font)
36 {
37     Camera *camera = nth_alloc(sizeof(Camera));
38
39     if (camera == NULL) {
40         return NULL;
41     }
42
43     camera->position = vec(0.0f, 0.0f);
44     camera->debug_mode = 0;
45     camera->blackwhite_mode = 0;
46     camera->renderer = renderer;
47     camera->font = font;
48
49     return camera;
50 }
51
52 void destroy_camera(Camera *camera)
53 {
54     assert(camera);
55
56     free(camera);
57 }
58
59 int camera_fill_rect(Camera *camera,
60                      Rect rect,
61                      Color color)
62 {
63     assert(camera);
64
65     SDL_Rect view_port;
66     SDL_RenderGetViewport(camera->renderer, &view_port);
67
68     const SDL_Rect sdl_rect = rect_for_sdl(
69         camera_rect(camera, &view_port, rect));
70
71     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
72
73     if (camera->debug_mode) {
74         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a / 2) < 0) {
75             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
76             return -1;
77         }
78     } else {
79         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
80             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
81             return -1;
82         }
83     }
84
85     if (SDL_RenderFillRect(camera->renderer, &sdl_rect) < 0) {
86         log_fail("SDL_RenderFillRect: %s\n", SDL_GetError());
87         return -1;
88     }
89
90     return 0;
91 }
92
93 int camera_draw_rect(Camera * camera,
94                      Rect rect,
95                      Color color)
96 {
97     assert(camera);
98
99     SDL_Rect view_port;
100     SDL_RenderGetViewport(camera->renderer, &view_port);
101
102     const SDL_Rect sdl_rect = rect_for_sdl(
103         camera_rect(camera, &view_port, rect));
104
105     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
106
107     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
108         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
109         return -1;
110     }
111
112     if (SDL_RenderDrawRect(camera->renderer, &sdl_rect) < 0) {
113         log_fail("SDL_RenderDrawRect: %s\n", SDL_GetError());
114         return -1;
115     }
116
117     return 0;
118 }
119
120 int camera_draw_triangle(Camera *camera,
121                          Triangle t,
122                          Color color)
123 {
124     assert(camera);
125
126     SDL_Rect view_port;
127     SDL_RenderGetViewport(camera->renderer, &view_port);
128
129     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
130
131     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
132         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
133         return -1;
134     }
135
136     if (draw_triangle(camera->renderer, camera_triangle(camera, &view_port, t)) < 0) {
137         return -1;
138     }
139
140     return 0;
141 }
142
143 int camera_fill_triangle(Camera *camera,
144                          Triangle t,
145                          Color color)
146 {
147     assert(camera);
148
149     SDL_Rect view_port;
150     SDL_RenderGetViewport(camera->renderer, &view_port);
151
152     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
153
154
155     if (camera->debug_mode) {
156         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a / 2) < 0) {
157             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
158             return -1;
159         }
160     } else {
161         if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
162             log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
163             return -1;
164         }
165     }
166
167     if (fill_triangle(camera->renderer, camera_triangle(camera, &view_port, t)) < 0) {
168         return -1;
169     }
170
171     return 0;
172 }
173
174 int camera_render_text(Camera *camera,
175                        const char *text,
176                        Vec size,
177                        Color c,
178                        Vec position)
179 {
180     SDL_Rect view_port;
181     SDL_RenderGetViewport(camera->renderer, &view_port);
182
183     const Vec scale = effective_scale(&view_port);
184     const Vec screen_position = camera_point(camera, &view_port, position);
185
186     if (sprite_font_render_text(
187             camera->font,
188             camera->renderer,
189             screen_position,
190             vec(size.x * scale.x, size.y * scale.y),
191             camera->blackwhite_mode ? color_desaturate(c) : c,
192             text) < 0) {
193         return -1;
194     }
195
196     return 0;
197 }
198
199 int camera_render_debug_text(Camera *camera,
200                              const char *text,
201                              Vec position)
202 {
203     assert(camera);
204     assert(text);
205
206     if (!camera->debug_mode) {
207         return 0;
208     }
209
210     if (camera_render_text(
211             camera,
212             text,
213             vec(2.0f, 2.0f),
214             rgba(0.0f, 0.0f, 0.0f, 1.0f),
215             position) < 0) {
216         return -1;
217     }
218
219     return 0;
220 }
221
222 int camera_clear_background(Camera *camera,
223                             Color color)
224 {
225     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
226
227     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
228         log_fail("SDL_SetRenderDrawColor: %s\n", SDL_GetError());
229         return -1;
230     }
231
232     if (SDL_RenderClear(camera->renderer) < 0) {
233         log_fail("SDL_RenderClear: %s\n", SDL_GetError());
234         return -1;
235     }
236
237     return 0;
238 }
239
240 void camera_center_at(Camera *camera, Point position)
241 {
242     assert(camera);
243     camera->position = position;
244 }
245
246 void camera_toggle_debug_mode(Camera *camera)
247 {
248     assert(camera);
249     camera->debug_mode = !camera->debug_mode;
250 }
251
252 void camera_disable_debug_mode(Camera *camera)
253 {
254     assert(camera);
255     camera->debug_mode = 0;
256 }
257
258 void camera_toggle_blackwhite_mode(Camera *camera)
259 {
260     assert(camera);
261     camera->blackwhite_mode = !camera->blackwhite_mode;
262 }
263
264 int camera_is_point_visible(const Camera *camera, Point p)
265 {
266     SDL_Rect view_port;
267     SDL_RenderGetViewport(camera->renderer, &view_port);
268
269     return rect_contains_point(
270         rect_from_sdl(&view_port),
271         camera_point(camera, &view_port, p));
272 }
273
274 Rect camera_view_port(const Camera *camera)
275 {
276     assert(camera);
277
278     SDL_Rect view_port;
279     SDL_RenderGetViewport(camera->renderer, &view_port);
280
281     const Vec s = effective_scale(&view_port);
282     const float w = (float) view_port.w * s.x;
283     const float h = (float) view_port.h * s.y;
284
285     return rect(camera->position.x - w * 0.5f,
286                 camera->position.y - h * 0.5f,
287                 w, h);
288 }
289
290 int camera_is_text_visible(const Camera *camera,
291                            Vec size,
292                            Vec position,
293                            const char *text)
294 {
295     assert(camera);
296     assert(text);
297
298     SDL_Rect view_port;
299     SDL_RenderGetViewport(camera->renderer, &view_port);
300
301     return rects_overlap(
302         camera_rect(
303             camera,
304             &view_port,
305             sprite_font_boundary_box(
306                 camera->font,
307                 position,
308                 size,
309                 text)),
310         rect_from_sdl(&view_port));
311 }
312
313 /* ---------- Private Function ---------- */
314
315 static Vec 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 Vec 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 static Vec camera_point(const Camera *camera,
332                           const SDL_Rect *view_port,
333                           const Vec p)
334
335 {
336     return vec_sum(
337         vec_entry_mult(
338             vec_sum(p, vec_neg(camera->position)),
339             effective_scale(view_port)),
340         vec((float) view_port->w * 0.5f,
341             (float) view_port->h * 0.5f));
342 }
343
344 static Triangle camera_triangle(const Camera *camera,
345                                   const SDL_Rect *view_port,
346                                   const Triangle t)
347 {
348     return triangle(
349         camera_point(camera, view_port, t.p1),
350         camera_point(camera, view_port, t.p2),
351         camera_point(camera, view_port, t.p3));
352 }
353
354 static Rect camera_rect(const Camera *camera,
355                           const SDL_Rect *view_port,
356                           const Rect rect)
357 {
358     return rect_from_vecs(
359         camera_point(
360             camera,
361             view_port,
362             vec(rect.x, rect.y)),
363         vec_entry_mult(
364             effective_scale(view_port),
365             vec(rect.w, rect.h)));
366 }
367
368 int camera_render_debug_rect(Camera *camera,
369                              Rect rect,
370                              Color c)
371 {
372     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 }