Refactor...

This commit is contained in:
2025-10-21 20:23:27 -04:00
parent 7f07eaa128
commit 5b0f9a39c4
104 changed files with 17010 additions and 13171 deletions

View File

@@ -1,266 +1,238 @@
#pragma once
#include "resources.h"
#include <filesystem>
#include <map>
#include <set>
#include <string>
#include <tinyxml2/tinyxml2.h>
#include <vector>
#define ANM2_SCALE_CONVERT(x) ((float)x / 100.0f)
#define ANM2_TINT_CONVERT(x) ((float)x / 255.0f)
#include "texture.h"
#include "types.h"
#define ANM2_FPS_MIN 0
#define ANM2_FPS_DEFAULT 30
#define ANM2_FPS_MAX 120
#define ANM2_FRAME_NUM_MIN 1
#define ANM2_FRAME_NUM_MAX 1000000
#define ANM2_FRAME_DELAY_MIN 1
#define ANM2_STRING_MAX 0xFF
namespace anm2ed::anm2
{
constexpr auto FRAME_NUM_MIN = 1;
constexpr auto FRAME_NUM_MAX = 100000000;
constexpr auto FRAME_DELAY_MIN = 1;
constexpr auto FRAME_DELAY_MAX = FRAME_NUM_MAX;
constexpr auto FPS_MIN = 1;
constexpr auto FPS_MAX = 120;
#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: {}"
#define ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
constexpr auto MERGED_STRING = "(Merged)";
#define ANM2_ANIMATION_DEFAULT "New Animation"
#define ANM2_EXTENSION "anm2"
#define ANM2_SPRITESHEET_EXTENSION "png"
enum Type
{
NONE,
ROOT,
LAYER,
NULL_,
TRIGGER
};
#define ANM2_ELEMENT_LIST \
X(ANIMATED_ACTOR, "AnimatedActor") \
X(INFO, "Info") \
X(CONTENT, "Content") \
X(SPRITESHEETS, "Spritesheets") \
X(SPRITESHEET, "Spritesheet") \
X(LAYERS, "Layers") \
X(LAYER, "Layer") \
X(NULLS, "Nulls") \
X(NULL, "Null") \
X(EVENTS, "Events") \
X(EVENT, "Event") \
X(ANIMATIONS, "Animations") \
X(ANIMATION, "Animation") \
X(ROOT_ANIMATION, "RootAnimation") \
X(FRAME, "Frame") \
X(LAYER_ANIMATIONS, "LayerAnimations") \
X(LAYER_ANIMATION, "LayerAnimation") \
X(NULL_ANIMATIONS, "NullAnimations") \
X(NULL_ANIMATION, "NullAnimation") \
X(TRIGGERS, "Triggers") \
X(TRIGGER, "Trigger")
class Reference
{
public:
int animationIndex{-1};
Type itemType{NONE};
int itemID{-1};
int frameIndex{-1};
int frameTime{-1};
typedef enum {
#define X(name, str) ANM2_ELEMENT_##name,
ANM2_ELEMENT_LIST
#undef X
ANM2_ELEMENT_COUNT
} Anm2Element;
void previous_frame(int max = FRAME_NUM_MAX - 1);
void next_frame(int max = FRAME_NUM_MAX - 1);
auto operator<=>(const Reference&) const = default;
};
const inline char* ANM2_ELEMENT_STRINGS[] = {
#define X(name, str) str,
ANM2_ELEMENT_LIST
#undef X
};
class Info
{
public:
std::string createdBy{"robot"};
std::string createdOn{};
int fps = 30;
int version{};
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ELEMENT_STRING_TO_ENUM, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT)
Info();
Info(tinyxml2::XMLElement* element);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
};
#define ANM2_ATTRIBUTE_LIST \
X(CREATED_BY, "CreatedBy") \
X(CREATED_ON, "CreatedOn") \
X(VERSION, "Version") \
X(FPS, "Fps") \
X(ID, "Id") \
X(PATH, "Path") \
X(NAME, "Name") \
X(SPRITESHEET_ID, "SpritesheetId") \
X(SHOW_RECT, "ShowRect") \
X(DEFAULT_ANIMATION, "DefaultAnimation") \
X(FRAME_NUM, "FrameNum") \
X(LOOP, "Loop") \
X(X_POSITION, "XPosition") \
X(Y_POSITION, "YPosition") \
X(X_PIVOT, "XPivot") \
X(Y_PIVOT, "YPivot") \
X(X_CROP, "XCrop") \
X(Y_CROP, "YCrop") \
X(WIDTH, "Width") \
X(HEIGHT, "Height") \
X(X_SCALE, "XScale") \
X(Y_SCALE, "YScale") \
X(DELAY, "Delay") \
X(VISIBLE, "Visible") \
X(RED_TINT, "RedTint") \
X(GREEN_TINT, "GreenTint") \
X(BLUE_TINT, "BlueTint") \
X(ALPHA_TINT, "AlphaTint") \
X(RED_OFFSET, "RedOffset") \
X(GREEN_OFFSET, "GreenOffset") \
X(BLUE_OFFSET, "BlueOffset") \
X(ROTATION, "Rotation") \
X(INTERPOLATED, "Interpolated") \
X(LAYER_ID, "LayerId") \
X(NULL_ID, "NullId") \
X(EVENT_ID, "EventId") \
X(AT_FRAME, "AtFrame")
class Spritesheet
{
public:
std::filesystem::path path{};
texture::Texture texture;
typedef enum {
#define X(name, str) ANM2_ATTRIBUTE_##name,
ANM2_ATTRIBUTE_LIST
#undef X
ANM2_ATTRIBUTE_COUNT
} Anm2Attribute;
Spritesheet();
Spritesheet(tinyxml2::XMLElement* element, int& id);
Spritesheet(const std::string& directory, const std::string& path = {});
bool save(const std::string& directory, const std::string& path = {});
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
void reload(const std::string& directory);
bool is_valid();
};
static const char* ANM2_ATTRIBUTE_STRINGS[] = {
#define X(name, str) str,
ANM2_ATTRIBUTE_LIST
#undef X
};
class Layer
{
public:
std::string name{"New Layer"};
int spritesheetID{};
DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ATTRIBUTE_STRING_TO_ENUM, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
Layer();
Layer(tinyxml2::XMLElement* element, int& id);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
};
enum Anm2Type { ANM2_NONE, ANM2_ROOT, ANM2_LAYER, ANM2_NULL, ANM2_TRIGGER, ANM2_COUNT };
class Null
{
public:
std::string name{"New Null"};
bool isShowRect{};
struct Anm2Spritesheet {
std::string path{};
Texture texture;
std::vector<uint8_t> pixels;
Null();
Null(tinyxml2::XMLElement* element, int& id);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
};
auto operator<=>(const Anm2Spritesheet&) const = default;
};
class Event
{
public:
std::string name{"New Event"};
struct Anm2Layer {
std::string name = "New Layer";
int spritesheetID{};
Event();
Event(tinyxml2::XMLElement* element, int& id);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
};
auto operator<=>(const Anm2Layer&) const = default;
};
struct Content
{
std::map<int, Spritesheet> spritesheets{};
std::map<int, Layer> layers{};
std::map<int, Null> nulls{};
std::map<int, Event> events{};
struct Anm2Null {
std::string name = "New Null";
bool isShowRect = false;
Content();
auto operator<=>(const Anm2Null&) const = default;
};
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
Content(tinyxml2::XMLElement* element);
bool spritesheet_add(const std::string& directory, const std::string& path, int& id);
void spritesheet_remove(int& id);
std::set<int> spritesheets_unused();
void layer_add(int& id);
void null_add(int& id);
void event_add(int& id);
};
struct Anm2Event {
std::string name = "New Event";
#define MEMBERS \
X(isVisible, bool, true) \
X(isInterpolated, bool, false) \
X(rotation, float, 0.0f) \
X(delay, int, FRAME_DELAY_MIN) \
X(atFrame, int, -1) \
X(eventID, int, -1) \
X(pivot, glm::vec2, {}) \
X(crop, glm::vec2, {}) \
X(position, glm::vec2, {}) \
X(size, glm::vec2, {}) \
X(scale, glm::vec2, glm::vec2(100.0f)) \
X(offset, glm::vec3, types::color::TRANSPARENT) \
X(tint, glm::vec4, types::color::WHITE)
auto operator<=>(const Anm2Event&) const = default;
};
#define ANM2_FRAME_MEMBERS \
X(isVisible, bool, true) \
X(isInterpolated, bool, false) \
X(rotation, float, 0.0f) \
X(delay, int, ANM2_FRAME_DELAY_MIN) \
X(atFrame, int, INDEX_NONE) \
X(eventID, int, ID_NONE) \
X(crop, vec2, {}) \
X(pivot, vec2, {}) \
X(position, vec2, {}) \
X(size, vec2, {}) \
X(scale, vec2, {100, 100}) \
X(offsetRGB, vec3, COLOR_OFFSET_NONE) \
X(tintRGBA, vec4, COLOR_OPAQUE)
struct Anm2Frame {
class Frame
{
public:
#define X(name, type, ...) type name = __VA_ARGS__;
ANM2_FRAME_MEMBERS
MEMBERS
#undef X
auto operator<=>(const Anm2Frame&) const = default;
};
struct Anm2FrameChange {
Frame();
Frame(tinyxml2::XMLElement* element, Type type);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, Type type);
void shorten();
void extend();
};
struct FrameChange
{
#define X(name, type, ...) std::optional<type> name{};
ANM2_FRAME_MEMBERS
MEMBERS
#undef X
};
};
struct Anm2Item {
bool isVisible = true;
std::vector<Anm2Frame> frames;
#undef MEMBERS
auto operator<=>(const Anm2Item&) const = default;
};
class Item
{
public:
std::vector<Frame> frames{};
bool isVisible{true};
struct Anm2Animation {
int frameNum = ANM2_FRAME_NUM_MIN;
std::string name = ANM2_ANIMATION_DEFAULT;
bool isLoop = true;
Anm2Item rootAnimation;
std::unordered_map<int, Anm2Item> layerAnimations;
std::vector<int> layerOrder;
std::map<int, Anm2Item> nullAnimations;
Anm2Item triggers;
Item();
auto operator<=>(const Anm2Animation&) const = default;
};
Item(tinyxml2::XMLElement* element, Type type, int* id = nullptr);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, Type type, int id = -1);
int length(Type type);
Frame frame_generate(float time, Type type);
};
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::vector<Anm2Animation> animations;
int fps = ANM2_FPS_DEFAULT;
int version{};
class Animation
{
public:
std::string name{"New Animation"};
int frameNum{FRAME_NUM_MIN};
bool isLoop{true};
Item rootAnimation;
std::unordered_map<int, Item> layerAnimations{};
std::vector<int> layerOrder{};
std::map<int, Item> nullAnimations{};
Item triggers;
auto operator<=>(const Anm2&) const = default;
};
Animation();
Animation(tinyxml2::XMLElement* element);
Item* item_get(Type type, int id = -1);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
int length();
};
struct Anm2Reference {
int animationIndex = ID_NONE;
Anm2Type itemType = ANM2_NONE;
int itemID = ID_NONE;
int frameIndex = INDEX_NONE;
float time = VALUE_NONE;
auto operator<=>(const Anm2Reference&) const = default;
};
struct Animations
{
std::string defaultAnimation{};
std::vector<Animation> items{};
enum Anm2MergeType { ANM2_MERGE_APPEND, ANM2_MERGE_REPLACE, ANM2_MERGE_PREPEND, ANM2_MERGE_IGNORE };
Animations();
enum Anm2ChangeType { ANM2_CHANGE_ADD, ANM2_CHANGE_SUBTRACT, ANM2_CHANGE_SET };
Animations(tinyxml2::XMLElement* element);
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
int length();
int merge(int target, std::set<int>& sources, types::merge::Type type, bool isDeleteAfter = true);
};
enum OnionskinDrawOrder { ONIONSKIN_BELOW, ONIONSKIN_ABOVE };
class Anm2
{
bool isValid{false};
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_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 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);
int anm2_null_add(Anm2* self);
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference reference, bool isRootTransform);
void anm2_animation_layer_animation_add(Anm2Animation* animation, int id);
void anm2_animation_layer_animation_remove(Anm2Animation* animation, int id);
void anm2_animation_length_set(Anm2Animation* self);
void anm2_animation_merge(Anm2* self, 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_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);
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, int columns, int count, int delay);
void anm2_item_frame_set(Anm2* self, Anm2Reference reference, const Anm2FrameChange& change, Anm2ChangeType changeType, int start, int count);
void anm2_layer_remove(Anm2* self, int id);
void anm2_new(Anm2* self);
void anm2_null_remove(Anm2* self, int id);
void anm2_scale(Anm2* self, float scale);
void anm2_spritesheet_texture_pixels_download(Anm2* self);
void anm2_spritesheet_texture_pixels_upload(Anm2* self);
float anm2_time_from_reference(Anm2* self, Anm2Reference reference);
public:
Info info{};
Content content{};
Animations animations{};
Anm2();
bool serialize(const std::string& path, std::string* errorString = nullptr);
std::string to_string();
Anm2(const std::string& path, std::string* errorString = nullptr);
uint64_t hash();
Animation* animation_get(Reference& reference);
Item* item_get(Reference& reference);
Frame* frame_get(Reference& reference);
bool spritesheet_add(const std::string& directory, const std::string& path, int& id);
Spritesheet* spritesheet_get(int id);
void spritesheet_remove(int id);
std::set<int> spritesheets_unused();
void layer_add(int& id);
void null_add(int& id);
void event_add(int& id);
std::set<int> events_unused();
std::set<int> layers_unused();
std::set<int> nulls_unused();
};
}