refactor pt 1; getting each thing right and then will push to main
This commit is contained in:
@@ -41,7 +41,7 @@ add_executable(${PROJECT_NAME}
|
||||
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
target_sources(${PROJECT_NAME} PRIVATE assets/Icon.rc)
|
||||
target_sources(${PROJECT_NAME} PRIVATE Icon.rc)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
endif()
|
||||
|
||||
@@ -72,4 +72,4 @@ target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL SDL3::SDL3)
|
||||
|
||||
message("System: ${CMAKE_SYSTEM_NAME}")
|
||||
message("Project: ${PROJECT_NAME}")
|
||||
message("Build: ${CMAKE_BUILD_TYPE}")
|
||||
message("Build: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
1
compile_commands.json
Symbolic link
1
compile_commands.json
Symbolic link
@@ -0,0 +1 @@
|
||||
build/compile_commands.json
|
||||
528
src/COMMON.h
528
src/COMMON.h
@@ -1,55 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#define GLAD_GL_IMPLEMENTATION
|
||||
#include <SDL3/SDL.h>
|
||||
#include <glad/glad.h>
|
||||
#include <GL/gl.h>
|
||||
#include <glm/glm/glm.hpp>
|
||||
#include <glm/glm/gtc/type_ptr.hpp>
|
||||
#include <glm/glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/glm/gtc/type_ptr.hpp>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <ranges>
|
||||
#include <map>
|
||||
#include <print>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define PI (GLM_PI)
|
||||
#define TAU (PI * 2)
|
||||
|
||||
using namespace glm;
|
||||
using namespace glm;
|
||||
|
||||
#define PREFERENCES_DIRECTORY "anm2ed"
|
||||
|
||||
#define ROUND_NEAREST_MULTIPLE(value, multiple) (roundf((value) / (multiple)) * (multiple))
|
||||
#define FLOAT_TO_U8(x) (static_cast<u8>((x) * 255.0f))
|
||||
#define U8_TO_FLOAT(x) ((x) / 255.0f)
|
||||
#define FLOAT_TO_UINT8(x) (static_cast<uint8_t>((x) * 255.0f))
|
||||
#define UINT8_TO_FLOAT(x) ((x) / 255.0f)
|
||||
#define PERCENT_TO_UNIT(x) (x / 100.0f)
|
||||
#define UNIT_TO_PERCENT(x) (x * 100.0f)
|
||||
#define SECOND 1000.0f
|
||||
@@ -62,15 +46,15 @@ using namespace glm;
|
||||
#define GL_ID_NONE 0
|
||||
|
||||
#ifdef _WIN32
|
||||
#define POPEN _popen
|
||||
#define PCLOSE _pclose
|
||||
#define PWRITE_MODE "wb"
|
||||
#define PREAD_MODE "r"
|
||||
#define POPEN _popen
|
||||
#define PCLOSE _pclose
|
||||
#define PWRITE_MODE "wb"
|
||||
#define PREAD_MODE "r"
|
||||
#else
|
||||
#define POPEN popen
|
||||
#define PCLOSE pclose
|
||||
#define PWRITE_MODE "w"
|
||||
#define PREAD_MODE "r"
|
||||
#define POPEN popen
|
||||
#define PCLOSE pclose
|
||||
#define PWRITE_MODE "w"
|
||||
#define PREAD_MODE "r"
|
||||
#endif
|
||||
|
||||
static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
|
||||
@@ -82,299 +66,254 @@ static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.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 inline std::string preferences_path_get(void)
|
||||
{
|
||||
char* preferencesPath = SDL_GetPrefPath("", PREFERENCES_DIRECTORY);
|
||||
std::string preferencesPathString = preferencesPath;
|
||||
SDL_free(preferencesPath);
|
||||
return preferencesPathString;
|
||||
static inline std::string preferences_path_get(void) {
|
||||
char* preferencesPath = SDL_GetPrefPath("", PREFERENCES_DIRECTORY);
|
||||
std::string preferencesPathString = preferencesPath;
|
||||
SDL_free(preferencesPath);
|
||||
return preferencesPathString;
|
||||
}
|
||||
|
||||
static inline bool string_to_bool(const std::string& string)
|
||||
{
|
||||
if (string == "1") return true;
|
||||
static inline bool string_to_bool(const std::string& string) {
|
||||
if (string == "1")
|
||||
return true;
|
||||
|
||||
std::string lower = string;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
return lower == "true";
|
||||
std::string lower = string;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
return lower == "true";
|
||||
}
|
||||
|
||||
static inline std::string string_quote(const std::string& string)
|
||||
{
|
||||
return "\"" + string + "\"";
|
||||
}
|
||||
static inline std::string string_quote(const std::string& string) { return "\"" + string + "\""; }
|
||||
|
||||
static inline std::string string_to_lowercase(std::string string) {
|
||||
std::transform
|
||||
(
|
||||
string.begin(), string.end(), string.begin(),
|
||||
[](u8 character){ return std::tolower(character); }
|
||||
);
|
||||
return string;
|
||||
std::transform(string.begin(), string.end(), string.begin(), [](char character) { return std::tolower(character); });
|
||||
return string;
|
||||
}
|
||||
|
||||
static inline std::string string_backslash_replace(std::string string)
|
||||
{
|
||||
for (char& character : string)
|
||||
if (character == '\\')
|
||||
character = '/';
|
||||
return string;
|
||||
static inline std::string string_backslash_replace(std::string string) {
|
||||
for (char& character : string)
|
||||
if (character == '\\')
|
||||
character = '/';
|
||||
return string;
|
||||
}
|
||||
|
||||
#define FLOAT_FORMAT_MAX_DECIMALS 5
|
||||
#define FLOAT_FORMAT_EPSILON 1e-5f
|
||||
static constexpr f32 FLOAT_FORMAT_POW10[] = {
|
||||
1.f,
|
||||
10.f,
|
||||
100.f,
|
||||
1000.f,
|
||||
10000.f,
|
||||
100000.f
|
||||
static constexpr float FLOAT_FORMAT_POW10[] = {1.f, 10.f, 100.f, 1000.f, 10000.f, 100000.f};
|
||||
|
||||
static inline int float_decimals_needed(float value) {
|
||||
for (int decimalCount = 0; decimalCount <= FLOAT_FORMAT_MAX_DECIMALS; ++decimalCount) {
|
||||
float scale = FLOAT_FORMAT_POW10[decimalCount];
|
||||
float rounded = roundf(value * scale) / scale;
|
||||
if (fabsf(value - rounded) < FLOAT_FORMAT_EPSILON)
|
||||
return decimalCount;
|
||||
}
|
||||
return FLOAT_FORMAT_MAX_DECIMALS;
|
||||
}
|
||||
|
||||
static inline const char* float_format_get(float value) {
|
||||
static std::string formatString;
|
||||
const int decimalCount = float_decimals_needed(value);
|
||||
formatString = (decimalCount == 0) ? "%.0f" : ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
}
|
||||
|
||||
static inline const char* vec2_format_get(const vec2& value) {
|
||||
static std::string formatString;
|
||||
const int decimalCountX = float_decimals_needed(value.x);
|
||||
const int decimalCountY = float_decimals_needed(value.y);
|
||||
const int decimalCount = (decimalCountX > decimalCountY) ? decimalCountX : decimalCountY;
|
||||
formatString = (decimalCount == 0) ? "%.0f" : ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
}
|
||||
|
||||
static inline std::string working_directory_from_file_set(const std::string& path) {
|
||||
std::filesystem::path filePath = path;
|
||||
std::filesystem::path parentPath = filePath.parent_path();
|
||||
std::filesystem::current_path(parentPath);
|
||||
return parentPath.string();
|
||||
};
|
||||
|
||||
static inline s32 f32_decimals_needed(f32 value)
|
||||
{
|
||||
for (s32 decimalCount = 0; decimalCount <= FLOAT_FORMAT_MAX_DECIMALS; ++decimalCount)
|
||||
{
|
||||
f32 scale = FLOAT_FORMAT_POW10[decimalCount];
|
||||
f32 rounded = roundf(value * scale) / scale;
|
||||
if (fabsf(value - rounded) < FLOAT_FORMAT_EPSILON)
|
||||
return decimalCount;
|
||||
}
|
||||
return FLOAT_FORMAT_MAX_DECIMALS;
|
||||
static inline std::string path_extension_change(const std::string& path, const std::string& extension) {
|
||||
std::filesystem::path filePath(path);
|
||||
filePath.replace_extension(extension);
|
||||
return filePath.string();
|
||||
}
|
||||
|
||||
static inline const char* f32_format_get(f32 value)
|
||||
{
|
||||
static std::string formatString;
|
||||
const s32 decimalCount = f32_decimals_needed(value);
|
||||
formatString = (decimalCount == 0)
|
||||
? "%.0f"
|
||||
: ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
static inline bool path_is_extension(const std::string& path, const std::string& extension) {
|
||||
auto e = std::filesystem::path(path).extension().string();
|
||||
std::transform(e.begin(), e.end(), e.begin(), ::tolower);
|
||||
return e == ("." + extension);
|
||||
}
|
||||
|
||||
static inline const char* vec2_format_get(const vec2& value)
|
||||
{
|
||||
static std::string formatString;
|
||||
const s32 decimalCountX = f32_decimals_needed(value.x);
|
||||
const s32 decimalCountY = f32_decimals_needed(value.y);
|
||||
const s32 decimalCount = (decimalCountX > decimalCountY) ? decimalCountX : decimalCountY;
|
||||
formatString = (decimalCount == 0)
|
||||
? "%.0f"
|
||||
: ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
static inline bool path_exists(const std::filesystem::path& pathCheck) {
|
||||
std::error_code errorCode;
|
||||
return std::filesystem::exists(pathCheck, errorCode) && ((void)std::filesystem::status(pathCheck, errorCode), !errorCode);
|
||||
}
|
||||
|
||||
static inline std::string working_directory_from_file_set(const std::string& path)
|
||||
{
|
||||
std::filesystem::path filePath = path;
|
||||
std::filesystem::path parentPath = filePath.parent_path();
|
||||
std::filesystem::current_path(parentPath);
|
||||
return parentPath.string();
|
||||
static inline bool path_is_valid(const std::filesystem::path& pathCheck) {
|
||||
namespace fs = std::filesystem;
|
||||
std::error_code ec;
|
||||
|
||||
if (fs::is_directory(pathCheck, ec))
|
||||
return false;
|
||||
|
||||
fs::path parentDir = pathCheck.has_parent_path() ? pathCheck.parent_path() : fs::path(".");
|
||||
if (!fs::is_directory(parentDir, ec))
|
||||
return false;
|
||||
|
||||
bool existedBefore = fs::exists(pathCheck, ec);
|
||||
std::ofstream testStream(pathCheck, std::ios::app | std::ios::binary);
|
||||
bool isValid = testStream.is_open();
|
||||
testStream.close();
|
||||
|
||||
if (!existedBefore && isValid)
|
||||
fs::remove(pathCheck, ec);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static inline int string_to_enum(const std::string& string, const char* const* array, int n) {
|
||||
for (int i = 0; i < n; i++)
|
||||
if (string == array[i])
|
||||
return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
static inline std::string path_extension_change(const std::string& path, const std::string& extension)
|
||||
{
|
||||
std::filesystem::path filePath(path);
|
||||
filePath.replace_extension(extension);
|
||||
return filePath.string();
|
||||
template <typename T> T& dummy_value() {
|
||||
static T value{};
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline bool path_is_extension(const std::string& path, const std::string& extension)
|
||||
{
|
||||
auto e = std::filesystem::path(path).extension().string();
|
||||
std::transform(e.begin(), e.end(), e.begin(), ::tolower);
|
||||
return e == ("." + extension);
|
||||
template <typename T> static inline int map_next_id_get(const std::map<int, T>& map) {
|
||||
int id = 0;
|
||||
|
||||
for (const auto& [key, value] : map) {
|
||||
if (key != id)
|
||||
break;
|
||||
++id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static inline bool path_exists(const std::filesystem::path& pathCheck)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
return std::filesystem::exists(pathCheck, errorCode) && ((void)std::filesystem::status(pathCheck, errorCode), !errorCode);
|
||||
template <typename Map> static inline auto map_find(Map& map, typename Map::key_type id) -> typename Map::mapped_type* {
|
||||
if (auto it = map.find(id); it != map.end())
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool path_is_valid(const std::filesystem::path& pathCheck)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
std::error_code ec;
|
||||
template <typename Map, typename Key> static inline void map_swap(Map& map, const Key& key1, const Key& key2) {
|
||||
if (key1 == key2)
|
||||
return;
|
||||
|
||||
if (fs::is_directory(pathCheck, ec)) return false;
|
||||
auto it1 = map.find(key1);
|
||||
auto it2 = map.find(key2);
|
||||
|
||||
fs::path parentDir = pathCheck.has_parent_path() ? pathCheck.parent_path() : fs::path(".");
|
||||
if (!fs::is_directory(parentDir, ec)) return false;
|
||||
|
||||
bool existedBefore = fs::exists(pathCheck, ec);
|
||||
std::ofstream testStream(pathCheck, std::ios::app | std::ios::binary);
|
||||
bool isValid = testStream.is_open();
|
||||
testStream.close();
|
||||
|
||||
if (!existedBefore && isValid)
|
||||
fs::remove(pathCheck, ec);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static inline s32 string_to_enum(const std::string& string, const char* const* array, s32 n)
|
||||
{
|
||||
for (s32 i = 0; i < n; i++)
|
||||
if (string == array[i])
|
||||
return i;
|
||||
return -1;
|
||||
if (it1 != map.end() && it2 != map.end()) {
|
||||
using std::swap;
|
||||
swap(it1->second, it2->second);
|
||||
} else if (it1 != map.end()) {
|
||||
map[key2] = std::move(it1->second);
|
||||
map.erase(it1);
|
||||
} else if (it2 != map.end()) {
|
||||
map[key1] = std::move(it2->second);
|
||||
map.erase(it2);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T& dummy_value()
|
||||
{
|
||||
static T value{};
|
||||
return value;
|
||||
template <typename T> static inline void map_insert_shift(std::map<int, T>& map, int index, const T& value) {
|
||||
const int insertIndex = index + 1;
|
||||
|
||||
std::vector<std::pair<int, T>> toShift;
|
||||
for (auto it = map.rbegin(); it != map.rend(); ++it) {
|
||||
if (it->first < insertIndex)
|
||||
break;
|
||||
toShift.emplace_back(it->first + 1, std::move(it->second));
|
||||
}
|
||||
|
||||
for (const auto& [newKey, _] : toShift)
|
||||
map.erase(newKey - 1);
|
||||
|
||||
for (auto& [newKey, val] : toShift)
|
||||
map[newKey] = std::move(val);
|
||||
|
||||
map[insertIndex] = value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline s32 map_next_id_get(const std::map<s32, T>& map)
|
||||
{
|
||||
s32 id = 0;
|
||||
|
||||
for (const auto& [key, value] : map)
|
||||
{
|
||||
if (key != id)
|
||||
break;
|
||||
++id;
|
||||
}
|
||||
|
||||
return id;
|
||||
template <typename T> static inline T* vector_find(std::vector<T>& v, const T& value) {
|
||||
auto it = std::find(v.begin(), v.end(), value);
|
||||
return (it != v.end()) ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
static inline auto map_find(Map& map, typename Map::key_type id)
|
||||
-> typename Map::mapped_type*
|
||||
{
|
||||
if (auto it = map.find(id); it != map.end())
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
template <typename T> static inline void vector_value_erase(std::vector<T>& v, const T& value) { v.erase(std::remove(v.begin(), v.end(), value), v.end()); }
|
||||
|
||||
template <typename T> static inline void vector_value_swap(std::vector<T>& v, const T& a, const T& b) {
|
||||
for (auto& element : v) {
|
||||
if (element == a)
|
||||
element = b;
|
||||
else if (element == b)
|
||||
element = a;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
static inline void map_swap(Map& map, const Key& key1, const Key& key2)
|
||||
{
|
||||
if (key1 == key2)
|
||||
return;
|
||||
|
||||
auto it1 = map.find(key1);
|
||||
auto it2 = map.find(key2);
|
||||
|
||||
if (it1 != map.end() && it2 != map.end())
|
||||
{
|
||||
using std::swap;
|
||||
swap(it1->second, it2->second);
|
||||
}
|
||||
else if (it1 != map.end())
|
||||
{
|
||||
map[key2] = std::move(it1->second);
|
||||
map.erase(it1);
|
||||
}
|
||||
else if (it2 != map.end())
|
||||
{
|
||||
map[key1] = std::move(it2->second);
|
||||
map.erase(it2);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& value)
|
||||
{
|
||||
const s32 insertIndex = index + 1;
|
||||
|
||||
std::vector<std::pair<int, T>> toShift;
|
||||
for (auto it = map.rbegin(); it != map.rend(); ++it)
|
||||
{
|
||||
if (it->first < insertIndex)
|
||||
break;
|
||||
toShift.emplace_back(it->first + 1, std::move(it->second));
|
||||
}
|
||||
|
||||
for (const auto& [newKey, _] : toShift)
|
||||
map.erase(newKey - 1);
|
||||
|
||||
for (auto& [newKey, val] : toShift)
|
||||
map[newKey] = std::move(val);
|
||||
|
||||
map[insertIndex] = value;
|
||||
static inline void unordered_set_id_toggle(std::unordered_set<int>& set, int id) {
|
||||
if (auto it = set.find(id); it != set.end())
|
||||
set.erase(it);
|
||||
else
|
||||
set.insert(id);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vector_value_erase(std::vector<T>& v, const T& value)
|
||||
{
|
||||
v.erase(std::remove(v.begin(), v.end(), value), v.end());
|
||||
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), float rotation = {}) {
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vector_value_swap(std::vector<T>& v, const T& a, const T& b)
|
||||
{
|
||||
for (auto& element : v)
|
||||
{
|
||||
if (element == a) element = b;
|
||||
else if (element == b) element = a;
|
||||
}
|
||||
static inline mat4 quad_model_parent_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), float rotation = {}) {
|
||||
vec2 scaleSign = glm::sign(scale);
|
||||
vec2 scaleAbsolute = glm::abs(scale);
|
||||
float 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));
|
||||
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;
|
||||
}
|
||||
|
||||
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
|
||||
{
|
||||
vec2 scaleAbsolute = glm::abs(scale);
|
||||
vec2 scaleSign = glm::sign(scale);
|
||||
vec2 pivotScaled = pivot * scaleAbsolute;
|
||||
vec2 sizeScaled = size * scaleAbsolute;
|
||||
#define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \
|
||||
static inline enumType function(const std::string& string) { return static_cast<enumType>(string_to_enum(string, stringArray, count)); };
|
||||
|
||||
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;
|
||||
}
|
||||
#define DATATYPE_LIST \
|
||||
X(TYPE_INT, int) \
|
||||
X(TYPE_BOOL, bool) \
|
||||
X(TYPE_FLOAT, float) \
|
||||
X(TYPE_STRING, std::string) \
|
||||
X(TYPE_IVEC2, ivec2) \
|
||||
X(TYPE_IVEC2_WH, ivec2) \
|
||||
X(TYPE_VEC2, vec2) \
|
||||
X(TYPE_VEC2_WH, vec2) \
|
||||
X(TYPE_VEC3, vec3) \
|
||||
X(TYPE_VEC4, vec4)
|
||||
|
||||
static inline mat4 quad_model_parent_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
|
||||
{
|
||||
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));
|
||||
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_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \
|
||||
static inline enumType function(const std::string& string) \
|
||||
{ \
|
||||
return static_cast<enumType>(string_to_enum(string, stringArray, count)); \
|
||||
};
|
||||
|
||||
#define DATATYPE_LIST \
|
||||
X(TYPE_INT, s32) \
|
||||
X(TYPE_BOOL, bool) \
|
||||
X(TYPE_FLOAT, f32) \
|
||||
X(TYPE_STRING, std::string) \
|
||||
X(TYPE_IVEC2, ivec2) \
|
||||
X(TYPE_IVEC2_WH, ivec2) \
|
||||
X(TYPE_VEC2, vec2) \
|
||||
X(TYPE_VEC2_WH, vec2) \
|
||||
X(TYPE_VEC3, vec3) \
|
||||
X(TYPE_VEC4, vec4)
|
||||
|
||||
enum DataType
|
||||
{
|
||||
#define X(symbol, ctype) symbol,
|
||||
DATATYPE_LIST
|
||||
#undef X
|
||||
enum DataType {
|
||||
#define X(symbol, ctype) symbol,
|
||||
DATATYPE_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
#define DATATYPE_TO_CTYPE(dt) DATATYPE_CTYPE_##dt
|
||||
@@ -382,8 +321,13 @@ enum DataType
|
||||
DATATYPE_LIST
|
||||
#undef X
|
||||
|
||||
enum OriginType
|
||||
{
|
||||
ORIGIN_TOP_LEFT,
|
||||
ORIGIN_CENTER
|
||||
enum OriginType { ORIGIN_TOP_LEFT, ORIGIN_CENTER };
|
||||
|
||||
struct WorkingDirectory {
|
||||
std::filesystem::path previous;
|
||||
WorkingDirectory(const std::string& file) {
|
||||
previous = std::filesystem::current_path();
|
||||
working_directory_from_file_set(file);
|
||||
}
|
||||
~WorkingDirectory() { std::filesystem::current_path(previous); }
|
||||
};
|
||||
347
src/PACKED.h
347
src/PACKED.h
@@ -1,347 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
|
||||
const u8 TEXTURE_ATLAS[] =
|
||||
{
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xa0,
|
||||
0x04, 0x03, 0x00, 0x00, 0x00, 0x01, 0x5e, 0x74, 0xbf, 0x00, 0x00, 0x00,
|
||||
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b,
|
||||
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x4c,
|
||||
0x54, 0x45, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0xff, 0xff, 0xff, 0x60,
|
||||
0x60, 0x60, 0xff, 0xff, 0xff, 0x3e, 0xd5, 0x47, 0x6d, 0x00, 0x00, 0x00,
|
||||
0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde,
|
||||
0x00, 0x00, 0x04, 0x62, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x59,
|
||||
0x5b, 0x6e, 0x24, 0x37, 0x0c, 0x2c, 0x80, 0xbc, 0x40, 0xee, 0x90, 0x03,
|
||||
0x10, 0x20, 0x0f, 0xc0, 0x80, 0x75, 0xff, 0x33, 0xe5, 0x83, 0xa2, 0x5a,
|
||||
0x63, 0xcf, 0xae, 0xa7, 0x37, 0xb1, 0x03, 0x6c, 0xac, 0x0f, 0x77, 0x6b,
|
||||
0x5a, 0xd5, 0x7c, 0x15, 0x49, 0xb5, 0x0c, 0xfc, 0xc2, 0x20, 0x9f, 0xff,
|
||||
0xae, 0xd9, 0xd7, 0x28, 0x00, 0xe0, 0xb1, 0x9e, 0x04, 0x40, 0x9a, 0x92,
|
||||
0x76, 0xac, 0x27, 0xf3, 0x78, 0x21, 0xbd, 0x00, 0x01, 0xa0, 0x04, 0x98,
|
||||
0xd0, 0x46, 0x26, 0x00, 0x04, 0x49, 0xe8, 0x9e, 0x36, 0xc0, 0x95, 0x80,
|
||||
0x2e, 0x51, 0x64, 0xdf, 0xee, 0x15, 0xa4, 0x92, 0x83, 0x58, 0x2a, 0x29,
|
||||
0x4b, 0xd8, 0x00, 0x9d, 0x17, 0xf6, 0x02, 0xc9, 0x56, 0x68, 0xe6, 0x6d,
|
||||
0x4b, 0xbf, 0xa2, 0x67, 0x4c, 0x4d, 0x35, 0xec, 0xb5, 0x89, 0x41, 0x24,
|
||||
0x9e, 0x01, 0x94, 0xd0, 0xd4, 0xb6, 0x34, 0x35, 0x7b, 0x81, 0x78, 0x90,
|
||||
0xe5, 0xf6, 0x08, 0x28, 0x40, 0x13, 0xcc, 0x13, 0x50, 0x08, 0x03, 0x20,
|
||||
0xee, 0x51, 0xee, 0x06, 0x48, 0xfb, 0x8e, 0x61, 0x64, 0x02, 0x9a, 0x4a,
|
||||
0x1c, 0x00, 0xe1, 0xf2, 0xb8, 0xb8, 0xbb, 0x1b, 0x80, 0x48, 0xad, 0xb6,
|
||||
0x75, 0x59, 0xcf, 0x04, 0xda, 0x4b, 0x00, 0x12, 0x03, 0x30, 0x77, 0x37,
|
||||
0x33, 0x21, 0x01, 0xd2, 0xae, 0xc0, 0x75, 0xd4, 0x2e, 0x40, 0xb0, 0x00,
|
||||
0x40, 0x44, 0xc4, 0x45, 0xa0, 0x4c, 0x40, 0x99, 0x17, 0x60, 0x02, 0x37,
|
||||
0x5e, 0xd2, 0x15, 0x5f, 0x98, 0x99, 0xd9, 0x13, 0xc0, 0xa2, 0x86, 0xb4,
|
||||
0xdf, 0xed, 0x22, 0x8d, 0x88, 0x00, 0x10, 0x5a, 0xff, 0xe1, 0xc7, 0xe4,
|
||||
0x83, 0xe1, 0x8b, 0xc6, 0xd0, 0x5c, 0xde, 0x48, 0x94, 0x07, 0x72, 0x5f,
|
||||
0xf3, 0xa1, 0xf9, 0xdb, 0xe7, 0x93, 0x17, 0xc1, 0xa6, 0x80, 0x93, 0xa4,
|
||||
0x5f, 0x9c, 0x92, 0xe5, 0x13, 0xbc, 0x71, 0x2f, 0x0a, 0x00, 0x02, 0x08,
|
||||
0x02, 0xac, 0xcd, 0x5a, 0x09, 0xba, 0xb3, 0xec, 0x12, 0x45, 0x77, 0x27,
|
||||
0x81, 0x26, 0x28, 0x40, 0xeb, 0x3c, 0x19, 0x40, 0x3f, 0xb7, 0x2d, 0x4a,
|
||||
0xe8, 0x1e, 0xa4, 0x0d, 0x40, 0x3a, 0x4f, 0x6c, 0xab, 0xd4, 0xcf, 0x01,
|
||||
0x68, 0xb9, 0x47, 0x42, 0xcb, 0x9d, 0x15, 0x39, 0x80, 0xc9, 0x93, 0xa1,
|
||||
0xf9, 0x7e, 0x2e, 0x15, 0x51, 0x15, 0xa6, 0xe5, 0x41, 0x8f, 0x44, 0x42,
|
||||
0xb3, 0x01, 0x4a, 0x68, 0x0e, 0xcd, 0xf7, 0x73, 0x9a, 0x6a, 0xa6, 0x30,
|
||||
0xd2, 0x59, 0xae, 0xd5, 0x00, 0xd9, 0x79, 0x32, 0x34, 0xd7, 0x72, 0x96,
|
||||
0x47, 0x42, 0x52, 0x4d, 0x52, 0x4d, 0x4b, 0x09, 0x8c, 0x4a, 0x57, 0x9e,
|
||||
0xc0, 0xdc, 0x1d, 0x80, 0x66, 0xd0, 0xa1, 0x09, 0x28, 0x48, 0x24, 0x84,
|
||||
0x2c, 0xf7, 0x65, 0x34, 0x09, 0x21, 0x13, 0x4e, 0x53, 0x29, 0xb1, 0x9e,
|
||||
0x97, 0x39, 0x01, 0x48, 0x01, 0x61, 0x00, 0xe9, 0xee, 0xed, 0x56, 0x8d,
|
||||
0x4a, 0x90, 0x10, 0xa7, 0x81, 0x66, 0x6b, 0x6e, 0xe2, 0xb4, 0x0e, 0x6d,
|
||||
0x01, 0x50, 0xba, 0xaf, 0xc0, 0x95, 0x66, 0x20, 0x08, 0x67, 0x39, 0x28,
|
||||
0xc7, 0x3c, 0xfd, 0x19, 0x35, 0x4c, 0x4d, 0xb2, 0xa9, 0x01, 0x53, 0x24,
|
||||
0xd4, 0x24, 0x05, 0x24, 0xf9, 0xc0, 0xbf, 0x83, 0x5c, 0x64, 0xcf, 0xdd,
|
||||
0xcf, 0x8c, 0xb9, 0xe6, 0xbf, 0xca, 0xf7, 0xb9, 0xbe, 0xdc, 0x1f, 0xd6,
|
||||
0x75, 0xa9, 0xa6, 0x49, 0x4d, 0x5e, 0x57, 0x00, 0x40, 0x57, 0xc2, 0x29,
|
||||
0xa1, 0x47, 0xed, 0xc6, 0x94, 0x9d, 0x7d, 0x5d, 0xc5, 0xcd, 0xde, 0xab,
|
||||
0xb6, 0xd8, 0xa9, 0xfd, 0x86, 0x5d, 0x11, 0x01, 0x40, 0xcd, 0xed, 0xf4,
|
||||
0xce, 0x2a, 0xba, 0xec, 0x6a, 0xd7, 0x45, 0x71, 0xcc, 0x4a, 0x00, 0x28,
|
||||
0x0a, 0x80, 0x0c, 0x93, 0x52, 0x6c, 0x96, 0xae, 0xc2, 0xa5, 0x00, 0xa2,
|
||||
0x25, 0x6d, 0x0d, 0x82, 0x06, 0x68, 0x16, 0x10, 0x9a, 0x9a, 0x88, 0x82,
|
||||
0xa6, 0x76, 0xfa, 0xa5, 0x2e, 0x09, 0x87, 0x4a, 0xc5, 0x4a, 0x80, 0x99,
|
||||
0x9a, 0xba, 0x12, 0x91, 0xa4, 0x76, 0x8c, 0xba, 0x09, 0xc5, 0xa9, 0x92,
|
||||
0x06, 0x03, 0xcf, 0x01, 0xd9, 0x12, 0x98, 0x38, 0x55, 0x6a, 0x01, 0x4f,
|
||||
0x55, 0xa2, 0x32, 0xb5, 0x99, 0xd5, 0x2a, 0x69, 0x26, 0xd0, 0x02, 0xde,
|
||||
0x19, 0x2d, 0x4c, 0xb0, 0x7b, 0x76, 0x8e, 0x77, 0x3a, 0x6f, 0xb4, 0xf2,
|
||||
0x0d, 0xe9, 0xc6, 0xad, 0xb9, 0xda, 0xf4, 0x14, 0x9c, 0xbe, 0x67, 0xc6,
|
||||
0x73, 0x4e, 0x29, 0x93, 0xcc, 0x01, 0xe4, 0x05, 0xe0, 0xdb, 0x2d, 0xc2,
|
||||
0xa6, 0xc6, 0x29, 0x61, 0xab, 0x24, 0x91, 0x3f, 0x22, 0x5f, 0xef, 0x08,
|
||||
0x94, 0x4c, 0x6a, 0x02, 0xd4, 0xa4, 0xe6, 0x43, 0xe9, 0xbe, 0x4d, 0xef,
|
||||
0x7f, 0x3a, 0x0e, 0x39, 0xba, 0xb3, 0xf7, 0xb1, 0x8b, 0x2d, 0xfd, 0xd4,
|
||||
0xa2, 0x1e, 0x15, 0x5b, 0xf7, 0xf4, 0x01, 0x58, 0xff, 0xb0, 0xe6, 0x52,
|
||||
0x5c, 0x3d, 0xe2, 0xec, 0x19, 0xd0, 0xea, 0x38, 0x09, 0x6b, 0x3d, 0x94,
|
||||
0x58, 0x8b, 0x62, 0xf7, 0x83, 0x69, 0x03, 0x4c, 0xb0, 0x37, 0x0c, 0x10,
|
||||
0xa6, 0x5a, 0x2f, 0xf4, 0xae, 0x64, 0x50, 0xb0, 0xb0, 0x38, 0x88, 0x7d,
|
||||
0x33, 0xb5, 0x27, 0x3c, 0xb5, 0x2b, 0x9f, 0x04, 0xe9, 0x6d, 0xad, 0xda,
|
||||
0x74, 0x9c, 0x01, 0x24, 0x9c, 0x4e, 0x27, 0x84, 0x4e, 0x76, 0x4a, 0x1b,
|
||||
0xbb, 0x75, 0x69, 0xd7, 0xd9, 0xde, 0xb5, 0xf5, 0x0e, 0x46, 0x89, 0x01,
|
||||
0x28, 0x49, 0x46, 0x02, 0x10, 0xea, 0x08, 0xb0, 0x8c, 0xe5, 0x19, 0x4d,
|
||||
0x9d, 0xbd, 0x59, 0x1b, 0x0c, 0x05, 0xd9, 0xfc, 0x0e, 0xe6, 0x08, 0xd0,
|
||||
0x1d, 0x87, 0x01, 0x00, 0x70, 0x77, 0x77, 0x87, 0x82, 0xac, 0x55, 0x41,
|
||||
0x29, 0x23, 0xe0, 0x08, 0xc0, 0x0e, 0x87, 0x01, 0x02, 0x81, 0x92, 0x5e,
|
||||
0xab, 0xc4, 0xb6, 0x1f, 0x1e, 0x0a, 0xc4, 0xb2, 0xe1, 0x04, 0xa4, 0x7a,
|
||||
0x0d, 0xab, 0x97, 0x26, 0x79, 0xf1, 0x9d, 0xd7, 0x26, 0x76, 0x01, 0xba,
|
||||
0x26, 0x74, 0xe0, 0xcb, 0x05, 0x86, 0x38, 0x13, 0xe4, 0x00, 0x2c, 0x1b,
|
||||
0x00, 0x19, 0x80, 0xb8, 0xd9, 0x63, 0xc9, 0x9c, 0x48, 0x7f, 0x15, 0xdd,
|
||||
0xf1, 0x3e, 0xbd, 0x24, 0x5f, 0x04, 0x0c, 0x68, 0x95, 0xd3, 0xb1, 0xc0,
|
||||
0xba, 0xbe, 0x82, 0xf5, 0x03, 0xc0, 0x2a, 0xa7, 0xcc, 0x8f, 0x00, 0x57,
|
||||
0x5d, 0x99, 0x52, 0xf1, 0x1a, 0x40, 0xb9, 0xdb, 0x74, 0xfe, 0x04, 0x70,
|
||||
0xd4, 0xc6, 0xb2, 0x01, 0x74, 0x33, 0x69, 0x00, 0xd7, 0xde, 0x6f, 0xae,
|
||||
0xdb, 0x06, 0xd9, 0x1f, 0x4d, 0x9d, 0x9b, 0x3f, 0x03, 0x6c, 0x22, 0xd4,
|
||||
0x00, 0xea, 0xc7, 0x80, 0x63, 0x38, 0x73, 0x01, 0xca, 0x2e, 0x95, 0xdc,
|
||||
0x59, 0xee, 0x57, 0x3e, 0x1c, 0x05, 0x79, 0xdc, 0xba, 0x32, 0xf5, 0xb9,
|
||||
0xd1, 0xba, 0xea, 0xed, 0xe9, 0xd6, 0x36, 0xfe, 0x63, 0xc0, 0xaa, 0x83,
|
||||
0x6b, 0xfd, 0x0b, 0x80, 0xfe, 0xd4, 0xc0, 0xec, 0x0a, 0xc4, 0x6d, 0xed,
|
||||
0x10, 0x1e, 0x36, 0x3b, 0xa7, 0x4a, 0xaf, 0x91, 0xf5, 0xb1, 0x0b, 0xdd,
|
||||
0x18, 0xfe, 0x66, 0xd8, 0xfb, 0x57, 0x3f, 0x07, 0x8c, 0x05, 0x1f, 0x02,
|
||||
0x82, 0x13, 0xe2, 0xaf, 0x02, 0xe0, 0x1b, 0xf0, 0x7b, 0x00, 0xd6, 0xde,
|
||||
0xef, 0x65, 0x80, 0xae, 0xae, 0xfd, 0x32, 0x80, 0xa9, 0x7c, 0xd8, 0x7e,
|
||||
0x7c, 0x08, 0xe8, 0xcf, 0xbf, 0x97, 0x01, 0xfd, 0xbd, 0x78, 0x9e, 0x07,
|
||||
0xfc, 0x7b, 0x00, 0x3e, 0x19, 0xff, 0x31, 0xe0, 0xf3, 0x8d, 0xfe, 0xc5,
|
||||
0x38, 0xdc, 0x8f, 0xf4, 0x6d, 0x2e, 0xdd, 0x66, 0xeb, 0xef, 0x99, 0xd3,
|
||||
0x73, 0x36, 0xf6, 0x32, 0x60, 0xce, 0x93, 0x5e, 0x07, 0xac, 0xd3, 0x9f,
|
||||
0x97, 0xfb, 0xc3, 0x9c, 0x6b, 0xdc, 0x00, 0xf4, 0x97, 0xe2, 0x0d, 0x40,
|
||||
0x9f, 0x59, 0xde, 0x01, 0x88, 0xe1, 0x5d, 0x37, 0xfd, 0x29, 0xe0, 0x7b,
|
||||
0x7c, 0x8f, 0xaf, 0x19, 0x77, 0x3f, 0xb6, 0xf6, 0x3f, 0x15, 0x5e, 0x1c,
|
||||
0xca, 0x9b, 0x04, 0x27, 0x70, 0xeb, 0xc0, 0x63, 0x8e, 0x1d, 0x3e, 0x0f,
|
||||
0xb0, 0x56, 0xfe, 0x99, 0x77, 0x00, 0x7f, 0xdc, 0x05, 0xfc, 0xf5, 0xe9,
|
||||
0x12, 0x6e, 0x01, 0x3e, 0xdf, 0xad, 0xb7, 0x23, 0x7d, 0x9f, 0x4b, 0xb7,
|
||||
0xd9, 0x7a, 0x3f, 0x1f, 0xbe, 0xc7, 0xff, 0x75, 0xfc, 0x0d, 0x3b, 0xd4,
|
||||
0xd5, 0x4b, 0x3b, 0xfe, 0xb6, 0x75, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
|
||||
0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
|
||||
};
|
||||
|
||||
const u32 TEXTURE_ATLAS_LENGTH = (u32)std::size(TEXTURE_ATLAS);
|
||||
const vec2 TEXTURE_ATLAS_SIZE = {96, 160};
|
||||
|
||||
enum AtlasType
|
||||
{
|
||||
ATLAS_NONE,
|
||||
ATLAS_FOLDER,
|
||||
ATLAS_ROOT,
|
||||
ATLAS_LAYER,
|
||||
ATLAS_NULL,
|
||||
ATLAS_TRIGGERS,
|
||||
ATLAS_VISIBLE,
|
||||
ATLAS_INVISIBLE,
|
||||
ATLAS_SHOW_RECT,
|
||||
ATLAS_HIDE_RECT,
|
||||
ATLAS_SHOW_UNUSED,
|
||||
ATLAS_HIDE_UNUSED,
|
||||
ATLAS_PAN,
|
||||
ATLAS_MOVE,
|
||||
ATLAS_ROTATE,
|
||||
ATLAS_SCALE,
|
||||
ATLAS_CROP,
|
||||
ATLAS_DRAW,
|
||||
ATLAS_ERASE,
|
||||
ATLAS_COLOR_PICKER,
|
||||
ATLAS_UNDO,
|
||||
ATLAS_REDO,
|
||||
ATLAS_ANIMATION,
|
||||
ATLAS_SPRITESHEET,
|
||||
ATLAS_EVENT,
|
||||
ATLAS_PLAY,
|
||||
ATLAS_PAUSE,
|
||||
ATLAS_ADD,
|
||||
ATLAS_REMOVE,
|
||||
ATLAS_TRIGGER,
|
||||
ATLAS_PIVOT,
|
||||
ATLAS_SQUARE,
|
||||
ATLAS_CIRCLE,
|
||||
ATLAS_PICKER,
|
||||
ATLAS_FRAME,
|
||||
ATLAS_FRAME_ALT,
|
||||
ATLAS_TARGET,
|
||||
ATLAS_TARGET_ALT,
|
||||
ATLAS_COUNT
|
||||
};
|
||||
|
||||
struct AtlasEntry
|
||||
{
|
||||
vec2 position;
|
||||
vec2 size;
|
||||
};
|
||||
|
||||
const vec2 ATLAS_SIZE_SMALL = {8, 8};
|
||||
const vec2 ATLAS_SIZE_NORMAL = {16, 16};
|
||||
const vec2 ATLAS_SIZE_OBLONG = {16, 40};
|
||||
const vec2 ATLAS_SIZE_BIG = {40, 40};
|
||||
|
||||
const inline AtlasEntry ATLAS_ENTRIES[ATLAS_COUNT] =
|
||||
{
|
||||
{{ 0, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 16, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 32, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 48, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 64, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 80, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{ 0, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 16, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 32, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 48, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 64, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 80, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{ 0, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 16, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 32, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 48, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 64, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 80, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{ 0, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 16, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 32, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 48, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 64, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 80, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{ 0, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{ 16, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{ 32, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{ 48, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{ 64, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{ 80, 64}, ATLAS_SIZE_SMALL },
|
||||
{{ 88, 64}, ATLAS_SIZE_SMALL },
|
||||
{{ 80, 72}, ATLAS_SIZE_SMALL },
|
||||
{{ 88, 72}, ATLAS_SIZE_SMALL },
|
||||
{{ 0, 80}, ATLAS_SIZE_OBLONG},
|
||||
{{16, 80}, ATLAS_SIZE_OBLONG},
|
||||
{{32, 80}, ATLAS_SIZE_OBLONG},
|
||||
{{48, 80}, ATLAS_SIZE_BIG},
|
||||
{{48, 120}, ATLAS_SIZE_BIG}
|
||||
};
|
||||
|
||||
#define ATLAS_POSITION(type) ATLAS_ENTRIES[type].position
|
||||
#define ATLAS_SIZE(type) ATLAS_ENTRIES[type].size
|
||||
#define ATLAS_UV_MIN(type) (ATLAS_POSITION(type) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_MAX(type) ((ATLAS_POSITION(type) + ATLAS_SIZE(type)) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_ARGS(type) ATLAS_UV_MIN(type), ATLAS_UV_MAX(type)
|
||||
#define ATLAS_UV_VERTICES(type) UV_VERTICES(ATLAS_UV_MIN(type), ATLAS_UV_MAX(type))
|
||||
|
||||
struct ShaderData
|
||||
{
|
||||
std::string vertex;
|
||||
std::string fragment;
|
||||
};
|
||||
|
||||
enum ShaderType
|
||||
{
|
||||
SHADER_LINE,
|
||||
SHADER_TEXTURE,
|
||||
SHADER_AXIS,
|
||||
SHADER_GRID,
|
||||
SHADER_COUNT
|
||||
};
|
||||
|
||||
const std::string SHADER_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position;
|
||||
layout (location = 1) in vec2 i_uv;
|
||||
out vec2 i_uv_out;
|
||||
uniform mat4 u_transform;
|
||||
void main()
|
||||
{
|
||||
i_uv_out = i_uv;
|
||||
gl_Position = u_transform * vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_AXIS_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position; // full screen line segment: -1..1
|
||||
uniform vec2 u_origin_ndc; // world origin in NDC
|
||||
uniform int u_axis; // 0 = X axis, 1 = Y axis
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos = (u_axis == 0)
|
||||
? vec2(i_position.x, u_origin_ndc.y) // horizontal line across screen
|
||||
: vec2(u_origin_ndc.x, i_position.x); // vertical line across screen;
|
||||
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_GRID_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout ( location = 0 ) in vec2 i_position;
|
||||
out vec2 clip;
|
||||
|
||||
void main() {
|
||||
clip = i_position;
|
||||
gl_Position = vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
out vec4 o_fragColor;
|
||||
uniform vec4 u_color;
|
||||
void main()
|
||||
{
|
||||
o_fragColor = u_color;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_TEXTURE_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
in vec2 i_uv_out;
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_tint;
|
||||
uniform vec3 u_color_offset;
|
||||
out vec4 o_fragColor;
|
||||
void main()
|
||||
{
|
||||
vec4 texColor = texture(u_texture, i_uv_out);
|
||||
texColor *= u_tint;
|
||||
texColor.rgb += u_color_offset;
|
||||
o_fragColor = texColor;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_GRID_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
in vec2 clip;
|
||||
|
||||
uniform mat4 u_model;
|
||||
uniform vec2 u_size;
|
||||
uniform vec2 u_offset;
|
||||
uniform vec4 u_color;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 w = u_model * vec4(clip, 0.0, 1.0);
|
||||
w /= w.w;
|
||||
vec2 world = w.xy;
|
||||
|
||||
vec2 g = (world - u_offset) / u_size;
|
||||
|
||||
vec2 d = abs(fract(g) - 0.5);
|
||||
float distance = min(d.x, d.y);
|
||||
|
||||
float fw = min(fwidth(g.x), fwidth(g.y));
|
||||
float alpha = 1.0 - smoothstep(0.0, fw, distance);
|
||||
|
||||
if (alpha <= 0.0) discard;
|
||||
o_fragColor = vec4(u_color.rgb, u_color.a * alpha);
|
||||
}
|
||||
)";
|
||||
|
||||
#define SHADER_UNIFORM_AXIS "u_axis"
|
||||
#define SHADER_UNIFORM_COLOR "u_color"
|
||||
#define SHADER_UNIFORM_TRANSFORM "u_transform"
|
||||
#define SHADER_UNIFORM_TINT "u_tint"
|
||||
#define SHADER_UNIFORM_COLOR_OFFSET "u_color_offset"
|
||||
#define SHADER_UNIFORM_OFFSET "u_offset"
|
||||
#define SHADER_UNIFORM_ORIGIN_NDC "u_origin_ndc"
|
||||
#define SHADER_UNIFORM_SIZE "u_size"
|
||||
#define SHADER_UNIFORM_MODEL "u_model"
|
||||
#define SHADER_UNIFORM_TEXTURE "u_texture"
|
||||
|
||||
const ShaderData SHADER_DATA[SHADER_COUNT] =
|
||||
{
|
||||
{SHADER_VERTEX, SHADER_FRAGMENT},
|
||||
{SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT},
|
||||
{SHADER_AXIS_VERTEX, SHADER_FRAGMENT},
|
||||
{SHADER_GRID_VERTEX, SHADER_GRID_FRAGMENT}
|
||||
};
|
||||
197
src/RESOURCE.h
Normal file
197
src/RESOURCE.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
|
||||
#define ATLAS_PATH "resources/atlas.png"
|
||||
#define FONT_PATH "resources/font.ttf"
|
||||
#define FONT_SIZE 16
|
||||
|
||||
const vec2 TEXTURE_ATLAS_SIZE = {96, 160};
|
||||
|
||||
enum AtlasType {
|
||||
ATLAS_NONE,
|
||||
ATLAS_FOLDER,
|
||||
ATLAS_ROOT,
|
||||
ATLAS_LAYER,
|
||||
ATLAS_NULL,
|
||||
ATLAS_TRIGGERS,
|
||||
ATLAS_VISIBLE,
|
||||
ATLAS_INVISIBLE,
|
||||
ATLAS_SHOW_RECT,
|
||||
ATLAS_HIDE_RECT,
|
||||
ATLAS_SHOW_UNUSED,
|
||||
ATLAS_HIDE_UNUSED,
|
||||
ATLAS_PAN,
|
||||
ATLAS_MOVE,
|
||||
ATLAS_ROTATE,
|
||||
ATLAS_SCALE,
|
||||
ATLAS_CROP,
|
||||
ATLAS_DRAW,
|
||||
ATLAS_ERASE,
|
||||
ATLAS_COLOR_PICKER,
|
||||
ATLAS_UNDO,
|
||||
ATLAS_REDO,
|
||||
ATLAS_ANIMATION,
|
||||
ATLAS_SPRITESHEET,
|
||||
ATLAS_EVENT,
|
||||
ATLAS_PLAY,
|
||||
ATLAS_PAUSE,
|
||||
ATLAS_ADD,
|
||||
ATLAS_REMOVE,
|
||||
ATLAS_TRIGGER,
|
||||
ATLAS_PIVOT,
|
||||
ATLAS_SQUARE,
|
||||
ATLAS_CIRCLE,
|
||||
ATLAS_PICKER,
|
||||
ATLAS_FRAME,
|
||||
ATLAS_FRAME_ALT,
|
||||
ATLAS_TARGET,
|
||||
ATLAS_TARGET_ALT,
|
||||
ATLAS_COUNT
|
||||
};
|
||||
|
||||
struct AtlasEntry {
|
||||
vec2 position;
|
||||
vec2 size;
|
||||
};
|
||||
|
||||
const vec2 ATLAS_SIZE_SMALL = {8, 8};
|
||||
const vec2 ATLAS_SIZE_NORMAL = {16, 16};
|
||||
const vec2 ATLAS_SIZE_OBLONG = {16, 40};
|
||||
const vec2 ATLAS_SIZE_BIG = {40, 40};
|
||||
|
||||
const inline AtlasEntry ATLAS_ENTRIES[ATLAS_COUNT] = {
|
||||
{{0, 0}, ATLAS_SIZE_NORMAL}, {{16, 0}, ATLAS_SIZE_NORMAL}, {{32, 0}, ATLAS_SIZE_NORMAL}, {{48, 0}, ATLAS_SIZE_NORMAL}, {{64, 0}, ATLAS_SIZE_NORMAL},
|
||||
{{80, 0}, ATLAS_SIZE_NORMAL}, {{0, 16}, ATLAS_SIZE_NORMAL}, {{16, 16}, ATLAS_SIZE_NORMAL}, {{32, 16}, ATLAS_SIZE_NORMAL}, {{48, 16}, ATLAS_SIZE_NORMAL},
|
||||
{{64, 16}, ATLAS_SIZE_NORMAL}, {{80, 16}, ATLAS_SIZE_NORMAL}, {{0, 32}, ATLAS_SIZE_NORMAL}, {{16, 32}, ATLAS_SIZE_NORMAL}, {{32, 32}, ATLAS_SIZE_NORMAL},
|
||||
{{48, 32}, ATLAS_SIZE_NORMAL}, {{64, 32}, ATLAS_SIZE_NORMAL}, {{80, 32}, ATLAS_SIZE_NORMAL}, {{0, 48}, ATLAS_SIZE_NORMAL}, {{16, 48}, ATLAS_SIZE_NORMAL},
|
||||
{{32, 48}, ATLAS_SIZE_NORMAL}, {{48, 48}, ATLAS_SIZE_NORMAL}, {{64, 48}, ATLAS_SIZE_NORMAL}, {{80, 48}, ATLAS_SIZE_NORMAL}, {{0, 64}, ATLAS_SIZE_NORMAL},
|
||||
{{16, 64}, ATLAS_SIZE_NORMAL}, {{32, 64}, ATLAS_SIZE_NORMAL}, {{48, 64}, ATLAS_SIZE_NORMAL}, {{64, 64}, ATLAS_SIZE_NORMAL}, {{80, 64}, ATLAS_SIZE_SMALL},
|
||||
{{88, 64}, ATLAS_SIZE_SMALL}, {{80, 72}, ATLAS_SIZE_SMALL}, {{88, 72}, ATLAS_SIZE_SMALL}, {{0, 80}, ATLAS_SIZE_OBLONG}, {{16, 80}, ATLAS_SIZE_OBLONG},
|
||||
{{32, 80}, ATLAS_SIZE_OBLONG}, {{48, 80}, ATLAS_SIZE_BIG}, {{48, 120}, ATLAS_SIZE_BIG}};
|
||||
|
||||
#define ATLAS_POSITION(type) ATLAS_ENTRIES[type].position
|
||||
#define ATLAS_SIZE(type) ATLAS_ENTRIES[type].size
|
||||
#define ATLAS_UV_MIN(type) (ATLAS_POSITION(type) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_MAX(type) ((ATLAS_POSITION(type) + ATLAS_SIZE(type)) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_ARGS(type) ATLAS_UV_MIN(type), ATLAS_UV_MAX(type)
|
||||
#define ATLAS_UV_VERTICES(type) UV_VERTICES(ATLAS_UV_MIN(type), ATLAS_UV_MAX(type))
|
||||
|
||||
struct ShaderData {
|
||||
std::string vertex;
|
||||
std::string fragment;
|
||||
};
|
||||
|
||||
enum ShaderType { SHADER_LINE, SHADER_TEXTURE, SHADER_AXIS, SHADER_GRID, SHADER_COUNT };
|
||||
|
||||
const std::string SHADER_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position;
|
||||
layout (location = 1) in vec2 i_uv;
|
||||
out vec2 i_uv_out;
|
||||
uniform mat4 u_transform;
|
||||
void main()
|
||||
{
|
||||
i_uv_out = i_uv;
|
||||
gl_Position = u_transform * vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_AXIS_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position; // full screen line segment: -1..1
|
||||
uniform vec2 u_origin_ndc; // world origin in NDC
|
||||
uniform int u_axis; // 0 = X axis, 1 = Y axis
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos = (u_axis == 0)
|
||||
? vec2(i_position.x, u_origin_ndc.y) // horizontal line across screen
|
||||
: vec2(u_origin_ndc.x, i_position.x); // vertical line across screen;
|
||||
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_GRID_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout ( location = 0 ) in vec2 i_position;
|
||||
out vec2 clip;
|
||||
|
||||
void main() {
|
||||
clip = i_position;
|
||||
gl_Position = vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
out vec4 o_fragColor;
|
||||
uniform vec4 u_color;
|
||||
void main()
|
||||
{
|
||||
o_fragColor = u_color;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_TEXTURE_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
in vec2 i_uv_out;
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_tint;
|
||||
uniform vec3 u_color_offset;
|
||||
out vec4 o_fragColor;
|
||||
void main()
|
||||
{
|
||||
vec4 texColor = texture(u_texture, i_uv_out);
|
||||
texColor *= u_tint;
|
||||
texColor.rgb += u_color_offset;
|
||||
o_fragColor = texColor;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string SHADER_GRID_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
in vec2 clip;
|
||||
|
||||
uniform mat4 u_model;
|
||||
uniform vec2 u_size;
|
||||
uniform vec2 u_offset;
|
||||
uniform vec4 u_color;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 w = u_model * vec4(clip, 0.0, 1.0);
|
||||
w /= w.w;
|
||||
vec2 world = w.xy;
|
||||
|
||||
vec2 g = (world - u_offset) / u_size;
|
||||
|
||||
vec2 d = abs(fract(g) - 0.5);
|
||||
float distance = min(d.x, d.y);
|
||||
|
||||
float fw = min(fwidth(g.x), fwidth(g.y));
|
||||
float alpha = 1.0 - smoothstep(0.0, fw, distance);
|
||||
|
||||
if (alpha <= 0.0) discard;
|
||||
o_fragColor = vec4(u_color.rgb, u_color.a * alpha);
|
||||
}
|
||||
)";
|
||||
|
||||
#define SHADER_UNIFORM_AXIS "u_axis"
|
||||
#define SHADER_UNIFORM_COLOR "u_color"
|
||||
#define SHADER_UNIFORM_TRANSFORM "u_transform"
|
||||
#define SHADER_UNIFORM_TINT "u_tint"
|
||||
#define SHADER_UNIFORM_COLOR_OFFSET "u_color_offset"
|
||||
#define SHADER_UNIFORM_OFFSET "u_offset"
|
||||
#define SHADER_UNIFORM_ORIGIN_NDC "u_origin_ndc"
|
||||
#define SHADER_UNIFORM_SIZE "u_size"
|
||||
#define SHADER_UNIFORM_MODEL "u_model"
|
||||
#define SHADER_UNIFORM_TEXTURE "u_texture"
|
||||
|
||||
const ShaderData SHADER_DATA[SHADER_COUNT] = {{SHADER_VERTEX, SHADER_FRAGMENT},
|
||||
{SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT},
|
||||
{SHADER_AXIS_VERTEX, SHADER_FRAGMENT},
|
||||
{SHADER_GRID_VERTEX, SHADER_GRID_FRAGMENT}};
|
||||
2553
src/anm2.cpp
2553
src/anm2.cpp
File diff suppressed because it is too large
Load Diff
411
src/anm2.h
411
src/anm2.h
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "resources.h"
|
||||
|
||||
#define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f)
|
||||
#define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f)
|
||||
#define ANM2_SCALE_CONVERT(x) ((float)x / 100.0f)
|
||||
#define ANM2_TINT_CONVERT(x) ((float)x / 255.0f)
|
||||
|
||||
#define ANM2_FPS_MIN 0
|
||||
#define ANM2_FPS_DEFAULT 30
|
||||
@@ -26,283 +26,240 @@
|
||||
#define ANM2_EXTENSION "anm2"
|
||||
#define ANM2_SPRITESHEET_EXTENSION "png"
|
||||
|
||||
#define ANM2_ELEMENT_LIST \
|
||||
X(ANIMATED_ACTOR, "AnimatedActor") \
|
||||
X(INFO, "Info") \
|
||||
X(CONTENT, "Content") \
|
||||
X(SPRITESHEETS, "Spritesheets") \
|
||||
X(SPRITESHEET, "Spritesheet") \
|
||||
X(LAYERS, "Layers") \
|
||||
X(LAYER, "Layer") \
|
||||
X(NULLS, "Nulls") \
|
||||
X(NULL, "Null") \
|
||||
X(EVENTS, "Events") \
|
||||
X(EVENT, "Event") \
|
||||
X(ANIMATIONS, "Animations") \
|
||||
X(ANIMATION, "Animation") \
|
||||
X(ROOT_ANIMATION, "RootAnimation") \
|
||||
X(FRAME, "Frame") \
|
||||
X(LAYER_ANIMATIONS, "LayerAnimations") \
|
||||
X(LAYER_ANIMATION, "LayerAnimation") \
|
||||
X(NULL_ANIMATIONS, "NullAnimations") \
|
||||
X(NULL_ANIMATION, "NullAnimation") \
|
||||
X(TRIGGERS, "Triggers") \
|
||||
X(TRIGGER, "Trigger")
|
||||
#define ANM2_ELEMENT_LIST \
|
||||
X(ANIMATED_ACTOR, "AnimatedActor") \
|
||||
X(INFO, "Info") \
|
||||
X(CONTENT, "Content") \
|
||||
X(SPRITESHEETS, "Spritesheets") \
|
||||
X(SPRITESHEET, "Spritesheet") \
|
||||
X(LAYERS, "Layers") \
|
||||
X(LAYER, "Layer") \
|
||||
X(NULLS, "Nulls") \
|
||||
X(NULL, "Null") \
|
||||
X(EVENTS, "Events") \
|
||||
X(EVENT, "Event") \
|
||||
X(ANIMATIONS, "Animations") \
|
||||
X(ANIMATION, "Animation") \
|
||||
X(ROOT_ANIMATION, "RootAnimation") \
|
||||
X(FRAME, "Frame") \
|
||||
X(LAYER_ANIMATIONS, "LayerAnimations") \
|
||||
X(LAYER_ANIMATION, "LayerAnimation") \
|
||||
X(NULL_ANIMATIONS, "NullAnimations") \
|
||||
X(NULL_ANIMATION, "NullAnimation") \
|
||||
X(TRIGGERS, "Triggers") \
|
||||
X(TRIGGER, "Trigger")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define X(name, str) ANM2_ELEMENT_##name,
|
||||
ANM2_ELEMENT_LIST
|
||||
#undef X
|
||||
ANM2_ELEMENT_COUNT
|
||||
typedef enum {
|
||||
#define X(name, str) ANM2_ELEMENT_##name,
|
||||
ANM2_ELEMENT_LIST
|
||||
#undef X
|
||||
ANM2_ELEMENT_COUNT
|
||||
} Anm2Element;
|
||||
|
||||
const inline char* ANM2_ELEMENT_STRINGS[] =
|
||||
{
|
||||
#define X(name, str) str,
|
||||
const inline char* ANM2_ELEMENT_STRINGS[] = {
|
||||
#define X(name, str) str,
|
||||
ANM2_ELEMENT_LIST
|
||||
#undef X
|
||||
#undef X
|
||||
};
|
||||
|
||||
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ELEMENT_STRING_TO_ENUM, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT)
|
||||
|
||||
#define ANM2_ATTRIBUTE_LIST \
|
||||
X(CREATED_BY, "CreatedBy") \
|
||||
X(CREATED_ON, "CreatedOn") \
|
||||
X(VERSION, "Version") \
|
||||
X(FPS, "Fps") \
|
||||
X(ID, "Id") \
|
||||
X(PATH, "Path") \
|
||||
X(NAME, "Name") \
|
||||
X(SPRITESHEET_ID, "SpritesheetId") \
|
||||
X(SHOW_RECT, "ShowRect") \
|
||||
X(DEFAULT_ANIMATION, "DefaultAnimation") \
|
||||
X(FRAME_NUM, "FrameNum") \
|
||||
X(LOOP, "Loop") \
|
||||
X(X_POSITION, "XPosition") \
|
||||
X(Y_POSITION, "YPosition") \
|
||||
X(X_PIVOT, "XPivot") \
|
||||
X(Y_PIVOT, "YPivot") \
|
||||
X(X_CROP, "XCrop") \
|
||||
X(Y_CROP, "YCrop") \
|
||||
X(WIDTH, "Width") \
|
||||
X(HEIGHT, "Height") \
|
||||
X(X_SCALE, "XScale") \
|
||||
X(Y_SCALE, "YScale") \
|
||||
X(DELAY, "Delay") \
|
||||
X(VISIBLE, "Visible") \
|
||||
X(RED_TINT, "RedTint") \
|
||||
X(GREEN_TINT, "GreenTint") \
|
||||
X(BLUE_TINT, "BlueTint") \
|
||||
X(ALPHA_TINT, "AlphaTint") \
|
||||
X(RED_OFFSET, "RedOffset") \
|
||||
X(GREEN_OFFSET, "GreenOffset") \
|
||||
X(BLUE_OFFSET, "BlueOffset") \
|
||||
X(ROTATION, "Rotation") \
|
||||
X(INTERPOLATED, "Interpolated") \
|
||||
X(LAYER_ID, "LayerId") \
|
||||
X(NULL_ID, "NullId") \
|
||||
X(EVENT_ID, "EventId") \
|
||||
X(AT_FRAME, "AtFrame")
|
||||
#define ANM2_ATTRIBUTE_LIST \
|
||||
X(CREATED_BY, "CreatedBy") \
|
||||
X(CREATED_ON, "CreatedOn") \
|
||||
X(VERSION, "Version") \
|
||||
X(FPS, "Fps") \
|
||||
X(ID, "Id") \
|
||||
X(PATH, "Path") \
|
||||
X(NAME, "Name") \
|
||||
X(SPRITESHEET_ID, "SpritesheetId") \
|
||||
X(SHOW_RECT, "ShowRect") \
|
||||
X(DEFAULT_ANIMATION, "DefaultAnimation") \
|
||||
X(FRAME_NUM, "FrameNum") \
|
||||
X(LOOP, "Loop") \
|
||||
X(X_POSITION, "XPosition") \
|
||||
X(Y_POSITION, "YPosition") \
|
||||
X(X_PIVOT, "XPivot") \
|
||||
X(Y_PIVOT, "YPivot") \
|
||||
X(X_CROP, "XCrop") \
|
||||
X(Y_CROP, "YCrop") \
|
||||
X(WIDTH, "Width") \
|
||||
X(HEIGHT, "Height") \
|
||||
X(X_SCALE, "XScale") \
|
||||
X(Y_SCALE, "YScale") \
|
||||
X(DELAY, "Delay") \
|
||||
X(VISIBLE, "Visible") \
|
||||
X(RED_TINT, "RedTint") \
|
||||
X(GREEN_TINT, "GreenTint") \
|
||||
X(BLUE_TINT, "BlueTint") \
|
||||
X(ALPHA_TINT, "AlphaTint") \
|
||||
X(RED_OFFSET, "RedOffset") \
|
||||
X(GREEN_OFFSET, "GreenOffset") \
|
||||
X(BLUE_OFFSET, "BlueOffset") \
|
||||
X(ROTATION, "Rotation") \
|
||||
X(INTERPOLATED, "Interpolated") \
|
||||
X(LAYER_ID, "LayerId") \
|
||||
X(NULL_ID, "NullId") \
|
||||
X(EVENT_ID, "EventId") \
|
||||
X(AT_FRAME, "AtFrame")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define X(name, str) ANM2_ATTRIBUTE_##name,
|
||||
ANM2_ATTRIBUTE_LIST
|
||||
#undef X
|
||||
ANM2_ATTRIBUTE_COUNT
|
||||
typedef enum {
|
||||
#define X(name, str) ANM2_ATTRIBUTE_##name,
|
||||
ANM2_ATTRIBUTE_LIST
|
||||
#undef X
|
||||
ANM2_ATTRIBUTE_COUNT
|
||||
} Anm2Attribute;
|
||||
|
||||
static const char* ANM2_ATTRIBUTE_STRINGS[] =
|
||||
{
|
||||
#define X(name, str) str,
|
||||
static const char* ANM2_ATTRIBUTE_STRINGS[] = {
|
||||
#define X(name, str) str,
|
||||
ANM2_ATTRIBUTE_LIST
|
||||
#undef X
|
||||
#undef X
|
||||
};
|
||||
|
||||
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ATTRIBUTE_STRING_TO_ENUM, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
|
||||
|
||||
enum Anm2Type
|
||||
{
|
||||
ANM2_NONE,
|
||||
ANM2_ROOT,
|
||||
ANM2_LAYER,
|
||||
ANM2_NULL,
|
||||
ANM2_TRIGGERS,
|
||||
ANM2_COUNT
|
||||
enum Anm2Type { ANM2_NONE, ANM2_ROOT, ANM2_LAYER, ANM2_NULL, ANM2_TRIGGER, ANM2_COUNT };
|
||||
|
||||
struct Anm2Spritesheet {
|
||||
std::string path{};
|
||||
Texture texture;
|
||||
std::vector<uint8_t> pixels;
|
||||
|
||||
auto operator<=>(const Anm2Spritesheet&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Spritesheet
|
||||
{
|
||||
std::string path{};
|
||||
Texture texture;
|
||||
std::vector<u8> pixels;
|
||||
struct Anm2Layer {
|
||||
std::string name = "New Layer";
|
||||
int spritesheetID{};
|
||||
|
||||
auto operator<=>(const Anm2Spritesheet&) const = default;
|
||||
auto operator<=>(const Anm2Layer&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Layer
|
||||
{
|
||||
std::string name = "New Layer";
|
||||
s32 spritesheetID{};
|
||||
struct Anm2Null {
|
||||
std::string name = "New Null";
|
||||
bool isShowRect = false;
|
||||
|
||||
auto operator<=>(const Anm2Layer&) const = default;
|
||||
auto operator<=>(const Anm2Null&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Null
|
||||
{
|
||||
std::string name = "New Null";
|
||||
bool isShowRect = false;
|
||||
struct Anm2Event {
|
||||
std::string name = "New Event";
|
||||
|
||||
auto operator<=>(const Anm2Null&) const = default;
|
||||
auto operator<=>(const Anm2Event&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Event
|
||||
{
|
||||
std::string name = "New Event";
|
||||
#define ANM2_FRAME_MEMBERS \
|
||||
X(isVisible, bool, true) \
|
||||
X(isInterpolated, bool, false) \
|
||||
X(rotation, float, 0.0f) \
|
||||
X(delay, int, ANM2_FRAME_DELAY_MIN) \
|
||||
X(atFrame, int, INDEX_NONE) \
|
||||
X(eventID, int, ID_NONE) \
|
||||
X(crop, vec2, {}) \
|
||||
X(pivot, vec2, {}) \
|
||||
X(position, vec2, {}) \
|
||||
X(size, vec2, {}) \
|
||||
X(scale, vec2, {100, 100}) \
|
||||
X(offsetRGB, vec3, COLOR_OFFSET_NONE) \
|
||||
X(tintRGBA, vec4, COLOR_OPAQUE)
|
||||
|
||||
auto operator<=>(const Anm2Event&) const = default;
|
||||
struct Anm2Frame {
|
||||
#define X(name, type, ...) type name = __VA_ARGS__;
|
||||
ANM2_FRAME_MEMBERS
|
||||
#undef X
|
||||
auto operator<=>(const Anm2Frame&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Frame
|
||||
{
|
||||
bool isVisible = true;
|
||||
bool isInterpolated = false;
|
||||
f32 rotation{};
|
||||
s32 delay = ANM2_FRAME_DELAY_MIN;
|
||||
s32 atFrame = INDEX_NONE;
|
||||
s32 eventID = ID_NONE;
|
||||
vec2 crop{};
|
||||
vec2 pivot{};
|
||||
vec2 position{};
|
||||
vec2 size{};
|
||||
vec2 scale = {100, 100};
|
||||
vec3 offsetRGB = COLOR_OFFSET_NONE;
|
||||
vec4 tintRGBA = COLOR_OPAQUE;
|
||||
|
||||
auto operator<=>(const Anm2Frame&) const = default;
|
||||
struct Anm2FrameChange {
|
||||
#define X(name, type, ...) std::optional<type> name{};
|
||||
ANM2_FRAME_MEMBERS
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct Anm2Item
|
||||
{
|
||||
bool isVisible = true;
|
||||
std::vector<Anm2Frame> frames;
|
||||
struct Anm2Item {
|
||||
bool isVisible = true;
|
||||
std::vector<Anm2Frame> frames;
|
||||
|
||||
auto operator<=>(const Anm2Item&) const = default;
|
||||
auto operator<=>(const Anm2Item&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Animation
|
||||
{
|
||||
s32 frameNum = ANM2_FRAME_NUM_MIN;
|
||||
std::string name = "New Animation";
|
||||
bool isLoop = true;
|
||||
Anm2Item rootAnimation;
|
||||
std::unordered_map<s32, Anm2Item> layerAnimations;
|
||||
std::vector<s32> layerOrder;
|
||||
std::map<s32, Anm2Item> nullAnimations;
|
||||
Anm2Item triggers;
|
||||
|
||||
auto operator<=>(const Anm2Animation&) const = default;
|
||||
struct Anm2Animation {
|
||||
int frameNum = ANM2_FRAME_NUM_MIN;
|
||||
std::string name = "New Animation";
|
||||
bool isLoop = true;
|
||||
Anm2Item rootAnimation;
|
||||
std::unordered_map<int, Anm2Item> layerAnimations;
|
||||
std::vector<int> layerOrder;
|
||||
std::map<int, Anm2Item> nullAnimations;
|
||||
Anm2Item triggers;
|
||||
|
||||
auto operator<=>(const Anm2Animation&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2
|
||||
{
|
||||
std::string path{};
|
||||
std::string createdBy = "robot";
|
||||
std::string createdOn{};
|
||||
std::map<s32, Anm2Spritesheet> spritesheets;
|
||||
std::map<s32, Anm2Layer> layers;
|
||||
std::map<s32, Anm2Null> nulls;
|
||||
std::map<s32, Anm2Event> events;
|
||||
std::map<s32, Anm2Animation> animations;
|
||||
s32 defaultAnimationID = ID_NONE;
|
||||
s32 fps = ANM2_FPS_DEFAULT;
|
||||
s32 version{};
|
||||
struct Anm2 {
|
||||
std::string path{};
|
||||
std::string createdBy = "robot";
|
||||
std::string createdOn{};
|
||||
std::map<int, Anm2Spritesheet> spritesheets;
|
||||
std::map<int, Anm2Layer> layers;
|
||||
std::map<int, Anm2Null> nulls;
|
||||
std::map<int, Anm2Event> events;
|
||||
std::map<int, Anm2Animation> animations;
|
||||
int defaultAnimationID = ID_NONE;
|
||||
int fps = ANM2_FPS_DEFAULT;
|
||||
int version{};
|
||||
|
||||
auto operator<=>(const Anm2&) const = default;
|
||||
auto operator<=>(const Anm2&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Reference
|
||||
{
|
||||
s32 animationID = ID_NONE;
|
||||
Anm2Type itemType = ANM2_NONE;
|
||||
s32 itemID = ID_NONE;
|
||||
s32 frameIndex = INDEX_NONE;
|
||||
f32 time = VALUE_NONE;
|
||||
auto operator<=>(const Anm2Reference&) const = default;
|
||||
struct Anm2Reference {
|
||||
int animationID = ID_NONE;
|
||||
Anm2Type itemType = ANM2_NONE;
|
||||
int itemID = ID_NONE;
|
||||
int frameIndex = INDEX_NONE;
|
||||
float time = VALUE_NONE;
|
||||
auto operator<=>(const Anm2Reference&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2FrameChange
|
||||
{
|
||||
std::optional<bool> isVisible;
|
||||
std::optional<bool> isInterpolated;
|
||||
std::optional<f32> rotation;
|
||||
std::optional<s32> delay;
|
||||
std::optional<vec2> crop;
|
||||
std::optional<vec2> pivot;
|
||||
std::optional<vec2> position;
|
||||
std::optional<vec2> size;
|
||||
std::optional<vec2> scale;
|
||||
std::optional<vec3> offsetRGB;
|
||||
std::optional<vec4> tintRGBA;
|
||||
};
|
||||
enum Anm2MergeType { ANM2_MERGE_APPEND, ANM2_MERGE_REPLACE, ANM2_MERGE_PREPEND, ANM2_MERGE_IGNORE };
|
||||
|
||||
enum Anm2MergeType
|
||||
{
|
||||
ANM2_MERGE_APPEND,
|
||||
ANM2_MERGE_REPLACE,
|
||||
ANM2_MERGE_PREPEND,
|
||||
ANM2_MERGE_IGNORE
|
||||
};
|
||||
enum Anm2ChangeType { ANM2_CHANGE_ADD, ANM2_CHANGE_SUBTRACT, ANM2_CHANGE_SET };
|
||||
|
||||
enum Anm2ChangeType
|
||||
{
|
||||
ANM2_CHANGE_ADD,
|
||||
ANM2_CHANGE_SUBTRACT,
|
||||
ANM2_CHANGE_SET
|
||||
};
|
||||
enum OnionskinDrawOrder { ONIONSKIN_BELOW, ONIONSKIN_ABOVE };
|
||||
|
||||
enum OnionskinDrawOrder
|
||||
{
|
||||
ONIONSKIN_BELOW,
|
||||
ONIONSKIN_ABOVE
|
||||
};
|
||||
|
||||
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference);
|
||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference reference);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference reference);
|
||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference reference);
|
||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference reference);
|
||||
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml);
|
||||
bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = true);
|
||||
bool anm2_frame_deserialize_from_xml(Anm2Frame* frame, const std::string& xml);
|
||||
bool anm2_serialize(Anm2* self, const std::string& path);
|
||||
s32 anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, s32 id = ID_NONE);
|
||||
s32 anm2_animation_length_get(Anm2Animation* self);
|
||||
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
|
||||
s32 anm2_layer_add(Anm2* self);
|
||||
s32 anm2_null_add(Anm2* self);
|
||||
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform);
|
||||
void anm2_animation_layer_animation_add(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_layer_animation_remove(Anm2Animation* animation, s32 id);
|
||||
int anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, int id = ID_NONE);
|
||||
int anm2_animation_length_get(Anm2Animation* self);
|
||||
int anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, float time);
|
||||
int anm2_layer_add(Anm2* self);
|
||||
int anm2_null_add(Anm2* self);
|
||||
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference reference, bool isRootTransform);
|
||||
void anm2_animation_layer_animation_add(Anm2Animation* animation, int id);
|
||||
void anm2_animation_layer_animation_remove(Anm2Animation* animation, int id);
|
||||
void anm2_animation_length_set(Anm2Animation* self);
|
||||
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
|
||||
void anm2_animation_null_animation_add(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_null_animation_remove(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_remove(Anm2* self, s32 id);
|
||||
void anm2_animation_merge(Anm2* self, int animationID, const std::vector<int>& mergeIDs, Anm2MergeType type);
|
||||
void anm2_animation_null_animation_add(Anm2Animation* animation, int id);
|
||||
void anm2_animation_null_animation_remove(Anm2Animation* animation, int id);
|
||||
void anm2_animation_remove(Anm2* self, int id);
|
||||
void anm2_animation_serialize_to_string(Anm2Animation* animation, std::string* string);
|
||||
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
|
||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
|
||||
void anm2_frame_remove(Anm2* self, Anm2Reference* reference);
|
||||
void anm2_frame_bake(Anm2* self, Anm2Reference reference, int interval, bool isRoundScale, bool isRoundRotation);
|
||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, float time);
|
||||
void anm2_frame_remove(Anm2* self, Anm2Reference reference);
|
||||
void anm2_frame_serialize_to_string(Anm2Frame* frame, Anm2Type type, std::string* string);
|
||||
void anm2_free(Anm2* self);
|
||||
void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
|
||||
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
|
||||
void anm2_layer_remove(Anm2* self, s32 id);
|
||||
void anm2_generate_from_grid(Anm2* self, Anm2Reference reference, vec2 startPosition, vec2 size, vec2 pivot, int columns, int count, int delay);
|
||||
void anm2_item_frame_set(Anm2* self, Anm2Reference reference, const Anm2FrameChange& change, Anm2ChangeType changeType, int start, int count);
|
||||
void anm2_layer_remove(Anm2* self, int id);
|
||||
void anm2_new(Anm2* self);
|
||||
void anm2_null_remove(Anm2* self, s32 id);
|
||||
void anm2_reference_clear(Anm2Reference* self);
|
||||
void anm2_reference_frame_clear(Anm2Reference* self);
|
||||
void anm2_reference_item_clear(Anm2Reference* self);
|
||||
void anm2_scale(Anm2* self, f32 scale);
|
||||
void anm2_null_remove(Anm2* self, int id);
|
||||
void anm2_scale(Anm2* self, float scale);
|
||||
void anm2_spritesheet_texture_pixels_download(Anm2* self);
|
||||
void anm2_spritesheet_texture_pixels_upload(Anm2* self);
|
||||
void anm2_spritesheet_texture_pixels_upload(Anm2* self);
|
||||
float anm2_time_from_reference(Anm2* self, Anm2Reference reference);
|
||||
360
src/canvas.cpp
360
src/canvas.cpp
@@ -1,244 +1,224 @@
|
||||
#include "canvas.h"
|
||||
|
||||
static void _canvas_framebuffer_set(Canvas* self, const ivec2& size)
|
||||
{
|
||||
self->size = size;
|
||||
self->previousSize = size;
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
|
||||
static void _canvas_framebuffer_set(Canvas* self, const ivec2& size) {
|
||||
self->size = size;
|
||||
self->previousSize = size;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->framebuffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->framebuffer, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, self->framebuffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->framebuffer, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void canvas_init(Canvas* self, const ivec2& size)
|
||||
{
|
||||
// Axis
|
||||
glGenVertexArrays(1, &self->axisVAO);
|
||||
glGenBuffers(1, &self->axisVBO);
|
||||
void canvas_init(Canvas* self, const ivec2& size) {
|
||||
// Axis
|
||||
glGenVertexArrays(1, &self->axisVAO);
|
||||
glGenBuffers(1, &self->axisVBO);
|
||||
|
||||
glBindVertexArray(self->axisVAO);
|
||||
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, 1, GL_FLOAT, GL_FALSE, sizeof(f32), (void*)0);
|
||||
|
||||
// Grid
|
||||
glGenVertexArrays(1, &self->gridVAO);
|
||||
glGenBuffers(1, &self->gridVBO);
|
||||
|
||||
glBindVertexArray(self->gridVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
|
||||
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);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(float), (void*)0);
|
||||
|
||||
// Rect
|
||||
glGenVertexArrays(1, &self->rectVAO);
|
||||
glGenBuffers(1, &self->rectVBO);
|
||||
// Grid
|
||||
glGenVertexArrays(1, &self->gridVAO);
|
||||
glGenBuffers(1, &self->gridVBO);
|
||||
|
||||
glBindVertexArray(self->rectVAO);
|
||||
glBindVertexArray(self->gridVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_RECT_VERTICES), CANVAS_RECT_VERTICES, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
|
||||
// Rect
|
||||
glGenVertexArrays(1, &self->rectVAO);
|
||||
glGenBuffers(1, &self->rectVBO);
|
||||
|
||||
// Grid
|
||||
glGenVertexArrays(1, &self->gridVAO);
|
||||
glBindVertexArray(self->gridVAO);
|
||||
glBindVertexArray(self->rectVAO);
|
||||
|
||||
glGenBuffers(1, &self->gridVBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_GRID_VERTICES), CANVAS_GRID_VERTICES, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_RECT_VERTICES), CANVAS_RECT_VERTICES, GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
// Grid
|
||||
glGenVertexArrays(1, &self->gridVAO);
|
||||
glBindVertexArray(self->gridVAO);
|
||||
|
||||
// Texture
|
||||
glGenVertexArrays(1, &self->textureVAO);
|
||||
glGenBuffers(1, &self->textureVBO);
|
||||
glGenBuffers(1, &self->textureEBO);
|
||||
glGenBuffers(1, &self->gridVBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_GRID_VERTICES), CANVAS_GRID_VERTICES, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(self->textureVAO);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * 4 * 4, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_DYNAMIC_DRAW);
|
||||
// Texture
|
||||
glGenVertexArrays(1, &self->textureVAO);
|
||||
glGenBuffers(1, &self->textureVBO);
|
||||
glGenBuffers(1, &self->textureEBO);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)0);
|
||||
glBindVertexArray(self->textureVAO);
|
||||
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32)));
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * 4, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
// Framebuffer
|
||||
glGenTextures(1, &self->framebuffer);
|
||||
glGenFramebuffers(1, &self->fbo);
|
||||
glGenRenderbuffers(1, &self->rbo);
|
||||
_canvas_framebuffer_set(self, size);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->textureEBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GL_TEXTURE_INDICES), GL_TEXTURE_INDICES, GL_DYNAMIC_DRAW);
|
||||
|
||||
self->isInit = true;
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Framebuffer
|
||||
glGenTextures(1, &self->framebuffer);
|
||||
glGenFramebuffers(1, &self->fbo);
|
||||
glGenRenderbuffers(1, &self->rbo);
|
||||
_canvas_framebuffer_set(self, size);
|
||||
|
||||
self->isInit = true;
|
||||
}
|
||||
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin)
|
||||
{
|
||||
f32 zoomFactor = PERCENT_TO_UNIT(zoom);
|
||||
mat4 projection = glm::ortho(0.0f, (f32)self->size.x, 0.0f, (f32)self->size.y, -1.0f, 1.0f);
|
||||
mat4 view = mat4{1.0f};
|
||||
vec2 size = vec2(self->size.x, self->size.y);
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case ORIGIN_TOP_LEFT:
|
||||
view = glm::translate(view, vec3(pan, 0.0f));
|
||||
break;
|
||||
default:
|
||||
view = glm::translate(view, vec3((size * 0.5f) + pan, 0.0f));
|
||||
break;
|
||||
}
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, float zoom, OriginType origin) {
|
||||
float zoomFactor = PERCENT_TO_UNIT(zoom);
|
||||
mat4 projection = glm::ortho(0.0f, (float)self->size.x, 0.0f, (float)self->size.y, -1.0f, 1.0f);
|
||||
mat4 view = mat4{1.0f};
|
||||
vec2 size = vec2(self->size.x, self->size.y);
|
||||
|
||||
view = glm::scale(view, vec3(zoomFactor, zoomFactor, 1.0f));
|
||||
|
||||
return projection * view;
|
||||
switch (origin) {
|
||||
case ORIGIN_TOP_LEFT:
|
||||
view = glm::translate(view, vec3(pan, 0.0f));
|
||||
break;
|
||||
default:
|
||||
view = glm::translate(view, vec3((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_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_viewport_set(Canvas* self) { glViewport(0, 0, (int)self->size.x, (int)self->size.y); }
|
||||
|
||||
void canvas_framebuffer_resize_check(Canvas* self) {
|
||||
if (self->previousSize != self->size)
|
||||
_canvas_framebuffer_set(self, self->size);
|
||||
}
|
||||
|
||||
void canvas_framebuffer_resize_check(Canvas* self)
|
||||
{
|
||||
if (self->previousSize != self->size)
|
||||
_canvas_framebuffer_set(self, self->size);
|
||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color) {
|
||||
mat4 inverseTransform = glm::inverse(transform);
|
||||
|
||||
glUseProgram(shader);
|
||||
|
||||
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_MODEL), 1, GL_FALSE, glm::value_ptr(inverseTransform));
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_SIZE), size.x, size.y);
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_OFFSET), offset.x, offset.y);
|
||||
glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
|
||||
|
||||
glBindVertexArray(self->gridVAO);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color)
|
||||
{
|
||||
mat4 inverseTransform = glm::inverse(transform);
|
||||
void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& transform, const float* vertices, vec4 tint, vec3 colorOffset) {
|
||||
glUseProgram(shader);
|
||||
|
||||
glUseProgram(shader);
|
||||
glBindVertexArray(self->textureVAO);
|
||||
|
||||
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_MODEL), 1, GL_FALSE, glm::value_ptr(inverseTransform));
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_SIZE), size.x, size.y);
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_OFFSET), offset.x, offset.y);
|
||||
glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_TEXTURE_VERTICES), vertices, GL_DYNAMIC_DRAW);
|
||||
|
||||
glBindVertexArray(self->gridVAO);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glUseProgram(0);
|
||||
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_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(CANVAS_TEXTURE_VERTICES), vertices, GL_DYNAMIC_DRAW);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color) {
|
||||
glUseProgram(shader);
|
||||
|
||||
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);
|
||||
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_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color)
|
||||
{
|
||||
glUseProgram(shader);
|
||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color) {
|
||||
vec4 originNDC = transform * vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
originNDC /= originNDC.w;
|
||||
|
||||
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);
|
||||
glUseProgram(shader);
|
||||
glBindVertexArray(self->axisVAO);
|
||||
glUniform4fv(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), 1, value_ptr(color));
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_ORIGIN_NDC), originNDC.x, originNDC.y);
|
||||
glUniform1i(glGetUniformLocation(shader, SHADER_UNIFORM_AXIS), 0);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glUniform1i(glGetUniformLocation(shader, SHADER_UNIFORM_AXIS), 1);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void canvas_bind(Canvas* self) { glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); }
|
||||
|
||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color)
|
||||
{
|
||||
vec4 originNDC = transform * vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
originNDC /= originNDC.w;
|
||||
void canvas_unbind(void) { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
glUseProgram(shader);
|
||||
glBindVertexArray(self->axisVAO);
|
||||
glUniform4fv(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), 1, value_ptr(color));
|
||||
glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_ORIGIN_NDC), originNDC.x, originNDC.y);
|
||||
glUniform1i(glGetUniformLocation(shader, SHADER_UNIFORM_AXIS), 0);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glUniform1i(glGetUniformLocation(shader, SHADER_UNIFORM_AXIS), 1);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
void canvas_free(Canvas* self) {
|
||||
if (!self->isInit)
|
||||
return;
|
||||
|
||||
void canvas_bind(Canvas* self)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
|
||||
}
|
||||
|
||||
void canvas_unbind(void)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void canvas_free(Canvas* self)
|
||||
{
|
||||
if (!self->isInit) return;
|
||||
|
||||
glDeleteFramebuffers(1, &self->fbo);
|
||||
glDeleteRenderbuffers(1, &self->rbo);
|
||||
glDeleteTextures(1, &self->framebuffer);
|
||||
glDeleteVertexArrays(1, &self->axisVAO);
|
||||
glDeleteVertexArrays(1, &self->rectVAO);
|
||||
glDeleteVertexArrays(1, &self->gridVAO);
|
||||
glDeleteVertexArrays(1, &self->textureVAO);
|
||||
glDeleteBuffers(1, &self->axisVBO);
|
||||
glDeleteBuffers(1, &self->rectVBO);
|
||||
glDeleteBuffers(1, &self->gridVBO);
|
||||
glDeleteBuffers(1, &self->textureVBO);
|
||||
glDeleteBuffers(1, &self->textureEBO);
|
||||
glDeleteFramebuffers(1, &self->fbo);
|
||||
glDeleteRenderbuffers(1, &self->rbo);
|
||||
glDeleteTextures(1, &self->framebuffer);
|
||||
glDeleteVertexArrays(1, &self->axisVAO);
|
||||
glDeleteVertexArrays(1, &self->rectVAO);
|
||||
glDeleteVertexArrays(1, &self->gridVAO);
|
||||
glDeleteVertexArrays(1, &self->textureVAO);
|
||||
glDeleteBuffers(1, &self->axisVBO);
|
||||
glDeleteBuffers(1, &self->rectVBO);
|
||||
glDeleteBuffers(1, &self->gridVBO);
|
||||
glDeleteBuffers(1, &self->textureVBO);
|
||||
glDeleteBuffers(1, &self->textureEBO);
|
||||
}
|
||||
86
src/canvas.h
86
src/canvas.h
@@ -13,64 +13,40 @@
|
||||
const inline vec2 CANVAS_PIVOT_SIZE = {4, 4};
|
||||
const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
|
||||
|
||||
const inline f32 CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};
|
||||
const inline float CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};
|
||||
|
||||
const inline f32 CANVAS_GRID_VERTICES[] =
|
||||
{
|
||||
-1.0f, -1.0f,
|
||||
3.0f, -1.0f,
|
||||
-1.0f, 3.0f
|
||||
const inline float CANVAS_GRID_VERTICES[] = {-1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f};
|
||||
|
||||
const inline float CANVAS_RECT_VERTICES[] = {0, 0, 1, 0, 1, 1, 0, 1};
|
||||
|
||||
const inline float CANVAS_TEXTURE_VERTICES[] = {0, 0, 0.0f, 0.0f, 1, 0, 1.0f, 0.0f, 1, 1, 1.0f, 1.0f, 0, 1, 0.0f, 1.0f};
|
||||
|
||||
struct Canvas {
|
||||
GLuint fbo{};
|
||||
GLuint rbo{};
|
||||
GLuint axisVAO{};
|
||||
GLuint axisVBO{};
|
||||
GLuint rectVAO{};
|
||||
GLuint rectVBO{};
|
||||
GLuint gridVAO{};
|
||||
GLuint gridVBO{};
|
||||
GLuint framebuffer{};
|
||||
GLuint textureVAO{};
|
||||
GLuint textureVBO{};
|
||||
GLuint textureEBO{};
|
||||
ivec2 size{};
|
||||
ivec2 previousSize{};
|
||||
bool isInit = false;
|
||||
};
|
||||
|
||||
const inline f32 CANVAS_RECT_VERTICES[] =
|
||||
{
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
const inline f32 CANVAS_TEXTURE_VERTICES[] =
|
||||
{
|
||||
0, 0, 0.0f, 0.0f,
|
||||
1, 0, 1.0f, 0.0f,
|
||||
1, 1, 1.0f, 1.0f,
|
||||
0, 1, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
struct Canvas
|
||||
{
|
||||
GLuint fbo{};
|
||||
GLuint rbo{};
|
||||
GLuint axisVAO{};
|
||||
GLuint axisVBO{};
|
||||
GLuint rectVAO{};
|
||||
GLuint rectVBO{};
|
||||
GLuint gridVAO{};
|
||||
GLuint gridVBO{};
|
||||
GLuint framebuffer{};
|
||||
GLuint textureVAO{};
|
||||
GLuint textureVBO{};
|
||||
GLuint textureEBO{};
|
||||
ivec2 size{};
|
||||
ivec2 previousSize{};
|
||||
bool isInit = false;
|
||||
};
|
||||
|
||||
#define UV_VERTICES(uvMin, uvMax) \
|
||||
{ \
|
||||
0, 0, uvMin.x, uvMin.y, \
|
||||
1, 0, uvMax.x, uvMin.y, \
|
||||
1, 1, uvMax.x, uvMax.y, \
|
||||
0, 1, uvMin.x, uvMax.y \
|
||||
}
|
||||
#define UV_VERTICES(uvMin, uvMax) {0, 0, uvMin.x, uvMin.y, 1, 0, uvMax.x, uvMin.y, 1, 1, uvMax.x, uvMax.y, 0, 1, uvMin.x, uvMax.y}
|
||||
|
||||
#define ATLAS_UV_MIN(type) (ATLAS_POSITION(type) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_MAX(type) ((ATLAS_POSITION(type) + ATLAS_SIZE(type)) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_ARGS(type) ATLAS_UV_MIN(type), ATLAS_UV_MAX(type)
|
||||
#define ATLAS_UV_VERTICES(type) UV_VERTICES(ATLAS_UV_MIN(type), ATLAS_UV_MAX(type))
|
||||
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, float zoom, OriginType origin);
|
||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
|
||||
void canvas_bind(Canvas* self);
|
||||
void canvas_clear(vec4& color);
|
||||
@@ -83,13 +59,5 @@ void canvas_framebuffer_resize_check(Canvas* self);
|
||||
void canvas_unbind(void);
|
||||
void canvas_viewport_set(Canvas* self);
|
||||
|
||||
void canvas_texture_draw
|
||||
(
|
||||
Canvas* self,
|
||||
GLuint& shader,
|
||||
GLuint& texture,
|
||||
mat4& transform,
|
||||
const f32* vertices = CANVAS_TEXTURE_VERTICES,
|
||||
vec4 tint = COLOR_OPAQUE,
|
||||
vec3 colorOffset = COLOR_OFFSET_NONE
|
||||
);
|
||||
void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& transform, const float* vertices = CANVAS_TEXTURE_VERTICES,
|
||||
vec4 tint = COLOR_OPAQUE, vec3 colorOffset = COLOR_OFFSET_NONE);
|
||||
@@ -1,113 +1,102 @@
|
||||
#include "clipboard.h"
|
||||
|
||||
void clipboard_copy(Clipboard* self)
|
||||
{
|
||||
std::string clipboardText{};
|
||||
|
||||
auto clipboard_text_set = [&]()
|
||||
{
|
||||
if (!SDL_SetClipboardText(clipboardText.c_str()))
|
||||
log_warning(std::format(CLIPBOARD_TEXT_SET_WARNING, SDL_GetError()));
|
||||
};
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
Anm2Frame* frame = anm2_frame_from_reference(self->anm2, reference);
|
||||
if (!frame) break;
|
||||
anm2_frame_serialize_to_string(frame, reference->itemType, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
Anm2Animation* animation = map_find(self->anm2->animations, *id);
|
||||
if (!animation) break;
|
||||
anm2_animation_serialize_to_string(animation, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
void clipboard_copy(Clipboard* self) {
|
||||
std::string clipboardText{};
|
||||
|
||||
auto clipboard_text_set = [&]() {
|
||||
if (!SDL_SetClipboardText(clipboardText.c_str()))
|
||||
log_warning(std::format(CLIPBOARD_TEXT_SET_WARNING, SDL_GetError()));
|
||||
};
|
||||
|
||||
switch (self->type) {
|
||||
case CLIPBOARD_FRAME: {
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference)
|
||||
break;
|
||||
Anm2Frame* frame = anm2_frame_from_reference(self->anm2, *reference);
|
||||
if (!frame)
|
||||
break;
|
||||
anm2_frame_serialize_to_string(frame, reference->itemType, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION: {
|
||||
int* id = std::get_if<int>(&self->location);
|
||||
if (!id)
|
||||
break;
|
||||
Anm2Animation* animation = map_find(self->anm2->animations, *id);
|
||||
if (!animation)
|
||||
break;
|
||||
anm2_animation_serialize_to_string(animation, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_cut(Clipboard* self)
|
||||
{
|
||||
clipboard_copy(self);
|
||||
void clipboard_cut(Clipboard* self) {
|
||||
clipboard_copy(self);
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
anm2_frame_remove(self->anm2, reference);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
anm2_animation_remove(self->anm2, *id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (self->type) {
|
||||
case CLIPBOARD_FRAME: {
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference)
|
||||
break;
|
||||
anm2_frame_remove(self->anm2, *reference);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION: {
|
||||
int* id = std::get_if<int>(&self->location);
|
||||
if (!id)
|
||||
break;
|
||||
anm2_animation_remove(self->anm2, *id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool clipboard_paste(Clipboard* self)
|
||||
{
|
||||
auto clipboard_string = [&]()
|
||||
{
|
||||
char* clipboardText = SDL_GetClipboardText();
|
||||
std::string clipboardString = std::string(clipboardText);
|
||||
SDL_free(clipboardText);
|
||||
return clipboardString;
|
||||
};
|
||||
bool clipboard_paste(Clipboard* self) {
|
||||
auto clipboard_string = [&]() {
|
||||
char* clipboardText = SDL_GetClipboardText();
|
||||
std::string clipboardString = std::string(clipboardText);
|
||||
SDL_free(clipboardText);
|
||||
return clipboardString;
|
||||
};
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
Anm2Frame frame;
|
||||
if (anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
|
||||
anm2_frame_add(self->anm2, &frame, reference);
|
||||
else return false;
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
Anm2Animation animation;
|
||||
if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
|
||||
anm2_animation_add(self->anm2, &animation, *id);
|
||||
else return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (self->type) {
|
||||
case CLIPBOARD_FRAME: {
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference)
|
||||
break;
|
||||
Anm2Frame frame;
|
||||
if (anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
|
||||
anm2_frame_add(self->anm2, &frame, *reference);
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION: {
|
||||
int* id = std::get_if<int>(&self->location);
|
||||
if (!id)
|
||||
break;
|
||||
Anm2Animation animation;
|
||||
if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
|
||||
anm2_animation_add(self->anm2, &animation, *id);
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void clipboard_init(Clipboard* self, Anm2* anm2)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
}
|
||||
void clipboard_init(Clipboard* self, Anm2* anm2) { self->anm2 = anm2; }
|
||||
|
||||
bool clipboard_is_value(void)
|
||||
{
|
||||
return SDL_HasClipboardText();
|
||||
}
|
||||
bool clipboard_is_value(void) { return SDL_HasClipboardText(); }
|
||||
@@ -4,20 +4,14 @@
|
||||
|
||||
#define CLIPBOARD_TEXT_SET_WARNING "Unable to set clipboard text! ({})"
|
||||
|
||||
enum ClipboardType
|
||||
{
|
||||
CLIPBOARD_NONE,
|
||||
CLIPBOARD_FRAME,
|
||||
CLIPBOARD_ANIMATION
|
||||
};
|
||||
enum ClipboardType { CLIPBOARD_NONE, CLIPBOARD_FRAME, CLIPBOARD_ANIMATION };
|
||||
|
||||
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, s32>;
|
||||
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, int>;
|
||||
|
||||
struct Clipboard
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
ClipboardType type;
|
||||
ClipboardLocation location;
|
||||
struct Clipboard {
|
||||
Anm2* anm2 = nullptr;
|
||||
ClipboardType type;
|
||||
ClipboardLocation location;
|
||||
};
|
||||
|
||||
bool clipboard_is_value(void);
|
||||
|
||||
111
src/dialog.cpp
111
src/dialog.cpp
@@ -1,91 +1,70 @@
|
||||
#include "dialog.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter)
|
||||
{
|
||||
Dialog* self;
|
||||
static void _dialog_callback(void* userdata, const char* const* filelist, int filter) {
|
||||
Dialog* self;
|
||||
|
||||
self = (Dialog*)userdata;
|
||||
self = (Dialog*)userdata;
|
||||
|
||||
if (filelist && filelist[0] && strlen(filelist[0]) > 0)
|
||||
{
|
||||
self->path = filelist[0];
|
||||
self->isSelected = true;
|
||||
self->selectedFilter = filter;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->isSelected = false;
|
||||
self->selectedFilter = INDEX_NONE;
|
||||
}
|
||||
if (filelist && filelist[0] && strlen(filelist[0]) > 0) {
|
||||
self->path = filelist[0];
|
||||
self->isSelected = true;
|
||||
self->selectedFilter = filter;
|
||||
} else {
|
||||
self->isSelected = false;
|
||||
self->selectedFilter = INDEX_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void dialog_init(Dialog* self, SDL_Window* window)
|
||||
{
|
||||
self->window = window;
|
||||
void dialog_init(Dialog* self, SDL_Window* window) { self->window = window; }
|
||||
|
||||
void dialog_anm2_open(Dialog* self) {
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr, false);
|
||||
self->type = DIALOG_ANM2_OPEN;
|
||||
}
|
||||
|
||||
void dialog_anm2_open(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr, false);
|
||||
self->type = DIALOG_ANM2_OPEN;
|
||||
void dialog_anm2_save(Dialog* self) {
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr);
|
||||
self->type = DIALOG_ANM2_SAVE;
|
||||
}
|
||||
|
||||
void dialog_anm2_save(Dialog* self)
|
||||
{
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr);
|
||||
self->type = DIALOG_ANM2_SAVE;
|
||||
void dialog_spritesheet_add(Dialog* self) {
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->type = DIALOG_SPRITESHEET_ADD;
|
||||
}
|
||||
|
||||
void dialog_spritesheet_add(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->type = DIALOG_SPRITESHEET_ADD;
|
||||
void dialog_spritesheet_replace(Dialog* self, int id) {
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->replaceID = id;
|
||||
self->type = DIALOG_SPRITESHEET_REPLACE;
|
||||
}
|
||||
|
||||
void dialog_spritesheet_replace(Dialog* self, s32 id)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->replaceID = id;
|
||||
self->type = DIALOG_SPRITESHEET_REPLACE;
|
||||
void dialog_render_path_set(Dialog* self, RenderType type) {
|
||||
SDL_DialogFileFilter filter = DIALOG_RENDER_FILE_FILTERS[type];
|
||||
|
||||
if (type == RENDER_PNG)
|
||||
SDL_ShowOpenFolderDialog(_dialog_callback, self, self->window, nullptr, false);
|
||||
else
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, &filter, 1, nullptr);
|
||||
self->type = DIALOG_RENDER_PATH_SET;
|
||||
}
|
||||
|
||||
void dialog_render_path_set(Dialog* self, RenderType type)
|
||||
{
|
||||
SDL_DialogFileFilter filter = DIALOG_RENDER_FILE_FILTERS[type];
|
||||
|
||||
if (type == RENDER_PNG)
|
||||
SDL_ShowOpenFolderDialog(_dialog_callback, self, self->window, nullptr, false);
|
||||
else
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, &filter, 1, nullptr);
|
||||
self->type = DIALOG_RENDER_PATH_SET;
|
||||
void dialog_ffmpeg_path_set(Dialog* self) {
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_FFMPEG, std::size(DIALOG_FILE_FILTER_FFMPEG), nullptr, false);
|
||||
self->type = DIALOG_FFMPEG_PATH_SET;
|
||||
}
|
||||
|
||||
void dialog_ffmpeg_path_set(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_FFMPEG, std::size(DIALOG_FILE_FILTER_FFMPEG), nullptr, false);
|
||||
self->type = DIALOG_FFMPEG_PATH_SET;
|
||||
}
|
||||
|
||||
void dialog_explorer_open(const std::string& path)
|
||||
{
|
||||
void dialog_explorer_open(const std::string& path) {
|
||||
#ifdef _WIN32
|
||||
ShellExecuteA(NULL, DIALOG_FILE_EXPLORER_COMMAND, path.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
#else
|
||||
char command[DIALOG_FILE_EXPLORER_COMMAND_SIZE];
|
||||
snprintf(command, sizeof(command), DIALOG_FILE_EXPLORER_COMMAND, path.c_str());
|
||||
system(command);
|
||||
ShellExecuteA(NULL, DIALOG_FILE_EXPLORER_COMMAND, path.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
#else
|
||||
char command[DIALOG_FILE_EXPLORER_COMMAND_SIZE];
|
||||
snprintf(command, sizeof(command), DIALOG_FILE_EXPLORER_COMMAND, path.c_str());
|
||||
system(command);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
dialog_reset(Dialog* self)
|
||||
{
|
||||
self->replaceID = ID_NONE;
|
||||
self->type = DIALOG_NONE;
|
||||
self->path.clear();
|
||||
self->isSelected = false;
|
||||
}
|
||||
void dialog_reset(Dialog* self) { *self = {self->window}; }
|
||||
57
src/dialog.h
57
src/dialog.h
@@ -7,30 +7,17 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIALOG_FILE_EXPLORER_COMMAND "open"
|
||||
#else
|
||||
#else
|
||||
#define DIALOG_FILE_EXPLORER_COMMAND "xdg-open \"%s\" &"
|
||||
#endif
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_ANM2[] =
|
||||
{
|
||||
{"Anm2 file", "anm2;xml"}
|
||||
};
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_ANM2[] = {{"Anm2 file", "anm2;xml"}};
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_PNG[] =
|
||||
{
|
||||
{"PNG image", "png"}
|
||||
};
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_PNG[] = {{"PNG image", "png"}};
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_RENDER_FILE_FILTERS[] =
|
||||
{
|
||||
{"PNG image", "png"},
|
||||
{"GIF image", "gif"},
|
||||
{"WebM video", "webm"},
|
||||
{"MP4 video", "mp4"}
|
||||
};
|
||||
const SDL_DialogFileFilter DIALOG_RENDER_FILE_FILTERS[] = {{"PNG image", "png"}, {"GIF image", "gif"}, {"WebM video", "webm"}, {"MP4 video", "mp4"}};
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_FFMPEG[] =
|
||||
{
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_FFMPEG[] = {
|
||||
#ifdef _WIN32
|
||||
{"Executable", "exe"}
|
||||
#else
|
||||
@@ -38,31 +25,29 @@ const SDL_DialogFileFilter DIALOG_FILE_FILTER_FFMPEG[] =
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DialogType
|
||||
{
|
||||
DIALOG_NONE,
|
||||
DIALOG_ANM2_OPEN,
|
||||
DIALOG_ANM2_SAVE,
|
||||
DIALOG_SPRITESHEET_ADD,
|
||||
DIALOG_SPRITESHEET_REPLACE,
|
||||
DIALOG_RENDER_PATH_SET,
|
||||
DIALOG_FFMPEG_PATH_SET
|
||||
enum DialogType {
|
||||
DIALOG_NONE,
|
||||
DIALOG_ANM2_OPEN,
|
||||
DIALOG_ANM2_SAVE,
|
||||
DIALOG_SPRITESHEET_ADD,
|
||||
DIALOG_SPRITESHEET_REPLACE,
|
||||
DIALOG_RENDER_PATH_SET,
|
||||
DIALOG_FFMPEG_PATH_SET
|
||||
};
|
||||
|
||||
struct Dialog
|
||||
{
|
||||
SDL_Window* window = nullptr;
|
||||
s32 selectedFilter = ID_NONE;
|
||||
std::string path{};
|
||||
s32 replaceID = ID_NONE;
|
||||
DialogType type = DIALOG_NONE;
|
||||
bool isSelected = false;
|
||||
struct Dialog {
|
||||
SDL_Window* window = nullptr;
|
||||
std::string path{};
|
||||
int selectedFilter = ID_NONE;
|
||||
int replaceID = ID_NONE;
|
||||
DialogType type = DIALOG_NONE;
|
||||
bool isSelected{};
|
||||
};
|
||||
|
||||
void dialog_init(Dialog* self, SDL_Window* window);
|
||||
void dialog_anm2_open(Dialog* self);
|
||||
void dialog_spritesheet_add(Dialog* self);
|
||||
void dialog_spritesheet_replace(Dialog* self, s32 id);
|
||||
void dialog_spritesheet_replace(Dialog* self, int id);
|
||||
void dialog_anm2_save(Dialog* self);
|
||||
void dialog_render_path_set(Dialog* self, RenderType type);
|
||||
void dialog_render_directory_set(Dialog* self);
|
||||
|
||||
@@ -1,61 +1,54 @@
|
||||
#include "editor.h"
|
||||
|
||||
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) {
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas, vec2());
|
||||
canvas_init(&self->canvas, vec2());
|
||||
}
|
||||
|
||||
void editor_draw(Editor* self)
|
||||
{
|
||||
ivec2& gridSize = self->settings->editorGridSize;
|
||||
ivec2& gridOffset = self->settings->editorGridOffset;
|
||||
vec4& gridColor = self->settings->editorGridColor;
|
||||
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
|
||||
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
|
||||
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->editorBackgroundColor);
|
||||
void editor_draw(Editor* self) {
|
||||
ivec2& gridSize = self->settings->editorGridSize;
|
||||
ivec2& gridOffset = self->settings->editorGridOffset;
|
||||
vec4& gridColor = self->settings->editorGridColor;
|
||||
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
|
||||
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
|
||||
|
||||
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->spritesheetID))
|
||||
{
|
||||
Texture& texture = spritesheet->texture;
|
||||
|
||||
mat4 spritesheetTransform = transform * quad_model_get(texture.size);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
if (self->settings->editorIsBorder)
|
||||
canvas_rect_draw(&self->canvas, shaderLine, spritesheetTransform, EDITOR_BORDER_COLOR);
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->editorBackgroundColor);
|
||||
|
||||
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
|
||||
|
||||
if (frame)
|
||||
{
|
||||
mat4 cropTransform = transform * quad_model_get(frame->size, frame->crop);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR);
|
||||
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->spritesheetID)) {
|
||||
Texture& texture = spritesheet->texture;
|
||||
|
||||
mat4 pivotTransform = transform * quad_model_get(CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, EDITOR_PIVOT_COLOR);
|
||||
}
|
||||
mat4 spritesheetTransform = transform * quad_model_get(texture.size);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);
|
||||
|
||||
if (self->settings->editorIsBorder)
|
||||
canvas_rect_draw(&self->canvas, shaderLine, spritesheetTransform, EDITOR_BORDER_COLOR);
|
||||
|
||||
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, *self->reference);
|
||||
|
||||
if (frame) {
|
||||
mat4 cropTransform = transform * quad_model_get(frame->size, frame->crop);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR);
|
||||
|
||||
mat4 pivotTransform = transform * quad_model_get(CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
|
||||
float vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, EDITOR_PIVOT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->settings->editorIsGrid)
|
||||
canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
|
||||
if (self->settings->editorIsGrid)
|
||||
canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
|
||||
|
||||
canvas_unbind();
|
||||
canvas_unbind();
|
||||
}
|
||||
|
||||
void editor_free(Editor* self)
|
||||
{
|
||||
canvas_free(&self->canvas);
|
||||
}
|
||||
void editor_free(Editor* self) { canvas_free(&self->canvas); }
|
||||
37
src/editor.h
37
src/editor.h
@@ -17,26 +17,25 @@ static const vec4 EDITOR_BORDER_COLOR = COLOR_OPAQUE;
|
||||
static const vec4 EDITOR_FRAME_COLOR = COLOR_RED;
|
||||
static const vec4 EDITOR_PIVOT_COLOR = COLOR_PINK;
|
||||
|
||||
struct Editor
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
Canvas canvas;
|
||||
GLuint fbo;
|
||||
GLuint rbo;
|
||||
GLuint gridVAO;
|
||||
GLuint gridVBO;
|
||||
GLuint texture;
|
||||
GLuint textureEBO;
|
||||
GLuint textureVAO;
|
||||
GLuint textureVBO;
|
||||
GLuint borderVAO;
|
||||
GLuint borderVBO;
|
||||
s32 spritesheetID = ID_NONE;
|
||||
struct Editor {
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
Canvas canvas;
|
||||
GLuint fbo;
|
||||
GLuint rbo;
|
||||
GLuint gridVAO;
|
||||
GLuint gridVBO;
|
||||
GLuint texture;
|
||||
GLuint textureEBO;
|
||||
GLuint textureVAO;
|
||||
GLuint textureVBO;
|
||||
GLuint borderVAO;
|
||||
GLuint borderVBO;
|
||||
int spritesheetID = ID_NONE;
|
||||
};
|
||||
|
||||
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_free(Editor* self);
|
||||
@@ -1,69 +1,56 @@
|
||||
#include "ffmpeg.h"
|
||||
|
||||
bool
|
||||
ffmpeg_render
|
||||
(
|
||||
const std::string& ffmpegPath,
|
||||
const std::string& outputPath,
|
||||
const std::vector<Texture>& frames,
|
||||
ivec2 size,
|
||||
s32 fps,
|
||||
enum RenderType type
|
||||
)
|
||||
{
|
||||
if (frames.empty() || size.x <= 0 || size.y <= 0 || fps <= 0 || ffmpegPath.empty() || outputPath.empty()) return false;
|
||||
|
||||
std::string command{};
|
||||
bool ffmpeg_render(const std::string& ffmpegPath, const std::string& outputPath, const std::vector<Texture>& frames, ivec2 size, int fps,
|
||||
enum RenderType type) {
|
||||
if (frames.empty() || size.x <= 0 || size.y <= 0 || fps <= 0 || ffmpegPath.empty() || outputPath.empty())
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case RENDER_GIF:
|
||||
command = std::format(FFMPEG_GIF_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
case RENDER_WEBM:
|
||||
command = std::format(FFMPEG_WEBM_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
case RENDER_MP4:
|
||||
command = std::format(FFMPEG_MP4_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::string command{};
|
||||
|
||||
switch (type) {
|
||||
case RENDER_GIF:
|
||||
command = std::format(FFMPEG_GIF_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
case RENDER_WEBM:
|
||||
command = std::format(FFMPEG_WEBM_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
case RENDER_MP4:
|
||||
command = std::format(FFMPEG_MP4_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
command = string_quote(command);
|
||||
command = string_quote(command);
|
||||
#endif
|
||||
|
||||
log_command(command);
|
||||
log_command(command);
|
||||
|
||||
FILE* fp = POPEN(command.c_str(), PWRITE_MODE);
|
||||
FILE* fp = POPEN(command.c_str(), PWRITE_MODE);
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
log_error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t frameBytes = size.x * size.y * TEXTURE_CHANNELS;
|
||||
if (!fp) {
|
||||
log_error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& frame : frames)
|
||||
{
|
||||
std::vector<u8> rgba = texture_download(&frame);
|
||||
size_t frameBytes = size.x * size.y * TEXTURE_CHANNELS;
|
||||
|
||||
if (rgba.size() != frameBytes)
|
||||
{
|
||||
PCLOSE(fp);
|
||||
return false;
|
||||
}
|
||||
for (const auto& frame : frames) {
|
||||
std::vector<u8> rgba = texture_download(&frame);
|
||||
|
||||
if (fwrite(rgba.data(), 1, frameBytes, fp) != frameBytes)
|
||||
{
|
||||
PCLOSE(fp);
|
||||
return false;
|
||||
}
|
||||
if (rgba.size() != frameBytes) {
|
||||
PCLOSE(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int code = PCLOSE(fp);
|
||||
if (fwrite(rgba.data(), 1, frameBytes, fp) != frameBytes) {
|
||||
PCLOSE(fp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (code == 0);
|
||||
const int code = PCLOSE(fp);
|
||||
|
||||
return (code == 0);
|
||||
}
|
||||
44
src/ffmpeg.h
44
src/ffmpeg.h
@@ -5,34 +5,22 @@
|
||||
|
||||
#define FFMPEG_POPEN_ERROR "popen() (for FFmpeg) failed!\n{}"
|
||||
|
||||
static constexpr const char* FFMPEG_GIF_FORMAT =
|
||||
"\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-lavfi \"split[s0][s1];"
|
||||
"[s0]palettegen=stats_mode=full[p];"
|
||||
"[s1][p]paletteuse=dither=floyd_steinberg\" "
|
||||
"-loop 0 \"{4}\"";
|
||||
static constexpr const char* FFMPEG_GIF_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-lavfi \"split[s0][s1];"
|
||||
"[s0]palettegen=stats_mode=full[p];"
|
||||
"[s1][p]paletteuse=dither=floyd_steinberg\" "
|
||||
"-loop 0 \"{4}\"";
|
||||
|
||||
static constexpr const char* FFMPEG_WEBM_FORMAT =
|
||||
"\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-c:v libvpx-vp9 -crf 30 -b:v 0 -pix_fmt yuva420p -row-mt 1 -threads 0 -speed 2 "
|
||||
"-auto-alt-ref 0 -an \"{4}\"";
|
||||
static constexpr const char* FFMPEG_WEBM_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-c:v libvpx-vp9 -crf 30 -b:v 0 -pix_fmt yuva420p -row-mt 1 -threads 0 -speed 2 "
|
||||
"-auto-alt-ref 0 -an \"{4}\"";
|
||||
|
||||
static constexpr const char* FFMPEG_MP4_FORMAT =
|
||||
"\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-vf \"format=yuv420p,scale=trunc(iw/2)*2:trunc(ih/2)*2\" "
|
||||
"-c:v libx265 -crf 20 -preset slow "
|
||||
"-tag:v hvc1 -movflags +faststart -an \"{4}\"";
|
||||
static constexpr const char* FFMPEG_MP4_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-vf \"format=yuv420p,scale=trunc(iw/2)*2:trunc(ih/2)*2\" "
|
||||
"-c:v libx265 -crf 20 -preset slow "
|
||||
"-tag:v hvc1 -movflags +faststart -an \"{4}\"";
|
||||
|
||||
bool
|
||||
ffmpeg_render
|
||||
(
|
||||
const std::string& ffmpegPath,
|
||||
const std::string& outputPath,
|
||||
const std::vector<Texture>& frames,
|
||||
ivec2 size,
|
||||
s32 fps,
|
||||
enum RenderType type
|
||||
);
|
||||
bool ffmpeg_render(const std::string& ffmpegPath, const std::string& outputPath, const std::vector<Texture>& frames, ivec2 size, int fps, enum RenderType type);
|
||||
@@ -1,57 +1,50 @@
|
||||
#include "generate_preview.h"
|
||||
|
||||
void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) {
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas, GENERATE_PREVIEW_SIZE);
|
||||
canvas_init(&self->canvas, GENERATE_PREVIEW_SIZE);
|
||||
}
|
||||
|
||||
void generate_preview_draw(GeneratePreview* self)
|
||||
{
|
||||
static auto& columns = self->settings->generateColumns;
|
||||
static auto& count = self->settings->generateCount;
|
||||
static GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
const mat4 transform = canvas_transform_get(&self->canvas, {}, CANVAS_ZOOM_DEFAULT, ORIGIN_CENTER);
|
||||
|
||||
vec2 startPosition = {self->settings->generateStartPosition.x, self->settings->generateStartPosition.y};
|
||||
vec2 size = {self->settings->generateSize.x, self->settings->generateSize.y};
|
||||
vec2 pivot = {self->settings->generatePivot.x, self->settings->generatePivot.y};
|
||||
void generate_preview_draw(GeneratePreview* self) {
|
||||
static auto& columns = self->settings->generateColumns;
|
||||
static auto& count = self->settings->generateCount;
|
||||
static GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
const mat4 transform = canvas_transform_get(&self->canvas, {}, CANVAS_ZOOM_DEFAULT, ORIGIN_CENTER);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
|
||||
vec2 startPosition = {self->settings->generateStartPosition.x, self->settings->generateStartPosition.y};
|
||||
vec2 size = {self->settings->generateSize.x, self->settings->generateSize.y};
|
||||
vec2 pivot = {self->settings->generatePivot.x, self->settings->generatePivot.y};
|
||||
|
||||
if (item)
|
||||
{
|
||||
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[self->reference->itemID].spritesheetID))
|
||||
{
|
||||
Texture& texture = spritesheet->texture;
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
const s32 index = std::clamp((s32)(self->time * count), 0, count);
|
||||
const s32 row = index / columns;
|
||||
const s32 column = index % columns;
|
||||
vec2 crop = startPosition + vec2(size.x * column, size.y * row);
|
||||
Anm2Item* item = anm2_item_from_reference(self->anm2, *self->reference);
|
||||
|
||||
vec2 textureSize = vec2(texture.size);
|
||||
vec2 uvMin = (crop + vec2(0.5f)) / textureSize;
|
||||
vec2 uvMax = (crop + size - vec2(0.5f)) / textureSize;
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
if (item) {
|
||||
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[self->reference->itemID].spritesheetID)) {
|
||||
Texture& texture = spritesheet->texture;
|
||||
|
||||
mat4 generateTransform = transform * quad_model_get(size, {}, pivot);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
|
||||
}
|
||||
const int index = std::clamp((int)(self->time * count), 0, count);
|
||||
const int row = index / columns;
|
||||
const int column = index % columns;
|
||||
vec2 crop = startPosition + vec2(size.x * column, size.y * row);
|
||||
|
||||
vec2 textureSize = vec2(texture.size);
|
||||
vec2 uvMin = (crop + vec2(0.5f)) / textureSize;
|
||||
vec2 uvMax = (crop + size - vec2(0.5f)) / textureSize;
|
||||
float vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
|
||||
mat4 generateTransform = transform * quad_model_get(size, {}, pivot);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
|
||||
}
|
||||
|
||||
canvas_unbind();
|
||||
}
|
||||
|
||||
canvas_unbind();
|
||||
}
|
||||
|
||||
void generate_preview_free(GeneratePreview* self)
|
||||
{
|
||||
canvas_free(&self->canvas);
|
||||
}
|
||||
void generate_preview_free(GeneratePreview* self) { canvas_free(&self->canvas); }
|
||||
@@ -1,23 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "anm2.h"
|
||||
#include "canvas.h"
|
||||
#include "resources.h"
|
||||
#include "settings.h"
|
||||
#include "canvas.h"
|
||||
|
||||
#define GENERATE_PREVIEW_TIME_MIN 0.0f
|
||||
#define GENERATE_PREVIEW_TIME_MAX 1.0f
|
||||
|
||||
const vec2 GENERATE_PREVIEW_SIZE = {325, 215};
|
||||
|
||||
struct GeneratePreview
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
Canvas canvas;
|
||||
f32 time{};
|
||||
struct GeneratePreview {
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
Canvas canvas;
|
||||
float time{};
|
||||
};
|
||||
|
||||
void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
|
||||
|
||||
5699
src/imgui.cpp
5699
src/imgui.cpp
File diff suppressed because it is too large
Load Diff
3293
src/imgui.h
3293
src/imgui.h
File diff suppressed because it is too large
Load Diff
88
src/main.cpp
88
src/main.cpp
@@ -1,61 +1,51 @@
|
||||
#include "main.h"
|
||||
|
||||
static bool _anm2_rescale(const std::string& file, f32 scale)
|
||||
{
|
||||
Anm2 anm2;
|
||||
static bool _anm2_rescale(const std::string& file, float scale) {
|
||||
Anm2 anm2;
|
||||
|
||||
if (!anm2_deserialize(&anm2, file, false)) return false;
|
||||
anm2_scale(&anm2, scale);
|
||||
return anm2_serialize(&anm2, file);
|
||||
if (!anm2_deserialize(&anm2, file, false))
|
||||
return false;
|
||||
anm2_scale(&anm2, scale);
|
||||
return anm2_serialize(&anm2, file);
|
||||
}
|
||||
|
||||
s32
|
||||
main(s32 argc, char* argv[])
|
||||
{
|
||||
State state;
|
||||
int main(int argc, char* argv[]) {
|
||||
State state;
|
||||
|
||||
log_init();
|
||||
log_init();
|
||||
|
||||
if (argc > 0 && argv[1])
|
||||
{
|
||||
if (std::string(argv[1]) == ARGUMENT_RESCALE)
|
||||
{
|
||||
if (argv[2] && argv[3])
|
||||
{
|
||||
if (_anm2_rescale(std::string(argv[2]), atof(argv[3])))
|
||||
{
|
||||
log_info(std::format(ARGUMENT_RESCALE_ANM2_INFO, argv[2], argv[3]));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
log_error(ARGUMENT_RESCALE_ANM2_ERROR);
|
||||
}
|
||||
else
|
||||
log_error(ARGUMENT_RESCALE_ARGUMENT_ERROR);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (std::string(argv[1]) == ARGUMENT_TEST && argv[2])
|
||||
{
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2]), false)) return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (std::string(argv[1]) == ARGUMENT_TEST_GL && argv[2])
|
||||
{
|
||||
if (!sdl_init(&state, true)) return EXIT_FAILURE;
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2]))) return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
if (argv[1]) state.argument = argv[1];
|
||||
}
|
||||
if (argc > 0 && argv[1]) {
|
||||
if (std::string(argv[1]) == ARGUMENT_RESCALE) {
|
||||
if (argv[2] && argv[3]) {
|
||||
if (_anm2_rescale(std::string(argv[2]), atof(argv[3]))) {
|
||||
log_info(std::format(ARGUMENT_RESCALE_ANM2_INFO, argv[2], argv[3]));
|
||||
return EXIT_SUCCESS;
|
||||
} else
|
||||
log_error(ARGUMENT_RESCALE_ANM2_ERROR);
|
||||
} else
|
||||
log_error(ARGUMENT_RESCALE_ARGUMENT_ERROR);
|
||||
|
||||
init(&state);
|
||||
return EXIT_FAILURE;
|
||||
} else if (std::string(argv[1]) == ARGUMENT_TEST && argv[2]) {
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2]), false))
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
} else if (std::string(argv[1]) == ARGUMENT_TEST_GL && argv[2]) {
|
||||
if (!sdl_init(&state, true))
|
||||
return EXIT_FAILURE;
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2])))
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
} else if (argv[1])
|
||||
state.argument = argv[1];
|
||||
}
|
||||
|
||||
while (state.isRunning)
|
||||
loop(&state);
|
||||
init(&state);
|
||||
|
||||
quit(&state);
|
||||
while (state.isRunning)
|
||||
loop(&state);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
quit(&state);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
471
src/preview.cpp
471
src/preview.cpp
@@ -1,282 +1,259 @@
|
||||
#include "preview.h"
|
||||
|
||||
static void _preview_render_textures_free(Preview* self)
|
||||
{
|
||||
for (auto& texture : self->renderFrames)
|
||||
texture_free(&texture);
|
||||
static void _preview_render_textures_free(Preview* self) {
|
||||
for (auto& texture : self->renderFrames)
|
||||
texture_free(&texture);
|
||||
|
||||
self->renderFrames.clear();
|
||||
self->renderFrames.clear();
|
||||
}
|
||||
|
||||
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings) {
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas, vec2());
|
||||
canvas_init(&self->canvas, vec2());
|
||||
}
|
||||
|
||||
void preview_tick(Preview* self)
|
||||
{
|
||||
f32& time = self->time;
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
|
||||
void preview_tick(Preview* self) {
|
||||
float& time = self->time;
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, *self->reference);
|
||||
|
||||
if (animation)
|
||||
{
|
||||
if (self->isPlaying)
|
||||
{
|
||||
if (self->isRender)
|
||||
{
|
||||
ivec2& size = self->canvas.size;
|
||||
u32 framebufferPixelCount = size.x * size.y * TEXTURE_CHANNELS;
|
||||
std::vector<u8> framebufferPixels(framebufferPixelCount);
|
||||
Texture frameTexture;
|
||||
if (animation) {
|
||||
if (self->isPlaying) {
|
||||
if (self->isRender) {
|
||||
ivec2& size = self->canvas.size;
|
||||
u32 framebufferPixelCount = size.x * size.y * TEXTURE_CHANNELS;
|
||||
std::vector<u8> framebufferPixels(framebufferPixelCount);
|
||||
Texture frameTexture;
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, self->canvas.fbo);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, self->canvas.fbo);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
texture_from_rgba_init(&frameTexture, size, framebufferPixels.data());
|
||||
self->renderFrames.push_back(frameTexture);
|
||||
}
|
||||
texture_from_rgba_init(&frameTexture, size, framebufferPixels.data());
|
||||
self->renderFrames.push_back(frameTexture);
|
||||
}
|
||||
|
||||
time += (f32)self->anm2->fps / TICK_DELAY;
|
||||
time += (float)self->anm2->fps / TICK_DELAY;
|
||||
|
||||
if (time >= (f32)animation->frameNum - 1)
|
||||
{
|
||||
if (self->isRender)
|
||||
{
|
||||
self->isRender = false;
|
||||
self->isRenderFinished = true;
|
||||
time = 0.0f;
|
||||
self->isPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->settings->playbackIsLoop)
|
||||
time = 0.0f;
|
||||
else
|
||||
{
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (f32)animation->frameNum - 1));
|
||||
self->isPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (time >= (float)animation->frameNum - 1) {
|
||||
if (self->isRender) {
|
||||
self->isRender = false;
|
||||
self->isRenderFinished = true;
|
||||
time = 0.0f;
|
||||
self->isPlaying = false;
|
||||
} else {
|
||||
if (self->settings->playbackIsLoop)
|
||||
time = 0.0f;
|
||||
else {
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (float)animation->frameNum - 1));
|
||||
self->isPlaying = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->settings->playbackIsClampPlayhead)
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (f32)animation->frameNum - 1));
|
||||
else
|
||||
time = std::max(time, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->settings->playbackIsClampPlayhead)
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (float)animation->frameNum - 1));
|
||||
else
|
||||
time = std::max(time, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void preview_draw(Preview* self)
|
||||
{
|
||||
ivec2& gridSize = self->settings->previewGridSize;
|
||||
ivec2& gridOffset = self->settings->previewGridOffset;
|
||||
vec4& gridColor = self->settings->previewGridColor;
|
||||
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
|
||||
GLuint& shaderAxis = self->resources->shaders[SHADER_AXIS];
|
||||
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
|
||||
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
if (self->settings->previewIsGrid)
|
||||
canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
|
||||
void preview_draw(Preview* self) {
|
||||
ivec2& gridSize = self->settings->previewGridSize;
|
||||
ivec2& gridOffset = self->settings->previewGridOffset;
|
||||
vec4& gridColor = self->settings->previewGridColor;
|
||||
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
|
||||
GLuint& shaderAxis = self->resources->shaders[SHADER_AXIS];
|
||||
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
|
||||
|
||||
if (self->settings->previewIsAxes)
|
||||
canvas_axes_draw(&self->canvas, shaderAxis, transform, self->settings->previewAxesColor);
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
auto animation_draw = [&](s32 animationID)
|
||||
{
|
||||
Anm2Animation* animation = map_find(self->anm2->animations, animationID);
|
||||
if (!animation) return;
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
|
||||
mat4 rootTransform = transform * model;
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_ROOT_COLOR;
|
||||
AtlasType atlas = self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, color);
|
||||
};
|
||||
if (self->settings->previewIsGrid)
|
||||
canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
|
||||
|
||||
auto layer_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
Anm2Item& layerAnimation = animation->layerAnimations[id];
|
||||
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) return;
|
||||
if (self->settings->previewIsAxes)
|
||||
canvas_axes_draw(&self->canvas, shaderAxis, transform, self->settings->previewAxesColor);
|
||||
|
||||
Anm2Frame frame;
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
|
||||
if (!frame.isVisible) return;
|
||||
auto animation_draw = [&](int animationID) {
|
||||
Anm2Animation* animation = map_find(self->anm2->animations, animationID);
|
||||
if (!animation)
|
||||
return;
|
||||
|
||||
mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 layerTransform = transform * (rootModel * model);
|
||||
vec3 frameColorOffset = frame.offsetRGB + colorOffset;
|
||||
vec4 frameTint = frame.tintRGBA;
|
||||
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
|
||||
|
||||
Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[id].spritesheetID);
|
||||
if (!spritesheet) return;
|
||||
|
||||
Texture& texture = spritesheet->texture;
|
||||
if (texture.isInvalid) return;
|
||||
|
||||
vec2 inset = 0.5f / vec2(texture.size);
|
||||
vec2 uvMin = frame.crop / vec2(texture.size) + inset;
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frameTint, frameColorOffset);
|
||||
|
||||
if (self->settings->previewIsBorder)
|
||||
{
|
||||
vec4 borderColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_BORDER_COLOR;
|
||||
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, borderColor);
|
||||
}
|
||||
|
||||
if (self->settings->previewIsPivots)
|
||||
{
|
||||
vec4 pivotColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_PIVOT_COLOR;
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 pivotTransform = transform * (rootModel * pivotModel);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, pivotColor);
|
||||
}
|
||||
};
|
||||
|
||||
auto null_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
Anm2Item& nullAnimation = animation->nullAnimations[id];
|
||||
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0) return;
|
||||
|
||||
Anm2Frame frame;
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, time);
|
||||
if (!frame.isVisible) return;
|
||||
|
||||
Anm2Null null = self->anm2->nulls[id];
|
||||
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) :
|
||||
(self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ?
|
||||
PREVIEW_NULL_SELECTED_COLOR : PREVIEW_NULL_COLOR;
|
||||
|
||||
vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
|
||||
AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
|
||||
|
||||
mat4 model = quad_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 nullTransform = transform * (rootModel * model);
|
||||
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
|
||||
|
||||
if (null.isShowRect)
|
||||
{
|
||||
mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 rectTransform = transform * (rootModel * rectModel);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
|
||||
}
|
||||
};
|
||||
|
||||
auto base_draw = [&](f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
Anm2Frame root;
|
||||
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, time);
|
||||
|
||||
mat4 rootModel = self->settings->previewIsRootTransform ?
|
||||
quad_model_parent_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
|
||||
|
||||
if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible)
|
||||
root_draw(root, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
for (auto id : animation->layerOrder)
|
||||
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
if (self->settings->previewIsIcons)
|
||||
for (auto& [id, _] : animation->nullAnimations)
|
||||
null_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
};
|
||||
|
||||
auto onionskin_draw = [&](s32 count, s32 direction, vec3 colorOffset)
|
||||
{
|
||||
for (s32 i = 1; i <= count; i++)
|
||||
{
|
||||
f32 time = self->time + (f32)(direction * i);
|
||||
f32 alphaOffset = (1.0f / (count + 1)) * i;
|
||||
base_draw(time, colorOffset, alphaOffset, true);
|
||||
}
|
||||
};
|
||||
|
||||
auto onionskins_draw = [&]()
|
||||
{
|
||||
if (!self->settings->onionskinIsEnabled) return;
|
||||
onionskin_draw(self->settings->onionskinBeforeCount, -1, self->settings->onionskinBeforeColorOffset);
|
||||
onionskin_draw(self->settings->onionskinAfterCount, 1, self->settings->onionskinAfterColorOffset);
|
||||
};
|
||||
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_BELOW) onionskins_draw();
|
||||
base_draw(self->time);
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_ABOVE) onionskins_draw();
|
||||
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = {}) {
|
||||
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
|
||||
mat4 rootTransform = transform * model;
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_ROOT_COLOR;
|
||||
AtlasType atlas = self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
|
||||
float vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, color);
|
||||
};
|
||||
|
||||
animation_draw(self->reference->animationID);
|
||||
animation_draw(self->animationOverlayID);
|
||||
auto layer_draw = [&](mat4 rootModel, int id, float time, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = {}) {
|
||||
Anm2Item& layerAnimation = animation->layerAnimations[id];
|
||||
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
|
||||
return;
|
||||
|
||||
canvas_unbind();
|
||||
Anm2Frame frame;
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
|
||||
if (!frame.isVisible)
|
||||
return;
|
||||
|
||||
mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 layerTransform = transform * (rootModel * model);
|
||||
vec3 frameColorOffset = frame.offsetRGB + colorOffset;
|
||||
vec4 frameTint = frame.tintRGBA;
|
||||
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
|
||||
|
||||
Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[id].spritesheetID);
|
||||
if (!spritesheet)
|
||||
return;
|
||||
|
||||
Texture& texture = spritesheet->texture;
|
||||
if (texture.isInvalid)
|
||||
return;
|
||||
|
||||
vec2 inset = 0.5f / vec2(texture.size);
|
||||
vec2 uvMin = frame.crop / vec2(texture.size) + inset;
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
||||
float vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frameTint, frameColorOffset);
|
||||
|
||||
if (self->settings->previewIsBorder) {
|
||||
vec4 borderColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_BORDER_COLOR;
|
||||
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, borderColor);
|
||||
}
|
||||
|
||||
if (self->settings->previewIsPivots) {
|
||||
vec4 pivotColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_PIVOT_COLOR;
|
||||
float vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 pivotTransform = transform * (rootModel * pivotModel);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, pivotColor);
|
||||
}
|
||||
};
|
||||
|
||||
auto null_draw = [&](mat4 rootModel, int id, float time, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = {}) {
|
||||
Anm2Item& nullAnimation = animation->nullAnimations[id];
|
||||
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
|
||||
return;
|
||||
|
||||
Anm2Frame frame;
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, time);
|
||||
if (!frame.isVisible)
|
||||
return;
|
||||
|
||||
Anm2Null null = self->anm2->nulls[id];
|
||||
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset)
|
||||
: (self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ? PREVIEW_NULL_SELECTED_COLOR
|
||||
: PREVIEW_NULL_COLOR;
|
||||
|
||||
vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
|
||||
AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
|
||||
|
||||
mat4 model = quad_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 nullTransform = transform * (rootModel * model);
|
||||
|
||||
float vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
|
||||
|
||||
if (null.isShowRect) {
|
||||
mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 rectTransform = transform * (rootModel * rectModel);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
|
||||
}
|
||||
};
|
||||
|
||||
auto base_draw = [&](float time, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = {}) {
|
||||
Anm2Frame root;
|
||||
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, time);
|
||||
|
||||
mat4 rootModel =
|
||||
self->settings->previewIsRootTransform ? quad_model_parent_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
|
||||
|
||||
if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible)
|
||||
root_draw(root, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
for (auto id : animation->layerOrder)
|
||||
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
if (self->settings->previewIsIcons)
|
||||
for (auto& [id, _] : animation->nullAnimations)
|
||||
null_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
};
|
||||
|
||||
auto onionskin_draw = [&](int count, int direction, vec3 colorOffset) {
|
||||
for (int i = 1; i <= count; i++) {
|
||||
float time = self->time + (float)(direction * i);
|
||||
float alphaOffset = (1.0f / (count + 1)) * i;
|
||||
base_draw(time, colorOffset, alphaOffset, true);
|
||||
}
|
||||
};
|
||||
|
||||
auto onionskins_draw = [&]() {
|
||||
if (!self->settings->onionskinIsEnabled)
|
||||
return;
|
||||
onionskin_draw(self->settings->onionskinBeforeCount, -1, self->settings->onionskinBeforeColorOffset);
|
||||
onionskin_draw(self->settings->onionskinAfterCount, 1, self->settings->onionskinAfterColorOffset);
|
||||
};
|
||||
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_BELOW)
|
||||
onionskins_draw();
|
||||
base_draw(self->time);
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_ABOVE)
|
||||
onionskins_draw();
|
||||
};
|
||||
|
||||
animation_draw(self->reference->animationID);
|
||||
animation_draw(self->animationOverlayID);
|
||||
|
||||
canvas_unbind();
|
||||
}
|
||||
|
||||
void preview_render_start(Preview* self)
|
||||
{
|
||||
self->isRender = true;
|
||||
self->isPlaying = true;
|
||||
self->time = 0.0f;
|
||||
_preview_render_textures_free(self);
|
||||
|
||||
self->normalCanvasSize = self->canvas.size;
|
||||
self->normalCanvasPan = self->settings->previewPan;
|
||||
self->normalCanvasZoom = self->settings->previewZoom;
|
||||
void preview_render_start(Preview* self) {
|
||||
self->isRender = true;
|
||||
self->isPlaying = true;
|
||||
self->time = 0.0f;
|
||||
_preview_render_textures_free(self);
|
||||
|
||||
if (self->settings->renderIsUseAnimationBounds)
|
||||
{
|
||||
vec4 rect = anm2_animation_rect_get(self->anm2, self->reference, self->settings->previewIsRootTransform);
|
||||
self->normalCanvasSize = self->canvas.size;
|
||||
self->normalCanvasPan = self->settings->previewPan;
|
||||
self->normalCanvasZoom = self->settings->previewZoom;
|
||||
|
||||
self->canvas.size = ivec2
|
||||
(
|
||||
ceilf(rect.z * self->settings->renderScale),
|
||||
ceilf(rect.w * self->settings->renderScale)
|
||||
);
|
||||
if (self->settings->renderIsUseAnimationBounds) {
|
||||
vec4 rect = anm2_animation_rect_get(self->anm2, *self->reference, self->settings->previewIsRootTransform);
|
||||
|
||||
vec2 rectCenter = vec2(rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f);
|
||||
self->settings->previewPan = -rectCenter * self->settings->renderScale;
|
||||
self->settings->previewZoom = UNIT_TO_PERCENT(self->settings->renderScale);
|
||||
}
|
||||
self->canvas.size = ivec2(ceilf(rect.z * self->settings->renderScale), ceilf(rect.w * self->settings->renderScale));
|
||||
|
||||
vec2 rectCenter = vec2(rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f);
|
||||
self->settings->previewPan = -rectCenter * self->settings->renderScale;
|
||||
self->settings->previewZoom = UNIT_TO_PERCENT(self->settings->renderScale);
|
||||
}
|
||||
}
|
||||
|
||||
void preview_render_end(Preview* self)
|
||||
{
|
||||
self->isRender = false;
|
||||
self->isPlaying = false;
|
||||
self->isRenderFinished = false;
|
||||
_preview_render_textures_free(self);
|
||||
|
||||
self->canvas.size = self->normalCanvasSize;
|
||||
self->settings->previewPan = self->normalCanvasPan;
|
||||
self->settings->previewZoom = self->normalCanvasZoom;
|
||||
void preview_render_end(Preview* self) {
|
||||
self->isRender = false;
|
||||
self->isPlaying = false;
|
||||
self->isRenderFinished = false;
|
||||
_preview_render_textures_free(self);
|
||||
|
||||
self->canvas.size = self->normalCanvasSize;
|
||||
self->settings->previewPan = self->normalCanvasPan;
|
||||
self->settings->previewZoom = self->normalCanvasZoom;
|
||||
}
|
||||
|
||||
void preview_free(Preview* self)
|
||||
{
|
||||
canvas_free(&self->canvas);
|
||||
}
|
||||
void preview_free(Preview* self) { canvas_free(&self->canvas); }
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "anm2.h"
|
||||
#include "canvas.h"
|
||||
#include "resources.h"
|
||||
#include "settings.h"
|
||||
#include "canvas.h"
|
||||
|
||||
const vec2 PREVIEW_SIZE = {2000, 2000};
|
||||
const vec2 PREVIEW_CANVAS_SIZE = {2000, 2000};
|
||||
@@ -26,23 +26,22 @@ const vec4 PREVIEW_NULL_COLOR = COLOR_BLUE;
|
||||
const vec4 PREVIEW_NULL_SELECTED_COLOR = COLOR_RED;
|
||||
const vec4 PREVIEW_PIVOT_COLOR = COLOR_RED;
|
||||
|
||||
struct Preview
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
s32 animationOverlayID = ID_NONE;
|
||||
Canvas canvas;
|
||||
vec2 normalCanvasSize{};
|
||||
vec2 normalCanvasPan{};
|
||||
f32 normalCanvasZoom{};
|
||||
bool isPlaying = false;
|
||||
bool isRender = false;
|
||||
bool isRenderFinished = false;
|
||||
bool isRenderCancelled = false;
|
||||
std::vector<Texture> renderFrames;
|
||||
f32 time{};
|
||||
struct Preview {
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
int animationOverlayID = ID_NONE;
|
||||
Canvas canvas;
|
||||
vec2 normalCanvasSize{};
|
||||
vec2 normalCanvasPan{};
|
||||
float normalCanvasZoom{};
|
||||
bool isPlaying = false;
|
||||
bool isRender = false;
|
||||
bool isRenderFinished = false;
|
||||
bool isRenderCancelled = false;
|
||||
std::vector<Texture> renderFrames;
|
||||
float time{};
|
||||
};
|
||||
|
||||
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#include "resources.h"
|
||||
|
||||
void resources_init(Resources* self)
|
||||
{
|
||||
texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
|
||||
|
||||
for (s32 i = 0; i < SHADER_COUNT; i++)
|
||||
shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment);
|
||||
void resources_init(Resources* self) {
|
||||
texture_from_path_init(&self->atlas, ATLAS_PATH);
|
||||
|
||||
for (int i = 0; i < SHADER_COUNT; i++)
|
||||
shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment);
|
||||
}
|
||||
|
||||
void resources_free(Resources* self)
|
||||
{
|
||||
for (auto& shader : self->shaders)
|
||||
shader_free(&shader);
|
||||
void resources_free(Resources* self) {
|
||||
for (auto& shader : self->shaders)
|
||||
shader_free(&shader);
|
||||
|
||||
texture_free(&self->atlas);
|
||||
texture_free(&self->atlas);
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "PACKED.h"
|
||||
#include "texture.h"
|
||||
#include "RESOURCE.h"
|
||||
#include "shader.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define RESOURCES_TEXTURES_FREE_INFO "Freed texture resources"
|
||||
|
||||
struct Resources
|
||||
{
|
||||
GLuint shaders[SHADER_COUNT];
|
||||
Texture atlas;
|
||||
struct Resources {
|
||||
GLuint shaders[SHADER_COUNT];
|
||||
Texture atlas;
|
||||
};
|
||||
|
||||
void resources_init(Resources* self);
|
||||
|
||||
543
src/settings.cpp
543
src/settings.cpp
@@ -1,276 +1,305 @@
|
||||
#include "settings.h"
|
||||
|
||||
static void _settings_setting_load(Settings* self, const std::string& line)
|
||||
{
|
||||
for (s32 i = 0; i < SETTINGS_COUNT; i++)
|
||||
{
|
||||
const auto& entry = SETTINGS_ENTRIES[i];
|
||||
const std::string& key = entry.key;
|
||||
void* target = (u8*)self + entry.offset;
|
||||
static void _settings_setting_load(Settings* self, const std::string& line) {
|
||||
for (int i = 0; i < SETTINGS_COUNT; i++) {
|
||||
const auto& entry = SETTINGS_ENTRIES[i];
|
||||
const std::string& key = entry.key;
|
||||
void* target = (u8*)self + entry.offset;
|
||||
|
||||
auto match_key = [&](const std::string& full) -> const char*
|
||||
{
|
||||
if (!line.starts_with(full))
|
||||
return nullptr;
|
||||
auto match_key = [&](const std::string& full) -> const char* {
|
||||
if (!line.starts_with(full))
|
||||
return nullptr;
|
||||
|
||||
size_t p = full.size();
|
||||
while (p < line.size() && std::isspace((u8)line[p])) ++p;
|
||||
if (p < line.size() && line[p] == '=')
|
||||
return line.c_str() + p + 1;
|
||||
return nullptr;
|
||||
};
|
||||
size_t p = full.size();
|
||||
while (p < line.size() && std::isspace((u8)line[p]))
|
||||
++p;
|
||||
if (p < line.size() && line[p] == '=')
|
||||
return line.c_str() + p + 1;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const char* value = nullptr;
|
||||
const char* value = nullptr;
|
||||
|
||||
switch (entry.type)
|
||||
{
|
||||
case TYPE_INT:
|
||||
if ((value = match_key(key))) { *(s32*)target = std::atoi(value); return; }
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if ((value = match_key(key))) { *(bool*)target = string_to_bool(value); return; }
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
if ((value = match_key(key))) { *(f32*)target = std::atof(value); return; }
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
if ((value = match_key(key))) { *(std::string*)target = value; return; }
|
||||
break;
|
||||
case 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; }
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH:
|
||||
{
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "W"))) { v->x = std::atoi(value); return; }
|
||||
if ((value = match_key(key + "H"))) { v->y = std::atoi(value); return; }
|
||||
break;
|
||||
};
|
||||
case 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; }
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH:
|
||||
{
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "W"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "H"))) { v->y = std::atof(value); return; }
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC3:
|
||||
{
|
||||
vec3* v = (vec3*)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; }
|
||||
break;
|
||||
}
|
||||
case 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; }
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
|
||||
}
|
||||
|
||||
std::string settings_path_get(void)
|
||||
{
|
||||
std::string filePath = preferences_path_get() + SETTINGS_PATH;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry)
|
||||
{
|
||||
u8* selfPointer = (u8*)self;
|
||||
std::string value;
|
||||
|
||||
switch (entry.type)
|
||||
{
|
||||
case TYPE_INT:
|
||||
value = std::format("{}", *(s32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
value = std::format("{}", *(bool*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
{
|
||||
const std::string data = *reinterpret_cast<const std::string*>(selfPointer + entry.offset);
|
||||
if (!data.empty())
|
||||
out << entry.key << "=" << data.c_str() << "\n";
|
||||
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_IVEC2_WH:
|
||||
{
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << data->x << "\n";
|
||||
out << entry.key << "H=" << 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_VEC2_WH:
|
||||
{
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC3:
|
||||
{
|
||||
vec3* data = (vec3*)(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";
|
||||
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:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_save(Settings* self)
|
||||
{
|
||||
const std::string path = settings_path_get();
|
||||
const std::filesystem::path filesystemPath(path);
|
||||
const std::filesystem::path directory = filesystemPath.parent_path();
|
||||
|
||||
if (!directory.empty())
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::create_directories(directory, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
log_error(std::format(SETTINGS_DIRECTORY_ERROR, directory.string(), errorCode.message()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string data;
|
||||
if (std::filesystem::exists(filesystemPath))
|
||||
{
|
||||
if (std::ifstream in(path, std::ios::binary); in)
|
||||
data.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::filesystem::path temp = filesystemPath;
|
||||
temp += SETTINGS_TEMPORARY_EXTENSION;
|
||||
|
||||
std::ofstream out(temp, std::ios::binary | std::ios::trunc);
|
||||
if (!out)
|
||||
{
|
||||
log_error(std::format(SETTINGS_INIT_ERROR, temp.string()));
|
||||
switch (entry.type) {
|
||||
case TYPE_INT:
|
||||
if ((value = match_key(key))) {
|
||||
*(int*)target = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
|
||||
out << SETTINGS_SECTION << "\n";
|
||||
for (s32 i = 0; i < SETTINGS_COUNT; i++)
|
||||
_settings_setting_write(self, out, SETTINGS_ENTRIES[i]);
|
||||
|
||||
out << "\n" << SETTINGS_SECTION_IMGUI << "\n";
|
||||
out << data;
|
||||
|
||||
out.flush();
|
||||
|
||||
if (!out.good())
|
||||
{
|
||||
log_error(std::format(SETTINGS_SAVE_ERROR, temp.string()));
|
||||
}
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if ((value = match_key(key))) {
|
||||
*(bool*)target = string_to_bool(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
if ((value = match_key(key))) {
|
||||
*(f32*)target = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
if ((value = match_key(key))) {
|
||||
*(std::string*)target = value;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH: {
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "W"))) {
|
||||
v->x = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "H"))) {
|
||||
v->y = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH: {
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "W"))) {
|
||||
v->x = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "H"))) {
|
||||
v->y = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC3: {
|
||||
vec3* v = (vec3*)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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
|
||||
}
|
||||
|
||||
std::string settings_path_get(void) {
|
||||
std::string filePath = preferences_path_get() + SETTINGS_PATH;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry) {
|
||||
u8* selfPointer = (u8*)self;
|
||||
std::string value;
|
||||
|
||||
switch (entry.type) {
|
||||
case TYPE_INT:
|
||||
value = std::format("{}", *(int*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
value = std::format("{}", *(bool*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_STRING: {
|
||||
const std::string data = *reinterpret_cast<const std::string*>(selfPointer + entry.offset);
|
||||
if (!data.empty())
|
||||
out << entry.key << "=" << data.c_str() << "\n";
|
||||
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_IVEC2_WH: {
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << data->x << "\n";
|
||||
out << entry.key << "H=" << 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_VEC2_WH: {
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC3: {
|
||||
vec3* data = (vec3*)(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";
|
||||
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:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_save(Settings* self) {
|
||||
const std::string path = settings_path_get();
|
||||
const std::filesystem::path filesystemPath(path);
|
||||
const std::filesystem::path directory = filesystemPath.parent_path();
|
||||
|
||||
if (!directory.empty()) {
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
// Windows can block rename if target exists; try remove+rename
|
||||
std::filesystem::remove(filesystemPath, errorCode);
|
||||
errorCode = {};
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
log_error(std::format(SETTINGS_SAVE_FINALIZE_ERROR, filesystemPath.string(), errorCode.message()));
|
||||
std::filesystem::remove(temp);
|
||||
return;
|
||||
}
|
||||
std::filesystem::create_directories(directory, errorCode);
|
||||
if (errorCode) {
|
||||
log_error(std::format(SETTINGS_DIRECTORY_ERROR, directory.string(), errorCode.message()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_info(std::format(SETTINGS_SAVE_INFO, path));
|
||||
std::string data;
|
||||
if (std::filesystem::exists(filesystemPath)) {
|
||||
if (std::ifstream in(path, std::ios::binary); in)
|
||||
data.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::filesystem::path temp = filesystemPath;
|
||||
temp += SETTINGS_TEMPORARY_EXTENSION;
|
||||
|
||||
std::ofstream out(temp, std::ios::binary | std::ios::trunc);
|
||||
if (!out) {
|
||||
log_error(std::format(SETTINGS_INIT_ERROR, temp.string()));
|
||||
return;
|
||||
}
|
||||
|
||||
out << SETTINGS_SECTION << "\n";
|
||||
for (int i = 0; i < SETTINGS_COUNT; i++)
|
||||
_settings_setting_write(self, out, SETTINGS_ENTRIES[i]);
|
||||
|
||||
out << "\n" << SETTINGS_SECTION_IMGUI << "\n";
|
||||
out << data;
|
||||
|
||||
out.flush();
|
||||
|
||||
if (!out.good()) {
|
||||
log_error(std::format(SETTINGS_SAVE_ERROR, temp.string()));
|
||||
return;
|
||||
}
|
||||
|
||||
out.close();
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode) {
|
||||
// Windows can block rename if target exists; try remove+rename
|
||||
std::filesystem::remove(filesystemPath, errorCode);
|
||||
errorCode = {};
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode) {
|
||||
log_error(std::format(SETTINGS_SAVE_FINALIZE_ERROR, filesystemPath.string(), errorCode.message()));
|
||||
std::filesystem::remove(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_info(std::format(SETTINGS_SAVE_INFO, path));
|
||||
}
|
||||
|
||||
void settings_init(Settings* self)
|
||||
{
|
||||
const std::string path = settings_path_get();
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
void settings_init(Settings* self) {
|
||||
const std::string path = settings_path_get();
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
|
||||
if (file)
|
||||
log_info(std::format(SETTINGS_INIT_INFO, path));
|
||||
else
|
||||
{
|
||||
log_warning(std::format(SETTINGS_INIT_WARNING, path));
|
||||
settings_save(self);
|
||||
std::ofstream out(path, std::ios::binary | std::ios::app);
|
||||
out << SETTINGS_IMGUI_DEFAULT;
|
||||
out.flush();
|
||||
out.close();
|
||||
file.open(path, std::ios::binary);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
bool inSettingsSection = false;
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
if (line == SETTINGS_SECTION)
|
||||
{
|
||||
inSettingsSection = true;
|
||||
continue;
|
||||
}
|
||||
if (line.empty()) continue;
|
||||
if (line == SETTINGS_SECTION_IMGUI) break;
|
||||
if (inSettingsSection) _settings_setting_load(self, line);
|
||||
if (file)
|
||||
log_info(std::format(SETTINGS_INIT_INFO, path));
|
||||
else {
|
||||
log_warning(std::format(SETTINGS_INIT_WARNING, path));
|
||||
settings_save(self);
|
||||
std::ofstream out(path, std::ios::binary | std::ios::app);
|
||||
out << SETTINGS_IMGUI_DEFAULT;
|
||||
out.flush();
|
||||
out.close();
|
||||
file.open(path, std::ios::binary);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
bool inSettingsSection = false;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
if (line == SETTINGS_SECTION) {
|
||||
inSettingsSection = true;
|
||||
continue;
|
||||
}
|
||||
if (line.empty())
|
||||
continue;
|
||||
if (line == SETTINGS_SECTION_IMGUI)
|
||||
break;
|
||||
if (inSettingsSection)
|
||||
_settings_setting_load(self, line);
|
||||
}
|
||||
}
|
||||
429
src/settings.h
429
src/settings.h
@@ -26,235 +26,240 @@
|
||||
#define SETTINGS_RENDER_FFMPEG_PATH_VALUE_DEFAULT "/usr/bin/ffmpeg"
|
||||
#endif
|
||||
|
||||
#define SETTINGS_LIST \
|
||||
/* name, symbol, type, defaultValue */ \
|
||||
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1600, 900}) \
|
||||
X(isVsync, IS_VSYNC, TYPE_BOOL, true) \
|
||||
\
|
||||
X(hotkeyCenterView, HOTKEY_CENTER_VIEW, TYPE_STRING, "Home") \
|
||||
X(hotkeyFit, HOTKEY_FIT, TYPE_STRING, "F") \
|
||||
X(hotkeyZoomIn, HOTKEY_ZOOM_IN, TYPE_STRING, "Ctrl++") \
|
||||
X(hotkeyZoomOut, HOTKEY_ZOOM_OUT, TYPE_STRING, "Ctrl+-") \
|
||||
X(hotkeyPlayPause, HOTKEY_PLAY_PAUSE, TYPE_STRING, "Space") \
|
||||
X(hotkeyOnionskin, HOTKEY_ONIONSKIN, TYPE_STRING, "O") \
|
||||
X(hotkeyNew, HOTKEY_NEW, TYPE_STRING, "Ctrl+N") \
|
||||
X(hotkeyOpen, HOTKEY_OPEN, TYPE_STRING, "Ctrl+O") \
|
||||
X(hotkeySave, HOTKEY_SAVE, TYPE_STRING, "Ctrl+S") \
|
||||
X(hotkeySaveAs, HOTKEY_SAVE_AS, TYPE_STRING, "Ctrl+Shift+S") \
|
||||
X(hotkeyExit, HOTKEY_EXIT, TYPE_STRING, "Alt+F4") \
|
||||
X(hotkeyPan, HOTKEY_PAN, TYPE_STRING, "P") \
|
||||
X(hotkeyMove, HOTKEY_MOVE, TYPE_STRING, "V") \
|
||||
X(hotkeyRotate, HOTKEY_ROTATE, TYPE_STRING, "R") \
|
||||
X(hotkeyScale, HOTKEY_SCALE, TYPE_STRING, "S") \
|
||||
X(hotkeyCrop, HOTKEY_CROP, TYPE_STRING, "C") \
|
||||
X(hotkeyDraw, HOTKEY_DRAW, TYPE_STRING, "B") \
|
||||
X(hotkeyErase, HOTKEY_ERASE, TYPE_STRING, "E") \
|
||||
X(hotkeyColorPicker, HOTKEY_COLOR_PICKER, TYPE_STRING, "I") \
|
||||
X(hotkeyUndo, HOTKEY_UNDO, TYPE_STRING, "Ctrl+Z") \
|
||||
X(hotkeyRedo, HOTKEY_REDO, TYPE_STRING, "Ctrl+Shift+Z") \
|
||||
X(hotkeyCopy, HOTKEY_COPY, TYPE_STRING, "Ctrl+C") \
|
||||
X(hotkeyCut, HOTKEY_CUT, TYPE_STRING, "Ctrl+X") \
|
||||
X(hotkeyPaste, HOTKEY_PASTE, TYPE_STRING, "Ctrl+V") \
|
||||
\
|
||||
X(playbackIsLoop, PLAYBACK_IS_LOOP, TYPE_BOOL, true) \
|
||||
X(playbackIsClampPlayhead,PLAYBACK_IS_CLAMP_PLAYHEAD, TYPE_BOOL, true) \
|
||||
\
|
||||
X(changeIsCrop, CHANGE_IS_CROP, TYPE_BOOL, false) \
|
||||
X(changeIsSize, CHANGE_IS_SIZE, TYPE_BOOL, false) \
|
||||
X(changeIsPosition, CHANGE_IS_POSITION, TYPE_BOOL, false) \
|
||||
X(changeIsPivot, CHANGE_IS_PIVOT, TYPE_BOOL, false) \
|
||||
X(changeIsScale, CHANGE_IS_SCALE, TYPE_BOOL, false) \
|
||||
X(changeIsRotation, CHANGE_IS_ROTATION, TYPE_BOOL, false) \
|
||||
X(changeIsDelay, CHANGE_IS_DELAY, TYPE_BOOL, false) \
|
||||
X(changeIsTint, CHANGE_IS_TINT, TYPE_BOOL, false) \
|
||||
X(changeIsColorOffset, CHANGE_IS_COLOR_OFFSET, TYPE_BOOL, false) \
|
||||
X(changeIsVisibleSet, CHANGE_IS_VISIBLE_SET, TYPE_BOOL, false) \
|
||||
X(changeIsInterpolatedSet,CHANGE_IS_INTERPOLATED_SET, TYPE_BOOL, false) \
|
||||
X(changeIsFromSelectedFrame,CHANGE_IS_FROM_SELECTED_FRAME,TYPE_BOOL, false) \
|
||||
X(changeCrop, CHANGE_CROP, TYPE_VEC2, {}) \
|
||||
X(changeSize, CHANGE_SIZE, TYPE_VEC2, {}) \
|
||||
X(changePosition, CHANGE_POSITION, TYPE_VEC2, {}) \
|
||||
X(changePivot, CHANGE_PIVOT, TYPE_VEC2, {}) \
|
||||
X(changeScale, CHANGE_SCALE, TYPE_VEC2, {}) \
|
||||
X(changeRotation, CHANGE_ROTATION, TYPE_FLOAT, 0.0f) \
|
||||
X(changeDelay, CHANGE_DELAY, TYPE_INT, 0) \
|
||||
X(changeTint, CHANGE_TINT, TYPE_VEC4, {}) \
|
||||
X(changeColorOffset, CHANGE_COLOR_OFFSET, TYPE_VEC3, {}) \
|
||||
X(changeIsVisible, CHANGE_IS_VISIBLE, TYPE_BOOL, false) \
|
||||
X(changeIsInterpolated, CHANGE_IS_INTERPOLATED, TYPE_BOOL, false) \
|
||||
X(changeNumberFrames, CHANGE_NUMBER_FRAMES, TYPE_INT, 1) \
|
||||
\
|
||||
X(scaleValue, SCALE_VALUE, TYPE_FLOAT, 1.0f) \
|
||||
\
|
||||
X(previewIsAxes, PREVIEW_IS_AXES, TYPE_BOOL, true) \
|
||||
X(previewIsGrid, PREVIEW_IS_GRID, TYPE_BOOL, true) \
|
||||
X(previewIsRootTransform, PREVIEW_IS_ROOT_TRANSFORM, TYPE_BOOL, true) \
|
||||
X(previewIsTriggers, PREVIEW_IS_TRIGGERS, TYPE_BOOL, true) \
|
||||
X(previewIsPivots, PREVIEW_IS_PIVOTS, TYPE_BOOL, false) \
|
||||
X(previewIsIcons, PREVIEW_IS_ICONS, TYPE_BOOL, true) \
|
||||
X(previewIsBorder, PREVIEW_IS_BORDER, TYPE_BOOL, false) \
|
||||
X(previewIsAltIcons, PREVIEW_IS_ALT_ICONS, TYPE_BOOL, false) \
|
||||
X(previewOverlayTransparency,PREVIEW_OVERLAY_TRANSPARENCY,TYPE_FLOAT, 255.0f) \
|
||||
X(previewZoom, PREVIEW_ZOOM, TYPE_FLOAT, 200.0f) \
|
||||
X(previewPan, PREVIEW_PAN, TYPE_VEC2, {}) \
|
||||
X(previewGridSize, PREVIEW_GRID_SIZE, TYPE_IVEC2, {32,32}) \
|
||||
X(previewGridOffset, PREVIEW_GRID_OFFSET, TYPE_IVEC2, {}) \
|
||||
X(previewGridColor, PREVIEW_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(previewAxesColor, PREVIEW_AXES_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(previewBackgroundColor, PREVIEW_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
|
||||
\
|
||||
X(propertiesIsRound, PROPERTIES_IS_ROUND, TYPE_BOOL, false) \
|
||||
\
|
||||
X(generateStartPosition, GENERATE_START_POSITION, TYPE_IVEC2, {}) \
|
||||
X(generateSize, GENERATE_SIZE, TYPE_IVEC2, {64,64}) \
|
||||
X(generatePivot, GENERATE_PIVOT, TYPE_IVEC2, {32,32}) \
|
||||
X(generateRows, GENERATE_ROWS, TYPE_INT, 4) \
|
||||
X(generateColumns, GENERATE_COLUMNS, TYPE_INT, 4) \
|
||||
X(generateCount, GENERATE_COUNT, TYPE_INT, 16) \
|
||||
X(generateDelay, GENERATE_DELAY, TYPE_INT, 1) \
|
||||
\
|
||||
X(editorIsGrid, EDITOR_IS_GRID, TYPE_BOOL, true) \
|
||||
X(editorIsGridSnap, EDITOR_IS_GRID_SNAP, TYPE_BOOL, true) \
|
||||
X(editorIsBorder, EDITOR_IS_BORDER, TYPE_BOOL, true) \
|
||||
X(editorZoom, EDITOR_ZOOM, TYPE_FLOAT, 200.0f) \
|
||||
X(editorPan, EDITOR_PAN, TYPE_VEC2, {0.0,0.0}) \
|
||||
X(editorGridSize, EDITOR_GRID_SIZE, TYPE_IVEC2, {32,32}) \
|
||||
X(editorGridOffset, EDITOR_GRID_OFFSET, TYPE_IVEC2, {32,32}) \
|
||||
X(editorGridColor, EDITOR_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(editorBackgroundColor, EDITOR_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
|
||||
\
|
||||
X(mergeType, MERGE_TYPE, TYPE_INT, ANM2_MERGE_APPEND) \
|
||||
X(mergeIsDeleteAnimationsAfter,MERGE_IS_DELETE_ANIMATIONS_AFTER,TYPE_BOOL, false) \
|
||||
\
|
||||
X(bakeInterval, BAKE_INTERVAL, TYPE_INT, 1) \
|
||||
X(bakeIsRoundScale, BAKE_IS_ROUND_SCALE, TYPE_BOOL, true) \
|
||||
X(bakeIsRoundRotation, BAKE_IS_ROUND_ROTATION, TYPE_BOOL, true) \
|
||||
\
|
||||
X(timelineAddItemType, TIMELINE_ADD_ITEM_TYPE, TYPE_INT, ANM2_LAYER) \
|
||||
X(timelineIsShowUnused, TIMELINE_IS_SHOW_UNUSED, TYPE_BOOL, true) \
|
||||
\
|
||||
X(onionskinIsEnabled, ONIONSKIN_IS_ENABLED, TYPE_BOOL, false) \
|
||||
X(onionskinDrawOrder, ONIONSKIN_DRAW_ORDER, TYPE_INT, ONIONSKIN_BELOW) \
|
||||
X(onionskinBeforeCount, ONIONSKIN_BEFORE_COUNT, TYPE_INT, 0) \
|
||||
X(onionskinAfterCount, ONIONSKIN_AFTER_COUNT, TYPE_INT, 0) \
|
||||
X(onionskinBeforeColorOffset,ONIONSKIN_BEFORE_COLOR_OFFSET,TYPE_VEC3, COLOR_RED) \
|
||||
X(onionskinAfterColorOffset, ONIONSKIN_AFTER_COLOR_OFFSET,TYPE_VEC3, COLOR_BLUE) \
|
||||
\
|
||||
X(tool, TOOL, TYPE_INT, TOOL_PAN) \
|
||||
X(toolColor, TOOL_COLOR, TYPE_VEC4, {1.0,1.0,1.0,1.0}) \
|
||||
\
|
||||
X(renderType, RENDER_TYPE, TYPE_INT, RENDER_PNG) \
|
||||
X(renderPath, RENDER_PATH, TYPE_STRING, ".") \
|
||||
X(renderFormat, RENDER_FORMAT, TYPE_STRING, "{}.png") \
|
||||
X(renderIsUseAnimationBounds,RENDER_IS_USE_ANIMATION_BOUNDS,TYPE_BOOL, true) \
|
||||
X(renderIsTransparent, RENDER_IS_TRANSPARENT, TYPE_BOOL, true) \
|
||||
X(renderScale, RENDER_SCALE, TYPE_FLOAT, 1.0f) \
|
||||
X(renderFFmpegPath, RENDER_FFMPEG_PATH, TYPE_STRING, SETTINGS_RENDER_FFMPEG_PATH_VALUE_DEFAULT)
|
||||
#define SETTINGS_LIST \
|
||||
/* Symbol / Name / Type / Default */ \
|
||||
X(WINDOW_SIZE, windowSize, TYPE_IVEC2_WH, {1600, 900}) \
|
||||
X(IS_VSYNC, isVsync, TYPE_BOOL, true) \
|
||||
X(DISPLAY_SCALE, displayScale, TYPE_FLOAT, 1.0f) \
|
||||
\
|
||||
X(HOTKEY_CENTER_VIEW, hotkeyCenterView, TYPE_STRING, "Home") \
|
||||
X(HOTKEY_FIT, hotkeyFit, TYPE_STRING, "F") \
|
||||
X(HOTKEY_ZOOM_IN, hotkeyZoomIn, TYPE_STRING, "Ctrl++") \
|
||||
X(HOTKEY_ZOOM_OUT, hotkeyZoomOut, TYPE_STRING, "Ctrl+-") \
|
||||
X(HOTKEY_PLAY_PAUSE, hotkeyPlayPause, TYPE_STRING, "Space") \
|
||||
X(HOTKEY_ONIONSKIN, hotkeyOnionskin, TYPE_STRING, "O") \
|
||||
X(HOTKEY_NEW, hotkeyNew, TYPE_STRING, "Ctrl+N") \
|
||||
X(HOTKEY_OPEN, hotkeyOpen, TYPE_STRING, "Ctrl+O") \
|
||||
X(HOTKEY_SAVE, hotkeySave, TYPE_STRING, "Ctrl+S") \
|
||||
X(HOTKEY_SAVE_AS, hotkeySaveAs, TYPE_STRING, "Ctrl+Shift+S") \
|
||||
X(HOTKEY_EXIT, hotkeyExit, TYPE_STRING, "Alt+F4") \
|
||||
X(HOTKEY_SHORTEN_FRAME, hotkeyShortenFrame, TYPE_STRING, "F4") \
|
||||
X(HOTKEY_EXTEND_FRAME, hotkeyExtendFrame, TYPE_STRING, "F5") \
|
||||
X(HOTKEY_INSERT_FRAME, hotkeyInsertFrame, TYPE_STRING, "F6") \
|
||||
X(HOTKEY_PREVIOUS_FRAME, hotkeyPreviousFrame, TYPE_STRING, "Comma") \
|
||||
X(HOTKEY_NEXT_FRAME, hotkeyNextFrame, TYPE_STRING, "Period") \
|
||||
X(HOTKEY_PAN, hotkeyPan, TYPE_STRING, "P") \
|
||||
X(HOTKEY_MOVE, hotkeyMove, TYPE_STRING, "V") \
|
||||
X(HOTKEY_ROTATE, hotkeyRotate, TYPE_STRING, "R") \
|
||||
X(HOTKEY_SCALE, hotkeyScale, TYPE_STRING, "S") \
|
||||
X(HOTKEY_CROP, hotkeyCrop, TYPE_STRING, "C") \
|
||||
X(HOTKEY_DRAW, hotkeyDraw, TYPE_STRING, "B") \
|
||||
X(HOTKEY_ERASE, hotkeyErase, TYPE_STRING, "E") \
|
||||
X(HOTKEY_COLOR_PICKER, hotkeyColorPicker, TYPE_STRING, "I") \
|
||||
X(HOTKEY_UNDO, hotkeyUndo, TYPE_STRING, "Ctrl+Z") \
|
||||
X(HOTKEY_REDO, hotkeyRedo, TYPE_STRING, "Ctrl+Shift+Z") \
|
||||
X(HOTKEY_COPY, hotkeyCopy, TYPE_STRING, "Ctrl+C") \
|
||||
X(HOTKEY_CUT, hotkeyCut, TYPE_STRING, "Ctrl+X") \
|
||||
X(HOTKEY_PASTE, hotkeyPaste, TYPE_STRING, "Ctrl+V") \
|
||||
\
|
||||
X(PLAYBACK_IS_LOOP, playbackIsLoop, TYPE_BOOL, true) \
|
||||
X(PLAYBACK_IS_CLAMP_PLAYHEAD, playbackIsClampPlayhead, TYPE_BOOL, true) \
|
||||
\
|
||||
X(CHANGE_IS_CROP, changeIsCrop, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_SIZE, changeIsSize, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_POSITION, changeIsPosition, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_PIVOT, changeIsPivot, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_SCALE, changeIsScale, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_ROTATION, changeIsRotation, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_DELAY, changeIsDelay, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_TINT, changeIsTint, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_COLOR_OFFSET, changeIsColorOffset, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_VISIBLE_SET, changeIsVisibleSet, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_INTERPOLATED_SET, changeIsInterpolatedSet, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_FROM_SELECTED_FRAME, changeIsFromSelectedFrame, TYPE_BOOL, false) \
|
||||
X(CHANGE_CROP, changeCrop, TYPE_VEC2, {}) \
|
||||
X(CHANGE_SIZE, changeSize, TYPE_VEC2, {}) \
|
||||
X(CHANGE_POSITION, changePosition, TYPE_VEC2, {}) \
|
||||
X(CHANGE_PIVOT, changePivot, TYPE_VEC2, {}) \
|
||||
X(CHANGE_SCALE, changeScale, TYPE_VEC2, {}) \
|
||||
X(CHANGE_ROTATION, changeRotation, TYPE_FLOAT, 0.0f) \
|
||||
X(CHANGE_DELAY, changeDelay, TYPE_INT, 0) \
|
||||
X(CHANGE_TINT, changeTint, TYPE_VEC4, {}) \
|
||||
X(CHANGE_COLOR_OFFSET, changeColorOffset, TYPE_VEC3, {}) \
|
||||
X(CHANGE_IS_VISIBLE, changeIsVisible, TYPE_BOOL, false) \
|
||||
X(CHANGE_IS_INTERPOLATED, changeIsInterpolated, TYPE_BOOL, false) \
|
||||
X(CHANGE_NUMBER_FRAMES, changeNumberFrames, TYPE_INT, 1) \
|
||||
\
|
||||
X(SCALE_VALUE, scaleValue, TYPE_FLOAT, 1.0f) \
|
||||
\
|
||||
X(PREVIEW_IS_AXES, previewIsAxes, TYPE_BOOL, true) \
|
||||
X(PREVIEW_IS_GRID, previewIsGrid, TYPE_BOOL, true) \
|
||||
X(PREVIEW_IS_ROOT_TRANSFORM, previewIsRootTransform, TYPE_BOOL, true) \
|
||||
X(PREVIEW_IS_TRIGGERS, previewIsTriggers, TYPE_BOOL, true) \
|
||||
X(PREVIEW_IS_PIVOTS, previewIsPivots, TYPE_BOOL, false) \
|
||||
X(PREVIEW_IS_ICONS, previewIsIcons, TYPE_BOOL, true) \
|
||||
X(PREVIEW_IS_BORDER, previewIsBorder, TYPE_BOOL, false) \
|
||||
X(PREVIEW_IS_ALT_ICONS, previewIsAltIcons, TYPE_BOOL, false) \
|
||||
X(PREVIEW_OVERLAY_TRANSPARENCY, previewOverlayTransparency, TYPE_FLOAT, 255.0f) \
|
||||
X(PREVIEW_ZOOM, previewZoom, TYPE_FLOAT, 200.0f) \
|
||||
X(PREVIEW_PAN, previewPan, TYPE_VEC2, {}) \
|
||||
X(PREVIEW_GRID_SIZE, previewGridSize, TYPE_IVEC2, {32, 32}) \
|
||||
X(PREVIEW_GRID_OFFSET, previewGridOffset, TYPE_IVEC2, {}) \
|
||||
X(PREVIEW_GRID_COLOR, previewGridColor, TYPE_VEC4, {1.0, 1.0, 1.0, 0.125}) \
|
||||
X(PREVIEW_AXES_COLOR, previewAxesColor, TYPE_VEC4, {1.0, 1.0, 1.0, 0.125}) \
|
||||
X(PREVIEW_BACKGROUND_COLOR, previewBackgroundColor, TYPE_VEC4, {0.113, 0.184, 0.286, 1.0}) \
|
||||
\
|
||||
X(PROPERTIES_IS_ROUND, propertiesIsRound, TYPE_BOOL, false) \
|
||||
\
|
||||
X(GENERATE_START_POSITION, generateStartPosition, TYPE_IVEC2, {}) \
|
||||
X(GENERATE_SIZE, generateSize, TYPE_IVEC2, {64, 64}) \
|
||||
X(GENERATE_PIVOT, generatePivot, TYPE_IVEC2, {32, 32}) \
|
||||
X(GENERATE_ROWS, generateRows, TYPE_INT, 4) \
|
||||
X(GENERATE_COLUMNS, generateColumns, TYPE_INT, 4) \
|
||||
X(GENERATE_COUNT, generateCount, TYPE_INT, 16) \
|
||||
X(GENERATE_DELAY, generateDelay, TYPE_INT, 1) \
|
||||
\
|
||||
X(EDITOR_IS_GRID, editorIsGrid, TYPE_BOOL, true) \
|
||||
X(EDITOR_IS_GRID_SNAP, editorIsGridSnap, TYPE_BOOL, true) \
|
||||
X(EDITOR_IS_BORDER, editorIsBorder, TYPE_BOOL, true) \
|
||||
X(EDITOR_ZOOM, editorZoom, TYPE_FLOAT, 200.0f) \
|
||||
X(EDITOR_PAN, editorPan, TYPE_VEC2, {0.0, 0.0}) \
|
||||
X(EDITOR_GRID_SIZE, editorGridSize, TYPE_IVEC2, {32, 32}) \
|
||||
X(EDITOR_GRID_OFFSET, editorGridOffset, TYPE_IVEC2, {32, 32}) \
|
||||
X(EDITOR_GRID_COLOR, editorGridColor, TYPE_VEC4, {1.0, 1.0, 1.0, 0.125}) \
|
||||
X(EDITOR_BACKGROUND_COLOR, editorBackgroundColor, TYPE_VEC4, {0.113, 0.184, 0.286, 1.0}) \
|
||||
\
|
||||
X(MERGE_TYPE, mergeType, TYPE_INT, ANM2_MERGE_APPEND) \
|
||||
X(MERGE_IS_DELETE_ANIMATIONS_AFTER, mergeIsDeleteAnimationsAfter, TYPE_BOOL, false) \
|
||||
\
|
||||
X(BAKE_INTERVAL, bakeInterval, TYPE_INT, 1) \
|
||||
X(BAKE_IS_ROUND_SCALE, bakeIsRoundScale, TYPE_BOOL, true) \
|
||||
X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, TYPE_BOOL, true) \
|
||||
\
|
||||
X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, TYPE_INT, ANM2_LAYER) \
|
||||
X(TIMELINE_IS_SHOW_UNUSED, timelineIsShowUnused, TYPE_BOOL, true) \
|
||||
\
|
||||
X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, TYPE_BOOL, false) \
|
||||
X(ONIONSKIN_DRAW_ORDER, onionskinDrawOrder, TYPE_INT, ONIONSKIN_BELOW) \
|
||||
X(ONIONSKIN_BEFORE_COUNT, onionskinBeforeCount, TYPE_INT, 0) \
|
||||
X(ONIONSKIN_AFTER_COUNT, onionskinAfterCount, TYPE_INT, 0) \
|
||||
X(ONIONSKIN_BEFORE_COLOR_OFFSET, onionskinBeforeColorOffset, TYPE_VEC3, COLOR_RED) \
|
||||
X(ONIONSKIN_AFTER_COLOR_OFFSET, onionskinAfterColorOffset, TYPE_VEC3, COLOR_BLUE) \
|
||||
\
|
||||
X(TOOL, tool, TYPE_INT, TOOL_PAN) \
|
||||
X(TOOL_COLOR, toolColor, TYPE_VEC4, {1.0, 1.0, 1.0, 1.0}) \
|
||||
\
|
||||
X(RENDER_TYPE, renderType, TYPE_INT, RENDER_PNG) \
|
||||
X(RENDER_PATH, renderPath, TYPE_STRING, ".") \
|
||||
X(RENDER_FORMAT, renderFormat, TYPE_STRING, "{}.png") \
|
||||
X(RENDER_IS_USE_ANIMATION_BOUNDS, renderIsUseAnimationBounds, TYPE_BOOL, true) \
|
||||
X(RENDER_IS_TRANSPARENT, renderIsTransparent, TYPE_BOOL, true) \
|
||||
X(RENDER_SCALE, renderScale, TYPE_FLOAT, 1.0f) \
|
||||
X(RENDER_FFMPEG_PATH, renderFFmpegPath, TYPE_STRING, SETTINGS_RENDER_FFMPEG_PATH_VALUE_DEFAULT)
|
||||
|
||||
#define X(name, symbol, type, ...) \
|
||||
const inline DATATYPE_TO_CTYPE(type) SETTINGS_##symbol##_DEFAULT = __VA_ARGS__;
|
||||
#define X(symbol, name, type, ...) const inline DATATYPE_TO_CTYPE(type) SETTINGS_##symbol##_DEFAULT = __VA_ARGS__;
|
||||
SETTINGS_LIST
|
||||
#undef X
|
||||
|
||||
struct Settings
|
||||
{
|
||||
#define X(name, symbol, type, ...) \
|
||||
DATATYPE_TO_CTYPE(type) name = SETTINGS_##symbol##_DEFAULT;
|
||||
struct Settings {
|
||||
#define X(symbol, name, type, ...) DATATYPE_TO_CTYPE(type) name = SETTINGS_##symbol##_DEFAULT;
|
||||
SETTINGS_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct SettingsEntry {
|
||||
std::string key;
|
||||
DataType type;
|
||||
int offset;
|
||||
};
|
||||
|
||||
const inline SettingsEntry SETTINGS_ENTRIES[] = {
|
||||
#define X(symbol, name, type, ...) {#name, type, offsetof(Settings, name)},
|
||||
SETTINGS_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct SettingsEntry
|
||||
{
|
||||
std::string key;
|
||||
DataType type;
|
||||
s32 offset;
|
||||
#undef X
|
||||
};
|
||||
|
||||
const inline SettingsEntry SETTINGS_ENTRIES[] =
|
||||
{
|
||||
#define X(name, symbol, type, ...) \
|
||||
{ #name, type, offsetof(Settings, name) },
|
||||
SETTINGS_LIST
|
||||
#undef X
|
||||
};
|
||||
constexpr int SETTINGS_COUNT = (int)std::size(SETTINGS_ENTRIES);
|
||||
|
||||
constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);
|
||||
#define HOTKEY_LIST \
|
||||
X(NONE, "None") \
|
||||
X(CENTER_VIEW, "Center View") \
|
||||
X(FIT, "Fit") \
|
||||
X(ZOOM_IN, "Zoom In") \
|
||||
X(ZOOM_OUT, "Zoom Out") \
|
||||
X(PLAY_PAUSE, "Play/Pause") \
|
||||
X(ONIONSKIN, "Onionskin") \
|
||||
X(NEW, "New") \
|
||||
X(OPEN, "Open") \
|
||||
X(SAVE, "Save") \
|
||||
X(SAVE_AS, "Save As") \
|
||||
X(EXIT, "Exit") \
|
||||
X(SHORTEN_FRAME, "Shorten Frame") \
|
||||
X(EXTEND_FRAME, "Extend Frame") \
|
||||
X(INSERT_FRAME, "Insert Frame") \
|
||||
X(PREVIOUS_FRAME, "Previous Frame") \
|
||||
X(NEXT_FRAME, "Next Frame") \
|
||||
X(PAN, "Pan") \
|
||||
X(MOVE, "Move") \
|
||||
X(ROTATE, "Rotate") \
|
||||
X(SCALE, "Scale") \
|
||||
X(CROP, "Crop") \
|
||||
X(DRAW, "Draw") \
|
||||
X(ERASE, "Erase") \
|
||||
X(COLOR_PICKER, "Color Picker") \
|
||||
X(UNDO, "Undo") \
|
||||
X(REDO, "Redo") \
|
||||
X(COPY, "Copy") \
|
||||
X(CUT, "Cut") \
|
||||
X(PASTE, "Paste")
|
||||
|
||||
#define HOTKEY_LIST \
|
||||
X(NONE, "None") \
|
||||
X(CENTER_VIEW, "Center View") \
|
||||
X(FIT, "Fit") \
|
||||
X(ZOOM_IN, "Zoom In") \
|
||||
X(ZOOM_OUT, "Zoom Out") \
|
||||
X(PLAY_PAUSE, "Play/Pause") \
|
||||
X(ONIONSKIN, "Onionskin") \
|
||||
X(NEW, "New") \
|
||||
X(OPEN, "Open") \
|
||||
X(SAVE, "Save") \
|
||||
X(SAVE_AS, "Save As") \
|
||||
X(EXIT, "Exit") \
|
||||
X(PAN, "Pan") \
|
||||
X(MOVE, "Move") \
|
||||
X(ROTATE, "Rotate") \
|
||||
X(SCALE, "Scale") \
|
||||
X(CROP, "Crop") \
|
||||
X(DRAW, "Draw") \
|
||||
X(ERASE, "Erase") \
|
||||
X(COLOR_PICKER, "Color Picker") \
|
||||
X(UNDO, "Undo") \
|
||||
X(REDO, "Redo") \
|
||||
X(COPY, "Copy") \
|
||||
X(CUT, "Cut") \
|
||||
X(PASTE, "Paste") \
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define X(name, str) HOTKEY_##name,
|
||||
HOTKEY_LIST
|
||||
#undef X
|
||||
HOTKEY_COUNT
|
||||
typedef enum {
|
||||
#define X(name, str) HOTKEY_##name,
|
||||
HOTKEY_LIST
|
||||
#undef X
|
||||
HOTKEY_COUNT
|
||||
} HotkeyType;
|
||||
|
||||
const inline char* HOTKEY_STRINGS[] =
|
||||
{
|
||||
#define X(name, str) str,
|
||||
const inline char* HOTKEY_STRINGS[] = {
|
||||
#define X(name, str) str,
|
||||
HOTKEY_LIST
|
||||
#undef X
|
||||
#undef X
|
||||
};
|
||||
|
||||
using HotkeyMember = std::string Settings::*;
|
||||
|
||||
const inline HotkeyMember SETTINGS_HOTKEY_MEMBERS[HOTKEY_COUNT] =
|
||||
{
|
||||
nullptr,
|
||||
&Settings::hotkeyCenterView,
|
||||
&Settings::hotkeyFit,
|
||||
&Settings::hotkeyZoomIn,
|
||||
&Settings::hotkeyZoomOut,
|
||||
&Settings::hotkeyPlayPause,
|
||||
&Settings::hotkeyOnionskin,
|
||||
&Settings::hotkeyNew,
|
||||
&Settings::hotkeyOpen,
|
||||
&Settings::hotkeySave,
|
||||
&Settings::hotkeySaveAs,
|
||||
&Settings::hotkeyExit,
|
||||
&Settings::hotkeyPan,
|
||||
&Settings::hotkeyMove,
|
||||
&Settings::hotkeyRotate,
|
||||
&Settings::hotkeyScale,
|
||||
&Settings::hotkeyCrop,
|
||||
&Settings::hotkeyDraw,
|
||||
&Settings::hotkeyErase,
|
||||
&Settings::hotkeyColorPicker,
|
||||
&Settings::hotkeyUndo,
|
||||
&Settings::hotkeyRedo,
|
||||
&Settings::hotkeyCopy,
|
||||
&Settings::hotkeyCut,
|
||||
&Settings::hotkeyPaste
|
||||
};
|
||||
const inline HotkeyMember SETTINGS_HOTKEY_MEMBERS[HOTKEY_COUNT] = {nullptr,
|
||||
&Settings::hotkeyCenterView,
|
||||
&Settings::hotkeyFit,
|
||||
&Settings::hotkeyZoomIn,
|
||||
&Settings::hotkeyZoomOut,
|
||||
&Settings::hotkeyPlayPause,
|
||||
&Settings::hotkeyOnionskin,
|
||||
&Settings::hotkeyNew,
|
||||
&Settings::hotkeyOpen,
|
||||
&Settings::hotkeySave,
|
||||
&Settings::hotkeySaveAs,
|
||||
&Settings::hotkeyExit,
|
||||
&Settings::hotkeyShortenFrame,
|
||||
&Settings::hotkeyExtendFrame,
|
||||
&Settings::hotkeyInsertFrame,
|
||||
&Settings::hotkeyPreviousFrame,
|
||||
&Settings::hotkeyNextFrame,
|
||||
&Settings::hotkeyPan,
|
||||
&Settings::hotkeyMove,
|
||||
&Settings::hotkeyRotate,
|
||||
&Settings::hotkeyScale,
|
||||
&Settings::hotkeyCrop,
|
||||
&Settings::hotkeyDraw,
|
||||
&Settings::hotkeyErase,
|
||||
&Settings::hotkeyColorPicker,
|
||||
&Settings::hotkeyUndo,
|
||||
&Settings::hotkeyRedo,
|
||||
&Settings::hotkeyCopy,
|
||||
&Settings::hotkeyCut,
|
||||
&Settings::hotkeyPaste};
|
||||
|
||||
const std::string SETTINGS_IMGUI_DEFAULT = R"(
|
||||
const inline std::string SETTINGS_IMGUI_DEFAULT = R"(
|
||||
# Dear ImGui
|
||||
[Window][## Window]
|
||||
Pos=0,32
|
||||
|
||||
@@ -1,53 +1,49 @@
|
||||
#include "shader.h"
|
||||
|
||||
static bool _shader_compile(GLuint* self, const std::string& text)
|
||||
{
|
||||
s32 isCompile;
|
||||
const GLchar* source = text.c_str();
|
||||
static bool _shader_compile(GLuint* self, const std::string& text) {
|
||||
int isCompile;
|
||||
const GLchar* source = text.c_str();
|
||||
|
||||
glShaderSource(*self, 1, &source, nullptr);
|
||||
glCompileShader(*self);
|
||||
glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile);
|
||||
glShaderSource(*self, 1, &source, nullptr);
|
||||
glCompileShader(*self);
|
||||
glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile);
|
||||
|
||||
if (!isCompile)
|
||||
{
|
||||
std::string compileLog(SHADER_INFO_LOG_MAX, '\0');
|
||||
glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, nullptr, compileLog.data());
|
||||
log_error(std::format(SHADER_INIT_ERROR, *self, compileLog.c_str()));
|
||||
return false;
|
||||
}
|
||||
if (!isCompile) {
|
||||
std::string compileLog(SHADER_INFO_LOG_MAX, '\0');
|
||||
glGetShaderInfoLog(*self, SHADER_INFO_LOG_MAX, nullptr, compileLog.data());
|
||||
log_error(std::format(SHADER_INIT_ERROR, *self, compileLog.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initializes a given shader with vertex/fragment
|
||||
bool shader_init(GLuint* self, const std::string& vertex, const std::string& fragment)
|
||||
{
|
||||
GLuint vertexHandle;
|
||||
GLuint fragmentHandle;
|
||||
bool shader_init(GLuint* self, const std::string& vertex, const std::string& fragment) {
|
||||
GLuint vertexHandle;
|
||||
GLuint fragmentHandle;
|
||||
|
||||
vertexHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
vertexHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
if (!_shader_compile(&vertexHandle, vertex) || !_shader_compile(&fragmentHandle, fragment))
|
||||
return false;
|
||||
if (!_shader_compile(&vertexHandle, vertex) || !_shader_compile(&fragmentHandle, fragment))
|
||||
return false;
|
||||
|
||||
*self = glCreateProgram();
|
||||
*self = glCreateProgram();
|
||||
|
||||
glAttachShader(*self, vertexHandle);
|
||||
glAttachShader(*self, fragmentHandle);
|
||||
glAttachShader(*self, vertexHandle);
|
||||
glAttachShader(*self, fragmentHandle);
|
||||
|
||||
glLinkProgram(*self);
|
||||
glLinkProgram(*self);
|
||||
|
||||
glDeleteShader(vertexHandle);
|
||||
glDeleteShader(fragmentHandle);
|
||||
glDeleteShader(vertexHandle);
|
||||
glDeleteShader(fragmentHandle);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void shader_free(GLuint* self)
|
||||
{
|
||||
if (*self == GL_ID_NONE) return;
|
||||
|
||||
glDeleteProgram(*self);
|
||||
void shader_free(GLuint* self) {
|
||||
if (*self == GL_ID_NONE)
|
||||
return;
|
||||
|
||||
glDeleteProgram(*self);
|
||||
}
|
||||
@@ -1,77 +1,67 @@
|
||||
#include "snapshots.h"
|
||||
|
||||
static void _snapshot_stack_push(SnapshotStack* stack, Snapshot* snapshot)
|
||||
{
|
||||
if (stack->top >= SNAPSHOT_STACK_MAX)
|
||||
{
|
||||
for (s32 i = 0; i < SNAPSHOT_STACK_MAX - 1; i++)
|
||||
stack->snapshots[i] = stack->snapshots[i + 1];
|
||||
stack->top = SNAPSHOT_STACK_MAX - 1;
|
||||
}
|
||||
stack->snapshots[stack->top++] = *snapshot;
|
||||
static void _snapshot_stack_push(SnapshotStack* stack, Snapshot* snapshot) {
|
||||
if (stack->top >= SNAPSHOT_STACK_MAX) {
|
||||
for (int i = 0; i < SNAPSHOT_STACK_MAX - 1; i++)
|
||||
stack->snapshots[i] = stack->snapshots[i + 1];
|
||||
stack->top = SNAPSHOT_STACK_MAX - 1;
|
||||
}
|
||||
stack->snapshots[stack->top++] = *snapshot;
|
||||
}
|
||||
|
||||
static Snapshot* _snapshot_stack_pop(SnapshotStack* stack)
|
||||
{
|
||||
if (stack->top == 0) return nullptr;
|
||||
return &stack->snapshots[--stack->top];
|
||||
static Snapshot* _snapshot_stack_pop(SnapshotStack* stack) {
|
||||
if (stack->top == 0)
|
||||
return nullptr;
|
||||
return &stack->snapshots[--stack->top];
|
||||
}
|
||||
|
||||
static void _snapshot_set(Snapshots* self, Snapshot* snapshot)
|
||||
{
|
||||
if (!snapshot) return;
|
||||
static void _snapshot_set(Snapshots* self, Snapshot* snapshot) {
|
||||
if (!snapshot)
|
||||
return;
|
||||
|
||||
*self->anm2 = snapshot->anm2;
|
||||
*self->reference = snapshot->reference;
|
||||
self->preview->time = snapshot->time;
|
||||
self->action = snapshot->action;
|
||||
*self->anm2 = snapshot->anm2;
|
||||
*self->reference = snapshot->reference;
|
||||
self->preview->time = snapshot->time;
|
||||
self->action = snapshot->action;
|
||||
|
||||
anm2_spritesheet_texture_pixels_upload(self->anm2);
|
||||
anm2_spritesheet_texture_pixels_upload(self->anm2);
|
||||
}
|
||||
|
||||
Snapshot snapshot_get(Snapshots* self)
|
||||
{
|
||||
Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, self->action};
|
||||
anm2_spritesheet_texture_pixels_download(&snapshot.anm2);
|
||||
return snapshot;
|
||||
Snapshot snapshot_get(Snapshots* self) {
|
||||
Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, self->action};
|
||||
anm2_spritesheet_texture_pixels_download(&snapshot.anm2);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->preview = preview;
|
||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview) {
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->preview = preview;
|
||||
}
|
||||
|
||||
void snapshots_reset(Snapshots* self)
|
||||
{
|
||||
self->undoStack = SnapshotStack{};
|
||||
self->redoStack = SnapshotStack{};
|
||||
self->action.clear();
|
||||
void snapshots_reset(Snapshots* self) {
|
||||
self->undoStack = SnapshotStack{};
|
||||
self->redoStack = SnapshotStack{};
|
||||
self->action.clear();
|
||||
}
|
||||
|
||||
void snapshots_undo_push(Snapshots* self, Snapshot* snapshot)
|
||||
{
|
||||
self->redoStack.top = 0;
|
||||
_snapshot_stack_push(&self->undoStack, snapshot);
|
||||
void snapshots_undo_push(Snapshots* self, Snapshot* snapshot) {
|
||||
self->redoStack.top = 0;
|
||||
_snapshot_stack_push(&self->undoStack, snapshot);
|
||||
}
|
||||
|
||||
void snapshots_undo(Snapshots* self)
|
||||
{
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->undoStack))
|
||||
{
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->redoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
void snapshots_undo(Snapshots* self) {
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->undoStack)) {
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->redoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
void snapshots_redo(Snapshots* self)
|
||||
{
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->redoStack))
|
||||
{
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->undoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
void snapshots_redo(Snapshots* self) {
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->redoStack)) {
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->undoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
}
|
||||
@@ -2,35 +2,31 @@
|
||||
|
||||
#include "anm2.h"
|
||||
#include "preview.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define SNAPSHOT_STACK_MAX 100
|
||||
#define SNAPSHOT_ACTION "Action"
|
||||
|
||||
struct Snapshot
|
||||
{
|
||||
Anm2 anm2;
|
||||
Anm2Reference reference;
|
||||
f32 time = 0.0f;
|
||||
std::string action = SNAPSHOT_ACTION;
|
||||
struct Snapshot {
|
||||
Anm2 anm2;
|
||||
Anm2Reference reference;
|
||||
float time = 0.0f;
|
||||
std::string action = SNAPSHOT_ACTION;
|
||||
};
|
||||
|
||||
struct SnapshotStack
|
||||
{
|
||||
Snapshot snapshots[SNAPSHOT_STACK_MAX];
|
||||
s32 top = 0;
|
||||
struct SnapshotStack {
|
||||
Snapshot snapshots[SNAPSHOT_STACK_MAX];
|
||||
int top = 0;
|
||||
|
||||
bool is_empty() const { return top == 0; }
|
||||
bool is_empty() const { return top == 0; }
|
||||
};
|
||||
|
||||
struct Snapshots
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
Preview* preview = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
std::string action = SNAPSHOT_ACTION;
|
||||
SnapshotStack undoStack;
|
||||
SnapshotStack redoStack;
|
||||
struct Snapshots {
|
||||
Anm2* anm2 = nullptr;
|
||||
Preview* preview = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
std::string action = SNAPSHOT_ACTION;
|
||||
SnapshotStack undoStack;
|
||||
SnapshotStack redoStack;
|
||||
};
|
||||
|
||||
void snapshots_undo_push(Snapshots* self, Snapshot* snapshot);
|
||||
|
||||
45
src/state.h
45
src/state.h
@@ -27,29 +27,28 @@
|
||||
#define STATE_GL_VERSION_MAJOR 3
|
||||
#define STATE_GL_VERSION_MINOR 3
|
||||
|
||||
struct State
|
||||
{
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Imgui imgui;
|
||||
Dialog dialog;
|
||||
Editor editor;
|
||||
Preview preview;
|
||||
GeneratePreview generatePreview;
|
||||
Anm2 anm2;
|
||||
Anm2Reference reference;
|
||||
Resources resources;
|
||||
Settings settings;
|
||||
Snapshots snapshots;
|
||||
Clipboard clipboard;
|
||||
std::string argument{};
|
||||
std::string lastAction{};
|
||||
u64 lastTick{};
|
||||
u64 tick{};
|
||||
u64 update{};
|
||||
u64 lastUpdate{};
|
||||
bool isRunning = true;
|
||||
};
|
||||
struct State {
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Imgui imgui;
|
||||
Dialog dialog;
|
||||
Editor editor;
|
||||
Preview preview;
|
||||
GeneratePreview generatePreview;
|
||||
Anm2 anm2;
|
||||
Anm2Reference reference;
|
||||
Resources resources;
|
||||
Settings settings;
|
||||
Snapshots snapshots;
|
||||
Clipboard clipboard;
|
||||
std::string argument{};
|
||||
std::string lastAction{};
|
||||
uint64_t lastTick{};
|
||||
uint64_t tick{};
|
||||
uint64_t update{};
|
||||
uint64_t lastUpdate{};
|
||||
bool isRunning = true;
|
||||
};
|
||||
|
||||
bool sdl_init(State* self, bool isTestMode);
|
||||
void init(State* state);
|
||||
|
||||
167
src/texture.cpp
167
src/texture.cpp
@@ -1,129 +1,100 @@
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
#define STBI_NO_HDR
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb_image_write.h>
|
||||
|
||||
static void _texture_gl_set(Texture* self, const u8* data)
|
||||
{
|
||||
if (self->id == GL_ID_NONE) glGenTextures(1, &self->id);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
static void _texture_gl_set(Texture* self, const uint8_t* data) {
|
||||
if (self->id == GL_ID_NONE)
|
||||
glGenTextures(1, &self->id);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
std::vector<u8> texture_download(const 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);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
|
||||
return pixels;
|
||||
std::vector<uint8_t> texture_download(const Texture* self) {
|
||||
std::vector<uint8_t> pixels(self->size.x * self->size.y * TEXTURE_CHANNELS);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->id);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
bool texture_from_path_init(Texture* self, const std::string& path)
|
||||
{
|
||||
u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
log_error(std::format(TEXTURE_INIT_ERROR, path));
|
||||
return false;
|
||||
}
|
||||
bool texture_from_path_init(Texture* self, const std::string& path) {
|
||||
uint8_t* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
|
||||
|
||||
self->isInvalid = false;
|
||||
if (!data) {
|
||||
log_error(std::format(TEXTURE_INIT_ERROR, path));
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info(std::format(TEXTURE_INIT_INFO, path));
|
||||
self->isInvalid = false;
|
||||
|
||||
_texture_gl_set(self, data);
|
||||
log_info(std::format(TEXTURE_INIT_INFO, path));
|
||||
|
||||
return true;
|
||||
_texture_gl_set(self, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, const u8* data, u32 length)
|
||||
{
|
||||
*self = Texture{};
|
||||
self->size = size;
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, const uint8_t* data) {
|
||||
*self = Texture{};
|
||||
self->size = size;
|
||||
self->isInvalid = false;
|
||||
|
||||
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
|
||||
_texture_gl_set(self, data);
|
||||
|
||||
if (!textureData)
|
||||
{
|
||||
self->isInvalid = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
_texture_gl_set(self, textureData);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data)
|
||||
{
|
||||
*self = Texture{};
|
||||
self->size = size;
|
||||
self->isInvalid = false;
|
||||
bool texture_from_rgba_write(const std::string& path, const uint8_t* data, ivec2 size) {
|
||||
bool isSuccess = stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
|
||||
if (!isSuccess)
|
||||
log_error(std::format(TEXTURE_SAVE_ERROR, path));
|
||||
else
|
||||
log_info(std::format(TEXTURE_SAVE_INFO, path));
|
||||
|
||||
_texture_gl_set(self, data);
|
||||
|
||||
return true;
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size)
|
||||
{
|
||||
bool isSuccess = stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
|
||||
if (!isSuccess) log_error(std::format(TEXTURE_SAVE_ERROR, path));
|
||||
else log_info(std::format(TEXTURE_SAVE_INFO, path));
|
||||
|
||||
return isSuccess;
|
||||
bool texture_from_gl_write(Texture* self, const std::string& path) { return texture_from_rgba_write(path, texture_download(self).data(), self->size); }
|
||||
|
||||
void texture_free(Texture* self) {
|
||||
if (self->isInvalid)
|
||||
return;
|
||||
|
||||
glDeleteTextures(1, &self->id);
|
||||
*self = Texture{};
|
||||
}
|
||||
|
||||
bool texture_from_gl_write(Texture* self, const std::string& path)
|
||||
{
|
||||
return texture_from_rgba_write(path, texture_download(self).data(), self->size);
|
||||
}
|
||||
|
||||
void texture_free(Texture* self)
|
||||
{
|
||||
if (self->isInvalid) return;
|
||||
|
||||
glDeleteTextures(1, &self->id);
|
||||
*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;
|
||||
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_UINT8(color.r), FLOAT_TO_UINT8(color.g), FLOAT_TO_UINT8(color.b), FLOAT_TO_UINT8(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;
|
||||
}
|
||||
|
||||
@@ -8,16 +8,14 @@
|
||||
#define TEXTURE_SAVE_INFO "Saved texture to: {}"
|
||||
#define TEXTURE_SAVE_ERROR "Failed to save texture to: {}"
|
||||
|
||||
struct Texture
|
||||
{
|
||||
GLuint id = GL_ID_NONE;
|
||||
ivec2 size{};
|
||||
bool isInvalid = true;
|
||||
struct Texture {
|
||||
GLuint id = GL_ID_NONE;
|
||||
ivec2 size{};
|
||||
bool isInvalid = true;
|
||||
|
||||
auto operator<=>(const Texture&) const = default;
|
||||
auto operator<=>(const Texture&) const = default;
|
||||
};
|
||||
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, const u8* data, u32 length);
|
||||
bool texture_from_gl_write(Texture* self, const std::string& path);
|
||||
bool texture_from_path_init(Texture* self, const std::string& path);
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data);
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
#include "window.h"
|
||||
#include <SDL3/SDL_video.h>
|
||||
|
||||
void window_title_from_path_set(SDL_Window* self, const std::string& path)
|
||||
{
|
||||
if (!path.empty())
|
||||
SDL_SetWindowTitle(self, std::format(WINDOW_TITLE_FORMAT, path).c_str());
|
||||
else
|
||||
SDL_SetWindowTitle(self, WINDOW_TITLE);
|
||||
void window_title_from_path_set(SDL_Window* self, const std::string& path) {
|
||||
if (!path.empty())
|
||||
SDL_SetWindowTitle(self, std::format(WINDOW_TITLE_FORMAT, path).c_str());
|
||||
else
|
||||
SDL_SetWindowTitle(self, WINDOW_TITLE);
|
||||
}
|
||||
|
||||
void window_vsync_set(bool isVsync)
|
||||
{
|
||||
if (isVsync)
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
else
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
void window_vsync_set(bool isVsync) { SDL_GL_SetSwapInterval(isVsync); }
|
||||
Reference in New Issue
Block a user