The Update(TM), Part 2

This commit is contained in:
2025-08-09 00:32:14 -04:00
parent fe8bdae9a8
commit b9c9105621
29 changed files with 2656 additions and 1987 deletions

View File

@@ -6,8 +6,7 @@ endif()
project(anm2ed CXX) project(anm2ed CXX)
find_package(SDL3 REQUIRED)
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared)
find_package(GLEW REQUIRED) find_package(GLEW REQUIRED)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
@@ -28,22 +27,30 @@ if (WIN32)
enable_language("RC") enable_language("RC")
set (WIN32_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assets/win_icon.rc) set (WIN32_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assets/win_icon.rc)
endif() endif()
add_executable(${PROJECT_NAME} ${SOURCES} ${WIN32_RESOURCES}) add_executable(${PROJECT_NAME} ${SOURCES} ${WIN32_RESOURCES})
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src) target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src)
if (NOT MSVC) if (NOT MSVC)
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1") set(CMAKE_CXX_FLAGS "-O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1")
else() else()
set(CMAKE_CXX_FLAGS "/std:c++latest /EHsc") # /std:c++latest is required to make MSVC grant access to all the latest C++ stuff (C++23 is listed as preview even on dev previews of MSVC....) set(CMAKE_CXX_FLAGS "/std:c++latest /EHsc")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG -g")
else()
set(CMAKE_BUILD_TYPE "Release")
endif() endif()
if(NOT MSVC) if(NOT MSVC)
target_link_libraries(${PROJECT_NAME} PRIVATE m) target_link_libraries(${PROJECT_NAME} PRIVATE m)
endif() endif()
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL GLEW::GLEW SDL3::SDL3)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL GLEW::GLEW SDL3::SDL3)
message("System: ${CMAKE_SYSTEM_NAME}") message("System: ${CMAKE_SYSTEM_NAME}")
message("Project: ${PROJECT_NAME}") message("Project: ${PROJECT_NAME}")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#include <SDL3/SDL.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <GL/glew.h> #include <GL/glew.h>
#include <GL/gl.h> #include <GL/gl.h>
@@ -45,12 +43,9 @@ typedef double f64;
using namespace glm; using namespace glm;
#define MIN(x, min) (x < min ? min : x) #define ROUND_NEAREST_MULTIPLE(value, multiple) (roundf((value) / (multiple)) * (multiple))
#define MAX(x, max) (x > max ? max : x) #define FLOAT_TO_U8(x) (static_cast<u8>((x) * 255.0f))
#define CLAMP(x, min, max) (MIN(MAX(x, max), min)) #define U8_TO_FLOAT(x) ((x) / 255.0f)
#define ROUND_NEAREST_FLOAT(value, multiple) (roundf((value) / (multiple)) * (multiple))
#define COLOR_FLOAT_TO_INT(x) (static_cast<int>((x) * 255.0f))
#define COLOR_INT_TO_FLOAT(x) ((x) / 255.0f)
#define PERCENT_TO_UNIT(x) (x / 100.0f) #define PERCENT_TO_UNIT(x) (x / 100.0f)
#define TICK_DELAY 33.3f #define TICK_DELAY 33.3f
#define TICK_CATCH_UP_MAX (33.3f * 5) #define TICK_CATCH_UP_MAX (33.3f * 5)
@@ -58,14 +53,14 @@ using namespace glm;
#define TICK_RATE (SECOND / TICK_DELAY) #define TICK_RATE (SECOND / TICK_DELAY)
#define ID_NONE -1 #define ID_NONE -1
#define INDEX_NONE -1 #define INDEX_NONE -1
#define LENGTH_NONE -1 #define TIME_NONE -1.0f
#define UV_VERTICES(uvMin, uvMax) \ #define UV_VERTICES(uvMin, uvMax) \
{ \ { \
0, 0, uvMin.x, uvMin.y, \ 0, 0, uvMin.x, uvMin.y, \
1, 0, uvMax.x, uvMin.y, \ 1, 0, uvMax.x, uvMin.y, \
1, 1, uvMax.x, uvMax.y, \ 1, 1, uvMax.x, uvMax.y, \
0, 1, uvMin.x, uvMax.y, \ 0, 1, uvMin.x, uvMax.y \
} }
static const f32 GL_VERTICES[] = static const f32 GL_VERTICES[] =
@@ -76,7 +71,7 @@ static const f32 GL_VERTICES[] =
0, 1 0, 1
}; };
static const f32 GL_UV_VERTICES[] = constexpr f32 GL_UV_VERTICES[] =
{ {
0, 0, 0.0f, 0.0f, 0, 0, 0.0f, 0.0f,
1, 0, 1.0f, 0.0f, 1, 0, 1.0f, 0.0f,
@@ -88,8 +83,9 @@ static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f}; static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f};
static const vec4 COLOR_GREEN = {0.0f, 1.0f, 0.0f, 1.0f}; static const vec4 COLOR_GREEN = {0.0f, 1.0f, 0.0f, 1.0f};
static const vec4 COLOR_BLUE = {0.0f, 0.0f, 1.0f, 1.0f}; static const vec4 COLOR_BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
static const vec4 COLOR_PINK = {1.0f, 0.0f, 1.0f, 1.0f};
static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f}; static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f};
static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 1.0f}; static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 0.0f};
static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f}; static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f};
static inline void log_error(const std::string& string) static inline void log_error(const std::string& string)
@@ -102,6 +98,11 @@ static inline void log_info(const std::string& string)
std::println("[INFO] {}", string); std::println("[INFO] {}", string);
} }
static inline void log_warning(const std::string& string)
{
std::println("[WARNING] {}", string);
}
static inline bool string_to_bool(const std::string& string) static inline bool string_to_bool(const std::string& string)
{ {
if (string == "1") return true; if (string == "1") return true;
@@ -147,28 +148,11 @@ static inline s32 map_next_id_get(const std::map<s32, T>& map)
} }
template<typename T> template<typename T>
static inline s32 vector_next_id_get(const std::vector<T>& vec) static inline T* map_find(std::map<s32, T>& map, s32 id)
{ {
std::unordered_set<s32> usedIDs; if (auto it = map.find(id); it != map.end())
for (const auto& item : vec) return &it->second;
usedIDs.insert(item.id); return nullptr;
for (s32 i = 0; ; ++i)
if (!usedIDs.contains(i))
return i;
}
template<typename T>
void vector_swap_by_id(std::vector<T>& vec, s32 idA, s32 idB)
{
if (idA == idB)
return;
auto itA = std::find_if(vec.begin(), vec.end(), [=](const T& item) { return item.id == idA; });
auto itB = std::find_if(vec.begin(), vec.end(), [=](const T& item) { return item.id == idB; });
if (itA != vec.end() && itB != vec.end())
std::swap(*itA, *itB);
} }
template<typename Map, typename Key> template<typename Map, typename Key>
@@ -219,6 +203,39 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
map[insertIndex] = value; map[insertIndex] = value;
} }
static inline mat4 quad_model_get(vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale)
{
vec2 scaleAbsolute = glm::abs(scale);
vec2 scaleSign = glm::sign(scale);
vec2 pivotScaled = pivot * scaleAbsolute;
vec2 sizeScaled = size * scaleAbsolute;
mat4 model(1.0f);
model = glm::translate(model, vec3(position - pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotScaled, 0.0f));
model = glm::scale(model, vec3(scaleSign, 1.0f));
model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1));
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
model = glm::scale(model, vec3(sizeScaled, 1.0f));
return model;
}
static inline mat4 quad_parent_model_get(vec2 position, vec2 pivot, f32 rotation, vec2 scale)
{
vec2 scaleSign = glm::sign(scale);
vec2 scaleAbsolute = glm::abs(scale);
f32 handedness = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
mat4 local(1.0f);
local = glm::translate(local, vec3(pivot, 0.0f));
local = glm::scale(local, vec3(scaleSign, 1.0f)); // mirror if needed
local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
local = glm::translate(local, vec3(-pivot, 0.0f));
local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
}
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \ #define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
static inline std::string function(s32 index) \ static inline std::string function(s32 index) \
{ \ { \
@@ -230,3 +247,21 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
{ \ { \
return static_cast<enumType>(string_to_enum(string, stringArray, count)); \ return static_cast<enumType>(string_to_enum(string, stringArray, count)); \
}; };
enum DataType
{
TYPE_INT,
TYPE_BOOL,
TYPE_FLOAT,
TYPE_STRING,
TYPE_IVEC2,
TYPE_VEC2,
TYPE_VEC4
};
enum OriginType
{
ORIGIN_TOP_LEFT,
ORIGIN_CENTER
};

View File

@@ -4,151 +4,151 @@
#include "COMMON.h" #include "COMMON.h"
const u32 TEXTURE_ATLAS_LENGTH = 1696; const u32 TEXTURE_ATLAS_LENGTH = 1698;
const u8 TEXTURE_ATLAS[] = const u8 TEXTURE_ATLAS[] =
{ {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68,
0x08, 0x06, 0x00, 0x00, 0x00, 0x0e, 0xcb, 0xf5, 0x55, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x0e, 0xcb, 0xf5, 0x55, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b,
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x06, 0x52, 0x49, 0x44, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x06, 0x54, 0x49, 0x44,
0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0x8b, 0x4e, 0xeb, 0x30, 0x0c, 0x65, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0x8b, 0x4e, 0xeb, 0x30, 0x0c, 0x65,
0xd5, 0x3e, 0x14, 0xbe, 0x0c, 0xfe, 0x74, 0x97, 0x4a, 0x04, 0x05, 0x5f, 0xd5, 0x3e, 0x14, 0xbe, 0x0c, 0xfe, 0x74, 0x97, 0x48, 0x04, 0x05, 0x5f,
0x3f, 0x8e, 0x1d, 0xa7, 0xe9, 0x56, 0x5b, 0x42, 0x6c, 0xb4, 0x69, 0x52, 0x3f, 0x8e, 0x1d, 0xa7, 0xed, 0x56, 0x5b, 0x42, 0x6c, 0xb4, 0x69, 0x52,
0x1f, 0xbf, 0xf3, 0xe0, 0xed, 0xad, 0xe8, 0x9c, 0xf4, 0x20, 0xf4, 0xcc, 0x1f, 0xbf, 0xf3, 0xe0, 0xed, 0xad, 0xe8, 0x9c, 0xf4, 0x20, 0xf4, 0xcc,
0xef, 0x10, 0x69, 0xfb, 0xfe, 0xfe, 0xfe, 0xfb, 0xee, 0xfb, 0xe7, 0x99, 0xef, 0x10, 0x69, 0xfb, 0xfe, 0xfe, 0xfe, 0xfb, 0xee, 0xed, 0xf3, 0xca,
0x7d, 0xa9, 0x8c, 0x7f, 0x46, 0x20, 0x32, 0xc6, 0xee, 0x6d, 0xdb, 0x83, 0xbe, 0x54, 0xc6, 0x3f, 0x23, 0x10, 0x19, 0x63, 0xf7, 0xb6, 0x1d, 0xc1,
0xd5, 0xc0, 0x93, 0x80, 0x13, 0x01, 0xd5, 0x3a, 0xd4, 0xae, 0xf5, 0xd2, 0xea, 0xe0, 0x49, 0xc0, 0x89, 0x80, 0x6a, 0x1d, 0x6a, 0xd7, 0x46, 0x69,
0x22, 0xd1, 0xcf, 0xc0, 0x86, 0x24, 0xd1, 0x62, 0xea, 0x03, 0xa4, 0x19, 0x91, 0xe8, 0x67, 0x60, 0x53, 0x92, 0x68, 0x31, 0xf5, 0x01, 0xd2, 0x0a,
0x00, 0x34, 0xc6, 0x5b, 0xbc, 0x6a, 0xd7, 0xdd, 0x2a, 0x94, 0xf1, 0xe2, 0x00, 0x3a, 0xe3, 0x2d, 0x5e, 0xf5, 0xeb, 0x6e, 0x15, 0xca, 0x78, 0xf1,
0x3b, 0x00, 0xfb, 0xcf, 0x08, 0x23, 0xb8, 0x71, 0x3c, 0x82, 0x34, 0xcb, 0x06, 0x40, 0xfb, 0x99, 0x61, 0x04, 0x37, 0x8e, 0x47, 0x90, 0x56, 0x99,
0x04, 0x71, 0xc2, 0x47, 0x9f, 0xe9, 0x02, 0x20, 0x4b, 0xf2, 0xa8, 0x34, 0x20, 0x4e, 0xf8, 0xe8, 0x33, 0x5d, 0x00, 0x64, 0x49, 0x1e, 0x95, 0x66,
0xa3, 0xd2, 0x3f, 0x0a, 0x52, 0x54, 0x13, 0x22, 0x82, 0x82, 0x8e, 0xa7, 0x54, 0xfa, 0x67, 0x41, 0x8a, 0x6a, 0x42, 0x44, 0x50, 0xd0, 0xf1, 0xf4,
0xb5, 0xbb, 0xf5, 0x2a, 0x71, 0xfb, 0x26, 0x4d, 0x5d, 0xda, 0x75, 0x7a, 0x76, 0xb7, 0x51, 0x25, 0x6e, 0xdf, 0xa4, 0xa9, 0x4b, 0xbf, 0x4e, 0xef,
0x7f, 0xd4, 0x37, 0x70, 0xfd, 0x71, 0x7d, 0x5b, 0xf7, 0xa9, 0xaa, 0x1d, 0x8f, 0xfa, 0x06, 0xae, 0x3f, 0xae, 0x6f, 0xeb, 0x3e, 0x55, 0xb5, 0x03,
0xe8, 0x37, 0xd2, 0x7f, 0x7f, 0x2f, 0x32, 0x8e, 0xf6, 0x4c, 0x15, 0x00, 0xfd, 0x46, 0xfa, 0x1f, 0xef, 0x45, 0xc6, 0xd1, 0x9f, 0xa9, 0x02, 0xc0,
0x8e, 0xf9, 0x33, 0x00, 0xb0, 0xfa, 0xb7, 0x18, 0x40, 0x4d, 0xc4, 0xe7, 0x31, 0x7f, 0x05, 0x00, 0x56, 0xff, 0x16, 0x03, 0xa8, 0x89, 0xf8, 0xfc,
0xe7, 0x27, 0x7b, 0xdf, 0xc7, 0xc7, 0xc7, 0x9f, 0xef, 0x5f, 0x5f, 0x5f, 0xfc, 0x64, 0xef, 0xfb, 0xf8, 0xf8, 0xf8, 0xf3, 0xfd, 0xeb, 0xeb, 0xeb,
0xb7, 0x15, 0x00, 0xec, 0xe3, 0x68, 0x7d, 0x6f, 0x88, 0x34, 0xa1, 0x12, 0x76, 0x04, 0x00, 0x6d, 0x1c, 0xbd, 0xef, 0x0d, 0x91, 0x26, 0x54, 0x62,
0x33, 0x4a, 0x3d, 0x23, 0xdb, 0x67, 0xca, 0x34, 0x84, 0xf6, 0x36, 0x7d, 0x66, 0x69, 0x64, 0x64, 0xff, 0x4c, 0x99, 0x86, 0x50, 0x6b, 0x33, 0xb6,
0x3b, 0xfa, 0xdd, 0x6b, 0xfa, 0x50, 0x3f, 0x40, 0xc7, 0x2d, 0xf1, 0xad, 0xa3, 0xdf, 0xbd, 0xa6, 0x0f, 0xf5, 0x03, 0x74, 0xdc, 0x12, 0xdf, 0x46,
0x07, 0xde, 0x34, 0x29, 0x9a, 0x59, 0xca, 0xd2, 0x80, 0x7d, 0xd0, 0x4d, 0xe0, 0x4d, 0x93, 0xa2, 0x99, 0xa5, 0x2c, 0x0d, 0x68, 0x83, 0xee, 0x52,
0x6a, 0x2d, 0xad, 0xf0, 0x68, 0x82, 0x44, 0x9a, 0xe4, 0x6b, 0x63, 0xe9, 0x6b, 0x69, 0x85, 0x47, 0x13, 0x24, 0xd2, 0x24, 0x5f, 0x1b, 0xcb, 0x28,
0x25, 0x17, 0xd5, 0x96, 0xfe, 0x79, 0x5c, 0xfb, 0x8d, 0x76, 0xe4, 0x65, 0xb9, 0xa8, 0xb6, 0x8c, 0xcf, 0xe3, 0xda, 0x6f, 0xb4, 0x23, 0x2f, 0xf3,
0xbe, 0x57, 0x32, 0xbd, 0x0c, 0xf1, 0x30, 0xdf, 0x7a, 0x4e, 0x7f, 0x9d, 0xbd, 0x92, 0xe9, 0x65, 0x88, 0x87, 0xf9, 0xd6, 0x73, 0xc6, 0xeb, 0x54,
0x6a, 0x5b, 0x23, 0xca, 0xfc, 0xfe, 0xf3, 0x7e, 0xcd, 0x13, 0x19, 0xb5, 0xdb, 0x3a, 0x51, 0xe6, 0x8f, 0x9f, 0xdb, 0x35, 0x4f, 0x64, 0xd4, 0xfb,
0x3e, 0x1b, 0xa9, 0xe3, 0x43, 0x23, 0x84, 0xd1, 0x30, 0x54, 0x7a, 0x06, 0xec, 0xa4, 0x8e, 0x0f, 0x8d, 0x10, 0x66, 0xc3, 0x50, 0xe9, 0x19, 0x5c,
0x17, 0xef, 0x7b, 0xd5, 0x9e, 0xfb, 0xdd, 0xc7, 0xe7, 0xf4, 0x77, 0xf4, 0xbc, 0xef, 0x55, 0x7b, 0xee, 0xf7, 0x18, 0x9f, 0xd3, 0xdf, 0xd1, 0x77,
0xdd, 0x35, 0x1e, 0xa4, 0x65, 0x90, 0x2b, 0x12, 0xb1, 0x8c, 0xac, 0x5b, 0xd7, 0x78, 0x90, 0x96, 0x41, 0x1e, 0x91, 0x88, 0x65, 0x64, 0xdd, 0x1a,
0x03, 0x43, 0x32, 0x53, 0xa7, 0x01, 0xe0, 0x0c, 0xa5, 0x88, 0x8c, 0x7e, 0x18, 0x92, 0x99, 0x3a, 0x0d, 0x00, 0x67, 0x28, 0x45, 0x64, 0xf4, 0x83,
0x10, 0xa6, 0x47, 0x92, 0x2e, 0xed, 0xbe, 0x74, 0xfe, 0xbc, 0x42, 0x31, 0x30, 0x3d, 0x92, 0x74, 0x69, 0xf7, 0xa5, 0xf3, 0xe7, 0x15, 0x8a, 0x71,
0xce, 0xe3, 0x98, 0x8b, 0x8a, 0x8a, 0xb2, 0xcd, 0x21, 0xd2, 0x7e, 0x9b, 0x1e, 0xc7, 0x5c, 0x54, 0x54, 0x94, 0x6d, 0x0e, 0x91, 0xf6, 0xdb, 0x2a,
0xa5, 0xf6, 0xd1, 0x88, 0xe6, 0x2c, 0xfd, 0xd3, 0x44, 0x2c, 0xc2, 0xfc, 0xb5, 0x8f, 0x46, 0x34, 0x67, 0xe9, 0x9f, 0x26, 0x62, 0x11, 0xe6, 0xcf,
0x91, 0xf6, 0x87, 0x3b, 0x71, 0x24, 0x92, 0xd2, 0x98, 0xd9, 0xd7, 0xe1, 0xb4, 0xdf, 0xdd, 0x89, 0x23, 0x91, 0x94, 0xc6, 0xcc, 0xb1, 0x0e, 0x0f,
0x81, 0x68, 0xec, 0x11, 0x79, 0x87, 0x51, 0x1e, 0x1c, 0xca, 0x7c, 0xcf, 0x44, 0x63, 0x8f, 0xc8, 0x3b, 0xcc, 0xf2, 0x60, 0x57, 0xe6, 0x7b, 0xae,
0x35, 0x4f, 0xba, 0x2f, 0x55, 0x48, 0xad, 0x4a, 0xeb, 0x0a, 0x6d, 0x0c, 0x79, 0xd2, 0x7d, 0xa9, 0x42, 0x6a, 0x55, 0x5a, 0x8f, 0xd0, 0xc6, 0x30,
0x33, 0x3e, 0xa2, 0xc2, 0x54, 0xc2, 0xe8, 0x8f, 0x25, 0x0d, 0x5e, 0x09, 0xe3, 0x23, 0x2a, 0x4c, 0x25, 0x8c, 0xfe, 0x58, 0xd2, 0xe0, 0x95, 0x30,
0xd3, 0xfa, 0x1f, 0x8d, 0xea, 0xb2, 0x4c, 0xa8, 0x1b, 0x00, 0x54, 0x85, 0xad, 0xff, 0xd9, 0xa8, 0x2e, 0xcb, 0x84, 0xba, 0x01, 0x40, 0x55, 0xd8,
0xad, 0xf8, 0xd8, 0x62, 0xbe, 0x14, 0x47, 0x47, 0x01, 0x50, 0x27, 0x3a, 0x8a, 0x8f, 0x2d, 0xe6, 0x4b, 0x71, 0x74, 0x14, 0x00, 0x75, 0xa2, 0x43,
0x04, 0x93, 0x20, 0xf5, 0xcf, 0x4d, 0x27, 0xd2, 0xbf, 0xa1, 0x02, 0x84, 0x30, 0x09, 0x52, 0xff, 0xdc, 0x74, 0x22, 0xfd, 0x1b, 0x2a, 0x40, 0x88,
0x08, 0x1b, 0x5b, 0xfe, 0x95, 0x6a, 0x36, 0x5c, 0x9d, 0xc4, 0x6a, 0xd7, 0xb0, 0xb1, 0xe5, 0x5f, 0xa9, 0x66, 0xc3, 0xd5, 0x49, 0xac, 0x76, 0x63,
0x97, 0x86, 0xdb, 0x75, 0xee, 0x39, 0xfb, 0x33, 0x3c, 0x75, 0x7a, 0xad, 0x69, 0xb8, 0x5f, 0xe7, 0x9e, 0xd3, 0x9e, 0xe1, 0xa9, 0xd3, 0x6b, 0xe5,
0x3c, 0x6e, 0x55, 0x75, 0xa5, 0x62, 0x62, 0x2b, 0x25, 0x73, 0x45, 0xb3, 0x71, 0xab, 0xaa, 0x2b, 0x15, 0x13, 0x7b, 0x29, 0x99, 0x2b, 0x9a, 0xf5,
0x56, 0x54, 0xeb, 0xcb, 0xcd, 0x5a, 0xe1, 0x50, 0x2b, 0xc2, 0xfd, 0x77, 0xa2, 0xda, 0x58, 0x6e, 0xd6, 0x0a, 0x87, 0x5a, 0x11, 0xee, 0xbf, 0xfb,
0x7f, 0x8f, 0x70, 0xff, 0x99, 0xce, 0x64, 0x49, 0x13, 0xce, 0x92, 0xf4, 0x47, 0x84, 0xc7, 0xcf, 0x74, 0x26, 0x4b, 0x9a, 0x70, 0x96, 0xa4, 0x9f,
0x73, 0xce, 0x4f, 0x92, 0xd6, 0x11, 0x0d, 0xf0, 0xfa, 0x1d, 0x69, 0xf6, 0x73, 0x7e, 0x92, 0xb4, 0xce, 0x68, 0x80, 0xd7, 0xef, 0x48, 0xb3, 0x57,
0x8a, 0x8b, 0x5e, 0xb8, 0x68, 0xe6, 0x91, 0x48, 0xac, 0xed, 0xa4, 0x1d, 0x5c, 0xf4, 0xc2, 0x45, 0x33, 0x8f, 0x44, 0x62, 0x6d, 0x27, 0xed, 0x88,
0xd1, 0x81, 0x71, 0xe0, 0xf4, 0x2a, 0xac, 0x31, 0x5f, 0xb2, 0xd7, 0x1e, 0x0e, 0x8c, 0x03, 0x67, 0x54, 0x61, 0x8d, 0xf9, 0x92, 0xbd, 0xf6, 0x00,
0x00, 0x90, 0x32, 0x00, 0xe2, 0x90, 0x4f, 0x01, 0x00, 0x32, 0x01, 0x61, 0x80, 0x94, 0x01, 0x10, 0x87, 0x7c, 0x0a, 0x00, 0x90, 0x09, 0x08, 0xeb,
0x5d, 0xa7, 0x0c, 0x45, 0x0b, 0x77, 0x51, 0x0d, 0xb0, 0x6c, 0x2d, 0x12, 0x3a, 0x65, 0x28, 0x5a, 0xb8, 0x8b, 0x6a, 0x80, 0x65, 0x6b, 0x91, 0x88,
0x71, 0x49, 0x8c, 0xb6, 0xfc, 0x9c, 0xd5, 0x7e, 0xa8, 0x6e, 0x24, 0xbd, 0x4b, 0x62, 0xb4, 0xe5, 0xe7, 0xac, 0xf6, 0x53, 0x75, 0x23, 0xe9, 0x25,
0x84, 0x95, 0x54, 0x68, 0x8e, 0x56, 0x32, 0x4b, 0x5e, 0x27, 0xac, 0x45, 0xac, 0xa4, 0x42, 0x73, 0xb4, 0x92, 0x59, 0xf2, 0x3a, 0x61, 0x2d, 0x8a,
0x31, 0x52, 0xff, 0x5a, 0x9e, 0x70, 0x16, 0x27, 0xbc, 0xd1, 0x09, 0x93, 0x91, 0xfa, 0xd7, 0xf2, 0x84, 0xb3, 0x38, 0xe1, 0x8d, 0x4e, 0x98, 0xf4,
0xd6, 0xd1, 0x8d, 0x50, 0x1b, 0x04, 0xe7, 0x68, 0xb9, 0x89, 0x06, 0x6b, 0x8e, 0x6e, 0x84, 0xfa, 0x20, 0x38, 0x47, 0xcb, 0x4d, 0x34, 0x58, 0x53,
0x0a, 0xd0, 0x33, 0x09, 0x43, 0xfb, 0x6d, 0xe3, 0xd4, 0x9e, 0x45, 0x1d, 0x80, 0x9e, 0x49, 0x18, 0xda, 0x6f, 0x1f, 0xa7, 0xf6, 0x2c, 0xea, 0x10,
0xa2, 0xd5, 0x47, 0xbb, 0x7f, 0x7f, 0xd6, 0xfe, 0xfc, 0x7e, 0xf2, 0x65, 0xad, 0x3e, 0xfa, 0xfd, 0xed, 0x59, 0xed, 0xf9, 0xe3, 0xe4, 0x4b, 0xfb,
0xff, 0xdc, 0x3b, 0x52, 0xce, 0xd9, 0x7b, 0x26, 0xa8, 0xd4, 0x7b, 0xb5, 0x3c, 0x3a, 0x52, 0xce, 0xd9, 0x7b, 0x26, 0xa8, 0xd4, 0x7b, 0xb5, 0x50,
0x50, 0xd4, 0x93, 0x41, 0x6a, 0x0e, 0xd9, 0x93, 0x88, 0x71, 0xa6, 0x8d, 0xd4, 0x93, 0x41, 0x6a, 0x0e, 0xd9, 0x93, 0x88, 0x71, 0xa6, 0x8d, 0x33,
0x33, 0x8b, 0x52, 0x1f, 0x88, 0x4f, 0x3a, 0x73, 0x69, 0x7d, 0x59, 0x29, 0x8b, 0x52, 0x1f, 0x88, 0x4f, 0x3a, 0x73, 0x69, 0xfd, 0xb0, 0x52, 0x04,
0x82, 0xfa, 0x18, 0xcd, 0x27, 0x79, 0x4a, 0x11, 0x88, 0x56, 0x3c, 0x6d, 0xf5, 0x31, 0x9a, 0x4f, 0xf2, 0x94, 0x22, 0x10, 0xad, 0x78, 0xda, 0x52,
0x29, 0x62, 0x66, 0x31, 0x2e, 0x23, 0x93, 0xf5, 0x48, 0xfe, 0x28, 0xf3, 0xc4, 0xca, 0x62, 0x5c, 0x46, 0x26, 0xeb, 0x91, 0xfc, 0x59, 0xe6, 0xbd,
0x5e, 0x65, 0x3e, 0xe5, 0xe5, 0xcb, 0xd1, 0x45, 0x67, 0xa4, 0xa8, 0x09, 0xca, 0x7c, 0xca, 0xcb, 0x97, 0xa3, 0x8b, 0xce, 0x48, 0x51, 0x13, 0xc2,
0xe1, 0x12, 0x1c, 0xed, 0x73, 0x76, 0xfb, 0xd9, 0xb5, 0xf7, 0x8c, 0x67, 0x25, 0x38, 0xda, 0xe7, 0xec, 0xf6, 0xab, 0x6b, 0xef, 0x19, 0xcf, 0xa6,
0x53, 0x33, 0x78, 0xf3, 0x76, 0x22, 0xd5, 0x5b, 0x46, 0x56, 0xcf, 0x8d, 0x66, 0xf0, 0xe6, 0xed, 0x44, 0xaa, 0xb7, 0xcc, 0xac, 0x9e, 0x9b, 0x6d,
0xb6, 0x47, 0x6a, 0x42, 0x99, 0x00, 0x44, 0x9f, 0x4f, 0x85, 0x78, 0x0f, 0x8f, 0xd4, 0x84, 0x32, 0x01, 0x88, 0x3e, 0x9f, 0x0a, 0x71, 0x0b, 0x6b,
0x6b, 0x37, 0x2f, 0xc2, 0x59, 0xd1, 0xc0, 0xa8, 0x34, 0x65, 0x66, 0xd2, 0x37, 0x2f, 0xc2, 0x59, 0xd1, 0xc0, 0xac, 0x34, 0x65, 0x66, 0xd2, 0xab,
0xb3, 0x35, 0xa1, 0x59, 0x10, 0xd7, 0xc2, 0x2c, 0x69, 0xe5, 0x58, 0x46, 0x35, 0xa1, 0x5b, 0x10, 0xd7, 0xc2, 0x2c, 0x69, 0xe5, 0x58, 0x46, 0x48,
0x48, 0x96, 0x0d, 0x80, 0xa7, 0xf6, 0x82, 0x2e, 0x3b, 0xc9, 0xdc, 0xe8, 0x96, 0x0d, 0x80, 0xa7, 0xf6, 0x82, 0x2e, 0x3b, 0xc9, 0xdc, 0xe8, 0xc1,
0xc1, 0xd5, 0xcb, 0x42, 0x21, 0x93, 0xa7, 0x3c, 0xe1, 0x01, 0x2f, 0x23, 0xd5, 0xcb, 0x42, 0x21, 0x93, 0xa7, 0x3c, 0xe1, 0x01, 0x2f, 0x23, 0xd2,
0xd2, 0x18, 0xdd, 0x1b, 0x30, 0x63, 0x8f, 0x01, 0xb7, 0x27, 0x82, 0x15, 0x98, 0xdd, 0x1b, 0xb0, 0x62, 0x8f, 0x01, 0xb7, 0x27, 0x82, 0x15, 0x80,
0x80, 0x68, 0xb6, 0x8b, 0x30, 0x10, 0xd1, 0x80, 0x8c, 0x49, 0xef, 0x15, 0x68, 0xb6, 0x8b, 0x30, 0x10, 0xd1, 0x80, 0x8c, 0x49, 0xef, 0x23, 0x00,
0x00, 0x44, 0x98, 0xaf, 0xee, 0x8e, 0xf1, 0xee, 0x46, 0xf1, 0xdc, 0x4b, 0x88, 0x30, 0x5f, 0xdd, 0x1d, 0xe3, 0xdd, 0x8d, 0xe2, 0xb9, 0x97, 0xaa,
0xd5, 0x70, 0x86, 0x0f, 0xf0, 0x26, 0x69, 0x52, 0x35, 0x77, 0xf4, 0xdd, 0xe1, 0x0a, 0x1f, 0xe0, 0x4d, 0xd2, 0xa4, 0x6a, 0xee, 0xec, 0xbb, 0xbb,
0x5d, 0xcc, 0x47, 0x6a, 0xe7, 0x11, 0x0d, 0xd0, 0x26, 0x43, 0x66, 0x85, 0x98, 0x8f, 0xd4, 0xce, 0x23, 0x1a, 0xa0, 0x4d, 0x86, 0xac, 0x0a, 0x1d,
0x8e, 0xd1, 0xad, 0x47, 0x99, 0x5b, 0x96, 0xdc, 0xcc, 0xb7, 0x4c, 0x90, 0xa3, 0x5b, 0x8f, 0x32, 0xb7, 0x2c, 0xb9, 0x99, 0x6f, 0x99, 0x20, 0x6d,
0xb6, 0xf7, 0x09, 0x51, 0x5f, 0xd4, 0x84, 0x78, 0x19, 0xec, 0xda, 0x81, 0xef, 0x13, 0xa2, 0xbe, 0xa8, 0x09, 0xf1, 0x32, 0xd8, 0xb5, 0x03, 0x31,
0x98, 0x08, 0x80, 0x37, 0x87, 0x82, 0xf6, 0xb9, 0x69, 0xd5, 0x4a, 0x24, 0x11, 0x00, 0x6f, 0x0e, 0x05, 0xed, 0x73, 0xd3, 0xaa, 0x95, 0x48, 0x34,
0x9a, 0x88, 0xda, 0xc8, 0x55, 0xa9, 0x7d, 0x14, 0x00, 0x14, 0x20, 0x17, 0x11, 0xb5, 0x91, 0x47, 0xa5, 0xf6, 0x51, 0x00, 0x50, 0x80, 0x5c, 0xcc,
0xf3, 0x11, 0x67, 0x19, 0xdd, 0x25, 0x99, 0x15, 0x57, 0x67, 0x56, 0x17, 0x47, 0x9c, 0x65, 0x74, 0x97, 0x64, 0x56, 0x5c, 0x9d, 0x59, 0x5d, 0x9c,
0x47, 0x7c, 0x80, 0xc7, 0x3f, 0xa0, 0xcc, 0xbf, 0x69, 0x8c, 0x90, 0x36, 0xf1, 0x01, 0x1e, 0xff, 0x80, 0x32, 0xff, 0xa6, 0x31, 0x42, 0xda, 0x70,
0x9c, 0x69, 0x9b, 0xf9, 0xd0, 0x89, 0x87, 0x3e, 0xfb, 0xb5, 0x32, 0xe1, 0xa6, 0x6d, 0xe6, 0x43, 0x27, 0x1e, 0xc6, 0xec, 0xd7, 0xca, 0x84, 0x23,
0xc8, 0xf3, 0x2d, 0x60, 0xd1, 0x77, 0x40, 0x33, 0x5f, 0x8e, 0x77, 0x08, 0xcf, 0xb7, 0x80, 0x45, 0xdf, 0x01, 0xcd, 0x7c, 0x39, 0xde, 0x21, 0xc0,
0x70, 0x5b, 0xa4, 0x51, 0x76, 0xd6, 0x69, 0xf5, 0xdb, 0x66, 0xe5, 0xa4, 0x6d, 0x91, 0x46, 0xd9, 0x59, 0xa7, 0xd5, 0x6f, 0x9f, 0x95, 0x93, 0xbe,
0xef, 0x67, 0xa8, 0x9d, 0xf5, 0x33, 0x76, 0x9e, 0xb1, 0x6d, 0xd2, 0xcb, 0x9f, 0xa1, 0x76, 0x36, 0xce, 0xd8, 0x79, 0xc6, 0xb6, 0x49, 0x2f, 0x3b,
0x8e, 0x48, 0x17, 0x52, 0x60, 0xe3, 0x24, 0x30, 0x52, 0x7c, 0xf3, 0x92, 0x23, 0x5d, 0x48, 0x81, 0x8d, 0x93, 0xc0, 0x48, 0xf1, 0xcd, 0x4b, 0xd2,
0xb4, 0x7b, 0x71, 0xff, 0x5b, 0x64, 0x47, 0xa6, 0x26, 0x28, 0x45, 0x45, 0xee, 0xc5, 0xf6, 0xb7, 0xc8, 0x8e, 0x4c, 0x4d, 0x50, 0x8a, 0x8a, 0xae,
0xd7, 0xa2, 0x88, 0xb6, 0x6e, 0xcf, 0xf4, 0x82, 0xd6, 0x1a, 0xd0, 0x91, 0x45, 0x11, 0x6d, 0xdd, 0x9e, 0xe9, 0x05, 0xad, 0x35, 0xa0, 0x33, 0xa5,
0xd2, 0x30, 0xd2, 0xef, 0x0a, 0x80, 0x7e, 0x07, 0x10, 0x5d, 0x1d, 0xcc, 0x61, 0xa4, 0xdf, 0x23, 0x00, 0xfa, 0x1d, 0x40, 0x74, 0x75, 0x30, 0x77,
0x9d, 0x14, 0xe2, 0x79, 0x16, 0x77, 0x0e, 0xcf, 0x08, 0x33, 0xb4, 0xb5, 0x52, 0x88, 0xe7, 0x59, 0xdc, 0x39, 0x3c, 0x33, 0xcc, 0xd0, 0xd6, 0xf6,
0x3d, 0x56, 0xff, 0xa3, 0x0c, 0x0e, 0x8d, 0x7d, 0xe4, 0xc5, 0xb9, 0x75, 0x58, 0xfd, 0xcf, 0x32, 0x38, 0x34, 0xf6, 0x99, 0x17, 0xe7, 0xd6, 0x99,
0xa6, 0x68, 0x92, 0xc6, 0xad, 0x4f, 0x8d, 0x68, 0x8a, 0x96, 0x34, 0x66, 0xa2, 0x49, 0x1a, 0xb7, 0x3e, 0x35, 0xa2, 0x29, 0x5a, 0xd2, 0x98, 0x59,
0xd6, 0xbd, 0x34, 0x06, 0x87, 0xf3, 0x18, 0x74, 0x6d, 0xbf, 0x87, 0x19, 0xf7, 0xd2, 0x18, 0x1c, 0xce, 0x63, 0xd0, 0xb5, 0xfd, 0x1e, 0x66, 0x58,
0xd6, 0x20, 0xa3, 0x4c, 0x97, 0x18, 0xcf, 0xad, 0x6a, 0xf3, 0x08, 0x41, 0x83, 0x8c, 0x32, 0x5d, 0x62, 0x3c, 0xb7, 0xaa, 0xcd, 0x23, 0x04, 0x91,
0x64, 0xee, 0xc3, 0x03, 0xc0, 0xdd, 0x52, 0x55, 0xee, 0xe4, 0x11, 0xe4, 0xb9, 0x0f, 0x0f, 0x00, 0x77, 0x4b, 0x55, 0xb9, 0x93, 0x47, 0x90, 0x63,
0x58, 0x80, 0x3e, 0xc1, 0xfa, 0xbe, 0xf7, 0x8f, 0xca, 0x5b, 0xe1, 0x5e, 0x01, 0xc6, 0x04, 0xeb, 0xfb, 0xde, 0x3f, 0x2a, 0x6f, 0x85, 0x7b, 0x91,
0x24, 0x94, 0x6b, 0xcb, 0xc1, 0xe9, 0x12, 0x78, 0xc9, 0xee, 0x6b, 0x63, 0x50, 0xae, 0x2f, 0x07, 0xa7, 0x4b, 0xe0, 0x25, 0xbb, 0xaf, 0x8d, 0x21,
0x88, 0x1e, 0x54, 0x22, 0xdd, 0xeb, 0x3e, 0xff, 0xc2, 0xda, 0x58, 0x81, 0x7a, 0x50, 0x89, 0x74, 0xaf, 0xfb, 0xfc, 0x0b, 0x6b, 0x63, 0x05, 0x6a,
0xda, 0x43, 0xcd, 0x27, 0x58, 0x7d, 0x5a, 0xdf, 0x25, 0xc9, 0x97, 0xc6, 0x0f, 0x35, 0x9f, 0x60, 0xf5, 0x69, 0x7d, 0x97, 0x24, 0x5f, 0x1a, 0x3b,
0x8e, 0xae, 0x09, 0x8a, 0xbc, 0x6b, 0xaa, 0x0f, 0xe0, 0x54, 0x35, 0x83, 0xba, 0x26, 0x28, 0xf2, 0xae, 0xa9, 0x3e, 0x80, 0x53, 0xd5, 0x0c, 0xe6,
0xf9, 0xd4, 0x27, 0x64, 0x02, 0xe0, 0x3d, 0xad, 0xcb, 0x6b, 0x2e, 0xbd, 0x53, 0x9f, 0x90, 0x09, 0x80, 0xf7, 0xb4, 0x2e, 0xaf, 0xb9, 0xf4, 0x9a,
0x66, 0x2b, 0x2d, 0xcc, 0xcb, 0x60, 0x3e, 0x8d, 0x3a, 0xb4, 0x4d, 0x76, 0xad, 0xb4, 0x30, 0x2f, 0x83, 0xf9, 0x34, 0xea, 0xd0, 0x36, 0xd9, 0x45,
0xd1, 0x10, 0x12, 0x29, 0x8d, 0x23, 0xa1, 0xe4, 0xc8, 0x7c, 0x72, 0x38, 0x43, 0x48, 0xa4, 0x34, 0x8e, 0x84, 0x92, 0x33, 0xf3, 0xc9, 0xe1, 0xec,
0x7b, 0xa6, 0x36, 0x94, 0x2b, 0xc6, 0x35, 0x7b, 0x89, 0x9c, 0x30, 0x15, 0x99, 0xda, 0x50, 0xae, 0x18, 0xd7, 0xed, 0x25, 0x72, 0xc2, 0x54, 0x74,
0x5d, 0xb6, 0xe2, 0x7d, 0x39, 0xba, 0xe5, 0x47, 0x3b, 0xbb, 0xa8, 0xad, 0xd9, 0x8a, 0xf7, 0xe5, 0xe8, 0x96, 0x1f, 0xed, 0xec, 0xa2, 0xbe, 0xda,
0x76, 0xf6, 0xd8, 0x74, 0xce, 0x07, 0x4a, 0x6d, 0x91, 0x62, 0xa1, 0x5a, 0xd9, 0x63, 0xd3, 0x39, 0x1f, 0x28, 0xb5, 0x45, 0x8a, 0x85, 0x6a, 0xb1,
0xac, 0xd3, 0x54, 0x0f, 0x65, 0x8e, 0x25, 0xf9, 0x23, 0x89, 0x11, 0xbd, 0x4e, 0x53, 0x3d, 0x94, 0x39, 0x96, 0xe4, 0xcf, 0x24, 0x46, 0xf4, 0x9a,
0x66, 0x1d, 0x37, 0xa3, 0x6d, 0xb7, 0x1a, 0x29, 0x31, 0x7b, 0x35, 0x07, 0x75, 0xdc, 0x8c, 0xb6, 0xdd, 0x6a, 0xa6, 0xc4, 0xec, 0xd5, 0x1c, 0xe4,
0xb9, 0xbe, 0x59, 0x92, 0x8d, 0x46, 0x3c, 0x9c, 0x84, 0x66, 0x2d, 0xb8, 0xfa, 0x66, 0x49, 0x36, 0x1a, 0xf1, 0x70, 0x12, 0x9a, 0xb5, 0xe0, 0x2a,
0xca, 0x2a, 0x86, 0x79, 0xc6, 0x72, 0xd4, 0x91, 0x6d, 0x77, 0x6a, 0x6e, 0xab, 0x18, 0xe6, 0x19, 0xcb, 0x5e, 0x47, 0xb6, 0xdd, 0xa9, 0xb9, 0xd1,
0x34, 0x55, 0xf7, 0x4a, 0xcf, 0x51, 0xcc, 0xe7, 0xfa, 0xe9, 0x77, 0x28, 0x54, 0xdd, 0x2b, 0x3d, 0x7b, 0x31, 0x9f, 0xeb, 0x67, 0xdc, 0xa1, 0x28,
0x4a, 0x87, 0xf8, 0x21, 0x1a, 0x37, 0xfb, 0x1d, 0xee, 0x9c, 0xcd, 0xef, 0x1d, 0xe2, 0x87, 0x68, 0xdc, 0xea, 0x77, 0xb8, 0x73, 0x36, 0x7f, 0xb4,
0x6d, 0x67, 0x1b, 0xcc, 0x0e, 0x44, 0xaf, 0x0d, 0x54, 0x33, 0xe8, 0xae, 0x9d, 0x7d, 0x30, 0x0d, 0x88, 0x51, 0x1b, 0xa8, 0x66, 0xd0, 0x5d, 0x2b,
0x95, 0x68, 0x2c, 0x9f, 0xe1, 0x23, 0x22, 0x4c, 0xa7, 0xe0, 0x69, 0xe7, 0xd1, 0x58, 0x3e, 0xc3, 0x47, 0x44, 0x98, 0x4e, 0xc1, 0xd3, 0xce, 0x87,
0xc3, 0x4d, 0x71, 0xc2, 0xda, 0xcb, 0xf6, 0xcc, 0x8f, 0x9a, 0xa4, 0x57, 0x5b, 0xe2, 0x84, 0xb5, 0x97, 0x1d, 0x99, 0x1f, 0x35, 0x49, 0xaf, 0x5e,
0xaf, 0x82, 0x0e, 0x39, 0xe1, 0xa2, 0x75, 0x74, 0xcb, 0x88, 0x6d, 0x47, 0x05, 0x9d, 0x72, 0xc2, 0x45, 0xc7, 0xd1, 0xed, 0x0c, 0x27, 0x4a, 0x9d,
0x34, 0xe2, 0x2c, 0x1a, 0xb5, 0xe2, 0x9c, 0xd4, 0xdf, 0x28, 0xa8, 0x68, 0x41, 0xa3, 0x8e, 0x38, 0x27, 0x55, 0xad, 0x05, 0xed, 0xc1, 0xc0, 0x3a,
0x1d, 0xdd, 0xa5, 0xc2, 0x15, 0xea, 0xec, 0x8a, 0x4a, 0x03, 0x0a, 0x80, 0x4e, 0xec, 0x07, 0x80, 0xa8, 0xc3, 0x9a, 0x9d, 0x43, 0x2d, 0x7a, 0xb2,
0x55, 0xfe, 0xa3, 0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x02, 0xa0, 0x19, 0xb1, 0x02, 0xe0, 0x45, 0xed, 0x7f, 0x69, 0x40, 0x01, 0x50, 0x54,
0x00, 0x28, 0x2a, 0x00, 0x0a, 0x80, 0xa2, 0x78, 0x26, 0x7c, 0x65, 0xd2, 0x00, 0x14, 0x00, 0x45, 0x05, 0x40, 0x01, 0x50, 0x54, 0x00, 0x5c, 0x31,
0xf6, 0xac, 0x1d, 0x51, 0x13, 0xba, 0x17, 0xe3, 0xed, 0x7b, 0x66, 0x02, 0x13, 0xbe, 0x32, 0x69, 0x7b, 0xd6, 0xf6, 0xa8, 0x09, 0xdd, 0x8b, 0xf1,
0xb1, 0x5d, 0x9d, 0xf9, 0xdc, 0x34, 0x26, 0xfd, 0xdb, 0xcc, 0x63, 0x10, 0xf6, 0x3d, 0x2b, 0x81, 0xd8, 0xae, 0xce, 0x7c, 0x6e, 0x1a, 0x93, 0xfe,
0x2e, 0xed, 0x03, 0x00, 0xc9, 0x9e, 0x6e, 0x82, 0xb6, 0xab, 0x4a, 0x3f, 0x6d, 0xe5, 0x31, 0x08, 0x97, 0xf6, 0x01, 0x80, 0x64, 0x2f, 0x37, 0x41,
0x62, 0x56, 0xf6, 0x5b, 0x46, 0xff, 0x55, 0x57, 0x01, 0x50, 0x51, 0xd0, 0xdb, 0x55, 0xa5, 0x1f, 0x31, 0x2b, 0xed, 0x96, 0xd9, 0x7f, 0xd5, 0x55,
0x7a, 0x67, 0xea, 0x6d, 0x37, 0xb2, 0x09, 0xdc, 0x69, 0xe2, 0x4a, 0x03, 0x00, 0x54, 0x14, 0x74, 0xbc, 0x33, 0xf5, 0xb6, 0x9b, 0xd9, 0x04, 0xee,
0x2a, 0x11, 0x2b, 0x13, 0xf4, 0xf2, 0x91, 0x0c, 0xe4, 0x84, 0xb5, 0x49, 0x34, 0x71, 0xa5, 0x01, 0x95, 0x88, 0x95, 0x09, 0x7a, 0xf9, 0x48, 0x06,
0xf9, 0x99, 0xf9, 0x40, 0x69, 0x40, 0x99, 0xa0, 0x35, 0x1a, 0x03, 0x66, 0x72, 0xc2, 0xda, 0xa4, 0xfc, 0xca, 0x7c, 0xa0, 0x34, 0xa0, 0x4c, 0xd0,
0xc2, 0xd3, 0xb3, 0xe1, 0x4b, 0x6b, 0x00, 0x00, 0xc2, 0xf4, 0x83, 0x00, 0x31, 0x1a, 0x03, 0x66, 0xc2, 0xcb, 0xb3, 0xe1, 0x4b, 0x6b, 0x00, 0x00,
0x2f, 0x59, 0x0b, 0xea, 0xd7, 0xc0, 0x22, 0x07, 0x48, 0x1d, 0xb2, 0x3a, 0xc2, 0xf2, 0x65, 0x33, 0x97, 0xac, 0x05, 0x8d, 0x6b, 0x60, 0x91, 0x03,
0xba, 0x4c, 0xd1, 0xb8, 0x83, 0x2f, 0x00, 0x92, 0x80, 0xa8, 0xa5, 0x89, 0xa4, 0x76, 0x59, 0x1d, 0x5d, 0xa6, 0x68, 0xde, 0xc1, 0x17, 0x00, 0x49,
0x15, 0x05, 0x15, 0x15, 0x00, 0x05, 0x40, 0x51, 0x01, 0x70, 0x15, 0xf2, 0x40, 0xec, 0xb9, 0x34, 0xb1, 0xa2, 0xa0, 0x8a, 0x82, 0x0a, 0x80, 0xa2,
0x1e, 0x5a, 0x4d, 0x8f, 0xa1, 0x79, 0x95, 0xa4, 0x6c, 0xe9, 0x7f, 0xbc, 0x02, 0xa0, 0x00, 0x28, 0x3a, 0x0a, 0x80, 0xfe, 0x5f, 0x83, 0xbc, 0xb4,
0x88, 0xfe, 0x3f, 0xdf, 0x02, 0xe0, 0x60, 0x10, 0xac, 0x4d, 0xd3, 0x91, 0xd7, 0x1e, 0xaa, 0x4b, 0x68, 0x80, 0x17, 0x84, 0x62, 0xfe, 0x02, 0x13,
0x3e, 0xaf, 0x0c, 0xc0, 0x1f, 0x1f, 0xd0, 0xfe, 0x89, 0x99, 0x44, 0x47, 0x84, 0x82, 0x90, 0xcd, 0xfc, 0xab, 0xef, 0x92, 0xf9, 0xe3, 0x03, 0x2c,
0x6d, 0xdd, 0xbc, 0xb4, 0x13, 0x96, 0x40, 0x28, 0xe6, 0x1f, 0x18, 0x05, 0x10, 0x4a, 0xf2, 0x77, 0x70, 0xc2, 0x12, 0x08, 0xc5, 0xfc, 0x1d, 0xa3,
0x51, 0x10, 0x8a, 0xf9, 0xf3, 0x48, 0xac, 0x05, 0xfd, 0x30, 0xfc, 0xd1, 0x20, 0x0a, 0x42, 0x31, 0x7f, 0x1d, 0x89, 0xb5, 0xa0, 0x1f, 0x86, 0x3f,
0x7d, 0x7e, 0x69, 0x5a, 0xb5, 0x71, 0xfa, 0x1f, 0xcc, 0xd3, 0x69, 0xcc, 0x86, 0xcf, 0x2f, 0x6d, 0xc7, 0x8f, 0xda, 0x38, 0xfd, 0x0f, 0xc3, 0x64,
0x40, 0x9f, 0x7b, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xcf, 0x81, 0x94, 0xa2, 0x56, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0xae, 0x42, 0x60, 0x82 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
}; };
enum TextureType enum TextureType
@@ -178,8 +178,8 @@ enum TextureType
TEXTURE_EVENT, TEXTURE_EVENT,
TEXTURE_TRIGGER, TEXTURE_TRIGGER,
TEXTURE_PIVOT, TEXTURE_PIVOT,
TEXTURE_UNINTERPOLATED, TEXTURE_SQUARE,
TEXTURE_INTERPOLATED, TEXTURE_CIRCLE,
TEXTURE_PICKER, TEXTURE_PICKER,
TEXTURE_FRAME_ALT, TEXTURE_FRAME_ALT,
TEXTURE_FRAME, TEXTURE_FRAME,
@@ -229,6 +229,8 @@ const vec2 ATLAS_UVS[TEXTURE_COUNT][2] =
{ ATLAS_UV( 48, 64), ATLAS_UV( 88,104) } /* 40 x 40 */ { ATLAS_UV( 48, 64), ATLAS_UV( 88,104) } /* 40 x 40 */
}; };
#define ATLAS_UV_ARGS(type) ATLAS_UVS[type][0], ATLAS_UVS[type][1]
const vec2 ATLAS_SIZES[TEXTURE_COUNT] = const vec2 ATLAS_SIZES[TEXTURE_COUNT] =
{ {
TEXTURE_SIZE, TEXTURE_SIZE,
@@ -277,7 +279,6 @@ enum ShaderType
{ {
SHADER_LINE, SHADER_LINE,
SHADER_TEXTURE, SHADER_TEXTURE,
SHADER_LINE_DOTTED,
SHADER_COUNT SHADER_COUNT
}; };
@@ -285,11 +286,8 @@ const std::string SHADER_VERTEX = R"(
#version 330 core #version 330 core
layout (location = 0) in vec2 i_position; layout (location = 0) in vec2 i_position;
layout (location = 1) in vec2 i_uv; layout (location = 1) in vec2 i_uv;
out vec2 i_uv_out; out vec2 i_uv_out;
uniform mat4 u_transform; uniform mat4 u_transform;
void main() void main()
{ {
i_uv_out = i_uv; i_uv_out = i_uv;
@@ -301,7 +299,6 @@ const std::string SHADER_FRAGMENT = R"(
#version 330 core #version 330 core
out vec4 o_fragColor; out vec4 o_fragColor;
uniform vec4 u_color; uniform vec4 u_color;
void main() void main()
{ {
o_fragColor = u_color; o_fragColor = u_color;
@@ -324,25 +321,6 @@ void main()
} }
)"; )";
const std::string SHADER_LINE_DOTTED_FRAGMENT = R"(
#version 330 core
uniform vec4 u_color;
out vec4 o_fragColor;
void main()
{
float patternX = mod(gl_FragCoord.x, 10.0);
float patternY = mod(gl_FragCoord.y, 10.0);
if (patternX < 5.0 || patternY < 5.0)
{
o_fragColor = u_color;
}
else
{
discard;
}
}
)";
#define SHADER_UNIFORM_COLOR "u_color" #define SHADER_UNIFORM_COLOR "u_color"
#define SHADER_UNIFORM_TRANSFORM "u_transform" #define SHADER_UNIFORM_TRANSFORM "u_transform"
#define SHADER_UNIFORM_TINT "u_tint" #define SHADER_UNIFORM_TINT "u_tint"
@@ -352,6 +330,5 @@ void main()
const ShaderData SHADER_DATA[SHADER_COUNT] = const ShaderData SHADER_DATA[SHADER_COUNT] =
{ {
{SHADER_VERTEX, SHADER_FRAGMENT}, {SHADER_VERTEX, SHADER_FRAGMENT},
{SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT}, {SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT}
{SHADER_VERTEX, SHADER_LINE_DOTTED_FRAGMENT}
}; };

View File

@@ -11,7 +11,7 @@ static void _anm2_created_on_set(Anm2* self)
std::tm localTime = *std::localtime(&time); std::tm localTime = *std::localtime(&time);
std::ostringstream timeString; std::ostringstream timeString;
timeString << std::put_time(&localTime, STRING_ANM2_CREATED_ON_FORMAT); timeString << std::put_time(&localTime, ANM2_CREATED_ON_FORMAT);
self->createdOn = timeString.str(); self->createdOn = timeString.str();
} }
@@ -54,7 +54,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Spritesheets // Spritesheets
spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]); spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]);
for (auto & [id, spritesheet] : self->spritesheets) for (auto& [id, spritesheet] : self->spritesheets)
{ {
XMLElement* spritesheetElement; XMLElement* spritesheetElement;
@@ -70,7 +70,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Layers // Layers
layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]); layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]);
for (auto & [id, layer] : self->layers) for (auto& [id, layer] : self->layers)
{ {
XMLElement* layerElement; XMLElement* layerElement;
@@ -88,7 +88,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Nulls // Nulls
nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]); nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]);
for (auto & [id, null] : self->nulls) for (auto& [id, null] : self->nulls)
{ {
XMLElement* nullElement; XMLElement* nullElement;
@@ -108,7 +108,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Events // Events
eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]); eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]);
for (auto & [id, event] : self->events) for (auto& [id, event] : self->events)
{ {
XMLElement* eventElement; XMLElement* eventElement;
@@ -125,7 +125,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Animations // Animations
animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]); animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]);
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->defaultAnimation.c_str()); // DefaultAnimation animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
for (auto& [id, animation] : self->animations) for (auto& [id, animation] : self->animations)
{ {
@@ -160,13 +160,13 @@ bool anm2_serialize(Anm2* self, const std::string& path)
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); // Delay 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_VISIBLE], frame.isVisible); // Visible
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.r)); // RedTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.g)); // GreenTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.b)); // BlueTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.a)); // AlphaTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.r)); // RedOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.g)); // GreenOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.b)); // BlueOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated
@@ -189,7 +189,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], layerID); // LayerId layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], layerID); // LayerId
layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); // Visible layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); // Visible
for (auto & frame : layerAnimation.frames) for (auto& frame : layerAnimation.frames)
{ {
XMLElement* frameElement; XMLElement* frameElement;
@@ -208,13 +208,13 @@ bool anm2_serialize(Anm2* self, const std::string& path)
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */ 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_VISIBLE], frame.isVisible); // Visible
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.r)); // RedTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.g)); // GreenTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.b)); // BlueTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.a)); // AlphaTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.r)); // RedOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.g)); // GreenOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.b)); // BlueOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated
@@ -229,7 +229,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// NullAnimations // NullAnimations
nullAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]); nullAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]);
for (const auto & [nullID, nullAnimation] : animation.nullAnimations) for (const auto& [nullID, nullAnimation] : animation.nullAnimations)
{ {
XMLElement* nullAnimationElement; XMLElement* nullAnimationElement;
@@ -238,7 +238,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], nullID); // NullId nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], nullID); // NullId
nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], nullAnimation.isVisible); // Visible nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], nullAnimation.isVisible); // Visible
for (const auto & frame : nullAnimation.frames) for (const auto& frame : nullAnimation.frames)
{ {
XMLElement* frameElement; XMLElement* frameElement;
@@ -253,13 +253,13 @@ bool anm2_serialize(Anm2* self, const std::string& path)
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // 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_DELAY], frame.delay); // Delay
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); // Visible frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); // Visible
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.r)); // RedTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.g)); // GreenTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.b)); // BlueTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], COLOR_FLOAT_TO_INT(frame.tintRGBA.a)); // AlphaTint frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.r)); // RedOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.g)); // GreenOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], COLOR_FLOAT_TO_INT(frame.offsetRGB.b)); // BlueOffset frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation
frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated
@@ -274,7 +274,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Triggers // Triggers
triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]); triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]);
for (const auto & frame : animation.triggers.frames) for (const auto& frame : animation.triggers.frames)
{ {
XMLElement* triggerElement; XMLElement* triggerElement;
@@ -328,10 +328,11 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
s32 layerMapIndex = 0; s32 layerMapIndex = 0;
bool isLayerMapSet = false; bool isLayerMapSet = false;
bool isFirstAnimationDone = false; bool isFirstAnimationDone = false;
std::string defaultAnimation{};
if (!self || path.empty()) return false; if (!self || path.empty()) return false;
*self = Anm2{}; anm2_new(self);
xmlError = xmlDocument.LoadFile(path.c_str()); xmlError = xmlDocument.LoadFile(path.c_str());
@@ -418,13 +419,13 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
self->createdOn = xmlAttribute->Value(); self->createdOn = xmlAttribute->Value();
break; break;
case ANM2_ATTRIBUTE_VERSION: // Version case ANM2_ATTRIBUTE_VERSION: // Version
self->version = atoi(xmlAttribute->Value()); self->version = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_FPS: // FPS case ANM2_ATTRIBUTE_FPS: // FPS
self->fps = atoi(xmlAttribute->Value()); self->fps = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_ID: // ID case ANM2_ATTRIBUTE_ID: // ID
id = atoi(xmlAttribute->Value()); id = std::atoi(xmlAttribute->Value());
switch (anm2Element) switch (anm2Element)
{ {
case ANM2_ELEMENT_SPRITESHEET: // Spritesheet case ANM2_ELEMENT_SPRITESHEET: // Spritesheet
@@ -448,7 +449,7 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
} }
break; break;
case ANM2_ATTRIBUTE_LAYER_ID: // LayerId case ANM2_ATTRIBUTE_LAYER_ID: // LayerId
id = atoi(xmlAttribute->Value()); id = std::atoi(xmlAttribute->Value());
if (!isLayerMapSet) if (!isLayerMapSet)
{ {
@@ -460,7 +461,7 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
item = &animation->layerAnimations[id]; item = &animation->layerAnimations[id];
break; break;
case ANM2_ATTRIBUTE_NULL_ID: // NullId case ANM2_ATTRIBUTE_NULL_ID: // NullId
id = atoi(xmlAttribute->Value()); id = std::atoi(xmlAttribute->Value());
animation->nullAnimations[id] = addItem; animation->nullAnimations[id] = addItem;
item = &animation->nullAnimations[id]; item = &animation->nullAnimations[id];
break; break;
@@ -487,52 +488,52 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
} }
break; break;
case ANM2_ATTRIBUTE_SPRITESHEET_ID: case ANM2_ATTRIBUTE_SPRITESHEET_ID:
layer->spritesheetID = atoi(xmlAttribute->Value()); layer->spritesheetID = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_SHOW_RECT: case ANM2_ATTRIBUTE_SHOW_RECT:
null->isShowRect = string_to_bool(xmlAttribute->Value()); null->isShowRect = string_to_bool(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_DEFAULT_ANIMATION: case ANM2_ATTRIBUTE_DEFAULT_ANIMATION:
self->defaultAnimation = xmlAttribute->Value(); defaultAnimation = xmlAttribute->Value();
break; break;
case ANM2_ATTRIBUTE_FRAME_NUM: case ANM2_ATTRIBUTE_FRAME_NUM:
animation->frameNum = atoi(xmlAttribute->Value()); animation->frameNum = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_LOOP: case ANM2_ATTRIBUTE_LOOP:
animation->isLoop = string_to_bool(xmlAttribute->Value()); animation->isLoop = string_to_bool(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_X_POSITION: case ANM2_ATTRIBUTE_X_POSITION:
frame->position.x = atof(xmlAttribute->Value()); frame->position.x = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_Y_POSITION: case ANM2_ATTRIBUTE_Y_POSITION:
frame->position.y = atof(xmlAttribute->Value()); frame->position.y = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_X_PIVOT: case ANM2_ATTRIBUTE_X_PIVOT:
frame->pivot.x = atof(xmlAttribute->Value()); frame->pivot.x = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_Y_PIVOT: case ANM2_ATTRIBUTE_Y_PIVOT:
frame->pivot.y = atof(xmlAttribute->Value()); frame->pivot.y = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_X_CROP: case ANM2_ATTRIBUTE_X_CROP:
frame->crop.x = atof(xmlAttribute->Value()); frame->crop.x = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_Y_CROP: case ANM2_ATTRIBUTE_Y_CROP:
frame->crop.y = atof(xmlAttribute->Value()); frame->crop.y = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_WIDTH: case ANM2_ATTRIBUTE_WIDTH:
frame->size.x = atof(xmlAttribute->Value()); frame->size.x = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_HEIGHT: case ANM2_ATTRIBUTE_HEIGHT:
frame->size.y = atof(xmlAttribute->Value()); frame->size.y = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_X_SCALE: case ANM2_ATTRIBUTE_X_SCALE:
frame->scale.x = atof(xmlAttribute->Value()); frame->scale.x = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_Y_SCALE: case ANM2_ATTRIBUTE_Y_SCALE:
frame->scale.y = atof(xmlAttribute->Value()); frame->scale.y = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_DELAY: case ANM2_ATTRIBUTE_DELAY:
frame->delay = atoi(xmlAttribute->Value()); frame->delay = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_VISIBLE: case ANM2_ATTRIBUTE_VISIBLE:
switch (anm2Element) switch (anm2Element)
@@ -550,37 +551,37 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
} }
break; break;
case ANM2_ATTRIBUTE_RED_TINT: case ANM2_ATTRIBUTE_RED_TINT:
frame->tintRGBA.r = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->tintRGBA.r = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_GREEN_TINT: case ANM2_ATTRIBUTE_GREEN_TINT:
frame->tintRGBA.g = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->tintRGBA.g = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_BLUE_TINT: case ANM2_ATTRIBUTE_BLUE_TINT:
frame->tintRGBA.b = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->tintRGBA.b = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_ALPHA_TINT: case ANM2_ATTRIBUTE_ALPHA_TINT:
frame->tintRGBA.a = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->tintRGBA.a = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_RED_OFFSET: case ANM2_ATTRIBUTE_RED_OFFSET:
frame->offsetRGB.r = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->offsetRGB.r = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_GREEN_OFFSET: case ANM2_ATTRIBUTE_GREEN_OFFSET:
frame->offsetRGB.g = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->offsetRGB.g = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_BLUE_OFFSET: case ANM2_ATTRIBUTE_BLUE_OFFSET:
frame->offsetRGB.b = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value())); frame->offsetRGB.b = U8_TO_FLOAT(std::atoi(xmlAttribute->Value()));
break; break;
case ANM2_ATTRIBUTE_ROTATION: case ANM2_ATTRIBUTE_ROTATION:
frame->rotation = atof(xmlAttribute->Value()); frame->rotation = std::atof(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_INTERPOLATED: case ANM2_ATTRIBUTE_INTERPOLATED:
frame->isInterpolated = string_to_bool(xmlAttribute->Value()); frame->isInterpolated = string_to_bool(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_EVENT_ID: case ANM2_ATTRIBUTE_EVENT_ID:
frame->eventID = atoi(xmlAttribute->Value()); frame->eventID = std::atoi(xmlAttribute->Value());
break; break;
case ANM2_ATTRIBUTE_AT_FRAME: case ANM2_ATTRIBUTE_AT_FRAME:
frame->atFrame = atoi(xmlAttribute->Value()); frame->atFrame = std::atoi(xmlAttribute->Value());
break; break;
default: default:
break; break;
@@ -589,7 +590,8 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
xmlAttribute = xmlAttribute->Next(); xmlAttribute = xmlAttribute->Next();
} }
if (anm2Element == ANM2_ELEMENT_SPRITESHEET) resources_texture_init(resources, spritesheet->path , id); if (anm2Element == ANM2_ELEMENT_SPRITESHEET)
resources_texture_init(resources, spritesheet->path , id);
xmlChild = xmlElement->FirstChildElement(); xmlChild = xmlElement->FirstChildElement();
@@ -615,6 +617,11 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
} }
} }
// Set default animation ID
for (auto& [id, animation] : self->animations)
if (animation.name == defaultAnimation)
self->defaultAnimationID = id;
log_info(std::format(ANM2_READ_INFO, path)); log_info(std::format(ANM2_READ_INFO, path));
// Return to old working directory // Return to old working directory
@@ -625,24 +632,48 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
void anm2_layer_add(Anm2* self) void anm2_layer_add(Anm2* self)
{ {
s32 id = map_next_id_get(self->layers); s32 id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{}; self->layers[id] = Anm2Layer{};
self->layerMap[self->layers.size() - 1] = id;
for (auto & [animationID, animation] : self->animations)
for (auto& [_, animation] : self->animations)
animation.layerAnimations[id] = Anm2Item{}; animation.layerAnimations[id] = Anm2Item{};
} }
void anm2_layer_remove(Anm2* self, s32 id) void anm2_layer_remove(Anm2* self, s32 id)
{ {
// Make sure the layer exists if (!self->layers.contains(id))
auto it = self->layers.find(id); return;
if (it == self->layers.end())
return;
self->layers.erase(id); self->layers.erase(id);
for (auto & [animationID, animation] : self->animations) for (auto it = self->layerMap.begin(); it != self->layerMap.end(); ++it)
{
if (it->second == id)
{
self->layerMap.erase(it);
break;
}
}
std::map<s32, s32> newLayerMap;
s32 newIndex = 0;
for (const auto& [_, layerID] : self->layerMap)
newLayerMap[newIndex++] = layerID;
self->layerMap = std::move(newLayerMap);
for (auto& [_, animation] : self->animations)
animation.layerAnimations.erase(id); animation.layerAnimations.erase(id);
} }
@@ -652,23 +683,40 @@ void anm2_null_add(Anm2* self)
self->nulls[id] = Anm2Null{}; self->nulls[id] = Anm2Null{};
for (auto & [animationID, animation] : self->animations) for (auto& [_, animation] : self->animations)
animation.nullAnimations[id] = Anm2Item{}; animation.nullAnimations[id] = Anm2Item{};
} }
void anm2_null_remove(Anm2* self, s32 id) void anm2_null_remove(Anm2* self, s32 id)
{ {
// Make sure the null exists if (!self->nulls.contains(id))
auto it = self->nulls.find(id); return;
if (it == self->nulls.end())
return;
self->nulls.erase(id); self->nulls.erase(id);
for (auto & [animationID, animation] : self->animations) std::map<s32, Anm2Null> newNulls;
animation.nullAnimations.erase(id); s32 newID = 0;
for (const auto& [_, null] : self->nulls)
newNulls[newID++] = null;
self->nulls = std::move(newNulls);
for (auto& [_, animation] : self->animations)
{
if (animation.nullAnimations.contains(id))
animation.nullAnimations.erase(id);
std::map<s32, Anm2Item> newNullAnims;
s32 newAnimID = 0;
for (const auto& [_, nullAnim] : animation.nullAnimations)
newNullAnims[newAnimID++] = nullAnim;
animation.nullAnimations = std::move(newNullAnims);
}
} }
s32 anm2_animation_add(Anm2* self) s32 anm2_animation_add(Anm2* self)
{ {
s32 id = map_next_id_get(self->animations); s32 id = map_next_id_get(self->animations);
@@ -676,7 +724,7 @@ s32 anm2_animation_add(Anm2* self)
for (auto& [layerID, layer] : self->layers) for (auto& [layerID, layer] : self->layers)
animation.layerAnimations[layerID] = Anm2Item{}; animation.layerAnimations[layerID] = Anm2Item{};
for (auto & [nullID, null] : self->nulls) for (auto& [nullID, null] : self->nulls)
animation.nullAnimations[nullID] = Anm2Item{}; animation.nullAnimations[nullID] = Anm2Item{};
animation.rootAnimation.frames.push_back(Anm2Frame{}); animation.rootAnimation.frames.push_back(Anm2Frame{});
@@ -699,13 +747,19 @@ void anm2_new(Anm2* self)
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference) Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference)
{ {
auto it = self->animations.find(reference->animationID); if (reference->animationID == ID_NONE) return nullptr;
if (it == self->animations.end()) return nullptr;
return &it->second; if (!self->animations.contains(reference->animationID))
return nullptr;
return &self->animations[reference->animationID];
} }
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference) Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
{ {
if (reference->itemType == ANM2_NONE)
return nullptr;
Anm2Animation* animation = anm2_animation_from_reference(self, reference); Anm2Animation* animation = anm2_animation_from_reference(self, reference);
if (!animation) return nullptr; if (!animation) return nullptr;
@@ -715,19 +769,11 @@ Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
case ANM2_ROOT: case ANM2_ROOT:
return &animation->rootAnimation; return &animation->rootAnimation;
case ANM2_LAYER: case ANM2_LAYER:
{ if (!animation->layerAnimations.contains(reference->itemID)) return nullptr;
auto it = animation->layerAnimations.find(reference->itemID); return &animation->layerAnimations[reference->itemID];
if (it == animation->layerAnimations.end())
return nullptr;
return &it->second;
}
case ANM2_NULL: case ANM2_NULL:
{ if (!animation->nullAnimations.contains(reference->itemID)) return nullptr;
auto it = animation->nullAnimations.find(reference->itemID); return &animation->nullAnimations[reference->itemID];
if (it == animation->nullAnimations.end())
return nullptr;
return &it->second;
}
case ANM2_TRIGGERS: case ANM2_TRIGGERS:
return &animation->triggers; return &animation->triggers;
default: default:
@@ -740,7 +786,9 @@ Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference)
Anm2Item* item = anm2_item_from_reference(self, reference); Anm2Item* item = anm2_item_from_reference(self, reference);
if (!item) return nullptr; if (!item) return nullptr;
if (reference->frameIndex < 0 || reference->frameIndex >= (s32)item->frames.size()) return nullptr;
if (reference->frameIndex <= INDEX_NONE || reference->frameIndex >= (s32)item->frames.size())
return nullptr;
return &item->frames[reference->frameIndex]; return &item->frames[reference->frameIndex];
} }
@@ -777,19 +825,19 @@ void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference,
Anm2Animation* animation = anm2_animation_from_reference(self, &reference); Anm2Animation* animation = anm2_animation_from_reference(self, &reference);
if (!animation) return; if (!animation) return;
if (time < 0 || time > animation->frameNum) return;
time = std::clamp(time, 0.0f, animation->frameNum - 1.0f);
Anm2Item* item = anm2_item_from_reference(self, &reference); Anm2Item* item = anm2_item_from_reference(self, &reference);
if (!item) return; if (!item) return;
Anm2Frame* nextFrame = nullptr; Anm2Frame* frameNext = nullptr;
s32 delayCurrent = 0; s32 delayCurrent = 0;
s32 delayNext = 0; s32 delayNext = 0;
for (auto [i, iFrame] : std::views::enumerate(item->frames)) for (auto [i, iFrame] : std::views::enumerate(item->frames))
{ {
if (reference.itemType == ANM2_TRIGGERS) if (reference.itemType == ANM2_TRIGGERS)
{ {
if ((s32)time == iFrame.atFrame) if ((s32)time == iFrame.atFrame)
@@ -807,9 +855,9 @@ void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference,
if (time >= delayCurrent && time < delayNext) if (time >= delayCurrent && time < delayNext)
{ {
if (i + 1 < (s32)item->frames.size()) if (i + 1 < (s32)item->frames.size())
nextFrame = &item->frames[i + 1]; frameNext = &item->frames[i + 1];
else else
nextFrame = nullptr; frameNext = nullptr;
break; break;
} }
@@ -820,15 +868,15 @@ void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference,
if (reference.itemType == ANM2_TRIGGERS) if (reference.itemType == ANM2_TRIGGERS)
return; return;
if (frame->isInterpolated && nextFrame) if (frame->isInterpolated && frameNext && frame->delay > 1)
{ {
f32 interpolationTime = (time - delayCurrent) / (delayNext - delayCurrent); f32 interpolation = (time - delayCurrent) / (delayNext - delayCurrent);
frame->rotation = glm::mix(frame->rotation, nextFrame->rotation, interpolationTime);; frame->rotation = glm::mix(frame->rotation, frameNext->rotation, interpolation);
frame->position = glm::mix(frame->position, nextFrame->position, interpolationTime);; frame->position = glm::mix(frame->position, frameNext->position, interpolation);
frame->scale = glm::mix(frame->scale, nextFrame->scale, interpolationTime);; frame->scale = glm::mix(frame->scale, frameNext->scale, interpolation);
frame->offsetRGB = glm::mix(frame->offsetRGB, nextFrame->offsetRGB, interpolationTime);; frame->offsetRGB = glm::mix(frame->offsetRGB, frameNext->offsetRGB, interpolation);
frame->tintRGBA = glm::mix(frame->tintRGBA, nextFrame->tintRGBA, interpolationTime);; frame->tintRGBA = glm::mix(frame->tintRGBA, frameNext->tintRGBA, interpolation);
} }
} }
@@ -870,7 +918,8 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
Anm2Animation* animation = anm2_animation_from_reference(self, reference); Anm2Animation* animation = anm2_animation_from_reference(self, reference);
Anm2Item* item = anm2_item_from_reference(self, reference); Anm2Item* item = anm2_item_from_reference(self, reference);
if (!animation || !item) return nullptr; if (!animation || !item)
return nullptr;
if (item) if (item)
{ {
@@ -879,7 +928,7 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
if (reference->itemType == ANM2_TRIGGERS) if (reference->itemType == ANM2_TRIGGERS)
{ {
for (auto & frameCheck : item->frames) if (frameCheck.atFrame == time) return nullptr; for (auto& frameCheck : item->frames) if (frameCheck.atFrame == time) return nullptr;
frame.atFrame = time; frame.atFrame = time;
index = item->frames.size(); index = item->frames.size();
@@ -888,7 +937,7 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
{ {
s32 frameDelayCount = 0; s32 frameDelayCount = 0;
for (auto & frameCheck : item->frames) for (auto& frameCheck : item->frames)
frameDelayCount += frameCheck.delay; frameDelayCount += frameCheck.delay;
if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum) return nullptr; if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum) return nullptr;
@@ -929,4 +978,106 @@ void anm2_reference_item_clear(Anm2Reference* self)
void anm2_reference_frame_clear(Anm2Reference* self) void anm2_reference_frame_clear(Anm2Reference* self)
{ {
self->frameIndex = INDEX_NONE; self->frameIndex = INDEX_NONE;
}
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type)
{
Anm2Animation newAnimation = self->animations[animationID];
newAnimation.rootAnimation.frames.clear();
for (auto& [id, layerAnimation] : newAnimation.layerAnimations)
layerAnimation.frames.clear();
for (auto& [id, nullAnimation] : newAnimation.nullAnimations)
nullAnimation.frames.clear();
newAnimation.triggers.frames.clear();
auto merge_item = [&](Anm2Item& destinationItem, const Anm2Item& sourceItem)
{
switch (type)
{
case ANM2_MERGE_APPEND_FRAMES:
destinationItem.frames.insert(destinationItem.frames.end(), sourceItem.frames.begin(), sourceItem.frames.end());
break;
case ANM2_MERGE_PREPEND_FRAMES:
destinationItem.frames.insert(destinationItem.frames.begin(), sourceItem.frames.begin(), sourceItem.frames.end());
break;
case ANM2_MERGE_REPLACE_FRAMES:
if (destinationItem.frames.size() < sourceItem.frames.size())
destinationItem.frames.resize(sourceItem.frames.size());
for (s32 i = 0; i < (s32)sourceItem.frames.size(); i++)
destinationItem.frames[i] = sourceItem.frames[i];
break;
case ANM2_MERGE_IGNORE:
break;
}
};
for (auto mergeID : mergeIDs)
{
const Anm2Animation& mergeAnimation = self->animations[mergeID];
merge_item(newAnimation.rootAnimation, mergeAnimation.rootAnimation);
for (const auto& [id, layerAnimation] : mergeAnimation.layerAnimations)
merge_item(newAnimation.layerAnimations[id], layerAnimation);
for (const auto& [id, nullAnimation] : mergeAnimation.nullAnimations)
merge_item(newAnimation.nullAnimations[id], nullAnimation);
merge_item(newAnimation.triggers, mergeAnimation.triggers);
}
self->animations[animationID] = newAnimation;
anm2_animation_length_set(&self->animations[animationID]);
}
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation)
{
Anm2Item* item = anm2_item_from_reference(self, reference);
if (!item) return;
Anm2Frame* frame = anm2_frame_from_reference(self, reference);
if (!frame) return;
Anm2Reference referenceNext = *reference;
referenceNext.frameIndex = reference->frameIndex + 1;
Anm2Frame* frameNext = anm2_frame_from_reference(self, &referenceNext);
if (!frameNext) return;
const Anm2Frame baseFrame = *frame;
const Anm2Frame baseFrameNext = *frameNext;
s32 delay = 0;
s32 insertIndex = reference->frameIndex;
while (delay < baseFrame.delay)
{
f32 interpolation = (f32)delay / baseFrame.delay;
Anm2Frame baked = *frame;
baked.delay = std::min(interval, baseFrame.delay - delay);
baked.isInterpolated = (insertIndex == reference->frameIndex) ? baseFrame.isInterpolated : false;
baked.rotation = glm::mix(baseFrame.rotation, baseFrameNext.rotation, interpolation);
baked.position = glm::mix(baseFrame.position, baseFrameNext.position, interpolation);
baked.scale = glm::mix(baseFrame.scale, baseFrameNext.scale, interpolation);
baked.offsetRGB = glm::mix(baseFrame.offsetRGB, baseFrameNext.offsetRGB, interpolation);
baked.tintRGBA = glm::mix(baseFrame.tintRGBA, baseFrameNext.tintRGBA, interpolation);
if (isRoundScale) baked.scale = vec2((s32)baked.scale.x, (s32)baked.scale.y);
if (isRoundRotation) baked.rotation = (s32)baked.rotation;
if (insertIndex == reference->frameIndex)
item->frames[insertIndex] = baked;
else
item->frames.insert(item->frames.begin() + insertIndex, baked);
insertIndex++;
delay += baked.delay;
}
} }

View File

@@ -17,7 +17,7 @@
#define ANM2_READ_INFO "Read anm2 from file: {}" #define ANM2_READ_INFO "Read anm2 from file: {}"
#define ANM2_WRITE_ERROR "Failed to write anm2 to file: {}" #define ANM2_WRITE_ERROR "Failed to write anm2 to file: {}"
#define ANM2_WRITE_INFO "Wrote anm2 to file: {}" #define ANM2_WRITE_INFO "Wrote anm2 to file: {}"
#define STRING_ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p" #define ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
/* Elements */ /* Elements */
#define ANM2_ELEMENT_LIST \ #define ANM2_ELEMENT_LIST \
@@ -132,7 +132,7 @@ struct Anm2Spritesheet
struct Anm2Layer struct Anm2Layer
{ {
std::string name = "New Layer"; std::string name = "New Layer";
s32 spritesheetID = -1; s32 spritesheetID = ID_NONE;
}; };
struct Anm2Null struct Anm2Null
@@ -154,12 +154,12 @@ struct Anm2Frame
s32 delay = ANM2_FRAME_DELAY_MIN; s32 delay = ANM2_FRAME_DELAY_MIN;
s32 atFrame = INDEX_NONE; s32 atFrame = INDEX_NONE;
s32 eventID = ID_NONE; s32 eventID = ID_NONE;
vec2 crop = {0.0f, 0.0f}; vec2 crop{};
vec2 pivot = {0.0f, 0.0f}; vec2 pivot{};
vec2 position = {0.0f, 0.0f}; vec2 position{};
vec2 size = {0.0f, 0.0f}; vec2 size{};
vec2 scale = {100.0f, 100.0f}; vec2 scale{};
vec3 offsetRGB = {0.0f, 0.0f, 0.0f}; vec3 offsetRGB{};
vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f}; vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
}; };
@@ -183,7 +183,6 @@ struct Anm2Animation
struct Anm2 struct Anm2
{ {
std::string path{}; std::string path{};
std::string defaultAnimation{};
std::string createdBy = "robot"; std::string createdBy = "robot";
std::string createdOn{}; std::string createdOn{};
std::map<s32, Anm2Spritesheet> spritesheets; std::map<s32, Anm2Spritesheet> spritesheets;
@@ -191,9 +190,10 @@ struct Anm2
std::map<s32, Anm2Null> nulls; std::map<s32, Anm2Null> nulls;
std::map<s32, Anm2Event> events; std::map<s32, Anm2Event> events;
std::map<s32, Anm2Animation> animations; std::map<s32, Anm2Animation> animations;
std::map<s32, s32> layerMap; // id, index std::map<s32, s32> layerMap; // index, id
s32 defaultAnimationID{};
s32 fps = 30; s32 fps = 30;
s32 version = 0; s32 version{};
}; };
struct Anm2Reference struct Anm2Reference
@@ -223,6 +223,14 @@ struct Anm2FrameWithReference
Anm2Frame frame; Anm2Frame frame;
}; };
enum Anm2MergeType
{
ANM2_MERGE_APPEND_FRAMES,
ANM2_MERGE_REPLACE_FRAMES,
ANM2_MERGE_PREPEND_FRAMES,
ANM2_MERGE_IGNORE
};
void anm2_layer_add(Anm2* self); void anm2_layer_add(Anm2* self);
void anm2_layer_remove(Anm2* self, s32 id); void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self); void anm2_null_add(Anm2* self);
@@ -244,4 +252,6 @@ void anm2_reference_clear(Anm2Reference* self);
void anm2_reference_item_clear(Anm2Reference* self); void anm2_reference_item_clear(Anm2Reference* self);
void anm2_reference_frame_clear(Anm2Reference* self); void anm2_reference_frame_clear(Anm2Reference* self);
s32 anm2_animation_length_get(Anm2Animation* self); s32 anm2_animation_length_get(Anm2Animation* self);
void anm2_animation_length_set(Anm2Animation* self); void anm2_animation_length_set(Anm2Animation* self);
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);

