Compare commits

..

13 Commits

29 changed files with 26478 additions and 896 deletions

View File

@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.15)
if(WIN32 AND DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain file")
@@ -7,53 +8,53 @@ endif()
project(anm2ed CXX)
find_package(SDL3 REQUIRED)
find_package(GLEW REQUIRED)
find_package(OpenGL REQUIRED)
# Gather project sources
set(GLAD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad.cpp)
file(GLOB SOURCES
"include/imgui/imgui.cpp"
"include/imgui/imgui_draw.cpp"
"include/imgui/imgui_tables.cpp"
"include/imgui/imgui_widgets.cpp"
"include/imgui/backends/imgui_impl_sdl3.cpp"
"include/imgui/backends/imgui_impl_opengl3.cpp"
"include/tinyxml2/tinyxml2.cpp"
"${PROJECT_SOURCE_DIR}/src/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*.h"
${GLAD_SRC}
include/imgui/imgui.cpp
include/imgui/imgui_draw.cpp
include/imgui/imgui_widgets.cpp
include/imgui/imgui_tables.cpp
include/imgui/backends/imgui_impl_sdl3.cpp
include/imgui/backends/imgui_impl_opengl3.cpp
include/tinyxml2/tinyxml2.cpp
src/*.cpp
src/*.h
)
if (WIN32)
enable_language("RC")
set (WIN32_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assets/Icon.rc)
if(NOT MSVC)
set_source_files_properties(${GLAD_SRC} PROPERTIES COMPILE_FLAGS "-Wno-cast-function-type")
endif()
add_executable(${PROJECT_NAME} ${SOURCES} ${WIN32_RESOURCES})
add_executable(${PROJECT_NAME} ${SOURCES})
if(WIN32)
enable_language(RC)
target_sources(${PROJECT_NAME} PRIVATE assets/Icon.rc)
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
target_include_directories(${PROJECT_NAME} PRIVATE include include/glad include/imgui include/tinyxml2 src)
target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 src)
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "-O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1")
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /std:c++latest /EHsc)
target_link_options(${PROJECT_NAME} PRIVATE /STACK:0xffffff)
else()
set(CMAKE_CXX_FLAGS "/std:c++latest /EHsc")
target_link_options(${PROJECT_NAME} PRIVATE "/STACK:0xffffff")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG -g")
else()
set(CMAKE_BUILD_TYPE "Release")
endif()
if(NOT MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE -O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(${PROJECT_NAME} PRIVATE -DDEBUG -g)
else()
set(CMAKE_BUILD_TYPE "Release")
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE m)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL GLEW::GLEW SDL3::SDL3)
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("Flags: ${CMAKE_CXX_FLAGS}")
message("Build: ${CMAKE_BUILD_TYPE}")

15
CMakeSettings.json Normal file
View File

@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}

View File

@@ -18,11 +18,20 @@ A reimplementation of *The Binding of Isaac: Rebirth*'s proprietary animation ed
## Dependencies
Download these from your package manager:
- SDL3
- GLEW
Note, to render animations, you'll need to download [FFmpeg](https://ffmpeg.org/download.html) and specify its install path in the program.
## Build (Linux)
## Build
### Windows
Visual Studio is recommended; make sure your installation has "Desktop development with C++" and ".NET desktop development" workloads.
Install and configure [vcpkg](https://vcpkg.io/en/).
Build should be straightforward from there.
### Linux
After cloning and enter the repository's directory, make sure to initialize the submodules:
@@ -35,4 +44,4 @@ mkdir build
cd build
cmake ..
make
```
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

311
include/KHR/khrplatform.h Normal file
View File

@@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

9045
include/glad/glad.cpp Normal file

File diff suppressed because one or more lines are too long

15776
include/glad/glad.h Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
#pragma once
#include <SDL3/SDL.h>
#include <GL/glew.h>
#include <glad/glad.h>
#include <GL/gl.h>
#include <glm/glm/glm.hpp>
#include <glm/glm/gtc/type_ptr.hpp>
@@ -10,6 +10,7 @@
#include <algorithm>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstring>
#include <filesystem>
@@ -24,7 +25,7 @@
#include <string>
#include <unordered_set>
#include <variant>
#include <vector>
#include <vector>
typedef uint8_t u8;
typedef uint16_t u16;
@@ -342,8 +343,6 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
map[insertIndex] = value;
}
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
static inline std::string function(s32 index) \
{ \
@@ -356,19 +355,30 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
return static_cast<enumType>(string_to_enum(string, stringArray, count)); \
};
#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
enum DataType
{
TYPE_INT,
TYPE_BOOL,
TYPE_FLOAT,
TYPE_STRING,
TYPE_IVEC2,
TYPE_VEC2,
TYPE_VEC3,
TYPE_VEC4
#define X(symbol, ctype) symbol,
DATATYPE_LIST
#undef X
};
#define DATATYPE_TO_CTYPE(dt) DATATYPE_CTYPE_##dt
#define X(symbol, ctype) typedef ctype DATATYPE_CTYPE_##symbol;
DATATYPE_LIST
#undef X
enum OriginType
{
ORIGIN_TOP_LEFT,

View File

@@ -7,104 +7,106 @@
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, 0x78,
0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x33, 0xea, 0xfd, 0x00, 0x00, 0x00,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xa0,
0x02, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x1e, 0x81, 0x1f, 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, 0xff, 0xff, 0xff, 0x60, 0x60, 0x60, 0xff,
0xff, 0xff, 0x60, 0x60, 0x60, 0x15, 0x68, 0x14, 0xc2, 0x00, 0x00, 0x00,
0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde,
0x00, 0x00, 0x03, 0xf4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x98,
0x6d, 0x6e, 0xde, 0x38, 0x0c, 0x84, 0x27, 0x20, 0x0f, 0xb0, 0xc1, 0x5e,
0x60, 0x81, 0x5c, 0x80, 0x9b, 0xe1, 0x01, 0x08, 0x88, 0xf7, 0x3f, 0xd3,
0x42, 0x94, 0x04, 0xa9, 0xad, 0x83, 0xd8, 0x0b, 0x34, 0xed, 0x8f, 0x4e,
0x90, 0xc8, 0xb4, 0xf9, 0x84, 0x5f, 0xd2, 0xeb, 0x20, 0xf8, 0x3f, 0x22,
0x71, 0x29, 0x31, 0x94, 0x9a, 0x97, 0xd7, 0xe1, 0x4f, 0xd6, 0x12, 0x42,
0xc6, 0xe1, 0x4f, 0xda, 0xf1, 0x0b, 0x99, 0x0e, 0x68, 0xdd, 0x07, 0x68,
0x90, 0x22, 0x87, 0x4b, 0x23, 0x09, 0x29, 0x73, 0x03, 0x29, 0x04, 0x64,
0x86, 0x22, 0xeb, 0x72, 0x7b, 0x90, 0xc2, 0x92, 0xed, 0x94, 0x84, 0xae,
0x1c, 0x80, 0x90, 0x38, 0x1c, 0xd4, 0x80, 0xd3, 0x16, 0x9b, 0x00, 0xc9,
0x61, 0xd1, 0xc4, 0xa4, 0xb2, 0xd7, 0xed, 0x30, 0x53, 0xba, 0x02, 0x84,
0x10, 0x13, 0x94, 0xac, 0xdb, 0x15, 0x26, 0x1b, 0xe9, 0x19, 0xdf, 0x02,
0x5e, 0x16, 0xed, 0x04, 0x1c, 0x2d, 0x0a, 0xc8, 0xe6, 0xd9, 0x01, 0x1d,
0xbd, 0x63, 0x0b, 0xd2, 0x3a, 0x20, 0xc4, 0x01, 0x28, 0x41, 0x60, 0x10,
0xe5, 0x8f, 0x66, 0xe2, 0x1d, 0x58, 0xd5, 0x17, 0x46, 0x2c, 0x00, 0x0b,
0x88, 0xee, 0x1f, 0xa1, 0x64, 0xcd, 0x08, 0x3c, 0xe7, 0x7c, 0x02, 0x8d,
0x5e, 0x01, 0x54, 0x35, 0x55, 0x21, 0x95, 0x06, 0x6d, 0x02, 0x7b, 0x70,
0xab, 0x4b, 0xf5, 0xb0, 0x14, 0x5d, 0x17, 0xc0, 0xdc, 0x1a, 0xca, 0x52,
0xa0, 0x9e, 0xcc, 0x18, 0x00, 0x94, 0x31, 0x7e, 0xf0, 0xf3, 0xcd, 0x87,
0xc0, 0x2f, 0x96, 0x92, 0x71, 0x65, 0x8b, 0x61, 0xd9, 0xd8, 0xda, 0xe7,
0xa2, 0x71, 0x6c, 0x81, 0xec, 0x6b, 0x1e, 0x00, 0xb9, 0x89, 0xa3, 0xbd,
0x70, 0x74, 0xaa, 0x83, 0x40, 0x07, 0x85, 0x7c, 0x07, 0x80, 0x37, 0x66,
0xd0, 0x8e, 0xd0, 0xcc, 0x4c, 0x12, 0xb0, 0xa2, 0x01, 0xc6, 0xe8, 0x9f,
0x8b, 0xb5, 0x0a, 0xa0, 0x91, 0xe4, 0xf0, 0xef, 0x84, 0x32, 0xb3, 0x91,
0xb1, 0x00, 0x25, 0x20, 0x65, 0x8b, 0xc9, 0x04, 0xa4, 0x00, 0xf1, 0xcc,
0x66, 0xb5, 0xd0, 0x9b, 0x2d, 0x60, 0x9d, 0x13, 0x96, 0x80, 0x56, 0xcf,
0xc5, 0xa0, 0xde, 0x9a, 0x7b, 0x0b, 0xf1, 0x6c, 0xcc, 0x13, 0x80, 0x10,
0x07, 0x50, 0xcf, 0xc5, 0xc0, 0x10, 0x31, 0x53, 0x36, 0x4b, 0x7a, 0x8a,
0x0f, 0x40, 0xb1, 0xce, 0xc9, 0x4a, 0x49, 0x4c, 0xe9, 0xdd, 0x56, 0x93,
0xe8, 0xdf, 0xe2, 0x42, 0x60, 0x45, 0xd8, 0xe7, 0x64, 0x15, 0xdd, 0x9f,
0x67, 0xb7, 0x21, 0x20, 0x61, 0x50, 0xd2, 0x33, 0x67, 0xd1, 0x64, 0xb7,
0x0d, 0xc9, 0xa8, 0xb6, 0x92, 0x00, 0xe9, 0x91, 0xd5, 0x25, 0x47, 0x1d,
0x4a, 0x56, 0x5f, 0xd1, 0x01, 0x69, 0x6e, 0x05, 0x75, 0x00, 0x62, 0xd3,
0x8e, 0xb2, 0x6b, 0xb4, 0x5e, 0x83, 0xcb, 0x9c, 0x83, 0xab, 0x2c, 0x1a,
0x91, 0xf4, 0xec, 0x40, 0xd9, 0xc2, 0x08, 0xba, 0x5e, 0x6d, 0x8d, 0xe8,
0x55, 0x21, 0xcb, 0x0e, 0xe0, 0xf5, 0x9f, 0xb2, 0x31, 0xec, 0xeb, 0xcd,
0xb7, 0x26, 0x9a, 0x79, 0xd8, 0x58, 0xf6, 0x73, 0x89, 0xed, 0xf5, 0x96,
0xc8, 0xbd, 0xee, 0xd4, 0xc4, 0x28, 0xc6, 0xbd, 0x96, 0x32, 0xf6, 0x7b,
0xa0, 0xd6, 0x5d, 0xbc, 0xac, 0xf0, 0x38, 0xa4, 0x05, 0x9c, 0xa9, 0xfd,
0x05, 0xfc, 0x4d, 0x76, 0x52, 0xc6, 0x6f, 0xb0, 0x09, 0x18, 0x5e, 0x01,
0x89, 0x8c, 0xb3, 0x3b, 0x52, 0x77, 0xc9, 0x58, 0x87, 0xb1, 0x03, 0x98,
0xc0, 0xcb, 0x2b, 0xe0, 0x54, 0x00, 0xd6, 0x42, 0x5d, 0x00, 0xeb, 0x80,
0x60, 0x7d, 0x70, 0x49, 0x8d, 0xc4, 0xce, 0x94, 0xa4, 0x8d, 0xc1, 0x3b,
0xd0, 0xc4, 0xc4, 0xd0, 0x1c, 0x62, 0x52, 0xe1, 0x06, 0x50, 0x29, 0xed,
0x08, 0x4e, 0x37, 0xa0, 0xdf, 0x33, 0x11, 0x2e, 0x09, 0x51, 0x40, 0x75,
0xa1, 0xed, 0x94, 0xf0, 0x2a, 0x8d, 0x0d, 0xd7, 0x80, 0x81, 0x26, 0x45,
0x9d, 0x29, 0x55, 0x80, 0xeb, 0x94, 0x28, 0x34, 0x61, 0x55, 0x6e, 0x05,
0x88, 0x19, 0x50, 0x01, 0x7e, 0x2c, 0x5a, 0x69, 0x20, 0x19, 0x73, 0x2a,
0xb6, 0xfa, 0x07, 0x91, 0x0a, 0x70, 0xd5, 0x56, 0x23, 0xb9, 0xc7, 0xb9,
0x46, 0x4a, 0xab, 0x00, 0x57, 0x83, 0x33, 0xd2, 0x16, 0x60, 0x1b, 0x20,
0x4a, 0x17, 0x5b, 0x63, 0x47, 0x38, 0x52, 0xd2, 0x66, 0x57, 0x9b, 0x6f,
0x5d, 0xd0, 0x3a, 0x40, 0x31, 0x80, 0x52, 0xab, 0xc6, 0xe5, 0xf6, 0xfe,
0x1a, 0xd9, 0x11, 0x1b, 0x43, 0x4a, 0x4c, 0xc5, 0xb0, 0xc7, 0x22, 0xd1,
0x7c, 0x27, 0x76, 0x24, 0xc9, 0x5c, 0x40, 0x58, 0x2d, 0xd3, 0x56, 0x27,
0x01, 0x9e, 0x00, 0x01, 0x88, 0x33, 0x46, 0x18, 0x9f, 0x0f, 0xb5, 0x4d,
0xa7, 0x56, 0xed, 0xec, 0x8a, 0xf5, 0x1e, 0x36, 0xb0, 0xf9, 0x4c, 0xeb,
0xfd, 0x2d, 0x86, 0x63, 0x92, 0x3e, 0x32, 0xa6, 0x83, 0x43, 0xc0, 0xba,
0x60, 0xa0, 0xd4, 0xf2, 0xfd, 0x2d, 0x19, 0x23, 0x00, 0x73, 0x54, 0x2b,
0x01, 0xf9, 0x16, 0x30, 0x24, 0xfb, 0x17, 0x94, 0x49, 0x66, 0xc1, 0x41,
0x7a, 0x54, 0x00, 0xa8, 0x03, 0xb4, 0xfd, 0x17, 0x8c, 0x10, 0x03, 0x98,
0xa9, 0xd6, 0xb4, 0x95, 0xb2, 0x02, 0x84, 0xb5, 0xd9, 0x19, 0xb1, 0xb9,
0xcc, 0x46, 0x2a, 0x04, 0x1d, 0x18, 0x95, 0xda, 0x0a, 0x20, 0xe8, 0x3a,
0x01, 0x00, 0x59, 0x2a, 0xc0, 0xd7, 0x3b, 0x51, 0x57, 0x00, 0x94, 0x26,
0xb0, 0x47, 0xa5, 0x50, 0x08, 0x99, 0x05, 0x60, 0x34, 0x11, 0xc7, 0x69,
0xdc, 0x35, 0x9c, 0x80, 0x49, 0xfa, 0xda, 0xd5, 0x18, 0x80, 0x61, 0x48,
0x56, 0x97, 0x6c, 0x03, 0x75, 0xbb, 0x0d, 0x0f, 0x7a, 0x2a, 0x02, 0x8d,
0xb8, 0x04, 0xb2, 0x04, 0x40, 0x17, 0xa0, 0x19, 0x11, 0x38, 0x24, 0x6b,
0xd2, 0x86, 0x2f, 0x95, 0x1c, 0xf1, 0xd4, 0x6e, 0x01, 0x1b, 0x62, 0x00,
0x2f, 0x9c, 0x86, 0x30, 0x84, 0x31, 0xba, 0xf1, 0x01, 0x20, 0x44, 0x07,
0x68, 0x9f, 0x01, 0xfb, 0x73, 0x65, 0x00, 0xb4, 0x7b, 0x80, 0x10, 0x13,
0xa0, 0x5d, 0x03, 0x27, 0x54, 0xf7, 0x63, 0x01, 0xb4, 0x0d, 0x94, 0xed,
0x6b, 0x3d, 0x6a, 0x50, 0x92, 0x5c, 0x00, 0xe3, 0x63, 0xe0, 0xdc, 0x39,
0xbe, 0x00, 0xff, 0x10, 0x38, 0x95, 0xb4, 0x09, 0x78, 0xec, 0x94, 0x32,
0xe9, 0x99, 0x40, 0x96, 0xf6, 0x07, 0xf2, 0x6e, 0x6b, 0xf9, 0x7f, 0x54,
0x74, 0x01, 0x67, 0x5b, 0x81, 0xf2, 0xbf, 0x05, 0x28, 0xb1, 0x9a, 0x75,
0x0f, 0x90, 0xe1, 0x98, 0x81, 0xb5, 0xdb, 0x75, 0x5c, 0x67, 0x5e, 0x01,
0x37, 0xb5, 0x8b, 0x7e, 0xac, 0xfc, 0x4e, 0x81, 0xef, 0x44, 0xbb, 0x06,
0x56, 0x05, 0x9f, 0x02, 0x8d, 0x25, 0xff, 0x3a, 0x00, 0x7f, 0x80, 0xdf,
0x12, 0x20, 0x69, 0x4f, 0x00, 0x76, 0xd9, 0x7d, 0x40, 0x08, 0x9a, 0xf0,
0x01, 0x60, 0xa0, 0xbd, 0x3c, 0x00, 0x58, 0x45, 0x8b, 0xfd, 0x0c, 0x80,
0x3f, 0xea, 0xdf, 0x2f, 0x05, 0xbe, 0xbe, 0xe8, 0xe7, 0x73, 0x78, 0x3e,
0xe9, 0xe7, 0x7b, 0x69, 0x13, 0xf6, 0xe7, 0x4c, 0x3f, 0x06, 0x94, 0xa5,
0xb8, 0x05, 0xec, 0x10, 0x8e, 0xfb, 0x80, 0x56, 0x00, 0xdc, 0x7e, 0x3f,
0x54, 0x08, 0xc7, 0x13, 0x40, 0xc9, 0x78, 0x04, 0xa0, 0x39, 0x9e, 0x01,
0xe5, 0x94, 0x53, 0x9f, 0xfd, 0x33, 0xf0, 0x3f, 0x53, 0x26, 0x87, 0x23,
0x59, 0xac, 0x2b, 0x06, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
0xae, 0x42, 0x60, 0x82
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0c, 0x50, 0x4c,
0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60,
0x60, 0x60, 0x58, 0xe6, 0xdc, 0x65, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52,
0x4e, 0x53, 0x00, 0x00, 0x76, 0x93, 0xcd, 0x38, 0x00, 0x00, 0x04, 0x15,
0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xe5, 0xd3, 0xb1, 0x6a, 0x1c, 0x47,
0x1c, 0xc7, 0xf1, 0x2f, 0xb3, 0xc4, 0x2c, 0xe3, 0x63, 0xaf, 0x4b, 0x91,
0x6a, 0x49, 0xb5, 0xcc, 0x1d, 0xf6, 0x81, 0x1b, 0xb1, 0x36, 0x56, 0x1e,
0x21, 0x75, 0xde, 0xc0, 0x79, 0x8a, 0xc1, 0x95, 0x48, 0x61, 0x5c, 0x24,
0xfd, 0x20, 0x1c, 0x98, 0xfc, 0x77, 0xd1, 0x2d, 0x49, 0x63, 0x14, 0x81,
0xb6, 0x34, 0x41, 0xe4, 0x19, 0x5c, 0x8a, 0xb3, 0x09, 0x02, 0x37, 0xe1,
0x72, 0xf8, 0x92, 0x19, 0x5d, 0xb4, 0x91, 0x6e, 0x94, 0x20, 0xab, 0x48,
0xe1, 0x1f, 0x0c, 0x03, 0xf7, 0xe1, 0xfe, 0x33, 0xf3, 0xdf, 0x19, 0xae,
0x8f, 0x76, 0x5c, 0x44, 0x59, 0x28, 0x3a, 0x34, 0x00, 0x22, 0xe8, 0xd6,
0xb4, 0x00, 0x18, 0x0f, 0xda, 0xa1, 0xff, 0x60, 0x86, 0x72, 0xda, 0x96,
0x5a, 0x3c, 0x2c, 0x44, 0x89, 0xd8, 0x00, 0x63, 0x43, 0x89, 0x46, 0xbb,
0x92, 0x00, 0x22, 0x46, 0xc4, 0xc7, 0x52, 0xe6, 0x27, 0x87, 0xc6, 0xb8,
0xf8, 0x43, 0x66, 0x89, 0xb3, 0xb2, 0x68, 0x23, 0x8e, 0x0a, 0x5f, 0x4d,
0x61, 0x07, 0x65, 0x09, 0xa5, 0x06, 0x50, 0xae, 0x2a, 0x21, 0xaf, 0x94,
0x65, 0x77, 0xd1, 0xac, 0x36, 0xd0, 0x51, 0x69, 0x1b, 0x61, 0x52, 0xf4,
0xec, 0xbe, 0x3f, 0x5e, 0x51, 0xb7, 0xe8, 0xb7, 0x62, 0xa9, 0x0c, 0x11,
0x6a, 0x0d, 0xd9, 0x7a, 0xdd, 0x73, 0x3a, 0x41, 0xf3, 0x06, 0xb4, 0xc7,
0x05, 0xd0, 0x1a, 0x46, 0xeb, 0xd5, 0x49, 0xed, 0x74, 0xab, 0x37, 0x07,
0x8f, 0x50, 0x34, 0xf0, 0xe0, 0xc1, 0xbb, 0x99, 0xb1, 0xca, 0x07, 0x88,
0x07, 0x0c, 0xbb, 0x52, 0x1e, 0x38, 0x39, 0xe9, 0x2f, 0x00, 0x11, 0x6a,
0x91, 0x16, 0x07, 0x3c, 0x98, 0x51, 0xf7, 0x59, 0xab, 0xb7, 0x9a, 0x48,
0xcf, 0x6d, 0x53, 0xb7, 0xc3, 0x7c, 0x29, 0x22, 0x14, 0x22, 0xdd, 0x58,
0xe4, 0x0c, 0x86, 0xc4, 0x2d, 0xdf, 0x85, 0xd7, 0x85, 0xd3, 0xdd, 0xa5,
0x32, 0x7a, 0xbd, 0x74, 0x39, 0x94, 0xba, 0x1f, 0xb6, 0x48, 0x26, 0xd2,
0xd7, 0xeb, 0x45, 0x1b, 0xa0, 0xc6, 0xb4, 0x43, 0x99, 0xe3, 0xf7, 0x76,
0xb2, 0x6e, 0x4e, 0x03, 0x84, 0x6f, 0x73, 0x01, 0x0f, 0x7f, 0xfb, 0xf9,
0xed, 0xe4, 0xfd, 0xfc, 0x34, 0x57, 0xb6, 0xac, 0x94, 0x1b, 0xa0, 0xfd,
0x7c, 0xaf, 0x3e, 0x5d, 0x1e, 0x4f, 0xfe, 0x82, 0x59, 0xa5, 0xed, 0x00,
0x3b, 0xd3, 0x9d, 0xe9, 0xc4, 0x10, 0x4a, 0x51, 0x19, 0x06, 0x50, 0xda,
0xd9, 0xba, 0x59, 0xb7, 0x39, 0xda, 0xd5, 0x7e, 0x3c, 0x2c, 0x9e, 0x75,
0x45, 0xaf, 0xe7, 0x6b, 0x97, 0x73, 0xef, 0xa5, 0x76, 0xbb, 0x03, 0xb0,
0x68, 0x50, 0xf3, 0xa5, 0xbd, 0xcb, 0x8f, 0xcf, 0x0a, 0xb7, 0x3c, 0xde,
0x6e, 0x49, 0x3b, 0xdd, 0x19, 0x8b, 0x8c, 0x12, 0x4d, 0xd4, 0x8e, 0x7a,
0x7e, 0xc6, 0x7f, 0x47, 0xd9, 0x30, 0x92, 0xef, 0x23, 0x8c, 0x58, 0x4e,
0x7d, 0x53, 0xb9, 0x30, 0x80, 0x15, 0x18, 0x4f, 0x1c, 0x22, 0x94, 0x10,
0x07, 0x64, 0x2b, 0x62, 0x36, 0x17, 0xba, 0x54, 0xb2, 0x57, 0x42, 0x0e,
0xd3, 0x55, 0x28, 0x11, 0x41, 0xb7, 0xb5, 0x03, 0xd9, 0x23, 0xc2, 0xe1,
0x8c, 0xfc, 0xed, 0xc3, 0x32, 0x74, 0x36, 0x5c, 0xb4, 0x92, 0x85, 0x2d,
0x01, 0xd4, 0xa2, 0xa7, 0xea, 0x8a, 0xaa, 0x2a, 0xba, 0xca, 0xf4, 0xf8,
0x92, 0x4d, 0xa9, 0xbb, 0x8d, 0xc5, 0x3f, 0x7d, 0x6a, 0x44, 0x44, 0x0c,
0x78, 0xd4, 0x41, 0x2c, 0xc5, 0xbd, 0x03, 0x18, 0xc0, 0xfa, 0x52, 0xdb,
0x58, 0x8a, 0xc3, 0x97, 0x30, 0x94, 0xda, 0xf7, 0x46, 0xc8, 0x4b, 0xd4,
0x1e, 0x8b, 0xd7, 0x5c, 0x2c, 0x5e, 0x5b, 0x69, 0x8d, 0x27, 0x07, 0x65,
0xd5, 0xc4, 0x32, 0x6c, 0xd7, 0x8b, 0x33, 0x1e, 0x4a, 0x8c, 0xd7, 0xcf,
0x60, 0x38, 0xa0, 0x17, 0x1b, 0xfe, 0x11, 0xc1, 0xc1, 0xd0, 0x92, 0xf0,
0x8f, 0x4d, 0xa9, 0xc7, 0x16, 0x86, 0x26, 0x22, 0xde, 0xf8, 0x7d, 0xab,
0xab, 0x7d, 0xfb, 0x28, 0xd1, 0xf6, 0x9b, 0xc4, 0x02, 0x28, 0x20, 0x73,
0xc0, 0x08, 0x78, 0x04, 0xea, 0xd7, 0x4e, 0x9d, 0x83, 0x85, 0x79, 0x80,
0x57, 0xc0, 0x12, 0xb2, 0x43, 0xa7, 0xcf, 0xc1, 0x31, 0x69, 0xa1, 0xee,
0x3c, 0x64, 0x0b, 0x0b, 0x0b, 0x67, 0x44, 0xfa, 0x4c, 0xc4, 0xeb, 0x37,
0x90, 0xf9, 0xa9, 0x87, 0xb1, 0x74, 0x28, 0xdd, 0x89, 0x88, 0xd3, 0x22,
0xa2, 0x7b, 0x28, 0x7e, 0xbf, 0xdf, 0xc2, 0x63, 0x39, 0x23, 0x57, 0xbd,
0x39, 0x07, 0x3f, 0x9e, 0xcf, 0x5d, 0xbd, 0x9c, 0xf7, 0xf0, 0x4b, 0xd3,
0x53, 0x66, 0x9d, 0xb6, 0x9c, 0x4e, 0x50, 0x2e, 0x80, 0x91, 0x03, 0x4b,
0xb6, 0x3f, 0x07, 0xfb, 0xea, 0xb5, 0xb2, 0x54, 0x15, 0xca, 0x8e, 0x60,
0x56, 0xca, 0x01, 0x14, 0xfe, 0x08, 0x45, 0x09, 0xe7, 0xc0, 0x78, 0xbd,
0x3e, 0x2b, 0xe5, 0x10, 0x6a, 0x99, 0x91, 0x8f, 0x2c, 0x10, 0x00, 0x46,
0xcc, 0x66, 0x66, 0x7e, 0x08, 0x5a, 0x7a, 0x72, 0x05, 0x10, 0xd6, 0x20,
0x42, 0x75, 0xff, 0x25, 0x28, 0x81, 0x3c, 0x07, 0x54, 0xd8, 0x95, 0x8d,
0x80, 0x39, 0x05, 0x9a, 0x77, 0xa3, 0xc2, 0x0d, 0x10, 0xd6, 0xa0, 0x0e,
0xb0, 0x7b, 0xd2, 0x43, 0x80, 0x70, 0x72, 0xcb, 0x87, 0x45, 0x59, 0x00,
0x76, 0x12, 0x10, 0xa9, 0x45, 0x5b, 0x50, 0xad, 0xe9, 0xd1, 0x1d, 0x03,
0x28, 0x87, 0xf6, 0x57, 0xc1, 0x78, 0xd0, 0x16, 0x2d, 0x36, 0x01, 0x06,
0xb4, 0xf8, 0x01, 0x36, 0x04, 0x4d, 0x8f, 0x16, 0xb1, 0x01, 0xb4, 0x48,
0x17, 0x46, 0x5c, 0x23, 0x13, 0x09, 0xd0, 0x5e, 0x06, 0x40, 0x49, 0x13,
0xe0, 0xe8, 0x9f, 0x40, 0xcc, 0xd8, 0xa3, 0xa5, 0xe9, 0x03, 0x8c, 0x97,
0xc7, 0x67, 0xa1, 0x57, 0xf1, 0x5a, 0xc7, 0xed, 0xca, 0xd1, 0xa5, 0xc5,
0x8d, 0xff, 0x7b, 0xbb, 0x4d, 0x9f, 0x80, 0x1a, 0xf4, 0x11, 0x09, 0x98,
0xc2, 0xb8, 0x87, 0x6c, 0xb5, 0xdb, 0x33, 0x3e, 0x1b, 0x20, 0x95, 0xb8,
0xf8, 0xb5, 0x59, 0x6f, 0xd2, 0x43, 0x8c, 0x72, 0x0c, 0x10, 0x96, 0xd8,
0x82, 0x85, 0x48, 0xf3, 0x61, 0xc0, 0xc7, 0x02, 0x46, 0xd2, 0xa0, 0x44,
0x5c, 0x12, 0xf4, 0x53, 0xe7, 0xd3, 0xa0, 0x5c, 0x95, 0x84, 0x4a, 0x39,
0x7d, 0x03, 0x90, 0x8b, 0xdc, 0x1c, 0x6e, 0xb3, 0x78, 0xfa, 0x1c, 0xe9,
0x93, 0xa7, 0x7b, 0x95, 0xee, 0xee, 0xff, 0xff, 0xcd, 0x6b, 0x91, 0x36,
0x09, 0x85, 0x48, 0x97, 0x84, 0x4c, 0xa4, 0x4f, 0xbe, 0x0f, 0x16, 0x0d,
0x69, 0xa8, 0x5b, 0xd2, 0x50, 0x74, 0x49, 0x88, 0x3f, 0x6d, 0x3d, 0xe7,
0x54, 0x44, 0x48, 0xc6, 0x88, 0xfc, 0x40, 0x22, 0x9f, 0x0a, 0x88, 0x4d,
0xc0, 0xd7, 0x16, 0x94, 0x67, 0x2b, 0x9f, 0xc4, 0x32, 0x26, 0x51, 0xe9,
0x3b, 0x80, 0x3b, 0xcf, 0xb7, 0xe0, 0xab, 0x2f, 0x01, 0x78, 0xb1, 0x05,
0x4f, 0xbe, 0x00, 0xa0, 0xda, 0x82, 0xef, 0x89, 0xd1, 0x5c, 0xc9, 0x9d,
0x6f, 0x37, 0xb3, 0xbd, 0x0a, 0x9b, 0x55, 0xd5, 0x55, 0xf8, 0x8c, 0x4d,
0x5c, 0x02, 0x92, 0xdb, 0x7a, 0xc2, 0x26, 0xd5, 0x6d, 0x61, 0x28, 0xad,
0x6f, 0x0d, 0x0e, 0x50, 0xf6, 0xa6, 0x00, 0xb7, 0x82, 0xad, 0x73, 0x55,
0xb7, 0x85, 0xeb, 0xdb, 0xae, 0xaf, 0xfb, 0x50, 0xca, 0x0e, 0xf3, 0x65,
0x78, 0x4e, 0xfa, 0x32, 0xe0, 0xd2, 0xc7, 0x80, 0x6a, 0x98, 0x92, 0xdb,
0x7a, 0x01, 0x89, 0x8b, 0x95, 0xbc, 0xd4, 0x78, 0xd2, 0xcf, 0x00, 0x93,
0x7e, 0x38, 0xa0, 0x86, 0xa7, 0x96, 0x78, 0x9c, 0xff, 0xfe, 0x9c, 0xff,
0x04, 0xe2, 0xd8, 0x90, 0xc1, 0x18, 0xe1, 0xf4, 0x9b, 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, 120};
const vec2 TEXTURE_ATLAS_SIZE = {96, 160};
enum AtlasType
{
@@ -118,8 +120,8 @@ enum AtlasType
ATLAS_INVISIBLE,
ATLAS_SHOW_RECT,
ATLAS_HIDE_RECT,
ATLAS_SHOW_TARGETS,
ATLAS_HIDE_TARGETS,
ATLAS_PLACEHOLDER,
ATLAS_PLACEHOLDER2,
ATLAS_PAN,
ATLAS_MOVE,
ATLAS_ROTATE,
@@ -145,6 +147,7 @@ enum AtlasType
ATLAS_FRAME,
ATLAS_FRAME_ALT,
ATLAS_TARGET,
ATLAS_TARGET_ALT,
ATLAS_COUNT
};
@@ -197,7 +200,8 @@ const inline AtlasEntry ATLAS_ENTRIES[ATLAS_COUNT] =
{{ 0, 80}, ATLAS_SIZE_OBLONG},
{{16, 80}, ATLAS_SIZE_OBLONG},
{{32, 80}, ATLAS_SIZE_OBLONG},
{{48, 80}, ATLAS_SIZE_BIG}
{{48, 80}, ATLAS_SIZE_BIG},
{{48, 120}, ATLAS_SIZE_BIG}
};
#define ATLAS_POSITION(type) ATLAS_ENTRIES[type].position
@@ -217,6 +221,7 @@ enum ShaderType
{
SHADER_LINE,
SHADER_TEXTURE,
SHADER_AXIS,
SHADER_GRID,
SHADER_COUNT
};
@@ -234,6 +239,33 @@ void main()
}
)";
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;
@@ -260,17 +292,6 @@ void main()
}
)";
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_GRID_FRAGMENT = R"(
#version 330 core
in vec2 clip;
@@ -301,12 +322,13 @@ void main()
}
)";
#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"
@@ -315,5 +337,6 @@ 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}
};

View File

@@ -95,9 +95,9 @@ bool anm2_serialize(Anm2* self, const std::string& path)
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], null.name.c_str()); // Name
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID
// special case; only serialize if this is true
if (null.isShowRect)
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); // ShowRect
nullsElement->InsertEndChild(nullElement);
}
@@ -123,7 +123,9 @@ bool anm2_serialize(Anm2* self, const std::string& path)
// Animations
animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]);
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
if (self->defaultAnimationID != ID_NONE)
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
for (auto& [id, animation] : self->animations)
{
@@ -645,8 +647,7 @@ void anm2_layer_add(Anm2* self)
void anm2_layer_remove(Anm2* self, s32 id)
{
if (!self->layers.contains(id))
return;
if (!self->layers.contains(id)) return;
self->layers.erase(id);
@@ -740,18 +741,12 @@ void anm2_new(Anm2* self)
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference)
{
if (reference->animationID == ID_NONE) return nullptr;
if (!self->animations.contains(reference->animationID))
return nullptr;
return &self->animations[reference->animationID];
return map_find(self->animations, reference->animationID);
}
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
{
if (reference->itemType == ANM2_NONE)
return nullptr;
if (reference->itemType == ANM2_NONE) return nullptr;
Anm2Animation* animation = anm2_animation_from_reference(self, reference);
@@ -759,22 +754,13 @@ Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
switch (reference->itemType)
{
case ANM2_ROOT:
return &animation->rootAnimation;
case ANM2_LAYER:
if (!animation->layerAnimations.contains(reference->itemID)) return nullptr;
return &animation->layerAnimations[reference->itemID];
case ANM2_NULL:
if (!animation->nullAnimations.contains(reference->itemID)) return nullptr;
return &animation->nullAnimations[reference->itemID];
case ANM2_TRIGGERS:
return &animation->triggers;
default:
return nullptr;
case ANM2_ROOT: return &animation->rootAnimation;
case ANM2_LAYER: return map_find(animation->layerAnimations, reference->itemID);
case ANM2_NULL: return map_find(animation->nullAnimations, reference->itemID);
case ANM2_TRIGGERS: return &animation->triggers;
default: return nullptr;
}
}
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference)
{
Anm2Item* item = anm2_item_from_reference(self, reference);
@@ -911,40 +897,42 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference
{
Anm2Animation* animation = anm2_animation_from_reference(self, reference);
Anm2Item* item = anm2_item_from_reference(self, reference);
if (!animation || !item)
if (!animation || !item)
return nullptr;
if (item)
Anm2Frame frameAdd = frame ? *frame : Anm2Frame{};
s32 index = reference->frameIndex + 1;
if (reference->itemType == ANM2_TRIGGERS)
{
Anm2Frame frameAdd = frame ? *frame : Anm2Frame{};
s32 index = reference->frameIndex + 1;
s32 triggerIndex = time;
if (reference->itemType == ANM2_TRIGGERS)
for (auto& frameCheck : item->frames)
{
s32 index = time;
for (auto& frameCheck : item->frames)
if (frameCheck.atFrame == time)
{
if (frameCheck.atFrame == time)
{
index++;
break;
}
triggerIndex++;
break;
}
}
frameAdd.atFrame = index;
index = item->frames.size();
return &item->frames.emplace_back(frameAdd);
frameAdd.atFrame = triggerIndex;
return &item->frames.emplace_back(frameAdd);
}
else
{
if (index >= static_cast<s32>(item->frames.size()))
{
item->frames.push_back(frameAdd);
return &item->frames.back();
}
else
{
item->frames.insert(item->frames.begin() + index, frameAdd);
return &item->frames[index];
return &item->frames[static_cast<size_t>(index)];
}
}
return nullptr;
}
void anm2_frame_erase(Anm2* self, Anm2Reference* reference)

View File

@@ -22,7 +22,6 @@
#define ANM2_EXTENSION "anm2"
#define ANM2_SPRITESHEET_EXTENSION "png"
/* Elements */
#define ANM2_ELEMENT_LIST \
X(ANIMATED_ACTOR, "AnimatedActor") \
X(INFO, "Info") \
@@ -46,14 +45,16 @@
X(TRIGGERS, "Triggers") \
X(TRIGGER, "Trigger")
typedef enum {
typedef enum
{
#define X(name, str) ANM2_ELEMENT_##name,
ANM2_ELEMENT_LIST
#undef X
ANM2_ELEMENT_COUNT
} Anm2Element;
static const char* ANM2_ELEMENT_STRINGS[] = {
const inline char* ANM2_ELEMENT_STRINGS[] =
{
#define X(name, str) str,
ANM2_ELEMENT_LIST
#undef X
@@ -101,14 +102,16 @@ DEFINE_ENUM_TO_STRING_FUNCTION(ANM2_ELEMENT_ENUM_TO_STRING, ANM2_ELEMENT_STRINGS
X(EVENT_ID, "EventId") \
X(AT_FRAME, "AtFrame")
typedef enum {
typedef enum
{
#define X(name, str) ANM2_ATTRIBUTE_##name,
ANM2_ATTRIBUTE_LIST
#undef X
ANM2_ATTRIBUTE_COUNT
} Anm2Attribute;
static const char* ANM2_ATTRIBUTE_STRINGS[] = {
static const char* ANM2_ATTRIBUTE_STRINGS[] =
{
#define X(name, str) str,
ANM2_ATTRIBUTE_LIST
#undef X
@@ -211,7 +214,7 @@ struct Anm2
std::map<s32, Anm2Event> events;
std::map<s32, Anm2Animation> animations;
std::map<s32, s32> layerMap; // index, id
s32 defaultAnimationID{};
s32 defaultAnimationID = ID_NONE;
s32 fps = ANM2_FPS_DEFAULT;
s32 version{};
};
@@ -258,6 +261,12 @@ enum Anm2ChangeType
ANM2_CHANGE_SET
};
enum OnionskinDrawOrder
{
ONIONSKIN_BELOW,
ONIONSKIN_ABOVE
};
void anm2_layer_add(Anm2* self);
void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self);

View File

@@ -33,7 +33,7 @@ void canvas_init(Canvas* self, const ivec2& size)
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);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(f32), (void*)0);
// Grid
glGenVertexArrays(1, &self->gridVAO);
@@ -96,6 +96,8 @@ void canvas_init(Canvas* self, const ivec2& size)
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)
@@ -197,11 +199,17 @@ void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform,
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;
glUseProgram(shader);
glBindVertexArray(self->axisVAO);
glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
glDrawArrays(GL_LINES, 0, 4);
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);
}
@@ -218,6 +226,8 @@ void canvas_unbind(void)
void canvas_free(Canvas* self)
{
if (!self->isInit) return;
glDeleteFramebuffers(1, &self->fbo);
glDeleteRenderbuffers(1, &self->rbo);
glDeleteTextures(1, &self->framebuffer);

View File

@@ -5,23 +5,15 @@
#define CANVAS_ZOOM_MIN 1.0f
#define CANVAS_ZOOM_MAX 2000.0f
#define CANVAS_ZOOM_DEFAULT 100.0f
#define CANVAS_ZOOM_STEP 10.0f
#define CANVAS_ZOOM_MOD 10.0f
#define CANVAS_ZOOM_STEP 100.0f
#define CANVAS_GRID_MIN 1
#define CANVAS_GRID_MAX 1000
#define CANVAS_GRID_DEFAULT 32
#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
const inline vec2 CANVAS_PIVOT_SIZE = {8, 8};
const inline vec2 CANVAS_PIVOT_SIZE = {4, 4};
const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
const inline f32 CANVAS_AXIS_VERTICES[] =
{
-CANVAS_LINE_LENGTH, 0.0f,
CANVAS_LINE_LENGTH, 0.0f,
0.0f, -CANVAS_LINE_LENGTH,
0.0f, CANVAS_LINE_LENGTH
};
const inline f32 CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};
const inline f32 CANVAS_GRID_VERTICES[] =
{
@@ -62,6 +54,7 @@ struct Canvas
GLuint textureEBO{};
ivec2 size{};
ivec2 previousSize{};
bool isInit = false;
};
#define UV_VERTICES(uvMin, uvMax) \

View File

@@ -7,7 +7,6 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
case CLIPBOARD_FRAME:
{
Anm2FrameWithReference* frameWithReference = std::get_if<Anm2FrameWithReference>(&self->data);
if (!frameWithReference) break;
anm2_frame_erase(anm2, &frameWithReference->reference);
@@ -16,7 +15,6 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
case CLIPBOARD_ANIMATION:
{
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
if (!animationWithID) break;
for (auto & [id, animation] : anm2->animations)
@@ -58,7 +56,6 @@ static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* locati
case CLIPBOARD_ANIMATION:
{
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
if (!animationWithID) break;
s32 index = 0;

View File

@@ -26,9 +26,9 @@ void editor_draw(Editor* self)
canvas_viewport_set(&self->canvas);
canvas_clear(self->settings->editorBackgroundColor);
if (self->spritesheetID != ID_NONE)
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->spritesheetID))
{
Texture& texture = self->anm2->spritesheets[self->spritesheetID].texture;
Texture& texture = spritesheet->texture;
mat4 spritesheetTransform = transform * canvas_model_get(texture.size);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);

View File

@@ -53,15 +53,9 @@ ffmpeg_render
log_error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
return false;
}
size_t frameBytes = size.x * size.y * TEXTURE_CHANNELS;
// supposedly, might help with video corruption issues on windows?
#if _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
for (const auto& frame : frames)
{
std::vector<u8> rgba = texture_download(&frame);

View File

@@ -26,23 +26,27 @@ void generate_preview_draw(GeneratePreview* self)
canvas_clear(self->settings->previewBackgroundColor);
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
Texture& texture = self->anm2->spritesheets[self->anm2->layers[self->reference->itemID].spritesheetID].texture;
if (item && !texture.isInvalid)
if (item)
{
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);
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[self->reference->itemID].spritesheetID))
{
Texture& texture = spritesheet->texture;
vec2 uvMin = crop / vec2(texture.size);
vec2 uvMax = (crop + size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
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);
mat4 generateTransform = transform * canvas_model_get(size, {}, pivot);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
vec2 uvMin = crop / vec2(texture.size);
vec2 uvMax = (crop + size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
mat4 generateTransform = transform * canvas_model_get(size, {}, pivot);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
}
}
canvas_unbind();
}

