diff --git a/.vscode/launch.json b/.vscode/launch.json index af68a68..110830b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "request": "launch", "preLaunchTask": "Build Debug", "name": "Debug", - "program": "${workspaceFolder}/build/game_debug.exe", + "program": "${workspaceFolder}/game_debug.exe", "args": [], "cwd": "${workspaceFolder}" }, diff --git a/.vscode/settings.json b/.vscode/settings.json index f7964b5..6c4f812 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,10 @@ { + "workbench.colorCustomizations": { + "activityBar.background": "#322C2D", + "titleBar.activeBackground": "#463E3F", + "titleBar.activeForeground": "#FAFAFA" + }, "[odin]": { - "editor.formatOnSave": true, - "editor.tabSize": 4 + "editor.formatOnSave": true } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 784b8a9..cc072d6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,90 +3,97 @@ "command": "", "args": [], "tasks": [ - { - "label": "Build Debug", - "type": "shell", - "windows": { - "command": "${workspaceFolder}/scripts/build_debug.bat" + { + "label": "Build Debug", + "type": "shell", + "windows": { + "command": "${workspaceFolder}/scripts/build_debug.bat", + }, + "linux": { + "command": "${workspaceFolder}/scripts/build_debug.sh", + }, + "osx": { + "command": "${workspaceFolder}/scripts/build_debug.sh", + }, + "group": { + "kind": "build", + "isDefault": false + }, }, - "linux": { - "command": "${workspaceFolder}/scripts/build_debug.sh" + { + "label": "Build Release", + "type": "shell", + "windows": { + "command": "${workspaceFolder}/scripts/build_release.bat", + }, + "linux": { + "command": "${workspaceFolder}/scripts/build_release.sh", + }, + "osx": { + "command": "${workspaceFolder}/scripts/build_release.sh", + }, + "group": "build" }, - "osx": { - "command": "${workspaceFolder}/scripts/build_debug.sh" + { + "label": "Clean build folder(s)", + "type": "shell", + "windows": { + "command": "cd ${workspaceFolder}\\build && rm game*; cd ${workspaceFolder} && rm aseprite_odin_generator*", + }, + // "linux": { + // "command": "${workspaceFolder}/scripts/build_release.sh", + // }, + // "osx": { + // "command": "${workspaceFolder}/scripts/build_release.sh", + // }, + "group": "build" }, - "group": { - "kind": "build", - "isDefault": true + { + "label": "Build Hot Reload", + "type": "shell", + "windows": { + "command": "${workspaceFolder}/scripts/build_hot_reload.bat; start game.exe", + }, + "linux": { + "command": "${workspaceFolder}/scripts/build_hot_reload.sh", + }, + "osx": { + "command": "${workspaceFolder}/scripts/build_hot_reload.sh", + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true + }, + "group": { + "kind": "build", + "isDefault": false + }, }, - "problemMatcher": [] - }, - { - "label": "Build Release", - "type": "shell", - "windows": { - "command": "${workspaceFolder}/scripts/build_release.bat" - }, - "linux": { - "command": "${workspaceFolder}/scripts/build_release.sh" - }, - "osx": { - "command": "${workspaceFolder}/scripts/build_release.sh" - }, - "group": "build" - }, - { - "label": "Clean build folder(s)", - "type": "shell", - "windows": { - "command": "cd ${workspaceFolder}\\build && rm game*; cd ${workspaceFolder} && rm aseprite_odin_generator*" - }, - "group": "build" - }, - { - "label": "Build Hot Reload", - "type": "shell", - "windows": { - "command": "${workspaceFolder}/scripts/build_hot_reload.bat; start game.exe" - }, - "linux": { - "command": "${workspaceFolder}/scripts/build_hot_reload.sh" - }, - "osx": { - "command": "${workspaceFolder}/scripts/build_hot_reload.sh" - }, - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "dedicated", - "showReuseMessage": false, - "clear": true - }, - "group": "build", - "problemMatcher": [] - }, - { - "label": "Build&Run Atlas Generator Test", - "type": "shell", - "windows": { - "command": "${workspaceFolder}/scripts/build_generator_debug.bat && build_generator\\aseprite_odin_generator.exe -input-files:value_of_custom_arg -h" - }, - "options": { - "cwd": "${workspaceFolder}" - }, - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "dedicated", - "showReuseMessage": false, - "clear": true - }, - "group": { - "kind": "build", - "isDefault": false + { + "label": "Build&Run Atlas Generator Test", + "type": "shell", + "windows": { + "command": "${workspaceFolder}/scripts/build_generator_debug.bat && build_generator\\aseprite_odin_generator.exe -input-files:value_of_custom_arg -h", + }, + "options": { + "cwd": "${workspaceFolder}" + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true + }, + "group": { + "kind": "build", + "isDefault": true + }, } - } ] } \ No newline at end of file diff --git a/src/game.odin b/src/game.odin index b289b82..237bfac 100644 --- a/src/game.odin +++ b/src/game.odin @@ -14,7 +14,6 @@ package game import "core:fmt" import "core:math" import "core:strings" -import "utils" import rl "vendor:raylib" import diag "dialog" @@ -262,8 +261,8 @@ draw_atlas_settings_and_preview :: proc() { // rl.GuiLine({y = elements_height, width = left_half_rect.width}, "Actions") // elements_height += small_offset + actions_label_y := elements_height { - actions_label_y := elements_height defer rl.GuiGroupBox( { x = small_offset / 2, @@ -287,7 +286,6 @@ draw_atlas_settings_and_preview :: proc() { g_mem.should_open_file_dialog = true g_mem.source_location_type = .SourceFiles } - if rl.GuiButton( { x = left_half_rect.width / 2, @@ -314,7 +312,6 @@ draw_atlas_settings_and_preview :: proc() { ) { g_mem.should_render_atlas = true } - if rl.GuiButton( { x = left_half_rect.width / 2, @@ -339,7 +336,6 @@ draw_atlas_settings_and_preview :: proc() { ) { save_output() } - if rl.GuiButton( { x = left_half_rect.width / 2, @@ -349,9 +345,6 @@ draw_atlas_settings_and_preview :: proc() { }, "Save To...", ) { - if output_folder, ok := g_mem.output_folder_path.(string); ok { - save_metadata_simple(output_folder) - } } elements_height += small_offset * 2 } @@ -474,9 +467,7 @@ open_file_dialog :: proc() { file_path: cstring patterns: []cstring = {"*.png"} if default_path, ok := g_mem.output_folder_path.(string); ok { - default_path_filename := strings.concatenate( - {default_path, os_file_separator, "atlas.png"}, - ) + default_path_filename := strings.concatenate({default_path, os_file_separator, "atlas.png"}) default_path_to_save: cstring = strings.clone_to_cstring(default_path_filename) file_path = cstring( diag.save_file_dialog( @@ -500,9 +491,9 @@ open_file_dialog :: proc() { } clear_atlas_data :: proc() { - if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok { - delete(metadata) - // g_mem.atlas_metadata = nil - } + if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok { + delete(metadata) + // g_mem.atlas_metadata = nil + } g_mem.atlas_render_has_preview = false } diff --git a/src/generator.odin b/src/generator.odin index 571cca2..d0f01d8 100644 --- a/src/generator.odin +++ b/src/generator.odin @@ -102,7 +102,7 @@ unmarshall_aseprite_files :: proc( atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entry: AtlasEntry) { atlas_entry.frames = auto_cast len(document.frames) fmt.println("N Frames: ", len(document.frames)) - // NOTE(stefan): Since the expected input for the program is multiple files containing a single sprite + // note(stefan): Since the expected input for the program is multiple files containing a single sprite // it's probably a safe assumption most of the files will be a single layer with 1 or more frames // which means we can first prod the file for information about how many frames are there and // allocate a slice that is going to be [Frames X Layers]CellData. @@ -250,7 +250,7 @@ pack_atlas_entries :: proc( } cell_metadata := SpriteAtlasMetadata { name = cell_name, - location = { + location = { auto_cast rect.x + auto_cast offset_x, auto_cast rect.y + auto_cast offset_y, }, @@ -264,10 +264,8 @@ pack_atlas_entries :: proc( SourceCodeGeneratorMetadata :: struct { file_defines: struct { - top: string, - bottom: string, - file_name: string, - file_extension: string, + top: string, + bottom: string, }, lanugage_settings: struct { first_class_enum_arrays: bool, // for languages that support creating arrays that contain for each enum value an entry in the enum_data.entry_line: .EnumCase = {array entry} @@ -292,23 +290,18 @@ SourceCodeGeneratorMetadata :: struct { } odin_source_generator_metadata := SourceCodeGeneratorMetadata { - file_defines = { - top = "package atlas_bindings\n\n", - bottom = "", - file_name = "metadata", - file_extension = ".odin", - }, - custom_data_type = { + file_defines = {top = "package atlas_bindings\n\n", bottom = ""}, + custom_data_type = { name = "AtlasRect", type_declaration = "%v :: struct {{ x, y, w, h: i32 }}\n\n", }, - enum_data = { + enum_data = { name = "AtlasEnum", begin_line = "%v :: enum {{\n", entry_line = "\t%s,\n", end_line = "}\n\n", }, - array_data = { + array_data = { name = "ATLAS_SPRITES", type = "[]AtlasRect", begin_line = "%v := %v {{\n", @@ -320,23 +313,18 @@ odin_source_generator_metadata := SourceCodeGeneratorMetadata { cpp_source_generator_metadata := SourceCodeGeneratorMetadata { - file_defines = { - top = "#include \n\n", - bottom = "", - file_name = "metadata", - file_extension = ".hpp", - }, - custom_data_type = { + file_defines = {top = "#include \n\n", bottom = ""}, + custom_data_type = { name = "AtlasRect", type_declaration = "struct %v {{\n\tint x;\n\tint y;\n\tint w;\n\tint h;\n}};\n\n", }, - enum_data = { + enum_data = { name = "AtlasEnum", begin_line = "enum %v {{\n", entry_line = "\t%s,\n", end_line = "\n\tCOUNT\n}\n\n", }, - array_data = { + array_data = { name = "ATLAS_SPRITES", type = "AtlasRect[size_t(AtlasEnum::COUNT)-1]", begin_line = "{1} {0} = {{\n", @@ -396,9 +384,14 @@ generate_odin_enums_and_atlas_offsets_file_sb :: proc( metadata_source_code_generate :: proc( metadata: []SpriteAtlasMetadata, - codegen: SourceCodeGeneratorMetadata, + code_generation_metadata: Maybe(SourceCodeGeneratorMetadata), alloc := context.allocator, ) -> strings.Builder { + codegen, ok := code_generation_metadata.(SourceCodeGeneratorMetadata) + + if !ok { + return generate_odin_enums_and_atlas_offsets_file_sb(metadata, alloc) + } sb := strings.builder_make(alloc) // strings.write_string(&sb, "package atlas_bindings\n\n") @@ -459,117 +452,6 @@ metadata_source_code_generate :: proc( } -save_output :: proc() { - output_path, ok := g_mem.output_folder_path.(string) - if !ok || output_path == "" { - fmt.println("Output path is empty!") - return - } - - image := rl.LoadImageFromTexture(g_mem.atlas_render_texture_target.texture) - rl.ImageFlipVertical(&image) - - cstring_atlas_output_path := strings.clone_to_cstring( - strings.concatenate({output_path, os_file_separator, "atlas.png"}), - ) - - rl.ExportImage(image, cstring_atlas_output_path) - - if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok { - fmt.println("Building metadata...") - if json_metadata, jok := json.marshal(metadata); jok == nil { - os.write_entire_file( - strings.concatenate({output_path, os_file_separator, "metadata.json"}), - json_metadata, - ) - } else { - fmt.println("Failed to marshall the atlas metadata to a json!") - } - - // TODO(stefan): Think of a more generic alternative to just straight output to a odin file - // maybe supply a config.json that defines the start, end, line by line entry and enum format strings - // this way you can essentially support any language - sb := generate_odin_enums_and_atlas_offsets_file_sb(metadata[:]) - odin_metadata := strings.to_string(sb) - ok := os.write_entire_file( - strings.concatenate({output_path, os_file_separator, "metadata.odin"}), - transmute([]byte)odin_metadata, - ) - if !ok { - fmt.println("Failed to save 'metadata.odin'") - } - } else { - fmt.println("No metadata to export!") - } - -} - -save_metadata_simple :: proc( - output_path: string, - json_file_name: Maybe(string), - source_file_name: Maybe(string), - source_gen_metadata: Maybe(SourceCodeGeneratorMetadata), -) { - json_file_base_name, json_file_name_ok := json_file_name.(string) - source_file_base_name, source_file_name_ok := source_file_name.(string) - - if !json_file_name_ok && !source_file_name_ok { - fmt.println("Neither a json file name or a source code filename has been provided!") - return - } - - metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata);if !ok { - fmt.println("No metadata to export!") - } - - fmt.println("Building metadata...") - if json_file_name_ok { - if json_metadata, jok := json.marshal(metadata); jok == nil { - json_output_path := strings.concatenate( - {output_path, os_file_separator, json_file_base_name}, - ) - if ok = os.write_entire_file(json_output_path, json_metadata); !ok { - fmt.println("Failed to write json to file: ", json_output_path) - } - } else { - fmt.println("Failed to marshall the atlas metadata to a json!") - } - } - - // note(stefan): Having source_file_name & source_gen_metadata is redundant but this is fine for now - if source_file_name_ok { - // if src_gen_metadata - if codegen, cok := source_gen_metadata.(SourceCodeGeneratorMetadata); cok { - sb := metadata_source_code_generate(metadata[:], codegen) - - source_metadata := strings.to_string(sb) - source_output_path := strings.concatenate( - { - output_path, - os_file_separator, - codegen.file_defines.file_name, - codegen.file_defines.file_extension, - }, - ) - ok := os.write_entire_file(source_output_path, transmute([]byte)source_metadata) - if !ok { - fmt.println("Failed to save source code to file:", source_output_path) - } - } else { - sb := metadata_source_code_generate(metadata[:], odin_source_generator_metadata) - odin_metadata := strings.to_string(sb) - source_output_path := strings.concatenate( - {output_path, os_file_separator, "metadata.odin"}, - ) - - ok := os.write_entire_file(source_output_path, transmute([]byte)odin_metadata) - if !ok { - fmt.println("Failed to save source code to file:", source_output_path) - } - } - } -} - save_metadata :: proc( settings: utils.CLIPackerSettings, atlas_entries: []AtlasEntry, @@ -591,3 +473,46 @@ save_metadata :: proc( os.write_entire_file(source_code_path, transmute([]byte)source_code_output_str) } } + +save_output :: proc() { + output_path, ok := g_mem.output_folder_path.(string) + if !ok { + fmt.println("Output path is empty!") + return + } else if output_path == "" { + fmt.println("Output path is empty!") + return + } + + image := rl.LoadImageFromTexture(g_mem.atlas_render_texture_target.texture) + rl.ImageFlipVertical(&image) + + output_path = strings.concatenate({output_path, os_file_separator, "atlas.png"}) + cstring_output_path := strings.clone_to_cstring(output_path) + + rl.ExportImage(image, cstring_output_path) + + if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok { + if json_metadata, jok := json.marshal(metadata); jok == nil { + os.write_entire_file( + strings.concatenate({output_path, os_file_separator, "metadata.json"}), + json_metadata, + ) + } else { + fmt.println("Failed to marshall the atlas metadata to a json!") + } + + // TODO(stefan): Think of a more generic alternative to just straight output to a odin file + // maybe supply a config.json that defines the start, end, line by line entry and enum format strings + // this way you can essentially support any language + sb := generate_odin_enums_and_atlas_offsets_file_sb(metadata[:]) + odin_metadata := strings.to_string(sb) + os.write_entire_file( + strings.concatenate({output_path, os_file_separator, "metadata.odin"}), + transmute([]byte)odin_metadata, + ) + } else { + fmt.println("No metadata to export!") + } + +} diff --git a/src/utils/cli.odin b/src/utils/cli.odin index 8b9411f..d6aa08a 100644 --- a/src/utils/cli.odin +++ b/src/utils/cli.odin @@ -97,7 +97,6 @@ parse_arguments :: proc(args: []string) -> (cliargs: map[CLIFlagType]CLIFlag) { for arg in args { arg_name_and_value, err := s.split(arg, ":") - if err != nil {continue} name := arg_name_and_value[0] if name[0] == '-' {