View File

@@ -2,10 +2,19 @@
#include "canvas.h" #include "canvas.h"
void canvas_init(Canvas* self, vec2 size) static void _canvas_texture_free(Canvas* self)
{ {
self->size = size; if (self->fbo != 0) glDeleteFramebuffers(1, &self->fbo);
if (self->rbo != 0) glDeleteRenderbuffers(1, &self->rbo);
if (self->texture != 0) glDeleteTextures(1, &self->texture);
}
static void _canvas_texture_init(Canvas* self, vec2& size)
{
_canvas_texture_free(self);
self->size = size;
glGenFramebuffers(1, &self->fbo); glGenFramebuffers(1, &self->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
@@ -24,6 +33,43 @@ void canvas_init(Canvas* self, vec2 size)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void canvas_init(Canvas* self)
{
// Axis
glGenVertexArrays(1, &self->axisVAO);
glGenBuffers(1, &self->axisVBO);
glBindVertexArray(self->axisVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->axisVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_AXIS_VERTICES), CANVAS_AXIS_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
// Grid
glGenVertexArrays(1, &self->gridVAO);
glGenBuffers(1, &self->gridVBO);
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
// Rect
glGenVertexArrays(1, &self->rectVAO);
glGenBuffers(1, &self->rectVBO);
glBindVertexArray(self->rectVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
// Texture // Texture
glGenVertexArrays(1, &self->textureVAO); glGenVertexArrays(1, &self->textureVAO);
@@ -36,15 +82,240 @@ void canvas_init(Canvas* self, vec2 size)
glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_DYNAMIC_DRAW);
// Position attribute
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0);
// UV attribute
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32))); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32)));
glBindVertexArray(0);
}
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin)
{
f32 zoomFactor = PERCENT_TO_UNIT(zoom);
mat4 projection = glm::ortho(0.0f, self->size.x, 0.0f, self->size.y, -1.0f, 1.0f);
mat4 view = mat4{1.0f};
switch (origin)
{
case ORIGIN_TOP_LEFT:
view = glm::translate(view, vec3(pan, 0.0f));
break;
default:
view = glm::translate(view, vec3((self->size * 0.5f) + pan, 0.0f));
break;
}
view = glm::scale(view, vec3(zoomFactor, zoomFactor, 1.0f));
return projection * view;
}
void canvas_clear(vec4& color)
{
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT);
}
void canvas_viewport_set(Canvas* self)
{
glViewport(0, 0, (s32)self->size.x, (s32)self->size.y);
}
void canvas_texture_set(Canvas* self)
{
static vec2 previousSize = {-1, -1};
if (previousSize != self->size)
{
_canvas_texture_init(self, self->size);
previousSize = self->size;
}
}
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, f32& zoom, ivec2& size, ivec2& offset, vec4& color)
{
if (size.x <= 0 || size.y <= 0)
return; // avoid div-by-zero
std::vector<f32> vertices;
vec2 gridSize = self->size * (PERCENT_TO_UNIT(CANVAS_ZOOM_MAX - zoom));
// First visible vertical line <= 0
s32 startX = -(offset.x % size.x);
if (startX > 0) startX -= size.x;
for (s32 x = startX; x <= gridSize.x; x += size.x)
{
vertices.push_back((f32)x);
vertices.push_back(0.0f);
vertices.push_back((f32)x);
vertices.push_back((f32)gridSize.y);
}
// First visible horizontal line <= 0
s32 startY = -(offset.y % size.y);
if (startY > 0) startY -= size.y;
for (s32 y = startY; y <= gridSize.y; y += size.y)
{
vertices.push_back(0.0f);
vertices.push_back((f32)y);
vertices.push_back((f32)gridSize.x);
vertices.push_back((f32)y);
}
s32 vertexCount = (s32)vertices.size() / 2;
if (vertexCount == 0)
return;
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
glUseProgram(shader);
glBindVertexArray(self->gridVAO);
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
glDrawArrays(GL_LINES, 0, vertexCount);
glBindVertexArray(0); glBindVertexArray(0);
} glUseProgram(0);
}
void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& transform, const f32* vertices, vec4 tint, vec3 colorOffset)
{
glUseProgram(shader);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_UV_VERTICES), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(glGetUniformLocation(shader, SHADER_UNIFORM_TEXTURE), 0);
glUniform3fv(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(colorOffset));
glUniform4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TINT), 1, value_ptr(tint));
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color)
{
glUseProgram(shader);
glBindVertexArray(self->rectVAO);
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glUniform4fv(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), 1, value_ptr(color));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
}
void canvas_rect_dotted_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color)
{
glUseProgram(shader);
glBindVertexArray(self->rectVAO);
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glUniform4fv(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), 1, value_ptr(color));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
}
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color)
{
glUseProgram(shader);
glBindVertexArray(self->axisVAO);
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
glDrawArrays(GL_LINES, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
}
void canvas_bind(Canvas* self)
{
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
}
void canvas_unbind(void)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void canvas_free(Canvas* self)
{
_canvas_texture_free(self);
}
mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt)
{
vec2 scaleAbsolute = glm::abs(scale);
vec2 scaleSign = glm::sign(scale);
f32 usedSign = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
vec2 sizeScaled = size * scaleAbsolute;
vec2 pivotScaled = pivot * scaleAbsolute;
vec2 pivotAltScaled = pivotAlt * scaleAbsolute;
vec2 pivotAltMirrored = pivotScaled + (pivotAltScaled - pivotScaled) * scaleSign;
mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotScaled, 0.0f));
model = glm::scale(model, vec3(scaleSign, 1.0f));
model = glm::rotate(model, glm::radians(rotation) * usedSign, vec3(0,0,1));
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotAltMirrored, 0.0f));
model = glm::rotate(model, glm::radians(rotationAlt) * usedSign, vec3(0,0,1));
model = glm::translate(model, vec3(-pivotAltMirrored, 0.0f));
model = glm::scale(model, vec3(sizeScaled, 1.0f));
return transform * model;
}
/*
mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt)
{
vec2 scaleAbsolute = abs(scale);
vec2 signScale = glm::sign(scale);
vec2 pivotScaled = pivot * scaleAbsolute;
vec2 pivotAltScaled = pivotAlt * scaleAbsolute;
vec2 sizeScaled = size * scaleAbsolute;
mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotScaled, 0.0f));
model = glm::scale(model, vec3(signScale, 1.0f)); // Flip
model = glm::rotate(model, radians(rotation), vec3(0,0,1));
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotAltScaled, 0.0f));
model = glm::rotate(model, radians(rotationAlt), vec3(0,0,1));
model = glm::translate(model, vec3(-pivotAltScaled, 0.0f));
model = glm::scale(model, vec3(sizeScaled, 1.0f));
return transform * model;
}
*/

