package assets_gen import os "core:os/os2" import "core:fmt" import "core:strings" import "core:path/filepath" import "core:image" import "core:image/png" import "core:image/qoi" import ase "aseprite" import aseutil "aseprite/utils" load_world :: proc(path: string, file: ^os.File, _: ^os.File) { load_json_world(path) } load_room :: proc(path: string, file: ^os.File, _: ^os.File) { load_json_room(path, file) paths_to_res_type[path] = "Room_Id" } load_tileset :: proc(path: string, _: ^os.File, _: ^os.File) { load_json_tileset(path) } load_qoi :: proc(path: string, qoi: ^os.File, output: ^os.File) { rel_path, rel_err := filepath.rel( output_dir, path, context.temp_allocator, ) if rel_err != nil { die( "Could not find relative path from %v to %v (%v)", output_dir, path, rel_err, ) } line := fmt.aprintf("{{data = #load(%w), anim = .NONE}}", rel_path) images[filepath.stem(path)] = line paths_to_res_type[path] = "Image_Id" } load_png :: proc(path: string, png_file: ^os.File, output: ^os.File) { // Convert all PNG files to QOI and store those png_bytes, read_err := os.read_entire_file(png_file, context.allocator) if read_err != nil { die("Could not read file (%v)", read_err) } defer delete(png_bytes) img, png_err := png.load_from_bytes(png_bytes) if png_err != nil { die("Could not parse file (%v)", png_err) } defer image.destroy(img) info, fi_err := os.fstat(png_file, allocator = context.temp_allocator) if fi_err != nil { die("Could not load file info (%v)", fi_err) } compiled_path := strings.concatenate( {COMPILED_DIR, filepath.stem(info.name), ".qoi"}, allocator = context.temp_allocator, ) qoi_err := qoi.save_to_file(compiled_path, img) if qoi_err != nil { die("Could not convert PNG to QOI (%v)", qoi_err) } abs_path, found_abs := filepath.abs( compiled_path, context.temp_allocator ) if !found_abs { die("Could not find absolute path for %v", compiled_path) } rel_path, rel_err := filepath.rel( output_dir, abs_path, context.temp_allocator, ) if rel_err != nil { die( "Could not find relative path from %v to %v (%v)", output_dir, compiled_path, rel_err, ) } line := fmt.aprintf("{{data = #load(%w), anim = .NONE}}", rel_path) images[filepath.stem(path)] = line paths_to_res_type[path] = "Image_Id" } @(private="file") _load_sprite_sheet :: proc( path: string, doc: ^ase.Document ) { sprite_sheet, ss_err := aseutil.create_sprite_sheet(doc, { size = {int(doc.header.width), int(doc.header.height)}, count = int(doc.header.frames), }) if ss_err != nil { die( "Could not create sprite sheet from aseprite file %v (%v)", path, ss_err, ) } defer aseutil.destroy(sprite_sheet) pixels := make( []image.RGBA_Pixel, sprite_sheet.width * sprite_sheet.height, allocator = context.temp_allocator, ) i := 0 for i < sprite_sheet.width * sprite_sheet.height { channel := i * 4 pixels[i].r = sprite_sheet.data[channel + 0] pixels[i].g = sprite_sheet.data[channel + 1] pixels[i].b = sprite_sheet.data[channel + 2] pixels[i].a = sprite_sheet.data[channel + 3] i += 1 } img, img_ok := image.pixels_to_image( pixels, sprite_sheet.width, sprite_sheet.height, ) if !img_ok { die("Could not create sprite sheet image %v", path) } compiled_path := strings.concatenate( {COMPILED_DIR, filepath.stem(path), "-sheet.qoi"}, allocator = context.temp_allocator, ) qoi_err := qoi.save_to_file(compiled_path, &img) if qoi_err != nil { die("Could not save spritesheet %v (%v)", path, qoi_err) } abs_path, found_abs := filepath.abs( compiled_path, context.temp_allocator ) if !found_abs { die("Could not find absolute path for %v", compiled_path) } rel_path, rel_err := filepath.rel( output_dir, abs_path, context.temp_allocator, ) if rel_err != nil { die( "Could not find relative path from %v to %v (%v)", output_dir, compiled_path, rel_err, ) } anim_name := strings.to_screaming_snake_case( filepath.stem(path), allocator = context.temp_allocator, ) line := fmt.aprintf("{{data = #load(%w), anim = .%v}}", rel_path, anim_name) images[filepath.stem(path)] = line paths_to_res_type[path] = "Image_Id" } @(private="file") _load_animation :: proc(path: string, doc: ^ase.Document) { tags: map[string]struct{ from: i32, to: i32, } defer delete(tags) for frame in doc.frames { for chunk in frame.chunks { bin_tags, is_tags := chunk.(ase.Tags_Chunk) if !is_tags { continue } for bin_tag in bin_tags { tags[bin_tag.name] = { from = i32(bin_tag.from_frame), to = i32(bin_tag.to_frame), } } } } frame_durations := make([]i32, doc.header.frames) defer delete(frame_durations) i := 0 for frame in doc.frames { frame_durations[i] = i32(frame.header.duration) i += 1 } line := fmt.aprintf( "{{frame_count = %w, frame_durations = %w, tags = %w}}", len(frame_durations), frame_durations[:], tags, ) animations[filepath.stem(path)] = line } load_ase :: proc(path: string, ase_file: ^os.File, output: ^os.File) { doc: ase.Document defer ase.destroy_doc(&doc) unmarshal_err := ase.unmarshal(&doc, path, alloc = context.temp_allocator) if unmarshal_err != nil { die("Could not unmarshal aseprite file %v (%v)", path, unmarshal_err) } // Load animation _load_animation(path, &doc) // Load sprite sheet _load_sprite_sheet(path, &doc) }