diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a700a49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +concept/ +packed/ diff --git a/.gitmodules b/.gitmodules index bb0c0b7..8b36230 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,10 @@ [submodule "include/imgui"] path = include/imgui url = https://github.com/ocornut/imgui + branch = docking [submodule "include/glm"] path = include/glm url = https://github.com/g-truc/glm +[submodule "include/tinyxml2"] + path = include/tinyxml2 + url = https://github.com/leethomason/tinyxml2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 916accc..b751cb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,16 +3,21 @@ project(anm2ed CXX) # Gather project sources file(GLOB SOURCES - "${PROJECT_SOURCE_DIR}/include/imgui/*.cpp" - "${PROJECT_SOURCE_DIR}/include/pugixml/*.cpp" + "include/imgui/imgui.cpp" + "include/imgui/imgui_draw.cpp" + "include/imgui/imgui_tables.cpp" + "include/imgui/imgui_widgets.cpp" + "include/imgui/backends/imgui_impl_sdl3.cpp" + "include/imgui/backends/imgui_impl_opengl3.cpp" + "include/tinyxml2/tinyxml2.cpp" "${PROJECT_SOURCE_DIR}/src/*.cpp" ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE include src) +target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src ) -set(CMAKE_CXX_FLAGS "-g -O2 -std=c++20 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess") +set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete") target_link_libraries(${PROJECT_NAME} PRIVATE m GL GLEW SDL3 SDL3_mixer) diff --git a/include/imgui b/include/imgui index cfa43e7..d896eab 160000 --- a/include/imgui +++ b/include/imgui @@ -1 +1 @@ -Subproject commit cfa43e721aa1300b17d633f7590516587ff23617 +Subproject commit d896eab16620f80e3cae9b164eb62f71b47f6e45 diff --git a/src/COMMON.h b/src/COMMON.h index 634933d..830b4d4 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -5,25 +5,23 @@ #include #include -#include -#include +#include +#include #include #include -#include #include #include #include #include #include +#include +#include +#include #include +#include #include "STRINGS.h" -#include "RESOURCES.h" - -using namespace glm; -using namespace pugi; -using namespace std; typedef uint8_t u8; typedef uint16_t u16; @@ -40,15 +38,40 @@ typedef double f64; #define PI (GLM_PI) #define TAU (PI * 2) -#define ATAN(x1, x2, y1, y2) (fmod((atan2(y2 - y1, x2 - x1) + TAU), TAU)) /* [0, 2PI] */ -struct State +using namespace glm; // fuck you + +#define MIN(x, min) (x < min ? min : x) +#define MAX(x, max) (x > max ? max : x) +#define CLAMP(x, min, max) (MIN(MAX(x, max), min)) + +static inline const char* enum_to_string(const char* arr[], s32 count, s32 index) { return (index >= 0 && index < count) ? arr[index] : "undefined"; } +static inline s32 string_to_enum(const char* str, const char* const* arr, s32 n) { for (s32 i=0; i +static inline s32 map_next_id_get(const std::map& map) { + s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id; +} + +template +static inline void map_swap(Map& map, const Key& key1, const Key& key2) { - SDL_Window* window; - SDL_Renderer* renderer; - SDL_GLContext glContext; - vec3 clearColor = {0.69, 0.69, 0.69}; - u64 tick = 0; - u64 lastTick = 0; - bool isRunning = true; -}; + auto it1 = map.find(key1); + auto it2 = map.find(key2); + if (it1 == map.end() || it2 == map.end()) + return; + + using std::swap; + swap(it1->second, it2->second); +} + +#define DEFINE_ENUM_TO_STRING_FN(fn_name, arr, count) \ + static inline const char* fn_name(s32 index) { \ + return enum_to_string(arr, count, index); \ + } + +#define DEFINE_STRING_TO_ENUM_FN(fn_name, enum_type, str_array, count) \ + static inline enum_type fn_name(const char* str) { \ + return (enum_type)string_to_enum(str, str_array, count); \ + } diff --git a/src/PACKED.h b/src/PACKED.h new file mode 100644 index 0000000..d5cd79f --- /dev/null +++ b/src/PACKED.h @@ -0,0 +1,302 @@ +#pragma once + +#include "COMMON.h" + +struct PackedData +{ + const u8* data; + u32 length; +}; + +static const unsigned char texture_eye_closed[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x88, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xb5, 0x52, 0x41, 0x0e, 0xc0, 0x20, 0x08, 0x13, + 0xb2, 0x7f, 0xab, 0x2f, 0xdf, 0xe4, 0xc0, 0xd2, 0x90, 0x82, 0xcb, 0xb2, + 0x71, 0x32, 0xd0, 0x4a, 0xa1, 0xb4, 0xf6, 0x47, 0x8c, 0x31, 0xce, 0x18, + 0x96, 0x63, 0x58, 0x89, 0x09, 0x03, 0x57, 0x9f, 0xcb, 0x8a, 0xf4, 0x03, + 0x24, 0x47, 0x60, 0x56, 0x93, 0x08, 0x98, 0x73, 0xd2, 0xce, 0xbd, 0x77, + 0xaa, 0x44, 0x7d, 0xe6, 0x4a, 0x76, 0x24, 0x23, 0x47, 0x76, 0xdd, 0x91, + 0xec, 0x75, 0xcf, 0x99, 0x0a, 0x7d, 0xda, 0x39, 0x1b, 0x4d, 0x51, 0xfe, + 0x7a, 0xcb, 0x8e, 0x8c, 0x98, 0x9b, 0x1b, 0xbd, 0x46, 0xff, 0xab, 0x1b, + 0xb9, 0x77, 0x60, 0x49, 0xb6, 0xa8, 0x6a, 0x27, 0x56, 0x33, 0x35, 0x92, + 0x1d, 0xd0, 0xce, 0x4e, 0xb7, 0xf1, 0x40, 0x5f, 0xab, 0x2b, 0x44, 0x85, + 0xf4, 0x90, 0xde, 0x9e, 0xb2, 0x32, 0x00, 0x93, 0x6f, 0xb9, 0x48, 0xfe, + 0x24, 0x2e, 0x5d, 0x0c, 0x84, 0xee, 0xa1, 0x31, 0xa2, 0x43, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_eye_closed_len = 214; + +static const unsigned char texture_animation[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x69, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xcd, 0x51, 0x41, 0x0e, 0xc0, 0x20, 0x08, 0x83, + 0xc5, 0x7f, 0x03, 0x2f, 0x77, 0x72, 0x20, 0x61, 0x99, 0x0c, 0x8c, 0xcb, + 0xb2, 0x5e, 0x14, 0x05, 0x4a, 0x0b, 0xc0, 0x97, 0x60, 0xe6, 0x6e, 0xb0, + 0xb7, 0x96, 0x15, 0x59, 0xb2, 0x88, 0xd4, 0x99, 0x8c, 0xc5, 0x33, 0xea, + 0x7d, 0x96, 0xdb, 0x32, 0x26, 0x1c, 0x78, 0x22, 0x9b, 0x4a, 0xc8, 0x8a, + 0x2e, 0xb9, 0xde, 0x20, 0x3d, 0x89, 0x08, 0xfc, 0x24, 0x51, 0x6c, 0x24, + 0xc7, 0x6b, 0x2b, 0xf2, 0xab, 0xa9, 0xac, 0xf3, 0xe6, 0x81, 0x8e, 0x18, + 0x39, 0xbd, 0xdc, 0x35, 0xfb, 0xf3, 0xf1, 0xb6, 0x07, 0xdb, 0x0d, 0xb0, + 0x22, 0x21, 0x90, 0x85, 0xf0, 0x0b, 0x9c, 0x7f, 0x4e, 0x4a, 0x7e, 0x6e, + 0xdb, 0xca, 0xac, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82 +}; +static const unsigned int texture_animation_len = 183; + +static const unsigned char texture_event[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5e, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xcd, 0x91, 0x49, 0x0e, 0x00, 0x21, 0x08, 0x04, + 0x71, 0xe2, 0xbf, 0x81, 0x97, 0xbb, 0x25, 0x24, 0x73, 0x40, 0x6c, 0x2f, + 0x6a, 0x5d, 0xa1, 0x41, 0x0b, 0xa2, 0x13, 0x88, 0x48, 0xe9, 0x78, 0xb5, + 0xbc, 0x0a, 0x5b, 0x50, 0x55, 0xdd, 0x7a, 0x42, 0xc2, 0xa3, 0xb1, 0xb1, + 0x35, 0x00, 0x09, 0x4f, 0x07, 0xa0, 0x61, 0x77, 0xc0, 0x3f, 0x6c, 0xff, + 0x6e, 0x12, 0xd3, 0x96, 0xed, 0x88, 0xa5, 0xf1, 0x88, 0xbe, 0x00, 0x96, + 0x68, 0xcd, 0xcc, 0x4c, 0x91, 0x8f, 0xfb, 0x67, 0xfc, 0xa2, 0xe7, 0x23, + 0x67, 0x9c, 0x6e, 0xf7, 0x84, 0xbd, 0x49, 0x05, 0xbd, 0xc3, 0x78, 0xc4, + 0x32, 0xe6, 0xac, 0x29, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_event_len = 172; + +static const unsigned char texture_eye_open[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x64, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x05, 0x58, 0x41, 0x43, 0x43, + 0xc3, 0x7f, 0x74, 0x00, 0x12, 0x23, 0x4b, 0x23, 0x21, 0x83, 0x18, 0x91, + 0x39, 0x20, 0x05, 0x20, 0xba, 0xb1, 0xb1, 0x11, 0xab, 0x05, 0xf5, 0xf5, + 0xf5, 0x10, 0x4d, 0x40, 0x80, 0xd5, 0x66, 0x10, 0x8d, 0xcc, 0x46, 0xb6, + 0x15, 0x9b, 0x3c, 0xdc, 0x56, 0x74, 0xa7, 0xc1, 0x5c, 0x02, 0x93, 0x43, + 0x16, 0x43, 0xf7, 0x2a, 0x8a, 0x00, 0xb2, 0x01, 0xc8, 0x06, 0x12, 0x92, + 0xc7, 0x08, 0x03, 0x64, 0xdb, 0x51, 0x6c, 0x42, 0x53, 0x83, 0x33, 0x16, + 0xb0, 0x99, 0x8e, 0x4f, 0x8e, 0xe8, 0xa8, 0x24, 0x2a, 0x2d, 0x0c, 0x3d, + 0x00, 0x00, 0xde, 0x2d, 0xf2, 0x3a, 0xa5, 0xc2, 0xad, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_eye_open_len = 178; + +static const unsigned char texture_layer[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x6b, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xb5, 0x90, 0x8b, 0x0a, 0xc0, 0x20, 0x08, 0x45, + 0x33, 0xf6, 0xdf, 0xea, 0x97, 0x57, 0x83, 0x09, 0x19, 0x3e, 0x16, 0x5b, + 0x17, 0xc2, 0x44, 0xcf, 0x35, 0x2b, 0xe5, 0xa4, 0xda, 0xa3, 0xa8, 0x07, + 0x3c, 0xd0, 0x6c, 0x1e, 0x0a, 0x0d, 0xb2, 0x69, 0x96, 0x11, 0xec, 0x80, + 0x96, 0x51, 0xfd, 0xfa, 0x4f, 0xff, 0xac, 0x40, 0x44, 0x0a, 0x44, 0x44, + 0x13, 0x64, 0x66, 0x95, 0x0f, 0x0e, 0xae, 0xa8, 0x51, 0x8c, 0x56, 0xd0, + 0x5d, 0x61, 0x7d, 0x89, 0xa7, 0x7b, 0xb2, 0xdc, 0xeb, 0x0c, 0xcf, 0x85, + 0x0c, 0x54, 0xc3, 0x24, 0xb1, 0xa2, 0x1c, 0xab, 0xee, 0x4d, 0x69, 0x6f, + 0x62, 0xb6, 0xe3, 0x3e, 0x94, 0x99, 0x1d, 0x53, 0x07, 0x98, 0x83, 0x59, + 0xf9, 0x38, 0x5d, 0x6e, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_layer_len = 185; + +static const unsigned char texture_null[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5f, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xb5, 0x52, 0xcb, 0x0a, 0x00, 0x20, 0x08, 0xab, + 0xbe, 0x5c, 0xbf, 0xbc, 0xc7, 0x41, 0xa8, 0x70, 0x91, 0x69, 0x3b, 0x09, + 0xce, 0xa1, 0x9b, 0x29, 0xfd, 0x02, 0x11, 0x55, 0xc1, 0xa8, 0x11, 0x2f, + 0xa3, 0xc6, 0x18, 0x5c, 0x88, 0x1d, 0x1a, 0xaf, 0x78, 0x37, 0x85, 0x02, + 0xcc, 0xac, 0xd6, 0x26, 0x88, 0x07, 0x4f, 0x1b, 0xb8, 0x4f, 0x70, 0x09, + 0x48, 0x84, 0xf3, 0x29, 0xa7, 0x28, 0x61, 0xfe, 0xbb, 0x17, 0x50, 0x64, + 0x1e, 0x42, 0xc6, 0xed, 0x7d, 0x11, 0xcb, 0x96, 0xa7, 0xd1, 0x78, 0x31, + 0x26, 0xde, 0x3e, 0x4d, 0xc8, 0x73, 0x85, 0xa3, 0x01, 0xa9, 0x8a, 0x70, + 0xd3, 0x3d, 0xa4, 0xfe, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_null_len = 173; + +static const unsigned char texture_rect_hide[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x88, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xb5, 0x93, 0x51, 0x0e, 0x80, 0x20, 0x0c, 0x43, + 0x65, 0x17, 0x07, 0x4e, 0xae, 0xc1, 0x64, 0xa6, 0xab, 0x1d, 0x12, 0x8d, + 0xfd, 0x31, 0x91, 0xbe, 0x8d, 0xcd, 0xba, 0x6d, 0x7f, 0xa8, 0xb5, 0xb6, + 0xb3, 0xc6, 0x3b, 0xe5, 0x2d, 0x08, 0x8d, 0x67, 0xad, 0x75, 0x5a, 0xbc, + 0xf7, 0xee, 0xfe, 0x93, 0x35, 0x3c, 0x44, 0xb8, 0x90, 0x94, 0x27, 0xe8, + 0xe9, 0xaa, 0x3c, 0x52, 0x3a, 0xf3, 0x0a, 0x8c, 0x8d, 0x8a, 0x1b, 0xfc, + 0xda, 0x0a, 0xc6, 0xb1, 0xd8, 0x6f, 0xb3, 0x85, 0x29, 0x98, 0x65, 0x5f, + 0xe0, 0xb4, 0xc0, 0x2a, 0x7c, 0x15, 0xf0, 0x6f, 0xcb, 0x8b, 0x44, 0x78, + 0x2c, 0xcd, 0x97, 0x8d, 0x8c, 0x61, 0x28, 0x42, 0xc2, 0x08, 0xce, 0x6e, + 0x10, 0x4c, 0x1c, 0xa4, 0x2c, 0xa1, 0xde, 0xfd, 0x96, 0x05, 0xf5, 0x0f, + 0x64, 0x01, 0x0a, 0x39, 0x50, 0xc5, 0x38, 0xb2, 0xdc, 0x55, 0x8d, 0xfd, + 0x4a, 0x07, 0xf4, 0xc4, 0xc2, 0x2e, 0xb7, 0xd3, 0xf8, 0x61, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_rect_hide_len = 214; + +static const unsigned char texture_rect_show[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5c, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x34, 0xa0, 0xa1, 0xa1, 0xe1, + 0x3f, 0xb1, 0x00, 0xa4, 0x16, 0xc3, 0x00, 0x9c, 0x12, 0x38, 0x2c, 0xc2, + 0x6a, 0x00, 0xb1, 0xae, 0x45, 0x56, 0xcb, 0x44, 0xa9, 0xd7, 0x87, 0xa8, + 0x01, 0x8d, 0x8d, 0x8d, 0x0c, 0x14, 0x05, 0x22, 0x32, 0x60, 0x21, 0x27, + 0xe4, 0x41, 0x80, 0x11, 0x08, 0x88, 0xf6, 0x02, 0x48, 0x33, 0xb2, 0xb3, + 0x41, 0x6c, 0x0c, 0x17, 0xe3, 0x4a, 0x48, 0xc8, 0xe2, 0xb0, 0x94, 0x88, + 0x35, 0x41, 0xa1, 0x27, 0x65, 0x64, 0xc3, 0xd0, 0xf9, 0xc8, 0x9a, 0x19, + 0x29, 0x0d, 0x03, 0x8a, 0x01, 0x00, 0x43, 0x33, 0x93, 0xa2, 0x31, 0x56, + 0x19, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82 +}; +static const unsigned int texture_rect_show_len = 170; + +static const unsigned char texture_root[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x4d, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0xfe, 0xa0, 0xa1, 0xa1, 0xe1, + 0x3f, 0x3e, 0xc0, 0x48, 0xc8, 0x00, 0x90, 0x22, 0x8a, 0x6d, 0x27, 0xdb, + 0x00, 0x98, 0x33, 0xf1, 0xa9, 0x61, 0x22, 0x56, 0x21, 0x5e, 0x03, 0x90, + 0x9d, 0x8c, 0xce, 0x6e, 0x6c, 0x6c, 0x24, 0x2d, 0xa4, 0x49, 0x71, 0x3e, + 0x5e, 0xff, 0x12, 0x6b, 0x00, 0xdc, 0x0b, 0x30, 0xa7, 0x22, 0x6b, 0x24, + 0xc6, 0xf9, 0x8c, 0xf8, 0xe2, 0x9c, 0x11, 0x08, 0x68, 0x12, 0x75, 0x83, + 0x0b, 0x00, 0x00, 0xff, 0xce, 0x78, 0xa9, 0x56, 0xe2, 0xe3, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_root_len = 155; + +static const unsigned char texture_spritesheet[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x72, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xd5, 0x51, 0x41, 0x0e, 0x80, 0x30, 0x08, 0x63, + 0xc6, 0x7f, 0x0f, 0x5e, 0x8e, 0xd3, 0xc8, 0xd2, 0x6d, 0x64, 0xc3, 0x78, + 0x30, 0x36, 0xe1, 0xd2, 0xb4, 0x40, 0x81, 0xe8, 0x2b, 0x30, 0xb3, 0xbe, + 0x32, 0x6a, 0xc1, 0x54, 0x84, 0xc0, 0x69, 0xc8, 0x87, 0x8c, 0xc6, 0xe1, + 0x64, 0x6c, 0xb0, 0xa1, 0x39, 0xe7, 0x4c, 0x22, 0x72, 0xd5, 0x2a, 0xb7, + 0x69, 0x76, 0x34, 0xf7, 0xf0, 0xb8, 0xd3, 0x58, 0xf4, 0xa9, 0x21, 0x35, + 0x00, 0x8c, 0x87, 0xde, 0x1a, 0x21, 0xdd, 0x58, 0x7d, 0xc1, 0xd5, 0x60, + 0xd7, 0x27, 0xd3, 0xeb, 0x0d, 0xbc, 0xbf, 0xda, 0x91, 0xbc, 0x3b, 0xb8, + 0x11, 0x22, 0x31, 0x22, 0x11, 0x87, 0x18, 0xb3, 0xf5, 0x87, 0x0d, 0xfa, + 0xf5, 0xff, 0x81, 0x03, 0xfa, 0x3e, 0xcf, 0x06, 0xd1, 0x47, 0xd3, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_spritesheet_len = 192; + +static const unsigned char texture_trigger[] = +{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5f, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0xd5, 0x92, 0x01, 0x0a, 0x00, 0x20, 0x08, 0x03, + 0xab, 0x97, 0xeb, 0xcb, 0x2d, 0x21, 0x41, 0x64, 0x42, 0x51, 0x10, 0x1d, + 0x44, 0x20, 0x6e, 0xc8, 0xb4, 0x94, 0x5b, 0xc8, 0x64, 0x57, 0xd7, 0x62, + 0x81, 0x88, 0x44, 0x9f, 0xa2, 0x3f, 0x12, 0xc1, 0xba, 0x09, 0xfd, 0x34, + 0xd9, 0x44, 0xb0, 0xee, 0x05, 0x12, 0x30, 0x63, 0x9b, 0xcc, 0x1b, 0xd4, + 0xe8, 0x5a, 0x07, 0x2b, 0x59, 0x68, 0x1f, 0xcc, 0xe0, 0x38, 0xc4, 0x15, + 0x98, 0x39, 0x5f, 0x63, 0xdc, 0x80, 0x00, 0xb6, 0xee, 0xc0, 0x07, 0x97, + 0xad, 0xf5, 0xee, 0x21, 0xfd, 0x67, 0xf0, 0x9e, 0x0e, 0x79, 0xfa, 0x9b, + 0x11, 0x47, 0x7c, 0xbe, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_trigger_len = 173; + +static const unsigned char texture_arrow_up[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x43, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0xa0, 0x25, 0xf8, 0x0f, 0x05, 0xf8, + 0xd4, 0x30, 0xe2, 0xd3, 0x8c, 0xa2, 0x10, 0x08, 0x88, 0x36, 0x00, 0x97, + 0xad, 0xd8, 0x0c, 0x61, 0x24, 0x56, 0x33, 0x2e, 0x43, 0x18, 0x49, 0xd1, + 0x8c, 0xcd, 0x10, 0x46, 0x52, 0x35, 0xa3, 0x1b, 0xc2, 0x48, 0x8e, 0x66, + 0x64, 0x43, 0x28, 0x8e, 0x05, 0x26, 0x4a, 0xd3, 0xca, 0xa8, 0x01, 0x54, + 0x30, 0x80, 0x62, 0x00, 0x00, 0xbc, 0x66, 0x24, 0x0c, 0xf7, 0xc3, 0x33, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82 +}; +static const unsigned int texture_arrow_up_len = 145; +static const unsigned char texture_arrow_down[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c, + 0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x4b, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0xa0, 0x10, 0x30, 0xe2, 0x92, 0xf8, + 0x0f, 0x04, 0x28, 0x0a, 0x81, 0x00, 0x9b, 0x3a, 0x26, 0x4a, 0x5d, 0x30, + 0x6a, 0x00, 0x15, 0x0c, 0x60, 0xc4, 0x16, 0x65, 0x44, 0x6b, 0x46, 0x8e, + 0x5b, 0x52, 0x0d, 0x81, 0xe9, 0x65, 0xc4, 0x97, 0x78, 0x08, 0x69, 0xc6, + 0x9a, 0x12, 0x09, 0x19, 0x82, 0x9e, 0x22, 0x19, 0x89, 0x49, 0xc6, 0xf8, + 0x92, 0x33, 0xc5, 0x79, 0x01, 0x2f, 0xf8, 0x0f, 0x05, 0x0c, 0xb4, 0x04, + 0x00, 0x05, 0x8c, 0x24, 0x0c, 0x43, 0x26, 0x91, 0x46, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +static const unsigned int texture_arrow_down_len = 153; + +#define PACKED_TEXTURE_COUNT (PACKED_TEXTURE_ARROW_DOWN + 1) +enum PackedTextureType +{ + PACKED_TEXTURE_EYE_OPEN, + PACKED_TEXTURE_EYE_CLOSED, + PACKED_TEXTURE_ANIMATION, + PACKED_TEXTURE_EVENT, + PACKED_TEXTURE_LAYER, + PACKED_TEXTURE_NULL, + PACKED_TEXTURE_ROOT, + PACKED_TEXTURE_SPRITESHEET, + PACKED_TEXTURE_TRIGGER, + PACKED_TEXTURE_RECT_SHOW, + PACKED_TEXTURE_RECT_HIDE, + PACKED_TEXTURE_ARROW_UP, + PACKED_TEXTURE_ARROW_DOWN +}; + +static const PackedData PACKED_TEXTURE_DATA[PACKED_TEXTURE_COUNT] = +{ + {texture_eye_open, texture_eye_open_len}, + {texture_eye_closed, texture_eye_closed_len}, + {texture_animation, texture_animation_len}, + {texture_event, texture_event_len}, + {texture_layer, texture_layer_len}, + {texture_null, texture_null_len}, + {texture_root, texture_root_len}, + {texture_spritesheet, texture_spritesheet_len}, + {texture_trigger, texture_trigger_len}, + {texture_rect_show, texture_rect_show_len}, + {texture_rect_hide, texture_rect_hide_len}, + {texture_arrow_up, texture_arrow_up_len}, + {texture_arrow_down, texture_arrow_down_len}, +}; diff --git a/src/RESOURCES.h b/src/RESOURCES.h deleted file mode 100644 index c55b170..0000000 --- a/src/RESOURCES.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#define RESOURCE_SHADER_VERTEX_TEXTURE_QUAD "res/shader/texture_quad.vs" -#define RESOURCE_SHADER_FRAGMENT_TEXTURE_QUAD "res/shader/texture_quad.fs" diff --git a/src/STRINGS.h b/src/STRINGS.h index 1fd5b3d..48c4a3d 100644 --- a/src/STRINGS.h +++ b/src/STRINGS.h @@ -1,13 +1,18 @@ #pragma once +#define STRING_UNDEFINED "undefined" +#define STRING_EMPTY "" + #define STRING_WINDOW_TITLE "Anm2Ed" +#define STRING_WINDOW_TITLE_EDITING "Anm2Ed (%s)" #define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL (%s)\n" #define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n" #define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %s\n" -#define STRING_ERROR_SHADER_INIT "[ERROR] Could not initialize shader: %s\n%s" -#define STRING_ERROR_TEXTURE_INIT "[ERROR] Could not initialize texture: %s (%s)\n" -#define STRING_ERROR_ANM2_INIT "[ERROR] Could not initialize anm2: %s (%s)\n" +#define STRING_ERROR_SHADER_INIT "[ERROR] Could not initialize shader: %i\n%s" +#define STRING_ERROR_TEXTURE_INIT "[ERROR] Could not initialize texture: %s\n" +#define STRING_ERROR_ANM2_READ "[ERROR] Could not read anm2 from file %s: %s\n" +#define STRING_ERROR_ANM2_WRITE "[ERROR] Could not write anm2 to file %s: %s\n" #define STRING_INFO_INIT "[INFO] Initializing\n" #define STRING_INFO_QUIT "[INFO] Exited\n" @@ -16,11 +21,201 @@ #define STRING_INFO_GLEW_INIT "[INFO] Initialized GLEW\n" #define STRING_INFO_GL_CONTEXT_INIT "[INFO] Initialized OpenGL context\n" #define STRING_INFO_FILE_READ "[INFO] Read from file: %s\n" -#define STRING_INFO_SHADER_INIT "[INFO] Initialized shader: %s & %s\n" +#define STRING_INFO_SHADER_INIT "[INFO] Initialized shader: %i\n" #define STRING_INFO_TEXTURE_INIT "[INFO] Initialized texture: %s\n" -#define STRING_INFO_ANM2_INIT "[INFO] Initialized anm2: %s\n" +#define STRING_INFO_ANM2_WRITE "[INFO] Wrote anm2 to file: %s\n" +#define STRING_INFO_ANM2_READ "[INFO] Read anm2 from file: %s\n" #define STRING_INFO_IMGUI_INIT "[INFO] Initialized Dear Imgui\n" #define STRING_INFO_IMGUI_FREE "[INFO] Freed Dear Imgui\n" +#define STRING_DIALOG_ANM2_READ "Select .anm2 file to read..." +#define STRING_DIALOG_ANM2_WRITE "Select .anm2 file to write to..." +#define STRING_DIALOG_ANM2_FILTER "Anm2 Files (*.anm2)" +#define STRING_DIALOG_ANM2_FILTER_WILDCARD "*.anm2" +#define STRING_DIALOG_ALL_FILTER "All Files (*.*)" +#define STRING_DIALOG_ALL_FILTER_WILDCARD "*" + +#define STRING_ANM2_CREATED_BY_DEFAULT "Unknown" +#define STRING_ANM2_NEW_ANIMATION "New Animation" +#define STRING_ANM2_NEW_LAYER "New Layer" +#define STRING_ANM2_NEW_NULL "New Null" +#define STRING_ANM2_NEW_EVENT "New Event" +#define STRING_ANM2_ROOT "Root" + +#define STRING_IMGUI_WINDOW "Window" +#define STRING_IMGUI_DOCKSPACE "Dockspace" +#define STRING_IMGUI_TASKBAR "Taskbar" +#define STRING_IMGUI_TASKBAR_FILE "File" + +#define STRING_IMGUI_FILE_MENU "File Menu" +#define STRING_IMGUI_FILE_NEW "New" +#define STRING_IMGUI_FILE_OPEN "Open" +#define STRING_IMGUI_FILE_SAVE "Save" +#define STRING_IMGUI_FILE_SAVE_AS "Save As" + +#define STRING_IMGUI_PROPERTIES "Properties" +#define STRING_IMGUI_PROPERTIES_FPS "FPS" +#define STRING_IMGUI_PROPERTIES_FPS_LABEL "##FPS" +#define STRING_IMGUI_PROPERTIES_CREATED_BY "Created by:" +#define STRING_IMGUI_PROPERTIES_CREATED_BY_LABEL "##Created By" +#define STRING_IMGUI_PROPERTIES_CREATED_ON "Created on: %s" +#define STRING_IMGUI_PROPERTIES_VERSION "Version: %i" + +#define STRING_IMGUI_ANIMATIONS "Animations" +#define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation" +#define STRING_IMGUI_ANIMATIONS_ADD "Add" +#define STRING_IMGUI_ANIMATIONS_REMOVE "Remove" +#define STRING_IMGUI_ANIMATIONS_DUPLICATE "Duplicate" +#define STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT "Set as Default" +#define STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT "%s (*)" + +#define STRING_IMGUI_EVENTS "Events" +#define STRING_IMGUI_EVENTS_EVENT_LABEL "##Event" +#define STRING_IMGUI_EVENTS_ADD "Add" +#define STRING_IMGUI_EVENTS_REMOVE "Remove" +#define STRING_IMGUI_EVENT_FORMAT "#%i %s" + +#define STRING_IMGUI_SPRITESHEETS "Spritesheets" +#define STRING_IMGUI_SPRITESHEETS_ADD "Add" +#define STRING_IMGUI_SPRITESHEETS_REMOVE "Remove" +#define STRING_IMGUI_SPRITESHEETS_RELOAD "Reload" +#define STRING_IMGUI_SPRITESHEETS_REPLACE "Replace" +#define STRING_IMGUI_SPRITESHEET_FORMAT "#%i %s" + +#define STRING_IMGUI_FRAME_PROPERTIES "Frame Properties" +#define STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION "Crop Position" +#define STRING_IMGUI_FRAME_PROPERTIES_CROP_SIZE "Crop Size" +#define STRING_IMGUI_FRAME_PROPERTIES_POSITION "Position" +#define STRING_IMGUI_FRAME_PROPERTIES_PIVOT "Pivot" +#define STRING_IMGUI_FRAME_PROPERTIES_SCALE "Scale" +#define STRING_IMGUI_FRAME_PROPERTIES_ROTATION "Rotation" +#define STRING_IMGUI_FRAME_PROPERTIES_VISIBLE "Visible" +#define STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED "Interpolated" +#define STRING_IMGUI_FRAME_PROPERTIES_DURATION "Duration" +#define STRING_IMGUI_FRAME_PROPERTIES_TINT "Tint" +#define STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET "Color Offset" + +#define STRING_IMGUI_ANIMATION_PREVIEW "Animation Preview" +#define STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS "##Animation Preview Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS "##Grid Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_GRID "Grid" +#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE "##Grid Size" +#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR "Color" +#define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS "##Zoom Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM "Zoom" +#define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS "##Background Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR "Background Color" + +#define STRING_IMGUI_SPRITESHEET_EDITOR "Spritesheet Editor" + +#define STRING_IMGUI_TIMELINE "Timeline" +#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE "Shift Above" +#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW "Shift Below" +#define STRING_IMGUI_TIMELINE_ROOT "Root" +#define STRING_IMGUI_TIMELINE_ELEMENT_FORMAT "#%i %s" +#define STRING_IMGUI_TIMELINE_SPRITESHEET_FORMAT "#%i" +#define STRING_IMGUI_TIMELINE_SPRITESHEET_UNKNOWN "#?" +#define STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL "##Timeline Element Spritesheet ID" +#define STRING_IMGUI_TIMELINE_ELEMENT_NAME_LABEL "##Timeline Element Name" +#define STRING_IMGUI_TIMELINE_TRIGGERS "Triggers" +#define STRING_IMGUI_TIMELINE_ANIMATION_LABEL "##Timeline Animation" +#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ARROWS_LABEL "##Timeline Shift Arrows" +#define STRING_IMGUI_TIMELINE_ANIMATIONS "Animations" +#define STRING_IMGUI_TIMELINE_PLAY "|> Play" +#define STRING_IMGUI_TIMELINE_PAUSE "|| Pause" +#define STRING_IMGUI_TIMELINE_ADD_FRAME "+ Add Frame" +#define STRING_IMGUI_TIMELINE_REMOVE_FRAME "- Remove Frame" +#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "= Fit Animation Length" +#define STRING_IMGUI_TIMELINE_ANIMATION LENGTH "Animation Length:" +#define STRING_IMGUI_TIMELINE_VISIBLE "Visible" +#define STRING_IMGUI_TIMELINE_LAYER "Layer" +#define STRING_IMGUI_TIMELINE_NULL "Null" +#define STRING_IMGUI_TIMELINE_RECT "Rect" +#define STRING_IMGUI_TIMELINE_ELEMENTS "Elements" +#define STRING_IMGUI_TIMELINE_ELEMENT_ADD "Add" +#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU "Element Add Menu" +#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER "Add Layer..." +#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL "Add Null..." +#define STRING_IMGUI_TIMELINE_ELEMENT_REMOVE "Remove" + +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT "Select the animation to edit.\nYou can also click the name to edit it." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD "Add a new animation." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE "Duplicates the selected animation." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT "Sets the selected animation as the default.\nDefault animations are marked with \"(*)\"." +#define STRING_IMGUI_TOOLTIP_EVENTS_SELECT "Set the event for the trigger, or rename it." +#define STRING_IMGUI_TOOLTIP_EVENTS_ADD "Add a new event." +#define STRING_IMGUI_TOOLTIP_EVENTS_REMOVE "Removes the selected event." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD "Opens the file dialog to load in a new sprite." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE "Removes the selected spritesheet." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD "Reloads the selected spritesheet." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE "Replaces the selected spritesheet; opens up the file dialog." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_FPS "Change the FPS of the animation." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY "Change the author of the animation." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_ON_NOW "Set the date of creation to the current system time." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION "Change the position of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION "Change the crop position of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE "Change the crop size of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT "Change the pivot of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE "Change the scale of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION "Change the rotation of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION "Change the duration of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT "Change the tint of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET "Change the color offset of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE "Toggles the visibility of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED "Toggles the interpolation of the frame.\nBetween keyframes, will transform the values in the in-betweens to be smooth." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD "Add a layer or null timeline element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NAME "Click to rename the element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET "Click to change the spritesheet the layer is using." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE "Remove a timeline element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP "Shift this timeline element above.\nElements with higher indices will display further behind." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN "Shift this timeline element below.\nElements with lower indices will display further in front." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT "Toggle visibility for a rectangle around the null." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT "This is the Root element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER "This is a Layer element.\nThese are the main graphical animation elements.\nClick to rename." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NULL "This is a Null element.\nThese are invisible elements where a game engine may have access to them for additional effects.\nClick to rename." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event." +#define STRING_IMGUI_TOOLTIP_TIMELINE_PLAY "Plays the animation." +#define STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE "Pauses the animation." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID "Toggles grid visibility on the animation preview." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR "Changes the animation preview grid color." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the animation preview grid size (number of rows/columns)." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview." + #define STRING_OPENGL_VERSION "#version 330" + +#define SHADER_VERTEX "#version 330 core\n" \ +"in vec2 l_position;\n" \ +"in vec2 l_uv;\n" \ +"\n" \ +"uniform mat3 u_view;\n" \ +"uniform mat3 u_projection;\n" \ +"uniform mat3 u_model;\n" \ +"\n" \ +"out vec2 o_uv;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" gl_Position = vec4(u_projection * u_view * u_model * vec3(l_position, 1.0), 1.0);\n" \ +" o_uv = l_uv;\n" \ +"}" + +#define SHADER_FRAGMENT "#version 330 core\n" \ +"uniform vec4 u_tint;\n" \ +"uniform vec4 u_color_offset;\n" \ +"uniform sampler2D u_texture;\n" \ +"\n" \ +"in vec2 i_uv;\n" \ +"\n" \ +"out vec4 FragColor;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" FragColor = texture(u_texture, i_uv) * u_tint;\n" \ +" FragColor.rgb += u_color_offset.rgb;\n" \ +"\n" \ +" if (FragColor.a == 0.0) discard;\n" \ +"}\n" diff --git a/src/anm2.cpp b/src/anm2.cpp index 22545c8..c8589eb 100644 --- a/src/anm2.cpp +++ b/src/anm2.cpp @@ -1,32 +1,722 @@ #include "anm2.h" -bool -anm2_init(Anm2* self, const char* path) +using namespace tinyxml2; + +/* Sets the anm2's date to the system's current date */ +void +anm2_created_on_set(Anm2* self) { - xml_document document; - xml_parse_result result; + time_t currentTime; + struct tm* local; + char date[ANM2_STRING_MAX]; - memset(self, '\0', sizeof(Anm2)); + currentTime = time(NULL); + local = localtime(¤tTime); - result = document.load_file(path); + strftime(date, ANM2_STRING_MAX, "%d-%B-%Y %I:%M:%S %p", local); + + strncpy(self->createdOn, date, ANM2_STRING_MAX); +} - if (!result) +/* Serializes the anm2 struct into XML and exports it to the given path */ +bool +anm2_serialize(Anm2* self, const char* path) +{ + XMLDocument document; + XMLError error; + + XMLElement* animatedActorElement; + XMLElement* infoElement; + XMLElement* contentElement; + XMLElement* spritesheetsElement; + XMLElement* layersElement; + XMLElement* nullsElement; + XMLElement* eventsElement; + XMLElement* animationsElement; + + if (!self || !path) + return false; + + /* Update creation date on first version */ + if (self->version == 0) + anm2_created_on_set(self); + + /* Increment anm2's version */ + self->version++; + + /* Set the anm2's date to the system time */ + + /* AnimatedActor */ + animatedActorElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); + document.InsertFirstChild(animatedActorElement); + + /* Info */ + infoElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_INFO]); + infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_BY], self->createdBy); /* CreatedBy */ + infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_ON], self->createdOn); /* CreatedOn */ + infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VERSION], self->version); /* Version; note its incrementation */ + infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FPS], self->fps); /* FPS */ + animatedActorElement->InsertEndChild(infoElement); + + /* Content */ + contentElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_CONTENT]); + + /* Spritesheets */ + spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]); + + for (auto & [id, spritesheet] : self->spritesheets) { - printf(STRING_ERROR_ANM2_INIT, path, result.description()); + XMLElement* spritesheetElement; + + /* Spritesheet */ + spritesheetElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]); + spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_PATH], spritesheet.path); /* Path */ + spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */ + spritesheetsElement->InsertEndChild(spritesheetElement); + } + + contentElement->InsertEndChild(spritesheetsElement); + + /* Layers */ + layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]); + + for (auto & [id, layer] : self->layers) + { + XMLElement* layerElement; + + /* Layer */ + layerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]); + layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], layer.name); /* Path */ + layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */ + layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SPRITESHEET_ID], layer.spritesheetID); /* SpritesheetId */ + + layersElement->InsertEndChild(layerElement); + } + + contentElement->InsertEndChild(layersElement); + + /* Nulls */ + nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]); + + for (auto & [id, null] : self->nulls) + { + XMLElement* nullElement; + + /* Null */ + nullElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]); + nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], null.name); /* Name */ + nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */ + + /* special case; only serialize if this is true */ + if (null.isShowRect) + nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); /* ShowRect */ + nullsElement->InsertEndChild(nullElement); + } + + contentElement->InsertEndChild(nullsElement); + + /* Events */ + eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]); + + for (auto & [id, event] : self->events) + { + XMLElement* eventElement; + + /* Event */ + eventElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]); + eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], event.name); /* Name */ + eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */ + eventsElement->InsertEndChild(eventElement); + } + + contentElement->InsertEndChild(eventsElement); + + animatedActorElement->InsertEndChild(contentElement); + + /* Animations */ + animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]); + animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->defaultAnimation); + + for (auto & [id, animation] : self->animations) + { + XMLElement* animationElement; + XMLElement* rootAnimationElement; + XMLElement* layerAnimationsElement; + XMLElement* nullAnimationsElement; + XMLElement* triggersElement; + + /* Animation */ + animationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]); + + animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], animation.name); /* Name */ + animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FRAME_NUM], animation.frameNum); /* FrameNum */ + animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LOOP], animation.isLoop); /* Loop */ + + /* RootAnimation */ + rootAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION]); + + for (auto & frame : animation.rootAnimation.frames) + { + XMLElement* frameElement; + + /* Frame */ + frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); + + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */ + + rootAnimationElement->InsertEndChild(frameElement); + } + + animationElement->InsertEndChild(rootAnimationElement); + + /* LayerAnimations */ + layerAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]); + + for (const auto & [layerID, layerAnimation] : animation.layerAnimations) + { + XMLElement* layerAnimationElement; + + /* LayerAnimation */ + layerAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]); + layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], layerID); /* LayerID */ + layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); /* Visible */ + + for (auto & frame : layerAnimation.frames) + { + XMLElement* frameElement; + + /* Frame */ + frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); + + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_CROP], frame.crop.x); /* XCrop */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_CROP], frame.crop.y); /* YCrop */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_WIDTH], frame.size.x); /* Width */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_HEIGHT], frame.size.y); /* Height */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */ + + layerAnimationElement->InsertEndChild(frameElement); + } + + layerAnimationsElement->InsertEndChild(layerAnimationElement); + } + + animationElement->InsertEndChild(layerAnimationsElement); + + /* NullAnimations */ + nullAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]); + + for (const auto & [nullID, nullAnimation] : animation.nullAnimations) + { + XMLElement* nullAnimationElement; + + /* NullAnimation */ + nullAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]); + nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], nullID); /* NullID */ + nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], nullAnimation.isVisible); /* Visible */ + + for (auto & frame : nullAnimation.frames) + { + XMLElement* frameElement; + + /* Frame */ + frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); + + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */ + frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */ + + nullAnimationElement->InsertEndChild(frameElement); + } + + nullAnimationsElement->InsertEndChild(nullAnimationElement); + } + + animationElement->InsertEndChild(nullAnimationsElement); + + /* Triggers */ + triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]); + + for (auto & trigger : animation.triggers.items) + { + XMLElement* triggerElement; + + /* Trigger */ + triggerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]); + triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], trigger.eventID); /* EventID */ + triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], trigger.atFrame); /* AtFrame */ + triggersElement->InsertEndChild(triggerElement); + } + + animationElement->InsertEndChild(triggersElement); + + animationsElement->InsertEndChild(animationElement); + } + + animatedActorElement->InsertEndChild(animationsElement); + + error = document.SaveFile(path); + + if (error != XML_SUCCESS) + { + printf(STRING_ERROR_ANM2_WRITE, path, document.ErrorStr()); return false; } - for (pugi::xml_node node : document.children()) - printf("%s\n", node.name()); + printf(STRING_INFO_ANM2_WRITE, path); + strncpy(self->path, path, PATH_MAX - 1); + + return true; +} - printf(STRING_INFO_ANM2_INIT, path); +/* Loads the .anm2 file and deserializes it into the struct equivalent */ +bool +anm2_deserialize(Anm2* self, const char* path) +{ + XMLDocument document; + XMLError error; + const XMLElement* element; + const XMLElement* root; + Anm2Spritesheet* lastSpritesheet = NULL; + Anm2Layer* lastLayer = NULL; + Anm2Null* lastNull = NULL; + Anm2Event* lastEvent = NULL; + Anm2Animation* lastAnimation = NULL; + Anm2LayerAnimation* lastLayerAnimation = NULL; + Anm2NullAnimation* lastNullAnimation = NULL; + Anm2Frame* lastFrame = NULL; + Anm2Trigger* lastTrigger = NULL; + Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR; + Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID; + Anm2AnimationType animationType = ANM2_ROOT_ANIMATION; + Anm2Null tempNull; + Anm2Layer tempLayer; + Anm2Spritesheet tempSpritesheet; + Anm2Event tempEvent; + + *self = Anm2{}; + + error = document.LoadFile(path); + + if (error != XML_SUCCESS) + { + printf(STRING_ERROR_ANM2_READ, path, document.ErrorStr()); + return false; + } + + strncpy(self->path, path, PATH_MAX - 1); + + root = document.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); + element = root; + + while (element) + { + const XMLAttribute* attribute; + const XMLElement* child; + s32 id; + + /* Elements */ + anm2Element = anm2_element_from_string(element->Name()); + + switch (anm2Element) + { + case ANM2_ELEMENT_SPRITESHEET: + lastSpritesheet = &tempSpritesheet; + break; + case ANM2_ELEMENT_LAYER: + lastLayer = &tempLayer; + break; + case ANM2_ELEMENT_NULL: + lastNull = &tempNull; + break; + case ANM2_ELEMENT_EVENT: + lastEvent = &tempEvent; + break; + case ANM2_ELEMENT_ANIMATION: + id = map_next_id_get(self->animations); + self->animations[id] = Anm2Animation{}; + lastAnimation = &self->animations[id]; + break; + case ANM2_ELEMENT_ROOT_ANIMATION: + animationType = ANM2_ROOT_ANIMATION; + break; + case ANM2_ELEMENT_LAYER_ANIMATION: + animationType = ANM2_LAYER_ANIMATION; + lastLayerAnimation = NULL; + break; + case ANM2_ELEMENT_NULL_ANIMATION: + animationType = ANM2_NULL_ANIMATION; + lastNullAnimation = NULL; + break; + case ANM2_ELEMENT_FRAME: + switch (animationType) + { + case ANM2_ROOT_ANIMATION: + lastAnimation->rootAnimation.frames.push_back(Anm2Frame{}); + lastFrame = &lastAnimation->rootAnimation.frames.back(); + break; + case ANM2_LAYER_ANIMATION: + if (!lastLayerAnimation) break; + lastLayerAnimation->frames.push_back(Anm2Frame{}); + lastFrame = &lastLayerAnimation->frames.back(); + break; + case ANM2_NULL_ANIMATION: + if (!lastNullAnimation) break; + lastNullAnimation->frames.push_back(Anm2Frame{}); + lastFrame = &lastNullAnimation->frames.back(); + break; + default: + break; + } + break; + case ANM2_ELEMENT_TRIGGER: + lastAnimation->triggers.items.push_back(Anm2Trigger{}); + lastTrigger = &lastAnimation->triggers.items.back(); + break; + default: + break; + } + + + /* Attributes */ + attribute = element->FirstAttribute(); + + while (attribute) + { + anm2Attribute = anm2_attribute_from_string(attribute->Name()); + + switch (anm2Attribute) + { + case ANM2_ATTRIBUTE_CREATED_BY: + strncpy(self->createdBy, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ATTRIBUTE_CREATED_ON: + strncpy(self->createdOn, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ATTRIBUTE_VERSION: + self->version = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_FPS: + self->fps = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_ID: + id = atoi(attribute->Value()); + switch (anm2Element) + { + case ANM2_ELEMENT_SPRITESHEET: + self->spritesheets[id] = tempSpritesheet; + lastSpritesheet = &self->spritesheets[id]; + break; + case ANM2_ELEMENT_LAYER: + self->layers[id] = tempLayer; + lastLayer = &self->layers[id]; + break; + case ANM2_ELEMENT_NULL: + self->nulls[id] = tempNull; + lastNull = &self->nulls[id]; + break; + case ANM2_ELEMENT_EVENT: + self->events[id] = tempEvent; + lastEvent = &self->events[id]; + break; + default: + break; + } + break; + case ANM2_ATTRIBUTE_PATH: + strncpy(lastSpritesheet->path, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ATTRIBUTE_NAME: + switch (anm2Element) + { + case ANM2_ELEMENT_LAYER: + strncpy(lastLayer->name, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ELEMENT_NULL: + strncpy(lastNull->name, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ELEMENT_ANIMATION: + strncpy(lastAnimation->name, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ELEMENT_EVENT: + strncpy(lastEvent->name, attribute->Value(), ANM2_STRING_MAX - 1); + break; + default: + break; + } + break; + case ANM2_ATTRIBUTE_SPRITESHEET_ID: + lastLayer->spritesheetID = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_SHOW_RECT: + switch (anm2Element) + { + case ANM2_ELEMENT_NULL: + lastNull->isShowRect = string_to_bool(attribute->Value()); + break; + default: + break; + } + break; + case ANM2_ATTRIBUTE_DEFAULT_ANIMATION: + strncpy(self->defaultAnimation, attribute->Value(), ANM2_STRING_MAX - 1); + break; + case ANM2_ATTRIBUTE_FRAME_NUM: + lastAnimation->frameNum = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_LOOP: + lastAnimation->isLoop = string_to_bool(attribute->Value()); + break; + case ANM2_ATTRIBUTE_X_POSITION: + lastFrame->position.x = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_Y_POSITION: + lastFrame->position.y = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_X_PIVOT: + lastFrame->pivot.x = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_Y_PIVOT: + lastFrame->pivot.y = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_X_CROP: + lastFrame->crop.x = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_Y_CROP: + lastFrame->crop.y = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_WIDTH: + lastFrame->size.x = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_HEIGHT: + lastFrame->size.y = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_X_SCALE: + lastFrame->scale.x = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_Y_SCALE: + lastFrame->scale.y = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_DELAY: + lastFrame->delay = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_VISIBLE: + switch (anm2Element) + { + case ANM2_ELEMENT_FRAME: + lastFrame->isVisible = string_to_bool(attribute->Value()); + break; + case ANM2_LAYER_ANIMATION: + lastLayerAnimation->isVisible = string_to_bool(attribute->Value()); + break; + case ANM2_NULL_ANIMATION: + lastNullAnimation->isVisible = string_to_bool(attribute->Value()); + break; + default: + break; + } + break; + case ANM2_ATTRIBUTE_RED_TINT: + lastFrame->tintRGBA.r = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_GREEN_TINT: + lastFrame->tintRGBA.g = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_BLUE_TINT: + lastFrame->tintRGBA.b = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_ALPHA_TINT: + lastFrame->tintRGBA.a = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_RED_OFFSET: + lastFrame->offsetRGB.r = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_GREEN_OFFSET: + lastFrame->offsetRGB.g = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_BLUE_OFFSET: + lastFrame->offsetRGB.b = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_ROTATION: + lastFrame->rotation = atof(attribute->Value()); + break; + case ANM2_ATTRIBUTE_INTERPOLATED: + lastFrame->isInterpolated = string_to_bool(attribute->Value()); + break; + case ANM2_ATTRIBUTE_LAYER_ID: + id = atoi(attribute->Value()); + lastAnimation->layerAnimations[id] = Anm2LayerAnimation{}; + lastLayerAnimation = &lastAnimation->layerAnimations[id]; + break; + case ANM2_ATTRIBUTE_NULL_ID: + id = atoi(attribute->Value()); + lastAnimation->nullAnimations[id] = Anm2NullAnimation{}; + lastNullAnimation = &lastAnimation->nullAnimations[id]; + break; + case ANM2_ATTRIBUTE_EVENT_ID: + lastTrigger->eventID = atoi(attribute->Value()); + break; + case ANM2_ATTRIBUTE_AT_FRAME: + lastTrigger->atFrame = atoi(attribute->Value()); + break; + default: + break; + } + + attribute = attribute->Next(); + } + + /* Iterate through children */ + child = element->FirstChildElement(); + + if (child) + { + element = child; + continue; + } + + /* Iterate through siblings */ + while (element) + { + const XMLElement* next; + + next = element->NextSiblingElement(); + + if (next) + { + element = next; + break; + } + + /* If no siblings, return to parent. If no parent, end parsing */ + element = element->Parent() ? element->Parent()->ToElement() : NULL; + } + } + + printf(STRING_INFO_ANM2_READ, path); return true; } +/* Adds a new layer to the anm2 */ void -anm2_free(Anm2* self) +anm2_layer_add(Anm2* self) { + s32 id = map_next_id_get(self->layers); + self->layers[id] = Anm2Layer{}; + for (auto & [animationID, animation] : self->animations) + animation.layerAnimations[id] = Anm2LayerAnimation{}; } + +/* Removes a layer from the anm2 given the index/id */ +void +anm2_layer_remove(Anm2* self, s32 id) +{ + self->layers.erase(id); + + for (auto& animationPair : self->animations) + animationPair.second.layerAnimations.erase(id); +} + +/* Adds a new null to the anm2 */ +void +anm2_null_add(Anm2* self) +{ + s32 id = map_next_id_get(self->nulls); + + self->nulls[id] = Anm2Null{}; + + for (auto & [animationID, animation] : self->animations) + animation.nullAnimations[id] = Anm2NullAnimation{}; +} + +/* Removes a null from the anm2 given the index/id */ +void +anm2_null_remove(Anm2* self, s32 id) +{ + self->nulls.erase(id); + + for (auto& animationPair : self->animations) + animationPair.second.nullAnimations.erase(id); +} + +/* Adds a new animation to the anm2, makes sure to keep the layeranimations/nullsanimation check */ +s32 +anm2_animation_add(Anm2* self) +{ + s32 id = map_next_id_get(self->animations); + Anm2Animation animation; + + /* match layers */ + for (auto & [layerID, layer] : self->layers) + animation.layerAnimations[layerID] = Anm2LayerAnimation{}; + + /* match nulls */ + for (auto & [nullID, null] : self->nulls) + animation.nullAnimations[nullID] = Anm2NullAnimation{}; + + self->animations[id] = animation; + + return id; +} + +void +anm2_animation_remove(Anm2* self, s32 id) +{ + self->animations.erase(id); +} + +/* Makes an entirely new anm2 */ +void +anm2_new(Anm2* self) +{ + *self = Anm2{}; + anm2_created_on_set(self); +} \ No newline at end of file diff --git a/src/anm2.h b/src/anm2.h index 1c89559..aa9c93e 100644 --- a/src/anm2.h +++ b/src/anm2.h @@ -5,285 +5,216 @@ #define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f) #define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f) -#define ANM2_STRING_MAX 255 +#define ANM2_STRING_MAX 0xFF +#define ANM2_STRING_FORMATTED_MAX 0xFFF +#define ANM2_BUFFER_MAX 0xFFFFF +#define ANM2_FPS_MIN 0 +#define ANM2_FPS_DEFAULT 30 +#define ANM2_FPS_MAX 60 /* Elements */ -#define STRING_ANM2_ELEMENT_ANIMATED_ACTOR "AnimatedActor" -#define STRING_ANM2_ELEMENT_INFO "Info" -#define STRING_ANM2_ELEMENT_CONTENT "Content" -#define STRING_ANM2_ELEMENT_SPRITESHEETS "Spritesheets" -#define STRING_ANM2_ELEMENT_SPRITESHEET "Spritesheet" -#define STRING_ANM2_ELEMENT_LAYERS "Layers" -#define STRING_ANM2_ELEMENT_LAYER "Layer" -#define STRING_ANM2_ELEMENT_NULLS "Nulls" -#define STRING_ANM2_ELEMENT_NULL "Null" -#define STRING_ANM2_ELEMENT_EVENTS "Events" -#define STRING_ANM2_ELEMENT_EVENT "Event" -#define STRING_ANM2_ELEMENT_ANIMATIONS "Animations" -#define STRING_ANM2_ELEMENT_ANIMATION "Animation" -#define STRING_ANM2_ELEMENT_ROOT_ANIMATION "RootAnimation" -#define STRING_ANM2_ELEMENT_FRAME "Frame" -#define STRING_ANM2_ELEMENT_LAYER_ANIMATIONS "LayerAnimations" -#define STRING_ANM2_ELEMENT_LAYER_ANIMATION "LayerAnimation" -#define STRING_ANM2_ELEMENT_NULL_ANIMATIONS "NullAnimations" -#define STRING_ANM2_ELEMENT_NULL_ANIMATION "NullAnimation" -#define STRING_ANM2_ELEMENT_TRIGGERS "Triggers" -#define STRING_ANM2_ELEMENT_TRIGGER "Trigger" +#define ANM2_ELEMENT_LIST \ + X(ANIMATED_ACTOR, "AnimatedActor") \ + X(INFO, "Info") \ + X(CONTENT, "Content") \ + X(SPRITESHEETS, "Spritesheets") \ + X(SPRITESHEET, "Spritesheet") \ + X(LAYERS, "Layers") \ + X(LAYER, "Layer") \ + X(NULLS, "Nulls") \ + X(NULL, "Null") \ + X(EVENTS, "Events") \ + X(EVENT, "Event") \ + X(ANIMATIONS, "Animations") \ + X(ANIMATION, "Animation") \ + X(ROOT_ANIMATION, "RootAnimation") \ + X(FRAME, "Frame") \ + X(LAYER_ANIMATIONS, "LayerAnimations") \ + X(LAYER_ANIMATION, "LayerAnimation") \ + X(NULL_ANIMATIONS, "NullAnimations") \ + X(NULL_ANIMATION, "NullAnimation") \ + X(TRIGGERS, "Triggers") \ + X(TRIGGER, "Trigger") -/* Attributes */ -#define STRING_ANM2_ATTRIBUTE_CREATED_BY "CreatedBy" -#define STRING_ANM2_ATTRIBUTE_CREATED_ON "CreatedOn" -#define STRING_ANM2_ATTRIBUTE_FPS "Fps" -#define STRING_ANM2_ATTRIBUTE_VERSION "Version" -#define STRING_ANM2_ATTRIBUTE_ID "Id" -#define STRING_ANM2_ATTRIBUTE_PATH "Path" -#define STRING_ANM2_ATTRIBUTE_NAME "Name" -#define STRING_ANM2_ATTRIBUTE_SPRITESHEET_ID "SpritesheetId" -#define STRING_ANM2_ATTRIBUTE_SHOW_RECT "ShowRect" -#define STRING_ANM2_ATTRIBUTE_DEFAULT_ANIMATION "DefaultAnimation" -#define STRING_ANM2_ATTRIBUTE_FRAME_NUM "FrameNum" -#define STRING_ANM2_ATTRIBUTE_LOOP "Loop" -#define STRING_ANM2_ATTRIBUTE_X_POSITION "XPosition" -#define STRING_ANM2_ATTRIBUTE_Y_POSITION "YPosition" -#define STRING_ANM2_ATTRIBUTE_X_PIVOT "XPivot" -#define STRING_ANM2_ATTRIBUTE_Y_PIVOT "YPivot" -#define STRING_ANM2_ATTRIBUTE_X_CROP "XCrop" -#define STRING_ANM2_ATTRIBUTE_Y_CROP "YCrop" -#define STRING_ANM2_ATTRIBUTE_WIDTH "Width" -#define STRING_ANM2_ATTRIBUTE_HEIGHT "Height" -#define STRING_ANM2_ATTRIBUTE_X_SCALE "XScale" -#define STRING_ANM2_ATTRIBUTE_Y_SCALE "YScale" -#define STRING_ANM2_ATTRIBUTE_DELAY "Delay" -#define STRING_ANM2_ATTRIBUTE_VISIBLE "Visible" -#define STRING_ANM2_ATTRIBUTE_RED_TINT "RedTint" -#define STRING_ANM2_ATTRIBUTE_GREEN_TINT "GreenTint" -#define STRING_ANM2_ATTRIBUTE_BLUE_TINT "BlueTint" -#define STRING_ANM2_ATTRIBUTE_ALPHA_TINT "AlphaTint" -#define STRING_ANM2_ATTRIBUTE_RED_OFFSET "RedOffset" -#define STRING_ANM2_ATTRIBUTE_GREEN_OFFSET "GreenOffset" -#define STRING_ANM2_ATTRIBUTE_BLUE_OFFSET "BlueOffset" -#define STRING_ANM2_ATTRIBUTE_ROTATION "Rotation" -#define STRING_ANM2_ATTRIBUTE_INTERPOLATED "Interpolated" -#define STRING_ANM2_ATTRIBUTE_LAYER_ID "LayerId" -#define STRING_ANM2_ATTRIBUTE_NULL_ID "NullId" -#define STRING_ANM2_ATTRIBUTE_EVENT_ID "EventId" -#define STRING_ANM2_ATTRIBUTE_AT_FRAME "AtFrame" +typedef enum { + #define X(name, str) ANM2_ELEMENT_##name, + ANM2_ELEMENT_LIST + #undef X + ANM2_ELEMENT_COUNT +} Anm2Element; -#define ANM2_ELEMENT_COUNT (ANM2_ELEMENT_TRIGGER + 1) -enum Anm2Element -{ - ANM2_ELEMENT_ANIMATED_ACTOR, - ANM2_ELEMENT_INFO, - ANM2_ELEMENT_CONTENT, - ANM2_ELEMENT_SPRITESHEETS, - ANM2_ELEMENT_SPRITESHEET, - ANM2_ELEMENT_LAYERS, - ANM2_ELEMENT_LAYER, - ANM2_ELEMENT_NULLS, - ANM2_ELEMENT_NULL, - ANM2_ELEMENT_EVENTS, - ANM2_ELEMENT_EVENT, - ANM2_ELEMENT_ANIMATIONS, - ANM2_ELEMENT_ANIMATION, - ANM2_ELEMENT_ROOT_ANIMATION, - ANM2_ELEMENT_FRAME, - ANM2_ELEMENT_LAYER_ANIMATIONS, - ANM2_ELEMENT_LAYER_ANIMATION, - ANM2_ELEMENT_NULL_ANIMATIONS, - ANM2_ELEMENT_NULL_ANIMATION, - ANM2_ELEMENT_TRIGGERS, - ANM2_ELEMENT_TRIGGER +static const char* ANM2_ELEMENT_STRINGS[] = { + #define X(name, str) str, + ANM2_ELEMENT_LIST + #undef X }; -static const char* ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_COUNT] = -{ - STRING_ANM2_ELEMENT_ANIMATED_ACTOR, - STRING_ANM2_ELEMENT_INFO, - STRING_ANM2_ELEMENT_CONTENT, - STRING_ANM2_ELEMENT_SPRITESHEETS, - STRING_ANM2_ELEMENT_SPRITESHEET, - STRING_ANM2_ELEMENT_LAYERS, - STRING_ANM2_ELEMENT_LAYER, - STRING_ANM2_ELEMENT_NULLS, - STRING_ANM2_ELEMENT_NULL, - STRING_ANM2_ELEMENT_EVENTS, - STRING_ANM2_ELEMENT_EVENT, - STRING_ANM2_ELEMENT_ANIMATIONS, - STRING_ANM2_ELEMENT_ANIMATION, - STRING_ANM2_ELEMENT_ROOT_ANIMATION, - STRING_ANM2_ELEMENT_FRAME, - STRING_ANM2_ELEMENT_LAYER_ANIMATIONS, - STRING_ANM2_ELEMENT_LAYER_ANIMATION, - STRING_ANM2_ELEMENT_NULL_ANIMATIONS, - STRING_ANM2_ELEMENT_NULL_ANIMATION, - STRING_ANM2_ELEMENT_TRIGGERS, - STRING_ANM2_ELEMENT_TRIGGER +DEFINE_STRING_TO_ENUM_FN(anm2_element_from_string, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT) + +#define ANM2_ATTRIBUTE_LIST \ + X(CREATED_BY, "CreatedBy") \ + X(CREATED_ON, "CreatedOn") \ + X(VERSION, "Version") \ + X(FPS, "Fps") \ + X(ID, "Id") \ + X(PATH, "Path") \ + X(NAME, "Name") \ + X(SPRITESHEET_ID, "SpritesheetId") \ + X(SHOW_RECT, "ShowRect") \ + X(DEFAULT_ANIMATION, "DefaultAnimation") \ + X(FRAME_NUM, "FrameNum") \ + X(LOOP, "Loop") \ + X(X_POSITION, "XPosition") \ + X(Y_POSITION, "YPosition") \ + X(X_PIVOT, "XPivot") \ + X(Y_PIVOT, "YPivot") \ + X(X_CROP, "XCrop") \ + X(Y_CROP, "YCrop") \ + X(WIDTH, "Width") \ + X(HEIGHT, "Height") \ + X(X_SCALE, "XScale") \ + X(Y_SCALE, "YScale") \ + X(DELAY, "Delay") \ + X(VISIBLE, "Visible") \ + X(RED_TINT, "RedTint") \ + X(GREEN_TINT, "GreenTint") \ + X(BLUE_TINT, "BlueTint") \ + X(ALPHA_TINT, "AlphaTint") \ + X(RED_OFFSET, "RedOffset") \ + X(GREEN_OFFSET, "GreenOffset") \ + X(BLUE_OFFSET, "BlueOffset") \ + X(ROTATION, "Rotation") \ + X(INTERPOLATED, "Interpolated") \ + X(LAYER_ID, "LayerId") \ + X(NULL_ID, "NullId") \ + X(EVENT_ID, "EventId") \ + X(AT_FRAME, "AtFrame") + +typedef enum { + #define X(name, str) ANM2_ATTRIBUTE_##name, + ANM2_ATTRIBUTE_LIST + #undef X + ANM2_ATTRIBUTE_COUNT +} Anm2Attribute; + +static const char* ANM2_ATTRIBUTE_STRINGS[] = { + #define X(name, str) str, + ANM2_ATTRIBUTE_LIST + #undef X }; -#define ANM2_ATTRIBUTE_COUNT (ANM2_ATTRIBUTE_AT_FRAME + 1) -enum Anm2Attribute -{ - ANM2_ATTRIBUTE_CREATED_BY, - ANM2_ATTRIBUTE_CREATED_ON, - ANM2_ATTRIBUTE_FPS, - ANM2_ATTRIBUTE_VERSION, - ANM2_ATTRIBUTE_ID, - ANM2_ATTRIBUTE_PATH, - ANM2_ATTRIBUTE_NAME, - ANM2_ATTRIBUTE_SPRITESHEET_ID, - ANM2_ATTRIBUTE_SHOW_RECT, - ANM2_ATTRIBUTE_DEFAULT_ANIMATION, - ANM2_ATTRIBUTE_FRAME_NUM, - ANM2_ATTRIBUTE_LOOP, - ANM2_ATTRIBUTE_X_POSITION, - ANM2_ATTRIBUTE_Y_POSITION, - ANM2_ATTRIBUTE_X_PIVOT, - ANM2_ATTRIBUTE_Y_PIVOT, - ANM2_ATTRIBUTE_X_CROP, - ANM2_ATTRIBUTE_Y_CROP, - ANM2_ATTRIBUTE_WIDTH, - ANM2_ATTRIBUTE_HEIGHT, - ANM2_ATTRIBUTE_X_SCALE, - ANM2_ATTRIBUTE_Y_SCALE, - ANM2_ATTRIBUTE_DELAY, - ANM2_ATTRIBUTE_VISIBLE, - ANM2_ATTRIBUTE_RED_TINT, - ANM2_ATTRIBUTE_GREEN_TINT, - ANM2_ATTRIBUTE_BLUE_TINT, - ANM2_ATTRIBUTE_ALPHA_TINT, - ANM2_ATTRIBUTE_RED_OFFSET, - ANM2_ATTRIBUTE_GREEN_OFFSET, - ANM2_ATTRIBUTE_BLUE_OFFSET, - ANM2_ATTRIBUTE_ROTATION, - ANM2_ATTRIBUTE_INTERPOLATED, - ANM2_ATTRIBUTE_LAYER_ID, - ANM2_ATTRIBUTE_NULL_ID, - ANM2_ATTRIBUTE_EVENT_ID, - ANM2_ATTRIBUTE_AT_FRAME -}; +DEFINE_STRING_TO_ENUM_FN(anm2_attribute_from_string, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT) -static const char* ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_COUNT] = -{ - STRING_ANM2_ATTRIBUTE_CREATED_BY, - STRING_ANM2_ATTRIBUTE_CREATED_ON, - STRING_ANM2_ATTRIBUTE_FPS, - STRING_ANM2_ATTRIBUTE_VERSION, - STRING_ANM2_ATTRIBUTE_ID, - STRING_ANM2_ATTRIBUTE_PATH, - STRING_ANM2_ATTRIBUTE_NAME, - STRING_ANM2_ATTRIBUTE_SPRITESHEET_ID, - STRING_ANM2_ATTRIBUTE_SHOW_RECT, - STRING_ANM2_ATTRIBUTE_DEFAULT_ANIMATION, - STRING_ANM2_ATTRIBUTE_FRAME_NUM, - STRING_ANM2_ATTRIBUTE_LOOP, - STRING_ANM2_ATTRIBUTE_X_POSITION, - STRING_ANM2_ATTRIBUTE_Y_POSITION, - STRING_ANM2_ATTRIBUTE_X_PIVOT, - STRING_ANM2_ATTRIBUTE_Y_PIVOT, - STRING_ANM2_ATTRIBUTE_X_CROP, - STRING_ANM2_ATTRIBUTE_Y_CROP, - STRING_ANM2_ATTRIBUTE_WIDTH, - STRING_ANM2_ATTRIBUTE_HEIGHT, - STRING_ANM2_ATTRIBUTE_X_SCALE, - STRING_ANM2_ATTRIBUTE_Y_SCALE, - STRING_ANM2_ATTRIBUTE_DELAY, - STRING_ANM2_ATTRIBUTE_VISIBLE, - STRING_ANM2_ATTRIBUTE_RED_TINT, - STRING_ANM2_ATTRIBUTE_GREEN_TINT, - STRING_ANM2_ATTRIBUTE_BLUE_TINT, - STRING_ANM2_ATTRIBUTE_ALPHA_TINT, - STRING_ANM2_ATTRIBUTE_RED_OFFSET, - STRING_ANM2_ATTRIBUTE_GREEN_OFFSET, - STRING_ANM2_ATTRIBUTE_BLUE_OFFSET, - STRING_ANM2_ATTRIBUTE_ROTATION, - STRING_ANM2_ATTRIBUTE_INTERPOLATED, - STRING_ANM2_ATTRIBUTE_LAYER_ID, - STRING_ANM2_ATTRIBUTE_NULL_ID, - STRING_ANM2_ATTRIBUTE_EVENT_ID, - STRING_ANM2_ATTRIBUTE_AT_FRAME -}; - -#define ANM2_ANIMATION_TYPE_COUNT (ANM2_ANIMATION_NULL + 1) +#define ANM2_ANIMATION_TYPE_COUNT (ANM2_ANIMATION_TRIGGERS + 1) enum Anm2AnimationType { - ANM2_ANIMATION_ROOT, - ANM2_ANIMATION_LAYER, - ANM2_ANIMATION_NULL + ANM2_NONE, + ANM2_ROOT_ANIMATION, + ANM2_LAYER_ANIMATION, + ANM2_NULL_ANIMATION, + ANM2_TRIGGERS }; struct Anm2Spritesheet { - s32 id; - char path[ANM2_STRING_MAX]; + char path[ANM2_STRING_MAX] = STRING_EMPTY; }; struct Anm2Layer { - s32 id; - char name[ANM2_STRING_MAX]; - s32 spritesheetID; + s32 spritesheetID = -1; + char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_LAYER; +}; + +struct Anm2Null +{ + char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_NULL; + bool isShowRect = false; }; struct Anm2Event { - s32 id; - char name[ANM2_STRING_MAX]; + char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_EVENT; }; struct Anm2Trigger { - s32 eventID; - s32 frame; + s32 eventID = -1; + s32 atFrame = -1; }; struct Anm2Frame { - bool isInterpolated; - bool isVisible; - f32 rotation; - s32 delay; - ivec2 crop; - ivec2 pivot; - ivec2 position; - ivec2 size; - ivec2 scale; - ivec3 colorOffset; - ivec4 tint; - s32 index; + bool isInterpolated = false; + bool isVisible = true; + f32 rotation = 1.0f; + s32 delay = 1; + vec2 crop = {0.0f, 0.0f}; + vec2 pivot = {0.0f, 0.0f}; + vec2 position = {0.0f, 0.0f}; + vec2 size = {1.0f, 1.0f}; + vec2 scale = {1.0f, 1.0f}; + ivec3 offsetRGB = {0, 0, 0}; + ivec4 tintRGBA = {255, 255, 255, 255}; }; struct Anm2LayerAnimation { - s32 layerID; - bool isVisible; - vector frames; + bool isVisible = true; + std::vector frames; +}; + +struct Anm2NullAnimation +{ + bool isVisible = true; + std::vector frames; +}; + +struct Anm2RootAnimation +{ + bool isVisible = true; + std::vector frames; +}; + +struct Anm2Triggers +{ + bool isVisible = true; + std::vector items; }; struct Anm2Animation { - s32 id; - s32 length; - char name[ANM2_STRING_MAX]; - bool isVisible; - bool isLoop; - Anm2Frame rootAnimationFrame; - vector layerAnimations; - vector triggers; + s32 frameNum = 0; + char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_ANIMATION; + bool isLoop = true; + Anm2RootAnimation rootAnimation; + std::map layerAnimations; + std::map nullAnimations; + Anm2Triggers triggers; }; struct Anm2 { - s32 fps; - s32 version; - char createdBy[ANM2_STRING_MAX]; - char createdOn[ANM2_STRING_MAX]; - char defaultAnimation[ANM2_STRING_MAX]; - bool isInit; - vector spritesheets; - vector layers; - vector events; - vector animations; + char path[PATH_MAX] = STRING_EMPTY; + s32 fps = ANM2_FPS_DEFAULT; + s32 version = 0; + char createdBy[ANM2_STRING_MAX] = STRING_ANM2_CREATED_BY_DEFAULT; + char createdOn[ANM2_STRING_MAX] = STRING_EMPTY; + char defaultAnimation[ANM2_STRING_MAX] = STRING_EMPTY; + std::map spritesheets; + std::map layers; + std::map nulls; + std::map events; + std::map animations; }; -bool anm2_init(Anm2* self, const char* path); -void anm2_free(Anm2* self); +void anm2_layer_add(Anm2* self); +void anm2_layer_remove(Anm2* self, s32 id); +void anm2_null_add(Anm2* self); +void anm2_null_remove(Anm2* self, s32 id); +bool anm2_serialize(Anm2* self, const char* path); +bool anm2_deserialize(Anm2* self, const char* path); +void anm2_new(Anm2* self); +void anm2_created_on_set(Anm2* self); +s32 anm2_animation_add(Anm2* self); +void anm2_animation_remove(Anm2* self, s32 id); \ No newline at end of file diff --git a/src/dialog.cpp b/src/dialog.cpp new file mode 100644 index 0000000..d049743 --- /dev/null +++ b/src/dialog.cpp @@ -0,0 +1,59 @@ +#include "dialog.h" + +static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter); + +static void +_dialog_callback(void* userdata, const char* const* filelist, s32 filter) +{ + Dialog* self; + + self = (Dialog*)userdata; + + if (filelist && filelist[0] && strlen(filelist[0]) > 0) + { + strncpy(self->path, filelist[0], PATH_MAX - 1); + self->isSelected = true; + } + else + self->isSelected = false; + +} + +/* Opens file dialog for user to pick anm2 files */ +void +dialog_anm2_open(Dialog* self) +{ + SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL, false); + self->type = DIALOG_ANM2_OPEN; +} + +/* Opens file dialog for user to save new anm2 files */ +void +dialog_anm2_save(Dialog* self) +{ + SDL_ShowSaveFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL); + self->type = DIALOG_ANM2_SAVE; +} + +void +dialog_tick(Dialog* self) +{ + if (self->isSelected) + { + switch (self->type) + { + case DIALOG_ANM2_OPEN: + anm2_deserialize(self->anm2, self->path); + break; + case DIALOG_ANM2_SAVE: + anm2_serialize(self->anm2, self->path); + break; + default: + break; + } + + memset(self->path, '\0', PATH_MAX); + self->isSelected = false; + } +} + diff --git a/src/dialog.h b/src/dialog.h new file mode 100644 index 0000000..c772457 --- /dev/null +++ b/src/dialog.h @@ -0,0 +1,26 @@ +#pragma once + +#include "anm2.h" + +static const SDL_DialogFileFilter ANM2_DIALOG_FILE_FILTER[] = +{ + {"Anm2", "anm2;xml"} +}; + +enum DialogType +{ + DIALOG_ANM2_OPEN, + DIALOG_ANM2_SAVE +}; + +struct Dialog +{ + Anm2* anm2 = NULL; + enum DialogType type = DIALOG_ANM2_OPEN; + char path[PATH_MAX] = ""; + bool isSelected = false; +}; + +void dialog_anm2_open(Dialog* self); +void dialog_anm2_save(Dialog* self); +void dialog_tick(Dialog* self); \ No newline at end of file diff --git a/src/file.cpp b/src/file.cpp deleted file mode 100644 index d74a3d4..0000000 --- a/src/file.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "file.h" - -bool -file_read(const char* path, void* buffer, size_t size) -{ - SDL_IOStream* ioStream; - - memset(buffer, '\0', size); - - ioStream = SDL_IOFromFile(path, "r"); - - if (!ioStream) - { - printf(STRING_ERROR_FILE_READ, path); - return false; - } - - if (!SDL_ReadIO(ioStream, buffer, size)) - { - printf(STRING_ERROR_FILE_READ, path); - return false; - } - - SDL_CloseIO(ioStream); - - return true; -} - diff --git a/src/file.h b/src/file.h deleted file mode 100644 index e4e72f0..0000000 --- a/src/file.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "COMMON.h" - -bool file_read(const char* path, void* buffer, size_t size); diff --git a/src/imgui.cpp b/src/imgui.cpp index 389d0c3..e201a95 100644 --- a/src/imgui.cpp +++ b/src/imgui.cpp @@ -1,5 +1,280 @@ #include "imgui.h" +static void _imgui_tooltip(const char* tooltip); +static void _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID); + +/* Makes a tooltip! */ +static void _imgui_tooltip(const char* tooltip) +{ + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) + ImGui::SetTooltip("%s", tooltip); +} + +/* Displays each element of the timeline of a selected animation */ +static void +_imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID) +{ + static s32 selectedElementIndex = -1; + static s32 selectedSpritesheetIndex = -1; + Anm2Layer* layer = NULL; + Anm2LayerAnimation* layerAnimation = NULL; + Anm2Null* null = NULL; + Anm2NullAnimation* nullAnimation = NULL; + Anm2RootAnimation* rootAnimation = NULL; + Anm2Triggers* triggers = NULL; + ImTextureID iconTexture = -1; + + bool isArrows = false; + bool* isShowRect = NULL; + bool* isVisible = NULL; + char nameBuffer[ANM2_STRING_MAX] = STRING_EMPTY; + char nameVisible[ANM2_STRING_FORMATTED_MAX] = STRING_EMPTY; + char* namePointer = NULL; + s32* spritesheetID = NULL; + bool isSelected = selectedElementIndex == *index; + bool isChangeable = type != ANM2_ROOT_ANIMATION && type != ANM2_TRIGGERS; + + switch (type) + { + case ANM2_ROOT_ANIMATION: + rootAnimation = (Anm2RootAnimation*)element; + iconTexture = self->packed->textures[PACKED_TEXTURE_ROOT].handle; + strncpy(nameVisible, STRING_IMGUI_TIMELINE_ROOT, ANM2_STRING_FORMATTED_MAX); + isVisible = &rootAnimation->isVisible; + break; + case ANM2_LAYER_ANIMATION: + layerAnimation = (Anm2LayerAnimation*)element; + layer = &self->anm2->layers[*id]; + iconTexture = self->packed->textures[PACKED_TEXTURE_LAYER].handle; + isVisible = &layerAnimation->isVisible; + spritesheetID = &layer->spritesheetID; + namePointer = layer->name; + snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer); + snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer); + break; + case ANM2_NULL_ANIMATION: + nullAnimation = (Anm2NullAnimation*)element; + null = &self->anm2->nulls[*id]; + iconTexture = self->packed->textures[PACKED_TEXTURE_NULL].handle; + isVisible = &nullAnimation->isVisible; + isShowRect = &null->isShowRect; + namePointer = null->name; + snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer); + snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer); + break; + case ANM2_TRIGGERS: + triggers = (Anm2Triggers*)element; + iconTexture = self->packed->textures[PACKED_TEXTURE_TRIGGER].handle; + strncpy(nameVisible, STRING_IMGUI_TIMELINE_TRIGGERS, ANM2_STRING_FORMATTED_MAX); + isVisible = &triggers->isVisible; + break; + default: + break; + } + + ImGui::BeginChild(nameVisible, IMGUI_TIMELINE_ELEMENT_SIZE, true, ImGuiWindowFlags_NoScrollbar); + + ImGui::PushID(*index); + + /* Shift arrows */ + if (isChangeable) + { + bool isSwap = false; + bool isReversed = (type == ANM2_LAYER_ANIMATION); + + auto arrows_draw = [&](auto it, auto begin, auto end, auto& map, bool* didSwap, bool isReversed) + { + bool canMoveUp = isReversed ? (std::next(it) != end) : (it != begin); + bool canMoveDown = isReversed ? (it != begin) : (std::next(it) != end); + + isArrows = canMoveUp || canMoveDown; + + if (isArrows) + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ARROWS_LABEL, IMGUI_TIMELINE_SHIFT_ARROWS_SIZE); + + if (canMoveUp) + { + auto target = isReversed ? std::next(it) : std::prev(it); + if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE, self->packed->textures[PACKED_TEXTURE_ARROW_UP].handle, IMGUI_ICON_SIZE)) + { + map_swap(map, it->first, target->first); + *didSwap = true; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP); + + } + + if (canMoveDown) + { + if (!canMoveUp) + ImGui::Dummy(IMGUI_ICON_BUTTON_SIZE); + + auto target = isReversed ? std::prev(it) : std::next(it); + if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW, self->packed->textures[PACKED_TEXTURE_ARROW_DOWN].handle, IMGUI_ICON_SIZE)) + { + map_swap(map, it->first, target->first); + *didSwap = true; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN); + + } + + if (isArrows) + { + ImGui::EndChild(); + ImGui::SameLine(); + } + }; + + if (type == ANM2_LAYER_ANIMATION) + { + auto it = std::find_if(self->anm2->layers.begin(), self->anm2->layers.end(), + [&](const auto& pair) { return &pair.second == layer; }); + + if (it != self->anm2->layers.end()) + arrows_draw(it, self->anm2->layers.begin(), self->anm2->layers.end(), self->anm2->layers, &isSwap, isReversed); + } + + if (type == ANM2_NULL_ANIMATION) + { + auto it = std::find_if(self->anm2->nulls.begin(), self->anm2->nulls.end(), + [&](const auto& pair) { return &pair.second == null; }); + + if (it != self->anm2->nulls.end()) + arrows_draw(it, self->anm2->nulls.begin(), self->anm2->nulls.end(), self->anm2->nulls, &isSwap, isReversed); + } + } + + ImGui::BeginGroup(); + + ImGui::Image(iconTexture, IMGUI_ICON_SIZE); + ImGui::SameLine(); + + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_NAME_LABEL, IMGUI_TIMELINE_ELEMENT_NAME_SIZE); + + if (isSelected && isChangeable) + { + if (ImGui::InputText(STRING_IMGUI_TIMELINE_ANIMATION_LABEL, nameBuffer, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) + { + strncpy(namePointer, nameBuffer, ANM2_STRING_MAX); + selectedElementIndex = -1; + } + } + else + { + if (ImGui::Selectable(nameVisible, isSelected)) + { + selectedElementIndex = *index; + + if (selectType && selectID) + { + *selectType = type; + *selectID = *id; + } + } + + } + + switch (type) + { + case ANM2_ROOT_ANIMATION: + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT); + break; + case ANM2_LAYER_ANIMATION: + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER); + break; + case ANM2_NULL_ANIMATION: + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NULL); + break; + case ANM2_TRIGGERS: + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS); + break; + default: + break; + } + + ImGui::EndChild(); + + /* Visiblity */ + if (isVisible) + { + ImTextureID visibilityIcon = *isVisible + ? self->packed->textures[PACKED_TEXTURE_EYE_OPEN].handle + : self->packed->textures[PACKED_TEXTURE_EYE_CLOSED].handle; + + ImGui::SameLine(); + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); + + if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_VISIBLE, visibilityIcon, IMGUI_ICON_SIZE)) + *isVisible = !*isVisible; + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE); + } + + /* Spritesheet IDs */ + if (spritesheetID) + { + char spritesheetIDName[ANM2_STRING_FORMATTED_MAX]; + + if (*spritesheetID == -1) + snprintf(spritesheetIDName, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_SPRITESHEET_UNKNOWN); + else + snprintf(spritesheetIDName, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_SPRITESHEET_FORMAT, *spritesheetID); + + ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); + ImGui::SameLine(); + + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, IMGUI_TIMELINE_SPRITESHEET_ID_SIZE); + + if (selectedSpritesheetIndex == *index) + { + if (ImGui::DragInt(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, spritesheetID, 0, 1, self->anm2->spritesheets.size() - 1)) + selectedSpritesheetIndex = -1; + } + else + { + if (ImGui::Selectable(spritesheetIDName)) + selectedSpritesheetIndex = *index; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET); + + ImGui::EndChild(); + } + + /* ShowRect */ + if (isShowRect) + { + ImTextureID rectIcon = *isShowRect + ? self->packed->textures[PACKED_TEXTURE_RECT_HIDE].handle + : self->packed->textures[PACKED_TEXTURE_RECT_SHOW].handle; + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); + + if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_RECT, rectIcon, IMGUI_ICON_SIZE)) + *isShowRect = !*isShowRect; + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT); + } + + if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + { + selectedElementIndex = -1; + selectedSpritesheetIndex = -1; + } + + ImGui::PopID(); + + ImGui::EndGroup(); + + ImGui::EndChild(); + + *index = *index + 1; +} + void imgui_init(SDL_Window* window, SDL_GLContext glContext) { @@ -11,31 +286,604 @@ imgui_init(SDL_Window* window, SDL_GLContext glContext) ImGui_ImplSDL3_InitForOpenGL(window, glContext); ImGui_ImplOpenGL3_Init(STRING_OPENGL_VERSION); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + printf(STRING_INFO_IMGUI_INIT); } void -imgui_tick(State* state) +imgui_tick(Imgui* self) { + static s32 selectedSpritesheetID = -1; + static s32 selectedEventID = -1; + static s32 selectedAnimationID = -1; + static s32 selectedTimelineElementID = -1; + static Anm2AnimationType selectedTimelineElementType = ANM2_NONE; + static bool isInterpolated = true; + static bool isVisible = true; + + static f32 rotation = 0; + static s32 duration = 0; + static vec2 cropPosition = {0, 0}; + static vec2 cropSize = {0, 0}; + static vec2 pivot = {0, 0}; + static vec2 position = {0, 0}; + static vec2 xyScale = {0, 0}; + static vec3 offset = {0.0f, 0.0f, 0.0f}; + static vec4 tint = {0.0f, 0.0f, 0.0f, 1.0f}; + + ImVec2 previewWindowSize; + ImVec2 previewSize; + f32 previewAspect = 0.0f; + f32 previewWindowAspect = 0.0f; + + ImGuiWindowFlags taskbarWindowFlags; + ImGuiWindowFlags dockspaceWindowFlags; + ImGuiDockNodeFlags dockNodeFlags; + ImGui_ImplSDL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame(); ImGui::NewFrame(); - ImGui::Begin("Hello, world!"); - ImGui::Text("Color"); - ImGui::ColorEdit3("Color", value_ptr(state->clearColor)); + taskbarWindowFlags = 0 | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoSavedSettings; + + dockspaceWindowFlags = 0 | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus; + + dockNodeFlags = 0 | + ImGuiDockNodeFlags_PassthruCentralNode; + + ImGuiViewport* viewport = ImGui::GetMainViewport(); + + /* Taskbar */ + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, IMGUI_TASKBAR_HEIGHT)); + + ImGui::Begin(STRING_IMGUI_TASKBAR, NULL, taskbarWindowFlags); + + if (ImGui::Selectable(STRING_IMGUI_TASKBAR_FILE, false, 0, ImGui::CalcTextSize(STRING_IMGUI_TASKBAR_FILE))) + ImGui::OpenPopup(STRING_IMGUI_FILE_MENU); + + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) + ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y + ImGui::GetItemRectSize().y)); + + if (ImGui::BeginPopup(STRING_IMGUI_FILE_MENU)) + { + if (ImGui::Selectable(STRING_IMGUI_FILE_NEW)) + { + selectedAnimationID = -1; + selectedSpritesheetID = -1; + selectedEventID = -1; + anm2_new(self->anm2); + window_title_from_anm2_set(self->window, self->anm2); + + } + + if (ImGui::Selectable(STRING_IMGUI_FILE_OPEN)) + { + selectedAnimationID = -1; + selectedEventID = -1; + selectedSpritesheetID = -1; + dialog_anm2_open(self->dialog); + window_title_from_anm2_set(self->window, self->anm2); + } + + if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE)) + { + if (!strcmp(self->anm2->path, STRING_EMPTY) == 0) + anm2_serialize(self->anm2, self->anm2->path); + else + dialog_anm2_save(self->dialog); + } + + if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE_AS)) + { + dialog_anm2_save(self->dialog); + window_title_from_anm2_set(self->window, self->anm2); + } + + ImGui::EndPopup(); + } + ImGui::End(); + + /* Dockspace */ + ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + IMGUI_TASKBAR_HEIGHT)); + ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, viewport->Size.y - IMGUI_TASKBAR_HEIGHT)); + ImGui::SetNextWindowViewport(viewport->ID); + + ImGui::Begin(STRING_IMGUI_WINDOW, NULL, dockspaceWindowFlags); + + ImGuiID dockspace_id = ImGui::GetID(STRING_IMGUI_DOCKSPACE); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockNodeFlags); + + ImGui::End(); + + /* -- Properties -- */ + ImGui::Begin(STRING_IMGUI_PROPERTIES); + + /* FPS */ + ImGui::AlignTextToFramePadding(); + ImGui::Text(STRING_IMGUI_PROPERTIES_FPS); + ImGui::SameLine(); + ImGui::SliderInt(STRING_IMGUI_PROPERTIES_FPS_LABEL, &self->anm2->fps, ANM2_FPS_MIN, ANM2_FPS_MAX); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_PROPERTIES_FPS); + + /* CreatedBy */ + ImGui::AlignTextToFramePadding(); + ImGui::Text(STRING_IMGUI_PROPERTIES_CREATED_BY); + ImGui::SameLine(); + ImGui::InputText(STRING_IMGUI_PROPERTIES_CREATED_BY_LABEL, self->anm2->createdBy, ANM2_STRING_MAX); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY); + + /* CreatedOn */ + ImGui::Text(STRING_IMGUI_PROPERTIES_CREATED_ON, self->anm2->createdOn); + + /* Version */ + ImGui::Text(STRING_IMGUI_PROPERTIES_VERSION, self->anm2->version); + + ImGui::End(); + + /* -- Animations -- */ + ImGui::Begin(STRING_IMGUI_ANIMATIONS); + + /* Iterate through all animations, can be selected and names can be edited */ + for (auto & [id, animation] : self->anm2->animations) + { + char name[ANM2_STRING_FORMATTED_MAX]; + char oldName[ANM2_STRING_MAX]; + bool isSelected = selectedAnimationID == id; + static s32 defaultAnimationID = -1; + + /* Distinguish default animation; if animation has the same name, only mark the first one as default */ + if (strcmp(animation.name, self->anm2->defaultAnimation) == 0) + { + snprintf(name, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT, animation.name); + defaultAnimationID = id; + } + else + strncpy(name, animation.name, ANM2_STRING_FORMATTED_MAX - 1); + + ImGui::PushID(id); + + ImGui::Image(self->packed->textures[PACKED_TEXTURE_ANIMATION].handle, IMGUI_ICON_SIZE); + ImGui::SameLine(); + + if (isSelected) + { + if (ImGui::InputText(STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL, animation.name, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) + { + if (id == defaultAnimationID) + strncpy(self->anm2->defaultAnimation, animation.name, ANM2_STRING_MAX); + + selectedAnimationID = -1; + } + } + else + { + if (ImGui::Selectable(name, isSelected)) + selectedAnimationID = id; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT); + + ImGui::PopID(); + } + + if (ImGui::Button(STRING_IMGUI_ANIMATIONS_ADD)) + { + s32 id; + bool isDefault = self->anm2->animations.size() == 0; /* first animation is default automatically */ + + id = anm2_animation_add(self->anm2); + + selectedAnimationID = id; + + if (isDefault) + strncpy(self->anm2->defaultAnimation, self->anm2->animations[id].name, ANM2_STRING_MAX); + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD); + + ImGui::SameLine(); + + if (selectedAnimationID != -1) + { + /* Remove */ + if (ImGui::Button(STRING_IMGUI_ANIMATIONS_REMOVE)) + { + anm2_animation_remove(self->anm2, selectedAnimationID); + selectedAnimationID = -1; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE); + + ImGui::SameLine(); + + /* Duplicate */ + if (ImGui::Button(STRING_IMGUI_ANIMATIONS_DUPLICATE)) + { + s32 id = map_next_id_get(self->anm2->animations); + self->anm2->animations.insert({id, self->anm2->animations[selectedAnimationID]}); + selectedAnimationID = id; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE); + + if (ImGui::Button(STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT)) + { + strncpy(self->anm2->defaultAnimation, self->anm2->animations[selectedAnimationID].name, ANM2_STRING_MAX); + selectedAnimationID = -1; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT); + } + + if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + selectedAnimationID = -1; + + ImGui::End(); + + /* -- Events -- */ + ImGui::Begin(STRING_IMGUI_EVENTS); + + /* Iterate through all events, can be selected and names can be edited */ + for (auto & [id, event] : self->anm2->events) + { + char eventString[ANM2_STRING_FORMATTED_MAX]; + bool isSelected; + + snprintf(eventString, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_EVENT_FORMAT, (s32)id, event.name); + + ImGui::PushID(id); + + ImGui::Image(self->packed->textures[PACKED_TEXTURE_EVENT].handle, IMGUI_ICON_SIZE); + ImGui::SameLine(); + + isSelected = selectedEventID == id; + + if (isSelected) + { + if (ImGui::InputText(STRING_IMGUI_EVENTS_EVENT_LABEL, event.name, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) + selectedEventID = -1; + } + else + { + if (ImGui::Selectable(eventString, isSelected)) + selectedEventID = id; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_SELECT); + + ImGui::PopID(); + } + + if (ImGui::Button(STRING_IMGUI_EVENTS_ADD)) + { + s32 id = map_next_id_get(self->anm2->events); + self->anm2->events[id] = Anm2Event{}; + selectedEventID = id; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_ADD); + + ImGui::SameLine(); + + if (selectedEventID != -1) + { + if (ImGui::Button(STRING_IMGUI_EVENTS_REMOVE)) + { + self->anm2->events.erase(selectedEventID); + selectedEventID = -1; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_REMOVE); + } + + if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + selectedEventID = -1; + + ImGui::End(); + + /* -- Spritesheets -- */ + ImGui::Begin(STRING_IMGUI_SPRITESHEETS); + + for (auto [id, spritesheet] : self->anm2->spritesheets) + { + char spritesheetString[ANM2_STRING_FORMATTED_MAX]; + bool isSelected = false; + + snprintf(spritesheetString, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_SPRITESHEET_FORMAT, (s32)id, spritesheet.path); + + ImGui::BeginChild(spritesheetString, IMGUI_SPRITESHEET_SIZE, true, ImGuiWindowFlags_None); + + ImGui::PushID(id); + + ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); + ImGui::SameLine(); + + isSelected = selectedSpritesheetID == id; + if (ImGui::Selectable(spritesheetString, isSelected)) + selectedSpritesheetID = id; + + ImGui::PopID(); + + ImGui::EndChild(); + } + + ImGui::Button(STRING_IMGUI_SPRITESHEETS_ADD); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD); + + ImGui::SameLine(); + + if (selectedSpritesheetID != -1) + { + if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REMOVE)) + { + self->anm2->spritesheets.erase(selectedSpritesheetID); + selectedSpritesheetID = -1; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE); + + ImGui::SameLine(); + ImGui::Button(STRING_IMGUI_SPRITESHEETS_RELOAD); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD); + ImGui::SameLine(); + ImGui::Button(STRING_IMGUI_SPRITESHEETS_REPLACE); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE); + } + + if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + selectedSpritesheetID = -1; + + ImGui::End(); + + /* -- Frame Properties -- */ + + ImGui::Begin(STRING_IMGUI_FRAME_PROPERTIES); + + ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_POSITION, value_ptr(position), 1, 0, 0, "%.0f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION); + + ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION, value_ptr(cropPosition), 1, 0, 0, "%.0f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION); + + ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_SIZE, value_ptr(cropSize), 1, 0, 0, "%.0f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE); + + ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_PIVOT, value_ptr(pivot), 1, 0, 0, "%.0f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT); + + ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_SCALE, value_ptr(xyScale), 1.0, 0, 0, "%.1f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE); + + ImGui::DragFloat(STRING_IMGUI_FRAME_PROPERTIES_ROTATION, &rotation, 1, 0, 0, "%.1f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION); + + ImGui::DragInt(STRING_IMGUI_FRAME_PROPERTIES_DURATION, &duration, 1, 0, 255); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION); + + ImGui::ColorEdit4(STRING_IMGUI_FRAME_PROPERTIES_TINT, value_ptr(tint)); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT); + + ImGui::ColorEdit3(STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, value_ptr(offset)); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET); + + ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_VISIBLE, &isVisible); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE); + + ImGui::SameLine(); + + ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED, &isInterpolated); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED); + + ImGui::End(); + + /* -- Animation Preview -- */ + + ImGui::Begin(STRING_IMGUI_ANIMATION_PREVIEW); + + /* Settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE, true); + + /* Grid settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE, true); + + /* Grid toggle */ + ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_GRID, &self->preview->isGrid); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID); + + ImGui::SameLine(); + + /* Grid Color */ + ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR, value_ptr(self->preview->gridColor), ImGuiColorEditFlags_NoInputs); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR); + + /* Grid Size */ + ImGui::InputInt2(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE, value_ptr(self->preview->gridSize)); + self->preview->gridSize.x = CLAMP(self->preview->gridSize.x, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); + self->preview->gridSize.y = CLAMP(self->preview->gridSize.y, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE); + + ImGui::EndChild(); + + ImGui::SameLine(); + + /* Zoom settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE, true); + + /* Zoom */ + ImGui::DragFloat(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM, &self->preview->zoom, 1, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX, "%.0f"); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM); + + ImGui::EndChild(); + + ImGui::SameLine(); + + /* Background settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE, true); + + /* Background color */ + ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, value_ptr(self->preview->color), ImGuiColorEditFlags_NoInputs); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR); + + ImGui::EndChild(); + + ImGui::SameLine(); + + ImGui::EndChild(); + + ImGui::Image(self->preview->texture, ImVec2(PREVIEW_SIZE.x, PREVIEW_SIZE.y)); + + ImGui::End(); + + /* -- Spritesheet Editor -- */ + + ImGui::Begin(STRING_IMGUI_SPRITESHEET_EDITOR); + + ImGui::End(); + + /* -- Timeline -- */ + ImGui::Begin(STRING_IMGUI_TIMELINE); + + if (selectedAnimationID != -1) + { + s32 index = 0; + Anm2Animation* animation = &self->anm2->animations[selectedAnimationID]; + + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ANIMATIONS, IMGUI_TIMELINE_ELEMENT_LIST_SIZE, true); + + /* Root */ + _imgui_timeline_element(self, &animation->rootAnimation, NULL, &index, ANM2_ROOT_ANIMATION, NULL, NULL); + + /* reverse order */ + for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++) + { + s32 id = it->first; + Anm2LayerAnimation& layer = it->second; + + _imgui_timeline_element(self, &layer, &id, &index, ANM2_LAYER_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); + } + + for (auto & [id, null] : animation->nullAnimations) + _imgui_timeline_element(self, &null, (s32*)&id, &index, ANM2_NULL_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); + + /* Triggers */ + _imgui_timeline_element(self, &animation->triggers, NULL, &index, ANM2_TRIGGERS, NULL, NULL); + + /* Element configuration */ + if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_ADD)) + ImGui::OpenPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD); + + + if (ImGui::BeginPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU)) + { + if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER)) + anm2_layer_add(self->anm2); + + if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL)) + anm2_null_add(self->anm2); + + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + if + ( + selectedTimelineElementID != -1 && + ((selectedTimelineElementType == ANM2_LAYER_ANIMATION) || (selectedTimelineElementType == ANM2_NULL_ANIMATION)) + ) + { + if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_REMOVE)) + { + switch (selectedTimelineElementType) + { + case ANM2_LAYER_ANIMATION: + anm2_layer_remove(self->anm2, selectedTimelineElementID); + break; + case ANM2_NULL_ANIMATION: + anm2_null_remove(self->anm2, selectedTimelineElementID); + break; + default: + break; + } + + selectedTimelineElementID = -1; + selectedTimelineElementType = ANM2_NONE; + } + + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE); + } + else + { + selectedTimelineElementID = -1; + selectedTimelineElementType = ANM2_NONE; + } + + ImGui::EndChild(); + + ImGui::SameLine(); + + /* Animation playback and frames */ + static bool isPlaying = false; + + if (isPlaying) + { + if (ImGui::Button(STRING_IMGUI_TIMELINE_PAUSE)) + { + isPlaying = !isPlaying; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_PLAY); + + } + else + { + if (ImGui::Button(STRING_IMGUI_TIMELINE_PLAY)) + { + isPlaying = !isPlaying; + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE); + } + + /* + ImGui::SameLine(); + + if (ImGui::Button(STRING_IMGUI_TIMELINE_ADD_FRAME)) + { + + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD); + + ImGui::SameLine(); + + if (ImGui::Button(STRING_IMGUI_TIMELINE_REMOVE_FRAME)) + { + + } + */ + } + + ImGui::End(); + } void -imgui_draw(void) +imgui_draw(Imgui* self) { ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void -imgui_free(void) +imgui_free(Imgui* self) { ImGui_ImplSDL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown(); diff --git a/src/imgui.h b/src/imgui.h index 3af30fd..e29546e 100644 --- a/src/imgui.h +++ b/src/imgui.h @@ -1,15 +1,50 @@ #pragma once -#include "COMMON.h" +#include "dialog.h" +#include "packed.h" +#include "preview.h" +#include "window.h" #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#define IMGUI_ENABLE_DOCKING #include -#include -#include +#include +#include +#include #define IMGUI_DRAG_SPEED 1.0 +#define IMGUI_TASKBAR_HEIGHT 32 + +static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4}; + +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 64}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE = {150, 64}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE = {150, 64}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE = {150, 64}; + +static const ImVec2 IMGUI_TIMELINE_ELEMENT_LIST_SIZE = {260, 0}; +static const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {230, 64}; +static const ImVec2 IMGUI_TIMELINE_SPRITESHEET_ID_SIZE = {50, 20}; +static const ImVec2 IMGUI_TIMELINE_SHIFT_ARROWS_SIZE = {24, 48}; +static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {125, 20}; + +static const ImVec2 IMGUI_SPRITESHEET_SIZE = {0, 150}; +static const ImVec2 IMGUI_ICON_SIZE = {16, 16}; +static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {22, 22}; +static const ImVec2 IMGUI_DUMMY_SIZE = {1, 1}; + +#define IMGUI_TIMELINE_SHIFT_ARROWS_WIDTH (IMGUI_TIMELINE_SHIFT_ARROWS_SIZE.x * 1.35) + +struct Imgui +{ + Dialog* dialog = NULL; + Packed* packed = NULL; + Anm2* anm2 = NULL; + Preview* preview = NULL; + SDL_Window* window = NULL; +}; void imgui_init(SDL_Window* window, SDL_GLContext glContext); -void imgui_tick(State* state); -void imgui_draw(); -void imgui_free(); +void imgui_tick(Imgui* self); +void imgui_draw(Imgui* self); +void imgui_free(Imgui* self); diff --git a/src/main.cpp b/src/main.cpp index e65c763..d26f19b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,128 +1,23 @@ #include "main.h" -static void _init(State* state); -static void _draw(State* state); -static void _loop(State* state); -static void _quit(State* state); -static void _tick(State* state); - -static void -_init(State* state) -{ - Shader shader; - - printf(STRING_INFO_INIT); - - if (!SDL_Init(SDL_INIT_VIDEO)) - { - printf(STRING_ERROR_SDL_INIT, SDL_GetError()); - _quit(state); - } - - printf(STRING_INFO_SDL_INIT); - - SDL_CreateWindowAndRenderer - ( - STRING_WINDOW_TITLE, - WINDOW_WIDTH, - WINDOW_HEIGHT, - WINDOW_FLAGS, - &state->window, - &state->renderer - ); - - state->glContext = SDL_GL_CreateContext(state->window); - - if (!state->glContext) - { - printf(STRING_ERROR_GL_CONTEXT_INIT, SDL_GetError()); - _quit(state); - } - - glewInit(); - - printf(STRING_INFO_GLEW_INIT); - - shader_init(&shader, RESOURCE_SHADER_VERTEX_TEXTURE_QUAD, RESOURCE_SHADER_FRAGMENT_TEXTURE_QUAD); - - imgui_init(state->window, state->glContext); - - Anm2 anm2; - - anm2_init(&anm2, "res/anm2/005.031_key.anm2"); -} - -void -_loop(State* state) -{ - state->tick = SDL_GetTicks(); - - while (state->tick > state->lastTick + TICK_DELAY) - { - state->tick = SDL_GetTicks(); - - if (state->tick - state->lastTick < TICK_DELAY) - SDL_Delay(TICK_DELAY - (state->tick - state->lastTick)); - - _tick(state); - - imgui_tick(state); - - _draw(state); - - state->lastTick = state->tick; - } -} - -void -_tick(State* state) -{ - SDL_Event event; - - while(SDL_PollEvent(&event)) - { - ImGui_ImplSDL3_ProcessEvent(&event); - - if (event.type == SDL_EVENT_QUIT) - state->isRunning = false; - } -} - -void -_draw(State* state) -{ - glClearColor(state->clearColor.x, state->clearColor.y, state->clearColor.z, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - imgui_draw(); - - SDL_GL_SwapWindow(state->window); -} - -void -_quit(State* state) -{ - imgui_free(); - - SDL_GL_DestroyContext(state->glContext); - SDL_Quit(); - - printf(STRING_INFO_SDL_QUIT); - - printf(STRING_INFO_QUIT); -} - s32 main(s32 argc, char* argv[]) { State state; - _init(&state); + /* If anm2 given on command line, set state argument to that (will be loaded) */ + if (argc > 0 && argv[1]) + { + strncpy(state.argument, argv[1], PATH_MAX - 1); + state.isArgument = true; + } + + init(&state); while (state.isRunning) - _loop(&state); + loop(&state); - _quit(&state); + quit(&state); return EXIT_SUCCESS; -} +} \ No newline at end of file diff --git a/src/main.h b/src/main.h index 0f963f7..02bedad 100644 --- a/src/main.h +++ b/src/main.h @@ -1,11 +1,3 @@ #pragma once -#include "texture.h" -#include "shader.h" -#include "imgui.h" -#include "anm2.h" - -#define TICK_DELAY 16 -#define WINDOW_WIDTH 1600 -#define WINDOW_HEIGHT 900 -#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE \ No newline at end of file +#include "state.h" \ No newline at end of file diff --git a/src/packed.cpp b/src/packed.cpp new file mode 100644 index 0000000..6a35bcc --- /dev/null +++ b/src/packed.cpp @@ -0,0 +1,17 @@ +#include "packed.h" + +/* Loads in packed data */ +void +packed_init(Packed* self) +{ + for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++) + texture_from_data_init(&self->textures[i], (u8*)PACKED_TEXTURE_DATA[i].data, PACKED_TEXTURE_DATA[i].length); +} + +/* Frees packed data */ +void +packed_free(Packed* self) +{ + for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++) + texture_free(&self->textures[i]); +} \ No newline at end of file diff --git a/src/packed.h b/src/packed.h new file mode 100644 index 0000000..220cc89 --- /dev/null +++ b/src/packed.h @@ -0,0 +1,12 @@ +#pragma once + +#include "PACKED.h" +#include "texture.h" + +struct Packed +{ + Texture textures[PACKED_TEXTURE_COUNT]; +}; + +void packed_init(Packed* self); +void packed_free(Packed* self); \ No newline at end of file diff --git a/src/preview.cpp b/src/preview.cpp new file mode 100644 index 0000000..ce5d377 --- /dev/null +++ b/src/preview.cpp @@ -0,0 +1,48 @@ +#include "preview.h" + + +/* Initializes the preview */ +void +preview_init(Preview* self) +{ + glGenFramebuffers(1, &self->fbo); + + glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); + + glGenTextures(1, &self->texture); + glBindTexture(GL_TEXTURE_2D, self->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PREVIEW_SIZE.x, PREVIEW_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0); + + glGenRenderbuffers(1, &self->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, self->rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, PREVIEW_SIZE.x, PREVIEW_SIZE.y); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void +preview_draw(Preview* self) +{ + glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); + glViewport(0, 0, PREVIEW_SIZE.x, PREVIEW_SIZE.y); + + glClearColor(self->color.r, self->color.g, self->color.b, self->color.a); + glClear(GL_COLOR_BUFFER_BIT); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind +} + +void +preview_free(Preview* self) +{ + glDeleteTextures(1, &self->texture); + glDeleteFramebuffers(1, &self->fbo); + glDeleteRenderbuffers(1, &self->rbo); + + memset(self, '\0', sizeof(Preview)); +} \ No newline at end of file diff --git a/src/preview.h b/src/preview.h new file mode 100644 index 0000000..17c9c56 --- /dev/null +++ b/src/preview.h @@ -0,0 +1,26 @@ +#pragma once + +#include "texture.h" + +static const ivec2 PREVIEW_SIZE = {1280, 720}; + +#define PREVIEW_ZOOM_MIN 0 +#define PREVIEW_ZOOM_MAX 400 +#define PREVIEW_GRID_MIN 0 +#define PREVIEW_GRID_MAX 20 + +struct Preview +{ + GLuint texture; + GLuint fbo; + GLuint rbo; + bool isGrid; + f32 zoom = 100; + ivec2 gridSize = {10, 10}; + vec4 color = {0.69, 0.69, 0.69, 1.0f}; + vec4 gridColor = {0.35, 0.35, 0.35, 1.0f}; +}; + +void preview_init(Preview* self); +void preview_draw(Preview* self); +void preview_free(Preview* self); diff --git a/src/shader.cpp b/src/shader.cpp index 82dab41..1c58b08 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -1,17 +1,14 @@ #include "shader.h" -static bool _shader_compile(GLuint handle, const char* path); +static bool _shader_compile(GLuint handle, const char* text); static bool -_shader_compile(GLuint handle, const char* path) +_shader_compile(GLuint handle, const char* text) { - char buffer[SHADER_BUFFER_MAX]; char compileLog[SHADER_BUFFER_MAX]; s32 isCompile; - file_read(path, buffer, SHADER_BUFFER_MAX); - - const GLchar* source = buffer; + const GLchar* source = text; glShaderSource(handle, 1, &source, NULL); @@ -21,7 +18,7 @@ _shader_compile(GLuint handle, const char* path) if (!isCompile) { glGetShaderInfoLog(handle, SHADER_BUFFER_MAX, NULL, compileLog); - printf(STRING_ERROR_SHADER_INIT, path, compileLog); + printf(STRING_ERROR_SHADER_INIT, handle, compileLog); return false; } @@ -29,7 +26,7 @@ _shader_compile(GLuint handle, const char* path) } bool -shader_init(Shader* self, const char* vertexPath, const char* fragmentPath) +shader_init(Shader* self, const char* vertex, const char* fragment) { GLuint vertexHandle; GLuint fragmentHandle; @@ -42,8 +39,8 @@ shader_init(Shader* self, const char* vertexPath, const char* fragmentPath) if ( - !_shader_compile(vertexHandle, vertexPath) || - !_shader_compile(fragmentHandle, fragmentPath) + !_shader_compile(vertexHandle, vertex) || + !_shader_compile(fragmentHandle, fragment) ) return false; @@ -57,7 +54,7 @@ shader_init(Shader* self, const char* vertexPath, const char* fragmentPath) glDeleteShader(vertexHandle); glDeleteShader(fragmentHandle); - printf(STRING_INFO_SHADER_INIT, vertexPath, fragmentPath); + printf(STRING_INFO_SHADER_INIT, self->handle); return true; } diff --git a/src/shader.h b/src/shader.h index fed30ba..f85e69d 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,6 +1,6 @@ #pragma once -#include "file.h" +#include "COMMON.h" #define SHADER_BUFFER_MAX 2048 @@ -9,6 +9,6 @@ struct Shader GLuint handle = 0; }; -bool shader_init(Shader* self, const char* vertexPath, const char* fragmentPath); +bool shader_init(Shader* self, const char* vertex, const char* fragment); void shader_free(Shader* self); void shader_use(Shader* self); diff --git a/src/state.cpp b/src/state.cpp new file mode 100644 index 0000000..5f3c1b8 --- /dev/null +++ b/src/state.cpp @@ -0,0 +1,128 @@ +#include "state.h" + +static void _tick(State* state); +static void _draw(State* state); + +static void +_tick(State* state) +{ + SDL_Event event; + + while(SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + + if (event.type == SDL_EVENT_QUIT) + state->isRunning = false; + } + + dialog_tick(&state->dialog); + imgui_tick(&state->imgui); +} + +static void +_draw(State* state) +{ + preview_draw(&state->preview); + imgui_draw(&state->imgui); + + SDL_GL_SwapWindow(state->window); +} + +void +init(State* state) +{ + Shader shader; + + printf(STRING_INFO_INIT); + + if (!SDL_Init(SDL_INIT_VIDEO)) + { + printf(STRING_ERROR_SDL_INIT, SDL_GetError()); + quit(state); + } + + printf(STRING_INFO_SDL_INIT); + + SDL_CreateWindowAndRenderer + ( + STRING_WINDOW_TITLE, + WINDOW_WIDTH, + WINDOW_HEIGHT, + WINDOW_FLAGS, + &state->window, + &state->renderer + ); + + state->glContext = SDL_GL_CreateContext(state->window); + + if (!state->glContext) + { + printf(STRING_ERROR_GL_CONTEXT_INIT, SDL_GetError()); + quit(state); + } + + glewInit(); + + preview_init(&state->preview); + + printf(STRING_INFO_GLEW_INIT); + + packed_init(&state->packed); + + if (state->isArgument) + anm2_deserialize(&state->anm2, state->argument); + else + anm2_new(&state->anm2); + + window_title_from_anm2_set(state->window, &state->anm2); + + state->imgui = + { + &state->dialog, + &state->packed, + &state->anm2, + &state->preview, + state->window + }; + + imgui_init(state->window, state->glContext); + + state->dialog.anm2 = &state->anm2; +} + +void +loop(State* state) +{ + state->tick = SDL_GetTicks(); + + while (state->tick > state->lastTick + TICK_DELAY) + { + state->tick = SDL_GetTicks(); + + if (state->tick - state->lastTick < TICK_DELAY) + SDL_Delay(TICK_DELAY - (state->tick - state->lastTick)); + + _tick(state); + + _draw(state); + + state->lastTick = state->tick; + } +} + +void +quit(State* state) +{ + imgui_free(&state->imgui); + preview_free(&state->preview); + packed_free(&state->packed); + + SDL_GL_DestroyContext(state->glContext); + SDL_Quit(); + + printf(STRING_INFO_SDL_QUIT); + + printf(STRING_INFO_QUIT); +} + diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..1e79174 --- /dev/null +++ b/src/state.h @@ -0,0 +1,30 @@ +#pragma once + +#include "shader.h" +#include "imgui.h" + +#define TICK_DELAY 16 +#define WINDOW_WIDTH 1600 +#define WINDOW_HEIGHT 900 +#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE + +struct State +{ + SDL_Window* window; + SDL_Renderer* renderer; + SDL_GLContext glContext; + Imgui imgui; + Dialog dialog; + Preview preview; + Anm2 anm2; + Packed packed; + char argument[PATH_MAX] = STRING_EMPTY; + bool isArgument = false; + u64 tick = 0; + u64 lastTick = 0; + bool isRunning = true; +}; + +void init(State* state); +void loop(State* state); +void quit(State* state); \ No newline at end of file diff --git a/src/texture.cpp b/src/texture.cpp index 2139e30..fa07ba2 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -6,16 +6,9 @@ #define STB_IMAGE_IMPLEMENTATION #include -bool -texture_init(Texture* self, const char* path) +void +texture_gl_set(Texture* self, void* data) { - void* data; - - data = stbi_load(path, &self->size[0], &self->size[1], &self->channels, 4); - - if (!data) - return false; - glGenTextures(1, &self->handle); glBindTexture(GL_TEXTURE_2D, self->handle); @@ -27,7 +20,40 @@ texture_init(Texture* self, const char* path) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_2D, 0); /* unbinds */ +} + +bool +texture_from_path_init(Texture* self, const char* path) +{ + void* data; + + data = stbi_load(path, &self->size.x, &self->size.y, &self->channels, 4); + + if (!data) + { + printf(STRING_ERROR_TEXTURE_INIT, path); + return false; + } + + printf(STRING_INFO_TEXTURE_INIT, path); + + texture_gl_set(self, data); + + return true; +} + +bool +texture_from_data_init(Texture* self, const u8* data, u32 length) +{ + void* textureData; + + textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, 4); + + if (!textureData) + return false; + + texture_gl_set(self, textureData); return true; } diff --git a/src/texture.h b/src/texture.h index aad95f7..a25bc58 100644 --- a/src/texture.h +++ b/src/texture.h @@ -9,5 +9,7 @@ struct Texture s32 channels = -1; }; -bool texture_init(Texture* self, const char* path); +void texture_gl_set(Texture* self, void* data); +bool texture_from_path_init(Texture* self, const char* path); +bool texture_from_data_init(Texture* self, const u8* data, u32 length); void texture_free(Texture* self); diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 0000000..49dbafb --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,15 @@ +#include "window.h" + +/* Sets the window title from the given anm2 */ +void +window_title_from_anm2_set(SDL_Window* self, Anm2* anm2) +{ + if (!strcmp(anm2->path, STRING_EMPTY) == 0) + { + char windowTitle[WINDOW_TITLE_MAX]; + snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, anm2->path); + SDL_SetWindowTitle(self, windowTitle); + } + else + SDL_SetWindowTitle(self, STRING_WINDOW_TITLE); +} \ No newline at end of file diff --git a/src/window.h b/src/window.h new file mode 100644 index 0000000..c092061 --- /dev/null +++ b/src/window.h @@ -0,0 +1,7 @@ +#pragma once + +#include "anm2.h" + +#define WINDOW_TITLE_MAX 0xFF + PATH_MAX + +void window_title_from_anm2_set(SDL_Window* self, Anm2* anm2); \ No newline at end of file