View File

@@ -1,33 +1,75 @@
#pragma once #pragma once
#include "texture.h" #include "resources.h"
#include "shader.h"
#define CANVAS_ZOOM_MIN 1 #define CANVAS_ZOOM_MIN 1.0f
#define CANVAS_ZOOM_MAX 1000 #define CANVAS_ZOOM_MAX 2000.0f
#define CANVAS_ZOOM_STEP 25 #define CANVAS_ZOOM_STEP 10.0f
#define CANVAS_LINE_LENGTH 100000.0f #define CANVAS_ZOOM_MOD 10.0f
#define CANVAS_GRID_RANGE 100
#define CANVAS_GRID_MIN 1 #define CANVAS_GRID_MIN 1
#define CANVAS_GRID_MAX 100 #define CANVAS_GRID_MAX 1000
#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
static const vec2 CANVAS_GRID_SIZE = {3200, 1600};
static const vec2 CANVAS_PIVOT_SIZE = {4, 4};
const f32 CANVAS_AXIS_VERTICES[] = const f32 CANVAS_AXIS_VERTICES[] =
{ {
-CANVAS_LINE_LENGTH, 0.0f, -CANVAS_LINE_LENGTH, 0.0f,
CANVAS_LINE_LENGTH, 0.0f, CANVAS_LINE_LENGTH, 0.0f,
0.0f, -CANVAS_LINE_LENGTH, 0.0f, -CANVAS_LINE_LENGTH,
0.0f, CANVAS_LINE_LENGTH 0.0f, CANVAS_LINE_LENGTH
}; };
struct Canvas struct Canvas
{ {
GLuint fbo; GLuint fbo{};
GLuint rbo; GLuint rbo{};
GLuint texture; GLuint axisVAO{};
GLuint textureEBO; GLuint axisVBO{};
GLuint textureVAO; GLuint rectVAO{};
GLuint textureVBO; GLuint rectVBO{};
vec2 size; GLuint gridVAO{};
GLuint gridVBO{};
GLuint texture{};
GLuint textureEBO{};
GLuint textureVAO{};
GLuint textureVBO{};
vec2 size{};
}; };
void canvas_init(Canvas* self, vec2 size); void canvas_init(Canvas* self);
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin);
void canvas_clear(vec4& color);
void canvas_bind(Canvas* self);
void canvas_viewport_set(Canvas* self);
void canvas_unbind(void);
void canvas_texture_set(Canvas* self);
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, f32& zoom, ivec2& size, ivec2& offset, vec4& color);
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
void canvas_free(Canvas* self);
void canvas_draw(Canvas* self);
mat4 canvas_mvp_get
(
mat4& transform,
vec2 size,
vec2 position = {0.0f, 0.0f},
vec2 pivot = {0.0f, 0.0f},
f32 rotation = {0.0f},
vec2 scale = {1.0f, 1.0f},
vec2 pivotAlt = {0.0f, 0.0f},
f32 rotationAlt = {0.0f}
);
void canvas_texture_draw
(
Canvas* self,
GLuint& shader,
GLuint& texture,
mat4& transform,
const f32* vertices = GL_UV_VERTICES,
vec4 tint = COLOR_OPAQUE,
vec3 colorOffset = COLOR_OFFSET_NONE
);

