]> git.lizzy.rs Git - nothing.git/blob - src/game/camera.c
Merge pull request #266 from tsoding/265
[nothing.git] / src / game / camera.c
1 #include <SDL2/SDL.h>
2 #include <assert.h>
3 #include <math.h>
4
5 #include "camera.h"
6 #include "sdl/renderer.h"
7 #include "system/error.h"
8
9 #define RATIO_X 16.0f
10 #define RATIO_Y 9.0f
11
12 struct camera_t {
13     int debug_mode;
14     int blackwhite_mode;
15     point_t position;
16     SDL_Renderer *renderer;
17     sprite_font_t *font;
18 };
19
20 static vec_t effective_ratio(const SDL_Rect *view_port);
21 static vec_t effective_scale(const SDL_Rect *view_port);
22 static vec_t camera_point(const camera_t *camera,
23                           const SDL_Rect *view_port,
24                           const vec_t p);
25 static rect_t camera_rect(const camera_t *camera,
26                           const SDL_Rect *view_port,
27                           const rect_t rect);
28 static triangle_t camera_triangle(const camera_t *camera,
29                                   const SDL_Rect *view_port,
30                                   const triangle_t t);
31
32 camera_t *create_camera(SDL_Renderer *renderer,
33                         sprite_font_t *font)
34 {
35     camera_t *camera = malloc(sizeof(camera_t));
36
37     if (camera == NULL) {
38         throw_error(ERROR_TYPE_LIBC);
39         return NULL;
40     }
41
42     camera->position = vec(0.0f, 0.0f);
43     camera->debug_mode = 0;
44     camera->blackwhite_mode = 0;
45     camera->renderer = renderer;
46     camera->font = font;
47
48     return camera;
49 }
50
51 void destroy_camera(camera_t *camera)
52 {
53     assert(camera);
54
55     free(camera);
56 }
57
58
59 int camera_fill_rect(camera_t *camera,
60                      rect_t rect,
61                      color_t 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             throw_error(ERROR_TYPE_SDL2);
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             throw_error(ERROR_TYPE_SDL2);
81             return -1;
82         }
83     }
84
85     if (SDL_RenderFillRect(camera->renderer, &sdl_rect) < 0) {
86         throw_error(ERROR_TYPE_SDL2);
87         return -1;
88     }
89
90     return 0;
91 }
92
93 int camera_draw_rect(camera_t * camera,
94                      rect_t rect,
95                      color_t 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         throw_error(ERROR_TYPE_SDL2);
109         return -1;
110     }
111
112     if (SDL_RenderDrawRect(camera->renderer, &sdl_rect) < 0) {
113         throw_error(ERROR_TYPE_SDL2);
114         return -1;
115     }
116
117     return 0;
118 }
119
120 int camera_draw_triangle(camera_t *camera,
121                          triangle_t t,
122                          color_t 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         throw_error(ERROR_TYPE_SDL2);
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_t *camera,
144                          triangle_t t,
145                          color_t 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             throw_error(ERROR_TYPE_SDL2);
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             throw_error(ERROR_TYPE_SDL2);
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_t *camera,
175                        const char *text,
176                        vec_t size,
177                        color_t c,
178                        vec_t position)
179 {
180     SDL_Rect view_port;
181     SDL_RenderGetViewport(camera->renderer, &view_port);
182
183     const vec_t scale = effective_scale(&view_port);
184     const vec_t 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_clear_background(camera_t *camera,
200                             color_t color)
201 {
202     const SDL_Color sdl_color = color_for_sdl(camera->blackwhite_mode ? color_desaturate(color) : color);
203
204     if (SDL_SetRenderDrawColor(camera->renderer, sdl_color.r, sdl_color.g, sdl_color.b, sdl_color.a) < 0) {
205         throw_error(ERROR_TYPE_SDL2);
206         return -1;
207     }
208
209     if (SDL_RenderClear(camera->renderer) < 0) {
210         throw_error(ERROR_TYPE_SDL2);
211         return -1;
212     }
213
214     return 0;
215 }
216
217 void camera_center_at(camera_t *camera, point_t position)
218 {
219     assert(camera);
220     camera->position = position;
221 }
222
223 void camera_toggle_debug_mode(camera_t *camera)
224 {
225     assert(camera);
226     camera->debug_mode = !camera->debug_mode;
227 }
228
229 void camera_disable_debug_mode(camera_t *camera)
230 {
231     assert(camera);
232     camera->debug_mode = 0;
233 }
234
235 void camera_toggle_blackwhite_mode(camera_t *camera)
236 {
237     assert(camera);
238     camera->blackwhite_mode = !camera->blackwhite_mode;
239 }
240
241 int camera_is_point_visible(const camera_t *camera, point_t p)
242 {
243     SDL_Rect view_port;
244     SDL_RenderGetViewport(camera->renderer, &view_port);
245
246     return rect_contains_point(
247         rect_from_sdl(&view_port),
248         camera_point(camera, &view_port, p));
249 }
250
251 rect_t camera_view_port(const camera_t *camera)
252 {
253     assert(camera);
254
255     SDL_Rect view_port;
256     SDL_RenderGetViewport(camera->renderer, &view_port);
257
258     const vec_t s = effective_scale(&view_port);
259     const float w = (float) view_port.w * s.x;
260     const float h = (float) view_port.h * s.y;
261
262     return rect(camera->position.x - w * 0.5f,
263                 camera->position.y - h * 0.5f,
264                 w, h);
265 }
266
267 int camera_is_text_visible(const camera_t *camera,
268                            vec_t size,
269                            vec_t position,
270                            const char *text)
271 {
272     assert(camera);
273     assert(text);
274
275     SDL_Rect view_port;
276     SDL_RenderGetViewport(camera->renderer, &view_port);
277
278     return rects_overlap(
279         camera_rect(
280             camera,
281             &view_port,
282             sprite_font_boundary_box(
283                 camera->font,
284                 position,
285                 size,
286                 text)),
287         rect_from_sdl(&view_port));
288 }
289
290 /* ---------- Private Function ---------- */
291
292 static vec_t effective_ratio(const SDL_Rect *view_port)
293 {
294     if ((float) view_port->w / RATIO_X > (float) view_port->h / RATIO_Y) {
295         return vec(RATIO_X, (float) view_port->h / ((float) view_port->w / RATIO_X));
296     } else {
297         return vec((float) view_port->w / ((float) view_port->h / RATIO_Y), RATIO_Y);
298     }
299 }
300
301 static vec_t effective_scale(const SDL_Rect *view_port)
302 {
303     return vec_entry_div(
304         vec((float) view_port->w, (float) view_port->h),
305         vec_scala_mult(effective_ratio(view_port), 50.0f));
306 }
307
308 static vec_t camera_point(const camera_t *camera,
309                           const SDL_Rect *view_port,
310                           const vec_t p)
311
312 {
313     return vec_sum(
314         vec_entry_mult(
315             vec_sum(p, vec_neg(camera->position)),
316             effective_scale(view_port)),
317         vec((float) view_port->w * 0.5f,
318             (float) view_port->h * 0.5f));
319 }
320
321 static triangle_t camera_triangle(const camera_t *camera,
322                                   const SDL_Rect *view_port,
323                                   const triangle_t t)
324 {
325     return triangle(
326         camera_point(camera, view_port, t.p1),
327         camera_point(camera, view_port, t.p2),
328         camera_point(camera, view_port, t.p3));
329 }
330
331 static rect_t camera_rect(const camera_t *camera,
332                           const SDL_Rect *view_port,
333                           const rect_t rect)
334 {
335     return rect_from_vecs(
336         camera_point(
337             camera,
338             view_port,
339             vec(rect.x, rect.y)),
340         vec_entry_mult(
341             effective_scale(view_port),
342             vec(rect.w, rect.h)));
343 }