ST_engine  0.3-ALPHA
drawing_manager.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <drawing_manager/drawing_manager.hpp>
11 #include <ST_util/string_util.hpp>
12 #include "main/metrics.hpp"
13 #include "main/timer.hpp"
14 
15 static bool singleton_initialized = false;
16 
23 drawing_manager::drawing_manager(SDL_Window *window, message_bus &gMessageBus) : gMessage_bus(gMessageBus) {
24 
25  if (singleton_initialized) {
26  throw std::runtime_error("The drawing manager cannot be initialized more than once!");
27  } else {
28  singleton_initialized = true;
29  }
30 
31  if (TTF_Init() < 0) {
32  fprintf(stderr, "Failed to initialize SDL_TTF: %s\n", TTF_GetError());
33  exit(1);
34  }
35 
36  //Subscribe to certain messages
37  gMessage_bus.subscribe(VSYNC_STATE, &msg_sub);
38  gMessage_bus.subscribe(SET_VSYNC, &msg_sub);
39  gMessage_bus.subscribe(SHOW_COLLISIONS, &msg_sub);
40  gMessage_bus.subscribe(SHOW_FPS, &msg_sub);
41  gMessage_bus.subscribe(SHOW_METRICS, &msg_sub);
42  gMessage_bus.subscribe(SET_DARKNESS, &msg_sub);
43  gMessage_bus.subscribe(SURFACES_ASSETS, &msg_sub);
44  gMessage_bus.subscribe(FONTS_ASSETS, &msg_sub);
45  gMessage_bus.subscribe(ENABLE_LIGHTING, &msg_sub);
46  gMessage_bus.subscribe(SET_INTERNAL_RESOLUTION, &msg_sub);
47 
48  //debug collisions aren't shown by default
49  collisions_shown = false;
50 
51  //Variables for lights
52  darkness_level = 0;
53  lights_quality = 5;
54 
55  //hash of default font
56  default_font_normal = ST::hash_string(DEFAULT_FONT_NORMAL);
57  default_font_small = ST::hash_string(DEFAULT_FONT_SMALL);
58 
59  //Initialize the rendering object
60  ST::renderer_sdl::initialize(window, w_width, w_height);
61  uint32_t screen_width_height = w_width | static_cast<uint32_t>(w_height << 16U);
62  gMessage_bus.send_msg(new message(VIRTUAL_SCREEN_COORDINATES, screen_width_height));
63 }
64 
72 void drawing_manager::update(const ST::level &temp, float fps, console &gConsole, ST::metrics metrics) {
73  camera = temp.camera;
74  handle_messages();
75 
76  ticks = SDL_GetTicks(); //CPU ticks since start
77  ST::renderer_sdl::clear_screen(temp.background_color);
78 
79  draw_background(temp.background, temp.parallax_speed);
80 
81  std::vector<ST::entity> entities{};
82  std::copy_if(temp.entities.begin(), temp.entities.end(), std::back_inserter(entities), [this](ST::entity e) {
83  return is_onscreen(e);
84  });
85 
86  draw_entities(entities);
87  ST::renderer_sdl::draw_overlay(temp.overlay, static_cast<uint8_t>(ticks % temp.overlay_sprite_num),
88  temp.overlay_sprite_num);
89  draw_text_objects(temp.text_objects);
90 
91  if (lighting_enabled) {
92  process_lights(temp.lights);
93  draw_lights();
94  }
95 
96  if (collisions_shown) {
97  draw_collisions(entities);
98  draw_coordinates(entities);
99  }
100  draw_fps(fps);
101 
102  if (!gConsole.is_open()) {
103  draw_metrics(metrics, temp, entities);
104  } else {
105  draw_console(gConsole);
106  }
107 
108 
110 }
111 
116 void drawing_manager::draw_text_objects(const std::vector<ST::text> &objects) const {
117  for (auto &i: objects) {
118  if (is_onscreen(i)) {
119  ST::renderer_sdl::draw_text_lru_cached(i.font, i.text_string, i.x, i.y, i.color);
120  }
121  }
122 }
123 
128 void drawing_manager::draw_fps(float fps) const {
129  if (show_fps) {
130  SDL_Color color_font = {255, 0, 255, 255};
131  ST::renderer_sdl::draw_text_cached_glyphs(default_font_normal,
132  "fps:" + std::to_string(static_cast<int32_t>(fps)), 0, 50,
133  color_font);
134  }
135 }
136 
141 void drawing_manager::draw_metrics(ST::metrics metrics, const ST::level &level,
142  const std::vector<ST::entity> &entities) const {
143  if (metrics_shown) {
144  SDL_Color color_font = {255, 0, 255, 255};
146  "game_logic_time:" + std::to_string(metrics.game_logic_time), 0, 100,
147  color_font);
149  "physics_time:" + std::to_string(metrics.physics_time), 0, 130,
150  color_font);
152  "render_time:" + std::to_string(metrics.render_time), 0, 160,
153  color_font);
155  "frame_time:" + std::to_string(metrics.frame_time), 0, 190,
156  color_font);
158  "entities_on_screen:" + std::to_string(entities.size()), 0, 220,
159  color_font);
161  "physics_objects:" + std::to_string(level.physics_objects_count), 0,
162  250, color_font);
164  "entities_total:" + std::to_string(level.entities.size()), 0, 280,
165  color_font);
166  }
167 }
168 
173 void drawing_manager::draw_console(console &console) const {
174  ST::renderer_sdl::draw_rectangle_filled(0, 0, w_width, w_height / 2, console.color);
175  int pos = w_height / 2;
176  SDL_Color log_entry_color;
177  for (auto i = console.entries.rbegin(); i != console.entries.rend(); ++i) {
178  int pos_offset = pos - console.font_size + console.scroll_offset;
179  if (pos_offset > 0 && pos_offset <= w_height / 2 + 50 - console.font_size * 2) {
180  switch (i->type) {
181  case ST::log_type::ERROR:
182  log_entry_color = console.color_error;
183  break;
184 
185  case ST::log_type::INFO:
186  log_entry_color = console.color_info;
187  break;
188 
189  default:
190  log_entry_color = console.color_success;
191  break;
192  }
193  ST::renderer_sdl::draw_text_cached_glyphs(default_font_normal, i->text, 0,
194  pos - console.font_size - 20 + console.scroll_offset,
195  log_entry_color);
196  }
197  pos -= console.font_size + 5;
198  }
199  ST::renderer_sdl::draw_rectangle_filled(0, w_height / 2 - console.font_size - 12, w_width, 3, console.color_text);
200  int32_t cursor_draw_position;
201  if (console.cursor_position == console.composition.size()) {
202  cursor_draw_position = ST::renderer_sdl::draw_text_cached_glyphs(default_font_normal,
203  "Input: " + console.composition, 0,
204  w_height / 2, console.color_text);
205  } else {
206  std::string to_cursor = console.composition.substr(0, console.cursor_position);
207  std::string after_cursor = console.composition.substr(console.cursor_position, INT_MAX);
208  cursor_draw_position = ST::renderer_sdl::draw_text_cached_glyphs(default_font_normal, "Input: " + to_cursor, 0,
209  w_height / 2, console.color_text);
210  ST::renderer_sdl::draw_text_cached_glyphs(default_font_normal, after_cursor, cursor_draw_position, w_height / 2,
211  console.color_text);
212  }
213  if (ticks - console.cursor_timer < 250 || console.cursor_timer == 0) {
215  cursor_draw_position, w_height / 2 - 50 + 5, 3,
216  console.font_size, console.color_text);
217  }
218  console.cursor_timer = (ticks - console.cursor_timer >= 500) * ticks +
219  (ticks - console.cursor_timer < 500) * console.cursor_timer;
220 }
221 
227 void drawing_manager::process_lights(const std::vector<ST::light> &lights) {
228  memset(lightmap, darkness_level, sizeof lightmap);
229  for (const auto &light: lights) {
230  int x = !light.is_static * (light.origin_x - camera.x) + light.is_static * light.origin_x;
231  int y = !light.is_static * (light.origin_y - camera.y) + light.is_static * light.origin_y;
232  double count = 0;
233  double step = darkness_level / static_cast<double>(light.radius);
234  count = 0;
235  int radius = light.radius;
236  int intensity = light.intensity;
237  if (x - radius - intensity > w_width || y - radius - intensity > w_height) {
238  continue;
239  }
240  double step2 = 0;
241  for (int i = y; i < y + radius + intensity; ++i) {
242  for (int j = x; j < x + radius + intensity; ++j) {
243  if (j > 0 && j < w_width && i > 0 && i < w_height) {
244  lightmap[j][i] = (uint8_t) light.brightness + static_cast<uint8_t>(count);
245  }
246  if (count + light.brightness < darkness_level && j > x + intensity) {
247  count += step;
248  }
249  }
250  count = 0;
251  count += step2;
252  if (step2 + light.brightness < darkness_level && i > y + intensity)
253  step2 += step;
254  }
255  count = 0;
256  step2 = 0;
257  for (int i = y; i > y - radius - intensity; --i) {
258  for (int j = x; j > x - radius - intensity; --j) {
259  if (j > 0 && j < w_width && i > 0 && i < w_height) {
260  lightmap[j][i] = light.brightness + (uint8_t) count;
261  }
262  if (count + light.brightness < darkness_level && j < x - intensity) {
263  count += step;
264  }
265  }
266  count = 0;
267  count += step2;
268  if (step2 + light.brightness < darkness_level && i < y - intensity) {
269  step2 += step;
270  }
271  }
272  count = 0;
273  step2 = 0;
274  for (int i = y; i > y - radius - intensity; --i) {
275  for (int j = x; j < x + radius + intensity; ++j) {
276  if (j > 0 && j < w_width && i > 0 && i < w_height) {
277  lightmap[j][i] = light.brightness + (uint8_t) count;
278  }
279  if (count + light.brightness < darkness_level && j > x + intensity) {
280  count += step;
281  }
282  }
283  count = 0;
284  count += step2;
285  if (step2 + light.brightness < darkness_level && i < y - intensity) {
286  step2 += step;
287  }
288  }
289  count = 0;
290  step2 = 0;
291  for (int i = y; i < y + radius + intensity; ++i) {
292  for (int j = x; j > x - radius - intensity; --j) {
293  if (j > 0 && j < w_width && i > 0 && i < w_height) {
294  lightmap[j][i] = light.brightness + (uint8_t) count;
295  }
296  if (count + light.brightness < darkness_level && j < x - intensity) {
297  count += step;
298  }
299  }
300  count = 0;
301  count += step2;
302  if (step2 + light.brightness < darkness_level && i > y + intensity) {
303  step2 += step;
304  }
305  }
306  }
307 }
308 
312 void drawing_manager::draw_lights() const {
313  //Losing a lot of fps here :)
314  SDL_Rect tempRect = {0, 0, lights_quality, lights_quality};
315  SDL_Color light_color;
316 
317  for (int i = 0; i <= w_width - lights_quality; i += lights_quality) {
318  int a = 1;
319  tempRect.x = i;
320  for (int j = 0; j <= w_height - lights_quality; j += lights_quality) {
321  if (lightmap[i][j] == lightmap[i][j + lights_quality] && j + lights_quality == w_height) {
322  tempRect.y = j - a * lights_quality;
323  tempRect.h = (a + 1) * lights_quality;
324  light_color = {0, 0, 0, lightmap[i][j]};
325  ST::renderer_sdl::draw_rectangle_filled(tempRect.x, tempRect.y, tempRect.w, tempRect.h, light_color);
326  } else if (lightmap[i][j] == lightmap[i][j + lights_quality]) {
327  a++;
328  } else {
329  tempRect.h = a * lights_quality;
330  tempRect.y = j - a * lights_quality;
331  light_color = {0, 0, 0, lightmap[i][j]};
332  ST::renderer_sdl::draw_rectangle_filled(tempRect.x, tempRect.y, tempRect.w, tempRect.h, light_color);
333  a = 1;
334  }
335  }
336  }
337 }
338 
343 void drawing_manager::handle_messages() {
344  message *temp = msg_sub.get_next_message();
345  while (temp != nullptr) {
346  switch (temp->msg_name) {
347  case SET_VSYNC: {
348  auto arg = static_cast<bool>(temp->base_data0);
349  if (arg) {
351  } else {
353  }
354  gMessage_bus.send_msg(new message(VSYNC_STATE, arg));
355  break;
356  }
357  case SET_DARKNESS:
358  set_darkness(static_cast<uint8_t>(temp->base_data0));
359  break;
360  case SHOW_COLLISIONS:
361  collisions_shown = static_cast<bool>(temp->base_data0);
362  break;
363  case SHOW_FPS:
364  show_fps = static_cast<bool>(temp->base_data0);
365  break;
366  case SHOW_METRICS:
367  metrics_shown = static_cast<bool>(temp->base_data0);
368  break;
369  case ENABLE_LIGHTING:
370  lighting_enabled = static_cast<bool>(temp->base_data0);
371  break;
372  case SURFACES_ASSETS: {
373  auto surfaces = *static_cast<ska::bytell_hash_map<uint16_t, SDL_Surface *> **>(temp->get_data());
375  break;
376  }
377  case FONTS_ASSETS: {
378  auto fonts = *static_cast<ska::bytell_hash_map<uint16_t, TTF_Font *> **>(temp->get_data());
380  break;
381  }
382  case SET_INTERNAL_RESOLUTION: {
383  auto data = temp->base_data0;
384  w_width = data & 0x0000ffffU;
385  w_height = (data >> 16U) & 0x0000ffffU;
386  gMessage_bus.send_msg(new message(VIRTUAL_SCREEN_COORDINATES, data));
387  ST::renderer_sdl::set_resolution(w_width, w_height);
388  break;
389  }
390  }
391  delete temp;
392  temp = msg_sub.get_next_message();
393  }
394 }
395 
400 void drawing_manager::set_darkness(uint8_t arg) {
401  darkness_level = arg;
402  memset(lightmap, arg, sizeof lightmap);
403 }
404 
409 void drawing_manager::draw_entities(const std::vector<ST::entity> &entities) const {
410  uint32_t time = ticks >> 7U; //ticks/128
411  for (auto &i: entities) {
412  int32_t camera_offset_x = (!i.is_static()) * camera.x; //If entity isn't static add camera offset
413  int32_t camera_offset_y = (camera_offset_x != 0) * camera.y;
415  i.x - camera_offset_x,
416  i.y - camera_offset_y,
417  time % i.sprite_num,
418  i.animation,
419  i.animation_num,
420  i.sprite_num,
421  i.tex_scale_x,
422  i.tex_scale_y);
423  }
424 }
425 
431 bool drawing_manager::is_onscreen(const ST::entity &i) const {
432  return i.is_visible() &&
433  (i.is_static() ||
434  (i.x - camera.x + static_cast<int>(static_cast<float>(i.tex_w) * i.tex_scale_x) >= 0 &&
435  i.x - camera.x <= w_width/* &&
436  i.y + camera.y > 0 && //TODO: Not working quite right
437  i.y - camera.y - static_cast<int>(static_cast<float>(i.tex_h) * i.tex_scale_y) <= w_height - static_cast<int>(static_cast<float>(i.tex_h) * i.tex_scale_y)*/));
438 }
439 
445 bool drawing_manager::is_onscreen(const ST::text &i) const {
446  return i.is_visible && i.x < camera.x + w_width; //TODO: finish
447 }
448 
453 void drawing_manager::draw_collisions(const std::vector<ST::entity> &entities) const {
454  for (auto &i: entities) {
455  int32_t x_offset = (!i.is_static()) * camera.x;
456  int32_t y_offset = (x_offset != 0) * camera.y;
457  uint8_t b = (!i.is_affected_by_physics()) * 220;
458  uint8_t r = (!b) * 240;
460  i.y - y_offset + i.get_col_y_offset(), i.get_col_x(), i.get_col_y(),
461  {r, 0, b, 100});
462  }
463 }
464 
469 void drawing_manager::draw_coordinates(const std::vector<ST::entity> &entities) const {
470  for (auto &i: entities) {
471  if (i.is_affected_by_physics()) {
472  int32_t x_offset = (!i.is_static()) * camera.x;
473  int32_t y_offset = (x_offset != 0) * camera.y;
474  SDL_Color colour_text = {255, 255, 0, 255};
475  ST::renderer_sdl::draw_text_cached_glyphs(default_font_small, "x: " + std::to_string(i.x), i.x - x_offset,
476  i.y - y_offset - i.tex_h, colour_text);
477  ST::renderer_sdl::draw_text_cached_glyphs(default_font_small, "y: " + std::to_string(i.y), i.x - x_offset,
478  i.y - y_offset - i.tex_h + 30, colour_text);
479  }
480  }
481 }
482 
488 void drawing_manager::draw_background(const uint16_t background[PARALLAX_BG_LAYERS],
489  const uint8_t parallax_speed[PARALLAX_BG_LAYERS]) const {
490  for (uint8_t i = 0; i < PARALLAX_BG_LAYERS; i++) {
492  (camera.x * (parallax_speed[i] << 3)) / (w_width >> 1) % w_width);
493  }
494 }
495 
502  TTF_Quit();
503  singleton_initialized = false;
504 }
This class represents all static or dynamic objects in the game (excluding text, see ST::text)
Definition: entity.hpp:24
int32_t get_col_y() const
Definition: entity.hpp:170
int16_t get_col_x_offset() const
Definition: entity.hpp:178
int16_t get_col_y_offset() const
Definition: entity.hpp:186
int32_t get_col_x() const
Definition: entity.hpp:162
This object contains all the data for a level and provides functions for loading and unloading a leve...
Definition: level.hpp:29
This object represents the console window.
Definition: console.hpp:17
bool is_open() const
Definition: console.cpp:227
drawing_manager(SDL_Window *window, message_bus &gMessageBus)
void update(const ST::level &temp, float fps, console &gConsole, ST::metrics metrics)
An fps counter.
Definition: fps.hpp:21
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
void * get_data() const
Definition: message.hpp:80
message * get_next_message()
Definition: subscriber.hpp:39
uint16_t draw_text_lru_cached(uint16_t font, const std::string &arg2, int x, int y, SDL_Color color_font)
void upload_fonts(ska::bytell_hash_map< uint16_t, TTF_Font * > *fonts)
void upload_surfaces(ska::bytell_hash_map< uint16_t, SDL_Surface * > *surfaces)
void draw_overlay(uint16_t arg, uint8_t sprite, uint8_t sprite_num)
void draw_sprite_scaled(uint16_t arg, int32_t x, int32_t y, uint8_t sprite, uint8_t animation, uint8_t animation_num, uint8_t sprite_num, float scale_x, float scale_y)
void clear_screen(SDL_Color color)
uint16_t draw_text_cached_glyphs(uint16_t font, const std::string &text, int x, int y, SDL_Color color_font)
void draw_background_parallax(uint16_t arg, uint16_t offset)
int8_t initialize(SDL_Window *win, int16_t width, int16_t height)
void set_resolution(int16_t r_width, int16_t r_height)
void draw_rectangle_filled(int32_t x, int32_t y, int32_t w, int32_t h, SDL_Color color)
This struct represents text objects in the game.
Definition: text.hpp:23