View File

@@ -47,17 +47,6 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
} }
break; break;
} }
case CLIPBOARD_EVENT:
{
Anm2EventWithID eventWithID = std::get<Anm2EventWithID>(self->data);
for (auto & [id, event] : anm2->events)
{
if (id == eventWithID.id)
anm2->events.erase(eventWithID.id);
}
break;
}
default: default:
break; break;
} }
@@ -81,7 +70,7 @@ static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* locati
if (!animation || !anm2Item) break; if (!animation || !anm2Item) break;
s32 insertIndex = (reference->itemType == ANM2_TRIGGERS) ? s32 insertIndex = (reference->itemType == ANM2_TRIGGERS) ?
reference->frameIndex : MAX(reference->frameIndex, (s32)anm2Item->frames.size()); reference->frameIndex : std::max(reference->frameIndex, (s32)anm2Item->frames.size());
anm2Item->frames.insert(anm2Item->frames.begin() + insertIndex, frameWithReference->frame); anm2Item->frames.insert(anm2Item->frames.begin() + insertIndex, frameWithReference->frame);
@@ -95,23 +84,11 @@ static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* locati
if (std::holds_alternative<s32>(*location)) if (std::holds_alternative<s32>(*location))
index = std::get<s32>(*location); index = std::get<s32>(*location);
index = CLAMP(index, 0, (s32)anm2->animations.size()); index = std::clamp(index, 0, (s32)anm2->animations.size());
map_insert_shift(anm2->animations, index, std::get<Anm2AnimationWithID>(self->data).animation); map_insert_shift(anm2->animations, index, std::get<Anm2AnimationWithID>(self->data).animation);
break; break;
} }
case CLIPBOARD_EVENT:
{
s32 index = 0;
if (std::holds_alternative<s32>(*location))
index = std::get<s32>(*location);
index = CLAMP(index, 0, (s32)anm2->events.size());
map_insert_shift(anm2->events, index, std::get<Anm2EventWithID>(self->data).event);
break;
}
default: default:
break; break;
} }

