This commit is contained in:
2025-06-19 05:29:06 -04:00
parent 9929e69f22
commit 91190987ed
30 changed files with 2754 additions and 475 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
concept/
packed/

4
.gitmodules vendored
View File

@ -1,6 +1,10 @@
[submodule "include/imgui"] [submodule "include/imgui"]
path = include/imgui path = include/imgui
url = https://github.com/ocornut/imgui url = https://github.com/ocornut/imgui
branch = docking
[submodule "include/glm"] [submodule "include/glm"]
path = include/glm path = include/glm
url = https://github.com/g-truc/glm url = https://github.com/g-truc/glm
[submodule "include/tinyxml2"]
path = include/tinyxml2
url = https://github.com/leethomason/tinyxml2

View File

@ -3,16 +3,21 @@ project(anm2ed CXX)
# Gather project sources # Gather project sources
file(GLOB SOURCES file(GLOB SOURCES
"${PROJECT_SOURCE_DIR}/include/imgui/*.cpp" "include/imgui/imgui.cpp"
"${PROJECT_SOURCE_DIR}/include/pugixml/*.cpp" "include/imgui/imgui_draw.cpp"
"include/imgui/imgui_tables.cpp"
"include/imgui/imgui_widgets.cpp"
"include/imgui/backends/imgui_impl_sdl3.cpp"
"include/imgui/backends/imgui_impl_opengl3.cpp"
"include/tinyxml2/tinyxml2.cpp"
"${PROJECT_SOURCE_DIR}/src/*.cpp" "${PROJECT_SOURCE_DIR}/src/*.cpp"
) )
add_executable(${PROJECT_NAME} ${SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE include src) target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src )
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++20 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess") set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete")
target_link_libraries(${PROJECT_NAME} PRIVATE m GL GLEW SDL3 SDL3_mixer) target_link_libraries(${PROJECT_NAME} PRIVATE m GL GLEW SDL3 SDL3_mixer)

View File

@ -5,25 +5,23 @@
#include <GL/glew.h> #include <GL/glew.h>
#include <GL/gl.h> #include <GL/gl.h>
#include <glm/glm.hpp> #include <glm/glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/glm/gtc/type_ptr.hpp>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <pugixml.hpp>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <tinyxml2.h>
#include <algorithm>
#include <ranges>
#include <vector> #include <vector>
#include <map>
#include "STRINGS.h" #include "STRINGS.h"
#include "RESOURCES.h"
using namespace glm;
using namespace pugi;
using namespace std;
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16; typedef uint16_t u16;
@ -40,15 +38,40 @@ typedef double f64;
#define PI (GLM_PI) #define PI (GLM_PI)
#define TAU (PI * 2) #define TAU (PI * 2)
#define ATAN(x1, x2, y1, y2) (fmod((atan2(y2 - y1, x2 - x1) + TAU), TAU)) /* [0, 2PI] */
struct State using namespace glm; // fuck you
#define MIN(x, min) (x < min ? min : x)
#define MAX(x, max) (x > max ? max : x)
#define CLAMP(x, min, max) (MIN(MAX(x, max), min))
static inline const char* enum_to_string(const char* arr[], s32 count, s32 index) { return (index >= 0 && index < count) ? arr[index] : "undefined"; }
static inline s32 string_to_enum(const char* str, const char* const* arr, s32 n) { for (s32 i=0; i<n; ++i) if (!strcmp(str, arr[i])) return i; return -1; }
static inline bool string_to_bool(const char* str) { if (strcmp(str, "true") == 0) return true; return false; }
template<typename T>
static inline s32 map_next_id_get(const std::map<s32, T>& map) {
s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id;
}
template<typename Map, typename Key>
static inline void map_swap(Map& map, const Key& key1, const Key& key2)
{ {
SDL_Window* window; auto it1 = map.find(key1);
SDL_Renderer* renderer; auto it2 = map.find(key2);
SDL_GLContext glContext; if (it1 == map.end() || it2 == map.end())
vec3 clearColor = {0.69, 0.69, 0.69}; return;
u64 tick = 0;
u64 lastTick = 0; using std::swap;
bool isRunning = true; swap(it1->second, it2->second);
}; }
#define DEFINE_ENUM_TO_STRING_FN(fn_name, arr, count) \
static inline const char* fn_name(s32 index) { \
return enum_to_string(arr, count, index); \
}
#define DEFINE_STRING_TO_ENUM_FN(fn_name, enum_type, str_array, count) \
static inline enum_type fn_name(const char* str) { \
return (enum_type)string_to_enum(str, str_array, count); \
}

302
src/PACKED.h Normal file
View File