View File

@@ -1,5 +1,27 @@
#include "imgui.h"
static bool _imgui_chord_pressed(ImGuiKeyChord chord)
{
if (chord == IMGUI_CHORD_NONE) return false;
ImGuiKey key = (ImGuiKey)(chord & ~ImGuiMod_Mask_);
if (key == ImGuiKey_None) return false;
if (key < ImGuiKey_NamedKey_BEGIN || key >= ImGuiKey_NamedKey_END) return false;
return ImGui::IsKeyChordPressed(chord);
}
static const char* _imgui_f32_format_get(const ImguiItem& item, f32& value)
{
if (item.isEmptyFormat) return "";
return f32_format_get(value);
}
static const char* _imgui_vec2_format_get(const ImguiItem& item, vec2& value)
{
if (item.isEmptyFormat) return "";
return vec2_format_get(value);
}
static bool _imgui_window_color_from_position_get(SDL_Window* self, const vec2& position, vec4& color)
{
ImGuiIO& io = ImGui::GetIO();
@@ -26,7 +48,6 @@ static void _imgui_anm2_open(Imgui* self, const std::string& path)
if (anm2_deserialize(self->anm2, path))
{
window_title_from_path_set(self->window, path);
snapshots_reset(self->snapshots);
imgui_log_push(self, std::format(IMGUI_LOG_FILE_OPEN_FORMAT, path));
}
else
@@ -35,15 +56,16 @@ static void _imgui_anm2_open(Imgui* self, const std::string& path)
static void _imgui_spritesheet_add(Imgui* self, const std::string& path)
{
std::filesystem::path workingPath = std::filesystem::current_path();
std::string spritesheetPath = path;
if (!self->anm2->path.empty())
if (self->anm2->path.empty())
{
std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string();
imgui_log_push(self, IMGUI_LOG_NO_ANM2_PATH);
return;
}
std::filesystem::path workingPath = std::filesystem::current_path();
std::string spritesheetPath = path;
std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string();
s32 id = map_next_id_get(self->anm2->spritesheets);
self->anm2->spritesheets[id] = Anm2Spritesheet{};
self->anm2->spritesheets[id].path = spritesheetPath;
@@ -139,6 +161,7 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
break;
case IMGUI_INPUT_INT:
case IMGUI_INPUT_TEXT:
case IMGUI_INPUT_FLOAT:
case IMGUI_DRAG_FLOAT:
case IMGUI_SLIDER_FLOAT:
case IMGUI_COLOR_EDIT:
@@ -165,6 +188,16 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
default:
break;
}
/*
if (self.is_hotkey())
{
std::string chordString = imgui_string_from_chord_get(imgui->hotkeys[self.hotkey]);
if (isShortcutInLabel)
label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
}
*/
}
static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType type, bool& isActivated)
@@ -188,7 +221,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
ImU32 color = ImGui::GetColorU32(ImGuiCol_Text);
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, color, 1.0f);
if (ImGui::IsKeyChordPressed(ImGuiMod_Alt | self.mnemonicKey))
if (_imgui_chord_pressed(ImGuiMod_Alt | self.mnemonicKey))
{
if (!self.isDisabled) isActivated = true;
imgui_close_current_popup(imgui);
@@ -197,19 +230,20 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
if (self.isUseItemActivated && !self.isDisabled) isActivated = ImGui::IsItemActivated();
if (imgui->isContextualActionsEnabled && (self.is_chord() && ImGui::IsKeyChordPressed(self.chord)))
if (self.is_focus_window() && (imgui_window_get() == self.focusWindow))
if (!self.isDisabled) isActivated = true;
if
(
imgui->isContextualActionsEnabled && _imgui_chord_pressed(self.chord_get()) &&
self.is_focus_window() && (imgui_window_get() == self.focusWindow)
)
if (!self.isDisabled) isActivated = true;
if (self.is_tooltip() && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal))
ImGui::SetTooltip(self.tooltip.c_str());
if (isActivated)
{
if (self.is_undoable())
imgui_snapshot(imgui, self.snapshotAction);
if (self.function) self.function(imgui);
if (self.is_undoable()) imgui_snapshot(imgui, self.snapshotAction);
if (self.is_function()) self.function(imgui);
if (self.is_popup())
{
@@ -257,7 +291,6 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
if (self.isSeparator) ImGui::Separator();
}
#define IMGUI_ITEM_FUNCTION(NAME, TYPE, FUNCTION) \
static bool NAME(ImguiItem self, Imgui* imgui) \
{ \
@@ -359,6 +392,8 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, bool& boolValue)
ImguiItem checkboxItem = self.copy \
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
checkboxItem.isDisabled = false; \
checkboxItem.isSeparator = false; \
checkboxItem.value = 0; \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&] { return FUNCTION; })(); \
@@ -373,6 +408,18 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, VALUE& value, bool& boolVa
ImguiItem checkboxItem = self.copy \
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
checkboxItem.isDisabled = false; \
checkboxItem.isSeparator = false; \
checkboxItem.value = 0; \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&](VALUE& value) { return FUNCTION; })(value); \
if (isCheckboxActivated) isActivated = true; \
return isActivated; \
}
#define IMGUI_ITEM_AND_CHECKBOX_FUNCTION(NAME, VALUE, FUNCTION) \
static bool NAME(const ImguiItem& self, const ImguiItem& checkboxItem, Imgui* imgui, VALUE& value, bool& boolValue) \
{ \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&](VALUE& value) { return FUNCTION; })(value); \
@@ -390,16 +437,22 @@ IMGUI_ITEM_FUNCTION(_imgui_begin_child, IMGUI_CHILD, ImGui::BeginChild(self.labe
static void _imgui_end_child(void) {ImGui::EndChild(); }
IMGUI_ITEM_VOID_FUNCTION(_imgui_text, IMGUI_TEXT, ImGui::Text(self.label_get()));
IMGUI_ITEM_FUNCTION(_imgui_button, IMGUI_BUTTON, ImGui::Button(self.label_get(), _imgui_item_size_get(self, type)));
IMGUI_ITEM_FUNCTION(_imgui_begin_table, IMGUI_TABLE, ImGui::BeginTable(self.label_get(), self.value, self.flags));
static void _imgui_end_table(void) {ImGui::EndTable(); }
static void _imgui_table_setup_column(const char* text) {ImGui::TableSetupColumn(text); }
static void _imgui_table_headers_row(void) {ImGui::TableHeadersRow(); }
static void _imgui_table_next_row(void) {ImGui::TableNextRow(); }
static void _imgui_table_set_column_index(s32 index) {ImGui::TableSetColumnIndex(index); }
IMGUI_ITEM_FUNCTION(_imgui_selectable, IMGUI_SELECTABLE, ImGui::Selectable(self.label_get(), self.isSelected, self.flags, _imgui_item_size_get(self, type)));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_radio_button, IMGUI_RADIO_BUTTON, s32, ImGui::RadioButton(self.label_get(), &value, self.value));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_button, IMGUI_COLOR_BUTTON, vec4, ImGui::ColorButton(self.label_get(), ImVec4(value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_checkbox, IMGUI_CHECKBOX, bool, ImGui::Checkbox(self.label_get(), &value));
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int, IMGUI_INPUT_INT, s32, ImGui::InputInt(self.label_get(), &value, self.step, self.stepFast, self.flags));
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int2, IMGUI_INPUT_INT, ivec2, ImGui::InputInt2(self.label_get(), value_ptr(value), self.flags));
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_float, IMGUI_INPUT_FLOAT, f32, ImGui::InputFloat(self.label_get(), &value, self.step, self.stepFast, f32_format_get(value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_slider_float, IMGUI_SLIDER_FLOAT, f32, ImGui::SliderFloat(self.label_get(), &value, self.min, self.max, f32_format_get(value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float, IMGUI_DRAG_FLOAT, f32, ImGui::DragFloat(self.label_get(), &value, self.speed, self.min, self.max, f32_format_get(value)));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float2, IMGUI_DRAG_FLOAT, vec2, ImGui::DragFloat2(self.label_get(), value_ptr(value), self.speed, self.min, self.max, vec2_format_get(value)));
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_float, IMGUI_INPUT_FLOAT, f32, ImGui::InputFloat(self.label_get(), &value, self.step, self.stepFast, _imgui_f32_format_get(self, value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_slider_float, IMGUI_SLIDER_FLOAT, f32, ImGui::SliderFloat(self.label_get(), &value, self.min, self.max, _imgui_f32_format_get(self, value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float, IMGUI_DRAG_FLOAT, f32, ImGui::DragFloat(self.label_get(), &value, self.speed, self.min, self.max, _imgui_f32_format_get(self, value)));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float2, IMGUI_DRAG_FLOAT, vec2, ImGui::DragFloat2(self.label_get(), value_ptr(value), self.speed, self.min, self.max, _imgui_vec2_format_get(self, value)));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_edit3, IMGUI_COLOR_EDIT, vec3, ImGui::ColorEdit3(self.label_get(), value_ptr(value), self.flags));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_edit4, IMGUI_COLOR_EDIT, vec4, ImGui::ColorEdit4(self.label_get(), value_ptr(value), self.flags));
IMGUI_ITEM_CHECKBOX_FUNCTION(_imgui_checkbox_selectable, _imgui_selectable(self, imgui));
@@ -1199,7 +1252,7 @@ static void _imgui_timeline(Imgui* self)
if (_imgui_button(IMGUI_ADD_FRAME.copy({!item}), self))
{
Anm2Reference frameReference = *self->reference;
frameReference.frameIndex = std::clamp(frameReference.frameIndex, 0, (s32)item->frames.size() - 1);
frameReference.frameIndex = item->frames.empty() ? 0 : std::clamp(frameReference.frameIndex, 0, static_cast<s32>(item->frames.size() - 1));
Anm2Frame* addFrame = anm2_frame_from_reference(self->anm2, &frameReference);
anm2_frame_add(self->anm2, addFrame, &frameReference);
}
@@ -1251,6 +1304,40 @@ static void _imgui_timeline(Imgui* self)
_imgui_end(); // IMGUI_TIMELINE
}
static void _imgui_onionskin(Imgui* self)
{
IMGUI_BEGIN_OR_RETURN(IMGUI_ONIONSKIN, self);
static auto& isEnabled = self->settings->onionskinIsEnabled;
static auto& beforeCount = self->settings->onionskinBeforeCount;
static auto& afterCount = self->settings->onionskinAfterCount;
static auto& beforeColorOffset = self->settings->onionskinBeforeColorOffset;
static auto& afterColorOffset = self->settings->onionskinAfterColorOffset;
static auto& drawOrder = self->settings->onionskinDrawOrder;
_imgui_checkbox(IMGUI_ONIONSKIN_ENABLED, self, isEnabled);
auto onionskin_section = [&](auto& text, auto& count, auto& colorOffset)
{
ImGui::PushID(text.label.c_str());
_imgui_text(text, self);
_imgui_input_int(IMGUI_ONIONSKIN_COUNT.copy({!isEnabled}), self, count);
_imgui_color_edit3(IMGUI_ONIONSKIN_COLOR_OFFSET.copy({!isEnabled}), self, colorOffset);
ImGui::PopID();
};
onionskin_section(IMGUI_ONIONSKIN_BEFORE, beforeCount, beforeColorOffset);
onionskin_section(IMGUI_ONIONSKIN_AFTER, afterCount, afterColorOffset);
ImGui::Separator();
_imgui_text(IMGUI_ONIONSKIN_DRAW_ORDER, self);
_imgui_radio_button(IMGUI_ONIONSKIN_BELOW.copy({!isEnabled}), self, drawOrder);
_imgui_radio_button(IMGUI_ONIONSKIN_ABOVE.copy({!isEnabled}), self, drawOrder);
_imgui_end(); // IMGUI_ONIONSKIN
}
static void _imgui_taskbar(Imgui* self)
{
static ImguiPopupState exitConfirmState = IMGUI_POPUP_STATE_CLOSED;
@@ -1264,15 +1351,15 @@ static void _imgui_taskbar(Imgui* self)
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
_imgui_selectable(IMGUI_FILE.copy({}), self);
_imgui_selectable(IMGUI_FILE, self);
if (imgui_begin_popup(IMGUI_FILE.popup, self))
{
_imgui_selectable(IMGUI_NEW, self);
_imgui_selectable(IMGUI_NEW.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_OPEN, self);
_imgui_selectable(IMGUI_SAVE, self);
_imgui_selectable(IMGUI_SAVE_AS, self);
_imgui_selectable(IMGUI_EXPLORE_ANM2_LOCATION, self);
_imgui_selectable(IMGUI_SAVE.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_SAVE_AS.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_EXPLORE_ANM2_LOCATION.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_EXIT, self);
imgui_end_popup(self);
}
@@ -1324,7 +1411,7 @@ static void _imgui_taskbar(Imgui* self)
static auto& columns = self->settings->generateColumns;
static auto& count = self->settings->generateCount;
static auto& delay = self->settings->generateDelay;
static auto& time = self->generatePreview->time;
static f32& time = self->generatePreview->time;
_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD, self);
_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_START_POSITION, self, startPosition);
@@ -1427,6 +1514,7 @@ static void _imgui_taskbar(Imgui* self)
_imgui_checkbox_color_edit4(IMGUI_FRAME_PROPERTIES_TINT.copy({!isTint}), self, tint, isTint);
_imgui_checkbox_color_edit3(IMGUI_FRAME_PROPERTIES_COLOR_OFFSET.copy({!isColorOffset}), self, colorOffset, isColorOffset);
_imgui_checkbox_checkbox(IMGUI_FRAME_PROPERTIES_VISIBLE.copy({!isVisibleSet}), self, isVisible, isVisibleSet);
ImGui::NewLine();
_imgui_checkbox_checkbox(IMGUI_FRAME_PROPERTIES_INTERPOLATED.copy({!isInterpolatedSet}), self, isInterpolated, isInterpolatedSet);
_imgui_end_child(); // IMGUI_FOOTER_CHILD
@@ -1645,6 +1733,73 @@ static void _imgui_taskbar(Imgui* self)
if (imgui_begin_popup(IMGUI_SETTINGS.popup, self, IMGUI_SETTINGS.popupSize))
{
if (_imgui_checkbox_selectable(IMGUI_VSYNC, self, self->settings->isVsync)) window_vsync_set(self->settings->isVsync);
_imgui_selectable(IMGUI_HOTKEYS, self);
if (_imgui_selectable(IMGUI_DEFAULT_SETTINGS, self)) *self->settings = Settings();
imgui_end_popup(self);
}
if (imgui_begin_popup_modal(IMGUI_HOTKEYS.popup, self, IMGUI_HOTKEYS.popupSize))
{
_imgui_begin_child(IMGUI_HOTKEYS_CHILD, self);
if (_imgui_begin_table(IMGUI_HOTKEYS_TABLE, self))
{
static s32 selectedIndex = INDEX_NONE;
_imgui_table_setup_column(IMGUI_HOTKEYS_FUNCTION);
_imgui_table_setup_column(IMGUI_HOTKEYS_HOTKEY);
_imgui_table_headers_row();
for (s32 i = 0; i < HOTKEY_COUNT; i++)
{
if (!SETTINGS_HOTKEY_MEMBERS[i]) continue;
bool isSelected = selectedIndex == i;
const char* string = HOTKEY_STRINGS[i];
std::string* settingString = &(self->settings->*SETTINGS_HOTKEY_MEMBERS[i]);
std::string chordString = isSelected ? IMGUI_HOTKEY_CHANGE : *settingString;
ImGui::PushID(i);
_imgui_table_next_row();
_imgui_table_set_column_index(0);
ImGui::TextUnformatted(string);
_imgui_table_set_column_index(1);
if (ImGui::Selectable(chordString.c_str(), isSelected)) selectedIndex = i;
ImGui::PopID();
if (isSelected)
{
ImGuiKeyChord chord = IMGUI_CHORD_NONE;
if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) chord |= ImGuiMod_Ctrl;
if (ImGui::IsKeyDown(ImGuiMod_Shift)) chord |= ImGuiMod_Shift;
if (ImGui::IsKeyDown(ImGuiMod_Alt)) chord |= ImGuiMod_Alt;
if (ImGui::IsKeyDown(ImGuiMod_Super)) chord |= ImGuiMod_Super;
for (auto& [_, key] : IMGUI_KEY_MAP)
{
if (ImGui::IsKeyPressed(key))
{
chord |= key;
imgui_hotkey_chord_registry()[i] = chord;
*settingString = imgui_string_from_chord_get(chord);
selectedIndex = INDEX_NONE;
break;
}
}
}
}
_imgui_end_table();
}
_imgui_end_child(); // IMGUI_HOTKEYS_CHILD;
_imgui_begin_child(IMGUI_HOTKEYS_OPTIONS_CHILD, self);
if (_imgui_button(IMGUI_HOTKEYS_CONFIRM, self)) imgui_close_current_popup(self);
_imgui_end_child(); // IMGUI_HOTKEYS_OPTIONS_CHILD
imgui_end_popup(self);
}
@@ -1754,12 +1909,12 @@ static void _imgui_animations(Imgui* self)
_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_ANIMATION_ADD, self))
if (_imgui_button(IMGUI_ANIMATION_ADD.copy({self->anm2->path.empty()}), self))
{
s32 id = anm2_animation_add(self->anm2);
self->reference->animationID = id;
if (self->anm2->animations.size() == 0)
if (self->anm2->animations.size() == 1)
self->anm2->defaultAnimationID = id;
}
@@ -1924,7 +2079,7 @@ static void _imgui_events(Imgui* self)
_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_EVENTS_ADD, self))
if (_imgui_button(IMGUI_EVENTS_ADD.copy({self->anm2->path.empty()}), self))
{
s32 id = map_next_id_get(self->anm2->events);
self->anm2->events[id] = Anm2Event{};
@@ -2027,8 +2182,8 @@ static void _imgui_spritesheets(Imgui* self)
_imgui_begin_child(IMGUI_SPRITESHEETS_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_SPRITESHEET_ADD, self))
dialog_spritesheet_add(self->dialog);
if (_imgui_button(IMGUI_SPRITESHEET_ADD.copy({self->anm2->path.empty()}), self))
dialog_spritesheet_add(self->dialog);
if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_ADD)
{
@@ -2142,7 +2297,7 @@ static void _imgui_animation_preview(Imgui* self)
_imgui_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom);
if (_imgui_button(IMGUI_CANVAS_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
if (_imgui_button(IMGUI_ANIMATION_PREVIEW_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
ImGui::Text(mousePositionString.c_str());
_imgui_end_child(); //IMGUI_CANVAS_VIEW_CHILD
@@ -2180,6 +2335,8 @@ static void _imgui_animation_preview(Imgui* self)
_imgui_checkbox(IMGUI_CANVAS_AXES, self, self->settings->previewIsAxes);
ImGui::SameLine();
_imgui_color_edit4(IMGUI_CANVAS_AXES_COLOR, self, self->settings->previewAxesColor);
ImGui::SameLine();
_imgui_checkbox(IMGUI_CANVAS_ALT_ICONS, self, self->settings->previewIsAltIcons);
_imgui_checkbox(IMGUI_CANVAS_ROOT_TRANSFORM, self, self->settings->previewIsRootTransform);
ImGui::SameLine();
_imgui_checkbox(IMGUI_CANVAS_TRIGGERS, self, self->settings->previewIsTriggers);
@@ -2227,8 +2384,8 @@ static void _imgui_animation_preview(Imgui* self)
const bool isUp = ImGui::IsKeyPressed(IMGUI_INPUT_UP);
const bool isDown = ImGui::IsKeyPressed(IMGUI_INPUT_DOWN);
const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_OUT);
const bool isZoomIn = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_IN]);
const bool isZoomOut = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_OUT]);
const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
@@ -2292,7 +2449,6 @@ static void _imgui_animation_preview(Imgui* self)
if (mouseWheel != 0 || isZoomIn || isZoomOut)
{
f32 delta = (mouseWheel > 0 || isZoomIn) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP;
delta = isMod ? delta * CANVAS_ZOOM_MOD : delta;
zoom = std::clamp(ROUND_NEAREST_MULTIPLE(zoom + delta, CANVAS_ZOOM_STEP), CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX);
}
}
@@ -2326,7 +2482,7 @@ static void _imgui_spritesheet_editor(Imgui* self)
_imgui_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom);
if (_imgui_button(IMGUI_CANVAS_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
if (_imgui_button(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
ImGui::Text(mousePositionString.c_str());
_imgui_end_child(); // IMGUI_CANVAS_VIEW_CHILD
@@ -2357,9 +2513,8 @@ static void _imgui_spritesheet_editor(Imgui* self)
const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_OUT);
const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
const bool isZoomIn = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_IN]);
const bool isZoomOut = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_OUT]);
const f32 mouseWheel = ImGui::GetIO().MouseWheel;
const ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
@@ -2401,9 +2556,9 @@ static void _imgui_spritesheet_editor(Imgui* self)
if (!texture) break;
vec4 color = tool == TOOL_ERASE ? COLOR_TRANSPARENT : toolColor;
if (isMouseClick)
imgui_snapshot(self, IMGUI_ACTION_DRAW);
imgui_snapshot(self, tool == TOOL_DRAW ? IMGUI_ACTION_DRAW : IMGUI_ACTION_ERASE);
if (isMouseDown)
texture_pixel_set(texture, position, color);
@@ -2428,7 +2583,6 @@ static void _imgui_spritesheet_editor(Imgui* self)
if (mouseWheel != 0 || isZoomIn || isZoomOut)
{
f32 delta = (mouseWheel > 0 || isZoomIn) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP;
delta = isMod ? delta * CANVAS_ZOOM_MOD : delta;
zoom = std::clamp(ROUND_NEAREST_MULTIPLE(zoom + delta, CANVAS_ZOOM_STEP), CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX);
}
}
@@ -2443,7 +2597,7 @@ static void _imgui_frame_properties(Imgui* self)
bool isLayerFrame = frame && type == ANM2_LAYER;
if (type != ANM2_TRIGGERS || !frame)
if (!frame || type != ANM2_TRIGGERS)
{
_imgui_drag_float2(IMGUI_FRAME_PROPERTIES_CROP.copy({!isLayerFrame}), self, !isLayerFrame ? dummy_value<vec2>() : frame->crop);
_imgui_drag_float2(IMGUI_FRAME_PROPERTIES_SIZE.copy({!isLayerFrame}), self, !isLayerFrame ? dummy_value<vec2>() : frame->size);
@@ -2524,7 +2678,6 @@ static void _imgui_log(Imgui* self)
}
}
static void _imgui_dock(Imgui* self)
{
ImguiItem window = IMGUI_WINDOW_MAIN;
@@ -2544,6 +2697,7 @@ static void _imgui_dock(Imgui* self)
_imgui_animation_preview(self);
_imgui_spritesheet_editor(self);
_imgui_timeline(self);
_imgui_onionskin(self);
_imgui_frame_properties(self);
_imgui_end(); // IMGUI_WINDOW_MAIN
@@ -2594,6 +2748,12 @@ void imgui_init
io.ConfigWindowsMoveFromTitleBarOnly = true;
ImGui::LoadIniSettingsFromDisk(settings_path_get().c_str());
for (s32 i = 0; i < HOTKEY_COUNT; i++)
{
if (!SETTINGS_HOTKEY_MEMBERS[i]) continue;
imgui_hotkey_chord_registry()[i] = imgui_chord_from_string_get(*&(self->settings->*SETTINGS_HOTKEY_MEMBERS[i]));
}
}
void imgui_update(Imgui* self)
@@ -2608,13 +2768,13 @@ void imgui_update(Imgui* self)
if (self->isContextualActionsEnabled)
{
for (const auto& hotkey : imgui_hotkey_registry())
for (const auto& item : imgui_item_registry())
{
if (ImGui::IsKeyChordPressed(hotkey.chord))
if (item->is_chord() && _imgui_chord_pressed(item->chord_get()))
{
if (hotkey.is_undoable()) imgui_snapshot(self, hotkey.snapshotAction);
if (hotkey.is_focus_window()) continue;
hotkey.function(self);
if (item->is_undoable()) imgui_snapshot(self, item->snapshotAction);
if (item->is_focus_window()) continue;
if (item->is_function()) item->function(self);
}
}
}
@@ -2658,7 +2818,6 @@ void imgui_update(Imgui* self)
break;
}
}
}
void imgui_draw(void)
@@ -2669,6 +2828,8 @@ void imgui_draw(void)
void imgui_free(void)
{
if (!ImGui::GetCurrentContext()) return;
ImGui_ImplSDL3_Shutdown();
ImGui_ImplOpenGL3_Shutdown();
ImGui::SaveIniSettingsToDisk(settings_path_get().c_str());

View File

@@ -52,14 +52,15 @@
#define IMGUI_FRAME_BORDER 2.0f
#define IMGUI_LOG_DURATION 3.0f
#define IMGUI_LOG_PADDING 10.0f
#define IMGUI_PLAYHEAD_LINE_COLOR IM_COL32(255, 255, 255, 255)
#define IMGUI_TRIGGERS_EVENT_COLOR IM_COL32(255, 255, 255, 128)
#define IMGUI_PLAYHEAD_LINE_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX)
#define IMGUI_TRIGGERS_EVENT_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, 128)
#define IMGUI_PLAYHEAD_LINE_WIDTH 2.0f
#define IMGUI_SPRITESHEETS_FOOTER_HEIGHT 65
#define IMGUI_TIMELINE_FRAME_MULTIPLE 5
#define IMGUI_TIMELINE_MERGE
#define IMGUI_TOOL_COLOR_PICKER_DURATION 0.25f
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
#define IMGUI_CHORD_REPEAT_TIME 0.25f
#define IMGUI_ACTION_FRAME_CROP "Frame Crop"
#define IMGUI_ACTION_FRAME_SWAP "Frame Swap"
@@ -69,12 +70,15 @@
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
#define IMGUI_ACTION_DRAW "Draw"
#define IMGUI_ACTION_ERASE "Erase"
#define IMGUI_ACTION_RELOAD_SPRITESHEET "Reload Spritesheet(s)"
#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet"
#define IMGUI_ACTION_OPEN_FILE "Open File"
#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
#define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize
#define IMGUI_LOG_NO_ANM2_PATH "Please save the .anm2 to a path first!"
#define IMGUI_LOG_FILE_OPEN_FORMAT "Opened anm2: {}"
#define IMGUI_LOG_FILE_SAVE_FORMAT "Saved anm2 to: {}"
#define IMGUI_LOG_SPRITESHEET_RELOAD "Reloaded selected spritesheets"
@@ -91,7 +95,7 @@
#define IMGUI_NONE "None"
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
#define IMGUI_BUFFER_MAX 255
#define IMGUI_BUFFER_MAX UCHAR_MAX
#define IMGUI_INVISIBLE_LABEL_MARKER "##"
#define IMGUI_ITEM_SELECTABLE_EDITABLE_LABEL "## Editing"
#define IMGUI_LOG_FORMAT "## Log {}"
@@ -105,7 +109,7 @@
#define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}"
#define IMGUI_SELECTABLE_INPUT_INT_FORMAT "#{}"
#define IMGUI_TIMELINE_ANIMATION_NONE "Select an animation to show timeline..."
#define IMGUI_HOTKEY_CHANGE "Input new hotkey..."
#define IMGUI_LABEL_SHORTCUT_FORMAT "({})"
#define IMGUI_TOOLTIP_SHORTCUT_FORMAT "\n(Shortcut: {})"
#define IMGUI_INVISIBLE_FORMAT "## {}"
@@ -204,18 +208,13 @@ struct Imgui
bool isTryQuit = false;
};
typedef void(*ImguiFunction)(Imgui*);
struct ImguiHotkey
static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
{
ImGuiKeyChord chord;
ImguiFunction function;
std::string focusWindow{};
std::string snapshotAction{};
bool is_focus_window() const { return !focusWindow.empty(); }
bool is_undoable() const { return !snapshotAction.empty(); }
};
self->snapshots->action = action;
Snapshot snapshot = snapshot_get(self->snapshots);
snapshots_undo_push(self->snapshots, &snapshot);
}
static void imgui_log_push(Imgui* self, const std::string& text)
{
@@ -223,12 +222,6 @@ static void imgui_log_push(Imgui* self, const std::string& text)
log_imgui(text);
}
static std::vector<ImguiHotkey>& imgui_hotkey_registry()
{
static std::vector<ImguiHotkey> registry;
return registry;
}
static inline void imgui_file_new(Imgui* self)
{
anm2_reference_clear(self->reference);
@@ -272,12 +265,6 @@ static inline void imgui_explore(Imgui* self)
dialog_explorer_open(parentPath.string());
}
static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
{
Snapshot snapshot = snapshot_get(self->snapshots);
snapshots_undo_push(self->snapshots, &snapshot);
}
static inline void imgui_tool_pan_set(Imgui* self)
{
self->settings->tool = TOOL_PAN;
@@ -350,6 +337,125 @@ static inline void imgui_paste(Imgui* self)
clipboard_paste(self->clipboard);
}
static inline void imgui_onionskin_toggle(Imgui* self)
{
self->settings->onionskinIsEnabled = !self->settings->onionskinIsEnabled;
}
static const std::unordered_map<std::string, ImGuiKey> IMGUI_KEY_MAP =
{
{ "A", ImGuiKey_A },
{ "B", ImGuiKey_B },
{ "C", ImGuiKey_C },
{ "D", ImGuiKey_D },
{ "E", ImGuiKey_E },
{ "F", ImGuiKey_F },
{ "G", ImGuiKey_G },
{ "H", ImGuiKey_H },
{ "I", ImGuiKey_I },
{ "J", ImGuiKey_J },
{ "K", ImGuiKey_K },
{ "L", ImGuiKey_L },
{ "M", ImGuiKey_M },
{ "N", ImGuiKey_N },
{ "O", ImGuiKey_O },
{ "P", ImGuiKey_P },
{ "Q", ImGuiKey_Q },
{ "R", ImGuiKey_R },
{ "S", ImGuiKey_S },
{ "T", ImGuiKey_T },
{ "U", ImGuiKey_U },
{ "V", ImGuiKey_V },
{ "W", ImGuiKey_W },
{ "X", ImGuiKey_X },
{ "Y", ImGuiKey_Y },
{ "Z", ImGuiKey_Z },
{ "0", ImGuiKey_0 },
{ "1", ImGuiKey_1 },
{ "2", ImGuiKey_2 },
{ "3", ImGuiKey_3 },
{ "4", ImGuiKey_4 },
{ "5", ImGuiKey_5 },
{ "6", ImGuiKey_6 },
{ "7", ImGuiKey_7 },
{ "8", ImGuiKey_8 },
{ "9", ImGuiKey_9 },
{ "Num0", ImGuiKey_Keypad0 },
{ "Num1", ImGuiKey_Keypad1 },
{ "Num2", ImGuiKey_Keypad2 },
{ "Num3", ImGuiKey_Keypad3 },
{ "Num4", ImGuiKey_Keypad4 },
{ "Num5", ImGuiKey_Keypad5 },
{ "Num6", ImGuiKey_Keypad6 },
{ "Num7", ImGuiKey_Keypad7 },
{ "Num8", ImGuiKey_Keypad8 },
{ "Num9", ImGuiKey_Keypad9 },
{ "NumAdd", ImGuiKey_KeypadAdd },
{ "NumSubtract", ImGuiKey_KeypadSubtract },
{ "NumMultiply", ImGuiKey_KeypadMultiply },
{ "NumDivide", ImGuiKey_KeypadDivide },
{ "NumEnter", ImGuiKey_KeypadEnter },
{ "NumDecimal", ImGuiKey_KeypadDecimal },
{ "F1", ImGuiKey_F1 },
{ "F2", ImGuiKey_F2 },
{ "F3", ImGuiKey_F3 },
{ "F4", ImGuiKey_F4 },
{ "F5", ImGuiKey_F5 },
{ "F6", ImGuiKey_F6 },
{ "F7", ImGuiKey_F7 },
{ "F8", ImGuiKey_F8 },
{ "F9", ImGuiKey_F9 },
{ "F10", ImGuiKey_F10 },
{ "F11", ImGuiKey_F11 },
{ "F12", ImGuiKey_F12 },
{ "Up", ImGuiKey_UpArrow },
{ "Down", ImGuiKey_DownArrow },
{ "Left", ImGuiKey_LeftArrow },
{ "Right", ImGuiKey_RightArrow },
{ "Space", ImGuiKey_Space },
{ "Enter", ImGuiKey_Enter },
{ "Escape", ImGuiKey_Escape },
{ "Tab", ImGuiKey_Tab },
{ "Backspace", ImGuiKey_Backspace },
{ "Delete", ImGuiKey_Delete },
{ "Insert", ImGuiKey_Insert },
{ "Home", ImGuiKey_Home },
{ "End", ImGuiKey_End },
{ "PageUp", ImGuiKey_PageUp },
{ "PageDown", ImGuiKey_PageDown },
{ "Minus", ImGuiKey_Minus },
{ "Equal", ImGuiKey_Equal },
{ "LeftBracket", ImGuiKey_LeftBracket },
{ "RightBracket", ImGuiKey_RightBracket },
{ "Semicolon", ImGuiKey_Semicolon },
{ "Apostrophe", ImGuiKey_Apostrophe },
{ "Comma", ImGuiKey_Comma },
{ "Period", ImGuiKey_Period },
{ "Slash", ImGuiKey_Slash },
{ "Backslash", ImGuiKey_Backslash },
{ "GraveAccent", ImGuiKey_GraveAccent },
{ "MouseLeft", ImGuiKey_MouseLeft },
{ "MouseRight", ImGuiKey_MouseRight },
{ "MouseMiddle", ImGuiKey_MouseMiddle },
{ "MouseX1", ImGuiKey_MouseX1 },
{ "MouseX2", ImGuiKey_MouseX2 }
};
static std::unordered_map<std::string, ImGuiKey> IMGUI_MOD_MAP =
{
{ "Ctrl", ImGuiMod_Ctrl },
{ "Shift", ImGuiMod_Shift },
{ "Alt", ImGuiMod_Alt },
{ "Super", ImGuiMod_Super },
};
static inline ImGuiKey imgui_key_from_char_get(char c)
{
if (c >= 'a' && c <= 'z') c -= 'a' - 'A';
@@ -364,16 +470,12 @@ static inline std::string imgui_string_from_chord_get(ImGuiKeyChord chord)
if (chord & ImGuiMod_Ctrl) result += "Ctrl+";
if (chord & ImGuiMod_Shift) result += "Shift+";
if (chord & ImGuiMod_Alt) result += "Alt+";
if (chord & ImGuiMod_Super) result += "Super+";
ImGuiKey key = (ImGuiKey)(chord & ~ImGuiMod_Mask_);
if (key >= ImGuiKey_A && key <= ImGuiKey_Z)
result.push_back('A' + (key - ImGuiKey_A));
else if (key >= ImGuiKey_0 && key <= ImGuiKey_9)
result.push_back('0' + (key - ImGuiKey_0));
else
if (key != ImGuiKey_None)
{
// Fallback to ImGui's built-in name for non-alphanumerics
const char* name = ImGui::GetKeyName(key);
if (name && *name)
result += name;
@@ -381,9 +483,44 @@ static inline std::string imgui_string_from_chord_get(ImGuiKeyChord chord)
result += "Unknown";
}
if (!result.empty() && result.back() == '+')
result.pop_back();
return result;
}
static inline ImGuiKeyChord imgui_chord_from_string_get(const std::string& str)
{
ImGuiKeyChord chord = 0;
ImGuiKey baseKey = ImGuiKey_None;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, '+'))
{
// trim
token.erase(0, token.find_first_not_of(" \t\r\n"));
token.erase(token.find_last_not_of(" \t\r\n") + 1);
if (token.empty())
continue;
if (auto it = IMGUI_MOD_MAP.find(token); it != IMGUI_MOD_MAP.end()) {
chord |= it->second;
}
else if (baseKey == ImGuiKey_None) {
if (auto it2 = IMGUI_KEY_MAP.find(token); it2 != IMGUI_KEY_MAP.end())
baseKey = it2->second;
}
}
if (baseKey != ImGuiKey_None)
chord |= baseKey;
return chord;
}
static void imgui_contextual_actions_enable(Imgui* self) { self->isContextualActionsEnabled = true; }
static void imgui_contextual_actions_disable(Imgui* self){ self->isContextualActionsEnabled = false; }
static inline bool imgui_is_popup_open(const std::string& label) { return ImGui::IsPopupOpen(label.c_str()); }
@@ -446,6 +583,7 @@ enum ImguiItemType
IMGUI_WINDOW,
IMGUI_DOCKSPACE,
IMGUI_CHILD,
IMGUI_TABLE,
IMGUI_OPTION_POPUP,
IMGUI_SELECTABLE,
IMGUI_BUTTON,
@@ -475,6 +613,22 @@ struct ImguiItemOverride
s32 value{};
};
struct ImguiItem;
static std::vector<ImguiItem*>& imgui_item_registry(void)
{
static std::vector<ImguiItem*> registry;
return registry;
}
static ImGuiKeyChord* imgui_hotkey_chord_registry(void)
{
static ImGuiKeyChord registry[HOTKEY_COUNT];
return registry;
}
typedef void(*ImguiFunction)(Imgui*);
// Item
struct ImguiItem
{
@@ -486,8 +640,9 @@ struct ImguiItem
std::string dragDrop{};
std::string focusWindow{};
std::vector<std::string> items{};
AtlasType atlas;
AtlasType atlas = ATLAS_NONE;
ImGuiKeyChord chord = IMGUI_CHORD_NONE;
HotkeyType hotkey = HOTKEY_NONE;
ImGuiKey mnemonicKey = ImGuiKey_None;
s32 mnemonicIndex = INDEX_NONE;
ImVec2 size{};
@@ -497,6 +652,7 @@ struct ImguiItem
ImguiPopupType popupType = IMGUI_POPUP_CENTER_WINDOW;
bool isDisabled = false;
bool isMnemonicDisabled = false;
bool isEmptyFormat = false;
bool isSelected = false;
bool isUseItemActivated = false;
bool isSizeToText = false;
@@ -511,7 +667,7 @@ struct ImguiItem
s32 min{};
s32 max{};
s32 value{};
vec2 atlasOffset;
vec2 atlasOffset{};
s32 border{};
s32 flags{};
s32 windowFlags{};
@@ -521,17 +677,9 @@ struct ImguiItem
{
static s32 idNew = 0;
id = idNew++;
if (is_chord())
{
std::string chordString = imgui_string_from_chord_get(chord);
if (isShortcutInLabel)
label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
if (function)
imgui_hotkey_registry().push_back({chord, function, focusWindow, snapshotAction});
}
imgui_item_registry().push_back(this);
std::string labelNew{};
for (s32 i = 0; i < (s32)label.size(); i++)
@@ -551,7 +699,7 @@ struct ImguiItem
i++;
}
}
else
else
labelNew += label[i];
}
@@ -574,12 +722,20 @@ struct ImguiItem
return out;
}
ImGuiKeyChord chord_get() const
{
if (is_hotkey()) return imgui_hotkey_chord_registry()[hotkey];
return chord;
}
bool is_border() const { return border != 0; }
bool is_row() const { return rowCount != 0; }
bool is_chord() const { return chord != IMGUI_CHORD_NONE; }
bool is_hotkey() const { return hotkey != HOTKEY_NONE; }
bool is_chord() const { return chord != IMGUI_CHORD_NONE || is_hotkey(); }
bool is_drag_drop() const { return !dragDrop.empty(); }
bool is_focus_window() const { return !focusWindow.empty(); }
bool is_popup() const { return !popup.empty(); }
bool is_function() const { return function; }
bool is_size() const { return size != ImVec2(); }
bool is_popup_size() const { return popupSize != ImVec2(); }
bool is_tooltip() const { return !tooltip.empty(); }
@@ -639,7 +795,7 @@ IMGUI_ITEM(IMGUI_NEW,
self.label = "&New ",
self.tooltip = "Load a blank .anm2 file to edit.",
self.function = imgui_file_new,
self.chord = ImGuiMod_Ctrl | ImGuiKey_N,
self.hotkey = HOTKEY_NEW,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -648,7 +804,7 @@ IMGUI_ITEM(IMGUI_OPEN,
self.label = "&Open ",
self.tooltip = "Open an existing .anm2 file to edit.",
self.function = imgui_file_open,
self.chord = ImGuiMod_Ctrl | ImGuiKey_O,
self.hotkey = HOTKEY_OPEN,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -657,7 +813,7 @@ IMGUI_ITEM(IMGUI_SAVE,
self.label = "&Save ",
self.tooltip = "Saves the current .anm2 file to its path.\nIf no path exists, one can be chosen.",
self.function = imgui_file_save,
self.chord = ImGuiMod_Ctrl | ImGuiKey_S,
self.hotkey = HOTKEY_SAVE,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -666,7 +822,7 @@ IMGUI_ITEM(IMGUI_SAVE_AS,
self.label = "S&ave As ",
self.tooltip = "Saves the current .anm2 file to a chosen path.",
self.function = imgui_file_save_as,
self.chord = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_S,
self.hotkey = HOTKEY_SAVE_AS,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -683,7 +839,7 @@ IMGUI_ITEM(IMGUI_EXIT,
self.label = "&Exit ",
self.tooltip = "Exits the program.",
self.function = imgui_quit,
self.chord = ImGuiMod_Alt | ImGuiKey_F4,
self.hotkey = HOTKEY_EXIT,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -693,6 +849,11 @@ IMGUI_ITEM(IMGUI_EXIT_CONFIRMATION,
self.text = "Unsaved changes will be lost!\nAre you sure you want to exit?"
);
IMGUI_ITEM(IMGUI_OPEN_CONFIRMATION,
self.label = "Open Confirmation",
self.text = "Unsaved changes will be lost!\nAre you sure you open a new file?"
);
IMGUI_ITEM(IMGUI_WIZARD,
self.label = "&Wizard",
self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
@@ -743,12 +904,14 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_PIVOT,
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_ROWS,
self.label = "Rows",
self.tooltip = "Set how many rows will be used in the generated animation.",
self.min = 1,
self.max = 1000
);
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_COLUMNS,
self.label = "Columns",
self.tooltip = "Set how many columns will be used in the generated animation.",
self.min = 1,
self.max = 1000
);
@@ -791,6 +954,7 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER,
self.max = GENERATE_PREVIEW_TIME_MAX,
self.value = GENERATE_PREVIEW_TIME_MIN,
self.rowCount = 1,
self.isEmptyFormat = true,
self.flags = ImGuiSliderFlags_NoInput
);
@@ -807,7 +971,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES,
self.tooltip = "Change all frame properties in the selected animation item (or selected frame).",
self.popup = "Change All Frame Properties",
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {500, 380}
self.popupSize = {500, 405}
);
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
@@ -815,7 +979,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
self.size =
{
IMGUI_CHANGE_ALL_FRAME_PROPERTIES.popupSize.x,
250
275
},
self.flags = true
);
@@ -857,7 +1021,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD,
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT,
self.label = "Subtract",
self.tooltip = "The specified values will be added to all selected frames.",
self.tooltip = "The specified values will be subtracted from all selected frames.",
self.snapshotAction = "Subtract Frame Properties",
self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
self.isSameLine = true
@@ -882,7 +1046,7 @@ IMGUI_ITEM(IMGUI_SCALE_ANM2,
self.tooltip = "Scale up all size and position-related frame properties in the anm2.",
self.popup = "Scale Anm2",
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {260, 72},
self.popupSize = {260, 75},
self.isSizeToText = true,
self.isSeparator = true
);
@@ -958,7 +1122,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT,
self.label = "Format",
self.tooltip = "(PNG images only).\nSet the format of each output frame; i.e., its filename.\nThe format will only take one argument; that being the frame's index.\nFor example, a format like \"{}.png\" will export a frame of index 0 as \"0.png\".",
self.max = 255
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CONFIRM,
@@ -1009,6 +1173,48 @@ IMGUI_ITEM(IMGUI_SETTINGS,
IMGUI_ITEM(IMGUI_VSYNC,
self.label = "&Vsync",
self.tooltip = "Toggle vertical sync; synchronizes program framerate with your monitor's refresh rate.",
self.isSizeToText = true,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_HOTKEYS,
self.label = "&Hotkeys",
self.tooltip = "Change the program's hotkeys.",
self.popup = "Hotkeys",
self.popupSize = {500, 405},
self.isSizeToText = true,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_HOTKEYS_CHILD,
self.label = "## Hotkeys Child",
self.size = {IMGUI_HOTKEYS.popupSize.x, IMGUI_HOTKEYS.popupSize.y - 35},
self.flags = true
);
#define IMGUI_HOTKEYS_FUNCTION "Function"
#define IMGUI_HOTKEYS_HOTKEY "Hotkey"
IMGUI_ITEM(IMGUI_HOTKEYS_TABLE,
self.label = "## Hotkeys Table",
self.value = 2,
self.flags = ImGuiTableFlags_Borders
);
IMGUI_ITEM(IMGUI_HOTKEYS_OPTIONS_CHILD,
self.label = "## Merge Options Child",
self.size = {IMGUI_HOTKEYS.popupSize.x, 35},
self.flags = true
);
IMGUI_ITEM(IMGUI_HOTKEYS_CONFIRM,
self.label = "Confirm",
self.tooltip = "Use these hotkeys.",
self.rowCount = 1
);
IMGUI_ITEM(IMGUI_DEFAULT_SETTINGS,
self.label = "&Reset to Default Settings",
self.tooltip = "Reset the program's settings to their default state.",
self.isSizeToText = true
);
@@ -1287,14 +1493,11 @@ IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
self.tooltip = "Change the zoom of the canvas.",
self.min = CANVAS_ZOOM_MIN,
self.max = CANVAS_ZOOM_MAX,
self.speed = 1.0f,
self.value = CANVAS_ZOOM_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the canvas.",
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_CANVAS_VISUAL_CHILD,
self.label = "## Animation Preview Visual Child",
@@ -1316,8 +1519,8 @@ IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY,
IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY_TRANSPARENCY,
self.label = "Alpha",
self.tooltip = "Set the transparency of the animation overlay.",
self.value = 255,
self.max = 255
self.value = SETTINGS_PREVIEW_OVERLAY_TRANSPARENCY_DEFAULT,
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_CANVAS_HELPER_CHILD,
@@ -1340,28 +1543,37 @@ IMGUI_ITEM(IMGUI_CANVAS_AXES_COLOR,
IMGUI_ITEM(IMGUI_CANVAS_ROOT_TRANSFORM,
self.label = "Root Transform",
self.tooltip = "Toggles the root frames's attributes transforming the other items in an animation.",
self.value = true
self.value = SETTINGS_PREVIEW_IS_ROOT_TRANSFORM_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_TRIGGERS,
self.label = "Triggers",
self.tooltip = "Toggles activated triggers drawing their event name.",
self.value = true
self.value = SETTINGS_PREVIEW_IS_TRIGGERS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_PIVOTS,
self.label = "Pivots",
self.tooltip = "Toggles drawing each layer's pivot."
self.tooltip = "Toggles drawing each layer's pivot.",
self.value = SETTINGS_PREVIEW_IS_PIVOTS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_TARGETS,
self.label = "Targets",
self.tooltip = "Toggles drawing the targets (i.e., the colored root/null icons)."
self.tooltip = "Toggles drawing the targets (the colored root/null icons).",
self.value = SETTINGS_PREVIEW_IS_TARGETS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_ALT_ICONS,
self.label = "Alt Icons",
self.tooltip = "Toggles the use of alternate icons for the targets (the colored root/null icons).",
self.value = SETTINGS_PREVIEW_IS_ALT_ICONS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_BORDER,
self.label = "Border",
self.tooltip = "Toggles the appearance of a border around the items."
self.tooltip = "Toggles the appearance of a border around the items.",
self.value = SETTINGS_PREVIEW_IS_BORDER_DEFAULT
);
IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW,
@@ -1369,11 +1581,27 @@ IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW,
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the animation preview.",
self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_ANIMATION_PREVIEW.label,
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR,
self.label = "Spritesheet Editor",
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the spritesheet editor.",
self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_SPRITESHEET_EDITOR.label,
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES, self.label = "Frame Properties");
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_POSITION,
@@ -1408,8 +1636,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
self.label = "Scale",
self.tooltip = "Change the scale of the selected frame.",
self.snapshotAction = "Frame Scale",
self.isUseItemActivated = true,
self.value = 100
self.isUseItemActivated = true
);
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_ROTATION,
@@ -1494,7 +1721,7 @@ IMGUI_ITEM(IMGUI_TOOL_PAN,
self.label = "## Pan",
self.tooltip = "Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou can also use the middle mouse button to pan at any time.",
self.function = imgui_tool_pan_set,
self.chord = ImGuiKey_P,
self.hotkey = HOTKEY_PAN,
self.atlas = ATLAS_PAN
);
@@ -1502,7 +1729,7 @@ IMGUI_ITEM(IMGUI_TOOL_MOVE,
self.label = "## Move",
self.tooltip = "Use the move tool.\nWill move the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_move_set,
self.chord = ImGuiKey_M,
self.hotkey = HOTKEY_MOVE,
self.atlas = ATLAS_MOVE
);
@@ -1510,7 +1737,7 @@ IMGUI_ITEM(IMGUI_TOOL_ROTATE,
self.label = "## Rotate",
self.tooltip = "Use the rotate tool.\nWill rotate the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_rotate_set,
self.chord = ImGuiKey_R,
self.hotkey = HOTKEY_ROTATE,
self.atlas = ATLAS_ROTATE
);
@@ -1518,7 +1745,7 @@ IMGUI_ITEM(IMGUI_TOOL_SCALE,
self.label = "## Scale",
self.tooltip = "Use the scale tool.\nWill scale the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_scale_set,
self.chord = ImGuiKey_S,
self.hotkey = HOTKEY_SCALE,
self.atlas = ATLAS_SCALE
);
@@ -1526,7 +1753,7 @@ IMGUI_ITEM(IMGUI_TOOL_CROP,
self.label = "## Crop",
self.tooltip = "Use the crop tool.\nWill produce a crop rectangle based on how the cursor is dragged.\n(Spritesheet Editor only.)",
self.function = imgui_tool_crop_set,
self.chord = ImGuiKey_C,
self.hotkey = HOTKEY_CROP,
self.atlas = ATLAS_CROP
);
@@ -1534,7 +1761,7 @@ IMGUI_ITEM(IMGUI_TOOL_DRAW,
self.label = "## Draw",
self.tooltip = "Draws pixels onto the selected spritesheet, with the current color.\n(Spritesheet Editor only.)",
self.function = imgui_tool_draw_set,
self.chord = ImGuiKey_B,
self.hotkey = HOTKEY_DRAW,
self.atlas = ATLAS_DRAW
);
@@ -1542,7 +1769,7 @@ IMGUI_ITEM(IMGUI_TOOL_ERASE,
self.label = "## Erase",
self.tooltip = "Erases pixels from the selected spritesheet.\n(Spritesheet Editor only.)",
self.function = imgui_tool_erase_set,
self.chord = ImGuiKey_E,
self.hotkey = HOTKEY_ERASE,
self.atlas = ATLAS_ERASE
);
@@ -1550,23 +1777,23 @@ IMGUI_ITEM(IMGUI_TOOL_COLOR_PICKER,
self.label = "## Color Picker",
self.tooltip = "Selects a color from the canvas, to be used for drawing.\n(Spritesheet Editor only).",
self.function = imgui_tool_color_picker_set,
self.chord = ImGuiKey_W,
self.hotkey = HOTKEY_COLOR_PICKER,
self.atlas = ATLAS_COLOR_PICKER
);
IMGUI_ITEM(IMGUI_TOOL_UNDO,
self.label = "## Undo",
self.tooltip = "Undoes the last action.",
self.tooltip = "Undo the last action.",
self.function = imgui_undo,
self.chord = ImGuiKey_Z,
self.hotkey = HOTKEY_UNDO,
self.atlas = ATLAS_UNDO
);
IMGUI_ITEM(IMGUI_TOOL_REDO,
self.label = "## Redo",
self.tooltip = "Redoes the last action.",
self.tooltip = "Redo the last action.",
self.function = imgui_redo,
self.chord = ImGuiMod_Shift + ImGuiKey_Z,
self.hotkey = HOTKEY_REDO,
self.atlas = ATLAS_REDO
);
@@ -1864,7 +2091,7 @@ IMGUI_ITEM(IMGUI_PLAY,
self.label = "|> Play",
self.tooltip = "Play the current animation, if paused.",
self.focusWindow = IMGUI_TIMELINE.label,
self.chord = ImGuiKey_Space,
self.hotkey = HOTKEY_PLAY_PAUSE,
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
@@ -1873,7 +2100,7 @@ IMGUI_ITEM(IMGUI_PAUSE,
self.label = "|| Pause",
self.tooltip = "Pause the current animation, if playing.",
self.focusWindow = IMGUI_TIMELINE.label,
self.chord = ImGuiKey_Space,
self.hotkey = HOTKEY_PLAY_PAUSE,
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
@@ -1981,7 +2208,55 @@ IMGUI_ITEM(IMGUI_CREATED_BY,
self.label = "Author",
self.tooltip = "Sets the author of the animation.",
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.max = 255
self.max = UCHAR_MAX
);
#define IMGUI_ONIONSKIN_ROW_COUNT 3
IMGUI_ITEM(IMGUI_ONIONSKIN, self.label = "Onionskin");
IMGUI_ITEM(IMGUI_ONIONSKIN_ENABLED,
self.label = "Enabled",
self.tooltip = "Toggle onionskin (previews of frames before/after the current one.)",
self.function = imgui_onionskin_toggle,
self.hotkey = HOTKEY_ONIONSKIN,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_BEFORE, self.label = "-- Before-- ");
IMGUI_ITEM(IMGUI_ONIONSKIN_AFTER, self.label = "-- After -- ");
IMGUI_ITEM(IMGUI_ONIONSKIN_COUNT,
self.label = "Count",
self.tooltip = "Set the number of previewed frames appearing.",
self.min = 1,
self.max = 100,
self.value = SETTINGS_ONIONSKIN_BEFORE_COUNT_DEFAULT,
self.rowCount = IMGUI_ONIONSKIN_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_COLOR_OFFSET,
self.label = "Color Offset",
self.tooltip = "Set the color offset of the previewed frames.",
self.flags = ImGuiColorEditFlags_NoInputs,
self.rowCount = IMGUI_ONIONSKIN_ROW_COUNT
);
IMGUI_ITEM(IMGUI_ONIONSKIN_DRAW_ORDER,
self.label = "Draw Order",
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_BELOW,
self.label = "Below",
self.tooltip = "The onionskin frames will draw below the base frame.",
self.value = ONIONSKIN_BELOW,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_ABOVE,
self.label = "Above",
self.tooltip = "The onionskin frames will draw above the base frame.",
self.value = ONIONSKIN_ABOVE
);
IMGUI_ITEM(IMGUI_CONTEXT_MENU, self.label = "## Context Menu");
@@ -1991,7 +2266,7 @@ IMGUI_ITEM(IMGUI_CUT,
self.tooltip = "Cuts the currently selected contextual element; removing it and putting it to the clipboard.",
self.snapshotAction = "Cut",
self.function = imgui_cut,
self.chord = ImGuiMod_Ctrl | ImGuiKey_X,
self.hotkey = HOTKEY_CUT,
self.isSizeToText = true
);
@@ -2000,7 +2275,7 @@ IMGUI_ITEM(IMGUI_COPY,
self.tooltip = "Copies the currently selected contextual element to the clipboard.",
self.snapshotAction = "Copy",
self.function = imgui_copy,
self.chord = ImGuiMod_Ctrl | ImGuiKey_C,
self.hotkey = HOTKEY_COPY,
self.isSizeToText = true
);
@@ -2009,7 +2284,7 @@ IMGUI_ITEM(IMGUI_PASTE,
self.tooltip = "Pastes the currently selection contextual element from the clipboard.",
self.snapshotAction = "Paste",
self.function = imgui_paste,
self.chord = ImGuiMod_Ctrl | ImGuiKey_V,
self.hotkey = HOTKEY_PASTE,
self.isSizeToText = true
);
@@ -2018,7 +2293,7 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT,
self.tooltip = "Rename the selected item.",
self.snapshotAction = "Rename Item",
self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue,
self.max = 255
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
@@ -2028,8 +2303,6 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
self.step = 0
);
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
IMGUI_ITEM(IMGUI_POPUP_OK,
self.label = "OK",

View File

@@ -1,5 +1,7 @@
#pragma once
#include <SDL3/SDL_main.h>
#define ARGUMENT_RESCALE "--rescale"
#define ARGUMENT_RESCALE_ARGUMENT_ERROR "--rescale: specify both anm2 and scale arguments"
#define ARGUMENT_RESCALE_ANM2_ERROR "Unable to rescale anm2 {} by value {}. Make sure the file is valid."

View File

@@ -81,6 +81,7 @@ void preview_draw(Preview* self)
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);
@@ -95,153 +96,141 @@ void preview_draw(Preview* self)
canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
if (self->settings->previewIsAxes)
canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxesColor);
canvas_axes_draw(&self->canvas, shaderAxis, transform, self->settings->previewAxesColor);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
s32& animationID = self->reference->animationID;
if (animation)
auto animation_draw = [&](s32 animationID)
{
Anm2Frame root;
mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
Anm2Animation* animation = map_find(self->anm2->animations, animationID);
if (!animation) return;
if (self->settings->previewIsRootTransform)
rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation);
// Root
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
mat4 model = canvas_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
mat4 rootTransform = transform * model;
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
}
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);
};
// Layers
for (auto [i, id] : self->anm2->layerMap)
auto layer_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
Anm2Frame frame;
Anm2Item& layerAnimation = animation->layerAnimations[id];
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) return;
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
if (!frame.isVisible)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
if (!frame.isVisible) return;
mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 layerTransform = transform * (rootModel * model);
Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
if (!texture.isInvalid)
{
vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
}
if (self->settings->previewIsBorder)
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
if (self->settings->previewIsPivots)
{
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
mat4 pivotModel = canvas_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, PREVIEW_PIVOT_COLOR);
}
}
// Nulls
if (self->settings->previewIsTargets)
{
for (auto& [id, nullAnimation] : animation->nullAnimations)
{
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, self->time);
if (!frame.isVisible)
continue;
Anm2Null null = self->anm2->nulls[id];
vec4 color = (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 : ATLAS_TARGET;
mat4 model = canvas_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 = canvas_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);
}
}
}
}
s32& animationOverlayID = self->animationOverlayID;
Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
if (animationOverlay)
{
Anm2Frame root;
mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale));
for (auto [i, id] : self->anm2->layerMap)
{
Anm2Frame frame;
Anm2Item& layerAnimation = animationOverlay->layerAnimations[id];
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationOverlayID, ANM2_LAYER, id}, self->time);
if (!frame.isVisible)
continue;
Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
vec3 frameColorOffset = frame.offsetRGB + colorOffset;
vec4 frameTint = frame.tintRGBA;
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
if (texture.isInvalid) continue;
Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[id].spritesheetID);
if (!spritesheet) return;
Texture& texture = spritesheet->texture;
if (texture.isInvalid) return;
vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frameTint, frameColorOffset);
mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 layerTransform = transform * (rootModel * model);
if (self->settings->previewIsBorder)
{
vec4 borderColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_BORDER_COLOR;
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, borderColor);
}
vec4 tint = frame.tintRGBA;
tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
if (self->settings->previewIsPivots)
{
vec4 pivotColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_PIVOT_COLOR;
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
mat4 pivotModel = canvas_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);
}
};
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, tint, frame.offsetRGB);
}
}
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 = canvas_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 = canvas_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 ?
canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
root_draw(root, colorOffset, alphaOffset, isOnionskin);
for (auto [i, id] : self->anm2->layerMap)
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
if (self->settings->previewIsTargets)
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();
};
animation_draw(self->reference->animationID);
animation_draw(self->animationOverlayID);
canvas_unbind();
}

