From 5d6a370646c904ab1381fb7fd93816a116972ae3 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 21 Feb 2022 19:56:10 -0600 Subject: [PATCH] dropping all updates --- docs/items.md | 15 +++++ run_tree/scenes/default.items | 5 ++ run_tree/scenes/quick_save.scene | 4 +- src/build.onyx | 2 + src/entity/editor.onyx | 17 +++++- src/entity/items.onyx | 95 +++++++++++++++++++++++++++++++- src/entity/manager.onyx | 9 +++ src/entity/player.onyx | 7 ++- src/entity/store.onyx | 24 -------- src/game.onyx | 15 ++++- src/gfx/texture.onyx | 33 +++++++++-- src/utils/any_utils.onyx | 42 ++++++++++++++ src/utils/asset_loader.onyx | 61 ++++++++++++++++++++ 13 files changed, 288 insertions(+), 41 deletions(-) create mode 100644 docs/items.md create mode 100644 run_tree/scenes/default.items create mode 100644 src/utils/any_utils.onyx create mode 100644 src/utils/asset_loader.onyx diff --git a/docs/items.md b/docs/items.md new file mode 100644 index 0000000..7565c69 --- /dev/null +++ b/docs/items.md @@ -0,0 +1,15 @@ +Items +----- + +Items in this game will be defined in a file, or a set of files. +They will have the following properties: + - unique id + - display name + - texture (or some way of drawing the item) + +The file that the items will be stored in will have a similar format +to the entity scene format: + +item_id: +name="Test" +texture="./assets/..." diff --git a/run_tree/scenes/default.items b/run_tree/scenes/default.items new file mode 100644 index 0000000..f9d6bf4 --- /dev/null +++ b/run_tree/scenes/default.items @@ -0,0 +1,5 @@ +:beer +name="Beer" +weight=12.34 + + diff --git a/run_tree/scenes/quick_save.scene b/run_tree/scenes/quick_save.scene index 9a525a6..17de2c4 100644 --- a/run_tree/scenes/quick_save.scene +++ b/run_tree/scenes/quick_save.scene @@ -47,7 +47,7 @@ entity.size.x = 370.0000 entity.size.y = 24.0000 target_openness = 0.8000 -:Item +:Item_Entity entity.id = 4 entity.flags = 0 entity.pos.x = 891.5230 @@ -59,7 +59,7 @@ color.g = 0.0000 color.b = 1.0000 color.a = 1.0000 -:Item +:Item_Entity entity.id = 5 entity.flags = 4 entity.pos.x = 167.6909 diff --git a/src/build.onyx b/src/build.onyx index 013b443..c886307 100644 --- a/src/build.onyx +++ b/src/build.onyx @@ -30,6 +30,8 @@ DEBUG :: false #load "gfx/texture" #load "gfx/ui" +#load "utils/any_utils" +#load "utils/asset_loader" #load "utils/input" #load "utils/vecmath" diff --git a/src/entity/editor.onyx b/src/entity/editor.onyx index 089a33f..93cf791 100644 --- a/src/entity/editor.onyx +++ b/src/entity/editor.onyx @@ -4,15 +4,16 @@ // // - [x] Create new entity // - [ ] Edit entity properties in UI -// - [ ] Serialize / Deserialize a scene +// - [x] Serialize / Deserialize a scene // use package core use package opengles use package glfw3 -Editor_Range :: struct {min, max: f32;} -Editor_Disabled :: struct {} +Editor_Range :: struct {min, max: f32;} +Editor_Disabled :: struct {} +Editor_Custom_Field :: struct { func: () -> void; } editor_init :: () { editor_font = font_lookup(.{"./assets/fonts/calibri.ttf", 18}); @@ -107,6 +108,11 @@ editor_update :: (dt: f32) { if resizing { selected_entity.size += mouse_get_delta_vector(); } + + if is_key_just_down(GLFW_KEY_DELETE) { + scene->delete(selected_entity); + selected_entity_id = Entity_Nothing; + } } } @@ -212,6 +218,11 @@ editor_draw :: () { info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(entity.type); font_print(editor_big_font, x + 2, y + 24, info.name); + if draw_button(.{ x + w - 150, y, 150, 24 }, "Delete") { + scene->delete(entity); + selected_entity_id = Entity_Nothing; + return; + } if active_index >= 0 do sidebar_width += w; render_struct_fields(any.{~~entity, entity.type}, 0, x, y + 4, w, h); diff --git a/src/entity/items.onyx b/src/entity/items.onyx index e0fa2e2..e6c5b50 100644 --- a/src/entity/items.onyx +++ b/src/entity/items.onyx @@ -1,9 +1,93 @@ use package core + Item :: struct { + name: str; + weight := 10.0f; + + color := Color.{ 1, 1, 0 }; +} + +Item_Store :: struct { + item_allocator: Allocator; + items: Map(str, ^Item); + + load_items_from_file :: item_store_load_items_from_file; +} + +item_store_make :: () -> Item_Store { + is: Item_Store; + is.item_allocator = context.allocator; + map.init(^is.items); + + return is; +} + +item_store_load_items_from_file :: (use this: ^Item_Store, path: str) { + @CopyNPaste // from entity/store.onyx + + err, input_file := os.open(path, .Read); + if err != .None { + printf("Failed to open file: {}\n", path); + return; + } + defer os.close(^input_file); + reader := io.reader_make(^input_file); + + use type_info; + + current_item: ^Item; + + 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 ":" { + item_id := string.advance(line) + |> string.strip_whitespace(); + + if items->has(item_id) { + printf("Duplicate definition for item with id '{}', on line: {}.\n", item_id); + return; + } + + item_id = string.alloc_copy(item_id, allocator=item_allocator); + + current_item = new(Item, allocator=item_allocator); + items[item_id] = current_item; + continue; + } + + if current_item == null do continue; + + var_name := string.read_until(^line, #char "=") + |> string.strip_whitespace(); + string.advance(^line, 1); + string.strip_whitespace(^line); + + member := get_any_for_member(ptr_to_any(current_item), var_name); + if member.data == null { + printf("'{}' is not a valid member of Item on line {}.\n", var_name, line_number); + continue; + } + + if !conv.parse_any(member.data, member.type, line) { + printf("Unable to parse '{}' for type '{}' on line {}.\n", line, member.type, line_number); + continue; + } + } +} + + +Item_Entity :: struct { use entity: Entity; + [Editor_Custom_Field.{render_item_picker}] + item: str; color: Color; init_data :: struct { @@ -11,7 +95,7 @@ Item :: struct { color := Color.{1, 0, 1, 1}; } - init :: (use this: ^Item, data: init_data) { + init :: (use this: ^Item_Entity, data: init_data) { this.pos = data.pos; this.size = .{16, 16}; this.color = data.color; @@ -20,10 +104,17 @@ Item :: struct { get_rect :: Entity.get_rect - draw :: (use this: ^Item) { + draw :: (use this: ^Item_Entity) { immediate_set_color(this.color); r := this->get_rect(); immediate_rectangle(r.x, r.y, r.w, r.h); } } + + +#local { + render_item_picker :: () { + + } +} \ No newline at end of file diff --git a/src/entity/manager.onyx b/src/entity/manager.onyx index 68aca33..6a157d0 100644 --- a/src/entity/manager.onyx +++ b/src/entity/manager.onyx @@ -43,6 +43,7 @@ IsEntity :: interface (e: $E) { Entity_Info :: struct { create_default : (scene: ^Entity_Manager) -> ^Entity = null_proc; + destroy : (entity: ^Entity) -> void = null_proc; update : (entity: ^Entity, dt: f32) -> void = null_proc; draw : (entity: ^Entity) -> void = null_proc; get_rect : (entity: ^Entity) -> Rect = null_proc; @@ -66,6 +67,7 @@ Entity_Manager :: struct { update :: entity_manager_update; draw :: entity_manager_draw; get :: entity_manager_get; + delete :: entity_manager_delete; query :: entity_manager_query; query_by_type :: entity_manager_query_by_type; query_by_flags :: entity_manager_query_by_flags; @@ -116,6 +118,7 @@ entity_manager_register :: (use this: ^Entity_Manager, $entity_type: type_expr) @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.destroy) { info.destroy = entity_type.destroy; if DEBUG do printf("{} has '{}'.\n", entity_type, "destroy"); } #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"); } @@ -178,6 +181,12 @@ entity_manager_draw :: (use this: ^Entity_Manager) { entity_manager_get :: (use this: ^Entity_Manager, id: Entity_ID) => entity_map[id]; +entity_manager_delete :: (use this: ^Entity_Manager, ent: ^Entity) { + map.delete(^entity_map, ent.id); + array.remove(^entities, ent); // This preserves the order of the entities, but does that matter? + raw_free(entity_allocator, ent); +} + entity_manager_query :: (use this: ^Entity_Manager, area: Rect) -> [] ^Entity { ents: [..] ^Entity; for entities { diff --git a/src/entity/player.onyx b/src/entity/player.onyx index 5a3f091..850ef5b 100644 --- a/src/entity/player.onyx +++ b/src/entity/player.onyx @@ -178,12 +178,13 @@ Player :: struct { immediate_set_color(color); rect := this->get_rect(); - immediate_image(^player_texture, rect.x, rect.y, rect.w, rect.h); + immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h); } } -player_texture: Texture; - +player_assets: struct { + ["assets/images/player.png"] texture: Texture; +} Wall :: struct { diff --git a/src/entity/store.onyx b/src/entity/store.onyx index 54c0b3e..810604d 100644 --- a/src/entity/store.onyx +++ b/src/entity/store.onyx @@ -136,30 +136,6 @@ entity_manager_load_from_file :: (use this: ^Entity_Manager, filename: str) { } 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}; - } - } - } - - return .{null, void}; - } } diff --git a/src/game.onyx b/src/game.onyx index 5f6c872..36e28a5 100644 --- a/src/game.onyx +++ b/src/game.onyx @@ -7,17 +7,28 @@ use package glfw3 // scene: Entity_Manager; +item_store: Item_Store; game_init :: () { - player_texture = texture_make(#cstr "./assets/images/player.png"); + // player_texture = texture_make(#cstr "./assets/images/player.png"); + + // This process of queueing the asset bucket should + // be made automatic somehow... + queue_assets(^player_assets); + + + load_assets(); scene = entity_manager_create(); scene->register(Player); scene->register(Wall); scene->register(Door); - scene->register(Item); + scene->register(Item_Entity); scene->load_from_file("scenes/quick_save.scene"); + item_store = item_store_make(); + item_store->load_items_from_file("scenes/default.items"); + #if DEBUG { println("Registered Entity types:"); for scene.entity_types.entries { diff --git a/src/gfx/texture.onyx b/src/gfx/texture.onyx index 5d51168..b5c1427 100644 --- a/src/gfx/texture.onyx +++ b/src/gfx/texture.onyx @@ -1,19 +1,36 @@ use package core +use package core.intrinsics.onyx { __zero_value as Zero } use package opengles use package stb_image +#local texture_cache: Map(str, Texture); + Texture :: struct { texture: GLint; width, height, channels: i32; filename: str; } -texture_make :: (path: cstr) -> Texture { +texture_make :: #match {} +#match texture_make (filename: str) -> (Texture, bool) { + buffer: [512] u8; + memory.copy(~~ buffer, filename.data, math.min(filename.count, 511)); + return texture_make(cast(cstr) buffer); +} + +#match texture_make (path: cstr) -> (Texture, bool) { + filename := string.from_cstr(path); + if texture_cache->has(filename) { + return texture_cache[filename], true; + } + tex: Texture; - tex.filename = path |> string.from_cstr(); + tex.filename = filename; pixels := stbi_load(path, ^tex.width, ^tex.height, ^tex.channels, 4); - assert(pixels != null, "Failed to load texture."); + if pixels == null { + return Zero(Texture), false; + } defer stbi_image_free(pixels); glGenTextures(1, ^tex.texture); @@ -28,11 +45,17 @@ texture_make :: (path: cstr) -> Texture { glBindTexture(GL_TEXTURE_2D, 0); - return tex; + // This assumes that the filename data is going to be persistant forever. + // Not a great assumption to make but is it really worth copying it? + texture_cache[filename] = tex; + return tex, true; +} + +texture_free :: (use tex: ^Texture) { + glDeleteTextures(1, ^texture); } texture_use :: (use tex: ^Texture, texture_binding := 0) { glActiveTexture(GL_TEXTURE0 + texture_binding); glBindTexture(GL_TEXTURE_2D, texture); } - diff --git a/src/utils/any_utils.onyx b/src/utils/any_utils.onyx new file mode 100644 index 0000000..46d1b17 --- /dev/null +++ b/src/utils/any_utils.onyx @@ -0,0 +1,42 @@ +// +// The contents of this file will probably be merged with the +// core libraries in Onyx at some point. I just haven't flushed +// out the details yet on what everything should exactly do and +// be responsible for. +// + +use package core + +ptr_to_any :: (x: ^$T) -> any { + return .{x, T}; +} + +// +// This function looks up a member's offset and type (effectively +// an 'any'), given the base pointer and type in the form of an +// 'any' and the member_name, which is a '.' separate list of +// symbols. +// +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}; + } + } + } + + return .{null, void}; +} diff --git a/src/utils/asset_loader.onyx b/src/utils/asset_loader.onyx new file mode 100644 index 0000000..15a0b17 --- /dev/null +++ b/src/utils/asset_loader.onyx @@ -0,0 +1,61 @@ + +use package core + +queue_assets :: (store: ^$T, callsite := #callsite) { + assets_to_load << .{ T, store, callsite }; +} + +load_assets :: () { + for^ assets_to_load { + load_asset_bucket(it); + } + + array.clear(^assets_to_load); +} + + +#local { + assets_to_load: [..] Asset_Bucket_To_Load; + + Asset_Bucket_To_Load :: struct { + type: type_expr; + dest: rawptr; + + location: CallSite; + } + + load_asset_bucket :: (bucket: ^Asset_Bucket_To_Load) { + use type_info; + + struct_info := cast(^Type_Info_Struct) get_type_info(bucket.type); + if struct_info.kind != .Struct do return; + + for struct_info.members { + load_asset(cast(^u8) bucket.dest + it.offset, it.type, bucket.location, it.tags); + } + } + + load_asset :: (dest: rawptr, type: type_expr, location: CallSite, tags: [] any) { + switch type { + case Texture { + out_texture := cast(^Texture) dest; + path_tag := array.first(tags, (x) => x.type == str); + + if path_tag != null { + path := *cast(^str) path_tag.data; + if texture, success := texture_make(path); success { + *out_texture = texture; + } else { + printf("[ERROR] Failed to load texture '{}' for asset queued here: {}:{},{}.\n", path, location.file, location.line, location.column); + } + } else { + printf("[ERROR] Texture path not found for texture asset load here: {}:{},{}.\n", location.file, location.line, location.column); + } + } + + case #default { + printf("[WARN] Asset loader does not know how to load a '{}'.\n", type); + } + } + } +} -- 2.25.1