A 3D game engine from scratch.
1// (c) 2020 Vlad-Stefan Harbuz <vlad@vladh.net>
2
3#include "debug_ui.hpp"
4#include "entities.hpp"
5#include "engine.hpp"
6#include "intrinsics.hpp"
7
8
9void debug_ui::render_debug_ui() {
10 engine::State *engine_state = engine::debug_get_engine_state();
11
12 WindowSize *window_size = core::get_window_size();
13
14 char debug_text[1 << 14];
15 size_t dt_size = sizeof(debug_text);
16
17 renderer::start_drawing_gui();
18
19 gui::tick_heading();
20
21 {
22 strcpy(debug_text, "Peony debug info: ");
23 strcat(debug_text, engine_state->current_scene_name);
24 gui::Container *container = gui::make_container(debug_text, v2(25.0f, 25.0f));
25
26 snprintf(debug_text, dt_size, "%ux%u", window_size->width, window_size->height);
27 gui::draw_named_value(container, "screen size", debug_text);
28
29 snprintf(debug_text, dt_size, "%ux%u",
30 window_size->screencoord_width, window_size->screencoord_height);
31 gui::draw_named_value(container, "window size", debug_text);
32
33 snprintf(debug_text, dt_size, "%u fps", engine_state->perf_counters.last_fps);
34 gui::draw_named_value(container, "fps", debug_text);
35
36 snprintf(debug_text, dt_size, "%.2f ms", engine_state->perf_counters.dt_average * 1000.0f);
37 gui::draw_named_value(container, "dt", debug_text);
38
39 snprintf(debug_text, dt_size, "%.2f", 1.0f + engine_state->timescale_diff);
40 gui::draw_named_value(container, "ts", debug_text);
41
42 snprintf(debug_text, dt_size, engine_state->is_world_loaded ? "yes" : "no");
43 gui::draw_named_value(container, "is_world_loaded", debug_text);
44
45 snprintf(debug_text, dt_size, "%u", mats::get_n_materials());
46 gui::draw_named_value(container, "materials.length", debug_text);
47
48 snprintf(debug_text, dt_size, "%u", entities::get_n_entities());
49 gui::draw_named_value(container, "entities.length", debug_text);
50
51 snprintf(debug_text, dt_size, "%u", engine_state->model_loaders.length);
52 gui::draw_named_value(container, "model_loaders.length", debug_text);
53
54 snprintf(debug_text, dt_size, "%u", engine_state->n_valid_model_loaders);
55 gui::draw_named_value(container, "n_valid_model_loaders", debug_text);
56
57 snprintf(debug_text, dt_size, "%u", engine_state->entity_loaders.length);
58 gui::draw_named_value(container, "entities.length", debug_text);
59
60 snprintf(debug_text, dt_size, "%u", engine_state->n_valid_entity_loaders);
61 gui::draw_named_value(container, "n_valid_entity_loaders", debug_text);
62
63 if (gui::draw_toggle(container, "Wireframe mode", renderer::should_use_wireframe())) {
64 renderer::set_should_use_wireframe(!renderer::should_use_wireframe());
65 if (renderer::should_use_wireframe()) {
66 gui::set_heading("Wireframe mode on.", 1.0f, 1.0f, 1.0f);
67 } else {
68 gui::set_heading("Wireframe mode off.", 1.0f, 1.0f, 1.0f);
69 }
70 }
71
72 if (gui::draw_toggle(container, "FPS limit", engine_state->should_limit_fps)) {
73 engine_state->should_limit_fps = !engine_state->should_limit_fps;
74 if (engine_state->should_limit_fps) {
75 gui::set_heading("FPS limit enabled.", 1.0f, 1.0f, 1.0f);
76 } else {
77 gui::set_heading("FPS limit disabled.", 1.0f, 1.0f, 1.0f);
78 }
79 }
80
81 if (gui::draw_toggle(container, "Manual frame advance", engine_state->is_manual_frame_advance_enabled)) {
82 engine_state->is_manual_frame_advance_enabled = !engine_state->is_manual_frame_advance_enabled;
83 if (engine_state->is_manual_frame_advance_enabled) {
84 gui::set_heading("Manual frame advance enabled.", 1.0f, 1.0f, 1.0f);
85 } else {
86 gui::set_heading("Manual frame advance disabled.", 1.0f, 1.0f, 1.0f);
87 }
88 }
89
90 if (gui::draw_toggle(container, "Pause", engine_state->should_pause)) {
91 engine_state->should_pause = !engine_state->should_pause;
92 if (engine_state->should_pause) {
93 gui::set_heading("Pause enabled.", 1.0f, 1.0f, 1.0f);
94 } else {
95 gui::set_heading("Pause disabled.", 1.0f, 1.0f, 1.0f);
96 }
97 }
98
99 if (gui::draw_button(container, "Reload shaders")) {
100 mats::reload_shaders();
101 gui::set_heading("Shaders reloaded.", 1.0f, 1.0f, 1.0f);
102 }
103
104 if (gui::draw_button(container, "Delete PBO")) {
105 mats::delete_persistent_pbo();
106 gui::set_heading("PBO deleted.", 1.0f, 1.0f, 1.0f);
107 }
108 }
109
110 {
111 gui::Container *container = gui::make_container("Entities", v2(window_size->width - 400.0f, 25.0f));
112 get_scene_text_representation(debug_text);
113 gui::draw_body_text(container, debug_text);
114 }
115
116 {
117 gui::Container *container = gui::make_container("Materials", v2(window_size->width - 600.0f, 25.0f));
118 get_materials_text_representation(debug_text);
119 gui::draw_body_text(container, debug_text);
120 }
121
122 gui::draw_console(input::get_text_input());
123 renderer::render_gui();
124 gui::update();
125}
126
127
128void
129debug_ui::get_entity_text_representation(char *text, entities::Entity *entity, u8 depth)
130{
131 entities::Handle handle = entity->handle;
132 spatial::Component *spatial_component = spatial::get_component(handle);
133
134 // Children will be drawn under their parents.
135 if (
136 depth == 0 &&
137 spatial::is_spatial_component_valid(spatial_component) &&
138 spatial_component->parent_entity_handle != entities::NO_ENTITY_HANDLE
139 ) {
140 return;
141 }
142
143 bool has_spatial_component = spatial::is_spatial_component_valid(spatial_component);
144 bool has_drawable_component = drawable::is_component_valid(
145 drawable::get_component(handle));
146 bool has_light_component = lights::is_light_component_valid(
147 lights::get_component(handle));
148 bool has_behavior_component = behavior::is_behavior_component_valid(
149 behavior::get_component(handle));
150 bool has_animation_component = anim::is_animation_component_valid(
151 anim::get_component(handle));
152
153 for (u8 level = 0; level < depth; level++) {
154 strcat(text, " ");
155 }
156
157 strcat(text, "- ");
158 strcat(text, entity->debug_name);
159
160 strcat(text, "@");
161 sprintf(text + strlen(text), "%d", entity->handle);
162
163 if (
164 !has_spatial_component &&
165 !has_drawable_component &&
166 !has_light_component &&
167 !has_behavior_component &&
168 !has_animation_component
169 ) {
170 strcat(text, " (orphan)");
171 }
172
173 if (has_spatial_component) {
174 strcat(text, " +S");
175 }
176 if (has_drawable_component) {
177 strcat(text, " +D");
178 }
179 if (has_light_component) {
180 strcat(text, " +L");
181 }
182 if (has_behavior_component) {
183 strcat(text, " +B");
184 }
185 if (has_animation_component) {
186 strcat(text, " +A");
187 }
188
189 if (spatial::is_spatial_component_valid(spatial_component)) {
190 // NOTE: This is super slow lol.
191 u32 n_children_found = 0;
192 each (child_spatial_component, *spatial::get_components()) {
193 if (
194 child_spatial_component->parent_entity_handle ==
195 spatial_component->entity_handle
196 ) {
197 n_children_found++;
198 if (n_children_found > 5) {
199 continue;
200 }
201 entities::Handle child_handle = child_spatial_component->entity_handle;
202 entities::Entity *child_entity = entities::get_entity(child_handle);
203
204 if (text[strlen(text) - 1] != '\n') {
205 strcat(text, "\n");
206 }
207 get_entity_text_representation(text, child_entity, depth + 1);
208 }
209 }
210 if (n_children_found > 5) {
211 for (u8 level = 0; level < (depth + 1); level++) {
212 strcat(text, " ");
213 }
214 strcat(text, "(and ");
215 sprintf(text + strlen(text), "%d", n_children_found - 5);
216 strcat(text, " more)");
217 }
218 }
219
220 if (text[strlen(text) - 1] != '\n') {
221 strcat(text, "\n");
222 }
223}
224
225
226void
227debug_ui::get_scene_text_representation(char *text)
228{
229 text[0] = '\0';
230
231 constexpr u32 const MAX_N_SHOWN_ENTITIES = 35;
232 u32 idx_entity = 0;
233 each (entity, *entities::get_entities()) {
234 if (idx_entity > MAX_N_SHOWN_ENTITIES) {
235 sprintf(text + strlen(text),
236 "...and %d more\n",
237 entities::get_n_entities() - idx_entity);
238 break;;
239 }
240 get_entity_text_representation(text, entity, 0);
241 idx_entity++;
242 }
243
244 if (text[strlen(text) - 1] == '\n') {
245 text[strlen(text) - 1] = '\0';
246 }
247}
248
249
250void
251debug_ui::get_materials_text_representation(char *text)
252{
253 text[0] = '\0';
254
255 strcat(text, "Internal:\n");
256
257 bool have_seen_non_internal = false;
258
259 u32 idx = 0;
260 each (material, *mats::get_materials()) {
261 strcat(text, "- ");
262 strcat(text, material->name);
263 strcat(text, "\n");
264 if (mats::is_material_at_idx_internal(idx) && !have_seen_non_internal) {
265 have_seen_non_internal = true;
266 strcat(text, "Non-internal: \n");
267 }
268 idx++;
269 }
270
271 if (text[strlen(text) - 1] == '\n') {
272 text[strlen(text) - 1] = '\0';
273 }
274}