From 2b590e7bbafd85738081632bef5453a7ead19869 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 6 Feb 2022 17:57:43 -0600 Subject: [PATCH] started work saving and loading a scene --- src/build.onyx | 1 + src/entity/editor.onyx | 35 ++++++---- src/entity/manager.onyx | 32 ++++++--- src/entity/player.onyx | 56 ++++++++++------ src/entity/store.onyx | 142 ++++++++++++++++++++++++++++++++++++++++ src/gfx/immediate.onyx | 22 +++++++ src/main.onyx | 28 ++++---- src/utils/vecmath.onyx | 3 + 8 files changed, 264 insertions(+), 55 deletions(-) create mode 100644 src/entity/store.onyx diff --git a/src/build.onyx b/src/build.onyx index f80a96d..9fe3030 100644 --- a/src/build.onyx +++ b/src/build.onyx @@ -18,6 +18,7 @@ DEBUG :: false #load "entity/editor" #load "entity/manager" #load "entity/player" +#load "entity/store" #load "gfx/font" #load "gfx/immediate" diff --git a/src/entity/editor.onyx b/src/entity/editor.onyx index d723eb5..6c71db0 100644 --- a/src/entity/editor.onyx +++ b/src/entity/editor.onyx @@ -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; diff --git a/src/entity/manager.onyx b/src/entity/manager.onyx index 84ff8b0..b18c077 100644 --- a/src/entity/manager.onyx +++ b/src/entity/manager.onyx @@ -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; diff --git a/src/entity/player.onyx b/src/entity/player.onyx index 7d4d26f..c59d482 100644 --- a/src/entity/player.onyx +++ b/src/entity/player.onyx @@ -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 index 0000000..bc3293c --- /dev/null +++ b/src/entity/store.onyx @@ -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 { + +} diff --git a/src/gfx/immediate.onyx b/src/gfx/immediate.onyx index 65f6ce1..b15825a 100644 --- a/src/gfx/immediate.onyx +++ b/src/gfx/immediate.onyx @@ -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 { diff --git a/src/main.onyx b/src/main.onyx index 8c40123..aed6668 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -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; diff --git a/src/utils/vecmath.onyx b/src/utils/vecmath.onyx index 32b24f1..ad3937c 100644 --- a/src/utils/vecmath.onyx +++ b/src/utils/vecmath.onyx @@ -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 { -- 2.25.1