Skip to content

Commit 51e39a4

Browse files
committed
Implement game object creation in the editor.
1 parent 7534ab1 commit 51e39a4

3 files changed

Lines changed: 215 additions & 7 deletions

File tree

src/editor/src/ui/world_inspector.c

Lines changed: 188 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
#include "ui/world_inspector.h"
22

33
#include <stdio.h>
4+
#include <stdbool.h>
45
#include <world.h>
56
#include <ui/theme.h>
6-
#include "game/model.h"
7-
#include "game/camera.h"
7+
#include <game/model.h>
8+
#include <game/camera.h>
89
#include <widget/widget.h>
910
#include <widget/text_widget.h>
1011
#include <widget/button_widget.h>
1112
#include <misc/wchar_funcs.h>
12-
#include <stdbool.h>
13+
#include <type_database.h>
1314
#include <io/log.h>
1415

16+
#define BUTTON_HIDDEN_X_POS 10.0f
17+
#define CREATE_NEW_OBJ_TEXT "Create new object"
18+
1519
enum te_world_item_type {
1620
TE_WIT_MODEL,
1721
TE_WIT_CAMERA,
@@ -37,6 +41,9 @@ struct te_world_inspector {
3741
te_button_widget* button_3dobj;
3842
te_button_widget* button_2dobj;
3943

44+
// Text of the button to create new game objects.
45+
te_text_widget* button_text_create_new_obj;
46+
4047
// Valid while spawned, buttons that fill all available space (moved outside of the viewport if should not be visible).
4148
// Number of items in this array is @ref item_buttons_count.
4249
te_button_widget** item_buttons;
@@ -58,6 +65,8 @@ struct te_world_inspector {
5865

5966
// `true` if should display world's "3D objects", `false` if "2D objects".
6067
bool is_3dobj_mode_selected;
68+
69+
bool is_creating_new_game_obj;
6170
};
6271

6372
te_world_inspector*
@@ -66,12 +75,17 @@ world_inspector_create(void) {
6675

6776
inspector->left_panel = NULL;
6877
inspector->item_list = NULL;
78+
inspector->button_2dobj = NULL;
79+
inspector->button_3dobj = NULL;
6980
inspector->game_world = NULL;
81+
inspector->button_text_create_new_obj = NULL;
82+
inspector->page_text = NULL;
7083
inspector->item_buttons = NULL;
7184
inspector->item_buttons_count = 0;
7285
inspector->item_list_count = 0;
7386
inspector->current_page = 0;
7487
inspector->is_3dobj_mode_selected = true;
88+
inspector->is_creating_new_game_obj = false;
7589

7690
return inspector;
7791
}
@@ -272,6 +286,7 @@ refresh_item_names(te_world_inspector* inspector) {
272286
widget_set_relative_size(widget, size);
273287
}
274288

289+
// Get button text.
275290
unsigned int child_count;
276291
te_widget** child_widgets =
277292
widget_get_child_widgets_tmp(button_widget_get_widget(button), &child_count);
@@ -321,7 +336,7 @@ refresh_item_names(te_world_inspector* inspector) {
321336

322337
vec2 pos;
323338
widget_get_relative_position(widget, pos);
324-
pos[0] = 10.0f;
339+
pos[0] = BUTTON_HIDDEN_X_POS;
325340

326341
widget_set_relative_position(
327342
button_widget_get_widget(inspector->item_buttons[button_idx]), pos);
@@ -401,6 +416,126 @@ on_button_2dobj_clicked(te_button_widget* button) {
401416
button_widget_set_color(inspector->button_3dobj, color);
402417
}
403418

419+
static void
420+
on_button_create_new_object_clicked(te_button_widget* button) {
421+
te_world_inspector* inspector = widget_get_custom_ptr(button_widget_get_widget(button));
422+
423+
inspector->is_creating_new_game_obj = !inspector->is_creating_new_game_obj;
424+
425+
if (inspector->is_creating_new_game_obj) {
426+
// Turn this button into a "cancel" button.
427+
unsigned int text_len;
428+
wchar_t* wtext = wchar_from_char("Cancel object creation", &text_len);
429+
text_widget_set_text_own(inspector->button_text_create_new_obj, wtext, text_len);
430+
} else {
431+
unsigned int text_len;
432+
wchar_t* wtext = wchar_from_char(CREATE_NEW_OBJ_TEXT, &text_len);
433+
text_widget_set_text_own(inspector->button_text_create_new_obj, wtext, text_len);
434+
435+
refresh_item_names(inspector);
436+
return;
437+
}
438+
439+
// First hide all world item buttons.
440+
for (unsigned int button_idx = 0; button_idx < inspector->item_buttons_count;
441+
button_idx++) {
442+
te_widget* widget = button_widget_get_widget(inspector->item_buttons[button_idx]);
443+
444+
vec2 pos;
445+
widget_get_relative_position(widget, pos);
446+
pos[0] = BUTTON_HIDDEN_X_POS;
447+
448+
widget_set_relative_position(
449+
button_widget_get_widget(inspector->item_buttons[button_idx]), pos);
450+
}
451+
452+
unsigned int type_count;
453+
const char** types = type_database_get_all_type_ids(&type_count);
454+
455+
// Now unhide buttons with possible options.
456+
const float hpadding = theme_get_horizontal_padding() / theme_get_left_panel_width();
457+
for (unsigned int type_idx = 0;
458+
type_idx < type_count && type_idx < inspector->item_buttons_count; type_idx++) {
459+
te_button_widget* button = inspector->item_buttons[type_idx];
460+
{
461+
// Show button.
462+
te_widget* widget = button_widget_get_widget(button);
463+
464+
vec2 pos;
465+
widget_get_relative_position(widget, pos);
466+
pos[0] = hpadding;
467+
468+
widget_set_relative_position(widget, pos);
469+
}
470+
471+
// Get button text.
472+
unsigned int child_count;
473+
te_widget** child_widgets =
474+
widget_get_child_widgets_tmp(button_widget_get_widget(button), &child_count);
475+
te_text_widget* button_text = NULL;
476+
for (unsigned int i = 0; i < child_count; i++) {
477+
if (!widget_is_serialization_allowed(child_widgets[i])) {
478+
// Internal widget (rect) of the button.
479+
continue;
480+
}
481+
button_text = widget_get_owner(child_widgets[i]);
482+
break;
483+
}
484+
485+
// Set text.
486+
unsigned int text_len;
487+
wchar_t* wtext = wchar_from_char(types[type_idx], &text_len);
488+
text_widget_set_text_own(button_text, wtext, text_len);
489+
}
490+
491+
free(types);
492+
}
493+
494+
static void
495+
on_button_list_item_clicked(te_button_widget* button) {
496+
te_world_inspector* inspector = widget_get_custom_ptr(button_widget_get_widget(button));
497+
498+
if (inspector->is_creating_new_game_obj) {
499+
// Button name stores type ID from type database.
500+
501+
// Get button text.
502+
unsigned int child_count;
503+
te_widget** child_widgets =
504+
widget_get_child_widgets_tmp(button_widget_get_widget(button), &child_count);
505+
te_text_widget* button_text = NULL;
506+
for (unsigned int i = 0; i < child_count; i++) {
507+
if (!widget_is_serialization_allowed(child_widgets[i])) {
508+
// Internal widget (rect) of the button.
509+
continue;
510+
}
511+
button_text = widget_get_owner(child_widgets[i]);
512+
break;
513+
}
514+
515+
unsigned int text_len;
516+
wchar_t* wtext = text_widget_get_text(button_text, &text_len);
517+
char* type_id = wchar_to_char(wtext, &text_len);
518+
519+
const te_type_info* info = type_database_get_type_info(type_id);
520+
if (info == NULL) {
521+
log_error_fmt("expected to get a valid type info for type ID \"%s\"", type_id);
522+
abort();
523+
}
524+
free(type_id);
525+
526+
void* new_game_obj = info->create();
527+
info->spawn(inspector->game_world, new_game_obj);
528+
529+
inspector->is_creating_new_game_obj = false;
530+
wtext = wchar_from_char(CREATE_NEW_OBJ_TEXT, &text_len);
531+
text_widget_set_text_own(inspector->button_text_create_new_obj, wtext, text_len);
532+
533+
rebuild_item_list(inspector);
534+
refresh_item_names(inspector);
535+
return;
536+
}
537+
}
538+
404539
void
405540
world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
406541
if (inspector->left_panel != NULL) {
@@ -440,7 +575,7 @@ world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
440575
wchar_t* title_text = wchar_from_char("World inspector:", &title_len);
441576
text_widget_set_text_own(title, title_text, title_len);
442577
}
443-
y_pos += vspacing;
578+
y_pos += vspacing / 2.0f;
444579

445580
// World object type buttons: 3D objects or 2D objects.
446581
{
@@ -537,6 +672,48 @@ world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
537672
text = wchar_from_char("2D objects", &text_len);
538673
text_widget_set_text_own(text_2dobj, text, text_len);
539674
}
675+
y_pos += theme_get_button_height() + vspacing / 2.0f;
676+
677+
// Button to create new game objects.
678+
{
679+
te_button_widget* button = button_widget_create();
680+
{
681+
te_widget* widget = button_widget_get_widget(button);
682+
widget_set_custom_ptr(widget, inspector);
683+
widget_set_parent(widget, left_panel);
684+
widget_set_relative_position(widget, (vec2){hpadding, y_pos});
685+
widget_set_relative_size(widget, (vec2){total_width, theme_get_button_height()});
686+
}
687+
688+
vec4 color;
689+
theme_get_button_color(color);
690+
button_widget_set_color(button, color);
691+
692+
theme_get_button_color_hovered(color);
693+
button_widget_set_color_hovered(button, color);
694+
695+
theme_get_button_color_pressed(color);
696+
button_widget_set_color_pressed(button, color);
697+
698+
button_widget_set_on_clicked(button, on_button_create_new_object_clicked);
699+
700+
// Button text.
701+
te_text_widget* text_widget = text_widget_create();
702+
inspector->button_text_create_new_obj = text_widget;
703+
{
704+
te_widget* widget = text_widget_get_widget(text_widget);
705+
widget_set_parent(widget, button_widget_get_widget(button));
706+
widget_set_relative_position(
707+
widget, (vec2){hpadding_in_button, vpadding_in_button});
708+
widget_set_relative_size(
709+
widget, (vec2){1.0f - hpadding_in_button, 1.0f - vpadding_in_button});
710+
}
711+
text_widget_set_text_height(text_widget, theme_get_text_height());
712+
713+
unsigned int text_len;
714+
wchar_t* text = wchar_from_char(CREATE_NEW_OBJ_TEXT, &text_len);
715+
text_widget_set_text_own(text_widget, text, text_len);
716+
}
540717
y_pos += theme_get_button_height();
541718

542719
const float world_item_list_y_pos = y_pos;
@@ -558,7 +735,7 @@ world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
558735
<= world_item_list_y_pos + list_and_nav_menu_height);
559736
}
560737

561-
const float list_button_width = total_width;
738+
// Create buttons.
562739
inspector->item_buttons = malloc(sizeof(te_button_widget*) * button_count);
563740
inspector->item_buttons_count = button_count;
564741
for (unsigned int i = 0; i < button_count; i++) {
@@ -568,9 +745,11 @@ world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
568745
{
569746
te_widget* widget = button_widget_get_widget(button);
570747
widget_set_parent(widget, left_panel);
748+
widget_set_custom_value(widget, i);
749+
widget_set_custom_ptr(widget, inspector);
571750
widget_set_relative_position(widget, (vec2){hpadding, y_pos});
572751
widget_set_relative_size(
573-
widget, (vec2){list_button_width, theme_get_button_height()});
752+
widget, (vec2){total_width, theme_get_button_height()});
574753
}
575754

576755
vec4 color;
@@ -583,6 +762,8 @@ world_inspector_add(te_world_inspector* inspector, te_widget* left_panel) {
583762
theme_get_button_color_pressed(color);
584763
button_widget_set_color_pressed(button, color);
585764

765+
button_widget_set_on_clicked(button, on_button_list_item_clicked);
766+
586767
// Button text.
587768
te_text_widget* text = text_widget_create();
588769
{

src/engine_lib/include/type_database.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ void type_database_register_type(te_type_info* info);
143143
// Returns NULL if not registered. Do not free/destroy returned pointer.
144144
const te_type_info* type_database_get_type_info(const char* id);
145145

146+
// Returns array (which you need to free) to static strings (which you don't need to free)
147+
// to IDs of all currently registered types.
148+
const char** type_database_get_all_type_ids(unsigned int* count);
149+
146150
// ------------------------------------------------------------------------------------------------
147151
// PRIVATE API
148152
// ------------------------------------------------------------------------------------------------

src/engine_lib/src/type_database.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,26 @@ type_database_get_type_info(const char* id) {
437437

438438
return *found;
439439
}
440+
441+
const char**
442+
type_database_get_all_type_ids(unsigned int* count) {
443+
(*count) = (unsigned int)hashmap_count(type_database.types);
444+
if ((*count) == 0) {
445+
return NULL;
446+
}
447+
448+
const char** array = malloc(sizeof(const char*) * (*count));
449+
450+
size_t iter = 0;
451+
size_t item_idx = 0;
452+
void* item;
453+
while (hashmap_iter(type_database.types, &iter, &item)) {
454+
const te_type_info** ptr = item;
455+
te_type_info* info = (te_type_info*)*ptr;
456+
457+
array[item_idx] = info->id;
458+
item_idx += 1;
459+
}
460+
461+
return array;
462+
}

0 commit comments

Comments
 (0)