Compare commits

...

2 commits

Author SHA1 Message Date
84db74586b misc: settings update 2024-08-30 12:33:40 +03:00
178b0d5525 feat: Added methods for writing out the metadata about the atlas elements
misc: Refactoring
2024-08-30 12:32:57 +03:00
6 changed files with 235 additions and 161 deletions

2
.vscode/launch.json vendored
View file

@ -6,7 +6,7 @@
"request": "launch", "request": "launch",
"preLaunchTask": "Build Debug", "preLaunchTask": "Build Debug",
"name": "Debug", "name": "Debug",
"program": "${workspaceFolder}/game_debug.exe", "program": "${workspaceFolder}/build/game_debug.exe",
"args": [], "args": [],
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
}, },

View file

@ -1,10 +1,6 @@
{ {
"workbench.colorCustomizations": {
"activityBar.background": "#322C2D",
"titleBar.activeBackground": "#463E3F",
"titleBar.activeForeground": "#FAFAFA"
},
"[odin]": { "[odin]": {
"editor.formatOnSave": true "editor.formatOnSave": true,
"editor.tabSize": 4
} }
} }

41
.vscode/tasks.json vendored
View file

@ -7,30 +7,31 @@
"label": "Build Debug", "label": "Build Debug",
"type": "shell", "type": "shell",
"windows": { "windows": {
"command": "${workspaceFolder}/scripts/build_debug.bat", "command": "${workspaceFolder}/scripts/build_debug.bat"
}, },
"linux": { "linux": {
"command": "${workspaceFolder}/scripts/build_debug.sh", "command": "${workspaceFolder}/scripts/build_debug.sh"
}, },
"osx": { "osx": {
"command": "${workspaceFolder}/scripts/build_debug.sh", "command": "${workspaceFolder}/scripts/build_debug.sh"
}, },
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": false "isDefault": true
}, },
"problemMatcher": []
}, },
{ {
"label": "Build Release", "label": "Build Release",
"type": "shell", "type": "shell",
"windows": { "windows": {
"command": "${workspaceFolder}/scripts/build_release.bat", "command": "${workspaceFolder}/scripts/build_release.bat"
}, },
"linux": { "linux": {
"command": "${workspaceFolder}/scripts/build_release.sh", "command": "${workspaceFolder}/scripts/build_release.sh"
}, },
"osx": { "osx": {
"command": "${workspaceFolder}/scripts/build_release.sh", "command": "${workspaceFolder}/scripts/build_release.sh"
}, },
"group": "build" "group": "build"
}, },
@ -38,27 +39,21 @@
"label": "Clean build folder(s)", "label": "Clean build folder(s)",
"type": "shell", "type": "shell",
"windows": { "windows": {
"command": "cd ${workspaceFolder}\\build && rm game*; cd ${workspaceFolder} && rm aseprite_odin_generator*", "command": "cd ${workspaceFolder}\\build && rm game*; cd ${workspaceFolder} && rm aseprite_odin_generator*"
}, },
// "linux": {
// "command": "${workspaceFolder}/scripts/build_release.sh",
// },
// "osx": {
// "command": "${workspaceFolder}/scripts/build_release.sh",
// },
"group": "build" "group": "build"
}, },
{ {
"label": "Build Hot Reload", "label": "Build Hot Reload",
"type": "shell", "type": "shell",
"windows": { "windows": {
"command": "${workspaceFolder}/scripts/build_hot_reload.bat; start game.exe", "command": "${workspaceFolder}/scripts/build_hot_reload.bat; start game.exe"
}, },
"linux": { "linux": {
"command": "${workspaceFolder}/scripts/build_hot_reload.sh", "command": "${workspaceFolder}/scripts/build_hot_reload.sh"
}, },
"osx": { "osx": {
"command": "${workspaceFolder}/scripts/build_hot_reload.sh", "command": "${workspaceFolder}/scripts/build_hot_reload.sh"
}, },
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -68,16 +63,14 @@
"showReuseMessage": false, "showReuseMessage": false,
"clear": true "clear": true
}, },
"group": { "group": "build",
"kind": "build", "problemMatcher": []
"isDefault": false
},
}, },
{ {
"label": "Build&Run Atlas Generator Test", "label": "Build&Run Atlas Generator Test",
"type": "shell", "type": "shell",
"windows": { "windows": {
"command": "${workspaceFolder}/scripts/build_generator_debug.bat && build_generator\\aseprite_odin_generator.exe -input-files:value_of_custom_arg -h", "command": "${workspaceFolder}/scripts/build_generator_debug.bat && build_generator\\aseprite_odin_generator.exe -input-files:value_of_custom_arg -h"
}, },
"options": { "options": {
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
@ -92,8 +85,8 @@
}, },
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": false
}, }
} }
] ]
} }