View File

@@ -7,7 +7,6 @@ enum ClipboardItemType
CLIPBOARD_NONE, CLIPBOARD_NONE,
CLIPBOARD_FRAME, CLIPBOARD_FRAME,
CLIPBOARD_ANIMATION, CLIPBOARD_ANIMATION,
CLIPBOARD_EVENT
}; };
struct ClipboardItem struct ClipboardItem
@@ -22,9 +21,6 @@ struct ClipboardItem
ClipboardItem(const Anm2AnimationWithID& anim) ClipboardItem(const Anm2AnimationWithID& anim)
: data(anim), type(CLIPBOARD_ANIMATION) {} : data(anim), type(CLIPBOARD_ANIMATION) {}
ClipboardItem(const Anm2EventWithID& event)
: data(event), type(CLIPBOARD_EVENT) {}
}; };
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, s32>; using ClipboardLocation = std::variant<std::monostate, Anm2Reference, s32>;

View File

@@ -14,9 +14,13 @@ static void _dialog_callback(void* userdata, const char* const* filelist, s32 fi
{ {
self->path = filelist[0]; self->path = filelist[0];
self->isSelected = true; self->isSelected = true;
self->selectedFilter = filter;
} }
else else
{
self->isSelected = false; self->isSelected = false;
self->selectedFilter = INDEX_NONE;
}
} }
void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window) void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window)
@@ -51,8 +55,10 @@ void dialog_png_replace(Dialog* self)
self->type = DIALOG_PNG_REPLACE; self->type = DIALOG_PNG_REPLACE;
} }
void dialog_tick(Dialog* self) void dialog_update(Dialog* self)
{ {
self->isJustSelected = false;
if (self->isSelected) if (self->isSelected)
{ {
Texture texture; Texture texture;
@@ -73,7 +79,7 @@ void dialog_tick(Dialog* self)
case DIALOG_PNG_OPEN: case DIALOG_PNG_OPEN:
id = map_next_id_get(self->resources->textures); id = map_next_id_get(self->resources->textures);
self->anm2->spritesheets[id] = Anm2Spritesheet{}; self->anm2->spritesheets[id] = Anm2Spritesheet{};
self->path = self->anm2->spritesheets[id].path; self->anm2->spritesheets[id].path = self->path;
resources_texture_init(self->resources, self->path, id); resources_texture_init(self->resources, self->path, id);
break; break;
case DIALOG_PNG_REPLACE: case DIALOG_PNG_REPLACE:
@@ -85,8 +91,23 @@ void dialog_tick(Dialog* self)
break; break;
} }
self->lastType = self->type;
self->lastPath = self->path;
self->type = DIALOG_NONE;
self->path.clear(); self->path.clear();
self->isJustSelected = true;
self->isSelected = false; self->isSelected = false;
} }
} }
void
dialog_reset(Dialog* self)
{
self->lastType = DIALOG_NONE;
self->type = DIALOG_NONE;
self->lastPath.clear();
self->path.clear();
self->isJustSelected = false;
self->isSelected = false;
}

