Compare commits

...

36 Commits

Author SHA1 Message Date
cf9f04ecdc Float fixes, sensible settings 2025-09-14 21:27:14 -04:00
ce3f0b3415 SEMICOLON MOMENT 2025-09-14 21:03:11 -04:00
e0732ee6b9 Fixes for exit confirmation when saved 2025-09-14 21:00:54 -04:00
Shweet
4ce5a225c3 Update README.md 2025-09-14 18:15:09 -04:00
6d191f186d minor spelling mistake 2025-09-14 16:14:21 -04:00
70d4d44c80 update workshop 2025-09-13 20:17:39 -04:00
d35958a289 update readme 2025-09-13 20:04:42 -04:00
b65bcd2316 update readme 2025-09-13 20:01:57 -04:00
cc4501d2f1 add steam workshop 2025-09-13 20:00:50 -04:00
bc8cc37042 add steam workshop 2025-09-13 20:00:08 -04:00
c3bb2bcf17 Fix for auto fullscreen on Windows 2025-09-13 14:07:15 -04:00
9afb845a36 add slight texture inset for layer UVs. 2025-09-13 12:28:37 -04:00
fe32c74967 onionskin can now be 0 2025-09-13 10:39:53 -04:00
c78f2ee17b update gitignore 2025-09-13 10:35:01 -04:00
0a8fa44ee6 update atlas 2025-09-13 10:32:47 -04:00
f7c4bcea52 update windos build 2025-09-12 16:47:16 -07:00
45865bd4d1 refactor to not expose tinyxml2 symbols 2025-09-12 19:17:54 -04:00
c7dc0db9ce namespace fix 2025-09-12 18:57:50 -04:00
3da8928d13 fix triggers not serializing correctly 2025-09-12 18:39:09 -04:00
f8fb3df8d4 Redid layer system, added layer/null windows, more quality of life, polish 2025-09-12 18:06:30 -04:00
6deaaea374 Change clipboard system entirely, refactored anm2 serializing/deserializing, quick shell script for atlas update 2025-09-11 17:18:27 -04:00
b0e52bd444 allow anm2s with variable layers per animation. it'll just load all of them. not 1:1 to the behavior of nicalis editor but it'll make those sorts of files load in an editable fashion 2025-09-09 18:34:35 -04:00
b2cf67a823 Added fit, fixed the layer -1 bug finally, refactored here and there 2025-09-09 17:51:17 -04:00
9ad464a74a Onionskin, input rebinding, alt icons, settings refactor 2025-09-08 20:18:28 -04:00
4d098f8f1c Fixes to generate preview 2025-09-05 20:07:49 -04:00
a992661c6a Removed GLEW dependency (using GLAD instead), axes now use shader, should now work better 2025-09-05 16:37:45 -04:00
fa6109cb2e Updated settings, added reset to default settings option, tried to fix -1 map bug 2025-09-01 22:00:12 -04:00
d50fded04d Update cmake 2025-09-01 18:09:26 -04:00
d92dbc5b6d This is again a bandaid solution to this map with -1 problem, but it'll work for the normies 2025-08-28 17:00:21 -04:00
2c62f5701c Hotfix for adding frames, spritesheets, etc. 2025-08-28 08:37:47 -07:00
8f3c666922 Need to save anm2 first before doing anything, should eliminate a few problems 2025-08-28 10:27:47 -04:00
Shweet
de034b9b29 Update README.md 2025-08-27 22:43:01 -04:00
0ddc616e02 Update default settings 2025-08-27 22:22:08 -07:00
0f06837965 Update README.md 2025-08-27 18:29:03 -07:00
e2f1afbefb Update README.md 2025-08-27 18:28:38 -07:00
b667710c99 Windows builds updates; prepare for release 2025-08-27 18:23:12 -07:00
39 changed files with 28430 additions and 2114 deletions

7
.gitignore vendored
View File

@@ -1,4 +1,11 @@
build/
concept/
release/
packed/
vcpkg_installed/
out/
include/imgui/
include/glm/
include/tinyxml2
workshop/resources
.vs/

View File

@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.15)
if(WIN32 AND DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain file")
@@ -7,53 +8,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
View 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": ""
}
]
}

View File