View file

@ -14,6 +14,7 @@ package game
import "core:fmt" import "core:fmt"
import "core:math" import "core:math"
import "core:strings" import "core:strings"
import "utils"
import rl "vendor:raylib" import rl "vendor:raylib"
import diag "dialog" import diag "dialog"
@ -261,8 +262,8 @@ draw_atlas_settings_and_preview :: proc() {
// rl.GuiLine({y = elements_height, width = left_half_rect.width}, "Actions") // rl.GuiLine({y = elements_height, width = left_half_rect.width}, "Actions")
// elements_height += small_offset // elements_height += small_offset
actions_label_y := elements_height
{ {
actions_label_y := elements_height
defer rl.GuiGroupBox( defer rl.GuiGroupBox(
{ {
x = small_offset / 2, x = small_offset / 2,
@ -286,6 +287,7 @@ draw_atlas_settings_and_preview :: proc() {
g_mem.should_open_file_dialog = true g_mem.should_open_file_dialog = true
g_mem.source_location_type = .SourceFiles g_mem.source_location_type = .SourceFiles
} }
if rl.GuiButton( if rl.GuiButton(
{ {
x = left_half_rect.width / 2, x = left_half_rect.width / 2,
@ -312,6 +314,7 @@ draw_atlas_settings_and_preview :: proc() {
) { ) {
g_mem.should_render_atlas = true g_mem.should_render_atlas = true
} }
if rl.GuiButton( if rl.GuiButton(
{ {
x = left_half_rect.width / 2, x = left_half_rect.width / 2,
@ -336,6 +339,7 @@ draw_atlas_settings_and_preview :: proc() {
) { ) {
save_output() save_output()
} }
if rl.GuiButton( if rl.GuiButton(
{ {
x = left_half_rect.width / 2, x = left_half_rect.width / 2,
@ -345,6 +349,9 @@ draw_atlas_settings_and_preview :: proc() {
}, },
"Save To...", "Save To...",
) { ) {
if output_folder, ok := g_mem.output_folder_path.(string); ok {
save_metadata_simple(output_folder)
}
} }
elements_height += small_offset * 2 elements_height += small_offset * 2
} }
@ -467,7 +474,9 @@ open_file_dialog :: proc() {
file_path: cstring file_path: cstring
patterns: []cstring = {"*.png"} patterns: []cstring = {"*.png"}
if default_path, ok := g_mem.output_folder_path.(string); ok { 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, os_file_separator, "atlas.png"},
)
default_path_to_save: cstring = strings.clone_to_cstring(default_path_filename) default_path_to_save: cstring = strings.clone_to_cstring(default_path_filename)
file_path = cstring( file_path = cstring(
diag.save_file_dialog( diag.save_file_dialog(

View file

@ -102,7 +102,7 @@ unmarshall_aseprite_files :: proc(
atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entry: AtlasEntry) { atlas_entry_from_compressed_cells :: proc(document: ase.Document) -> (atlas_entry: AtlasEntry) {
atlas_entry.frames = auto_cast len(document.frames) atlas_entry.frames = auto_cast len(document.frames)
fmt.println("N Frames: ", 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 // 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 // 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 // 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. // allocate a slice that is going to be [Frames X Layers]CellData.
@ -266,6 +266,8 @@ SourceCodeGeneratorMetadata :: struct {
file_defines: struct { file_defines: struct {
top: string, top: string,
bottom: string, bottom: string,
file_name: string,
file_extension: string,
}, },
lanugage_settings: struct { 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} 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}
@ -290,7 +292,12 @@ SourceCodeGeneratorMetadata :: struct {
} }
odin_source_generator_metadata := SourceCodeGeneratorMetadata { odin_source_generator_metadata := SourceCodeGeneratorMetadata {
file_defines = {top = "package atlas_bindings\n\n", bottom = ""}, file_defines = {
top = "package atlas_bindings\n\n",
bottom = "",
file_name = "metadata",
file_extension = ".odin",
},
custom_data_type = { custom_data_type = {
name = "AtlasRect", name = "AtlasRect",
type_declaration = "%v :: struct {{ x, y, w, h: i32 }}\n\n", type_declaration = "%v :: struct {{ x, y, w, h: i32 }}\n\n",
@ -313,7 +320,12 @@ odin_source_generator_metadata := SourceCodeGeneratorMetadata {
cpp_source_generator_metadata := SourceCodeGeneratorMetadata { cpp_source_generator_metadata := SourceCodeGeneratorMetadata {
file_defines = {top = "#include <iostream>\n\n", bottom = ""}, file_defines = {
top = "#include <iostream>\n\n",
bottom = "",
file_name = "metadata",
file_extension = ".hpp",
},
custom_data_type = { custom_data_type = {
name = "AtlasRect", name = "AtlasRect",
type_declaration = "struct %v {{\n\tint x;\n\tint y;\n\tint w;\n\tint h;\n}};\n\n", type_declaration = "struct %v {{\n\tint x;\n\tint y;\n\tint w;\n\tint h;\n}};\n\n",
@ -384,14 +396,9 @@ generate_odin_enums_and_atlas_offsets_file_sb :: proc(
metadata_source_code_generate :: proc( metadata_source_code_generate :: proc(
metadata: []SpriteAtlasMetadata, metadata: []SpriteAtlasMetadata,
code_generation_metadata: Maybe(SourceCodeGeneratorMetadata), codegen: SourceCodeGeneratorMetadata,
alloc := context.allocator, alloc := context.allocator,
) -> strings.Builder { ) -> 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) sb := strings.builder_make(alloc)
// strings.write_string(&sb, "package atlas_bindings\n\n") // strings.write_string(&sb, "package atlas_bindings\n\n")
@ -452,6 +459,117 @@ metadata_source_code_generate :: proc(
} }
save_output :: proc() {
output_path, ok := g_mem.output_folder_path.(string)
if !ok || output_path == "" {
fmt.println("Output path is empty!")
return
}
image := rl.LoadImageFromTexture(g_mem.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"}),
)
rl.ExportImage(image, cstring_atlas_output_path)
if metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata); ok {
fmt.println("Building metadata...")
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)
ok := os.write_entire_file(
strings.concatenate({output_path, os_file_separator, "metadata.odin"}),
transmute([]byte)odin_metadata,
)
if !ok {
fmt.println("Failed to save 'metadata.odin'")
}
} else {
fmt.println("No metadata to export!")
}
}
save_metadata_simple :: proc(
output_path: string,
json_file_name: Maybe(string),
source_file_name: Maybe(string),
source_gen_metadata: Maybe(SourceCodeGeneratorMetadata),
) {
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!")
return
}
metadata, ok := g_mem.atlas_metadata.([dynamic]SpriteAtlasMetadata);if !ok {
fmt.println("No metadata to export!")
}
fmt.println("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},
)
if ok = os.write_entire_file(json_output_path, json_metadata); !ok {
fmt.println("Failed to write json to file: ", json_output_path)
}
} else {
fmt.println("Failed to marshall the atlas metadata to a json!")
}
}
// note(stefan): Having source_file_name & source_gen_metadata is redundant but this is fine for now
if source_file_name_ok {
// if src_gen_metadata
if codegen, cok := source_gen_metadata.(SourceCodeGeneratorMetadata); cok {
sb := metadata_source_code_generate(metadata[:], codegen)
source_metadata := strings.to_string(sb)
source_output_path := strings.concatenate(
{
output_path,
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)
}
} 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"},
)
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)
}
}
}
}
save_metadata :: proc( save_metadata :: proc(
settings: utils.CLIPackerSettings, settings: utils.CLIPackerSettings,
atlas_entries: []AtlasEntry, atlas_entries: []AtlasEntry,
@ -473,46 +591,3 @@ save_metadata :: proc(
os.write_entire_file(source_code_path, transmute([]byte)source_code_output_str) os.write_entire_file(source_code_path, transmute([]byte)source_code_output_str)
} }
} }
save_output :: proc() {
output_path, ok := g_mem.output_folder_path.(string)
if !ok {
fmt.println("Output path is empty!")
return
} else if output_path == "" {
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"})
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!")
}
}

View file

@ -97,6 +97,7 @@ parse_arguments :: proc(args: []string) -> (cliargs: map[CLIFlagType]CLIFlag) {
for arg in args { for arg in args {
arg_name_and_value, err := s.split(arg, ":") arg_name_and_value, err := s.split(arg, ":")
if err != nil {continue}
name := arg_name_and_value[0] name := arg_name_and_value[0]
if name[0] == '-' { if name[0] == '-' {