View File

@@ -31,9 +31,13 @@ struct Dialog
Resources* resources = nullptr; Resources* resources = nullptr;
SDL_Window* window = nullptr; SDL_Window* window = nullptr;
std::string path{}; std::string path{};
std::string lastPath{};
s32 replaceID = ID_NONE; s32 replaceID = ID_NONE;
s32 selectedFilter{};
DialogType type = DIALOG_NONE; DialogType type = DIALOG_NONE;
DialogType lastType = DIALOG_NONE;
bool isSelected = false; bool isSelected = false;
bool isJustSelected = false;
}; };
void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window); void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window);
@@ -42,4 +46,5 @@ void dialog_png_open(Dialog* self);
void dialog_png_replace(Dialog* self); void dialog_png_replace(Dialog* self);
void dialog_anm2_save(Dialog* self); void dialog_anm2_save(Dialog* self);
void dialog_frame_directory_open(Dialog* self); void dialog_frame_directory_open(Dialog* self);
void dialog_tick(Dialog* self); void dialog_update(Dialog* self);
void dialog_reset(Dialog* self);

View File

@@ -2,47 +2,6 @@
#include "editor.h" #include "editor.h"
static s32 _editor_grid_set(Editor* self)
{
std::vector<f32> vertices;
s32 verticalLineCount = (s32)(EDITOR_SIZE.x / MIN(self->settings->editorGridSizeX, EDITOR_GRID_MIN));
s32 horizontalLineCount = (s32)(EDITOR_SIZE.y / MIN(self->settings->editorGridSizeY, EDITOR_GRID_MIN));
// Vertical
for (s32 i = 0; i <= verticalLineCount; i++)
{
s32 x = i * self->settings->editorGridSizeX - self->settings->editorGridOffsetX;
f32 normX = (2.0f * x) / EDITOR_SIZE.x - 1.0f;
vertices.push_back(normX);
vertices.push_back(-1.0f);
vertices.push_back(normX);
vertices.push_back(1.0f);
}
// Horizontal
for (s32 i = 0; i <= horizontalLineCount; i++)
{
s32 y = i * self->settings->editorGridSizeY - self->settings->editorGridOffsetY;
f32 normY = (2.0f * y) / EDITOR_SIZE.y - 1.0f;
vertices.push_back(-1.0f);
vertices.push_back(normY);
vertices.push_back(1.0f);
vertices.push_back(normY);
}
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
return (s32)vertices.size();
}
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
{ {
self->anm2 = anm2; self->anm2 = anm2;
@@ -50,253 +9,53 @@ void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources*
self->resources = resources; self->resources = resources;
self->settings = settings; self->settings = settings;
// Framebuffer + texture canvas_init(&self->canvas);
glGenFramebuffers(1, &self->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glGenTextures(1, &self->texture);
glBindTexture(GL_TEXTURE_2D, self->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)EDITOR_SIZE.x, (s32)EDITOR_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0);
glGenRenderbuffers(1, &self->rbo);
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (s32)EDITOR_SIZE.x, (s32)EDITOR_SIZE.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Grid
glGenVertexArrays(1, &self->gridVAO);
glGenBuffers(1, &self->gridVBO);
// Border
glGenVertexArrays(1, &self->borderVAO);
glGenBuffers(1, &self->borderVBO);
glBindVertexArray(self->borderVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->borderVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
// Texture
glGenVertexArrays(1, &self->textureVAO);
glGenBuffers(1, &self->textureVBO);
glGenBuffers(1, &self->textureEBO);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW);
// Position attribute
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0);
// UV position attribute
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32)));
glBindVertexArray(0);
_editor_grid_set(self);
} }
void editor_draw(Editor* self) void editor_draw(Editor* self)
{ {
GLuint shaderLine = self->resources->shaders[SHADER_LINE]; ivec2& gridSize = self->settings->editorGridSize;
GLuint shaderLineDotted = self->resources->shaders[SHADER_LINE_DOTTED]; ivec2& gridOffset = self->settings->editorGridOffset;
GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE]; vec4& gridColor = self->settings->editorGridColor;
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
f32 zoomFactor = PERCENT_TO_UNIT(self->settings->editorZoom); GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
// Get normalized panning canvas_texture_set(&self->canvas);
glm::vec2 ndcPan = glm::vec2(-self->settings->editorPanX / (EDITOR_SIZE.x / 2.0f), -self->settings->editorPanY / (EDITOR_SIZE.y / 2.0f));
canvas_bind(&self->canvas);
canvas_viewport_set(&self->canvas);
canvas_clear(self->settings->editorBackgroundColor);
glm::mat4 editorTransform = glm::translate(glm::mat4(1.0f), glm::vec3(ndcPan, 0.0f)); if (self->spritesheetID != ID_NONE)
editorTransform = glm::scale(editorTransform, glm::vec3(zoomFactor, zoomFactor, 1.0f));
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glViewport(0, 0, EDITOR_SIZE.x, EDITOR_SIZE.y);
glClearColor
(
self->settings->editorBackgroundColorR,
self->settings->editorBackgroundColorG,
self->settings->editorBackgroundColorB,
self->settings->editorBackgroundColorA
);
glClear(GL_COLOR_BUFFER_BIT);
// Drawing the selected spritesheet
if (self->spritesheetID > -1)
{ {
Texture* texture = &self->resources->textures[self->spritesheetID]; Texture texture = self->resources->textures[self->spritesheetID];
mat4 mvp = canvas_mvp_get(transform, texture.size);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, mvp);
glm::mat4 spritesheetTransform = editorTransform;
glm::vec2 ndcScale = glm::vec2(texture->size.x, texture->size.y) / (EDITOR_SIZE * 0.5f);
spritesheetTransform = glm::scale(spritesheetTransform, glm::vec3(ndcScale, 1.0f));
glUseProgram(shaderTexture);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_UV_VERTICES), GL_UV_VERTICES, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, value_ptr(COLOR_OPAQUE));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(COLOR_OFFSET_NONE));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(spritesheetTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);
glUseProgram(0);
// Border around the spritesheet
if (self->settings->editorIsBorder) if (self->settings->editorIsBorder)
{ canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_BORDER_COLOR);
glUseProgram(shaderLineDotted);
glBindVertexArray(self->borderVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, glm::value_ptr(spritesheetTransform));
glUniform4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR), 1, glm::value_ptr(EDITOR_BORDER_TINT));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
}
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference); Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
// Drawing the frame's crop and pivot
if (frame) if (frame)
{ {
// Crop mvp = canvas_mvp_get(transform, frame->size, frame->crop);
glm::mat4 rectTransform = editorTransform; canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_FRAME_COLOR);
glm::vec2 rectNDCPos = frame->crop / (EDITOR_SIZE / 2.0f);
glm::vec2 rectNDCScale = frame->size / (EDITOR_SIZE / 2.0f);
rectTransform = glm::translate(rectTransform, glm::vec3(rectNDCPos, 0.0f));
rectTransform = glm::scale(rectTransform, glm::vec3(rectNDCScale, 1.0f));
glUseProgram(shaderLineDotted);
glBindVertexArray(self->borderVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, glm::value_ptr(rectTransform));
glUniform4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR), 1, glm::value_ptr(EDITOR_FRAME_TINT));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
// Pivot
glm::mat4 pivotTransform = editorTransform;
glm::vec2 pivotNDCPos = ((frame->crop + frame->pivot) - (EDITOR_PIVOT_SIZE / 2.0f)) / (EDITOR_SIZE / 2.0f);
glm::vec2 pivotNDCScale = EDITOR_PIVOT_SIZE / (EDITOR_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(pivotNDCPos, 0.0f));
pivotTransform = glm::scale(pivotTransform, glm::vec3(pivotNDCScale, 1.0f));
glUseProgram(shaderTexture);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
mvp = canvas_mvp_get(transform, CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_PIVOT); f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_PIVOT);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, mvp, vertices, EDITOR_PIVOT_COLOR);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->resources->atlas.id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, value_ptr(EDITOR_FRAME_TINT));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(COLOR_OFFSET_NONE));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(pivotTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
} }
} }
// Grid
if (self->settings->editorIsGrid) if (self->settings->editorIsGrid)
{ canvas_grid_draw(&self->canvas, shaderLine, transform, self->settings->editorZoom, gridSize, gridOffset, gridColor);
static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -1;
glm::mat4 gridTransform = editorTransform; canvas_unbind();
glm::vec2 gridNDCPos = (EDITOR_SIZE / 2.0f) / (EDITOR_SIZE / 2.0f);
gridTransform = glm::translate(gridTransform, glm::vec3(gridNDCPos, 0.0f));
ivec2 gridSize = ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
if (previousGridSize != gridSize || previousGridOffset != gridOffset)
{
gridVertexCount = _editor_grid_set(self);
previousGridSize = gridSize;
previousGridOffset = gridOffset;
}
glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(gridTransform));
glUniform4f
(
glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR),
self->settings->editorGridColorR, self->settings->editorGridColorG, self->settings->editorGridColorB, self->settings->editorGridColorA
);
glDrawArrays(GL_LINES, 0, gridVertexCount);
glBindVertexArray(0);
glUseProgram(0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void editor_tick(Editor* self)
{
self->settings->editorZoom = CLAMP(self->settings->editorZoom, EDITOR_ZOOM_MIN, EDITOR_ZOOM_MAX);
} }
void editor_free(Editor* self) void editor_free(Editor* self)
{ {
glDeleteTextures(1, &self->texture); canvas_free(&self->canvas);
glDeleteFramebuffers(1, &self->fbo);
glDeleteRenderbuffers(1, &self->rbo);
} }

View File

@@ -1,23 +1,21 @@
#pragma once #pragma once
#include "anm2.h" #include "anm2.h"
#include "canvas.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
#include "canvas.h"
#define EDITOR_ZOOM_MIN 1 #define EDITOR_ZOOM_MIN 1.0f
#define EDITOR_ZOOM_MAX 1000 #define EDITOR_ZOOM_MAX 1000.0f
#define EDITOR_ZOOM_STEP 25 #define EDITOR_ZOOM_STEP 25.0
#define EDITOR_GRID_MIN 1 #define EDITOR_GRID_MIN 1
#define EDITOR_GRID_MAX 1000 #define EDITOR_GRID_MAX 1000
#define EDITOR_GRID_OFFSET_MIN 0 #define EDITOR_GRID_OFFSET_MIN 0
#define EDITOR_GRID_OFFSET_MAX 100 #define EDITOR_GRID_OFFSET_MAX 100
const vec2 EDITOR_SIZE = {2000, 2000}; static const vec4 EDITOR_BORDER_COLOR = COLOR_OPAQUE;
const vec2 EDITOR_PIVOT_SIZE = {4, 4}; static const vec4 EDITOR_FRAME_COLOR = COLOR_RED;
const vec4 EDITOR_TEXTURE_TINT = COLOR_OPAQUE; static const vec4 EDITOR_PIVOT_COLOR = COLOR_PINK;
const vec4 EDITOR_BORDER_TINT = COLOR_OPAQUE;
const vec4 EDITOR_FRAME_TINT = COLOR_RED;
struct Editor struct Editor
{ {
@@ -25,6 +23,7 @@ struct Editor
Anm2Reference* reference = nullptr; Anm2Reference* reference = nullptr;
Resources* resources = nullptr; Resources* resources = nullptr;
Settings* settings = nullptr; Settings* settings = nullptr;
Canvas canvas;
GLuint fbo; GLuint fbo;
GLuint rbo; GLuint rbo;
GLuint gridVAO; GLuint gridVAO;
@@ -39,7 +38,5 @@ struct Editor
}; };
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings); void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
void editor_draw(Editor* self); void editor_draw(Editor* self);
void editor_tick(Editor* self);
void editor_free(Editor* self); void editor_free(Editor* self);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,59 +2,6 @@
#include "preview.h" #include "preview.h"
static void _preview_axis_set(Preview* self)
{
glBindVertexArray(self->axisVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->axisVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_AXIS_VERTICES), CANVAS_AXIS_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
glBindVertexArray(0);
}
static s32 _preview_grid_set(Preview* self)
{
std::vector<f32> vertices;
s32 verticalLineCount = (s32)(PREVIEW_SIZE.x / MIN(self->settings->previewGridSizeX, PREVIEW_GRID_MIN));
s32 horizontalLineCount = (s32)(PREVIEW_SIZE.y / MIN(self->settings->previewGridSizeY, PREVIEW_GRID_MIN));
// Vertical
for (s32 i = 0; i <= verticalLineCount; i++)
{
s32 x = i * self->settings->previewGridSizeX - self->settings->previewGridOffsetX;
f32 normX = (2.0f * x) / PREVIEW_SIZE.x - 1.0f;
vertices.push_back(normX);
vertices.push_back(-1.0f);
vertices.push_back(normX);
vertices.push_back(1.0f);
}
// Horizontal
for (s32 i = 0; i <= horizontalLineCount; i++)
{
s32 y = i * self->settings->previewGridSizeY - self->settings->previewGridOffsetY;
f32 normY = (2.0f * y) / PREVIEW_SIZE.y - 1.0f;
vertices.push_back(-1.0f);
vertices.push_back(normY);
vertices.push_back(1.0f);
vertices.push_back(normY);
}
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
return (s32)vertices.size();
}
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
{ {
self->anm2 = anm2; self->anm2 = anm2;
@@ -62,87 +9,19 @@ void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources
self->resources = resources; self->resources = resources;
self->settings = settings; self->settings = settings;
// Framebuffer + texture canvas_init(&self->canvas);
glGenFramebuffers(1, &self->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glGenTextures(1, &self->texture);
glBindTexture(GL_TEXTURE_2D, self->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)PREVIEW_SIZE.x, (s32)PREVIEW_SIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0);
glGenRenderbuffers(1, &self->rbo);
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (s32)PREVIEW_SIZE.x, (s32)PREVIEW_SIZE.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Axis
glGenVertexArrays(1, &self->axisVAO);
glGenBuffers(1, &self->axisVBO);
// Grid
glGenVertexArrays(1, &self->gridVAO);
glGenBuffers(1, &self->gridVBO);
// Rect
glGenVertexArrays(1, &self->rectVAO);
glGenBuffers(1, &self->rectVBO);
glBindVertexArray(self->rectVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
// Texture
glGenVertexArrays(1, &self->textureVAO);
glGenBuffers(1, &self->textureVBO);
glGenBuffers(1, &self->textureEBO);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_STATIC_DRAW);
// Position attribute
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0);
// UV attribute
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32)));
glBindVertexArray(0);
_preview_axis_set(self);
_preview_grid_set(self);
} }
void preview_tick(Preview* self) void preview_tick(Preview* self)
{ {
self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
// If animation is valid, manage playback
if (animation) if (animation)
{ {
if (self->isPlaying) if (self->isPlaying)
{ {
self->time += (f32)self->anm2->fps / TICK_RATE; self->time += (f32)self->anm2->fps / TICK_RATE;
// If looping, return back to 0; if not, stop at length
if (self->time >= (f32)animation->frameNum - 1) if (self->time >= (f32)animation->frameNum - 1)
{ {
if (self->settings->playbackIsLoop && !self->isRecording) if (self->settings->playbackIsLoop && !self->isRecording)
@@ -152,414 +31,181 @@ void preview_tick(Preview* self)
} }
} }
// Make sure to clamp time within appropriate range
if (!self->isPlaying) if (!self->isPlaying)
self->time = CLAMP(self->time, 0.0f, (f32)animation->frameNum - 1); self->time = std::clamp(self->time, 0.0f, (f32)animation->frameNum - 1);
} }
} }
void preview_draw(Preview* self) void preview_draw(Preview* self)
{ {
GLuint shaderLine = self->resources->shaders[SHADER_LINE]; ivec2& gridSize = self->settings->previewGridSize;
GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE]; ivec2& gridOffset = self->settings->previewGridOffset;
static bool isRecordThisFrame = false; vec4& gridColor = self->settings->previewGridColor;
static f32 recordFrameTimeNext = 0.0f; f32& zoom = self->settings->previewZoom;
static s32 recordFrameIndex = 0; GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
f32 zoomFactor = PERCENT_TO_UNIT(self->settings->previewZoom); mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
glm::vec2 ndcPan = glm::vec2(-self->settings->previewPanX / (PREVIEW_SIZE.x / 2.0f), -self->settings->previewPanY / (PREVIEW_SIZE.y / 2.0f));
glm::mat4 previewTransform = glm::translate(glm::mat4(1.0f), glm::vec3(ndcPan, 0.0f)); canvas_texture_set(&self->canvas);
previewTransform = glm::scale(previewTransform, glm::vec3(zoomFactor, zoomFactor, 1.0f));
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glViewport(0, 0, (s32)PREVIEW_SIZE.x, (s32)PREVIEW_SIZE.y);
glClearColor
(
self->settings->previewBackgroundColorR,
self->settings->previewBackgroundColorG,
self->settings->previewBackgroundColorB,
self->settings->previewBackgroundColorA
);
glClear(GL_COLOR_BUFFER_BIT);
// Grid
if (self->settings->previewIsGrid)
{
static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -1;
ivec2 gridSize = ivec2(self->settings->previewGridSizeX, self->settings->previewGridSizeY);
ivec2 gridOffset = ivec2(self->settings->previewGridOffsetX, self->settings->previewGridOffsetY);
if (previousGridSize != gridSize || previousGridOffset != gridOffset)
{
gridVertexCount = _preview_grid_set(self);
previousGridSize = gridSize;
previousGridOffset = gridOffset;
}
glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(previewTransform));
glUniform4f
(
glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR),
self->settings->previewGridColorR, self->settings->previewGridColorG, self->settings->previewGridColorB, self->settings->previewGridColorA
);
glDrawArrays(GL_LINES, 0, gridVertexCount);
glBindVertexArray(0); canvas_bind(&self->canvas);
glUseProgram(0); canvas_viewport_set(&self->canvas);
} canvas_clear(self->settings->previewBackgroundColor);
if (self->settings->previewIsGrid)
canvas_grid_draw(&self->canvas, shaderLine, transform, zoom, gridSize, gridOffset, gridColor);
// Axes
if (self->settings->previewIsAxis) if (self->settings->previewIsAxis)
{ canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxisColor);
glUseProgram(shaderLine);
glBindVertexArray(self->axisVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(previewTransform));
glUniform4f
(
glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR),
self->settings->previewAxisColorR, self->settings->previewAxisColorG, self->settings->previewAxisColorB, self->settings->previewAxisColorA
);
glDrawArrays(GL_LINES, 0, 2);
glUniform4f
(
glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR),
self->settings->previewAxisColorR, self->settings->previewAxisColorG, self->settings->previewAxisColorB, self->settings->previewAxisColorA
);
glDrawArrays(GL_LINES, 2, 2);
glBindVertexArray(0);
glUseProgram(0);
}
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
s32& animationID = self->reference->animationID; s32& animationID = self->reference->animationID;
// Animation
if (animation) if (animation)
{ {
Anm2Frame rootFrame; Anm2Frame root;
Anm2Frame frame; mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{animationID, ANM2_ROOT, 0, 0}, self->time);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
// Root
if (animation->rootAnimation.isVisible && root.isVisible)
{
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale));
mat4 rootTransform = transform * model;
f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_TARGET);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
}
// Layers // Layers
for (auto [i, id] : self->anm2->layerMap) for (auto [i, id] : self->anm2->layerMap)
{ {
Anm2Frame frame;
Anm2Item& layerAnimation = animation->layerAnimations[id]; Anm2Item& layerAnimation = animation->layerAnimations[id];
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue; continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, self->time); anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
if (!frame.isVisible) if (!frame.isVisible)
continue; continue;
Texture* texture = &self->resources->textures[self->anm2->layers[id].spritesheetID]; Texture texture = self->resources->textures[self->anm2->layers[id].spritesheetID];
if (texture->isInvalid) if (texture.isInvalid)
continue; continue;
glm::mat4 layerTransform = previewTransform; vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
glm::vec2 position = self->settings->previewIsRootTransform ?
(frame.position + rootFrame.position) : frame.position;
glm::vec2 scale = self->settings->previewIsRootTransform ?
PERCENT_TO_UNIT(frame.scale) * PERCENT_TO_UNIT(rootFrame.scale) : PERCENT_TO_UNIT(frame.scale);
glm::vec2 ndcPos = position / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPivotOffset = (frame.pivot * scale) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = (frame.size * scale) / (PREVIEW_SIZE / 2.0f);
f32 rotation = frame.rotation;
layerTransform = glm::translate(layerTransform, glm::vec3(ndcPos - ndcPivotOffset, 0.0f));
layerTransform = glm::translate(layerTransform, glm::vec3(ndcPivotOffset, 0.0f));
layerTransform = glm::rotate(layerTransform, glm::radians(rotation), glm::vec3(0, 0, 1));
layerTransform = glm::translate(layerTransform, glm::vec3(-ndcPivotOffset, 0.0f));
layerTransform = glm::scale(layerTransform, glm::vec3(ndcScale, 1.0f));
glm::vec2 uvMin = frame.crop / glm::vec2(texture->size);
glm::vec2 uvMax = (frame.crop + frame.size) / glm::vec2(texture->size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax); f32 vertices[] = UV_VERTICES(uvMin, uvMax);
glUseProgram(shaderTexture); mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
mat4 layerTransform = transform * (rootModel * model);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0); canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, (f32*)value_ptr(frame.tintRGBA));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, (f32*)value_ptr(frame.offsetRGB));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(layerTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
// Root target if (self->settings->previewIsBorder)
if (animation->rootAnimation.isVisible && rootFrame.isVisible) canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
{
glm::mat4 rootTransform = previewTransform;
glm::vec2 ndcPos = (rootFrame.position - (PREVIEW_TARGET_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = PREVIEW_TARGET_SIZE / (PREVIEW_SIZE / 2.0f);
rootTransform = glm::translate(rootTransform, glm::vec3(ndcPos, 0.0f)); if (self->settings->previewIsShowPivot)
rootTransform = glm::scale(rootTransform, glm::vec3(ndcScale, 1.0f));
f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_TARGET);
glUseProgram(shaderTexture);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->resources->atlas.id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, value_ptr(PREVIEW_ROOT_TINT));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(COLOR_OFFSET_NONE));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(rootTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
// Layer pivots
if (self->settings->previewIsShowPivot)
{
// Layers (Reversed)
for (auto & [id, layerAnimation] : animation->layerAnimations)
{ {
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id, 0}, self->time);
if (!frame.isVisible)
continue;
glm::mat4 pivotTransform = previewTransform;
glm::vec2 position = self->settings->previewIsRootTransform
? (frame.position + rootFrame.position) : frame.position;
glm::vec2 scale = self->settings->previewIsRootTransform ?
PERCENT_TO_UNIT(frame.scale) * PERCENT_TO_UNIT(rootFrame.scale) : PERCENT_TO_UNIT(frame.scale);
glm::vec2 ndcPos = (position - (PREVIEW_PIVOT_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = PREVIEW_PIVOT_SIZE / (PREVIEW_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(ndcPos, 0.0f));
pivotTransform = glm::scale(pivotTransform, glm::vec3(ndcScale, 1.0f));
f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_PIVOT); f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_PIVOT);
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
glUseProgram(shaderTexture); mat4 pivotTransform = transform * (rootModel * pivotModel);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->resources->atlas.id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, value_ptr(PREVIEW_PIVOT_TINT));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(COLOR_OFFSET_NONE));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(pivotTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
} }
} }
// Null target/rect // Nulls
for (auto & [id, nullAnimation] : animation->nullAnimations) for (auto& [id, nullAnimation] : animation->nullAnimations)
{ {
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0) if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue; continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id, 0}, self->time); Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, self->time);
if (!frame.isVisible) if (!frame.isVisible)
continue; continue;
Anm2Null* null = &self->anm2->nulls[id]; Anm2Null null = self->anm2->nulls[id];
glm::mat4 nullTransform = previewTransform; vec4 color = (self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ?
PREVIEW_NULL_SELECTED_COLOR :
PREVIEW_NULL_COLOR;
TextureType textureType = null->isShowRect ? TEXTURE_UNINTERPOLATED : TEXTURE_TARGET; vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
glm::vec2 size = null->isShowRect ? PREVIEW_POINT_SIZE : PREVIEW_TARGET_SIZE; TextureType texture = null.isShowRect ? TEXTURE_SQUARE : TEXTURE_TARGET;
glm::vec2 pos = self->settings->previewIsRootTransform ? frame.position + (rootFrame.position) - (size / 2.0f) : frame.position - (size / 2.0f);
mat4 model = quad_model_get(size, frame.position, size * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
mat4 nullTransform = transform * (rootModel * model);
f32 vertices[] = ATLAS_UV_VERTICES(texture);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
glm::vec2 ndcPos = pos / (PREVIEW_SIZE / 2.0f); if (null.isShowRect)
glm::vec2 ndcScale = size / (PREVIEW_SIZE / 2.0f);
nullTransform = glm::translate(nullTransform, glm::vec3(ndcPos, 0.0f));
nullTransform = glm::scale(nullTransform, glm::vec3(ndcScale, 1.0f));
f32 vertices[] = ATLAS_UV_VERTICES(textureType);
glUseProgram(shaderTexture);
glBindVertexArray(self->textureVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->resources->atlas.id);
glUniform1i(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TEXTURE), 0);
glUniform4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TINT), 1, value_ptr(PREVIEW_NULL_TINT));
glUniform3fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_COLOR_OFFSET), 1, value_ptr(COLOR_OFFSET_NONE));
glUniformMatrix4fv(glGetUniformLocation(shaderTexture, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(nullTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
// Null rect
if (null->isShowRect)
{ {
glm::mat4 rectTransform = previewTransform; mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
mat4 rectTransform = transform * (rootModel * rectModel);
glm::vec2 rectPos = pos - (PREVIEW_NULL_RECT_SIZE / 2.0f); canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
glm::vec2 rectNDCPos = rectPos / (PREVIEW_SIZE / 2.0f);
glm::vec2 rectNDCScale = PREVIEW_NULL_RECT_SIZE / (PREVIEW_SIZE / 2.0f);
rectTransform = glm::translate(rectTransform, glm::vec3(rectNDCPos, 0.0f));
rectTransform = glm::scale(rectTransform, glm::vec3(rectNDCScale, 1.0f));
glUseProgram(shaderLine);
glBindVertexArray(self->rectVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, glm::value_ptr(rectTransform));
glUniform4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_COLOR), 1, glm::value_ptr(PREVIEW_NULL_TINT));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(0);
glUseProgram(0);
} }
} }
} }
/* s32& animationOverlayID = self->animationOverlayID;
if (self->isRecording && animation) Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
if (animationOverlay)
{ {
if (recordFrameIndex == 0) Anm2Frame root;
mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
for (auto [i, id] : self->anm2->layerMap)
{ {
if Anm2Frame frame;
( Anm2Item& layerAnimation = animation->layerAnimations[id];
std::filesystem::exists(STRING_PREVIEW_FRAMES_DIRECTORY) &&
std::filesystem::is_directory(STRING_PREVIEW_FRAMES_DIRECTORY))
{
for (const auto & entry : std::filesystem::directory_iterator(STRING_PREVIEW_FRAMES_DIRECTORY))
std::filesystem::remove(entry);
}
else
std::filesystem::create_directories(STRING_PREVIEW_FRAMES_DIRECTORY);
self->isPlaying = true;
}
if (isRecordThisFrame) if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
{ continue;
size_t frameSize = (self->recordSize.x * self->recordSize.y * 4);
u8* frame = (u8*)calloc(frameSize, 1);
std:: string path;
vec2 position = anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationOverlayID, ANM2_LAYER, id}, self->time);
{
self->settings->previewPanX - (PREVIEW_SIZE.x / 2.0f) + (self->recordSize.x / 2.0f),
self->settings->previewPanY - (PREVIEW_SIZE.y / 2.0f) + (self->recordSize.y / 2.0f)
};
path = std::format(STRING_PREVIEW_FRAMES_FORMAT, STRING_PREVIEW_FRAMES_DIRECTORY, recordFrameIndex); if (!frame.isVisible)
continue;
glReadBuffer(GL_FRONT); Texture texture = self->resources->textures[self->anm2->layers[id].spritesheetID];
glReadPixels
(
(s32)position.x,
(s32)position.y,
self->recordSize.x,
self->recordSize.y,
GL_RGBA,
GL_UNSIGNED_BYTE,
frame
);
texture_from_data_write(path, frame, self->recordSize.x, self->recordSize.y); if (texture.isInvalid)
continue;
free(frame); vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
isRecordThisFrame = false; mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
recordFrameIndex++; mat4 layerTransform = transform * (rootModel * model);
}
else vec4 tint = frame.tintRGBA;
{ tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
if (self->time >= (f32)animation->frameNum - 1)
{ canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, tint, frame.offsetRGB);
self->isRecording = false;
self->isPlaying = false;
recordFrameIndex = 0;
recordFrameTimeNext = 0;
self->time = 0.0f;
}
else if (self->time >= recordFrameTimeNext)
{
isRecordThisFrame = true;
recordFrameTimeNext = self->time + (f32)self->anm2->fps / TICK_RATE;
}
} }
} }
*/
glBindFramebuffer(GL_FRAMEBUFFER, 0); canvas_unbind();
} }
void preview_free(Preview* self) void preview_free(Preview* self)
{ {
glDeleteTextures(1, &self->texture); canvas_free(&self->canvas);
glDeleteFramebuffers(1, &self->fbo);
glDeleteRenderbuffers(1, &self->rbo);
} }