@@ -6,23 +6,37 @@ 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:
@@ -35,4 +49,7 @@ mkdir build
cd build
cmake ..
make
```
```
## Happy animating!
![Isaac](https://private-user-images.githubusercontent.com/129694724/482938896-b7f4c7c4-ce38-4062-81e9-bea119c66d1a.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTc4MDY3NTIsIm5iZiI6MTc1NzgwNjQ1MiwicGF0aCI6Ii8xMjk2OTQ3MjQvNDgyOTM4ODk2LWI3ZjRjN2M0LWNlMzgtNDA2Mi04MWU5LWJlYTExOWM2NmQxYS5naWY_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwOTEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDkxM1QyMzM0MTJaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0xZmU3YmExYWJhZjg1NGZiNTNjODM0NGYyZGI5MjM2MzIxNGM1YTEyOWM2MjAxNDQwZWJhODRhMzUxYjcyZjQ5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ZROJVPS4oIyhl3kt-FQc3DjI5mci32AHwStjG0Sk8TM)

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
View 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
View File

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

9045
include/glad/glad.cpp Normal file

File diff suppressed because one or more lines are too long

15776
include/glad/glad.h Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
#pragma once
#include <SDL3/SDL.h>
#include <GL/glew.h>
#include <glad/glad.h>
#include <GL/gl.h>
#include <glm/glm/glm.hpp>
#include <glm/glm/gtc/type_ptr.hpp>
@@ -10,6 +10,7 @@
#include <algorithm>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstring>
#include <filesystem>
@@ -24,7 +25,7 @@
#include <string>
#include <unordered_set>
#include <variant>
#include <vector>
#include <vector>
typedef uint8_t u8;
typedef uint16_t u16;
@@ -50,15 +51,17 @@ using namespace glm;
#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"
@@ -70,7 +73,6 @@ using namespace glm;
#define PREAD_MODE "r"
#endif
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};
@@ -103,9 +105,30 @@ static inline std::string string_quote(const std::string& string)
return "\"" + string + "\"";
}
#define FLOAT_FORMAT_MAX_DECIMALS 2
#define FLOAT_FORMAT_EPSILON 1e-6f
static constexpr f32 FLOAT_FORMAT_POW10[] = {1.f, 10.f, 100.f};
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;
}
#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)
{
@@ -119,8 +142,11 @@ static inline s32 f32_decimals_needed(f32 value)
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])
if (fabsf(scaledFraction - roundf(scaledFraction)) <
FLOAT_FORMAT_EPSILON * FLOAT_FORMAT_POW10[decimalCount])
{
return decimalCount;
}
}
return FLOAT_FORMAT_MAX_DECIMALS;
}
@@ -129,7 +155,9 @@ 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");
formatString = (decimalCount == 0)
? "%.0f"
: ("%." + std::to_string(decimalCount) + "f");
return formatString.c_str();
}
@@ -139,69 +167,12 @@ static inline const char* vec2_format_get(const vec2& value)
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");
formatString = (decimalCount == 0)
? "%.0f"
: ("%." + std::to_string(decimalCount) + "f");
return formatString.c_str();
}
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;
};
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();
};
static inline std::string working_directory_from_file_set(const std::string& path)
{
std::filesystem::path filePath = path;
@@ -236,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;
@@ -247,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++)
@@ -286,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;
@@ -342,13 +309,54 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
map[insertIndex] = value;
}
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;
}
}
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
static inline std::string function(s32 index) \
{ \
return enum_to_string(array, count, index); \
};
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
{
vec2 scaleAbsolute = glm::abs(scale);
vec2 scaleSign = glm::sign(scale);
vec2 pivotScaled = pivot * scaleAbsolute;
vec2 sizeScaled = size * scaleAbsolute;
mat4 model(1.0f);
model = glm::translate(model, vec3(position - pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotScaled, 0.0f));
model = glm::scale(model, vec3(scaleSign, 1.0f));
model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1));
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
model = glm::scale(model, vec3(sizeScaled, 1.0f));
return model;
}
static inline mat4 quad_model_parent_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
{
vec2 scaleSign = glm::sign(scale);
vec2 scaleAbsolute = glm::abs(scale);
f32 handedness = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
mat4 local(1.0f);
local = glm::translate(local, vec3(pivot, 0.0f));
local = glm::scale(local, vec3(scaleSign, 1.0f));
local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
local = glm::translate(local, vec3(-pivot, 0.0f));
local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
}
#define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \
static inline enumType function(const std::string& string) \
@@ -356,19 +364,30 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
return static_cast<enumType>(string_to_enum(string, stringArray, count)); \
};
#define DATATYPE_LIST \
X(TYPE_INT, s32) \
X(TYPE_BOOL, bool) \
X(TYPE_FLOAT, f32) \
X(TYPE_STRING, std::string) \
X(TYPE_IVEC2, ivec2) \
X(TYPE_IVEC2_WH, ivec2) \
X(TYPE_VEC2, vec2) \
X(TYPE_VEC2_WH, vec2) \
X(TYPE_VEC3, vec3) \
X(TYPE_VEC4, vec4)
enum DataType
enum DataType
{
TYPE_INT,
TYPE_BOOL,
TYPE_FLOAT,
TYPE_STRING,
TYPE_IVEC2,
TYPE_VEC2,
TYPE_VEC3,
TYPE_VEC4
#define X(symbol, ctype) symbol,
DATATYPE_LIST
#undef X
};
#define DATATYPE_TO_CTYPE(dt) DATATYPE_CTYPE_##dt
#define X(symbol, ctype) typedef ctype DATATYPE_CTYPE_##symbol;
DATATYPE_LIST
#undef X
enum OriginType
{
ORIGIN_TOP_LEFT,

View File

@@ -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,17 +297,6 @@ void main()
}
)";
const std::string SHADER_GRID_VERTEX = R"(
#version 330 core
layout ( location = 0 ) in vec2 i_position;
out vec2 clip;
void main() {
clip = i_position;
gl_Position = vec4(i_position, 0.0, 1.0);
}
)";
const std::string SHADER_GRID_FRAGMENT = R"(
#version 330 core
in vec2 clip;
@@ -301,12 +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"
@@ -315,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}
};
};

