Compare commits
44 Commits
28c0de1848
...
master
Author | SHA1 | Date | |
---|---|---|---|
cf9f04ecdc | |||
ce3f0b3415 | |||
e0732ee6b9 | |||
|
4ce5a225c3 | ||
6d191f186d | |||
70d4d44c80 | |||
d35958a289 | |||
b65bcd2316 | |||
cc4501d2f1 | |||
bc8cc37042 | |||
c3bb2bcf17 | |||
9afb845a36 | |||
fe32c74967 | |||
c78f2ee17b | |||
0a8fa44ee6 | |||
f7c4bcea52 | |||
45865bd4d1 | |||
c7dc0db9ce | |||
3da8928d13 | |||
f8fb3df8d4 | |||
6deaaea374 | |||
b0e52bd444 | |||
b2cf67a823 | |||
9ad464a74a | |||
4d098f8f1c | |||
a992661c6a | |||
fa6109cb2e | |||
d50fded04d | |||
d92dbc5b6d | |||
2c62f5701c | |||
8f3c666922 | |||
|
de034b9b29 | ||
0ddc616e02 | |||
0f06837965 | |||
e2f1afbefb | |||
b667710c99 | |||
3546f27655 | |||
c9056ce707 | |||
ff69218d50 | |||
afbaf2bc65 | |||
8388fd5d99 | |||
f75369f670 | |||
5ee3ec4351 | |||
67d36df971 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,11 @@
|
||||
build/
|
||||
concept/
|
||||
release/
|
||||
packed/
|
||||
vcpkg_installed/
|
||||
out/
|
||||
include/imgui/
|
||||
include/glm/
|
||||
include/tinyxml2
|
||||
workshop/resources
|
||||
.vs/
|
||||
|
@@ -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,68 @@ endif()
|
||||
project(anm2ed CXX)
|
||||
|
||||
find_package(SDL3 REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
# Gather project sources
|
||||
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"
|
||||
set(GLAD_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad.cpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
enable_language("RC")
|
||||
set (WIN32_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assets/Icon.rc)
|
||||
endif()
|
||||
set(IMGUI_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/imgui.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/imgui_draw.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/imgui_widgets.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/imgui_tables.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/backends/imgui_impl_sdl3.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui/backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES} ${WIN32_RESOURCES})
|
||||
set(TINYXML2_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/tinyxml2/tinyxml2.cpp
|
||||
)
|
||||
|
||||
file(GLOB PROJECT_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
${GLAD_SRC}
|
||||
${IMGUI_SRC}
|
||||
${TINYXML2_SRC}
|
||||
${PROJECT_SRC}
|
||||
)
|
||||
|
||||
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/imgui include/tinyxml2 src)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/glad
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/imgui
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/tinyxml2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/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}")
|
26
CMakeSettings.json
Normal file
26
CMakeSettings.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "x64-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Release",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
}
|
||||
]
|
||||
}
|
33
README.md
33
README.md
@@ -6,27 +6,41 @@ A reimplementation of *The Binding of Isaac: Rebirth*'s proprietary animation ed
|
||||
|
||||
## Features
|
||||
- Extended version of the original proprietary Nicalis animation editor
|
||||
- Smooth [Dear ImGui](https://github.com/ocornut/imgui) interface; docking, dragging and dropping, etc.
|
||||
- Smooth [Dear ImGui](https://github.com/ocornut/imgui) interface; docking, dragging and dropping, etc. You might be familiar with it from You might be familiar with it from (Repentogon)
|
||||
- New features
|
||||
- Can output .webm or *.png sequence
|
||||
- Can output .webm, .mp4 or *.png sequence (wih FFmpeg)
|
||||
- Cutting, copying and pasting
|
||||
- Additional wizard options
|
||||
- Robust snapshot (undo/redo) system
|
||||
- Additional hotkeys/shortcuts
|
||||
- Additional hotkeys/shortcuts (rebindable!)
|
||||
- Onionskinning
|
||||
- Settings that will preserve on exit (stored in %APPDATA% on Windows or ~/.local/share on Linux)
|
||||
|
||||
### Note: Difference from Nicalis editor
|
||||
Layers/nulls are handled differently (in their own window) and are not on the timeline! Add a layer/null in the Layers/Nulls window, and then add a LayerAnimation/NullAnimation in the timeline!
|
||||
|
||||
### Note: Rendering Animations
|
||||
You will need FFmpeg installed! Get it from [here](https://ffmpeg.org/download.html), and point to the downloaded ffmpeg executable within the program!
|
||||
|
||||
## 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
|
||||
|
||||
## Build (Linux)
|
||||
### 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:
|
||||
|
||||
```git submodules update --init```
|
||||
```git submodule update --init```
|
||||
|
||||
Then:
|
||||
|
||||
@@ -35,4 +49,7 @@ mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
```
|
||||
|
||||
## Happy animating!
|
||||

|
||||
|
BIN
assets/atlas.png
BIN
assets/atlas.png
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
66
assets/atlas_data_write.sh
Executable file
66
assets/atlas_data_write.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
INPUT="atlas.png"
|
||||
OUTPUT="../src/PACKED.h"
|
||||
TMP="atlas.bytes"
|
||||
|
||||
# Ensure deps
|
||||
command -v optipng >/dev/null 2>&1 || { echo "error: optipng not found in PATH" >&2; exit 1; }
|
||||
command -v xxd >/dev/null 2>&1 || { echo "error: xxd not found in PATH" >&2; exit 1; }
|
||||
|
||||
# 1) Optimize PNG in place
|
||||
optipng -o7 "$INPUT" >/dev/null
|
||||
|
||||
# 2) Extract ONLY the bytes from xxd -i (between '= {' and '};')
|
||||
# Using awk avoids the earlier '{' vs '= {' mismatch bug.
|
||||
xxd -i "$INPUT" \
|
||||
| awk '
|
||||
/= *\{/ {inside=1; next}
|
||||
inside && /};/ {inside=0; exit}
|
||||
inside {print}
|
||||
' > "$TMP"
|
||||
|
||||
# Sanity check: make sure we got something
|
||||
if ! [ -s "$TMP" ]; then
|
||||
echo "error: failed to extract bytes from xxd output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3) Replace ONLY the bytes inside TEXTURE_ATLAS[] initializer
|
||||
# - Find the exact declaration line for TEXTURE_ATLAS
|
||||
# - Print that line
|
||||
# - On the following line with just '{', print it, then insert bytes,
|
||||
# then skip everything until the matching '};' and print that once.
|
||||
awk -v tmpfile="$TMP" '
|
||||
BEGIN { state=0 } # 0=normal, 1=after decl waiting for {, 2=skipping old bytes until };
|
||||
|
||||
# Match the TEXTURE_ATLAS declaration line precisely
|
||||
$0 ~ /^[[:space:]]*const[[:space:]]+u8[[:space:]]+TEXTURE_ATLAS\[\][[:space:]]*=/ {
|
||||
print; state=1; next
|
||||
}
|
||||
|
||||
# After the decl, the next line with a lone "{" starts the initializer
|
||||
state==1 && $0 ~ /^[[:space:]]*{[[:space:]]*$/ {
|
||||
print # print the opening brace line
|
||||
while ((getline line < tmpfile) > 0) print line # insert fresh bytes
|
||||
close(tmpfile)
|
||||
state=2 # now skip old initializer content until we hit the closing "};"
|
||||
next
|
||||
}
|
||||
|
||||
# While skipping, suppress lines until the closing "};", which we reprint once
|
||||
state==2 {
|
||||
if ($0 ~ /^[[:space:]]*};[[:space:]]*$/) {
|
||||
print # print the closing brace+semicolon
|
||||
state=0
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
# Default: pass through unchanged
|
||||
{ print }
|
||||
' "$OUTPUT" > "$OUTPUT.tmp" && mv "$OUTPUT.tmp" "$OUTPUT"
|
||||
|
||||
rm -f "$TMP"
|
||||
echo "Updated $OUTPUT with optimized bytes from $INPUT."
|
311
include/KHR/khrplatform.h
Normal file
311
include/KHR/khrplatform.h
Normal 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
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
15776
include/glad/glad.h
Normal file
File diff suppressed because one or more lines are too long
245
src/COMMON.h
245
src/COMMON.h
@@ -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;
|
||||
@@ -44,51 +45,34 @@ typedef double f64;
|
||||
|
||||
using namespace glm;
|
||||
|
||||
#define PREFERENCES_DIRECTORY "anm2ed"
|
||||
|
||||
#define ROUND_NEAREST_MULTIPLE(value, multiple) (roundf((value) / (multiple)) * (multiple))
|
||||
#define FLOAT_TO_U8(x) (static_cast<u8>((x) * 255.0f))
|
||||
#define U8_TO_FLOAT(x) ((x) / 255.0f)
|
||||
#define PERCENT_TO_UNIT(x) (x / 100.0f)
|
||||
#define UNIT_TO_PERCENT(x) (x * 100.0f)
|
||||
#define SECOND 1000.0f
|
||||
#define TICK_DELAY (SECOND / 30.0)
|
||||
#define UPDATE_DELAY (SECOND / 120.0)
|
||||
#define ID_NONE -1
|
||||
#define INDEX_NONE -1
|
||||
#define VALUE_NONE -1
|
||||
#define TIME_NONE -1.0f
|
||||
#define GL_ID_NONE 0
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifdef _WIN32
|
||||
#define POPEN _popen
|
||||
#define PCLOSE _pclose
|
||||
#define PWRITE_MODE "wb"
|
||||
#define PREAD_MODE "r"
|
||||
#else
|
||||
#define POPEN popen
|
||||
#define PCLOSE pclose
|
||||
#define PWRITE_MODE "w"
|
||||
#define PREAD_MODE "r"
|
||||
#endif
|
||||
|
||||
#define UV_VERTICES(uvMin, uvMax) \
|
||||
{ \
|
||||
0, 0, uvMin.x, uvMin.y, \
|
||||
1, 0, uvMax.x, uvMin.y, \
|
||||
1, 1, uvMax.x, uvMax.y, \
|
||||
0, 1, uvMin.x, uvMax.y \
|
||||
}
|
||||
|
||||
static const f32 GL_VERTICES[] =
|
||||
{
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
constexpr f32 GL_UV_VERTICES[] =
|
||||
{
|
||||
0, 0, 0.0f, 0.0f,
|
||||
1, 0, 1.0f, 0.0f,
|
||||
1, 1, 1.0f, 1.0f,
|
||||
0, 1, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
|
||||
static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f};
|
||||
static const vec4 COLOR_GREEN = {0.0f, 1.0f, 0.0f, 1.0f};
|
||||
@@ -98,19 +82,12 @@ static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
static inline void log_error(const std::string& string)
|
||||
static inline std::string preferences_path_get(void)
|
||||
{
|
||||
std::println("[ERROR] {}", string);
|
||||
}
|
||||
|
||||
static inline void log_info(const std::string& string)
|
||||
{
|
||||
std::println("[INFO] {}", string);
|
||||
}
|
||||
|
||||
static inline void log_warning(const std::string& string)
|
||||
{
|
||||
std::println("[WARNING] {}", string);
|
||||
char* preferencesPath = SDL_GetPrefPath("", PREFERENCES_DIRECTORY);
|
||||
std::string preferencesPathString = preferencesPath;
|
||||
SDL_free(preferencesPath);
|
||||
return preferencesPathString;
|
||||
}
|
||||
|
||||
static inline bool string_to_bool(const std::string& string)
|
||||
@@ -128,65 +105,74 @@ static inline std::string string_quote(const std::string& string)
|
||||
return "\"" + string + "\"";
|
||||
}
|
||||
|
||||
static inline std::string path_canonical_resolve
|
||||
(
|
||||
const std::string& inputPath,
|
||||
const std::string& basePath = std::filesystem::current_path().string()
|
||||
)
|
||||
{
|
||||
auto strings_equal_ignore_case = [](std::string a, std::string b) {
|
||||
auto to_lower = [](unsigned char c) { return static_cast<char>(std::tolower(c)); };
|
||||
std::transform(a.begin(), a.end(), a.begin(), to_lower);
|
||||
std::transform(b.begin(), b.end(), b.begin(), to_lower);
|
||||
return a == b;
|
||||
};
|
||||
static inline std::string string_to_lowercase(std::string string) {
|
||||
std::transform
|
||||
(
|
||||
string.begin(), string.end(), string.begin(),
|
||||
[](u8 character){ return std::tolower(character); }
|
||||
);
|
||||
return string;
|
||||
}
|
||||
|
||||
std::string sanitized = inputPath;
|
||||
std::replace(sanitized.begin(), sanitized.end(), '\\', '/');
|
||||
|
||||
std::filesystem::path normalizedPath = sanitized;
|
||||
std::filesystem::path absolutePath = normalizedPath.is_absolute()
|
||||
? normalizedPath
|
||||
: (std::filesystem::path(basePath) / normalizedPath);
|
||||
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(absolutePath, error)) {
|
||||
std::error_code canonicalError;
|
||||
std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(absolutePath, canonicalError);
|
||||
return (canonicalError ? absolutePath : canonicalPath).generic_string();
|
||||
}
|
||||
|
||||
std::filesystem::path resolvedPath = absolutePath.root_path();
|
||||
std::filesystem::path remainingPath = absolutePath.relative_path();
|
||||
|
||||
for (const std::filesystem::path& segment : remainingPath) {
|
||||
std::filesystem::path candidatePath = resolvedPath / segment;
|
||||
if (std::filesystem::exists(candidatePath, error)) {
|
||||
resolvedPath = candidatePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool matched = false;
|
||||
if (std::filesystem::exists(resolvedPath, error) && std::filesystem::is_directory(resolvedPath, error)) {
|
||||
for (const auto& directoryEntry : std::filesystem::directory_iterator(resolvedPath, error)) {
|
||||
if (strings_equal_ignore_case(directoryEntry.path().filename().string(), segment.string())) {
|
||||
resolvedPath = directoryEntry.path();
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matched) return sanitized;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(resolvedPath, error))
|
||||
return sanitized;
|
||||
|
||||
std::error_code canonicalError;
|
||||
std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(resolvedPath, canonicalError);
|
||||
return (canonicalError ? resolvedPath : canonicalPath).generic_string();
|
||||
#define FLOAT_FORMAT_MAX_DECIMALS 9
|
||||
#define FLOAT_FORMAT_EPSILON 1e-9f
|
||||
static constexpr f32 FLOAT_FORMAT_POW10[] = {
|
||||
1.f,
|
||||
10.f,
|
||||
100.f,
|
||||
1000.f,
|
||||
10000.f,
|
||||
100000.f,
|
||||
1000000.f,
|
||||
10000000.f,
|
||||
100000000.f,
|
||||
1000000000.f
|
||||
};
|
||||
|
||||
static inline s32 f32_decimals_needed(f32 value)
|
||||
{
|
||||
f32 integerPart = 0.f;
|
||||
f32 fractionalPart = modff(value, &integerPart);
|
||||
fractionalPart = fabsf(fractionalPart);
|
||||
|
||||
if (fractionalPart < FLOAT_FORMAT_EPSILON)
|
||||
return 0;
|
||||
|
||||
for (s32 decimalCount = 1; decimalCount <= FLOAT_FORMAT_MAX_DECIMALS; ++decimalCount)
|
||||
{
|
||||
f32 scaledFraction = fractionalPart * FLOAT_FORMAT_POW10[decimalCount];
|
||||
if (fabsf(scaledFraction - roundf(scaledFraction)) <
|
||||
FLOAT_FORMAT_EPSILON * FLOAT_FORMAT_POW10[decimalCount])
|
||||
{
|
||||
return decimalCount;
|
||||
}
|
||||
}
|
||||
return FLOAT_FORMAT_MAX_DECIMALS;
|
||||
}
|
||||
|
||||
static inline const char* f32_format_get(f32 value)
|
||||
{
|
||||
static std::string formatString;
|
||||
const s32 decimalCount = f32_decimals_needed(value);
|
||||
formatString = (decimalCount == 0)
|
||||
? "%.0f"
|
||||
: ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
}
|
||||
|
||||
static inline const char* vec2_format_get(const vec2& value)
|
||||
{
|
||||
static std::string formatString;
|
||||
const s32 decimalCountX = f32_decimals_needed(value.x);
|
||||
const s32 decimalCountY = f32_decimals_needed(value.y);
|
||||
const s32 decimalCount = (decimalCountX > decimalCountY) ? decimalCountX : decimalCountY;
|
||||
formatString = (decimalCount == 0)
|
||||
? "%.0f"
|
||||
: ("%." + std::to_string(decimalCount) + "f");
|
||||
return formatString.c_str();
|
||||
}
|
||||
|
||||
static inline std::string working_directory_from_file_set(const std::string& path)
|
||||
{
|
||||
std::filesystem::path filePath = path;
|
||||
@@ -221,7 +207,6 @@ static inline bool path_is_valid(const std::filesystem::path& pathCheck)
|
||||
std::error_code ec;
|
||||
|
||||
if (fs::is_directory(pathCheck, ec)) return false;
|
||||
if (fs::exists(pathCheck, ec) && !fs::is_regular_file(pathCheck, ec)) return false;
|
||||
|
||||
fs::path parentDir = pathCheck.has_parent_path() ? pathCheck.parent_path() : fs::path(".");
|
||||
if (!fs::is_directory(parentDir, ec)) return false;
|
||||
@@ -232,16 +217,11 @@ static inline bool path_is_valid(const std::filesystem::path& pathCheck)
|
||||
testStream.close();
|
||||
|
||||
if (!existedBefore && isValid)
|
||||
fs::remove(pathCheck, ec); // cleanup if we created it
|
||||
fs::remove(pathCheck, ec);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static inline const char* enum_to_string(const char* array[], s32 count, s32 index)
|
||||
{
|
||||
return (index >= 0 && index < count) ? array[index] : "";
|
||||
};
|
||||
|
||||
static inline s32 string_to_enum(const std::string& string, const char* const* array, s32 n)
|
||||
{
|
||||
for (s32 i = 0; i < n; i++)
|
||||
@@ -271,8 +251,10 @@ static inline s32 map_next_id_get(const std::map<s32, T>& map)
|
||||
return id;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T* map_find(std::map<s32, T>& map, s32 id)
|
||||
|
||||
template <typename Map>
|
||||
static inline auto map_find(Map& map, typename Map::key_type id)
|
||||
-> typename Map::mapped_type*
|
||||
{
|
||||
if (auto it = map.find(id); it != map.end())
|
||||
return &it->second;
|
||||
@@ -327,7 +309,23 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
|
||||
map[insertIndex] = value;
|
||||
}
|
||||
|
||||
static inline mat4 quad_model_get(vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale)
|
||||
template <typename T>
|
||||
void vector_value_erase(std::vector<T>& v, const T& value)
|
||||
{
|
||||
v.erase(std::remove(v.begin(), v.end(), value), v.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vector_value_swap(std::vector<T>& v, const T& a, const T& b)
|
||||
{
|
||||
for (auto& element : v)
|
||||
{
|
||||
if (element == a) element = b;
|
||||
else if (element == b) element = a;
|
||||
}
|
||||
}
|
||||
|
||||
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
|
||||
{
|
||||
vec2 scaleAbsolute = glm::abs(scale);
|
||||
vec2 scaleSign = glm::sign(scale);
|
||||
@@ -344,7 +342,7 @@ static inline mat4 quad_model_get(vec2 size, vec2 position, vec2 pivot, f32 rota
|
||||
return model;
|
||||
}
|
||||
|
||||
static inline mat4 quad_parent_model_get(vec2 position, vec2 pivot, f32 rotation, vec2 scale)
|
||||
static inline mat4 quad_model_parent_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
|
||||
{
|
||||
vec2 scaleSign = glm::sign(scale);
|
||||
vec2 scaleAbsolute = glm::abs(scale);
|
||||
@@ -352,7 +350,7 @@ static inline mat4 quad_parent_model_get(vec2 position, vec2 pivot, f32 rotation
|
||||
|
||||
mat4 local(1.0f);
|
||||
local = glm::translate(local, vec3(pivot, 0.0f));
|
||||
local = glm::scale(local, vec3(scaleSign, 1.0f)); // mirror if needed
|
||||
local = glm::scale(local, vec3(scaleSign, 1.0f));
|
||||
local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
|
||||
local = glm::translate(local, vec3(-pivot, 0.0f));
|
||||
local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
|
||||
@@ -360,31 +358,36 @@ static inline mat4 quad_parent_model_get(vec2 position, vec2 pivot, f32 rotation
|
||||
return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
|
||||
}
|
||||
|
||||
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
|
||||
static inline std::string function(s32 index) \
|
||||
{ \
|
||||
return enum_to_string(array, count, index); \
|
||||
};
|
||||
|
||||
#define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \
|
||||
static inline enumType function(const std::string& string) \
|
||||
{ \
|
||||
return static_cast<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,
|
||||
|
258
src/PACKED.h
258
src/PACKED.h
@@ -1,5 +1,3 @@
|
||||
// Includes packed resources (i.e., textures, shaders)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
@@ -7,104 +5,113 @@
|
||||
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,
|
||||
0x04, 0x03, 0x00, 0x00, 0x00, 0x01, 0x5e, 0x74, 0xbf, 0x00, 0x00, 0x00,
|
||||
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b,
|
||||
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x4c,
|
||||
0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x60, 0x60, 0x60, 0xff,
|
||||
0xff, 0xff, 0x60, 0x60, 0x60, 0x15, 0x68, 0x14, 0xc2, 0x00, 0x00, 0x00,
|
||||
0x54, 0x45, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0xff, 0xff, 0xff, 0x60,
|
||||
0x60, 0x60, 0xff, 0xff, 0xff, 0x3e, 0xd5, 0x47, 0x6d, 0x00, 0x00, 0x00,
|
||||
0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde,
|
||||
0x00, 0x00, 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
|
||||
0x00, 0x00, 0x04, 0x62, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x59,
|
||||
0x5b, 0x6e, 0x24, 0x37, 0x0c, 0x2c, 0x80, 0xbc, 0x40, 0xee, 0x90, 0x03,
|
||||
0x10, 0x20, 0x0f, 0xc0, 0x80, 0x75, 0xff, 0x33, 0xe5, 0x83, 0xa2, 0x5a,
|
||||
0x63, 0xcf, 0xae, 0xa7, 0x37, 0xb1, 0x03, 0x6c, 0xac, 0x0f, 0x77, 0x6b,
|
||||
0x5a, 0xd5, 0x7c, 0x15, 0x49, 0xb5, 0x0c, 0xfc, 0xc2, 0x20, 0x9f, 0xff,
|
||||
0xae, 0xd9, 0xd7, 0x28, 0x00, 0xe0, 0xb1, 0x9e, 0x04, 0x40, 0x9a, 0x92,
|
||||
0x76, 0xac, 0x27, 0xf3, 0x78, 0x21, 0xbd, 0x00, 0x01, 0xa0, 0x04, 0x98,
|
||||
0xd0, 0x46, 0x26, 0x00, 0x04, 0x49, 0xe8, 0x9e, 0x36, 0xc0, 0x95, 0x80,
|
||||
0x2e, 0x51, 0x64, 0xdf, 0xee, 0x15, 0xa4, 0x92, 0x83, 0x58, 0x2a, 0x29,
|
||||
0x4b, 0xd8, 0x00, 0x9d, 0x17, 0xf6, 0x02, 0xc9, 0x56, 0x68, 0xe6, 0x6d,
|
||||
0x4b, 0xbf, 0xa2, 0x67, 0x4c, 0x4d, 0x35, 0xec, 0xb5, 0x89, 0x41, 0x24,
|
||||
0x9e, 0x01, 0x94, 0xd0, 0xd4, 0xb6, 0x34, 0x35, 0x7b, 0x81, 0x78, 0x90,
|
||||
0xe5, 0xf6, 0x08, 0x28, 0x40, 0x13, 0xcc, 0x13, 0x50, 0x08, 0x03, 0x20,
|
||||
0xee, 0x51, 0xee, 0x06, 0x48, 0xfb, 0x8e, 0x61, 0x64, 0x02, 0x9a, 0x4a,
|
||||
0x1c, 0x00, 0xe1, 0xf2, 0xb8, 0xb8, 0xbb, 0x1b, 0x80, 0x48, 0xad, 0xb6,
|
||||
0x75, 0x59, 0xcf, 0x04, 0xda, 0x4b, 0x00, 0x12, 0x03, 0x30, 0x77, 0x37,
|
||||
0x33, 0x21, 0x01, 0xd2, 0xae, 0xc0, 0x75, 0xd4, 0x2e, 0x40, 0xb0, 0x00,
|
||||
0x40, 0x44, 0xc4, 0x45, 0xa0, 0x4c, 0x40, 0x99, 0x17, 0x60, 0x02, 0x37,
|
||||
0x5e, 0xd2, 0x15, 0x5f, 0x98, 0x99, 0xd9, 0x13, 0xc0, 0xa2, 0x86, 0xb4,
|
||||
0xdf, 0xed, 0x22, 0x8d, 0x88, 0x00, 0x10, 0x5a, 0xff, 0xe1, 0xc7, 0xe4,
|
||||
0x83, 0xe1, 0x8b, 0xc6, 0xd0, 0x5c, 0xde, 0x48, 0x94, 0x07, 0x72, 0x5f,
|
||||
0xf3, 0xa1, 0xf9, 0xdb, 0xe7, 0x93, 0x17, 0xc1, 0xa6, 0x80, 0x93, 0xa4,
|
||||
0x5f, 0x9c, 0x92, 0xe5, 0x13, 0xbc, 0x71, 0x2f, 0x0a, 0x00, 0x02, 0x08,
|
||||
0x02, 0xac, 0xcd, 0x5a, 0x09, 0xba, 0xb3, 0xec, 0x12, 0x45, 0x77, 0x27,
|
||||
0x81, 0x26, 0x28, 0x40, 0xeb, 0x3c, 0x19, 0x40, 0x3f, 0xb7, 0x2d, 0x4a,
|
||||
0xe8, 0x1e, 0xa4, 0x0d, 0x40, 0x3a, 0x4f, 0x6c, 0xab, 0xd4, 0xcf, 0x01,
|
||||
0x68, 0xb9, 0x47, 0x42, 0xcb, 0x9d, 0x15, 0x39, 0x80, 0xc9, 0x93, 0xa1,
|
||||
0xf9, 0x7e, 0x2e, 0x15, 0x51, 0x15, 0xa6, 0xe5, 0x41, 0x8f, 0x44, 0x42,
|
||||
0xb3, 0x01, 0x4a, 0x68, 0x0e, 0xcd, 0xf7, 0x73, 0x9a, 0x6a, 0xa6, 0x30,
|
||||
0xd2, 0x59, 0xae, 0xd5, 0x00, 0xd9, 0x79, 0x32, 0x34, 0xd7, 0x72, 0x96,
|
||||
0x47, 0x42, 0x52, 0x4d, 0x52, 0x4d, 0x4b, 0x09, 0x8c, 0x4a, 0x57, 0x9e,
|
||||
0xc0, 0xdc, 0x1d, 0x80, 0x66, 0xd0, 0xa1, 0x09, 0x28, 0x48, 0x24, 0x84,
|
||||
0x2c, 0xf7, 0x65, 0x34, 0x09, 0x21, 0x13, 0x4e, 0x53, 0x29, 0xb1, 0x9e,
|
||||
0x97, 0x39, 0x01, 0x48, 0x01, 0x61, 0x00, 0xe9, 0xee, 0xed, 0x56, 0x8d,
|
||||
0x4a, 0x90, 0x10, 0xa7, 0x81, 0x66, 0x6b, 0x6e, 0xe2, 0xb4, 0x0e, 0x6d,
|
||||
0x01, 0x50, 0xba, 0xaf, 0xc0, 0x95, 0x66, 0x20, 0x08, 0x67, 0x39, 0x28,
|
||||
0xc7, 0x3c, 0xfd, 0x19, 0x35, 0x4c, 0x4d, 0xb2, 0xa9, 0x01, 0x53, 0x24,
|
||||
0xd4, 0x24, 0x05, 0x24, 0xf9, 0xc0, 0xbf, 0x83, 0x5c, 0x64, 0xcf, 0xdd,
|
||||
0xcf, 0x8c, 0xb9, 0xe6, 0xbf, 0xca, 0xf7, 0xb9, 0xbe, 0xdc, 0x1f, 0xd6,
|
||||
0x75, 0xa9, 0xa6, 0x49, 0x4d, 0x5e, 0x57, 0x00, 0x40, 0x57, 0xc2, 0x29,
|
||||
0xa1, 0x47, 0xed, 0xc6, 0x94, 0x9d, 0x7d, 0x5d, 0xc5, 0xcd, 0xde, 0xab,
|
||||
0xb6, 0xd8, 0xa9, 0xfd, 0x86, 0x5d, 0x11, 0x01, 0x40, 0xcd, 0xed, 0xf4,
|
||||
0xce, 0x2a, 0xba, 0xec, 0x6a, 0xd7, 0x45, 0x71, 0xcc, 0x4a, 0x00, 0x28,
|
||||
0x0a, 0x80, 0x0c, 0x93, 0x52, 0x6c, 0x96, 0xae, 0xc2, 0xa5, 0x00, 0xa2,
|
||||
0x25, 0x6d, 0x0d, 0x82, 0x06, 0x68, 0x16, 0x10, 0x9a, 0x9a, 0x88, 0x82,
|
||||
0xa6, 0x76, 0xfa, 0xa5, 0x2e, 0x09, 0x87, 0x4a, 0xc5, 0x4a, 0x80, 0x99,
|
||||
0x9a, 0xba, 0x12, 0x91, 0xa4, 0x76, 0x8c, 0xba, 0x09, 0xc5, 0xa9, 0x92,
|
||||
0x06, 0x03, 0xcf, 0x01, 0xd9, 0x12, 0x98, 0x38, 0x55, 0x6a, 0x01, 0x4f,
|
||||
0x55, 0xa2, 0x32, 0xb5, 0x99, 0xd5, 0x2a, 0x69, 0x26, 0xd0, 0x02, 0xde,
|
||||
0x19, 0x2d, 0x4c, 0xb0, 0x7b, 0x76, 0x8e, 0x77, 0x3a, 0x6f, 0xb4, 0xf2,
|
||||
0x0d, 0xe9, 0xc6, 0xad, 0xb9, 0xda, 0xf4, 0x14, 0x9c, 0xbe, 0x67, 0xc6,
|
||||
0x73, 0x4e, 0x29, 0x93, 0xcc, 0x01, 0xe4, 0x05, 0xe0, 0xdb, 0x2d, 0xc2,
|
||||
0xa6, 0xc6, 0x29, 0x61, 0xab, 0x24, 0x91, 0x3f, 0x22, 0x5f, 0xef, 0x08,
|
||||
0x94, 0x4c, 0x6a, 0x02, 0xd4, 0xa4, 0xe6, 0x43, 0xe9, 0xbe, 0x4d, 0xef,
|
||||
0x7f, 0x3a, 0x0e, 0x39, 0xba, 0xb3, 0xf7, 0xb1, 0x8b, 0x2d, 0xfd, 0xd4,
|
||||
0xa2, 0x1e, 0x15, 0x5b, 0xf7, 0xf4, 0x01, 0x58, 0xff, 0xb0, 0xe6, 0x52,
|
||||
0x5c, 0x3d, 0xe2, 0xec, 0x19, 0xd0, 0xea, 0x38, 0x09, 0x6b, 0x3d, 0x94,
|
||||
0x58, 0x8b, 0x62, 0xf7, 0x83, 0x69, 0x03, 0x4c, 0xb0, 0x37, 0x0c, 0x10,
|
||||
0xa6, 0x5a, 0x2f, 0xf4, 0xae, 0x64, 0x50, 0xb0, 0xb0, 0x38, 0x88, 0x7d,
|
||||
0x33, 0xb5, 0x27, 0x3c, 0xb5, 0x2b, 0x9f, 0x04, 0xe9, 0x6d, 0xad, 0xda,
|
||||
0x74, 0x9c, 0x01, 0x24, 0x9c, 0x4e, 0x27, 0x84, 0x4e, 0x76, 0x4a, 0x1b,
|
||||
0xbb, 0x75, 0x69, 0xd7, 0xd9, 0xde, 0xb5, 0xf5, 0x0e, 0x46, 0x89, 0x01,
|
||||
0x28, 0x49, 0x46, 0x02, 0x10, 0xea, 0x08, 0xb0, 0x8c, 0xe5, 0x19, 0x4d,
|
||||
0x9d, 0xbd, 0x59, 0x1b, 0x0c, 0x05, 0xd9, 0xfc, 0x0e, 0xe6, 0x08, 0xd0,
|
||||
0x1d, 0x87, 0x01, 0x00, 0x70, 0x77, 0x77, 0x87, 0x82, 0xac, 0x55, 0x41,
|
||||
0x29, 0x23, 0xe0, 0x08, 0xc0, 0x0e, 0x87, 0x01, 0x02, 0x81, 0x92, 0x5e,
|
||||
0xab, 0xc4, 0xb6, 0x1f, 0x1e, 0x0a, 0xc4, 0xb2, 0xe1, 0x04, 0xa4, 0x7a,
|
||||
0x0d, 0xab, 0x97, 0x26, 0x79, 0xf1, 0x9d, 0xd7, 0x26, 0x76, 0x01, 0xba,
|
||||
0x26, 0x74, 0xe0, 0xcb, 0x05, 0x86, 0x38, 0x13, 0xe4, 0x00, 0x2c, 0x1b,
|
||||
0x00, 0x19, 0x80, 0xb8, 0xd9, 0x63, 0xc9, 0x9c, 0x48, 0x7f, 0x15, 0xdd,
|
||||
0xf1, 0x3e, 0xbd, 0x24, 0x5f, 0x04, 0x0c, 0x68, 0x95, 0xd3, 0xb1, 0xc0,
|
||||
0xba, 0xbe, 0x82, 0xf5, 0x03, 0xc0, 0x2a, 0xa7, 0xcc, 0x8f, 0x00, 0x57,
|
||||
0x5d, 0x99, 0x52, 0xf1, 0x1a, 0x40, 0xb9, 0xdb, 0x74, 0xfe, 0x04, 0x70,
|
||||
0xd4, 0xc6, 0xb2, 0x01, 0x74, 0x33, 0x69, 0x00, 0xd7, 0xde, 0x6f, 0xae,
|
||||
0xdb, 0x06, 0xd9, 0x1f, 0x4d, 0x9d, 0x9b, 0x3f, 0x03, 0x6c, 0x22, 0xd4,
|
||||
0x00, 0xea, 0xc7, 0x80, 0x63, 0x38, 0x73, 0x01, 0xca, 0x2e, 0x95, 0xdc,
|
||||
0x59, 0xee, 0x57, 0x3e, 0x1c, 0x05, 0x79, 0xdc, 0xba, 0x32, 0xf5, 0xb9,
|
||||
0xd1, 0xba, 0xea, 0xed, 0xe9, 0xd6, 0x36, 0xfe, 0x63, 0xc0, 0xaa, 0x83,
|
||||
0x6b, 0xfd, 0x0b, 0x80, 0xfe, 0xd4, 0xc0, 0xec, 0x0a, 0xc4, 0x6d, 0xed,
|
||||
0x10, 0x1e, 0x36, 0x3b, 0xa7, 0x4a, 0xaf, 0x91, 0xf5, 0xb1, 0x0b, 0xdd,
|
||||
0x18, 0xfe, 0x66, 0xd8, 0xfb, 0x57, 0x3f, 0x07, 0x8c, 0x05, 0x1f, 0x02,
|
||||
0x82, 0x13, 0xe2, 0xaf, 0x02, 0xe0, 0x1b, 0xf0, 0x7b, 0x00, 0xd6, 0xde,
|
||||
0xef, 0x65, 0x80, 0xae, 0xae, 0xfd, 0x32, 0x80, 0xa9, 0x7c, 0xd8, 0x7e,
|
||||
0x7c, 0x08, 0xe8, 0xcf, 0xbf, 0x97, 0x01, 0xfd, 0xbd, 0x78, 0x9e, 0x07,
|
||||
0xfc, 0x7b, 0x00, 0x3e, 0x19, 0xff, 0x31, 0xe0, 0xf3, 0x8d, 0xfe, 0xc5,
|
||||
0x38, 0xdc, 0x8f, 0xf4, 0x6d, 0x2e, 0xdd, 0x66, 0xeb, 0xef, 0x99, 0xd3,
|
||||
0x73, 0x36, 0xf6, 0x32, 0x60, 0xce, 0x93, 0x5e, 0x07, 0xac, 0xd3, 0x9f,
|
||||
0x97, 0xfb, 0xc3, 0x9c, 0x6b, 0xdc, 0x00, 0xf4, 0x97, 0xe2, 0x0d, 0x40,
|
||||
0x9f, 0x59, 0xde, 0x01, 0x88, 0xe1, 0x5d, 0x37, 0xfd, 0x29, 0xe0, 0x7b,
|
||||
0x7c, 0x8f, 0xaf, 0x19, 0x77, 0x3f, 0xb6, 0xf6, 0x3f, 0x15, 0x5e, 0x1c,
|
||||
0xca, 0x9b, 0x04, 0x27, 0x70, 0xeb, 0xc0, 0x63, 0x8e, 0x1d, 0x3e, 0x0f,
|
||||
0xb0, 0x56, 0xfe, 0x99, 0x77, 0x00, 0x7f, 0xdc, 0x05, 0xfc, 0xf5, 0xe9,
|
||||
0x12, 0x6e, 0x01, 0x3e, 0xdf, 0xad, 0xb7, 0x23, 0x7d, 0x9f, 0x4b, 0xb7,
|
||||
0xd9, 0x7a, 0x3f, 0x1f, 0xbe, 0xc7, 0xff, 0x75, 0xfc, 0x0d, 0x3b, 0xd4,
|
||||
0xd5, 0x4b, 0x3b, 0xfe, 0xb6, 0x75, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
|
||||
0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
|
||||
};
|
||||
|
||||
const u32 TEXTURE_ATLAS_LENGTH = (u32)std::size(TEXTURE_ATLAS);
|
||||
const vec2 TEXTURE_ATLAS_SIZE = {96, 120};
|
||||
const vec2 TEXTURE_ATLAS_SIZE = {96, 160};
|
||||
|
||||
enum AtlasType
|
||||
{
|
||||
@@ -118,8 +125,8 @@ enum AtlasType
|
||||
ATLAS_INVISIBLE,
|
||||
ATLAS_SHOW_RECT,
|
||||
ATLAS_HIDE_RECT,
|
||||
ATLAS_SHOW_TARGETS,
|
||||
ATLAS_HIDE_TARGETS,
|
||||
ATLAS_SHOW_UNUSED,
|
||||
ATLAS_HIDE_UNUSED,
|
||||
ATLAS_PAN,
|
||||
ATLAS_MOVE,
|
||||
ATLAS_ROTATE,
|
||||
@@ -145,6 +152,7 @@ enum AtlasType
|
||||
ATLAS_FRAME,
|
||||
ATLAS_FRAME_ALT,
|
||||
ATLAS_TARGET,
|
||||
ATLAS_TARGET_ALT,
|
||||
ATLAS_COUNT
|
||||
};
|
||||
|
||||
@@ -197,7 +205,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 +226,7 @@ enum ShaderType
|
||||
{
|
||||
SHADER_LINE,
|
||||
SHADER_TEXTURE,
|
||||
SHADER_AXIS,
|
||||
SHADER_GRID,
|
||||
SHADER_COUNT
|
||||
};
|
||||
@@ -234,6 +244,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,36 +297,23 @@ 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;
|
||||
|
||||
uniform mat4 u_model; // inverse of your world->clip matrix (MVP)
|
||||
uniform vec2 u_size; // world-space cell size (e.g. 64,64)
|
||||
uniform vec2 u_offset; // world-space grid offset (shifts entire grid)
|
||||
uniform vec4 u_color; // RGBA
|
||||
uniform mat4 u_model;
|
||||
uniform vec2 u_size;
|
||||
uniform vec2 u_offset;
|
||||
uniform vec4 u_color;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
// clip -> world on z=0 plane
|
||||
vec4 w = u_model * vec4(clip, 0.0, 1.0);
|
||||
w /= w.w;
|
||||
vec2 world = w.xy;
|
||||
|
||||
// grid space
|
||||
vec2 g = (world - u_offset) / u_size;
|
||||
|
||||
vec2 d = abs(fract(g) - 0.5);
|
||||
@@ -303,12 +327,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"
|
||||
@@ -317,5 +342,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}
|
||||
};
|
||||
};
|
||||
|
1301
src/anm2.cpp
1301
src/anm2.cpp
File diff suppressed because it is too large
Load Diff
204
src/anm2.h
204
src/anm2.h
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
#include "resources.h"
|
||||
|
||||
#define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f)
|
||||
@@ -14,7 +13,11 @@
|
||||
#define ANM2_FRAME_DELAY_MIN 1
|
||||
#define ANM2_STRING_MAX 0xFF
|
||||
|
||||
#define ANM2_EMPTY_ERROR "No path given for anm2"
|
||||
#define ANM2_READ_ERROR "Failed to read anm2 from file: {}"
|
||||
#define ANM2_PARSE_ERROR "Failed to parse anm2: {} ({})"
|
||||
#define ANM2_FRAME_PARSE_ERROR "Failed to parse frame: {} ({})"
|
||||
#define ANM2_ANIMATION_PARSE_ERROR "Failed to parse frame: {} ({})"
|
||||
#define ANM2_READ_INFO "Read anm2 from file: {}"
|
||||
#define ANM2_WRITE_ERROR "Failed to write anm2 to file: {}"
|
||||
#define ANM2_WRITE_INFO "Wrote anm2 to file: {}"
|
||||
@@ -23,7 +26,6 @@
|
||||
#define ANM2_EXTENSION "anm2"
|
||||
#define ANM2_SPRITESHEET_EXTENSION "png"
|
||||
|
||||
/* Elements */
|
||||
#define ANM2_ELEMENT_LIST \
|
||||
X(ANIMATED_ACTOR, "AnimatedActor") \
|
||||
X(INFO, "Info") \
|
||||
@@ -47,21 +49,22 @@
|
||||
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
|
||||
};
|
||||
|
||||
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ELEMENT_STRING_TO_ENUM, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT)
|
||||
DEFINE_ENUM_TO_STRING_FUNCTION(ANM2_ELEMENT_ENUM_TO_STRING, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT)
|
||||
|
||||
#define ANM2_ATTRIBUTE_LIST \
|
||||
X(CREATED_BY, "CreatedBy") \
|
||||
@@ -102,21 +105,22 @@ 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
|
||||
};
|
||||
|
||||
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ATTRIBUTE_STRING_TO_ENUM, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
|
||||
DEFINE_ENUM_TO_STRING_FUNCTION(ANM2_ATTRIBUTE_ENUM_TO_STRING, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
|
||||
|
||||
enum Anm2Type
|
||||
{
|
||||
@@ -131,23 +135,33 @@ enum Anm2Type
|
||||
struct Anm2Spritesheet
|
||||
{
|
||||
std::string path{};
|
||||
Texture texture;
|
||||
std::vector<u8> pixels;
|
||||
|
||||
auto operator<=>(const Anm2Spritesheet&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Layer
|
||||
{
|
||||
std::string name = "New Layer";
|
||||
s32 spritesheetID = ID_NONE;
|
||||
s32 spritesheetID{};
|
||||
|
||||
auto operator<=>(const Anm2Layer&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Null
|
||||
{
|
||||
std::string name = "New Null";
|
||||
bool isShowRect = false;
|
||||
|
||||
auto operator<=>(const Anm2Null&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Event
|
||||
{
|
||||
std::string name = "New Event";
|
||||
|
||||
auto operator<=>(const Anm2Event&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Frame
|
||||
@@ -163,8 +177,59 @@ struct Anm2Frame
|
||||
vec2 position{};
|
||||
vec2 size{};
|
||||
vec2 scale = {100, 100};
|
||||
vec3 offsetRGB{};
|
||||
vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
vec3 offsetRGB = COLOR_OFFSET_NONE;
|
||||
vec4 tintRGBA = COLOR_OPAQUE;
|
||||
|
||||
auto operator<=>(const Anm2Frame&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Item
|
||||
{
|
||||
bool isVisible = true;
|
||||
std::vector<Anm2Frame> frames;
|
||||
|
||||
auto operator<=>(const Anm2Item&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Animation
|
||||
{
|
||||
s32 frameNum = ANM2_FRAME_NUM_MIN;
|
||||
std::string name = "New Animation";
|
||||
bool isLoop = true;
|
||||
Anm2Item rootAnimation;
|
||||
std::unordered_map<s32, Anm2Item> layerAnimations;
|
||||
std::vector<s32> layerOrder;
|
||||
std::map<s32, Anm2Item> nullAnimations;
|
||||
Anm2Item triggers;
|
||||
|
||||
auto operator<=>(const Anm2Animation&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2
|
||||
{
|
||||
std::string path{};
|
||||
std::string createdBy = "robot";
|
||||
std::string createdOn{};
|
||||
std::map<s32, Anm2Spritesheet> spritesheets;
|
||||
std::map<s32, Anm2Layer> layers;
|
||||
std::map<s32, Anm2Null> nulls;
|
||||
std::map<s32, Anm2Event> events;
|
||||
std::map<s32, Anm2Animation> animations;
|
||||
s32 defaultAnimationID = ID_NONE;
|
||||
s32 fps = ANM2_FPS_DEFAULT;
|
||||
s32 version{};
|
||||
|
||||
auto operator<=>(const Anm2&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2Reference
|
||||
{
|
||||
s32 animationID = ID_NONE;
|
||||
Anm2Type itemType = ANM2_NONE;
|
||||
s32 itemID = ID_NONE;
|
||||
s32 frameIndex = INDEX_NONE;
|
||||
f32 time = VALUE_NONE;
|
||||
auto operator<=>(const Anm2Reference&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2FrameChange
|
||||
@@ -182,66 +247,6 @@ struct Anm2FrameChange
|
||||
std::optional<vec4> tintRGBA;
|
||||
};
|
||||
|
||||
struct Anm2Item
|
||||
{
|
||||
bool isVisible = true;
|
||||
std::vector<Anm2Frame> frames;
|
||||
};
|
||||
|
||||
struct Anm2Animation
|
||||
{
|
||||
s32 frameNum = ANM2_FRAME_NUM_MIN;
|
||||
std::string name = "New Animation";
|
||||
bool isLoop = true;
|
||||
Anm2Item rootAnimation;
|
||||
std::map<s32, Anm2Item> layerAnimations;
|
||||
std::map<s32, Anm2Item> nullAnimations;
|
||||
Anm2Item triggers;
|
||||
};
|
||||
|
||||
struct Anm2
|
||||
{
|
||||
std::string path{};
|
||||
std::string createdBy = "robot";
|
||||
std::string createdOn{};
|
||||
std::map<s32, Anm2Spritesheet> spritesheets;
|
||||
std::map<s32, Anm2Layer> layers;
|
||||
std::map<s32, Anm2Null> nulls;
|
||||
std::map<s32, Anm2Event> events;
|
||||
std::map<s32, Anm2Animation> animations;
|
||||
std::map<s32, s32> layerMap; // index, id
|
||||
s32 defaultAnimationID{};
|
||||
s32 fps = ANM2_FPS_DEFAULT;
|
||||
s32 version{};
|
||||
};
|
||||
|
||||
struct Anm2Reference
|
||||
{
|
||||
s32 animationID = ID_NONE;
|
||||
Anm2Type itemType = ANM2_NONE;
|
||||
s32 itemID = ID_NONE;
|
||||
s32 frameIndex = INDEX_NONE;
|
||||
auto operator<=>(const Anm2Reference&) const = default;
|
||||
};
|
||||
|
||||
struct Anm2AnimationWithID
|
||||
{
|
||||
s32 id;
|
||||
Anm2Animation animation;
|
||||
};
|
||||
|
||||
struct Anm2EventWithID
|
||||
{
|
||||
s32 id;
|
||||
Anm2Event event;
|
||||
};
|
||||
|
||||
struct Anm2FrameWithReference
|
||||
{
|
||||
Anm2Reference reference;
|
||||
Anm2Frame frame;
|
||||
};
|
||||
|
||||
enum Anm2MergeType
|
||||
{
|
||||
ANM2_MERGE_APPEND_FRAMES,
|
||||
@@ -257,31 +262,48 @@ enum Anm2ChangeType
|
||||
ANM2_CHANGE_SET
|
||||
};
|
||||
|
||||
void anm2_layer_add(Anm2* self);
|
||||
void anm2_layer_remove(Anm2* self, s32 id);
|
||||
void anm2_null_add(Anm2* self);
|
||||
void anm2_null_remove(Anm2* self, s32 id);
|
||||
bool anm2_serialize(Anm2* self, const std::string& path);
|
||||
bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path);
|
||||
void anm2_new(Anm2* self);
|
||||
void anm2_created_on_set(Anm2* self);
|
||||
s32 anm2_animation_add(Anm2* self);
|
||||
void anm2_animation_remove(Anm2* self, s32 id);
|
||||
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const std::string& path, s32 id);
|
||||
enum OnionskinDrawOrder
|
||||
{
|
||||
ONIONSKIN_BELOW,
|
||||
ONIONSKIN_ABOVE
|
||||
};
|
||||
|
||||
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference);
|
||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time = 0.0f);
|
||||
void anm2_frame_erase(Anm2* self, Anm2Reference* reference);
|
||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
|
||||
void anm2_reference_clear(Anm2Reference* self);
|
||||
void anm2_reference_item_clear(Anm2Reference* self);
|
||||
void anm2_reference_frame_clear(Anm2Reference* self);
|
||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml);
|
||||
bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = true);
|
||||
bool anm2_frame_deserialize_from_xml(Anm2Frame* frame, const std::string& xml);
|
||||
bool anm2_serialize(Anm2* self, const std::string& path);
|
||||
s32 anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, s32 id = ID_NONE);
|
||||
s32 anm2_animation_length_get(Anm2Animation* self);
|
||||
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
|
||||
s32 anm2_layer_add(Anm2* self);
|
||||
s32 anm2_null_add(Anm2* self);
|
||||
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform);
|
||||
void anm2_animation_layer_animation_add(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_layer_animation_remove(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_length_set(Anm2Animation* self);
|
||||
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
|
||||
void anm2_animation_null_animation_add(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_null_animation_remove(Anm2Animation* animation, s32 id);
|
||||
void anm2_animation_remove(Anm2* self, s32 id);
|
||||
void anm2_animation_serialize_to_string(Anm2Animation* animation, std::string* string);
|
||||
void anm2_created_on_set(Anm2* self);
|
||||
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
|
||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
|
||||
void anm2_frame_remove(Anm2* self, Anm2Reference* reference);
|
||||
void anm2_frame_serialize_to_string(Anm2Frame* frame, Anm2Type type, std::string* string);
|
||||
void anm2_free(Anm2* self);
|
||||
void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
|
||||
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
|
||||
void anm2_layer_remove(Anm2* self, s32 id);
|
||||
void anm2_new(Anm2* self);
|
||||
void anm2_null_remove(Anm2* self, s32 id);
|
||||
void anm2_reference_clear(Anm2Reference* self);
|
||||
void anm2_reference_frame_clear(Anm2Reference* self);
|
||||
void anm2_reference_item_clear(Anm2Reference* self);
|
||||
void anm2_scale(Anm2* self, f32 scale);
|
||||
void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
|
||||
void anm2_spritesheet_texture_pixels_download(Anm2* self);
|
||||
void anm2_spritesheet_texture_pixels_upload(Anm2* self);
|
107
src/canvas.cpp
107
src/canvas.cpp
@@ -1,39 +1,27 @@
|
||||
#include "canvas.h"
|
||||
|
||||
static void _canvas_texture_free(Canvas* self)
|
||||
static void _canvas_framebuffer_set(Canvas* self, const ivec2& size)
|
||||
{
|
||||
if (self->fbo != 0) glDeleteFramebuffers(1, &self->fbo);
|
||||
if (self->rbo != 0) glDeleteRenderbuffers(1, &self->rbo);
|
||||
if (self->texture != 0) glDeleteTextures(1, &self->texture);
|
||||
}
|
||||
|
||||
static void _canvas_texture_init(Canvas* self, const vec2& size)
|
||||
{
|
||||
_canvas_texture_free(self);
|
||||
|
||||
self->size = size;
|
||||
self->previousSize = size;
|
||||
|
||||
glGenFramebuffers(1, &self->fbo);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
|
||||
|
||||
glGenTextures(1, &self->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, self->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (s32)self->size.x, (s32)self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glBindTexture(GL_TEXTURE_2D, self->framebuffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->framebuffer, 0);
|
||||
|
||||
glGenRenderbuffers(1, &self->rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (s32)self->size.x, (s32)self->size.y);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void canvas_init(Canvas* self, const vec2& size)
|
||||
void canvas_init(Canvas* self, const ivec2& size)
|
||||
{
|
||||
// Axis
|
||||
glGenVertexArrays(1, &self->axisVAO);
|
||||
@@ -45,7 +33,7 @@ void canvas_init(Canvas* self, const vec2& 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);
|
||||
@@ -64,7 +52,7 @@ void canvas_init(Canvas* self, const vec2& size)
|
||||
glBindVertexArray(self->rectVAO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_RECT_VERTICES), CANVAS_RECT_VERTICES, GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
|
||||
@@ -103,14 +91,21 @@ void canvas_init(Canvas* self, const vec2& size)
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
_canvas_texture_init(self, size);
|
||||
// Framebuffer
|
||||
glGenTextures(1, &self->framebuffer);
|
||||
glGenFramebuffers(1, &self->fbo);
|
||||
glGenRenderbuffers(1, &self->rbo);
|
||||
_canvas_framebuffer_set(self, size);
|
||||
|
||||
self->isInit = true;
|
||||
}
|
||||
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin)
|
||||
{
|
||||
f32 zoomFactor = PERCENT_TO_UNIT(zoom);
|
||||
mat4 projection = glm::ortho(0.0f, self->size.x, 0.0f, self->size.y, -1.0f, 1.0f);
|
||||
mat4 projection = glm::ortho(0.0f, (f32)self->size.x, 0.0f, (f32)self->size.y, -1.0f, 1.0f);
|
||||
mat4 view = mat4{1.0f};
|
||||
vec2 size = vec2(self->size.x, self->size.y);
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
@@ -118,7 +113,7 @@ mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin)
|
||||
view = glm::translate(view, vec3(pan, 0.0f));
|
||||
break;
|
||||
default:
|
||||
view = glm::translate(view, vec3((self->size * 0.5f) + pan, 0.0f));
|
||||
view = glm::translate(view, vec3((size * 0.5f) + pan, 0.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -138,15 +133,10 @@ void canvas_viewport_set(Canvas* self)
|
||||
glViewport(0, 0, (s32)self->size.x, (s32)self->size.y);
|
||||
}
|
||||
|
||||
void canvas_texture_set(Canvas* self)
|
||||
void canvas_framebuffer_resize_check(Canvas* self)
|
||||
{
|
||||
static vec2 previousSize = {-1, -1};
|
||||
|
||||
if (previousSize != self->size)
|
||||
{
|
||||
_canvas_texture_init(self, self->size);
|
||||
previousSize = self->size;
|
||||
}
|
||||
if (self->previousSize != self->size)
|
||||
_canvas_framebuffer_set(self, self->size);
|
||||
}
|
||||
|
||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color)
|
||||
@@ -174,7 +164,7 @@ void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& tr
|
||||
glBindVertexArray(self->textureVAO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_UV_VERTICES), vertices, GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_TEXTURE_VERTICES), vertices, GL_DYNAMIC_DRAW);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
@@ -207,13 +197,20 @@ void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform,
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -230,30 +227,18 @@ void canvas_unbind(void)
|
||||
|
||||
void canvas_free(Canvas* self)
|
||||
{
|
||||
_canvas_texture_free(self);
|
||||
}
|
||||
if (!self->isInit) return;
|
||||
|
||||
mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt)
|
||||
{
|
||||
vec2 scaleAbsolute = glm::abs(scale);
|
||||
vec2 scaleSign = glm::sign(scale);
|
||||
f32 usedSign = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
vec2 sizeScaled = size * scaleAbsolute;
|
||||
vec2 pivotScaled = pivot * scaleAbsolute;
|
||||
vec2 pivotAltScaled = pivotAlt * scaleAbsolute;
|
||||
|
||||
vec2 pivotAltMirrored = pivotScaled + (pivotAltScaled - pivotScaled) * scaleSign;
|
||||
|
||||
mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f));
|
||||
model = glm::translate(model, vec3(pivotScaled, 0.0f));
|
||||
model = glm::scale(model, vec3(scaleSign, 1.0f));
|
||||
model = glm::rotate(model, glm::radians(rotation) * usedSign, vec3(0,0,1));
|
||||
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
|
||||
model = glm::translate(model, vec3(pivotAltMirrored, 0.0f));
|
||||
model = glm::rotate(model, glm::radians(rotationAlt) * usedSign, vec3(0,0,1));
|
||||
model = glm::translate(model, vec3(-pivotAltMirrored, 0.0f));
|
||||
model = glm::scale(model, vec3(sizeScaled, 1.0f));
|
||||
|
||||
return transform * model;
|
||||
glDeleteFramebuffers(1, &self->fbo);
|
||||
glDeleteRenderbuffers(1, &self->rbo);
|
||||
glDeleteTextures(1, &self->framebuffer);
|
||||
glDeleteVertexArrays(1, &self->axisVAO);
|
||||
glDeleteVertexArrays(1, &self->rectVAO);
|
||||
glDeleteVertexArrays(1, &self->gridVAO);
|
||||
glDeleteVertexArrays(1, &self->textureVAO);
|
||||
glDeleteBuffers(1, &self->axisVBO);
|
||||
glDeleteBuffers(1, &self->rectVBO);
|
||||
glDeleteBuffers(1, &self->gridVBO);
|
||||
glDeleteBuffers(1, &self->textureVBO);
|
||||
glDeleteBuffers(1, &self->textureEBO);
|
||||
}
|
94
src/canvas.h
94
src/canvas.h
@@ -5,32 +5,39 @@
|
||||
#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)
|
||||
|
||||
static const vec2 CANVAS_GRID_SIZE = {3200, 1600};
|
||||
static const vec2 CANVAS_PIVOT_SIZE = {8, 8};
|
||||
static const vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
|
||||
const inline vec2 CANVAS_PIVOT_SIZE = {4, 4};
|
||||
const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
|
||||
|
||||
const f32 CANVAS_AXIS_VERTICES[] =
|
||||
{
|
||||
-CANVAS_LINE_LENGTH, 0.0f,
|
||||
CANVAS_LINE_LENGTH, 0.0f,
|
||||
0.0f, -CANVAS_LINE_LENGTH,
|
||||
0.0f, CANVAS_LINE_LENGTH
|
||||
};
|
||||
const inline f32 CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};
|
||||
|
||||
const f32 CANVAS_GRID_VERTICES[] =
|
||||
const inline f32 CANVAS_GRID_VERTICES[] =
|
||||
{
|
||||
-1.0f, -1.0f,
|
||||
3.0f, -1.0f,
|
||||
-1.0f, 3.0f
|
||||
};
|
||||
|
||||
const inline f32 CANVAS_RECT_VERTICES[] =
|
||||
{
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1
|
||||
};
|
||||
|
||||
const inline f32 CANVAS_TEXTURE_VERTICES[] =
|
||||
{
|
||||
0, 0, 0.0f, 0.0f,
|
||||
1, 0, 1.0f, 0.0f,
|
||||
1, 1, 1.0f, 1.0f,
|
||||
0, 1, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
struct Canvas
|
||||
{
|
||||
GLuint fbo{};
|
||||
@@ -41,37 +48,40 @@ struct Canvas
|
||||
GLuint rectVBO{};
|
||||
GLuint gridVAO{};
|
||||
GLuint gridVBO{};
|
||||
GLuint texture{};
|
||||
GLuint textureEBO{};
|
||||
GLuint framebuffer{};
|
||||
GLuint textureVAO{};
|
||||
GLuint textureVBO{};
|
||||
vec2 size{};
|
||||
GLuint textureEBO{};
|
||||
ivec2 size{};
|
||||
ivec2 previousSize{};
|
||||
bool isInit = false;
|
||||
};
|
||||
|
||||
void canvas_init(Canvas* self, const vec2& size);
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
|
||||
void canvas_clear(vec4& color);
|
||||
void canvas_bind(Canvas* self);
|
||||
void canvas_viewport_set(Canvas* self);
|
||||
void canvas_unbind(void);
|
||||
void canvas_texture_set(Canvas* self);
|
||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color);
|
||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
|
||||
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
|
||||
void canvas_free(Canvas* self);
|
||||
void canvas_draw(Canvas* self);
|
||||
#define UV_VERTICES(uvMin, uvMax) \
|
||||
{ \
|
||||
0, 0, uvMin.x, uvMin.y, \
|
||||
1, 0, uvMax.x, uvMin.y, \
|
||||
1, 1, uvMax.x, uvMax.y, \
|
||||
0, 1, uvMin.x, uvMax.y \
|
||||
}
|
||||
|
||||
mat4 canvas_mvp_get
|
||||
(
|
||||
mat4& transform,
|
||||
vec2 size,
|
||||
vec2 position = {0.0f, 0.0f},
|
||||
vec2 pivot = {0.0f, 0.0f},
|
||||
f32 rotation = {0.0f},
|
||||
vec2 scale = {1.0f, 1.0f},
|
||||
vec2 pivotAlt = {0.0f, 0.0f},
|
||||
f32 rotationAlt = {0.0f}
|
||||
);
|
||||
#define ATLAS_UV_MIN(type) (ATLAS_POSITION(type) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_MAX(type) ((ATLAS_POSITION(type) + ATLAS_SIZE(type)) / TEXTURE_ATLAS_SIZE)
|
||||
#define ATLAS_UV_ARGS(type) ATLAS_UV_MIN(type), ATLAS_UV_MAX(type)
|
||||
#define ATLAS_UV_VERTICES(type) UV_VERTICES(ATLAS_UV_MIN(type), ATLAS_UV_MAX(type))
|
||||
|
||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
|
||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
|
||||
void canvas_bind(Canvas* self);
|
||||
void canvas_clear(vec4& color);
|
||||
void canvas_draw(Canvas* self);
|
||||
void canvas_free(Canvas* self);
|
||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color);
|
||||
void canvas_init(Canvas* self, const ivec2& size);
|
||||
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
|
||||
void canvas_framebuffer_resize_check(Canvas* self);
|
||||
void canvas_unbind(void);
|
||||
void canvas_viewport_set(Canvas* self);
|
||||
|
||||
void canvas_texture_draw
|
||||
(
|
||||
@@ -79,7 +89,7 @@ void canvas_texture_draw
|
||||
GLuint& shader,
|
||||
GLuint& texture,
|
||||
mat4& transform,
|
||||
const f32* vertices = GL_UV_VERTICES,
|
||||
vec4 tint = COLOR_OPAQUE,
|
||||
const f32* vertices = CANVAS_TEXTURE_VERTICES,
|
||||
vec4 tint = COLOR_OPAQUE,
|
||||
vec3 colorOffset = COLOR_OFFSET_NONE
|
||||
);
|
@@ -1,97 +1,105 @@
|
||||
#include "clipboard.h"
|
||||
|
||||
static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
|
||||
{
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2FrameWithReference* frameWithReference = std::get_if<Anm2FrameWithReference>(&self->data);
|
||||
|
||||
if (!frameWithReference) break;
|
||||
|
||||
anm2_frame_erase(anm2, &frameWithReference->reference);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
|
||||
|
||||
if (!animationWithID) break;
|
||||
|
||||
for (auto & [id, animation] : anm2->animations)
|
||||
{
|
||||
if (id == animationWithID->id)
|
||||
{
|
||||
anm2->animations.erase(animationWithID->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* location, Anm2* anm2)
|
||||
{
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2FrameWithReference* frameWithReference = std::get_if<Anm2FrameWithReference>(&self->data);
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(location);
|
||||
|
||||
if (!frameWithReference || !reference) break;
|
||||
if (frameWithReference->reference.itemType != reference->itemType) break;
|
||||
|
||||
Anm2Animation* animation = anm2_animation_from_reference(anm2, reference);
|
||||
Anm2Item* anm2Item = anm2_item_from_reference(anm2, reference);
|
||||
|
||||
if (!animation || !anm2Item) break;
|
||||
|
||||
anm2_frame_add(anm2, &frameWithReference->frame, reference, reference->frameIndex);
|
||||
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
|
||||
|
||||
if (!animationWithID) break;
|
||||
|
||||
s32 index = 0;
|
||||
|
||||
if (std::holds_alternative<s32>(*location))
|
||||
index = std::get<s32>(*location);
|
||||
else
|
||||
break;
|
||||
|
||||
index = std::clamp(index, 0, (s32)anm2->animations.size());
|
||||
|
||||
map_insert_shift(anm2->animations, index, animationWithID->animation);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_copy(Clipboard* self)
|
||||
{
|
||||
self->item = self->hoveredItem;
|
||||
std::string clipboardText{};
|
||||
|
||||
auto clipboard_text_set = [&]()
|
||||
{
|
||||
if (!SDL_SetClipboardText(clipboardText.c_str()))
|
||||
log_warning(std::format(CLIPBOARD_TEXT_SET_WARNING, SDL_GetError()));
|
||||
};
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
Anm2Frame* frame = anm2_frame_from_reference(self->anm2, reference);
|
||||
if (!frame) break;
|
||||
anm2_frame_serialize_to_string(frame, reference->itemType, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
Anm2Animation* animation = map_find(self->anm2->animations, *id);
|
||||
if (!animation) break;
|
||||
anm2_animation_serialize_to_string(animation, &clipboardText);
|
||||
clipboard_text_set();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_cut(Clipboard* self)
|
||||
{
|
||||
self->item = self->hoveredItem;
|
||||
_clipboard_item_remove(&self->item, self->anm2);
|
||||
clipboard_copy(self);
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
anm2_frame_remove(self->anm2, reference);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
anm2_animation_remove(self->anm2, *id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_paste(Clipboard* self)
|
||||
bool clipboard_paste(Clipboard* self)
|
||||
{
|
||||
_clipboard_item_paste(&self->item, &self->location, self->anm2);
|
||||
auto clipboard_string = [&]()
|
||||
{
|
||||
char* clipboardText = SDL_GetClipboardText();
|
||||
std::string clipboardString = std::string(clipboardText);
|
||||
SDL_free(clipboardText);
|
||||
return clipboardString;
|
||||
};
|
||||
|
||||
switch (self->type)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
|
||||
if (!reference) break;
|
||||
Anm2Frame frame;
|
||||
if (anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
|
||||
anm2_frame_add(self->anm2, &frame, reference);
|
||||
else return false;
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
s32* id = std::get_if<s32>(&self->location);
|
||||
if (!id) break;
|
||||
Anm2Animation animation;
|
||||
if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
|
||||
anm2_animation_add(self->anm2, &animation, *id);
|
||||
else return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clipboard_init(Clipboard* self, Anm2* anm2)
|
||||
@@ -99,3 +107,7 @@ void clipboard_init(Clipboard* self, Anm2* anm2)
|
||||
self->anm2 = anm2;
|
||||
}
|
||||
|
||||
bool clipboard_is_value(void)
|
||||
{
|
||||
return SDL_HasClipboardText();
|
||||
}
|
@@ -2,34 +2,26 @@
|
||||
|
||||
#include "anm2.h"
|
||||
|
||||
enum ClipboardItemType
|
||||
#define CLIPBOARD_TEXT_SET_WARNING "Unable to set clipboard text! ({})"
|
||||
|
||||
enum ClipboardType
|
||||
{
|
||||
CLIPBOARD_NONE,
|
||||
CLIPBOARD_FRAME,
|
||||
CLIPBOARD_ANIMATION,
|
||||
CLIPBOARD_ANIMATION
|
||||
};
|
||||
|
||||
struct ClipboardItem
|
||||
{
|
||||
std::variant<std::monostate, Anm2FrameWithReference, Anm2AnimationWithID, Anm2EventWithID> data = std::monostate();
|
||||
ClipboardItemType type = CLIPBOARD_NONE;
|
||||
|
||||
ClipboardItem() = default;
|
||||
ClipboardItem(const Anm2FrameWithReference& frame) : data(frame), type(CLIPBOARD_FRAME) {}
|
||||
ClipboardItem(const Anm2AnimationWithID& animation) : data(animation), type(CLIPBOARD_ANIMATION) {}
|
||||
};
|
||||
|
||||
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, s32>;
|
||||
|
||||
struct Clipboard
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
ClipboardItem item;
|
||||
ClipboardItem hoveredItem;
|
||||
ClipboardType type;
|
||||
ClipboardLocation location;
|
||||
};
|
||||
|
||||
bool clipboard_is_value(void);
|
||||
void clipboard_copy(Clipboard* self);
|
||||
void clipboard_cut(Clipboard* self);
|
||||
void clipboard_paste(Clipboard* self);
|
||||
bool clipboard_paste(Clipboard* self);
|
||||
void clipboard_init(Clipboard* self, Anm2* anm2);
|
@@ -73,11 +73,11 @@ void dialog_ffmpeg_path_set(Dialog* self)
|
||||
void dialog_explorer_open(const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ShellExecuteA(NULL, "open", path.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
ShellExecuteA(NULL, DIALOG_FILE_EXPLORER_COMMAND, path.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
#else
|
||||
char cmd[512];
|
||||
snprintf(cmd, sizeof(cmd), "xdg-open \"%s\" &", path.c_str());
|
||||
system(cmd);
|
||||
char command[DIALOG_FILE_EXPLORER_COMMAND_SIZE];
|
||||
snprintf(command, sizeof(command), DIALOG_FILE_EXPLORER_COMMAND, path.c_str());
|
||||
system(command);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,14 @@
|
||||
#include "render.h"
|
||||
#include "window.h"
|
||||
|
||||
#define DIALOG_FILE_EXPLORER_COMMAND_SIZE 512
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIALOG_FILE_EXPLORER_COMMAND "open"
|
||||
#else
|
||||
#define DIALOG_FILE_EXPLORER_COMMAND "xdg-open \"%s\" &"
|
||||
#endif
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_ANM2[] =
|
||||
{
|
||||
{"Anm2 file", "anm2;xml"}
|
||||
|
@@ -20,31 +20,32 @@ void editor_draw(Editor* self)
|
||||
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
|
||||
|
||||
canvas_texture_set(&self->canvas);
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
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->resources->textures[self->spritesheetID];
|
||||
mat4 mvp = canvas_mvp_get(transform, texture.size);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, mvp);
|
||||
Texture& texture = spritesheet->texture;
|
||||
|
||||
mat4 spritesheetTransform = transform * quad_model_get(texture.size);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);
|
||||
|
||||
if (self->settings->editorIsBorder)
|
||||
canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_BORDER_COLOR);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, spritesheetTransform, EDITOR_BORDER_COLOR);
|
||||
|
||||
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
|
||||
|
||||
if (frame)
|
||||
{
|
||||
mvp = canvas_mvp_get(transform, frame->size, frame->crop);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_FRAME_COLOR);
|
||||
mat4 cropTransform = transform * quad_model_get(frame->size, frame->crop);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR);
|
||||
|
||||
mvp = canvas_mvp_get(transform, CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
|
||||
mat4 pivotTransform = transform * quad_model_get(CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, mvp, vertices, EDITOR_PIVOT_COLOR);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, EDITOR_PIVOT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -34,14 +34,16 @@ ffmpeg_render
|
||||
command = string_quote(command);
|
||||
#endif
|
||||
|
||||
log_command(command);
|
||||
|
||||
FILE* fp = POPEN(command.c_str(), PWRITE_MODE);
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
log_info(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
|
||||
log_error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t frameBytes = size.x * size.y * TEXTURE_CHANNELS;
|
||||
|
||||
for (const auto& frame : frames)
|
||||
|
@@ -26,25 +26,28 @@ void generate_preview_draw(GeneratePreview* self)
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
|
||||
Texture* texture = map_find(self->resources->textures, self->anm2->layers[self->reference->itemID].spritesheetID);
|
||||
|
||||
if (item && texture && !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 model = quad_model_get(size, {}, pivot, {}, CANVAS_SCALE_DEFAULT);
|
||||
mat4 generateTransform = transform * model;
|
||||
vec2 textureSize = vec2(texture.size);
|
||||
vec2 uvMin = (crop + vec2(0.5f)) / textureSize;
|
||||
vec2 uvMax = (crop + size - vec2(0.5f)) / textureSize;
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture->id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
|
||||
mat4 generateTransform = transform * quad_model_get(size, {}, pivot);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
canvas_unbind();
|
||||
}
|
||||
|
||||
|
1103
src/imgui.cpp
1103
src/imgui.cpp
File diff suppressed because it is too large
Load Diff
976
src/imgui.h
976
src/imgui.h
File diff suppressed because it is too large
Load Diff
56
src/log.cpp
Normal file
56
src/log.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "log.h"
|
||||
|
||||
inline std::ofstream logFile;
|
||||
|
||||
std::string log_path_get(void)
|
||||
{
|
||||
return preferences_path_get() + LOG_PATH;
|
||||
}
|
||||
|
||||
void log_write(const std::string& string)
|
||||
{
|
||||
std::println("{}", string);
|
||||
|
||||
if (logFile.is_open())
|
||||
{
|
||||
logFile << string << '\n';
|
||||
logFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
void log_init(void)
|
||||
{
|
||||
std::string logFilepath = log_path_get();
|
||||
logFile.open(logFilepath, std::ios::out | std::ios::trunc);
|
||||
if (!logFile) std::println("{}", std::format(LOG_INIT_ERROR, logFilepath));
|
||||
}
|
||||
|
||||
void log_error(const std::string& error)
|
||||
{
|
||||
log_write(LOG_ERROR_FORMAT + error);
|
||||
}
|
||||
|
||||
void log_info(const std::string& info)
|
||||
{
|
||||
log_write(LOG_INFO_FORMAT + info);
|
||||
}
|
||||
|
||||
void log_warning(const std::string& warning)
|
||||
{
|
||||
log_write(LOG_WARNING_FORMAT + warning);
|
||||
}
|
||||
|
||||
void log_imgui(const std::string& imgui)
|
||||
{
|
||||
log_write(LOG_IMGUI_FORMAT + imgui);
|
||||
}
|
||||
|
||||
void log_command(const std::string& command)
|
||||
{
|
||||
log_write(LOG_COMMAND_FORMAT + command);
|
||||
}
|
||||
|
||||
void log_free(void)
|
||||
{
|
||||
logFile.close();
|
||||
}
|
21
src/log.h
Normal file
21
src/log.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
|
||||
#define LOG_WARNING_FORMAT "[WARNING] "
|
||||
#define LOG_ERROR_FORMAT "[ERROR] "
|
||||
#define LOG_INFO_FORMAT "[INFO] "
|
||||
#define LOG_IMGUI_FORMAT "[IMGUI] "
|
||||
#define LOG_INIT_ERROR "[ERROR] Failed to open log file: {}"
|
||||
#define LOG_COMMAND_FORMAT "[COMMAND] "
|
||||
#define LOG_PATH "log.txt"
|
||||
|
||||
std::string log_path_get(void);
|
||||
void log_init(void);
|
||||
void log_write(const std::string& file);
|
||||
void log_error(const std::string& error);
|
||||
void log_info(const std::string& info);
|
||||
void log_warning(const std::string& warning);
|
||||
void log_imgui(const std::string& imgui);
|
||||
void log_command(const std::string& command);
|
||||
void log_free(void);
|
18
src/main.cpp
18
src/main.cpp
@@ -4,7 +4,7 @@ static bool _anm2_rescale(const std::string& file, f32 scale)
|
||||
{
|
||||
Anm2 anm2;
|
||||
|
||||
if (!anm2_deserialize(&anm2, nullptr, file)) return false;
|
||||
if (!anm2_deserialize(&anm2, file, false)) return false;
|
||||
anm2_scale(&anm2, scale);
|
||||
return anm2_serialize(&anm2, file);
|
||||
}
|
||||
@@ -14,6 +14,8 @@ main(s32 argc, char* argv[])
|
||||
{
|
||||
State state;
|
||||
|
||||
log_init();
|
||||
|
||||
if (argc > 0 && argv[1])
|
||||
{
|
||||
if (std::string(argv[1]) == ARGUMENT_RESCALE)
|
||||
@@ -33,9 +35,19 @@ main(s32 argc, char* argv[])
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (std::string(argv[1]) == ARGUMENT_TEST && argv[2])
|
||||
{
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2]), false)) return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (std::string(argv[1]) == ARGUMENT_TEST_GL && argv[2])
|
||||
{
|
||||
if (!sdl_init(&state, true)) return EXIT_FAILURE;
|
||||
if (anm2_deserialize(&state.anm2, std::string(argv[2]))) return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
if (argv[1])
|
||||
state.argument = argv[1];
|
||||
if (argv[1]) state.argument = argv[1];
|
||||
}
|
||||
|
||||
init(&state);
|
||||
|
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#define ARGUMENT_TEST "--test"
|
||||
#define ARGUMENT_TEST_GL "--test-gl"
|
||||
|
||||
#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."
|
||||
|
227
src/preview.cpp
227
src/preview.cpp
@@ -29,7 +29,7 @@ void preview_tick(Preview* self)
|
||||
{
|
||||
if (self->isRender)
|
||||
{
|
||||
vec2& size = self->canvas.size;
|
||||
ivec2& size = self->canvas.size;
|
||||
u32 framebufferPixelCount = size.x * size.y * TEXTURE_CHANNELS;
|
||||
std::vector<u8> framebufferPixels(framebufferPixelCount);
|
||||
Texture frameTexture;
|
||||
@@ -40,7 +40,7 @@ void preview_tick(Preview* self)
|
||||
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
texture_from_rgba_init(&frameTexture, size, TEXTURE_CHANNELS, framebufferPixels.data());
|
||||
texture_from_rgba_init(&frameTexture, size, framebufferPixels.data());
|
||||
self->renderFrames.push_back(frameTexture);
|
||||
}
|
||||
|
||||
@@ -81,11 +81,12 @@ 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);
|
||||
|
||||
canvas_texture_set(&self->canvas);
|
||||
canvas_framebuffer_resize_check(&self->canvas);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
@@ -95,154 +96,142 @@ 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 = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
|
||||
|
||||
// Root
|
||||
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
|
||||
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale));
|
||||
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
|
||||
mat4 rootTransform = transform * model;
|
||||
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;
|
||||
Anm2Frame frame;
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
|
||||
if (!frame.isVisible) return;
|
||||
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
|
||||
|
||||
if (!frame.isVisible)
|
||||
continue;
|
||||
|
||||
mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 layerTransform = transform * (rootModel * model);
|
||||
vec3 frameColorOffset = frame.offsetRGB + colorOffset;
|
||||
vec4 frameTint = frame.tintRGBA;
|
||||
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
|
||||
|
||||
Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[id].spritesheetID);
|
||||
if (!spritesheet) return;
|
||||
|
||||
Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
|
||||
|
||||
if (texture && !texture->isInvalid)
|
||||
{
|
||||
vec2 uvMin = frame.crop / vec2(texture->size);
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
Texture& texture = spritesheet->texture;
|
||||
if (texture.isInvalid) return;
|
||||
|
||||
vec2 inset = 0.5f / vec2(texture.size);
|
||||
vec2 uvMin = frame.crop / vec2(texture.size) + inset;
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frameTint, frameColorOffset);
|
||||
|
||||
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);
|
||||
{
|
||||
vec4 borderColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_BORDER_COLOR;
|
||||
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, borderColor);
|
||||
}
|
||||
|
||||
if (self->settings->previewIsPivots)
|
||||
{
|
||||
vec4 pivotColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_PIVOT_COLOR;
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 pivotTransform = transform * (rootModel * pivotModel);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, pivotColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Nulls
|
||||
if (self->settings->previewIsTargets)
|
||||
auto null_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
|
||||
{
|
||||
for (auto& [id, nullAnimation] : animation->nullAnimations)
|
||||
{
|
||||
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
|
||||
continue;
|
||||
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}, 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 = quad_model_get(size, frame.position, size * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 nullTransform = transform * (rootModel * model);
|
||||
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
|
||||
|
||||
if (null.isShowRect)
|
||||
{
|
||||
mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
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 = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
|
||||
|
||||
for (auto [i, id] : self->anm2->layerMap)
|
||||
{
|
||||
Anm2Frame frame;
|
||||
Anm2Item& layerAnimation = animationOverlay->layerAnimations[id];
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, time);
|
||||
if (!frame.isVisible) return;
|
||||
|
||||
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
|
||||
continue;
|
||||
Anm2Null null = self->anm2->nulls[id];
|
||||
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationOverlayID, ANM2_LAYER, id}, self->time);
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) :
|
||||
(self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ?
|
||||
PREVIEW_NULL_SELECTED_COLOR : PREVIEW_NULL_COLOR;
|
||||
|
||||
if (!frame.isVisible)
|
||||
continue;
|
||||
vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
|
||||
AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
|
||||
|
||||
mat4 model = quad_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 nullTransform = transform * (rootModel * model);
|
||||
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
|
||||
|
||||
Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
|
||||
|
||||
if (!texture || texture->isInvalid)
|
||||
continue;
|
||||
if (null.isShowRect)
|
||||
{
|
||||
mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
|
||||
mat4 rectTransform = transform * (rootModel * rectModel);
|
||||
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
|
||||
}
|
||||
};
|
||||
|
||||
vec2 uvMin = frame.crop / vec2(texture->size);
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
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 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 layerTransform = transform * (rootModel * model);
|
||||
mat4 rootModel = self->settings->previewIsRootTransform ?
|
||||
quad_model_parent_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
|
||||
|
||||
vec4 tint = frame.tintRGBA;
|
||||
tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
|
||||
if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible)
|
||||
root_draw(root, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, tint, frame.offsetRGB);
|
||||
}
|
||||
}
|
||||
for (auto id : animation->layerOrder)
|
||||
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
|
||||
if (self->settings->previewIsIcons)
|
||||
for (auto& [id, _] : animation->nullAnimations)
|
||||
null_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
|
||||
};
|
||||
|
||||
auto onionskin_draw = [&](s32 count, s32 direction, vec3 colorOffset)
|
||||
{
|
||||
for (s32 i = 1; i <= count; i++)
|
||||
{
|
||||
f32 time = self->time + (f32)(direction * i);
|
||||
f32 alphaOffset = (1.0f / (count + 1)) * i;
|
||||
base_draw(time, colorOffset, alphaOffset, true);
|
||||
}
|
||||
};
|
||||
|
||||
auto onionskins_draw = [&]()
|
||||
{
|
||||
if (!self->settings->onionskinIsEnabled) return;
|
||||
onionskin_draw(self->settings->onionskinBeforeCount, -1, self->settings->onionskinBeforeColorOffset);
|
||||
onionskin_draw(self->settings->onionskinAfterCount, 1, self->settings->onionskinAfterColorOffset);
|
||||
};
|
||||
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_BELOW) onionskins_draw();
|
||||
base_draw(self->time);
|
||||
if (self->settings->onionskinDrawOrder == ONIONSKIN_ABOVE) onionskins_draw();
|
||||
};
|
||||
|
||||
animation_draw(self->reference->animationID);
|
||||
animation_draw(self->animationOverlayID);
|
||||
|
||||
canvas_unbind();
|
||||
}
|
||||
|
@@ -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",
|
||||
};
|
@@ -1,20 +1,8 @@
|
||||
#include "resources.h"
|
||||
|
||||
void resources_texture_init(Resources* self, const std::string& path, s32 id)
|
||||
{
|
||||
Texture texture;
|
||||
|
||||
if (map_find(self->textures, id))
|
||||
texture_free(&self->textures[id]);
|
||||
|
||||
texture_from_path_init(&texture, path);
|
||||
|
||||
self->textures[id] = texture;
|
||||
}
|
||||
|
||||
void resources_init(Resources* self)
|
||||
{
|
||||
texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, TEXTURE_CHANNELS, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
|
||||
texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
|
||||
|
||||
for (s32 i = 0; i < SHADER_COUNT; i++)
|
||||
shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment);
|
||||
@@ -22,18 +10,8 @@ void resources_init(Resources* self)
|
||||
|
||||
void resources_free(Resources* self)
|
||||
{
|
||||
resources_textures_free(self);
|
||||
|
||||
for (auto& shader : self->shaders)
|
||||
shader_free(&shader);
|
||||
|
||||
texture_free(&self->atlas);
|
||||
}
|
||||
|
||||
void resources_textures_free(Resources* self)
|
||||
{
|
||||
for (auto& [id, texture] : self->textures)
|
||||
texture_free(&self->textures[id]);
|
||||
|
||||
log_info(RESOURCES_TEXTURES_FREE_INFO);
|
||||
}
|
@@ -10,10 +10,7 @@ struct Resources
|
||||
{
|
||||
GLuint shaders[SHADER_COUNT];
|
||||
Texture atlas;
|
||||
std::map<s32, Texture> textures;
|
||||
};
|
||||
|
||||
void resources_init(Resources* self);
|
||||
void resources_texture_init(Resources* self, const std::string& path, s32 id);
|
||||
void resources_free(Resources* self);
|
||||
void resources_textures_free(Resources* self);
|
||||
|
178
src/settings.cpp
178
src/settings.cpp
@@ -6,77 +6,92 @@ 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)
|
||||
{
|
||||
char* path = SDL_GetPrefPath("", SETTINGS_FOLDER);
|
||||
std::string filePath = std::string(path) + SETTINGS_PATH;
|
||||
SDL_free(path);
|
||||
std::string filePath = preferences_path_get() + SETTINGS_PATH;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@@ -113,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);
|
||||
@@ -120,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);
|
||||
@@ -216,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);
|
||||
}
|
||||
}
|
566
src/settings.h
566
src/settings.h
@@ -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,147 @@
|
||||
#define SETTINGS_PATH "settings.ini"
|
||||
#define SETTINGS_TEMPORARY_EXTENSION ".tmp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SETTINGS_FFMPEG_PATH_VALUE_DEFAULT "C:\\ffmpeg\\bin\\ffmpeg.exe"
|
||||
#else
|
||||
#define SETTINGS_FFMPEG_PATH_VALUE_DEFAULT "/usr/bin/ffmpeg"
|
||||
#endif
|
||||
|
||||
#define SETTINGS_LIST \
|
||||
/* name, symbol, type, defaultValue */ \
|
||||
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1600, 900}) \
|
||||
X(isVsync, IS_VSYNC, TYPE_BOOL, true) \
|
||||
\
|
||||
X(hotkeyCenterView, HOTKEY_CENTER_VIEW, TYPE_STRING, "Home") \
|
||||
X(hotkeyFit, HOTKEY_FIT, TYPE_STRING, "F") \
|
||||
X(hotkeyZoomIn, HOTKEY_ZOOM_IN, TYPE_STRING, "Ctrl++") \
|
||||
X(hotkeyZoomOut, HOTKEY_ZOOM_OUT, TYPE_STRING, "Ctrl+-") \
|
||||
X(hotkeyPlayPause, HOTKEY_PLAY_PAUSE, TYPE_STRING, "Space") \
|
||||
X(hotkeyOnionskin, HOTKEY_ONIONSKIN, TYPE_STRING, "O") \
|
||||
X(hotkeyNew, HOTKEY_NEW, TYPE_STRING, "Ctrl+N") \
|
||||
X(hotkeyOpen, HOTKEY_OPEN, TYPE_STRING, "Ctrl+O") \
|
||||
X(hotkeySave, HOTKEY_SAVE, TYPE_STRING, "Ctrl+S") \
|
||||
X(hotkeySaveAs, HOTKEY_SAVE_AS, TYPE_STRING, "Ctrl+Shift+S") \
|
||||
X(hotkeyExit, HOTKEY_EXIT, TYPE_STRING, "Alt+F4") \
|
||||
X(hotkeyPan, HOTKEY_PAN, TYPE_STRING, "P") \
|
||||
X(hotkeyMove, HOTKEY_MOVE, TYPE_STRING, "V") \
|
||||
X(hotkeyRotate, HOTKEY_ROTATE, TYPE_STRING, "R") \
|
||||
X(hotkeyScale, HOTKEY_SCALE, TYPE_STRING, "S") \
|
||||
X(hotkeyCrop, HOTKEY_CROP, TYPE_STRING, "C") \
|
||||
X(hotkeyDraw, HOTKEY_DRAW, TYPE_STRING, "B") \
|
||||
X(hotkeyErase, HOTKEY_ERASE, TYPE_STRING, "E") \
|
||||
X(hotkeyColorPicker, HOTKEY_COLOR_PICKER, TYPE_STRING, "I") \
|
||||
X(hotkeyUndo, HOTKEY_UNDO, TYPE_STRING, "Ctrl+Z") \
|
||||
X(hotkeyRedo, HOTKEY_REDO, TYPE_STRING, "Ctrl+Shift+Z") \
|
||||
X(hotkeyCopy, HOTKEY_COPY, TYPE_STRING, "Ctrl+C") \
|
||||
X(hotkeyCut, HOTKEY_CUT, TYPE_STRING, "Ctrl+X") \
|
||||
X(hotkeyPaste, HOTKEY_PASTE, TYPE_STRING, "Ctrl+V") \
|
||||
\
|
||||
X(playbackIsLoop, PLAYBACK_IS_LOOP, TYPE_BOOL, true) \
|
||||
X(playbackIsClampPlayhead,PLAYBACK_IS_CLAMP_PLAYHEAD, TYPE_BOOL, true) \
|
||||
\
|
||||
X(changeIsCrop, CHANGE_IS_CROP, TYPE_BOOL, false) \
|
||||
X(changeIsSize, CHANGE_IS_SIZE, TYPE_BOOL, false) \
|
||||
X(changeIsPosition, CHANGE_IS_POSITION, TYPE_BOOL, false) \
|
||||
X(changeIsPivot, CHANGE_IS_PIVOT, TYPE_BOOL, false) \
|
||||
X(changeIsScale, CHANGE_IS_SCALE, TYPE_BOOL, false) \
|
||||
X(changeIsRotation, CHANGE_IS_ROTATION, TYPE_BOOL, false) \
|
||||
X(changeIsDelay, CHANGE_IS_DELAY, TYPE_BOOL, false) \
|
||||
X(changeIsTint, CHANGE_IS_TINT, TYPE_BOOL, false) \
|
||||
X(changeIsColorOffset, CHANGE_IS_COLOR_OFFSET, TYPE_BOOL, false) \
|
||||
X(changeIsVisibleSet, CHANGE_IS_VISIBLE_SET, TYPE_BOOL, false) \
|
||||
X(changeIsInterpolatedSet,CHANGE_IS_INTERPOLATED_SET, TYPE_BOOL, false) \
|
||||
X(changeIsFromSelectedFrame,CHANGE_IS_FROM_SELECTED_FRAME,TYPE_BOOL, false) \
|
||||
X(changeCrop, CHANGE_CROP, TYPE_VEC2, {}) \
|
||||
X(changeSize, CHANGE_SIZE, TYPE_VEC2, {}) \
|
||||
X(changePosition, CHANGE_POSITION, TYPE_VEC2, {}) \
|
||||
X(changePivot, CHANGE_PIVOT, TYPE_VEC2, {}) \
|
||||
X(changeScale, CHANGE_SCALE, TYPE_VEC2, {}) \
|
||||
X(changeRotation, CHANGE_ROTATION, TYPE_FLOAT, 0.0f) \
|
||||
X(changeDelay, CHANGE_DELAY, TYPE_INT, 0) \
|
||||
X(changeTint, CHANGE_TINT, TYPE_VEC4, {}) \
|
||||
X(changeColorOffset, CHANGE_COLOR_OFFSET, TYPE_VEC3, {}) \
|
||||
X(changeIsVisible, CHANGE_IS_VISIBLE, TYPE_BOOL, false) \
|
||||
X(changeIsInterpolated, CHANGE_IS_INTERPOLATED, TYPE_BOOL, false) \
|
||||
X(changeNumberFrames, CHANGE_NUMBER_FRAMES, TYPE_INT, 1) \
|
||||
\
|
||||
X(scaleValue, SCALE_VALUE, TYPE_FLOAT, 1.0f) \
|
||||
\
|
||||
X(previewIsAxes, PREVIEW_IS_AXES, TYPE_BOOL, true) \
|
||||
X(previewIsGrid, PREVIEW_IS_GRID, TYPE_BOOL, true) \
|
||||
X(previewIsRootTransform, PREVIEW_IS_ROOT_TRANSFORM, TYPE_BOOL, true) \
|
||||
X(previewIsTriggers, PREVIEW_IS_TRIGGERS, TYPE_BOOL, true) \
|
||||
X(previewIsPivots, PREVIEW_IS_PIVOTS, TYPE_BOOL, false) \
|
||||
X(previewIsIcons, PREVIEW_IS_ICONS, TYPE_BOOL, true) \
|
||||
X(previewIsBorder, PREVIEW_IS_BORDER, TYPE_BOOL, false) \
|
||||
X(previewIsAltIcons, PREVIEW_IS_ALT_ICONS, TYPE_BOOL, false) \
|
||||
X(previewOverlayTransparency,PREVIEW_OVERLAY_TRANSPARENCY,TYPE_FLOAT, 255.0f) \
|
||||
X(previewZoom, PREVIEW_ZOOM, TYPE_FLOAT, 200.0f) \
|
||||
X(previewPan, PREVIEW_PAN, TYPE_VEC2, {}) \
|
||||
X(previewGridSize, PREVIEW_GRID_SIZE, TYPE_IVEC2, {32,32}) \
|
||||
X(previewGridOffset, PREVIEW_GRID_OFFSET, TYPE_IVEC2, {}) \
|
||||
X(previewGridColor, PREVIEW_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(previewAxesColor, PREVIEW_AXES_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(previewBackgroundColor, PREVIEW_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
|
||||
\
|
||||
X(propertiesIsRound, PROPERTIES_IS_ROUND, TYPE_BOOL, false) \
|
||||
\
|
||||
X(generateStartPosition, GENERATE_START_POSITION, TYPE_IVEC2, {}) \
|
||||
X(generateSize, GENERATE_SIZE, TYPE_IVEC2, {64,64}) \
|
||||
X(generatePivot, GENERATE_PIVOT, TYPE_IVEC2, {32,32}) \
|
||||
X(generateRows, GENERATE_ROWS, TYPE_INT, 4) \
|
||||
X(generateColumns, GENERATE_COLUMNS, TYPE_INT, 4) \
|
||||
X(generateCount, GENERATE_COUNT, TYPE_INT, 16) \
|
||||
X(generateDelay, GENERATE_DELAY, TYPE_INT, 1) \
|
||||
\
|
||||
X(editorIsGrid, EDITOR_IS_GRID, TYPE_BOOL, true) \
|
||||
X(editorIsGridSnap, EDITOR_IS_GRID_SNAP, TYPE_BOOL, true) \
|
||||
X(editorIsBorder, EDITOR_IS_BORDER, TYPE_BOOL, true) \
|
||||
X(editorZoom, EDITOR_ZOOM, TYPE_FLOAT, 200.0f) \
|
||||
X(editorPan, EDITOR_PAN, TYPE_VEC2, {0.0,0.0}) \
|
||||
X(editorGridSize, EDITOR_GRID_SIZE, TYPE_IVEC2, {32,32}) \
|
||||
X(editorGridOffset, EDITOR_GRID_OFFSET, TYPE_IVEC2, {32,32}) \
|
||||
X(editorGridColor, EDITOR_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(editorBackgroundColor, EDITOR_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
|
||||
\
|
||||
X(mergeType, MERGE_TYPE, TYPE_INT, ANM2_MERGE_APPEND_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(timelineAddItemType, TIMELINE_ADD_ITEM_TYPE, TYPE_INT, ANM2_LAYER) \
|
||||
X(timelineIsShowUnused, TIMELINE_IS_SHOW_UNUSED, TYPE_BOOL, true) \
|
||||
\
|
||||
X(onionskinIsEnabled, ONIONSKIN_IS_ENABLED, TYPE_BOOL, false) \
|
||||
X(onionskinDrawOrder, ONIONSKIN_DRAW_ORDER, TYPE_INT, ONIONSKIN_BELOW) \
|
||||
X(onionskinBeforeCount, ONIONSKIN_BEFORE_COUNT, TYPE_INT, 0) \
|
||||
X(onionskinAfterCount, ONIONSKIN_AFTER_COUNT, TYPE_INT, 0) \
|
||||
X(onionskinBeforeColorOffset,ONIONSKIN_BEFORE_COLOR_OFFSET,TYPE_VEC3, COLOR_RED) \
|
||||
X(onionskinAfterColorOffset, ONIONSKIN_AFTER_COLOR_OFFSET,TYPE_VEC3, COLOR_BLUE) \
|
||||
\
|
||||
X(tool, TOOL, TYPE_INT, TOOL_PAN) \
|
||||
X(toolColor, TOOL_COLOR, TYPE_VEC4, {1.0,1.0,1.0,1.0}) \
|
||||
\
|
||||
X(renderType, RENDER_TYPE, TYPE_INT, RENDER_PNG) \
|
||||
X(renderPath, RENDER_PATH, TYPE_STRING, ".") \
|
||||
X(renderFormat, RENDER_FORMAT, TYPE_STRING, "{}.png") \
|
||||
X(ffmpegPath, FFMPEG_PATH, TYPE_STRING, SETTINGS_FFMPEG_PATH_VALUE_DEFAULT)
|
||||
|
||||
#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 +168,94 @@ 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(FIT, "Fit") \
|
||||
X(ZOOM_IN, "Zoom In") \
|
||||
X(ZOOM_OUT, "Zoom Out") \
|
||||
X(PLAY_PAUSE, "Play/Pause") \
|
||||
X(ONIONSKIN, "Onionskin") \
|
||||
X(NEW, "New") \
|
||||
X(OPEN, "Open") \
|
||||
X(SAVE, "Save") \
|
||||
X(SAVE_AS, "Save As") \
|
||||
X(EXIT, "Exit") \
|
||||
X(PAN, "Pan") \
|
||||
X(MOVE, "Move") \
|
||||
X(ROTATE, "Rotate") \
|
||||
X(SCALE, "Scale") \
|
||||
X(CROP, "Crop") \
|
||||
X(DRAW, "Draw") \
|
||||
X(ERASE, "Erase") \
|
||||
X(COLOR_PICKER, "Color Picker") \
|
||||
X(UNDO, "Undo") \
|
||||
X(REDO, "Redo") \
|
||||
X(COPY, "Copy") \
|
||||
X(CUT, "Cut") \
|
||||
X(PASTE, "Paste") \
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define X(name, str) HOTKEY_##name,
|
||||
HOTKEY_LIST
|
||||
#undef X
|
||||
HOTKEY_COUNT
|
||||
} 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::hotkeyFit,
|
||||
&Settings::hotkeyZoomIn,
|
||||
&Settings::hotkeyZoomOut,
|
||||
&Settings::hotkeyPlayPause,
|
||||
&Settings::hotkeyOnionskin,
|
||||
&Settings::hotkeyNew,
|
||||
&Settings::hotkeyOpen,
|
||||
&Settings::hotkeySave,
|
||||
&Settings::hotkeySaveAs,
|
||||
&Settings::hotkeyExit,
|
||||
&Settings::hotkeyPan,
|
||||
&Settings::hotkeyMove,
|
||||
&Settings::hotkeyRotate,
|
||||
&Settings::hotkeyScale,
|
||||
&Settings::hotkeyCrop,
|
||||
&Settings::hotkeyDraw,
|
||||
&Settings::hotkeyErase,
|
||||
&Settings::hotkeyColorPicker,
|
||||
&Settings::hotkeyUndo,
|
||||
&Settings::hotkeyRedo,
|
||||
&Settings::hotkeyCopy,
|
||||
&Settings::hotkeyCut,
|
||||
&Settings::hotkeyPaste
|
||||
};
|
||||
|
||||
const 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 +265,86 @@ Collapsed=0
|
||||
|
||||
[Window][Tools]
|
||||
Pos=8,40
|
||||
Size=39,654
|
||||
Size=38,516
|
||||
Collapsed=0
|
||||
DockId=0x0000000B,0
|
||||
|
||||
[Window][Animations]
|
||||
Pos=1452,388
|
||||
Size=458,306
|
||||
Pos=1289,307
|
||||
Size=303,249
|
||||
Collapsed=0
|
||||
DockId=0x0000000A,0
|
||||
|
||||
[Window][Events]
|
||||
Pos=1025,348
|
||||
Size=425,346
|
||||
Pos=957,264
|
||||
Size=330,292
|
||||
Collapsed=0
|
||||
DockId=0x00000008,0
|
||||
DockId=0x00000008,2
|
||||
|
||||
[Window][Spritesheets]
|
||||
Pos=1452,40
|
||||
Size=458,346
|
||||
Pos=1289,40
|
||||
Size=303,265
|
||||
Collapsed=0
|
||||
DockId=0x00000009,0
|
||||
|
||||
[Window][Animation Preview]
|
||||
Pos=49,40
|
||||
Size=974,654
|
||||
Pos=48,40
|
||||
Size=907,516
|
||||
Collapsed=0
|
||||
DockId=0x0000000C,0
|
||||
|
||||
[Window][Spritesheet Editor]
|
||||
Pos=49,40
|
||||
Size=974,654
|
||||
Pos=48,40
|
||||
Size=907,516
|
||||
Collapsed=0
|
||||
DockId=0x0000000C,1
|
||||
|
||||
[Window][Timeline]
|
||||
Pos=8,696
|
||||
Size=1902,360
|
||||
Pos=8,558
|
||||
Size=1584,334
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Frame Properties]
|
||||
Pos=1025,40
|
||||
Size=425,306
|
||||
Pos=957,40
|
||||
Size=330,222
|
||||
Collapsed=0
|
||||
DockId=0x00000007,0
|
||||
|
||||
[Window][Onionskin]
|
||||
Pos=957,264
|
||||
Size=330,292
|
||||
Collapsed=0
|
||||
DockId=0x00000008,3
|
||||
|
||||
[Window][Layers]
|
||||
Pos=957,264
|
||||
Size=330,292
|
||||
Collapsed=0
|
||||
DockId=0x00000008,0
|
||||
|
||||
[Window][Nulls]
|
||||
Pos=957,264
|
||||
Size=330,292
|
||||
Collapsed=0
|
||||
DockId=0x00000008,1
|
||||
|
||||
|
||||
[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
|
||||
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
|
||||
DockSpace ID=0xFC02A410 Window=0x0E46F4F7 Pos=8,40 Size=1584,852 Split=Y
|
||||
DockNode ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,680 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1017,1016 Split=X Selected=0x024430EF
|
||||
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1264,654 Split=X Selected=0x024430EF
|
||||
DockNode ID=0x0000000B Parent=0x00000005 SizeRef=38,654 Selected=0x18A5FDB9
|
||||
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=1224,654 CentralNode=1 Selected=0x024430EF
|
||||
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=330,654 Split=Y Selected=0x754E368F
|
||||
DockNode ID=0x00000007 Parent=0x00000006 SizeRef=631,293 Selected=0x754E368F
|
||||
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=631,385 Selected=0xCD8384B1
|
||||
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=303,1016 Split=Y Selected=0x4EFD0020
|
||||
DockNode ID=0x00000009 Parent=0x00000002 SizeRef=634,349 Selected=0x4EFD0020
|
||||
DockNode ID=0x0000000A Parent=0x00000002 SizeRef=634,329 Selected=0xC1986EE2
|
||||
DockNode ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,334 Selected=0x4F89F0DC
|
||||
|
||||
)";
|
||||
|
||||
void settings_save(Settings* self);
|
||||
|
@@ -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);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
#include "log.h"
|
||||
|
||||
#define SHADER_INFO_LOG_MAX 0xFF
|
||||
#define SHADER_INIT_ERROR "Failed to initialize shader {}:\n{}"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#include "snapshots.h"
|
||||
|
||||
static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot)
|
||||
static void _snapshot_stack_push(SnapshotStack* stack, Snapshot* snapshot)
|
||||
{
|
||||
if (stack->top >= SNAPSHOT_STACK_MAX)
|
||||
{
|
||||
@@ -11,20 +11,29 @@ static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot)
|
||||
stack->snapshots[stack->top++] = *snapshot;
|
||||
}
|
||||
|
||||
static bool _snapshot_stack_pop(SnapshotStack* stack, Snapshot* snapshot)
|
||||
static Snapshot* _snapshot_stack_pop(SnapshotStack* stack)
|
||||
{
|
||||
if (stack->top == 0) return false;
|
||||
|
||||
*snapshot = stack->snapshots[--stack->top];
|
||||
return true;
|
||||
if (stack->top == 0) return nullptr;
|
||||
return &stack->snapshots[--stack->top];
|
||||
}
|
||||
|
||||
static void _snapshot_set(Snapshots* self, const Snapshot& snapshot)
|
||||
static void _snapshot_set(Snapshots* self, Snapshot* snapshot)
|
||||
{
|
||||
*self->anm2 = snapshot.anm2;
|
||||
*self->reference = snapshot.reference;
|
||||
self->preview->time = snapshot.time;
|
||||
self->action = snapshot.action;
|
||||
if (!snapshot) return;
|
||||
|
||||
*self->anm2 = snapshot->anm2;
|
||||
*self->reference = snapshot->reference;
|
||||
self->preview->time = snapshot->time;
|
||||
self->action = snapshot->action;
|
||||
|
||||
anm2_spritesheet_texture_pixels_upload(self->anm2);
|
||||
}
|
||||
|
||||
Snapshot snapshot_get(Snapshots* self)
|
||||
{
|
||||
Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, self->action};
|
||||
anm2_spritesheet_texture_pixels_download(&snapshot.anm2);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview)
|
||||
@@ -41,18 +50,17 @@ void snapshots_reset(Snapshots* self)
|
||||
self->action.clear();
|
||||
}
|
||||
|
||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot)
|
||||
void snapshots_undo_push(Snapshots* self, Snapshot* snapshot)
|
||||
{
|
||||
_snapshot_stack_push(&self->undoStack, snapshot);
|
||||
self->redoStack.top = 0;
|
||||
_snapshot_stack_push(&self->undoStack, snapshot);
|
||||
}
|
||||
|
||||
void snapshots_undo(Snapshots* self)
|
||||
{
|
||||
Snapshot snapshot;
|
||||
if (_snapshot_stack_pop(&self->undoStack, &snapshot))
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->undoStack))
|
||||
{
|
||||
Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->redoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
@@ -60,10 +68,9 @@ void snapshots_undo(Snapshots* self)
|
||||
|
||||
void snapshots_redo(Snapshots* self)
|
||||
{
|
||||
Snapshot snapshot;
|
||||
if (_snapshot_stack_pop(&self->redoStack, &snapshot))
|
||||
if (Snapshot* snapshot = _snapshot_stack_pop(&self->redoStack))
|
||||
{
|
||||
Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
|
||||
Snapshot current = snapshot_get(self);
|
||||
_snapshot_stack_push(&self->undoStack, ¤t);
|
||||
_snapshot_set(self, snapshot);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include "preview.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define SNAPSHOT_STACK_MAX 1000
|
||||
#define SNAPSHOT_STACK_MAX 100
|
||||
#define SNAPSHOT_ACTION "Action"
|
||||
|
||||
struct Snapshot
|
||||
@@ -33,8 +33,9 @@ struct Snapshots
|
||||
SnapshotStack redoStack;
|
||||
};
|
||||
|
||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot);
|
||||
void snapshots_undo_push(Snapshots* self, Snapshot* snapshot);
|
||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview);
|
||||
void snapshots_undo(Snapshots* self);
|
||||
void snapshots_redo(Snapshots* self);
|
||||
void snapshots_reset(Snapshots* self);
|
||||
void snapshots_reset(Snapshots* self);
|
||||
Snapshot snapshot_get(Snapshots* self);
|
@@ -22,20 +22,16 @@ static void _draw(State* self)
|
||||
SDL_GL_SwapWindow(self->window);
|
||||
}
|
||||
|
||||
void init(State* self)
|
||||
bool sdl_init(State* self, bool isTestMode = false)
|
||||
{
|
||||
|
||||
log_info(STATE_INIT_INFO);
|
||||
|
||||
settings_init(&self->settings);
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
log_error(std::format(STATE_SDL_INIT_ERROR, SDL_GetError()));
|
||||
quit(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info(STATE_SDL_INIT_INFO);
|
||||
if (!isTestMode) log_info(STATE_SDL_INIT_INFO);
|
||||
|
||||
// Todo, when sdl3 mixer is released officially
|
||||
/*
|
||||
@@ -63,33 +59,59 @@ void init(State* self)
|
||||
log_info(STATE_MIX_INIT_INFO);
|
||||
*/
|
||||
|
||||
SDL_CreateWindowAndRenderer
|
||||
(
|
||||
WINDOW_TITLE,
|
||||
self->settings.windowSize.x,
|
||||
self->settings.windowSize.y,
|
||||
WINDOW_FLAGS,
|
||||
&self->window,
|
||||
&self->renderer
|
||||
);
|
||||
|
||||
if (isTestMode)
|
||||
{
|
||||
self->window = SDL_CreateWindow
|
||||
(
|
||||
WINDOW_TITLE,
|
||||
WINDOW_TEST_MODE_SIZE.x, WINDOW_TEST_MODE_SIZE.y,
|
||||
WINDOW_TEST_MODE_FLAGS
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ivec2 windowSize = self->settings.windowSize;
|
||||
|
||||
// Fix for auto-fullscreen on Windows
|
||||
if (SDL_DisplayID* displayIDs = SDL_GetDisplays(nullptr))
|
||||
if (displayIDs[0])
|
||||
if (const SDL_DisplayMode* displayMode = SDL_GetDesktopDisplayMode(displayIDs[0]))
|
||||
if (windowSize.x == displayMode->w && windowSize.y == displayMode->h)
|
||||
windowSize -= ivec2(1, 1);
|
||||
|
||||
self->window = SDL_CreateWindow
|
||||
(
|
||||
WINDOW_TITLE,
|
||||
windowSize.x,
|
||||
windowSize.y,
|
||||
WINDOW_FLAGS
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
window_vsync_set(self->settings.isVsync);
|
||||
|
||||
if (!self->glContext)
|
||||
{
|
||||
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, SDL_GetError()));
|
||||
quit(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
glewInit();
|
||||
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
|
||||
{
|
||||
log_error(std::format(STATE_GLAD_INIT_ERROR));
|
||||
quit(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION)));
|
||||
if (!isTestMode) log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION)));
|
||||
|
||||
window_vsync_set(self->settings.isVsync);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -97,14 +119,33 @@ void init(State* self)
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init(State* self)
|
||||
{
|
||||
log_info(STATE_INIT_INFO);
|
||||
|
||||
settings_init(&self->settings);
|
||||
|
||||
if (!sdl_init(self)) return;
|
||||
|
||||
if (!self->argument.empty())
|
||||
{
|
||||
anm2_deserialize(&self->anm2, self->argument);
|
||||
window_title_from_path_set(self->window, self->argument);
|
||||
}
|
||||
else
|
||||
anm2_new(&self->anm2);
|
||||
|
||||
resources_init(&self->resources);
|
||||
dialog_init(&self->dialog, self->window);
|
||||
clipboard_init(&self->clipboard, &self->anm2);
|
||||
snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview);
|
||||
preview_init(&self->preview, &self->anm2, &self->reference, &self->resources, &self->settings);
|
||||
generate_preview_init(&self->generatePreview, &self->anm2, &self->reference, &self->resources, &self->settings);
|
||||
editor_init(&self->editor, &self->anm2, &self->reference, &self->resources, &self->settings);
|
||||
snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview);
|
||||
|
||||
imgui_init
|
||||
(
|
||||
@@ -122,14 +163,6 @@ void init(State* self)
|
||||
self->window,
|
||||
&self->glContext
|
||||
);
|
||||
|
||||
if (!self->argument.empty())
|
||||
{
|
||||
anm2_deserialize(&self->anm2, &self->resources, self->argument);
|
||||
window_title_from_path_set(self->window, self->argument);
|
||||
}
|
||||
else
|
||||
anm2_new(&self->anm2);
|
||||
}
|
||||
|
||||
void loop(State* self)
|
||||
@@ -191,4 +224,5 @@ void quit(State* self)
|
||||
|
||||
settings_save(&self->settings);
|
||||
log_info(STATE_QUIT_INFO);
|
||||
log_free();
|
||||
}
|
@@ -2,13 +2,14 @@
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#define STATE_INIT_INFO "Initializing..."
|
||||
#define STATE_INIT_INFO "Initializing anm2ed (Version 1.1)"
|
||||
#define STATE_SDL_INIT_ERROR "Failed to initialize SDL! {}"
|
||||
#define STATE_SDL_INIT_INFO "Initialized SDL"
|
||||
#define STATE_MIX_INIT_WARNING "Unable to initialize SDL_mixer! {}"
|
||||
#define STATE_MIX_AUDIO_DEVICE_INIT_WARNING "Unable to initialize audio device! {}"
|
||||
#define STATE_MIX_INIT_INFO "Initialized SDL_mixer"
|
||||
#define STATE_GL_CONTEXT_INIT_ERROR "Failed to initialize OpenGL context! {}"
|
||||
#define STATE_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,10 +24,12 @@
|
||||
#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;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_GLContext glContext;
|
||||
Imgui imgui;
|
||||
Dialog dialog;
|
||||
@@ -48,6 +51,7 @@ struct State
|
||||
bool isRunning = true;
|
||||
};
|
||||
|
||||
bool sdl_init(State* self, bool isTestMode);
|
||||
void init(State* state);
|
||||
void loop(State* state);
|
||||
void quit(State* state);
|
@@ -1,3 +1,10 @@
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
#define STBI_ONLY_PNG
|
||||
@@ -10,7 +17,8 @@
|
||||
|
||||
static void _texture_gl_set(Texture* self, const u8* data)
|
||||
{
|
||||
glGenTextures(1, &self->id);
|
||||
if (self->id == GL_ID_NONE) glGenTextures(1, &self->id);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@@ -26,6 +34,7 @@ std::vector<u8> texture_download(const Texture* self)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->id);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
|
||||
return pixels;
|
||||
@@ -33,20 +42,16 @@ std::vector<u8> texture_download(const Texture* self)
|
||||
|
||||
bool texture_from_path_init(Texture* self, const std::string& path)
|
||||
{
|
||||
*self = Texture{};
|
||||
u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
|
||||
u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = stbi_load(path_canonical_resolve(path).c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
|
||||
if (!data)
|
||||
{
|
||||
log_error(std::format(TEXTURE_INIT_ERROR, path));
|
||||
self->isInvalid = true;
|
||||
return false;
|
||||
}
|
||||
log_error(std::format(TEXTURE_INIT_ERROR, path));
|
||||
return false;
|
||||
}
|
||||
|
||||
self->isInvalid = false;
|
||||
|
||||
log_info(std::format(TEXTURE_INIT_INFO, path));
|
||||
|
||||
_texture_gl_set(self, data);
|
||||
@@ -54,13 +59,12 @@ bool texture_from_path_init(Texture* self, const std::string& path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, const u8* data, u32 length)
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, const u8* data, u32 length)
|
||||
{
|
||||
*self = Texture{};
|
||||
self->size = size;
|
||||
self->channels = channels;
|
||||
|
||||
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
|
||||
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
|
||||
|
||||
if (!textureData)
|
||||
{
|
||||
@@ -73,11 +77,11 @@ bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, con
|
||||
return true;
|
||||
}
|
||||
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* data)
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data)
|
||||
{
|
||||
*self = Texture{};
|
||||
self->size = size;
|
||||
self->channels = channels;
|
||||
self->isInvalid = false;
|
||||
|
||||
_texture_gl_set(self, data);
|
||||
|
||||
@@ -87,13 +91,8 @@ bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* d
|
||||
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size)
|
||||
{
|
||||
bool isSuccess = stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
|
||||
if (!isSuccess)
|
||||
{
|
||||
isSuccess = stbi_write_png(path_canonical_resolve(path).c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
|
||||
if (!isSuccess) log_info(std::format(TEXTURE_SAVE_ERROR, path));
|
||||
}
|
||||
|
||||
log_info(std::format(TEXTURE_SAVE_INFO, path));
|
||||
if (!isSuccess) log_error(std::format(TEXTURE_SAVE_ERROR, path));
|
||||
else log_info(std::format(TEXTURE_SAVE_INFO, path));
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
@@ -105,6 +104,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{};
|
||||
}
|
||||
@@ -126,33 +127,3 @@ bool texture_pixel_set(Texture* self, ivec2 position, vec4 color)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Texture texture_copy(Texture* self)
|
||||
{
|
||||
Texture copy = *self;
|
||||
_texture_gl_set(©, nullptr);
|
||||
|
||||
GLuint fboSource;
|
||||
GLuint fboDestination;
|
||||
glGenFramebuffers(1, &fboSource);
|
||||
glGenFramebuffers(1, &fboDestination);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSource);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->id, 0);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDestination);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copy.id, 0);
|
||||
|
||||
glBlitFramebuffer
|
||||
(
|
||||
0, 0, self->size.x, self->size.y,
|
||||
0, 0, self->size.x, self->size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fboSource);
|
||||
glDeleteFramebuffers(1, &fboDestination);
|
||||
|
||||
return copy;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "COMMON.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TEXTURE_CHANNELS 4
|
||||
#define TEXTURE_INIT_INFO "Initialized texture from file: {}"
|
||||
@@ -10,18 +10,18 @@
|
||||
|
||||
struct Texture
|
||||
{
|
||||
GLuint id = 0;
|
||||
ivec2 size = {0, 0};
|
||||
s32 channels = -1;
|
||||
bool isInvalid = false;
|
||||
GLuint id = GL_ID_NONE;
|
||||
ivec2 size{};
|
||||
bool isInvalid = true;
|
||||
|
||||
auto operator<=>(const Texture&) const = default;
|
||||
};
|
||||
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, const u8* data, u32 length);
|
||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, const u8* data, u32 length);
|
||||
bool texture_from_gl_write(Texture* self, const std::string& path);
|
||||
bool texture_from_path_init(Texture* self, const std::string& path);
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* data);
|
||||
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data);
|
||||
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size);
|
||||
bool texture_pixel_set(Texture* self, ivec2 position, vec4 color);
|
||||
void texture_free(Texture* self);
|
||||
std::vector<u8> texture_download(const Texture* self);
|
||||
Texture texture_copy(Texture* self);
|
||||
std::vector<u8> texture_download(const Texture* self);
|
@@ -5,6 +5,9 @@
|
||||
#define WINDOW_TITLE "Anm2Ed"
|
||||
#define WINDOW_TITLE_FORMAT "Anm2Ed ({})"
|
||||
#define WINDOW_FLAGS SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL
|
||||
#define WINDOW_TEST_MODE_FLAGS WINDOW_FLAGS | SDL_WINDOW_HIDDEN
|
||||
|
||||
static const ivec2 WINDOW_TEST_MODE_SIZE = {1, 1};
|
||||
|
||||
void window_title_from_path_set(SDL_Window* self, const std::string& path);
|
||||
bool window_color_from_position_get(SDL_Window* self, vec2 position, vec4* color);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"dependencies": [
|
||||
"sdl3",
|
||||
"glew"
|
||||
"sdl3"
|
||||
]
|
||||
}
|
||||
|
48
workshop/metadata.xml
Normal file
48
workshop/metadata.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<name>Anm2Ed (Animation Editor)</name>
|
||||
<directory>anm2ed</directory>
|
||||
<id>3567731616</id>
|
||||
<description>
|
||||
[h1]NOTE: THIS IS NOT A MOD! SUBSCRIBING TO THIS ISN'T NECESSARY (though it's appreciated)! It's recommended you head to the download page below![/h1]
|
||||
|
||||
[h2]Anm2Ed[/h2]
|
||||
|
||||
[img]https://files.catbox.moe/3bj6za.png[/img]
|
||||
|
||||
A reimplementation of [i]The Binding of Isaac: Rebirth[/i]'s proprietary animation editor. Manipulates the XML-based ".anm2" format, used for in-game tweened animations.
|
||||
|
||||
[h2]Features[/h2]
|
||||
[list]
|
||||
[*] Extended version of the original proprietary Nicalis animation editor
|
||||
[*] Smooth [url=https://github.com/ocornut/imgui]Dear ImGui[/url] interface; docking, dragging and dropping, etc. You might be familiar with it from [url=https://steamcommunity.com/sharedfiles/filedetails/?id=3127536138]Repentogon[/url].
|
||||
[*] New to this editor:
|
||||
[list]
|
||||
[*] Can output .webm. .mp4 or *.png sequence (with FFmpeg)
|
||||
[*] Cutting, copying and pasting frames/animations
|
||||
[*] Additional wizard options
|
||||
[*] Robust snapshot (undo/redo) system
|
||||
[*] Additional hotkeys/shortcuts (rebindable!)
|
||||
[*] Onionskinning
|
||||
[*] Settings that will preserve on exit (stored in %APPDATA% on Windows or ~/.local/share on Linux)
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
[h3]Note: Difference from Nicalis editor[/h3]
|
||||
Layers/nulls are handled differently (in their own window) and are not on the timeline! Add a layer/null in the Layers/Nulls window, and then add a LayerAnimation/NullAnimation in the timeline!
|
||||
|
||||
[h3]Note on Rendering Animations[/h3]
|
||||
You will need FFmpeg installed! Get it from [url=https://ffmpeg.org/download.html]here[/url] and point to the downloaded ffmpeg executable within the program!
|
||||
|
||||
[h2]Download (Windows)[/h2]
|
||||
https://github.com/ShweetsStuff/anm2ed/releases
|
||||
Extract with 7z and run .exe. Check that you have the latest [url=https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170]Microsoft C++ redistributable[/url] (if you play a lot of games on Steam, you likely will have it).
|
||||
|
||||
Alternatively, if you have subscribed to the mod, you can find the latest release inside the "resources" folder. Once downloaded, you can put it wherever you want.
|
||||
|
||||
[h3]Happy animating![/h3]
|
||||
[img]https://files.catbox.moe/4auc1c.gif[/img]
|
||||
</description>
|
||||
<version>1.1</version>
|
||||
<visibility>Public</visibility>
|
||||
</metadata>
|
BIN
workshop/preview.png
Normal file
BIN
workshop/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Reference in New Issue
Block a user