View File

@@ -6,6 +6,7 @@
#include "canvas.h" #include "canvas.h"
const vec2 PREVIEW_SIZE = {2000, 2000}; const vec2 PREVIEW_SIZE = {2000, 2000};
const vec2 PREVIEW_CANVAS_SIZE = {2000, 2000};
const vec2 PREVIEW_CENTER = {0, 0}; const vec2 PREVIEW_CENTER = {0, 0};
#define PREVIEW_ZOOM_MIN 1 #define PREVIEW_ZOOM_MIN 1
@@ -18,11 +19,12 @@ const vec2 PREVIEW_CENTER = {0, 0};
const vec2 PREVIEW_NULL_RECT_SIZE = {100, 100}; const vec2 PREVIEW_NULL_RECT_SIZE = {100, 100};
const vec2 PREVIEW_POINT_SIZE = {2, 2}; const vec2 PREVIEW_POINT_SIZE = {2, 2};
const vec2 PREVIEW_PIVOT_SIZE = {4, 4};
const vec4 PREVIEW_ROOT_TINT = COLOR_GREEN;
const vec4 PREVIEW_NULL_TINT = COLOR_BLUE;
const vec4 PREVIEW_PIVOT_TINT = COLOR_RED;
const vec2 PREVIEW_TARGET_SIZE = {16, 16}; const vec2 PREVIEW_TARGET_SIZE = {16, 16};
const vec4 PREVIEW_BORDER_COLOR = COLOR_RED;
const vec4 PREVIEW_ROOT_COLOR = COLOR_GREEN;
const vec4 PREVIEW_NULL_COLOR = COLOR_BLUE;
const vec4 PREVIEW_NULL_SELECTED_COLOR = COLOR_RED;
const vec4 PREVIEW_PIVOT_COLOR = COLOR_RED;
struct Preview struct Preview
{ {
@@ -30,21 +32,10 @@ struct Preview
Anm2Reference* reference = nullptr; Anm2Reference* reference = nullptr;
Resources* resources = nullptr; Resources* resources = nullptr;
Settings* settings = nullptr; Settings* settings = nullptr;
GLuint axisVAO = ID_NONE; s32 animationOverlayID = ID_NONE;
GLuint axisVBO = ID_NONE; Canvas canvas;
GLuint fbo = ID_NONE;
GLuint gridVAO = ID_NONE;
GLuint gridVBO = ID_NONE;
GLuint rbo = ID_NONE;
GLuint texture = ID_NONE;
GLuint rectVAO = ID_NONE;
GLuint rectVBO = ID_NONE;
GLuint textureEBO = ID_NONE;
GLuint textureVAO = ID_NONE;
GLuint textureVBO = ID_NONE;
bool isPlaying = false; bool isPlaying = false;
bool isRecording = false; bool isRecording = false;
vec2 recordSize{};
f32 time{}; f32 time{};
}; };

View File