File diff suppressed because it is too large Load Diff

View File

@@ -13,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: {}"
@@ -22,7 +26,6 @@
#define ANM2_EXTENSION "anm2"
#define ANM2_SPRITESHEET_EXTENSION "png"
/* Elements */
#define ANM2_ELEMENT_LIST \
X(ANIMATED_ACTOR, "AnimatedActor") \
X(INFO, "Info") \
@@ -46,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") \
@@ -101,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
{
@@ -132,23 +137,31 @@ 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
@@ -166,6 +179,57 @@ struct Anm2Frame
vec2 scale = {100, 100};
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
@@ -183,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,
@@ -258,33 +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, const std::string& path);
void anm2_new(Anm2* self);
void anm2_free(Anm2* self);
void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self);
void anm2_animation_remove(Anm2* self, 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_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
void anm2_scale(Anm2* self, f32 scale);
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_spritesheet_texture_pixels_upload(Anm2* self);
void anm2_spritesheet_texture_pixels_download(Anm2* self);
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_spritesheet_texture_pixels_download(Anm2* self);
void anm2_spritesheet_texture_pixels_upload(Anm2* self);

View File

@@ -33,7 +33,7 @@ void canvas_init(Canvas* self, const ivec2& size)
glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_AXIS_VERTICES), CANVAS_AXIS_VERTICES, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(f32), (void*)0);
// Grid
glGenVertexArrays(1, &self->gridVAO);
@@ -96,6 +96,8 @@ void canvas_init(Canvas* self, const ivec2& size)
glGenFramebuffers(1, &self->fbo);
glGenRenderbuffers(1, &self->rbo);
_canvas_framebuffer_set(self, size);
self->isInit = true;
}
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin)
@@ -195,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);
}
@@ -218,6 +227,8 @@ void canvas_unbind(void)
void canvas_free(Canvas* self)
{
if (!self->isInit) return;
glDeleteFramebuffers(1, &self->fbo);
glDeleteRenderbuffers(1, &self->rbo);
glDeleteTextures(1, &self->framebuffer);
@@ -230,37 +241,4 @@ void canvas_free(Canvas* self)
glDeleteBuffers(1, &self->gridVBO);
glDeleteBuffers(1, &self->textureVBO);
glDeleteBuffers(1, &self->textureEBO);
}
mat4 canvas_model_get(vec2 size, vec2 position, vec2 pivot, vec2 scale, f32 rotation)
{
vec2 scaleAbsolute = glm::abs(scale);
vec2 scaleSign = glm::sign(scale);
vec2 pivotScaled = pivot * scaleAbsolute;
vec2 sizeScaled = size * scaleAbsolute;
mat4 model(1.0f);
model = glm::translate(model, vec3(position - pivotScaled, 0.0f));
model = glm::translate(model, vec3(pivotScaled, 0.0f));
model = glm::scale(model, vec3(scaleSign, 1.0f));
model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1));
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
model = glm::scale(model, vec3(sizeScaled, 1.0f));
return model;
}
mat4 canvas_parent_model_get(vec2 position, vec2 pivot, vec2 scale, f32 rotation)
{
vec2 scaleSign = glm::sign(scale);
vec2 scaleAbsolute = glm::abs(scale);
f32 handedness = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
mat4 local(1.0f);
local = glm::translate(local, vec3(pivot, 0.0f));
local = glm::scale(local, vec3(scaleSign, 1.0f));
local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
local = glm::translate(local, vec3(-pivot, 0.0f));
local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
}