View File

@@ -42,6 +42,8 @@ struct Preview
f32 time{};
};
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
void preview_draw(Preview* self);
void preview_tick(Preview* self);

View File

@@ -7,17 +7,16 @@ enum RenderType
RENDER_PNG,
RENDER_GIF,
RENDER_WEBM,
RENDER_MP4
RENDER_MP4,
RENDER_COUNT
};
constexpr inline s32 RENDER_COUNT = RENDER_MP4 + 1;
const inline std::string RENDER_TYPE_STRINGS[] =
{
"PNG Images",
"GIF image",
"WebM video",
"MP4 video"
"MP4 video",
};
const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
@@ -25,5 +24,5 @@ const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
".png",
".gif",
".webm",
".mp4"
".mp4",
};

View File

@@ -6,70 +6,87 @@ static void _settings_setting_load(Settings* self, const std::string& line)
{
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))
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;
if (p < line.size() && line[p] == '=')
return line.c_str() + p + 1;
return nullptr;
};
auto* value = match_key(key);
const char* value = nullptr;
if (value)
switch (entry.type)
{
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:
{
case TYPE_INT:
*(s32*)target = std::atoi(value);
return;
case TYPE_BOOL:
*(bool*)target = string_to_bool(value);
return;
case TYPE_FLOAT:
*(f32*)target = std::atof(value);
return;
case TYPE_STRING:
*(std::string*)target = value;
return;
default:
break;
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;
}
}
if (entry.type == TYPE_VEC2)
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
}
else if (entry.type == TYPE_IVEC2)
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
}
else if (entry.type == TYPE_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; }
}
else if (entry.type == TYPE_VEC4)
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
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)
@@ -111,6 +128,13 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
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);
@@ -118,6 +142,13 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
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);
@@ -214,47 +245,32 @@ void settings_init(Settings* self)
{
const std::string path = settings_path_get();
std::ifstream file(path, std::ios::binary);
std::istream* in = nullptr;
std::istringstream defaultSettings;
if (file)
{
log_info(std::format(SETTINGS_INIT_INFO, path));
in = &file;
}
else
{
log_error(std::format(SETTINGS_INIT_ERROR, path));
log_info(SETTINGS_DEFAULT_INFO);
defaultSettings.str(SETTINGS_DEFAULT);
in = &defaultSettings;
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(*in, line))
{
if (line == SETTINGS_SECTION)
{
inSettingsSection = true;
continue;
}
if (line == SETTINGS_SECTION_IMGUI) break;
if (inSettingsSection) _settings_setting_load(self, line);
}
// Save default settings
if (!file)
while (std::getline(file, line))
{
std::ofstream out(path, std::ios::binary | std::ios::trunc);
if (out)
if (line == SETTINGS_SECTION)
{
out << SETTINGS_DEFAULT;
out.flush();
log_info(std::format(SETTINGS_SAVE_INFO, path));
}
else
log_error(std::format(SETTINGS_DEFAULT_ERROR, path));
inSettingsSection = true;
continue;
}
if (line.empty()) continue;
if (line == SETTINGS_SECTION_IMGUI) break;
if (inSettingsSection) _settings_setting_load(self, line);
}
}

