started work saving and loading a scene
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 6 Feb 2022 23:57:43 +0000 (17:57 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 6 Feb 2022 23:57:43 +0000 (17:57 -0600)
src/build.onyx
src/entity/editor.onyx
src/entity/manager.onyx
src/entity/player.onyx
src/entity/store.onyx [new file with mode: 0644]
src/gfx/immediate.onyx
src/main.onyx
src/utils/vecmath.onyx

index f80a96d27b4c78af51eeebe0ba146a2f38bbb29c..9fe3030f078e7f672ce66819af214519da9944d0 100644 (file)
@@ -18,6 +18,7 @@ DEBUG :: false
 #load "entity/editor"
 #load "entity/manager"
 #load "entity/player"
+#load "entity/store"
 
 #load "gfx/font"
 #load "gfx/immediate"
index d723eb5fb7875d99c7b4710da23d00c85e232e5e..6c71db020774bd5347734c72aaa6756011a938d1 100644 (file)
@@ -53,6 +53,7 @@ editor_update :: (dt: f32) {
         entity_type := ^scene.entity_types.entries[active_index].value;
 
         entity := entity_type.create_default(^scene);
+        scene->add(entity);
         entity.pos = mouse_pos;
     }
 }
@@ -60,27 +61,34 @@ editor_update :: (dt: f32) {
 #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;
+    if mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+        if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) || is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) {
+            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;
+            for scene.entities {
+                get_rect := scene.entity_types[it.type].get_rect;
+                if get_rect == null_proc do continue;
 
-            if get_rect(it) |> Rect.contains(mouse_pos) {
-                selected_entity_id = it.id;
-                break;
+                if get_rect(it) |> Rect.contains(mouse_pos) {
+                    selected_entity_id = it.id;
+                    break;
+                }
             }
+            
+            dragging = is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && selected_entity_id != Entity_Nothing;
+            resizing = is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) && selected_entity_id != Entity_Nothing;
         }
-
-        dragging = selected_entity_id != Entity_Nothing;
     }
 
     if !is_button_down(GLFW_MOUSE_BUTTON_LEFT) {
         dragging = false;
     }
 
+    if !is_button_down(GLFW_MOUSE_BUTTON_RIGHT) {
+        resizing = false;
+    }
+
     if selected_entity_id != Entity_Nothing && active_tab == .Edit {
         selected_entity := scene->get(selected_entity_id);
 
@@ -92,6 +100,10 @@ editor_update :: (dt: f32) {
             if is_key_down(GLFW_KEY_LEFT)  do selected_entity.pos.x -= 32 * dt;
             if is_key_down(GLFW_KEY_RIGHT) do selected_entity.pos.x += 32 * dt;
         }
+
+        if resizing {
+            selected_entity.size += mouse_get_delta_vector();
+        }
     }
 }
 
@@ -310,6 +322,7 @@ editor_draw :: () {
 
     selected_entity_id: Entity_ID;
     dragging := false;
+    resizing := false;
 
     sidebar_width := 0.0f;
     menubar_height := 40.0f;
index 84ff8b044c5f8d291b65cd2640a497c4817427c4..b18c077125dce0b9e45017a572f5e5d87580b62b 100644 (file)
@@ -8,17 +8,27 @@ Entity_ID :: #distinct u32
 #operator == (a, b: Entity_ID) => cast(u32) a == cast(u32) b;
 #operator != (a, b: Entity_ID) => cast(u32) a != cast(u32) b;
 
+#init #after conv.custom_formatters_initialized () {
+    conv.register_custom_parser((e: ^Entity_ID, line: str, _: Allocator) => {
+        *e = ~~ cast(u32) conv.str_to_i64(line);
+        return true;
+    });
+}
+
 Entity :: struct {
     id: Entity_ID;
+    [Entity_Store.Skip]
     type: type_expr;
     flags: Entity_Flags;
 
-    pos: Vector2;
+    pos:  Vector2;
+    size: Vector2;
+
+    get_rect :: (use e: ^Entity) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
 }
 
 Entity_Flags :: enum #flags {
     // These have defined values for stable serialization.