View File

@@ -5,23 +5,15 @@
#define CANVAS_ZOOM_MIN 1.0f
#define CANVAS_ZOOM_MAX 2000.0f
#define CANVAS_ZOOM_DEFAULT 100.0f
#define CANVAS_ZOOM_STEP 10.0f
#define CANVAS_ZOOM_MOD 10.0f
#define CANVAS_ZOOM_STEP 100.0f
#define CANVAS_GRID_MIN 1
#define CANVAS_GRID_MAX 1000
#define CANVAS_GRID_DEFAULT 32
#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
const inline vec2 CANVAS_PIVOT_SIZE = {8, 8};
const inline vec2 CANVAS_PIVOT_SIZE = {4, 4};
const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
const inline f32 CANVAS_AXIS_VERTICES[] =
{
-CANVAS_LINE_LENGTH, 0.0f,
CANVAS_LINE_LENGTH, 0.0f,
0.0f, -CANVAS_LINE_LENGTH,
0.0f, CANVAS_LINE_LENGTH
};
const inline f32 CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};
const inline f32 CANVAS_GRID_VERTICES[] =
{
@@ -62,6 +54,7 @@ struct Canvas
GLuint textureEBO{};
ivec2 size{};
ivec2 previousSize{};
bool isInit = false;
};
#define UV_VERTICES(uvMin, uvMax) \
@@ -72,6 +65,11 @@ struct Canvas
0, 1, uvMin.x, uvMax.y \
}
#define ATLAS_UV_MIN(type) (ATLAS_POSITION(type) / TEXTURE_ATLAS_SIZE)
#define ATLAS_UV_MAX(type) ((ATLAS_POSITION(type) + ATLAS_SIZE(type)) / TEXTURE_ATLAS_SIZE)
#define ATLAS_UV_ARGS(type) ATLAS_UV_MIN(type), ATLAS_UV_MAX(type)
#define ATLAS_UV_VERTICES(type) UV_VERTICES(ATLAS_UV_MIN(type), ATLAS_UV_MAX(type))
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
void canvas_bind(Canvas* self);
@@ -84,8 +82,6 @@ void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform,
void canvas_framebuffer_resize_check(Canvas* self);
void canvas_unbind(void);
void canvas_viewport_set(Canvas* self);
mat4 canvas_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {});
mat4 canvas_parent_model_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {});
void canvas_texture_draw
(

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -26,11 +26,11 @@ void editor_draw(Editor* self)
canvas_viewport_set(&self->canvas);
canvas_clear(self->settings->editorBackgroundColor);
if (self->spritesheetID != ID_NONE)
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->spritesheetID))
{
Texture& texture = self->anm2->spritesheets[self->spritesheetID].texture;
Texture& texture = spritesheet->texture;
mat4 spritesheetTransform = transform * canvas_model_get(texture.size);
mat4 spritesheetTransform = transform * quad_model_get(texture.size);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);
if (self->settings->editorIsBorder)
@@ -40,10 +40,10 @@ void editor_draw(Editor* self)
if (frame)
{
mat4 cropTransform = transform * canvas_model_get(frame->size, frame->crop);
mat4 cropTransform = transform * quad_model_get(frame->size, frame->crop);
canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR);
mat4 pivotTransform = transform * canvas_model_get(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, pivotTransform, vertices, EDITOR_PIVOT_COLOR);
}