@ -0,0 +1,302 @@
#pragma once
#include "COMMON.h"
struct PackedData
{
const u8* data;
u32 length;
};
static const unsigned char texture_eye_closed[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x88, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xb5, 0x52, 0x41, 0x0e, 0xc0, 0x20, 0x08, 0x13,
0xb2, 0x7f, 0xab, 0x2f, 0xdf, 0xe4, 0xc0, 0xd2, 0x90, 0x82, 0xcb, 0xb2,
0x71, 0x32, 0xd0, 0x4a, 0xa1, 0xb4, 0xf6, 0x47, 0x8c, 0x31, 0xce, 0x18,
0x96, 0x63, 0x58, 0x89, 0x09, 0x03, 0x57, 0x9f, 0xcb, 0x8a, 0xf4, 0x03,
0x24, 0x47, 0x60, 0x56, 0x93, 0x08, 0x98, 0x73, 0xd2, 0xce, 0xbd, 0x77,
0xaa, 0x44, 0x7d, 0xe6, 0x4a, 0x76, 0x24, 0x23, 0x47, 0x76, 0xdd, 0x91,
0xec, 0x75, 0xcf, 0x99, 0x0a, 0x7d, 0xda, 0x39, 0x1b, 0x4d, 0x51, 0xfe,
0x7a, 0xcb, 0x8e, 0x8c, 0x98, 0x9b, 0x1b, 0xbd, 0x46, 0xff, 0xab, 0x1b,
0xb9, 0x77, 0x60, 0x49, 0xb6, 0xa8, 0x6a, 0x27, 0x56, 0x33, 0x35, 0x92,
0x1d, 0xd0, 0xce, 0x4e, 0xb7, 0xf1, 0x40, 0x5f, 0xab, 0x2b, 0x44, 0x85,
0xf4, 0x90, 0xde, 0x9e, 0xb2, 0x32, 0x00, 0x93, 0x6f, 0xb9, 0x48, 0xfe,
0x24, 0x2e, 0x5d, 0x0c, 0x84, 0xee, 0xa1, 0x31, 0xa2, 0x43, 0x00, 0x00,
0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_eye_closed_len = 214;
static const unsigned char texture_animation[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x69, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xcd, 0x51, 0x41, 0x0e, 0xc0, 0x20, 0x08, 0x83,
0xc5, 0x7f, 0x03, 0x2f, 0x77, 0x72, 0x20, 0x61, 0x99, 0x0c, 0x8c, 0xcb,
0xb2, 0x5e, 0x14, 0x05, 0x4a, 0x0b, 0xc0, 0x97, 0x60, 0xe6, 0x6e, 0xb0,
0xb7, 0x96, 0x15, 0x59, 0xb2, 0x88, 0xd4, 0x99, 0x8c, 0xc5, 0x33, 0xea,
0x7d, 0x96, 0xdb, 0x32, 0x26, 0x1c, 0x78, 0x22, 0x9b, 0x4a, 0xc8, 0x8a,
0x2e, 0xb9, 0xde, 0x20, 0x3d, 0x89, 0x08, 0xfc, 0x24, 0x51, 0x6c, 0x24,
0xc7, 0x6b, 0x2b, 0xf2, 0xab, 0xa9, 0xac, 0xf3, 0xe6, 0x81, 0x8e, 0x18,
0x39, 0xbd, 0xdc, 0x35, 0xfb, 0xf3, 0xf1, 0xb6, 0x07, 0xdb, 0x0d, 0xb0,
0x22, 0x21, 0x90, 0x85, 0xf0, 0x0b, 0x9c, 0x7f, 0x4e, 0x4a, 0x7e, 0x6e,
0xdb, 0xca, 0xac, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82
};
static const unsigned int texture_animation_len = 183;
static const unsigned char texture_event[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5e, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xcd, 0x91, 0x49, 0x0e, 0x00, 0x21, 0x08, 0x04,
0x71, 0xe2, 0xbf, 0x81, 0x97, 0xbb, 0x25, 0x24, 0x73, 0x40, 0x6c, 0x2f,
0x6a, 0x5d, 0xa1, 0x41, 0x0b, 0xa2, 0x13, 0x88, 0x48, 0xe9, 0x78, 0xb5,
0xbc, 0x0a, 0x5b, 0x50, 0x55, 0xdd, 0x7a, 0x42, 0xc2, 0xa3, 0xb1, 0xb1,
0x35, 0x00, 0x09, 0x4f, 0x07, 0xa0, 0x61, 0x77, 0xc0, 0x3f, 0x6c, 0xff,
0x6e, 0x12, 0xd3, 0x96, 0xed, 0x88, 0xa5, 0xf1, 0x88, 0xbe, 0x00, 0x96,
0x68, 0xcd, 0xcc, 0x4c, 0x91, 0x8f, 0xfb, 0x67, 0xfc, 0xa2, 0xe7, 0x23,
0x67, 0x9c, 0x6e, 0xf7, 0x84, 0xbd, 0x49, 0x05, 0xbd, 0xc3, 0x78, 0xc4,
0x32, 0xe6, 0xac, 0x29, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_event_len = 172;
static const unsigned char texture_eye_open[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x64, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x05, 0x58, 0x41, 0x43, 0x43,
0xc3, 0x7f, 0x74, 0x00, 0x12, 0x23, 0x4b, 0x23, 0x21, 0x83, 0x18, 0x91,
0x39, 0x20, 0x05, 0x20, 0xba, 0xb1, 0xb1, 0x11, 0xab, 0x05, 0xf5, 0xf5,
0xf5, 0x10, 0x4d, 0x40, 0x80, 0xd5, 0x66, 0x10, 0x8d, 0xcc, 0x46, 0xb6,
0x15, 0x9b, 0x3c, 0xdc, 0x56, 0x74, 0xa7, 0xc1, 0x5c, 0x02, 0x93, 0x43,
0x16, 0x43, 0xf7, 0x2a, 0x8a, 0x00, 0xb2, 0x01, 0xc8, 0x06, 0x12, 0x92,
0xc7, 0x08, 0x03, 0x64, 0xdb, 0x51, 0x6c, 0x42, 0x53, 0x83, 0x33, 0x16,
0xb0, 0x99, 0x8e, 0x4f, 0x8e, 0xe8, 0xa8, 0x24, 0x2a, 0x2d, 0x0c, 0x3d,
0x00, 0x00, 0xde, 0x2d, 0xf2, 0x3a, 0xa5, 0xc2, 0xad, 0x18, 0x00, 0x00,
0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_eye_open_len = 178;
static const unsigned char texture_layer[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x6b, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xb5, 0x90, 0x8b, 0x0a, 0xc0, 0x20, 0x08, 0x45,
0x33, 0xf6, 0xdf, 0xea, 0x97, 0x57, 0x83, 0x09, 0x19, 0x3e, 0x16, 0x5b,
0x17, 0xc2, 0x44, 0xcf, 0x35, 0x2b, 0xe5, 0xa4, 0xda, 0xa3, 0xa8, 0x07,
0x3c, 0xd0, 0x6c, 0x1e, 0x0a, 0x0d, 0xb2, 0x69, 0x96, 0x11, 0xec, 0x80,
0x96, 0x51, 0xfd, 0xfa, 0x4f, 0xff, 0xac, 0x40, 0x44, 0x0a, 0x44, 0x44,
0x13, 0x64, 0x66, 0x95, 0x0f, 0x0e, 0xae, 0xa8, 0x51, 0x8c, 0x56, 0xd0,
0x5d, 0x61, 0x7d, 0x89, 0xa7, 0x7b, 0xb2, 0xdc, 0xeb, 0x0c, 0xcf, 0x85,
0x0c, 0x54, 0xc3, 0x24, 0xb1, 0xa2, 0x1c, 0xab, 0xee, 0x4d, 0x69, 0x6f,
0x62, 0xb6, 0xe3, 0x3e, 0x94, 0x99, 0x1d, 0x53, 0x07, 0x98, 0x83, 0x59,
0xf9, 0x38, 0x5d, 0x6e, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_layer_len = 185;
static const unsigned char texture_null[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5f, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xb5, 0x52, 0xcb, 0x0a, 0x00, 0x20, 0x08, 0xab,
0xbe, 0x5c, 0xbf, 0xbc, 0xc7, 0x41, 0xa8, 0x70, 0x91, 0x69, 0x3b, 0x09,
0xce, 0xa1, 0x9b, 0x29, 0xfd, 0x02, 0x11, 0x55, 0xc1, 0xa8, 0x11, 0x2f,
0xa3, 0xc6, 0x18, 0x5c, 0x88, 0x1d, 0x1a, 0xaf, 0x78, 0x37, 0x85, 0x02,
0xcc, 0xac, 0xd6, 0x26, 0x88, 0x07, 0x4f, 0x1b, 0xb8, 0x4f, 0x70, 0x09,
0x48, 0x84, 0xf3, 0x29, 0xa7, 0x28, 0x61, 0xfe, 0xbb, 0x17, 0x50, 0x64,
0x1e, 0x42, 0xc6, 0xed, 0x7d, 0x11, 0xcb, 0x96, 0xa7, 0xd1, 0x78, 0x31,
0x26, 0xde, 0x3e, 0x4d, 0xc8, 0x73, 0x85, 0xa3, 0x01, 0xa9, 0x8a, 0x70,
0xd3, 0x3d, 0xa4, 0xfe, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_null_len = 173;
static const unsigned char texture_rect_hide[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x88, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xb5, 0x93, 0x51, 0x0e, 0x80, 0x20, 0x0c, 0x43,
0x65, 0x17, 0x07, 0x4e, 0xae, 0xc1, 0x64, 0xa6, 0xab, 0x1d, 0x12, 0x8d,
0xfd, 0x31, 0x91, 0xbe, 0x8d, 0xcd, 0xba, 0x6d, 0x7f, 0xa8, 0xb5, 0xb6,
0xb3, 0xc6, 0x3b, 0xe5, 0x2d, 0x08, 0x8d, 0x67, 0xad, 0x75, 0x5a, 0xbc,
0xf7, 0xee, 0xfe, 0x93, 0x35, 0x3c, 0x44, 0xb8, 0x90, 0x94, 0x27, 0xe8,
0xe9, 0xaa, 0x3c, 0x52, 0x3a, 0xf3, 0x0a, 0x8c, 0x8d, 0x8a, 0x1b, 0xfc,
0xda, 0x0a, 0xc6, 0xb1, 0xd8, 0x6f, 0xb3, 0x85, 0x29, 0x98, 0x65, 0x5f,
0xe0, 0xb4, 0xc0, 0x2a, 0x7c, 0x15, 0xf0, 0x6f, 0xcb, 0x8b, 0x44, 0x78,
0x2c, 0xcd, 0x97, 0x8d, 0x8c, 0x61, 0x28, 0x42, 0xc2, 0x08, 0xce, 0x6e,
0x10, 0x4c, 0x1c, 0xa4, 0x2c, 0xa1, 0xde, 0xfd, 0x96, 0x05, 0xf5, 0x0f,
0x64, 0x01, 0x0a, 0x39, 0x50, 0xc5, 0x38, 0xb2, 0xdc, 0x55, 0x8d, 0xfd,
0x4a, 0x07, 0xf4, 0xc4, 0xc2, 0x2e, 0xb7, 0xd3, 0xf8, 0x61, 0x00, 0x00,
0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_rect_hide_len = 214;
static const unsigned char texture_rect_show[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5c, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x34, 0xa0, 0xa1, 0xa1, 0xe1,
0x3f, 0xb1, 0x00, 0xa4, 0x16, 0xc3, 0x00, 0x9c, 0x12, 0x38, 0x2c, 0xc2,
0x6a, 0x00, 0xb1, 0xae, 0x45, 0x56, 0xcb, 0x44, 0xa9, 0xd7, 0x87, 0xa8,
0x01, 0x8d, 0x8d, 0x8d, 0x0c, 0x14, 0x05, 0x22, 0x32, 0x60, 0x21, 0x27,
0xe4, 0x41, 0x80, 0x11, 0x08, 0x88, 0xf6, 0x02, 0x48, 0x33, 0xb2, 0xb3,
0x41, 0x6c, 0x0c, 0x17, 0xe3, 0x4a, 0x48, 0xc8, 0xe2, 0xb0, 0x94, 0x88,
0x35, 0x41, 0xa1, 0x27, 0x65, 0x64, 0xc3, 0xd0, 0xf9, 0xc8, 0x9a, 0x19,
0x29, 0x0d, 0x03, 0x8a, 0x01, 0x00, 0x43, 0x33, 0x93, 0xa2, 0x31, 0x56,
0x19, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42,
0x60, 0x82
};
static const unsigned int texture_rect_show_len = 170;
static const unsigned char texture_root[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x4d, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0xfe, 0xa0, 0xa1, 0xa1, 0xe1,
0x3f, 0x3e, 0xc0, 0x48, 0xc8, 0x00, 0x90, 0x22, 0x8a, 0x6d, 0x27, 0xdb,
0x00, 0x98, 0x33, 0xf1, 0xa9, 0x61, 0x22, 0x56, 0x21, 0x5e, 0x03, 0x90,
0x9d, 0x8c, 0xce, 0x6e, 0x6c, 0x6c, 0x24, 0x2d, 0xa4, 0x49, 0x71, 0x3e,
0x5e, 0xff, 0x12, 0x6b, 0x00, 0xdc, 0x0b, 0x30, 0xa7, 0x22, 0x6b, 0x24,
0xc6, 0xf9, 0x8c, 0xf8, 0xe2, 0x9c, 0x11, 0x08, 0x68, 0x12, 0x75, 0x83,
0x0b, 0x00, 0x00, 0xff, 0xce, 0x78, 0xa9, 0x56, 0xe2, 0xe3, 0x36, 0x00,
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_root_len = 155;
static const unsigned char texture_spritesheet[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x72, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xd5, 0x51, 0x41, 0x0e, 0x80, 0x30, 0x08, 0x63,
0xc6, 0x7f, 0x0f, 0x5e, 0x8e, 0xd3, 0xc8, 0xd2, 0x6d, 0x64, 0xc3, 0x78,
0x30, 0x36, 0xe1, 0xd2, 0xb4, 0x40, 0x81, 0xe8, 0x2b, 0x30, 0xb3, 0xbe,
0x32, 0x6a, 0xc1, 0x54, 0x84, 0xc0, 0x69, 0xc8, 0x87, 0x8c, 0xc6, 0xe1,
0x64, 0x6c, 0xb0, 0xa1, 0x39, 0xe7, 0x4c, 0x22, 0x72, 0xd5, 0x2a, 0xb7,
0x69, 0x76, 0x34, 0xf7, 0xf0, 0xb8, 0xd3, 0x58, 0xf4, 0xa9, 0x21, 0x35,
0x00, 0x8c, 0x87, 0xde, 0x1a, 0x21, 0xdd, 0x58, 0x7d, 0xc1, 0xd5, 0x60,
0xd7, 0x27, 0xd3, 0xeb, 0x0d, 0xbc, 0xbf, 0xda, 0x91, 0xbc, 0x3b, 0xb8,
0x11, 0x22, 0x31, 0x22, 0x11, 0x87, 0x18, 0xb3, 0xf5, 0x87, 0x0d, 0xfa,
0xf5, 0xff, 0x81, 0x03, 0xfa, 0x3e, 0xcf, 0x06, 0xd1, 0x47, 0xd3, 0x66,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_spritesheet_len = 192;
static const unsigned char texture_trigger[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x5f, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0xd5, 0x92, 0x01, 0x0a, 0x00, 0x20, 0x08, 0x03,
0xab, 0x97, 0xeb, 0xcb, 0x2d, 0x21, 0x41, 0x64, 0x42, 0x51, 0x10, 0x1d,
0x44, 0x20, 0x6e, 0xc8, 0xb4, 0x94, 0x5b, 0xc8, 0x64, 0x57, 0xd7, 0x62,
0x81, 0x88, 0x44, 0x9f, 0xa2, 0x3f, 0x12, 0xc1, 0xba, 0x09, 0xfd, 0x34,
0xd9, 0x44, 0xb0, 0xee, 0x05, 0x12, 0x30, 0x63, 0x9b, 0xcc, 0x1b, 0xd4,
0xe8, 0x5a, 0x07, 0x2b, 0x59, 0x68, 0x1f, 0xcc, 0xe0, 0x38, 0xc4, 0x15,
0x98, 0x39, 0x5f, 0x63, 0xdc, 0x80, 0x00, 0xb6, 0xee, 0xc0, 0x07, 0x97,
0xad, 0xf5, 0xee, 0x21, 0xfd, 0x67, 0xf0, 0x9e, 0x0e, 0x79, 0xfa, 0x9b,
0x11, 0x47, 0x7c, 0xbe, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_trigger_len = 173;
static const unsigned char texture_arrow_up[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x43, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0xa0, 0x25, 0xf8, 0x0f, 0x05, 0xf8,
0xd4, 0x30, 0xe2, 0xd3, 0x8c, 0xa2, 0x10, 0x08, 0x88, 0x36, 0x00, 0x97,
0xad, 0xd8, 0x0c, 0x61, 0x24, 0x56, 0x33, 0x2e, 0x43, 0x18, 0x49, 0xd1,
0x8c, 0xcd, 0x10, 0x46, 0x52, 0x35, 0xa3, 0x1b, 0xc2, 0x48, 0x8e, 0x66,
0x64, 0x43, 0x28, 0x8e, 0x05, 0x26, 0x4a, 0xd3, 0xca, 0xa8, 0x01, 0x54,
0x30, 0x80, 0x62, 0x00, 0x00, 0xbc, 0x66, 0x24, 0x0c, 0xf7, 0xc3, 0x33,
0x72, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
0x82
};
static const unsigned int texture_arrow_up_len = 145;
static const unsigned char texture_arrow_down[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x5c, 0x46, 0x00, 0x00, 0x5c,
0x46, 0x01, 0x14, 0x94, 0x43, 0x41, 0x00, 0x00, 0x00, 0x4b, 0x49, 0x44,
0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0xa0, 0x10, 0x30, 0xe2, 0x92, 0xf8,
0x0f, 0x04, 0x28, 0x0a, 0x81, 0x00, 0x9b, 0x3a, 0x26, 0x4a, 0x5d, 0x30,
0x6a, 0x00, 0x15, 0x0c, 0x60, 0xc4, 0x16, 0x65, 0x44, 0x6b, 0x46, 0x8e,
0x5b, 0x52, 0x0d, 0x81, 0xe9, 0x65, 0xc4, 0x97, 0x78, 0x08, 0x69, 0xc6,
0x9a, 0x12, 0x09, 0x19, 0x82, 0x9e, 0x22, 0x19, 0x89, 0x49, 0xc6, 0xf8,
0x92, 0x33, 0xc5, 0x79, 0x01, 0x2f, 0xf8, 0x0f, 0x05, 0x0c, 0xb4, 0x04,
0x00, 0x05, 0x8c, 0x24, 0x0c, 0x43, 0x26, 0x91, 0x46, 0x00, 0x00, 0x00,
0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned int texture_arrow_down_len = 153;
#define PACKED_TEXTURE_COUNT (PACKED_TEXTURE_ARROW_DOWN + 1)
enum PackedTextureType
{
PACKED_TEXTURE_EYE_OPEN,
PACKED_TEXTURE_EYE_CLOSED,
PACKED_TEXTURE_ANIMATION,
PACKED_TEXTURE_EVENT,
PACKED_TEXTURE_LAYER,
PACKED_TEXTURE_NULL,
PACKED_TEXTURE_ROOT,
PACKED_TEXTURE_SPRITESHEET,
PACKED_TEXTURE_TRIGGER,
PACKED_TEXTURE_RECT_SHOW,
PACKED_TEXTURE_RECT_HIDE,
PACKED_TEXTURE_ARROW_UP,
PACKED_TEXTURE_ARROW_DOWN
};
static const PackedData PACKED_TEXTURE_DATA[PACKED_TEXTURE_COUNT] =
{
{texture_eye_open, texture_eye_open_len},
{texture_eye_closed, texture_eye_closed_len},
{texture_animation, texture_animation_len},
{texture_event, texture_event_len},
{texture_layer, texture_layer_len},
{texture_null, texture_null_len},
{texture_root, texture_root_len},
{texture_spritesheet, texture_spritesheet_len},
{texture_trigger, texture_trigger_len},
{texture_rect_show, texture_rect_show_len},
{texture_rect_hide, texture_rect_hide_len},
{texture_arrow_up, texture_arrow_up_len},
{texture_arrow_down, texture_arrow_down_len},
};

View File

@ -1,4 +0,0 @@
#pragma once
#define RESOURCE_SHADER_VERTEX_TEXTURE_QUAD "res/shader/texture_quad.vs"
#define RESOURCE_SHADER_FRAGMENT_TEXTURE_QUAD "res/shader/texture_quad.fs"

View File

@ -1,13 +1,18 @@
#pragma once #pragma once
#define STRING_UNDEFINED "undefined"
#define STRING_EMPTY ""
#define STRING_WINDOW_TITLE "Anm2Ed" #define STRING_WINDOW_TITLE "Anm2Ed"
#define STRING_WINDOW_TITLE_EDITING "Anm2Ed (%s)"
#define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL (%s)\n" #define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL (%s)\n"
#define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n" #define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n"
#define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %s\n" #define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %s\n"
#define STRING_ERROR_SHADER_INIT "[ERROR] Could not initialize shader: %s\n%s" #define STRING_ERROR_SHADER_INIT "[ERROR] Could not initialize shader: %i\n%s"
#define STRING_ERROR_TEXTURE_INIT "[ERROR] Could not initialize texture: %s (%s)\n" #define STRING_ERROR_TEXTURE_INIT "[ERROR] Could not initialize texture: %s\n"
#define STRING_ERROR_ANM2_INIT "[ERROR] Could not initialize anm2: %s (%s)\n" #define STRING_ERROR_ANM2_READ "[ERROR] Could not read anm2 from file %s: %s\n"
#define STRING_ERROR_ANM2_WRITE "[ERROR] Could not write anm2 to file %s: %s\n"
#define STRING_INFO_INIT "[INFO] Initializing\n" #define STRING_INFO_INIT "[INFO] Initializing\n"
#define STRING_INFO_QUIT "[INFO] Exited\n" #define STRING_INFO_QUIT "[INFO] Exited\n"
@ -16,11 +21,201 @@
#define STRING_INFO_GLEW_INIT "[INFO] Initialized GLEW\n" #define STRING_INFO_GLEW_INIT "[INFO] Initialized GLEW\n"
#define STRING_INFO_GL_CONTEXT_INIT "[INFO] Initialized OpenGL context\n" #define STRING_INFO_GL_CONTEXT_INIT "[INFO] Initialized OpenGL context\n"
#define STRING_INFO_FILE_READ "[INFO] Read from file: %s\n" #define STRING_INFO_FILE_READ "[INFO] Read from file: %s\n"
#define STRING_INFO_SHADER_INIT "[INFO] Initialized shader: %s & %s\n" #define STRING_INFO_SHADER_INIT "[INFO] Initialized shader: %i\n"
#define STRING_INFO_TEXTURE_INIT "[INFO] Initialized texture: %s\n" #define STRING_INFO_TEXTURE_INIT "[INFO] Initialized texture: %s\n"
#define STRING_INFO_ANM2_INIT "[INFO] Initialized anm2: %s\n" #define STRING_INFO_ANM2_WRITE "[INFO] Wrote anm2 to file: %s\n"
#define STRING_INFO_ANM2_READ "[INFO] Read anm2 from file: %s\n"
#define STRING_INFO_IMGUI_INIT "[INFO] Initialized Dear Imgui\n" #define STRING_INFO_IMGUI_INIT "[INFO] Initialized Dear Imgui\n"
#define STRING_INFO_IMGUI_FREE "[INFO] Freed Dear Imgui\n" #define STRING_INFO_IMGUI_FREE "[INFO] Freed Dear Imgui\n"
#define STRING_DIALOG_ANM2_READ "Select .anm2 file to read..."
#define STRING_DIALOG_ANM2_WRITE "Select .anm2 file to write to..."
#define STRING_DIALOG_ANM2_FILTER "Anm2 Files (*.anm2)"
#define STRING_DIALOG_ANM2_FILTER_WILDCARD "*.anm2"
#define STRING_DIALOG_ALL_FILTER "All Files (*.*)"
#define STRING_DIALOG_ALL_FILTER_WILDCARD "*"
#define STRING_ANM2_CREATED_BY_DEFAULT "Unknown"
#define STRING_ANM2_NEW_ANIMATION "New Animation"
#define STRING_ANM2_NEW_LAYER "New Layer"
#define STRING_ANM2_NEW_NULL "New Null"
#define STRING_ANM2_NEW_EVENT "New Event"
#define STRING_ANM2_ROOT "Root"
#define STRING_IMGUI_WINDOW "Window"
#define STRING_IMGUI_DOCKSPACE "Dockspace"
#define STRING_IMGUI_TASKBAR "Taskbar"
#define STRING_IMGUI_TASKBAR_FILE "File"
#define STRING_IMGUI_FILE_MENU "File Menu"
#define STRING_IMGUI_FILE_NEW "New"
#define STRING_IMGUI_FILE_OPEN "Open"
#define STRING_IMGUI_FILE_SAVE "Save"
#define STRING_IMGUI_FILE_SAVE_AS "Save As"
#define STRING_IMGUI_PROPERTIES "Properties"
#define STRING_IMGUI_PROPERTIES_FPS "FPS"
#define STRING_IMGUI_PROPERTIES_FPS_LABEL "##FPS"
#define STRING_IMGUI_PROPERTIES_CREATED_BY "Created by:"
#define STRING_IMGUI_PROPERTIES_CREATED_BY_LABEL "##Created By"
#define STRING_IMGUI_PROPERTIES_CREATED_ON "Created on: %s"
#define STRING_IMGUI_PROPERTIES_VERSION "Version: %i"
#define STRING_IMGUI_ANIMATIONS "Animations"
#define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation"
#define STRING_IMGUI_ANIMATIONS_ADD "Add"
#define STRING_IMGUI_ANIMATIONS_REMOVE "Remove"
#define STRING_IMGUI_ANIMATIONS_DUPLICATE "Duplicate"
#define STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT "Set as Default"
#define STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT "%s (*)"
#define STRING_IMGUI_EVENTS "Events"
#define STRING_IMGUI_EVENTS_EVENT_LABEL "##Event"
#define STRING_IMGUI_EVENTS_ADD "Add"
#define STRING_IMGUI_EVENTS_REMOVE "Remove"
#define STRING_IMGUI_EVENT_FORMAT "#%i %s"
#define STRING_IMGUI_SPRITESHEETS "Spritesheets"
#define STRING_IMGUI_SPRITESHEETS_ADD "Add"
#define STRING_IMGUI_SPRITESHEETS_REMOVE "Remove"
#define STRING_IMGUI_SPRITESHEETS_RELOAD "Reload"
#define STRING_IMGUI_SPRITESHEETS_REPLACE "Replace"
#define STRING_IMGUI_SPRITESHEET_FORMAT "#%i %s"
#define STRING_IMGUI_FRAME_PROPERTIES "Frame Properties"
#define STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION "Crop Position"
#define STRING_IMGUI_FRAME_PROPERTIES_CROP_SIZE "Crop Size"
#define STRING_IMGUI_FRAME_PROPERTIES_POSITION "Position"
#define STRING_IMGUI_FRAME_PROPERTIES_PIVOT "Pivot"
#define STRING_IMGUI_FRAME_PROPERTIES_SCALE "Scale"
#define STRING_IMGUI_FRAME_PROPERTIES_ROTATION "Rotation"
#define STRING_IMGUI_FRAME_PROPERTIES_VISIBLE "Visible"
#define STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED "Interpolated"
#define STRING_IMGUI_FRAME_PROPERTIES_DURATION "Duration"
#define STRING_IMGUI_FRAME_PROPERTIES_TINT "Tint"
#define STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET "Color Offset"
#define STRING_IMGUI_ANIMATION_PREVIEW "Animation Preview"
#define STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS "##Animation Preview Settings"
#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS "##Grid Settings"
#define STRING_IMGUI_ANIMATION_PREVIEW_GRID "Grid"
#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE "##Grid Size"
#define STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR "Color"
#define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS "##Zoom Settings"
#define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM "Zoom"
#define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS "##Background Settings"
#define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR "Background Color"
#define STRING_IMGUI_SPRITESHEET_EDITOR "Spritesheet Editor"
#define STRING_IMGUI_TIMELINE "Timeline"
#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE "Shift Above"
#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW "Shift Below"
#define STRING_IMGUI_TIMELINE_ROOT "Root"
#define STRING_IMGUI_TIMELINE_ELEMENT_FORMAT "#%i %s"
#define STRING_IMGUI_TIMELINE_SPRITESHEET_FORMAT "#%i"
#define STRING_IMGUI_TIMELINE_SPRITESHEET_UNKNOWN "#?"
#define STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL "##Timeline Element Spritesheet ID"
#define STRING_IMGUI_TIMELINE_ELEMENT_NAME_LABEL "##Timeline Element Name"
#define STRING_IMGUI_TIMELINE_TRIGGERS "Triggers"
#define STRING_IMGUI_TIMELINE_ANIMATION_LABEL "##Timeline Animation"
#define STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ARROWS_LABEL "##Timeline Shift Arrows"
#define STRING_IMGUI_TIMELINE_ANIMATIONS "Animations"
#define STRING_IMGUI_TIMELINE_PLAY "|> Play"
#define STRING_IMGUI_TIMELINE_PAUSE "|| Pause"
#define STRING_IMGUI_TIMELINE_ADD_FRAME "+ Add Frame"
#define STRING_IMGUI_TIMELINE_REMOVE_FRAME "- Remove Frame"
#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "= Fit Animation Length"
#define STRING_IMGUI_TIMELINE_ANIMATION LENGTH "Animation Length:"
#define STRING_IMGUI_TIMELINE_VISIBLE "Visible"
#define STRING_IMGUI_TIMELINE_LAYER "Layer"
#define STRING_IMGUI_TIMELINE_NULL "Null"
#define STRING_IMGUI_TIMELINE_RECT "Rect"
#define STRING_IMGUI_TIMELINE_ELEMENTS "Elements"
#define STRING_IMGUI_TIMELINE_ELEMENT_ADD "Add"
#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU "Element Add Menu"
#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER "Add Layer..."
#define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL "Add Null..."
#define STRING_IMGUI_TIMELINE_ELEMENT_REMOVE "Remove"
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT "Select the animation to edit.\nYou can also click the name to edit it."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD "Add a new animation."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE "Duplicates the selected animation."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT "Sets the selected animation as the default.\nDefault animations are marked with \"(*)\"."
#define STRING_IMGUI_TOOLTIP_EVENTS_SELECT "Set the event for the trigger, or rename it."
#define STRING_IMGUI_TOOLTIP_EVENTS_ADD "Add a new event."
#define STRING_IMGUI_TOOLTIP_EVENTS_REMOVE "Removes the selected event."
#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD "Opens the file dialog to load in a new sprite."
#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE "Removes the selected spritesheet."
#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD "Reloads the selected spritesheet."
#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE "Replaces the selected spritesheet; opens up the file dialog."
#define STRING_IMGUI_TOOLTIP_PROPERTIES_FPS "Change the FPS of the animation."
#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY "Change the author of the animation."
#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_ON_NOW "Set the date of creation to the current system time."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION "Change the position of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION "Change the crop position of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE "Change the crop size of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT "Change the pivot of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE "Change the scale of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION "Change the rotation of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION "Change the duration of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT "Change the tint of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET "Change the color offset of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE "Toggles the visibility of the frame."
#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED "Toggles the interpolation of the frame.\nBetween keyframes, will transform the values in the in-betweens to be smooth."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD "Add a layer or null timeline element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NAME "Click to rename the element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET "Click to change the spritesheet the layer is using."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE "Remove a timeline element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP "Shift this timeline element above.\nElements with higher indices will display further behind."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN "Shift this timeline element below.\nElements with lower indices will display further in front."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT "Toggle visibility for a rectangle around the null."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT "This is the Root element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER "This is a Layer element.\nThese are the main graphical animation elements.\nClick to rename."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NULL "This is a Null element.\nThese are invisible elements where a game engine may have access to them for additional effects.\nClick to rename."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event."
#define STRING_IMGUI_TOOLTIP_TIMELINE_PLAY "Plays the animation."
#define STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE "Pauses the animation."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID "Toggles grid visibility on the animation preview."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR "Changes the animation preview grid color."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the animation preview grid size (number of rows/columns)."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview."
#define STRING_OPENGL_VERSION "#version 330" #define STRING_OPENGL_VERSION "#version 330"
#define SHADER_VERTEX "#version 330 core\n" \
"in vec2 l_position;\n" \
"in vec2 l_uv;\n" \
"\n" \
"uniform mat3 u_view;\n" \
"uniform mat3 u_projection;\n" \
"uniform mat3 u_model;\n" \
"\n" \
"out vec2 o_uv;\n" \
"\n" \
"void main()\n" \
"{\n" \
" gl_Position = vec4(u_projection * u_view * u_model * vec3(l_position, 1.0), 1.0);\n" \
" o_uv = l_uv;\n" \
"}"
#define SHADER_FRAGMENT "#version 330 core\n" \
"uniform vec4 u_tint;\n" \
"uniform vec4 u_color_offset;\n" \
"uniform sampler2D u_texture;\n" \
"\n" \
"in vec2 i_uv;\n" \
"\n" \
"out vec4 FragColor;\n" \
"\n" \
"void main()\n" \
"{\n" \
" FragColor = texture(u_texture, i_uv) * u_tint;\n" \
" FragColor.rgb += u_color_offset.rgb;\n" \
"\n" \
" if (FragColor.a == 0.0) discard;\n" \
"}\n"

View File

@ -1,32 +1,722 @@
#include "anm2.h" #include "anm2.h"
bool using namespace tinyxml2;
anm2_init(Anm2* self, const char* path)
/* Sets the anm2's date to the system's current date */
void
anm2_created_on_set(Anm2* self)
{ {
xml_document document; time_t currentTime;
xml_parse_result result; struct tm* local;
char date[ANM2_STRING_MAX];
memset(self, '\0', sizeof(Anm2)); currentTime = time(NULL);
local = localtime(&currentTime);
result = document.load_file(path); strftime(date, ANM2_STRING_MAX, "%d-%B-%Y %I:%M:%S %p", local);
strncpy(self->createdOn, date, ANM2_STRING_MAX);
}
if (!result) /* Serializes the anm2 struct into XML and exports it to the given path */
bool
anm2_serialize(Anm2* self, const char* path)
{
XMLDocument document;
XMLError error;
XMLElement* animatedActorElement;
XMLElement* infoElement;
XMLElement* contentElement;
XMLElement* spritesheetsElement;
XMLElement* layersElement;
XMLElement* nullsElement;
XMLElement* eventsElement;
XMLElement* animationsElement;
if (!self || !path)
return false;
/* Update creation date on first version */
if (self->version == 0)
anm2_created_on_set(self);
/* Increment anm2's version */
self->version++;
/* Set the anm2's date to the system time */
/* AnimatedActor */
animatedActorElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
document.InsertFirstChild(animatedActorElement);
/* Info */
infoElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_INFO]);
infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_BY], self->createdBy); /* CreatedBy */
infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_ON], self->createdOn); /* CreatedOn */
infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VERSION], self->version); /* Version; note its incrementation */
infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FPS], self->fps); /* FPS */
animatedActorElement->InsertEndChild(infoElement);
/* Content */
contentElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_CONTENT]);
/* Spritesheets */
spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]);
for (auto & [id, spritesheet] : self->spritesheets)
{ {
printf(STRING_ERROR_ANM2_INIT, path, result.description()); XMLElement* spritesheetElement;
/* Spritesheet */
spritesheetElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]);
spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_PATH], spritesheet.path); /* Path */
spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */
spritesheetsElement->InsertEndChild(spritesheetElement);
}
contentElement->InsertEndChild(spritesheetsElement);
/* Layers */
layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]);
for (auto & [id, layer] : self->layers)
{
XMLElement* layerElement;
/* Layer */
layerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]);
layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], layer.name); /* Path */
layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */
layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SPRITESHEET_ID], layer.spritesheetID); /* SpritesheetId */
layersElement->InsertEndChild(layerElement);
}
contentElement->InsertEndChild(layersElement);
/* Nulls */
nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]);
for (auto & [id, null] : self->nulls)
{
XMLElement* nullElement;
/* Null */
nullElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]);
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], null.name); /* Name */
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */
/* special case; only serialize if this is true */
if (null.isShowRect)
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); /* ShowRect */
nullsElement->InsertEndChild(nullElement);
}
contentElement->InsertEndChild(nullsElement);
/* Events */
eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]);
for (auto & [id, event] : self->events)
{
XMLElement* eventElement;
/* Event */
eventElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]);
eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], event.name); /* Name */
eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); /* ID */
eventsElement->InsertEndChild(eventElement);
}
contentElement->InsertEndChild(eventsElement);
animatedActorElement->InsertEndChild(contentElement);
/* Animations */
animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]);
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->defaultAnimation);
for (auto & [id, animation] : self->animations)
{
XMLElement* animationElement;
XMLElement* rootAnimationElement;
XMLElement* layerAnimationsElement;
XMLElement* nullAnimationsElement;
XMLElement* triggersElement;
/* Animation */
animationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]);
animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], animation.name); /* Name */
animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FRAME_NUM], animation.frameNum); /* FrameNum */
animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LOOP], animation.isLoop); /* Loop */
/* RootAnimation */
rootAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION]);
for (auto & frame : animation.rootAnimation.frames)
{
XMLElement* frameElement;
/* Frame */
frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */
rootAnimationElement->InsertEndChild(frameElement);
}
animationElement->InsertEndChild(rootAnimationElement);
/* LayerAnimations */
layerAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]);
for (const auto & [layerID, layerAnimation] : animation.layerAnimations)
{
XMLElement* layerAnimationElement;
/* LayerAnimation */
layerAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]);
layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], layerID); /* LayerID */
layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); /* Visible */
for (auto & frame : layerAnimation.frames)
{
XMLElement* frameElement;
/* Frame */
frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_CROP], frame.crop.x); /* XCrop */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_CROP], frame.crop.y); /* YCrop */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_WIDTH], frame.size.x); /* Width */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_HEIGHT], frame.size.y); /* Height */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */
layerAnimationElement->InsertEndChild(frameElement);
}
layerAnimationsElement->InsertEndChild(layerAnimationElement);
}
animationElement->InsertEndChild(layerAnimationsElement);
/* NullAnimations */
nullAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]);
for (const auto & [nullID, nullAnimation] : animation.nullAnimations)
{
XMLElement* nullAnimationElement;
/* NullAnimation */
nullAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]);
nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], nullID); /* NullID */
nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], nullAnimation.isVisible); /* Visible */
for (auto & frame : nullAnimation.frames)
{
XMLElement* frameElement;
/* Frame */
frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); /* XPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); /* YPosition */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); /* XPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); /* YPivot */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); /* XScale */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); /* Visible */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], frame.tintRGBA.r); /* RedTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], frame.tintRGBA.g); /* GreenTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], frame.tintRGBA.b); /* BlueTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], frame.tintRGBA.a); /* AlphaTint */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], frame.offsetRGB.r); /* RedOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], frame.offsetRGB.g); /* GreenOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], frame.offsetRGB.b); /* BlueOffset */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); /* Rotation */
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); /* Interpolated */
nullAnimationElement->InsertEndChild(frameElement);
}
nullAnimationsElement->InsertEndChild(nullAnimationElement);
}
animationElement->InsertEndChild(nullAnimationsElement);
/* Triggers */
triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]);
for (auto & trigger : animation.triggers.items)
{
XMLElement* triggerElement;
/* Trigger */
triggerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]);
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], trigger.eventID); /* EventID */
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], trigger.atFrame); /* AtFrame */
triggersElement->InsertEndChild(triggerElement);
}
animationElement->InsertEndChild(triggersElement);
animationsElement->InsertEndChild(animationElement);
}
animatedActorElement->InsertEndChild(animationsElement);
error = document.SaveFile(path);
if (error != XML_SUCCESS)
{
printf(STRING_ERROR_ANM2_WRITE, path, document.ErrorStr());
return false; return false;
} }
for (pugi::xml_node node : document.children()) printf(STRING_INFO_ANM2_WRITE, path);
printf("%s\n", node.name()); strncpy(self->path, path, PATH_MAX - 1);
return true;
}
printf(STRING_INFO_ANM2_INIT, path); /* Loads the .anm2 file and deserializes it into the struct equivalent */
bool
anm2_deserialize(Anm2* self, const char* path)
{
XMLDocument document;
XMLError error;
const XMLElement* element;
const XMLElement* root;
Anm2Spritesheet* lastSpritesheet = NULL;
Anm2Layer* lastLayer = NULL;
Anm2Null* lastNull = NULL;
Anm2Event* lastEvent = NULL;
Anm2Animation* lastAnimation = NULL;
Anm2LayerAnimation* lastLayerAnimation = NULL;
Anm2NullAnimation* lastNullAnimation = NULL;
Anm2Frame* lastFrame = NULL;
Anm2Trigger* lastTrigger = NULL;
Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR;
Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID;
Anm2AnimationType animationType = ANM2_ROOT_ANIMATION;
Anm2Null tempNull;
Anm2Layer tempLayer;
Anm2Spritesheet tempSpritesheet;
Anm2Event tempEvent;
*self = Anm2{};
error = document.LoadFile(path);
if (error != XML_SUCCESS)
{
printf(STRING_ERROR_ANM2_READ, path, document.ErrorStr());
return false;
}
strncpy(self->path, path, PATH_MAX - 1);
root = document.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
element = root;
while (element)
{
const XMLAttribute* attribute;
const XMLElement* child;
s32 id;
/* Elements */
anm2Element = anm2_element_from_string(element->Name());
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
lastSpritesheet = &tempSpritesheet;
break;
case ANM2_ELEMENT_LAYER:
lastLayer = &tempLayer;
break;
case ANM2_ELEMENT_NULL:
lastNull = &tempNull;
break;
case ANM2_ELEMENT_EVENT:
lastEvent = &tempEvent;
break;
case ANM2_ELEMENT_ANIMATION:
id = map_next_id_get(self->animations);
self->animations[id] = Anm2Animation{};
lastAnimation = &self->animations[id];
break;
case ANM2_ELEMENT_ROOT_ANIMATION:
animationType = ANM2_ROOT_ANIMATION;
break;
case ANM2_ELEMENT_LAYER_ANIMATION:
animationType = ANM2_LAYER_ANIMATION;
lastLayerAnimation = NULL;
break;
case ANM2_ELEMENT_NULL_ANIMATION:
animationType = ANM2_NULL_ANIMATION;
lastNullAnimation = NULL;
break;
case ANM2_ELEMENT_FRAME:
switch (animationType)
{
case ANM2_ROOT_ANIMATION:
lastAnimation->rootAnimation.frames.push_back(Anm2Frame{});
lastFrame = &lastAnimation->rootAnimation.frames.back();
break;
case ANM2_LAYER_ANIMATION:
if (!lastLayerAnimation) break;
lastLayerAnimation->frames.push_back(Anm2Frame{});
lastFrame = &lastLayerAnimation->frames.back();
break;
case ANM2_NULL_ANIMATION:
if (!lastNullAnimation) break;
lastNullAnimation->frames.push_back(Anm2Frame{});
lastFrame = &lastNullAnimation->frames.back();
break;
default:
break;
}
break;
case ANM2_ELEMENT_TRIGGER:
lastAnimation->triggers.items.push_back(Anm2Trigger{});
lastTrigger = &lastAnimation->triggers.items.back();
break;
default:
break;
}
/* Attributes */
attribute = element->FirstAttribute();
while (attribute)
{
anm2Attribute = anm2_attribute_from_string(attribute->Name());
switch (anm2Attribute)
{
case ANM2_ATTRIBUTE_CREATED_BY:
strncpy(self->createdBy, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_CREATED_ON:
strncpy(self->createdOn, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_VERSION:
self->version = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_FPS:
self->fps = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_ID:
id = atoi(attribute->Value());
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
self->spritesheets[id] = tempSpritesheet;
lastSpritesheet = &self->spritesheets[id];
break;
case ANM2_ELEMENT_LAYER:
self->layers[id] = tempLayer;
lastLayer = &self->layers[id];
break;
case ANM2_ELEMENT_NULL:
self->nulls[id] = tempNull;
lastNull = &self->nulls[id];
break;
case ANM2_ELEMENT_EVENT:
self->events[id] = tempEvent;
lastEvent = &self->events[id];
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_PATH:
strncpy(lastSpritesheet->path, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_NAME:
switch (anm2Element)
{
case ANM2_ELEMENT_LAYER:
strncpy(lastLayer->name, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_NULL:
strncpy(lastNull->name, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_ANIMATION:
strncpy(lastAnimation->name, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_EVENT:
strncpy(lastEvent->name, attribute->Value(), ANM2_STRING_MAX - 1);
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_SPRITESHEET_ID:
lastLayer->spritesheetID = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_SHOW_RECT:
switch (anm2Element)
{
case ANM2_ELEMENT_NULL:
lastNull->isShowRect = string_to_bool(attribute->Value());
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_DEFAULT_ANIMATION:
strncpy(self->defaultAnimation, attribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_FRAME_NUM:
lastAnimation->frameNum = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_LOOP:
lastAnimation->isLoop = string_to_bool(attribute->Value());
break;
case ANM2_ATTRIBUTE_X_POSITION:
lastFrame->position.x = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_Y_POSITION:
lastFrame->position.y = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_X_PIVOT:
lastFrame->pivot.x = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_Y_PIVOT:
lastFrame->pivot.y = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_X_CROP:
lastFrame->crop.x = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_Y_CROP:
lastFrame->crop.y = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_WIDTH:
lastFrame->size.x = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_HEIGHT:
lastFrame->size.y = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_X_SCALE:
lastFrame->scale.x = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_Y_SCALE:
lastFrame->scale.y = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_DELAY:
lastFrame->delay = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_VISIBLE:
switch (anm2Element)
{
case ANM2_ELEMENT_FRAME:
lastFrame->isVisible = string_to_bool(attribute->Value());
break;
case ANM2_LAYER_ANIMATION:
lastLayerAnimation->isVisible = string_to_bool(attribute->Value());
break;
case ANM2_NULL_ANIMATION:
lastNullAnimation->isVisible = string_to_bool(attribute->Value());
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_RED_TINT:
lastFrame->tintRGBA.r = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_GREEN_TINT:
lastFrame->tintRGBA.g = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_BLUE_TINT:
lastFrame->tintRGBA.b = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_ALPHA_TINT:
lastFrame->tintRGBA.a = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_RED_OFFSET:
lastFrame->offsetRGB.r = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_GREEN_OFFSET:
lastFrame->offsetRGB.g = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_BLUE_OFFSET:
lastFrame->offsetRGB.b = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_ROTATION:
lastFrame->rotation = atof(attribute->Value());
break;
case ANM2_ATTRIBUTE_INTERPOLATED:
lastFrame->isInterpolated = string_to_bool(attribute->Value());
break;
case ANM2_ATTRIBUTE_LAYER_ID:
id = atoi(attribute->Value());
lastAnimation->layerAnimations[id] = Anm2LayerAnimation{};
lastLayerAnimation = &lastAnimation->layerAnimations[id];
break;
case ANM2_ATTRIBUTE_NULL_ID:
id = atoi(attribute->Value());
lastAnimation->nullAnimations[id] = Anm2NullAnimation{};
lastNullAnimation = &lastAnimation->nullAnimations[id];
break;
case ANM2_ATTRIBUTE_EVENT_ID:
lastTrigger->eventID = atoi(attribute->Value());
break;
case ANM2_ATTRIBUTE_AT_FRAME:
lastTrigger->atFrame = atoi(attribute->Value());
break;
default:
break;
}
attribute = attribute->Next();
}
/* Iterate through children */
child = element->FirstChildElement();
if (child)
{
element = child;
continue;
}
/* Iterate through siblings */
while (element)
{
const XMLElement* next;
next = element->NextSiblingElement();
if (next)
{
element = next;
break;
}
/* If no siblings, return to parent. If no parent, end parsing */
element = element->Parent() ? element->Parent()->ToElement() : NULL;
}
}
printf(STRING_INFO_ANM2_READ, path);
return true; return true;
} }
/* Adds a new layer to the anm2 */
void void
anm2_free(Anm2* self) anm2_layer_add(Anm2* self)
{ {
s32 id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{};
for (auto & [animationID, animation] : self->animations)
animation.layerAnimations[id] = Anm2LayerAnimation{};
} }
/* Removes a layer from the anm2 given the index/id */
void
anm2_layer_remove(Anm2* self, s32 id)
{
self->layers.erase(id);
for (auto& animationPair : self->animations)
animationPair.second.layerAnimations.erase(id);
}
/* Adds a new null to the anm2 */
void
anm2_null_add(Anm2* self)
{
s32 id = map_next_id_get(self->nulls);
self->nulls[id] = Anm2Null{};
for (auto & [animationID, animation] : self->animations)
animation.nullAnimations[id] = Anm2NullAnimation{};
}
/* Removes a null from the anm2 given the index/id */
void
anm2_null_remove(Anm2* self, s32 id)
{
self->nulls.erase(id);
for (auto& animationPair : self->animations)
animationPair.second.nullAnimations.erase(id);
}
/* Adds a new animation to the anm2, makes sure to keep the layeranimations/nullsanimation check */
s32
anm2_animation_add(Anm2* self)
{
s32 id = map_next_id_get(self->animations);
Anm2Animation animation;
/* match layers */
for (auto & [layerID, layer] : self->layers)
animation.layerAnimations[layerID] = Anm2LayerAnimation{};
/* match nulls */
for (auto & [nullID, null] : self->nulls)
animation.nullAnimations[nullID] = Anm2NullAnimation{};
self->animations[id] = animation;
return id;
}
void
anm2_animation_remove(Anm2* self, s32 id)
{
self->animations.erase(id);
}
/* Makes an entirely new anm2 */
void
anm2_new(Anm2* self)
{
*self = Anm2{};
anm2_created_on_set(self);
}

View File

@ -5,285 +5,216 @@
#define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f) #define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f)
#define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f) #define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f)
#define ANM2_STRING_MAX 255 #define ANM2_STRING_MAX 0xFF
#define ANM2_STRING_FORMATTED_MAX 0xFFF
#define ANM2_BUFFER_MAX 0xFFFFF
#define ANM2_FPS_MIN 0
#define ANM2_FPS_DEFAULT 30
#define ANM2_FPS_MAX 60
/* Elements */ /* Elements */
#define STRING_ANM2_ELEMENT_ANIMATED_ACTOR "AnimatedActor" #define ANM2_ELEMENT_LIST \
#define STRING_ANM2_ELEMENT_INFO "Info" X(ANIMATED_ACTOR, "AnimatedActor") \
#define STRING_ANM2_ELEMENT_CONTENT "Content" X(INFO, "Info") \
#define STRING_ANM2_ELEMENT_SPRITESHEETS "Spritesheets" X(CONTENT, "Content") \
#define STRING_ANM2_ELEMENT_SPRITESHEET "Spritesheet" X(SPRITESHEETS, "Spritesheets") \
#define STRING_ANM2_ELEMENT_LAYERS "Layers" X(SPRITESHEET, "Spritesheet") \
#define STRING_ANM2_ELEMENT_LAYER "Layer" X(LAYERS, "Layers") \
#define STRING_ANM2_ELEMENT_NULLS "Nulls" X(LAYER, "Layer") \
#define STRING_ANM2_ELEMENT_NULL "Null" X(NULLS, "Nulls") \
#define STRING_ANM2_ELEMENT_EVENTS "Events" X(NULL, "Null") \
#define STRING_ANM2_ELEMENT_EVENT "Event" X(EVENTS, "Events") \
#define STRING_ANM2_ELEMENT_ANIMATIONS "Animations" X(EVENT, "Event") \
#define STRING_ANM2_ELEMENT_ANIMATION "Animation" X(ANIMATIONS, "Animations") \
#define STRING_ANM2_ELEMENT_ROOT_ANIMATION "RootAnimation" X(ANIMATION, "Animation") \
#define STRING_ANM2_ELEMENT_FRAME "Frame" X(ROOT_ANIMATION, "RootAnimation") \
#define STRING_ANM2_ELEMENT_LAYER_ANIMATIONS "LayerAnimations" X(FRAME, "Frame") \
#define STRING_ANM2_ELEMENT_LAYER_ANIMATION "LayerAnimation" X(LAYER_ANIMATIONS, "LayerAnimations") \
#define STRING_ANM2_ELEMENT_NULL_ANIMATIONS "NullAnimations" X(LAYER_ANIMATION, "LayerAnimation") \
#define STRING_ANM2_ELEMENT_NULL_ANIMATION "NullAnimation" X(NULL_ANIMATIONS, "NullAnimations") \
#define STRING_ANM2_ELEMENT_TRIGGERS "Triggers" X(NULL_ANIMATION, "NullAnimation") \
#define STRING_ANM2_ELEMENT_TRIGGER "Trigger" X(TRIGGERS, "Triggers") \
X(TRIGGER, "Trigger")
/* Attributes */ typedef enum {
#define STRING_ANM2_ATTRIBUTE_CREATED_BY "CreatedBy" #define X(name, str) ANM2_ELEMENT_##name,
#define STRING_ANM2_ATTRIBUTE_CREATED_ON "CreatedOn" ANM2_ELEMENT_LIST
#define STRING_ANM2_ATTRIBUTE_FPS "Fps" #undef X
#define STRING_ANM2_ATTRIBUTE_VERSION "Version" ANM2_ELEMENT_COUNT
#define STRING_ANM2_ATTRIBUTE_ID "Id" } Anm2Element;
#define STRING_ANM2_ATTRIBUTE_PATH "Path"
#define STRING_ANM2_ATTRIBUTE_NAME "Name"
#define STRING_ANM2_ATTRIBUTE_SPRITESHEET_ID "SpritesheetId"
#define STRING_ANM2_ATTRIBUTE_SHOW_RECT "ShowRect"
#define STRING_ANM2_ATTRIBUTE_DEFAULT_ANIMATION "DefaultAnimation"
#define STRING_ANM2_ATTRIBUTE_FRAME_NUM "FrameNum"
#define STRING_ANM2_ATTRIBUTE_LOOP "Loop"
#define STRING_ANM2_ATTRIBUTE_X_POSITION "XPosition"
#define STRING_ANM2_ATTRIBUTE_Y_POSITION "YPosition"
#define STRING_ANM2_ATTRIBUTE_X_PIVOT "XPivot"
#define STRING_ANM2_ATTRIBUTE_Y_PIVOT "YPivot"
#define STRING_ANM2_ATTRIBUTE_X_CROP "XCrop"
#define STRING_ANM2_ATTRIBUTE_Y_CROP "YCrop"
#define STRING_ANM2_ATTRIBUTE_WIDTH "Width"
#define STRING_ANM2_ATTRIBUTE_HEIGHT "Height"
#define STRING_ANM2_ATTRIBUTE_X_SCALE "XScale"
#define STRING_ANM2_ATTRIBUTE_Y_SCALE "YScale"
#define STRING_ANM2_ATTRIBUTE_DELAY "Delay"
#define STRING_ANM2_ATTRIBUTE_VISIBLE "Visible"
#define STRING_ANM2_ATTRIBUTE_RED_TINT "RedTint"
#define STRING_ANM2_ATTRIBUTE_GREEN_TINT "GreenTint"
#define STRING_ANM2_ATTRIBUTE_BLUE_TINT "BlueTint"
#define STRING_ANM2_ATTRIBUTE_ALPHA_TINT "AlphaTint"
#define STRING_ANM2_ATTRIBUTE_RED_OFFSET "RedOffset"
#define STRING_ANM2_ATTRIBUTE_GREEN_OFFSET "GreenOffset"
#define STRING_ANM2_ATTRIBUTE_BLUE_OFFSET "BlueOffset"
#define STRING_ANM2_ATTRIBUTE_ROTATION "Rotation"
#define STRING_ANM2_ATTRIBUTE_INTERPOLATED "Interpolated"
#define STRING_ANM2_ATTRIBUTE_LAYER_ID "LayerId"
#define STRING_ANM2_ATTRIBUTE_NULL_ID "NullId"
#define STRING_ANM2_ATTRIBUTE_EVENT_ID "EventId"
#define STRING_ANM2_ATTRIBUTE_AT_FRAME "AtFrame"
#define ANM2_ELEMENT_COUNT (ANM2_ELEMENT_TRIGGER + 1) static const char* ANM2_ELEMENT_STRINGS[] = {
enum Anm2Element #define X(name, str) str,
{ ANM2_ELEMENT_LIST
ANM2_ELEMENT_ANIMATED_ACTOR, #undef X
ANM2_ELEMENT_INFO,
ANM2_ELEMENT_CONTENT,
ANM2_ELEMENT_SPRITESHEETS,
ANM2_ELEMENT_SPRITESHEET,
ANM2_ELEMENT_LAYERS,
ANM2_ELEMENT_LAYER,
ANM2_ELEMENT_NULLS,
ANM2_ELEMENT_NULL,
ANM2_ELEMENT_EVENTS,
ANM2_ELEMENT_EVENT,
ANM2_ELEMENT_ANIMATIONS,
ANM2_ELEMENT_ANIMATION,
ANM2_ELEMENT_ROOT_ANIMATION,
ANM2_ELEMENT_FRAME,
ANM2_ELEMENT_LAYER_ANIMATIONS,
ANM2_ELEMENT_LAYER_ANIMATION,
ANM2_ELEMENT_NULL_ANIMATIONS,
ANM2_ELEMENT_NULL_ANIMATION,
ANM2_ELEMENT_TRIGGERS,
ANM2_ELEMENT_TRIGGER
}; };
static const char* ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_COUNT] = DEFINE_STRING_TO_ENUM_FN(anm2_element_from_string, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT)
{
STRING_ANM2_ELEMENT_ANIMATED_ACTOR, #define ANM2_ATTRIBUTE_LIST \
STRING_ANM2_ELEMENT_INFO, X(CREATED_BY, "CreatedBy") \
STRING_ANM2_ELEMENT_CONTENT, X(CREATED_ON, "CreatedOn") \
STRING_ANM2_ELEMENT_SPRITESHEETS, X(VERSION, "Version") \
STRING_ANM2_ELEMENT_SPRITESHEET, X(FPS, "Fps") \
STRING_ANM2_ELEMENT_LAYERS, X(ID, "Id") \
STRING_ANM2_ELEMENT_LAYER, X(PATH, "Path") \
STRING_ANM2_ELEMENT_NULLS, X(NAME, "Name") \
STRING_ANM2_ELEMENT_NULL, X(SPRITESHEET_ID, "SpritesheetId") \
STRING_ANM2_ELEMENT_EVENTS, X(SHOW_RECT, "ShowRect") \
STRING_ANM2_ELEMENT_EVENT, X(DEFAULT_ANIMATION, "DefaultAnimation") \
STRING_ANM2_ELEMENT_ANIMATIONS, X(FRAME_NUM, "FrameNum") \
STRING_ANM2_ELEMENT_ANIMATION, X(LOOP, "Loop") \
STRING_ANM2_ELEMENT_ROOT_ANIMATION, X(X_POSITION, "XPosition") \
STRING_ANM2_ELEMENT_FRAME, X(Y_POSITION, "YPosition") \
STRING_ANM2_ELEMENT_LAYER_ANIMATIONS, X(X_PIVOT, "XPivot") \
STRING_ANM2_ELEMENT_LAYER_ANIMATION, X(Y_PIVOT, "YPivot") \
STRING_ANM2_ELEMENT_NULL_ANIMATIONS, X(X_CROP, "XCrop") \
STRING_ANM2_ELEMENT_NULL_ANIMATION, X(Y_CROP, "YCrop") \
STRING_ANM2_ELEMENT_TRIGGERS, X(WIDTH, "Width") \
STRING_ANM2_ELEMENT_TRIGGER X(HEIGHT, "Height") \
X(X_SCALE, "XScale") \
X(Y_SCALE, "YScale") \
X(DELAY, "Delay") \
X(VISIBLE, "Visible") \
X(RED_TINT, "RedTint") \
X(GREEN_TINT, "GreenTint") \
X(BLUE_TINT, "BlueTint") \
X(ALPHA_TINT, "AlphaTint") \
X(RED_OFFSET, "RedOffset") \
X(GREEN_OFFSET, "GreenOffset") \
X(BLUE_OFFSET, "BlueOffset") \
X(ROTATION, "Rotation") \
X(INTERPOLATED, "Interpolated") \
X(LAYER_ID, "LayerId") \
X(NULL_ID, "NullId") \
X(EVENT_ID, "EventId") \
X(AT_FRAME, "AtFrame")
typedef enum {
#define X(name, str) ANM2_ATTRIBUTE_##name,
ANM2_ATTRIBUTE_LIST
#undef X
ANM2_ATTRIBUTE_COUNT
} Anm2Attribute;
static const char* ANM2_ATTRIBUTE_STRINGS[] = {
#define X(name, str) str,
ANM2_ATTRIBUTE_LIST
#undef X
}; };
#define ANM2_ATTRIBUTE_COUNT (ANM2_ATTRIBUTE_AT_FRAME + 1) DEFINE_STRING_TO_ENUM_FN(anm2_attribute_from_string, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
enum Anm2Attribute
{
ANM2_ATTRIBUTE_CREATED_BY,
ANM2_ATTRIBUTE_CREATED_ON,
ANM2_ATTRIBUTE_FPS,
ANM2_ATTRIBUTE_VERSION,
ANM2_ATTRIBUTE_ID,
ANM2_ATTRIBUTE_PATH,
ANM2_ATTRIBUTE_NAME,
ANM2_ATTRIBUTE_SPRITESHEET_ID,
ANM2_ATTRIBUTE_SHOW_RECT,
ANM2_ATTRIBUTE_DEFAULT_ANIMATION,
ANM2_ATTRIBUTE_FRAME_NUM,
ANM2_ATTRIBUTE_LOOP,
ANM2_ATTRIBUTE_X_POSITION,
ANM2_ATTRIBUTE_Y_POSITION,
ANM2_ATTRIBUTE_X_PIVOT,
ANM2_ATTRIBUTE_Y_PIVOT,
ANM2_ATTRIBUTE_X_CROP,
ANM2_ATTRIBUTE_Y_CROP,
ANM2_ATTRIBUTE_WIDTH,
ANM2_ATTRIBUTE_HEIGHT,
ANM2_ATTRIBUTE_X_SCALE,
ANM2_ATTRIBUTE_Y_SCALE,
ANM2_ATTRIBUTE_DELAY,
ANM2_ATTRIBUTE_VISIBLE,
ANM2_ATTRIBUTE_RED_TINT,
ANM2_ATTRIBUTE_GREEN_TINT,
ANM2_ATTRIBUTE_BLUE_TINT,
ANM2_ATTRIBUTE_ALPHA_TINT,
ANM2_ATTRIBUTE_RED_OFFSET,
ANM2_ATTRIBUTE_GREEN_OFFSET,
ANM2_ATTRIBUTE_BLUE_OFFSET,
ANM2_ATTRIBUTE_ROTATION,
ANM2_ATTRIBUTE_INTERPOLATED,
ANM2_ATTRIBUTE_LAYER_ID,
ANM2_ATTRIBUTE_NULL_ID,
ANM2_ATTRIBUTE_EVENT_ID,
ANM2_ATTRIBUTE_AT_FRAME
};
static const char* ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_COUNT] = #define ANM2_ANIMATION_TYPE_COUNT (ANM2_ANIMATION_TRIGGERS + 1)
{
STRING_ANM2_ATTRIBUTE_CREATED_BY,
STRING_ANM2_ATTRIBUTE_CREATED_ON,
STRING_ANM2_ATTRIBUTE_FPS,
STRING_ANM2_ATTRIBUTE_VERSION,
STRING_ANM2_ATTRIBUTE_ID,
STRING_ANM2_ATTRIBUTE_PATH,
STRING_ANM2_ATTRIBUTE_NAME,
STRING_ANM2_ATTRIBUTE_SPRITESHEET_ID,
STRING_ANM2_ATTRIBUTE_SHOW_RECT,
STRING_ANM2_ATTRIBUTE_DEFAULT_ANIMATION,
STRING_ANM2_ATTRIBUTE_FRAME_NUM,
STRING_ANM2_ATTRIBUTE_LOOP,
STRING_ANM2_ATTRIBUTE_X_POSITION,
STRING_ANM2_ATTRIBUTE_Y_POSITION,
STRING_ANM2_ATTRIBUTE_X_PIVOT,
STRING_ANM2_ATTRIBUTE_Y_PIVOT,
STRING_ANM2_ATTRIBUTE_X_CROP,
STRING_ANM2_ATTRIBUTE_Y_CROP,
STRING_ANM2_ATTRIBUTE_WIDTH,
STRING_ANM2_ATTRIBUTE_HEIGHT,
STRING_ANM2_ATTRIBUTE_X_SCALE,
STRING_ANM2_ATTRIBUTE_Y_SCALE,
STRING_ANM2_ATTRIBUTE_DELAY,
STRING_ANM2_ATTRIBUTE_VISIBLE,
STRING_ANM2_ATTRIBUTE_RED_TINT,
STRING_ANM2_ATTRIBUTE_GREEN_TINT,
STRING_ANM2_ATTRIBUTE_BLUE_TINT,
STRING_ANM2_ATTRIBUTE_ALPHA_TINT,
STRING_ANM2_ATTRIBUTE_RED_OFFSET,
STRING_ANM2_ATTRIBUTE_GREEN_OFFSET,
STRING_ANM2_ATTRIBUTE_BLUE_OFFSET,
STRING_ANM2_ATTRIBUTE_ROTATION,
STRING_ANM2_ATTRIBUTE_INTERPOLATED,
STRING_ANM2_ATTRIBUTE_LAYER_ID,
STRING_ANM2_ATTRIBUTE_NULL_ID,
STRING_ANM2_ATTRIBUTE_EVENT_ID,
STRING_ANM2_ATTRIBUTE_AT_FRAME
};
#define ANM2_ANIMATION_TYPE_COUNT (ANM2_ANIMATION_NULL + 1)
enum Anm2AnimationType enum Anm2AnimationType
{ {
ANM2_ANIMATION_ROOT, ANM2_NONE,
ANM2_ANIMATION_LAYER, ANM2_ROOT_ANIMATION,
ANM2_ANIMATION_NULL ANM2_LAYER_ANIMATION,
ANM2_NULL_ANIMATION,
ANM2_TRIGGERS
}; };
struct Anm2Spritesheet struct Anm2Spritesheet
{ {
s32 id; char path[ANM2_STRING_MAX] = STRING_EMPTY;
char path[ANM2_STRING_MAX];
}; };
struct Anm2Layer struct Anm2Layer
{ {
s32 id; s32 spritesheetID = -1;
char name[ANM2_STRING_MAX]; char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_LAYER;
s32 spritesheetID; };
struct Anm2Null
{
char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_NULL;
bool isShowRect = false;
}; };
struct Anm2Event struct Anm2Event
{ {
s32 id; char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_EVENT;
char name[ANM2_STRING_MAX];
}; };
struct Anm2Trigger struct Anm2Trigger
{ {
s32 eventID; s32 eventID = -1;
s32 frame; s32 atFrame = -1;
}; };
struct Anm2Frame struct Anm2Frame
{ {
bool isInterpolated; bool isInterpolated = false;
bool isVisible; bool isVisible = true;
f32 rotation; f32 rotation = 1.0f;
s32 delay; s32 delay = 1;
ivec2 crop; vec2 crop = {0.0f, 0.0f};
ivec2 pivot; vec2 pivot = {0.0f, 0.0f};
ivec2 position; vec2 position = {0.0f, 0.0f};
ivec2 size; vec2 size = {1.0f, 1.0f};
ivec2 scale; vec2 scale = {1.0f, 1.0f};
ivec3 colorOffset; ivec3 offsetRGB = {0, 0, 0};
ivec4 tint; ivec4 tintRGBA = {255, 255, 255, 255};
s32 index;
}; };
struct Anm2LayerAnimation struct Anm2LayerAnimation
{ {
s32 layerID; bool isVisible = true;
bool isVisible; std::vector<Anm2Frame> frames;
vector<Anm2Frame> frames; };
struct Anm2NullAnimation
{
bool isVisible = true;
std::vector<Anm2Frame> frames;
};
struct Anm2RootAnimation
{
bool isVisible = true;
std::vector<Anm2Frame> frames;
};
struct Anm2Triggers
{
bool isVisible = true;
std::vector<Anm2Trigger> items;
}; };
struct Anm2Animation struct Anm2Animation
{ {
s32 id; s32 frameNum = 0;
s32 length; char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_ANIMATION;
char name[ANM2_STRING_MAX]; bool isLoop = true;
bool isVisible; Anm2RootAnimation rootAnimation;
bool isLoop; std::map<s32, Anm2LayerAnimation> layerAnimations;
Anm2Frame rootAnimationFrame; std::map<s32, Anm2NullAnimation> nullAnimations;
vector<Anm2LayerAnimation> layerAnimations; Anm2Triggers triggers;
vector<Anm2Trigger> triggers;
}; };
struct Anm2 struct Anm2
{ {
s32 fps; char path[PATH_MAX] = STRING_EMPTY;
s32 version; s32 fps = ANM2_FPS_DEFAULT;
char createdBy[ANM2_STRING_MAX]; s32 version = 0;
char createdOn[ANM2_STRING_MAX]; char createdBy[ANM2_STRING_MAX] = STRING_ANM2_CREATED_BY_DEFAULT;
char defaultAnimation[ANM2_STRING_MAX]; char createdOn[ANM2_STRING_MAX] = STRING_EMPTY;
bool isInit; char defaultAnimation[ANM2_STRING_MAX] = STRING_EMPTY;
vector<Anm2Spritesheet> spritesheets; std::map<s32, Anm2Spritesheet> spritesheets;
vector<Anm2Layer> layers; std::map<s32, Anm2Layer> layers;
vector<Anm2Event> events; std::map<s32, Anm2Null> nulls;
vector<Anm2Animation> animations; std::map<s32, Anm2Event> events;
std::map<s32, Anm2Animation> animations;
}; };
bool anm2_init(Anm2* self, const char* path); void anm2_layer_add(Anm2* self);
void anm2_free(Anm2* self); void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self);
void anm2_null_remove(Anm2* self, s32 id);
bool anm2_serialize(Anm2* self, const char* path);
bool anm2_deserialize(Anm2* self, const char* path);
void anm2_new(Anm2* self);
void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self);
void anm2_animation_remove(Anm2* self, s32 id);

59
src/dialog.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "dialog.h"
static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter);
static void
_dialog_callback(void* userdata, const char* const* filelist, s32 filter)
{
Dialog* self;
self = (Dialog*)userdata;
if (filelist && filelist[0] && strlen(filelist[0]) > 0)
{
strncpy(self->path, filelist[0], PATH_MAX - 1);
self->isSelected = true;
}
else
self->isSelected = false;
}
/* Opens file dialog for user to pick anm2 files */
void
dialog_anm2_open(Dialog* self)
{
SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL, false);
self->type = DIALOG_ANM2_OPEN;
}
/* Opens file dialog for user to save new anm2 files */
void
dialog_anm2_save(Dialog* self)
{
SDL_ShowSaveFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL);
self->type = DIALOG_ANM2_SAVE;
}
void
dialog_tick(Dialog* self)
{
if (self->isSelected)
{
switch (self->type)
{
case DIALOG_ANM2_OPEN:
anm2_deserialize(self->anm2, self->path);
break;
case DIALOG_ANM2_SAVE:
anm2_serialize(self->anm2, self->path);
break;
default:
break;
}
memset(self->path, '\0', PATH_MAX);
self->isSelected = false;
}
}

26
src/dialog.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "anm2.h"
static const SDL_DialogFileFilter ANM2_DIALOG_FILE_FILTER[] =
{
{"Anm2", "anm2;xml"}
};
enum DialogType
{
DIALOG_ANM2_OPEN,
DIALOG_ANM2_SAVE
};
struct Dialog
{
Anm2* anm2 = NULL;
enum DialogType type = DIALOG_ANM2_OPEN;
char path[PATH_MAX] = "";
bool isSelected = false;
};
void dialog_anm2_open(Dialog* self);
void dialog_anm2_save(Dialog* self);
void dialog_tick(Dialog* self);

View File

@ -1,28 +0,0 @@
#include "file.h"
bool
file_read(const char* path, void* buffer, size_t size)
{
SDL_IOStream* ioStream;
memset(buffer, '\0', size);
ioStream = SDL_IOFromFile(path, "r");
if (!ioStream)
{
printf(STRING_ERROR_FILE_READ, path);
return false;
}
if (!SDL_ReadIO(ioStream, buffer, size))
{
printf(STRING_ERROR_FILE_READ, path);
return false;
}
SDL_CloseIO(ioStream);
return true;
}

View File

@ -1,5 +0,0 @@
#pragma once
#include "COMMON.h"
bool file_read(const char* path, void* buffer, size_t size);

View File

@ -1,5 +1,280 @@
#include "imgui.h" #include "imgui.h"
static void _imgui_tooltip(const char* tooltip);
static void _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID);
/* Makes a tooltip! */
static void _imgui_tooltip(const char* tooltip)
{
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal))
ImGui::SetTooltip("%s", tooltip);
}
/* Displays each element of the timeline of a selected animation */
static void
_imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID)
{
static s32 selectedElementIndex = -1;
static s32 selectedSpritesheetIndex = -1;
Anm2Layer* layer = NULL;
Anm2LayerAnimation* layerAnimation = NULL;
Anm2Null* null = NULL;
Anm2NullAnimation* nullAnimation = NULL;
Anm2RootAnimation* rootAnimation = NULL;
Anm2Triggers* triggers = NULL;
ImTextureID iconTexture = -1;
bool isArrows = false;
bool* isShowRect = NULL;
bool* isVisible = NULL;
char nameBuffer[ANM2_STRING_MAX] = STRING_EMPTY;
char nameVisible[ANM2_STRING_FORMATTED_MAX] = STRING_EMPTY;
char* namePointer = NULL;
s32* spritesheetID = NULL;
bool isSelected = selectedElementIndex == *index;
bool isChangeable = type != ANM2_ROOT_ANIMATION && type != ANM2_TRIGGERS;
switch (type)
{
case ANM2_ROOT_ANIMATION:
rootAnimation = (Anm2RootAnimation*)element;
iconTexture = self->packed->textures[PACKED_TEXTURE_ROOT].handle;
strncpy(nameVisible, STRING_IMGUI_TIMELINE_ROOT, ANM2_STRING_FORMATTED_MAX);
isVisible = &rootAnimation->isVisible;
break;
case ANM2_LAYER_ANIMATION:
layerAnimation = (Anm2LayerAnimation*)element;
layer = &self->anm2->layers[*id];
iconTexture = self->packed->textures[PACKED_TEXTURE_LAYER].handle;
isVisible = &layerAnimation->isVisible;
spritesheetID = &layer->spritesheetID;
namePointer = layer->name;
snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer);
snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer);
break;
case ANM2_NULL_ANIMATION:
nullAnimation = (Anm2NullAnimation*)element;
null = &self->anm2->nulls[*id];
iconTexture = self->packed->textures[PACKED_TEXTURE_NULL].handle;
isVisible = &nullAnimation->isVisible;
isShowRect = &null->isShowRect;
namePointer = null->name;
snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer);
snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer);
break;
case ANM2_TRIGGERS:
triggers = (Anm2Triggers*)element;
iconTexture = self->packed->textures[PACKED_TEXTURE_TRIGGER].handle;
strncpy(nameVisible, STRING_IMGUI_TIMELINE_TRIGGERS, ANM2_STRING_FORMATTED_MAX);
isVisible = &triggers->isVisible;
break;
default:
break;
}
ImGui::BeginChild(nameVisible, IMGUI_TIMELINE_ELEMENT_SIZE, true, ImGuiWindowFlags_NoScrollbar);
ImGui::PushID(*index);
/* Shift arrows */
if (isChangeable)
{
bool isSwap = false;
bool isReversed = (type == ANM2_LAYER_ANIMATION);
auto arrows_draw = [&](auto it, auto begin, auto end, auto& map, bool* didSwap, bool isReversed)
{
bool canMoveUp = isReversed ? (std::next(it) != end) : (it != begin);
bool canMoveDown = isReversed ? (it != begin) : (std::next(it) != end);
isArrows = canMoveUp || canMoveDown;
if (isArrows)
ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ARROWS_LABEL, IMGUI_TIMELINE_SHIFT_ARROWS_SIZE);
if (canMoveUp)
{
auto target = isReversed ? std::next(it) : std::prev(it);
if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE, self->packed->textures[PACKED_TEXTURE_ARROW_UP].handle, IMGUI_ICON_SIZE))
{
map_swap(map, it->first, target->first);
*didSwap = true;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP);
}
if (canMoveDown)
{
if (!canMoveUp)
ImGui::Dummy(IMGUI_ICON_BUTTON_SIZE);
auto target = isReversed ? std::prev(it) : std::next(it);
if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW, self->packed->textures[PACKED_TEXTURE_ARROW_DOWN].handle, IMGUI_ICON_SIZE))
{
map_swap(map, it->first, target->first);
*didSwap = true;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN);
}
if (isArrows)
{
ImGui::EndChild();
ImGui::SameLine();
}
};
if (type == ANM2_LAYER_ANIMATION)
{
auto it = std::find_if(self->anm2->layers.begin(), self->anm2->layers.end(),
[&](const auto& pair) { return &pair.second == layer; });
if (it != self->anm2->layers.end())
arrows_draw(it, self->anm2->layers.begin(), self->anm2->layers.end(), self->anm2->layers, &isSwap, isReversed);
}
if (type == ANM2_NULL_ANIMATION)
{
auto it = std::find_if(self->anm2->nulls.begin(), self->anm2->nulls.end(),
[&](const auto& pair) { return &pair.second == null; });
if (it != self->anm2->nulls.end())
arrows_draw(it, self->anm2->nulls.begin(), self->anm2->nulls.end(), self->anm2->nulls, &isSwap, isReversed);
}
}
ImGui::BeginGroup();
ImGui::Image(iconTexture, IMGUI_ICON_SIZE);
ImGui::SameLine();
ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_NAME_LABEL, IMGUI_TIMELINE_ELEMENT_NAME_SIZE);
if (isSelected && isChangeable)
{
if (ImGui::InputText(STRING_IMGUI_TIMELINE_ANIMATION_LABEL, nameBuffer, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue))
{
strncpy(namePointer, nameBuffer, ANM2_STRING_MAX);
selectedElementIndex = -1;
}
}
else
{
if (ImGui::Selectable(nameVisible, isSelected))
{
selectedElementIndex = *index;
if (selectType && selectID)
{
*selectType = type;
*selectID = *id;
}
}
}
switch (type)
{
case ANM2_ROOT_ANIMATION:
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT);
break;
case ANM2_LAYER_ANIMATION:
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER);
break;
case ANM2_NULL_ANIMATION:
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NULL);
break;
case ANM2_TRIGGERS:
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS);
break;
default:
break;
}
ImGui::EndChild();
/* Visiblity */
if (isVisible)
{
ImTextureID visibilityIcon = *isVisible
? self->packed->textures[PACKED_TEXTURE_EYE_OPEN].handle
: self->packed->textures[PACKED_TEXTURE_EYE_CLOSED].handle;
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2);
if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_VISIBLE, visibilityIcon, IMGUI_ICON_SIZE))
*isVisible = !*isVisible;
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE);
}
/* Spritesheet IDs */
if (spritesheetID)
{
char spritesheetIDName[ANM2_STRING_FORMATTED_MAX];
if (*spritesheetID == -1)
snprintf(spritesheetIDName, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_SPRITESHEET_UNKNOWN);
else
snprintf(spritesheetIDName, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_SPRITESHEET_FORMAT, *spritesheetID);
ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE);
ImGui::SameLine();
ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, IMGUI_TIMELINE_SPRITESHEET_ID_SIZE);
if (selectedSpritesheetIndex == *index)
{
if (ImGui::DragInt(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, spritesheetID, 0, 1, self->anm2->spritesheets.size() - 1))
selectedSpritesheetIndex = -1;
}
else
{
if (ImGui::Selectable(spritesheetIDName))
selectedSpritesheetIndex = *index;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET);
ImGui::EndChild();
}
/* ShowRect */
if (isShowRect)
{
ImTextureID rectIcon = *isShowRect
? self->packed->textures[PACKED_TEXTURE_RECT_HIDE].handle
: self->packed->textures[PACKED_TEXTURE_RECT_SHOW].handle;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2);
if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_RECT, rectIcon, IMGUI_ICON_SIZE))
*isShowRect = !*isShowRect;
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT);
}
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
selectedElementIndex = -1;
selectedSpritesheetIndex = -1;
}
ImGui::PopID();
ImGui::EndGroup();
ImGui::EndChild();
*index = *index + 1;
}
void void
imgui_init(SDL_Window* window, SDL_GLContext glContext) imgui_init(SDL_Window* window, SDL_GLContext glContext)
{ {
@ -11,31 +286,604 @@ imgui_init(SDL_Window* window, SDL_GLContext glContext)
ImGui_ImplSDL3_InitForOpenGL(window, glContext); ImGui_ImplSDL3_InitForOpenGL(window, glContext);
ImGui_ImplOpenGL3_Init(STRING_OPENGL_VERSION); ImGui_ImplOpenGL3_Init(STRING_OPENGL_VERSION);
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
printf(STRING_INFO_IMGUI_INIT); printf(STRING_INFO_IMGUI_INIT);
} }
void void
imgui_tick(State* state) imgui_tick(Imgui* self)
{ {
static s32 selectedSpritesheetID = -1;
static s32 selectedEventID = -1;
static s32 selectedAnimationID = -1;
static s32 selectedTimelineElementID = -1;
static Anm2AnimationType selectedTimelineElementType = ANM2_NONE;
static bool isInterpolated = true;
static bool isVisible = true;
static f32 rotation = 0;
static s32 duration = 0;
static vec2 cropPosition = {0, 0};
static vec2 cropSize = {0, 0};
static vec2 pivot = {0, 0};
static vec2 position = {0, 0};
static vec2 xyScale = {0, 0};
static vec3 offset = {0.0f, 0.0f, 0.0f};
static vec4 tint = {0.0f, 0.0f, 0.0f, 1.0f};
ImVec2 previewWindowSize;
ImVec2 previewSize;
f32 previewAspect = 0.0f;
f32 previewWindowAspect = 0.0f;
ImGuiWindowFlags taskbarWindowFlags;
ImGuiWindowFlags dockspaceWindowFlags;
ImGuiDockNodeFlags dockNodeFlags;
ImGui_ImplSDL3_NewFrame(); ImGui_ImplSDL3_NewFrame();
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
ImGui::Begin("Hello, world!"); taskbarWindowFlags = 0 |
ImGui::Text("Color"); ImGuiWindowFlags_NoTitleBar |
ImGui::ColorEdit3("Color", value_ptr(state->clearColor)); ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoSavedSettings;
dockspaceWindowFlags = 0 |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus;
dockNodeFlags = 0 |
ImGuiDockNodeFlags_PassthruCentralNode;
ImGuiViewport* viewport = ImGui::GetMainViewport();
/* Taskbar */
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, IMGUI_TASKBAR_HEIGHT));
ImGui::Begin(STRING_IMGUI_TASKBAR, NULL, taskbarWindowFlags);
if (ImGui::Selectable(STRING_IMGUI_TASKBAR_FILE, false, 0, ImGui::CalcTextSize(STRING_IMGUI_TASKBAR_FILE)))
ImGui::OpenPopup(STRING_IMGUI_FILE_MENU);
if (ImGui::IsItemHovered() || ImGui::IsItemActive())
ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y + ImGui::GetItemRectSize().y));
if (ImGui::BeginPopup(STRING_IMGUI_FILE_MENU))
{
if (ImGui::Selectable(STRING_IMGUI_FILE_NEW))
{
selectedAnimationID = -1;
selectedSpritesheetID = -1;
selectedEventID = -1;
anm2_new(self->anm2);
window_title_from_anm2_set(self->window, self->anm2);
}
if (ImGui::Selectable(STRING_IMGUI_FILE_OPEN))
{
selectedAnimationID = -1;
selectedEventID = -1;
selectedSpritesheetID = -1;
dialog_anm2_open(self->dialog);
window_title_from_anm2_set(self->window, self->anm2);
}
if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE))
{
if (!strcmp(self->anm2->path, STRING_EMPTY) == 0)
anm2_serialize(self->anm2, self->anm2->path);
else
dialog_anm2_save(self->dialog);
}
if (ImGui::Selectable(STRING_IMGUI_FILE_SAVE_AS))
{
dialog_anm2_save(self->dialog);
window_title_from_anm2_set(self->window, self->anm2);
}
ImGui::EndPopup();
}
ImGui::End(); ImGui::End();
/* Dockspace */
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + IMGUI_TASKBAR_HEIGHT));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, viewport->Size.y - IMGUI_TASKBAR_HEIGHT));
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::Begin(STRING_IMGUI_WINDOW, NULL, dockspaceWindowFlags);
ImGuiID dockspace_id = ImGui::GetID(STRING_IMGUI_DOCKSPACE);
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockNodeFlags);
ImGui::End();
/* -- Properties -- */
ImGui::Begin(STRING_IMGUI_PROPERTIES);
/* FPS */
ImGui::AlignTextToFramePadding();
ImGui::Text(STRING_IMGUI_PROPERTIES_FPS);
ImGui::SameLine();
ImGui::SliderInt(STRING_IMGUI_PROPERTIES_FPS_LABEL, &self->anm2->fps, ANM2_FPS_MIN, ANM2_FPS_MAX);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_PROPERTIES_FPS);
/* CreatedBy */
ImGui::AlignTextToFramePadding();
ImGui::Text(STRING_IMGUI_PROPERTIES_CREATED_BY);
ImGui::SameLine();
ImGui::InputText(STRING_IMGUI_PROPERTIES_CREATED_BY_LABEL, self->anm2->createdBy, ANM2_STRING_MAX);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY);
/* CreatedOn */
ImGui::Text(STRING_IMGUI_PROPERTIES_CREATED_ON, self->anm2->createdOn);
/* Version */
ImGui::Text(STRING_IMGUI_PROPERTIES_VERSION, self->anm2->version);
ImGui::End();
/* -- Animations -- */
ImGui::Begin(STRING_IMGUI_ANIMATIONS);
/* Iterate through all animations, can be selected and names can be edited */
for (auto & [id, animation] : self->anm2->animations)
{
char name[ANM2_STRING_FORMATTED_MAX];
char oldName[ANM2_STRING_MAX];
bool isSelected = selectedAnimationID == id;
static s32 defaultAnimationID = -1;
/* Distinguish default animation; if animation has the same name, only mark the first one as default */
if (strcmp(animation.name, self->anm2->defaultAnimation) == 0)
{
snprintf(name, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT, animation.name);
defaultAnimationID = id;
}
else
strncpy(name, animation.name, ANM2_STRING_FORMATTED_MAX - 1);
ImGui::PushID(id);
ImGui::Image(self->packed->textures[PACKED_TEXTURE_ANIMATION].handle, IMGUI_ICON_SIZE);
ImGui::SameLine();
if (isSelected)
{
if (ImGui::InputText(STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL, animation.name, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue))
{
if (id == defaultAnimationID)
strncpy(self->anm2->defaultAnimation, animation.name, ANM2_STRING_MAX);
selectedAnimationID = -1;
}
}
else
{
if (ImGui::Selectable(name, isSelected))
selectedAnimationID = id;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT);
ImGui::PopID();
}
if (ImGui::Button(STRING_IMGUI_ANIMATIONS_ADD))
{
s32 id;
bool isDefault = self->anm2->animations.size() == 0; /* first animation is default automatically */
id = anm2_animation_add(self->anm2);
selectedAnimationID = id;
if (isDefault)
strncpy(self->anm2->defaultAnimation, self->anm2->animations[id].name, ANM2_STRING_MAX);
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD);
ImGui::SameLine();
if (selectedAnimationID != -1)
{
/* Remove */
if (ImGui::Button(STRING_IMGUI_ANIMATIONS_REMOVE))
{
anm2_animation_remove(self->anm2, selectedAnimationID);
selectedAnimationID = -1;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE);
ImGui::SameLine();
/* Duplicate */
if (ImGui::Button(STRING_IMGUI_ANIMATIONS_DUPLICATE))
{
s32 id = map_next_id_get(self->anm2->animations);
self->anm2->animations.insert({id, self->anm2->animations[selectedAnimationID]});
selectedAnimationID = id;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE);
if (ImGui::Button(STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT))
{
strncpy(self->anm2->defaultAnimation, self->anm2->animations[selectedAnimationID].name, ANM2_STRING_MAX);
selectedAnimationID = -1;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT);
}
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
selectedAnimationID = -1;
ImGui::End();
/* -- Events -- */
ImGui::Begin(STRING_IMGUI_EVENTS);
/* Iterate through all events, can be selected and names can be edited */
for (auto & [id, event] : self->anm2->events)
{
char eventString[ANM2_STRING_FORMATTED_MAX];
bool isSelected;
snprintf(eventString, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_EVENT_FORMAT, (s32)id, event.name);
ImGui::PushID(id);
ImGui::Image(self->packed->textures[PACKED_TEXTURE_EVENT].handle, IMGUI_ICON_SIZE);
ImGui::SameLine();
isSelected = selectedEventID == id;
if (isSelected)
{
if (ImGui::InputText(STRING_IMGUI_EVENTS_EVENT_LABEL, event.name, ANM2_STRING_MAX, ImGuiInputTextFlags_EnterReturnsTrue))
selectedEventID = -1;
}
else
{
if (ImGui::Selectable(eventString, isSelected))
selectedEventID = id;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_SELECT);
ImGui::PopID();
}
if (ImGui::Button(STRING_IMGUI_EVENTS_ADD))
{
s32 id = map_next_id_get(self->anm2->events);
self->anm2->events[id] = Anm2Event{};
selectedEventID = id;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_ADD);
ImGui::SameLine();
if (selectedEventID != -1)
{
if (ImGui::Button(STRING_IMGUI_EVENTS_REMOVE))
{
self->anm2->events.erase(selectedEventID);
selectedEventID = -1;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_EVENTS_REMOVE);
}
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
selectedEventID = -1;
ImGui::End();
/* -- Spritesheets -- */
ImGui::Begin(STRING_IMGUI_SPRITESHEETS);
for (auto [id, spritesheet] : self->anm2->spritesheets)
{
char spritesheetString[ANM2_STRING_FORMATTED_MAX];
bool isSelected = false;
snprintf(spritesheetString, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_SPRITESHEET_FORMAT, (s32)id, spritesheet.path);
ImGui::BeginChild(spritesheetString, IMGUI_SPRITESHEET_SIZE, true, ImGuiWindowFlags_None);
ImGui::PushID(id);
ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE);
ImGui::SameLine();
isSelected = selectedSpritesheetID == id;
if (ImGui::Selectable(spritesheetString, isSelected))
selectedSpritesheetID = id;
ImGui::PopID();
ImGui::EndChild();
}
ImGui::Button(STRING_IMGUI_SPRITESHEETS_ADD);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD);
ImGui::SameLine();
if (selectedSpritesheetID != -1)
{
if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REMOVE))
{
self->anm2->spritesheets.erase(selectedSpritesheetID);
selectedSpritesheetID = -1;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE);
ImGui::SameLine();
ImGui::Button(STRING_IMGUI_SPRITESHEETS_RELOAD);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD);
ImGui::SameLine();
ImGui::Button(STRING_IMGUI_SPRITESHEETS_REPLACE);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE);
}
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
selectedSpritesheetID = -1;
ImGui::End();
/* -- Frame Properties -- */
ImGui::Begin(STRING_IMGUI_FRAME_PROPERTIES);
ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_POSITION, value_ptr(position), 1, 0, 0, "%.0f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION);
ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION, value_ptr(cropPosition), 1, 0, 0, "%.0f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION);
ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_CROP_SIZE, value_ptr(cropSize), 1, 0, 0, "%.0f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE);
ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_PIVOT, value_ptr(pivot), 1, 0, 0, "%.0f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT);
ImGui::DragFloat2(STRING_IMGUI_FRAME_PROPERTIES_SCALE, value_ptr(xyScale), 1.0, 0, 0, "%.1f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE);
ImGui::DragFloat(STRING_IMGUI_FRAME_PROPERTIES_ROTATION, &rotation, 1, 0, 0, "%.1f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION);
ImGui::DragInt(STRING_IMGUI_FRAME_PROPERTIES_DURATION, &duration, 1, 0, 255);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION);
ImGui::ColorEdit4(STRING_IMGUI_FRAME_PROPERTIES_TINT, value_ptr(tint));
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT);
ImGui::ColorEdit3(STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, value_ptr(offset));
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET);
ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_VISIBLE, &isVisible);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE);
ImGui::SameLine();
ImGui::Checkbox(STRING_IMGUI_FRAME_PROPERTIES_INTERPOLATED, &isInterpolated);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED);
ImGui::End();
/* -- Animation Preview -- */
ImGui::Begin(STRING_IMGUI_ANIMATION_PREVIEW);
/* Settings */
ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE, true);
/* Grid settings */
ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE, true);
/* Grid toggle */
ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_GRID, &self->preview->isGrid);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID);
ImGui::SameLine();
/* Grid Color */
ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR, value_ptr(self->preview->gridColor), ImGuiColorEditFlags_NoInputs);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR);
/* Grid Size */
ImGui::InputInt2(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE, value_ptr(self->preview->gridSize));
self->preview->gridSize.x = CLAMP(self->preview->gridSize.x, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX);
self->preview->gridSize.y = CLAMP(self->preview->gridSize.y, PREVIEW_GRID_MIN, PREVIEW_GRID_MAX);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE);
ImGui::EndChild();
ImGui::SameLine();
/* Zoom settings */
ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE, true);
/* Zoom */
ImGui::DragFloat(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM, &self->preview->zoom, 1, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX, "%.0f");
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM);
ImGui::EndChild();
ImGui::SameLine();
/* Background settings */
ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE, true);
/* Background color */
ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, value_ptr(self->preview->color), ImGuiColorEditFlags_NoInputs);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR);
ImGui::EndChild();
ImGui::SameLine();
ImGui::EndChild();
ImGui::Image(self->preview->texture, ImVec2(PREVIEW_SIZE.x, PREVIEW_SIZE.y));
ImGui::End();
/* -- Spritesheet Editor -- */
ImGui::Begin(STRING_IMGUI_SPRITESHEET_EDITOR);
ImGui::End();
/* -- Timeline -- */
ImGui::Begin(STRING_IMGUI_TIMELINE);
if (selectedAnimationID != -1)
{
s32 index = 0;
Anm2Animation* animation = &self->anm2->animations[selectedAnimationID];
ImGui::BeginChild(STRING_IMGUI_TIMELINE_ANIMATIONS, IMGUI_TIMELINE_ELEMENT_LIST_SIZE, true);
/* Root */
_imgui_timeline_element(self, &animation->rootAnimation, NULL, &index, ANM2_ROOT_ANIMATION, NULL, NULL);
/* reverse order */
for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++)
{
s32 id = it->first;
Anm2LayerAnimation& layer = it->second;
_imgui_timeline_element(self, &layer, &id, &index, ANM2_LAYER_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID);
}
for (auto & [id, null] : animation->nullAnimations)
_imgui_timeline_element(self, &null, (s32*)&id, &index, ANM2_NULL_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID);
/* Triggers */
_imgui_timeline_element(self, &animation->triggers, NULL, &index, ANM2_TRIGGERS, NULL, NULL);
/* Element configuration */
if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_ADD))
ImGui::OpenPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU);
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD);
if (ImGui::BeginPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU))
{
if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER))
anm2_layer_add(self->anm2);
if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL))
anm2_null_add(self->anm2);
ImGui::EndPopup();
}
ImGui::SameLine();
if
(
selectedTimelineElementID != -1 &&
((selectedTimelineElementType == ANM2_LAYER_ANIMATION) || (selectedTimelineElementType == ANM2_NULL_ANIMATION))
)
{
if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_REMOVE))
{
switch (selectedTimelineElementType)
{
case ANM2_LAYER_ANIMATION:
anm2_layer_remove(self->anm2, selectedTimelineElementID);
break;
case ANM2_NULL_ANIMATION:
anm2_null_remove(self->anm2, selectedTimelineElementID);
break;
default:
break;
}
selectedTimelineElementID = -1;
selectedTimelineElementType = ANM2_NONE;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE);
}
else
{
selectedTimelineElementID = -1;
selectedTimelineElementType = ANM2_NONE;
}
ImGui::EndChild();
ImGui::SameLine();
/* Animation playback and frames */
static bool isPlaying = false;
if (isPlaying)
{
if (ImGui::Button(STRING_IMGUI_TIMELINE_PAUSE))
{
isPlaying = !isPlaying;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_PLAY);
}
else
{
if (ImGui::Button(STRING_IMGUI_TIMELINE_PLAY))
{
isPlaying = !isPlaying;
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE);
}
/*
ImGui::SameLine();
if (ImGui::Button(STRING_IMGUI_TIMELINE_ADD_FRAME))
{
}
_imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD);
ImGui::SameLine();
if (ImGui::Button(STRING_IMGUI_TIMELINE_REMOVE_FRAME))
{
}
*/
}
ImGui::End();
} }
void void
imgui_draw(void) imgui_draw(Imgui* self)
{ {
ImGui::Render(); ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
} }
void void
imgui_free(void) imgui_free(Imgui* self)
{ {
ImGui_ImplSDL3_Shutdown(); ImGui_ImplSDL3_Shutdown();
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();

View File

@ -1,15 +1,50 @@
#pragma once #pragma once
#include "COMMON.h" #include "dialog.h"
#include "packed.h"
#include "preview.h"
#include "window.h"
#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#define IMGUI_ENABLE_DOCKING
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <imgui/imgui_impl_sdl3.h> #include <imgui/imgui_internal.h>
#include <imgui/imgui_impl_opengl3.h> #include <imgui/backends/imgui_impl_sdl3.h>
#include <imgui/backends/imgui_impl_opengl3.h>
#define IMGUI_DRAG_SPEED 1.0 #define IMGUI_DRAG_SPEED 1.0
#define IMGUI_TASKBAR_HEIGHT 32
static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 64};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE = {150, 64};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE = {150, 64};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE = {150, 64};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_LIST_SIZE = {260, 0};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {230, 64};
static const ImVec2 IMGUI_TIMELINE_SPRITESHEET_ID_SIZE = {50, 20};
static const ImVec2 IMGUI_TIMELINE_SHIFT_ARROWS_SIZE = {24, 48};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {125, 20};
static const ImVec2 IMGUI_SPRITESHEET_SIZE = {0, 150};
static const ImVec2 IMGUI_ICON_SIZE = {16, 16};
static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {22, 22};
static const ImVec2 IMGUI_DUMMY_SIZE = {1, 1};
#define IMGUI_TIMELINE_SHIFT_ARROWS_WIDTH (IMGUI_TIMELINE_SHIFT_ARROWS_SIZE.x * 1.35)
struct Imgui
{
Dialog* dialog = NULL;
Packed* packed = NULL;
Anm2* anm2 = NULL;
Preview* preview = NULL;
SDL_Window* window = NULL;
};
void imgui_init(SDL_Window* window, SDL_GLContext glContext); void imgui_init(SDL_Window* window, SDL_GLContext glContext);
void imgui_tick(State* state); void imgui_tick(Imgui* self);
void imgui_draw(); void imgui_draw(Imgui* self);
void imgui_free(); void imgui_free(Imgui* self);

View File

@ -1,128 +1,23 @@
#include "main.h" #include "main.h"
static void _init(State* state);
static void _draw(State* state);
static void _loop(State* state);
static void _quit(State* state);
static void _tick(State* state);
static void
_init(State* state)
{
Shader shader;
printf(STRING_INFO_INIT);
if (!SDL_Init(SDL_INIT_VIDEO))
{
printf(STRING_ERROR_SDL_INIT, SDL_GetError());
_quit(state);
}
printf(STRING_INFO_SDL_INIT);
SDL_CreateWindowAndRenderer
(
STRING_WINDOW_TITLE,
WINDOW_WIDTH,
WINDOW_HEIGHT,
WINDOW_FLAGS,
&state->window,
&state->renderer
);
state->glContext = SDL_GL_CreateContext(state->window);
if (!state->glContext)
{
printf(STRING_ERROR_GL_CONTEXT_INIT, SDL_GetError());
_quit(state);
}
glewInit();
printf(STRING_INFO_GLEW_INIT);
shader_init(&shader, RESOURCE_SHADER_VERTEX_TEXTURE_QUAD, RESOURCE_SHADER_FRAGMENT_TEXTURE_QUAD);
imgui_init(state->window, state->glContext);
Anm2 anm2;
anm2_init(&anm2, "res/anm2/005.031_key.anm2");
}
void
_loop(State* state)
{
state->tick = SDL_GetTicks();
while (state->tick > state->lastTick + TICK_DELAY)
{
state->tick = SDL_GetTicks();
if (state->tick - state->lastTick < TICK_DELAY)
SDL_Delay(TICK_DELAY - (state->tick - state->lastTick));
_tick(state);
imgui_tick(state);
_draw(state);
state->lastTick = state->tick;
}
}
void
_tick(State* state)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT)
state->isRunning = false;
}
}
void
_draw(State* state)
{
glClearColor(state->clearColor.x, state->clearColor.y, state->clearColor.z, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
imgui_draw();
SDL_GL_SwapWindow(state->window);
}
void
_quit(State* state)
{
imgui_free();
SDL_GL_DestroyContext(state->glContext);
SDL_Quit();
printf(STRING_INFO_SDL_QUIT);
printf(STRING_INFO_QUIT);
}
s32 s32
main(s32 argc, char* argv[]) main(s32 argc, char* argv[])
{ {
State state; State state;
_init(&state); /* If anm2 given on command line, set state argument to that (will be loaded) */
if (argc > 0 && argv[1])
{
strncpy(state.argument, argv[1], PATH_MAX - 1);
state.isArgument = true;
}
init(&state);
while (state.isRunning) while (state.isRunning)
_loop(&state); loop(&state);
_quit(&state); quit(&state);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1,11 +1,3 @@
#pragma once #pragma once
#include "texture.h" #include "state.h"
#include "shader.h"
#include "imgui.h"
#include "anm2.h"
#define TICK_DELAY 16
#define WINDOW_WIDTH 1600
#define WINDOW_HEIGHT 900
#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE

17
src/packed.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "packed.h"
/* Loads in packed data */
void
packed_init(Packed* self)
{
for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++)
texture_from_data_init(&self->textures[i], (u8*)PACKED_TEXTURE_DATA[i].data, PACKED_TEXTURE_DATA[i].length);
}
/* Frees packed data */
void
packed_free(Packed* self)
{
for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++)
texture_free(&self->textures[i]);
}

12
src/packed.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "PACKED.h"
#include "texture.h"
struct Packed
{
Texture textures[PACKED_TEXTURE_COUNT];
};
void packed_init(Packed* self);
void packed_free(Packed* self);

48
src/preview.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "preview.h"
/* Initializes the preview */
void
preview_init(Preview* self)
{
glGenFramebuffers(1, &self->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glGenTextures(1, &self->texture);
glBindTexture(GL_TEXTURE_2D, self->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PREVIEW_SIZE.x, PREVIEW_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0);
glGenRenderbuffers(1, &self->rbo);
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, PREVIEW_SIZE.x, PREVIEW_SIZE.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void
preview_draw(Preview* self)
{
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glViewport(0, 0, PREVIEW_SIZE.x, PREVIEW_SIZE.y);
glClearColor(self->color.r, self->color.g, self->color.b, self->color.a);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind
}
void
preview_free(Preview* self)
{
glDeleteTextures(1, &self->texture);
glDeleteFramebuffers(1, &self->fbo);
glDeleteRenderbuffers(1, &self->rbo);
memset(self, '\0', sizeof(Preview));
}

26
src/preview.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "texture.h"
static const ivec2 PREVIEW_SIZE = {1280, 720};
#define PREVIEW_ZOOM_MIN 0
#define PREVIEW_ZOOM_MAX 400
#define PREVIEW_GRID_MIN 0
#define PREVIEW_GRID_MAX 20
struct Preview
{
GLuint texture;
GLuint fbo;
GLuint rbo;
bool isGrid;
f32 zoom = 100;
ivec2 gridSize = {10, 10};
vec4 color = {0.69, 0.69, 0.69, 1.0f};
vec4 gridColor = {0.35, 0.35, 0.35, 1.0f};
};
void preview_init(Preview* self);
void preview_draw(Preview* self);
void preview_free(Preview* self);

View File

@ -1,17 +1,14 @@
#include "shader.h" #include "shader.h"
static bool _shader_compile(GLuint handle, const char* path); static bool _shader_compile(GLuint handle, const char* text);
static bool static bool
_shader_compile(GLuint handle, const char* path) _shader_compile(GLuint handle, const char* text)
{ {
char buffer[SHADER_BUFFER_MAX];
char compileLog[SHADER_BUFFER_MAX]; char compileLog[SHADER_BUFFER_MAX];
s32 isCompile; s32 isCompile;
file_read(path, buffer, SHADER_BUFFER_MAX); const GLchar* source = text;
const GLchar* source = buffer;
glShaderSource(handle, 1, &source, NULL); glShaderSource(handle, 1, &source, NULL);
@ -21,7 +18,7 @@ _shader_compile(GLuint handle, const char* path)
if (!isCompile) if (!isCompile)
{ {
glGetShaderInfoLog(handle, SHADER_BUFFER_MAX, NULL, compileLog); glGetShaderInfoLog(handle, SHADER_BUFFER_MAX, NULL, compileLog);
printf(STRING_ERROR_SHADER_INIT, path, compileLog); printf(STRING_ERROR_SHADER_INIT, handle, compileLog);
return false; return false;
} }
@ -29,7 +26,7 @@ _shader_compile(GLuint handle, const char* path)
} }
bool bool
shader_init(Shader* self, const char* vertexPath, const char* fragmentPath) shader_init(Shader* self, const char* vertex, const char* fragment)
{ {
GLuint vertexHandle; GLuint vertexHandle;
GLuint fragmentHandle; GLuint fragmentHandle;
@ -42,8 +39,8 @@ shader_init(Shader* self, const char* vertexPath, const char* fragmentPath)
if if
( (
!_shader_compile(vertexHandle, vertexPath) || !_shader_compile(vertexHandle, vertex) ||
!_shader_compile(fragmentHandle, fragmentPath) !_shader_compile(fragmentHandle, fragment)
) )
return false; return false;
@ -57,7 +54,7 @@ shader_init(Shader* self, const char* vertexPath, const char* fragmentPath)
glDeleteShader(vertexHandle); glDeleteShader(vertexHandle);
glDeleteShader(fragmentHandle); glDeleteShader(fragmentHandle);
printf(STRING_INFO_SHADER_INIT, vertexPath, fragmentPath); printf(STRING_INFO_SHADER_INIT, self->handle);
return true; return true;
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "file.h" #include "COMMON.h"
#define SHADER_BUFFER_MAX 2048 #define SHADER_BUFFER_MAX 2048
@ -9,6 +9,6 @@ struct Shader
GLuint handle = 0; GLuint handle = 0;
}; };
bool shader_init(Shader* self, const char* vertexPath, const char* fragmentPath); bool shader_init(Shader* self, const char* vertex, const char* fragment);
void shader_free(Shader* self); void shader_free(Shader* self);
void shader_use(Shader* self); void shader_use(Shader* self);

128
src/state.cpp Normal file
View File

@ -0,0 +1,128 @@
#include "state.h"
static void _tick(State* state);
static void _draw(State* state);
static void
_tick(State* state)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT)
state->isRunning = false;
}
dialog_tick(&state->dialog);
imgui_tick(&state->imgui);
}
static void
_draw(State* state)
{
preview_draw(&state->preview);
imgui_draw(&state->imgui);
SDL_GL_SwapWindow(state->window);
}
void
init(State* state)
{
Shader shader;
printf(STRING_INFO_INIT);
if (!SDL_Init(SDL_INIT_VIDEO))
{
printf(STRING_ERROR_SDL_INIT, SDL_GetError());
quit(state);
}
printf(STRING_INFO_SDL_INIT);
SDL_CreateWindowAndRenderer
(
STRING_WINDOW_TITLE,
WINDOW_WIDTH,
WINDOW_HEIGHT,
WINDOW_FLAGS,
&state->window,
&state->renderer
);
state->glContext = SDL_GL_CreateContext(state->window);
if (!state->glContext)
{
printf(STRING_ERROR_GL_CONTEXT_INIT, SDL_GetError());
quit(state);
}
glewInit();
preview_init(&state->preview);
printf(STRING_INFO_GLEW_INIT);
packed_init(&state->packed);
if (state->isArgument)
anm2_deserialize(&state->anm2, state->argument);
else
anm2_new(&state->anm2);
window_title_from_anm2_set(state->window, &state->anm2);
state->imgui =
{
&state->dialog,
&state->packed,
&state->anm2,
&state->preview,
state->window
};
imgui_init(state->window, state->glContext);
state->dialog.anm2 = &state->anm2;
}
void
loop(State* state)
{
state->tick = SDL_GetTicks();
while (state->tick > state->lastTick + TICK_DELAY)
{
state->tick = SDL_GetTicks();
if (state->tick - state->lastTick < TICK_DELAY)
SDL_Delay(TICK_DELAY - (state->tick - state->lastTick));
_tick(state);
_draw(state);
state->lastTick = state->tick;
}
}
void
quit(State* state)
{
imgui_free(&state->imgui);
preview_free(&state->preview);
packed_free(&state->packed);
SDL_GL_DestroyContext(state->glContext);
SDL_Quit();
printf(STRING_INFO_SDL_QUIT);
printf(STRING_INFO_QUIT);
}

