From 7510894c780d53193f762b4f0190d42eb8760ad5 Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Sun, 21 Apr 2024 13:59:27 +0300 Subject: [PATCH] working packing generator example --- .vscode/tasks.json | 8 +- .../aseprite_odin_generator.odin | 44 ++++++ src/aseprite_odin_generator/big.aseprite | Bin 683 -> 1115 bytes src/aseprite_odin_generator/generator.odin | 82 ---------- src/generator.odin | 147 ++++++++++++++++++ 5 files changed, 198 insertions(+), 83 deletions(-) create mode 100644 src/aseprite_odin_generator/aseprite_odin_generator.odin delete mode 100644 src/aseprite_odin_generator/generator.odin create mode 100644 src/generator.odin diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b1eca51..edc8dcf 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -59,7 +59,13 @@ { "label": "Build&Run Tile Generator Test", "type": "shell", - "command": "odin run src/aseprite_odin_generator -out:build/aseprite_odin_generator.exe", + "command": "odin run", + "args": [ + "src/aseprite_odin_generator", + "-define:RAYLIB_SHARED=true", + "-out:build_generator/aseprite_odin_generator.exe", + "-debug" + ], "options": { "cwd": "${workspaceFolder}" }, diff --git a/src/aseprite_odin_generator/aseprite_odin_generator.odin b/src/aseprite_odin_generator/aseprite_odin_generator.odin new file mode 100644 index 0000000..b0012e9 --- /dev/null +++ b/src/aseprite_odin_generator/aseprite_odin_generator.odin @@ -0,0 +1,44 @@ +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/big.aseprite b/src/aseprite_odin_generator/big.aseprite index bfc55aa64ad69013a578098a6aac7774308fdcf1..e656ff87413c8ec49fd351b5c8b70bb0f2f782f5 100644 GIT binary patch delta 693 zcmZ3@dYgkOnq?wW9q(;s28LfBIT%tH7#KJ<_9`+?E?|_aH@*SnvMMkza5E?{{AU7k zz`%;ZlA&VG+e_YD4F&=%7iBK~j(3~cs_;lX>xxgfc#5#{O!aA(${zCMi z+&y*c>W}B_so%Dp!%uqt{P(I&pU<7SImc{Uy?%7rE1-d8S*gqYSdv`|gnA~UZeZ{Cv#tg9JKwK=zd0vc?9%>EZt2%J&uqVDF%jqyRs}`|QHF9P zkC`*bgFJTAkn4bgfJ#Vn0 zJ$rLh&Ao}WDYKP+Cw=b8TD(i-s#~e%qTZ@_wOQ{v%pTeGoLoG^X3@-!_n{{JU77#q zoELieHZD|V_s{?H62HkmKU+2HG1Ga5&~jBqkc*fY3K+s+E~*DQ(i;eYPP*fm*X#gv z(*6HJ`~3N)tX;fDDbit;702uk`;N?dd3;0Ck+)?&^X0!E|6F=ut^eg|rzhPHZoQt$F`#owV9Bzh->>`0CNsqpQ2aR(G)2avi_h^*`nQR@YoB7E7@;t6lT!&Bn-k=PU1|-7Nk*=XGV(gwz5v3nNCawZmfn?&>H%D@9!y>)))=W#Dx;6FC>*Yrty-HX8XY_sQn{V^p z+uxEkJ$>`P=Z{xk=N)_>d;RNO?Kjir-Lw{wUc1%wc*?rX)o-rG&8z;(TCq5v7XX$s BNSXiu delta 217 zcmcc3v6_`>HPb|ApR%(W{~Ce-|WdN_QqWQEqs5uv{H%ll(dP4ngI(* (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) + } +}