View File

@@ -1,10 +1,5 @@
#include "ffmpeg.h"
static std::string ffmpeg_log_path_get(void)
{
return preferences_path_get() + FFMPEG_LOG_PATH;
}
bool
ffmpeg_render
(
@@ -35,13 +30,8 @@ ffmpeg_render
break;
}
// ffmpeg output will be piped into the log
std::string logOutput = " 2>> \"" + ffmpeg_log_path_get() + "\"";
#if _WIN32
command = string_quote(command) + logOutput;
#else
command += logOutput;
command = string_quote(command);
#endif
log_command(command);
@@ -53,15 +43,9 @@ ffmpeg_render
log_error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
return false;
}
size_t frameBytes = size.x * size.y * TEXTURE_CHANNELS;
// supposedly, might help with video corruption issues on windows?
#if _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
for (const auto& frame : frames)
{
std::vector<u8> rgba = texture_download(&frame);

View File

@@ -4,8 +4,6 @@
#include "texture.h"
#define FFMPEG_POPEN_ERROR "popen() (for FFmpeg) failed!\n{}"
#define FFMPEG_LOG_BUFFER_SIZE 256
#define FFMPEG_LOG_PATH "ffmpeg.txt"
static constexpr const char* FFMPEG_GIF_FORMAT =
"\"{0}\" -y "

View File

@@ -26,23 +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 = self->anm2->spritesheets[self->anm2->layers[self->reference->itemID].spritesheetID].texture;
if (item && !texture.isInvalid)
if (item)
{
const s32 index = std::clamp((s32)(self->time * count), 0, count);
const s32 row = index / columns;
const s32 column = index % columns;
vec2 crop = startPosition + vec2(size.x * column, size.y * row);
if (Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[self->reference->itemID].spritesheetID))
{
Texture& texture = spritesheet->texture;
vec2 uvMin = crop / vec2(texture.size);
vec2 uvMax = (crop + size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
const s32 index = std::clamp((s32)(self->time * count), 0, count);
const s32 row = index / columns;
const s32 column = index % columns;
vec2 crop = startPosition + vec2(size.x * column, size.y * row);
mat4 generateTransform = transform * canvas_model_get(size, {}, pivot);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
vec2 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);
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();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ static bool _anm2_rescale(const std::string& file, f32 scale)
{
Anm2 anm2;
if (!anm2_deserialize(&anm2, file)) return false;
if (!anm2_deserialize(&anm2, file, false)) return false;
anm2_scale(&anm2, scale);
return anm2_serialize(&anm2, file);
}
@@ -35,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);

View File

@@ -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."

View File

@@ -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,6 +81,7 @@ void preview_draw(Preview* self)
ivec2& gridOffset = self->settings->previewGridOffset;
vec4& gridColor = self->settings->previewGridColor;
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
GLuint& shaderAxis = self->resources->shaders[SHADER_AXIS];
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
@@ -95,153 +96,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 = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation);
// Root
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
mat4 model = canvas_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
mat4 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 = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
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 = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
if (!texture.isInvalid)
{
vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
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 = canvas_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
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 = canvas_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 nullTransform = transform * (rootModel * model);
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
if (null.isShowRect)
{
mat4 rectModel = canvas_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 rectTransform = transform * (rootModel * rectModel);
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
}
}
}
}
s32& animationOverlayID = self->animationOverlayID;
Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
if (animationOverlay)
{
Anm2Frame root;
mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale));
for (auto [i, id] : self->anm2->layerMap)
{
Anm2Frame frame;
Anm2Item& layerAnimation = animationOverlay->layerAnimations[id];
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 = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
if (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 = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
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();
}

View File

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

View File

@@ -2,7 +2,7 @@
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);

View File

