dynamic entity creation; better font rendering
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 2 Feb 2022 04:28:56 +0000 (22:28 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 2 Feb 2022 04:28:56 +0000 (22:28 -0600)
src/entity/editor.onyx
src/entity/manager.onyx
src/entity/player.onyx
src/gfx/font.onyx
src/main.onyx

index 9fdae6f84ff11ae88672f9665238f6601fa33284..19ea6b45dc747f6c66720907672f6c15f7ef0999 100644 (file)
@@ -11,6 +11,9 @@ use package core
 use package opengles
 use package glfw3
 
+Editor_Range    :: struct {min, max: f32;}
+Editor_Disabled :: struct {}
+
 editor_init :: () {
     editor_font = font_lookup(.{"./assets/fonts/calibri.ttf", 18});
     editor_big_font = font_lookup(.{"./assets/fonts/calibri.ttf", 24});
@@ -29,7 +32,10 @@ editor_update :: (dt: f32) {
     move_towards(^editor_openness, editor_target_openness, dt * 6);
 
     handle_clicking_tab(dt);
-    handle_entity_selction_and_dragging(dt);
+    switch active_tab {
+        case .Create do handle_placing_entities();
+        case .Edit   do handle_entity_selction_and_dragging(dt);
+    }
 }
 
 #local handle_clicking_tab :: (dt: f32) {
@@ -39,11 +45,26 @@ editor_update :: (dt: f32) {
     clicked_tab = .None;
 }
 
+#local handle_placing_entities :: () {
+    if active_index < 0 do return;
+
+    if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) {
+        entity_type := ^scene.entity_types.entries[active_index].value;
+        printf("ADDING {}\n", entity_type);
+
+        pos := mouse_get_position_vector();
+        entity := entity_type.make(^scene); // scene->make(entity_type);
+        entity.pos = pos;
+    }
+}
+
 #local handle_entity_selction_and_dragging :: (dt: f32) {
     mouse_pos := mouse_get_position_vector();
 
     if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+        if active_tab == .Edit do active_index = -1;
         selected_entity_id = Entity_Nothing;
