diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e4908a1..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "src/aseprite"]
- path = src/aseprite
- url = https://github.com/bersK/odin-aseprite.git
-[submodule "src/dialog/libtinyfiledialogs"]
- path = src/dialog/libtinyfiledialogs
- url = https://github.com/native-toolkit/libtinyfiledialogs.git
diff --git a/README.md b/README.md
index 3a0d31f..5f53772 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,16 @@ Yet-Another-Atlas-Packer by Stefan Stefanov
## Description
-Simple atlas packer using `stb_rect_pack` from the `stb` family of header libraries & `raylib` for rendering/ui. Here's a quick preview on [youtube](https://youtu.be/4_dKq7G57Lw) of the application.
+Simple atlas packer for .aseprite files. Generates metadata and potentially embeds in an output source file of your choosing.
+
+Uses `stb_rect_pack` from the `stb` family of header libraries & `raylib` for rendering/UI. Here's a quick preview on [youtube](https://youtu.be/4_dKq7G57Lw) of the application.
-The goal of the tool is to take in multiple aseprite files and pack them into a single atlas, outputting some metadata in the process in the form of
-json and/or source files for direct use in odin (maybe more languages too).
+The goal of the tool is to take in multiple aseprite files and pack them into a single atlas, outputting metadata in the process in the form of
+JSON and/or source files for direct use in odin (or other languages through a customization file).
I'm using a library for marshalling the aseprite files found [here](https://github.com/blob1807/odin-aseprite) on github.
diff --git a/src/aseprite_odin_generator/aseprite_odin_generator.odin b/examples/aseprite_odin_generator.odin
similarity index 51%
rename from src/aseprite_odin_generator/aseprite_odin_generator.odin
rename to examples/aseprite_odin_generator.odin
index 563452d..d400cc7 100644
--- a/src/aseprite_odin_generator/aseprite_odin_generator.odin
+++ b/examples/aseprite_odin_generator.odin
@@ -1,42 +1,29 @@
-package generator
+package cli
-import ase "../aseprite"
+import ase "../vendors/aseprite"
import "core:encoding/json"
import "core:fmt"
-import "core:mem"
import "core:os"
-import fp "core:path/filepath"
import "core:slice"
import s "core:strings"
-import "core:testing"
import rl "vendor:raylib"
import stbrp "vendor:stb/rect_pack"
-import gen ".."
-import utils "../utils"
+import gen "../src/generator"
ATLAS_SIZE :: 512
-IMPORT_PATH :: "./src/aseprite_odin_generator/big.aseprite"
-EXPORT_PATH :: "./src/aseprite_odin_generator/atlas.png"
+IMPORT_PATH :: "./example.aseprite"
+EXPORT_PATH :: "./atlas.png"
main :: proc() {
- args := utils.parse_arguments(os.args[1:])
- fmt.println(args)
-
- if ok := utils.CLIFlagType.Help in args; ok {
- fmt.println("Help called!")
- utils.print_help()
- return
- }
ase_file, ase_ok := os.read_entire_file(IMPORT_PATH)
if !ase_ok {
fmt.panicf("Couldn't load file!")
}
- cwd := os.get_current_directory()
- target_dir := s.concatenate({cwd, "\\src\\aseprite_odin_generator\\"})
+ target_dir := os.get_current_directory()
atlas: rl.Image = rl.GenImageColor(ATLAS_SIZE, ATLAS_SIZE, rl.BLANK)
atlas_entries: [dynamic]gen.AtlasEntry = make([dynamic]gen.AtlasEntry)
@@ -45,13 +32,10 @@ main :: proc() {
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)
+ os.write_entire_file("./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("./output.odin", transmute([]byte)odin_output_str)
rl.ExportImage(atlas, EXPORT_PATH)
}
diff --git a/src/aseprite_odin_generator/big.aseprite b/examples/sample.aseprite
similarity index 100%
rename from src/aseprite_odin_generator/big.aseprite
rename to examples/sample.aseprite
diff --git a/repo_assets/image.png b/resources/repo_assets/image.png
similarity index 100%
rename from repo_assets/image.png
rename to resources/repo_assets/image.png
diff --git a/styles/style_candy.rgs b/resources/styles/style_candy.rgs
similarity index 100%
rename from styles/style_candy.rgs
rename to resources/styles/style_candy.rgs
diff --git a/scripts/build_atlas.bat b/scripts/build_atlas.bat
new file mode 100644
index 0000000..d650386
--- /dev/null
+++ b/scripts/build_atlas.bat
@@ -0,0 +1,2 @@
+@echo off
+odin build src/frontend -define:RAYLIB_SHARED=true -out:build/yaap-debug.exe -debug
\ No newline at end of file
diff --git a/scripts/build_atlas_release.bat b/scripts/build_atlas_release.bat
new file mode 100644
index 0000000..9fb82e6
--- /dev/null
+++ b/scripts/build_atlas_release.bat
@@ -0,0 +1,2 @@
+@echo off
+odin build src/frontend -define:RAYLIB_SHARED=true -out:build/yaap.exe -o:speed
\ No newline at end of file
diff --git a/scripts/build_cli.bat b/scripts/build_cli.bat
new file mode 100644
index 0000000..a13a781
--- /dev/null
+++ b/scripts/build_cli.bat
@@ -0,0 +1,2 @@
+@echo off
+odin build examples/aseprite_odin_generator.odin -file -define:RAYLIB_SHARED=true -out:build/yaap-cli-debug.exe -debug
\ No newline at end of file
diff --git a/scripts/build_cli_release.bat b/scripts/build_cli_release.bat
new file mode 100644
index 0000000..5bd9255
--- /dev/null
+++ b/scripts/build_cli_release.bat
@@ -0,0 +1,2 @@
+@echo off
+odin build examples/aseprite_odin_generator.odin -file -define:RAYLIB_SHARED=true -out:build/yaap-cli.exe -o:speed
\ No newline at end of file
diff --git a/scripts/build_debug.bat b/scripts/build_debug.bat
deleted file mode 100644
index d5ab9ff..0000000
--- a/scripts/build_debug.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-odin build src/main_release -define:RAYLIB_SHARED=true -out:build/game_debug.exe -debug
diff --git a/scripts/build_debug.sh b/scripts/build_debug.sh
deleted file mode 100755
index 7df52c7..0000000
--- a/scripts/build_debug.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-odin build src/main_release -out:build/game_debug.bin -no-bounds-check -debug
diff --git a/scripts/build_generator_debug.bat b/scripts/build_generator_debug.bat
deleted file mode 100644
index 2340f93..0000000
--- a/scripts/build_generator_debug.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-odin build src/aseprite_odin_generator -define:RAYLIB_SHARED=true -out:build_generator/aseprite_odin_generator.exe -debug
diff --git a/scripts/build_hot_reload.bat b/scripts/build_hot_reload.bat
deleted file mode 100644
index 3890995..0000000
--- a/scripts/build_hot_reload.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@echo off
-
-rem Build game.dll
-odin build src -show-timings -use-separate-modules -define:RAYLIB_SHARED=true -build-mode:dll -out:build/game.dll -strict-style -vet-unused -vet-using-stmt -vet-using-param -vet-style -vet-semicolon -debug
-IF %ERRORLEVEL% NEQ 0 exit /b 1
-
-rem If game.exe already running: Then only compile game.dll and exit cleanly
-QPROCESS "game.exe">NUL
-IF %ERRORLEVEL% EQU 0 exit /b 1
-
-rem build game.exe
-odin build src/main_hot_reload -use-separate-modules -out:build/game.exe -strict-style -vet-using-stmt -vet-using-param -vet-style -vet-semicolon -debug
-IF %ERRORLEVEL% NEQ 0 exit /b 1
-
-rem copy raylib.dll from odin folder to here
-if not exist "raylib.dll" (
- echo "Please copy raylib.dll from /vendor/raylib/windows/raylib.dll to the same directory as game.exe"
- exit /b 1
-)
-
-exit /b 0
diff --git a/scripts/build_hot_reload.sh b/scripts/build_hot_reload.sh
deleted file mode 100755
index 996a14d..0000000
--- a/scripts/build_hot_reload.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-
-VET="-strict-style -vet-unused -vet-using-stmt -vet-using-param -vet-style -vet-semicolon"
-
-# NOTE: this is a recent addition to the Odin compiler, if you don't have this command
-# you can change this to the path to the Odin folder that contains vendor, eg: "~/Odin".
-ROOT=$(odin root)
-if [ ! $? -eq 0 ]; then
- echo "Your Odin compiler does not have the 'odin root' command, please update or hardcode it in the script."
- exit 1
-fi
-
-set -eu
-
-# Figure out the mess that is dynamic libraries.
-case $(uname) in
-"Darwin")
- case $(uname -m) in
- "arm64") LIB_PATH="macos-arm64" ;;
- *) LIB_PATH="macos" ;;
- esac
-
- DLL_EXT=".dylib"
- EXTRA_LINKER_FLAGS="-Wl,-rpath $ROOT/vendor/raylib/$LIB_PATH"
- ;;
-*)
- DLL_EXT=".so"
- EXTRA_LINKER_FLAGS="'-Wl,-rpath=\$ORIGIN/linux'"
-
- # Copy the linux libraries into the project automatically.
- if [ ! -d "linux" ]; then
- mkdir linux
- cp -r $ROOT/vendor/raylib/linux/libraylib*.so* linux
- fi
- ;;
-esac
-
-# Build the game.
-odin build src -use-separate-modules -extra-linker-flags:"$EXTRA_LINKER_FLAGS" -show-timings -define:RAYLIB_SHARED=true -build-mode:dll -out:build/game_tmp$DLL_EXT -debug $VET
-
-# Need to use a temp file on Linux because it first writes an empty `game.so`, which the game will load before it is actually fully written.
-mv ./build/game_tmp$DLL_EXT ./build/game$DLL_EXT
-
-# Do not build the game.bin if it is already running.
-if ! pgrep game.bin > /dev/null; then
- odin build src/main_hot_reload -use-separate-modules -out:build/game.bin $VET -debug
-fi
diff --git a/scripts/build_release.bat b/scripts/build_release.bat
deleted file mode 100644
index 0d1ac5b..0000000
--- a/scripts/build_release.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-odin build src/main_release -define:RAYLIB_SHARED=true -out:build/game_release.exe -no-bounds-check -o:speed -strict-style -vet-unused -vet-using-stmt -vet-using-param -vet-style -vet-semicolon -subsystem:windows
diff --git a/scripts/build_release.sh b/scripts/build_release.sh
deleted file mode 100644
index 257abc0..0000000
--- a/scripts/build_release.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-odin build src/main_release -out:build/game_release.bin -no-bounds-check -o:speed -strict-style -vet-unused -vet-using-stmt -vet-using-param -vet-style -vet-semicolon
diff --git a/scripts/setup.bat b/scripts/setup.bat
new file mode 100644
index 0000000..abf078d
--- /dev/null
+++ b/scripts/setup.bat
@@ -0,0 +1,14 @@
+@echo off
+
+mkdir build
+
+FOR /F "tokens=*" %%g IN ('odin root') do (SET ODIN_ROOT=%%g)
+
+echo %ODIN_ROOT%
+
+@REM If it fails to find your odin root folder, copy the raylib.dll manually into the build folder, it's a runtime requirement
+copy %ODIN_ROOT%\vendor\raylib\windows\raylib.dll build\raylib.dll
+
+pushd vendors\dialog
+call .\build.bat
+popd
\ No newline at end of file
diff --git a/src/aseprite_odin_generator/atlas.png b/src/aseprite_odin_generator/atlas.png
deleted file mode 100644
index 94facee..0000000
Binary files a/src/aseprite_odin_generator/atlas.png and /dev/null differ
diff --git a/src/aseprite_odin_generator/metadata.json b/src/aseprite_odin_generator/metadata.json
deleted file mode 100644
index be61ff1..0000000
--- a/src/aseprite_odin_generator/metadata.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"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
diff --git a/src/globals.odin b/src/frontend/globals.odin
similarity index 87%
rename from src/globals.odin
rename to src/frontend/globals.odin
index 7808893..c9aae32 100644
--- a/src/globals.odin
+++ b/src/frontend/globals.odin
@@ -1,13 +1,12 @@
+package frontend
-package game
-
+import generator "../generator"
import rl "vendor:raylib"
-PixelWindowHeight :: 180
+PIXEL_WINDOW_HEIGHT :: 180
FILE_DIALOG_SIZE :: 1000
scaling: f32 = 2
-w, h: f32
WindowInformation :: struct {
w: f32,
@@ -38,7 +37,7 @@ PackerSettings :: struct {
output_odin: bool,
}
-GameMemory :: struct {
+ApplicationState :: struct {
file_dialog_text_buffer: [FILE_DIALOG_SIZE + 1]u8,
is_packing_whole_source_folder: bool,
should_open_file_dialog: bool,
@@ -59,5 +58,7 @@ GameMemory :: struct {
should_render_atlas: bool,
atlas_render_has_preview: bool,
atlas_render_size: i32,
- atlas_metadata: Maybe([dynamic]SpriteAtlasMetadata),
+ atlas_metadata: Maybe([dynamic]generator.SpriteAtlasMetadata),
}
+
+g_mem: ApplicationState
diff --git a/src/game.odin b/src/frontend/main.odin
similarity index 51%
rename from src/game.odin
rename to src/frontend/main.odin
index b289b82..5962de0 100644
--- a/src/game.odin
+++ b/src/frontend/main.odin
@@ -1,35 +1,65 @@
-// This file is compiled as part of the `odin.dll` file. It contains the
-// procs that `game.exe` will call, such as:
-//
-// game_init: Sets up the game state
-// game_update: Run once per frame
-// game_shutdown: Shuts down game and frees memory
-// game_memory: Run just before a hot reload, so game.exe has a pointer to the
-// game's memory.
-// game_hot_reloaded: Run after a hot reload so that the `g_mem` global variable
-// can be set to whatever pointer it was in the old DLL.
-
-package game
+package frontend
import "core:fmt"
+import "core:log"
import "core:math"
+import "core:mem"
+import "core:mem/virtual"
+import "core:os"
import "core:strings"
-import "utils"
import rl "vendor:raylib"
-import diag "dialog"
+import diag "../../vendors/dialog"
+import generator "../generator"
-g_mem: ^GameMemory
+main :: proc() {
+ default_allocator := context.allocator
+ tracking_allocator: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&tracking_allocator, default_allocator)
+ context.allocator = mem.tracking_allocator(&tracking_allocator)
-game_camera :: proc() -> rl.Camera2D {
- w = f32(rl.GetScreenWidth())
- h = f32(rl.GetScreenHeight())
+ mode: int = 0
+ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
+ mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IROTH
+ }
- return {zoom = h / PixelWindowHeight, target = {}, offset = {w / 2, h / 2}}
-}
+ logh, logh_err := os.open("log.txt", (os.O_CREATE | os.O_TRUNC | os.O_RDWR), mode)
-ui_camera :: proc() -> rl.Camera2D {
- return {zoom = scaling}
+ if logh_err == os.ERROR_NONE {
+ os.stdout = logh
+ os.stderr = logh
+ }
+
+ logger :=
+ logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
+ context.logger = logger
+ defer if logh_err == os.ERROR_NONE {
+ log.destroy_file_logger(logger)
+ }
+
+ rl.SetConfigFlags({.WINDOW_RESIZABLE})
+ rl.InitWindow(1400, 800, "YAAP - Yet Another Atlas Packer")
+ defer rl.CloseWindow()
+ rl.SetWindowMinSize(1400, 800)
+
+ for !rl.WindowShouldClose() {
+ update()
+ draw()
+
+ for b in tracking_allocator.bad_free_array {
+ log.error("Bad free at: %v", b.location)
+ }
+
+ clear(&tracking_allocator.bad_free_array)
+
+ free_all(context.temp_allocator)
+ }
+
+ for key, value in tracking_allocator.allocation_map {
+ log.error("%v: Leaked %v bytes\n", value.location, value.size)
+ }
+
+ mem.tracking_allocator_destroy(&tracking_allocator)
}
update :: proc() {
@@ -39,11 +69,10 @@ update :: proc() {
win_info.h = f32(rl.GetScreenHeight())
win_info.height_scaled = win_info.h / scaling
win_info.width_scaled = win_info.w / scaling
- w = win_info.w
- h = win_info.h
// Update the virtual mouse position (needed for GUI interaction to work properly for instance)
- rl.SetMouseScale(1 / scaling, 1 / scaling)
+ mouse_scale := 1 / scaling
+ rl.SetMouseScale(mouse_scale, mouse_scale)
if g_mem.should_open_file_dialog {
open_file_dialog()
@@ -65,6 +94,10 @@ draw :: proc() {
free_all(context.temp_allocator)
}
+ui_camera :: proc() -> rl.Camera2D {
+ return {zoom = scaling}
+}
+
draw_screen_ui :: proc() {
rl.BeginMode2D(ui_camera())
defer rl.EndMode2D()
@@ -72,19 +105,47 @@ draw_screen_ui :: proc() {
draw_atlas_settings_and_preview()
}
+pick_sources :: proc() {
+ g_mem.should_open_file_dialog = true
+ g_mem.source_location_type = .SourceFiles
+}
+
+pick_output :: proc() {
+ g_mem.should_open_file_dialog = true
+ g_mem.source_location_type = .OutputFolder
+}
+
+pack_atlas :: proc() {
+ g_mem.should_render_atlas = true
+}
+
+save :: proc() {
+ generator.save_output(
+ g_mem.output_folder_path,
+ g_mem.atlas_metadata,
+ g_mem.atlas_render_texture_target,
+ )
+}
+
+save_to :: proc() {
+ // if output_folder, ok := g_mem.output_folder_path.(string); ok {
+ // generator.save_metadata_simple(output_folder, g_mem.atlas_metadata, nil, nil, nil)
+ // }
+}
+
draw_screen_target :: proc() {
atlas_render_target := &g_mem.atlas_render_texture_target
rl.BeginTextureMode(atlas_render_target^)
defer rl.EndTextureMode()
- atlas_entries: [dynamic]AtlasEntry
+ atlas_entries: [dynamic]generator.AtlasEntry
delete(atlas_entries)
if files, ok := g_mem.source_files_to_pack.([]string); ok {
- unmarshall_aseprite_files(files, &atlas_entries)
+ generator.unmarshall_aseprite_files(files, &atlas_entries)
} else {
- fmt.println("No source folder or files set! Can't pack the void!!!")
+ log.error("No source folder or files set! Can't pack the void!!!")
g_mem.should_render_atlas = false
return
}
@@ -97,12 +158,17 @@ draw_screen_target :: proc() {
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)
+ g_mem.atlas_metadata = generator.pack_atlas_entries(
+ atlas_entries[:],
+ &atlas,
+ padding_x,
+ padding_y,
+ )
// OpenGL's Y buffer is flipped
rl.ImageFlipVertical(&atlas)
// rl.UnloadTexture(atlas_render_target.texture)
- fmt.println("Packed everything!")
+ log.info("Packed everything!")
atlas_render_target.texture = rl.LoadTextureFromImage(atlas)
g_mem.should_render_atlas = false
@@ -113,14 +179,14 @@ draw_atlas_settings_and_preview :: proc() {
left_half_rect := rl.Rectangle {
x = 0,
y = 0,
- width = auto_cast g_mem.window_info.width_scaled / 3,
- height = auto_cast g_mem.window_info.height_scaled,
+ width = cast(f32)g_mem.window_info.width_scaled / 3,
+ height = cast(f32)g_mem.window_info.height_scaled,
}
right_half_rect := rl.Rectangle {
- x = auto_cast g_mem.window_info.width_scaled / 3,
+ x = cast(f32)g_mem.window_info.width_scaled / 3,
y = 0,
- width = auto_cast (g_mem.window_info.width_scaled / 3) * 2,
- height = auto_cast g_mem.window_info.height_scaled,
+ width = cast(f32)(g_mem.window_info.width_scaled / 3) * 2,
+ height = cast(f32)g_mem.window_info.height_scaled,
}
rl.DrawRectangleRec(left_half_rect, rl.WHITE)
rl.DrawRectangleRec(right_half_rect, rl.MAROON)
@@ -149,9 +215,9 @@ draw_atlas_settings_and_preview :: proc() {
elements_height += small_offset / 2
@(static)
- DropdownBox000EditMode: bool
+ dropdown_resolution_edit_mode: bool
@(static)
- DropdownBox000Active: i32
+ dropdown_resolution_mode: i32
dropdown_rect := rl.Rectangle {
x = small_offset,
@@ -159,20 +225,18 @@ draw_atlas_settings_and_preview :: proc() {
width = left_half_rect.width - small_offset * 2,
height = small_offset,
}
-
// Because we want to render this ontop of everything else, we can just 'defer' it at the end of the draw function
defer {
- if DropdownBox000EditMode {rl.GuiLock()}
+ if dropdown_resolution_edit_mode {rl.GuiLock()}
if rl.GuiDropdownBox(
dropdown_rect,
"256x;512x;1024x;2048x;4096x",
- &DropdownBox000Active,
- DropdownBox000EditMode,
+ &dropdown_resolution_mode,
+ dropdown_resolution_edit_mode,
) {
- DropdownBox000EditMode = !DropdownBox000EditMode
- fmt.println(DropdownBox000Active)
- g_mem.atlas_render_size = 256 * auto_cast math.pow(2, f32(DropdownBox000Active))
+ dropdown_resolution_edit_mode = !dropdown_resolution_edit_mode
+ g_mem.atlas_render_size = 256 * auto_cast math.pow(2, f32(dropdown_resolution_mode))
}
rl.GuiUnlock()
}
@@ -183,77 +247,90 @@ draw_atlas_settings_and_preview :: proc() {
if SettingsDropdownBoxActive == 0 {
padding_settings_y := elements_height
{
- defer rl.GuiGroupBox(
- {
- x = small_offset / 2,
- y = padding_settings_y,
- width = left_half_rect.width - small_offset,
+ defer {
+ padding_settings_rect := rl.Rectangle {
+ x = small_offset / 2,
+ y = padding_settings_y,
+ width = left_half_rect.width - small_offset,
height = elements_height - padding_settings_y,
- },
- "Padding Settings",
- )
+ }
+ rl.GuiGroupBox(padding_settings_rect, "Padding Settings")
+ }
elements_height += small_offset
+ enable_padding_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = small_offset,
+ height = small_offset,
+ }
rl.GuiCheckBox(
- {
- x = small_offset,
- y = elements_height,
- width = small_offset,
- height = small_offset,
- },
+ enable_padding_rect,
" Enable padding",
&g_mem.packer_settings.padding_enabled,
)
elements_height += small_offset * 2
- if (rl.GuiSpinner(
- {
- x = small_offset,
- y = elements_height,
- width = big_offset * 2,
- height = small_offset,
- },
- "",
- &g_mem.packer_settings.pixel_padding_x_int,
- 0,
- 10,
- spinner_edit_mode,
- )) >
- 0 {spinner_edit_mode = !spinner_edit_mode}
- rl.GuiLabel(
- {
- x = (small_offset * 2) + big_offset * 2,
- y = elements_height,
- width = big_offset,
+ // Padding X spinner and label
+ {
+ padding_x_spinner_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = big_offset * 2,
height = small_offset,
- },
- "Padding X",
- )
+ }
+ padding_x_spinner := rl.GuiSpinner(
+ padding_x_spinner_rect,
+ "",
+ &g_mem.packer_settings.pixel_padding_x_int,
+ 0,
+ 10,
+ spinner_edit_mode,
+ )
+ if (padding_x_spinner) > 0 {
+ spinner_edit_mode = !spinner_edit_mode
+ }
+ rl.GuiLabel(
+ {
+ x = (small_offset * 2) + big_offset * 2,
+ y = elements_height,
+ width = big_offset,
+ height = small_offset,
+ },
+ "Padding X",
+ )
+ }
elements_height += small_offset * 2
- if (rl.GuiSpinner(
- {
- x = small_offset,
- y = elements_height,
- width = big_offset * 2,
- height = small_offset,
- },
- "",
- &g_mem.packer_settings.pixel_padding_y_int,
- 0,
- 10,
- spinner_edit_mode,
- )) >
- 0 {spinner_edit_mode = !spinner_edit_mode}
- rl.GuiLabel(
- {
- x = (small_offset * 2) + big_offset * 2,
- y = elements_height,
- width = big_offset,
+ // Padding Y spinner and label
+ {
+ padding_y_spinner_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = big_offset * 2,
height = small_offset,
- },
- "Padding Y",
- )
+ }
+ padding_y_spinner := rl.GuiSpinner(
+ padding_y_spinner_rect,
+ "",
+ &g_mem.packer_settings.pixel_padding_y_int,
+ 0,
+ 10,
+ spinner_edit_mode,
+ )
+ if (padding_y_spinner) > 0 {
+ spinner_edit_mode = !spinner_edit_mode
+ }
+ rl.GuiLabel(
+ {
+ x = (small_offset * 2) + big_offset * 2,
+ y = elements_height,
+ width = big_offset,
+ height = small_offset,
+ },
+ "Padding Y",
+ )
+ }
elements_height += small_offset * 2
}
@@ -264,94 +341,78 @@ draw_atlas_settings_and_preview :: proc() {
{
actions_label_y := elements_height
- defer rl.GuiGroupBox(
- {
- x = small_offset / 2,
- y = actions_label_y,
- width = left_half_rect.width - small_offset,
+
+ defer {
+ actions_rect := rl.Rectangle {
+ x = small_offset / 2,
+ y = actions_label_y,
+ width = left_half_rect.width - small_offset,
height = elements_height - actions_label_y,
- },
- "Actions",
- )
+ }
+ rl.GuiGroupBox(actions_rect, "Actions")
+ }
elements_height += small_offset
- if rl.GuiButton(
- {
- x = small_offset,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Pick Source(s)",
- ) {
- g_mem.should_open_file_dialog = true
- g_mem.source_location_type = .SourceFiles
+ pick_sources_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(pick_sources_rect, "Pick Source(s)") {
+ pick_sources()
}
- if rl.GuiButton(
- {
- x = left_half_rect.width / 2,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Pick Output",
- ) {
- g_mem.should_open_file_dialog = true
- g_mem.source_location_type = .OutputFolder
+ pick_output_rect := rl.Rectangle {
+ x = left_half_rect.width / 2,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(pick_output_rect, "Pick Output") {
+ pick_output()
}
elements_height += small_offset * 2
-
- if rl.GuiButton(
- {
- x = small_offset,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Pack Atlas",
- ) {
- g_mem.should_render_atlas = true
+ pack_atlas_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(pack_atlas_rect, "Pack Atlas") {
+ pack_atlas()
}
- if rl.GuiButton(
- {
- x = left_half_rect.width / 2,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Clear Atlas",
- ) {
+ clear_atlas_rect := rl.Rectangle {
+ x = left_half_rect.width / 2,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(clear_atlas_rect, "Clear Atlas") {
clear_atlas_data()
}
elements_height += small_offset * 2
- if rl.GuiButton(
- {
- x = small_offset,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Save",
- ) {
- save_output()
+ save_rect := rl.Rectangle {
+ x = small_offset,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(save_rect, "Save") {
+ save()
}
- if rl.GuiButton(
- {
- x = left_half_rect.width / 2,
- y = elements_height,
- width = left_half_rect.width / 2 - small_offset,
- height = small_offset,
- },
- "Save To...",
- ) {
- if output_folder, ok := g_mem.output_folder_path.(string); ok {
- save_metadata_simple(output_folder)
- }
+ save_to_rect := rl.Rectangle {
+ x = left_half_rect.width / 2,
+ y = elements_height,
+ width = left_half_rect.width / 2 - small_offset,
+ height = small_offset,
+ }
+ if rl.GuiButton(save_to_rect, "Save To...") {
+ save_to()
}
elements_height += small_offset * 2
}
@@ -437,9 +498,9 @@ open_file_dialog :: proc() {
source_files_to_pack := strings.clone_from_cstring(file_paths_conc, context.allocator)
g_mem.source_files_to_pack = strings.split(source_files_to_pack, "|")
- fmt.println(g_mem.source_files_to_pack)
+ log.info(g_mem.source_files_to_pack)
} else {
- fmt.println("No files were selected!")
+ log.error("No files were selected!")
}
case .SourceFolder:
@@ -451,9 +512,9 @@ open_file_dialog :: proc() {
)
if len(file) > 0 {
g_mem.source_location_to_pack = strings.clone_from_cstring(file)
- fmt.println(g_mem.source_location_to_pack)
+ log.info(g_mem.source_location_to_pack)
} else {
- fmt.println("Got an empty path from the file dialog!")
+ log.error("Got an empty path from the file dialog!")
}
case .OutputFolder:
@@ -465,9 +526,9 @@ open_file_dialog :: proc() {
)
if len(file) > 0 {
g_mem.output_folder_path = strings.clone_from_cstring(file)
- fmt.println(g_mem.output_folder_path)
+ log.info(g_mem.output_folder_path)
} else {
- fmt.println("Got an empty path from the file dialog!")
+ log.error("Got an empty path from the file dialog!")
}
case .SaveFileAs:
@@ -475,7 +536,7 @@ open_file_dialog :: proc() {
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, generator.OS_FILE_SEPARATOR, "atlas.png"},
)
default_path_to_save: cstring = strings.clone_to_cstring(default_path_filename)
file_path = cstring(
@@ -491,7 +552,11 @@ open_file_dialog :: proc() {
file_path = cstring(diag.save_file_dialog("Save as...", "", 1, &patterns[0], "Atlas"))
}
if file_path != nil {
- save_output()
+ generator.save_output(
+ g_mem.output_folder_path,
+ g_mem.atlas_metadata,
+ g_mem.atlas_render_texture_target,
+ )
}
}
@@ -500,9 +565,8 @@ open_file_dialog :: proc() {
}
clear_atlas_data :: proc() {
- if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok {
+ if metadata, ok := g_mem.atlas_metadata.([dynamic]generator.SpriteAtlasMetadata); ok {
delete(metadata)
- // g_mem.atlas_metadata = nil
}
g_mem.atlas_render_has_preview = false
}
diff --git a/src/utils/cli.odin b/src/generator/cli.odin
similarity index 66%
rename from src/utils/cli.odin
rename to src/generator/cli.odin
index 8b9411f..5a48ebe 100644
--- a/src/utils/cli.odin
+++ b/src/generator/cli.odin
@@ -1,8 +1,11 @@
-package utils
+package generator
import "core:fmt"
import s "core:strings"
+// todo(stefan): Simplify this whole flags business,
+// this can be implemented in a simpler fashion
+
CLIFlagType :: enum {
Unknown,
InputFiles,
@@ -18,7 +21,7 @@ CLIFlagType :: enum {
CLI_FLAG_STRINGS := [CLIFlagType][]string {
.Unknown = {""},
.Help = {"h", "help"},
- .InputFiles = {"f", "input-files"},
+ .InputFiles = {"i", "input-files"},
.InputFolder = {"d", "input-directory"},
.OutputFolder = {"o", "out"},
.EnableMetadataOutput = {"m", "export-metadata"},
@@ -30,13 +33,13 @@ CLI_FLAG_STRINGS := [CLIFlagType][]string {
CLI_FLAG_DESCRIPTIONS := [CLIFlagType]string {
.Unknown = "Invalid flag",
.Help = "Prints the help message... hello!",
- .InputFiles = "(real) path the source files for the packer (realpaths only), for multiple files you can provide one string of concateneted paths, separated by a ';'",
- .InputFolder = "(real) path to a folder full of source files. This is an alternative to the -i[,input-files] flag",
- .OutputFolder = "(real) path to the output folder for all the resulting files to be saved to.",
+ .InputFiles = "Full path to the source files for the packer, for multiple files you can provide one string of concateneted paths, separated by a ';'",
+ .InputFolder = "Full path to a folder full of source files. This is an alternative to the -i[,input-files] flag",
+ .OutputFolder = "Full path to the output folder for all the resulting files to be saved to.",
.EnableMetadataOutput = "Whether or not to export metadata (JSON or source files with the offsets for the packer sprites in the atlas)",
- .ConfigPath = "(real) path to a config file (json) that contains string definitions for exporting custom source files. More on this in the docs.",
- .MetadataJSONOutputPath = "(real) path for the resulting JSON that will be generated for the atlas. It overrides the name & location in regards to the -o[,output-folder] flag",
- .SourceCodeOutputPathOutputPath = "(real) path for the resulting source code file that will be generated for the atlas. It overrides the name & location in regards to the -o[,output-folder] flag",
+ .ConfigPath = "Full path to a config file (json) that contains string definitions for exporting custom source files. More on this in the docs.",
+ .MetadataJSONOutputPath = "Full path for the resulting JSON that will be generated for the atlas. It overrides the name & location in regards to the -o[,output-folder] flag",
+ .SourceCodeOutputPathOutputPath = "Full path for the resulting source code file that will be generated for the atlas. It overrides the name & location in regards to the -o[,output-folder] flag",
}
CLIOutputSettings :: struct {
@@ -83,12 +86,9 @@ print_help :: proc() {
for flag in CLIFlagType {
if flag == .Unknown do continue
- fmt.printfln(
- "Flag: -%v,%v \t -- %v",
- CLI_FLAG_STRINGS[flag][0],
- CLI_FLAG_STRINGS[flag][1],
- CLI_FLAG_DESCRIPTIONS[flag],
- )
+ flag_info := CLI_FLAG_STRINGS[flag]
+ flag_desc := CLI_FLAG_DESCRIPTIONS[flag]
+ fmt.printfln("Flag: -%v,%v \t -- %v", flag_info[0], flag_info[1], flag_desc)
}
}
diff --git a/src/generator.odin b/src/generator/generator.odin
similarity index 82%
rename from src/generator.odin
rename to src/generator/generator.odin
index 571cca2..5e78041 100644
--- a/src/generator.odin
+++ b/src/generator/generator.odin
@@ -1,8 +1,9 @@
-package game
+package generator
-import ase "./aseprite"
+import ase "../../vendors/aseprite"
import "core:encoding/json"
import "core:fmt"
+import "core:log"
import "core:os"
import fp "core:path/filepath"
import "core:slice"
@@ -10,15 +11,12 @@ import "core:strings"
import rl "vendor:raylib"
import stbrp "vendor:stb/rect_pack"
-import utils "./utils"
-
when ODIN_OS == .Windows {
- os_file_separator :: "\\"
+ OS_FILE_SEPARATOR :: "\\"
} else {
- os_file_separator :: "/"
+ OS_FILE_SEPARATOR :: "/"
}
-
CellData :: struct {
layer_index: u16,
opacity: u8,
@@ -40,6 +38,35 @@ SpriteAtlasMetadata :: struct {
size: [2]i32,
}
+SourceCodeGeneratorMetadata :: struct {
+ file_defines: struct {
+ top: string,
+ bottom: string,
+ file_name: string,
+ file_extension: 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,
+ },
+}
+
unmarshall_aseprite_dir :: proc(
path: string,
atlas_entries: ^[dynamic]AtlasEntry,
@@ -53,7 +80,7 @@ unmarshall_aseprite_dir :: proc(
unmarshall_aseprite_files_file_info(fis, atlas_entries, alloc)
}
} else {
- fmt.println("Couldn't open folder: ", path)
+ log.errorf("Couldn't open folder: ", path)
}
}
@@ -87,7 +114,7 @@ unmarshall_aseprite_files :: proc(
extension := fp.ext(file)
if extension != ".aseprite" do continue
- fmt.println("Unmarshalling file: ", file)
+ log.infof("Unmarshalling file: ", file)
ase.unmarshal_from_filename(file, &aseprite_document, alloc)
atlas_entry := atlas_entry_from_compressed_cells(aseprite_document)
atlas_entry.path = file
@@ -97,11 +124,11 @@ unmarshall_aseprite_files :: proc(
}
/*
- Goes through all the chunks in an aseprite document & copies the `Com_Image_Cel` cells in a separate image
+ 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) {
atlas_entry.frames = auto_cast len(document.frames)
- fmt.println("N Frames: ", len(document.frames))
+ log.infof("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
@@ -110,13 +137,13 @@ atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entr
// 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))
+ log.infof("Frame_{0} Chunks: ", frameIdx, len(frame.chunks))
for chunk in frame.chunks {
if cel_chunk, ok := chunk.(ase.Cel_Chunk); ok {
cel_img, ci_ok := cel_chunk.cel.(ase.Com_Image_Cel)
if !ci_ok do continue
- fmt.println(cel_chunk.layer_index)
+ log.info(cel_chunk.layer_index)
cell := CellData {
img = rl.Image {
@@ -134,7 +161,7 @@ atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entr
}
if layer_chunk, ok := chunk.(ase.Layer_Chunk); ok {
- fmt.println("Layer chunk: ", layer_chunk)
+ log.infof("Layer chunk: ", layer_chunk)
append(&atlas_entry.layer_names, layer_chunk.name)
}
}
@@ -148,7 +175,7 @@ atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entr
}
/*
- Takes in a slice of entries, an output texture and offsets (offset_x/y)
+ Takes in a slice of entries, an output texture and offsets (offset_x/y)
*/
pack_atlas_entries :: proc(
entries: []AtlasEntry,
@@ -201,10 +228,10 @@ pack_atlas_entries :: proc(
stbrp.init_target(&ctx, atlas.width, atlas.height, &nodes[0], i32(num_entries))
res := stbrp.pack_rects(&ctx, &rects[0], i32(num_entries))
if res == 1 {
- fmt.println("Packed everything successfully!")
- fmt.printfln("Rects: {0}", rects[:])
+ log.info("Packed everything successfully!")
+ log.infof("Rects: {0}", rects[:])
} else {
- fmt.println("Failed to pack everything!")
+ log.error("Failed to pack everything!")
}
for rect, rectIdx in rects {
@@ -228,7 +255,7 @@ pack_atlas_entries :: proc(
// note(stefan): drawing the sprite in the atlas in the packed coordinates
rl.ImageDraw(atlas, cell.img, src_rect, dst_rect, rl.WHITE)
- fmt.printfln("Src rect: {0}\nDst rect:{1}", src_rect, dst_rect)
+ log.infof("Src rect: {0}\nDst rect:{1}", src_rect, dst_rect)
}
metadata := make([dynamic]SpriteAtlasMetadata, allocator)
@@ -250,7 +277,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,
},
@@ -262,53 +289,24 @@ pack_atlas_entries :: proc(
return metadata
}
-SourceCodeGeneratorMetadata :: struct {
- file_defines: struct {
- top: string,
- bottom: string,
- file_name: string,
- file_extension: 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 = {
+ file_defines = {
top = "package atlas_bindings\n\n",
bottom = "",
file_name = "metadata",
file_extension = ".odin",
},
- custom_data_type = {
+ 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 +318,23 @@ odin_source_generator_metadata := SourceCodeGeneratorMetadata {
cpp_source_generator_metadata := SourceCodeGeneratorMetadata {
- file_defines = {
+ file_defines = {
top = "#include \n\n",
bottom = "",
file_name = "metadata",
file_extension = ".hpp",
},
- custom_data_type = {
+ 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",
@@ -346,9 +344,9 @@ cpp_source_generator_metadata := SourceCodeGeneratorMetadata {
}
/*
- 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
+ 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,
@@ -389,7 +387,7 @@ generate_odin_enums_and_atlas_offsets_file_sb :: proc(
// end offsets array
strings.write_string(&sb, "}\n\n")
- fmt.println("\n", strings.to_string(sb))
+ log.info("\n", strings.to_string(sb))
return sb
}
@@ -405,26 +403,21 @@ metadata_source_code_generate :: proc(
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(
@@ -448,42 +441,46 @@ metadata_source_code_generate :: proc(
}
}
// 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))
+ log.info("\n", strings.to_string(sb))
return sb
}
-save_output :: proc() {
- output_path, ok := g_mem.output_folder_path.(string)
+save_output :: proc(
+ output_folder_path: Maybe(string),
+ atlas_metadata: Maybe([dynamic]SpriteAtlasMetadata),
+ atlas_render_texture_target: rl.RenderTexture2D,
+) {
+ output_path, ok := output_folder_path.(string)
if !ok || output_path == "" {
- fmt.println("Output path is empty!")
+ log.error("Output path is empty!")
return
}
- image := rl.LoadImageFromTexture(g_mem.atlas_render_texture_target.texture)
+ image := rl.LoadImageFromTexture(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"}),
+ 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 metadata, ok := atlas_metadata.([dynamic]SpriteAtlasMetadata); ok {
+ log.info("Building metadata...")
if json_metadata, jok := json.marshal(metadata); jok == nil {
os.write_entire_file(
- strings.concatenate({output_path, os_file_separator, "metadata.json"}),
+ strings.concatenate({output_path, OS_FILE_SEPARATOR, "metadata.json"}),
json_metadata,
)
} else {
- fmt.println("Failed to marshall the atlas metadata to a json!")
+ log.error("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
@@ -492,14 +489,14 @@ save_output :: proc() {
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"}),
+ strings.concatenate({output_path, OS_FILE_SEPARATOR, "metadata.odin"}),
transmute([]byte)odin_metadata,
)
if !ok {
- fmt.println("Failed to save 'metadata.odin'")
+ log.error("Failed to save 'metadata.odin'")
}
} else {
- fmt.println("No metadata to export!")
+ log.error("No metadata to export!")
}
}
@@ -509,30 +506,31 @@ save_metadata_simple :: proc(
json_file_name: Maybe(string),
source_file_name: Maybe(string),
source_gen_metadata: Maybe(SourceCodeGeneratorMetadata),
+ atlas_metadata: Maybe([dynamic]SpriteAtlasMetadata),
) {
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!")
+ log.error("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!")
+ metadata, ok := atlas_metadata.([dynamic]SpriteAtlasMetadata);if !ok {
+ log.error("No metadata to export!")
}
- fmt.println("Building metadata...")
+ log.info("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},
+ {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)
+ log.errorf("Failed to write json to file: ", json_output_path)
}
} else {
- fmt.println("Failed to marshall the atlas metadata to a json!")
+ log.error("Failed to marshall the atlas metadata to a json!")
}
}
@@ -544,45 +542,45 @@ save_metadata_simple :: proc(
source_metadata := strings.to_string(sb)
source_output_path := strings.concatenate(
- {
+ {
output_path,
- os_file_separator,
+ 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)
+ log.errorf("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"},
+ {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)
+ log.errorf("Failed to save source code to file:", source_output_path)
}
}
}
}
save_metadata :: proc(
- settings: utils.CLIPackerSettings,
+ settings: CLIPackerSettings,
atlas_entries: []AtlasEntry,
atlas_metadata: []SpriteAtlasMetadata,
) {
- metadata, ok := settings.metadata.(utils.CLIMetadataSettings);if !ok do return
+ metadata, ok := settings.metadata.(CLIMetadataSettings);if !ok do return
if json_path, ok := metadata.json_path.(string); ok {
json_bytes, jerr := json.marshal(atlas_metadata)
if jerr == nil {
os.write_entire_file(json_path, json_bytes)
} else {
- fmt.println("Failed to marshall metadata")
+ log.error("Failed to marshall metadata")
}
}
if source_code_path, ok := metadata.source_code_path.(string); ok {
diff --git a/src/main_hot_reload/main_hot_reload.odin b/src/main_hot_reload/main_hot_reload.odin
deleted file mode 100644
index 99f4692..0000000
--- a/src/main_hot_reload/main_hot_reload.odin
+++ /dev/null
@@ -1,196 +0,0 @@
-// Development game exe. Loads game.dll and reloads it whenever it changes.
-
-package main
-
-import "core:c/libc"
-import "core:dynlib"
-import "core:fmt"
-import "core:log"
-import "core:mem"
-import "core:os"
-
-when ODIN_OS == .Windows {
- DLL_EXT :: ".dll"
-} else when ODIN_OS == .Darwin {
- DLL_EXT :: ".dylib"
-} else {
- DLL_EXT :: ".so"
-}
-
-copy_dll :: proc(to: string) -> bool {
- exit: i32
- when ODIN_OS == .Windows {
- exit = libc.system(fmt.ctprintf("copy game.dll {0}", to))
- } else {
- exit = libc.system(fmt.ctprintf("cp game" + DLL_EXT + " {0}", to))
- }
-
- if exit != 0 {
- fmt.printfln("Failed to copy game" + DLL_EXT + " to {0}", to)
- return false
- }
-
- return true
-}
-
-GameAPI :: struct {
- lib: dynlib.Library,
- init_window: proc(),
- init: proc(),
- update: proc() -> bool,
- shutdown: proc(),
- shutdown_window: proc(),
- memory: proc() -> rawptr,
- memory_size: proc() -> int,
- hot_reloaded: proc(mem: rawptr),
- force_reload: proc() -> bool,
- force_restart: proc() -> bool,
- modification_time: os.File_Time,
- api_version: int,
-}
-
-load_game_api :: proc(api_version: int) -> (api: GameAPI, ok: bool) {
- mod_time, mod_time_error := os.last_write_time_by_name("game" + DLL_EXT)
- if mod_time_error != os.ERROR_NONE {
- fmt.printfln(
- "Failed getting last write time of game" + DLL_EXT + ", error code: {1}",
- mod_time_error,
- )
- return
- }
-
- // NOTE: this needs to be a relative path for Linux to work.
- game_dll_name := fmt.tprintf(
- "{0}game_{1}" + DLL_EXT,
- "./" when ODIN_OS != .Windows else "",
- api_version,
- )
- copy_dll(game_dll_name) or_return
-
- _, ok = dynlib.initialize_symbols(&api, game_dll_name, "game_", "lib")
- if !ok {
- fmt.printfln("Failed initializing symbols: {0}", dynlib.last_error())
- }
-
- api.api_version = api_version
- api.modification_time = mod_time
- ok = true
-
- return
-}
-
-unload_game_api :: proc(api: ^GameAPI) {
- if api.lib != nil {
- if !dynlib.unload_library(api.lib) {
- fmt.printfln("Failed unloading lib: {0}", dynlib.last_error())
- }
- }
-
- if os.remove(fmt.tprintf("game_{0}" + DLL_EXT, api.api_version)) != 0 {
- fmt.printfln("Failed to remove game_{0}" + DLL_EXT + " copy", api.api_version)
- }
-}
-
-main :: proc() {
- context.logger = log.create_console_logger()
-
- default_allocator := context.allocator
- tracking_allocator: mem.Tracking_Allocator
- mem.tracking_allocator_init(&tracking_allocator, default_allocator)
- context.allocator = mem.tracking_allocator(&tracking_allocator)
-
- reset_tracking_allocator :: proc(a: ^mem.Tracking_Allocator) -> bool {
- err := false
-
- for _, value in a.allocation_map {
- fmt.printf("%v: Leaked %v bytes\n", value.location, value.size)
- err = true
- }
-
- mem.tracking_allocator_clear(a)
- return err
- }
-
- game_api_version := 0
- game_api, game_api_ok := load_game_api(game_api_version)
-
- if !game_api_ok {
- fmt.println("Failed to load Game API")
- return
- }
-
- game_api_version += 1
- game_api.init_window()
- game_api.init()
-
- old_game_apis := make([dynamic]GameAPI, default_allocator)
-
- window_open := true
- for window_open {
- window_open = game_api.update()
- force_reload := game_api.force_reload()
- force_restart := game_api.force_restart()
- reload := force_reload || force_restart
- game_dll_mod, game_dll_mod_err := os.last_write_time_by_name("game" + DLL_EXT)
-
- if game_dll_mod_err == os.ERROR_NONE && game_api.modification_time != game_dll_mod {
- reload = true
- }
-
- if reload {
- new_game_api, new_game_api_ok := load_game_api(game_api_version)
-
- if new_game_api_ok {
- if game_api.memory_size() != new_game_api.memory_size() || force_restart {
- game_api.shutdown()
- reset_tracking_allocator(&tracking_allocator)
-
- for &g in old_game_apis {
- unload_game_api(&g)
- }
-
- clear(&old_game_apis)
- unload_game_api(&game_api)
- game_api = new_game_api
- game_api.init()
- } else {
- append(&old_game_apis, game_api)
- game_memory := game_api.memory()
- game_api = new_game_api
- game_api.hot_reloaded(game_memory)
- }
-
- game_api_version += 1
- }
- }
-
- for b in tracking_allocator.bad_free_array {
- log.error("Bad free at: %v", b.location)
- }
-
- clear(&tracking_allocator.bad_free_array)
- free_all(context.temp_allocator)
- }
-
- free_all(context.temp_allocator)
- game_api.shutdown()
- reset_tracking_allocator(&tracking_allocator)
-
- for &g in old_game_apis {
- unload_game_api(&g)
- }
-
- delete(old_game_apis)
-
- game_api.shutdown_window()
- unload_game_api(&game_api)
- mem.tracking_allocator_destroy(&tracking_allocator)
-}
-
-// make game use good GPU on laptops etc
-
-@(export)
-NvOptimusEnablement: u32 = 1
-
-@(export)
-AmdPowerXpressRequestHighPerformance: i32 = 1
diff --git a/src/main_release/main_release.odin b/src/main_release/main_release.odin
deleted file mode 100644
index 1d335d1..0000000
--- a/src/main_release/main_release.odin
+++ /dev/null
@@ -1,77 +0,0 @@
-// For making a release exe that does not use hot reload.
-
-package main_release
-
-import "core:log"
-import "core:os"
-
-import game ".."
-
-UseTrackingAllocator :: #config(UseTrackingAllocator, false)
-
-main :: proc() {
- when UseTrackingAllocator {
- default_allocator := context.allocator
- tracking_allocator: Tracking_Allocator
- tracking_allocator_init(&tracking_allocator, default_allocator)
- context.allocator = allocator_from_tracking_allocator(&tracking_allocator)
- }
-
- mode: int = 0
- when ODIN_OS == .Linux || ODIN_OS == .Darwin {
- mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IROTH
- }
-
- logh, logh_err := os.open("log.txt", (os.O_CREATE | os.O_TRUNC | os.O_RDWR), mode)
-
- if logh_err == os.ERROR_NONE {
- os.stdout = logh
- os.stderr = logh
- }
-
- logger :=
- logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
- context.logger = logger
-
- game.game_init_window()
- game.game_init()
-
- window_open := true
- for window_open {
- window_open = game.game_update()
-
- when UseTrackingAllocator {
- for b in tracking_allocator.bad_free_array {
- log.error("Bad free at: %v", b.location)
- }
-
- clear(&tracking_allocator.bad_free_array)
- }
-
- free_all(context.temp_allocator)
- }
-
- free_all(context.temp_allocator)
- game.game_shutdown()
- game.game_shutdown_window()
-
- if logh_err == os.ERROR_NONE {
- log.destroy_file_logger(&logger)
- }
-
- when UseTrackingAllocator {
- for key, value in tracking_allocator.allocation_map {
- log.error("%v: Leaked %v bytes\n", value.location, value.size)
- }
-
- tracking_allocator_destroy(&tracking_allocator)
- }
-}
-
-// make game use good GPU on laptops etc
-
-@(export)
-NvOptimusEnablement: u32 = 1
-
-@(export)
-AmdPowerXpressRequestHighPerformance: i32 = 1
diff --git a/src/symbol_exports.odin b/src/symbol_exports.odin
deleted file mode 100644
index d5ada25..0000000
--- a/src/symbol_exports.odin
+++ /dev/null
@@ -1,88 +0,0 @@
-package game
-
-import rl "vendor:raylib"
-
-@(export)
-game_update :: proc() -> bool {
- update()
- draw()
- return !rl.WindowShouldClose()
-}
-
-@(export)
-game_init_window :: proc() {
- rl.SetConfigFlags({.WINDOW_RESIZABLE})
- rl.InitWindow(1400, 800, "YAAP - Yet Another Atlas Packer")
- rl.SetWindowMinSize(1400, 800)
-}
-
-@(export)
-game_init :: proc() {
- g_mem = new(GameMemory)
-
- g_mem^ = GameMemory{}
-
- game_hot_reloaded(g_mem)
-
-
- when !ODIN_DEBUG {
- rl.SetExitKey(nil)
- }
-
- current_monitor := rl.GetCurrentMonitor()
- g_mem.monitor_info = MonitorInformation {
- max_width = auto_cast rl.GetMonitorWidth(current_monitor),
- max_height = auto_cast rl.GetMonitorHeight(current_monitor),
- }
-
- g_mem.window_info = WindowInformation {
- w = 1280,
- h = 720,
- }
-
- g_mem.atlas_render_texture_target = rl.LoadRenderTexture(256, 256)
- g_mem.atlas_render_size = 256
-
- checkered_img := rl.GenImageChecked(256, 256, 256 / 4, 256 / 4, rl.GRAY, rl.DARKGRAY)
- defer rl.UnloadImage(checkered_img)
- g_mem.atlas_checked_background.texture = rl.LoadTextureFromImage(checkered_img)
-
- rl.SetTargetFPS(rl.GetMonitorRefreshRate(current_monitor))
- rl.GuiLoadStyle("./styles/style_candy.rgs")
-}
-
-@(export)
-game_shutdown :: proc() {
- free(g_mem)
-}
-
-@(export)
-game_shutdown_window :: proc() {
- rl.CloseWindow()
-}
-
-@(export)
-game_memory :: proc() -> rawptr {
- return g_mem
-}
-
-@(export)
-game_memory_size :: proc() -> int {
- return size_of(GameMemory)
-}
-
-@(export)
-game_hot_reloaded :: proc(mem: rawptr) {
- g_mem = (^GameMemory)(mem)
- rl.GuiLoadStyle("./styles/style_candy.rgs")
-}
-
-@(export)
-game_force_reload :: proc() -> bool {
- return rl.IsKeyPressed(.F5)
-}
-
-@(export)
-game_force_restart :: proc() -> bool {
- return rl.IsKeyPressed(.F6)
-}
diff --git a/src/utils/animation.odin b/src/utils/animation.odin
deleted file mode 100644
index 4a55c6e..0000000
--- a/src/utils/animation.odin
+++ /dev/null
@@ -1,53 +0,0 @@
-// This implements simple animations using sprite sheets. The texture in the
-// `Animation` struct is assumed to contain a horizontal strip of the frames
-// in the animation. Call `animation_update` to update and then call
-// `animation_rect` when you wish to know the source rect to use in the texture
-// With the source rect you can run rl.DrawTextureRec to draw the current frame.
-
-package utils
-
-import "core:log"
-
-Animation :: struct {
- texture: Texture,
- num_frames: int,
- current_frame: int,
- frame_timer: f32,
- frame_length: f32,
-}
-
-animation_create :: proc(tex: Texture, num_frames: int, frame_length: f32) -> Animation {
- return(
- Animation {
- texture = tex,
- num_frames = num_frames,
- frame_length = frame_length,
- frame_timer = frame_length,
- } \
- )
-}
-
-animation_update :: proc(a: ^Animation, dt: f32) {
- a.frame_timer -= dt
-
- if a.frame_timer <= 0 {
- a.frame_timer = a.frame_length + a.frame_timer
- a.current_frame += 1
-
- if a.current_frame >= a.num_frames {
- a.current_frame = 0
- }
- }
-}
-
-animation_rect :: proc(a: Animation) -> Rect {
- if a.num_frames == 0 {
- log.error("Animation has zero frames")
- return RectEmpty
- }
-
- w := f32(a.texture.width) / f32(a.num_frames)
- h := f32(a.texture.height)
-
- return {x = f32(a.current_frame) * w, y = 0, width = w, height = h}
-}
diff --git a/src/utils/handle_array.odin b/src/utils/handle_array.odin
deleted file mode 100644
index b6b59d1..0000000
--- a/src/utils/handle_array.odin
+++ /dev/null
@@ -1,141 +0,0 @@
-// This handle-based array gives you a statically allocated array where you can
-// use index based handles instead of pointers. The handles have a generation
-// that makes sure you don't get bugs when slots are re-used.
-// Read more about it here: https://floooh.github.io/2018/06/17/handles-vs-pointers.html */
-
-package utils
-
-Handle :: struct($T: typeid) {
- // idx 0 means unused. Note that slot 0 is a dummy slot, it can never be used.
- idx: u32,
- gen: u32,
-}
-
-HandleArrayItem :: struct($T: typeid) {
- item: T,
- handle: Handle(T),
-}
-
-// TODO: Add a freelist that uses some kind of bit array... We should be able to
-// check 64 item slots at a time that way, but without any dynamic array.
-HandleArray :: struct($T: typeid, $N: int) {
- items: #soa[N]HandleArrayItem(T),
- num_items: u32,
-}
-
-ha_add :: proc(a: ^HandleArray($T, $N), v: T) -> (Handle(T), bool) #optional_ok {
- for idx in 1 ..< a.num_items {
- i := &a.items[idx]
-
- if idx != 0 && i.handle.idx == 0 {
- i.handle.idx = u32(idx)
- i.item = v
- return i.handle, true
- }
- }
-
- // Index 0 is dummy
- if a.num_items == 0 {
- a.num_items += 1
- }
-
- if a.num_items == len(a.items) {
- return {}, false
- }
-
- idx := a.num_items
- i := &a.items[a.num_items]
- a.num_items += 1
- i.handle.idx = idx
- i.handle.gen = 1
- i.item = v
- return i.handle, true
-}
-
-ha_get :: proc(a: HandleArray($T, $N), h: Handle(T)) -> (T, bool) {
- if h.idx == 0 {
- return {}, false
- }
-
- if int(h.idx) < len(a.items) && h.idx < a.num_items && a.items[h.idx].handle == h {
- return a.items[h.idx].item, true
- }
-
- return {}, false
-}
-
-ha_get_ptr :: proc(a: HandleArray($T, $N), h: Handle(T)) -> ^T {
- if h.idx == 0 {
- return nil
- }
-
- if int(h.idx) < len(a.items) && h.idx < a.num_items && a.items[h.idx].handle == h {
- return &ha.items[h.idx].item
- }
-
- return nil
-}
-
-ha_remove :: proc(a: ^HandleArray($T, $N), h: Handle(T)) {
- if h.idx == 0 {
- return
- }
-
- if int(h.idx) < len(a.items) && h.idx < a.num_items && a.items[h.idx].handle == h {
- a.items[h.idx].handle.idx = 0
- a.items[h.idx].handle.gen += 1
- }
-}
-
-ha_valid :: proc(a: HandleArray($T, $N), h: Handle(T)) -> bool {
- if h.idx == 0 {
- return false
- }
-
- return int(h.idx) < len(a.items) && h.idx < a.num_items && a.items[h.idx].handle == h
-}
-
-HandleArrayIter :: struct($T: typeid, $N: int) {
- a: ^HandleArray(T, N),
- index: int,
-}
-
-ha_make_iter :: proc(a: ^HandleArray($T, $N)) -> HandleArrayIter(T, N) {
- return HandleArrayIter(T, N){a = a}
-}
-
-ha_iter :: proc(it: ^HandleArrayIter($T, $N)) -> (val: T, h: Handle(T), cond: bool) {
- cond = it.index < int(it.a.num_items)
-
- for ; cond; cond = it.index < int(it.a.num_items) {
- if it.a.items[it.index].handle.idx == 0 {
- it.index += 1
- continue
- }
-
- val = it.a.items[it.index].item
- h = it.a.items[it.index].handle
- it.index += 1
- break
- }
-
- return
-}
-
-ha_iter_ptr :: proc(it: ^HandleArrayIter($T, $N)) -> (val: ^T, h: Handle(T), cond: bool) {
- cond = it.index < int(it.a.num_items)
-
- for ; cond; cond = it.index < int(it.a.num_items) {
- if it.a.items[it.index].handle.idx == 0 {
- it.index += 1
- continue
- }
-
- val = &it.a.items[it.index].item
- h = it.a.items[it.index].handle
- it.index += 1
- break
- }
-
- return
-}
diff --git a/src/utils/helpers.odin b/src/utils/helpers.odin
deleted file mode 100644
index 07a5a86..0000000
--- a/src/utils/helpers.odin
+++ /dev/null
@@ -1,55 +0,0 @@
-// generic odin helpers
-
-package utils
-
-import "core:intrinsics"
-import "core:reflect"
-import "core:strings"
-import rl "vendor:raylib"
-
-Texture :: rl.Texture
-Color :: rl.Color
-
-Rect :: rl.Rectangle
-RectEmpty :: Rect{}
-
-increase_or_wrap_enum :: proc(e: $T) -> T {
- ei := int(e) + 1
-
- if ei >= len(T) {
- ei = 0
- }
-
- return T(ei)
-}
-
-union_type :: proc(a: any) -> typeid {
- return reflect.union_variant_typeid(a)
-}
-
-temp_cstring :: proc(s: string) -> cstring {
- return strings.clone_to_cstring(s, context.temp_allocator)
-}
-
-// There is a remap in core:math but it doesn't clamp in the new range, which I
-// always want.
-remap :: proc "contextless" (
- old_value, old_min, old_max, new_min, new_max: $T,
-) -> (
- x: T,
-) where intrinsics.type_is_numeric(T),
- !intrinsics.type_is_array(T) {
- old_range := old_max - old_min
- new_range := new_max - new_min
- if old_range == 0 {
- return new_range / 2
- }
- return clamp(((old_value - old_min) / old_range) * new_range + new_min, new_min, new_max)
-}
-
-Vec2i :: [2]int
-Vec2 :: [2]f32
-
-vec2_from_vec2i :: proc(p: Vec2i) -> Vec2 {
- return {f32(p.x), f32(p.y)}
-}
diff --git a/src/aseprite b/vendors/aseprite
similarity index 100%
rename from src/aseprite
rename to vendors/aseprite
diff --git a/src/dialog/build.bat b/vendors/dialog/build.bat
similarity index 100%
rename from src/dialog/build.bat
rename to vendors/dialog/build.bat
diff --git a/src/dialog/build.sh b/vendors/dialog/build.sh
old mode 100755
new mode 100644
similarity index 100%
rename from src/dialog/build.sh
rename to vendors/dialog/build.sh
diff --git a/src/dialog/libtinyfiledialogs b/vendors/dialog/libtinyfiledialogs
similarity index 100%
rename from src/dialog/libtinyfiledialogs
rename to vendors/dialog/libtinyfiledialogs
diff --git a/src/dialog/tinyfiledialog.odin b/vendors/dialog/tinyfiledialog.odin
similarity index 100%
rename from src/dialog/tinyfiledialog.odin
rename to vendors/dialog/tinyfiledialog.odin