@@ -6,70 +6,87 @@ static void _settings_setting_load(Settings* self, const std::string& line)
{
const auto& entry = SETTINGS_ENTRIES[i];
const std::string& key = entry.key;
void* target = (u8*)self + entry.offset;
auto match_key = [&](const std::string& full) -> const char*
{
if (!line.starts_with(full))
if (!line.starts_with(full))
return nullptr;
size_t p = full.size();
while (p < line.size() && std::isspace((u8)line[p])) ++p;
if (p < line.size() && line[p] == '=') return line.c_str() + p + 1;
if (p < line.size() && line[p] == '=')
return line.c_str() + p + 1;
return nullptr;
};
auto* value = match_key(key);
const char* value = nullptr;
if (value)
switch (entry.type)
{
switch (entry.type)
case TYPE_INT:
if ((value = match_key(key))) { *(s32*)target = std::atoi(value); return; }
break;
case TYPE_BOOL:
if ((value = match_key(key))) { *(bool*)target = string_to_bool(value); return; }
break;
case TYPE_FLOAT:
if ((value = match_key(key))) { *(f32*)target = std::atof(value); return; }
break;
case TYPE_STRING:
if ((value = match_key(key))) { *(std::string*)target = value; return; }
break;
case TYPE_IVEC2:
{
case TYPE_INT:
*(s32*)target = std::atoi(value);
return;
case TYPE_BOOL:
*(bool*)target = string_to_bool(value);
return;
case TYPE_FLOAT:
*(f32*)target = std::atof(value);
return;
case TYPE_STRING:
*(std::string*)target = value;
return;
default:
break;
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
break;
}
}
if (entry.type == TYPE_VEC2)
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
}
else if (entry.type == TYPE_IVEC2)
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
}
else if (entry.type == TYPE_VEC3)
{
vec3* v = (vec3*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
}
else if (entry.type == TYPE_VEC4)
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
case TYPE_IVEC2_WH:
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "W"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "H"))) { v->y = std::atoi(value); return; }
break;
};
case TYPE_VEC2:
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
break;
}
case TYPE_VEC2_WH:
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "W"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "H"))) { v->y = std::atof(value); return; }
break;
};
case TYPE_VEC3:
{
vec3* v = (vec3*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
break;
}
case TYPE_VEC4:
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
break;
}
default:
break;
}
}
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
}
std::string settings_path_get(void)
@@ -111,6 +128,13 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
out << entry.key << "Y=" << data->y << "\n";
break;
}
case TYPE_IVEC2_WH:
{
ivec2* data = (ivec2*)(selfPointer + entry.offset);
out << entry.key << "W=" << data->x << "\n";
out << entry.key << "H=" << data->y << "\n";
break;
}
case TYPE_VEC2:
{
vec2* data = (vec2*)(selfPointer + entry.offset);
@@ -118,6 +142,13 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
break;
}
case TYPE_VEC2_WH:
{
vec2* data = (vec2*)(selfPointer + entry.offset);
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
break;
}
case TYPE_VEC3:
{
vec3* data = (vec3*)(selfPointer + entry.offset);
@@ -214,47 +245,32 @@ void settings_init(Settings* self)
{
const std::string path = settings_path_get();
std::ifstream file(path, std::ios::binary);
std::istream* in = nullptr;
std::istringstream defaultSettings;
if (file)
{
log_info(std::format(SETTINGS_INIT_INFO, path));
in = &file;
}
else
{
log_error(std::format(SETTINGS_INIT_ERROR, path));
log_info(SETTINGS_DEFAULT_INFO);
defaultSettings.str(SETTINGS_DEFAULT);
in = &defaultSettings;
log_warning(std::format(SETTINGS_INIT_WARNING, path));
settings_save(self);
std::ofstream out(path, std::ios::binary | std::ios::app);
out << SETTINGS_IMGUI_DEFAULT;
out.flush();
out.close();
file.open(path, std::ios::binary);
}
std::string line;
bool inSettingsSection = false;
while (std::getline(*in, line))
{
if (line == SETTINGS_SECTION)
{
inSettingsSection = true;
continue;
}
if (line == SETTINGS_SECTION_IMGUI) break;
if (inSettingsSection) _settings_setting_load(self, line);
}
// Save default settings
if (!file)
while (std::getline(file, line))
{
std::ofstream out(path, std::ios::binary | std::ios::trunc);
if (out)
if (line == SETTINGS_SECTION)
{
out << SETTINGS_DEFAULT;
out.flush();
log_info(std::format(SETTINGS_SAVE_INFO, path));
}
else
log_error(std::format(SETTINGS_DEFAULT_ERROR, path));
inSettingsSection = true;
continue;
}
if (line.empty()) continue;
if (line == SETTINGS_SECTION_IMGUI) break;
if (inSettingsSection) _settings_setting_load(self, line);
}
}