@@ -2,34 +2,59 @@
static void _settings_setting_load(Settings* self, const std::string& line) static void _settings_setting_load(Settings* self, const std::string& line)
{ {
for (s32 i = 0; i < SETTINGS_COUNT; i++) for (s32 i = 0; i < SETTINGS_COUNT; i++)
{ {
const std::string& key = SETTINGS_ENTRIES[i].key; const auto& entry = SETTINGS_ENTRIES[i];
size_t keyLength = key.length(); const std::string& key = entry.key;
void* target = (u8*)self + entry.offset;
if (line.compare(0, keyLength, key) == 0) auto match_key = [&](const std::string& full) -> const char*
{ {
const char* value = line.c_str() + keyLength; return (line.starts_with(full) && line[full.size()] == '=') ? line.c_str() + full.size() + 1 : nullptr;
void* target = (u8*)self + SETTINGS_ENTRIES[i].offset; };
switch (SETTINGS_ENTRIES[i].type) auto* value = match_key(key);
if (value)
{
switch (entry.type)
{ {
case SETTINGS_TYPE_INT: case TYPE_INT:
*(s32*)target = std::atoi(value); *(s32*)target = std::atoi(value);
break; return;
case SETTINGS_TYPE_BOOL: case TYPE_BOOL:
*(s32*)target = string_to_bool(std::string(value)); *(bool*)target = string_to_bool(value);
break; return;
case SETTINGS_TYPE_FLOAT: case TYPE_FLOAT:
*(f32*)target = std::atof(value); *(f32*)target = std::atof(value);
break; return;
case SETTINGS_TYPE_STRING: case TYPE_STRING:
*(std::string*)target = std::string(value); *(std::string*)target = value;
break; return;
default: default:
break; break;
} }
return; }
if (entry.type == TYPE_VEC2)
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
}
else if (entry.type == TYPE_IVEC2)
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
}
else if (entry.type == TYPE_VEC4)
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
} }
} }
} }
@@ -41,23 +66,48 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
switch (entry.type) switch (entry.type)
{ {
case SETTINGS_TYPE_INT: case TYPE_INT:
value = std::format("{}", *(s32*)(selfPointer + entry.offset)); value = std::format("{}", *(s32*)(selfPointer + entry.offset));
out << entry.key << "=" << value << "\n";
break; break;
case SETTINGS_TYPE_BOOL: case TYPE_BOOL:
value = std::format("{}", *(bool*)(selfPointer + entry.offset)); value = std::format("{}", *(bool*)(selfPointer + entry.offset));
out << entry.key << "=" << value << "\n";
break; break;
case SETTINGS_TYPE_FLOAT: case TYPE_FLOAT:
value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset)); value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset));
out << entry.key << "=" << value << "\n";
break; break;
case SETTINGS_TYPE_STRING: case TYPE_STRING:
value = *(std::string*)(selfPointer + entry.offset); value = *(std::string*)(selfPointer + entry.offset);
out << entry.key << "=" << value << "\n";
break; break;
case TYPE_IVEC2:
{
ivec2* data = (ivec2*)(selfPointer + entry.offset);
out << entry.key << "X=" << data->x << "\n";
out << entry.key << "Y=" << data->y << "\n";
break;
}
case TYPE_VEC2:
{
vec2* data = (vec2*)(selfPointer + entry.offset);
out << entry.key << "X=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
break;
}
case TYPE_VEC4:
{
vec4* data = (vec4*)(selfPointer + entry.offset);
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
out << entry.key << "A=" << std::format(SETTINGS_FLOAT_FORMAT, data->a) << "\n";
break;
}
default: default:
break; break;
} }
out << entry.key << value << "\n";
} }
void settings_save(Settings* self) void settings_save(Settings* self)
@@ -75,6 +125,7 @@ void settings_save(Settings* self)
input.close(); input.close();
std::ofstream output(SETTINGS_PATH); std::ofstream output(SETTINGS_PATH);
if (!output) if (!output)
{ {
log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH)); log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH));

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "COMMON.h" #include "tool.h"
#define SETTINGS_BUFFER 0xFFFF #define SETTINGS_BUFFER 0xFFFF
#define SETTINGS_BUFFER_ITEM 0xFF #define SETTINGS_BUFFER_ITEM 0xFF
@@ -8,124 +8,73 @@
#define SETTINGS_SECTION_IMGUI "# Dear ImGui" #define SETTINGS_SECTION_IMGUI "# Dear ImGui"
#define SETTINGS_INIT_ERROR "Failed to read settings file! ({})" #define SETTINGS_INIT_ERROR "Failed to read settings file! ({})"
#define SETTINGS_PATH "settings.ini" #define SETTINGS_PATH "settings.ini"
#define SETTINGS_FLOAT_FORMAT "{:.3f}"
enum SettingsValueType
{
SETTINGS_TYPE_INT,
SETTINGS_TYPE_FLOAT,
SETTINGS_TYPE_BOOL,
SETTINGS_TYPE_STRING
};
struct SettingsEntry struct SettingsEntry
{ {
std::string key; std::string key;
SettingsValueType type; DataType type;
s32 offset; s32 offset;
}; };
struct Settings struct Settings
{ {
s32 windowW = 1920; ivec2 windowSize = {1080, 720};
s32 windowH = 1080;
bool playbackIsLoop = true; bool playbackIsLoop = true;
bool previewIsAxis = true; bool previewIsAxis = true;
bool previewIsGrid = true; bool previewIsGrid = true;
bool previewIsRootTransform = false; bool previewIsRootTransform = false;
bool previewIsShowPivot = false; bool previewIsShowPivot = false;
f32 previewPanX = 0.0f; bool previewIsBorder = false;
f32 previewPanY = 0.0f; f32 previewOverlayTransparency = 255.0f;
f32 previewZoom = 200.0f; f32 previewZoom = 200.0;
s32 previewGridSizeX = 32; vec2 previewPan = {0.0, 0.0};
s32 previewGridSizeY = 32; ivec2 previewGridSize = {32, 32};
s32 previewGridOffsetX = 0; ivec2 previewGridOffset{};
s32 previewGridOffsetY = 0; vec4 previewGridColor = {1.0, 1.0, 1.0, 0.125};
f32 previewGridColorR = 1.0f; vec4 previewAxisColor = {1.0, 1.0, 1.0, 0.125};
f32 previewGridColorG = 1.0f; vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
f32 previewGridColorB = 1.0f;
f32 previewGridColorA = 0.125f;
f32 previewAxisColorR = 1.0f;
f32 previewAxisColorG = 1.0f;
f32 previewAxisColorB = 1.0f;
f32 previewAxisColorA = 0.5f;
f32 previewBackgroundColorR = 0.113f;
f32 previewBackgroundColorG = 0.184f;
f32 previewBackgroundColorB = 0.286f;
f32 previewBackgroundColorA = 1.0f;
bool editorIsGrid = true; bool editorIsGrid = true;
bool editorIsGridSnap = true; bool editorIsGridSnap = true;
bool editorIsBorder = true; bool editorIsBorder = true;
f32 editorPanX = 0.0f; f32 editorZoom = 200.0;
f32 editorPanY = 0.0f; vec2 editorPan = {0.0, 0.0};
f32 editorZoom = 200.0f; ivec2 editorGridSize = {32, 32};
s32 editorGridSizeX = 32; ivec2 editorGridOffset = {32, 32};
s32 editorGridSizeY = 32; vec4 editorGridColor = {1.0, 1.0, 1.0, 0.125};
s32 editorGridOffsetX = 32; vec4 editorBackgroundColor = {0.113, 0.184, 0.286, 1.0};
s32 editorGridOffsetY = 32; ToolType tool = TOOL_PAN;
f32 editorGridColorR = 1.0f; vec4 toolColor = {1.0, 1.0, 1.0, 1.0};
f32 editorGridColorG = 1.0f;
f32 editorGridColorB = 1.0f;
f32 editorGridColorA = 0.125f;
f32 editorBackgroundColorR = 0.113f;
f32 editorBackgroundColorG = 0.184f;
f32 editorBackgroundColorB = 0.286f;
f32 editorBackgroundColorA = 1.0f;
f32 toolColorR = 1.0f;
f32 toolColorG = 1.0f;
f32 toolColorB = 1.0f;
f32 toolColorA = 1.0f;
}; };
const SettingsEntry SETTINGS_ENTRIES[] = const SettingsEntry SETTINGS_ENTRIES[] =
{ {
{"windowW=", SETTINGS_TYPE_INT, offsetof(Settings, windowW)}, {"window", TYPE_IVEC2, offsetof(Settings, windowSize)},
{"windowH=", SETTINGS_TYPE_INT, offsetof(Settings, windowH)}, {"playbackIsLoop", TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
{"playbackIsLoop=", SETTINGS_TYPE_BOOL, offsetof(Settings, playbackIsLoop)}, {"previewIsAxis", TYPE_BOOL, offsetof(Settings, previewIsAxis)},
{"previewIsAxis=", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsAxis)}, {"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
{"previewIsGrid=", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsGrid)}, {"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
{"previewIsRootTransform=", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsRootTransform)}, {"previewIsShowPivot", TYPE_BOOL, offsetof(Settings, previewIsShowPivot)},
{"previewIsShowPivot=", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsShowPivot)}, {"previewIsBorder", TYPE_BOOL, offsetof(Settings, previewIsBorder)},
{"previewPanX=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewPanX)}, {"previewOverlayTransparency", TYPE_FLOAT, offsetof(Settings, previewOverlayTransparency)},
{"previewPanY=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewPanY)}, {"previewZoom", TYPE_FLOAT, offsetof(Settings, previewZoom)},
{"previewZoom=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewZoom)}, {"previewPan", TYPE_VEC2, offsetof(Settings, previewPan)},
{"previewGridSizeX=", SETTINGS_TYPE_INT, offsetof(Settings, previewGridSizeX)}, {"previewGridSize", TYPE_IVEC2, offsetof(Settings, previewGridSize)},
{"previewGridSizeY=", SETTINGS_TYPE_INT, offsetof(Settings, previewGridSizeY)}, {"previewGridOffset", TYPE_IVEC2, offsetof(Settings, previewGridOffset)},
{"previewGridOffsetX=", SETTINGS_TYPE_INT, offsetof(Settings, previewGridOffsetX)}, {"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
{"previewGridOffsetY=", SETTINGS_TYPE_INT, offsetof(Settings, previewGridOffsetY)}, {"previewAxisColor", TYPE_VEC4, offsetof(Settings, previewAxisColor)},
{"previewGridColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewGridColorR)}, {"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
{"previewGridColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewGridColorG)}, {"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
{"previewGridColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewGridColorB)}, {"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
{"previewGridColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewGridColorA)}, {"editorIsBorder", TYPE_BOOL, offsetof(Settings, editorIsBorder)},
{"previewAxisColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewAxisColorR)}, {"editorZoom", TYPE_FLOAT, offsetof(Settings, editorZoom)},
{"previewAxisColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewAxisColorG)}, {"editorPan", TYPE_VEC2, offsetof(Settings, editorPan)},
{"previewAxisColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewAxisColorB)}, {"editorGridSize", TYPE_IVEC2, offsetof(Settings, editorGridSize)},
{"previewAxisColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewAxisColorA)}, {"editorGridOffset", TYPE_IVEC2, offsetof(Settings, editorGridOffset)},
{"previewBackgroundColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorR)}, {"editorGridColor", TYPE_VEC4, offsetof(Settings, editorGridColor)},
{"previewBackgroundColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorG)}, {"editorBackgroundColor", TYPE_VEC4, offsetof(Settings, editorBackgroundColor)},
{"previewBackgroundColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorB)}, {"tool", TYPE_INT, offsetof(Settings, tool)},
{"previewBackgroundColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorA)}, {"toolColor", TYPE_VEC4, offsetof(Settings, toolColor)},
{"editorIsGrid=", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsGrid)},
{"editorIsGridSnap=", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
{"editorIsBorder=", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsBorder)},
{"editorPanX=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorPanX)},
{"editorPanY=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorPanY)},
{"editorZoom=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorZoom)},
{"editorGridSizeX=", SETTINGS_TYPE_INT, offsetof(Settings, editorGridSizeX)},
{"editorGridSizeY=", SETTINGS_TYPE_INT, offsetof(Settings, editorGridSizeY)},
{"editorGridOffsetX=", SETTINGS_TYPE_INT, offsetof(Settings, editorGridOffsetX)},
{"editorGridOffsetY=", SETTINGS_TYPE_INT, offsetof(Settings, editorGridOffsetY)},
{"editorGridColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorGridColorR)},
{"editorGridColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorGridColorG)},
{"editorGridColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorGridColorB)},
{"editorGridColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorGridColorA)},
{"editorBackgroundColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorR)},
{"editorBackgroundColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorG)},
{"editorBackgroundColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorB)},
{"editorBackgroundColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorBackgroundColorA)},
{"toolColorR=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorR)},
{"toolColorG=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorG)},
{"toolColorB=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorB)},
{"toolColorA=", SETTINGS_TYPE_FLOAT, offsetof(Settings, toolColorA)}
}; };
constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES); constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);

View File

@@ -2,20 +2,18 @@
static bool _shader_compile(GLuint* self, const std::string& text) static bool _shader_compile(GLuint* self, const std::string& text)
{ {
std::string compileLog;
s32 isCompile; s32 isCompile;
const GLchar* source = text.c_str(); const GLchar* source = text.c_str();
glShaderSource(*self, 1, &source, nullptr); glShaderSource(*self, 1, &source, nullptr);
glCompileShader(*self); glCompileShader(*self);
glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile); glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile);
if (!isCompile) if (!isCompile)
{ {
glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, nullptr, &compileLog[0]); std::string compileLog(SHADER_INFO_LOG_MAX, '\0');
log_error(std::format(SHADER_INIT_ERROR, *self, compileLog)); glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, nullptr, compileLog.data());
log_error(std::format(SHADER_INIT_ERROR, *self, compileLog.c_str()));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ static void _snapshot_set(Snapshots* self, const Snapshot& snapshot)
*self->anm2 = snapshot.anm2; *self->anm2 = snapshot.anm2;
*self->reference = snapshot.reference; *self->reference = snapshot.reference;
self->preview->time = snapshot.time; self->preview->time = snapshot.time;
self->action = snapshot.action;
} }
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview) void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview)
@@ -44,7 +45,7 @@ void snapshots_undo(Snapshots* self)
Snapshot snapshot; Snapshot snapshot;
if (_snapshot_stack_pop(&self->undoStack, &snapshot)) if (_snapshot_stack_pop(&self->undoStack, &snapshot))
{ {
Snapshot current = {*self->anm2, *self->reference, self->preview->time}; Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
_snapshot_stack_push(&self->redoStack, &current); _snapshot_stack_push(&self->redoStack, &current);
_snapshot_set(self, snapshot); _snapshot_set(self, snapshot);
} }
@@ -55,7 +56,7 @@ void snapshots_redo(Snapshots* self)
Snapshot snapshot; Snapshot snapshot;
if (_snapshot_stack_pop(&self->redoStack, &snapshot)) if (_snapshot_stack_pop(&self->redoStack, &snapshot))
{ {
Snapshot current = {*self->anm2, *self->reference, self->preview->time}; Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
_snapshot_stack_push(&self->undoStack, &current); _snapshot_stack_push(&self->undoStack, &current);
_snapshot_set(self, snapshot); _snapshot_set(self, snapshot);
} }

View File

@@ -3,13 +3,15 @@
#include "anm2.h" #include "anm2.h"
#include "preview.h" #include "preview.h"
#define SNAPSHOT_STACK_MAX 100 #define SNAPSHOT_STACK_MAX 1000
#define SNAPSHOT_ACTION "Action"
struct Snapshot struct Snapshot
{ {
Anm2 anm2; Anm2 anm2;
Anm2Reference reference; Anm2Reference reference;
f32 time = 0.0f; f32 time = 0.0f;
std::string action = SNAPSHOT_ACTION;
}; };
struct SnapshotStack struct SnapshotStack
@@ -23,6 +25,7 @@ struct Snapshots
Anm2* anm2 = nullptr; Anm2* anm2 = nullptr;
Preview* preview = nullptr; Preview* preview = nullptr;
Anm2Reference* reference = nullptr; Anm2Reference* reference = nullptr;
std::string action = SNAPSHOT_ACTION;
SnapshotStack undoStack; SnapshotStack undoStack;
SnapshotStack redoStack; SnapshotStack redoStack;
}; };

View File

@@ -2,14 +2,18 @@
static void _tick(State* self) static void _tick(State* self)
{ {
SDL_GetWindowSize(self->window, &self->settings.windowW, &self->settings.windowH);
editor_tick(&self->editor);
preview_tick(&self->preview); preview_tick(&self->preview);
dialog_tick(&self->dialog); }
imgui_tick(&self->imgui);
if (self->imgui.isQuit) self->isRunning = false; static void _update(State* self)
{
SDL_GetWindowSize(self->window, &self->settings.windowSize.x, &self->settings.windowSize.y);
imgui_update(&self->imgui);
dialog_update(&self->dialog);
if (self->imgui.isQuit)
self->isRunning = false;
} }
static void _draw(State* self) static void _draw(State* self)
@@ -35,11 +39,37 @@ void init(State* self)
log_info(STATE_SDL_INIT_INFO); log_info(STATE_SDL_INIT_INFO);
// Todo, when sdl3 mixer is released officially
/*
if ((Mix_Init(STATE_MIX_FLAGS) & mixFlags) != mixFlags)
log_warning(std::format(STATE_MIX_INIT_WARNING, Mix_GetError()));
if
(
Mix_OpenAudioDevice
(
STATE_MIX_SAMPLE_RATE,
STATE_MIX_FORMAT,
STATE_MIX_CHANNELS,
STATE_CHUNK_SIZE,
STATE_MIX_DEVICE,
STATE_MIX_ALLOWED_CHANGES
)
< 0
)
{
log_warning(std::format(STATE_MIX_INIT_WARNING, Mix_GetError()));
Mix_Quit();
}
else
log_info(STATE_MIX_INIT_INFO);
*/
SDL_CreateWindowAndRenderer SDL_CreateWindowAndRenderer
( (
WINDOW_TITLE, WINDOW_TITLE,
self->settings.windowW, self->settings.windowSize.x,
self->settings.windowH, self->settings.windowSize.y,
WINDOW_FLAGS, WINDOW_FLAGS,
&self->window, &self->window,
&self->renderer &self->renderer
@@ -63,9 +93,11 @@ void init(State* self)
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glLineWidth(STATE_GL_LINE_WIDTH); glLineWidth(STATE_GL_LINE_WIDTH);
glDisable(GL_MULTISAMPLE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LINE_SMOOTH);
resources_init(&self->resources); resources_init(&self->resources);
clipboard_init(&self->clipboard, &self->anm2); clipboard_init(&self->clipboard, &self->anm2);
dialog_init(&self->dialog, &self->anm2, &self->reference, &self->resources, self->window); dialog_init(&self->dialog, &self->anm2, &self->reference, &self->resources, self->window);
@@ -98,6 +130,7 @@ void init(State* self)
anm2_new(&self->anm2); anm2_new(&self->anm2);
} }
void loop(State* self) void loop(State* self)
{ {
self->tick = SDL_GetTicks(); self->tick = SDL_GetTicks();
@@ -112,22 +145,27 @@ void loop(State* self)
_tick(self); _tick(self);
self->lastTick = self->tick; self->lastTick = self->tick;
} }
_update(self);
_draw(self); _draw(self);
} }
void quit(State* self) void quit(State* self)
{ {
imgui_free(); imgui_free();
settings_save(&self->settings);
preview_free(&self->preview); preview_free(&self->preview);
editor_free(&self->editor); editor_free(&self->editor);
resources_free(&self->resources); resources_free(&self->resources);
/*
Mix_CloseAudio();
Mix_Quit();
*/
SDL_GL_DestroyContext(self->glContext); SDL_GL_DestroyContext(self->glContext);
SDL_Quit(); SDL_Quit();
settings_save(&self->settings);
log_info(STATE_QUIT_INFO); log_info(STATE_QUIT_INFO);
} }

View File

@@ -5,11 +5,22 @@
#define STATE_INIT_INFO "Initializing..." #define STATE_INIT_INFO "Initializing..."
#define STATE_SDL_INIT_ERROR "Failed to initialize SDL! {}" #define STATE_SDL_INIT_ERROR "Failed to initialize SDL! {}"
#define STATE_SDL_INIT_INFO "Initialized SDL" #define STATE_SDL_INIT_INFO "Initialized SDL"
#define STATE_MIX_INIT_WARNING "Unable to initialize SDL_mixer! {}"
#define STATE_MIX_AUDIO_DEVICE_INIT_WARNING "Unable to initialize audio device! {}"
#define STATE_MIX_INIT_INFO "Initialized SDL_mixer"
#define STATE_GL_CONTEXT_INIT_ERROR "Failed to initialize OpenGL context! {}" #define STATE_GL_CONTEXT_INIT_ERROR "Failed to initialize OpenGL context! {}"
#define STATE_GL_CONTEXT_INIT_INFO "Initialized OpenGL context (OpenGL {})" #define STATE_GL_CONTEXT_INIT_INFO "Initialized OpenGL context (OpenGL {})"
#define STATE_QUIT_INFO "Exiting..." #define STATE_QUIT_INFO "Exiting..."
#define STATE_GL_LINE_WIDTH 2.0f #define STATE_GL_LINE_WIDTH 2.0f
#define STATE_MIX_FLAGS (MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_WAV)
#define STATE_MIX_SAMPLE_RATE 44100
#define STATE_MIX_FORMAT MIX_DEFAULT_FORMAT
#define STATE_MIX_CHANNELS 2
#define STATE_MIX_CHUNK_SIZE 1024
#define STATE_MIX_DEVICE NULL
#define STATE_MIX_ALLOWED_CHANGES SDL_AUDIO_ALLOW_FORMAT_CHANGE
struct State struct State
{ {
SDL_Window* window; SDL_Window* window;
@@ -25,11 +36,13 @@ struct State
Settings settings; Settings settings;
Snapshots snapshots; Snapshots snapshots;
Clipboard clipboard; Clipboard clipboard;
bool isRunning = true;
std::string argument{}; std::string argument{};
u64 lastTick = 0; std::string lastAction{};
u64 tick = 0; u64 lastTick{};
u64 tick{};
bool isRunning = true;
bool is_last_action() const { return !lastAction.empty(); }
bool is_argument() const { return !argument.empty(); } bool is_argument() const { return !argument.empty(); }
}; };

View File

@@ -8,6 +8,18 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h> #include <stb_image_write.h>
static std::vector<u8> _texture_download(Texture* self)
{
std::vector<u8> pixels(self->size.x * self->size.y * TEXTURE_CHANNELS);
glBindTexture(GL_TEXTURE_2D, self->id);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
return pixels;
}
void texture_gl_set(Texture* self, void* data) void texture_gl_set(Texture* self, void* data)
{ {
glGenTextures(1, &self->id); glGenTextures(1, &self->id);
@@ -48,13 +60,37 @@ bool texture_from_data_init(Texture* self, const u8* data, u32 length)
return true; return true;
} }
bool texture_from_data_write(const std::string& path, const u8* data, s32 width, s32 height) bool texture_from_data_write(const std::string& path, const u8* data, ivec2 size)
{ {
return (bool)stbi_write_png(path.c_str(), width, height, TEXTURE_CHANNELS, data, width * TEXTURE_CHANNELS); log_info(std::format(TEXTURE_SAVE_INFO, path));
return (bool)stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
}
bool texture_from_gl_write(Texture* self, const std::string& path)
{
return texture_from_data_write(path, _texture_download(self).data(), self->size);
} }
void texture_free(Texture* self) void texture_free(Texture* self)
{ {
glDeleteTextures(1, &self->id); glDeleteTextures(1, &self->id);
*self = Texture{}; *self = Texture{};
} }
bool texture_pixel_set(Texture* self, ivec2 position, vec4 color)
{
if
(
position.x < 0 || position.y < 0 ||
position.x >= self->size.x || position.y >= self->size.y
)
return false;
uint8_t rgba8[4] = {FLOAT_TO_U8(color.r), FLOAT_TO_U8(color.g), FLOAT_TO_U8(color.b), FLOAT_TO_U8(color.a)};
glBindTexture(GL_TEXTURE_2D, self->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0,position.x, position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba8);
return true;
}

View File

@@ -5,6 +5,7 @@
#define TEXTURE_CHANNELS 4 #define TEXTURE_CHANNELS 4
#define TEXTURE_INIT_INFO "Initialized texture from file: {}" #define TEXTURE_INIT_INFO "Initialized texture from file: {}"
#define TEXTURE_INIT_ERROR "Failed to initialize texture from file: {}" #define TEXTURE_INIT_ERROR "Failed to initialize texture from file: {}"
#define TEXTURE_SAVE_INFO "Saved texture to: {}"
struct Texture struct Texture
{ {
@@ -18,4 +19,7 @@ void texture_gl_set(Texture* self, void* data);
bool texture_from_path_init(Texture* self, const std::string& path); bool texture_from_path_init(Texture* self, const std::string& path);
bool texture_from_data_init(Texture* self, const u8* data, u32 length); bool texture_from_data_init(Texture* self, const u8* data, u32 length);
void texture_free(Texture* self); void texture_free(Texture* self);
bool texture_from_data_write(const std::string& path, const u8* data, s32 width, s32 height); std::vector<u8> texture_download(Texture* self);
bool texture_from_data_write(const std::string& path, const u8* data, ivec2 size);
bool texture_pixel_set(Texture* self, ivec2 position, vec4 color);
bool texture_from_gl_write(Texture* self, const std::string& path);

52
src/tool.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include "COMMON.h"
#define TOOL_STEP 1
#define TOOL_STEP_MOD 10
enum ToolType
{
TOOL_PAN,
TOOL_MOVE,
TOOL_ROTATE,
TOOL_SCALE,
TOOL_CROP,
TOOL_DRAW,
TOOL_ERASE,
TOOL_COLOR_PICKER,
TOOL_UNDO,
TOOL_REDO,
TOOL_COLOR,
TOOL_COUNT,
};
const SDL_SystemCursor MOUSE_CURSOR_DEFAULT = SDL_SYSTEM_CURSOR_DEFAULT;
const SDL_SystemCursor TOOL_MOUSE_CURSORS[TOOL_COUNT] =
{
SDL_SYSTEM_CURSOR_POINTER,
SDL_SYSTEM_CURSOR_MOVE,
SDL_SYSTEM_CURSOR_CROSSHAIR,
SDL_SYSTEM_CURSOR_NE_RESIZE,
SDL_SYSTEM_CURSOR_CROSSHAIR,
SDL_SYSTEM_CURSOR_CROSSHAIR,
SDL_SYSTEM_CURSOR_CROSSHAIR,
SDL_SYSTEM_CURSOR_CROSSHAIR,
SDL_SYSTEM_CURSOR_DEFAULT,
SDL_SYSTEM_CURSOR_DEFAULT
};
const bool TOOL_MOUSE_CURSOR_IS_CONSTANT[TOOL_COUNT] =
{
false,
false,
false,
false,
false,
false,
false,
true,
false,
false,
false
};

View File

@@ -4,8 +4,7 @@
#define WINDOW_TITLE "Anm2Ed" #define WINDOW_TITLE "Anm2Ed"
#define WINDOW_TITLE_FORMAT "Anm2Ed ({})" #define WINDOW_TITLE_FORMAT "Anm2Ed ({})"
#define WINDOW_WIDTH 1920
#define WINDOW_HEIGHT 1080
#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL #define WINDOW_FLAGS SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL
void window_title_from_path_set(SDL_Window* self, const std::string& path); void window_title_from_path_set(SDL_Window* self, const std::string& path);
bool window_color_from_position_get(SDL_Window* self, vec2 position, vec4* color);