slowly getting back...also, static linking

This commit is contained in:
2025-09-21 23:57:17 -04:00
parent 8039df667c
commit 7f07eaa128
20 changed files with 4579 additions and 631 deletions

7
.gitignore vendored
View File

@@ -4,8 +4,9 @@ release/
packed/
vcpkg_installed/
out/
include/imgui/
include/glm/
include/tinyxml2
external/
external/
external/
external/
workshop/resources
.vs/

7
.gitmodules vendored
View File

@@ -7,6 +7,7 @@
branch = docking
[submodule "external/tinyxml2"]
path = external/tinyxml2
url = https://github.com/external/tinyxml2
url = https://github.com/leethomason/tinyxml2
[submodule "external/SDL"]
path = external/SDL
url = https://github.com/libsdl-org/SDL.git

View File

@@ -1,5 +1,8 @@
{
"C_Cpp.formatting": "clangFormat",
"editor.formatOnSave": true,
"clang-format.style": "file"
"clang-format.style": "file",
"clangd.arguments": [
"--compile-commands-dir=build"
]
}

View File

@@ -1,35 +1,39 @@
cmake_minimum_required(VERSION 3.15)
project(anm2ed CXX)
# Optional: auto-pick up vcpkg toolchain on Windows
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")
endif()
project(anm2ed CXX)
find_package(SDL3 REQUIRED)
find_package(OpenGL REQUIRED)
set(GLAD_SRC
${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad.cpp
# Export compile_commands.json (for clangd, etc.)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(CMAKE_EXPORT_COMPILE_COMMANDS)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_BINARY_DIR}/compile_commands.json
${CMAKE_SOURCE_DIR}/compile_commands.json
)
endif()
set(GLAD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad.cpp)
set(IMGUI_SRC
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/imgui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/imgui_draw.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/imgui_widgets.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/imgui_tables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/backends/imgui_impl_sdl3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui/backends/imgui_impl_opengl3.cpp
external/imgui/imgui.cpp
external/imgui/imgui_draw.cpp
external/imgui/imgui_widgets.cpp
external/imgui/imgui_tables.cpp
external/imgui/backends/imgui_impl_sdl3.cpp
external/imgui/backends/imgui_impl_opengl3.cpp
)
set(TINYXML2_SRC
${CMAKE_CURRENT_SOURCE_DIR}/external/tinyxml2/tinyxml2.cpp
)
set(TINYXML2_SRC external/tinyxml2/tinyxml2.cpp)
file(GLOB PROJECT_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
file(GLOB PROJECT_SRC CONFIGURE_DEPENDS
src/*.cpp
src/*.h
)
add_executable(${PROJECT_NAME}
@@ -39,39 +43,48 @@ add_executable(${PROJECT_NAME}
${PROJECT_SRC}
)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_STATIC ON CACHE BOOL "" FORCE)
add_subdirectory(external/SDL EXCLUDE_FROM_ALL)
if(WIN32)
enable_language(RC)
target_sources(${PROJECT_NAME} PRIVATE Icon.rc)
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
set_property(TARGET ${PROJECT_NAME} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/glad
${CMAKE_CURRENT_SOURCE_DIR}/external
${CMAKE_CURRENT_SOURCE_DIR}/external/imgui
${CMAKE_CURRENT_SOURCE_DIR}/external/glm
${CMAKE_CURRENT_SOURCE_DIR}/external/tinyxml2
${CMAKE_CURRENT_SOURCE_DIR}/src
)
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /std:c++latest /EHsc)
target_compile_options(${PROJECT_NAME} PRIVATE /EHsc)
target_link_options(${PROJECT_NAME} PRIVATE /STACK:0xffffff)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -O2 -std=c++23 -Wall -Wextra -pedantic -fmax-errors=1)
target_compile_options(${PROJECT_NAME} PRIVATE
-O2 -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")
target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG)
target_compile_options(${PROJECT_NAME} PRIVATE -g)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE m)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL SDL3::SDL3)
target_compile_definitions(${PROJECT_NAME} PRIVATE IMGUI_DISABLE_OBSOLETE_FUNCTIONS IMGUI_DEBUG_PARANOID IMGUI_ENABLE_DOCKING)
message("System: ${CMAKE_SYSTEM_NAME}")
message("Project: ${PROJECT_NAME}")
message("Build: ${CMAKE_BUILD_TYPE}")
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
target_include_directories(${PROJECT_NAME} PRIVATE
external
external/imgui
external/glm
external/tinyxml2
include
include/glad
src
)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL SDL3::SDL3-static)
message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Project: ${PROJECT_NAME}")
message(STATUS "Build: ${CMAKE_BUILD_TYPE}")

View File

@@ -1 +1 @@
build/compile_commands.json
/home/anon/sda/Personal/Repos/anm2ed/build/compile_commands.json

View File

@@ -1,5 +1,6 @@
#pragma once
#include <set>
#define GLAD_GL_IMPLEMENTATION
#include <SDL3/SDL.h>
#include <glad/glad.h>
@@ -10,7 +11,6 @@
#include <algorithm>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstring>
#include <filesystem>
@@ -241,6 +241,35 @@ template <typename T> static inline void map_insert_shift(std::map<int, T>& map,
map[insertID] = value;
}
template <typename Map> auto map_keys_to_set(const Map& m) {
using Key = typename Map::key_type;
std::unordered_set<Key> s;
s.reserve(m.size());
for (const auto& [key, _] : m) {
s.insert(key);
}
return s;
}
template <typename Set> Set set_symmetric_difference(const Set& a, const Set& b) {
Set result;
result.reserve(a.size() + b.size());
for (const auto& x : a) {
if (!b.contains(x)) {
result.insert(x);
}
}
for (const auto& x : b) {
if (!a.contains(x)) {
result.insert(x);
}
}
return result;
}
template <typename T> static inline T* vector_find(std::vector<T>& v, const T& value) {
auto it = std::find(v.begin(), v.end(), value);
return (it != v.end()) ? &(*it) : nullptr;
@@ -257,11 +286,49 @@ template <typename T> static inline void vector_value_swap(std::vector<T>& v, co
}
}
static inline void unordered_set_id_toggle(std::unordered_set<int>& set, int id) {
if (auto it = set.find(id); it != set.end())
template <typename T> static inline T* vector_get(std::vector<T>& v, size_t index) {
if (index < v.size())
return &v[index];
return nullptr;
}
template <typename T> static inline void vector_move(std::vector<T>& v, size_t from, size_t to) {
if (from >= v.size() || to >= v.size() || from == to)
return;
if (from < to) {
std::rotate(v.begin() + from, v.begin() + from + 1, v.begin() + to + 1);
} else {
std::rotate(v.begin() + to, v.begin() + from, v.begin() + from + 1);
}
}
template <typename T> void vector_erase_indices(std::vector<T>& v, const std::set<int>& indices) {
size_t i = 0;
v.erase(std::remove_if(v.begin(), v.end(), [&](const T&) { return indices.count(i++) > 0; }), v.end());
}
template <typename T> static inline void set_key_toggle(std::set<T>& set, T key) {
if (auto it = set.find(key); it != set.end())
set.erase(it);
else
set.insert(id);
set.insert(key);
}
template <typename T> static inline void set_list(std::set<T>& s, const T& key, bool isCtrl, bool isShift, T* lastSelected) {
if (isShift && lastSelected) {
s.clear();
T a = std::min(*lastSelected, key);
T b = std::max(*lastSelected, key);
for (T i = a; i <= b; i++)
s.insert(i);
} else if (isCtrl) {
set_key_toggle(s, key);
*lastSelected = key;
} else {
s = {key};
*lastSelected = key;
}
}
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), float rotation = {}) {

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,8 @@ static void _anm2_frame_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument*
XMLDocument localDocument;
XMLDocument* useDocument = document ? document : &localDocument;
XMLElement* element = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
XMLElement* element = type == ANM2_TRIGGER ? useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER])
: useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
if (type == ANM2_TRIGGER) {
element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], frame->eventID); // EventID
@@ -118,8 +119,8 @@ static void _anm2_animation_serialize(Anm2Animation* animation, XMLDocument* doc
// Triggers
XMLElement* triggersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]);
for (auto& frame : animation->triggers.frames)
_anm2_frame_serialize(&frame, ANM2_TRIGGER, useDocument, triggersElement);
for (auto& trigger : animation->triggers.frames)
_anm2_frame_serialize(&trigger, ANM2_TRIGGER, useDocument, triggersElement);
element->InsertEndChild(triggersElement);
@@ -220,11 +221,9 @@ bool anm2_serialize(Anm2* self, const std::string& path) {
// Animations
XMLElement* animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]);
if (self->defaultAnimationID != ID_NONE)
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION],
self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->defaultAnimation.c_str()); // DefaultAnimation
for (auto& [id, animation] : self->animations)
for (auto& animation : self->animations)
_anm2_animation_serialize(&animation, &document, animationsElement);
animatedActorElement->InsertEndChild(animationsElement);
@@ -426,13 +425,9 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) {
anm2_new(self);
self->path = path;
std::string defaultAnimation{};
int id{};
// Save old working directory and then use anm2's path as directory
// (used for loading textures from anm2 correctly which are relative)
std::filesystem::path workingPath = std::filesystem::current_path();
working_directory_from_file_set(path);
WorkingDirectory workingDirectory(path);
const XMLElement* root = document.RootElement();
@@ -506,13 +501,13 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) {
switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) {
case ANM2_ATTRIBUTE_NAME:
addLayer.name = std::string(attribute->Value());
break; // Name
break;
case ANM2_ATTRIBUTE_ID:
id = std::atoi(attribute->Value());
break; // ID
break;
case ANM2_ATTRIBUTE_SPRITESHEET_ID:
addLayer.spritesheetID = std::atoi(attribute->Value());
break; // ID
break;
default:
break;
}
@@ -575,7 +570,7 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) {
for (const XMLAttribute* attribute = animations->FirstAttribute(); attribute; attribute = attribute->Next()) {
switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) {
case ANM2_ATTRIBUTE_DEFAULT_ANIMATION:
defaultAnimation = std::string(attribute->Value());
self->defaultAnimation = std::string(attribute->Value());
break; // DefaultAnimation
default:
break;
@@ -585,18 +580,12 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) {
// Animation
for (const XMLElement* animation = animations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]); animation;
animation = animation->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]))
_anm2_animation_deserialize(&self->animations[map_next_id_get(self->animations)], animation);
_anm2_animation_deserialize(&self->animations.emplace_back(Anm2Animation()), animation);
}
for (auto& [id, animation] : self->animations)
if (animation.name == defaultAnimation)
self->defaultAnimationID = id;
if (isTextures)
anm2_spritesheet_texture_pixels_download(self);
std::filesystem::current_path(workingPath);
log_info(std::format(ANM2_READ_INFO, path));
return true;
@@ -625,7 +614,7 @@ int anm2_layer_add(Anm2* self) {
void anm2_layer_remove(Anm2* self, int id) {
self->layers.erase(id);
for (auto& [_, animation] : self->animations)
for (auto& animation : self->animations)
anm2_animation_layer_animation_remove(&animation, id);
}
@@ -641,36 +630,32 @@ void anm2_null_remove(Anm2* self, int id) {
self->nulls.erase(id);
for (auto& [_, animation] : self->animations)
for (auto& animation : self->animations)
anm2_animation_null_animation_remove(&animation, id);
}
int anm2_animation_add(Anm2* self, Anm2Animation* animation, int id) {
int addID = map_next_id_get(self->animations);
int anm2_animation_add(Anm2* self, Anm2Animation* animation, int index) {
Anm2Animation localAnimation;
Anm2Animation* addAnimation = animation ? animation : &localAnimation;
Anm2Animation addAnimation = animation ? *animation : Anm2Animation();
if (!animation)
addAnimation->rootAnimation.frames.push_back(Anm2Frame{});
addAnimation.rootAnimation.frames.push_back(Anm2Frame{});
if (id != ID_NONE) {
map_insert_shift(self->animations, id, *addAnimation);
return id + 1;
} else
self->animations[addID] = *addAnimation;
int addIndex = index != INDEX_NONE ? index : (int)self->animations.size() - 1;
return addID;
self->animations.insert(self->animations.begin() + addIndex, addAnimation);
return addIndex;
}
void anm2_animation_remove(Anm2* self, int id) { self->animations.erase(id); }
void anm2_animations_remove(Anm2* self, const std::set<int> indices) { vector_erase_indices(self->animations, indices); };
void anm2_new(Anm2* self) {
*self = Anm2{};
_anm2_created_on_set(self);
}
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference reference) { return map_find(self->animations, reference.animationID); }
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference reference) { return vector_get(self->animations, reference.animationIndex); }
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference reference) {
if (reference.itemType == ANM2_NONE)
@@ -928,8 +913,8 @@ void anm2_item_frame_set(Anm2* self, Anm2Reference reference, const Anm2FrameCha
}
}
void anm2_animation_merge(Anm2* self, int animationID, const std::vector<int>& mergeIDs, Anm2MergeType type) {
Anm2Animation newAnimation = self->animations[animationID];
void anm2_animation_merge(Anm2* self, int animationIndex, std::set<int> mergeIndices, Anm2MergeType type) {
Anm2Animation newAnimation = self->animations[animationIndex];
auto merge_item = [&](Anm2Item& destinationItem, const Anm2Item& sourceItem) {
switch (type) {
@@ -950,11 +935,11 @@ void anm2_animation_merge(Anm2* self, int animationID, const std::vector<int>& m
}
};
for (auto mergeID : mergeIDs) {
if (animationID == mergeID)
for (auto mergeIndices : mergeIndices) {
if (animationIndex == mergeIndices)
continue;
const Anm2Animation& mergeAnimation = self->animations[mergeID];
const Anm2Animation& mergeAnimation = self->animations[mergeIndices];
merge_item(newAnimation.rootAnimation, mergeAnimation.rootAnimation);
@@ -967,9 +952,9 @@ void anm2_animation_merge(Anm2* self, int animationID, const std::vector<int>& m
merge_item(newAnimation.triggers, mergeAnimation.triggers);
}
self->animations[animationID] = newAnimation;
self->animations[animationIndex] = newAnimation;
anm2_animation_length_set(&self->animations[animationID]);
anm2_animation_length_set(&self->animations[animationIndex]);
}
void anm2_frame_bake(Anm2* self, Anm2Reference reference, int interval, bool isRoundScale, bool isRoundRotation) {
@@ -981,7 +966,7 @@ void anm2_frame_bake(Anm2* self, Anm2Reference reference, int interval, bool isR
if (!frame)
return;
Anm2Reference referenceNext = {reference.animationID, reference.itemType, reference.itemID, reference.frameIndex + 1};
Anm2Reference referenceNext = {reference.animationIndex, reference.itemType, reference.itemID, reference.frameIndex + 1};
Anm2Frame* frameNext = anm2_frame_from_reference(self, referenceNext);
if (!frameNext)
frameNext = frame;
@@ -1028,7 +1013,7 @@ void anm2_scale(Anm2* self, float scale) {
frame.pivot = vec2((int)(frame.pivot.x * scale), (int)(frame.pivot.y * scale));
};
for (auto& [_, animation] : self->animations) {
for (auto& animation : self->animations) {
for (auto& frame : animation.rootAnimation.frames)
frame_scale(frame);
@@ -1112,7 +1097,7 @@ vec4 anm2_animation_rect_get(Anm2* self, Anm2Reference reference, bool isRootTra
for (float t = 0.0f; t <= animation->frameNum; t += 1.0f) {
for (const auto& [id, _] : animation->layerAnimations) {
anm2_frame_from_time(self, &frame, {reference.animationID, ANM2_LAYER, id}, t);
anm2_frame_from_time(self, &frame, {reference.animationIndex, ANM2_LAYER, id}, t);
if (!frame.isVisible)
continue;
if (frame.size.x <= 0 || frame.size.y <= 0)
@@ -1120,7 +1105,7 @@ vec4 anm2_animation_rect_get(Anm2* self, Anm2Reference reference, bool isRootTra
mat4 rootModel(1.0f);
if (isRootTransform) {
anm2_frame_from_time(self, &root, {reference.animationID, ANM2_ROOT}, t);
anm2_frame_from_time(self, &root, {reference.animationIndex, ANM2_ROOT}, t);
rootModel = quad_model_parent_get(root.position, root.pivot, PERCENT_TO_UNIT(root.scale), root.rotation);
}
@@ -1149,7 +1134,7 @@ void anm2_animation_serialize_to_string(Anm2Animation* animation, std::string* s
void anm2_frame_serialize_to_string(Anm2Frame* frame, Anm2Type type, std::string* string) { _anm2_frame_serialize(frame, type, nullptr, nullptr, string); }
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml) {
bool anm2_animations_deserialize_from_xml(std::vector<Anm2Animation>& animations, const std::string& xml) {
XMLDocument document;
auto animation_deserialize_error = [&]() {
@@ -1160,13 +1145,12 @@ bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::st
if (document.Parse(xml.c_str()) != XML_SUCCESS)
return animation_deserialize_error();
const XMLElement* element = document.RootElement();
if (!element)
return animation_deserialize_error();
if (std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]))
for (const XMLElement* element = document.RootElement(); element; element = element->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION])) {
if (std::string(document.RootElement()->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]))
return animation_deserialize_error();
_anm2_animation_deserialize(&animations.emplace_back(Anm2Animation()), element);
}
_anm2_animation_deserialize(animation, element);
return true;
}

View File

@@ -23,6 +23,7 @@
#define ANM2_WRITE_INFO "Wrote anm2 to file: {}"
#define ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
#define ANM2_ANIMATION_DEFAULT "New Animation"
#define ANM2_EXTENSION "anm2"
#define ANM2_SPRITESHEET_EXTENSION "png"
@@ -185,7 +186,7 @@ struct Anm2Item {
struct Anm2Animation {
int frameNum = ANM2_FRAME_NUM_MIN;
std::string name = "New Animation";
std::string name = ANM2_ANIMATION_DEFAULT;
bool isLoop = true;
Anm2Item rootAnimation;
std::unordered_map<int, Anm2Item> layerAnimations;
@@ -200,12 +201,12 @@ struct Anm2 {
std::string path{};
std::string createdBy = "robot";
std::string createdOn{};
std::string defaultAnimation = ANM2_ANIMATION_DEFAULT;
std::map<int, Anm2Spritesheet> spritesheets;
std::map<int, Anm2Layer> layers;
std::map<int, Anm2Null> nulls;
std::map<int, Anm2Event> events;
std::map<int, Anm2Animation> animations;
int defaultAnimationID = ID_NONE;
std::vector<Anm2Animation> animations;
int fps = ANM2_FPS_DEFAULT;
int version{};
@@ -213,7 +214,7 @@ struct Anm2 {
};
struct Anm2Reference {
int animationID = ID_NONE;
int animationIndex = ID_NONE;
Anm2Type itemType = ANM2_NONE;
int itemID = ID_NONE;
int frameIndex = INDEX_NONE;
@@ -231,11 +232,11 @@ Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference reference
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference reference);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference reference);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference reference);
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml);
bool anm2_animations_deserialize_from_xml(std::vector<Anm2Animation>& animations, 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);
int anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, int id = ID_NONE);
int anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, int index = INDEX_NONE);
int anm2_animation_length_get(Anm2Animation* self);
int anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, float time);
int anm2_layer_add(Anm2* self);
@@ -244,10 +245,10 @@ vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference reference, bool isRootTra
void anm2_animation_layer_animation_add(Anm2Animation* animation, int id);
void anm2_animation_layer_animation_remove(Anm2Animation* animation, int id);
void anm2_animation_length_set(Anm2Animation* self);
void anm2_animation_merge(Anm2* self, int animationID, const std::vector<int>& mergeIDs, Anm2MergeType type);
void anm2_animation_merge(Anm2* self, int animationID, std::set<int> mergeIndices, Anm2MergeType type);
void anm2_animation_null_animation_add(Anm2Animation* animation, int id);
void anm2_animation_null_animation_remove(Anm2Animation* animation, int id);
void anm2_animation_remove(Anm2* self, int id);
void anm2_animations_remove(Anm2* self, const std::set<int> indices);
void anm2_animation_serialize_to_string(Anm2Animation* animation, std::string* string);
void anm2_frame_bake(Anm2* self, Anm2Reference reference, int interval, bool isRoundScale, bool isRoundRotation);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, float time);

View File

@@ -10,27 +10,27 @@ void clipboard_copy(Clipboard* self) {
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;
if (Anm2Reference* reference = std::get_if<Anm2Reference>(&self->destination)) {
if (Anm2Frame* frame = anm2_frame_from_reference(self->anm2, *reference)) {
anm2_frame_serialize_to_string(frame, reference->itemType, &clipboardText);
clipboard_text_set();
}
}
break;
}
case CLIPBOARD_ANIMATION: {
int* id = std::get_if<int>(&self->location);
if (!id)
break;
Anm2Animation* animation = map_find(self->anm2->animations, *id);
if (!animation)
break;
anm2_animation_serialize_to_string(animation, &clipboardText);
if (std::set<int>* set = std::get_if<std::set<int>>(&self->source)) {
for (auto& i : *set) {
if (Anm2Animation* animation = anm2_animation_from_reference(self->anm2, {i})) {
std::string animationText{};
anm2_animation_serialize_to_string(animation, &animationText);
clipboardText += animationText;
}
}
clipboard_text_set();
}
break;
} break;
}
default:
break;
}
@@ -41,17 +41,13 @@ void clipboard_cut(Clipboard* self) {
switch (self->type) {
case CLIPBOARD_FRAME: {
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
if (!reference)
break;
if (Anm2Reference* reference = std::get_if<Anm2Reference>(&self->destination))
anm2_frame_remove(self->anm2, *reference);
break;
}
case CLIPBOARD_ANIMATION: {
int* id = std::get_if<int>(&self->location);
if (!id)
break;
anm2_animation_remove(self->anm2, *id);
if (std::set<int>* set = std::get_if<std::set<int>>(&self->source))
anm2_animations_remove(self->anm2, *set);
break;
}
default:
@@ -69,25 +65,25 @@ bool clipboard_paste(Clipboard* self) {
switch (self->type) {
case CLIPBOARD_FRAME: {
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
if (!reference)
break;
if (Anm2Reference* reference = std::get_if<Anm2Reference>(&self->destination)) {
Anm2Frame frame;
if (anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
anm2_frame_add(self->anm2, &frame, *reference);
else
if (!anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
return false;
anm2_frame_add(self->anm2, &frame, *reference);
}
break;
}
case CLIPBOARD_ANIMATION: {
int* id = std::get_if<int>(&self->location);
if (!id)
break;
Anm2Animation animation;
if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
anm2_animation_add(self->anm2, &animation, *id);
else
if (int* index = std::get_if<int>(&self->destination)) {
std::vector<Anm2Animation> clipboardAnimations;
if (!anm2_animations_deserialize_from_xml(clipboardAnimations, clipboard_string()))
return false;
int useIndex = std::clamp(*index + 1, 0, (int)self->anm2->animations.size());
for (auto& animation : clipboardAnimations)
anm2_animation_add(self->anm2, &animation, useIndex++);
}
break;
}
default:
@@ -98,5 +94,4 @@ bool clipboard_paste(Clipboard* self) {
}
void clipboard_init(Clipboard* self, Anm2* anm2) { self->anm2 = anm2; }
bool clipboard_is_value(void) { return SDL_HasClipboardText(); }

View File

@@ -1,17 +1,19 @@
#pragma once
#include "anm2.h"
#include <variant>
#define CLIPBOARD_TEXT_SET_WARNING "Unable to set clipboard text! ({})"
enum ClipboardType { CLIPBOARD_NONE, CLIPBOARD_FRAME, CLIPBOARD_ANIMATION };
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, int>;
using ClipboardValue = std::variant<std::monostate, Anm2Reference, std::set<int>, int>;
struct Clipboard {
Anm2* anm2 = nullptr;
ClipboardType type;
ClipboardLocation location;
ClipboardValue source;
ClipboardValue destination;
};
bool clipboard_is_value(void);

File diff suppressed because it is too large Load Diff

View File

@@ -12,11 +12,9 @@
#include "snapshots.h"
#include "tool.h"
#include "window.h"
#include <SDL3/SDL_mouse.h>
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
#define IMGUI_DEBUG_PARANOID
#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#define IMGUI_ENABLE_DOCKING
#define IM_VEC2_CLASS_EXTRA \
inline bool operator==(const ImVec2& rhs) const { return x == rhs.x && y == rhs.y; } \
inline bool operator!=(const ImVec2& rhs) const { return !(*this == rhs); } \
@@ -83,7 +81,6 @@
#define IMGUI_CHORD_NONE (ImGuiMod_None)
#define IMGUI_FRAME_BORDER 2.0f
#define IMGUI_LOG_DURATION 3.0f
#define IMGUI_LOG_PADDING 10.0f
#define IMGUI_TEXT_HEIGHT_PADDING 4.0f
#define IMGUI_PLAYHEAD_LINE_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX)
#define IMGUI_TRIGGERS_EVENT_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, 128)
@@ -97,7 +94,7 @@
#define IMGUI_ACTION_FRAME_CROP "Frame Crop"
#define IMGUI_ACTION_FRAME_MOVE "Frame Move"
#define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap"
#define IMGUI_ACTION_ANIMATION_MOVE "Move Animation"
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame"
#define IMGUI_ACTION_ITEM_SWAP "Item Swap"
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
@@ -140,7 +137,11 @@
#define IMGUI_LOG_ANIMATION_PASTE_ERROR "Failed to parse clipboard text as an animation."
#define IMGUI_LOG_FRAME_PASTE_ERROR "Failed to parse clipboard text as a frame."
#define IMGUI_LOG_RELOAD_SPRITESHEET "Reloaded spritesheet(s)."
#define IMGUI_LOG_REMOVE_UNUSED_SPRITESHEETS "Removed unused spritesheet(s)."
#define IMGUI_LOG_REMOVE_UNUSED_EVENTS "Removed unused event(s)."
#define IMGUI_LOG_ADD_SPRITESHEET_ERROR "Failed to add spritesheet: {}. Make sure it's a valid PNG file."
#define IMGUI_LOG_ADD_SPRITESHEET "Added spritesheet #{}: {}"
#define IMGUI_LOG_REPLACE_SPRITESHEET "Replaced spritesheet #{}: {}"
#define IMGUI_NONE "None"
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
@@ -168,6 +169,8 @@
#define IMGUI_TRIGGERS_FONT_SCALE 2.0
const ImVec2 IMGUI_TOOL_BUTTON_SIZE = {24, 24};
const ImVec2 IMGUI_TOOL_COLOR_SIZE = {24, 24};
const ImVec2 IMGUI_TIMELINE_FRAME_SIZE = {12, 36};
const ImVec2 IMGUI_TIMELINE_FRAME_ATLAS_OFFSET = {ATLAS_SIZE_SMALL.x * 0.25f, (IMGUI_TIMELINE_FRAME_SIZE.y * 0.5f) - (ATLAS_SIZE_SMALL.y * 0.5f)};
const ImVec2 IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE = {150, 0};
@@ -198,6 +201,8 @@ const ImGuiKey IMGUI_INPUT_ZOOM_OUT = ImGuiKey_2;
const ImGuiKey IMGUI_INPUT_ENTER = ImGuiKey_Enter;
const ImGuiKey IMGUI_INPUT_RENAME = ImGuiKey_F2;
const ImGuiKey IMGUI_INPUT_DEFAULT = ImGuiKey_Home;
const ImGuiKeyChord IMGUI_CHORD_SELECT_ALL = ImGuiMod_Ctrl | ImGuiKey_A;
const ImGuiKeyChord IMGUI_CHORD_SELECT_NONE = ImGuiKey_Escape;
const ImGuiMouseButton IMGUI_MOUSE_DEFAULT = ImGuiMouseButton_Middle;
enum ImguiPopupType { IMGUI_POPUP_NONE, IMGUI_POPUP_BY_ITEM, IMGUI_POPUP_CENTER_WINDOW };
@@ -228,14 +233,22 @@ struct Imgui {
Clipboard* clipboard = nullptr;
SDL_Window* window = nullptr;
SDL_GLContext* glContext = nullptr;
Anm2 saveAnm2 = Anm2();
ImFont* fonts[FONT_COUNT] = {};
ImGuiStyle style;
std::vector<ImguiLogItem> log{};
std::string pendingPopup{};
ImguiPopupType pendingPopupType = IMGUI_POPUP_NONE;
ImVec2 pendingPopupPosition{};
std::vector<ImguiLogItem> log;
ImGuiStyle style;
Anm2 saveAnm2;
SDL_SystemCursor cursor;
SDL_SystemCursor pendingCursor;
std::set<int> selectedAnimationIndices{};
int lastAnimationIndex = ID_NONE;
std::set<int> selectedSpritesheetIDs{};
int lastSpritesheetID = ID_NONE;
int selectedEventID = ID_NONE;
int selectedLayerID = ID_NONE;
int selectedNullID = ID_NONE;
SDL_SystemCursor cursor = SDL_SYSTEM_CURSOR_DEFAULT;
SDL_SystemCursor pendingCursor = SDL_SYSTEM_CURSOR_DEFAULT;
bool isCursorSet = false;
bool isContextualActionsEnabled = true;
bool isQuit = false;
@@ -254,9 +267,12 @@ static void imgui_log_push(Imgui* self, const std::string& text) {
}
static inline void imgui_anm2_new(Imgui* self) {
*self->reference = Anm2Reference();
anm2_free(self->anm2);
anm2_new(self->anm2);
*self->reference = Anm2Reference();
self->selectedAnimationIndices = {};
self->selectedSpritesheetIDs = {};
}
static inline void imgui_file_open(Imgui* self) { dialog_anm2_open(self->dialog); }
@@ -576,7 +592,8 @@ enum ImguiItemType {
IMGUI_ITEM,
IMGUI_TEXT,
IMGUI_IMAGE,
IMGUI_WINDOW,
IMGUI_BEGIN_WINDOW,
IMGUI_END_WINDOW,
IMGUI_DOCKSPACE,
IMGUI_BEGIN_CHILD,
IMGUI_END_CHILD,
@@ -611,6 +628,7 @@ static ImGuiKeyChord* imgui_hotkey_chord_registry(void) {
}
typedef void (*ImguiFunction)(Imgui*);
using IntStringMap = std::map<int, std::string>;
#define IMGUI_ITEM_MEMBERS \
X(label, std::string, false, {}) \
@@ -619,14 +637,16 @@ typedef void (*ImguiFunction)(Imgui*);
X(popup, std::string, false, {}) \
X(dragDrop, std::string, true, {}) \
X(focusWindow, std::string, true, {}) \
X(items, std::vector<std::string>, false, {}) \
X(items, IntStringMap, false, {}) \
X(atlas, AtlasType, false, ATLAS_NONE) \
X(textureID, int, false, ID_NONE) \
X(chord, ImGuiKeyChord, true, {}) \
X(hotkey, HotkeyType, true, {}) \
X(mnemonicKey, ImGuiKey, false, ImGuiKey_None) \
X(mnemonicIndex, int, false, INDEX_NONE) \
X(position, vec2, true, {}) \
X(size, vec2, true, {}) \
X(scale, float, false, 1.0f) \
X(uvMin, vec2, false, vec2()) \
X(uvMax, vec2, false, vec2(1.0f)) \
X(popupSize, vec2, false, {}) \
@@ -638,9 +658,12 @@ typedef void (*ImguiFunction)(Imgui*);
X(isMnemonicDisabled, bool, false, false) \
X(isEmptyFormat, bool, false, false) \
X(isUseItemActivated, bool, false, false) \
X(isSizeToText, bool, false, false) \
X(isSizeToRegion, bool, false, false) \
X(isWidthToText, bool, false, false) \
X(isWidthToRegion, bool, false, false) \
X(isHeightToRegion, bool, false, false) \
X(isHotkeyInLabel, bool, false, false) \
X(isAllowHotkeyWhenFocusWindow, bool, false, false) \
X(isAtlasStretch, bool, false, false) \
X(isSameLine, bool, false, false) \
X(isSeparator, bool, false, false) \
X(id, int, false, 0) \
@@ -651,16 +674,19 @@ typedef void (*ImguiFunction)(Imgui*);
X(min, int, true, {}) \
X(max, int, true, {}) \
X(value, int, false, {}) \
X(atlasOffset, vec2, false, {}) \
X(atlasOffset, vec2, true, {}) \
X(cursorPosition, vec2, true, {}) \
X(cursorOffset, vec2, false, {}) \
X(textPosition, vec2, true, {}) \
X(textOffset, vec2, false, {}) \
X(itemSpacing, vec2, true, {}) \
X(windowPadding, vec2, true, {}) \
X(framePadding, vec2, true, {}) \
X(border, int, true, {}) \
X(flags, int, false, {}) \
X(windowFlags, int, false, {}) \
X(rowCount, int, true, {})
X(rowCount, int, true, {}) \
X(font, ImFont*, true, {})
struct ImguiItemOverride {
#define X(name, type, isOptional, ...) std::optional<type> name = {};
@@ -714,6 +740,8 @@ struct ImguiItem {
IMGUI_ITEM_MEMBERS
#undef X
out.id += out.idOffset;
return out;
}
@@ -761,27 +789,27 @@ IMGUI_ITEM(IMGUI_TASKBAR, self.label = "Taskbar", self.size = {0, 32},
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings);
IMGUI_ITEM(IMGUI_FILE, self.label = "&File", self.tooltip = "Opens the file menu, for reading/writing anm2 files.", self.popup = "## File Popup",
self.popupType = IMGUI_POPUP_BY_ITEM, self.isSizeToText = true, self.isSameLine = true);
self.popupType = IMGUI_POPUP_BY_ITEM, self.isWidthToText = true, self.isHeightToRegion = true, self.isSameLine = true);
IMGUI_ITEM(IMGUI_NEW, self.label = "&New", self.tooltip = "Load a blank .anm2 file to edit.", self.function = imgui_file_new, self.hotkey = HOTKEY_NEW,
self.isSizeToText = true, self.isHotkeyInLabel = true);
self.isWidthToRegion = true, self.isHotkeyInLabel = true);
IMGUI_ITEM(IMGUI_OPEN, self.label = "&Open", self.tooltip = "Open an existing .anm2 file to edit.", self.function = imgui_file_open, self.hotkey = HOTKEY_OPEN,
self.isSizeToText = true, self.isHotkeyInLabel = true);
self.isWidthToRegion = true, self.isHotkeyInLabel = true);
IMGUI_ITEM(IMGUI_SAVE, self.label = "&Save",
self.tooltip = "Saves the current .anm2 file to its path.\nIf no "
"path exists, one can be chosen.",
self.function = imgui_file_save, self.hotkey = HOTKEY_SAVE, self.isSizeToText = true, self.isHotkeyInLabel = true);
self.function = imgui_file_save, self.hotkey = HOTKEY_SAVE, self.isWidthToText = true, self.isHotkeyInLabel = true);
IMGUI_ITEM(IMGUI_SAVE_AS, self.label = "S&ave As", self.tooltip = "Saves the current .anm2 file to a chosen path.", self.function = imgui_file_save_as,
self.hotkey = HOTKEY_SAVE_AS, self.isSizeToText = true, self.isHotkeyInLabel = true);
self.hotkey = HOTKEY_SAVE_AS, self.isWidthToText = true, self.isHotkeyInLabel = true);
IMGUI_ITEM(IMGUI_EXPLORE_ANM2_LOCATION, self.label = "E&xplore Anm2 Location", self.tooltip = "Open the system's file explorer in the anm2's path.",
self.function = imgui_explore, self.isSizeToText = true, self.isSeparator = true);
self.function = imgui_explore, self.isWidthToText = true, self.isSeparator = true);
IMGUI_ITEM(IMGUI_EXIT, self.label = "&Exit", self.tooltip = "Exits the program.", self.function = imgui_quit, self.hotkey = HOTKEY_EXIT,
self.isSizeToText = true, self.isHotkeyInLabel = true);
self.isWidthToText = true, self.isHotkeyInLabel = true);
IMGUI_ITEM(IMGUI_EXIT_CONFIRMATION, self.label = "Exit Confirmation", self.tooltip = "Unsaved changes will be lost!\nAre you sure you want to exit?");
@@ -790,7 +818,8 @@ IMGUI_ITEM(IMGUI_OPEN_CONFIRMATION, self.label = "Open Confirmation", self.toolt
IMGUI_ITEM(IMGUI_NO_ANM2_PATH_CONFIRMATION, self.label = "No Anm2 Path", self.tooltip = "You will need to load or make a new .anm2 file first!\n");
IMGUI_ITEM(IMGUI_WIZARD, self.label = "&Wizard", self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
self.popup = "## Wizard Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isSizeToText = true, self.isSameLine = true);
self.popup = "## Wizard Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isHeightToRegion = true, self.isWidthToText = true,
self.isSameLine = true);
#define IMGUI_GENERATE_ANIMATION_FROM_GRID_PADDING 40
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID, self.label = "&Generate Animation from Grid", self.tooltip = "Generate a new animation from grid values.",
@@ -875,11 +904,10 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CANCEL, self.label = "Cancel", self
self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT);
IMGUI_ITEM(IMGUI_SCALE_ANM2, self.label = "S&cale Anm2", self.tooltip = "Scale up all size and position-related frame properties in the anm2.",
self.popup = "Scale Anm2", self.popupType = IMGUI_POPUP_CENTER_WINDOW, self.popupSize = {260, 75}, self.isSizeToText = true,
self.popup = "Scale Anm2", self.popupType = IMGUI_POPUP_CENTER_WINDOW, self.popupSize = {260, 75}, self.isWidthToText = true,
self.isSeparator = true);
IMGUI_ITEM(IMGUI_SCALE_ANM2_OPTIONS_CHILD, self.label = "## Scale Anm2 Options Child",
self.size = {IMGUI_SCALE_ANM2.popupSize.x, IMGUI_SCALE_ANM2.popupSize.y - IMGUI_FOOTER_CHILD.size->y}, self.flags = true);
IMGUI_ITEM(IMGUI_SCALE_ANM2_OPTIONS_CHILD, self.label = "## Scale Anm2 Options Child", self.flags = true);
IMGUI_ITEM(IMGUI_SCALE_ANM2_VALUE, self.label = "Value",
self.tooltip = "The size and position-related frame properties in "
@@ -894,11 +922,9 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION, self.label = "&Render Animation",
"options can be customized.",
self.popup = "Render Animation", self.popupSize = {500, 170}, self.popupType = IMGUI_POPUP_CENTER_WINDOW);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CHILD, self.label = "## Render Animation Child",
self.size = {IMGUI_RENDER_ANIMATION.popupSize.x, IMGUI_RENDER_ANIMATION.popupSize.y - IMGUI_FOOTER_CHILD.size->y}, self.flags = true);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CHILD, self.label = "## Render Animation Child", self.flags = true);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FOOTER_CHILD, self.label = "## Render Animation Footer Child",
self.size = {IMGUI_RENDER_ANIMATION.popupSize.x, IMGUI_FOOTER_CHILD.size->y}, self.flags = true);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FOOTER_CHILD, self.label = "## Render Animation Footer Child", self.flags = true);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE, self.label = "## Location Browse", self.tooltip = "Open file explorer to pick rendered animation location.",
self.atlas = ATLAS_FOLDER, self.isSameLine = true);
@@ -920,7 +946,11 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_PATH, self.label = "FFmpeg Path",
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT, self.label = "Output",
self.tooltip = "Select the rendered animation output.\nIt can either be "
"one animated image or a sequence of frames.",
self.items = {std::begin(RENDER_TYPE_STRINGS), std::end(RENDER_TYPE_STRINGS)}, self.value = RENDER_PNG);
self.items = {{RENDER_PNG, RENDER_TYPE_STRINGS[RENDER_PNG]},
{RENDER_GIF, RENDER_TYPE_STRINGS[RENDER_GIF]},
{RENDER_WEBM, RENDER_TYPE_STRINGS[RENDER_WEBM]},
{RENDER_MP4, RENDER_TYPE_STRINGS[RENDER_MP4]}},
self.value = RENDER_PNG);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT, self.label = "Format",
self.tooltip = "(PNG images only).\nSet the format of each output frame; i.e., "
@@ -950,28 +980,29 @@ IMGUI_ITEM(IMGUI_RENDERING_ANIMATION_INFO, self.label = "Recording frames. Once
IMGUI_ITEM(IMGUI_RENDERING_ANIMATION_CANCEL, self.label = "Cancel", self.tooltip = "Cancel rendering the animation.", self.rowCount = 1);
IMGUI_ITEM(IMGUI_PLAYBACK, self.label = "&Playback", self.tooltip = "Opens the playback menu, for configuring playback settings.",
self.popup = "## Playback Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isSizeToText = true, self.isSameLine = true);
self.popup = "## Playback Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isWidthToText = true, self.isHeightToRegion = true,
self.isSameLine = true);
IMGUI_ITEM(IMGUI_ALWAYS_LOOP, self.label = "&Always Loop",
self.tooltip = "Sets the animation playback to always loop, "
"regardless of the animation's loop setting.",
self.isSizeToText = true);
self.isWidthToText = true);
IMGUI_ITEM(IMGUI_CLAMP_PLAYHEAD, self.label = "&Clamp Playhead",
self.tooltip = "The playhead (draggable icon on timeline) won't be "
"able to exceed the animation length.",
self.isSizeToText = true);
self.isWidthToText = true);
IMGUI_ITEM(IMGUI_SETTINGS, self.label = "&Settings", self.tooltip = "Opens the setting menu, for configuring general program settings.",
self.popup = "## Settings Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isSizeToText = true);
self.popup = "## Settings Popup", self.popupType = IMGUI_POPUP_BY_ITEM, self.isWidthToText = true, self.isHeightToRegion = true);
IMGUI_ITEM(IMGUI_VSYNC, self.label = "&Vsync",
self.tooltip = "Toggle vertical sync; synchronizes program "
"framerate with your monitor's refresh rate.",
self.isSizeToText = true, self.isSeparator = true);
self.isWidthToText = true, self.isSeparator = true);
IMGUI_ITEM(IMGUI_HOTKEYS, self.label = "&Hotkeys", self.tooltip = "Change the program's hotkeys.", self.popup = "Hotkeys", self.popupSize = {500, 405},
self.isSizeToText = true, self.isSeparator = true);
self.isWidthToText = true, self.isSeparator = true);
IMGUI_ITEM(IMGUI_HOTKEYS_CHILD, self.label = "## Hotkeys Child", self.size = {IMGUI_HOTKEYS.popupSize.x, IMGUI_HOTKEYS.popupSize.y - 35}, self.flags = true);
@@ -984,12 +1015,18 @@ IMGUI_ITEM(IMGUI_HOTKEYS_OPTIONS_CHILD, self.label = "## Merge Options Child", s
IMGUI_ITEM(IMGUI_HOTKEYS_CONFIRM, self.label = "Confirm", self.tooltip = "Use these hotkeys.", self.rowCount = 1);
IMGUI_ITEM(IMGUI_DEFAULT_SETTINGS, self.label = "&Reset to Default Settings", self.tooltip = "Reset the program's settings to their default state.",
self.isSizeToText = true);
self.isWidthToText = true);
IMGUI_ITEM(IMGUI_ANM2S, self.label = "## Anm2s", self.size = {0, 32},
self.flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings);
IMGUI_ITEM(IMGUI_ANM2, self.label = "## Anm2");
IMGUI_ITEM(IMGUI_LAYERS, self.label = "Layers", self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
IMGUI_ITEM(IMGUI_LAYERS_CHILD, self.label = "## Layers Child", self.flags = true);
IMGUI_ITEM(IMGUI_LAYER, self.label = "## Layer Item", self.dragDrop = "## Layer Drag Drop", self.atlas = ATLAS_LAYER, self.isSizeToRegion = true,
IMGUI_ITEM(IMGUI_LAYER, self.label = "## Layer Item", self.dragDrop = "## Layer Drag Drop", self.atlas = ATLAS_LAYER, self.isWidthToRegion = true,
self.idOffset = 3000);
IMGUI_ITEM(IMGUI_LAYER_SPRITESHEET_ID, self.label = "## Spritesheet ID", self.tooltip = "Change the spritesheet ID this layer uses.",
@@ -1024,7 +1061,7 @@ IMGUI_ITEM(IMGUI_ANIMATIONS, self.label = "Animations", self.flags = ImGuiWindow
IMGUI_ITEM(IMGUI_ANIMATIONS_CHILD, self.label = "## Animations Child", self.flags = true);
IMGUI_ITEM(IMGUI_ANIMATION, self.label = "## Animation Item", self.dragDrop = "## Animation Drag Drop", self.atlas = ATLAS_ANIMATION,
self.isSizeToRegion = true, self.idOffset = 2000);
self.isWidthToRegion = true, self.idOffset = 2000);
#define IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT 5
IMGUI_ITEM(IMGUI_ANIMATION_ADD, self.label = "Add", self.tooltip = "Adds a new animation.", self.snapshotAction = "Add Animation",
@@ -1071,12 +1108,28 @@ IMGUI_ITEM(IMGUI_MERGE_DELETE_ANIMATIONS_AFTER, self.label = "Delete Animations
IMGUI_ITEM(IMGUI_MERGE_CONFIRM, self.label = "Merge", self.tooltip = "Merge the selected animations with the options set.",
self.snapshotAction = "Merge Animations", self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT, self.isSameLine = true);
IMGUI_ITEM(IMGUI_ANIMATION_REMOVE, self.label = "Remove", self.tooltip = "Remove the selected animation.", self.snapshotAction = "Remove Animation",
IMGUI_ITEM(IMGUI_ANIMATION_REMOVE, self.label = "Remove", self.tooltip = "Remove the selected animation(s).", self.snapshotAction = "Remove Animation(s)",
self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.chord = ImGuiKey_Delete, self.focusWindow = IMGUI_ANIMATIONS.label, self.isSameLine = true);
IMGUI_ITEM(IMGUI_ANIMATION_DEFAULT, self.label = "Default", self.tooltip = "Set the selected animation as the default one.",
IMGUI_ITEM(IMGUI_ANIMATION_DEFAULT, self.label = "Default", self.tooltip = "Set the referenced animation as the default one.",
self.snapshotAction = "Default Animation", self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.isSameLine = true);
static inline void imgui_animations_select_all(Imgui* self) {
for (int i = 0; i < (int)self->anm2->animations.size(); i++)
self->selectedAnimationIndices.insert(i);
}
IMGUI_ITEM(IMGUI_ANIMATION_SELECT_ALL, self.label = "## Select None", self.hotkey = HOTKEY_SELECT_ALL, self.focusWindow = IMGUI_ANIMATIONS.label,
self.function = imgui_animations_select_all, self.isAllowHotkeyWhenFocusWindow = true);
static inline void imgui_animations_select_none(Imgui* self) {
self->selectedAnimationIndices.clear();
*self->reference = Anm2Reference();
}
IMGUI_ITEM(IMGUI_ANIMATION_SELECT_NONE, self.label = "## Select None", self.hotkey = HOTKEY_SELECT_NONE, self.focusWindow = IMGUI_ANIMATIONS.label,
self.function = imgui_animations_select_none, self.isAllowHotkeyWhenFocusWindow = true);
IMGUI_ITEM(IMGUI_EVENTS, self.label = "Events", self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
IMGUI_ITEM(IMGUI_EVENTS_CHILD, self.label = "## Events Child", self.flags = true);
@@ -1100,8 +1153,7 @@ IMGUI_ITEM(IMGUI_SPRITESHEET_CHILD, self.label = "## Spritesheet Child", self.fl
IMGUI_ITEM(IMGUI_SPRITESHEET, self.label = "## Spritesheet", self.dragDrop = "## Spritesheet Drag Drop");
IMGUI_ITEM(IMGUI_SPRITESHEET_TEXT, self.label = "## Spritesheet Text", self.atlas = ATLAS_SPRITESHEET, self.itemSpacing = vec2(8, 0));
IMGUI_ITEM(IMGUI_SPRITESHEETS_FOOTER_CHILD, self.label = "## Spritesheets Footer Child", self.size = {0, IMGUI_FOOTER_CHILD.size->y * 1.66f},
self.flags = true);
IMGUI_ITEM(IMGUI_SPRITESHEETS_FOOTER_CHILD, self.label = "## Spritesheets Footer Child", self.flags = true);
#define IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT 4
#define IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT 3
@@ -1117,12 +1169,12 @@ IMGUI_ITEM(IMGUI_SPRITESHEETS_REPLACE, self.label = "Replace", self.tooltip = "R
IMGUI_ITEM(IMGUI_SPRITESHEETS_REMOVE_UNUSED, self.label = "Remove Unused",
self.tooltip = "Remove all unused spritesheets in the anm2 (i.e., "
"the spritesheet isn't used in any layer animations).",
self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT);
self.snapshotAction = "Remove Unused Spritesheets", self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT);
IMGUI_ITEM(IMGUI_SPRITESHEETS_SELECT_ALL, self.label = "Select All", self.tooltip = "Select all spritesheets.", self.chord = ImGuiMod_Ctrl + ImGuiKey_A,
IMGUI_ITEM(IMGUI_SPRITESHEETS_SELECT_ALL, self.label = "Select All", self.tooltip = "Select all spritesheets.", self.hotkey = HOTKEY_SELECT_ALL,
self.focusWindow = IMGUI_SPRITESHEETS.label, self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT, self.isSameLine = true);
IMGUI_ITEM(IMGUI_SPRITESHEETS_SELECT_NONE, self.label = "Select None", self.tooltip = "Unselect all spritesheets.", self.chord = ImGuiKey_Escape,
IMGUI_ITEM(IMGUI_SPRITESHEETS_SELECT_NONE, self.label = "Select None", self.tooltip = "Unselect all spritesheets.", self.hotkey = HOTKEY_SELECT_NONE,
self.focusWindow = IMGUI_SPRITESHEETS.label, self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT, self.isSameLine = true);
IMGUI_ITEM(IMGUI_SPRITESHEET_SAVE, self.label = "Save", self.tooltip = "Save the selected spritesheets to their original locations.",
@@ -1274,7 +1326,7 @@ IMGUI_ITEM(IMGUI_TOOLS, self.label = "Tools");
IMGUI_ITEM(IMGUI_TOOL_PAN, self.label = "## Pan",
self.tooltip = "Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou "
"can also use the middle mouse button to pan at any time.",
self.function = imgui_tool_pan_set, self.hotkey = HOTKEY_PAN, self.atlas = ATLAS_PAN);
self.function = imgui_tool_pan_set, self.hotkey = HOTKEY_PAN, self.atlas = ATLAS_PAN, self.isAtlasStretch = true);
IMGUI_ITEM(IMGUI_TOOL_MOVE, self.label = "## Move",
self.tooltip = "Use the move tool.\nAnimation Preview: Will move the position "
@@ -1374,12 +1426,12 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD, self.label = "## Item Prop
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_LAYER, self.label = "Layer",
self.tooltip = "The item will be a layer item.\nA layer item is a "
"primary graphical item, using a spritesheet.",
self.isSizeToText = true, self.value = ANM2_LAYER, self.isSameLine = true);
self.isWidthToText = true, self.value = ANM2_LAYER, self.isSameLine = true);
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_NULL, self.label = "Null",
self.tooltip = "The item will be a null item.\nA null item is an "
"invisible item, often accessed by a game engine.",
self.isSizeToText = true, self.value = ANM2_NULL);
self.isWidthToText = true, self.value = ANM2_NULL);
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_ITEMS_CHILD, self.label = "## Item Properties Items", self.size = {IMGUI_POPUP_ITEM_PROPERTIES_SIZE.x, 250},
self.flags = true);
@@ -1598,16 +1650,16 @@ IMGUI_ITEM(IMGUI_CONTEXT_MENU, self.label = "## Context Menu");
IMGUI_ITEM(IMGUI_CUT, self.label = "Cut",
self.tooltip = "Cuts the currently selected contextual element; "
"removing it and putting it to the clipboard.",
self.snapshotAction = "Cut", self.function = imgui_cut, self.hotkey = HOTKEY_CUT, self.isSizeToText = true);
self.snapshotAction = "Cut", self.function = imgui_cut, self.hotkey = HOTKEY_CUT, self.isWidthToText = true);
IMGUI_ITEM(IMGUI_COPY, self.label = "Copy", self.tooltip = "Copies the currently selected contextual element to the clipboard.", self.snapshotAction = "Copy",
self.function = imgui_copy, self.hotkey = HOTKEY_COPY, self.isSizeToText = true);
IMGUI_ITEM(IMGUI_COPY, self.label = "Copy", self.tooltip = "Copies the currently selected contextual element to the clipboard.", self.function = imgui_copy,
self.hotkey = HOTKEY_COPY, self.isWidthToText = true);
IMGUI_ITEM(IMGUI_PASTE, self.label = "Paste", self.tooltip = "Pastes the currently selection contextual element from the clipboard.",
self.snapshotAction = "Paste", self.function = imgui_paste, self.hotkey = HOTKEY_PASTE, self.isSizeToText = true);
self.snapshotAction = "Paste", self.function = imgui_paste, self.hotkey = HOTKEY_PASTE, self.isWidthToText = true);
IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT, self.label = "## Input Text", self.tooltip = "Rename the selected item.", self.snapshotAction = "Rename Item",
self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue, self.max = UCHAR_MAX);
IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT, self.label = "## Input Text", self.tooltip = "Rename the selected item.", self.snapshotAction = "Rename",
self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue, self.max = 0xFFFF);
IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT, self.label = "## Input Int", self.tooltip = "Change the selected item's value.", self.snapshotAction = "Change Value",
self.step = 0);

View File

@@ -87,8 +87,8 @@ void preview_draw(Preview* self) {
if (self->settings->previewIsAxes)
canvas_axes_draw(&self->canvas, shaderAxis, transform, self->settings->previewAxesColor);
auto animation_draw = [&](int animationID) {
Anm2Animation* animation = map_find(self->anm2->animations, animationID);
auto animation_draw = [&](int animationIndex) {
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, {animationIndex});
if (!animation)
return;
@@ -107,7 +107,7 @@ void preview_draw(Preview* self) {
return;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationIndex, ANM2_LAYER, id}, time);
if (!frame.isVisible)
return;
@@ -151,7 +151,7 @@ void preview_draw(Preview* self) {
return;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationIndex, ANM2_NULL, id}, time);
if (!frame.isVisible)
return;
@@ -180,7 +180,7 @@ void preview_draw(Preview* self) {
auto base_draw = [&](float time, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = {}) {
Anm2Frame root;
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, time);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationIndex, ANM2_ROOT}, time);
mat4 rootModel =
self->settings->previewIsRootTransform ? quad_model_parent_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
@@ -218,7 +218,7 @@ void preview_draw(Preview* self) {
onionskins_draw();
};
animation_draw(self->reference->animationID);
animation_draw(self->reference->animationIndex);
animation_draw(self->animationOverlayID);
canvas_unbind();

View File

@@ -2,25 +2,16 @@
#include "COMMON.h"
enum RenderType
{
RENDER_PNG,
RENDER_GIF,
RENDER_WEBM,
RENDER_MP4,
RENDER_COUNT
};
enum RenderType { RENDER_PNG, RENDER_GIF, RENDER_WEBM, RENDER_MP4, RENDER_COUNT };
const inline std::string RENDER_TYPE_STRINGS[] =
{
const inline std::string RENDER_TYPE_STRINGS[] = {
"PNG Images",
"GIF image",
"WebM video",
"MP4 video",
};
const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
{
const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] = {
".png",
".gif",
".webm",

View File

@@ -1,7 +1,8 @@
#include "resources.h"
#include "RESOURCE.h"
void resources_init(Resources* self) {
texture_from_path_init(&self->atlas, ATLAS_PATH);
texture_from_memory_init(&self->atlas, TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
for (int i = 0; i < SHADER_COUNT; i++)
shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment);

View File

@@ -61,6 +61,8 @@
X(HOTKEY_COPY, hotkeyCopy, TYPE_STRING, "Ctrl+C") \
X(HOTKEY_CUT, hotkeyCut, TYPE_STRING, "Ctrl+X") \
X(HOTKEY_PASTE, hotkeyPaste, TYPE_STRING, "Ctrl+V") \
X(HOTKEY_SELECT_ALL, hotkeySelectAll, TYPE_STRING, "Ctrl+A") \
X(HOTKEY_SELECT_NONE, hotkeySelectNone, TYPE_STRING, "Ctrl+Shift+A") \
\
X(PLAYBACK_IS_LOOP, playbackIsLoop, TYPE_BOOL, true) \
X(PLAYBACK_IS_CLAMP_PLAYHEAD, playbackIsClampPlayhead, TYPE_BOOL, true) \
@@ -211,7 +213,9 @@ constexpr int SETTINGS_COUNT = (int)std::size(SETTINGS_ENTRIES);
X(REDO, "Redo") \
X(COPY, "Copy") \
X(CUT, "Cut") \
X(PASTE, "Paste")
X(PASTE, "Paste") \
X(SELECT_ALL, "Select All") \
X(SELECT_NONE, "Select None")
typedef enum {
#define X(name, str) HOTKEY_##name,
@@ -257,7 +261,9 @@ const inline HotkeyMember SETTINGS_HOTKEY_MEMBERS[HOTKEY_COUNT] = {nullptr,
&Settings::hotkeyRedo,
&Settings::hotkeyCopy,
&Settings::hotkeyCut,
&Settings::hotkeyPaste};
&Settings::hotkeyPaste,
&Settings::hotkeySelectAll,
&Settings::hotkeySelectNone};
const inline std::string SETTINGS_IMGUI_DEFAULT = R"(
# Dear ImGui

View File

@@ -17,7 +17,7 @@ struct SnapshotStack {
Snapshot snapshots[SNAPSHOT_STACK_MAX];
int top = 0;
bool is_empty() const { return top == 0; }
bool empty() const { return top == 0; }
};
struct Snapshots {

View File

@@ -76,6 +76,22 @@ bool texture_from_rgba_write(const std::string& path, const uint8_t* data, ivec2
return isSuccess;
}
bool texture_from_memory_init(Texture* self, ivec2 size, const uint8_t* data, size_t length) {
*self = Texture{};
self->size = size;
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
if (!textureData)
return false;
self->isInvalid = false;
_texture_gl_set(self, textureData);
return true;
}
bool texture_from_gl_write(Texture* self, const std::string& path) { return texture_from_rgba_write(path, texture_download(self).data(), self->size); }
void texture_free(Texture* self) {

View File

@@ -18,8 +18,9 @@ struct Texture {
bool texture_from_gl_write(Texture* self, const std::string& path);
bool texture_from_path_init(Texture* self, const std::string& path);
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data);
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size);
bool texture_from_rgba_init(Texture* self, ivec2 size, const uint8_t* data);
bool texture_from_rgba_write(const std::string& path, const uint8_t* 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);
bool texture_from_memory_init(Texture* self, ivec2 size, const uint8_t* data, size_t length);
std::vector<uint8_t> texture_download(const Texture* self);