View File

@@ -4,17 +4,15 @@
#include "render.h"
#include "tool.h"
#define SETTINGS_BUFFER 0xFFFF
#define SETTINGS_BUFFER_ITEM 0xFF
#define SETTINGS_SECTION "[Settings]"
#define SETTINGS_SECTION_IMGUI "# Dear ImGui"
#define SETTINGS_INIT_ERROR "Failed to read settings file: {}"
#define SETTINGS_DEFAULT_ERROR "Failed to write default settings file: {}"
#define SETTINGS_INIT_WARNING "Unable to read settings file: {}; using default settings"
#define SETTINGS_INIT_ERROR "Unable to read settings file: {}"
#define SETTINGS_SAVE_ERROR "Failed to write settings file: {}"
#define SETTINGS_SAVE_FINALIZE_ERROR "Failed to write settings file: {} ({})"
#define SETTINGS_VALUE_INIT_WARNING "Unknown setting: {}"
#define SETTINGS_FLOAT_FORMAT "{:.3f}"
#define SETTINGS_INIT_INFO "Initialized settings from: {}"
#define SETTINGS_DEFAULT_INFO "Using default settings"
#define SETTINGS_DIRECTORY_ERROR "Failed to create settings directory: {} ({})"
#define SETTINGS_SAVE_INFO "Saved settings to: {}"
@@ -22,6 +20,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);

View File

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

View File

@@ -22,17 +22,16 @@ static void _draw(State* self)
SDL_GL_SwapWindow(self->window);
}
void init(State* self)
bool sdl_init(State* self, bool isTestMode = false)
{
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
/*
@@ -60,17 +59,39 @@ void init(State* self)
log_info(STATE_MIX_INIT_INFO);
*/
self->window = SDL_CreateWindow
(
WINDOW_TITLE,
self->settings.windowSize.x,
self->settings.windowSize.y,
WINDOW_FLAGS
);
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);
@@ -78,17 +99,17 @@ void init(State* self)
{
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, SDL_GetError()));
quit(self);
return false;
}
GLenum glewError = glewInit();
if (glewError != GLEW_OK)
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
{
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, (const char*)glewGetErrorString(glewError)));
log_error(std::format(STATE_GLAD_INIT_ERROR));
quit(self);
return 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);
@@ -98,7 +119,18 @@ 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);

View File

@@ -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,6 +24,9 @@
#define STATE_MIX_DEVICE NULL
#define STATE_MIX_ALLOWED_CHANGES SDL_AUDIO_ALLOW_FORMAT_CHANGE
#define STATE_GL_VERSION_MAJOR 3
#define STATE_GL_VERSION_MINOR 3
struct State
{
SDL_Window* window;
@@ -47,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);

View File

@@ -17,8 +17,7 @@
static void _texture_gl_set(Texture* self, const u8* data)
{
if (self->id == GL_ID_NONE)
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);
@@ -35,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;
@@ -42,16 +42,12 @@ std::vector<u8> texture_download(const Texture* self)
bool texture_from_path_init(Texture* self, const std::string& path)
{
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));
return false;
}
log_error(std::format(TEXTURE_INIT_ERROR, path));
return false;
}
self->isInvalid = false;
@@ -63,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)
{
@@ -82,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);
@@ -96,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;
}
@@ -114,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{};
}
@@ -134,4 +126,4 @@ bool texture_pixel_set(Texture* self, ivec2 position, vec4 color)
glTexSubImage2D(GL_TEXTURE_2D, 0,position.x, position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba8);
return true;
}
}

View File

@@ -12,16 +12,16 @@ struct Texture
{
GLuint id = GL_ID_NONE;
ivec2 size{};
s32 channels = TEXTURE_CHANNELS;
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);

View File

@@ -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);

View File

@@ -1,6 +1,5 @@
{
"dependencies": [
"sdl3",
"glew"
"sdl3"
]
}

48
workshop/metadata.xml Normal file
View 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 &quot;.anm2&quot; 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 &quot;resources&quot; 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB