diff --git a/.vscode/tasks.json b/.vscode/tasks.json index edc8dcf..b1eca51 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -59,13 +59,7 @@ { "label": "Build&Run Tile Generator Test", "type": "shell", - "command": "odin run", - "args": [ - "src/aseprite_odin_generator", - "-define:RAYLIB_SHARED=true", - "-out:build_generator/aseprite_odin_generator.exe", - "-debug" - ], + "command": "odin run src/aseprite_odin_generator -out:build/aseprite_odin_generator.exe", "options": { "cwd": "${workspaceFolder}" }, diff --git a/src/aseprite_odin_generator/aseprite_odin_generator.odin b/src/aseprite_odin_generator/aseprite_odin_generator.odin deleted file mode 100644 index b0012e9..0000000 --- a/src/aseprite_odin_generator/aseprite_odin_generator.odin +++ /dev/null @@ -1,44 +0,0 @@ -package generator - -import ase "../aseprite" -import "core:fmt" -import "core:mem" -import "core:os" -import fp "core:path/filepath" -import "core:slice" -import "core:strings" -import "core:testing" -import rl "vendor:raylib" - -import gen ".." - -ATLAS_SIZE :: 512 -IMPORT_PATH :: "./src/aseprite_odin_generator/big.aseprite" -EXPORT_PATH :: "./src/aseprite_odin_generator/atlas.png" - -main :: proc() { - fmt.println("Hello!") - ase_file, ase_ok := os.read_entire_file(IMPORT_PATH) - if !ase_ok { - fmt.panicf("Couldn't load file!") - } - - doc: ase.Document - read, um_err := ase.unmarshal_from_slice(ase_file, &doc) - if um_err != nil { - fmt.panicf("Couldn't unmarshall file!") - } else { - fmt.printfln("Read {0} bytes from file", read) - } - - fmt.println("Header:\n\t", doc.header) - // fmt.println("Frames:\n\t", doc.frames) - - atlas: rl.Image = rl.GenImageColor(ATLAS_SIZE, ATLAS_SIZE, rl.BLANK) - - atlas_entry := gen.atlas_entry_from_compressed_cells(doc) - // Packs the cells & blits them to the atlas - gen.pack_atlas_entries({atlas_entry}, &atlas) - - rl.ExportImage(atlas, EXPORT_PATH) -} diff --git a/src/aseprite_odin_generator/atlas.png b/src/aseprite_odin_generator/atlas.png index 36bbf35..02082b4 100644 Binary files a/src/aseprite_odin_generator/atlas.png and b/src/aseprite_odin_generator/atlas.png differ diff --git a/src/aseprite_odin_generator/big.aseprite b/src/aseprite_odin_generator/big.aseprite index e656ff8..bfc55aa 100644 Binary files a/src/aseprite_odin_generator/big.aseprite and b/src/aseprite_odin_generator/big.aseprite differ diff --git a/src/aseprite_odin_generator/generator.odin b/src/aseprite_odin_generator/generator.odin new file mode 100644 index 0000000..fd18903 --- /dev/null +++ b/src/aseprite_odin_generator/generator.odin @@ -0,0 +1,82 @@ +package generator + +import ase "../aseprite" +import "core:fmt" +import "core:mem" +import "core:os" +import fp "core:path/filepath" +import "core:slice" +import "core:strings" +import "core:testing" +import rl "vendor:raylib" + +ATLAS_SIZE :: 512 +EXPORT_PATH :: "E:/dev/odin-atlas-packer/src/aseprite_odin_generator/atlas.png" + +main :: proc() { + fmt.println("Hello!") + ase_file, ase_ok := os.read_entire_file( + "E:/dev/odin-atlas-packer/src/aseprite_odin_generator/big.aseprite", + ) + if !ase_ok { + fmt.panicf("Couldn't load file!") + } + + doc: ase.Document + read, um_err := ase.unmarshal_from_slice(ase_file, &doc) + if um_err != nil { + fmt.panicf("Couldn't unmarshall file!") + } else { + fmt.printfln("Read {0} bytes from file", read) + } + + fmt.println("Header:\n\t", doc.header) + // fmt.println("Frames:\n\t", doc.frames) + + images: [dynamic]rl.Image + atlas: rl.Image = rl.GenImageColor(ATLAS_SIZE, ATLAS_SIZE, rl.BLANK) + + for frame in doc.frames { + for chunk in frame.chunks { + cel_chunk, cok := chunk.(ase.Cel_Chunk) + if !cok { + continue + } + + cel_img, ci_ok := cel_chunk.cel.(ase.Com_Image_Cel) + if !ci_ok { + continue + } + append( + &images, + rl.Image { + data = rawptr(&cel_img.pixel[0]), + width = auto_cast cel_img.width, + height = auto_cast cel_img.height, + format = .UNCOMPRESSED_R8G8B8A8, + }, + ) + } + } + curr_x, curr_y: i32 + for img, img_i in images { + fmt.printfln("Image_{0}: {1}", img_i, img) + rl.ImageDraw( + &atlas, + img, + {0, 0, auto_cast img.width, auto_cast img.height}, + {auto_cast curr_x, auto_cast curr_y, auto_cast img.width, auto_cast img.height}, + rl.WHITE, + ) + curr_x += img.width + curr_y += img.height + } + + // todo: pack the rectangles + + // todo: blit them to the atlas + + // todo: generate metadata (json, odin enums) + + rl.ExportImage(atlas, EXPORT_PATH) +} diff --git a/src/generator.odin b/src/generator.odin deleted file mode 100644 index 3886bc3..0000000 --- a/src/generator.odin +++ /dev/null @@ -1,147 +0,0 @@ -package game - -import ase "./aseprite" -import "core:fmt" -import "core:os" -import fp "core:path/filepath" -import rl "vendor:raylib" -import stbrp "vendor:stb/rect_pack" - -AtlasEntry :: struct { - path: string, - cells: [dynamic]rl.Image, -} - -unmarshall_aseprite_dir :: proc(path: string, atlas_entries: ^[dynamic]AtlasEntry) { - fp.dir(path) - if fd, ok := os.open(path); ok == 0 { - if fi_files, fi_ok := os.read_dir(fd, -1); fi_ok == 0 { - unmarshall_aseprite_files_file_info(fi_files, atlas_entries) - } - } -} - -unmarshall_aseprite_files_file_info :: proc( - files: []os.File_Info, - atlas_entries: ^[dynamic]AtlasEntry, -) { - paths: [dynamic]string - for f in files { - append(&paths, f.fullpath) - } - unmarshall_aseprite_files(paths[:], atlas_entries) -} - -unmarshall_aseprite_files :: proc(file_paths: []string, atlas_entries: ^[dynamic]AtlasEntry) { - current_document: ase.Document - for fp in file_paths { - atlas_entry := atlas_entry_from_compressed_cells(current_document) - ase.unmarshal_from_filename(fp, ¤t_document) - append(atlas_entries, atlas_entry) - } -} - -/* - Goes through all the chunks in an aseprite document & copies the `Com_Image_Cel` cells in a separate image -*/ -atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entry: AtlasEntry) { - for frame in document.frames { - for chunk in frame.chunks { - cel_chunk, cok := chunk.(ase.Cel_Chunk) - if !cok { - continue - } - - cel_img, ci_ok := cel_chunk.cel.(ase.Com_Image_Cel) - if !ci_ok { - continue - } - append( - &atlas_entry.cells, - rl.Image { - data = rawptr(&cel_img.pixel[0]), - width = auto_cast cel_img.width, - height = auto_cast cel_img.height, - format = .UNCOMPRESSED_R8G8B8A8, - }, - ) - } - } - return -} - -/* - Takes in a slice of entries, an output texture, the width & height -*/ -pack_atlas_entries :: proc( - entries: []AtlasEntry, - atlas: ^rl.Image, - offset_x: int = 0, - offset_y: int = 0, -) { - all_entries: [dynamic]rl.Image // it's fine to store it like this, rl.Image just stores a pointer to the data - // todo: set up the stb_rect_pack rectangles - { - for entry in entries { - append(&all_entries, ..entry.cells[:]) - } - } - - num_entries := len(all_entries) - nodes := make([]stbrp.Node, num_entries) - rects := make([]stbrp.Rect, num_entries) - - EntryAndCell :: struct { - entry: ^AtlasEntry, - cell_of_entry: ^rl.Image, - } - rect_idx_to_entry_and_cell: map[int]EntryAndCell - - // Set the custom IDs - cellIdx: int - for &entry, entryIdx in entries { - for &cell in entry.cells { - // I can probably infer this information with just the id of the rect but I'm being lazy right now - map_insert(&rect_idx_to_entry_and_cell, cellIdx, EntryAndCell{&entry, &cell}) - rects[cellIdx].id = auto_cast entryIdx - cellIdx += 1 - } - } - - for entry, entryIdx in all_entries { - entry_stb_rect := &rects[entryIdx] - entry_stb_rect.w = auto_cast entry.width - entry_stb_rect.h = auto_cast entry.height - } - - ctx: stbrp.Context - stbrp.init_target(&ctx, atlas.width, atlas.height, &nodes[0], auto_cast num_entries) - res := stbrp.pack_rects(&ctx, &rects[0], auto_cast num_entries) - if bool(res) { - fmt.println("Packed everything successfully!") - fmt.printfln("Rects: {0}", rects[:]) - } else { - fmt.println("Failed to pack everything!") - } - - for rect, rectIdx in rects { - entry_and_cell := rect_idx_to_entry_and_cell[auto_cast rectIdx] - cell := entry_and_cell.cell_of_entry - // We're grabbing the whole cell (the image itself) - src_rect := rl.Rectangle { - x = 0, - y = 0, - width = auto_cast cell.width, - height = auto_cast cell.height, - } - // Placing it in the atlas in the calculated offsets (in the packing step) - dst_rect := rl.Rectangle { - auto_cast rect.x, - auto_cast rect.y, - auto_cast cell.width, - auto_cast cell.height, - } - - rl.ImageDraw(atlas, cell^, src_rect, dst_rect, rl.WHITE) - } -}