30
src/state.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include "shader.h"
#include "imgui.h"
#define TICK_DELAY 16
#define WINDOW_WIDTH 1600
#define WINDOW_HEIGHT 900
#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE
struct State
{
SDL_Window* window;
SDL_Renderer* renderer;
SDL_GLContext glContext;
Imgui imgui;
Dialog dialog;
Preview preview;
Anm2 anm2;
Packed packed;
char argument[PATH_MAX] = STRING_EMPTY;
bool isArgument = false;
u64 tick = 0;
u64 lastTick = 0;
bool isRunning = true;
};
void init(State* state);
void loop(State* state);
void quit(State* state);

View File

@ -6,16 +6,9 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h> #include <stb_image.h>
bool void
texture_init(Texture* self, const char* path) texture_gl_set(Texture* self, void* data)
{ {
void* data;
data = stbi_load(path, &self->size[0], &self->size[1], &self->channels, 4);
if (!data)
return false;
glGenTextures(1, &self->handle); glGenTextures(1, &self->handle);
glBindTexture(GL_TEXTURE_2D, self->handle); glBindTexture(GL_TEXTURE_2D, self->handle);
@ -27,7 +20,40 @@ texture_init(Texture* self, const char* path)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0); /* unbinds */
}
bool
texture_from_path_init(Texture* self, const char* path)
{
void* data;
data = stbi_load(path, &self->size.x, &self->size.y, &self->channels, 4);
if (!data)
{
printf(STRING_ERROR_TEXTURE_INIT, path);
return false;
}
printf(STRING_INFO_TEXTURE_INIT, path);
texture_gl_set(self, data);
return true;
}
bool
texture_from_data_init(Texture* self, const u8* data, u32 length)
{
void* textureData;
textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, 4);
if (!textureData)
return false;
texture_gl_set(self, textureData);
return true; return true;
} }

View File

@ -9,5 +9,7 @@ struct Texture
s32 channels = -1; s32 channels = -1;
}; };
bool texture_init(Texture* self, const char* path); void texture_gl_set(Texture* self, void* data);
bool texture_from_path_init(Texture* self, const char* path);
bool texture_from_data_init(Texture* self, const u8* data, u32 length);
void texture_free(Texture* self); void texture_free(Texture* self);

15
src/window.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "window.h"
/* Sets the window title from the given anm2 */
void
window_title_from_anm2_set(SDL_Window* self, Anm2* anm2)
{
if (!strcmp(anm2->path, STRING_EMPTY) == 0)
{
char windowTitle[WINDOW_TITLE_MAX];
snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, anm2->path);
SDL_SetWindowTitle(self, windowTitle);
}
else
SDL_SetWindowTitle(self, STRING_WINDOW_TITLE);
}

7
src/window.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "anm2.h"
#define WINDOW_TITLE_MAX 0xFF + PATH_MAX
void window_title_from_anm2_set(SDL_Window* self, Anm2* anm2);