diff --git a/CMakeLists.txt b/CMakeLists.txt index 04b7540..2d61608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ endif() project(anm2ed CXX) + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared) find_package(GLEW REQUIRED) find_package(OpenGL REQUIRED) @@ -29,6 +30,8 @@ if (WIN32) endif() add_executable(${PROJECT_NAME} ${SOURCES} ${WIN32_RESOURCES}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) + target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src) if (NOT MSVC) set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1") @@ -41,6 +44,7 @@ if(NOT MSVC) endif() target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL GLEW::GLEW SDL3::SDL3) + message("System: ${CMAKE_SYSTEM_NAME}") message("Project: ${PROJECT_NAME}") message("Build: ${CMAKE_BUILD_TYPE}") diff --git a/assets/Icon.ico b/assets/Icon.ico new file mode 100644 index 0000000..c9105c0 Binary files /dev/null and b/assets/Icon.ico differ diff --git a/assets/Icon.rc b/assets/Icon.rc new file mode 100644 index 0000000..0599d31 --- /dev/null +++ b/assets/Icon.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "Icon.ico" diff --git a/assets/atlas.kra b/assets/atlas.kra new file mode 100644 index 0000000..76039e4 Binary files /dev/null and b/assets/atlas.kra differ diff --git a/assets/atlas.png b/assets/atlas.png new file mode 100644 index 0000000..f137618 Binary files /dev/null and b/assets/atlas.png differ diff --git a/assets/bleh.png b/assets/bleh.png new file mode 100644 index 0000000..c656ed5 Binary files /dev/null and b/assets/bleh.png differ diff --git a/assets/bleh.txt b/assets/bleh.txt new file mode 100644 index 0000000..35d5689 --- /dev/null +++ b/assets/bleh.txt @@ -0,0 +1,256 @@ +unsigned char atlas_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, + 0x04, 0x03, 0x00, 0x00, 0x01, 0x8b, 0xe2, 0xd8, 0xf0, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, + 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x4c, + 0x54, 0x45, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0xff, 0xff, 0xff, 0x60, + 0x60, 0x60, 0xff, 0xff, 0xff, 0xb1, 0x03, 0xf0, 0x56, 0x00, 0x00, 0x00, + 0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde, + 0x00, 0x00, 0x04, 0x90, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xdd, 0x97, + 0x6b, 0x6e, 0x24, 0x39, 0x0e, 0x84, 0x3f, 0x2c, 0xe3, 0x40, 0x1a, 0x48, + 0x07, 0x88, 0x86, 0xe2, 0xfe, 0x67, 0x9a, 0x1f, 0x7a, 0x54, 0x96, 0xdb, + 0x9e, 0x71, 0xf7, 0x0c, 0xb0, 0x8b, 0x15, 0x0a, 0x56, 0xca, 0x4c, 0x8a, + 0xef, 0x20, 0x13, 0xd6, 0x92, 0x4a, 0xd2, 0x7e, 0x24, 0x03, 0x25, 0x5c, + 0x1a, 0xa0, 0x7b, 0x2a, 0x24, 0xe8, 0xad, 0x01, 0x8c, 0xc6, 0x87, 0x4d, + 0xf7, 0x74, 0x56, 0xe8, 0x8c, 0x7b, 0x6a, 0xe0, 0xe7, 0xd5, 0x96, 0xd7, + 0xb3, 0xf1, 0xc4, 0x86, 0x0e, 0x18, 0x72, 0x37, 0xbd, 0x9d, 0x30, 0x20, + 0xfd, 0x20, 0x0a, 0x26, 0x48, 0xb2, 0xa8, 0xbe, 0xf4, 0x77, 0x0d, 0x28, + 0x61, 0x4c, 0x59, 0x83, 0x68, 0x00, 0x46, 0x22, 0xa0, 0xa9, 0x60, 0x14, + 0x81, 0x88, 0x2c, 0x60, 0x30, 0x0a, 0x0d, 0x2c, 0x1d, 0x43, 0x1b, 0x48, + 0x8c, 0x2c, 0x53, 0x04, 0xdc, 0x43, 0x5e, 0x87, 0x24, 0x09, 0xef, 0xaf, + 0x95, 0x1b, 0xed, 0xe9, 0xb1, 0xbb, 0xec, 0x84, 0x24, 0x45, 0x20, 0x4a, + 0x26, 0xbd, 0xab, 0x4d, 0x83, 0xd3, 0x44, 0xb5, 0xeb, 0x8c, 0xbb, 0xd4, + 0xa6, 0x8d, 0xad, 0x06, 0xb4, 0x64, 0x98, 0xc8, 0x4a, 0x90, 0xcb, 0xae, + 0x36, 0x7f, 0x12, 0xc3, 0x34, 0x41, 0xb1, 0x82, 0xf1, 0x72, 0x8b, 0xbc, + 0x7e, 0x11, 0xc1, 0xc1, 0x3f, 0x29, 0x87, 0x83, 0x86, 0x20, 0xd0, 0x70, + 0x43, 0x4d, 0x06, 0x44, 0x68, 0xc8, 0x22, 0xa1, 0xf7, 0xf3, 0x36, 0x70, + 0x52, 0xe4, 0xeb, 0x43, 0xde, 0x28, 0xbe, 0x87, 0xed, 0xb7, 0x4d, 0xd1, + 0xdf, 0xdc, 0xf6, 0xca, 0x90, 0x27, 0x09, 0x40, 0x2b, 0x3b, 0x34, 0x96, + 0x9e, 0x52, 0x92, 0x95, 0x27, 0xf1, 0x8a, 0x79, 0x8c, 0x82, 0x95, 0x44, + 0x82, 0xea, 0xbd, 0xcb, 0x9a, 0xe2, 0x79, 0xf1, 0x89, 0xf8, 0x7b, 0xda, + 0x86, 0x4a, 0x5c, 0x49, 0x5b, 0x72, 0x02, 0xcc, 0x6e, 0xf5, 0x0c, 0x1d, + 0x79, 0x2e, 0xa9, 0x2c, 0x90, 0xad, 0xf4, 0x29, 0x64, 0xa5, 0xa6, 0xba, + 0xd0, 0x75, 0xb2, 0xe6, 0x8e, 0xc0, 0x6c, 0xc8, 0x5b, 0xee, 0xd3, 0x0e, + 0xdf, 0xbf, 0x1d, 0x35, 0x8e, 0x6b, 0xa4, 0xc4, 0x4a, 0x82, 0x62, 0xe4, + 0x4c, 0x05, 0xdd, 0x12, 0x78, 0xfa, 0x4e, 0xb1, 0x25, 0x1b, 0xd2, 0x88, + 0xa9, 0x75, 0x71, 0x01, 0xdd, 0x30, 0x0d, 0x0c, 0x98, 0x60, 0x0c, 0x95, + 0x86, 0x68, 0x35, 0xa7, 0x35, 0x66, 0x23, 0x6d, 0x11, 0x14, 0x50, 0x2d, + 0x76, 0xcd, 0x55, 0x6d, 0x8a, 0x02, 0xd5, 0x3f, 0x3a, 0x08, 0x45, 0x66, + 0x24, 0x69, 0x0f, 0xf7, 0x5d, 0x9f, 0x1e, 0x02, 0xbf, 0x4b, 0x58, 0xe6, + 0x7c, 0x24, 0xe4, 0xae, 0x9f, 0x39, 0xbc, 0xca, 0xf9, 0x13, 0x19, 0xd7, + 0xd9, 0x5f, 0x0a, 0xaf, 0xc4, 0x9f, 0x12, 0x28, 0xf3, 0xa9, 0x81, 0x5f, + 0xaf, 0x67, 0x68, 0xb3, 0xe2, 0xad, 0x4e, 0x43, 0xc8, 0xc9, 0x0f, 0x6f, + 0x2b, 0x8c, 0x1a, 0x4a, 0x37, 0xc6, 0x5e, 0xff, 0x60, 0x21, 0xef, 0xda, + 0x8d, 0x85, 0x93, 0x78, 0x0a, 0xfa, 0xe8, 0x8d, 0x61, 0xe4, 0x95, 0xf5, + 0x4e, 0xe6, 0xaa, 0xee, 0xde, 0x18, 0xc7, 0x76, 0x59, 0x8c, 0xfc, 0xb8, + 0x6a, 0x86, 0xe3, 0x94, 0x6f, 0x28, 0xce, 0x85, 0x31, 0x3d, 0x6c, 0x51, + 0xd2, 0x2a, 0x2b, 0xaf, 0x88, 0x81, 0x61, 0xd4, 0x87, 0x01, 0x0b, 0x99, + 0xe1, 0xcd, 0xd5, 0x07, 0xb3, 0x4f, 0xaf, 0x6a, 0xb7, 0xdd, 0x64, 0xab, + 0x19, 0x87, 0xe1, 0xd1, 0x27, 0xd0, 0x2c, 0x6c, 0x69, 0xc5, 0x50, 0x32, + 0x63, 0x76, 0x83, 0x3c, 0x5c, 0xc6, 0xdd, 0xab, 0x72, 0x32, 0xd1, 0x1c, + 0x14, 0x43, 0xf2, 0xe8, 0x2f, 0x80, 0x65, 0x29, 0x00, 0x32, 0xe3, 0x59, + 0x51, 0x7f, 0x15, 0x0f, 0x9f, 0xaa, 0xb9, 0xf7, 0x54, 0x43, 0x1e, 0xbb, + 0xa7, 0x40, 0x76, 0xbd, 0xc3, 0x68, 0x04, 0x6c, 0x16, 0x57, 0x56, 0x0d, + 0x9a, 0x9a, 0x2c, 0x82, 0xee, 0x55, 0xf6, 0xe9, 0x77, 0x83, 0x38, 0xbe, + 0x3a, 0xda, 0xaf, 0xb2, 0x0f, 0x0b, 0x8c, 0xe2, 0x0d, 0xca, 0x47, 0xdb, + 0x38, 0xce, 0x0e, 0x67, 0xa8, 0xfc, 0x8c, 0x41, 0x6f, 0x21, 0x39, 0x5d, + 0xa9, 0xed, 0x72, 0x5e, 0xc5, 0xb9, 0x6c, 0x09, 0x10, 0x6b, 0x42, 0xcd, + 0xa5, 0x94, 0x88, 0x77, 0x67, 0x12, 0x35, 0xa7, 0xc7, 0x98, 0x30, 0x59, + 0x1c, 0x95, 0x79, 0x10, 0xa7, 0x39, 0x41, 0x99, 0xa7, 0x3e, 0x8e, 0x0e, + 0xad, 0x65, 0x0e, 0x90, 0x97, 0x65, 0xba, 0x39, 0xdd, 0x56, 0x22, 0x0c, + 0xb6, 0xc9, 0xfd, 0x13, 0x87, 0x5b, 0xa6, 0xf7, 0xde, 0x3f, 0xb1, 0x01, + 0x92, 0xcc, 0xff, 0x19, 0x42, 0xfc, 0x39, 0x21, 0xba, 0x15, 0xf4, 0x4e, + 0xf0, 0xc5, 0xf5, 0x27, 0xe1, 0xa2, 0x92, 0xbf, 0x4b, 0xf8, 0xf2, 0xaa, + 0xbf, 0x12, 0xfe, 0xa5, 0xba, 0xff, 0xc4, 0x25, 0x7a, 0x87, 0xc4, 0xc7, + 0xe4, 0xf2, 0xce, 0xf0, 0x20, 0xa8, 0x7d, 0x41, 0x78, 0x07, 0x3f, 0x3e, + 0x01, 0xd4, 0x31, 0xef, 0x64, 0x70, 0x5a, 0x78, 0x4c, 0x25, 0xc9, 0x1f, + 0x2f, 0x8c, 0x46, 0xc7, 0x67, 0xfb, 0xc2, 0xae, 0x05, 0x8b, 0x64, 0x41, + 0xa4, 0x92, 0x64, 0xc6, 0x0b, 0xa0, 0x59, 0xe7, 0xc5, 0xb1, 0x7b, 0xa2, + 0x56, 0x93, 0x90, 0x51, 0x90, 0xcf, 0x0b, 0x6b, 0x12, 0x82, 0xea, 0x23, + 0x99, 0xbd, 0xbd, 0x33, 0x4c, 0xd0, 0xae, 0xd1, 0xc3, 0x50, 0xd9, 0xda, + 0x56, 0xef, 0xbd, 0x37, 0x60, 0x58, 0x73, 0x69, 0xb2, 0x8d, 0xc9, 0x02, + 0xda, 0x24, 0xf9, 0x21, 0x33, 0x96, 0xdf, 0xaa, 0xaa, 0x7a, 0x15, 0x8a, + 0x41, 0xf1, 0xcb, 0x68, 0x76, 0xff, 0x7a, 0xcc, 0xc8, 0xbb, 0xd7, 0x54, + 0xb1, 0xba, 0x33, 0x95, 0x46, 0xf8, 0xc7, 0xab, 0x3e, 0x04, 0xf7, 0x9e, + 0xef, 0x8c, 0x96, 0x0f, 0x4d, 0x7d, 0x25, 0xd9, 0x64, 0x4d, 0x0c, 0x23, + 0x1f, 0xd3, 0x60, 0x07, 0xb2, 0x92, 0xa4, 0x51, 0xe9, 0x7d, 0x24, 0x6d, + 0x8f, 0x6a, 0x54, 0x76, 0x42, 0x79, 0xb4, 0x9a, 0x82, 0x35, 0x78, 0x42, + 0xcd, 0x31, 0xe6, 0x1c, 0x4d, 0xb3, 0x8f, 0xf4, 0xe1, 0xc3, 0x70, 0xe2, + 0x13, 0x5b, 0xa7, 0x2d, 0x24, 0x09, 0x65, 0xb5, 0xb2, 0x9a, 0xa6, 0x02, + 0x87, 0xe1, 0x11, 0x1f, 0x4f, 0x18, 0xb2, 0xcc, 0x98, 0xc8, 0x50, 0x73, + 0x81, 0x71, 0xd2, 0x7b, 0x5f, 0x83, 0xa0, 0xc6, 0x34, 0x09, 0xd5, 0x97, + 0x4f, 0xcf, 0xa4, 0xee, 0x8f, 0x1d, 0x62, 0xcf, 0x13, 0x4d, 0xad, 0x4c, + 0x4f, 0x12, 0xda, 0x6e, 0xc2, 0xbc, 0xf6, 0x6f, 0x34, 0x4f, 0xbf, 0xef, + 0x5b, 0xa4, 0x9c, 0xdd, 0xb7, 0xd7, 0xfe, 0x1c, 0xc0, 0x6f, 0xf5, 0xf4, + 0xc6, 0xc9, 0xfb, 0x3d, 0x00, 0x9c, 0x79, 0xc6, 0x3c, 0xe5, 0x6f, 0x2c, + 0x56, 0xeb, 0xed, 0x26, 0x97, 0x80, 0xb1, 0x18, 0x9f, 0xb8, 0xf3, 0x78, + 0x40, 0xab, 0x1b, 0xe4, 0xe2, 0x88, 0x32, 0xce, 0x88, 0x75, 0x24, 0xec, + 0xd4, 0xd6, 0x7e, 0x7f, 0x00, 0x4a, 0x23, 0xda, 0x83, 0x28, 0x9c, 0x31, + 0xc3, 0xbb, 0x0b, 0x5a, 0x7e, 0x7d, 0x21, 0x8d, 0x2d, 0xdb, 0xbb, 0x84, + 0x6f, 0x47, 0x7c, 0x39, 0x70, 0x4b, 0x58, 0x4a, 0x8c, 0xfb, 0xf4, 0x60, + 0xf0, 0xc3, 0x81, 0x3a, 0x36, 0x18, 0xa8, 0x61, 0xfe, 0xc5, 0x55, 0x79, + 0x9b, 0xfd, 0xeb, 0x60, 0xc7, 0x09, 0x5d, 0xdf, 0x65, 0x3f, 0x97, 0xf7, + 0xe6, 0xca, 0x9d, 0xca, 0x3c, 0xde, 0x3b, 0x1f, 0x56, 0xdb, 0xb6, 0xbe, + 0x01, 0x56, 0x64, 0xc2, 0x29, 0xbb, 0xd1, 0xe7, 0xe8, 0x2f, 0xc7, 0x3f, + 0x6c, 0x6b, 0xc9, 0xdc, 0x5f, 0x8a, 0x35, 0xa1, 0xa7, 0xa7, 0x6f, 0x14, + 0x19, 0xde, 0x05, 0xf6, 0x3e, 0x1f, 0xfb, 0xbc, 0x2f, 0x80, 0xde, 0x7b, + 0xef, 0x1d, 0x91, 0xcc, 0x6c, 0x8d, 0xbd, 0x5d, 0xbd, 0x20, 0x69, 0xa7, + 0xfb, 0xc9, 0x8a, 0x06, 0x45, 0x21, 0xab, 0x4f, 0xf3, 0x48, 0x83, 0x3d, + 0xe1, 0x40, 0x66, 0x2f, 0x1a, 0x63, 0xab, 0xba, 0x25, 0x40, 0x65, 0xb9, + 0x7e, 0xe1, 0xfe, 0x83, 0xe1, 0x17, 0x57, 0xdf, 0xab, 0x7d, 0xf1, 0xd1, + 0xc5, 0xc7, 0x4b, 0xc7, 0xca, 0xde, 0xf9, 0xdb, 0x0c, 0xfc, 0xff, 0x31, + 0xdc, 0x46, 0xf7, 0x4d, 0x06, 0x99, 0xf8, 0x3f, 0xf9, 0x3e, 0xc3, 0x6a, + 0x01, 0x6f, 0x18, 0xf9, 0x15, 0x43, 0x7e, 0x5e, 0xfe, 0x77, 0x25, 0xfc, + 0xa6, 0x0d, 0xbf, 0xec, 0xa5, 0xff, 0x4a, 0xa4, 0x6b, 0x31, 0xb4, 0x6f, + 0x33, 0x9c, 0xb6, 0xfb, 0xed, 0x7a, 0x38, 0x1d, 0xe8, 0x17, 0x18, 0xa8, + 0xe7, 0xf7, 0x6e, 0xfb, 0x9b, 0x92, 0xfe, 0x13, 0xdc, 0x2c, 0x81, 0x38, + 0xc8, 0xfa, 0xf8, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82 +}; +unsigned int atlas_png_len = 1288; +unsigned char atlas_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x0e, 0xcb, 0xf5, 0x55, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, + 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x06, 0x52, 0x49, 0x44, + 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0x8b, 0x4e, 0xeb, 0x30, 0x0c, 0x65, + 0xd5, 0x3e, 0x14, 0xbe, 0x0c, 0xfe, 0x74, 0x97, 0x4a, 0x04, 0x05, 0x5f, + 0x3f, 0x8e, 0x1d, 0xa7, 0xe9, 0x56, 0x5b, 0x42, 0x6c, 0xb4, 0x69, 0x52, + 0x1f, 0xbf, 0xf3, 0xe0, 0xed, 0xad, 0xe8, 0x9c, 0xf4, 0x20, 0xf4, 0xcc, + 0xef, 0x10, 0x69, 0xfb, 0xfe, 0xfe, 0xfe, 0xfb, 0xee, 0xfb, 0xe7, 0x99, + 0x7d, 0xa9, 0x8c, 0x7f, 0x46, 0x20, 0x32, 0xc6, 0xee, 0x6d, 0xdb, 0x83, + 0xd5, 0xc0, 0x93, 0x80, 0x13, 0x01, 0xd5, 0x3a, 0xd4, 0xae, 0xf5, 0xd2, + 0x22, 0xd1, 0xcf, 0xc0, 0x86, 0x24, 0xd1, 0x62, 0xea, 0x03, 0xa4, 0x19, + 0x00, 0x34, 0xc6, 0x5b, 0xbc, 0x6a, 0xd7, 0xdd, 0x2a, 0x94, 0xf1, 0xe2, + 0x3b, 0x00, 0xfb, 0xcf, 0x08, 0x23, 0xb8, 0x71, 0x3c, 0x82, 0x34, 0xcb, + 0x04, 0x71, 0xc2, 0x47, 0x9f, 0xe9, 0x02, 0x20, 0x4b, 0xf2, 0xa8, 0x34, + 0xa3, 0xd2, 0x3f, 0x0a, 0x52, 0x54, 0x13, 0x22, 0x82, 0x82, 0x8e, 0xa7, + 0xb5, 0xbb, 0xf5, 0x2a, 0x71, 0xfb, 0x26, 0x4d, 0x5d, 0xda, 0x75, 0x7a, + 0x7f, 0xd4, 0x37, 0x70, 0xfd, 0x71, 0x7d, 0x5b, 0xf7, 0xa9, 0xaa, 0x1d, + 0xe8, 0x37, 0xd2, 0x7f, 0x7f, 0x2f, 0x32, 0x8e, 0xf6, 0x4c, 0x15, 0x00, + 0x8e, 0xf9, 0x33, 0x00, 0xb0, 0xfa, 0xb7, 0x18, 0x40, 0x4d, 0xc4, 0xe7, + 0xe7, 0x27, 0x7b, 0xdf, 0xc7, 0xc7, 0xc7, 0x9f, 0xef, 0x5f, 0x5f, 0x5f, + 0xb7, 0x15, 0x00, 0xec, 0xe3, 0x68, 0x7d, 0x6f, 0x88, 0x34, 0xa1, 0x12, + 0x33, 0x4a, 0x3d, 0x23, 0xdb, 0x67, 0xca, 0x34, 0x84, 0xf6, 0x36, 0x7d, + 0x3b, 0xfa, 0xdd, 0x6b, 0xfa, 0x50, 0x3f, 0x40, 0xc7, 0x2d, 0xf1, 0xad, + 0x07, 0xde, 0x34, 0x29, 0x9a, 0x59, 0xca, 0xd2, 0x80, 0x7d, 0xd0, 0x4d, + 0x6a, 0x2d, 0xad, 0xf0, 0x68, 0x82, 0x44, 0x9a, 0xe4, 0x6b, 0x63, 0xe9, + 0x25, 0x17, 0xd5, 0x96, 0xfe, 0x79, 0x5c, 0xfb, 0x8d, 0x76, 0xe4, 0x65, + 0xbe, 0x57, 0x32, 0xbd, 0x0c, 0xf1, 0x30, 0xdf, 0x7a, 0x4e, 0x7f, 0x9d, + 0x6a, 0x5b, 0x23, 0xca, 0xfc, 0xfe, 0xf3, 0x7e, 0xcd, 0x13, 0x19, 0xb5, + 0x3e, 0x1b, 0xa9, 0xe3, 0x43, 0x23, 0x84, 0xd1, 0x30, 0x54, 0x7a, 0x06, + 0x17, 0xef, 0x7b, 0xd5, 0x9e, 0xfb, 0xdd, 0xc7, 0xe7, 0xf4, 0x77, 0xf4, + 0xdd, 0x35, 0x1e, 0xa4, 0x65, 0x90, 0x2b, 0x12, 0xb1, 0x8c, 0xac, 0x5b, + 0x03, 0x43, 0x32, 0x53, 0xa7, 0x01, 0xe0, 0x0c, 0xa5, 0x88, 0x8c, 0x7e, + 0x10, 0xa6, 0x47, 0x92, 0x2e, 0xed, 0xbe, 0x74, 0xfe, 0xbc, 0x42, 0x31, + 0xce, 0xe3, 0x98, 0x8b, 0x8a, 0x8a, 0xb2, 0xcd, 0x21, 0xd2, 0x7e, 0x9b, + 0xa5, 0xf6, 0xd1, 0x88, 0xe6, 0x2c, 0xfd, 0xd3, 0x44, 0x2c, 0xc2, 0xfc, + 0x91, 0xf6, 0x87, 0x3b, 0x71, 0x24, 0x92, 0xd2, 0x98, 0xd9, 0xd7, 0xe1, + 0x81, 0x68, 0xec, 0x11, 0x79, 0x87, 0x51, 0x1e, 0x1c, 0xca, 0x7c, 0xcf, + 0x35, 0x4f, 0xba, 0x2f, 0x55, 0x48, 0xad, 0x4a, 0xeb, 0x0a, 0x6d, 0x0c, + 0x33, 0x3e, 0xa2, 0xc2, 0x54, 0xc2, 0xe8, 0x8f, 0x25, 0x0d, 0x5e, 0x09, + 0xd3, 0xfa, 0x1f, 0x8d, 0xea, 0xb2, 0x4c, 0xa8, 0x1b, 0x00, 0x54, 0x85, + 0xad, 0xf8, 0xd8, 0x62, 0xbe, 0x14, 0x47, 0x47, 0x01, 0x50, 0x27, 0x3a, + 0x04, 0x93, 0x20, 0xf5, 0xcf, 0x4d, 0x27, 0xd2, 0xbf, 0xa1, 0x02, 0x84, + 0x08, 0x1b, 0x5b, 0xfe, 0x95, 0x6a, 0x36, 0x5c, 0x9d, 0xc4, 0x6a, 0xd7, + 0x97, 0x86, 0xdb, 0x75, 0xee, 0x39, 0xfb, 0x33, 0x3c, 0x75, 0x7a, 0xad, + 0x3c, 0x6e, 0x55, 0x75, 0xa5, 0x62, 0x62, 0x2b, 0x25, 0x73, 0x45, 0xb3, + 0x56, 0x54, 0xeb, 0xcb, 0xcd, 0x5a, 0xe1, 0x50, 0x2b, 0xc2, 0xfd, 0x77, + 0x7f, 0x8f, 0x70, 0xff, 0x99, 0xce, 0x64, 0x49, 0x13, 0xce, 0x92, 0xf4, + 0x73, 0xce, 0x4f, 0x92, 0xd6, 0x11, 0x0d, 0xf0, 0xfa, 0x1d, 0x69, 0xf6, + 0x8a, 0x8b, 0x5e, 0xb8, 0x68, 0xe6, 0x91, 0x48, 0xac, 0xed, 0xa4, 0x1d, + 0xd1, 0x81, 0x71, 0xe0, 0xf4, 0x2a, 0xac, 0x31, 0x5f, 0xb2, 0xd7, 0x1e, + 0x00, 0x90, 0x32, 0x00, 0xe2, 0x90, 0x4f, 0x01, 0x00, 0x32, 0x01, 0x61, + 0x5d, 0xa7, 0x0c, 0x45, 0x0b, 0x77, 0x51, 0x0d, 0xb0, 0x6c, 0x2d, 0x12, + 0x71, 0x49, 0x8c, 0xb6, 0xfc, 0x9c, 0xd5, 0x7e, 0xa8, 0x6e, 0x24, 0xbd, + 0x84, 0x95, 0x54, 0x68, 0x8e, 0x56, 0x32, 0x4b, 0x5e, 0x27, 0xac, 0x45, + 0x31, 0x52, 0xff, 0x5a, 0x9e, 0x70, 0x16, 0x27, 0xbc, 0xd1, 0x09, 0x93, + 0xd6, 0xd1, 0x8d, 0x50, 0x1b, 0x04, 0xe7, 0x68, 0xb9, 0x89, 0x06, 0x6b, + 0x0a, 0xd0, 0x33, 0x09, 0x43, 0xfb, 0x6d, 0xe3, 0xd4, 0x9e, 0x45, 0x1d, + 0xa2, 0xd5, 0x47, 0xbb, 0x7f, 0x7f, 0xd6, 0xfe, 0xfc, 0x7e, 0xf2, 0x65, + 0xff, 0xdc, 0x3b, 0x52, 0xce, 0xd9, 0x7b, 0x26, 0xa8, 0xd4, 0x7b, 0xb5, + 0x50, 0xd4, 0x93, 0x41, 0x6a, 0x0e, 0xd9, 0x93, 0x88, 0x71, 0xa6, 0x8d, + 0x33, 0x8b, 0x52, 0x1f, 0x88, 0x4f, 0x3a, 0x73, 0x69, 0x7d, 0x59, 0x29, + 0x82, 0xfa, 0x18, 0xcd, 0x27, 0x79, 0x4a, 0x11, 0x88, 0x56, 0x3c, 0x6d, + 0x29, 0x62, 0x66, 0x31, 0x2e, 0x23, 0x93, 0xf5, 0x48, 0xfe, 0x28, 0xf3, + 0x5e, 0x65, 0x3e, 0xe5, 0xe5, 0xcb, 0xd1, 0x45, 0x67, 0xa4, 0xa8, 0x09, + 0xe1, 0x12, 0x1c, 0xed, 0x73, 0x76, 0xfb, 0xd9, 0xb5, 0xf7, 0x8c, 0x67, + 0x53, 0x33, 0x78, 0xf3, 0x76, 0x22, 0xd5, 0x5b, 0x46, 0x56, 0xcf, 0x8d, + 0xb6, 0x47, 0x6a, 0x42, 0x99, 0x00, 0x44, 0x9f, 0x4f, 0x85, 0x78, 0x0f, + 0x6b, 0x37, 0x2f, 0xc2, 0x59, 0xd1, 0xc0, 0xa8, 0x34, 0x65, 0x66, 0xd2, + 0xb3, 0x35, 0xa1, 0x59, 0x10, 0xd7, 0xc2, 0x2c, 0x69, 0xe5, 0x58, 0x46, + 0x48, 0x96, 0x0d, 0x80, 0xa7, 0xf6, 0x82, 0x2e, 0x3b, 0xc9, 0xdc, 0xe8, + 0xc1, 0xd5, 0xcb, 0x42, 0x21, 0x93, 0xa7, 0x3c, 0xe1, 0x01, 0x2f, 0x23, + 0xd2, 0x18, 0xdd, 0x1b, 0x30, 0x63, 0x8f, 0x01, 0xb7, 0x27, 0x82, 0x15, + 0x80, 0x68, 0xb6, 0x8b, 0x30, 0x10, 0xd1, 0x80, 0x8c, 0x49, 0xef, 0x15, + 0x00, 0x44, 0x98, 0xaf, 0xee, 0x8e, 0xf1, 0xee, 0x46, 0xf1, 0xdc, 0x4b, + 0xd5, 0x70, 0x86, 0x0f, 0xf0, 0x26, 0x69, 0x52, 0x35, 0x77, 0xf4, 0xdd, + 0x5d, 0xcc, 0x47, 0x6a, 0xe7, 0x11, 0x0d, 0xd0, 0x26, 0x43, 0x66, 0x85, + 0x8e, 0xd1, 0xad, 0x47, 0x99, 0x5b, 0x96, 0xdc, 0xcc, 0xb7, 0x4c, 0x90, + 0xb6, 0xf7, 0x09, 0x51, 0x5f, 0xd4, 0x84, 0x78, 0x19, 0xec, 0xda, 0x81, + 0x98, 0x08, 0x80, 0x37, 0x87, 0x82, 0xf6, 0xb9, 0x69, 0xd5, 0x4a, 0x24, + 0x9a, 0x88, 0xda, 0xc8, 0x55, 0xa9, 0x7d, 0x14, 0x00, 0x14, 0x20, 0x17, + 0xf3, 0x11, 0x67, 0x19, 0xdd, 0x25, 0x99, 0x15, 0x57, 0x67, 0x56, 0x17, + 0x47, 0x7c, 0x80, 0xc7, 0x3f, 0xa0, 0xcc, 0xbf, 0x69, 0x8c, 0x90, 0x36, + 0x9c, 0x69, 0x9b, 0xf9, 0xd0, 0x89, 0x87, 0x3e, 0xfb, 0xb5, 0x32, 0xe1, + 0xc8, 0xf3, 0x2d, 0x60, 0xd1, 0x77, 0x40, 0x33, 0x5f, 0x8e, 0x77, 0x08, + 0x70, 0x5b, 0xa4, 0x51, 0x76, 0xd6, 0x69, 0xf5, 0xdb, 0x66, 0xe5, 0xa4, + 0xef, 0x67, 0xa8, 0x9d, 0xf5, 0x33, 0x76, 0x9e, 0xb1, 0x6d, 0xd2, 0xcb, + 0x8e, 0x48, 0x17, 0x52, 0x60, 0xe3, 0x24, 0x30, 0x52, 0x7c, 0xf3, 0x92, + 0xb4, 0x7b, 0x71, 0xff, 0x5b, 0x64, 0x47, 0xa6, 0x26, 0x28, 0x45, 0x45, + 0xd7, 0xa2, 0x88, 0xb6, 0x6e, 0xcf, 0xf4, 0x82, 0xd6, 0x1a, 0xd0, 0x91, + 0xd2, 0x30, 0xd2, 0xef, 0x0a, 0x80, 0x7e, 0x07, 0x10, 0x5d, 0x1d, 0xcc, + 0x9d, 0x14, 0xe2, 0x79, 0x16, 0x77, 0x0e, 0xcf, 0x08, 0x33, 0xb4, 0xb5, + 0x3d, 0x56, 0xff, 0xa3, 0x0c, 0x0e, 0x8d, 0x7d, 0xe4, 0xc5, 0xb9, 0x75, + 0xa6, 0x68, 0x92, 0xc6, 0xad, 0x4f, 0x8d, 0x68, 0x8a, 0x96, 0x34, 0x66, + 0xd6, 0xbd, 0x34, 0x06, 0x87, 0xf3, 0x18, 0x74, 0x6d, 0xbf, 0x87, 0x19, + 0xd6, 0x20, 0xa3, 0x4c, 0x97, 0x18, 0xcf, 0xad, 0x6a, 0xf3, 0x08, 0x41, + 0x64, 0xee, 0xc3, 0x03, 0xc0, 0xdd, 0x52, 0x55, 0xee, 0xe4, 0x11, 0xe4, + 0x58, 0x80, 0x3e, 0xc1, 0xfa, 0xbe, 0xf7, 0x8f, 0xca, 0x5b, 0xe1, 0x5e, + 0x24, 0x94, 0x6b, 0xcb, 0xc1, 0xe9, 0x12, 0x78, 0xc9, 0xee, 0x6b, 0x63, + 0x88, 0x1e, 0x54, 0x22, 0xdd, 0xeb, 0x3e, 0xff, 0xc2, 0xda, 0x58, 0x81, + 0xda, 0x43, 0xcd, 0x27, 0x58, 0x7d, 0x5a, 0xdf, 0x25, 0xc9, 0x97, 0xc6, + 0x8e, 0xae, 0x09, 0x8a, 0xbc, 0x6b, 0xaa, 0x0f, 0xe0, 0x54, 0x35, 0x83, + 0xf9, 0xd4, 0x27, 0x64, 0x02, 0xe0, 0x3d, 0xad, 0xcb, 0x6b, 0x2e, 0xbd, + 0x66, 0x2b, 0x2d, 0xcc, 0xcb, 0x60, 0x3e, 0x8d, 0x3a, 0xb4, 0x4d, 0x76, + 0xd1, 0x10, 0x12, 0x29, 0x8d, 0x23, 0xa1, 0xe4, 0xc8, 0x7c, 0x72, 0x38, + 0x7b, 0xa6, 0x36, 0x94, 0x2b, 0xc6, 0x35, 0x7b, 0x89, 0x9c, 0x30, 0x15, + 0x5d, 0xb6, 0xe2, 0x7d, 0x39, 0xba, 0xe5, 0x47, 0x3b, 0xbb, 0xa8, 0xad, + 0x76, 0xf6, 0xd8, 0x74, 0xce, 0x07, 0x4a, 0x6d, 0x91, 0x62, 0xa1, 0x5a, + 0xac, 0xd3, 0x54, 0x0f, 0x65, 0x8e, 0x25, 0xf9, 0x23, 0x89, 0x11, 0xbd, + 0x66, 0x1d, 0x37, 0xa3, 0x6d, 0xb7, 0x1a, 0x29, 0x31, 0x7b, 0x35, 0x07, + 0xb9, 0xbe, 0x59, 0x92, 0x8d, 0x46, 0x3c, 0x9c, 0x84, 0x66, 0x2d, 0xb8, + 0xca, 0x2a, 0x86, 0x79, 0xc6, 0x72, 0xd4, 0x91, 0x6d, 0x77, 0x6a, 0x6e, + 0x34, 0x55, 0xf7, 0x4a, 0xcf, 0x51, 0xcc, 0xe7, 0xfa, 0xe9, 0x77, 0x28, + 0x4a, 0x87, 0xf8, 0x21, 0x1a, 0x37, 0xfb, 0x1d, 0xee, 0x9c, 0xcd, 0xef, + 0x6d, 0x67, 0x1b, 0xcc, 0x0e, 0x44, 0xaf, 0x0d, 0x54, 0x33, 0xe8, 0xae, + 0x95, 0x68, 0x2c, 0x9f, 0xe1, 0x23, 0x22, 0x4c, 0xa7, 0xe0, 0x69, 0xe7, + 0xc3, 0x4d, 0x71, 0xc2, 0xda, 0xcb, 0xf6, 0xcc, 0x8f, 0x9a, 0xa4, 0x57, + 0xaf, 0x82, 0x0e, 0x39, 0xe1, 0xa2, 0x75, 0x74, 0xcb, 0x88, 0x6d, 0x47, + 0x34, 0xe2, 0x2c, 0x1a, 0xb5, 0xe2, 0x9c, 0xd4, 0xdf, 0x28, 0xa8, 0x68, + 0x1d, 0xdd, 0xa5, 0xc2, 0x15, 0xea, 0xec, 0x8a, 0x4a, 0x03, 0x0a, 0x80, + 0x55, 0xfe, 0xa3, 0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x02, 0xa0, + 0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x78, 0x26, 0x7c, 0x65, 0xd2, + 0xf6, 0xac, 0x1d, 0x51, 0x13, 0xba, 0x17, 0xe3, 0xed, 0x7b, 0x66, 0x02, + 0xb1, 0x5d, 0x9d, 0xf9, 0xdc, 0x34, 0x26, 0xfd, 0xdb, 0xcc, 0x63, 0x10, + 0x2e, 0xed, 0x03, 0x00, 0xc9, 0x9e, 0x6e, 0x82, 0xb6, 0xab, 0x4a, 0x3f, + 0x62, 0x56, 0xf6, 0x5b, 0x46, 0xff, 0x55, 0x57, 0x01, 0x50, 0x51, 0xd0, + 0x7a, 0x67, 0xea, 0x6d, 0x37, 0xb2, 0x09, 0xdc, 0x69, 0xe2, 0x4a, 0x03, + 0x2a, 0x11, 0x2b, 0x13, 0xf4, 0xf2, 0x91, 0x0c, 0xe4, 0x84, 0xb5, 0x49, + 0xf9, 0x99, 0xf9, 0x40, 0x69, 0x40, 0x99, 0xa0, 0x35, 0x1a, 0x03, 0x66, + 0xc2, 0xd3, 0xb3, 0xe1, 0x4b, 0x6b, 0x00, 0x00, 0xc2, 0xf4, 0x83, 0x00, + 0x2f, 0x59, 0x0b, 0xea, 0xd7, 0xc0, 0x22, 0x07, 0x48, 0x1d, 0xb2, 0x3a, + 0xba, 0x4c, 0xd1, 0xb8, 0x83, 0x2f, 0x00, 0x92, 0x80, 0xa8, 0xa5, 0x89, + 0x15, 0x05, 0x15, 0x15, 0x00, 0x05, 0x40, 0x51, 0x01, 0x70, 0x15, 0xf2, + 0x1e, 0x5a, 0x4d, 0x8f, 0xa1, 0x79, 0x95, 0xa4, 0x6c, 0xe9, 0x7f, 0xbc, + 0x88, 0xfe, 0x3f, 0xdf, 0x02, 0xe0, 0x60, 0x10, 0xac, 0x4d, 0xd3, 0x91, + 0x3e, 0xaf, 0x0c, 0xc0, 0x1f, 0x1f, 0xd0, 0xfe, 0x89, 0x99, 0x44, 0x47, + 0x6d, 0xdd, 0xbc, 0xb4, 0x13, 0x96, 0x40, 0x28, 0xe6, 0x1f, 0x18, 0x05, + 0x51, 0x10, 0x8a, 0xf9, 0xf3, 0x48, 0xac, 0x05, 0xfd, 0x30, 0xfc, 0xd1, + 0x7d, 0x7e, 0x69, 0x5a, 0xb5, 0x71, 0xfa, 0x1f, 0xcc, 0xd3, 0x69, 0xcc, + 0x40, 0x9f, 0x7b, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82 +}; +unsigned int atlas_png_len = 1696; diff --git a/assets/win_icon.ico b/assets/win_icon.ico deleted file mode 100644 index 624177e..0000000 Binary files a/assets/win_icon.ico and /dev/null differ diff --git a/assets/win_icon.rc b/assets/win_icon.rc deleted file mode 100644 index dacd6e9..0000000 --- a/assets/win_icon.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "win_icon.ico" \ No newline at end of file diff --git a/src/COMMON.h b/src/COMMON.h index e48d386..5a4c0dd 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -13,17 +13,19 @@ #include #include #include +#include #include -#include +#include +#include #include #include -#include #include +#include +#include #include #include #include - -#include "STRINGS.h" +#include typedef uint8_t u8; typedef uint16_t u16; @@ -46,57 +48,17 @@ using namespace glm; #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)) - -// Converts a string to a bool -static inline bool string_to_bool(const std::string& string) -{ - return string == "true" || string == "1"; -}; - -// Given a file path, sets the working directory to it -static inline void working_directory_from_file_set(const std::string& path) -{ - std::filesystem::path filePath = path; - std::filesystem::path parentPath = filePath.parent_path(); - std::filesystem::current_path(parentPath); -}; - -// Converts an enum to a string value -static inline const char* enum_to_string(const char* array[], s32 count, s32 index) -{ - return (index >= 0 && index < count) ? array[index] : ""; -}; - -// Converts a string to an enum value -static inline s32 string_to_enum(const std::string& string, const char* const* array, s32 n) -{ - for (s32 i = 0; i < n; i++) - if (string == array[i]) - return i; - return -1; -}; - -// Macro to define enum to string functions -#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \ - static inline std::string function(s32 index) \ - { \ - return enum_to_string(array, count, index); \ - }; - -// Macro to define string to enum functions -#define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \ - static inline enumType function(const std::string& string) \ - { \ - return static_cast(string_to_enum(string, stringArray, count)); \ - }; - +#define ROUND_NEAREST_FLOAT(value, multiple) (roundf((value) / (multiple)) * (multiple)) #define COLOR_FLOAT_TO_INT(x) (static_cast((x) * 255.0f)) #define COLOR_INT_TO_FLOAT(x) ((x) / 255.0f) #define PERCENT_TO_UNIT(x) (x / 100.0f) - #define TICK_DELAY 33.3f +#define TICK_CATCH_UP_MAX (33.3f * 5) #define SECOND 1000.0f #define TICK_RATE (SECOND / TICK_DELAY) +#define ID_NONE -1 +#define INDEX_NONE -1 +#define LENGTH_NONE -1 #define UV_VERTICES(uvMin, uvMax) \ { \ @@ -122,11 +84,7 @@ static const f32 GL_UV_VERTICES[] = 0, 1, 0.0f, 1.0f }; - -#define IMVEC4_VEC4(value) ImVec4(value.r, value.g, value.b, value.a) - static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0}; - static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f}; static const vec4 COLOR_GREEN = {0.0f, 1.0f, 0.0f, 1.0f}; static const vec4 COLOR_BLUE = {0.0f, 0.0f, 1.0f, 1.0f}; @@ -134,15 +92,85 @@ static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f}; static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 1.0f}; static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f}; -template -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; +static inline void log_error(const std::string& string) +{ + std::println("[ERROR] {}", string); +} + +static inline void log_info(const std::string& string) +{ + std::println("[INFO] {}", string); +} + +static inline bool string_to_bool(const std::string& string) +{ + if (string == "1") return true; + + std::string lower = string; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + + return lower == "true"; +} + +static inline void working_directory_from_file_set(const std::string& path) +{ + std::filesystem::path filePath = path; + std::filesystem::path parentPath = filePath.parent_path(); + std::filesystem::current_path(parentPath); +}; + +static inline const char* enum_to_string(const char* array[], s32 count, s32 index) +{ + return (index >= 0 && index < count) ? array[index] : ""; +}; + +static inline s32 string_to_enum(const std::string& string, const char* const* array, s32 n) +{ + for (s32 i = 0; i < n; i++) + if (string == array[i]) + return i; + return -1; +}; + +template +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 s32 vector_next_id_get(const std::vector& vec) +{ + std::unordered_set usedIDs; + for (const auto& item : vec) + usedIDs.insert(item.id); + + for (s32 i = 0; ; ++i) + if (!usedIDs.contains(i)) + return i; +} + +template +void vector_swap_by_id(std::vector& vec, s32 idA, s32 idB) +{ + if (idA == idB) + return; + + auto itA = std::find_if(vec.begin(), vec.end(), [=](const T& item) { return item.id == idA; }); + auto itB = std::find_if(vec.begin(), vec.end(), [=](const T& item) { return item.id == idB; }); + + if (itA != vec.end() && itB != vec.end()) + std::swap(*itA, *itB); } -// Swaps elements in a map -// If neither key exists, do nothing -// If one key exists, change its ID -// If both keys exist, swap template static inline void map_swap(Map& map, const Key& key1, const Key& key2) { @@ -167,4 +195,38 @@ static inline void map_swap(Map& map, const Key& key1, const Key& key2) map[key1] = std::move(it2->second); map.erase(it2); } -}; \ No newline at end of file +}; + +template +static inline void map_insert_shift(std::map& map, s32 index, const T& value) +{ + const s32 insertIndex = index + 1; + + std::vector> toShift; + for (auto it = map.rbegin(); it != map.rend(); ++it) + { + if (it->first < insertIndex) + break; + toShift.emplace_back(it->first + 1, std::move(it->second)); + } + + for (const auto& [newKey, _] : toShift) + map.erase(newKey - 1); + + for (auto& [newKey, val] : toShift) + map[newKey] = std::move(val); + + map[insertIndex] = value; +} + +#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \ + static inline std::string function(s32 index) \ + { \ + return enum_to_string(array, count, index); \ + }; + +#define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \ + static inline enumType function(const std::string& string) \ + { \ + return static_cast(string_to_enum(string, stringArray, count)); \ + }; diff --git a/src/PACKED.h b/src/PACKED.h index 8d2698e..3092378 100644 --- a/src/PACKED.h +++ b/src/PACKED.h @@ -1,130 +1,197 @@ +// Includes packed resources (i.e., textures, shaders) + #pragma once #include "COMMON.h" -const u32 TEXTURE_ATLAS_LENGTH = 916; +const u32 TEXTURE_ATLAS_LENGTH = 1696; const u8 TEXTURE_ATLAS[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, - 0x02, 0x03, 0x00, 0x00, 0x00, 0x73, 0xa5, 0x1d, 0xc6, 0x00, 0x00, 0x00, - 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x60, - 0x60, 0x60, 0x13, 0x5e, 0x42, 0xae, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, - 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x03, 0x39, 0x49, - 0x44, 0x41, 0x54, 0x48, 0xc7, 0xcd, 0x95, 0xcf, 0x6b, 0x14, 0x31, 0x14, - 0xc7, 0xbf, 0x19, 0x26, 0xb2, 0xb3, 0xa7, 0x11, 0x26, 0xcb, 0xea, 0x69, - 0x90, 0x46, 0xb4, 0x7f, 0x45, 0x0a, 0xf5, 0xe0, 0x2d, 0x42, 0xde, 0x30, - 0x9d, 0xd3, 0x28, 0x56, 0xa4, 0x7f, 0x45, 0x0e, 0x0a, 0xf5, 0xe6, 0x61, - 0x47, 0xaa, 0xa7, 0x22, 0xac, 0xb4, 0xf3, 0x57, 0x9a, 0x97, 0x34, 0x16, - 0x71, 0x57, 0x29, 0x82, 0xf8, 0x20, 0xbb, 0x2f, 0xf3, 0x79, 0xf9, 0xbe, - 0x1f, 0x19, 0x76, 0xf1, 0x7b, 0x13, 0x06, 0xa8, 0x46, 0xc8, 0xbc, 0xd7, - 0x3d, 0x92, 0x29, 0x07, 0x48, 0x0b, 0xf9, 0x0d, 0x2d, 0xd8, 0x88, 0xf8, - 0x73, 0x22, 0x41, 0x64, 0x18, 0x2c, 0x15, 0xea, 0x28, 0x60, 0xa5, 0x89, - 0x58, 0x11, 0xb9, 0x28, 0xa5, 0x5e, 0x59, 0x96, 0x91, 0xf3, 0xd6, 0x02, - 0x85, 0x41, 0x04, 0xc2, 0x04, 0x40, 0xb6, 0x20, 0xf2, 0x7a, 0x9e, 0xfa, - 0x98, 0x98, 0xa5, 0x32, 0x10, 0x67, 0xd7, 0x66, 0x35, 0x77, 0x1b, 0x34, - 0xc2, 0xe0, 0x70, 0xea, 0xae, 0x6e, 0xc0, 0xf8, 0xf8, 0xf3, 0x9b, 0x8b, - 0xd5, 0xf5, 0xb0, 0xc1, 0xaa, 0xf2, 0x38, 0xbc, 0x3e, 0xbb, 0x8a, 0xda, - 0x17, 0x64, 0xfa, 0xfb, 0xc7, 0x7a, 0xb3, 0x3d, 0x5b, 0x41, 0x4b, 0xa0, - 0x98, 0x67, 0x8f, 0xcd, 0x0a, 0x12, 0x9f, 0x70, 0xb0, 0x3e, 0x58, 0xaf, - 0x14, 0x36, 0x90, 0x01, 0x2c, 0xe6, 0xab, 0x73, 0x6d, 0x65, 0x2f, 0xb9, - 0x54, 0x69, 0x8d, 0xee, 0xe6, 0x1e, 0x55, 0x07, 0x3c, 0x7a, 0xf4, 0xb5, - 0x55, 0x46, 0x38, 0x06, 0xc5, 0x58, 0x79, 0x39, 0xcc, 0x16, 0xc2, 0x01, - 0x38, 0x3f, 0xf7, 0x19, 0x60, 0xea, 0x20, 0x86, 0xad, 0x01, 0x2c, 0xc2, - 0x91, 0x16, 0xda, 0x17, 0x2c, 0x95, 0x8c, 0x28, 0x7b, 0x3e, 0x3b, 0xb7, - 0x43, 0xdc, 0x6f, 0xd2, 0x72, 0xf7, 0x3b, 0x00, 0x51, 0x86, 0xfc, 0x9d, - 0xa5, 0xd2, 0x88, 0xf3, 0x7d, 0xf0, 0xaa, 0x88, 0xc6, 0x25, 0xd1, 0x25, - 0x64, 0xd8, 0x71, 0x74, 0x86, 0xf7, 0x80, 0x8f, 0x95, 0x95, 0x7c, 0x9d, - 0x2a, 0x82, 0x2c, 0x59, 0x02, 0xb5, 0xf4, 0xc1, 0x69, 0xe0, 0x9a, 0x52, - 0x98, 0x9a, 0x47, 0x9f, 0x81, 0x86, 0xea, 0xd1, 0x08, 0xdb, 0x34, 0xd5, - 0xd8, 0x48, 0x22, 0x9b, 0x01, 0x07, 0xa3, 0x91, 0xa6, 0x21, 0x4a, 0xd7, - 0x9b, 0x01, 0x07, 0xa3, 0x51, 0x88, 0x80, 0x1f, 0x32, 0x0c, 0xa0, 0xe5, - 0x60, 0x48, 0x07, 0xcb, 0x52, 0x0c, 0x58, 0x2e, 0x00, 0x0e, 0x8e, 0x7b, - 0xcb, 0xc9, 0xd9, 0xe1, 0x02, 0xca, 0xe0, 0x68, 0xb7, 0xec, 0x53, 0x83, - 0xb9, 0x6b, 0x22, 0x94, 0x78, 0xf0, 0x5a, 0xda, 0xc3, 0x3e, 0x6e, 0x32, - 0xe0, 0x26, 0xef, 0xe1, 0xe5, 0xf3, 0xca, 0x6e, 0xcf, 0xf2, 0x83, 0xdb, - 0x91, 0xf4, 0xeb, 0x83, 0x25, 0xd1, 0x22, 0x4f, 0x38, 0x0f, 0x32, 0x6d, - 0xf4, 0x70, 0x79, 0xfb, 0xb6, 0xff, 0xc6, 0x7e, 0x09, 0xba, 0x8a, 0xfb, - 0x67, 0x8d, 0xbd, 0x91, 0xcd, 0x7e, 0x11, 0xc0, 0x13, 0xd4, 0x91, 0x7e, - 0x18, 0xa1, 0x5c, 0xf2, 0x81, 0x75, 0x00, 0x5b, 0xd4, 0x82, 0x8e, 0x51, - 0x9c, 0x72, 0x24, 0x03, 0x1e, 0xc0, 0x69, 0x8b, 0x62, 0x32, 0x40, 0x00, - 0x98, 0x6c, 0xbc, 0xd6, 0x08, 0xc4, 0xe4, 0xb1, 0xa4, 0xb1, 0x46, 0x80, - 0x42, 0x8e, 0xe5, 0xc5, 0xe3, 0x3a, 0x49, 0xdd, 0xeb, 0x0c, 0x9e, 0xd2, - 0x65, 0xcd, 0x27, 0x4a, 0xe1, 0x9b, 0xb1, 0x6a, 0x92, 0xd4, 0x83, 0x13, - 0xe0, 0x7d, 0xe7, 0x21, 0x4e, 0x8e, 0x51, 0x17, 0xa3, 0x3b, 0x3a, 0x52, - 0x49, 0xea, 0xf4, 0x35, 0x8a, 0x17, 0x03, 0x6a, 0x69, 0x0c, 0xcc, 0xbb, - 0x8f, 0x0c, 0x92, 0xd4, 0xf4, 0x11, 0x95, 0x7b, 0x0b, 0x45, 0x41, 0x08, - 0x35, 0x92, 0x94, 0x38, 0x86, 0x58, 0x19, 0x68, 0x6a, 0xb9, 0xfc, 0xb2, - 0x5c, 0x18, 0x70, 0xf2, 0xd8, 0xbe, 0x7c, 0x0e, 0x48, 0xf2, 0x0c, 0xf8, - 0x08, 0xb8, 0xdc, 0x9a, 0x77, 0x71, 0xa8, 0x84, 0x74, 0xa2, 0x04, 0x5b, - 0x02, 0x4f, 0x0d, 0x80, 0xee, 0xeb, 0x82, 0x41, 0x65, 0x11, 0x23, 0xa2, - 0xd4, 0x13, 0x0e, 0x39, 0x3c, 0xf7, 0xca, 0xbd, 0x30, 0x79, 0x88, 0xb2, - 0x61, 0x7f, 0xff, 0xd8, 0xf7, 0xb2, 0x82, 0x95, 0x17, 0xc0, 0x01, 0x57, - 0xc4, 0x2b, 0x80, 0x88, 0x06, 0x06, 0xef, 0x80, 0xf4, 0xc6, 0xf0, 0x4a, - 0x60, 0xc5, 0x51, 0xa3, 0x83, 0x48, 0x2f, 0x5c, 0x5c, 0x86, 0xfb, 0xf8, - 0x04, 0x14, 0xdd, 0x45, 0x70, 0x0c, 0x64, 0xaf, 0x2d, 0xaf, 0x04, 0x3c, - 0x50, 0x7d, 0xfb, 0xd2, 0x43, 0x01, 0xfc, 0x23, 0xc0, 0x2b, 0xa6, 0x5f, - 0x0e, 0x83, 0xd5, 0xdb, 0xc1, 0xa3, 0xf3, 0x50, 0x1e, 0x4e, 0xa1, 0x70, - 0xc2, 0xdc, 0x00, 0x45, 0x27, 0xa6, 0x20, 0x82, 0x02, 0x03, 0x04, 0x90, - 0xea, 0x6c, 0x6b, 0x3a, 0x81, 0xa0, 0x2e, 0x00, 0xe3, 0x94, 0x80, 0x03, - 0xdb, 0x72, 0x9e, 0x2f, 0x6b, 0x3a, 0x0d, 0x8e, 0x0b, 0x52, 0x2f, 0x9c, - 0x7a, 0x56, 0x38, 0xe5, 0xa4, 0xc5, 0x02, 0x6d, 0xab, 0x86, 0x00, 0x84, - 0x85, 0x36, 0xd4, 0x6b, 0x12, 0xbd, 0x0a, 0x28, 0x82, 0xe6, 0xe1, 0x6b, - 0xc4, 0x72, 0x1d, 0x59, 0x49, 0xca, 0xfe, 0x00, 0x50, 0x1b, 0x40, 0x03, - 0xc2, 0x71, 0x83, 0xca, 0x30, 0x88, 0x39, 0xa0, 0x03, 0x58, 0x03, 0x48, - 0x27, 0x10, 0xc1, 0xcf, 0x46, 0x4e, 0x11, 0x10, 0x93, 0xef, 0xb5, 0x39, - 0x98, 0x07, 0xe0, 0xff, 0x00, 0x6e, 0xef, 0x6f, 0x22, 0xea, 0xee, 0x0e, - 0xf0, 0xbf, 0x03, 0xe5, 0xf6, 0x00, 0xa2, 0xdd, 0x40, 0x1c, 0x1d, 0x99, - 0x9d, 0x40, 0x0a, 0x63, 0xef, 0x02, 0x14, 0x45, 0x73, 0x7b, 0xc1, 0x5f, - 0xe7, 0xc8, 0xe5, 0xde, 0xad, 0x41, 0x28, 0xf7, 0x3f, 0x5e, 0xd4, 0xbf, - 0x00, 0xc0, 0x1f, 0x81, 0x26, 0xea, 0x77, 0x02, 0xfe, 0xff, 0xde, 0x09, - 0x0a, 0x22, 0xef, 0x77, 0x01, 0x4c, 0x1d, 0x76, 0x03, 0xdd, 0xef, 0x01, - 0xd5, 0xb8, 0x07, 0x14, 0x7e, 0x27, 0xf8, 0x0e, 0xc6, 0xe6, 0x29, 0x49, - 0x20, 0x43, 0x4e, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x0e, 0xcb, 0xf5, 0x55, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, + 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x06, 0x52, 0x49, 0x44, + 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0x8b, 0x4e, 0xeb, 0x30, 0x0c, 0x65, + 0xd5, 0x3e, 0x14, 0xbe, 0x0c, 0xfe, 0x74, 0x97, 0x4a, 0x04, 0x05, 0x5f, + 0x3f, 0x8e, 0x1d, 0xa7, 0xe9, 0x56, 0x5b, 0x42, 0x6c, 0xb4, 0x69, 0x52, + 0x1f, 0xbf, 0xf3, 0xe0, 0xed, 0xad, 0xe8, 0x9c, 0xf4, 0x20, 0xf4, 0xcc, + 0xef, 0x10, 0x69, 0xfb, 0xfe, 0xfe, 0xfe, 0xfb, 0xee, 0xfb, 0xe7, 0x99, + 0x7d, 0xa9, 0x8c, 0x7f, 0x46, 0x20, 0x32, 0xc6, 0xee, 0x6d, 0xdb, 0x83, + 0xd5, 0xc0, 0x93, 0x80, 0x13, 0x01, 0xd5, 0x3a, 0xd4, 0xae, 0xf5, 0xd2, + 0x22, 0xd1, 0xcf, 0xc0, 0x86, 0x24, 0xd1, 0x62, 0xea, 0x03, 0xa4, 0x19, + 0x00, 0x34, 0xc6, 0x5b, 0xbc, 0x6a, 0xd7, 0xdd, 0x2a, 0x94, 0xf1, 0xe2, + 0x3b, 0x00, 0xfb, 0xcf, 0x08, 0x23, 0xb8, 0x71, 0x3c, 0x82, 0x34, 0xcb, + 0x04, 0x71, 0xc2, 0x47, 0x9f, 0xe9, 0x02, 0x20, 0x4b, 0xf2, 0xa8, 0x34, + 0xa3, 0xd2, 0x3f, 0x0a, 0x52, 0x54, 0x13, 0x22, 0x82, 0x82, 0x8e, 0xa7, + 0xb5, 0xbb, 0xf5, 0x2a, 0x71, 0xfb, 0x26, 0x4d, 0x5d, 0xda, 0x75, 0x7a, + 0x7f, 0xd4, 0x37, 0x70, 0xfd, 0x71, 0x7d, 0x5b, 0xf7, 0xa9, 0xaa, 0x1d, + 0xe8, 0x37, 0xd2, 0x7f, 0x7f, 0x2f, 0x32, 0x8e, 0xf6, 0x4c, 0x15, 0x00, + 0x8e, 0xf9, 0x33, 0x00, 0xb0, 0xfa, 0xb7, 0x18, 0x40, 0x4d, 0xc4, 0xe7, + 0xe7, 0x27, 0x7b, 0xdf, 0xc7, 0xc7, 0xc7, 0x9f, 0xef, 0x5f, 0x5f, 0x5f, + 0xb7, 0x15, 0x00, 0xec, 0xe3, 0x68, 0x7d, 0x6f, 0x88, 0x34, 0xa1, 0x12, + 0x33, 0x4a, 0x3d, 0x23, 0xdb, 0x67, 0xca, 0x34, 0x84, 0xf6, 0x36, 0x7d, + 0x3b, 0xfa, 0xdd, 0x6b, 0xfa, 0x50, 0x3f, 0x40, 0xc7, 0x2d, 0xf1, 0xad, + 0x07, 0xde, 0x34, 0x29, 0x9a, 0x59, 0xca, 0xd2, 0x80, 0x7d, 0xd0, 0x4d, + 0x6a, 0x2d, 0xad, 0xf0, 0x68, 0x82, 0x44, 0x9a, 0xe4, 0x6b, 0x63, 0xe9, + 0x25, 0x17, 0xd5, 0x96, 0xfe, 0x79, 0x5c, 0xfb, 0x8d, 0x76, 0xe4, 0x65, + 0xbe, 0x57, 0x32, 0xbd, 0x0c, 0xf1, 0x30, 0xdf, 0x7a, 0x4e, 0x7f, 0x9d, + 0x6a, 0x5b, 0x23, 0xca, 0xfc, 0xfe, 0xf3, 0x7e, 0xcd, 0x13, 0x19, 0xb5, + 0x3e, 0x1b, 0xa9, 0xe3, 0x43, 0x23, 0x84, 0xd1, 0x30, 0x54, 0x7a, 0x06, + 0x17, 0xef, 0x7b, 0xd5, 0x9e, 0xfb, 0xdd, 0xc7, 0xe7, 0xf4, 0x77, 0xf4, + 0xdd, 0x35, 0x1e, 0xa4, 0x65, 0x90, 0x2b, 0x12, 0xb1, 0x8c, 0xac, 0x5b, + 0x03, 0x43, 0x32, 0x53, 0xa7, 0x01, 0xe0, 0x0c, 0xa5, 0x88, 0x8c, 0x7e, + 0x10, 0xa6, 0x47, 0x92, 0x2e, 0xed, 0xbe, 0x74, 0xfe, 0xbc, 0x42, 0x31, + 0xce, 0xe3, 0x98, 0x8b, 0x8a, 0x8a, 0xb2, 0xcd, 0x21, 0xd2, 0x7e, 0x9b, + 0xa5, 0xf6, 0xd1, 0x88, 0xe6, 0x2c, 0xfd, 0xd3, 0x44, 0x2c, 0xc2, 0xfc, + 0x91, 0xf6, 0x87, 0x3b, 0x71, 0x24, 0x92, 0xd2, 0x98, 0xd9, 0xd7, 0xe1, + 0x81, 0x68, 0xec, 0x11, 0x79, 0x87, 0x51, 0x1e, 0x1c, 0xca, 0x7c, 0xcf, + 0x35, 0x4f, 0xba, 0x2f, 0x55, 0x48, 0xad, 0x4a, 0xeb, 0x0a, 0x6d, 0x0c, + 0x33, 0x3e, 0xa2, 0xc2, 0x54, 0xc2, 0xe8, 0x8f, 0x25, 0x0d, 0x5e, 0x09, + 0xd3, 0xfa, 0x1f, 0x8d, 0xea, 0xb2, 0x4c, 0xa8, 0x1b, 0x00, 0x54, 0x85, + 0xad, 0xf8, 0xd8, 0x62, 0xbe, 0x14, 0x47, 0x47, 0x01, 0x50, 0x27, 0x3a, + 0x04, 0x93, 0x20, 0xf5, 0xcf, 0x4d, 0x27, 0xd2, 0xbf, 0xa1, 0x02, 0x84, + 0x08, 0x1b, 0x5b, 0xfe, 0x95, 0x6a, 0x36, 0x5c, 0x9d, 0xc4, 0x6a, 0xd7, + 0x97, 0x86, 0xdb, 0x75, 0xee, 0x39, 0xfb, 0x33, 0x3c, 0x75, 0x7a, 0xad, + 0x3c, 0x6e, 0x55, 0x75, 0xa5, 0x62, 0x62, 0x2b, 0x25, 0x73, 0x45, 0xb3, + 0x56, 0x54, 0xeb, 0xcb, 0xcd, 0x5a, 0xe1, 0x50, 0x2b, 0xc2, 0xfd, 0x77, + 0x7f, 0x8f, 0x70, 0xff, 0x99, 0xce, 0x64, 0x49, 0x13, 0xce, 0x92, 0xf4, + 0x73, 0xce, 0x4f, 0x92, 0xd6, 0x11, 0x0d, 0xf0, 0xfa, 0x1d, 0x69, 0xf6, + 0x8a, 0x8b, 0x5e, 0xb8, 0x68, 0xe6, 0x91, 0x48, 0xac, 0xed, 0xa4, 0x1d, + 0xd1, 0x81, 0x71, 0xe0, 0xf4, 0x2a, 0xac, 0x31, 0x5f, 0xb2, 0xd7, 0x1e, + 0x00, 0x90, 0x32, 0x00, 0xe2, 0x90, 0x4f, 0x01, 0x00, 0x32, 0x01, 0x61, + 0x5d, 0xa7, 0x0c, 0x45, 0x0b, 0x77, 0x51, 0x0d, 0xb0, 0x6c, 0x2d, 0x12, + 0x71, 0x49, 0x8c, 0xb6, 0xfc, 0x9c, 0xd5, 0x7e, 0xa8, 0x6e, 0x24, 0xbd, + 0x84, 0x95, 0x54, 0x68, 0x8e, 0x56, 0x32, 0x4b, 0x5e, 0x27, 0xac, 0x45, + 0x31, 0x52, 0xff, 0x5a, 0x9e, 0x70, 0x16, 0x27, 0xbc, 0xd1, 0x09, 0x93, + 0xd6, 0xd1, 0x8d, 0x50, 0x1b, 0x04, 0xe7, 0x68, 0xb9, 0x89, 0x06, 0x6b, + 0x0a, 0xd0, 0x33, 0x09, 0x43, 0xfb, 0x6d, 0xe3, 0xd4, 0x9e, 0x45, 0x1d, + 0xa2, 0xd5, 0x47, 0xbb, 0x7f, 0x7f, 0xd6, 0xfe, 0xfc, 0x7e, 0xf2, 0x65, + 0xff, 0xdc, 0x3b, 0x52, 0xce, 0xd9, 0x7b, 0x26, 0xa8, 0xd4, 0x7b, 0xb5, + 0x50, 0xd4, 0x93, 0x41, 0x6a, 0x0e, 0xd9, 0x93, 0x88, 0x71, 0xa6, 0x8d, + 0x33, 0x8b, 0x52, 0x1f, 0x88, 0x4f, 0x3a, 0x73, 0x69, 0x7d, 0x59, 0x29, + 0x82, 0xfa, 0x18, 0xcd, 0x27, 0x79, 0x4a, 0x11, 0x88, 0x56, 0x3c, 0x6d, + 0x29, 0x62, 0x66, 0x31, 0x2e, 0x23, 0x93, 0xf5, 0x48, 0xfe, 0x28, 0xf3, + 0x5e, 0x65, 0x3e, 0xe5, 0xe5, 0xcb, 0xd1, 0x45, 0x67, 0xa4, 0xa8, 0x09, + 0xe1, 0x12, 0x1c, 0xed, 0x73, 0x76, 0xfb, 0xd9, 0xb5, 0xf7, 0x8c, 0x67, + 0x53, 0x33, 0x78, 0xf3, 0x76, 0x22, 0xd5, 0x5b, 0x46, 0x56, 0xcf, 0x8d, + 0xb6, 0x47, 0x6a, 0x42, 0x99, 0x00, 0x44, 0x9f, 0x4f, 0x85, 0x78, 0x0f, + 0x6b, 0x37, 0x2f, 0xc2, 0x59, 0xd1, 0xc0, 0xa8, 0x34, 0x65, 0x66, 0xd2, + 0xb3, 0x35, 0xa1, 0x59, 0x10, 0xd7, 0xc2, 0x2c, 0x69, 0xe5, 0x58, 0x46, + 0x48, 0x96, 0x0d, 0x80, 0xa7, 0xf6, 0x82, 0x2e, 0x3b, 0xc9, 0xdc, 0xe8, + 0xc1, 0xd5, 0xcb, 0x42, 0x21, 0x93, 0xa7, 0x3c, 0xe1, 0x01, 0x2f, 0x23, + 0xd2, 0x18, 0xdd, 0x1b, 0x30, 0x63, 0x8f, 0x01, 0xb7, 0x27, 0x82, 0x15, + 0x80, 0x68, 0xb6, 0x8b, 0x30, 0x10, 0xd1, 0x80, 0x8c, 0x49, 0xef, 0x15, + 0x00, 0x44, 0x98, 0xaf, 0xee, 0x8e, 0xf1, 0xee, 0x46, 0xf1, 0xdc, 0x4b, + 0xd5, 0x70, 0x86, 0x0f, 0xf0, 0x26, 0x69, 0x52, 0x35, 0x77, 0xf4, 0xdd, + 0x5d, 0xcc, 0x47, 0x6a, 0xe7, 0x11, 0x0d, 0xd0, 0x26, 0x43, 0x66, 0x85, + 0x8e, 0xd1, 0xad, 0x47, 0x99, 0x5b, 0x96, 0xdc, 0xcc, 0xb7, 0x4c, 0x90, + 0xb6, 0xf7, 0x09, 0x51, 0x5f, 0xd4, 0x84, 0x78, 0x19, 0xec, 0xda, 0x81, + 0x98, 0x08, 0x80, 0x37, 0x87, 0x82, 0xf6, 0xb9, 0x69, 0xd5, 0x4a, 0x24, + 0x9a, 0x88, 0xda, 0xc8, 0x55, 0xa9, 0x7d, 0x14, 0x00, 0x14, 0x20, 0x17, + 0xf3, 0x11, 0x67, 0x19, 0xdd, 0x25, 0x99, 0x15, 0x57, 0x67, 0x56, 0x17, + 0x47, 0x7c, 0x80, 0xc7, 0x3f, 0xa0, 0xcc, 0xbf, 0x69, 0x8c, 0x90, 0x36, + 0x9c, 0x69, 0x9b, 0xf9, 0xd0, 0x89, 0x87, 0x3e, 0xfb, 0xb5, 0x32, 0xe1, + 0xc8, 0xf3, 0x2d, 0x60, 0xd1, 0x77, 0x40, 0x33, 0x5f, 0x8e, 0x77, 0x08, + 0x70, 0x5b, 0xa4, 0x51, 0x76, 0xd6, 0x69, 0xf5, 0xdb, 0x66, 0xe5, 0xa4, + 0xef, 0x67, 0xa8, 0x9d, 0xf5, 0x33, 0x76, 0x9e, 0xb1, 0x6d, 0xd2, 0xcb, + 0x8e, 0x48, 0x17, 0x52, 0x60, 0xe3, 0x24, 0x30, 0x52, 0x7c, 0xf3, 0x92, + 0xb4, 0x7b, 0x71, 0xff, 0x5b, 0x64, 0x47, 0xa6, 0x26, 0x28, 0x45, 0x45, + 0xd7, 0xa2, 0x88, 0xb6, 0x6e, 0xcf, 0xf4, 0x82, 0xd6, 0x1a, 0xd0, 0x91, + 0xd2, 0x30, 0xd2, 0xef, 0x0a, 0x80, 0x7e, 0x07, 0x10, 0x5d, 0x1d, 0xcc, + 0x9d, 0x14, 0xe2, 0x79, 0x16, 0x77, 0x0e, 0xcf, 0x08, 0x33, 0xb4, 0xb5, + 0x3d, 0x56, 0xff, 0xa3, 0x0c, 0x0e, 0x8d, 0x7d, 0xe4, 0xc5, 0xb9, 0x75, + 0xa6, 0x68, 0x92, 0xc6, 0xad, 0x4f, 0x8d, 0x68, 0x8a, 0x96, 0x34, 0x66, + 0xd6, 0xbd, 0x34, 0x06, 0x87, 0xf3, 0x18, 0x74, 0x6d, 0xbf, 0x87, 0x19, + 0xd6, 0x20, 0xa3, 0x4c, 0x97, 0x18, 0xcf, 0xad, 0x6a, 0xf3, 0x08, 0x41, + 0x64, 0xee, 0xc3, 0x03, 0xc0, 0xdd, 0x52, 0x55, 0xee, 0xe4, 0x11, 0xe4, + 0x58, 0x80, 0x3e, 0xc1, 0xfa, 0xbe, 0xf7, 0x8f, 0xca, 0x5b, 0xe1, 0x5e, + 0x24, 0x94, 0x6b, 0xcb, 0xc1, 0xe9, 0x12, 0x78, 0xc9, 0xee, 0x6b, 0x63, + 0x88, 0x1e, 0x54, 0x22, 0xdd, 0xeb, 0x3e, 0xff, 0xc2, 0xda, 0x58, 0x81, + 0xda, 0x43, 0xcd, 0x27, 0x58, 0x7d, 0x5a, 0xdf, 0x25, 0xc9, 0x97, 0xc6, + 0x8e, 0xae, 0x09, 0x8a, 0xbc, 0x6b, 0xaa, 0x0f, 0xe0, 0x54, 0x35, 0x83, + 0xf9, 0xd4, 0x27, 0x64, 0x02, 0xe0, 0x3d, 0xad, 0xcb, 0x6b, 0x2e, 0xbd, + 0x66, 0x2b, 0x2d, 0xcc, 0xcb, 0x60, 0x3e, 0x8d, 0x3a, 0xb4, 0x4d, 0x76, + 0xd1, 0x10, 0x12, 0x29, 0x8d, 0x23, 0xa1, 0xe4, 0xc8, 0x7c, 0x72, 0x38, + 0x7b, 0xa6, 0x36, 0x94, 0x2b, 0xc6, 0x35, 0x7b, 0x89, 0x9c, 0x30, 0x15, + 0x5d, 0xb6, 0xe2, 0x7d, 0x39, 0xba, 0xe5, 0x47, 0x3b, 0xbb, 0xa8, 0xad, + 0x76, 0xf6, 0xd8, 0x74, 0xce, 0x07, 0x4a, 0x6d, 0x91, 0x62, 0xa1, 0x5a, + 0xac, 0xd3, 0x54, 0x0f, 0x65, 0x8e, 0x25, 0xf9, 0x23, 0x89, 0x11, 0xbd, + 0x66, 0x1d, 0x37, 0xa3, 0x6d, 0xb7, 0x1a, 0x29, 0x31, 0x7b, 0x35, 0x07, + 0xb9, 0xbe, 0x59, 0x92, 0x8d, 0x46, 0x3c, 0x9c, 0x84, 0x66, 0x2d, 0xb8, + 0xca, 0x2a, 0x86, 0x79, 0xc6, 0x72, 0xd4, 0x91, 0x6d, 0x77, 0x6a, 0x6e, + 0x34, 0x55, 0xf7, 0x4a, 0xcf, 0x51, 0xcc, 0xe7, 0xfa, 0xe9, 0x77, 0x28, + 0x4a, 0x87, 0xf8, 0x21, 0x1a, 0x37, 0xfb, 0x1d, 0xee, 0x9c, 0xcd, 0xef, + 0x6d, 0x67, 0x1b, 0xcc, 0x0e, 0x44, 0xaf, 0x0d, 0x54, 0x33, 0xe8, 0xae, + 0x95, 0x68, 0x2c, 0x9f, 0xe1, 0x23, 0x22, 0x4c, 0xa7, 0xe0, 0x69, 0xe7, + 0xc3, 0x4d, 0x71, 0xc2, 0xda, 0xcb, 0xf6, 0xcc, 0x8f, 0x9a, 0xa4, 0x57, + 0xaf, 0x82, 0x0e, 0x39, 0xe1, 0xa2, 0x75, 0x74, 0xcb, 0x88, 0x6d, 0x47, + 0x34, 0xe2, 0x2c, 0x1a, 0xb5, 0xe2, 0x9c, 0xd4, 0xdf, 0x28, 0xa8, 0x68, + 0x1d, 0xdd, 0xa5, 0xc2, 0x15, 0xea, 0xec, 0x8a, 0x4a, 0x03, 0x0a, 0x80, + 0x55, 0xfe, 0xa3, 0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x02, 0xa0, + 0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x78, 0x26, 0x7c, 0x65, 0xd2, + 0xf6, 0xac, 0x1d, 0x51, 0x13, 0xba, 0x17, 0xe3, 0xed, 0x7b, 0x66, 0x02, + 0xb1, 0x5d, 0x9d, 0xf9, 0xdc, 0x34, 0x26, 0xfd, 0xdb, 0xcc, 0x63, 0x10, + 0x2e, 0xed, 0x03, 0x00, 0xc9, 0x9e, 0x6e, 0x82, 0xb6, 0xab, 0x4a, 0x3f, + 0x62, 0x56, 0xf6, 0x5b, 0x46, 0xff, 0x55, 0x57, 0x01, 0x50, 0x51, 0xd0, + 0x7a, 0x67, 0xea, 0x6d, 0x37, 0xb2, 0x09, 0xdc, 0x69, 0xe2, 0x4a, 0x03, + 0x2a, 0x11, 0x2b, 0x13, 0xf4, 0xf2, 0x91, 0x0c, 0xe4, 0x84, 0xb5, 0x49, + 0xf9, 0x99, 0xf9, 0x40, 0x69, 0x40, 0x99, 0xa0, 0x35, 0x1a, 0x03, 0x66, + 0xc2, 0xd3, 0xb3, 0xe1, 0x4b, 0x6b, 0x00, 0x00, 0xc2, 0xf4, 0x83, 0x00, + 0x2f, 0x59, 0x0b, 0xea, 0xd7, 0xc0, 0x22, 0x07, 0x48, 0x1d, 0xb2, 0x3a, + 0xba, 0x4c, 0xd1, 0xb8, 0x83, 0x2f, 0x00, 0x92, 0x80, 0xa8, 0xa5, 0x89, + 0x15, 0x05, 0x15, 0x15, 0x00, 0x05, 0x40, 0x51, 0x01, 0x70, 0x15, 0xf2, + 0x1e, 0x5a, 0x4d, 0x8f, 0xa1, 0x79, 0x95, 0xa4, 0x6c, 0xe9, 0x7f, 0xbc, + 0x88, 0xfe, 0x3f, 0xdf, 0x02, 0xe0, 0x60, 0x10, 0xac, 0x4d, 0xd3, 0x91, + 0x3e, 0xaf, 0x0c, 0xc0, 0x1f, 0x1f, 0xd0, 0xfe, 0x89, 0x99, 0x44, 0x47, + 0x6d, 0xdd, 0xbc, 0xb4, 0x13, 0x96, 0x40, 0x28, 0xe6, 0x1f, 0x18, 0x05, + 0x51, 0x10, 0x8a, 0xf9, 0xf3, 0x48, 0xac, 0x05, 0xfd, 0x30, 0xfc, 0xd1, + 0x7d, 0x7e, 0x69, 0x5a, 0xb5, 0x71, 0xfa, 0x1f, 0xcc, 0xd3, 0x69, 0xcc, + 0x40, 0x9f, 0x7b, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; enum TextureType { + TEXTURE_NONE, + TEXTURE_RECORD, TEXTURE_ROOT, TEXTURE_LAYER, TEXTURE_NULL, TEXTURE_TRIGGERS, TEXTURE_VISIBLE, TEXTURE_INVISIBLE, - TEXTURE_ERROR, + TEXTURE_SHOW_RECT, + TEXTURE_HIDE_RECT, + TEXTURE_PAN, TEXTURE_MOVE, - TEXTURE_UP, - TEXTURE_DOWN, - TEXTURE_RECT, - TEXTURE_RECT_HIDE, + TEXTURE_ROTATE, + TEXTURE_SCALE, + TEXTURE_CROP, + TEXTURE_UNDO, + TEXTURE_REDO, TEXTURE_DRAW, TEXTURE_ERASE, - TEXTURE_SCALE, - TEXTURE_COLOR, - TEXTURE_PAN, - TEXTURE_CROP, + TEXTURE_COLOR_PICKER, TEXTURE_ANIMATION, TEXTURE_SPRITESHEET, TEXTURE_EVENT, - TEXTURE_ROTATE, - TEXTURE_REDO, TEXTURE_TRIGGER, TEXTURE_PIVOT, - TEXTURE_SQUARE, - TEXTURE_CIRCLE, + TEXTURE_UNINTERPOLATED, + TEXTURE_INTERPOLATED, TEXTURE_PICKER, - TEXTURE_FRAME, TEXTURE_FRAME_ALT, + TEXTURE_FRAME, TEXTURE_TARGET, TEXTURE_COUNT }; const vec2 ATLAS_SIZE = {96, 104}; -const vec2 TEXTURE_SIZE = {16, 16}; const vec2 TEXTURE_SIZE_SMALL = {8, 8}; -const vec2 TEXTURE_SIZE_BIG = {32, 32}; +const vec2 TEXTURE_SIZE = {16, 16}; const vec2 TEXTURE_SIZE_OBLONG = {16, 40}; +const vec2 TEXTURE_SIZE_BIG = {40, 40}; #define ATLAS_UV(x,y){(f32)x / ATLAS_SIZE[0], (f32) y / ATLAS_SIZE[1]} const vec2 ATLAS_UVS[TEXTURE_COUNT][2] = @@ -159,7 +226,7 @@ const vec2 ATLAS_UVS[TEXTURE_COUNT][2] = { ATLAS_UV( 0, 64), ATLAS_UV( 16,104) }, /* 16 x 40 */ { ATLAS_UV( 16, 64), ATLAS_UV( 32,104) }, { ATLAS_UV( 32, 64), ATLAS_UV( 48,104) }, - { ATLAS_UV( 48, 64), ATLAS_UV( 80, 96) } /* 32 x 32 */ + { ATLAS_UV( 48, 64), ATLAS_UV( 88,104) } /* 40 x 40 */ }; const vec2 ATLAS_SIZES[TEXTURE_COUNT] = @@ -287,4 +354,4 @@ const ShaderData SHADER_DATA[SHADER_COUNT] = {SHADER_VERTEX, SHADER_FRAGMENT}, {SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT}, {SHADER_VERTEX, SHADER_LINE_DOTTED_FRAGMENT} -}; \ No newline at end of file +}; diff --git a/src/STRINGS.h b/src/STRINGS.h deleted file mode 100644 index d6c4f25..0000000 --- a/src/STRINGS.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once - -#define STRING_WINDOW_TITLE "Anm2Ed" - -#define STRING_ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p" - -#define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL: " -#define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context: " -#define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: " -#define STRING_ERROR_SHADER_INIT "[ERROR] Could not initialize shader: " -#define STRING_ERROR_TEXTURE_INIT "[ERROR] Could not initialize texture: " -#define STRING_ERROR_ANM2_READ "[ERROR] Could not read anm2 from file: " -#define STRING_ERROR_ANM2_WRITE "[ERROR] Could not write anm2 to file: " -#define STRING_ERROR_SETTINGS_INIT "[ERROR] Could not load settings file: " - -#define STRING_INFO_INIT "[INFO] Initializing" -#define STRING_INFO_QUIT "[INFO] Exited" -#define STRING_INFO_SDL_INIT "[INFO] Initialized SDL" -#define STRING_INFO_SDL_QUIT "[INFO] Quit SDL" -#define STRING_INFO_GLEW_INIT "[INFO] Initialized GLEW" -#define STRING_INFO_GL_CONTEXT_INIT "[INFO] Initialized OpenGL context" -#define STRING_INFO_FILE_READ "[INFO] Read from file: " -#define STRING_INFO_TEXTURE_INIT "[INFO] Initialized texture: " -#define STRING_INFO_ANM2_WRITE "[INFO] Wrote anm2 to file: " -#define STRING_INFO_ANM2_READ "[INFO] Read anm2 from file: " -#define STRING_INFO_OPENGL "OpenGL" - -#define STRING_INFO_IMGUI_INIT "[INFO] Initialized Dear Imgui" -#define STRING_INFO_IMGUI_FREE "[INFO] Freed Dear Imgui" - -#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_PREVIEW_FRAMES_DIRECTORY "frames/" -#define STRING_PREVIEW_FRAMES_FORMAT "{}/{:03}.png" - -#define STRING_IMGUI_WINDOW "Window" -#define STRING_IMGUI_DOCKSPACE "Dockspace" - -#define STRING_IMGUI_TASKBAR "Taskbar" -#define STRING_IMGUI_TASKBAR_PLAYBACK "Playback" -#define STRING_IMGUI_TASKBAR_FILE "File" -#define STRING_IMGUI_TASKBAR_WIZARD "Wizard" - -#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_PLAYBACK_MENU "Playback Menu" -#define STRING_IMGUI_PLAYBACK_LOOP "Loop" - -#define STRING_IMGUI_WIZARD_MENU "Wizard Menu" -#define STRING_IMGUI_WIZARD_EXPORT_FRAMES_TO_PNG "Export Frames to PNG" - -#define STRING_IMGUI_RECORDING "Recording..." - -#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 "(*) {}" -#define STRING_IMGUI_ANIMATIONS_DRAG_DROP "Animation Drag/Drop" - -#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 "#{} {}" -#define STRING_IMGUI_EVENTS_DRAG_DROP "Event Drag/Drop" - -#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 "#{} {}" -#define STRING_IMGUI_SPRITESHEETS_DRAG_DROP "Spritesheet Drag/Drop" - -#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_FRAME_PROPERTIES_EVENT "Event" -#define STRING_IMGUI_FRAME_PROPERTIES_AT_FRAME "At Frame" -#define STRING_IMGUI_FRAME_PROPERTIES_FLIP_X "Flip X" -#define STRING_IMGUI_FRAME_PROPERTIES_FLIP_Y "Flip Y" -#define STRING_IMGUI_FRAME_PROPERTIES_ROOT "-- Root --" -#define STRING_IMGUI_FRAME_PROPERTIES_LAYER "-- Layer -- " -#define STRING_IMGUI_FRAME_PROPERTIES_NULL "-- Null --" -#define STRING_IMGUI_FRAME_PROPERTIES_TRIGGER "-- Trigger --" - -#define STRING_IMGUI_ANIMATION_PREVIEW "Animation Preview" -#define STRING_IMGUI_ANIMATION_PREVIEW_LABEL "##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 "Size" -#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_OFFSET "Offset" -#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR "Color" -#define STRING_IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS "##View 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_ANIMATION_PREVIEW_HELPER_SETTINGS "##Helper Settings" -#define STRING_IMGUI_ANIMATION_PREVIEW_CENTER_VIEW "Center View" -#define STRING_IMGUI_ANIMATION_PREVIEW_AXIS "Axes" -#define STRING_IMGUI_ANIMATION_PREVIEW_AXIS_COLOR "Color" -#define STRING_IMGUI_ANIMATION_PREVIEW_ROOT_TRANSFORM "Root Transform" -#define STRING_IMGUI_ANIMATION_PREVIEW_SHOW_PIVOT "Show Pivot" -#define STRING_IMGUI_ANIMATION_PREVIEW_POSITION "##Position" -#define STRING_IMGUI_POSITION_FORMAT "Position: {{{:5}, {:5}}}" - -#define STRING_IMGUI_SPRITESHEET_EDITOR "Spritesheet Editor" -#define STRING_IMGUI_SPRITESHEET_EDITOR_LABEL "##Animation Preview" -#define STRING_IMGUI_SPRITESHEET_EDITOR_SETTINGS "##Animation Preview Settings" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SETTINGS "##Grid Settings" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID "Grid" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SNAP "Snap" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SIZE "Size" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_OFFSET "Offset" -#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_COLOR "Color" -#define STRING_IMGUI_SPRITESHEET_EDITOR_VIEW_SETTINGS "##View Settings" -#define STRING_IMGUI_SPRITESHEET_EDITOR_ZOOM "Zoom" -#define STRING_IMGUI_SPRITESHEET_EDITOR_BACKGROUND_SETTINGS "##Background Settings" -#define STRING_IMGUI_SPRITESHEET_EDITOR_BACKGROUND_COLOR "Background Color" -#define STRING_IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW "Center View" -#define STRING_IMGUI_SPRITESHEET_EDITOR_BORDER "Border" -#define STRING_IMGUI_SPRITESHEET_EDITOR_POSITION "##Position" - -#define STRING_IMGUI_TIMELINE "Timeline" -#define STRING_IMGUI_TIMELINE_HEADER "##Header" -#define STRING_IMGUI_TIMELINE_ELEMENT_UP "##Up" -#define STRING_IMGUI_TIMELINE_ELEMENT_DOWN "##Down" -#define STRING_IMGUI_TIMELINE_ELEMENT_FRAMES "Element Frames" -#define STRING_IMGUI_TIMELINE_ROOT "Root" -#define STRING_IMGUI_TIMELINE_ITEM_FORMAT "#{} {}" -#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_FRAME_ADD "Add Frame" -#define STRING_IMGUI_TIMELINE_FRAMES "Frames" -#define STRING_IMGUI_TIMELINE_FRAME_LABEL "##Frame" -#define STRING_IMGUI_TIMELINE_TRIGGER_LABEL "##Trigger" -#define STRING_IMGUI_TIMELINE_FRAME_REMOVE "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_FRAME_INDICES "Frame Indices" -#define STRING_IMGUI_TIMELINE_ANIMATION_LENGTH "Animation Length" -#define STRING_IMGUI_TIMELINE_ELEMENT_TIMELINE "Element Timeline" -#define STRING_IMGUI_TIMELINE_ELEMENTS "Elements" -#define STRING_IMGUI_TIMELINE_ELEMENT_LIST "Element List" -#define STRING_IMGUI_TIMELINE_ELEMENT_ADD "Add Element" -#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 Element" -#define STRING_IMGUI_TIMELINE_LOOP "Loop" -#define STRING_IMGUI_TIMELINE_FPS "FPS" -#define STRING_IMGUI_TIMELINE_SPRITESHEET_ID_FORMAT "#{}" -#define STRING_IMGUI_TIMELINE_CREATED_BY "Author" -#define STRING_IMGUI_TIMELINE_CREATED_ON "Created on: %s" -#define STRING_IMGUI_TIMELINE_VERSION "Version: %i" -#define STRING_IMGUI_TIMELINE_VIEWER "Viewer" -#define STRING_IMGUI_TIMELINE_CHILD "Timeline" -#define STRING_IMGUI_TIMELINE_MAIN "Main" -#define STRING_IMGUI_TIMELINE_FRAME_DRAG_DROP "Frame Drag/Drop" -#define STRING_IMGUI_TIMELINE_ITEM_DRAG_DROP "Item Drag/Drop" - -#define STRING_IMGUI_TOOLS "Tools" -#define STRING_IMGUI_TOOLS_PAN "##Pan" -#define STRING_IMGUI_TOOLS_MOVE "##Move" -#define STRING_IMGUI_TOOLS_ROTATE "##Rotate" -#define STRING_IMGUI_TOOLS_SCALE "##Scale" -#define STRING_IMGUI_TOOLS_CROP "##Crop" - -#define STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD "Add a new animation." -#define STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE "Duplicates the selected animation." -#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation." -#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_SET_AS_DEFAULT "Sets the selected animation as the default.\nThe default animation is marked with \"(*)\"." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS "Toggles the display of the X/Y axes on the animation preview." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS_COLOR "Changes the color of the X/Y axes on the animation preview." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_CENTER_VIEW "Centers the animation preview's pan." -#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_OFFSET "Changes the animation preview grid's offset, in pixels." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the animation preview grid's size, in pixels." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ROOT_TRANSFORM "Toggle the properties of the Root element of an animation being able to change the properties (such as position or scale) of all of the animation's elements.\nNOTE: Scale/rotation currently not implemented. Matrix math is hard." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_SHOW_PIVOT "Toggle the pivots of layer animation elements being visible on the animation preview." -#define STRING_IMGUI_TOOLTIP_EVENTS_ADD "Add a new event." -#define STRING_IMGUI_TOOLTIP_EVENTS_REMOVE "Removes the selected event." -#define STRING_IMGUI_TOOLTIP_EVENTS_SELECT "Set the event for the trigger, or rename it." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_AT_FRAME "Sets the frame in which the trigger will activate." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET "Change the color offset of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION "Change the X/Y position of the crop of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE "Change the crop width/height of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION "Change the duration of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_EVENT "Sets the event the trigger will use." -#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_FRAME_PROPERTIES_PIVOT "Change the X/Y pivot of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION "Change the X/Y position of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION "Change the rotation of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE "Change the X/Y scale of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT "Change the tint of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE "Toggles the visibility of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_X "Change the sign of the X scale, to cheat flipping the layer horizontally.\n(Anm2 doesn't support flipping directly)." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_Y "Change the sign of the Y scale, to cheat flipping the layer vertically.\n(Anm2 doesn't support flipping directly)." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD "Opens the file dialog to load in a new spritesheet." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD "Reloads the selected spritesheet." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE "Removes the selected spritesheet." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE "Replaces the selected spritesheet; opens up the file dialog." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_SELECT "Select the spritesheet element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ANIMATION_LENGTH "Changes the current animation's length." -#define STRING_IMGUI_TOOLTIP_TIMELINE_CREATED_BY "Change the author of the animation." -#define STRING_IMGUI_TOOLTIP_TIMELINE_CREATED_ON_NOW "Set the date of creation to the current system time." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD "Add a layer or null timeline element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER "This is a Layer element.\nThese are the main graphical animation elements.\nClick to select or rename." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NAME "Click to rename the element." -#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 select or rename." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT "Toggle visibility for a rectangle around the null." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE "Remove a timeline element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT "This is the Root element.\nClick to select." -#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_SHIFT_UP "Shift this timeline element above.\nElements with higher indices will display further behind." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET "Click to change the spritesheet the layer is using." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event.\nClick to select." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_FPS "Change the FPS of the animation." -#define STRING_IMGUI_TOOLTIP_TIMELINE_FIT_ANIMATION_LENGTH "Sets the animation length to the latest frame." -#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD "Add a frame to the current selected element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_REMOVE "Removes the selected frame from the current selected element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_LOOP "Toggles the animation looping." -#define STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE "Pauses the animation." -#define STRING_IMGUI_TOOLTIP_TIMELINE_PLAY "Plays the animation." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_AXIS "Toggles the display of the X/Y axes on the spritesheet editor." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_AXIS_COLOR "Changes the color of the X/Y axes on the spritesheet editor." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BACKGROUND_COLOR "Changes the background color of the spritesheet editor." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_CENTER_VIEW "Centers the spritesheet editor's pan." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID "Toggles grid visibility on the spritesheet editor." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SNAP "When using the crop tool, will snap points to the grid." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_COLOR "Changes the spritesheet editor grid color." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_OFFSET "Changes the spritesheet editor grid's offset, in pixels." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SIZE "Changes the spritesheet editor grid's size, in pixels." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_ZOOM "Changes the spritesheet editor zoom level." -#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BORDER "Toggles a border appearing around the confines of the spritesheet." -#define STRING_IMGUI_TOOLTIP_TOOLS_PAN "Use the pan tool.\nWill shift the view as the cursor is dragged." -#define STRING_IMGUI_TOOLTIP_TOOLS_MOVE "Use the move tool.\nWill move selected elements as the cursor is dragged.\n(Animation Preview only.)" -#define STRING_IMGUI_TOOLTIP_TOOLS_ROTATE "Use the rotate tool.\nWill rotate selected elements as the cursor is dragged.\n(Animation Preview only.)" -#define STRING_IMGUI_TOOLTIP_TOOLS_SCALE "Use the scale tool.\nWill scale the selected elements as the cursor is dragged.\n(Animation Preview only.)" -#define STRING_IMGUI_TOOLTIP_TOOLS_CROP "Use the crop tool.\nWill set the crop of the selected elements as the cursor is dragged.\n(Spritesheet Editor only.)" -#define STRING_IMGUI_TOOLTIP_FILE_NEW "Load a blank .anm2 file to edit." -#define STRING_IMGUI_TOOLTIP_FILE_OPEN "Open an existing .anm2 file to edit." -#define STRING_IMGUI_TOOLTIP_FILE_SAVE "Save the current .anm2 file." -#define STRING_IMGUI_TOOLTIP_FILE_SAVE_AS "Save the current .anm2 file to a location." -#define STRING_IMGUI_TOOLTIP_PLAYBACK_IS_LOOP "Toggles the animation playback looping when the time reaches the end." -#define STRING_IMGUI_TOOLTIP_WIZARD_EXPORT_FRAMES_TO_PNG "Records the current animation and exports the frames as .pngs.\nLook in the working directory of the program for them." -#define STRING_IMGUI_TOOLTIP_FILE_MENU "Opens the file menu, for reading/writing anm2 files." -#define STRING_IMGUI_TOOLTIP_PLAYBACK_MENU "Opens the playback menu, for managing playback settings." -#define STRING_IMGUI_TOOLTIP_WIZARD_MENU "Opens the wizard menu, for handy functions involving the anm2." - -#define STRING_OPENGL_VERSION "#version 330" - -#define PATH_SETTINGS "settings.ini" diff --git a/src/anm2.cpp b/src/anm2.cpp index 8ffe636..9ab3390 100644 --- a/src/anm2.cpp +++ b/src/anm2.cpp @@ -1,12 +1,10 @@ +// Anm2 file format; serializing/deserializing + #include "anm2.h" using namespace tinyxml2; -static void _anm2_created_on_set(Anm2* self); - -// Sets the anm2's date to the system's current date -static void -_anm2_created_on_set(Anm2* self) +static void _anm2_created_on_set(Anm2* self) { auto now = std::chrono::system_clock::now(); std::time_t time = std::chrono::system_clock::to_time_t(now); @@ -17,9 +15,7 @@ _anm2_created_on_set(Anm2* self) self->createdOn = timeString.str(); } -// Serializes the anm2 struct into XML and exports it to the given path -bool -anm2_serialize(Anm2* self, const std::string& path) +bool anm2_serialize(Anm2* self, const std::string& path) { XMLDocument document; XMLError error; @@ -33,17 +29,11 @@ anm2_serialize(Anm2* self, const std::string& path) XMLElement* eventsElement; XMLElement* animationsElement; - if (!self || path.empty()) - return false; + if (!self || path.empty()) return false; - // Update creation date on first version - if (self->version == 0) - _anm2_created_on_set(self); + if (self->version == 0) _anm2_created_on_set(self); - // Set anm2 path to argument self->path = path; - - // Update version self->version++; // AnimatedActor @@ -137,7 +127,7 @@ anm2_serialize(Anm2* self, const std::string& path) animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]); animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->defaultAnimation.c_str()); // DefaultAnimation - for (const auto & [id, animation] : self->animations) + for (auto& [id, animation] : self->animations) { XMLElement* animationElement; XMLElement* rootAnimationElement; @@ -155,7 +145,7 @@ anm2_serialize(Anm2* self, const std::string& path) // RootAnimation rootAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION]); - for (const auto & frame : animation.rootAnimation.frames) + for (auto& frame : animation.rootAnimation.frames) { XMLElement* frameElement; @@ -188,8 +178,10 @@ anm2_serialize(Anm2* self, const std::string& path) // LayerAnimations layerAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]); - for (const auto & [layerID, layerAnimation] : animation.layerAnimations) + for (auto& [layerIndex, layerID] : self->layerMap) { + Anm2Item& layerAnimation = animation.layerAnimations[layerID]; + XMLElement* layerAnimationElement; // LayerAnimation @@ -304,30 +296,28 @@ anm2_serialize(Anm2* self, const std::string& path) if (error != XML_SUCCESS) { - std::cout << STRING_ERROR_ANM2_WRITE << path << std::endl; + log_error(std::format(ANM2_WRITE_ERROR, path)); return false; } - std::cout << STRING_INFO_ANM2_WRITE << path << std::endl; + log_info(std::format(ANM2_WRITE_INFO, path)); return true; } -// Loads the .anm2 file and deserializes it into the struct equivalent -bool -anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) +bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) { XMLDocument xmlDocument; XMLError xmlError; const XMLElement* xmlElement; const XMLElement* xmlRoot; - Anm2Animation* animation = NULL; - Anm2Layer* layer = NULL; - Anm2Null* null = NULL; - Anm2Item* item = NULL; - Anm2Event* event = NULL; - Anm2Frame* frame = NULL; - Anm2Spritesheet* spritesheet = NULL; + Anm2Animation* animation = nullptr; + Anm2Layer* layer = nullptr; + Anm2Null* null = nullptr; + Anm2Item* item = nullptr; + Anm2Event* event = nullptr; + Anm2Frame* frame = nullptr; + Anm2Spritesheet* spritesheet = nullptr; Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR; Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID; Anm2Item addItem; @@ -335,9 +325,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) Anm2Null addNull; Anm2Event addEvent; Anm2Spritesheet addSpritesheet; + s32 layerMapIndex = 0; + bool isLayerMapSet = false; + bool isFirstAnimationDone = false; - if (!self || path.empty()) - return false; + if (!self || path.empty()) return false; *self = Anm2{}; @@ -345,13 +337,10 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) if (xmlError != XML_SUCCESS) { - std::cout << STRING_ERROR_ANM2_READ << path << xmlDocument.ErrorStr() << std::endl; + log_error(std::format(ANM2_READ_ERROR, xmlDocument.ErrorStr())); return false; } - // Free the loaded textures used by resources so new ones for the anm2 can be loaded - resources_textures_free(resources); - // Save old working directory and then use anm2's path as directory // (used for loading textures from anm2 correctly which are relative) std::filesystem::path workingPath = std::filesystem::current_path(); @@ -365,8 +354,8 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) // Iterate through elements while (xmlElement) { - const XMLAttribute* xmlAttribute = NULL; - const XMLElement* xmlChild = NULL; + const XMLAttribute* xmlAttribute = nullptr; + const XMLElement* xmlChild = nullptr; s32 id = 0; anm2Element = ANM2_ELEMENT_STRING_TO_ENUM(xmlElement->Name()); @@ -389,6 +378,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) id = map_next_id_get(self->animations); self->animations[id] = Anm2Animation{}; animation = &self->animations[id]; + + if (isFirstAnimationDone) + isLayerMapSet = true; + + isFirstAnimationDone = true; break; case ANM2_ELEMENT_ROOT_ANIMATION: // RootAnimation item = &animation->rootAnimation; @@ -455,6 +449,13 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) break; case ANM2_ATTRIBUTE_LAYER_ID: // LayerId id = atoi(xmlAttribute->Value()); + + if (!isLayerMapSet) + { + self->layerMap[layerMapIndex] = id; + layerMapIndex++; + } + animation->layerAnimations[id] = addItem; item = &animation->layerAnimations[id]; break; @@ -588,9 +589,7 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) xmlAttribute = xmlAttribute->Next(); } - // Load this anm2's spritesheet textures - if (anm2Element == ANM2_ELEMENT_SPRITESHEET) - resources_texture_init(resources, spritesheet->path , id); + if (anm2Element == ANM2_ELEMENT_SPRITESHEET) resources_texture_init(resources, spritesheet->path , id); xmlChild = xmlElement->FirstChildElement(); @@ -600,7 +599,6 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) continue; } - // Iterate through siblings while (xmlElement) { const XMLElement* xmlNext; @@ -613,12 +611,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) break; } - // If no siblings, return to parent. If no parent, end parsing - xmlElement = xmlElement->Parent() ? xmlElement->Parent()->ToElement() : NULL; + xmlElement = xmlElement->Parent() ? xmlElement->Parent()->ToElement() : nullptr; } } - std::cout << STRING_INFO_ANM2_READ << path << std::endl; + log_info(std::format(ANM2_READ_INFO, path)); // Return to old working directory std::filesystem::current_path(workingPath); @@ -626,9 +623,7 @@ anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) return true; } -// Adds a layer to the anm2 -void -anm2_layer_add(Anm2* self) +void anm2_layer_add(Anm2* self) { s32 id = map_next_id_get(self->layers); @@ -638,9 +633,7 @@ anm2_layer_add(Anm2* self) animation.layerAnimations[id] = Anm2Item{}; } -// Removes a layer from the anm2 -void -anm2_layer_remove(Anm2* self, s32 id) +void anm2_layer_remove(Anm2* self, s32 id) { // Make sure the layer exists auto it = self->layers.find(id); @@ -653,9 +646,7 @@ anm2_layer_remove(Anm2* self, s32 id) animation.layerAnimations.erase(id); } -// Adds a null to the anm2 -void -anm2_null_add(Anm2* self) +void anm2_null_add(Anm2* self) { s32 id = map_next_id_get(self->nulls); @@ -665,9 +656,7 @@ anm2_null_add(Anm2* self) animation.nullAnimations[id] = Anm2Item{}; } -// Removes the specified null from the anm2 -void -anm2_null_remove(Anm2* self, s32 id) +void anm2_null_remove(Anm2* self, s32 id) { // Make sure the null exists auto it = self->nulls.find(id); @@ -680,26 +669,16 @@ anm2_null_remove(Anm2* self, s32 id) animation.nullAnimations.erase(id); } -// Adds an animation to the anm2 -s32 -anm2_animation_add(Anm2* self) +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] = Anm2Item{}; - } + for (auto& [layerID, layer] : self->layers) + animation.layerAnimations[layerID] = Anm2Item{}; + for (auto & [nullID, null] : self->nulls) + animation.nullAnimations[nullID] = Anm2Item{}; - // Match nulls - for (auto & [nullID, null] : self->nulls) - { - animation.nullAnimations[nullID] = Anm2Item{}; - } - - // Add a root frame animation.rootAnimation.frames.push_back(Anm2Frame{}); self->animations[id] = animation; @@ -707,38 +686,29 @@ anm2_animation_add(Anm2* self) return id; } -// Removes an animation by id from the anm2 -void -anm2_animation_remove(Anm2* self, s32 id) +void anm2_animation_remove(Anm2* self, s32 id) { self->animations.erase(id); } -// Sets the anm2 to default -void -anm2_new(Anm2* self) +void anm2_new(Anm2* self) { *self = Anm2{}; _anm2_created_on_set(self); } -Anm2Animation* -anm2_animation_from_reference(Anm2* self, Anm2Reference* reference) +Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference) { auto it = self->animations.find(reference->animationID); - if (it == self->animations.end()) - return NULL; + if (it == self->animations.end()) return nullptr; return &it->second; } -// Returns the item from a anm2 reference. -Anm2Item* -anm2_item_from_reference(Anm2* self, Anm2Reference* reference) +Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference) { Anm2Animation* animation = anm2_animation_from_reference(self, reference); - if (!animation) - return NULL; + if (!animation) return nullptr; switch (reference->itemType) { @@ -748,78 +718,108 @@ anm2_item_from_reference(Anm2* self, Anm2Reference* reference) { auto it = animation->layerAnimations.find(reference->itemID); if (it == animation->layerAnimations.end()) - return NULL; + return nullptr; return &it->second; } case ANM2_NULL: { auto it = animation->nullAnimations.find(reference->itemID); if (it == animation->nullAnimations.end()) - return NULL; + return nullptr; return &it->second; } case ANM2_TRIGGERS: return &animation->triggers; default: - return NULL; + return nullptr; } } -// Gets the frame from the reference's properties -Anm2Frame* -anm2_frame_from_reference(Anm2* self, Anm2Reference* reference) +Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference) { Anm2Item* item = anm2_item_from_reference(self, reference); - if (!item) - return NULL; - - if (reference->frameIndex < 0 || reference->frameIndex >= (s32)item->frames.size()) - return NULL; + if (!item) return nullptr; + if (reference->frameIndex < 0 || reference->frameIndex >= (s32)item->frames.size()) return nullptr; return &item->frames[reference->frameIndex]; } -// Creates or fetches a frame from a given time. -void -anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time) +s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time) { Anm2Animation* animation = anm2_animation_from_reference(self, &reference); - if (!animation) - return; - - if (time < 0 || time > animation->frameNum) - return; + if (!animation) return INDEX_NONE; + if (time < 0 || time > animation->frameNum) return INDEX_NONE; Anm2Item* item = anm2_item_from_reference(self, &reference); - if (!item) - return; + if (!item) return INDEX_NONE; - Anm2Frame* nextFrame = NULL; s32 delayCurrent = 0; s32 delayNext = 0; - for (s32 i = 0; i < (s32)item->frames.size(); i++) + for (auto [i, frame] : std::views::enumerate(item->frames)) { - *frame = item->frames[i]; - delayNext += frame->delay; + delayNext += frame.delay; - // Use the last parsed frame as the given frame. if (time >= delayCurrent && time < delayNext) - { - if (i + 1 < (s32)item->frames.size()) - nextFrame = &item->frames[i + 1]; - else - nextFrame = NULL; - break; - } + return i; - delayCurrent += frame->delay; + delayCurrent += frame.delay; } - // Interpolate, but only if there's a frame following. + return INDEX_NONE; +} + +void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time) +{ + Anm2Animation* animation = anm2_animation_from_reference(self, &reference); + + if (!animation) return; + if (time < 0 || time > animation->frameNum) return; + + Anm2Item* item = anm2_item_from_reference(self, &reference); + + if (!item) return; + + Anm2Frame* nextFrame = nullptr; + s32 delayCurrent = 0; + s32 delayNext = 0; + + for (auto [i, iFrame] : std::views::enumerate(item->frames)) + { + + if (reference.itemType == ANM2_TRIGGERS) + { + if ((s32)time == iFrame.atFrame) + { + *frame = iFrame; + break; + } + } + else + { + *frame = iFrame; + + delayNext += frame->delay; + + if (time >= delayCurrent && time < delayNext) + { + if (i + 1 < (s32)item->frames.size()) + nextFrame = &item->frames[i + 1]; + else + nextFrame = nullptr; + break; + } + + delayCurrent += frame->delay; + } + } + + if (reference.itemType == ANM2_TRIGGERS) + return; + if (frame->isInterpolated && nextFrame) { f32 interpolationTime = (time - delayCurrent) / (delayNext - delayCurrent); @@ -832,62 +832,54 @@ anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 } } -// Returns the animation's length, based on the present frames. -s32 -anm2_animation_length_get(Anm2* self, s32 animationID) +s32 anm2_animation_length_get(Anm2Animation* self) { - auto it = self->animations.find(animationID); - if (it == self->animations.end()) - return -1; + s32 length = 0; - Anm2Animation* animation = &it->second; - s32 delayHighest = 0; + auto accumulate_max_delay = [&](const std::vector& frames) + { + s32 delaySum = 0; + for (const auto& frame : frames) + { + delaySum += frame.delay; + length = std::max(length, delaySum); + } + }; - // Go through all items and see which one has the latest frame; that's the length - // Root - for (auto & frame : animation->rootAnimation.frames) - delayHighest = std::max(delayHighest, frame.delay); + accumulate_max_delay(self->rootAnimation.frames); - // Layer - for (auto & [id, item] : animation->layerAnimations) - for (auto & frame : item.frames) - delayHighest = std::max(delayHighest, frame.delay); + for (const auto& [_, item] : self->layerAnimations) + accumulate_max_delay(item.frames); - // Null - for (auto & [id, item] : animation->nullAnimations) - for (auto & frame : item.frames) - delayHighest = std::max(delayHighest, frame.delay); + for (const auto& [_, item] : self->nullAnimations) + accumulate_max_delay(item.frames); - // Triggers - for (auto & trigger : animation->triggers.frames) - delayHighest = std::max(delayHighest, trigger.atFrame); + for (const auto& frame : self->triggers.frames) + length = std::max(length, frame.atFrame); - return delayHighest; + return length; } -// Will try adding a frame to the anm2 given the specified reference -Anm2Frame* -anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time) +void anm2_animation_length_set(Anm2Animation* self) +{ + self->frameNum = anm2_animation_length_get(self); +} + +Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time) { Anm2Animation* animation = anm2_animation_from_reference(self, reference); Anm2Item* item = anm2_item_from_reference(self, reference); - if (!animation || !item) - return NULL; + if (!animation || !item) return nullptr; if (item) { Anm2Frame frame = Anm2Frame{}; - s32 index = -1; + s32 index = INDEX_NONE; if (reference->itemType == ANM2_TRIGGERS) { - // Don't add redundant triggers - for (auto & frameCheck : item->frames) - { - if (frameCheck.atFrame == time) - return NULL; - } + for (auto & frameCheck : item->frames) if (frameCheck.atFrame == time) return nullptr; frame.atFrame = time; index = item->frames.size(); @@ -896,20 +888,15 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time) { s32 frameDelayCount = 0; - // Get the latest frame delay, to see where this frame might lie for (auto & frameCheck : item->frames) frameDelayCount += frameCheck.delay; - // If adding the smallest frame delay would be over the length, don't bother - if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum) - return NULL; + if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum) return nullptr; - // Will insert next to frame if frame exists Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference); if (checkFrame) { - // Will shrink frame delay to fit if (frameDelayCount + checkFrame->delay > animation->frameNum) frame.delay = animation->frameNum - frameDelayCount; @@ -924,27 +911,22 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time) return &item->frames[index]; } - return NULL; + return nullptr; } -// Clears the anm2 reference, totally -void -anm2_reference_clear(Anm2Reference* self) +void anm2_reference_clear(Anm2Reference* self) { *self = Anm2Reference{}; } -// Clears the anm2 reference's item -void -anm2_reference_item_clear(Anm2Reference* self) +void anm2_reference_item_clear(Anm2Reference* self) { self->itemType = ANM2_NONE; - self->itemID = -1; + self->itemID = ID_NONE; + self->frameIndex = INDEX_NONE; } -// Clears the anm2 reference's frame -void -anm2_reference_frame_clear(Anm2Reference* self) +void anm2_reference_frame_clear(Anm2Reference* self) { - self->frameIndex = -1; + self->frameIndex = INDEX_NONE; } \ No newline at end of file diff --git a/src/anm2.h b/src/anm2.h index 0b0b880..3400e20 100644 --- a/src/anm2.h +++ b/src/anm2.h @@ -13,6 +13,12 @@ #define ANM2_FRAME_DELAY_MIN 1 #define ANM2_STRING_MAX 0xFF +#define ANM2_READ_ERROR "Failed to read anm2 from file: {}" +#define ANM2_READ_INFO "Read anm2 from file: {}" +#define ANM2_WRITE_ERROR "Failed to write anm2 to file: {}" +#define ANM2_WRITE_INFO "Wrote anm2 to file: {}" +#define STRING_ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p" + /* Elements */ #define ANM2_ELEMENT_LIST \ X(ANIMATED_ACTOR, "AnimatedActor") \ @@ -120,24 +126,24 @@ enum Anm2Type struct Anm2Spritesheet { - std::string path; + std::string path{}; }; struct Anm2Layer { - std::string name = STRING_ANM2_NEW_LAYER; + std::string name = "New Layer"; s32 spritesheetID = -1; }; struct Anm2Null { - std::string name = STRING_ANM2_NEW_NULL; + std::string name = "New Null"; bool isShowRect = false; }; struct Anm2Event { - std::string name = STRING_ANM2_NEW_EVENT; + std::string name = "New Event"; }; struct Anm2Frame @@ -146,8 +152,8 @@ struct Anm2Frame bool isVisible = true; f32 rotation = 1.0f; s32 delay = ANM2_FRAME_DELAY_MIN; - s32 atFrame = -1; - s32 eventID = -1; + s32 atFrame = INDEX_NONE; + s32 eventID = ID_NONE; vec2 crop = {0.0f, 0.0f}; vec2 pivot = {0.0f, 0.0f}; vec2 position = {0.0f, 0.0f}; @@ -166,7 +172,7 @@ struct Anm2Item struct Anm2Animation { s32 frameNum = ANM2_FRAME_NUM_MIN; - std::string name = STRING_ANM2_NEW_ANIMATION; + std::string name = "New Animation"; bool isLoop = true; Anm2Item rootAnimation; std::map layerAnimations; @@ -176,29 +182,47 @@ struct Anm2Animation struct Anm2 { - std::string path; - std::string defaultAnimation; - std::string createdBy = STRING_ANM2_CREATED_BY_DEFAULT; - std::string createdOn; + std::string path{}; + std::string defaultAnimation{}; + std::string createdBy = "robot"; + std::string createdOn{}; std::map spritesheets; std::map layers; std::map nulls; - std::map events; + std::map events; std::map animations; + std::map layerMap; // id, index s32 fps = 30; s32 version = 0; }; struct Anm2Reference { - s32 animationID = -1; + s32 animationID = ID_NONE; Anm2Type itemType = ANM2_NONE; - s32 itemID = -1; - s32 frameIndex = -1; - + s32 itemID = ID_NONE; + s32 frameIndex = INDEX_NONE; auto operator<=>(const Anm2Reference&) const = default; }; +struct Anm2AnimationWithID +{ + s32 id; + Anm2Animation animation; +}; + +struct Anm2EventWithID +{ + s32 id; + Anm2Event event; +}; + +struct Anm2FrameWithReference +{ + Anm2Reference reference; + Anm2Frame frame; +}; + void anm2_layer_add(Anm2* self); void anm2_layer_remove(Anm2* self, s32 id); void anm2_null_add(Anm2* self); @@ -213,9 +237,11 @@ void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const std:: Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference); Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference); Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference); +s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time); Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time); void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time); void anm2_reference_clear(Anm2Reference* self); void anm2_reference_item_clear(Anm2Reference* self); void anm2_reference_frame_clear(Anm2Reference* self); -s32 anm2_animation_length_get(Anm2* self, s32 animationID); \ No newline at end of file +s32 anm2_animation_length_get(Anm2Animation* self); +void anm2_animation_length_set(Anm2Animation* self); \ No newline at end of file diff --git a/src/canvas.cpp b/src/canvas.cpp new file mode 100644 index 0000000..6010b26 --- /dev/null +++ b/src/canvas.cpp @@ -0,0 +1,50 @@ +// Common rendering area + +#include "canvas.h" + +void canvas_init(Canvas* self, vec2 size) +{ + self->size = size; + + 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, (s32)self->size.x, (s32)self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + 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, (s32)self->size.x, (s32)self->size.y); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Texture + glGenVertexArrays(1, &self->textureVAO); + glGenBuffers(1, &self->textureVBO); + glGenBuffers(1, &self->textureEBO); + + glBindVertexArray(self->textureVAO); + + glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW); + + // Position attribute + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0); + + // UV attribute + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32))); + + glBindVertexArray(0); +} \ No newline at end of file diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 index 0000000..b012a63 --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,33 @@ +#pragma once + +#include "texture.h" +#include "shader.h" + +#define CANVAS_ZOOM_MIN 1 +#define CANVAS_ZOOM_MAX 1000 +#define CANVAS_ZOOM_STEP 25 +#define CANVAS_LINE_LENGTH 100000.0f +#define CANVAS_GRID_RANGE 100 +#define CANVAS_GRID_MIN 1 +#define CANVAS_GRID_MAX 100 + +const f32 CANVAS_AXIS_VERTICES[] = +{ + -CANVAS_LINE_LENGTH, 0.0f, + CANVAS_LINE_LENGTH, 0.0f, + 0.0f, -CANVAS_LINE_LENGTH, + 0.0f, CANVAS_LINE_LENGTH +}; + +struct Canvas +{ + GLuint fbo; + GLuint rbo; + GLuint texture; + GLuint textureEBO; + GLuint textureVAO; + GLuint textureVBO; + vec2 size; +}; + +void canvas_init(Canvas* self, vec2 size); \ No newline at end of file diff --git a/src/clipboard.cpp b/src/clipboard.cpp new file mode 100644 index 0000000..ade416c --- /dev/null +++ b/src/clipboard.cpp @@ -0,0 +1,140 @@ +#include "clipboard.h" + +static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2) +{ + switch (self->type) + { + case CLIPBOARD_FRAME: + { + Anm2FrameWithReference frameWithReference = std::get(self->data); + Anm2Animation* animation = anm2_animation_from_reference(anm2, &frameWithReference.reference); + + if (!animation) break; + + std::vector* frames = nullptr; + + switch (frameWithReference.reference.itemType) + { + case ANM2_ROOT: + frames = &animation->rootAnimation.frames; + break; + case ANM2_LAYER: + frames = &animation->layerAnimations[frameWithReference.reference.itemID].frames; + break; + case ANM2_NULL: + frames = &animation->nullAnimations[frameWithReference.reference.itemID].frames; + break; + case ANM2_TRIGGERS: + frames = &animation->triggers.frames; + break; + default: + break; + } + + if (frames) + frames->erase(frames->begin() + frameWithReference.reference.frameIndex); + + break; + } + case CLIPBOARD_ANIMATION: + { + Anm2AnimationWithID animationWithID = std::get(self->data); + + for (auto & [id, animation] : anm2->animations) + { + if (id == animationWithID.id) + anm2->animations.erase(animationWithID.id); + } + break; + } + case CLIPBOARD_EVENT: + { + Anm2EventWithID eventWithID = std::get(self->data); + + for (auto & [id, event] : anm2->events) + { + if (id == eventWithID.id) + anm2->events.erase(eventWithID.id); + } + break; + } + default: + break; + } +} + +static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* location, Anm2* anm2) +{ + switch (self->type) + { + case CLIPBOARD_FRAME: + { + Anm2FrameWithReference* frameWithReference = std::get_if(&self->data); + Anm2Reference* reference = std::get_if(location); + + if (!frameWithReference || !reference) break; + if (frameWithReference->reference.itemType != reference->itemType) break; + + Anm2Animation* animation = anm2_animation_from_reference(anm2, reference); + Anm2Item* anm2Item = anm2_item_from_reference(anm2, reference); + + if (!animation || !anm2Item) break; + + s32 insertIndex = (reference->itemType == ANM2_TRIGGERS) ? + reference->frameIndex : MAX(reference->frameIndex, (s32)anm2Item->frames.size()); + + anm2Item->frames.insert(anm2Item->frames.begin() + insertIndex, frameWithReference->frame); + + anm2_animation_length_set(animation); + break; + } + case CLIPBOARD_ANIMATION: + { + s32 index = 0; + + if (std::holds_alternative(*location)) + index = std::get(*location); + + index = CLAMP(index, 0, (s32)anm2->animations.size()); + + map_insert_shift(anm2->animations, index, std::get(self->data).animation); + break; + } + case CLIPBOARD_EVENT: + { + s32 index = 0; + + if (std::holds_alternative(*location)) + index = std::get(*location); + + index = CLAMP(index, 0, (s32)anm2->events.size()); + + map_insert_shift(anm2->events, index, std::get(self->data).event); + break; + } + default: + break; + } +} + +void clipboard_copy(Clipboard* self) +{ + self->item = self->hoveredItem; +} + +void clipboard_cut(Clipboard* self) +{ + self->item = self->hoveredItem; + _clipboard_item_remove(&self->item, self->anm2); +} + +void clipboard_paste(Clipboard* self) +{ + _clipboard_item_paste(&self->item, &self->location, self->anm2); +} + +void clipboard_init(Clipboard* self, Anm2* anm2) +{ + self->anm2 = anm2; +} + diff --git a/src/clipboard.h b/src/clipboard.h new file mode 100644 index 0000000..df4db7b --- /dev/null +++ b/src/clipboard.h @@ -0,0 +1,44 @@ +#pragma once + +#include "anm2.h" + +enum ClipboardItemType +{ + CLIPBOARD_NONE, + CLIPBOARD_FRAME, + CLIPBOARD_ANIMATION, + CLIPBOARD_EVENT +}; + +struct ClipboardItem +{ + std::variant data = std::monostate(); + ClipboardItemType type = CLIPBOARD_NONE; + + ClipboardItem() = default; + + ClipboardItem(const Anm2FrameWithReference& frame) + : data(frame), type(CLIPBOARD_FRAME) {} + + ClipboardItem(const Anm2AnimationWithID& anim) + : data(anim), type(CLIPBOARD_ANIMATION) {} + + ClipboardItem(const Anm2EventWithID& event) + : data(event), type(CLIPBOARD_EVENT) {} +}; + +using ClipboardLocation = std::variant; + +struct Clipboard +{ + Anm2* anm2 = nullptr; + ClipboardItem item; + ClipboardItem hoveredItem; + ClipboardLocation location; +}; + +void clipboard_copy(Clipboard* self); +void clipboard_cut(Clipboard* self); +void clipboard_paste(Clipboard* self); +void clipboard_init(Clipboard* self, Anm2* anm2); + diff --git a/src/dialog.cpp b/src/dialog.cpp index d39866c..5d61891 100644 --- a/src/dialog.cpp +++ b/src/dialog.cpp @@ -1,10 +1,10 @@ +// File dialog; saving/writing to files + #include "dialog.h" static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter); -// Callback that runs during the file dialog; will get the path and determine if one has been selected -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; @@ -19,9 +19,7 @@ _dialog_callback(void* userdata, const char* const* filelist, s32 filter) self->isSelected = false; } -// Initializes dialog -void -dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window) +void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window) { self->anm2 = anm2; self->reference = reference; @@ -29,41 +27,31 @@ dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resou self->window = window; } -// Opens file dialog to open a new anm2 -void -dialog_anm2_open(Dialog* self) +void dialog_anm2_open(Dialog* self) { - SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_ANM2, 1, NULL, false); + SDL_ShowOpenFileDialog(_dialog_callback, self, nullptr, DIALOG_FILE_FILTER_ANM2, 1, nullptr, false); self->type = DIALOG_ANM2_OPEN; } -// Opens file dialog to save anm2 -void -dialog_anm2_save(Dialog* self) +void dialog_anm2_save(Dialog* self) { - SDL_ShowSaveFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_ANM2, 1, NULL); + SDL_ShowSaveFileDialog(_dialog_callback, self, nullptr, DIALOG_FILE_FILTER_ANM2, 1, nullptr); self->type = DIALOG_ANM2_SAVE; } -// Opens file dialog to open png -void -dialog_png_open(Dialog* self) +void dialog_png_open(Dialog* self) { - SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_PNG, 1, NULL, false); + SDL_ShowOpenFileDialog(_dialog_callback, self, nullptr, DIALOG_FILE_FILTER_PNG, 1, nullptr, false); self->type = DIALOG_PNG_OPEN; } -// Opens file dialog to replace a given png -void -dialog_png_replace(Dialog* self) +void dialog_png_replace(Dialog* self) { - SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_PNG, 1, NULL, false); + SDL_ShowOpenFileDialog(_dialog_callback, self, nullptr, DIALOG_FILE_FILTER_PNG, 1, nullptr, false); self->type = DIALOG_PNG_REPLACE; } -// Ticks dialog -void -dialog_tick(Dialog* self) +void dialog_tick(Dialog* self) { if (self->isSelected) { diff --git a/src/dialog.h b/src/dialog.h index a531f4c..f9f895b 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -26,12 +26,12 @@ enum DialogType struct Dialog { - Anm2* anm2 = NULL; - Anm2Reference* reference = NULL; - Resources* resources = NULL; - SDL_Window* window = NULL; - std::string path; - s32 replaceID = -1; + Anm2* anm2 = nullptr; + Anm2Reference* reference = nullptr; + Resources* resources = nullptr; + SDL_Window* window = nullptr; + std::string path{}; + s32 replaceID = ID_NONE; DialogType type = DIALOG_NONE; bool isSelected = false; }; diff --git a/src/editor.cpp b/src/editor.cpp index 76fffb7..f308fba 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1,17 +1,15 @@ +// Handles the rendering of the spritesheet editor + #include "editor.h" -static s32 _editor_grid_set(Editor* self); - -// Sets the editor's grid -static s32 -_editor_grid_set(Editor* self) +static s32 _editor_grid_set(Editor* self) { std::vector vertices; s32 verticalLineCount = (s32)(EDITOR_SIZE.x / MIN(self->settings->editorGridSizeX, EDITOR_GRID_MIN)); s32 horizontalLineCount = (s32)(EDITOR_SIZE.y / MIN(self->settings->editorGridSizeY, EDITOR_GRID_MIN)); - /* Vertical */ + // Vertical for (s32 i = 0; i <= verticalLineCount; i++) { s32 x = i * self->settings->editorGridSizeX - self->settings->editorGridOffsetX; @@ -23,7 +21,7 @@ _editor_grid_set(Editor* self) vertices.push_back(1.0f); } - /* Horizontal */ + // Horizontal for (s32 i = 0; i <= horizontalLineCount; i++) { s32 y = i * self->settings->editorGridSizeY - self->settings->editorGridOffsetY; @@ -45,9 +43,7 @@ _editor_grid_set(Editor* self) return (s32)vertices.size(); } -// Initializes editor -void -editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) +void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) { self->anm2 = anm2; self->reference = reference; @@ -61,7 +57,7 @@ editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resou glGenTextures(1, &self->texture); glBindTexture(GL_TEXTURE_2D, self->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)EDITOR_SIZE.x, (s32)EDITOR_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)EDITOR_SIZE.x, (s32)EDITOR_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -98,7 +94,7 @@ editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resou glBindVertexArray(self->textureVAO); glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, NULL, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW); @@ -116,15 +112,14 @@ editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resou _editor_grid_set(self); } -// Draws the editor -void -editor_draw(Editor* self) +void editor_draw(Editor* self) { GLuint shaderLine = self->resources->shaders[SHADER_LINE]; GLuint shaderLineDotted = self->resources->shaders[SHADER_LINE_DOTTED]; GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE]; - f32 zoomFactor = PERCENT_TO_UNIT(self->settings->editorZoom); + f32 zoomFactor = PERCENT_TO_UNIT(self->settings->editorZoom); + // Get normalized panning glm::vec2 ndcPan = glm::vec2(-self->settings->editorPanX / (EDITOR_SIZE.x / 2.0f), -self->settings->editorPanY / (EDITOR_SIZE.y / 2.0f)); @@ -294,16 +289,12 @@ editor_draw(Editor* self) glBindFramebuffer(GL_FRAMEBUFFER, 0); } -// Ticks editor -void -editor_tick(Editor* self) +void editor_tick(Editor* self) { self->settings->editorZoom = CLAMP(self->settings->editorZoom, EDITOR_ZOOM_MIN, EDITOR_ZOOM_MAX); } -// Frees editor -void -editor_free(Editor* self) +void editor_free(Editor* self) { glDeleteTextures(1, &self->texture); glDeleteFramebuffers(1, &self->fbo); diff --git a/src/editor.h b/src/editor.h index a575833..3c662e0 100644 --- a/src/editor.h +++ b/src/editor.h @@ -3,6 +3,7 @@ #include "anm2.h" #include "resources.h" #include "settings.h" +#include "canvas.h" #define EDITOR_ZOOM_MIN 1 #define EDITOR_ZOOM_MAX 1000 @@ -12,7 +13,7 @@ #define EDITOR_GRID_OFFSET_MIN 0 #define EDITOR_GRID_OFFSET_MAX 100 -const vec2 EDITOR_SIZE = {5000, 5000}; +const vec2 EDITOR_SIZE = {2000, 2000}; const vec2 EDITOR_PIVOT_SIZE = {4, 4}; const vec4 EDITOR_TEXTURE_TINT = COLOR_OPAQUE; const vec4 EDITOR_BORDER_TINT = COLOR_OPAQUE; @@ -20,10 +21,10 @@ const vec4 EDITOR_FRAME_TINT = COLOR_RED; struct Editor { - Anm2* anm2 = NULL; - Anm2Reference* reference = NULL; - Resources* resources = NULL; - Settings* settings = NULL; + Anm2* anm2 = nullptr; + Anm2Reference* reference = nullptr; + Resources* resources = nullptr; + Settings* settings = nullptr; GLuint fbo; GLuint rbo; GLuint gridVAO; diff --git a/src/imgui.cpp b/src/imgui.cpp index 88eefea..cee1e67 100644 --- a/src/imgui.cpp +++ b/src/imgui.cpp @@ -1,1133 +1,1140 @@ +// Handles everything imgui + #include "imgui.h" -static void _imgui_tooltip(const std::string& tooltip); -static void _imgui_timeline_item_frames(Imgui* self, Anm2Reference reference, s32* index); -static void _imgui_timeline_item(Imgui* self, Anm2Reference reference, s32* index); -static void _imgui_timeline(Imgui* self); -static void _imgui_animations(Imgui* self); -static void _imgui_events(Imgui* self); -static void _imgui_spritesheets(Imgui* self); -static void _imgui_frame_properties(Imgui* self); -static void _imgui_spritesheet_editor(Imgui* self); -static void _imgui_animation_preview(Imgui* self); -static void _imgui_taskbar(Imgui* self); -static void _imgui_undoable(Imgui* self); -static void _imgui_undo_stack_push(Imgui* self); -static void _imgui_spritesheet_editor_set(Imgui* self, s32 spritesheetID); - -// Push undo stack using imgui's members -static void _imgui_undo_stack_push(Imgui* self) +template +static void _imgui_clipboard_hovered_item_set(Imgui* self, const T& data) { - Snapshot snapshot = {*self->anm2, *self->reference, *self->time}; - snapshots_undo_stack_push(self->snapshots, &snapshot); + self->clipboard->hoveredItem = ClipboardItem(data); } -// Tooltip for the last hovered widget -static void _imgui_tooltip(const std::string& tooltip) +static void _imgui_atlas_image(Imgui* self, TextureType texture) { - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - ImGui::SetTooltip(tooltip.c_str()); + ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[texture]), IMVEC2_ATLAS_UV_GET(texture)); } -// If the last widget is activated, pushes the undo stack -static void _imgui_undoable(Imgui* self) +static void _imgui_item_text(const ImguiItem& item) { - if (ImGui::IsItemActivated()) - _imgui_undo_stack_push(self); + ImGui::Text(item.label.c_str()); } -// Will push a new spritesheetID to the editor -static void _imgui_spritesheet_editor_set(Imgui* self, s32 spritesheetID) +static void _imgui_text_string(const std::string& string) { - // Make sure the spritesheet exists! - if (self->anm2->spritesheets.contains(spritesheetID)) - self->editor->spritesheetID = spritesheetID; + ImGui::Text(string.c_str()); } -// Drawing the frames of an Anm2Item -static void -_imgui_timeline_item_frames(Imgui* self, Anm2Reference reference, s32* index) +static bool _imgui_is_window_hovered(void) { - ImVec2 frameStartPos; - ImVec2 framePos; - ImVec2 frameFinishPos; - ImVec2 cursorPos = ImGui::GetCursorPos(); - ImVec4 frameColor; - ImVec4 hoveredColor; - ImVec4 activeColor; - - Anm2Animation* animation = anm2_animation_from_reference(self->anm2, &reference); - Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); + return ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); +} - switch (reference.itemType) +static bool _imgui_is_no_click_on_item(void) +{ + return ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && _imgui_is_window_hovered(); +} + +static void _imgui_item(Imgui* self, const ImguiItem& item, bool* isActivated) +{ + if (item.is_mnemonic()) { - case ANM2_ROOT: - frameColor = IMGUI_TIMELINE_ROOT_FRAME_COLOR; - hoveredColor = IMGUI_TIMELINE_ROOT_HIGHLIGHT_COLOR; - activeColor = IMGUI_TIMELINE_ROOT_ACTIVE_COLOR; - break; - case ANM2_LAYER: - frameColor = IMGUI_TIMELINE_LAYER_FRAME_COLOR; - hoveredColor = IMGUI_TIMELINE_LAYER_HIGHLIGHT_COLOR; - activeColor = IMGUI_TIMELINE_LAYER_ACTIVE_COLOR; - break; - case ANM2_NULL: - frameColor = IMGUI_TIMELINE_NULL_FRAME_COLOR; - hoveredColor = IMGUI_TIMELINE_NULL_HIGHLIGHT_COLOR; - activeColor = IMGUI_TIMELINE_NULL_ACTIVE_COLOR; - break; - case ANM2_TRIGGERS: - frameColor = IMGUI_TIMELINE_TRIGGERS_FRAME_COLOR; - hoveredColor = IMGUI_TIMELINE_TRIGGERS_HIGHLIGHT_COLOR; - activeColor = IMGUI_TIMELINE_TRIGGERS_ACTIVE_COLOR; - break; - default: - break; + ImVec2 pos = ImGui::GetItemRectMin(); + ImFont* font = ImGui::GetFont(); + f32 fontSize = ImGui::GetFontSize(); + const char* start = item.label.c_str(); + const char* charPointer = start + item.mnemonicIndex; + + pos.x += ImGui::GetStyle().FramePadding.x; + + f32 offset = font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, start, charPointer).x; + f32 charWidth = font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, charPointer, charPointer + 1).x; + + ImVec2 lineStart = ImVec2(pos.x + offset, pos.y + fontSize + 1.0f); + ImVec2 lineEnd = ImVec2(lineStart.x + charWidth, lineStart.y); + + ImU32 color = ImGui::GetColorU32(ImGuiCol_Text); + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, color, 1.0f); + + if (isActivated && ImGui::IsKeyChordPressed(ImGuiMod_Alt | item.mnemonicKey)) + { + *isActivated = true; + ImGui::CloseCurrentPopup(); + } } - ImGui::PushID(*index); - - // Draw only if the animation has a length above 0 - if (animation->frameNum > 0) + if (isActivated && self->isHotkeysEnabled && (item.is_chord() && ImGui::IsKeyChordPressed(item.chord))) { - ImVec2 frameListSize = {IMGUI_TIMELINE_FRAME_SIZE.x * animation->frameNum, IMGUI_TIMELINE_ELEMENTS_TIMELINE_SIZE.y}; - ImVec2 mousePosRelative; + bool isFocus = !item.is_focus_window() || (imgui_nav_window_root_get() == item.focusWindow); + + if (isFocus) + *isActivated = true; + } - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + if (item.is_tooltip() && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) + ImGui::SetTooltip(item.tooltip.c_str()); - ImGui::BeginChild(STRING_IMGUI_TIMELINE_FRAMES, frameListSize, true); - - // Will deselect frame if hovering and click; but, if it's later clicked, this won't have any effect - if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - anm2_reference_frame_clear(self->reference); - - vec2 mousePos = IMVEC2_TO_VEC2(ImGui::GetMousePos()); - vec2 windowPos = IMVEC2_TO_VEC2(ImGui::GetWindowPos()); - - f32 scrollX = ImGui::GetScrollX(); - f32 mousePosRelativeX = mousePos.x - windowPos.x - scrollX; - - frameStartPos = ImGui::GetCursorPos(); - - // Draw the frame background - for (s32 i = 0; i < animation->frameNum; i++) + if (isActivated && *isActivated) + { + if (item.isUndoable) imgui_undo_stack_push(self); + if (item.function) item.function(self); + + if (item.is_popup()) { - ImGui::PushID(i); + ImGui::OpenPopup(item.popup.c_str()); - ImVec2 frameTexturePos = ImGui::GetCursorScreenPos(); - - if (i % IMGUI_TIMELINE_FRAME_INDICES_MULTIPLE == 0) + switch (item.popupType) { - ImVec2 bgMin = frameTexturePos; - ImVec2 bgMax = ImVec2(frameTexturePos.x + IMGUI_TIMELINE_FRAME_SIZE.x, frameTexturePos.y + IMGUI_TIMELINE_FRAME_SIZE.y); - ImU32 bgColor = ImGui::GetColorU32(IMGUI_FRAME_OVERLAY_COLOR); - ImGui::GetWindowDrawList()->AddRectFilled(bgMin, bgMax, bgColor); + case POPUP_CENTER_SCREEN: + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + break; + case POPUP_BY_ITEM: + default: + ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y + ImGui::GetItemRectSize().y)); + break; } - - ImGui::Image(self->resources->atlas.id, IMGUI_TIMELINE_FRAME_SIZE, IMVEC2_ATLAS_UV_GET(TEXTURE_FRAME_ALT)); - - ImGui::SameLine(); - ImGui::PopID(); } + } +} - // Draw each frame - for (auto [i, frame] : std::views::enumerate(item->frames)) - { - reference.frameIndex = i; +static bool _imgui_item_combo(Imgui* self, const ImguiItem& item, s32* current, const char* const items[], s32 count) +{ + bool isActivated = ImGui::Combo(item.label.c_str(), current, items, count); + _imgui_item(self, item, &isActivated); + return isActivated; +} - TextureType textureType; - f32 frameWidth = IMGUI_TIMELINE_FRAME_SIZE.x * frame.delay; - ImVec2 frameSize = ImVec2(frameWidth, IMGUI_TIMELINE_FRAME_SIZE.y); +static bool _imgui_item_selectable(Imgui* self, const ImguiItem& item) +{ + const char* label = item.label.c_str(); + s32 flags = item.flags; - if (reference.itemType == ANM2_TRIGGERS) - { - framePos.x = frameStartPos.x + (IMGUI_TIMELINE_FRAME_SIZE.x * frame.atFrame); - textureType = TEXTURE_TRIGGER; - } - else - textureType = frame.isInterpolated ? TEXTURE_CIRCLE : TEXTURE_SQUARE; + if (item.isInactive || item.color.is_normal()) + { + ImVec4 color = item.isInactive ? IMGUI_INACTIVE_COLOR : item.color.normal; + ImGui::PushStyleColor(ImGuiCol_Text, color); + flags |= ImGuiSelectableFlags_Disabled; + } - ImGui::SetCursorPos(framePos); - - ImVec4 buttonColor = *self->reference == reference ? activeColor : frameColor; + ImVec2 size = item.is_size() ? item.size : item.isSizeToText ? ImGui::CalcTextSize(label) :ImVec2(0, 0); + bool isActivated = ImGui::Selectable(label, item.isSelected, flags, size); + _imgui_item(self, item, &isActivated); - ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, hoveredColor); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, activeColor); - ImGui::PushStyleColor(ImGuiCol_Border, IMGUI_FRAME_BORDER_COLOR); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, IMGUI_FRAME_BORDER); - - ImGui::PushID(i); + if (item.isInactive || item.color.is_normal()) ImGui::PopStyleColor(); - if (ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_LABEL, frameSize)) - { - s32 frameTime = (s32)(mousePosRelativeX / IMGUI_TIMELINE_FRAME_SIZE.x); + return isActivated; +} - *self->reference = reference; +static bool _imgui_item_inputint(Imgui* self, const ImguiItem& item, s32* value) +{ + if (item.is_size()) ImGui::SetNextItemWidth(item.size.x); - *self->time = frameTime; + bool isActivated = ImGui::InputInt(item.label.c_str(), value, item.step, item.stepFast, item.flags); + + if (item.min != 0) *value = MIN(*value, (s32)item.min); + if (item.max != 0) *value = MAX(*value, (s32)item.max); + + _imgui_item(self, item, nullptr); - // Set the preview's spritesheet ID - if (self->reference->itemType == ANM2_LAYER) - _imgui_spritesheet_editor_set(self, self->anm2->layers[self->reference->itemID].spritesheetID); - } + return isActivated; +} - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) - { - *self->reference = reference; +static void _imgui_item_inputint2(Imgui* self, const ImguiItem& item, s32 value[2]) +{ + if (item.is_size()) ImGui::SetNextItemWidth(item.size.x); - ImGui::SetDragDropPayload(STRING_IMGUI_TIMELINE_FRAME_DRAG_DROP, &reference, sizeof(Anm2Reference)); - ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_LABEL, frameSize); - ImGui::SetCursorPos(ImVec2(1.0f, (IMGUI_TIMELINE_FRAME_SIZE.y / 2) - (TEXTURE_SIZE_SMALL.y / 2))); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[textureType]), IMVEC2_ATLAS_UV_GET(textureType)); - ImGui::EndDragDropSource(); - } + ImGui::InputInt2(item.label.c_str(), value); + + if (item.min > 0) + { + value[0] = MIN(value[0], (s32)item.min); + value[1] = MIN(value[1], (s32)item.min); + } - if (self->reference->itemID == reference.itemID) - { - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(STRING_IMGUI_TIMELINE_FRAME_DRAG_DROP)) - { - Anm2Reference checkReference = *(Anm2Reference*)payload->Data; - if (checkReference != reference) - { - self->isSwap = true; - self->swapReference = reference; - - _imgui_undo_stack_push(self); - } - } - ImGui::EndDragDropTarget(); - } - } + if (item.max > 0) + { + value[0] = MAX(value[0], (s32)item.max); + value[1] = MAX(value[1], (s32)item.max); + } + + _imgui_item(self, item, nullptr); +} - ImGui::PopStyleVar(); - ImGui::PopStyleColor(4); +static bool _imgui_item_inputtext(Imgui* self, const ImguiItem& item, std::string* buffer) +{ + if ((s32)buffer->size() < item.max) buffer->resize(item.max); - ImGui::SetCursorPos(ImVec2(framePos.x + 1.0f, (framePos.y + (IMGUI_TIMELINE_FRAME_SIZE.y / 2)) - TEXTURE_SIZE_SMALL.y / 2)); + if (item.is_size()) + ImGui::SetNextItemWidth(item.size.x); + else + ImGui::SetNextItemWidth(ImGui::CalcTextSize(buffer->c_str()).x); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[textureType]), IMVEC2_ATLAS_UV_GET(textureType)); + bool isActivated = ImGui::InputText(item.label.c_str(), &(*buffer)[0], item.max, item.flags); + _imgui_item(self, item, nullptr); - ImGui::PopID(); + return isActivated; +} - if (reference.itemType != ANM2_TRIGGERS) - framePos.x += frameWidth; - } +static void _imgui_item_checkbox(Imgui* self, const ImguiItem& item, bool* value) +{ + ImGui::Checkbox(item.label.c_str(), value); - ImGui::EndChild(); - ImGui::PopStyleVar(2); + if (item.is_mnemonic() && ImGui::IsKeyChordPressed(ImGuiMod_Alt | item.mnemonicKey)) + { + *value = !*value; + ImGui::CloseCurrentPopup(); + } - ImGui::SetCursorPosX(cursorPos.x); - ImGui::SetCursorPosY(cursorPos.y + IMGUI_TIMELINE_FRAME_SIZE.y); + _imgui_item(self, item, nullptr); +} + +static bool _imgui_item_radio_button(Imgui* self, const ImguiItem& item, s32* value) +{ + if (item.is_size()) ImGui::SetNextItemWidth(item.size.x); + + bool isActivated = ImGui::RadioButton(item.label.c_str(), value, item.value); + _imgui_item(self, item, &isActivated); + return isActivated; +} + +static void _imgui_item_dragfloat(Imgui* self, const ImguiItem& item, f32* value) +{ + ImGui::DragFloat(item.label.c_str(), value, item.speed, item.min, item.max, item.format.c_str()); + _imgui_item(self, item, nullptr); +} + +static void _imgui_item_coloredit3(Imgui* self, const ImguiItem& item, f32 value[3]) +{ + ImGui::ColorEdit3(item.label.c_str(), value, item.flags); + _imgui_item(self, item, nullptr); +} + +static void _imgui_item_coloredit4(Imgui* self, const ImguiItem& item, f32 value[4]) +{ + ImGui::ColorEdit4(item.label.c_str(), value, item.flags); + _imgui_item(self, item, nullptr); +} + +static void _imgui_item_dragfloat2(Imgui* self, const ImguiItem& item, f32 value[2]) +{ + ImGui::DragFloat2(item.label.c_str(), value, item.speed, item.min, item.max, item.format.c_str()); + _imgui_item(self, item, nullptr); +} + +static bool _imgui_item_button(Imgui* self, const ImguiItem& item) +{ + bool isActivated = ImGui::Button(item.label.c_str(), item.size); + _imgui_item(self, item, &isActivated); + return isActivated; +} + +static bool _imgui_item_selectable_inputtext(Imgui* self, const ImguiItem& item, std::string* string, s32 id) +{ + static s32 renameID = ID_NONE; + static s32 itemID = ID_NONE; + const char* label = item.label.c_str(); + bool isActivated = false; + static std::string buffer{}; + + ImVec2 size = item.is_size() ? item.size : item.isSizeToText ? ImGui::CalcTextSize(label) :ImVec2(0, 0); + + if (renameID == id && itemID == item.id) + { + ImGui::PushID(id); + + ImguiItem itemRenamable = IMGUI_RENAMABLE; + itemRenamable.size = size; + self->isRename = true; + + isActivated = _imgui_item_inputtext(self, itemRenamable, &buffer); + + if (isActivated || _imgui_is_no_click_on_item()) + { + *string = buffer; + renameID = ID_NONE; + itemID = ID_NONE; + self->isRename = false; + } + + ImGui::PopID(); + } + else + { + isActivated = ImGui::Selectable(label, item.isSelected, 0, size); + + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) + { + buffer = *string; + renameID = id; + itemID = item.id; + ImGui::SetKeyboardFocusHere(-1); + } + } + + _imgui_item(self, item, &isActivated); + + return isActivated; +} + +static bool _imgui_item_selectable_inputint(Imgui* self, const ImguiItem& item, s32* value, s32 id) +{ + static s32 itemID = ID_NONE; + static s32 changeID = ID_NONE; + const char* label = item.label.c_str(); + bool isActivated = false; + ImVec2 size = item.is_size() ? item.size : item.isSizeToText ? ImGui::CalcTextSize(label) :ImVec2(0, 0); + + if (changeID == id && itemID == item.id) + { + ImGui::PushID(id); + + ImguiItem itemChangeable = IMGUI_CHANGEABLE; + itemChangeable.size = size; + self->isChangeValue = true; + + if (_imgui_item_inputint(self, itemChangeable, value)) + { + itemID = ID_NONE; + changeID = ID_NONE; + self->isChangeValue = false; + } + + if (_imgui_is_no_click_on_item()) + { + itemID = ID_NONE; + changeID = ID_NONE; + self->isChangeValue = false; + } + + ImGui::PopID(); + } + else + { + isActivated = ImGui::Selectable(label, item.isSelected, 0, size); + + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) + { + itemID = item.id; + changeID = id; + ImGui::SetKeyboardFocusHere(-1); + } + } + + _imgui_item(self, item, &isActivated); + + return isActivated; +} + +static void _imgui_item_atlas_image_text(Imgui* self, const ImguiItem& item) +{ + _imgui_atlas_image(self, item.texture); + ImGui::SameLine(); + ImGui::Text(item.label.c_str()); +} + +static bool _imgui_item_atlas_image_selectable(Imgui* self, const ImguiItem& item) +{ + _imgui_atlas_image(self, item.texture); + ImGui::SameLine(); + return _imgui_item_selectable(self, item); +} + + +static bool _imgui_item_atlas_image_selectable_inputtext(Imgui* self, ImguiItem& item, std::string* string, s32 id) +{ + _imgui_atlas_image(self, item.texture); + ImGui::SameLine(); + return _imgui_item_selectable_inputtext(self, item, string, id); +} + +static bool _imgui_item_atlas_image_selectable_inputint(Imgui* self, ImguiItem& item, s32* value, s32 id) +{ + _imgui_atlas_image(self, item.texture); + ImGui::SameLine(); + return _imgui_item_selectable_inputint(self, item, value, id); +} + +static bool _imgui_item_atlas_image_button(Imgui* self, const ImguiItem& item) +{ + bool isActivated = false; + ImVec2 imageSize = VEC2_TO_IMVEC2(ATLAS_SIZES[item.texture]); + ImVec2 buttonSize = item.is_size() ? item.size : imageSize; + + if (item.color.is_normal()) ImGui::PushStyleColor(ImGuiCol_Button, item.color.normal); + if (item.color.is_active()) ImGui::PushStyleColor(ImGuiCol_ButtonActive, item.color.active); + if (item.color.is_hovered()) ImGui::PushStyleColor(ImGuiCol_ButtonHovered, item.color.hovered); + if (item.isSelected) ImGui::PushStyleColor(ImGuiCol_Button, item.color.active); + if (item.is_border()) ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, item.border); + + if (item.is_size()) + { + isActivated = ImGui::Button(item.label.c_str(), buttonSize); + + ImVec2 pos = ImGui::GetItemRectMin(); + ImVec2 imageMin = pos + item.contentOffset; + ImVec2 imageMax = imageMin + imageSize; + + ImGui::GetWindowDrawList()->AddImage(self->resources->atlas.id, imageMin, imageMax, IMVEC2_ATLAS_UV_GET(item.texture), IM_COL32_WHITE); } else - ImGui::Dummy(IMGUI_DUMMY_SIZE); + isActivated = ImGui::ImageButton(item.label.c_str(), self->resources->atlas.id, buttonSize, IMVEC2_ATLAS_UV_GET(item.texture)); + _imgui_item(self, item, &isActivated); + + if (item.color.is_normal()) ImGui::PopStyleColor(); + if (item.color.is_active()) ImGui::PopStyleColor(); + if (item.color.is_hovered()) ImGui::PopStyleColor(); + if (item.isSelected) ImGui::PopStyleColor(); + if (item.is_border()) ImGui::PopStyleVar(); - (*index)++; - - ImGui::PopID(); + return isActivated; } -// Displays each item of the timeline of a selected animation -static void -_imgui_timeline_item(Imgui* self, Anm2Reference reference, s32* index) +static void _imgui_item_begin(const ImguiItem& item) { - static s32 textEntryItemIndex = -1; - static s32 textEntrySpritesheetIndex = -1; - - TextureType textureType = TEXTURE_ERROR; - s32* spritesheetID = NULL; - bool* isShowRect = NULL; - Anm2Null* null = NULL; - Anm2Layer* layer = NULL; - std::string nameVisible; - std::string* namePointer = NULL; - char nameBuffer[ANM2_STRING_MAX]; - memset(nameBuffer, '\0', sizeof(nameBuffer)); - - bool isChangeable = reference.itemType != ANM2_ROOT && reference.itemType != ANM2_TRIGGERS; - bool isSelected = self->reference->itemID == reference.itemID && self->reference->itemType == reference.itemType; - bool isTextEntry = textEntryItemIndex == *index && isChangeable; - bool isSpritesheetTextEntry = textEntrySpritesheetIndex == *index; - - f32 cursorPosY = ImGui::GetCursorPosY(); - ImVec4 color; - - Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); - - if (!item) - return; - - switch (reference.itemType) - { - case ANM2_ROOT: - textureType = TEXTURE_ROOT; - color = IMGUI_TIMELINE_ROOT_COLOR; - nameVisible = STRING_IMGUI_TIMELINE_ROOT; - break; - case ANM2_LAYER: - textureType = TEXTURE_LAYER; - color = IMGUI_TIMELINE_LAYER_COLOR; - layer = &self->anm2->layers[reference.itemID]; - spritesheetID = &layer->spritesheetID; - namePointer = &layer->name; - strncpy(nameBuffer, (*namePointer).c_str(), ANM2_STRING_MAX - 1); - nameVisible = std::format(STRING_IMGUI_TIMELINE_ITEM_FORMAT, reference.itemID, *namePointer); - break; - case ANM2_NULL: - textureType = TEXTURE_NULL; - color = IMGUI_TIMELINE_NULL_COLOR; - null = &self->anm2->nulls[reference.itemID]; - isShowRect = &null->isShowRect; - namePointer = &null->name; - strncpy(nameBuffer, (*namePointer).c_str(), ANM2_STRING_MAX - 1); - nameVisible = std::format(STRING_IMGUI_TIMELINE_ITEM_FORMAT, reference.itemID, *namePointer); - break; - case ANM2_TRIGGERS: - textureType = TEXTURE_TRIGGERS; - color = IMGUI_TIMELINE_TRIGGERS_COLOR; - nameVisible = STRING_IMGUI_TIMELINE_TRIGGERS; - break; - default: - break; - } - - ImGui::PushID(*index); - - ImGui::PushStyleColor(ImGuiCol_ChildBg, color); - ImGui::BeginChild(nameVisible.c_str(), IMGUI_TIMELINE_ELEMENT_SIZE, true, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar); - ImGui::PopStyleColor(); - - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[textureType]), IMVEC2_ATLAS_UV_GET(textureType)); - - ImGui::SameLine(); - - ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_NAME_LABEL, IMGUI_TIMELINE_ELEMENT_NAME_SIZE); - - if (isTextEntry) - { - if (ImGui::InputText(STRING_IMGUI_TIMELINE_ANIMATION_LABEL, nameBuffer, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) - { - *namePointer = nameBuffer; - textEntryItemIndex = -1; - } - _imgui_undoable(self); - - if (!ImGui::IsItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - textEntryItemIndex = -1; - } - else - { - if (ImGui::Selectable(nameVisible.c_str(), isSelected)) - { - *self->reference = reference; - anm2_reference_frame_clear(self->reference); - } - - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - textEntryItemIndex = *index; - } - - // Drag and drop items (only layers/nulls) - if (isChangeable && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) - { - *self->reference = reference; - anm2_reference_frame_clear(self->reference); - - ImGui::PushStyleColor(ImGuiCol_ChildBg, color); - ImGui::SetDragDropPayload(STRING_IMGUI_TIMELINE_ITEM_DRAG_DROP, &reference, sizeof(Anm2Frame)); - ImGui::PopStyleColor(); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[textureType]), IMVEC2_ATLAS_UV_GET(textureType)); - ImGui::SameLine(); - ImGui::Text(nameVisible.c_str()); - ImGui::EndDragDropSource(); - } - - if (self->reference->itemType == reference.itemType && ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(STRING_IMGUI_TIMELINE_ITEM_DRAG_DROP)) - { - Anm2Reference checkReference = *(Anm2Reference*)payload->Data; - if (checkReference != reference) - { - self->isSwap = true; - self->swapReference = reference; - - _imgui_undo_stack_push(self); - } - } - ImGui::EndDragDropTarget(); - } - - switch (reference.itemType) - { - case ANM2_ROOT: - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT); - break; - case ANM2_LAYER: - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER); - break; - case ANM2_NULL: - _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(); - - // IsVisible - ImVec2 cursorPos; - TextureType visibleTextureType = item->isVisible ? TEXTURE_VISIBLE : TEXTURE_INVISIBLE; - - ImGui::SameLine(); - - cursorPos = ImGui::GetCursorPos(); - ImGui::SetCursorPosX(cursorPos.x + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); - - if - ( - ImGui::ImageButton - ( - STRING_IMGUI_TIMELINE_VISIBLE, - self->resources->atlas.id, - VEC2_TO_IMVEC2(ATLAS_SIZES[visibleTextureType]), - IMVEC2_ATLAS_UV_GET(visibleTextureType) - ) - ) - item->isVisible = !item->isVisible; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE); - - ImGui::SetCursorPos(cursorPos); - - // Spritesheet IDs - if (spritesheetID) - { - std::string spritesheetIDName; - - spritesheetIDName = std::format(STRING_IMGUI_TIMELINE_SPRITESHEET_ID_FORMAT, *spritesheetID); - - ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_SIZE); - - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_SPRITESHEET]), IMVEC2_ATLAS_UV_GET(TEXTURE_SPRITESHEET)); - ImGui::SameLine(); - - if (isSpritesheetTextEntry) - { - if (ImGui::InputInt(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, spritesheetID, 0, 0, ImGuiInputTextFlags_None)) - textEntrySpritesheetIndex = -1; - _imgui_undoable(self); - - if (!ImGui::IsItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - textEntrySpritesheetIndex = -1; - } - else - { - ImGui::Selectable(spritesheetIDName.c_str()); - - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - textEntrySpritesheetIndex = *index; - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET); - - ImGui::EndChild(); - } - - // ShowRect - if (isShowRect) - { - TextureType rectTextureType = *isShowRect ? TEXTURE_RECT : TEXTURE_RECT_HIDE; - - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - ((IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2) * 4)); - - if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_RECT, self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[rectTextureType]), IMVEC2_ATLAS_UV_GET(rectTextureType))) - *isShowRect = !*isShowRect; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT); - } - - ImGui::EndChild(); - - (*index)++; - - ImGui::PopID(); - - ImGui::SetCursorPosY(cursorPosY + IMGUI_TIMELINE_ELEMENT_SIZE.y); + if (item.is_size()) ImGui::SetNextWindowSize(item.size); + ImGui::Begin(item.label.c_str(), nullptr, item.flags); } -// Timeline window -static void -_imgui_timeline(Imgui* self) +static void _imgui_item_end(void) { - ImGui::Begin(STRING_IMGUI_TIMELINE); + ImGui::End(); +} +static void _imgui_item_begin_child(const ImguiItem& item) +{ + if (item.color.is_normal()) ImGui::PushStyleColor(ImGuiCol_ChildBg, item.color.normal); + ImGui::BeginChild(item.label.c_str(), item.size, item.flags, item.flagsAlt); + if (item.color.is_normal()) ImGui::PopStyleColor(); +} + +static void _imgui_item_end_child(void) +{ + ImGui::EndChild(); +} + +static void _imgui_item_dockspace(const ImguiItem& item) +{ + ImGui::DockSpace(ImGui::GetID(item.label.c_str()), item.size, item.flags); +} + +static void _imgui_keyboard_navigation_set(bool value) +{ + if (value) ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + if (!value) ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard; +} + +static bool _imgui_item_yes_no_popup(Imgui* self, const ImguiItem& item) +{ + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + + if (ImGui::BeginPopupModal(item.popup.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text(item.label.c_str()); + ImGui::Separator(); + + if (_imgui_item_button(self, IMGUI_POPUP_YES_BUTTON)) + { + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + return true; + } + + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_POPUP_NO_BUTTON)) + ImGui::CloseCurrentPopup(); + + ImGui::EndPopup(); + } + + return false; +} + +static void _imgui_spritesheet_editor_set(Imgui* self, s32 id) +{ + if (self->anm2->spritesheets.contains(id)) self->editor->spritesheetID = id; +} + +static void _imgui_timeline(Imgui* self) +{ + static const ImU32 frameColor = ImGui::GetColorU32(IMGUI_TIMELINE_FRAME_COLOR); + static const ImU32 frameMultipleColor = ImGui::GetColorU32(IMGUI_TIMELINE_FRAME_MULTIPLE_COLOR); + static const ImU32 headerFrameColor = ImGui::GetColorU32(IMGUI_TIMELINE_HEADER_FRAME_COLOR); + static const ImU32 headerFrameMultipleColor = ImGui::GetColorU32(IMGUI_TIMELINE_HEADER_FRAME_MULTIPLE_COLOR); + static const ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text); + static ImVec2 scroll{}; + static ImVec2 pickerPos{}; + static Anm2Reference swapItemReference; + static bool isItemSwap = false; + static ImVec2 itemMin{}; + static ImVec2 mousePos{}; + static ImVec2 localMousePos{}; + static s32 frameIndex = INDEX_NONE; + static Anm2Reference hoverReference; + + static const ImVec2& frameSize = IMGUI_TIMELINE_FRAME_SIZE; + + _imgui_item_begin(IMGUI_TIMELINE); + Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); - if (animation) + if (!animation) { - ImVec2 cursorPos; - ImVec2 mousePos; - ImVec2 mousePosRelative; - s32 index = 0; - ImVec2 frameSize = IMGUI_TIMELINE_FRAME_SIZE; - ImVec2 pickerPos; - ImVec2 lineStart; - ImVec2 lineEnd; - ImDrawList* drawList; - static f32 itemScrollX = 0; - static f32 itemScrollY = 0; - static bool isPickerDragging; - ImVec2 frameIndicesSize = {frameSize.x * animation->frameNum, IMGUI_TIMELINE_FRAME_INDICES_SIZE.y}; - const char* buttonText = self->preview->isPlaying ? STRING_IMGUI_TIMELINE_PAUSE : STRING_IMGUI_TIMELINE_PLAY; - const char* buttonTooltipText = self->preview->isPlaying ? STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE : STRING_IMGUI_TOOLTIP_TIMELINE_PLAY; - ImVec2 region = ImGui::GetContentRegionAvail(); - ImVec2 windowSize; - s32& animationID = self->reference->animationID; + ImGui::Text(IMGUI_TIMELINE_NO_ANIMATION); + _imgui_item_end(); // IMGUI_TIMELINE + return; + } - ImVec2 timelineSize = {region.x, region.y - IMGUI_TIMELINE_OFFSET_Y}; - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - ImGui::BeginChild(STRING_IMGUI_TIMELINE_CHILD, timelineSize, true); - windowSize = ImGui::GetWindowSize(); + ImVec2 defaultItemSpacing = ImGui::GetStyle().ItemSpacing; + ImVec2 defaultWindowPadding = ImGui::GetStyle().WindowPadding; + ImVec2 defaultFramePadding = ImGui::GetStyle().FramePadding; - cursorPos = ImGui::GetCursorPos(); + ImguiItem timelineChild = IMGUI_TIMELINE_CHILD; + timelineChild.size.y = ImGui::GetContentRegionAvail().y - IMGUI_TIMELINE_FOOTER_HEIGHT; - drawList = ImGui::GetWindowDrawList(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + + _imgui_item_begin_child(timelineChild); + ImVec2 clipRectMin = ImGui::GetWindowDrawList()->GetClipRectMin(); + ImVec2 clipRectMax = ImGui::GetWindowDrawList()->GetClipRectMax(); + clipRectMin.x += IMGUI_TIMELINE_ITEM_SIZE.x; - ImGui::SetCursorPos(ImVec2(cursorPos.x + IMGUI_TIMELINE_ELEMENT_SIZE.x, cursorPos.y + IMGUI_TIMELINE_VIEWER_SIZE.y)); - ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_FRAMES, {0, 0}, false, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); - itemScrollX = ImGui::GetScrollX(); - itemScrollY = ImGui::GetScrollY(); + ImVec2 scrollDelta = {0, 0}; + + if (_imgui_is_window_hovered()) + { + ImGuiIO& io = ImGui::GetIO(); + f32 lineHeight = ImGui::GetTextLineHeight(); - // Root - _imgui_timeline_item_frames(self, Anm2Reference{animationID, ANM2_ROOT, 0, 0}, &index); + scrollDelta.x -= io.MouseWheelH * lineHeight; + scrollDelta.y -= io.MouseWheel * lineHeight * 3.0f; + } - // Layers (Reversed) - for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++) + std::function timeline_header = [&]() + { + static bool isHeaderClicked = false; + _imgui_item_begin_child(IMGUI_TIMELINE_HEADER); + + ImGui::SetScrollX(scroll.x); + + itemMin = ImGui::GetItemRectMin(); + mousePos = ImGui::GetMousePos(); + localMousePos = ImVec2(mousePos.x - itemMin.x + scroll.x, mousePos.y - itemMin.y); + frameIndex = CLAMP((s32)(localMousePos.x / frameSize.x), 0, (f32)(animation->frameNum - 1)); + + if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && _imgui_is_window_hovered()) + isHeaderClicked = true; + + if (isHeaderClicked) { - s32 id = it->first; - _imgui_timeline_item_frames(self, Anm2Reference{animationID, ANM2_LAYER, id, 0}, &index); + self->preview->time = frameIndex; + + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + isHeaderClicked = false; } - // Nulls - for (auto & [id, null] : animation->nullAnimations) - _imgui_timeline_item_frames(self, Anm2Reference{animationID, ANM2_NULL, id, 0}, &index); - - // Triggers - _imgui_timeline_item_frames(self, Anm2Reference{animationID, ANM2_TRIGGERS, 0, 0}, &index); - - // Swap the two selected elements - if (self->isSwap) + ImVec2 cursorPos = ImGui::GetCursorScreenPos(); + pickerPos = {cursorPos.x + (self->preview->time * frameSize.x), cursorPos.y}; + + ImDrawList* drawList = ImGui::GetWindowDrawList(); + + for (s32 i = 0; i < animation->frameNum; i++) { - Anm2Frame* aFrame = anm2_frame_from_reference(self->anm2, self->reference); - Anm2Frame* bFrame = anm2_frame_from_reference(self->anm2, &self->swapReference); - Anm2Frame oldFrame = *aFrame; + bool isMultiple = i % IMGUI_TIMELINE_FRAME_MULTIPLE == 0; + + ImVec2 framePos = ImGui::GetCursorScreenPos(); + ImVec2 bgMin = framePos; + ImVec2 bgMax = {framePos.x + frameSize.x, framePos.y + frameSize.y}; + ImU32 bgColor = isMultiple ? headerFrameMultipleColor : headerFrameColor; - // With triggers, just swap the event Id - if (self->reference->itemType == ANM2_TRIGGERS) + drawList->AddRectFilled(bgMin, bgMax, bgColor); + + if (i % IMGUI_TIMELINE_FRAME_MULTIPLE == 0) { - aFrame->eventID = bFrame->eventID; - bFrame->eventID = oldFrame.eventID; + std::string frameIndexString = std::to_string(i); + ImVec2 textSize = ImGui::CalcTextSize(frameIndexString.c_str()); + ImVec2 textPos = {framePos.x + (frameSize.x - textSize.x) * 0.5f, framePos.y + (frameSize.y - textSize.y) * 0.5f}; + drawList->AddText(textPos, textColor, frameIndexString.c_str()); } - else - { - *aFrame = *bFrame; - *bFrame = oldFrame; - } - - self->isSwap = false; - self->reference->frameIndex = self->swapReference.frameIndex; - self->swapReference = Anm2Reference{}; - } - - ImGui::EndChild(); - - ImGui::SetCursorPos(cursorPos); - - ImGui::PushStyleColor(ImGuiCol_ChildBg, IMGUI_TIMELINE_HEADER_COLOR); - ImGui::BeginChild(STRING_IMGUI_TIMELINE_HEADER, IMGUI_TIMELINE_ELEMENT_SIZE, true); - ImGui::EndChild(); - ImGui::PopStyleColor(); - - // Only draw if animation length isn't 0 - if (animation->frameNum > 0) - { - bool isMouseInElementsRegion = false; - - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - ImVec2 clipRectMin = {cursorScreenPos.x + IMGUI_TIMELINE_ELEMENT_SIZE.x, 0}; - ImVec2 clipRectMax = {cursorScreenPos.x + timelineSize.x + IMGUI_TIMELINE_FRAME_SIZE.x, cursorScreenPos.y + timelineSize.y}; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - - ImGui::SameLine(); + + _imgui_atlas_image(self, TEXTURE_FRAME); - ImGui::PushClipRect(clipRectMin, clipRectMax, true); - - ImGui::BeginChild(STRING_IMGUI_TIMELINE_FRAME_INDICES, {0, IMGUI_TIMELINE_FRAME_SIZE.y}); - ImGui::SetScrollX(itemScrollX); - - ImVec2 itemsRectMin = ImGui::GetWindowPos(); - ImVec2 itemsRectMax = ImVec2(itemsRectMin.x + frameIndicesSize.x, itemsRectMin.y + frameIndicesSize.y); - - cursorPos = ImGui::GetCursorScreenPos(); - mousePos = ImGui::GetMousePos(); - mousePosRelative = ImVec2(ImGui::GetMousePos().x - cursorPos.x, ImGui::GetMousePos().y - cursorPos.y); - - isMouseInElementsRegion = - mousePos.x >= itemsRectMin.x && mousePos.x < itemsRectMax.x && - mousePos.y >= itemsRectMin.y && mousePos.y < itemsRectMax.y; - - // Dragging/undragging the frame picker - if ((isMouseInElementsRegion && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) || isPickerDragging) - { - s32 frameIndex = CLAMP((s32)(mousePosRelative.x / frameSize.x), 0, (f32)(animation->frameNum - 1)); - *self->time = frameIndex; - - isPickerDragging = true; - } - - if (isPickerDragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - isPickerDragging = false; - - // Draw the frame indices, based on the animation length - for (s32 i = 0; i < animation->frameNum; i++) - { - ImVec2 imagePos = ImGui::GetCursorScreenPos(); - - if (i % IMGUI_TIMELINE_FRAME_INDICES_MULTIPLE == 0) - { - std::string frameIndexString; - frameIndexString = std::to_string(i); - - ImVec2 bgMin = imagePos; - ImVec2 bgMax = ImVec2(imagePos.x + IMGUI_TIMELINE_FRAME_SIZE.x, - imagePos.y + IMGUI_TIMELINE_FRAME_SIZE.y); - - ImU32 bgColor = ImGui::GetColorU32(IMGUI_FRAME_INDICES_OVERLAY_COLOR); - drawList->AddRectFilled(bgMin, bgMax, bgColor); - - ImVec2 textSize = ImGui::CalcTextSize(frameIndexString.c_str()); - - ImVec2 textPos; - textPos.x = imagePos.x + (IMGUI_TIMELINE_FRAME_SIZE.x - textSize.x) / 2.0f; - textPos.y = imagePos.y + (IMGUI_TIMELINE_FRAME_SIZE.y - textSize.y) / 2.0f; - - drawList->AddText(textPos, ImGui::GetColorU32(ImGuiCol_Text), frameIndexString.c_str()); - } - else - { - ImVec2 bgMin = imagePos; - ImVec2 bgMax = ImVec2(imagePos.x + IMGUI_TIMELINE_FRAME_SIZE.x, - imagePos.y + IMGUI_TIMELINE_FRAME_SIZE.y); - - ImU32 bgColor = ImGui::GetColorU32(IMGUI_FRAME_INDICES_COLOR); - drawList->AddRectFilled(bgMin, bgMax, bgColor); - } - - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_FRAME]), IMVEC2_ATLAS_UV_GET(TEXTURE_FRAME)); - + if (i < animation->frameNum - 1) ImGui::SameLine(); - } + } - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); + ImVec2& pos = pickerPos; + + ImDrawList* foregroundDrawList = ImGui::GetForegroundDrawList(); + + ImVec2 lineStart = {pos.x + (frameSize.x * 0.5f) - (IMGUI_TIMELINE_PICKER_LINE_WIDTH * 0.5f), pos.y + frameSize.y}; + ImVec2 lineEnd = {lineStart.x + IMGUI_TIMELINE_PICKER_LINE_WIDTH, lineStart.y + timelineChild.size.y - frameSize.y}; + + foregroundDrawList->PushClipRect(clipRectMin, clipRectMax, true); - pickerPos = ImVec2(cursorPos.x + *self->time * frameSize.x, cursorPos.y); - lineStart = ImVec2(pickerPos.x + frameSize.x / 2.0f, pickerPos.y + frameSize.y); - lineEnd = ImVec2(lineStart.x, lineStart.y + timelineSize.y - IMGUI_TIMELINE_FRAME_SIZE.y); + foregroundDrawList->AddImage(self->resources->atlas.id, pos, ImVec2(pos.x + frameSize.x, pos.y + frameSize.y), IMVEC2_ATLAS_UV_GET(TEXTURE_PICKER)); + foregroundDrawList->AddRectFilled(lineStart, lineEnd, IMGUI_PICKER_LINE_COLOR); - ImGui::GetWindowDrawList()->AddImage - ( - self->resources->atlas.id, - pickerPos, - ImVec2(pickerPos.x + frameSize.x, pickerPos.y + frameSize.y), - IMVEC2_ATLAS_UV_GET(TEXTURE_PICKER) - ); + foregroundDrawList->PopClipRect(); + + _imgui_item_end_child(); // IMGUI_TIMELINE_HEADER + }; + + std::function timeline_item_child = [&](Anm2Reference reference, s32* index) + { + Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); + + if (!item) return; + + ImVec2 buttonSize = VEC2_TO_IMVEC2(TEXTURE_SIZE) + (defaultFramePadding * ImVec2(2, 2)); + + Anm2Type& type = reference.itemType; + Anm2Layer* layer = nullptr; + Anm2Null* null = nullptr; + s32 buttonCount = type == ANM2_NULL ? 2 : 1; + f32 buttonAreaWidth = buttonCount * buttonSize.x + (buttonCount - 1) * defaultItemSpacing.x; + + ImguiItem imguiItem = *IMGUI_TIMELINE_ITEMS[type]; + ImguiItem imguiItemSelectable = *IMGUI_TIMELINE_ITEM_SELECTABLES[type]; + imguiItemSelectable.isSelected = self->reference->itemID == reference.itemID && self->reference->itemType == type; + + ImGui::PushID(reference.itemID); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, defaultItemSpacing); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, defaultWindowPadding); - drawList->AddRectFilled - ( - ImVec2(lineStart.x - IMGUI_PICKER_LINE_SIZE, lineStart.y), - ImVec2(lineStart.x + IMGUI_PICKER_LINE_SIZE, lineEnd.y), - IMGUI_PICKER_LINE_COLOR - ); + ImVec2 childPos = ImGui::GetCursorScreenPos(); + _imgui_item_begin_child(imguiItem); + ImVec2 childSize = ImGui::GetContentRegionAvail(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - ImGui::EndChild(); - ImGui::PopClipRect(); - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); - } - else + switch (type) { - ImGui::SameLine(); - ImGui::Dummy(frameIndicesSize); - } - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_LIST, IMGUI_TIMELINE_ELEMENT_LIST_SIZE, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); - ImGui::SetScrollY(itemScrollY); - - index = 0; - - // Root - _imgui_timeline_item(self, Anm2Reference{animationID, ANM2_ROOT, 0, 0}, &index); - - // Layer - for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++) - { - s32 id = it->first; - _imgui_timeline_item(self, Anm2Reference{animationID, ANM2_LAYER, id, 0}, &index); - } - - // Null - for (auto & [id, null] : animation->nullAnimations) - _imgui_timeline_item(self, Anm2Reference{animationID, ANM2_NULL, id, 0}, &index); - - // Triggers - _imgui_timeline_item(self, Anm2Reference{animationID, ANM2_TRIGGERS, 0, 0}, &index); - - // Swap the drag/drop elements - if (self->isSwap) - { - Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); - - switch (self->reference->itemType) - { - case ANM2_LAYER: - map_swap(self->anm2->layers, self->reference->itemID, self->swapReference.itemID); - map_swap(animation->layerAnimations, self->reference->itemID, self->swapReference.itemID); - break; - case ANM2_NULL: - map_swap(self->anm2->nulls, self->reference->itemID, self->swapReference.itemID); - map_swap(animation->nullAnimations, self->reference->itemID, self->swapReference.itemID); - break; - default: - break; - } - - self->isSwap = false; - self->reference->itemID = self->swapReference.itemID; - anm2_reference_clear(&self->swapReference); - } - - ImGui::EndChild(); - ImGui::EndChild(); - - // Add Element - 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); - _imgui_undoable(self); - - if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL)) - anm2_null_add(self->anm2); - _imgui_undoable(self); - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - - // Remove Element - if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_REMOVE)) - { - _imgui_undo_stack_push(self); - - switch (self->reference->itemType) - { - case ANM2_LAYER: - anm2_layer_remove(self->anm2, self->reference->itemID); - break; - case ANM2_NULL: - anm2_null_remove(self->anm2, self->reference->itemID); - break; - default: - break; - } - - anm2_reference_item_clear(self->reference); - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE); - - ImGui::SameLine(); - - // Play/Pause button - if (ImGui::Button(buttonText)) - { - self->preview->isPlaying = !self->preview->isPlaying; - - if ((s32)(*self->time) >= animation->frameNum - 1) - *self->time = 0.0f; - } - _imgui_tooltip(buttonTooltipText); - - ImGui::SameLine(); - - // Add frame - if (ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_ADD)) - anm2_frame_add(self->anm2, self->reference, (s32)*self->time); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD); - - ImGui::SameLine(); - - // Remove Frame - if (ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_REMOVE)) - { - Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference); - - if (frame) - { - _imgui_undo_stack_push(self); - Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference); - item->frames.erase(item->frames.begin() + index); - - anm2_reference_frame_clear(self->reference); - } - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_REMOVE); - - ImGui::SameLine(); - - // Fit Animation Length - if (ImGui::Button(STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH)) - { - s32 length = anm2_animation_length_get(self->anm2, self->reference->animationID); - animation->frameNum = length; - *self->time = CLAMP(*self->time, 0.0f, animation->frameNum - 1); - } - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FIT_ANIMATION_LENGTH); - - ImGui::SameLine(); - - // Animation Length - ImGui::SetNextItemWidth(IMGUI_TIMELINE_ANIMATION_LENGTH_WIDTH); - ImGui::InputInt(STRING_IMGUI_TIMELINE_ANIMATION_LENGTH, &animation->frameNum); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ANIMATION_LENGTH); - - animation->frameNum = CLAMP(animation->frameNum, ANM2_FRAME_NUM_MIN, ANM2_FRAME_NUM_MAX); - - ImGui::SameLine(); - - // FPS - ImGui::SetNextItemWidth(IMGUI_TIMELINE_FPS_WIDTH); - ImGui::SameLine(); - ImGui::InputInt(STRING_IMGUI_TIMELINE_FPS, &self->anm2->fps); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FPS); - - self->anm2->fps = CLAMP(self->anm2->fps, ANM2_FPS_MIN, ANM2_FPS_MAX); - - ImGui::SameLine(); - - // Loop - ImGui::Checkbox(STRING_IMGUI_TIMELINE_LOOP, &animation->isLoop); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_LOOP); - ImGui::SameLine(); - - // CreatedBy - ImGui::SetNextItemWidth(IMGUI_TIMELINE_CREATED_BY_WIDTH); - ImGui::SameLine(); - ImGui::InputText(STRING_IMGUI_TIMELINE_CREATED_BY, &self->anm2->createdBy[0], ANM2_STRING_MAX); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_CREATED_BY); - - ImGui::SameLine(); - - // CreatedOn - ImGui::Text(STRING_IMGUI_TIMELINE_CREATED_ON, self->anm2->createdOn.c_str()); - ImGui::SameLine(); - - // Version - ImGui::Text(STRING_IMGUI_TIMELINE_VERSION, self->anm2->version); - } - - ImGui::End(); -} - -// Taskbar -static void -_imgui_taskbar(Imgui* self) -{ - ImGuiWindowFlags taskbarWindowFlags = 0 | - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoScrollWithMouse | - ImGuiWindowFlags_NoSavedSettings; - - ImGuiViewport* viewport = ImGui::GetMainViewport(); - - ImGui::SetNextWindowPos(viewport->Pos); - ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, IMGUI_TASKBAR_HEIGHT)); - - ImGui::Begin(STRING_IMGUI_TASKBAR, NULL, taskbarWindowFlags); - - // File - if (ImGui::Selectable(STRING_IMGUI_TASKBAR_FILE, false, 0, ImGui::CalcTextSize(STRING_IMGUI_TASKBAR_FILE))) - ImGui::OpenPopup(STRING_IMGUI_FILE_MENU); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_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)) - { - anm2_reference_clear(self->reference); - anm2_new(self->anm2); - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FILE_NEW); - - if (ImGui::Selectable(STRING_IMGUI_FILE_OPEN)) - dialog_anm2_open(self->dialog); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FILE_OPEN); - - if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE)) - { - // Open dialog if path empty, otherwise save in-place - if (self->anm2->path.empty()) - dialog_anm2_save(self->dialog); - else - anm2_serialize(self->anm2, self->anm2->path); - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FILE_SAVE); - - if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE_AS)) - dialog_anm2_save(self->dialog); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FILE_SAVE_AS); - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - - // Playback - if (ImGui::Selectable(STRING_IMGUI_TASKBAR_PLAYBACK, false, 0, ImGui::CalcTextSize(STRING_IMGUI_TASKBAR_PLAYBACK))) - ImGui::OpenPopup(STRING_IMGUI_PLAYBACK_MENU); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_PLAYBACK_MENU); - - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) - ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y + ImGui::GetItemRectSize().y)); - - if (ImGui::BeginPopup(STRING_IMGUI_PLAYBACK_MENU)) - { - ImGui::Checkbox(STRING_IMGUI_PLAYBACK_LOOP, &self->settings->playbackIsLoop); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_PLAYBACK_IS_LOOP); - ImGui::EndPopup(); - } - - ImGui::SameLine(); - - // Wizard - if (ImGui::Selectable(STRING_IMGUI_TASKBAR_WIZARD, false, 0, ImGui::CalcTextSize(STRING_IMGUI_TASKBAR_WIZARD))) - ImGui::OpenPopup(STRING_IMGUI_WIZARD_MENU); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_WIZARD_MENU); - - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) - ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y + ImGui::GetItemRectSize().y)); - - if (ImGui::BeginPopup(STRING_IMGUI_WIZARD_MENU)) - { - if (ImGui::Selectable(STRING_IMGUI_WIZARD_EXPORT_FRAMES_TO_PNG)) - { - if (anm2_animation_from_reference(self->anm2, self->reference)) - { - self->preview->isRecording = true; - *self->time = true; - } - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_WIZARD_EXPORT_FRAMES_TO_PNG); - ImGui::EndPopup(); - } - - // Add a persistent tooltip to indicate recording - if (self->preview->isRecording) - { - ImVec2 mousePos = ImGui::GetMousePos(); - - ImGui::SetNextWindowPos(ImVec2(mousePos.x + IMGUI_RECORD_TOOLTIP_OFFSET.x, mousePos.y + IMGUI_RECORD_TOOLTIP_OFFSET.y)); - ImGui::BeginTooltip(); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(TEXTURE_SIZE), IMVEC2_ATLAS_UV_GET(TEXTURE_ANIMATION)); - ImGui::SameLine(); - ImGui::Text(STRING_IMGUI_RECORDING); - ImGui::EndTooltip(); - } - - ImGui::End(); -} - -// Tools -static void -_imgui_tools(Imgui* self) -{ - ImGui::Begin(STRING_IMGUI_TOOLS); - - ImVec2 availableSize = ImGui::GetContentRegionAvail(); - f32 availableWidth = availableSize.x; - - s32 buttonsPerRow = availableWidth / TEXTURE_SIZE.x + IMGUI_TOOLS_WIDTH_INCREMENT; - buttonsPerRow = MIN(buttonsPerRow, 1); - - for (s32 i = 0; i < TOOL_COUNT; i++) - { - const char* toolString; - const char* toolTooltip; - TextureType textureType; - - if (i > 0 && i % buttonsPerRow != 0) - ImGui::SameLine(); - - ImVec4 buttonColor = self->tool->type == (ToolType)i ? ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] : ImGui::GetStyle().Colors[ImGuiCol_Button]; - ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); - - switch (i) - { - case TOOL_PAN: - toolString = STRING_IMGUI_TOOLS_PAN; - toolTooltip = STRING_IMGUI_TOOLTIP_TOOLS_PAN; - textureType = TEXTURE_PAN; + case ANM2_ROOT: + case ANM2_TRIGGERS: + if (_imgui_item_atlas_image_selectable(self, imguiItemSelectable)) + *self->reference = reference; break; - case TOOL_MOVE: - toolString = STRING_IMGUI_TOOLS_MOVE; - toolTooltip = STRING_IMGUI_TOOLTIP_TOOLS_MOVE; - textureType = TEXTURE_MOVE; + case ANM2_LAYER: + layer = &self->anm2->layers[reference.itemID]; + imguiItemSelectable.label = std::format(IMGUI_TIMELINE_CHILD_ID_LABEL, reference.itemID, layer->name); + if (_imgui_item_atlas_image_selectable_inputtext(self, imguiItemSelectable, &layer->name, *index)) + *self->reference = reference; break; - case TOOL_ROTATE: - toolString = STRING_IMGUI_TOOLS_ROTATE; - toolTooltip = STRING_IMGUI_TOOLTIP_TOOLS_ROTATE; - textureType = TEXTURE_ROTATE; - break; - case TOOL_SCALE: - toolString = STRING_IMGUI_TOOLS_SCALE; - toolTooltip = STRING_IMGUI_TOOLTIP_TOOLS_SCALE; - textureType = TEXTURE_SCALE; - break; - case TOOL_CROP: - toolString = STRING_IMGUI_TOOLS_CROP; - toolTooltip = STRING_IMGUI_TOOLTIP_TOOLS_CROP; - textureType = TEXTURE_CROP; + case ANM2_NULL: + null = &self->anm2->nulls[reference.itemID]; + imguiItemSelectable.label = std::format(IMGUI_TIMELINE_CHILD_ID_LABEL, reference.itemID, null->name); + if (_imgui_item_atlas_image_selectable_inputtext(self, imguiItemSelectable, &null->name, *index)) + *self->reference = reference; break; default: break; } - if (ImGui::ImageButton(toolString, self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[textureType]), IMVEC2_ATLAS_UV_GET(textureType))) - self->tool->type = (ToolType)i; - - _imgui_tooltip(toolTooltip); - - ImGui::PopStyleColor(); - } - - ImGui::End(); - -} - -// Animations -static void -_imgui_animations(Imgui* self) -{ - static s32 textEntryAnimationID = -1; - - ImGui::Begin(STRING_IMGUI_ANIMATIONS); - - // Iterate through all animations, can be selected and names can be edited - for (auto & [id, animation] : self->anm2->animations) - { - std::string name; - bool isSelected = self->reference->animationID == id; - bool isTextEntry = textEntryAnimationID == id; - - // Distinguish default animation - if (animation.name == self->anm2->defaultAnimation) - name = std::format(STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT, animation.name); - else - name = animation.name; - - ImGui::PushID(id); - - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_ANIMATION]), IMVEC2_ATLAS_UV_GET(TEXTURE_ANIMATION)); - ImGui::SameLine(); - - if (isTextEntry) + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - if (ImGui::InputText(STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL, &animation.name[0], ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) - textEntryAnimationID = -1; - _imgui_undoable(self); - - if (!ImGui::IsItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - textEntryAnimationID = -1; + *self->reference = reference; + + ImGui::SetDragDropPayload(imguiItemSelectable.dragDrop.c_str(), &reference, sizeof(Anm2Reference)); + timeline_item_child(reference, index); + ImGui::EndDragDropSource(); } - else + + if (ImGui::BeginDragDropTarget()) { - if (ImGui::Selectable(name.c_str(), isSelected)) + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(imguiItemSelectable.dragDrop.c_str())) { - self->reference->animationID = id; - anm2_reference_item_clear(self->reference); - self->preview->isPlaying = false; - *self->time = 0.0f; + Anm2Reference checkReference = *(Anm2Reference*)payload->Data; + if (checkReference != reference) + { + swapItemReference = reference; + isItemSwap = true; + } } - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - textEntryAnimationID = id; + ImGui::EndDragDropTarget(); + } - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) - { - ImGui::SetDragDropPayload(STRING_IMGUI_ANIMATIONS_DRAG_DROP, &id, sizeof(s32)); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_ANIMATION]), IMVEC2_ATLAS_UV_GET(TEXTURE_ANIMATION)); + if (type == ANM2_LAYER) + { + ImguiItem spritesheetIDItem = IMGUI_TIMELINE_SPRITESHEET_ID; + spritesheetIDItem.label = std::format(IMGUI_TIMELINE_SPRITESHEET_ID_FORMAT, layer->spritesheetID); + ImGui::SameLine(); + _imgui_item_atlas_image_selectable_inputint(self, spritesheetIDItem, &layer->spritesheetID, *index); + } + + ImGui::SetCursorScreenPos({childPos.x + childSize.x - buttonAreaWidth, childPos.y + defaultWindowPadding.y}); + + if (type == ANM2_NULL) + { + ImguiItem rectItem = null->isShowRect ? IMGUI_TIMELINE_ITEM_SHOW_RECT : IMGUI_TIMELINE_ITEM_HIDE_RECT; + if (_imgui_item_atlas_image_button(self, rectItem)) + null->isShowRect = !null->isShowRect; + + ImGui::SameLine(0.0f, defaultItemSpacing.x); + } + + ImguiItem visibleItem = item->isVisible ? IMGUI_TIMELINE_ITEM_VISIBLE : IMGUI_TIMELINE_ITEM_INVISIBLE; + if (_imgui_item_atlas_image_button(self, visibleItem)) + item->isVisible = !item->isVisible; + + ImGui::PopStyleVar(2); + + _imgui_item_end_child(); // imguiItem + + ImGui::PopID(); + + (*index)++; + }; + + std::function timeline_items_child = [&]() + { + s32 index = 0; + + s32& animationID = self->reference->animationID; + + _imgui_item_begin_child(IMGUI_TIMELINE_ITEMS_CHILD); + ImGui::SetScrollY(scroll.y); + + timeline_item_child({animationID, ANM2_ROOT, ID_NONE, INDEX_NONE}, &index); + + for (auto it = self->anm2->layerMap.rbegin(); it != self->anm2->layerMap.rend(); it++) + timeline_item_child({animationID, ANM2_LAYER, it->second, INDEX_NONE}, &index); + + for (auto & [id, null] : animation->nullAnimations) + timeline_item_child({animationID, ANM2_NULL, id, INDEX_NONE}, &index); + + timeline_item_child({animationID, ANM2_TRIGGERS, ID_NONE, INDEX_NONE}, &index); + + _imgui_item_end_child(); // IMGUI_TIMELINE_ITEMS_CHILD + }; + + std::function timeline_item_frames = [&](Anm2Reference reference, s32* index) + { + ImDrawList* drawList = ImGui::GetWindowDrawList(); + + Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); + Anm2Type& type = reference.itemType; + + ImGui::PushID(*index); + + ImguiItem itemFramesChild = IMGUI_TIMELINE_ITEM_FRAMES_CHILD; + itemFramesChild.size.x = frameSize.x * animation->frameNum; + + _imgui_item_begin_child(itemFramesChild); + + if (_imgui_is_window_hovered()) + { + hoverReference = reference; + hoverReference.frameIndex = frameIndex; + self->clipboard->location = hoverReference; + } + + ImVec2 startPos = ImGui::GetCursorPos(); + + for (s32 i = 0; i < animation->frameNum; i++) + { + bool isMultiple = i % IMGUI_TIMELINE_FRAME_MULTIPLE == 0; + + ImVec2 framePos = ImGui::GetCursorScreenPos(); + ImVec2 bgMin = framePos; + ImVec2 bgMax = {framePos.x + frameSize.x, framePos.y + frameSize.y}; + + ImU32 bgColor = isMultiple ? frameMultipleColor : frameColor; + + drawList->AddRectFilled(bgMin, bgMax, bgColor); + + _imgui_atlas_image(self, TEXTURE_FRAME_ALT); + + if (i < animation->frameNum - 1) ImGui::SameLine(); - ImGui::Text(name.c_str()); - ImGui::EndDragDropSource(); + } + + ImGui::SetCursorPos(startPos); + + std::function timeline_item_frame = [&](s32 i, Anm2Frame& frame) + { + ImGui::PushID(i); + reference.frameIndex = i; + ImguiItem frameButton = *IMGUI_TIMELINE_FRAMES[type]; + ImVec2 framePos = ImGui::GetCursorPos(); + frameButton.texture = frame.isInterpolated ? TEXTURE_INTERPOLATED : TEXTURE_UNINTERPOLATED; + frameButton.size = {frameSize.x * frame.delay, frameSize.y}; + frameButton.isSelected = reference == *self->reference; + + if (type == ANM2_TRIGGERS) + { + framePos.x = startPos.x + (frameSize.x * frame.atFrame); + frameButton.texture = TEXTURE_TRIGGER; + } + + ImGui::SetCursorPos(framePos); + + if (_imgui_item_atlas_image_button(self, frameButton)) + { + *self->reference = reference; + + _imgui_spritesheet_editor_set(self, self->anm2->layers[self->reference->itemID].spritesheetID); + } + + if (ImGui::IsItemHovered()) + { + Anm2FrameWithReference frameWithReference = {reference, frame}; + _imgui_clipboard_hovered_item_set(self, frameWithReference); + } + + if (type == ANM2_TRIGGERS) + { + if (ImGui::IsItemActivated()) + imgui_undo_stack_push(self); + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip)) + { + frame.atFrame = frameIndex; + ImGui::EndDragDropSource(); + } + } + else + { + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) + { + ImGui::SetDragDropPayload(frameButton.dragDrop.c_str(), &reference, sizeof(Anm2Reference)); + timeline_item_frame(i, frame); + ImGui::EndDragDropSource(); + } } if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(STRING_IMGUI_ANIMATIONS_DRAG_DROP)) + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(frameButton.dragDrop.c_str())) { - s32 sourceID = *(s32*)payload->Data; - if (sourceID != id) + Anm2Reference swapReference = *(Anm2Reference*)payload->Data; + if (swapReference != reference) { - _imgui_undo_stack_push(self); - map_swap(self->anm2->animations, sourceID, id); + imgui_undo_stack_push(self); + + Anm2Frame* swapFrame = anm2_frame_from_reference(self->anm2, &reference); + Anm2Frame* dragFrame = anm2_frame_from_reference(self->anm2, &swapReference); + + if (swapFrame && dragFrame) + { + Anm2Frame oldFrame = *swapFrame; + + *swapFrame = *dragFrame; + *dragFrame = oldFrame; + + *self->reference = swapReference; + } } } ImGui::EndDragDropTarget(); } - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT); + + if (i < (s32)item->frames.size() - 1) + ImGui::SameLine(); + if (_imgui_is_no_click_on_item()) + anm2_reference_frame_clear(self->reference); + + ImGui::PopID(); + }; + + for (auto [i, frame] : std::views::enumerate(item->frames)) + timeline_item_frame(i, frame); + + _imgui_item_end_child(); // itemFramesChild + ImGui::PopID(); + + (*index)++; + }; + + std::function timeline_frames_child = [&]() + { + s32& animationID = self->reference->animationID; + s32 index = 0; + + _imgui_item_begin_child(IMGUI_TIMELINE_FRAMES_CHILD); + scroll.x = ImGui::GetScrollX() + scrollDelta.x; + scroll.y = ImGui::GetScrollY() + scrollDelta.y; + ImGui::SetScrollX(scroll.x); + ImGui::SetScrollY(scroll.y); + + timeline_item_frames({animationID, ANM2_ROOT, ID_NONE, INDEX_NONE}, &index); + + for (auto it = self->anm2->layerMap.rbegin(); it != self->anm2->layerMap.rend(); it++) + timeline_item_frames({animationID, ANM2_LAYER, it->second, INDEX_NONE}, &index); + + for (auto & [id, null] : animation->nullAnimations) + timeline_item_frames({animationID, ANM2_NULL, id, INDEX_NONE}, &index); + + timeline_item_frames({animationID, ANM2_TRIGGERS, ID_NONE, INDEX_NONE}, &index); + + _imgui_item_end_child(); // IMGUI_TIMELINE_FRAMES_CHILD + }; + + // In order to set scroll properly, timeline_frames_child must be called first + ImGui::SetCursorPos(ImVec2(IMGUI_TIMELINE_ITEM.size)); + timeline_frames_child(); + ImGui::SetCursorPos(ImVec2(0, 0)); + + _imgui_item_begin_child(IMGUI_TIMELINE_ITEM); + _imgui_item_end_child(); // IMGUI_TIMELINE_ITEM + ImGui::SameLine(); + + timeline_header(); + timeline_items_child(); + + ImGui::PopStyleVar(2); + + _imgui_item_end_child(); // IMGUI_TIMELINE_CHILD + + if (isItemSwap) + { + Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); + + switch (swapItemReference.itemType) + { + case ANM2_LAYER: + map_swap(self->anm2->layers, self->reference->itemID, swapItemReference.itemID); + map_swap(animation->layerAnimations, self->reference->itemID, swapItemReference.itemID); + break; + case ANM2_NULL: + map_swap(self->anm2->nulls, self->reference->itemID, swapItemReference.itemID); + map_swap(animation->nullAnimations, self->reference->itemID, swapItemReference.itemID); + break; + default: + break; + } + + self->reference->itemID = swapItemReference.itemID; + anm2_reference_clear(&swapItemReference); + isItemSwap = false; + } + + if(_imgui_item_button(self, IMGUI_TIMELINE_ADD_ELEMENT)) + ImGui::OpenPopup(IMGUI_TIMELINE_ADD_ELEMENT_POPUP); + + if (ImGui::BeginPopup(IMGUI_TIMELINE_ADD_ELEMENT_POPUP)) + { + if (_imgui_item_selectable(self, IMGUI_TIMELINE_ADD_ELEMENT_LAYER)) + anm2_layer_add(self->anm2); + + if (_imgui_item_selectable(self, IMGUI_TIMELINE_ADD_ELEMENT_NULL)) + anm2_null_add(self->anm2); + + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_TIMELINE_REMOVE_ELEMENT)) + { + switch (self->reference->itemType) + { + case ANM2_LAYER: + anm2_layer_remove(self->anm2, self->reference->itemID); + break; + case ANM2_NULL: + anm2_null_remove(self->anm2, self->reference->itemID); + break; + default: + break; + } + + anm2_reference_item_clear(self->reference); } - // Add - if (ImGui::Button(STRING_IMGUI_ANIMATIONS_ADD)) + ImGui::SameLine(); + + ImguiItem playPauseItem = self->preview->isPlaying ? IMGUI_TIMELINE_PAUSE : IMGUI_TIMELINE_PLAY; + if (_imgui_item_button(self, playPauseItem)) + self->preview->isPlaying = !self->preview->isPlaying; + + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_TIMELINE_ADD_FRAME)) + anm2_frame_add(self->anm2, self->reference, (s32)self->preview->time); + + ImGui::SameLine(); + + if(_imgui_item_button(self, IMGUI_TIMELINE_REMOVE_FRAME)) { - bool isDefault = (s32)self->anm2->animations.size() == 0; // First animation is default automatically + imgui_undo_stack_push(self); + if (anm2_frame_from_reference(self->anm2, self->reference)) + { + Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference); + item->frames.erase(item->frames.begin() + self->reference->frameIndex); + anm2_reference_frame_clear(self->reference); + } + } + + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_TIMELINE_FIT_ANIMATION_LENGTH)) + anm2_animation_length_set(animation); + + ImGui::SameLine(); + _imgui_item_inputint(self, IMGUI_TIMELINE_ANIMATION_LENGTH, &animation->frameNum); + ImGui::SameLine(); + _imgui_item_inputint(self, IMGUI_TIMELINE_FPS, &self->anm2->fps); + ImGui::SameLine(); + _imgui_item_checkbox(self, IMGUI_TIMELINE_LOOP, &animation->isLoop); + ImGui::SameLine(); + _imgui_item_inputtext(self, IMGUI_TIMELINE_CREATED_BY, &self->anm2->createdBy); + ImGui::SameLine(); + _imgui_text_string(IMGUI_TIMELINE_CREATED_ON.label + self->anm2->createdOn); + ImGui::SameLine(); + _imgui_text_string(IMGUI_TIMELINE_VERSION.label + std::to_string(self->anm2->version)); + + if (_imgui_is_no_click_on_item()) + anm2_reference_item_clear(self->reference); + + _imgui_item_end(); // IMGUI_TIMELINE + + self->preview->time = CLAMP(self->preview->time, 0.0f, animation->frameNum - 1); +} + +static void _imgui_taskbar(Imgui* self) +{ + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImguiItem taskbarItem = IMGUI_TASKBAR; + taskbarItem.size = {viewport->Size.x, IMGUI_TASKBAR.size.y}; + ImGui::SetNextWindowPos(viewport->Pos); + _imgui_item_begin(taskbarItem); + + _imgui_item_selectable(self, IMGUI_TASKBAR_FILE); + + if (ImGui::BeginPopup(IMGUI_FILE_POPUP)) + { + _imgui_item_selectable(self, IMGUI_FILE_NEW); + _imgui_item_selectable(self, IMGUI_FILE_OPEN); + _imgui_item_selectable(self, IMGUI_FILE_SAVE); + _imgui_item_selectable(self, IMGUI_FILE_SAVE_AS); + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + _imgui_item_selectable(self, IMGUI_TASKBAR_PLAYBACK); + + if (ImGui::BeginPopup(IMGUI_PLAYBACK_POPUP)) + { + _imgui_item_checkbox(self, IMGUI_PLAYBACK_ALWAYS_LOOP, &self->settings->playbackIsLoop); + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + _imgui_item_selectable(self, IMGUI_TASKBAR_WIZARD); + + if (ImGui::BeginPopup(IMGUI_WIZARD_POPUP)) + { + _imgui_item_selectable(self, IMGUI_WIZARD_RECORD_GIF_ANIMATION); + ImGui::EndPopup(); + } + + _imgui_item_end(); +} + +static void _imgui_tools(Imgui* self) +{ + _imgui_item_begin(IMGUI_TOOLS); + + f32 availableWidth = ImGui::GetContentRegionAvail().x; + f32 usedWidth = ImGui::GetStyle().FramePadding.x; + + for (s32 i = 0; i < TOOL_COUNT; i++) + { + const ImguiItem item = *IMGUI_TOOL_ITEMS[i]; + + if (i > 0 && usedWidth < availableWidth) + ImGui::SameLine(); + else + usedWidth = 0; + + if (i != TOOL_COLOR) + { + ImVec4 buttonColor = self->tool == (ToolType)i ? ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] : ImGui::GetStyle().Colors[ImGuiCol_Button]; + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + + _imgui_item_atlas_image_button(self, item); + + ImGui::PopStyleColor(); + } + else + _imgui_item_coloredit4(self, IMGUI_TOOL_COLOR, &self->settings->toolColorR); + + usedWidth += ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x; + } + + _imgui_item_end(); // IMGUI_TOOLS +} + +static void _imgui_animations(Imgui* self) +{ + _imgui_item_begin(IMGUI_ANIMATIONS); + ImVec2 windowSize = ImGui::GetContentRegionAvail(); + + std::function animation_item = [&](s32 id, Anm2Animation& animation) + { + ImGui::PushID(id); + + ImguiItem animationItem = IMGUI_ANIMATION; + animationItem.isSelected = self->reference->animationID == id; + animationItem.size.x = windowSize.x; + + if (animation.name == self->anm2->defaultAnimation) + animationItem.label = std::format(IMGUI_ANIMATION_DEFAULT_FORMAT, animation.name); + else + animationItem.label = animation.name; + + if (_imgui_item_atlas_image_selectable_inputtext(self, animationItem, &animation.name, id)) + { + self->reference->animationID = id; + anm2_reference_item_clear(self->reference); + self->preview->isPlaying = false; + self->preview->time = 0.0f; + } + + if (ImGui::IsItemHovered()) + { + Anm2AnimationWithID animationWithID = {id, animation}; + _imgui_clipboard_hovered_item_set(self, animationWithID); + self->clipboard->location = (s32)id; + } + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) + { + ImGui::SetDragDropPayload(animationItem.dragDrop.c_str(), &id, sizeof(s32)); + animation_item(id, animation); + ImGui::EndDragDropSource(); + } + + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(animationItem.dragDrop.c_str())) + { + s32 sourceID = *(s32*)payload->Data; + if (sourceID != id) + { + imgui_undo_stack_push(self); + map_swap(self->anm2->animations, sourceID, id); + } + } + + ImGui::EndDragDropTarget(); + } + + ImGui::PopID(); + }; + + for (auto & [id, animation] : self->anm2->animations) + animation_item(id, animation); + + Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); + + if (_imgui_item_button(self, IMGUI_ANIMATION_ADD)) + { + bool isDefault = (s32)self->anm2->animations.size() == 0; s32 id = anm2_animation_add(self->anm2); self->reference->animationID = id; @@ -1135,192 +1142,195 @@ _imgui_animations(Imgui* self) if (isDefault) self->anm2->defaultAnimation = self->anm2->animations[id].name; } - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD); ImGui::SameLine(); - Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); - - if (animation) + if (_imgui_item_button(self, IMGUI_ANIMATION_DUPLICATE) && animation) { - // Remove - if (ImGui::Button(STRING_IMGUI_ANIMATIONS_REMOVE)) - { - anm2_animation_remove(self->anm2, self->reference->animationID); - anm2_reference_clear(self->reference); - } - _imgui_undoable(self); - _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, *animation}); - self->reference->animationID = id; - } - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE); - - ImGui::SameLine(); - - // Set as default - if (ImGui::Button(STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT)) - self->anm2->defaultAnimation = animation->name; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT); + s32 id = map_next_id_get(self->anm2->animations); + self->anm2->animations.insert({id, *animation}); + self->reference->animationID = id; } - if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - anm2_reference_clear(self->reference); + ImGui::SameLine(); - ImGui::End(); -} + _imgui_item_button(self, IMGUI_ANIMATION_MERGE); -// Events -static void -_imgui_events(Imgui* self) -{ - static s32 selectedEventID = -1; - static s32 textEntryEventID = -1; - - ImGui::Begin(STRING_IMGUI_EVENTS); - - // Iterate through all events - for (auto & [id, event] : self->anm2->events) + if (ImGui::BeginPopupModal(IMGUI_MERGE_POPUP, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - std::string eventString; - bool isSelected = selectedEventID == id; - bool isTextEntry = textEntryEventID == id; - - eventString = std::format(STRING_IMGUI_EVENT_FORMAT, id, event.name); + static s32 selectedRadioButton = ID_NONE; + static bool isDeleteAnimationsAfter = false; - ImGui::PushID(id); + _imgui_item_begin_child(IMGUI_MERGE_ANIMATIONS_CHILD); + + for (auto& [id, animation] : self->anm2->animations) + animation_item(id, animation); + + _imgui_item_end_child(); //IMGUI_MERGE_ANIMATIONS_CHILD + + _imgui_item_begin_child(IMGUI_MERGE_ON_CONFLICT_CHILD); + + _imgui_item_text(IMGUI_MERGE_ON_CONFLICT); + + _imgui_item_radio_button(self, IMGUI_MERGE_APPEND_FRAMES, &selectedRadioButton); + ImGui::SameLine(); + _imgui_item_radio_button(self, IMGUI_MERGE_REPLACE_FRAMES, &selectedRadioButton); + _imgui_item_radio_button(self, IMGUI_MERGE_PREPEND_FRAMES, &selectedRadioButton); + ImGui::SameLine(); + _imgui_item_radio_button(self, IMGUI_MERGE_IGNORE, &selectedRadioButton); + + _imgui_item_end_child(); //IMGUI_MERGE_ON_CONFLICT_CHILD + + _imgui_item_begin_child(IMGUI_MERGE_OPTIONS_CHILD); + + _imgui_item_checkbox(self, IMGUI_MERGE_DELETE_ANIMATIONS_AFTER, &isDeleteAnimationsAfter); + + _imgui_item_end_child(); //IMGUI_MERGE_OPTIONS_CHILD + + if (_imgui_item_button(self, IMGUI_MERGE_CONFIRM)) + ImGui::CloseCurrentPopup(); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_EVENT]), IMVEC2_ATLAS_UV_GET(TEXTURE_EVENT)); ImGui::SameLine(); - if (isTextEntry) - { - if (ImGui::InputText(STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL, &event.name[0], ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue)) - { - selectedEventID = -1; - textEntryEventID = -1; - } - _imgui_undoable(self); + if (_imgui_item_button(self, IMGUI_MERGE_CANCEL)) + ImGui::CloseCurrentPopup(); - if (!ImGui::IsItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - textEntryEventID = -1; - } - else - { - if (ImGui::Selectable(eventString.c_str(), isSelected)) - selectedEventID = id; + ImGui::EndPopup(); + } - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - textEntryEventID = id; - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) - { - ImGui::SetDragDropPayload(STRING_IMGUI_EVENTS_DRAG_DROP, &id, sizeof(s32)); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_ANIMATION]), IMVEC2_ATLAS_UV_GET(TEXTURE_EVENT)); - ImGui::SameLine(); - ImGui::Text(eventString.c_str()); - ImGui::EndDragDropSource(); - } - - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(STRING_IMGUI_EVENTS_DRAG_DROP)) - { - s32 sourceID = *(s32*)payload->Data; - if (sourceID != id) - { - _imgui_undo_stack_push(self); - map_swap(self->anm2->events, sourceID, id); - } - } - ImGui::EndDragDropTarget(); - } - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_SELECT); - - ImGui::PopID(); + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_ANIMATION_REMOVE) && animation) + { + anm2_animation_remove(self->anm2, self->reference->animationID); + anm2_reference_clear(self->reference); } - if (ImGui::Button(STRING_IMGUI_EVENTS_ADD)) + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_ANIMATION_DEFAULT) && animation) + self->anm2->defaultAnimation = animation->name; + + if (_imgui_is_no_click_on_item()) + anm2_reference_clear(self->reference); + + _imgui_item_end(); +} + +static void _imgui_events(Imgui* self) +{ + static s32 selectedEventID = ID_NONE; + + _imgui_item_begin(IMGUI_EVENTS); + ImVec2 windowSize = ImGui::GetContentRegionAvail(); + + std::function event_item = [&](s32 id, Anm2Event& event) + { + ImGui::PushID(id); + + ImguiItem eventItem = IMGUI_EVENT; + eventItem.label = std::format(IMGUI_EVENT_FORMAT, id, event.name); + eventItem.isSelected = selectedEventID == id; + eventItem.size.x = windowSize.x; + + if (_imgui_item_atlas_image_selectable_inputtext(self, eventItem, &event.name, id)) + selectedEventID = id; + + if (ImGui::IsItemHovered()) + { + Anm2EventWithID eventWithID = {id, event}; + _imgui_clipboard_hovered_item_set(self, eventWithID); + self->clipboard->location = (s32)id; + } + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) + { + ImGui::SetDragDropPayload(eventItem.dragDrop.c_str(), &id, sizeof(s32)); + event_item(id, event); + ImGui::EndDragDropSource(); + } + + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(eventItem.dragDrop.c_str())) + { + s32 sourceID = *(s32*)payload->Data; + if (sourceID != id) + { + imgui_undo_stack_push(self); + map_swap(self->anm2->events, sourceID, id); + } + } + ImGui::EndDragDropTarget(); + } + + ImGui::PopID(); + }; + + for (auto& [id, event] : self->anm2->events) + event_item(id, event); + + if (_imgui_item_button(self, IMGUI_EVENT_ADD)) { s32 id = map_next_id_get(self->anm2->events); self->anm2->events[id] = Anm2Event{}; selectedEventID = id; } - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_ADD); - + ImGui::SameLine(); - if (selectedEventID != -1) + if (selectedEventID == ID_NONE) { - if (ImGui::Button(STRING_IMGUI_EVENTS_REMOVE)) - { - self->anm2->events.erase(selectedEventID); - selectedEventID = -1; - } - _imgui_undoable(self); + _imgui_item_end(); + return; + } + + if (_imgui_item_button(self, IMGUI_EVENT_REMOVE)) + { + self->anm2->events.erase(selectedEventID); + selectedEventID = ID_NONE; } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_REMOVE); - if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - selectedEventID = -1; + if (_imgui_is_no_click_on_item()) + selectedEventID = ID_NONE; - ImGui::End(); + _imgui_item_end(); } -// Spritesheets -static void -_imgui_spritesheets(Imgui* self) +static void _imgui_spritesheets(Imgui* self) { - static s32 selectedSpritesheetID = -1; + static s32 selectedSpritesheetID = ID_NONE; - ImGui::Begin(STRING_IMGUI_SPRITESHEETS); + _imgui_item_begin(IMGUI_SPRITESHEETS); - for (auto [id, spritesheet] : self->anm2->spritesheets) + std::function spritesheet_item = [&](s32 id, Anm2Spritesheet& spritesheet) { - ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE; - bool isSelected = selectedSpritesheetID == id; - Texture* texture = &self->resources->textures[id]; - - std::string spritesheetString = std::format(STRING_IMGUI_SPRITESHEET_FORMAT, id, spritesheet.path); - - ImGui::BeginChild(spritesheetString.c_str(), IMGUI_SPRITESHEET_SIZE, true, ImGuiWindowFlags_None); - ImGui::PushID(id); + + Texture* texture = &self->resources->textures[id]; + ImguiItem spritesheetItem = IMGUI_SPRITESHEET_CHILD; - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_SPRITESHEET]), IMVEC2_ATLAS_UV_GET(TEXTURE_SPRITESHEET)); - ImGui::SameLine(); - - if (ImGui::Selectable(spritesheetString.c_str(), isSelected)) + ImguiItem spritesheetItemSelectable = IMGUI_SPRITESHEET_SELECTABLE; + spritesheetItemSelectable.label = std::format(IMGUI_SPRITESHEET_FORMAT, id, spritesheet.path); + spritesheetItemSelectable.isSelected = selectedSpritesheetID == id; + + _imgui_item_begin_child(spritesheetItem); + if (_imgui_item_atlas_image_selectable(self, spritesheetItemSelectable)) { selectedSpritesheetID = id; _imgui_spritesheet_editor_set(self, selectedSpritesheetID); } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_SELECT); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload(STRING_IMGUI_SPRITESHEETS_DRAG_DROP, &id, sizeof(s32)); - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_SPRITESHEET]), IMVEC2_ATLAS_UV_GET(TEXTURE_SPRITESHEET)); - ImGui::SameLine(); - ImGui::Text(spritesheetString.c_str()); + ImGui::SetDragDropPayload(spritesheetItem.dragDrop.c_str(), &id, sizeof(s32)); + spritesheet_item(id, spritesheet); ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(STRING_IMGUI_SPRITESHEETS_DRAG_DROP)) + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(spritesheetItem.dragDrop.c_str())) { s32 sourceID = *(s32*)payload->Data; if (sourceID != id) @@ -1332,6 +1342,7 @@ _imgui_spritesheets(Imgui* self) ImGui::EndDragDropTarget(); } + ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE; f32 spritesheetAspect = (f32)self->resources->textures[id].size.x / self->resources->textures[id].size.y; if ((IMGUI_SPRITESHEET_PREVIEW_SIZE.x / IMGUI_SPRITESHEET_PREVIEW_SIZE.y) > spritesheetAspect) @@ -1340,802 +1351,472 @@ _imgui_spritesheets(Imgui* self) spritesheetPreviewSize.y = IMGUI_SPRITESHEET_PREVIEW_SIZE.x / spritesheetAspect; if (texture->isInvalid) - ImGui::Image(self->resources->atlas.id, VEC2_TO_IMVEC2(ATLAS_SIZES[TEXTURE_ERROR]), IMVEC2_ATLAS_UV_GET(TEXTURE_ERROR)); + _imgui_atlas_image(self, TEXTURE_NONE); else ImGui::Image(texture->id, spritesheetPreviewSize); - ImGui::PopID(); - - ImGui::EndChild(); - } + _imgui_item_end_child(); // spritesheetItem - if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_ADD)) - dialog_png_open(self->dialog); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD); + ImGui::PopID(); + }; + + for (auto [id, spritesheet] : self->anm2->spritesheets) + spritesheet_item(id, spritesheet); + + // TODO: match Nicalis + + _imgui_item_button(self, IMGUI_SPRITESHEET_ADD); ImGui::SameLine(); - // If spritesheet selected... - if (selectedSpritesheetID > -1) + if (selectedSpritesheetID == ID_NONE) { - // Remove - if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REMOVE)) - { - texture_free(&self->resources->textures[selectedSpritesheetID]); - self->resources->textures.erase(selectedSpritesheetID); - self->anm2->spritesheets.erase(selectedSpritesheetID); - selectedSpritesheetID = -1; - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE); - - ImGui::SameLine(); - - // Reload - if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_RELOAD)) - { - // Save the working path, set path, to the anm2's path, load spritesheet, return to old path - std::filesystem::path workingPath = std::filesystem::current_path(); - working_directory_from_file_set(self->anm2->path); - - resources_texture_init(self->resources, self->anm2->spritesheets[selectedSpritesheetID].path, selectedSpritesheetID); - - std::filesystem::current_path(workingPath); - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD); - ImGui::SameLine(); - - // Replace - if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REPLACE)) - { - self->dialog->replaceID = selectedSpritesheetID; - dialog_png_replace(self->dialog); - } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE); + _imgui_item_end(); + return; } - if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - selectedSpritesheetID = -1; + if (_imgui_item_button(self, IMGUI_SPRITESHEET_REMOVE)) + { + texture_free(&self->resources->textures[selectedSpritesheetID]); + self->resources->textures.erase(selectedSpritesheetID); + self->anm2->spritesheets.erase(selectedSpritesheetID); + selectedSpritesheetID = ID_NONE; + } + + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_SPRITESHEET_RELOAD)) + { + std::filesystem::path workingPath = std::filesystem::current_path(); + working_directory_from_file_set(self->anm2->path); + + resources_texture_init(self->resources, self->anm2->spritesheets[selectedSpritesheetID].path, selectedSpritesheetID); + + std::filesystem::current_path(workingPath); + } + ImGui::SameLine(); + + if (_imgui_item_button(self, IMGUI_SPRITESHEET_REPLACE)) + { + self->dialog->replaceID = selectedSpritesheetID; + dialog_png_replace(self->dialog); + } + + if (_imgui_is_no_click_on_item()) + selectedSpritesheetID = ID_NONE; - ImGui::End(); + _imgui_item_end(); } -// Animation Preview -static void -_imgui_animation_preview(Imgui* self) +static void _imgui_animation_preview(Imgui* self) { static bool isPreviewHover = false; - static bool isPreviewCenter = false; - static vec2 mousePos = {0, 0}; - - ImGui::Begin(STRING_IMGUI_ANIMATION_PREVIEW, NULL, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + static vec2 mousePos{}; + static vec2 previewPos{}; + static ImVec2 previewScreenPos{}; + _imgui_item_begin(IMGUI_ANIMATION_PREVIEW); ImVec2 windowSize = ImGui::GetWindowSize(); - - // Grid settings - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); - - // Grid toggle - ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_GRID, &self->settings->previewIsGrid); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID); - + ImVec2 previewWindowRectSize = ImGui::GetCurrentWindow()->ClipRect.GetSize(); + + _imgui_item_begin_child(IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS); + _imgui_item_checkbox(self, IMGUI_ANIMATION_PREVIEW_GRID, &self->settings->previewIsGrid); ImGui::SameLine(); - - // Grid Color - ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR, (f32*)&self->settings->previewGridColorR, ImGuiColorEditFlags_NoInputs); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR); - - // Grid Size - ImGui::InputInt2(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE, (s32*)&self->settings->previewGridSizeX); - self->settings->previewGridSizeX = CLAMP(self->settings->previewGridSizeX, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); - self->settings->previewGridSizeY = CLAMP(self->settings->previewGridSizeY, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE); - - // Grid Offset - ImGui::InputInt2(STRING_IMGUI_ANIMATION_PREVIEW_GRID_OFFSET, (s32*)&self->settings->previewGridOffsetX); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_OFFSET); - ImGui::EndChild(); + _imgui_item_coloredit4(self, IMGUI_ANIMATION_PREVIEW_GRID_COLOR, (f32*)&self->settings->previewGridColorR); + _imgui_item_inputint2(self, IMGUI_ANIMATION_PREVIEW_GRID_SIZE, (s32*)&self->settings->previewGridSizeX); + _imgui_item_inputint2(self, IMGUI_ANIMATION_PREVIEW_GRID_OFFSET, (s32*)&self->settings->previewGridOffsetX); + _imgui_item_end_child(); ImGui::SameLine(); - ImGui::SameLine(); - - // View settings - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); - - // Zoom - ImGui::DragFloat(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM, &self->settings->previewZoom, 1, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX, "%.0f"); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM); - - // Center view - if (ImGui::Button(STRING_IMGUI_ANIMATION_PREVIEW_CENTER_VIEW)) - isPreviewCenter = true; - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_CENTER_VIEW); - - // Mouse position; note that mousePos is relative to the animation preview and set later - std::string mousePositionString = std::format(STRING_IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y); + _imgui_item_begin_child(IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS); + _imgui_item_dragfloat(self, IMGUI_ANIMATION_PREVIEW_ZOOM, &self->settings->previewZoom); + if (_imgui_item_button(self, IMGUI_ANIMATION_PREVIEW_CENTER_VIEW)) + { + self->settings->previewPanX = -(previewWindowRectSize.x - PREVIEW_SIZE.x) * 0.5f; + self->settings->previewPanY = -((previewWindowRectSize.y - PREVIEW_SIZE.y) * 0.5f) + (previewPos.y * 0.5f); + } + std::string mousePositionString = std::format(IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y); ImGui::Text(mousePositionString.c_str()); - - ImGui::EndChild(); + _imgui_item_end_child(); ImGui::SameLine(); - // Background settings - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); - - // Background color - ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, (f32*)&self->settings->previewBackgroundColorR, ImGuiColorEditFlags_NoInputs); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR); - - ImGui::EndChild(); + _imgui_item_begin_child(IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS); + _imgui_item_coloredit4(self, IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, (f32*)&self->settings->previewBackgroundColorR); + _imgui_item_end_child(); ImGui::SameLine(); - // Helper settings - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_HELPER_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); - - // Axis toggle - ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_AXIS, &self->settings->previewIsAxis); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS); - + _imgui_item_begin_child(IMGUI_ANIMATION_PREVIEW_HELPER_SETTINGS); + _imgui_item_checkbox(self, IMGUI_ANIMATION_PREVIEW_AXIS, &self->settings->previewIsAxis); ImGui::SameLine(); + _imgui_item_coloredit4(self, IMGUI_ANIMATION_PREVIEW_AXIS_COLOR, (f32*)&self->settings->previewAxisColorR); + _imgui_item_checkbox(self, IMGUI_ANIMATION_PREVIEW_ROOT_TRANSFORM, &self->settings->previewIsRootTransform); + _imgui_item_checkbox(self, IMGUI_ANIMATION_PREVIEW_SHOW_PIVOT, &self->settings->previewIsShowPivot); + _imgui_item_end_child(); - // Axis colors - ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_AXIS_COLOR, (f32*)&self->settings->previewAxisColorR, ImGuiColorEditFlags_NoInputs); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS_COLOR); - - // Root transform - ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_ROOT_TRANSFORM, &self->settings->previewIsRootTransform); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ROOT_TRANSFORM); - - // Show pivot - ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_SHOW_PIVOT, &self->settings->previewIsShowPivot); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_SHOW_PIVOT); - - ImGui::EndChild(); - - // Animation preview texture - vec2 previewPos = IMVEC2_TO_VEC2(ImGui::GetCursorPos()); - + previewPos = IMVEC2_TO_VEC2(ImGui::GetCursorPos()); + previewScreenPos = ImGui::GetCursorScreenPos(); ImGui::Image(self->preview->texture, VEC2_TO_IMVEC2(PREVIEW_SIZE)); - - self->preview->recordSize = vec2(windowSize.x, windowSize.y - IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE.y); - - // Using tools when hovered + self->preview->recordSize = vec2(windowSize.x, windowSize.y - IMGUI_CANVAS_CHILD_SIZE.y); + if (ImGui::IsItemHovered()) { - vec2 windowPos = IMVEC2_TO_VEC2(ImGui::GetWindowPos()); + if (!isPreviewHover) + SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)); - // Setting mouse pos relative to preview - mousePos = IMVEC2_TO_VEC2(ImGui::GetMousePos()); + isPreviewHover = true; + } + else + isPreviewHover = false; - mousePos -= (windowPos + previewPos); - mousePos -= (PREVIEW_SIZE / 2.0f); - mousePos.x += self->settings->previewPanX; - mousePos.y += self->settings->previewPanY; - mousePos.x /= PERCENT_TO_UNIT(self->settings->previewZoom); - mousePos.y /= PERCENT_TO_UNIT(self->settings->previewZoom); + Anm2Frame trigger{}; + anm2_frame_from_time(self->anm2, &trigger, Anm2Reference{self->reference->animationID, ANM2_TRIGGERS}, self->preview->time); - Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference); + if (trigger.eventID != ID_NONE) + { + static const ImU32 textColor = ImGui::GetColorU32(IMGUI_TIMELINE_FRAME_COLOR); + ImGui::GetWindowDrawList()->AddText(previewScreenPos, textColor, self->anm2->events[trigger.eventID].name.c_str()); + } - if (self->reference->itemType == ANM2_TRIGGERS) - frame = NULL; + if (!isPreviewHover) + { + _imgui_keyboard_navigation_set(true); + _imgui_item_end(); + return; + } - // Allow use of keybinds for tools - self->tool->isEnabled = true; + _imgui_keyboard_navigation_set(false); + + vec2 windowPos = IMVEC2_TO_VEC2(ImGui::GetWindowPos()); + mousePos = IMVEC2_TO_VEC2(ImGui::GetMousePos()); - switch (self->tool->type) + mousePos -= (windowPos + previewPos); + mousePos -= (PREVIEW_SIZE * 0.5f); + mousePos += vec2(self->settings->previewPanX, self->settings->previewPanY); + mousePos /= PERCENT_TO_UNIT(self->settings->previewZoom); + + ToolType tool = self->tool; + bool isLeft = ImGui::IsKeyDown(IMGUI_INPUT_LEFT); + bool isRight = ImGui::IsKeyDown(IMGUI_INPUT_RIGHT); + bool isUp = ImGui::IsKeyDown(IMGUI_INPUT_UP); + bool isDown = ImGui::IsKeyDown(IMGUI_INPUT_DOWN); + bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_MOD); + bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left); + bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); + bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle); + ImVec2 mouseDelta = ImGui::GetIO().MouseDelta; + f32 mouseWheel = ImGui::GetIO().MouseWheel; + + SDL_SetCursor(SDL_CreateSystemCursor(IMGUI_TOOL_MOUSE_CURSORS[tool])); + + if (self->tool == TOOL_MOVE || self->tool == TOOL_SCALE || self->tool == TOOL_ROTATE) + if (isMouseClick || isLeft || isRight || isUp || isDown) + imgui_undo_stack_push(self); + + if ((self->tool == TOOL_PAN && isMouseDown) || isMouseMiddleDown) + { + self->settings->previewPanX -= mouseDelta.x; + self->settings->previewPanY -= mouseDelta.y; + } + + Anm2Frame* frame = nullptr; + if (self->reference->itemType != ANM2_TRIGGERS) + frame = anm2_frame_from_reference(self->anm2, self->reference); + + if (frame) + { + f32 step = isMod ? IMGUI_TOOL_STEP_MOD : IMGUI_TOOL_STEP; + + switch (tool) { - case TOOL_PAN: - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER)); - - if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - { - self->settings->previewPanX -= self->input->mouse.delta.x; - self->settings->previewPanY -= self->input->mouse.delta.y; - } - break; case TOOL_MOVE: - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE)); - - if (frame) + if (isMouseDown) + frame->position = IMVEC2_TO_VEC2(mousePos); + else { - f32 step = input_held(self->input, INPUT_MOD) ? PREVIEW_MOVE_STEP_MOD : PREVIEW_MOVE_STEP; - - if - ( - mouse_press(&self->input->mouse, MOUSE_LEFT) || - input_press(self->input, INPUT_LEFT) || - input_press(self->input, INPUT_RIGHT) || - input_press(self->input, INPUT_UP) || - input_press(self->input, INPUT_DOWN) - ) - _imgui_undo_stack_push(self); - - if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - frame->position = IMVEC2_TO_VEC2(mousePos); - - if (input_held(self->input, INPUT_LEFT)) - frame->position.x -= step; - - if (input_held(self->input, INPUT_RIGHT)) - frame->position.x += step; - - if (input_held(self->input, INPUT_UP)) - frame->position.y -= step; - - if (input_held(self->input, INPUT_DOWN)) - frame->position.y += step; + if (isLeft) frame->position.x -= step; + if (isRight) frame->position.x += step; + if (isUp) frame->position.y -= step; + if (isDown) frame->position.y += step; } break; case TOOL_ROTATE: - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)); - if (frame) + if (isMouseDown) + frame->rotation += mouseDelta.x; + else { - f32 step = input_held(self->input, INPUT_MOD) ? PREVIEW_ROTATE_STEP_MOD : PREVIEW_ROTATE_STEP; - - if - ( - mouse_press(&self->input->mouse, MOUSE_LEFT) || - input_press(self->input, INPUT_LEFT) || - input_press(self->input, INPUT_RIGHT) || - input_press(self->input, INPUT_UP) || - input_press(self->input, INPUT_DOWN) - ) - _imgui_undo_stack_push(self); - - if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - frame->rotation += (s32)self->input->mouse.delta.x; - - if - ( - input_held(self->input, INPUT_LEFT) || - input_held(self->input, INPUT_UP) - ) - frame->rotation -= step; - - if - ( - input_held(self->input, INPUT_RIGHT) || - input_held(self->input, INPUT_DOWN) - ) - frame->rotation += step; + if (isLeft || isUp) frame->rotation -= step; + if (isRight || isDown) frame->rotation += step; } break; case TOOL_SCALE: - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NE_RESIZE)); - if (frame) + if (isMouseDown) + frame->scale += IMVEC2_TO_VEC2(ImGui::GetIO().MouseDelta); + else { - f32 step = input_held(self->input, INPUT_MOD) ? PREVIEW_SCALE_STEP_MOD : PREVIEW_SCALE_STEP; - - if - ( - mouse_press(&self->input->mouse, MOUSE_LEFT) || - input_press(self->input, INPUT_LEFT) || - input_press(self->input, INPUT_RIGHT) || - input_press(self->input, INPUT_UP) || - input_press(self->input, INPUT_DOWN) - ) - _imgui_undo_stack_push(self); - - if (input_held(self->input, INPUT_LEFT)) - frame->scale.x -= step; - - if (input_held(self->input, INPUT_RIGHT)) - frame->scale.x += step; - - if (input_held(self->input, INPUT_UP)) - frame->scale.y -= step; - - if (input_held(self->input, INPUT_DOWN)) - frame->scale.y += step; - - if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - { - frame->scale.x += (s32)self->input->mouse.delta.x; - frame->scale.y += (s32)self->input->mouse.delta.y; - } + if (isLeft) frame->scale.x -= step; + if (isRight) frame->scale.x += step; + if (isUp) frame->scale.y -= step; + if (isDown) frame->scale.y += step; } break; - case TOOL_CROP: - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)); - break; default: break; - }; - - isPreviewHover = true; - - // Used to not be annoying when at lowest zoom - self->settings->previewZoom = self->settings->previewZoom == EDITOR_ZOOM_MIN ? 0 : self->settings->previewZoom; - - // Zoom in - if (self->input->mouse.wheelDeltaY > 0 || input_release(self->input, INPUT_ZOOM_IN)) - self->settings->previewZoom += PREVIEW_ZOOM_STEP; - - // Zoom out - if (self->input->mouse.wheelDeltaY < 0 || input_release(self->input, INPUT_ZOOM_OUT)) - self->settings->previewZoom -= PREVIEW_ZOOM_STEP; - - self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX); - } - else - { - if (isPreviewHover) - { - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)); - isPreviewHover = false; } } - if (isPreviewCenter) + if (mouseWheel != 0 || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN) || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_OUT)) { - ImVec2 previewWindowRectSize = ImGui::GetCurrentWindow()->ClipRect.GetSize(); - - // Based on the preview's crop in its window, adjust the pan - self->settings->previewPanX = -(previewWindowRectSize.x - PREVIEW_SIZE.x) / 2.0f; - self->settings->previewPanY = -((previewWindowRectSize.y - PREVIEW_SIZE.y) / 2.0f) + (IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE.y / 2.0f); - - isPreviewCenter = false; + const f32 delta = (mouseWheel > 0 || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN)) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP; + self->settings->previewZoom = ROUND_NEAREST_FLOAT(self->settings->previewZoom + delta, CANVAS_ZOOM_STEP); + self->settings->previewZoom = CLAMP(self->settings->previewZoom, CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX); } - ImGui::End(); + _imgui_item_end(); } -// Spritesheet Editor -static void -_imgui_spritesheet_editor(Imgui* self) +static void _imgui_spritesheet_editor(Imgui* self) { static bool isEditorHover = false; - static bool isEditorCenter = false; static vec2 mousePos = {0, 0}; + std::string mousePositionString = std::format(IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y); - ImGui::Begin(STRING_IMGUI_SPRITESHEET_EDITOR, NULL, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + _imgui_item_begin(IMGUI_SPRITESHEET_EDITOR); - // Grid settings - ImGui::BeginChild(STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SETTINGS, IMGUI_SPRITESHEET_EDITOR_SETTINGS_CHILD_SIZE, true); - - // Grid toggle - ImGui::Checkbox(STRING_IMGUI_SPRITESHEET_EDITOR_GRID, &self->settings->editorIsGrid); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID); - + _imgui_item_begin_child(IMGUI_SPRITESHEET_EDITOR_GRID_SETTINGS); + _imgui_item_checkbox(self, IMGUI_SPRITESHEET_EDITOR_GRID, &self->settings->editorIsGrid); ImGui::SameLine(); - - // Grid snap - ImGui::Checkbox(STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SNAP, &self->settings->editorIsGridSnap); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SNAP); - + _imgui_item_checkbox(self, IMGUI_SPRITESHEET_EDITOR_GRID_SNAP, &self->settings->editorIsGridSnap); ImGui::SameLine(); - - // Grid color - ImGui::ColorEdit4(STRING_IMGUI_SPRITESHEET_EDITOR_GRID_COLOR, (f32*)&self->settings->editorGridColorR, ImGuiColorEditFlags_NoInputs); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_COLOR); - - // Grid size - ImGui::InputInt2(STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SIZE, (s32*)&self->settings->editorGridSizeX); - self->settings->editorGridSizeX = CLAMP(self->settings->editorGridSizeX, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); - self->settings->editorGridSizeY = CLAMP(self->settings->editorGridSizeY, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SIZE); - - // Grid offset - ImGui::InputInt2(STRING_IMGUI_SPRITESHEET_EDITOR_GRID_OFFSET, (s32*)&self->settings->editorGridOffsetX); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_OFFSET); - - ImGui::EndChild(); + _imgui_item_coloredit4(self, IMGUI_SPRITESHEET_EDITOR_GRID_COLOR, (f32*)&self->settings->editorGridColorR); + _imgui_item_inputint2(self, IMGUI_SPRITESHEET_EDITOR_GRID_SIZE, (s32*)&self->settings->editorGridSizeX); + _imgui_item_inputint2(self, IMGUI_SPRITESHEET_EDITOR_GRID_OFFSET, (s32*)&self->settings->editorGridOffsetX); + _imgui_item_end_child(); ImGui::SameLine(); - // View settings - ImGui::BeginChild(STRING_IMGUI_SPRITESHEET_EDITOR_VIEW_SETTINGS, IMGUI_SPRITESHEET_EDITOR_SETTINGS_CHILD_SIZE, true); - - // Zoom - ImGui::DragFloat(STRING_IMGUI_SPRITESHEET_EDITOR_ZOOM, &self->settings->editorZoom, 1, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX, "%.0f"); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_ZOOM); - - // Center view - if (ImGui::Button(STRING_IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW)) - isEditorCenter = true; - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_CENTER_VIEW); - - // Info position - std::string mousePositionString = std::format(STRING_IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y); + _imgui_item_begin_child(IMGUI_SPRITESHEET_EDITOR_VIEW_SETTINGS); + _imgui_item_dragfloat(self, IMGUI_SPRITESHEET_EDITOR_ZOOM, &self->settings->editorZoom); + if (_imgui_item_button(self, IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW)) + { + self->settings->editorPanX = EDITOR_SIZE.x / 2.0f; + self->settings->editorPanY = EDITOR_SIZE.y / 2.0f; + } ImGui::Text(mousePositionString.c_str()); - - ImGui::EndChild(); - + _imgui_item_end_child(); + ImGui::SameLine(); - // Background settings - ImGui::BeginChild(STRING_IMGUI_SPRITESHEET_EDITOR_BACKGROUND_SETTINGS, IMGUI_SPRITESHEET_EDITOR_SETTINGS_CHILD_SIZE, true); - - // Background color - ImGui::ColorEdit4(STRING_IMGUI_SPRITESHEET_EDITOR_BACKGROUND_COLOR, (f32*)&self->settings->editorBackgroundColorR, ImGuiColorEditFlags_NoInputs); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BACKGROUND_COLOR); - - // Border - ImGui::Checkbox(STRING_IMGUI_SPRITESHEET_EDITOR_BORDER, &self->settings->editorIsBorder); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BORDER); - - ImGui::EndChild(); + _imgui_item_begin_child(IMGUI_SPRITESHEET_EDITOR_BACKGROUND_SETTINGS); + _imgui_item_coloredit4(self, IMGUI_SPRITESHEET_EDITOR_BACKGROUND_COLOR, (f32*)&self->settings->editorBackgroundColorR); + _imgui_item_checkbox(self, IMGUI_SPRITESHEET_EDITOR_BORDER, &self->settings->editorIsBorder); + _imgui_item_end_child(); vec2 editorPos = IMVEC2_TO_VEC2(ImGui::GetCursorPos()); ImGui::Image(self->editor->texture, VEC2_TO_IMVEC2(EDITOR_SIZE)); - // Panning and zoom - if (ImGui::IsItemHovered()) - { - vec2 windowPos = IMVEC2_TO_VEC2(ImGui::GetWindowPos()); - mousePos = IMVEC2_TO_VEC2(ImGui::GetMousePos()); - - mousePos -= (windowPos + editorPos); - mousePos -= (EDITOR_SIZE / 2.0f); - mousePos.x += self->settings->editorPanX; - mousePos.y += self->settings->editorPanY; - mousePos.x /= PERCENT_TO_UNIT(self->settings->editorZoom); - mousePos.y /= PERCENT_TO_UNIT(self->settings->editorZoom); - - isEditorHover = true; - - Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference); - - // Allow use of keybinds for tools - self->tool->isEnabled = true; - - // Only changing layer frames - if (self->reference->itemType != ANM2_LAYER) - frame = NULL; - - if (self->tool->type == TOOL_CROP && frame) - { - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)); - - if (mouse_press(&self->input->mouse, MOUSE_LEFT)) - { - _imgui_undo_stack_push(self); - - vec2 cropPosition = mousePos + IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS; - - if (self->settings->editorIsGridSnap) - { - cropPosition.x = (s32)(cropPosition.x / self->settings->editorGridSizeX) * self->settings->editorGridSizeX; - cropPosition.y = (s32)(cropPosition.y / self->settings->editorGridSizeX) * self->settings->editorGridSizeY; - } - - frame->crop = cropPosition; - frame->size = {0, 0}; - - } - else if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - { - vec2 sizePosition = mousePos + IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS; - - if (self->settings->editorIsGridSnap) - { - sizePosition.x = (s32)(sizePosition.x / self->settings->editorGridSizeX) * self->settings->editorGridSizeX; - sizePosition.y = (s32)(sizePosition.y / self->settings->editorGridSizeX) * self->settings->editorGridSizeY; - } - - frame->size = sizePosition - frame->crop; - } - } - else - { - SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER)); - - if (mouse_held(&self->input->mouse, MOUSE_LEFT)) - { - self->settings->editorPanX -= self->input->mouse.delta.x; - self->settings->editorPanY -= self->input->mouse.delta.y; - } - } - - // Used to not be annoying when at lowest zoom - self->settings->editorZoom = self->settings->editorZoom == EDITOR_ZOOM_MIN ? 0 : self->settings->editorZoom; - - // Zoom in - if (self->input->mouse.wheelDeltaY > 0 || input_press(self->input, INPUT_ZOOM_IN)) - self->settings->editorZoom += PREVIEW_ZOOM_STEP; - - // Zoom out - if (self->input->mouse.wheelDeltaY < 0 || input_press(self->input, INPUT_ZOOM_OUT)) - self->settings->editorZoom -= PREVIEW_ZOOM_STEP; - - self->settings->editorZoom = CLAMP(self->settings->editorZoom, EDITOR_ZOOM_MIN, EDITOR_ZOOM_MAX); - } - else + if (!ImGui::IsItemHovered()) { if (isEditorHover) { SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)); isEditorHover = false; } + _imgui_keyboard_navigation_set(true); + _imgui_item_end(); + return; } - if (isEditorCenter) - { - self->settings->editorPanX = EDITOR_SIZE.x / 2.0f; - self->settings->editorPanY = EDITOR_SIZE.y / 2.0f; - - isEditorCenter = false; - } + isEditorHover = true; + _imgui_keyboard_navigation_set(false); - ImGui::End(); + vec2 windowPos = IMVEC2_TO_VEC2(ImGui::GetWindowPos()); + mousePos = IMVEC2_TO_VEC2(ImGui::GetMousePos()); + + mousePos -= (windowPos + editorPos); + mousePos -= (EDITOR_SIZE * 0.5f); + mousePos += vec2(self->settings->editorPanX, self->settings->editorPanY); + mousePos /= PERCENT_TO_UNIT(self->settings->editorZoom); + + bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left); + bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); + bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle); + f32 mouseWheel = ImGui::GetIO().MouseWheel; + ImVec2 mouseDelta = ImGui::GetIO().MouseDelta; + + SDL_SetCursor(SDL_CreateSystemCursor(IMGUI_TOOL_MOUSE_CURSORS[self->tool])); + + if ((self->tool == TOOL_PAN && isMouseDown) || isMouseMiddleDown) + { + self->settings->editorPanX -= mouseDelta.x; + self->settings->editorPanY -= mouseDelta.y; + } + + Anm2Frame* frame = nullptr; + if (self->reference->itemType == ANM2_LAYER) + frame = anm2_frame_from_reference(self->anm2, self->reference); + + if (frame && self->tool == TOOL_CROP) + { + if (isMouseClick) + { + imgui_undo_stack_push(self); + + vec2 cropPosition = mousePos + IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS; + + if (self->settings->editorIsGridSnap) + { + cropPosition.x = (s32)(cropPosition.x / self->settings->editorGridSizeX) * self->settings->editorGridSizeX; + cropPosition.y = (s32)(cropPosition.y / self->settings->editorGridSizeX) * self->settings->editorGridSizeY; + } + + frame->crop = cropPosition; + frame->size = {0, 0}; + } + else if (isMouseDown) + { + vec2 sizePosition = mousePos + IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS; + + if (self->settings->editorIsGridSnap) + { + sizePosition.x = (s32)(sizePosition.x / self->settings->editorGridSizeX) * self->settings->editorGridSizeX; + sizePosition.y = (s32)(sizePosition.y / self->settings->editorGridSizeX) * self->settings->editorGridSizeY; + } + + frame->size = sizePosition - frame->crop; + } + } + + if (mouseWheel != 0 || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN) || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_OUT)) + { + const f32 delta = (mouseWheel > 0 || ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN)) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP; + self->settings->editorZoom = ROUND_NEAREST_FLOAT(self->settings->editorZoom + delta, CANVAS_ZOOM_STEP); + self->settings->editorZoom = CLAMP(self->settings->editorZoom, CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX); + } + + _imgui_item_end(); } -// Frame properties -static void -_imgui_frame_properties(Imgui* self) +static void _imgui_frame_properties(Imgui* self) { - ImGui::Begin(STRING_IMGUI_FRAME_PROPERTIES); - + _imgui_item_begin(IMGUI_FRAME_PROPERTIES); + Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference); - if (frame) + if (!frame) { - Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); + ImGui::Text(IMGUI_FRAME_PROPERTIES_NO_FRAME); + _imgui_item_end(); + return; + } + + Anm2Type type = self->reference->itemType; + Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); - std::vector eventNames; + ImGui::Text(IMGUI_FRAME_PROPERTIES_TITLE[type].c_str()); + + if (type == ANM2_ROOT || type == ANM2_NULL || type == ANM2_LAYER) + { + _imgui_item_dragfloat2(self, IMGUI_FRAME_PROPERTIES_POSITION, value_ptr(frame->position)); + + if (type == ANM2_LAYER) + { + _imgui_item_dragfloat2(self, IMGUI_FRAME_PROPERTIES_CROP, value_ptr(frame->crop)); + _imgui_item_dragfloat2(self, IMGUI_FRAME_PROPERTIES_SIZE, value_ptr(frame->size)); + _imgui_item_dragfloat2(self, IMGUI_FRAME_PROPERTIES_PIVOT, value_ptr(frame->pivot)); + } + + _imgui_item_dragfloat2(self, IMGUI_FRAME_PROPERTIES_SCALE, value_ptr(frame->scale)); + _imgui_item_dragfloat(self, IMGUI_FRAME_PROPERTIES_ROTATION, &frame->rotation); + _imgui_item_inputint(self, IMGUI_FRAME_PROPERTIES_DURATION, &frame->delay); + _imgui_item_coloredit4(self, IMGUI_FRAME_PROPERTIES_TINT, value_ptr(frame->tintRGBA)); + _imgui_item_coloredit3(self, IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, value_ptr(frame->offsetRGB)); + + if (_imgui_item_button(self, IMGUI_FRAME_PROPERTIES_FLIP_X)) + frame->scale.x = -frame->scale.x; + ImGui::SameLine(); + if (_imgui_item_button(self, IMGUI_FRAME_PROPERTIES_FLIP_Y)) + frame->scale.y = -frame->scale.y; + + _imgui_item_checkbox(self, IMGUI_FRAME_PROPERTIES_VISIBLE, &frame->isVisible); + ImGui::SameLine(); + _imgui_item_checkbox(self, IMGUI_FRAME_PROPERTIES_INTERPOLATED, &frame->isInterpolated); + + } + else if (type == ANM2_TRIGGERS) + { + std::vector eventStrings; + std::vector eventLabels; std::vector eventIDs; - static s32 selectedEventIndex = -1; + + eventStrings.reserve(self->anm2->events.size() + 1); + eventIDs.reserve(self->anm2->events.size() + 1); + eventLabels.reserve(self->anm2->events.size() + 1); - switch (self->reference->itemType) + eventIDs.push_back(ID_NONE); + eventStrings.push_back(IMGUI_EVENT_NONE); + eventLabels.push_back(eventStrings.back().c_str()); + + for (auto & [id, event] : self->anm2->events) { - case ANM2_ROOT: - ImGui::Text(STRING_IMGUI_FRAME_PROPERTIES_ROOT); - break; - case ANM2_LAYER: - ImGui::Text(STRING_IMGUI_FRAME_PROPERTIES_LAYER); - break; - case ANM2_NULL: - ImGui::Text(STRING_IMGUI_FRAME_PROPERTIES_NULL); - break; - case ANM2_TRIGGERS: - ImGui::Text(STRING_IMGUI_FRAME_PROPERTIES_TRIGGER); - break; - default: - break; + eventIDs.push_back(id); + eventStrings.push_back(std::format(IMGUI_EVENT_FORMAT, id, event.name)); + eventLabels.push_back(eventStrings.back().c_str()); } - switch (self->reference->itemType) - { - case ANM2_ROOT: - case ANM2_NULL: - // Position - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_POSITION, value_ptr(frame->position), 1, 0, 0, "%.0f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION); + s32 selectedEventIndex = std::find(eventIDs.begin(), eventIDs.end(), frame->eventID) - eventIDs.begin(); - // Scale - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_SCALE, value_ptr(frame->scale), 1.0, 0, 0, "%.1f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE); + if (_imgui_item_combo(self, IMGUI_FRAME_PROPERTIES_EVENT, &selectedEventIndex, eventLabels.data(), (s32)eventLabels.size())) + frame->eventID = eventIDs[selectedEventIndex]; - // Rotation - ImGui::DragFloat(STRING_IMGUI_FRAME_PROPERTIES_ROTATION, &frame->rotation, 1, 0, 0, "%.1f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION); - - // Duration - ImGui::InputInt(STRING_IMGUI_FRAME_PROPERTIES_DURATION, &frame->delay); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION); - frame->delay = CLAMP(frame->delay, ANM2_FRAME_DELAY_MIN, animation->frameNum + 1); - - // Tint - ImGui::ColorEdit4(STRING_IMGUI_FRAME_PROPERTIES_TINT, value_ptr(frame->tintRGBA)); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT); - - // Color Offset - ImGui::ColorEdit3(STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, value_ptr(frame->offsetRGB)); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET); - - // Visible - ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_VISIBLE, &frame->isVisible); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE); - - ImGui::SameLine(); - - // Interpolation - ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED, &frame->isInterpolated); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED); - - // Flip X - if (ImGui::Button(STRING_IMGUI_FRAME_PROPERTIES_FLIP_X)) - frame->scale.x = -frame->scale.x; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_X); - - ImGui::SameLine(); - - // Flip Y - if (ImGui::Button(STRING_IMGUI_FRAME_PROPERTIES_FLIP_Y)) - frame->scale.y = -frame->scale.y; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_Y); - break; - case ANM2_LAYER: - // Position - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_POSITION, value_ptr(frame->position), 1, 0, 0, "%.0f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION); - - // Crop Position - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION, value_ptr(frame->crop), 1, 0, 0, "%.0f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION); - - // Crop - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_SIZE, value_ptr(frame->size), 1, 0, 0, "%.0f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE); - - // Pivot - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_PIVOT, value_ptr(frame->pivot), 1, 0, 0, "%.0f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT); - - // Scale - ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_SCALE, value_ptr(frame->scale), 1.0, 0, 0, "%.1f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE); - - // Rotation - ImGui::DragFloat(STRING_IMGUI_FRAME_PROPERTIES_ROTATION, &frame->rotation, 1, 0, 0, "%.1f"); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION); - - // Duration - ImGui::InputInt(STRING_IMGUI_FRAME_PROPERTIES_DURATION, &frame->delay); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION); - // clamp delay - frame->delay = CLAMP(frame->delay, ANM2_FRAME_DELAY_MIN, animation->frameNum + 1); - - // Tint - ImGui::ColorEdit4(STRING_IMGUI_FRAME_PROPERTIES_TINT, value_ptr(frame->tintRGBA)); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT); - - // Color Offset - ImGui::ColorEdit3(STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, value_ptr(frame->offsetRGB)); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET); - - // Flip X - if (ImGui::Button(STRING_IMGUI_FRAME_PROPERTIES_FLIP_X)) - frame->scale.x = -frame->scale.x; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_X); - - ImGui::SameLine(); - - // Flip Y - if (ImGui::Button(STRING_IMGUI_FRAME_PROPERTIES_FLIP_Y)) - frame->scale.y = -frame->scale.y; - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_FLIP_Y); - - // Visible - ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_VISIBLE, &frame->isVisible); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE); - - ImGui::SameLine(); - - // Interpolation - ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED, &frame->isInterpolated); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED); - break; - case ANM2_TRIGGERS: - // Events drop down; pick one! - for (auto & [id, event] : self->anm2->events) - { - eventIDs.push_back(id); - eventNames.push_back(event.name.c_str()); - if (id == frame->eventID) - selectedEventIndex = eventIDs.size() - 1; - } - - if (ImGui::Combo(STRING_IMGUI_FRAME_PROPERTIES_EVENT, &selectedEventIndex, eventNames.data(), eventNames.size())) - { - frame->eventID = eventIDs[selectedEventIndex]; - selectedEventIndex = -1; - } - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_EVENT); - - // At Frame - ImGui::InputInt(STRING_IMGUI_FRAME_PROPERTIES_AT_FRAME, &frame->atFrame); - _imgui_undoable(self); - _imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_AT_FRAME); - frame->atFrame = CLAMP(frame->atFrame, 0, animation->frameNum- 1); - break; - default: - break; - } - + _imgui_item_inputint(self, IMGUI_FRAME_PROPERTIES_AT_FRAME, &frame->atFrame); + frame->atFrame = CLAMP(frame->atFrame, 0, animation->frameNum - 1); } - ImGui::End(); + _imgui_item_end(); } -// Initializes imgui -void -imgui_init -( - Imgui* self, - Dialog* dialog, - Resources* resources, - Input* input, - Anm2* anm2, - Anm2Reference* reference, - f32* time, - Editor* editor, - Preview* preview, - Settings* settings, - Tool* tool, - Snapshots* snapshots, - SDL_Window* window, - SDL_GLContext* glContext -) +static void _imgui_persistent(Imgui* self) { - IMGUI_CHECKVERSION(); + if (self->preview->isRecording) + { + ImVec2 mousePos = ImGui::GetMousePos(); - self->dialog = dialog; - self->resources = resources; - self->input = input; - self->anm2 = anm2; - self->reference = reference; - self->time = time; - self->editor = editor; - self->preview = preview; - self->settings = settings; - self->tool = tool; - self->snapshots = snapshots; - self->window = window; - self->glContext = glContext; + ImGui::SetNextWindowPos(ImVec2(mousePos.x + IMGUI_TOOLTIP_OFFSET.x, mousePos.y + IMGUI_TOOLTIP_OFFSET.y)); + ImGui::BeginTooltip(); + _imgui_item_atlas_image_text(self, IMGUI_RECORDING); + ImGui::EndTooltip(); + } - ImGui::CreateContext(); - ImGui::StyleColorsDark(); + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) + ImGui::OpenPopup(IMGUI_CONTEXT_MENU); - ImGui_ImplSDL3_InitForOpenGL(self->window, *self->glContext); - ImGui_ImplOpenGL3_Init(STRING_OPENGL_VERSION); + if (ImGui::BeginPopup(IMGUI_CONTEXT_MENU)) + { + ImguiItem pasteItem = IMGUI_PASTE; + pasteItem.isInactive = self->clipboard->item.type == CLIPBOARD_NONE; - ImGuiIO& io = ImGui::GetIO(); - io.IniFilename = NULL; - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.ConfigWindowsMoveFromTitleBarOnly = true; + _imgui_item_selectable(self, IMGUI_CUT); + _imgui_item_selectable(self, IMGUI_COPY); + _imgui_item_selectable(self, pasteItem); - ImGui::LoadIniSettingsFromDisk(PATH_SETTINGS); - - std::cout << STRING_INFO_IMGUI_INIT << std::endl; + ImGui::EndPopup(); + } } -// Main dockspace -static void -_imgui_dock(Imgui* self) +static void _imgui_dock(Imgui* self) { ImGuiViewport* viewport = ImGui::GetMainViewport(); - - ImGuiWindowFlags dockspaceWindowFlags = 0 | - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoNavFocus; - 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::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + IMGUI_TASKBAR.size.y)); + ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, viewport->Size.y - IMGUI_TASKBAR.size.y)); ImGui::SetNextWindowViewport(viewport->ID); - ImGui::Begin(STRING_IMGUI_WINDOW, NULL, dockspaceWindowFlags); + _imgui_item_begin(IMGUI_WINDOW); - ImGui::DockSpace(ImGui::GetID(STRING_IMGUI_DOCKSPACE), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode); + _imgui_item_dockspace(IMGUI_DOCKSPACE); _imgui_tools(self); _imgui_animations(self); @@ -2149,38 +1830,113 @@ _imgui_dock(Imgui* self) ImGui::End(); } -// Ticks imgui -void -imgui_tick(Imgui* self) +void imgui_init +( + Imgui* self, + Dialog* dialog, + Resources* resources, + Anm2* anm2, + Anm2Reference* reference, + Editor* editor, + Preview* preview, + Settings* settings, + Snapshots* snapshots, + Clipboard* clipboard, + SDL_Window* window, + SDL_GLContext* glContext +) +{ + IMGUI_CHECKVERSION(); + + self->dialog = dialog; + self->resources = resources; + self->anm2 = anm2; + self->reference = reference; + self->editor = editor; + self->preview = preview; + self->settings = settings; + self->snapshots = snapshots; + self->clipboard = clipboard; + self->window = window; + self->glContext = glContext; + + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + + ImGui_ImplSDL3_InitForOpenGL(self->window, *self->glContext); + ImGui_ImplOpenGL3_Init(IMGUI_OPENGL_VERSION); + + ImGuiIO& io = ImGui::GetIO(); + io.IniFilename = nullptr; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigWindowsMoveFromTitleBarOnly = true; + + ImGui::LoadIniSettingsFromDisk(SETTINGS_PATH); +} + +void imgui_tick(Imgui* self) { ImGui_ImplSDL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame(); ImGui::NewFrame(); - self->tool->isEnabled = false; - _imgui_taskbar(self); _imgui_dock(self); + _imgui_persistent(self); + + self->isHotkeysEnabled = !self->isRename && !self->isChangeValue; + + if (self->isHotkeysEnabled) + { + for (const auto& hotkey : imgui_hotkey_registry()) + { + if (ImGui::IsKeyChordPressed(hotkey.chord)) + { + if (hotkey.is_focus_window() && (imgui_nav_window_root_get() != hotkey.focusWindow)) + continue; + + hotkey.function(self); + } + } + } + + SDL_Event event; + + while(SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + + switch (event.type) + { + case SDL_EVENT_QUIT: + if (ImGui::IsPopupOpen(IMGUI_EXIT_CONFIRMATION_POPUP)) + { + self->isQuit = true; + break; + } + ImGui::OpenPopup(IMGUI_EXIT_CONFIRMATION_POPUP); + break; + default: + break; + } + } + + if (_imgui_item_yes_no_popup(self, IMGUI_EXIT_CONFIRMATION)) + self->isQuit = true; } -// Draws imgui -void -imgui_draw(void) +void imgui_draw(void) { ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } -// Frees imgui -void -imgui_free(void) +void imgui_free(void) { ImGui_ImplSDL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown(); - // Save Ini manually - ImGui::SaveIniSettingsToDisk(PATH_SETTINGS); + ImGui::SaveIniSettingsToDisk(SETTINGS_PATH); ImGui::DestroyContext(); - - std::cout << STRING_INFO_IMGUI_FREE << std::endl; -} +} \ No newline at end of file diff --git a/src/imgui.h b/src/imgui.h index a14f01a..06d36ca 100644 --- a/src/imgui.h +++ b/src/imgui.h @@ -1,129 +1,1580 @@ #pragma once -#include "dialog.h" -#include "resources.h" -#include "editor.h" -#include "preview.h" -#include "window.h" -#include "input.h" -#include "settings.h" -#include "snapshots.h" -#include "tool.h" - #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM #define IMGUI_ENABLE_DOCKING +#define IM_VEC2_CLASS_EXTRA \ + inline bool operator==(const ImVec2& rhs) const { return x == rhs.x && y == rhs.y; } \ + inline bool operator!=(const ImVec2& rhs) const { return !(*this == rhs); } \ + inline ImVec2 operator+(const ImVec2& rhs) const { return ImVec2(x + rhs.x, y + rhs.y); } \ + inline ImVec2 operator-(const ImVec2& rhs) const { return ImVec2(x - rhs.x, y - rhs.y); } \ + inline ImVec2 operator*(const ImVec2& rhs) const { return ImVec2(x * rhs.x, y * rhs.y); } \ + +#define IM_VEC3_CLASS_EXTRA \ + inline bool operator==(const ImVec3& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } \ + inline bool operator!=(const ImVec3& rhs) const { return !(*this == rhs); } \ + inline ImVec3 operator+(const ImVec3& rhs) const { return ImVec3(x + rhs.x, y + rhs.y, z + rhs.z); } \ + inline ImVec3 operator-(const ImVec3& rhs) const { return ImVec3(x - rhs.x, y - rhs.y, z - rhs.z); } \ + inline ImVec3 operator*(const ImVec3& rhs) const { return ImVec3(x * rhs.x, y * rhs.y, z * rhs.z); } + +#define IM_VEC4_CLASS_EXTRA \ + inline bool operator==(const ImVec4& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; } \ + inline bool operator!=(const ImVec4& rhs) const { return !(*this == rhs); } \ + inline ImVec4 operator+(const ImVec4& rhs) const { return ImVec4(x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w); } \ + inline ImVec4 operator-(const ImVec4& rhs) const { return ImVec4(x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w); } \ + inline ImVec4 operator*(const ImVec4& rhs) const { return ImVec4(x * rhs.x, y * rhs.y, z * rhs.z, w * rhs.w); } + #include #include #include #include -#define IMGUI_TIMELINE_ELEMENT_WIDTH 300 +#include "clipboard.h" +#include "dialog.h" +#include "editor.h" +#include "preview.h" +#include "resources.h" +#include "settings.h" +#include "snapshots.h" +#include "window.h" -#define IMGUI_DRAG_SPEED 1.0 -#define IMGUI_TASKBAR_HEIGHT 32 -#define IMGUI_TIMELINE_OFFSET_Y 24 -#define IMGUI_TIMELINE_ANIMATION_LENGTH_WIDTH 200 -#define IMGUI_TIMELINE_FPS_WIDTH 100 -#define IMGUI_TIMELINE_CREATED_BY_WIDTH 150 -#define IMGUI_TIMELINE_FRAME_INDICES_MULTIPLE 5 -#define IMGUI_TIMELINE_FRAME_INDICES_STRING_MAX 16 -#define IMGUI_PICKER_LINE_SIZE 1.0f +#define IMGUI_ANIMATIONS_LABEL "Animations" +#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}" +#define IMGUI_CHORD_NONE (ImGuiMod_None) +#define IMGUI_CONTEXT_MENU "## Context Menu" +#define IMGUI_EVENTS_LABEL "Events" +#define IMGUI_EVENT_FORMAT "#{} {}" +#define IMGUI_EVENT_NONE "None" +#define IMGUI_FILE_POPUP "## File Popup" #define IMGUI_FRAME_BORDER 2.0f +#define IMGUI_FRAME_PROPERTIES_NO_FRAME "Select a frame to show properties..." +#define IMGUI_ITEM_SELECTABLE_EDITABLE_LABEL "## Editing" +#define IMGUI_OPENGL_VERSION "#version 330" #define IMGUI_PICKER_LINE_COLOR IM_COL32(255, 255, 255, 255) -#define IMGUI_TOOLS_WIDTH_INCREMENT -2 +#define IMGUI_PLAYBACK_POPUP "## Playback Popup" +#define IMGUI_EXIT_CONFIRMATION_POPUP "Exit Confirmation" +#define IMGUI_POSITION_FORMAT "Position: {{{:5}, {:5}}}" +#define IMGUI_SPRITESHEET_FORMAT "#{} {}" +#define IMGUI_TIMELINE_ADD_ELEMENT_POPUP "## Add Element Popup" +#define IMGUI_TIMELINE_CHILD_ID_LABEL "#{} {}" +#define IMGUI_TIMELINE_FOOTER_HEIGHT 20 +#define IMGUI_TIMELINE_FRAME_BORDER 2 +#define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}" +#define IMGUI_TIMELINE_FRAME_MULTIPLE 5 +#define IMGUI_TIMELINE_LABEL "Timeline" +#define IMGUI_MERGE_POPUP "Merge Animations" +#define IMGUI_TIMELINE_NO_ANIMATION "Select an animation to show timeline..." +#define IMGUI_TIMELINE_PICKER_LINE_WIDTH 2.0f +#define IMGUI_TIMELINE_SPRITESHEET_ID_FORMAT "#{}" +#define IMGUI_TOOL_STEP 1 +#define IMGUI_TOOL_STEP_MOD 10 +#define IMGUI_WIZARD_POPUP "## Wizard Popup" #define VEC2_TO_IMVEC2(value) ImVec2(value.x, value.y) #define IMVEC2_TO_VEC2(value) glm::vec2(value.x, value.y) #define IMVEC2_ATLAS_UV_GET(type) VEC2_TO_IMVEC2(ATLAS_UVS[type][0]), VEC2_TO_IMVEC2(ATLAS_UVS[type][1]) -const vec2 IMGUI_TASKBAR_MARGINS = {8, 4}; -const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1}; - -const ImVec2 IMGUI_RECORD_TOOLTIP_OFFSET = {16, 16}; -const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 105}; -const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85}; -const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135}; - -const ImVec2 IMGUI_SPRITESHEET_EDITOR_SETTINGS_CHILD_SIZE = {200, 85}; -const ImVec2 IMGUI_SPRITESHEET_EDITOR_SETTINGS_SIZE = {1280, 105}; - -const ImVec2 IMGUI_TIMELINE_ELEMENT_LIST_SIZE = {300, 0}; -const ImVec2 IMGUI_TIMELINE_FRAMES_SIZE = {0, 0}; -const ImVec2 IMGUI_TIMELINE_ELEMENT_FRAMES_SIZE = {0, 0}; const ImVec2 IMGUI_TIMELINE_FRAME_SIZE = {16, 40}; -const ImVec2 IMGUI_TIMELINE_VIEWER_SIZE = {0, 40}; -const ImVec2 IMGUI_TIMELINE_ELEMENTS_TIMELINE_SIZE = {0, 40}; -const ImVec2 IMGUI_TIMELINE_FRAME_INDICES_SIZE = {0, 40}; -const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {300, 40}; -const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {150, 20}; -const ImVec2 IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_SIZE = {60, 20}; +const ImVec2 IMGUI_TIMELINE_FRAME_CONTENT_OFFSET = {TEXTURE_SIZE_SMALL.x * 0.25f, (IMGUI_TIMELINE_FRAME_SIZE.y * 0.5f) - (TEXTURE_SIZE_SMALL.y * 0.5f)}; +const ImVec2 IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE = {150, 0}; +const ImVec2 IMGUI_TIMELINE_ITEM_SIZE = {300, 40}; -const ImVec2 IMGUI_SPRITESHEET_SIZE = {0, 150}; -const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {100, 100}; -const ImVec2 IMGUI_IMAGE_TARGET_SIZE = {125, 125}; -const ImVec2 IMGUI_ICON_BUTTON_SIZE = {24, 24}; -const ImVec2 IMGUI_DUMMY_SIZE = {1, 1}; +const ImVec4 IMGUI_TIMELINE_FRAME_COLOR = {0.0f, 0.0f, 0.0f, 0.125}; +const ImVec4 IMGUI_TIMELINE_FRAME_MULTIPLE_COLOR = {0.113, 0.184, 0.286, 0.125}; +const ImVec4 IMGUI_TIMELINE_HEADER_FRAME_COLOR = {0.113, 0.184, 0.286, 0.5}; +const ImVec4 IMGUI_TIMELINE_HEADER_FRAME_MULTIPLE_COLOR = {0.113, 0.184, 0.286, 1.0}; +const ImVec4 IMGUI_INACTIVE_COLOR = {0.5, 0.5, 0.5, 1.0}; +const ImVec4 IMGUI_ACTIVE_COLOR = {1.0, 1.0, 1.0, 1.0}; -const ImVec4 IMGUI_TIMELINE_HEADER_COLOR = {0.04, 0.04, 0.04, 1.0f}; -const ImVec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.5f}; -const ImVec4 IMGUI_FRAME_OVERLAY_COLOR = {0.0f, 0.0f, 0.0f, 0.25f}; -const ImVec4 IMGUI_FRAME_INDICES_OVERLAY_COLOR = {0.113, 0.184, 0.286, 1.0f}; -const ImVec4 IMGUI_FRAME_INDICES_COLOR = {0.113, 0.184, 0.286, 0.5f}; +const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {125.0, 125.0}; +const ImVec2 IMGUI_TOOLTIP_OFFSET = {16, 8}; +const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1}; +const ImVec2 IMGUI_CANVAS_CHILD_SIZE = {200, 85}; -const ImVec4 IMGUI_TIMELINE_ROOT_COLOR = {0.010, 0.049, 0.078, 1.0f}; -const ImVec4 IMGUI_TIMELINE_LAYER_COLOR = {0.098, 0.039, 0.020, 1.0f}; -const ImVec4 IMGUI_TIMELINE_NULL_COLOR = {0.020, 0.049, 0.000, 1.0f}; -const ImVec4 IMGUI_TIMELINE_TRIGGERS_COLOR = {0.078, 0.020, 0.029, 1.0f}; +const ImGuiKey IMGUI_INPUT_DELETE = ImGuiKey_Delete; +const ImGuiKey IMGUI_INPUT_LEFT = ImGuiKey_LeftArrow; +const ImGuiKey IMGUI_INPUT_RIGHT = ImGuiKey_RightArrow; +const ImGuiKey IMGUI_INPUT_UP = ImGuiKey_UpArrow; +const ImGuiKey IMGUI_INPUT_DOWN = ImGuiKey_DownArrow; +const ImGuiKey IMGUI_INPUT_MOD = ImGuiMod_Shift; +const ImGuiKey IMGUI_INPUT_ZOOM_IN = ImGuiKey_1; +const ImGuiKey IMGUI_INPUT_ZOOM_OUT = ImGuiKey_2; +const ImGuiKey IMGUI_INPUT_PLAY = ImGuiKey_Space; -const ImVec4 IMGUI_TIMELINE_ROOT_FRAME_COLOR = {0.020, 0.294, 0.569, 0.5}; -const ImVec4 IMGUI_TIMELINE_LAYER_FRAME_COLOR = {0.529, 0.157, 0.000, 0.5}; -const ImVec4 IMGUI_TIMELINE_NULL_FRAME_COLOR = {0.137, 0.353, 0.000, 0.5}; -const ImVec4 IMGUI_TIMELINE_TRIGGERS_FRAME_COLOR = {0.529, 0.118, 0.196, 0.5}; +inline const std::string IMGUI_FRAME_PROPERTIES_TITLE[ANM2_COUNT] = +{ + "", + "-- Root --", + "-- Layer --", + "-- Null --", + "-- Trigger --", +}; -const ImVec4 IMGUI_TIMELINE_ROOT_HIGHLIGHT_COLOR = {0.314, 0.588, 0.843, 0.75}; -const ImVec4 IMGUI_TIMELINE_LAYER_HIGHLIGHT_COLOR = {0.882, 0.412, 0.216, 0.75}; -const ImVec4 IMGUI_TIMELINE_NULL_HIGHLIGHT_COLOR = {0.431, 0.647, 0.294, 0.75}; -const ImVec4 IMGUI_TIMELINE_TRIGGERS_HIGHLIGHT_COLOR = {0.804, 0.412, 0.490, 0.75}; +static inline ImGuiKey imgui_key_get(char c) +{ + if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; + if (c >= 'A' && c <= 'Z') return static_cast(ImGuiKey_A + (c - 'A')); + return ImGuiKey_None; +} +enum ToolType +{ + TOOL_PAN, + TOOL_MOVE, + TOOL_ROTATE, + TOOL_SCALE, + TOOL_CROP, + TOOL_DRAW, + TOOL_ERASE, + TOOL_COLOR_PICKER, + TOOL_UNDO, + TOOL_REDO, + TOOL_COLOR, + TOOL_COUNT, +}; -const ImVec4 IMGUI_TIMELINE_ROOT_ACTIVE_COLOR = {0.471, 0.882, 1.000, 0.75}; -const ImVec4 IMGUI_TIMELINE_LAYER_ACTIVE_COLOR = {1.000, 0.618, 0.324, 0.75}; -const ImVec4 IMGUI_TIMELINE_NULL_ACTIVE_COLOR = {0.646, 0.971, 0.441, 0.75}; -const ImVec4 IMGUI_TIMELINE_TRIGGERS_ACTIVE_COLOR = {1.000, 0.618, 0.735, 0.75}; +#define SDL_MOUSE_CURSOR_NULL (-1) +const SDL_SystemCursor IMGUI_TOOL_MOUSE_CURSORS[TOOL_COUNT] = +{ + SDL_SYSTEM_CURSOR_POINTER, + SDL_SYSTEM_CURSOR_MOVE, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_NE_RESIZE, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_DEFAULT, + SDL_SYSTEM_CURSOR_DEFAULT +}; struct Imgui { - Dialog* dialog = NULL; - Resources* resources = NULL; - Input* input = NULL; - Anm2* anm2 = NULL; - Anm2Reference* reference = NULL; - f32* time = NULL; - Editor* editor = NULL; - Preview* preview = NULL; - Settings* settings = NULL; - Tool* tool = NULL; - Snapshots* snapshots = NULL; - SDL_Window* window = NULL; - SDL_GLContext* glContext = NULL; - bool isSwap = false; - Anm2Reference swapReference; + Dialog* dialog = nullptr; + Resources* resources = nullptr; + Anm2* anm2 = nullptr; + Anm2Reference* reference = nullptr; + Editor* editor = nullptr; + Preview* preview = nullptr; + Settings* settings = nullptr; + Snapshots* snapshots = nullptr; + Clipboard* clipboard = nullptr; + SDL_Window* window = nullptr; + SDL_GLContext* glContext = nullptr; + ToolType tool = TOOL_PAN; + bool isHotkeysEnabled = true; + bool isRename = false; + bool isChangeValue = false; + bool isQuit = false; }; -void -imgui_init +typedef void(*ImguiFunction)(Imgui*); + +struct ImguiHotkey +{ + ImGuiKeyChord chord; + ImguiFunction function; + std::string focusWindow{}; + + bool is_focus_window() const { return !focusWindow.empty(); } +}; + +static std::vector& imgui_hotkey_registry() +{ + static std::vector registry; + return registry; +} + +enum PopupType +{ + POPUP_BY_ITEM, + POPUP_CENTER_SCREEN +}; + +struct ImguiColorSet +{ + ImVec4 normal{}; + ImVec4 active{}; + ImVec4 hovered{}; + + bool is_normal() const { return normal != ImVec4(); } + bool is_active() const { return active != ImVec4(); } + bool is_hovered() const { return hovered != ImVec4(); } +}; + +struct ImguiItemBuilder +{ + std::string label{}; + std::string tooltip{}; + std::string popup{}; + std::string dragDrop{}; + std::string format{}; + std::string focusWindow{}; + ImguiFunction function = nullptr; + ImGuiKeyChord chord = IMGUI_CHORD_NONE; + TextureType texture = TEXTURE_NONE; + PopupType popupType = POPUP_BY_ITEM; + bool isUndoable = false; + bool isSizeToText = true; + ImguiColorSet color{}; + ImVec2 size{}; + ImVec2 contentOffset{}; + f64 speed{}; + f64 min{}; + f64 max{}; + s32 border{}; + s32 step = 1; + s32 stepFast = 1; + s32 value = ID_NONE; + s32 id = ID_NONE; + s32 flags{}; + s32 flagsAlt{}; +}; + +struct ImguiItem +{ + std::string label{}; + std::string tooltip{}; + std::string popup{}; + std::string dragDrop{}; + std::string format{}; + std::string focusWindow{}; + ImguiFunction function = nullptr; + ImGuiKeyChord chord = IMGUI_CHORD_NONE; + TextureType texture = TEXTURE_NONE; + PopupType popupType = POPUP_BY_ITEM; + bool isUndoable = false; + bool isInactive = false; + bool isSizeToText = true; + bool isSelected = false; + f64 speed{}; + f64 min{}; + f64 max{}; + s32 border{}; + s32 step = 1; + s32 stepFast = 1; + s32 value = ID_NONE; + s32 id{}; + s32 flags{}; + s32 flagsAlt{}; + ImguiColorSet color{}; + ImVec2 size{}; + ImVec2 contentOffset{}; + ImGuiKey mnemonicKey = ImGuiKey_None; + s32 mnemonicIndex = -1; + + bool is_popup() const { return !popup.empty(); } + bool is_tooltip() const { return !tooltip.empty(); } + bool is_size() const { return size != ImVec2(); } + bool is_border() const { return border != 0; } + bool is_mnemonic() const { return mnemonicIndex != -1 && mnemonicKey != ImGuiKey_None; } + bool is_chord() const { return chord != IMGUI_CHORD_NONE;} + bool is_focus_window() const { return !focusWindow.empty(); } + + ImguiItem(const ImguiItemBuilder& builder) + { + static s32 newID = 0; + + tooltip = builder.tooltip; + popup = builder.popup; + dragDrop = builder.dragDrop; + format = builder.format; + focusWindow = builder.focusWindow; + function = builder.function; + chord = builder.chord; + popupType = builder.popupType; + texture = builder.texture; + isUndoable = builder.isUndoable; + isSizeToText = builder.isSizeToText; + color = builder.color; + size = builder.size; + contentOffset = builder.contentOffset; + speed = builder.speed; + min = builder.min; + max = builder.max; + border = builder.border; + step = builder.step; + stepFast = builder.stepFast; + value = builder.value; + flags = builder.flags; + flagsAlt = builder.flagsAlt; + + id = newID; + newID++; + + if (is_chord() && function) + imgui_hotkey_registry().push_back({chord, function, focusWindow}); + + mnemonicIndex = -1; + + for (s32 i = 0; i < (s32)builder.label.size(); i++) + { + if (builder.label[i] == '&') + { + if (builder.label[i + 1] == '&') + { + label += '&'; + i++; + } + else if (builder.label[i + 1] != '\0') + { + mnemonicKey = imgui_key_get(builder.label[i + 1]); + mnemonicIndex = (s32)label.size(); + label += builder.label[i + 1]; + i++; + } + } + else + label += builder.label[i]; + } + } +}; + +static inline std::string_view imgui_nav_window_root_get() +{ + ImGuiWindow* navWindow = ImGui::GetCurrentContext()->NavWindow; + if (!navWindow) + return {}; + + std::string_view name(navWindow->Name); + size_t slash = name.find('/'); + return (slash == std::string_view::npos) ? name : name.substr(0, slash); +} + +static inline void imgui_file_new(Imgui* self) +{ + anm2_reference_clear(self->reference); + anm2_new(self->anm2); +} + +static inline void imgui_file_open(Imgui* self) +{ + dialog_anm2_open(self->dialog); +} + +static inline void imgui_file_save(Imgui* self) +{ + if (self->anm2->path.empty()) + dialog_anm2_save(self->dialog); + else + anm2_serialize(self->anm2, self->anm2->path); +} + +static inline void imgui_file_save_as(Imgui* self) +{ + dialog_anm2_save(self->dialog); +} + +static inline void imgui_png_open(Imgui* self) +{ + dialog_png_open(self->dialog); +} + +static inline void imgui_generate_gif_animation(Imgui* self) +{ + if (anm2_animation_from_reference(self->anm2, self->reference)) + { + self->preview->isRecording = true; + self->preview->time = 0; + } +} + +static inline void imgui_undo_stack_push(Imgui* self) +{ + Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time}; + snapshots_undo_stack_push(self->snapshots, &snapshot); +} + +static inline void imgui_tool_pan_set(Imgui* self) +{ + self->tool = TOOL_PAN; +} + +static inline void imgui_tool_move_set(Imgui* self) +{ + self->tool = TOOL_MOVE; +} + +static inline void imgui_tool_rotate_set(Imgui* self) +{ + self->tool = TOOL_ROTATE; +} + +static inline void imgui_tool_scale_set(Imgui* self) +{ + self->tool = TOOL_SCALE; +} + +static inline void imgui_tool_crop_set(Imgui* self) +{ + self->tool = TOOL_CROP; +} + +static inline void imgui_tool_draw_set(Imgui* self) +{ + self->tool = TOOL_DRAW; +} + +static inline void imgui_tool_erase_set(Imgui* self) +{ + self->tool = TOOL_ERASE; +} + +static inline void imgui_tool_color_picker_set(Imgui* self) +{ + self->tool = TOOL_COLOR_PICKER; +} + +static inline void imgui_undo(Imgui* self) +{ + snapshots_undo(self->snapshots); +} + +static inline void imgui_redo(Imgui* self) +{ + snapshots_redo(self->snapshots); +} + +static inline void imgui_cut(Imgui* self) +{ + clipboard_cut(self->clipboard); +} + +static inline void imgui_copy(Imgui* self) +{ + clipboard_copy(self->clipboard); +} + +static inline void imgui_paste(Imgui* self) +{ + clipboard_paste(self->clipboard); +} + +const inline ImguiItem IMGUI_WINDOW = ImguiItem +({ + .label = "## Window", + .flags = ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus +}); + +const inline ImguiItem IMGUI_DOCKSPACE = ImguiItem +({ + .label = "## Dockspace", + .flags = ImGuiDockNodeFlags_PassthruCentralNode +}); + +const inline ImguiItem IMGUI_TASKBAR = ImguiItem +({ + .label = "## Taskbar", + .size = {0, 32}, + .flags = ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse | + ImGuiWindowFlags_NoSavedSettings +}); + +const inline ImguiItem IMGUI_TASKBAR_FILE = ImguiItem +({ + .label = "&File", + .tooltip = "Opens the file menu, for reading/writing anm2 files.", + .popup = IMGUI_FILE_POPUP, + .isSizeToText = true +}); + +const inline ImguiItem IMGUI_FILE_NEW = ImguiItem +({ + .label = "&New", + .tooltip = "Load a blank .anm2 file to edit.", + .function = imgui_file_new, + .chord = ImGuiMod_Ctrl | ImGuiKey_N +}); + +const inline ImguiItem IMGUI_FILE_OPEN = ImguiItem +({ + .label = "&Open", + .tooltip = "Open an existing .anm2 file to edit.", + .function = imgui_file_open, + .chord = ImGuiMod_Ctrl | ImGuiKey_O +}); + +const inline ImguiItem IMGUI_FILE_SAVE = ImguiItem +({ + .label = "&Save", + .tooltip = "Saves the current .anm2 file to its path.\nIf no path exists, one can be chosen.", + .function = imgui_file_save, + .chord = ImGuiMod_Ctrl | ImGuiKey_S +}); + +const inline ImguiItem IMGUI_FILE_SAVE_AS = ImguiItem +({ + .label = "S&ave As", + .tooltip = "Saves the current .anm2 file to a chosen path.", + .function = imgui_file_save_as, + .chord = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_S +}); + +const inline ImguiItem IMGUI_TASKBAR_PLAYBACK = ImguiItem +({ + .label = "&Playback", + .tooltip = "Opens the playback menu, for configuring playback settings.", + .popup = IMGUI_PLAYBACK_POPUP, + .isSizeToText = true +}); + +const inline ImguiItem IMGUI_PLAYBACK_ALWAYS_LOOP = ImguiItem +({ + .label = "&Always Loop", + .tooltip = "Sets the animation playback to always loop, regardless of the animation's loop setting." +}); + +const inline ImguiItem IMGUI_TASKBAR_WIZARD = ImguiItem +({ + .label = "&Wizard", + .tooltip = "Opens the wizard menu, for neat functions related to the .anm2.", + .popup = IMGUI_WIZARD_POPUP, + .isSizeToText = true +}); + +const inline ImguiItem IMGUI_WIZARD_RECORD_GIF_ANIMATION = ImguiItem +({ + .label = "G&enerate GIF Animation", + .tooltip = "Generates a GIF animation from the current animation.", + .function = imgui_generate_gif_animation +}); + +const inline ImguiItem IMGUI_ANIMATIONS = ImguiItem({IMGUI_ANIMATIONS_LABEL}); + +const inline ImguiItem IMGUI_ANIMATION = ImguiItem +({ + .label = "## Animation Item", + .dragDrop = "## Animation Drag Drop", + .texture = TEXTURE_ANIMATION, + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_ANIMATION_ADD = ImguiItem +({ + .label = "Add", + .tooltip = "Adds a new animation.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_ANIMATION_DUPLICATE = ImguiItem +({ + .label = "Duplicate", + .tooltip = "Duplicates the selected animation, placing it after.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_ANIMATION_MERGE = ImguiItem +({ + .label = "Merge", + .tooltip = "Open the animation merge popup, to merge animations together.", + .popup = IMGUI_MERGE_POPUP, + .popupType = POPUP_CENTER_SCREEN, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_MERGE_ANIMATIONS_CHILD = ImguiItem +({ + .label = "## Merge Animations", + .size = {300, 250}, + .flags = true +}); + +const inline ImguiItem IMGUI_MERGE_ON_CONFLICT_CHILD = ImguiItem +({ + .label = "## Merge On Conflict", + .size = {300, 75}, + .flags = true +}); + +const inline ImguiItem IMGUI_MERGE_ON_CONFLICT = ImguiItem({"On Conflict"}); + +const inline ImguiItem IMGUI_MERGE_APPEND_FRAMES = ImguiItem +({ + .label = "Append Frames ", + .tooltip = "On frame conflict, the merged animation will have the selected animations' frames appended, in top-to-bottom order.", + .value = 0 +}); + +const inline ImguiItem IMGUI_MERGE_REPLACE_FRAMES = ImguiItem +({ + .label = "Replace Frames", + .tooltip = "On frame conflict, the merged animation will have the latest selected animations' frames.", + .value = 1 +}); + +const inline ImguiItem IMGUI_MERGE_PREPEND_FRAMES = ImguiItem +({ + .label = "Prepend Frames", + .tooltip = "On frame conflict, the merged animation will have the selected animations' frames prepend, in top-to-bottom order.", + .value = 2 +}); + +const inline ImguiItem IMGUI_MERGE_IGNORE = ImguiItem +({ + .label = "Ignore ", + .tooltip = "On frame conflict, the merged animation will ignore the other selected animations' frames.", + .value = 3 +}); + +const inline ImguiItem IMGUI_MERGE_OPTIONS_CHILD = ImguiItem +({ + .label = "## Merge Options", + .size = {300, 35}, + .flags = true +}); + +const inline ImguiItem IMGUI_MERGE_DELETE_ANIMATIONS_AFTER = ImguiItem +({ + .label = "Delete Animations After Merging", + .tooltip = "After merging, the selected animations (besides the original) will be deleted." +}); + +const inline ImguiItem IMGUI_MERGE_CONFIRM = ImguiItem +({ + .label = "Merge", + .tooltip = "Merge the selected animations with the options set.", + .size = {150, 25}, +}); + +const inline ImguiItem IMGUI_MERGE_CANCEL = ImguiItem +({ + .label = "Cancel", + .tooltip = "Cancel merging.", + .size = {150, 25} +}); + +const inline ImguiItem IMGUI_ANIMATION_DEFAULT = ImguiItem +({ + .label = "Default", + .tooltip = "Sets the selected animation to be the default.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_ANIMATION_REMOVE = ImguiItem +({ + .label = "Remove", + .tooltip = "Removes the selected animation.", + .focusWindow = IMGUI_ANIMATIONS_LABEL, + .chord = ImGuiKey_Delete, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_EVENTS = ImguiItem({IMGUI_EVENTS_LABEL}); + +const inline ImguiItem IMGUI_EVENT = ImguiItem +({ + .label = "## Event Item", + .dragDrop = "## Event Drag Drop", + .texture = TEXTURE_EVENT, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_EVENT_ADD = ImguiItem +({ + .label = "Add", + .tooltip = "Adds a new event.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_EVENT_REMOVE = ImguiItem +({ + .label = "Remove", + .tooltip = "Remove the selected event.", + .focusWindow = IMGUI_EVENTS_LABEL, + .chord = ImGuiKey_Delete, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_SPRITESHEETS = ImguiItem({"Spritesheets"}); + +const inline ImguiItem IMGUI_SPRITESHEET_CHILD = ImguiItem +({ + .label = "## Spritesheet", + .size = {0, 175}, + .flags = true +}); + +const inline ImguiItem IMGUI_SPRITESHEET_SELECTABLE = ImguiItem +({ + .label = "## Spritesheet Selectable", + .dragDrop = "## Spritesheet Drag Drop", + .texture = TEXTURE_SPRITESHEET, + .flags = true +}); + +const inline ImguiItem IMGUI_SPRITESHEET_ADD = ImguiItem +({ + .label = "Add", + .tooltip = "Select an image to add as a spritesheet.", + .function = imgui_png_open +}); + +const inline ImguiItem IMGUI_SPRITESHEET_REMOVE = ImguiItem +({ + .label = "Remove", + .tooltip = "Remove the selected spritesheet." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_RELOAD = ImguiItem +({ + .label = "Reload", + .tooltip = "Reload the selected spritesheet." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_REPLACE = ImguiItem +({ + .label = "Replace", + .tooltip = "Replace the selected spritesheet with another." +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW = ImguiItem +({ + .label = "Animation Preview", + .flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS = ImguiItem +({ + .label = "## Grid Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_GRID = ImguiItem +({ + .label = "Grid", + .tooltip = "Toggles the visiblity of a grid over the animation preview." +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_GRID_COLOR = ImguiItem +({ + .label = "Color", + .tooltip = "Change the color of the animation preview grid.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_GRID_SIZE = ImguiItem +({ + .label = "Size", + .tooltip = "Change the size of the animation preview grid, in pixels.", + .min = CANVAS_GRID_MIN, + .max = CANVAS_GRID_MAX +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_GRID_OFFSET = ImguiItem +({ + .label = "Offset", + .tooltip = "Change the offset of the animation preview grid, in pixels." +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS = ImguiItem +({ + .label = "## View Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_ZOOM = ImguiItem +({ + .label = "Zoom", + .tooltip = "Change the animation preview's zoom.", + .format = "%.0f", + .min = CANVAS_ZOOM_MIN, + .max = CANVAS_ZOOM_MAX +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_CENTER_VIEW = ImguiItem +({ + .label = "Center View", + .tooltip = "Centers the current view.", + .size = {-FLT_MIN, 0} +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS = ImguiItem +({ + .label = "## Background Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR = ImguiItem +({ + .label = "Background Color", + .tooltip = "Change the background color of the animation preview.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_HELPER_SETTINGS = ImguiItem +({ + .label = "## Helper Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_AXIS = ImguiItem +({ + .label = "Axis", + .tooltip = "Toggle the display of the X/Y axes on the animation preview." +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_AXIS_COLOR = ImguiItem +({ + .label = "Color", + .tooltip = "Change the color of the animation preview's axes.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_ROOT_TRANSFORM = ImguiItem +({ + .label = "Root Transform", + .tooltip = "Toggles the root frames's attributes transforming the other items in an animation." +}); + +const inline ImguiItem IMGUI_ANIMATION_PREVIEW_SHOW_PIVOT = ImguiItem +({ + .label = "Show Pivot", + .tooltip = "Toggles the appearance of an icon for each animation item's pivot." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR = ImguiItem +({ + .label = "Spritesheet Editor", + .flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID_SETTINGS = ImguiItem +({ + .label = "## Grid Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID = ImguiItem +({ + .label = "Grid", + .tooltip = "Toggles the visiblity of a grid over the spritesheet editor." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID_SNAP = ImguiItem +({ + .label = "Snap", + .tooltip = "Using the crop tool will snap the points to the nearest grid point." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID_COLOR = ImguiItem +({ + .label = "Color", + .tooltip = "Change the color of the spritesheet editor grid.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID_SIZE = ImguiItem +({ + .label = "Size", + .tooltip = "Change the size of the spritesheet editor grid, in pixels.", + .min = EDITOR_GRID_MIN, + .max = EDITOR_GRID_MAX +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_GRID_OFFSET = ImguiItem +({ + .label = "Offset", + .tooltip = "Change the offset of the spritesheet editor grid, in pixels." +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_VIEW_SETTINGS = ImguiItem +({ + .label = "## View Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_ZOOM = ImguiItem +({ + .label = "Zoom", + .tooltip = "Change the spritesheet editor's zoom.", + .format = "%.0f", + .min = CANVAS_ZOOM_MIN, + .max = CANVAS_ZOOM_MAX +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW = ImguiItem +({ + .label = "Center View", + .tooltip = "Centers the current view.", + .size = {-FLT_MIN, 0}, +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_BACKGROUND_SETTINGS = ImguiItem +({ + .label = "## Background Settings", + .size = IMGUI_CANVAS_CHILD_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_BACKGROUND_COLOR = ImguiItem +({ + .label = "Background Color", + .tooltip = "Change the background color of the spritesheet editor.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem IMGUI_SPRITESHEET_EDITOR_BORDER = ImguiItem +({ + .label = "Border", + .tooltip = "Toggle a border appearing around the selected spritesheet." +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES = ImguiItem({"Frame Properties"}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_POSITION = ImguiItem +({ + .label = "Position", + .tooltip = "Change the position of the selected frame.", + .format = "%.0f", + .isUndoable = true, + .speed = 0.25f, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_CROP = ImguiItem +({ + .label = "Crop", + .tooltip = "Change the crop position of the selected frame.", + .format = "%.0f", + .isUndoable = true, + .speed = 0.25f, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_SIZE = ImguiItem +({ + .label = "Size", + .tooltip = "Change the size of the crop of the selected frame.", + .format = "%.0f", + .isUndoable = true, + .speed = 0.25f +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_PIVOT = ImguiItem +({ + .label = "Pivot", + .tooltip = "Change the pivot of the selected frame.", + .format = "%.0f", + .isUndoable = true, + .speed = 0.25f +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_SCALE = ImguiItem +({ + .label = "Scale", + .tooltip = "Change the scale of the selected frame.", + .format = "%.1f", + .isUndoable = true, + .speed = 0.25f +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_ROTATION = ImguiItem +({ + .label = "Rotation", + .tooltip = "Change the rotation of the selected frame.", + .format = "%.1f", + .isUndoable = true, + .speed = 0.25f +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_DURATION = ImguiItem +({ + .label = "Duration", + .tooltip = "Change the duration of the selected frame.", + .isUndoable = true, + .min = 1, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_TINT = ImguiItem +({ + .label = "Tint", + .tooltip = "Change the tint of the selected frame.", + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_COLOR_OFFSET = ImguiItem +({ + .label = "Color Offset", + .tooltip = "Change the color offset of the selected frame.", + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_VISIBLE = ImguiItem +({ + .label = "Visible", + .tooltip = "Toggles the visibility of the selected frame.", + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_INTERPOLATED = ImguiItem +({ + .label = "Interpolation", + .tooltip = "Toggles the interpolation of the selected frame.", + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_FLIP_X = ImguiItem +({ + .label = "Flip X", + .tooltip = "Change the sign of the X scale, to cheat flipping the layer horizontally.\n(Anm2 doesn't support flipping directly)", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_FLIP_Y = ImguiItem +({ + .label = "Flip Y", + .tooltip = "Change the sign of the Y scale, to cheat flipping the layer vertically.\n(Anm2 doesn't support flipping directly)", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_EVENT = ImguiItem +({ + .label = "Event", + .tooltip = "Change the event the trigger uses.\nNOTE: This sets the event ID, not the event. If the events change IDs, then this will need to be changed.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_FRAME_PROPERTIES_AT_FRAME = ImguiItem +({ + .label = "At Frame", + .tooltip = "Change the frame where the trigger occurs.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TOOLS = ImguiItem({"Tools"}); + +const inline ImguiItem IMGUI_TOOL_PAN = ImguiItem +({ + .label = "## Pan", + .tooltip = "Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou can also use the middle mouse button to pan at any time.", + .function = imgui_tool_pan_set, + .chord = ImGuiKey_P, + .texture = TEXTURE_PAN, +}); + +const inline ImguiItem IMGUI_TOOL_MOVE = ImguiItem +({ + .label = "## Move", + .tooltip = "Use the move tool.\nWill move the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)", + .function = imgui_tool_move_set, + .chord = ImGuiKey_M, + .texture = TEXTURE_MOVE, +}); + +const inline ImguiItem IMGUI_TOOL_ROTATE = ImguiItem +({ + .label = "## Rotate", + .tooltip = "Use the rotate tool.\nWill rotate the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)", + .function = imgui_tool_rotate_set, + .chord = ImGuiKey_R, + .texture = TEXTURE_ROTATE, +}); + +const inline ImguiItem IMGUI_TOOL_SCALE = ImguiItem +({ + .label = "## Scale", + .tooltip = "Use the scale tool.\nWill scale the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)", + .function = imgui_tool_scale_set, + .chord = ImGuiKey_S, + .texture = TEXTURE_SCALE, +}); + +const inline ImguiItem IMGUI_TOOL_CROP = ImguiItem +({ + .label = "## Crop", + .tooltip = "Use the crop tool.\nWill produce a crop rectangle based on how the cursor is dragged.\n(Spritesheet Editor only.)", + .function = imgui_tool_crop_set, + .chord = ImGuiKey_C, + .texture = TEXTURE_CROP, +}); + +const inline ImguiItem IMGUI_TOOL_DRAW = ImguiItem +({ + .label = "## Draw", + .tooltip = "Draws pixels onto the selected spritesheet, with the current color.\n(Spritesheet Editor only.)", + .function = imgui_tool_draw_set, + .chord = ImGuiKey_B, + .texture = TEXTURE_DRAW, +}); + +const inline ImguiItem IMGUI_TOOL_ERASE = ImguiItem +({ + .label = "## Erase", + .tooltip = "Erases pixels from the selected spritesheet.\n(Spritesheet Editor only.)", + .function = imgui_tool_erase_set, + .chord = ImGuiKey_E, + .texture = TEXTURE_ERASE, +}); + +const inline ImguiItem IMGUI_TOOL_COLOR_PICKER = ImguiItem +({ + .label = "## Color Picker", + .tooltip = "Selects a color from anywhere on the screen, to be used for drawing.", + .function = imgui_tool_color_picker_set, + .chord = ImGuiKey_W, + .texture = TEXTURE_COLOR_PICKER, +}); + +const inline ImguiItem IMGUI_TOOL_UNDO = ImguiItem +({ + .label = "## Undo", + .tooltip = "Undoes the last action.", + .function = imgui_undo, + .chord = ImGuiKey_Z, + .texture = TEXTURE_UNDO +}); + +const inline ImguiItem IMGUI_TOOL_REDO = ImguiItem +({ + .label = "## Redo", + .tooltip = "Redoes the last action.", + .function = imgui_redo, + .chord = ImGuiMod_Shift + ImGuiKey_Z, + .texture = TEXTURE_REDO +}); + +const inline ImguiItem IMGUI_TOOL_COLOR = ImguiItem +({ + .label = "## Color", + .tooltip = "Set the color, to be used by the draw tool.", + .flags = ImGuiColorEditFlags_NoInputs +}); + +const inline ImguiItem* IMGUI_TOOL_ITEMS[TOOL_COUNT] = +{ + &IMGUI_TOOL_PAN, + &IMGUI_TOOL_MOVE, + &IMGUI_TOOL_ROTATE, + &IMGUI_TOOL_SCALE, + &IMGUI_TOOL_CROP, + &IMGUI_TOOL_DRAW, + &IMGUI_TOOL_ERASE, + &IMGUI_TOOL_COLOR_PICKER, + &IMGUI_TOOL_UNDO, + &IMGUI_TOOL_REDO, + &IMGUI_TOOL_COLOR, +}; + +const inline ImguiItem IMGUI_TIMELINE = ImguiItem +({ + .label = IMGUI_TIMELINE_LABEL, + .flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_TIMELINE_CHILD = ImguiItem +({ + .label = "## Timeline Child", + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_HEADER = ImguiItem +({ + .label = "## Timeline Header", + .size = {0, IMGUI_TIMELINE_FRAME_SIZE.y}, + .flagsAlt = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEMS_CHILD = ImguiItem +({ + .label = "## Timeline Items", + .size = {IMGUI_TIMELINE_ITEM_SIZE.x, 0}, + .flagsAlt = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_FRAMES_CHILD = ImguiItem +({ + .label = "## Timeline Item Frames", + .size = {0, IMGUI_TIMELINE_FRAME_SIZE.y}, + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_FRAMES_CHILD = ImguiItem +({ + .label = "## Timeline Frames", + .size = {-1, -1}, + .flagsAlt = ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoScrollWithMouse +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM = ImguiItem +({ + .label = "## Timeline Item", + .size = IMGUI_TIMELINE_ITEM_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_ROOT = ImguiItem +({ + .label = "## Root Item", + .color = {{0.010, 0.049, 0.078, 1.0f}}, + .size = IMGUI_TIMELINE_ITEM_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_LAYER = ImguiItem +({ + .label = "## Layer Item", + .color = {{0.098, 0.039, 0.020, 1.0f}}, + .size = IMGUI_TIMELINE_ITEM_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_NULL = ImguiItem +({ + .label = "## Null Item", + .color = {{0.020, 0.049, 0.000, 1.0f}}, + .size = IMGUI_TIMELINE_ITEM_SIZE, + .flags = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_TRIGGERS = ImguiItem +({ + .label = "## Triggers Item", + .color = {{0.078, 0.020, 0.029, 1.0f}}, + .size = IMGUI_TIMELINE_ITEM_SIZE, + .flags = true +}); + +const inline ImguiItem* IMGUI_TIMELINE_ITEMS[ANM2_COUNT] +{ + &IMGUI_TIMELINE_ITEM, + &IMGUI_TIMELINE_ITEM_ROOT, + &IMGUI_TIMELINE_ITEM_LAYER, + &IMGUI_TIMELINE_ITEM_NULL, + &IMGUI_TIMELINE_ITEM_TRIGGERS +}; + +const inline ImguiItem IMGUI_TIMELINE_ITEM_SELECTABLE = ImguiItem +({ + .label = "## Selectable", + .size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE = ImguiItem +({ + .label = "Root", + .tooltip = "The root item of an animation.\nChanging its properties will transform the rest of the animation.", + .texture = TEXTURE_ROOT, + .size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE = ImguiItem +({ + .label = "## Layer Selectable", + .tooltip = "A layer item.\nA graphical item within the animation.", + .dragDrop = "## Layer Drag Drop", + .texture = TEXTURE_LAYER, + .size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_NULL_SELECTABLE = ImguiItem +({ + .label = "## Null Selectable", + .tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.", + .dragDrop = "## Null Drag Drop", + .texture = TEXTURE_NULL, + .size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE = ImguiItem +({ + .label = "Triggers", + .tooltip = "The animation's triggers.\nWill fire based on an event.", + .texture = TEXTURE_TRIGGERS, + .size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE +}); + +const inline ImguiItem* IMGUI_TIMELINE_ITEM_SELECTABLES[ANM2_COUNT] +{ + &IMGUI_TIMELINE_ITEM_SELECTABLE, + &IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE, + &IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE, + &IMGUI_TIMELINE_ITEM_NULL_SELECTABLE, + &IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE +}; + +const inline ImguiItem IMGUI_TIMELINE_ITEM_VISIBLE = ImguiItem +({ + .label = "## Visible", + .tooltip = "The item is visible.\nPress to set to invisible.", + .texture = TEXTURE_VISIBLE, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_INVISIBLE = ImguiItem +({ + .label = "## Invisible", + .tooltip = "The item is invisible.\nPress to set to visible.", + .texture = TEXTURE_INVISIBLE, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_SHOW_RECT = ImguiItem +({ + .label = "## Show Rect", + .tooltip = "The rect is shown.\nPress to hide rect.", + .texture = TEXTURE_SHOW_RECT, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ITEM_HIDE_RECT = ImguiItem +({ + .label = "## Invisible", + .tooltip = "The rect is hidden.\nPress to show rect.", + .texture = TEXTURE_HIDE_RECT, + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_SPRITESHEET_ID = ImguiItem +({ + .label = "## Spritesheet ID", + .tooltip = "Change the spritesheet ID this item uses.", + .texture = TEXTURE_SPRITESHEET, + .isUndoable = true, + .size = {32, 0}, +}); + +const inline ImguiItem IMGUI_TIMELINE_FRAME = ImguiItem +({ + .label = "## Frame", + .size = IMGUI_TIMELINE_FRAME_SIZE, + .contentOffset = IMGUI_TIMELINE_FRAME_CONTENT_OFFSET, + .border = IMGUI_TIMELINE_FRAME_BORDER, +}); + +const inline ImguiItem IMGUI_TIMELINE_ROOT_FRAME = ImguiItem +({ + .label = "## Root Frame", + .color = {{0.020, 0.294, 0.569, 0.5}, {0.471, 0.882, 1.000, 0.75}, {0.314, 0.588, 0.843, 0.75}}, + .size = IMGUI_TIMELINE_FRAME_SIZE, + .contentOffset = IMGUI_TIMELINE_FRAME_CONTENT_OFFSET, + .border = IMGUI_TIMELINE_FRAME_BORDER, +}); + +const inline ImguiItem IMGUI_TIMELINE_LAYER_FRAME = ImguiItem +({ + .label = "## Layer Frame", + .dragDrop = "## Layer Frame Drag Drop", + .color = {{0.529, 0.157, 0.000, 0.5}, {1.000, 0.618, 0.324, 0.75}, {0.882, 0.412, 0.216, 0.75}}, + .size = IMGUI_TIMELINE_FRAME_SIZE, + .contentOffset = IMGUI_TIMELINE_FRAME_CONTENT_OFFSET, + .border = IMGUI_TIMELINE_FRAME_BORDER, +}); + +const inline ImguiItem IMGUI_TIMELINE_NULL_FRAME = ImguiItem +({ + .label = "## Null Frame", + .dragDrop = "## Null Frame Drag Drop", + .color = {{0.137, 0.353, 0.000, 0.5}, {0.646, 0.971, 0.441, 0.75}, {0.431, 0.647, 0.294, 0.75}}, + .size = IMGUI_TIMELINE_FRAME_SIZE, + .contentOffset = IMGUI_TIMELINE_FRAME_CONTENT_OFFSET, + .border = IMGUI_TIMELINE_FRAME_BORDER, +}); + +const inline ImguiItem IMGUI_TIMELINE_TRIGGERS_FRAME = ImguiItem +({ + .label = "## Triggers Frame", + .color = {{0.529, 0.118, 0.196, 0.5}, {1.000, 0.618, 0.735, 0.75}, {0.804, 0.412, 0.490, 0.75}}, + .size = IMGUI_TIMELINE_FRAME_SIZE, + .contentOffset = IMGUI_TIMELINE_FRAME_CONTENT_OFFSET, + .border = IMGUI_TIMELINE_FRAME_BORDER, +}); + +const inline ImguiItem* IMGUI_TIMELINE_FRAMES[ANM2_COUNT] +{ + &IMGUI_TIMELINE_FRAME, + &IMGUI_TIMELINE_ROOT_FRAME, + &IMGUI_TIMELINE_LAYER_FRAME, + &IMGUI_TIMELINE_NULL_FRAME, + &IMGUI_TIMELINE_TRIGGERS_FRAME +}; + +const inline ImguiItem IMGUI_TIMELINE_ADD_ELEMENT = ImguiItem +({ + .label = "Add Element", + .tooltip = "Adds an element (layer or null) to the animation.", + .popup = IMGUI_TIMELINE_ADD_ELEMENT_POPUP +}); + +const inline ImguiItem IMGUI_TIMELINE_ADD_ELEMENT_LAYER = ImguiItem +({ + .label = "Layer", + .tooltip = "Adds a layer element.\nA layer element is a primary graphical element, using a spritesheet.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ADD_ELEMENT_NULL = ImguiItem +({ + .label = "Null", + .tooltip = "Adds a null element.\nA null element is an invisible element, often accessed by the game engine.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_REMOVE_ELEMENT = ImguiItem +({ + .label = "Remove Element", + .tooltip = "Removes an element (layer or null) from the animation." +}); + +const inline ImguiItem IMGUI_TIMELINE_PLAY = ImguiItem +({ + .label = "|> Play", + .tooltip = "Play the current animation, if paused." +}); + +const inline ImguiItem IMGUI_TIMELINE_PAUSE = ImguiItem +({ + .label = "|| Pause", + .tooltip = "Pause the current animation, if playing." +}); + +const inline ImguiItem IMGUI_TIMELINE_ADD_FRAME = ImguiItem +({ + .label = "Add Frame", + .tooltip = "Adds a frame to the selected animation item.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_REMOVE_FRAME = ImguiItem +({ + .label = "Remove Frame", + .tooltip = "Removes the selected frame from the selected animation item.", + .focusWindow = IMGUI_TIMELINE_LABEL, + .chord = ImGuiKey_Delete, + .isUndoable = true, +}); + +const inline ImguiItem IMGUI_TIMELINE_FIT_ANIMATION_LENGTH = ImguiItem +({ + .label = "Fit Animation Length", + .tooltip = "Sets the animation's length to the latest frame.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_ANIMATION_LENGTH = ImguiItem +({ + .label = "Animation Length", + .tooltip = "Sets the animation length.\n(Will not change frames.)", + .isUndoable = true, + .size = {100, 0}, + .min = ANM2_FRAME_NUM_MIN, + .max = ANM2_FRAME_NUM_MAX +}); + +const inline ImguiItem IMGUI_TIMELINE_FPS = ImguiItem +({ + .label = "FPS", + .tooltip = "Sets the animation's frames per second (its speed).", + .isUndoable = true, + .size = {100, 0}, + .min = ANM2_FPS_MIN, + .max = ANM2_FPS_MAX +}); + +const inline ImguiItem IMGUI_TIMELINE_LOOP = ImguiItem +({ + .label = "Loop", + .tooltip = "Toggles the animation looping.", + .isUndoable = true +}); + +const inline ImguiItem IMGUI_TIMELINE_CREATED_BY = ImguiItem +({ + .label = "Author", + .tooltip = "Sets the author of the animation.", + .isUndoable = true, + .size = {150, 0}, + .max = ANM2_STRING_MAX +}); + +const inline ImguiItem IMGUI_TIMELINE_CREATED_ON = ImguiItem({"Created on: "}); +const inline ImguiItem IMGUI_TIMELINE_VERSION = ImguiItem({"Version: "}); + +const inline ImguiItem IMGUI_RECORDING = ImguiItem +({ + .label = "Recording...", + .texture = TEXTURE_RECORD +}); + +const inline ImguiItem IMGUI_CUT = ImguiItem +({ + .label = "Cut", + .tooltip = "Cuts the currently selected contextual element; removing it and putting it to the clipboard.", + .function = imgui_cut, + .chord = ImGuiMod_Ctrl | ImGuiKey_X +}); + +const inline ImguiItem IMGUI_COPY = ImguiItem +({ + .label = "Copy", + .tooltip = "Copies the currently selected contextual element to the clipboard.", + .function = imgui_copy, + .chord = ImGuiMod_Ctrl | ImGuiKey_C +}); + +const inline ImguiItem IMGUI_PASTE = ImguiItem +({ + .label = "Paste", + .tooltip = "Pastes the currently selection contextual element from the clipboard.", + .function = imgui_paste, + .chord = ImGuiMod_Ctrl | ImGuiKey_V +}); + +const inline ImguiItem IMGUI_RENAMABLE = ImguiItem +({ + .label = "## Renaming", + .tooltip = "Rename the selected item.", + .max = 255, + .flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue +}); + +const inline ImguiItem IMGUI_CHANGEABLE = ImguiItem +({ + .label = "## Changing", + .tooltip = "Change the selected item's value.", + .step = 0 +}); + +const inline ImguiItem IMGUI_EXIT_CONFIRMATION = ImguiItem +({ + .label = "Unsaved changes will be lost!\nAre you sure you want to exit?", + .popup = IMGUI_EXIT_CONFIRMATION_POPUP +}); + +const inline ImguiItem IMGUI_POPUP_YES_BUTTON = ImguiItem +({ + .label = "Yes", + .size = {120, 0} +}); + +const inline ImguiItem IMGUI_POPUP_NO_BUTTON = ImguiItem +({ + .label = "No", + .size = {120, 0} +}); + +void imgui_init ( Imgui* self, Dialog* dialog, Resources* resources, - Input* input, Anm2* anm2, Anm2Reference* reference, - f32* time, Editor* editor, Preview* preview, Settings* settings, - Tool* tool, Snapshots* snapshots, + Clipboard* clipboard, SDL_Window* window, SDL_GLContext* glContext ); diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index 906143b..0000000 --- a/src/input.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "input.h" - -static void _mouse_tick(Mouse* self); - -// Ticks mouse -static void -_mouse_tick(Mouse* self) -{ - s32 state; - - std::memcpy(&self->previous, &self->current, sizeof(self->current)); - std::memset(&self->current, '\0', sizeof(self->current)); - - state = SDL_GetMouseState(NULL, NULL); - - if ((state & SDL_BUTTON_LMASK) != 0) - { - self->current[MOUSE_LEFT] = true; - } - - if ((state & SDL_BUTTON_RMASK) != 0) - { - self->current[MOUSE_RIGHT] = true; - } - - SDL_GetMouseState(&self->position.x, &self->position.y); - - self->delta = self->position - self->oldPosition; - self->oldPosition = self->position; -} - -// Ticks keyboard -static void -_keyboard_tick(Keyboard* self) -{ - const bool* state; - - std::memcpy(&self->previous, &self->current, sizeof(self->previous)); - std::memset(&self->current, '\0', sizeof(self->current)); - - state = SDL_GetKeyboardState(NULL); - - std::memcpy(&self->current, state, KEY_COUNT); -} - -// Checks to see if the given mouse button has been pressed -bool -mouse_press(Mouse* self, MouseType type) -{ - return (self->current[type] && !self->previous[type]); -} - -// Checks to see if the given mouse button is held -bool -mouse_held(Mouse* self, MouseType type) -{ - return (self->current[type] && self->previous[type]); -} - -// Checks to see if the given mouse button is released -bool -mouse_release(Mouse* self, MouseType type) -{ - return (!self->current[type] && self->previous[type]); -} - -// Checks to see if the given key is pressed -bool -key_press(Keyboard* self, KeyType type) -{ - return (self->current[type] && !self->previous[type]); -} - -// Checks to see if the given key is held -bool -key_held(Keyboard* self, KeyType type) -{ - return (self->current[type] && self->previous[type]); -} - -// Checks to see if the given key is released -bool -key_release(Keyboard* self, KeyType type) -{ - return (!self->current[type] && self->previous[type]); -} - -// Checks to see if the given input is pressed -bool -input_press(Input* self, InputType type) -{ - for (KeyType key : INPUT_KEYS[type]) - if (!key_press(&self->keyboard, (key))) - return false; - return true; -} - -// Checks to see if the given input is held -bool -input_held(Input* self, InputType type) -{ - for (KeyType key : INPUT_KEYS[type]) - if (!key_held(&self->keyboard, (key))) - return false; - return true; -} - -// Checks to see if the given input is held -bool -input_release(Input* self, InputType type) -{ - for (size_t i = 0; i < INPUT_KEYS[type].size(); i++) { - auto& key = INPUT_KEYS[type][i]; - //below is some hackery to ensure that multikey shortcuts work... - //ideally would split this into a separate function or rename this? - if (i != INPUT_KEYS[type].size()) { - if (!key_held(&self->keyboard, key)) return false; - } - // last key in the shortcut chain, so in a mod1-mod2-...-key it won't be a mod key - // todo: introduce a list of modifer keys as a more reliable solution - else if (!key_release(&self->keyboard, (key))) return false; - }; - return true; -} - -// Ticks input -void -input_tick(Input* self) -{ - _mouse_tick(&self->mouse); - _keyboard_tick(&self->keyboard); -} diff --git a/src/input.h b/src/input.h deleted file mode 100644 index 6107424..0000000 --- a/src/input.h +++ /dev/null @@ -1,310 +0,0 @@ -#pragma once - -#include "COMMON.h" - -enum MouseType -{ - MOUSE_LEFT, - MOUSE_RIGHT, - MOUSE_COUNT -}; - -enum KeyType -{ - KEY_UNKNOWN = 0, - KEY_UNKNOWN_TWO = 1, - KEY_UNKNOWN_THREE = 2, - KEY_UNKNOWN_FOUR = 3, - KEY_A = 4, - KEY_B = 5, - KEY_C = 6, - KEY_D = 7, - KEY_E = 8, - KEY_F = 9, - KEY_G = 10, - KEY_H = 11, - KEY_I = 12, - KEY_J = 13, - KEY_K = 14, - KEY_L = 15, - KEY_M = 16, - KEY_N = 17, - KEY_O = 18, - KEY_P = 19, - KEY_Q = 20, - KEY_R = 21, - KEY_S = 22, - KEY_T = 23, - KEY_U = 24, - KEY_V = 25, - KEY_W = 26, - KEY_X = 27, - KEY_Y = 28, - KEY_Z = 29, - KEY_1 = 30, - KEY_2 = 31, - KEY_3 = 32, - KEY_4 = 33, - KEY_5 = 34, - KEY_6 = 35, - KEY_7 = 36, - KEY_8 = 37, - KEY_9 = 38, - KEY_0 = 39, - KEY_RETURN = 40, - KEY_ESCAPE = 41, - KEY_BACKSPACE = 42, - KEY_TAB = 43, - KEY_SPACE = 44, - KEY_MINUS = 45, - KEY_EQUALS = 46, - KEY_LEFTBRACKET = 47, - KEY_RIGHTBRACKET = 48, - KEY_BACKSLASH = 49, - KEY_NONUSHASH = 50, - KEY_SEMICOLON = 51, - KEY_APOSTROPHE = 52, - KEY_GRAVE = 53, - KEY_COMMA = 54, - KEY_PERIOD = 55, - KEY_SLASH = 56, - KEY_CAPSLOCK = 57, - KEY_F1 = 58, - KEY_F2 = 59, - KEY_F3 = 60, - KEY_F4 = 61, - KEY_F5 = 62, - KEY_F6 = 63, - KEY_F7 = 64, - KEY_F8 = 65, - KEY_F9 = 66, - KEY_F10 = 67, - KEY_F11 = 68, - KEY_F12 = 69, - KEY_PRINTSCREEN = 70, - KEY_SCROLLLOCK = 71, - KEY_PAUSE = 72, - KEY_INSERT = 73, - KEY_HOME = 74, - KEY_PAGEUP = 75, - KEY_DELETE = 76, - KEY_END = 77, - KEY_PAGEDOWN = 78, - KEY_RIGHT = 79, - KEY_LEFT = 80, - KEY_DOWN = 81, - KEY_UP = 82, - KEY_NUMLOCKCLEAR = 83, - KEY_KP_DIVIDE = 84, - KEY_KP_MULTIPLY = 85, - KEY_KP_MINUS = 86, - KEY_KP_PLUS = 87, - KEY_KP_ENTER = 88, - KEY_KP_1 = 89, - KEY_KP_2 = 90, - KEY_KP_3 = 91, - KEY_KP_4 = 92, - KEY_KP_5 = 93, - KEY_KP_6 = 94, - KEY_KP_7 = 95, - KEY_KP_8 = 96, - KEY_KP_9 = 97, - KEY_KP_0 = 98, - KEY_KP_PERIOD = 99, - KEY_NONUSBACKSLASH = 100, - KEY_APPLICATION = 101, - KEY_POWER = 102, - KEY_KP_EQUALS = 103, - KEY_F13 = 104, - KEY_F14 = 105, - KEY_F15 = 106, - KEY_F16 = 107, - KEY_F17 = 108, - KEY_F18 = 109, - KEY_F19 = 110, - KEY_F20 = 111, - KEY_F21 = 112, - KEY_F22 = 113, - KEY_F23 = 114, - KEY_F24 = 115, - KEY_EXECUTE = 116, - KEY_HELP = 117, - KEY_MENU = 118, - KEY_SELECT = 119, - KEY_STOP = 120, - KEY_AGAIN = 121, - KEY_UNDO = 122, - KEY_CUT = 123, - KEY_COPY = 124, - KEY_PASTE = 125, - KEY_FIND = 126, - KEY_MUTE = 127, - KEY_VOLUMEUP = 128, - KEY_VOLUMEDOWN = 129, - KEY_LOCKINGCAPSLOCK = 130, - KEY_LOCKINGNUMLOCK = 131, - KEY_LOCKINGSCROLLLOCK = 132, - KEY_KP_COMMA = 133, - KEY_KP_EQUALSAS400 = 134, - KEY_INTERNATIONAL1 = 135, - KEY_INTERNATIONAL2 = 136, - KEY_INTERNATIONAL3 = 137, - KEY_INTERNATIONAL4 = 138, - KEY_INTERNATIONAL5 = 139, - KEY_INTERNATIONAL6 = 140, - KEY_INTERNATIONAL7 = 141, - KEY_INTERNATIONAL8 = 142, - KEY_INTERNATIONAL9 = 143, - KEY_LANG1 = 144, - KEY_LANG2 = 145, - KEY_LANG3 = 146, - KEY_LANG4 = 147, - KEY_LANG5 = 148, - KEY_LANG6 = 149, - KEY_LANG7 = 150, - KEY_LANG8 = 151, - KEY_LANG9 = 152, - KEY_ALTERASE = 153, - KEY_SYSREQ = 154, - KEY_CANCEL = 155, - KEY_CLEAR = 156, - KEY_PRIOR = 157, - KEY_RETURN2 = 158, - KEY_SEPARATOR = 159, - KEY_OUT = 160, - KEY_OPER = 161, - KEY_CLEARAGAIN = 162, - KEY_CRSEL = 163, - KEY_EXSEL = 164, - KEY_KP_00 = 176, - KEY_KP_000 = 177, - KEY_THOUSANDSSEPARATOR = 178, - KEY_DECIMALSEPARATOR = 179, - KEY_CURRENCYUNIT = 180, - KEY_CURRENCYSUBUNIT = 181, - KEY_KP_LEFTPAREN = 182, - KEY_KP_RIGHTPAREN = 183, - KEY_KP_LEFTBRACE = 184, - KEY_KP_RIGHTBRACE = 185, - KEY_KP_TAB = 186, - KEY_KP_BACKSPACE = 187, - KEY_KP_A = 188, - KEY_KP_B = 189, - KEY_KP_C = 190, - KEY_KP_D = 191, - KEY_KP_E = 192, - KEY_KP_F = 193, - KEY_KP_XOR = 194, - KEY_KP_POWER = 195, - KEY_KP_PERCENT = 196, - KEY_KP_LESS = 197, - KEY_KP_GREATER = 198, - KEY_KP_AMPERSAND = 199, - KEY_KP_DBLAMPERSAND = 200, - KEY_KP_VERTICALBAR = 201, - KEY_KP_DBLVERTICALBAR = 202, - KEY_KP_COLON = 203, - KEY_KP_HASH = 204, - KEY_KP_SPACE = 205, - KEY_KP_AT = 206, - KEY_KP_EXCLAM = 207, - KEY_KP_MEMSTORE = 208, - KEY_KP_MEMRECALL = 209, - KEY_KP_MEMCLEAR = 210, - KEY_KP_MEMADD = 211, - KEY_KP_MEMSUBTRACT = 212, - KEY_KP_MEMMULTIPLY = 213, - KEY_KP_MEMDIVIDE = 214, - KEY_KP_PLUSMINUS = 215, - KEY_KP_CLEAR = 216, - KEY_KP_CLEARENTRY = 217, - KEY_KP_BINARY = 218, - KEY_KP_OCTAL = 219, - KEY_KP_DECIMAL = 220, - KEY_KP_HEXADECIMAL = 221, - KEY_LCTRL = 224, - KEY_LSHIFT = 225, - KEY_LALT = 226, - KEY_LGUI = 227, - KEY_RCTRL = 228, - KEY_RSHIFT = 229, - KEY_RALT = 230, - KEY_RGUI = 231, - KEY_COUNT = 255 -}; - -enum InputType -{ - INPUT_MOD, - INPUT_DELETE, - INPUT_PLAY, - INPUT_PAN, - INPUT_MOVE, - INPUT_ROTATE, - INPUT_SCALE, - INPUT_CROP, - INPUT_LEFT, - INPUT_RIGHT, - INPUT_UP, - INPUT_DOWN, - INPUT_ZOOM_IN, - INPUT_ZOOM_OUT, - INPUT_UNDO, - INPUT_REDO, - INPUT_SAVE, - INPUT_COUNT -}; - -const std::vector INPUT_KEYS[INPUT_COUNT] = -{ - { KEY_LSHIFT }, - { KEY_DELETE }, - { KEY_SPACE }, - { KEY_P }, - { KEY_M }, - { KEY_R }, - { KEY_S }, - { KEY_C }, - { KEY_LEFT }, - { KEY_RIGHT }, - { KEY_UP }, - { KEY_DOWN }, - { KEY_1 }, - { KEY_2 }, - { KEY_Z }, - { KEY_Y }, - { KEY_LCTRL , KEY_S} -}; - -struct Keyboard -{ - bool current[KEY_COUNT] = { 0 }; - bool previous[KEY_COUNT] = { 0 }; -}; - -struct Mouse -{ - bool current[MOUSE_COUNT]; - bool previous[MOUSE_COUNT]; - vec2 position = {-1, -1}; - vec2 oldPosition = {-1, -1}; - vec2 delta = {-1, -1}; - s32 wheelDeltaY = 0; -}; - -struct Input -{ - Mouse mouse; - Keyboard keyboard; -}; - -bool mouse_press(Mouse* self, MouseType type); -bool mouse_held(Mouse* self, MouseType type); -bool mouse_release(Mouse* self, MouseType type); -bool key_press(Keyboard* self, KeyType type); -bool key_held(Keyboard* self, KeyType type); -bool key_release(Keyboard* self, KeyType type); -bool input_press(Input* self, InputType type); -bool input_held(Input* self, InputType type); -bool input_release(Input* self, InputType type); -void input_tick(Input* self); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 70699bf..0ca3856 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +// Main function + #include "main.h" s32 @@ -5,12 +7,8 @@ main(s32 argc, char* argv[]) { State state; - // If anm2 given on command line, set state argument to that (will be loaded) if (argc > 0 && argv[1]) - { state.argument = argv[1]; - state.isArgument = true; - } init(&state); diff --git a/src/preview.cpp b/src/preview.cpp index 49a3cd8..f3b3a37 100644 --- a/src/preview.cpp +++ b/src/preview.cpp @@ -1,15 +1,12 @@ +// Handles the rendering of the animation preview + #include "preview.h" -static void _preview_axis_set(Preview* self); -static s32 _preview_grid_set(Preview* self); - -// Sets the preview's axis (lines across x/y) -static void -_preview_axis_set(Preview* self) +static void _preview_axis_set(Preview* self) { glBindVertexArray(self->axisVAO); glBindBuffer(GL_ARRAY_BUFFER, self->axisVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(PREVIEW_AXIS_VERTICES), PREVIEW_AXIS_VERTICES, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_AXIS_VERTICES), CANVAS_AXIS_VERTICES, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0); @@ -17,16 +14,14 @@ _preview_axis_set(Preview* self) glBindVertexArray(0); } -// Sets and returns the grid's vertices -static s32 -_preview_grid_set(Preview* self) +static s32 _preview_grid_set(Preview* self) { std::vector vertices; s32 verticalLineCount = (s32)(PREVIEW_SIZE.x / MIN(self->settings->previewGridSizeX, PREVIEW_GRID_MIN)); s32 horizontalLineCount = (s32)(PREVIEW_SIZE.y / MIN(self->settings->previewGridSizeY, PREVIEW_GRID_MIN)); - /* Vertical */ + // Vertical for (s32 i = 0; i <= verticalLineCount; i++) { s32 x = i * self->settings->previewGridSizeX - self->settings->previewGridOffsetX; @@ -38,7 +33,7 @@ _preview_grid_set(Preview* self) vertices.push_back(1.0f); } - /* Horizontal */ + // Horizontal for (s32 i = 0; i <= horizontalLineCount; i++) { s32 y = i * self->settings->previewGridSizeY - self->settings->previewGridOffsetY; @@ -60,13 +55,10 @@ _preview_grid_set(Preview* self) return (s32)vertices.size(); } -// Initializes preview -void -preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings) +void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) { self->anm2 = anm2; self->reference = reference; - self->time = time; self->resources = resources; self->settings = settings; @@ -77,7 +69,7 @@ preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Res glGenTextures(1, &self->texture); glBindTexture(GL_TEXTURE_2D, self->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)PREVIEW_SIZE.x, (s32)PREVIEW_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)PREVIEW_SIZE.x, (s32)PREVIEW_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -118,7 +110,7 @@ preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Res glBindVertexArray(self->textureVAO); glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, NULL, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW); @@ -137,9 +129,7 @@ preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Res _preview_grid_set(self); } -// Ticks preview -void -preview_tick(Preview* self) +void preview_tick(Preview* self) { self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX); @@ -150,13 +140,13 @@ preview_tick(Preview* self) { if (self->isPlaying) { - *self->time += (f32)self->anm2->fps / TICK_RATE; + self->time += (f32)self->anm2->fps / TICK_RATE; // If looping, return back to 0; if not, stop at length - if (*self->time >= (f32)animation->frameNum - 1) + if (self->time >= (f32)animation->frameNum - 1) { if (self->settings->playbackIsLoop && !self->isRecording) - *self->time = 0.0f; + self->time = 0.0f; else self->isPlaying = false; } @@ -164,13 +154,11 @@ preview_tick(Preview* self) // Make sure to clamp time within appropriate range if (!self->isPlaying) - *self->time = CLAMP(*self->time, 0.0f, (f32)animation->frameNum - 1); + self->time = CLAMP(self->time, 0.0f, (f32)animation->frameNum - 1); } } -// Draws preview -void -preview_draw(Preview* self) +void preview_draw(Preview* self) { GLuint shaderLine = self->resources->shaders[SHADER_LINE]; GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE]; @@ -263,15 +251,17 @@ preview_draw(Preview* self) { Anm2Frame rootFrame; Anm2Frame frame; - anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{animationID, ANM2_ROOT, 0, 0}, *self->time); + anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{animationID, ANM2_ROOT, 0, 0}, self->time); // Layers - for (auto & [id, layerAnimation] : animation->layerAnimations) + for (auto [i, id] : self->anm2->layerMap) { + Anm2Item& layerAnimation = animation->layerAnimations[id]; + if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) continue; - anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, *self->time); + anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, self->time); if (!frame.isVisible) continue; @@ -372,7 +362,7 @@ preview_draw(Preview* self) if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) continue; - anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, *self->time); + anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, self->time); if (!frame.isVisible) continue; @@ -423,7 +413,7 @@ preview_draw(Preview* self) if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0) continue; - anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id, 0}, *self->time); + anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id, 0}, self->time); if (!frame.isVisible) continue; @@ -432,7 +422,7 @@ preview_draw(Preview* self) glm::mat4 nullTransform = previewTransform; - TextureType textureType = null->isShowRect ? TEXTURE_SQUARE : TEXTURE_TARGET; + TextureType textureType = null->isShowRect ? TEXTURE_UNINTERPOLATED : TEXTURE_TARGET; glm::vec2 size = null->isShowRect ? PREVIEW_POINT_SIZE : PREVIEW_TARGET_SIZE; glm::vec2 pos = self->settings->previewIsRootTransform ? frame.position + (rootFrame.position) - (size / 2.0f) : frame.position - (size / 2.0f); @@ -494,12 +484,11 @@ preview_draw(Preview* self) } } - // Manage recording + /* if (self->isRecording && animation) { if (recordFrameIndex == 0) { - // Create frames directory, if it exists if ( std::filesystem::exists(STRING_PREVIEW_FRAMES_DIRECTORY) && @@ -548,27 +537,27 @@ preview_draw(Preview* self) } else { - if (*self->time >= (f32)animation->frameNum - 1) + if (self->time >= (f32)animation->frameNum - 1) { self->isRecording = false; self->isPlaying = false; recordFrameIndex = 0; recordFrameTimeNext = 0; - *self->time = 0.0f; + self->time = 0.0f; } - else if (*self->time >= recordFrameTimeNext) + else if (self->time >= recordFrameTimeNext) { isRecordThisFrame = true; - recordFrameTimeNext = *self->time + (f32)self->anm2->fps / TICK_RATE; + recordFrameTimeNext = self->time + (f32)self->anm2->fps / TICK_RATE; } } } + */ glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void -preview_free(Preview* self) +void preview_free(Preview* self) { glDeleteTextures(1, &self->texture); glDeleteFramebuffers(1, &self->fbo); diff --git a/src/preview.h b/src/preview.h index 717e37d..8156b28 100644 --- a/src/preview.h +++ b/src/preview.h @@ -2,13 +2,12 @@ #include "anm2.h" #include "resources.h" -#include "input.h" #include "settings.h" +#include "canvas.h" -const vec2 PREVIEW_SIZE = {5000, 5000}; +const vec2 PREVIEW_SIZE = {2000, 2000}; const vec2 PREVIEW_CENTER = {0, 0}; - #define PREVIEW_ZOOM_MIN 1 #define PREVIEW_ZOOM_MAX 1000 #define PREVIEW_ZOOM_STEP 25 @@ -16,20 +15,6 @@ const vec2 PREVIEW_CENTER = {0, 0}; #define PREVIEW_GRID_MAX 1000 #define PREVIEW_GRID_OFFSET_MIN 0 #define PREVIEW_GRID_OFFSET_MAX 100 -#define PREVIEW_MOVE_STEP 1 -#define PREVIEW_MOVE_STEP_MOD 10 -#define PREVIEW_ROTATE_STEP 1 -#define PREVIEW_ROTATE_STEP_MOD 10 -#define PREVIEW_SCALE_STEP 1 -#define PREVIEW_SCALE_STEP_MOD 10 - -const f32 PREVIEW_AXIS_VERTICES[] = -{ - -1.0f, 0.0f, - 1.0f, 0.0f, - 0.0f, -1.0f, - 0.0f, 1.0f -}; const vec2 PREVIEW_NULL_RECT_SIZE = {100, 100}; const vec2 PREVIEW_POINT_SIZE = {2, 2}; @@ -41,30 +26,29 @@ const vec2 PREVIEW_TARGET_SIZE = {16, 16}; struct Preview { - Anm2* anm2 = NULL; - Anm2Reference* reference = NULL; - f32* time = NULL; - Input* input = NULL; - Resources* resources = NULL; - Settings* settings = NULL; - GLuint axisVAO; - GLuint axisVBO; - GLuint fbo; - GLuint gridVAO; - GLuint gridVBO; - GLuint rbo; - GLuint texture; - GLuint rectVAO; - GLuint rectVBO; - GLuint textureEBO; - GLuint textureVAO; - GLuint textureVBO; + Anm2* anm2 = nullptr; + Anm2Reference* reference = nullptr; + Resources* resources = nullptr; + Settings* settings = nullptr; + GLuint axisVAO = ID_NONE; + GLuint axisVBO = ID_NONE; + GLuint fbo = ID_NONE; + GLuint gridVAO = ID_NONE; + GLuint gridVBO = ID_NONE; + GLuint rbo = ID_NONE; + GLuint texture = ID_NONE; + GLuint rectVAO = ID_NONE; + GLuint rectVBO = ID_NONE; + GLuint textureEBO = ID_NONE; + GLuint textureVAO = ID_NONE; + GLuint textureVBO = ID_NONE; bool isPlaying = false; bool isRecording = false; - vec2 recordSize = {0.0f, 0.0f}; + vec2 recordSize{}; + f32 time{}; }; -void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings); +void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings); void preview_draw(Preview* self); void preview_tick(Preview* self); void preview_free(Preview* self); diff --git a/src/resources.cpp b/src/resources.cpp index cfb925d..c9fabbf 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -1,48 +1,39 @@ #include "resources.h" -// Loads a texture, given a path and an id for it to be assigned to -void -resources_texture_init(Resources* resources, const std::string& path, s32 id) +void resources_texture_init(Resources* resources, const std::string& path, s32 id) { Texture texture; - if (resources->textures.find(id) != resources->textures.end() && resources->textures[id].id != resources->textures[TEXTURE_ERROR].id) + if (resources->textures.find(id) != resources->textures.end() && resources->textures[id].id != resources->textures[TEXTURE_NONE].id) texture_free(&resources->textures[id]); - if (texture_from_path_init(&texture, path)) - resources->textures[id] = texture; - else - texture.isInvalid = true; + if (!texture_from_path_init(&texture, path)) + texture.isInvalid = true; - resources->textures[id] = texture; + resources->textures[id] = texture; } -// Loads in resources -void -resources_init(Resources* self) +void resources_init(Resources* self) { texture_from_data_init(&self->atlas, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH); - - for (s32 i = 0; i < SHADER_COUNT; i++) + + for (s32 i = 0; i < SHADER_COUNT; i++) shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment); } -// Frees resources -void -resources_free(Resources* self) +void resources_free(Resources* self) { resources_textures_free(self); - - for (s32 i = 0; i < SHADER_COUNT; i++) + + for (s32 i = 0; i < SHADER_COUNT; i++) shader_free(&self->shaders[i]); - texture_free(&self->atlas); } -// Frees loaded textures -void -resources_textures_free(Resources* self) +void resources_textures_free(Resources* self) { - for (auto & [id, texture] : self->textures) + for (auto & [id, texture] : self->textures) texture_free(&self->textures[id]); + + log_info(RESOURCES_TEXTURES_FREE_INFO); } \ No newline at end of file diff --git a/src/resources.h b/src/resources.h index 79335d9..94e3e3c 100644 --- a/src/resources.h +++ b/src/resources.h @@ -4,6 +4,8 @@ #include "texture.h" #include "shader.h" +#define RESOURCES_TEXTURES_FREE_INFO "Freed texture resources" + struct Resources { GLuint shaders[SHADER_COUNT]; diff --git a/src/settings.cpp b/src/settings.cpp index d171cc4..88e6aa7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,23 +1,17 @@ #include "settings.h" -static void _settings_setting_load(Settings* self, const std::string& line); -static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry); - -static void -_settings_setting_load(Settings* self, const std::string& line) +static void _settings_setting_load(Settings* self, const std::string& line) { for (s32 i = 0; i < SETTINGS_COUNT; i++) { const std::string& key = SETTINGS_ENTRIES[i].key; size_t keyLength = key.length(); - // Compare keys if (line.compare(0, keyLength, key) == 0) { const char* value = line.c_str() + keyLength; void* target = (u8*)self + SETTINGS_ENTRIES[i].offset; - // Based on type, assign value to offset of settings switch (SETTINGS_ENTRIES[i].type) { case SETTINGS_TYPE_INT: @@ -40,9 +34,7 @@ _settings_setting_load(Settings* self, const std::string& line) } } -// Writes a given setting to the stream -static void -_settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry) +static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry) { u8* selfPointer = (u8*)self; std::string value; @@ -68,60 +60,51 @@ _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry) out << entry.key << value << "\n"; } -// Saves the current settings -// Note: this is just for this program's settings, additional imgui settings handled elsewhere -void -settings_save(Settings* self) +void settings_save(Settings* self) { - std::ifstream input(PATH_SETTINGS); + std::ifstream input(SETTINGS_PATH); std::string oldContents; if (!input) { - std::cout << STRING_ERROR_SETTINGS_INIT << PATH_SETTINGS << std::endl; + log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH)); return; } - // We're writing after the imgui stuff oldContents.assign((std::istreambuf_iterator(input)), std::istreambuf_iterator()); input.close(); - std::ofstream output(PATH_SETTINGS); + std::ofstream output(SETTINGS_PATH); if (!output) { - std::cout << STRING_ERROR_SETTINGS_INIT << PATH_SETTINGS << std::endl; + log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH)); return; } - // [Settings] output << SETTINGS_SECTION << "\n"; - // Write each setting for (s32 i = 0; i < SETTINGS_COUNT; i++) _settings_setting_write(self, output, SETTINGS_ENTRIES[i]); - // Write the the imgui section output << "\n" << SETTINGS_SECTION_IMGUI << "\n"; output << oldContents; output.close(); } -// Load settings -void -settings_load(Settings* self) +void settings_init(Settings* self) { - std::ifstream file(PATH_SETTINGS); + std::ifstream file(SETTINGS_PATH); + if (!file) { - std::cerr << STRING_ERROR_SETTINGS_INIT << PATH_SETTINGS << std::endl; + log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH)); return; } std::string line; bool inSettingsSection = false; - // Iterare through settings lines until the imgui section is reached, then end while (std::getline(file, line)) { if (line == SETTINGS_SECTION) @@ -130,10 +113,7 @@ settings_load(Settings* self) continue; } - if (line == SETTINGS_SECTION_IMGUI) - break; - - if (inSettingsSection) - _settings_setting_load(self, line); + if (line == SETTINGS_SECTION_IMGUI) break; + if (inSettingsSection) _settings_setting_load(self, line); } } \ No newline at end of file diff --git a/src/settings.h b/src/settings.h index 0edc1f7..ad20453 100644 --- a/src/settings.h +++ b/src/settings.h @@ -6,6 +6,8 @@ #define SETTINGS_BUFFER_ITEM 0xFF #define SETTINGS_SECTION "[Settings]" #define SETTINGS_SECTION_IMGUI "# Dear ImGui" +#define SETTINGS_INIT_ERROR "Failed to read settings file! ({})" +#define SETTINGS_PATH "settings.ini" enum SettingsValueType { @@ -22,7 +24,6 @@ struct SettingsEntry s32 offset; }; -#define SETTINGS_COUNT 44 struct Settings { s32 windowW = 1920; @@ -69,9 +70,13 @@ struct Settings f32 editorBackgroundColorG = 0.184f; f32 editorBackgroundColorB = 0.286f; f32 editorBackgroundColorA = 1.0f; + f32 toolColorR = 1.0f; + f32 toolColorG = 1.0f; + f32 toolColorB = 1.0f; + f32 toolColorA = 1.0f; }; -const SettingsEntry SETTINGS_ENTRIES[SETTINGS_COUNT] = +const SettingsEntry SETTINGS_ENTRIES[] = { {"windowW=", SETTINGS_TYPE_INT, offsetof(Settings, windowW)}, {"windowH=", SETTINGS_TYPE_INT, offsetof(Settings, windowH)}, @@ -116,8 +121,13 @@ const SettingsEntry SETTINGS_ENTRIES[SETTINGS_COUNT] = {"editorBackgroundColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorR)}, {"editorBackgroundColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorG)}, {"editorBackgroundColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorB)}, - {"editorBackgroundColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorA)} + {"editorBackgroundColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorA)}, + {"toolColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorR)}, + {"toolColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorG)}, + {"toolColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorB)}, + {"toolColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorA)} }; +constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES); void settings_save(Settings* self); -void settings_load(Settings* self); \ No newline at end of file +void settings_init(Settings* self); \ No newline at end of file diff --git a/src/shader.cpp b/src/shader.cpp index de7e910..76de08b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -1,25 +1,21 @@ #include "shader.h" -static bool _shader_compile(GLuint* self, const std::string& text); - -// Compiles the shader; returns true/false based on success -static bool -_shader_compile(GLuint* self, const std::string& text) +static bool _shader_compile(GLuint* self, const std::string& text) { std::string compileLog; s32 isCompile; const GLchar* source = text.c_str(); - glShaderSource(*self, 1, &source, NULL); + glShaderSource(*self, 1, &source, nullptr); glCompileShader(*self); glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile); if (!isCompile) { - glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, NULL, &compileLog[0]); - std::cout << STRING_ERROR_SHADER_INIT << *self << std::endl << compileLog << std::endl; + glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, nullptr, &compileLog[0]); + log_error(std::format(SHADER_INIT_ERROR, *self, compileLog)); return false; } @@ -27,8 +23,7 @@ _shader_compile(GLuint* self, const std::string& text) } // Initializes a given shader with vertex/fragment -bool -shader_init(GLuint* self, const std::string& vertex, const std::string& fragment) +bool shader_init(GLuint* self, const std::string& vertex, const std::string& fragment) { GLuint vertexHandle; GLuint fragmentHandle; @@ -36,11 +31,7 @@ shader_init(GLuint* self, const std::string& vertex, const std::string& fragment vertexHandle = glCreateShader(GL_VERTEX_SHADER); fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER); - if - ( - !_shader_compile(&vertexHandle, vertex) || - !_shader_compile(&fragmentHandle, fragment) - ) + if (!_shader_compile(&vertexHandle, vertex) || !_shader_compile(&fragmentHandle, fragment)) return false; *self = glCreateProgram(); @@ -56,9 +47,7 @@ shader_init(GLuint* self, const std::string& vertex, const std::string& fragment return true; } -// Frees a given shader -void -shader_free(GLuint* self) +void shader_free(GLuint* self) { glDeleteProgram(*self); } \ No newline at end of file diff --git a/src/shader.h b/src/shader.h index 314745a..276a64c 100644 --- a/src/shader.h +++ b/src/shader.h @@ -3,6 +3,7 @@ #include "COMMON.h" #define SHADER_INFO_LOG_MAX 0xFF +#define SHADER_INIT_ERROR "Failed to initialize shader {}:\n{}" bool shader_init(GLuint* self, const std::string& vertex, const std::string& fragment); void shader_free(GLuint* self); \ No newline at end of file diff --git a/src/snapshots.cpp b/src/snapshots.cpp index 1307457..0ebf85c 100644 --- a/src/snapshots.cpp +++ b/src/snapshots.cpp @@ -1,112 +1,62 @@ #include "snapshots.h" -// Pushes the undo stack -void -snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot) +static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot) { - // If stack over the limit, shift it - if (self->undoStack.top >= SNAPSHOT_STACK_MAX) + if (stack->top >= SNAPSHOT_STACK_MAX) { for (s32 i = 0; i < SNAPSHOT_STACK_MAX - 1; i++) - self->undoStack.snapshots[i] = self->undoStack.snapshots[i + 1]; - - self->undoStack.top = SNAPSHOT_STACK_MAX - 1; + stack->snapshots[i] = stack->snapshots[i + 1]; + stack->top = SNAPSHOT_STACK_MAX - 1; } - - self->undoStack.snapshots[self->undoStack.top++] = *snapshot; - self->redoStack.top = 0; + stack->snapshots[stack->top++] = *snapshot; } -// Pops the undo stack -bool -snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot) +static bool _snapshot_stack_pop(SnapshotStack* stack, Snapshot* snapshot) { - if (self->undoStack.top == 0) - return false; - - *snapshot = self->undoStack.snapshots[--self->undoStack.top]; + if (stack->top == 0) return false; + *snapshot = stack->snapshots[--stack->top]; return true; } -// Pushes the redo stack -void -snapshots_redo_stack_push(Snapshots* self, Snapshot* snapshot) +static void _snapshot_set(Snapshots* self, const Snapshot& snapshot) { - if (self->redoStack.top >= SNAPSHOT_STACK_MAX) - { - for (s32 i = 0; i < SNAPSHOT_STACK_MAX - 1; i++) - self->redoStack.snapshots[i] = self->redoStack.snapshots[i + 1]; - self->redoStack.top = SNAPSHOT_STACK_MAX - 1; - } - - self->redoStack.snapshots[self->redoStack.top++] = *snapshot; + *self->anm2 = snapshot.anm2; + *self->reference = snapshot.reference; + self->preview->time = snapshot.time; } -// Pops the redo stack -bool -snapshots_redo_stack_pop(Snapshots* self, Snapshot* snapshot) -{ - if (self->redoStack.top == 0) - return false; - - *snapshot = self->redoStack.snapshots[--self->redoStack.top]; - return true; -} - -// Initializes snapshots -void -snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input) +void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview) { self->anm2 = anm2; self->reference = reference; - self->time = time; - self->input = input; + self->preview = preview; } -// Ticks snapshots -void -snapshots_tick(Snapshots* self) +void snapshots_undo_stack_push(Snapshots* self, const Snapshot* snapshot) { - /* Undo */ - if (input_press(self->input, INPUT_UNDO)) - self->isUndo = true; - - // isUndo disconnected, if another part of the program wants to set it - if (self->isUndo) + _snapshot_stack_push(&self->undoStack, snapshot); + self->redoStack.top = 0; +} + +void snapshots_undo(Snapshots* self) +{ + Snapshot snapshot; + if (_snapshot_stack_pop(&self->undoStack, &snapshot)) { - Snapshot snapshot; - if (snapshots_undo_stack_pop(self, &snapshot)) - { - Snapshot current = {*self->anm2, *self->reference, *self->time}; - snapshots_redo_stack_push(self, ¤t); - - *self->anm2 = snapshot.anm2; - *self->reference = snapshot.reference; - *self->time = snapshot.time; - } - - self->isUndo = false; + Snapshot current = {*self->anm2, *self->reference, self->preview->time}; + _snapshot_stack_push(&self->redoStack, ¤t); + _snapshot_set(self, snapshot); } +} - /* Redo */ - if (input_press(self->input, INPUT_REDO)) - self->isRedo = true; - - // isRedo disconnected, if another part of the program wants to set it - if (self->isRedo) +void snapshots_redo(Snapshots* self) +{ + Snapshot snapshot; + if (_snapshot_stack_pop(&self->redoStack, &snapshot)) { - Snapshot snapshot; - if (snapshots_redo_stack_pop(self, &snapshot)) - { - Snapshot current = {*self->anm2, *self->reference, *self->time}; - snapshots_undo_stack_push(self, ¤t); - - *self->anm2 = snapshot.anm2; - *self->reference = snapshot.reference; - *self->time = snapshot.time; - } - - self->isRedo = false; + Snapshot current = {*self->anm2, *self->reference, self->preview->time}; + _snapshot_stack_push(&self->undoStack, ¤t); + _snapshot_set(self, snapshot); } } \ No newline at end of file diff --git a/src/snapshots.h b/src/snapshots.h index c650362..719bc33 100644 --- a/src/snapshots.h +++ b/src/snapshots.h @@ -1,7 +1,7 @@ #pragma once #include "anm2.h" -#include "input.h" +#include "preview.h" #define SNAPSHOT_STACK_MAX 100 @@ -20,17 +20,14 @@ struct SnapshotStack struct Snapshots { - Anm2* anm2 = NULL; - Anm2Reference* reference = NULL; - f32* time = NULL; - Input* input = NULL; + Anm2* anm2 = nullptr; + Preview* preview = nullptr; + Anm2Reference* reference = nullptr; SnapshotStack undoStack; SnapshotStack redoStack; - bool isUndo = false; - bool isRedo = false; }; -void snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot); -bool snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot); -void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input); -void snapshots_tick(Snapshots* self); \ No newline at end of file +void snapshots_undo_stack_push(Snapshots* self, const Snapshot* snapshot); +void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview); +void snapshots_undo(Snapshots* self); +void snapshots_redo(Snapshots* self); \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp index 4cac41d..42b14b0 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1,182 +1,133 @@ #include "state.h" -static void _tick(State* state); -static void _draw(State* state); - -static void -_tick(State* state) +static void _tick(State* self) { - SDL_Event event; - SDL_MouseWheelEvent* mouseWheelEvent; + SDL_GetWindowSize(self->window, &self->settings.windowW, &self->settings.windowH); - state->input.mouse.wheelDeltaY = 0; + editor_tick(&self->editor); + preview_tick(&self->preview); + dialog_tick(&self->dialog); + imgui_tick(&self->imgui); - while(SDL_PollEvent(&event)) - { - ImGui_ImplSDL3_ProcessEvent(&event); - - switch (event.type) - { - case SDL_EVENT_QUIT: - state->isRunning = false; - break; - case SDL_EVENT_MOUSE_WHEEL: - mouseWheelEvent = &event.wheel; - state->input.mouse.wheelDeltaY = mouseWheelEvent->y; - break; - default: - break; - } - } - - SDL_GetWindowSize(state->window, &state->settings.windowW, &state->settings.windowH); - - input_tick(&state->input); - editor_tick(&state->editor); - preview_tick(&state->preview); - tool_tick(&state->tool); - snapshots_tick(&state->snapshots); - dialog_tick(&state->dialog); - imgui_tick(&state->imgui); - - if (input_release(&state->input, INPUT_SAVE)) - { - // Open dialog if path empty, otherwise save in-place - if (state->anm2.path.empty()) - dialog_anm2_save(&state->dialog); - else - anm2_serialize(&state->anm2, state->anm2.path); - } - - if (input_release(&state->input, INPUT_PLAY)) - { - state->preview.isPlaying = !state->preview.isPlaying; - state->preview.isRecording = false; - } + if (self->imgui.isQuit) self->isRunning = false; } -static void -_draw(State* state) +static void _draw(State* self) { - editor_draw(&state->editor); - preview_draw(&state->preview); + editor_draw(&self->editor); + preview_draw(&self->preview); imgui_draw(); - SDL_GL_SwapWindow(state->window); + SDL_GL_SwapWindow(self->window); } -void -init(State* state) +void init(State* self) { - settings_load(&state->settings); + settings_init(&self->settings); - std::cout << STRING_INFO_INIT << std::endl; + log_info(STATE_INIT_INFO); if (!SDL_Init(SDL_INIT_VIDEO)) { - std::cout << STRING_ERROR_SDL_INIT << SDL_GetError() << std::endl; - quit(state); + log_error(std::format(STATE_SDL_INIT_ERROR, SDL_GetError())); + quit(self); } + + log_info(STATE_SDL_INIT_INFO); SDL_CreateWindowAndRenderer ( - STRING_WINDOW_TITLE, - state->settings.windowW, - state->settings.windowH, + WINDOW_TITLE, + self->settings.windowW, + self->settings.windowH, WINDOW_FLAGS, - &state->window, - &state->renderer + &self->window, + &self->renderer ); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - state->glContext = SDL_GL_CreateContext(state->window); + glewInit(); - if (!state->glContext) + self->glContext = SDL_GL_CreateContext(self->window); + + if (!self->glContext) { - std::cout << STRING_ERROR_GL_CONTEXT_INIT << SDL_GetError() << std::endl; - quit(state); + log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, SDL_GetError())); + quit(self); } - std::cout << STRING_INFO_SDL_INIT << "(" << STRING_INFO_OPENGL << glGetString(GL_VERSION) << ")" << std::endl; - - glewInit(); + log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION))); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); - glLineWidth(LINE_WIDTH); + glLineWidth(STATE_GL_LINE_WIDTH); - std::cout << STRING_INFO_GLEW_INIT << std::endl; - - resources_init(&state->resources); - dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window); - tool_init(&state->tool, &state->input); - snapshots_init(&state->snapshots, &state->anm2, &state->reference, &state->time, &state->input); - preview_init(&state->preview, &state->anm2, &state->reference, &state->time, &state->resources, &state->settings); - editor_init(&state->editor, &state->anm2, &state->reference, &state->resources, &state->settings); + resources_init(&self->resources); + clipboard_init(&self->clipboard, &self->anm2); + dialog_init(&self->dialog, &self->anm2, &self->reference, &self->resources, self->window); + snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview); + preview_init(&self->preview, &self->anm2, &self->reference, &self->resources, &self->settings); + editor_init(&self->editor, &self->anm2, &self->reference, &self->resources, &self->settings); imgui_init ( - &state->imgui, - &state->dialog, - &state->resources, - &state->input, - &state->anm2, - &state->reference, - &state->time, - &state->editor, - &state->preview, - &state->settings, - &state->tool, - &state->snapshots, - state->window, - &state->glContext + &self->imgui, + &self->dialog, + &self->resources, + &self->anm2, + &self->reference, + &self->editor, + &self->preview, + &self->settings, + &self->snapshots, + &self->clipboard, + self->window, + &self->glContext ); - if (state->isArgument) + if (self->is_argument()) { - anm2_deserialize(&state->anm2, &state->resources, state->argument); - window_title_from_path_set(state->window, state->argument); + anm2_deserialize(&self->anm2, &self->resources, self->argument); + window_title_from_path_set(self->window, self->argument); } else - anm2_new(&state->anm2); + anm2_new(&self->anm2); } -void -loop(State* state) +void loop(State* self) { - state->tick = SDL_GetTicks(); + self->tick = SDL_GetTicks(); - while (state->tick > state->lastTick + TICK_DELAY) + while (self->tick > self->lastTick + TICK_DELAY) { - state->tick = SDL_GetTicks(); + self->tick = SDL_GetTicks(); - if (state->tick - state->lastTick < TICK_DELAY) - SDL_Delay(TICK_DELAY - (state->tick - state->lastTick)); + if (self->tick - self->lastTick < TICK_DELAY) + SDL_Delay(TICK_DELAY - (self->tick - self->lastTick)); - _tick(state); + _tick(self); + + self->lastTick = self->tick; - state->lastTick = state->tick; } - _draw(state); + _draw(self); } -void -quit(State* state) +void quit(State* self) { imgui_free(); - settings_save(&state->settings); - preview_free(&state->preview); - editor_free(&state->editor); - resources_free(&state->resources); + settings_save(&self->settings); + preview_free(&self->preview); + editor_free(&self->editor); + resources_free(&self->resources); - SDL_GL_DestroyContext(state->glContext); + SDL_GL_DestroyContext(self->glContext); SDL_Quit(); - std::cout << STRING_INFO_QUIT << std::endl; -} - + log_info(STATE_QUIT_INFO); +} \ No newline at end of file diff --git a/src/state.h b/src/state.h index ec5c656..2c002b6 100644 --- a/src/state.h +++ b/src/state.h @@ -2,10 +2,13 @@ #include "imgui.h" -#define WINDOW_WIDTH 1920 -#define WINDOW_HEIGHT 1080 -#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL -#define LINE_WIDTH 2.0f +#define STATE_INIT_INFO "Initializing..." +#define STATE_SDL_INIT_ERROR "Failed to initialize SDL! {}" +#define STATE_SDL_INIT_INFO "Initialized SDL" +#define STATE_GL_CONTEXT_INIT_ERROR "Failed to initialize OpenGL context! {}" +#define STATE_GL_CONTEXT_INIT_INFO "Initialized OpenGL context (OpenGL {})" +#define STATE_QUIT_INFO "Exiting..." +#define STATE_GL_LINE_WIDTH 2.0f struct State { @@ -13,22 +16,21 @@ struct State SDL_Renderer* renderer; SDL_GLContext glContext; Imgui imgui; - Input input; Dialog dialog; Editor editor; Preview preview; Anm2 anm2; Anm2Reference reference; - f32 time; Resources resources; Settings settings; - Tool tool; Snapshots snapshots; - bool isArgument = false; + Clipboard clipboard; bool isRunning = true; - std::string argument; + std::string argument{}; u64 lastTick = 0; u64 tick = 0; + + bool is_argument() const { return !argument.empty(); } }; void init(State* state); diff --git a/src/texture.cpp b/src/texture.cpp index 9789615..be32776 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -5,75 +5,55 @@ #define STBI_NO_HDR #define STB_IMAGE_IMPLEMENTATION #include - #define STB_IMAGE_WRITE_IMPLEMENTATION #include -// Generates GL texture and sets parameters -void -texture_gl_set(Texture* self, void* data) +void texture_gl_set(Texture* self, void* data) { glGenTextures(1, &self->id); - glBindTexture(GL_TEXTURE_2D, self->id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 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); } -// Initializes texture from path; returns true/false on success -bool -texture_from_path_init(Texture* self, const std::string& path) +bool texture_from_path_init(Texture* self, const std::string& path) { - void* data; - - data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, 4); + void* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS); if (!data) { - std::cout << STRING_ERROR_TEXTURE_INIT << path << std::endl; + log_error(std::format(TEXTURE_INIT_ERROR, path)); return false; } - std::cout << STRING_INFO_TEXTURE_INIT << path << std::endl; + log_info(std::format(TEXTURE_INIT_INFO, path)); texture_gl_set(self, data); return true; } -// Initializes texture from data; returns true/false on success -bool -texture_from_data_init(Texture* self, const u8* data, u32 length) +bool texture_from_data_init(Texture* self, const u8* data, u32 length) { - void* textureData; + void* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS); - textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, 4); - - if (!textureData) - return false; + if (!textureData) return false; texture_gl_set(self, textureData); return true; } -// Writes an image to the path from the data/size -bool -texture_from_data_write(const std::string& path, const u8* data, s32 width, s32 height) +bool texture_from_data_write(const std::string& path, const u8* data, s32 width, s32 height) { - return (bool)stbi_write_png(path.c_str(), width, height, 4, data, width * 4); + return (bool)stbi_write_png(path.c_str(), width, height, TEXTURE_CHANNELS, data, width * TEXTURE_CHANNELS); } -// Frees texture -void -texture_free(Texture* self) +void texture_free(Texture* self) { glDeleteTextures(1, &self->id); *self = Texture{}; diff --git a/src/texture.h b/src/texture.h index dc54141..40e51ce 100644 --- a/src/texture.h +++ b/src/texture.h @@ -2,6 +2,10 @@ #include "COMMON.h" +#define TEXTURE_CHANNELS 4 +#define TEXTURE_INIT_INFO "Initialized texture from file: {}" +#define TEXTURE_INIT_ERROR "Failed to initialize texture from file: {}" + struct Texture { GLuint id = 0; diff --git a/src/tool.cpp b/src/tool.cpp deleted file mode 100644 index a420bbe..0000000 --- a/src/tool.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "tool.h" - -// Initializes tools -void -tool_init(Tool* self, Input* input) -{ - self->input = input; -} - -// Ticks tools -void -tool_tick(Tool* self) -{ - if (!self->isEnabled) return; - - // Input handling for tools - if (input_release(self->input, INPUT_PAN)) - self->type = TOOL_PAN; - - if (input_release(self->input, INPUT_MOVE)) - self->type = TOOL_MOVE; - - if (input_release(self->input, INPUT_SCALE)) - self->type = TOOL_SCALE; - - if (input_release(self->input, INPUT_ROTATE)) - self->type = TOOL_ROTATE; - - if (input_release(self->input, INPUT_CROP)) - self->type = TOOL_CROP; -} \ No newline at end of file diff --git a/src/tool.h b/src/tool.h deleted file mode 100644 index 67ceaeb..0000000 --- a/src/tool.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "input.h" - -enum ToolType -{ - TOOL_PAN, - TOOL_MOVE, - TOOL_ROTATE, - TOOL_SCALE, - TOOL_CROP, - TOOL_COUNT -}; - -struct Tool -{ - Input* input = NULL; - ToolType type = TOOL_PAN; - bool isEnabled = false; -}; - -void tool_init(Tool* self, Input* input); -void tool_tick(Tool* self); \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index 8fcc9d4..bf9243f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,15 +1,9 @@ #include "window.h" -/* Sets the window title from the given anm2 */ -void -window_title_from_path_set(SDL_Window* self, const std::string& path) +void window_title_from_path_set(SDL_Window* self, const std::string& path) { - if (path.empty()) - { - std::string windowTitle = path; - windowTitle = windowTitle + " (" + path + ")"; - SDL_SetWindowTitle(self, windowTitle.c_str()); - } + if (!path.empty()) + SDL_SetWindowTitle(self, std::format(WINDOW_TITLE_FORMAT, path).c_str()); else - SDL_SetWindowTitle(self, STRING_WINDOW_TITLE); + SDL_SetWindowTitle(self, WINDOW_TITLE); } \ No newline at end of file diff --git a/src/window.h b/src/window.h index e314ae8..f53dd95 100644 --- a/src/window.h +++ b/src/window.h @@ -2,4 +2,10 @@ #include "COMMON.h" +#define WINDOW_TITLE "Anm2Ed" +#define WINDOW_TITLE_FORMAT "Anm2Ed ({})" +#define WINDOW_WIDTH 1920 +#define WINDOW_HEIGHT 1080 +#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL + void window_title_from_path_set(SDL_Window* self, const std::string& path); \ No newline at end of file