-
     Interactable :: 0x01;
     Solid        :: 0x02;
     Carryable    :: 0x04;
@@ -112,12 +122,17 @@ entity_manager_register :: (use this: ^Entity_Manager, $entity_type: type_expr)
     }
 }
 
-entity_manager_add :: (use this: ^Entity_Manager, entity: ^$T) -> Entity_ID where IsEntity(^T) {
-    this->register(T);
+entity_manager_add :: (use this: ^Entity_Manager, entity: ^Entity) -> Entity_ID {
+    // Automatically assign an id if the entity does not have one.
+    // This will not be used when loading the entity from a file.
+    if entity.id == 0 {
+        entity.id = next_entity_id;
+        next_entity_id = ~~(cast(u32) next_entity_id + 1);
+    } else {
+        next_entity_id = ~~(math.max(cast(u32) next_entity_id + 1, cast(u32) entity.id));
+    }
 
-    entity.type = T;
-    entity.id = next_entity_id;
-    next_entity_id = ~~(cast(u32) next_entity_id + 1);
+    assert(cast(u32) entity.type != 0, "Adding an entity without a type!");
 
     entities << entity;
     entity_map[entity.id] = entity;
@@ -126,13 +141,14 @@ entity_manager_add :: (use this: ^Entity_Manager, entity: ^$T) -> Entity_ID wher
 
 #local entity_manager_create_default :: (use this: ^Entity_Manager, $entity_type: type_expr) -> ^entity_type where IsEntity(^entity_type) {
     entity := new(entity_type, allocator=entity_allocator);
+    entity.type = entity_type;
     entity_type.init(entity, entity_type.init_data.{});
-    this->add(entity);
     return entity;
 }
 
 entity_manager_make :: (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 = entity_type;
     entity_type.init(entity, data);
     this->add(entity);
     return entity;
index 7d4d26fb36f168c26a411be814c9ab2976b34e9a..c59d482ee0106cceadf86e45eeef316798533e6e 100644 (file)
@@ -29,14 +29,29 @@ player_2_controls :: Player_Controls.{
     pick_up  = GLFW_KEY_PERIOD,
 }
 
+Facing :: enum {
+    Up    :: 0x01;
+    Down  :: 0x02;
+    Left  :: 0x03;
+    Right :: 0x04;
+}
+
+facing_to_direction_vector :: macro (f: Facing) -> Vector2 {
+    switch f {
+        case .Up    do return .{0, -1};
+        case .Down  do return .{0,  1};
+        case .Left  do return .{-1, 0};
+        case .Right do return .{1,  0};
+    }
+}
+
 Player :: struct {
     use entity: Entity;
 
     holding: Entity_ID;
     controls: Player_Controls;
     color := Color.{1,1,1};
-
-    Size :: 16.0f
+    facing := Facing.Up;
 
     init_data :: struct {
         pos := Vector2.{400, 200};
@@ -46,6 +61,7 @@ Player :: struct {
 
     init :: (use this: ^Player, data: init_data) {
         this.pos = data.pos;
+        this.size = .{32, 32};
         this.holding = Entity_Nothing;
 
         this.controls = data.controls;
@@ -54,18 +70,19 @@ Player :: struct {
         this.flags |= .Solid;
     }
 
-    get_rect :: (use this: ^Player) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+    get_rect :: Entity.get_rect
 
     update :: (use this: ^Player, dt: f32) {
-        speed :: Size * 8;
+        speed :: 128.0f;
 
         delta: Vector2;
-        if is_key_down(controls.up)    do delta.y -= speed * dt; 
-        if is_key_down(controls.down)  do delta.y += speed * dt; 
-        if is_key_down(controls.left)  do delta.x -= speed * dt; 
-        if is_key_down(controls.right) do delta.x += speed * dt; 
+        if is_key_down(controls.left)  { delta.x -= speed * dt; facing = .Left;  } 
+        if is_key_down(controls.right) { delta.x += speed * dt; facing = .Right; } 
+        if is_key_down(controls.up)    { delta.y -= speed * dt; facing = .Up;    } 
+        if is_key_down(controls.down)  { delta.y += speed * dt; facing = .Down;  } 
 
-        walls := scene->query_by_flags(.{pos.x - 100, pos.y - 100, 200, 200}, .Solid);
+        dist := math.max(size.x, size.y) * 2;
+        walls := scene->query_by_flags(.{pos.x - dist, pos.y - dist, dist * 2, dist * 2}, .Solid);
         defer memory.free_slice(^walls);
 
         try_move(this, .{delta.x, 0}, walls);
@@ -83,12 +100,14 @@ Player :: struct {
                 vtable := ^scene.entity_types[holding_object.type];
                 if vtable.get_rect != null_proc {
                     r := vtable.get_rect(holding_object);
-                    holding_object.pos = pos + .{Size + r.w / 2 + 4, 0};
+
+                    d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4};
+                    holding_object.pos = pos + facing_to_direction_vector(facing) * d;
                 }
                 holding = Entity_Nothing;
 
             } else {
-                area    := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
+                area    := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
                 objects := scene->query_by_flags(area, .Carryable);
                 defer memory.free_slice(^objects);
 
@@ -111,7 +130,7 @@ Player :: struct {
         // Try to interact with nearby objects
         //
         if is_key_just_up(controls.interact) {
-            area    := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
+            area    := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
             objects := scene->query_by_flags(area, .Interactable);
             defer memory.free_slice(^objects);
 
@@ -131,7 +150,7 @@ Player :: struct {
             vtable := ^scene.entity_types[holding_object.type];
             if vtable.get_rect != null_proc {
                 r := vtable.get_rect(holding_object);
-                holding_object.pos = pos - .{0, Size + r.h / 2};
+                holding_object.pos = pos - .{0, (size.y + r.h) / 2};
             }
         }
     }
@@ -168,7 +187,6 @@ player_texture: Texture;
 
 Wall :: struct {
     use entity: Entity;
-    size: Vector2;
 
     init_data :: struct {
         pos  := Vector2.{0,  0};
@@ -182,7 +200,7 @@ Wall :: struct {
         flags |= .Solid;
     }
 
-    get_rect :: (use this: ^Wall) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
+    get_rect :: Entity.get_rect
 
     draw :: (use this: ^Wall) {
         immediate_set_color(.{1,1,1});
@@ -194,9 +212,8 @@ Wall :: struct {
 
 Door :: struct {
     use entity: Entity;
-    size: Vector2;
 
-    openness := 0.0f;
+    [Entity_Store.Skip] openness := 0.0f;
     target_openness := 0.0f;
 
     init_data :: struct {
@@ -245,8 +262,6 @@ Door :: struct {
 Item :: struct {
     use entity: Entity;
 
-    Size :: 8.0f;
-
     color: Color;
 
     init_data :: struct {
@@ -256,11 +271,12 @@ Item :: struct {
 
     init :: (use this: ^Item, data: init_data) {
         this.pos   = data.pos;
+        this.size  = .{16, 16};
         this.color = data.color;
         this.flags |= .Carryable;
     }
 
-    get_rect :: (use this: ^Item) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+    get_rect :: Entity.get_rect
 
     draw :: (use this: ^Item) {
         immediate_set_color(this.color);
diff --git a/src/entity/store.onyx b/src/entity/store.onyx
new file mode 100644 (file)
index 0000000..bc3293c
--- /dev/null
@@ -0,0 +1,142 @@
+
+use package core
+
+// Flags to control how a field of a structure is being stored
+// in the level file.
+Entity_Store :: enum {
+    Skip;
+}
+
+entity_manager_save_to_file :: (use this: ^Entity_Manager, filename: str) {
+    err, output_file := os.open(filename, .Write);
+    defer os.close(^output_file);
+    writer_ := io.writer_make(^output_file);
+    writer := ^writer_;
+
+    // writer := ^stdio.print_writer;
+
+    use type_info;
+
+    for entities {
+        info := cast(^Type_Info_Struct) get_type_info(it.type);
+        io.write_format(writer, ":{}\n", info.name);
+        emit_struct_fields(any.{~~ it, it.type}, writer, "");
+        io.write(writer, "\n");
+    }
+
+    emit_struct_fields :: (v: any, dest: ^io.Writer, parent_name: str) {
+        use type_info;
+
+        info := cast(^Type_Info_Struct) get_type_info(v.type);
+        assert(info.kind == .Struct, "Expected type to be a structure.");
+
+        name_buffer: [1024] u8;
+        output_buffer: [2048] u8;
+        for info.members {
+            for it.tags {
+                if it.type != Entity_Store do continue;
+                if *(cast(^Entity_Store) it.data) == .Skip do continue continue;
+            }
+
+            member_info := get_type_info(it.type);
+            member_any  := any.{cast(^u8) v.data + it.offset, it.type};
+
+            name := conv.format(name_buffer, "{}{}{}",
+                parent_name,
+                "." if parent_name.count > 0 else "",
+                it.name);
+
+            switch member_info.kind {
+                case .Struct {
+                    emit_struct_fields(member_any, dest, name);
+                }
+
+                case .Basic, .Distinct, .Enum {
+                    output := conv.format_va(output_buffer, "{} = {d}\n", .[.{^name, str}, member_any]);
+                    io.write(dest, output);
+                }
+
+                case #default {
+                    msg := conv.format(output_buffer, "Unhandled output case: {}\n", it.type);
+                    // assert(false, msg);
+                    printf("ERROR: {}\n", msg);
+                }
+            }
+        }
+    }
+}
+
+entity_manager_load_from_file :: (use this: ^Entity_Manager, filename: str) {
+    err, input_file := os.open(filename, .Read);
+    defer os.close(^input_file);
+    reader := io.reader_make(^input_file);
+
+    use type_info;
+
+    current_entity: ^Entity;
+
+    line_number := 1;
+    while !io.reader_empty(^reader) {
+        defer line_number += 1;
+
+        line := io.read_line(^reader, inplace=true);
+        if line.count <= 1 do continue;
+
+        if line[0] == #char ":" {
+            if current_entity != null do this->add(current_entity);
+
+            entity_kind := string.advance(line)
+                        |> string.strip_whitespace();
+
+            entity_info : ^Entity_Info;
+            for^ entity_types.entries {
+                if (cast(^Type_Info_Struct) get_type_info(it.key)).name == entity_kind {
+                    entity_info = ^it.value;
+                    break;
+                }
+            }
+
+            current_entity = entity_info.create_default(this);
+            continue;
+        }
+
+        var_name := string.read_until(^line, #char "=")
+                 |> string.strip_whitespace();
+        string.advance(^line, 1);
+        string.strip_whitespace(^line);
+
+        member := get_any_for_member(any.{current_entity, current_entity.type}, var_name);
+        if !conv.parse_any(member.data, member.type, line) {
+            printf("Unable to parse '{}' for type '{}' on line {}.\n", line, member.type, line_number);
+        }
+    }
+
+    if current_entity != null do this->add(current_entity);
+
+    get_any_for_member :: (base: any, member_name: str) -> any {
+        use type_info;
+
+        name := member_name;
+        info := cast(^Type_Info_Struct) get_type_info(base.type);
+        assert(info.kind == .Struct, "bad type");
+
+        part_name := string.read_until(^name, #char ".");
+        string.advance(^name, 1);
+
+        for info.members {
+            if it.name == part_name {
+                member_info := get_type_info(it.type);
+                if member_info.kind == .Struct {
+                    return get_any_for_member(any.{cast(^u8) base.data + it.offset, it.type}, name);
+                } else {
+                    return any.{cast(^u8) base.data + it.offset, it.type};
+                }
+            }
+        }
+    }
+}
+
+
+#local {
+
+}
index 65f6ce157b9f790fc3aa2903abea46712439ff74..b15825a49537d7ae82412549ecc97996dab11e6e 100644 (file)
@@ -88,6 +88,28 @@ immediate_image :: (image: ^Texture, x, y, w, h: f32) {
     vertex_count += 6;
 }
 
+immediate_subimage :: (image: ^Texture, x, y, w, h: f32, tx, ty, tw, th: f32) {
+    if vertex_count > 0 do immediate_flush();
+
+    set_rendering_type(.Image);
+    texture_use(image);
+    shader_use(imgui_shader);
+    shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
+
+    sx := tx / ~~ image.width;
+    sy := ty / ~~ image.height;
+    sw := tw / ~~ image.width;
+    sh := th / ~~ image.height;
+
+    vertex_data[vertex_count + 0] = .{ .{x, y},     .{sx,      sy}, immediate_color };
+    vertex_data[vertex_count + 1] = .{ .{x+w, y},   .{sx+sw,   sy}, immediate_color };
+    vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+    vertex_data[vertex_count + 3] = .{ .{x, y},     .{sx,      sy}, immediate_color };
+    vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+    vertex_data[vertex_count + 5] = .{ .{x, y+h},   .{sx,   sy+sh}, immediate_color };
+    vertex_count += 6;
+}
+
 immediate_ellipse :: () {}
 
 Color :: struct {
index 8c401231ae735be3e7af213ff3ecf0960c553365..aed6668accc645f0b941770124355760108fa0ec 100644 (file)
@@ -45,22 +45,14 @@ 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->register(Player);
+    scene->register(Wall);
+    scene->register(Door);
+    scene->register(Item);
+    entity_manager_load_from_file(^scene, "./Asdf");
+
+    // scene->make(Player, .{ pos = .{300, 300}, controls=player_1_controls });
+    // scene->make(Player, .{ pos = .{400, 300}, controls=player_2_controls, color=.{1,0,0} });
     
     #if DEBUG {
         println("Registered Entity types:");
@@ -83,6 +75,10 @@ update :: (dt: f32) {
         editor_toggle();
     }
 
+    if is_key_just_up(GLFW_KEY_F8) {
+        entity_manager_save_to_file(^scene, "Asdf");
+    }
+
     if editor_shown() {
         editor_update(dt);
         return;
index 32b24f1252dc4c3b604035bd090894be6e001655..ad3937c0c6813b0d7c4cdcf52608d2e446134514 100644 (file)
@@ -44,16 +44,19 @@ Vector3 :: struct [conv.Custom_Format.{format_vector3}] {
 #operator + macro (v1, v2: Vector2)    => (typeof v1).{ v1.x + v2.x, v1.y + v2.y };
 #operator - macro (v1, v2: Vector2)    => (typeof v1).{ v1.x - v2.x, v1.y - v2.y };
 #operator * macro (v: Vector2, s: f32) => (typeof v1).{ v.x * s,     v.y * s     };
+#operator * macro (v1, v2: Vector2)    => (typeof v1).{ v1.x * v2.x, v1.y * v2.y };
 #operator == macro (v1, v2: Vector2)   => v1.x == v2.x && v1.y == v2.y;
 
 #operator + macro (v1, v2: Vector3)    => Vector3.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
 #operator - macro (v1, v2: Vector3)    => Vector3.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
 #operator * macro (v: Vector3, s: f32) => Vector3.{ v.x * s,     v.y * s,     v.z * s };
+#operator * macro (v1, v2: Vector3)    => (typeof v1).{ v1.x * v2.x, v1.y * v2.y, v1.z * v3.z };
 #operator == macro (v1, v2: Vector3)   => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
 
 #operator + macro (v1, v2: Vector3i)    => Vector3i.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
 #operator - macro (v1, v2: Vector3i)    => Vector3i.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
 #operator * macro (v: Vector3i, s: i32) => Vector3i.{ v.x * s,     v.y * s,     v.z * s };
+#operator * macro (v1, v2: Vector3i)    => (typeof v1).{ v1.x * v2.x, v1.y * v2.y, v1.z * v3.z };
 #operator == macro (v1, v2: Vector3i)   => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
 
 #local {