+
         for scene.entities {
             get_rect := scene.entity_types[it.type].get_rect;
             if get_rect == null_proc do continue;
@@ -89,8 +110,6 @@ editor_draw :: () {
         immediate_rectangle(r.x, r.y, r.w, r.h);
     }
 
-    background_color :: Color.{0.5, 0.5, 0.5, 0.7};
-
     { // Draw menu bar
         x := 0.0f;
         w := cast(f32) window_width;
@@ -105,7 +124,7 @@ editor_draw :: () {
         h -= 4;
         w = 100;
 
-        for type_info.enum_values(Tabs) {
+        for^ type_info.enum_values(Tabs) {
             // Don't draw the "None" item;
             if it.value == 0 do continue;
 
@@ -144,7 +163,7 @@ editor_draw :: () {
 
         switch active_tab {
             case .Create {
-
+                render_create_sidebar(x, y, w, h);
             }
 
             case .Edit {
@@ -157,6 +176,33 @@ editor_draw :: () {
     }
 }
 
+#local render_create_sidebar :: (x, y, w, h: f32) {
+    i := 0;
+    for scene.entity_types.entries {
+        defer i += 1;
+
+        type := it.key;
+        info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(type);
+
+        if active_index == i {
+            immediate_set_color(.{.5,.5,.3});
+        } elseif Rect.contains(.{x, y, w, 36.0f}, mouse_get_position_vector()) {
+            immediate_set_color(.{.4,.4,.4});
+
+            if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) {
+                active_index = i;
+            }
+        } else {
+            immediate_set_color(.{.3,.3,.3});
+        }
+
+        immediate_rectangle(x, y, w, 36.0f);
+
+        y += 40.0f;
+        font_draw_centered(editor_font, x + 4, y - 18.0f, w, info.name);
+    }
+}
+
 #local render_entity_fields :: (entity: ^Entity, x, y, w, h: f32) {
     assert(entity != null, "entity is null");
     assert(type_info.struct_inherits(entity.type, Entity), "entity is not an entity");
@@ -164,26 +210,38 @@ editor_draw :: () {
 
     font_print(editor_big_font, x + 2, y + 24, info.name);
 
-    render_struct_fields(any.{~~entity, entity.type}, 1, x, y + 4, w, h);
+    render_struct_fields(any.{~~entity, entity.type}, 0, x, y + 4, w, h);
 }
 
 #local render_struct_fields :: (v: any, i: i32, x, y, w, h: f32, depth := 0) -> (new_y: f32, new_i: i32) {
     Field_Height :: 22.0f
 
     info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(v.type);
-    for info.members {
+    for^ info.members {
         i += 1;
         y += Field_Height;
 
-        if i % 2 == 0 {
+        member_any := any.{cast(^u8) v.data + it.offset, it.type};
+
+        if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) {
+            if Rect.contains(.{x, y + 2, w, Field_Height + 2}, mouse_get_position_vector()) {
+                active_index = i - 1;
+            }
+        }
+
+        if active_index + 1 == i {
+           immediate_set_color(.{.4,.4,.2});
+           immediate_rectangle(x, y + 2, w, Field_Height + 2);
+
+           render_field_editor(member_any, y, it.name);
+
+        } elseif i % 2 == 0 {
            immediate_set_color(.{.3,.3,.3});
            immediate_rectangle(x, y + 2, w, Field_Height + 2);
         }
 
         font_print(editor_font, x + 2, y + Field_Height, it.name);
 
-        member_any := any.{cast(^u8) v.data + it.offset, it.type};
-
         #persist depth_colors := Color.[
             Color.{0.4, 0.2, 0.2},
             Color.{0.2, 0.4, 0.2},
@@ -213,7 +271,38 @@ editor_draw :: () {
     return y, i;
 }
 
+#local render_field_editor :: (v: any, y: f32, field_name: str) {
+    w := sidebar_width;
+    h := 200.0f;
+    x := ~~ window_width - sidebar_width * 2;
+    y = math.min(y, ~~ window_height - h);
+    immediate_set_color(.{.3,.3,.3});
+    immediate_rectangle(x, y, w, h);
+
+    y += 32;
+    font_print(editor_font, x, y, field_name);
+
+    switch v.type {
+        type_is(i32) {
+            font_print(editor_font, x, y + 32, "INT: {}\n", value);
+        }
+
+        type_is(f32) {
+            font_print(editor_font, x, y + 32, "FLOAT: {}\n", value);
+        }
+    }
+
+    type_is :: macro (T: type_expr, body: Code) {
+        case T {
+            value := *cast(^T) v.data;
+            #insert body;
+        }
+    }
+}
+
 #local {
+    background_color :: Color.{0.5, 0.5, 0.5, 0.7};
+
     editor_font: Font;
     editor_big_font: Font;
 
@@ -234,4 +323,6 @@ editor_draw :: () {
 
     active_tab  := Tabs.Create;
     clicked_tab := Tabs.None;
+
+    active_index := -1;
 }
index 30aa7e99b77da10c284a158174526b612a7d03d8..68119d5b67f81bfc3ba556b753f6dd94993418fa 100644 (file)
@@ -30,7 +30,9 @@ IsEntity :: interface (e: $E) {
     e.init(e, e.init_data.{}); // This constraint ensures that everything in the initialization data is given a default value.
 }
 
-Entity_Handles :: struct {
+Entity_Info :: struct {
+    make : (scene: ^Entity_Manager) -> ^Entity = null_proc;
+
     update   : (entity: ^Entity, dt: f32) -> void             = null_proc;
     draw     : (entity: ^Entity) -> void                      = null_proc;
     get_rect : (entity: ^Entity) -> Rect                      = null_proc;
@@ -44,13 +46,14 @@ Entity_Manager :: struct {
     entities: [..] ^Entity;
     entity_map: Map(Entity_ID, ^Entity);
 
-    entity_types: Map(type_expr, Entity_Handles);
+    entity_types: Map(type_expr, Entity_Info);
 
     next_entity_id: Entity_ID;
 
     register       :: entity_manager_register;
     add            :: entity_manager_add;
     make           :: entity_manager_make;
+    make_old       :: entity_manager_make_old;
     update         :: entity_manager_update;
     draw           :: entity_manager_draw;
     get            :: entity_manager_get;
@@ -95,17 +98,18 @@ entity_manager_register :: (use this: ^Entity_Manager, $entity_type: type_expr)
         }
 
 
-        handles := Entity_Handles.{};
+        info := Entity_Info.{};
+        info.make = #solidify entity_manager_make { entity_type=entity_type };
 
         @CompilerFeatures // Maybe there should be data stored in the Type_Info_Struct about
         // the functions/methods that are defined in the structs scope. I don't know if that
         // would be worthwhile or if it would just be bulking up the type info data.
-        #if #defined(entity_type.update)   { handles.update   = entity_type.update;   if DEBUG do printf("{} has '{}'.\n", entity_type, "update"); }
-        #if #defined(entity_type.draw)     { handles.draw     = entity_type.draw;     if DEBUG do printf("{} has '{}'.\n", entity_type, "draw"); }
-        #if #defined(entity_type.get_rect) { handles.get_rect = entity_type.get_rect; if DEBUG do printf("{} has '{}'.\n", entity_type, "get_rect"); }
-        #if #defined(entity_type.interact) { handles.interact = entity_type.interact; if DEBUG do printf("{} has '{}'.\n", entity_type, "interact"); }
+        #if #defined(entity_type.update)   { info.update   = entity_type.update;   if DEBUG do printf("{} has '{}'.\n", entity_type, "update"); }
+        #if #defined(entity_type.draw)     { info.draw     = entity_type.draw;     if DEBUG do printf("{} has '{}'.\n", entity_type, "draw"); }
+        #if #defined(entity_type.get_rect) { info.get_rect = entity_type.get_rect; if DEBUG do printf("{} has '{}'.\n", entity_type, "get_rect"); }
+        #if #defined(entity_type.interact) { info.interact = entity_type.interact; if DEBUG do printf("{} has '{}'.\n", entity_type, "interact"); }
 
-        entity_types[entity_type] = handles;
+        entity_types[entity_type] = info;
     }
 }
 
@@ -121,7 +125,14 @@ entity_manager_add :: (use this: ^Entity_Manager, entity: ^$T) -> Entity_ID wher
     return entity.id;
 }
 
-entity_manager_make :: (use this: ^Entity_Manager, $entity_type: type_expr, data: entity_type.init_data = .{}) -> ^entity_type where IsEntity(^entity_type) {
+entity_manager_make :: (use this: ^Entity_Manager, $entity_type: type_expr) -> ^entity_type where IsEntity(^entity_type) {
+    entity := new(entity_type, allocator=entity_allocator);
+    entity_type.init(entity, entity_type.init_data.{});
+    this->add(entity);
+    return entity;
+}
+
+entity_manager_make_old :: (use this: ^Entity_Manager, $entity_type: type_expr, data: entity_type.init_data = .{}) -> ^entity_type where IsEntity(^entity_type) {
     entity := new(entity_type, allocator=entity_allocator);
     entity_type.init(entity, data);
     this->add(entity);
index b1c6a366a11a3e471169395ea95fe0c6972f572d..736b4b88107a673ab75af81dd57410d0ee228656 100644 (file)
@@ -201,7 +201,7 @@ Door :: struct {
 
     init_data :: struct {
         pos := Vector2.{0, 0};
-        size := Vector2.{0, 0};
+        size := Vector2.{10, 10};
     }
 
     init :: (use this: ^Door, data: init_data) {
index 06599da200b7d57b1936cc592a9e84005f001667..3e32076439c4a8935d2d8d26a8b345d40a535c74 100644 (file)
@@ -66,6 +66,7 @@ Font :: struct {
     texture: GLint;
     texture_width, texture_height: i32;
     chars: [] stbtt_packedchar;
+    em: f32;
 }
 
 font_make :: (fd: FontDescriptor) -> Font {
@@ -102,7 +103,8 @@ font_make :: (fd: FontDescriptor) -> Font {
         texture = texture,
         texture_width = texture_size,
         texture_height = texture_size,
-        chars = char_data
+        chars = char_data,
+        em = ~~fd.size,
     };
 
     font_registry[fd] = font;
@@ -121,27 +123,59 @@ font_print :: (font: Font, x, y: f32, format: str, va: ..any) {
 }
 
 font_draw :: (font: Font, x, y: f32, msg: str) {
-    // If this is being used in conjunction with the immediate
-    // rendering system, make sure the immediate objects are flushed
-    // before trying to render over them.
-    #if #defined(immediate_flush) {
-        immediate_flush();
+    quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
+    quad_num := 0;
+
+    x_, y_ := x, y;
+
+    for msg {
+        if it == #char "\n" {
+            x_ = x;
+            y_ += font.em + 2;
+        }
+
+        stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
+            ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
+
+        quad_num += 1;
     }
 
+    font_render(font, quads[0 .. quad_num]);
+}
+
+font_draw_centered :: (font: Font, x, y, max_width: f32, msg: str) {
     quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
     quad_num := 0;
 
+    width := font_get_width(font, msg);
     x_, y_ := x, y;
+    x_ = (max_width - width) / 2 + x;
 
     for msg {
+        if it == #char "\n" {
+            x_ = (max_width - width) / 2 + x;
+            y_ += font.em + 2;
+        }
+
         stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
             ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
 
         quad_num += 1;
     }
 
+    font_render(font, quads[0 .. quad_num]);
+}
+
+#local font_render :: (font: Font, quads: [] stbtt_aligned_quad) {
+    // If this is being used in conjunction with the immediate
+    // rendering system, make sure the immediate objects are flushed
+    // before trying to render over them.
+    #if #defined(immediate_flush) {
+        immediate_flush();
+    }
+
     glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
-    glBufferSubData(GL_ARRAY_BUFFER, 0, quad_num * sizeof stbtt_aligned_quad, quads);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, quads.count * sizeof stbtt_aligned_quad, quads.data);
     glBindBuffer(GL_ARRAY_BUFFER, -1);
 
     glActiveTexture(GL_TEXTURE0);
@@ -152,21 +186,29 @@ font_draw :: (font: Font, x, y: f32, msg: str) {
 
     glDisable(GL_DEPTH_TEST);
     glBindVertexArray(font_vao);
-    glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, ~~0, quad_num);
+    glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, ~~0, quads.count);
     glBindTexture(GL_TEXTURE_2D, -1);
     glBindVertexArray(-1);
 }
 
 font_get_width :: (font: Font, msg: str) -> f32 {
     x_, y_ := 0.0f, 0.0f;
+    width := 0.0f;
 
     quad: stbtt_aligned_quad;
     for msg {
+        if it == #char "\n" {
+            width = math.max(width, x_);
+            x_ = 0;
+            y_ += font.em + 2;
+            continue;
+        }
+
         stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
             ~~(it - #char " "), ^x_, ^y_, ^quad, false);
     }
 
-    return x_;
+    return math.max(x_, width);
 }
 
 
index 8c401231ae735be3e7af213ff3ecf0960c553365..28656eae0182046de7133ec27dc38b04410ed581 100644 (file)
@@ -45,22 +45,22 @@ init :: () {
     #if DEBUG { debug_font = font_lookup(.{"./assets/fonts/calibri.ttf", 16}); }
 
     scene = entity_manager_create();
-    scene->make(Player, .{ pos = .{300, 300}, controls=player_1_controls });
-    scene->make(Player, .{ pos = .{400, 300}, controls=player_2_controls, color=.{1,0,0} });
-
-    scene->make(Wall, .{ .{100, 100}, .{400, 50} });
-    // scene->make(Wall, .{ .{100, 100}, .{50, 400} });
-    // scene->make(Wall, .{ .{450, 100}, .{50, 400} });
-    // scene->make(Wall, .{ .{100, 450}, .{400, 50} });
-
-    scene->make(Door, .{ .{150, 150}, .{50, 50} });
-    scene->make(Door, .{ .{400, 150}, .{50, 50} });
-    scene->make(Door, .{ .{400, 400}, .{50, 50} });
-    scene->make(Door, .{ .{150, 400}, .{50, 50} });
-
-    scene->make(Item, .{ pos=.{ 250, 250 }, color=.{1,0,0} });
-    scene->make(Item, .{ pos=.{ 275, 250 }, color=.{0,1,0} });
-    scene->make(Item, .{ pos=.{ 300, 250 }, color=.{0,0,1} });
+    scene->make_old(Player, .{ pos = .{300, 300}, controls=player_1_controls });
+    scene->make_old(Player, .{ pos = .{400, 300}, controls=player_2_controls, color=.{1,0,0} });
+
+    scene->make_old(Wall, .{ .{100, 100}, .{400, 50} });
+    // scene->make_old(Wall, .{ .{100, 100}, .{50, 400} });
+    // scene->make_old(Wall, .{ .{450, 100}, .{50, 400} });
+    // scene->make_old(Wall, .{ .{100, 450}, .{400, 50} });
+
+    scene->make_old(Door, .{ .{150, 150}, .{50, 50} });
+    scene->make_old(Door, .{ .{400, 150}, .{50, 50} });
+    scene->make_old(Door, .{ .{400, 400}, .{50, 50} });
+    scene->make_old(Door, .{ .{150, 400}, .{50, 50} });
+
+    scene->make_old(Item, .{ pos=.{ 250, 250 }, color=.{1,0,0} });
+    scene->make_old(Item, .{ pos=.{ 275, 250 }, color=.{0,1,0} });
+    scene->make_old(Item, .{ pos=.{ 300, 250 }, color=.{0,0,1} });
     
     #if DEBUG {
         println("Registered Entity types:");