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