diff --git a/.gitignore b/.gitignore index 91dc2bb..66483a8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,4 @@ ols.json libtinyfiledialogs.a libtinyfiledialogs.obj tinyfiledialogs.lib -tinyfiledialogs.obj - -.idea/ +tinyfiledialogs.obj \ No newline at end of file diff --git a/src/aseprite_odin_generator/aseprite_odin_generator.odin b/src/aseprite_odin_generator/aseprite_odin_generator.odin index 00549e7..225578e 100644 --- a/src/aseprite_odin_generator/aseprite_odin_generator.odin +++ b/src/aseprite_odin_generator/aseprite_odin_generator.odin @@ -30,35 +30,14 @@ main :: proc() { target_dir := s.concatenate({cwd, "\\src\\aseprite_odin_generator\\"}) atlas: rl.Image = rl.GenImageColor(ATLAS_SIZE, ATLAS_SIZE, rl.BLANK) - atlas_entries: [dynamic]gen.AtlasEntry = make([dynamic]gen.AtlasEntry) + atlas_entries: [dynamic]gen.AtlasEntry gen.unmarshall_aseprite_dir(target_dir, &atlas_entries) metadata := gen.pack_atlas_entries(atlas_entries[:], &atlas, 10, 10) json_bytes, jerr := json.marshal(metadata) - os.write_entire_file("src/aseprite_odin_generator/metadata.json", json_bytes) - sb := gen.metadata_source_code_generate(metadata[:], gen.odin_source_generator_metadata) - odin_output_str := s.to_string(sb) - os.write_entire_file( - "src/aseprite_odin_generator/output.odino", - transmute([]byte)odin_output_str, - ) + os.write_entire_file("src/aseprite_odin_generator/metadata.json", json_bytes) + rl.ExportImage(atlas, EXPORT_PATH) - - // something : string = "hello" - // fmt.printf("{1} {2} else", something, 10) - - // TestStruct :: struct { - // something: struct { - // name: string, - // age: int, - // }, - // } - // ts: TestStruct - // ts.something.name = "name" - - // jb, err := json.marshal(ts) - // sjb := transmute(string)jb - // fmt.println(sjb) } diff --git a/src/aseprite_odin_generator/metadata.json b/src/aseprite_odin_generator/metadata.json index be61ff1..a07b92b 100644 --- a/src/aseprite_odin_generator/metadata.json +++ b/src/aseprite_odin_generator/metadata.json @@ -1 +1 @@ -[{"name":"Edinica","location":[95,10],"size":[58,57]},{"name":"Dvoika_0","location":[234,10],"size":[55,31]},{"name":"Dvoika_1","location":[163,10],"size":[61,33]},{"name":"Troika","location":[10,10],"size":[75,75]}] \ No newline at end of file +[{"name":"Edinica","location":[95,10],"size":[58,57]},{"name":"Dvoika_0","location":[234,10],"size":[55,31]},{"name":"Troika","location":[10,10],"size":[75,75]},{"name":"Dvoika_1","location":[163,10],"size":[61,33]}] \ No newline at end of file diff --git a/src/game.odin b/src/game.odin index 237bfac..0df7608 100644 --- a/src/game.odin +++ b/src/game.odin @@ -95,8 +95,7 @@ draw_screen_target :: proc() { g_mem.packer_settings.pixel_padding_x_int if g_mem.packer_settings.padding_enabled else 0 padding_y := g_mem.packer_settings.pixel_padding_y_int if g_mem.packer_settings.padding_enabled else 0 - - g_mem.atlas_metadata = pack_atlas_entries(atlas_entries[:], &atlas, padding_x, padding_y) + pack_atlas_entries(atlas_entries[:], &atlas, padding_x, padding_y) // OpenGL's Y buffer is flipped rl.ImageFlipVertical(&atlas) @@ -321,7 +320,7 @@ draw_atlas_settings_and_preview :: proc() { }, "Clear Atlas", ) { - clear_atlas_data() + g_mem.atlas_render_has_preview = false } elements_height += small_offset * 2 @@ -467,7 +466,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, atlas_path}) default_path_to_save: cstring = strings.clone_to_cstring(default_path_filename) file_path = cstring( diag.save_file_dialog( @@ -489,11 +488,3 @@ open_file_dialog :: proc() { g_mem.should_open_file_dialog = false } - -clear_atlas_data :: proc() { - 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 a8db468..62483a8 100644 --- a/src/generator.odin +++ b/src/generator.odin @@ -1,10 +1,11 @@ package game import ase "./aseprite" +import "core:encoding/json" import "core:fmt" +import "core:mem" import "core:os" import fp "core:path/filepath" -import "core:slice" import "core:strings" import rl "vendor:raylib" import stbrp "vendor:stb/rect_pack" @@ -24,23 +25,24 @@ AtlasEntry :: struct { layer_cell_count: [dynamic]i32, } +SingleFrameSprite :: distinct rl.Rectangle + +AnimatedSprite :: struct { + x, y: i32, + width, height: i32, +} + SpriteAtlasMetadata :: struct { name: string, location: [2]i32, size: [2]i32, } -unmarshall_aseprite_dir :: proc( - path: string, - atlas_entries: ^[dynamic]AtlasEntry, - alloc := context.allocator, -) { - if len(path) == 0 do return - +unmarshall_aseprite_dir :: proc(path: string, atlas_entries: ^[dynamic]AtlasEntry) { if dir_fd, err := os.open(path, os.O_RDONLY); err == os.ERROR_NONE { fis: []os.File_Info if fis, err = os.read_dir(dir_fd, -1); err == os.ERROR_NONE { - unmarshall_aseprite_files_file_info(fis, atlas_entries, alloc) + unmarshall_aseprite_files_file_info(fis, atlas_entries) } } else { fmt.println("Couldn't open folder: ", path) @@ -50,35 +52,30 @@ unmarshall_aseprite_dir :: proc( unmarshall_aseprite_files_file_info :: proc( files: []os.File_Info, atlas_entries: ^[dynamic]AtlasEntry, - alloc := context.allocator, ) { if len(files) == 0 do return - paths := make([]string, len(files), alloc) + paths := make([]string, len(files)) defer delete(paths) for f, fi in files { paths[fi] = f.fullpath } - unmarshall_aseprite_files(paths[:], atlas_entries, alloc) + unmarshall_aseprite_files(paths[:], atlas_entries) } -unmarshall_aseprite_files :: proc( - file_paths: []string, - atlas_entries: ^[dynamic]AtlasEntry, - alloc := context.allocator, -) { +unmarshall_aseprite_files :: proc(file_paths: []string, atlas_entries: ^[dynamic]AtlasEntry) { if len(file_paths) == 0 do return aseprite_document: ase.Document for file in file_paths { extension := fp.ext(file) - if extension != ".aseprite" do continue + if extension != ".aseprite" {continue} fmt.println("Unmarshalling file: ", file) - ase.unmarshal_from_filename(file, &aseprite_document, alloc) + ase.unmarshal_from_filename(file, &aseprite_document) atlas_entry := atlas_entry_from_compressed_cells(aseprite_document) atlas_entry.path = file @@ -92,13 +89,6 @@ 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 - // 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. - // which would allow us to gain an already sorted list of sprites if we iterate all frames of a single layer - // instead of iterating all layers for each frame - // might be even quicker to first get that information an allocate at once the amount of cells we need for frame, frameIdx in document.frames { fmt.printfln("Frame_{0} Chunks: ", frameIdx, len(frame.chunks)) for chunk in frame.chunks { @@ -119,21 +109,14 @@ atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entr opacity = cel_chunk.opacity_level, layer_index = cel_chunk.layer_index, } - append(&atlas_entry.cells, cell) } - if layer_chunk, ok := chunk.(ase.Layer_Chunk); ok { fmt.println("Layer chunk: ", layer_chunk) append(&atlas_entry.layer_names, layer_chunk.name) } } } - - slice.sort_by(atlas_entry.cells[:], proc(i, j: CellData) -> bool { - return i.layer_index < j.layer_index - }) - return } @@ -227,17 +210,17 @@ pack_atlas_entries :: proc( entry := entry_and_cell.entry cell := entry_and_cell.cell_of_entry - cell_name: string - if entry.layer_cell_count[cell.layer_index] > 1 { - cell_name = fmt.aprintf( - "{0}_%d", - entry.layer_names[cell.layer_index], - cell.frame_index, - allocator, - ) - } else { - cell_name = entry.layer_names[cell.layer_index] - } + cell_name : string + if entry.layer_cell_count[cell.layer_index] > 1 { + cell_name = fmt.aprintf( + "{0}_%d", + entry.layer_names[cell.layer_index], + cell.frame_index, + allocator, + ) + } else { + cell_name = entry.layer_names[cell.layer_index] + } cell_metadata := SpriteAtlasMetadata { name = cell_name, location = { @@ -248,196 +231,5 @@ pack_atlas_entries :: proc( } append(&metadata, cell_metadata) } - return metadata } - -SourceCodeGeneratorMetadata :: struct { - file_defines: struct { - 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} - }, - custom_data_type: struct { - name: string, - type_declaration: string, // contains one param: custom_data_type.name + the rest of the type declaration like braces of the syntax & the type members - }, - enum_data: struct { - name: string, - begin_line: string, // contains one params: enum_data.name - entry_line: string, - end_line: string, - }, - array_data: struct { - name: string, - type: string, - begin_line: string, // array begin line contains 2 params in the listed order: array.name, array.type - entry_line: string, // array entry contains 5 params in the listed order: cell.name, cell.location.x, cell.location.y, cell.size.x, cell.size.y, - end_line: string, - }, -} - -odin_source_generator_metadata := SourceCodeGeneratorMetadata { - 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 = { - name = "AtlasEnum", - begin_line = "%v :: enum {{\n", - entry_line = "\t%s,\n", - end_line = "}\n\n", - }, - array_data = { - name = "ATLAS_SPRITES", - type = "[]AtlasRect", - begin_line = "%v := %v {{\n", - entry_line = "\t.%v = {{ x = %v, y = %v, w = %v, h = %v }},\n", - end_line = "}\n\n", - }, - lanugage_settings = {first_class_enum_arrays = true}, -} - - -cpp_source_generator_metadata := SourceCodeGeneratorMetadata { - 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 = { - name = "AtlasEnum", - begin_line = "enum %v {{\n", - entry_line = "\t%s,\n", - end_line = "\n\tCOUNT\n}\n\n", - }, - array_data = { - name = "ATLAS_SPRITES", - type = "AtlasRect[size_t(AtlasEnum::COUNT)-1]", - begin_line = "{1} {0} = {{\n", - entry_line = "\t{{ {1}, {2}, {3}, {4} }},\n", - end_line = "}\n\n", - }, -} - -/* - Generates a barebones file with the package name "atlas_bindings", - the file contains an array of offsets, indexed by an enum. - The enum has unique names -*/ -generate_odin_enums_and_atlas_offsets_file_sb :: proc( - metadata: []SpriteAtlasMetadata, - alloc := context.allocator, -) -> strings.Builder { - sb := strings.builder_make(alloc) - strings.write_string(&sb, "package atlas_bindings\n\n") - - // Introduce the Rect type - strings.write_string(&sb, "AtlasRect :: struct { x, y, w, h: i32 }\n\n") - // start enum - strings.write_string(&sb, "AtlasSprite :: enum {\n") - { - for cell in metadata { - strings.write_string(&sb, fmt.aprintf("\t%s,\n", cell.name)) - } - } - // end enum - strings.write_string(&sb, "}\n\n") - - // start offsets array - // todo(stefan): the name of the array can be based on the output name? - strings.write_string(&sb, "ATLAS_SPRITES := []AtlasRect {\n") - { - entry: string - for cell in metadata { - entry = fmt.aprintf( - "\t.%v = {{ x = %v, y = %v, w = %v, h = %v }},\n", - cell.name, - cell.location.x, - cell.location.y, - cell.size.x, - cell.size.y, - ) - strings.write_string(&sb, entry) - } - } - // end offsets array - strings.write_string(&sb, "}\n\n") - - fmt.println("\n", strings.to_string(sb)) - - return sb -} - -metadata_source_code_generate :: proc( - metadata: []SpriteAtlasMetadata, - 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") - strings.write_string(&sb, codegen.file_defines.top) - - // Introduce the Rect type - // strings.write_string(&sb, "AtlasRect :: struct { x, y, w, h: i32 }\n\n") - strings.write_string( - &sb, - fmt.aprintf(codegen.custom_data_type.type_declaration, codegen.custom_data_type.name), - ) - // start enum - // strings.write_string(&sb, "AtlasSprite :: enum {\n") - strings.write_string(&sb, fmt.aprintf(codegen.enum_data.begin_line, codegen.enum_data.name)) - { - for cell in metadata { - // strings.write_string(&sb, fmt.aprintf("\t%s,\n", cell.name)) - strings.write_string(&sb, fmt.aprintf(codegen.enum_data.entry_line, cell.name)) - } - } - // end enum - // strings.write_string(&sb, "}\n\n") - strings.write_string(&sb, codegen.enum_data.end_line) - - // start offsets array - // strings.write_string(&sb, "ATLAS_SPRITES := []AtlasRect {\n") - strings.write_string( - &sb, - fmt.aprintf( - codegen.array_data.begin_line, - codegen.array_data.name, - codegen.array_data.type, - ), - ) - { - entry: string - for cell in metadata { - entry = fmt.aprintf( - codegen.array_data.entry_line, // "\t.%v = {{ x = %v, y = %v, w = %v, h = %v }},\n", - cell.name, - cell.location.x, - cell.location.y, - cell.size.x, - cell.size.y, - ) - strings.write_string(&sb, entry) - } - } - // end offsets array - // strings.write_string(&sb, "}\n\n") - strings.write_string(&sb, codegen.array_data.end_line) - - strings.write_string(&sb, codegen.file_defines.bottom) - - fmt.println("\n", strings.to_string(sb)) - - return sb - -} diff --git a/src/globals.odin b/src/globals.odin index 7808893..42a8b54 100644 --- a/src/globals.odin +++ b/src/globals.odin @@ -59,5 +59,4 @@ GameMemory :: struct { should_render_atlas: bool, atlas_render_has_preview: bool, atlas_render_size: i32, - atlas_metadata: Maybe([dynamic]SpriteAtlasMetadata), } diff --git a/src/save_output.odin b/src/save_output.odin index e6498d5..68363fe 100644 --- a/src/save_output.odin +++ b/src/save_output.odin @@ -1,57 +1,28 @@ package game -import "core:fmt" import "core:strings" -import "core:os" -import "core:encoding/json" +import "core:fmt" import rl "vendor:raylib" - when ODIN_OS == .Windows { - os_file_separator :: "\\" + atlas_path :: "\\atlas.png" } else { - os_file_separator :: "/" + atlas_path :: "/atlas.png" } save_output :: proc() { if output_path, ok := g_mem.output_folder_path.(string); ok { - if len(output_path) == 0 { - fmt.println("Output path is empty!") - return - } + if len(output_path) == 0 { + 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"}) + output_path := strings.concatenate({output_path, atlas_path}) 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!") - } - } else { - fmt.println("Output path is empty!") - } + fmt.println("Output path is empty!") + } }