View File

@@ -4,17 +4,15 @@
#include "render.h"
#include "tool.h"
#define SETTINGS_BUFFER 0xFFFF
#define SETTINGS_BUFFER_ITEM 0xFF
#define SETTINGS_SECTION "[Settings]"
#define SETTINGS_SECTION_IMGUI "# Dear ImGui"
#define SETTINGS_INIT_ERROR "Failed to read settings file: {}"
#define SETTINGS_DEFAULT_ERROR "Failed to write default settings file: {}"
#define SETTINGS_INIT_WARNING "Unable to read settings file: {}; using default settings"
#define SETTINGS_INIT_ERROR "Unable to read settings file: {}"
#define SETTINGS_SAVE_ERROR "Failed to write settings file: {}"
#define SETTINGS_SAVE_FINALIZE_ERROR "Failed to write settings file: {} ({})"
#define SETTINGS_VALUE_INIT_WARNING "Unknown setting: {}"
#define SETTINGS_FLOAT_FORMAT "{:.3f}"
#define SETTINGS_INIT_INFO "Initialized settings from: {}"
#define SETTINGS_DEFAULT_INFO "Using default settings"
#define SETTINGS_DIRECTORY_ERROR "Failed to create settings directory: {} ({})"
#define SETTINGS_SAVE_INFO "Saved settings to: {}"
@@ -22,6 +20,135 @@
#define SETTINGS_PATH "settings.ini"
#define SETTINGS_TEMPORARY_EXTENSION ".tmp"
#define SETTINGS_LIST \
/* name, symbol, type, defaultValue */ \
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1280, 720}) \
X(isVsync, IS_VSYNC, TYPE_BOOL, true) \
\
X(hotkeyCenterView, HOTKEY_CENTER_VIEW, TYPE_STRING, "Home") \
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, false) \
X(previewIsTriggers, PREVIEW_IS_TRIGGERS, TYPE_BOOL, true) \
X(previewIsPivots, PREVIEW_IS_PIVOTS, TYPE_BOOL, false) \
X(previewIsTargets, PREVIEW_IS_TARGETS, 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(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_FRAMES) \
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(onionskinIsEnabled, ONIONSKIN_IS_ENABLED, TYPE_BOOL, false) \
X(onionskinDrawOrder, ONIONSKIN_DRAW_ORDER, TYPE_INT, ONIONSKIN_BELOW) \
X(onionskinBeforeCount, ONIONSKIN_BEFORE_COUNT, TYPE_INT, 1) \
X(onionskinAfterCount, ONIONSKIN_AFTER_COUNT, TYPE_INT, 1) \
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(ffmpegPath, FFMPEG_PATH, TYPE_STRING, "")
#define X(name, symbol, 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;
SETTINGS_LIST
#undef X
};
struct SettingsEntry
{
std::string key;
@@ -29,273 +156,92 @@ struct SettingsEntry
s32 offset;
};
struct Settings
const inline SettingsEntry SETTINGS_ENTRIES[] =
{
ivec2 windowSize = {1080, 720};
bool isVsync = true;
bool playbackIsLoop = true;
bool playbackIsClampPlayhead = true;
bool changeIsCrop = false;
bool changeIsSize = false;
bool changeIsPosition = false;
bool changeIsPivot = false;
bool changeIsScale = false;
bool changeIsRotation = false;
bool changeIsDelay = false;
bool changeIsTint = false;
bool changeIsColorOffset = false;
bool changeIsVisibleSet = false;
bool changeIsInterpolatedSet = false;
bool changeIsFromSelectedFrame = false;
vec2 changeCrop{};
vec2 changeSize{};
vec2 changePosition{};
vec2 changePivot{};
vec2 changeScale{};
f32 changeRotation{};
s32 changeDelay{};
vec4 changeTint{};
vec3 changeColorOffset{};
bool changeIsVisible{};
bool changeIsInterpolated{};
s32 changeNumberFrames = 1;
f32 scaleValue = 1.0f;
bool previewIsAxes = true;
bool previewIsGrid = true;
bool previewIsRootTransform = false;
bool previewIsTriggers = true;
bool previewIsPivots = false;
bool previewIsTargets = true;
bool previewIsBorder = false;
f32 previewOverlayTransparency = 255.0f;
f32 previewZoom = 200.0;
vec2 previewPan = {0.0, 0.0};
ivec2 previewGridSize = {32, 3};
ivec2 previewGridOffset{};
vec4 previewGridColor = {1.0, 1.0, 1.0, 0.125};
vec4 previewAxesColor = {1.0, 1.0, 1.0, 0.125};
vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
ivec2 generateStartPosition = {0, 0};
ivec2 generateSize = {64, 64};
ivec2 generatePivot = {32, 32};
s32 generateRows = 4;
s32 generateColumns = 4;
s32 generateCount = 16;
s32 generateDelay = 1;
bool editorIsGrid = true;
bool editorIsGridSnap = true;
bool editorIsBorder = true;
f32 editorZoom = 200.0;
vec2 editorPan = {0.0, 0.0};
ivec2 editorGridSize = {32, 32};
ivec2 editorGridOffset = {32, 32};
vec4 editorGridColor = {1.0, 1.0, 1.0, 0.125};
vec4 editorBackgroundColor = {0.113, 0.184, 0.286, 1.0};
s32 mergeType = ANM2_MERGE_APPEND_FRAMES;
bool mergeIsDeleteAnimationsAfter = false;
s32 bakeInterval = 1;
bool bakeIsRoundScale = true;
bool bakeIsRoundRotation = true;
s32 tool = TOOL_PAN;
vec4 toolColor = {1.0, 1.0, 1.0, 1.0};
s32 renderType = RENDER_PNG;
std::string renderPath = ".";
std::string renderFormat = "{}.png";
std::string ffmpegPath{};
};
const SettingsEntry SETTINGS_ENTRIES[] =
{
{"window", TYPE_IVEC2, offsetof(Settings, windowSize)},
{"isVsync", TYPE_BOOL, offsetof(Settings, isVsync)},
{"playbackIsLoop", TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
{"playbackIsClampPlayhead", TYPE_BOOL, offsetof(Settings, playbackIsClampPlayhead)},
{"changeIsCrop", TYPE_BOOL, offsetof(Settings, changeIsCrop)},
{"changeIsSize", TYPE_BOOL, offsetof(Settings, changeIsSize)},
{"changeIsPosition", TYPE_BOOL, offsetof(Settings, changeIsPosition)},
{"changeIsPivot", TYPE_BOOL, offsetof(Settings, changeIsPivot)},
{"changeIsScale", TYPE_BOOL, offsetof(Settings, changeIsScale)},
{"changeIsRotation", TYPE_BOOL, offsetof(Settings, changeIsRotation)},
{"changeIsDelay", TYPE_BOOL, offsetof(Settings, changeIsDelay)},
{"changeIsTint", TYPE_BOOL, offsetof(Settings, changeIsTint)},
{"changeIsColorOffset", TYPE_BOOL, offsetof(Settings, changeIsColorOffset)},
{"changeIsVisibleSet", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
{"changeIsInterpolatedSet", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
{"changeIsFromSelectedFrame", TYPE_BOOL, offsetof(Settings, changeIsFromSelectedFrame)},
{"changeCrop", TYPE_VEC2, offsetof(Settings, changeCrop)},
{"changeSize", TYPE_VEC2, offsetof(Settings, changeSize)},
{"changePosition", TYPE_VEC2, offsetof(Settings, changePosition)},
{"changePivot", TYPE_VEC2, offsetof(Settings, changePivot)},
{"changeScale", TYPE_VEC2, offsetof(Settings, changeScale)},
{"changeRotation", TYPE_FLOAT, offsetof(Settings, changeRotation)},
{"changeDelay", TYPE_INT, offsetof(Settings, changeDelay)},
{"changeTint", TYPE_VEC4, offsetof(Settings, changeTint)},
{"changeColorOffset", TYPE_VEC3, offsetof(Settings, changeColorOffset)},
{"changeIsVisible", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
{"changeIsInterpolated", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
{"changeNumberFrames", TYPE_INT, offsetof(Settings, changeNumberFrames)},
{"scaleValue", TYPE_FLOAT, offsetof(Settings, scaleValue)},
{"previewIsAxes", TYPE_BOOL, offsetof(Settings, previewIsAxes)},
{"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
{"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
{"previewIsTriggers", TYPE_BOOL, offsetof(Settings, previewIsTriggers)},
{"previewIsPivots", TYPE_BOOL, offsetof(Settings, previewIsPivots)},
{"previewIsTargets", TYPE_BOOL, offsetof(Settings, previewIsTargets)},
{"previewIsBorder", TYPE_BOOL, offsetof(Settings, previewIsBorder)},
{"previewOverlayTransparency", TYPE_FLOAT, offsetof(Settings, previewOverlayTransparency)},
{"previewZoom", TYPE_FLOAT, offsetof(Settings, previewZoom)},
{"previewPan", TYPE_VEC2, offsetof(Settings, previewPan)},
{"previewGridSize", TYPE_IVEC2, offsetof(Settings, previewGridSize)},
{"previewGridOffset", TYPE_IVEC2, offsetof(Settings, previewGridOffset)},
{"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
{"previewAxesColor", TYPE_VEC4, offsetof(Settings, previewAxesColor)},
{"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
{"generateStartPosition", TYPE_IVEC2, offsetof(Settings, generateStartPosition)},
{"generateSize", TYPE_IVEC2, offsetof(Settings, generateSize)},
{"generatePivot", TYPE_IVEC2, offsetof(Settings, generatePivot)},
{"generateRows", TYPE_INT, offsetof(Settings, generateRows)},
{"generateColumns", TYPE_INT, offsetof(Settings, generateColumns)},
{"generateCount", TYPE_INT, offsetof(Settings, generateCount)},
{"generateDelay", TYPE_INT, offsetof(Settings, generateDelay)},
{"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
{"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
{"editorIsBorder", TYPE_BOOL, offsetof(Settings, editorIsBorder)},
{"editorZoom", TYPE_FLOAT, offsetof(Settings, editorZoom)},
{"editorPan", TYPE_VEC2, offsetof(Settings, editorPan)},
{"editorGridSize", TYPE_IVEC2, offsetof(Settings, editorGridSize)},
{"editorGridOffset", TYPE_IVEC2, offsetof(Settings, editorGridOffset)},
{"editorGridColor", TYPE_VEC4, offsetof(Settings, editorGridColor)},
{"editorBackgroundColor", TYPE_VEC4, offsetof(Settings, editorBackgroundColor)},
{"mergeType", TYPE_INT, offsetof(Settings, mergeType)},
{"mergeIsDeleteAnimationsAfter", TYPE_BOOL, offsetof(Settings, mergeIsDeleteAnimationsAfter)},
{"bakeInterval", TYPE_INT, offsetof(Settings, bakeInterval)},
{"bakeRoundScale", TYPE_BOOL, offsetof(Settings, bakeIsRoundScale)},
{"bakeRoundRotation", TYPE_BOOL, offsetof(Settings, bakeIsRoundRotation)},
{"tool", TYPE_INT, offsetof(Settings, tool)},
{"toolColor", TYPE_VEC4, offsetof(Settings, toolColor)},
{"renderType", TYPE_INT, offsetof(Settings, renderType)},
{"renderPath", TYPE_STRING, offsetof(Settings, renderPath)},
{"renderFormat", TYPE_STRING, offsetof(Settings, renderFormat)},
{"ffmpegPath", TYPE_STRING, offsetof(Settings, ffmpegPath)}
#define X(name, symbol, type, ...) \
{ #name, type, offsetof(Settings, name) },
SETTINGS_LIST
#undef X
};
constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);
const std::string SETTINGS_DEFAULT = R"(
[Settings]
windowX=1920
windowY=1080
isVsync=true
playbackIsLoop=true
playbackIsClampPlayhead=false
changeIsCrop=false
changeIsSize=false
changeIsPosition=false
changeIsPivot=false
changeIsScale=false
changeIsRotation=false
changeIsDelay=false
changeIsTint=false
changeIsColorOffset=false
changeIsVisibleSet=false
changeIsInterpolatedSet=false
changeIsFromSelectedFrame=false
changeCropX=0.000
changeCropY=0.000
changeSizeX=0.000
changeSizeY=0.000
changePositionX=0.000
changePositionY=0.000
changePivotX=0.000
changePivotY=0.000
changeScaleX=0.000
changeScaleY=0.000
changeRotation=0.000
changeDelay=1
changeTintR=0.000
changeTintG=0.000
changeTintB=0.000
changeTintA=0.000
changeColorOffsetR=0.000
changeColorOffsetG=0.000
changeColorOffsetB=0.000
changeIsVisible=false
changeIsInterpolated=false
changeNumberFrames=1
scaleValue=1.000
previewIsAxes=true
previewIsGrid=false
previewIsRootTransform=true
previewIsTriggers=false
previewIsPivots=false
previewIsTargets=true
previewIsBorder=false
previewOverlayTransparency=255.000
previewZoom=400.000
previewPanX=0.000
previewPanY=0.000
previewGridSizeX=32
previewGridSizeY=32
previewGridOffsetX=0
previewGridOffsetY=0
previewGridColorR=1.000
previewGridColorG=1.000
previewGridColorB=1.000
previewGridColorA=0.125
previewAxesColorR=1.000
previewAxesColorG=1.000
previewAxesColorB=1.000
previewAxesColorA=0.125
previewBackgroundColorR=0.114
previewBackgroundColorG=0.184
previewBackgroundColorB=0.286
previewBackgroundColorA=1.000
generateStartPositionX=0.000
generateStartPositionY=0.000
generateSizeX=0.000
generateSizeY=0.000
generatePivotX=0.000
generatePivotY=0.000
generateRows=4
generateColumns=4
generateCount=16
generateDelay=1
editorIsGrid=true
editorIsGridSnap=true
editorIsBorder=true
editorZoom=400.000
editorPanX=0.000
editorPanY=0.000
editorGridSizeX=32
editorGridSizeY=32
editorGridOffsetX=0
editorGridOffsetY=0
editorGridColorR=1.000
editorGridColorG=1.000
editorGridColorB=1.000
editorGridColorA=0.125
editorBackgroundColorR=0.113
editorBackgroundColorG=0.183
editorBackgroundColorB=0.286
editorBackgroundColorA=1.000
mergeType=1
mergeIsDeleteAnimationsAfter=false
bakeInterval=1
bakeRoundScale=true
bakeRoundRotation=true
tool=0
toolColorR=0.000
toolColorG=0.000
toolColorB=0.000
toolColorA=1.000
renderType=0
renderPath=.
renderFormat={}.png
ffmpegPath=/usr/bin/ffmpeg
#define HOTKEY_LIST \
X(NONE, "None") \
X(CENTER_VIEW, "Center View") \
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
} HotkeyType;
const inline char* HOTKEY_STRINGS[] =
{
#define X(name, str) str,
HOTKEY_LIST
#undef X
};
using HotkeyMember = std::string Settings::*;
const inline HotkeyMember SETTINGS_HOTKEY_MEMBERS[HOTKEY_COUNT] =
{
nullptr,
&Settings::hotkeyCenterView,
&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 std::string SETTINGS_IMGUI_DEFAULT = R"(
# Dear ImGui
[Window][## Window]
Pos=0,32
Size=1918,1032
Size=1600,868
Collapsed=0
[Window][Debug##Default]
@@ -305,66 +251,66 @@ Collapsed=0
[Window][Tools]
Pos=8,40
Size=39,654
Size=39,612
Collapsed=0
DockId=0x0000000B,0
[Window][Animations]
Pos=1452,388
Size=458,306
Pos=1288,301
Size=304,351
Collapsed=0
DockId=0x0000000A,0
[Window][Events]
Pos=1025,348
Size=425,346
Pos=1005,353
Size=281,299
Collapsed=0
DockId=0x00000008,0
[Window][Spritesheets]
Pos=1452,40
Size=458,346
Pos=1288,40
Size=304,259
Collapsed=0
DockId=0x00000009,0
[Window][Animation Preview]
Pos=49,40
Size=974,654
Size=954,612
Collapsed=0
DockId=0x0000000C,0
[Window][Spritesheet Editor]
Pos=49,40
Size=974,654
Size=954,612
Collapsed=0
DockId=0x0000000C,1
[Window][Timeline]
Pos=8,696
Size=1902,360
Pos=8,654
Size=1584,238
Collapsed=0
DockId=0x00000004,0
[Window][Frame Properties]
Pos=1025,40
Size=425,306
Pos=1005,40
Size=281,311
Collapsed=0
DockId=0x00000007,0
[Docking][Data]
DockSpace ID=0xFC02A410 Window=0x0E46F4F7 Pos=8,40 Size=1902,1016 Split=Y
DockNode ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,654 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1442,1016 Split=X Selected=0x024430EF
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1015,654 Split=X Selected=0x024430EF
DockSpace ID=0xFC02A410 Window=0x0E46F4F7 Pos=8,40 Size=1584,852 Split=Y
DockNode ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,612 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1278,1016 Split=X Selected=0x024430EF
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=995,654 Split=X Selected=0x024430EF
DockNode ID=0x0000000B Parent=0x00000005 SizeRef=39,654 Selected=0x18A5FDB9
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=974,654 CentralNode=1 Selected=0x024430EF
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=425,654 Split=Y Selected=0x754E368F
DockNode ID=0x00000007 Parent=0x00000006 SizeRef=631,306 Selected=0x754E368F
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=631,346 Selected=0x8A65D963
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=458,1016 Split=Y Selected=0x4EFD0020
DockNode ID=0x00000009 Parent=0x00000002 SizeRef=634,346 Selected=0x4EFD0020
DockNode ID=0x0000000A Parent=0x00000002 SizeRef=634,306 Selected=0xC1986EE2
DockNode ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,360 Selected=0x4F89F0DC
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=954,654 CentralNode=1 Selected=0x024430EF
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=281,654 Split=Y Selected=0x754E368F
DockNode ID=0x00000007 Parent=0x00000006 SizeRef=631,311 Selected=0x754E368F
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=631,299 Selected=0x8A65D963
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=304,1016 Split=Y Selected=0x4EFD0020
DockNode ID=0x00000009 Parent=0x00000002 SizeRef=634,259 Selected=0x4EFD0020
DockNode ID=0x0000000A Parent=0x00000002 SizeRef=634,351 Selected=0xC1986EE2
DockNode ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,238 Selected=0x4F89F0DC
)";
void settings_save(Settings* self);

View File

@@ -47,5 +47,7 @@ bool shader_init(GLuint* self, const std::string& vertex, const std::string& fra
void shader_free(GLuint* self)
{
if (*self == GL_ID_NONE) return;
glDeleteProgram(*self);
}

View File

@@ -30,6 +30,7 @@ void init(State* self)
{
log_error(std::format(STATE_SDL_INIT_ERROR, SDL_GetError()));
quit(self);
return;
}
log_info(STATE_SDL_INIT_INFO);
@@ -69,8 +70,8 @@ void init(State* self)
);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, STATE_GL_VERSION_MAJOR);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, STATE_GL_VERSION_MINOR);
self->glContext = SDL_GL_CreateContext(self->window);
@@ -78,14 +79,14 @@ void init(State* self)
{
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, SDL_GetError()));
quit(self);
return;
}
GLenum glewError = glewInit();
if (glewError != GLEW_OK)
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
{
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, (const char*)glewGetErrorString(glewError)));
log_error(std::format(STATE_GLAD_INIT_ERROR));
quit(self);
return;
}
log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION)));

View File

@@ -9,6 +9,7 @@
#define STATE_MIX_AUDIO_DEVICE_INIT_WARNING "Unable to initialize audio device! {}"
#define STATE_MIX_INIT_INFO "Initialized SDL_mixer"
#define STATE_GL_CONTEXT_INIT_ERROR "Failed to initialize OpenGL context! {}"
#define STATE_GLAD_INIT_ERROR "Failed to initialize GLAD!"
#define STATE_GL_CONTEXT_INIT_INFO "Initialized OpenGL context (OpenGL {})"
#define STATE_QUIT_INFO "Exiting..."
#define STATE_GL_LINE_WIDTH 2.0f
@@ -23,6 +24,9 @@
#define STATE_MIX_DEVICE NULL
#define STATE_MIX_ALLOWED_CHANGES SDL_AUDIO_ALLOW_FORMAT_CHANGE
#define STATE_GL_VERSION_MAJOR 3
#define STATE_GL_VERSION_MINOR 3
struct State
{
SDL_Window* window;

View File

@@ -114,6 +114,8 @@ bool texture_from_gl_write(Texture* self, const std::string& path)
void texture_free(Texture* self)
{
if (self->isInvalid) return;
glDeleteTextures(1, &self->id);
*self = Texture{};
}