From 3546f276557c5e14bc9d6fb9fc1ca6668457782c Mon Sep 17 00:00:00 2001 From: shweet Date: Wed, 27 Aug 2025 00:28:41 -0400 Subject: [PATCH] Moved spritesheet texture handling to anm2 instead of resources; added undoing for spritesheet texture changes; refactoring --- README.md | 2 +- src/COMMON.h | 55 +--------------- src/PACKED.h | 10 ++- src/anm2.cpp | 47 ++++++++++++-- src/anm2.h | 14 ++-- src/canvas.cpp | 82 ++++++++++++----------- src/canvas.h | 76 +++++++++++++--------- src/editor.cpp | 19 +++--- src/ffmpeg.cpp | 7 +- src/ffmpeg.h | 1 + src/generate_preview.cpp | 14 ++-- src/imgui.cpp | 122 +++++++++++++++++++---------------- src/imgui.h | 136 ++++++++++++++++++++------------------- src/main.cpp | 2 +- src/preview.cpp | 39 ++++++----- src/resources.cpp | 22 ------- src/resources.h | 3 - src/snapshots.cpp | 45 +++++++------ src/snapshots.h | 7 +- src/state.cpp | 18 +++--- src/texture.cpp | 38 ++--------- src/texture.h | 8 +-- 22 files changed, 376 insertions(+), 391 deletions(-) diff --git a/README.md b/README.md index 70d7256..83674d9 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Note, to render animations, you'll need to download [FFmpeg](https://ffmpeg.org/ After cloning and enter the repository's directory, make sure to initialize the submodules: -```git submodules update --init``` +```git submodule update --init``` Then: diff --git a/src/COMMON.h b/src/COMMON.h index b59741e..5fb7c10 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -56,6 +56,7 @@ using namespace glm; #define ID_NONE -1 #define INDEX_NONE -1 #define TIME_NONE -1.0f +#define GL_ID_NONE 0 #if defined(_WIN32) #define POPEN _popen @@ -69,29 +70,6 @@ using namespace glm; #define PREAD_MODE "r" #endif -#define UV_VERTICES(uvMin, uvMax) \ -{ \ - 0, 0, uvMin.x, uvMin.y, \ - 1, 0, uvMax.x, uvMin.y, \ - 1, 1, uvMax.x, uvMax.y, \ - 0, 1, uvMin.x, uvMax.y \ -} - -static const f32 GL_VERTICES[] = -{ - 0, 0, - 1, 0, - 1, 1, - 0, 1 -}; - -constexpr f32 GL_UV_VERTICES[] = -{ - 0, 0, 0.0f, 0.0f, - 1, 0, 1.0f, 0.0f, - 1, 1, 1.0f, 1.0f, - 0, 1, 0.0f, 1.0f -}; static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0}; static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f}; @@ -364,38 +342,7 @@ static inline void map_insert_shift(std::map& map, s32 index, const T& v map[insertIndex] = value; } -static inline mat4 quad_model_get(vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale) -{ - 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_parent_model_get(vec2 position, vec2 pivot, f32 rotation, vec2 scale) -{ - 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)); // mirror if needed - 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_ENUM_TO_STRING_FUNCTION(function, array, count) \ static inline std::string function(s32 index) \ diff --git a/src/PACKED.h b/src/PACKED.h index 54c4cad..43ffcc7 100644 --- a/src/PACKED.h +++ b/src/PACKED.h @@ -275,21 +275,19 @@ const std::string SHADER_GRID_FRAGMENT = R"( #version 330 core in vec2 clip; -uniform mat4 u_model; // inverse of your world->clip matrix (MVP) -uniform vec2 u_size; // world-space cell size (e.g. 64,64) -uniform vec2 u_offset; // world-space grid offset (shifts entire grid) -uniform vec4 u_color; // RGBA +uniform mat4 u_model; +uniform vec2 u_size; +uniform vec2 u_offset; +uniform vec4 u_color; out vec4 o_fragColor; void main() { - // clip -> world on z=0 plane vec4 w = u_model * vec4(clip, 0.0, 1.0); w /= w.w; vec2 world = w.xy; - // grid space vec2 g = (world - u_offset) / u_size; vec2 d = abs(fract(g) - 0.5); diff --git a/src/anm2.cpp b/src/anm2.cpp index c7f242d..edad3cc 100644 --- a/src/anm2.cpp +++ b/src/anm2.cpp @@ -303,7 +303,7 @@ bool anm2_serialize(Anm2* self, const std::string& path) return true; } -bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) +bool anm2_deserialize(Anm2* self, const std::string& path) { XMLDocument xmlDocument; XMLError xmlError; @@ -588,8 +588,8 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) xmlAttribute = xmlAttribute->Next(); } - if (anm2Element == ANM2_ELEMENT_SPRITESHEET && resources) - resources_texture_init(resources, spritesheet->path, id); + if (anm2Element == ANM2_ELEMENT_SPRITESHEET) + texture_from_path_init(&spritesheet->texture, spritesheet->path); xmlChild = xmlElement->FirstChildElement(); @@ -620,6 +620,10 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path) if (animation.name == defaultAnimation) self->defaultAnimationID = id; + // Copy texture data to pixels (used for snapshots) + anm2_spritesheet_texture_pixels_download(self); + + // Read log_info(std::format(ANM2_READ_INFO, path)); // Return to old working directory @@ -706,7 +710,6 @@ void anm2_null_remove(Anm2* self, s32 id) } } - s32 anm2_animation_add(Anm2* self) { s32 id = map_next_id_get(self->animations); @@ -1167,4 +1170,40 @@ void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPos anm2_frame_add(self, &frame, &frameReference); frameReference.frameIndex++; } +} + +void anm2_free(Anm2* self) +{ + for (auto& [id, spritesheet] : self->spritesheets) + texture_free(&spritesheet.texture); +} + +void anm2_spritesheet_texture_pixels_upload(Anm2* self) +{ + for (auto& [_, spritesheet] : self->spritesheets) + { + Texture& texture = spritesheet.texture; + + if (texture.id != GL_ID_NONE && !texture.isInvalid) + { + assert(!spritesheet.pixels.empty()); + glBindTexture(GL_TEXTURE_2D, texture.id); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture.size.x, texture.size.y, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data()); + } + } +} + +void anm2_spritesheet_texture_pixels_download(Anm2* self) +{ + for (auto& [_, spritesheet] : self->spritesheets) + { + Texture& texture = spritesheet.texture; + + if (texture.id != GL_ID_NONE && !texture.isInvalid) + { + spritesheet.pixels.resize(texture.size.x * texture.size.y * texture.channels); + glBindTexture(GL_TEXTURE_2D, texture.id); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data()); + } + } } \ No newline at end of file diff --git a/src/anm2.h b/src/anm2.h index aa82771..378c6d1 100644 --- a/src/anm2.h +++ b/src/anm2.h @@ -130,6 +130,8 @@ enum Anm2Type struct Anm2Spritesheet { std::string path{}; + Texture texture; + std::vector pixels; }; struct Anm2Layer @@ -162,8 +164,8 @@ struct Anm2Frame vec2 position{}; vec2 size{}; vec2 scale = {100, 100}; - vec3 offsetRGB{}; - vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f}; + vec3 offsetRGB = COLOR_OFFSET_NONE; + vec4 tintRGBA = COLOR_OPAQUE; }; struct Anm2FrameChange @@ -261,12 +263,12 @@ void anm2_layer_remove(Anm2* self, s32 id); void anm2_null_add(Anm2* self); void anm2_null_remove(Anm2* self, s32 id); bool anm2_serialize(Anm2* self, const std::string& path); -bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path); +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); -void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const std::string& path, s32 id); Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference); Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference); Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference); @@ -283,4 +285,6 @@ void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector& m 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_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay); \ No newline at end of file +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); \ No newline at end of file diff --git a/src/canvas.cpp b/src/canvas.cpp index 998cbf7..0ec8607 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1,32 +1,19 @@ #include "canvas.h" -static void _canvas_texture_free(Canvas* self) +static void _canvas_framebuffer_set(Canvas* self, const ivec2& size) { - if (self->fbo != 0) glDeleteFramebuffers(1, &self->fbo); - if (self->rbo != 0) glDeleteRenderbuffers(1, &self->rbo); - if (self->texture != 0) glDeleteTextures(1, &self->texture); -} - -static void _canvas_texture_init(Canvas* self, const ivec2& size) -{ - _canvas_texture_free(self); - self->size = size; self->previousSize = size; - glGenFramebuffers(1, &self->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); - glGenTextures(1, &self->texture); - glBindTexture(GL_TEXTURE_2D, self->texture); + glBindTexture(GL_TEXTURE_2D, self->framebuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->framebuffer, 0); - glGenRenderbuffers(1, &self->rbo); glBindRenderbuffer(GL_RENDERBUFFER, self->rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo); @@ -65,7 +52,7 @@ void canvas_init(Canvas* self, const ivec2& size) glBindVertexArray(self->rectVAO); glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_RECT_VERTICES), CANVAS_RECT_VERTICES, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0); @@ -104,7 +91,11 @@ void canvas_init(Canvas* self, const ivec2& size) glBindVertexArray(0); - _canvas_texture_init(self, size); + // Framebuffer + glGenTextures(1, &self->framebuffer); + glGenFramebuffers(1, &self->fbo); + glGenRenderbuffers(1, &self->rbo); + _canvas_framebuffer_set(self, size); } mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin) @@ -140,10 +131,10 @@ void canvas_viewport_set(Canvas* self) glViewport(0, 0, (s32)self->size.x, (s32)self->size.y); } -void canvas_texture_set(Canvas* self) +void canvas_framebuffer_resize_check(Canvas* self) { if (self->previousSize != self->size) - _canvas_texture_init(self, self->size); + _canvas_framebuffer_set(self, self->size); } void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color) @@ -171,7 +162,7 @@ void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& tr glBindVertexArray(self->textureVAO); glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(GL_UV_VERTICES), vertices, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_TEXTURE_VERTICES), vertices, GL_DYNAMIC_DRAW); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); @@ -227,30 +218,49 @@ void canvas_unbind(void) void canvas_free(Canvas* self) { - _canvas_texture_free(self); + glDeleteFramebuffers(1, &self->fbo); + glDeleteRenderbuffers(1, &self->rbo); + glDeleteTextures(1, &self->framebuffer); + glDeleteVertexArrays(1, &self->axisVAO); + glDeleteVertexArrays(1, &self->rectVAO); + glDeleteVertexArrays(1, &self->gridVAO); + glDeleteVertexArrays(1, &self->textureVAO); + glDeleteBuffers(1, &self->axisVBO); + glDeleteBuffers(1, &self->rectVBO); + glDeleteBuffers(1, &self->gridVBO); + glDeleteBuffers(1, &self->textureVBO); + glDeleteBuffers(1, &self->textureEBO); } -mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt) +mat4 canvas_model_get(vec2 size, vec2 position, vec2 pivot, vec2 scale, f32 rotation) { vec2 scaleAbsolute = glm::abs(scale); vec2 scaleSign = glm::sign(scale); - f32 usedSign = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f; + vec2 pivotScaled = pivot * scaleAbsolute; + vec2 sizeScaled = size * scaleAbsolute; - vec2 sizeScaled = size * scaleAbsolute; - vec2 pivotScaled = pivot * scaleAbsolute; - vec2 pivotAltScaled = pivotAlt * scaleAbsolute; - - vec2 pivotAltMirrored = pivotScaled + (pivotAltScaled - pivotScaled) * scaleSign; - - mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f)); + 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) * usedSign, vec3(0,0,1)); + model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1)); model = glm::translate(model, vec3(-pivotScaled, 0.0f)); - model = glm::translate(model, vec3(pivotAltMirrored, 0.0f)); - model = glm::rotate(model, glm::radians(rotationAlt) * usedSign, vec3(0,0,1)); - model = glm::translate(model, vec3(-pivotAltMirrored, 0.0f)); model = glm::scale(model, vec3(sizeScaled, 1.0f)); + return model; +} - return transform * 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; } \ No newline at end of file diff --git a/src/canvas.h b/src/canvas.h index 5221cd5..1b08609 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -12,10 +12,10 @@ #define CANVAS_GRID_DEFAULT 32 #define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f) -static const vec2 CANVAS_PIVOT_SIZE = {8, 8}; -static const vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f}; +const inline vec2 CANVAS_PIVOT_SIZE = {8, 8}; +const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f}; -const f32 CANVAS_AXIS_VERTICES[] = +const inline f32 CANVAS_AXIS_VERTICES[] = { -CANVAS_LINE_LENGTH, 0.0f, CANVAS_LINE_LENGTH, 0.0f, @@ -23,13 +23,29 @@ const f32 CANVAS_AXIS_VERTICES[] = 0.0f, CANVAS_LINE_LENGTH }; -const f32 CANVAS_GRID_VERTICES[] = +const inline f32 CANVAS_GRID_VERTICES[] = { -1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f }; +const inline f32 CANVAS_RECT_VERTICES[] = +{ + 0, 0, + 1, 0, + 1, 1, + 0, 1 +}; + +const inline f32 CANVAS_TEXTURE_VERTICES[] = +{ + 0, 0, 0.0f, 0.0f, + 1, 0, 1.0f, 0.0f, + 1, 1, 1.0f, 1.0f, + 0, 1, 0.0f, 1.0f +}; + struct Canvas { GLuint fbo{}; @@ -40,38 +56,36 @@ struct Canvas GLuint rectVBO{}; GLuint gridVAO{}; GLuint gridVBO{}; - GLuint texture{}; - GLuint textureEBO{}; + GLuint framebuffer{}; GLuint textureVAO{}; GLuint textureVBO{}; + GLuint textureEBO{}; ivec2 size{}; ivec2 previousSize{}; }; -void canvas_init(Canvas* self, const ivec2& size); -mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin); -void canvas_clear(vec4& color); -void canvas_bind(Canvas* self); -void canvas_viewport_set(Canvas* self); -void canvas_unbind(void); -void canvas_texture_set(Canvas* self); -void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color); -void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color); -void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color); -void canvas_free(Canvas* self); -void canvas_draw(Canvas* self); +#define UV_VERTICES(uvMin, uvMax) \ +{ \ + 0, 0, uvMin.x, uvMin.y, \ + 1, 0, uvMax.x, uvMin.y, \ + 1, 1, uvMax.x, uvMax.y, \ + 0, 1, uvMin.x, uvMax.y \ +} -mat4 canvas_mvp_get -( - mat4& transform, - vec2 size, - vec2 position = {0.0f, 0.0f}, - vec2 pivot = {0.0f, 0.0f}, - f32 rotation = {0.0f}, - vec2 scale = {1.0f, 1.0f}, - vec2 pivotAlt = {0.0f, 0.0f}, - f32 rotationAlt = {0.0f} -); +mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin); +void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color); +void canvas_bind(Canvas* self); +void canvas_clear(vec4& color); +void canvas_draw(Canvas* self); +void canvas_free(Canvas* self); +void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color); +void canvas_init(Canvas* self, const ivec2& size); +void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color); +void canvas_framebuffer_resize_check(Canvas* self); +void canvas_unbind(void); +void canvas_viewport_set(Canvas* self); +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 ( @@ -79,7 +93,7 @@ void canvas_texture_draw GLuint& shader, GLuint& texture, mat4& transform, - const f32* vertices = GL_UV_VERTICES, - vec4 tint = COLOR_OPAQUE, + const f32* vertices = CANVAS_TEXTURE_VERTICES, + vec4 tint = COLOR_OPAQUE, vec3 colorOffset = COLOR_OFFSET_NONE ); \ No newline at end of file diff --git a/src/editor.cpp b/src/editor.cpp index 8479c79..75f27cd 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -20,7 +20,7 @@ void editor_draw(Editor* self) GLuint& shaderGrid = self->resources->shaders[SHADER_GRID]; mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT); - canvas_texture_set(&self->canvas); + canvas_framebuffer_resize_check(&self->canvas); canvas_bind(&self->canvas); canvas_viewport_set(&self->canvas); @@ -28,23 +28,24 @@ void editor_draw(Editor* self) if (self->spritesheetID != ID_NONE) { - Texture texture = self->resources->textures[self->spritesheetID]; - mat4 mvp = canvas_mvp_get(transform, texture.size); - canvas_texture_draw(&self->canvas, shaderTexture, texture.id, mvp); + Texture& texture = self->anm2->spritesheets[self->spritesheetID].texture; + + mat4 spritesheetTransform = transform * canvas_model_get(texture.size); + canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform); if (self->settings->editorIsBorder) - canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_BORDER_COLOR); + canvas_rect_draw(&self->canvas, shaderLine, spritesheetTransform, EDITOR_BORDER_COLOR); Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference); if (frame) { - mvp = canvas_mvp_get(transform, frame->size, frame->crop); - canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_FRAME_COLOR); + mat4 cropTransform = transform * canvas_model_get(frame->size, frame->crop); + canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR); - mvp = canvas_mvp_get(transform, CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f); + mat4 pivotTransform = transform * canvas_model_get(CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f); f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT); - canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, mvp, vertices, EDITOR_PIVOT_COLOR); + canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, EDITOR_PIVOT_COLOR); } } diff --git a/src/ffmpeg.cpp b/src/ffmpeg.cpp index 97a6cac..fec3312 100644 --- a/src/ffmpeg.cpp +++ b/src/ffmpeg.cpp @@ -1,5 +1,10 @@ #include "ffmpeg.h" +static std::string ffmpeg_log_path_get(void) +{ + return preferences_path_get() + FFMPEG_LOG_PATH; +} + bool ffmpeg_render ( @@ -31,7 +36,7 @@ ffmpeg_render } // ffmpeg output will be piped into the log - std::string logOutput = " 2>> \"" + log_path_get() + "\""; + std::string logOutput = " 2>> \"" + ffmpeg_log_path_get() + "\""; #if _WIN32 command = string_quote(command) + logOutput; diff --git a/src/ffmpeg.h b/src/ffmpeg.h index f96e3c1..1a6190c 100644 --- a/src/ffmpeg.h +++ b/src/ffmpeg.h @@ -5,6 +5,7 @@ #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 " diff --git a/src/generate_preview.cpp b/src/generate_preview.cpp index 785c9a0..2e0d46f 100644 --- a/src/generate_preview.cpp +++ b/src/generate_preview.cpp @@ -26,23 +26,21 @@ void generate_preview_draw(GeneratePreview* self) canvas_clear(self->settings->previewBackgroundColor); Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference); - Texture* texture = map_find(self->resources->textures, self->anm2->layers[self->reference->itemID].spritesheetID); + Texture& texture = self->anm2->spritesheets[self->anm2->layers[self->reference->itemID].spritesheetID].texture; - if (item && texture && !texture->isInvalid) + if (item && !texture.isInvalid) { 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); - vec2 uvMin = crop / vec2(texture->size); - vec2 uvMax = (crop + size) / vec2(texture->size); + vec2 uvMin = crop / vec2(texture.size); + vec2 uvMax = (crop + size) / vec2(texture.size); f32 vertices[] = UV_VERTICES(uvMin, uvMax); - mat4 model = quad_model_get(size, {}, pivot, {}, CANVAS_SCALE_DEFAULT); - mat4 generateTransform = transform * model; - - canvas_texture_draw(&self->canvas, shaderTexture, texture->id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE); + mat4 generateTransform = transform * canvas_model_get(size, {}, pivot); + canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE); } canvas_unbind(); diff --git a/src/imgui.cpp b/src/imgui.cpp index 7930768..48e7df1 100644 --- a/src/imgui.cpp +++ b/src/imgui.cpp @@ -19,11 +19,11 @@ static bool _imgui_window_color_from_position_get(SDL_Window* self, const vec2& return true; } -static void _imgui_anm2_new(Imgui* self, const std::string& path) +static void _imgui_anm2_open(Imgui* self, const std::string& path) { - *self->reference = Anm2Reference{}; - resources_textures_free(self->resources); - if (anm2_deserialize(self->anm2, self->resources, path)) + imgui_file_new(self); + + if (anm2_deserialize(self->anm2, path)) { window_title_from_path_set(self->window, path); snapshots_reset(self->snapshots); @@ -36,13 +36,18 @@ static void _imgui_anm2_new(Imgui* self, const std::string& path) static void _imgui_spritesheet_add(Imgui* self, const std::string& path) { std::filesystem::path workingPath = std::filesystem::current_path(); - std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path); - std::string spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string(); + std::string spritesheetPath = path; - s32 id = map_next_id_get(self->resources->textures); + if (!self->anm2->path.empty()) + { + std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path); + spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string(); + } + + s32 id = map_next_id_get(self->anm2->spritesheets); self->anm2->spritesheets[id] = Anm2Spritesheet{}; self->anm2->spritesheets[id].path = spritesheetPath; - resources_texture_init(self->resources, spritesheetPath, id); + texture_from_path_init(&self->anm2->spritesheets[id].texture, spritesheetPath); std::filesystem::current_path(workingPath); } @@ -202,7 +207,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType if (isActivated) { if (self.is_undoable()) - imgui_undo_push(imgui, self.undoAction); + imgui_snapshot(imgui, self.snapshotAction); if (self.function) self.function(imgui); @@ -666,7 +671,7 @@ static void _imgui_timeline(Imgui* self) if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) { if (!isPlayheadDrag) - imgui_undo_push(self, IMGUI_ACTION_MOVE_PLAYHEAD); + imgui_snapshot(self, IMGUI_ACTION_MOVE_PLAYHEAD); isPlayheadDrag = true; } @@ -1018,10 +1023,10 @@ static void _imgui_timeline(Imgui* self) } if (type == ANM2_TRIGGERS) - imgui_undo_push(self, IMGUI_ACTION_TRIGGER_MOVE); + imgui_snapshot(self, IMGUI_ACTION_TRIGGER_MOVE); else if (isModCtrl) { - imgui_undo_push(self, IMGUI_ACTION_FRAME_DELAY); + imgui_snapshot(self, IMGUI_ACTION_FRAME_DELAY); frameDelayStart = draggingFrame->delay; frameDelayTimeStart = frameTime; } @@ -1067,7 +1072,7 @@ static void _imgui_timeline(Imgui* self) Anm2Reference swapReference = *(Anm2Reference*)payload->Data; if (swapReference != reference) { - imgui_undo_push(self, IMGUI_ACTION_FRAME_SWAP); + imgui_snapshot(self, IMGUI_ACTION_FRAME_SWAP); Anm2Frame* swapFrame = anm2_frame_from_reference(self->anm2, &reference); Anm2Frame* dragFrame = anm2_frame_from_reference(self->anm2, &swapReference); @@ -1274,7 +1279,7 @@ static void _imgui_taskbar(Imgui* self) if (self->dialog->isSelected && self->dialog->type == DIALOG_ANM2_OPEN) { - _imgui_anm2_new(self, self->dialog->path); + _imgui_anm2_open(self, self->dialog->path); dialog_reset(self->dialog); } @@ -1336,7 +1341,7 @@ static void _imgui_taskbar(Imgui* self) _imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD, self); generate_preview_draw(self->generatePreview); - ImGui::Image(self->generatePreview->canvas.texture, GENERATE_PREVIEW_SIZE); + ImGui::Image(self->generatePreview->canvas.framebuffer, GENERATE_PREVIEW_SIZE); _imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD, self); _imgui_slider_float(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER, self, time); @@ -1730,7 +1735,7 @@ static void _imgui_animations(Imgui* self) s32 sourceID = *(s32*)payload->Data; if (sourceID != id) { - imgui_undo_push(self, IMGUI_ACTION_ANIMATION_SWAP); + imgui_snapshot(self, IMGUI_ACTION_ANIMATION_SWAP); map_swap(self->anm2->animations, sourceID, id); } } @@ -1963,7 +1968,7 @@ static void _imgui_spritesheets(Imgui* self) { ImGui::PushID(id); - Texture* texture = &self->resources->textures[id]; + Texture& texture = spritesheet.texture; bool isContains = selectedIDs.contains(id); _imgui_begin_child(IMGUI_SPRITESHEET_CHILD, self); @@ -1992,26 +1997,23 @@ static void _imgui_spritesheets(Imgui* self) { s32 sourceID = *(s32*)payload->Data; if (sourceID != id) - { map_swap(self->anm2->spritesheets, sourceID, id); - map_swap(self->resources->textures, sourceID, id); - } } ImGui::EndDragDropTarget(); } ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE; - f32 spritesheetAspect = (f32)self->resources->textures[id].size.x / self->resources->textures[id].size.y; + f32 spritesheetAspect = (f32)texture.size.x / texture.size.y; if ((IMGUI_SPRITESHEET_PREVIEW_SIZE.x / IMGUI_SPRITESHEET_PREVIEW_SIZE.y) > spritesheetAspect) spritesheetPreviewSize.x = IMGUI_SPRITESHEET_PREVIEW_SIZE.y * spritesheetAspect; else spritesheetPreviewSize.y = IMGUI_SPRITESHEET_PREVIEW_SIZE.x / spritesheetAspect; - if (texture->isInvalid) + if (texture.isInvalid) _imgui_atlas(ATLAS_NONE, self); else - ImGui::Image(texture->id, spritesheetPreviewSize); + ImGui::Image(texture.id, spritesheetPreviewSize); _imgui_end_child(); // IMGUI_SPRITESHEET_CHILD @@ -2033,14 +2035,19 @@ static void _imgui_spritesheets(Imgui* self) _imgui_spritesheet_add(self, self->dialog->path); dialog_reset(self->dialog); } - + if (_imgui_button(IMGUI_SPRITESHEETS_RELOAD.copy({selectedIDs.empty()}), self)) { + if (selectedIDs.size() > 0) + imgui_snapshot(self, IMGUI_ACTION_RELOAD_SPRITESHEET); + for (auto& id : selectedIDs) { std::filesystem::path workingPath = std::filesystem::current_path(); working_directory_from_file_set(self->anm2->path); - resources_texture_init(self->resources, self->anm2->spritesheets[id].path, id); + Texture texture; + texture_from_path_init(&texture, self->anm2->spritesheets[id].path); + self->anm2->spritesheets[id].texture = texture; std::filesystem::current_path(workingPath); } } @@ -2050,14 +2057,17 @@ static void _imgui_spritesheets(Imgui* self) if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_REPLACE) { + imgui_snapshot(self, IMGUI_ACTION_REPLACE_SPRITESHEET); + std::filesystem::path workingPath = std::filesystem::current_path(); std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path); std::string spritesheetPath = std::filesystem::relative(self->dialog->path, anm2WorkingPath).string(); self->anm2->spritesheets[self->dialog->replaceID].path = spritesheetPath; - resources_texture_init(self->resources, spritesheetPath, self->dialog->replaceID); + Texture texture; + texture_from_path_init(&texture, spritesheetPath); + self->anm2->spritesheets[self->dialog->replaceID].texture = texture; dialog_reset(self->dialog); - std::filesystem::current_path(workingPath); } @@ -2073,8 +2083,8 @@ static void _imgui_spritesheets(Imgui* self) { if (!usedSpritesheetIDs.count(it->first)) { + texture_free(&self->anm2->spritesheets[it->first].texture); it = self->anm2->spritesheets.erase(it); - texture_free(&self->resources->textures[it->first]); } else it++; @@ -2092,12 +2102,11 @@ static void _imgui_spritesheets(Imgui* self) { for (auto& id : selectedIDs) { - Anm2Spritesheet* spritesheet = &self->anm2->spritesheets[id]; - Texture* texture = &self->resources->textures[id]; + Anm2Spritesheet& spritesheet = self->anm2->spritesheets[id]; std::filesystem::path workingPath = std::filesystem::current_path(); working_directory_from_file_set(self->anm2->path); - texture_from_gl_write(texture, spritesheet->path); - imgui_log_push(self, std::format(IMGUI_LOG_SPRITESHEET_SAVE_FORMAT, id, spritesheet->path)); + texture_from_gl_write(&spritesheet.texture, spritesheet.path); + imgui_log_push(self, std::format(IMGUI_LOG_SPRITESHEET_SAVE_FORMAT, id, spritesheet.path)); std::filesystem::current_path(workingPath); } } @@ -2116,7 +2125,6 @@ static void _imgui_animation_preview(Imgui* self) static ivec2& size = self->preview->canvas.size; static vec2 mousePos{}; static vec2 previewPos{}; - static ImVec2 previewScreenPos{}; std::string mousePositionString = std::format(IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y); @@ -2182,13 +2190,10 @@ static void _imgui_animation_preview(Imgui* self) _imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->previewIsBorder); _imgui_end_child(); // IMGUI_CANVAS_HELPER_CHILD - previewPos = vec2(ImGui::GetCursorPos()); - previewScreenPos = vec2(ImGui::GetCursorScreenPos()); - - vec2 imageSize = ImGui::GetContentRegionAvail(); - size = ivec2(imageSize); + ImVec2 previewCursorScreenPos = ImGui::GetCursorScreenPos(); + size = ivec2(vec2(ImGui::GetContentRegionAvail())); preview_draw(self->preview); - ImGui::Image(self->preview->canvas.texture, imageSize); + ImGui::Image(self->preview->canvas.framebuffer, vec2(size)); if (self->settings->previewIsTriggers) { @@ -2198,7 +2203,7 @@ static void _imgui_animation_preview(Imgui* self) if (trigger.eventID != ID_NONE) { f32 textScale = ImGui::GetCurrentWindow()->FontWindowScale; - ImVec2 textPos = previewScreenPos + ImGui::GetStyle().ItemSpacing; + ImVec2 textPos = previewCursorScreenPos + ImGui::GetStyle().ItemSpacing; ImGui::SetWindowFontScale(IMGUI_TRIGGERS_FONT_SCALE); ImGui::GetWindowDrawList()->AddText(textPos, IMGUI_TRIGGERS_EVENT_COLOR, self->anm2->events[trigger.eventID].name.c_str()); ImGui::SetWindowFontScale(textScale); @@ -2215,7 +2220,7 @@ static void _imgui_animation_preview(Imgui* self) _imgui_end(); // IMGUI_ANIMATION_PREVIEW - mousePos = (vec2((ImGui::GetMousePos()) - (ImGui::GetWindowPos() + previewPos)) - (imageSize * 0.5f) - pan) / PERCENT_TO_UNIT(zoom); + mousePos = vec2(ImGui::GetMousePos() - previewCursorScreenPos - pan - (vec2(size) * 0.5f)) / PERCENT_TO_UNIT(zoom); const bool isLeft = ImGui::IsKeyPressed(IMGUI_INPUT_LEFT); const bool isRight = ImGui::IsKeyPressed(IMGUI_INPUT_RIGHT); @@ -2232,7 +2237,7 @@ static void _imgui_animation_preview(Imgui* self) if (tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE) if (isMouseClick || isLeft || isRight || isUp || isDown) - imgui_undo_push(self, IMGUI_ACTION_FRAME_TRANSFORM); + imgui_snapshot(self, IMGUI_ACTION_FRAME_TRANSFORM); if ((tool == TOOL_PAN && isMouseDown) || isMouseMiddleDown) pan += vec2(mouseDelta.x, mouseDelta.y); @@ -2332,11 +2337,10 @@ static void _imgui_spritesheet_editor(Imgui* self) _imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->editorIsBorder); _imgui_end_child(); // IMGUI_CANVAS_VISUAL_CHILD - ImVec2 editorPos = ImGui::GetCursorPos(); - vec2 imageSize = ImGui::GetContentRegionAvail(); - size = ivec2(imageSize); + ImVec2 editorCursorScreenPos = ImGui::GetCursorScreenPos(); + size = ivec2(vec2(ImGui::GetContentRegionAvail())); editor_draw(self->editor); - ImGui::Image(self->editor->canvas.texture, imageSize); + ImGui::Image(self->editor->canvas.framebuffer, vec2(size)); if (ImGui::IsItemHovered()) self->pendingCursor = TOOL_CURSORS[tool]; @@ -2348,7 +2352,7 @@ static void _imgui_spritesheet_editor(Imgui* self) _imgui_end(); // IMGUI_SPRITESHEET_EDITOR - mousePos = (vec2((ImGui::GetMousePos()) - (ImGui::GetWindowPos() + editorPos)) - pan) / PERCENT_TO_UNIT(zoom); + mousePos = vec2(ImGui::GetMousePos() - editorCursorScreenPos - pan) / PERCENT_TO_UNIT(zoom); const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left); const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); @@ -2366,7 +2370,8 @@ static void _imgui_spritesheet_editor(Imgui* self) if (self->reference->itemType == ANM2_LAYER) frame = anm2_frame_from_reference(self->anm2, self->reference); - Texture* texture = map_find(self->resources->textures, self->editor->spritesheetID); + Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->editor->spritesheetID); + Texture* texture = spritesheet ? &spritesheet->texture : nullptr; vec2 position = mousePos; @@ -2377,16 +2382,13 @@ static void _imgui_spritesheet_editor(Imgui* self) if (self->settings->editorIsGridSnap) { - position = - { - (s32)((position.x - gridOffset.x) / gridSize.x) * gridSize.x + gridOffset.x, - (s32)((position.y - gridOffset.y) / gridSize.y) * gridSize.y + gridOffset.y - }; + position.x = roundf(position.x / gridSize.x) * gridSize.x + gridOffset.x - (gridSize.x * 0.5f); + position.y = roundf(position.y / gridSize.y) * gridSize.y + gridOffset.y - (gridSize.y * 0.5f); } if (isMouseClick) { - imgui_undo_push(self, IMGUI_ACTION_FRAME_CROP); + imgui_snapshot(self, IMGUI_ACTION_FRAME_CROP); frame->crop = position; frame->size = ivec2(0,0); } @@ -2396,9 +2398,13 @@ static void _imgui_spritesheet_editor(Imgui* self) case TOOL_DRAW: case TOOL_ERASE: { - if (!frame || !texture) break; + if (!texture) break; + vec4 color = tool == TOOL_ERASE ? COLOR_TRANSPARENT : toolColor; + if (isMouseClick) + imgui_snapshot(self, IMGUI_ACTION_DRAW); + if (isMouseDown) texture_pixel_set(texture, position, color); break; @@ -2606,7 +2612,7 @@ void imgui_update(Imgui* self) { if (ImGui::IsKeyChordPressed(hotkey.chord)) { - if (hotkey.is_undoable()) imgui_undo_push(self, hotkey.undoAction); + if (hotkey.is_undoable()) imgui_snapshot(self, hotkey.snapshotAction); if (hotkey.is_focus_window()) continue; hotkey.function(self); } @@ -2634,9 +2640,11 @@ void imgui_update(Imgui* self) const char* droppedFile = event.drop.data; if (path_is_extension(droppedFile, ANM2_EXTENSION)) - _imgui_anm2_new(self, droppedFile); + _imgui_anm2_open(self, droppedFile); else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION)) _imgui_spritesheet_add(self, droppedFile); + else + imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR); break; } diff --git a/src/imgui.h b/src/imgui.h index 840697d..a5b30d5 100644 --- a/src/imgui.h +++ b/src/imgui.h @@ -68,12 +68,16 @@ #define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame" #define IMGUI_ACTION_FRAME_DELAY "Frame Delay" #define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead" +#define IMGUI_ACTION_DRAW "Draw" +#define IMGUI_ACTION_RELOAD_SPRITESHEET "Reload Spritesheet(s)" +#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet" #define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove #define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize #define IMGUI_LOG_FILE_OPEN_FORMAT "Opened anm2: {}" #define IMGUI_LOG_FILE_SAVE_FORMAT "Saved anm2 to: {}" +#define IMGUI_LOG_SPRITESHEET_RELOAD "Reloaded selected spritesheets" #define IMGUI_LOG_RENDER_ANIMATION_FRAMES_SAVE_FORMAT "Saved rendered frames to: {}" #define IMGUI_LOG_RENDER_ANIMATION_SAVE_FORMAT "Saved rendered animation to: {}" #define IMGUI_LOG_RENDER_ANIMATION_NO_ANIMATION_ERROR "No animation selected; rendering cancelled." @@ -83,6 +87,7 @@ #define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_PATH_ERROR "Invalid FFmpeg path! Make sure you have it installed and the path is correct." #define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_ERROR "FFmpeg could not render animation! Check paths or your FFmpeg installation." #define IMGUI_LOG_SPRITESHEET_SAVE_FORMAT "Saved spritesheet #{} to: {}" +#define IMGUI_LOG_DRAG_DROP_ERROR "Invalid file for dragging/dropping!" #define IMGUI_NONE "None" #define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}" @@ -206,10 +211,10 @@ struct ImguiHotkey ImGuiKeyChord chord; ImguiFunction function; std::string focusWindow{}; - std::string undoAction{}; + std::string snapshotAction{}; bool is_focus_window() const { return !focusWindow.empty(); } - bool is_undoable() const { return !undoAction.empty(); } + bool is_undoable() const { return !snapshotAction.empty(); } }; static void imgui_log_push(Imgui* self, const std::string& text) @@ -227,8 +232,8 @@ static std::vector& imgui_hotkey_registry() static inline void imgui_file_new(Imgui* self) { anm2_reference_clear(self->reference); + anm2_free(self->anm2); anm2_new(self->anm2); - resources_textures_free(self->resources); } static inline void imgui_file_open(Imgui* self) @@ -267,9 +272,9 @@ static inline void imgui_explore(Imgui* self) dialog_explorer_open(parentPath.string()); } -static inline void imgui_undo_push(Imgui* self, const std::string& action = SNAPSHOT_ACTION) +static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION) { - Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, action}; + Snapshot snapshot = snapshot_get(self->snapshots); snapshots_undo_push(self->snapshots, &snapshot); } @@ -476,7 +481,7 @@ struct ImguiItem std::string label{}; std::string tooltip{}; std::string& text = tooltip; - std::string undoAction{}; + std::string snapshotAction{}; std::string popup{}; std::string dragDrop{}; std::string focusWindow{}; @@ -524,7 +529,7 @@ struct ImguiItem label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString); tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString); if (function) - imgui_hotkey_registry().push_back({chord, function, focusWindow, undoAction}); + imgui_hotkey_registry().push_back({chord, function, focusWindow, snapshotAction}); } std::string labelNew{}; @@ -578,7 +583,7 @@ struct ImguiItem bool is_size() const { return size != ImVec2(); } bool is_popup_size() const { return popupSize != ImVec2(); } bool is_tooltip() const { return !tooltip.empty(); } - bool is_undoable() const { return !undoAction.empty(); } + bool is_undoable() const { return !snapshotAction.empty(); } bool is_mnemonic() const { return mnemonicKey != ImGuiKey_None; } bool is_range() const { return min != 0 || max != 0; } const char* label_get() const { return label.c_str(); } @@ -792,7 +797,7 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER, IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE, self.label = "Generate", self.tooltip = "Generate an animation with the used settings.", - self.undoAction = "Generate Animation from Grid", + self.snapshotAction = "Generate Animation from Grid", self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.isSameLine = true ); @@ -845,7 +850,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_NUMBER_FRAMES, IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD, self.label = "Add", self.tooltip = "The specified values will be added to all specified frames.", - self.undoAction = "Add Frame Properties", + self.snapshotAction = "Add Frame Properties", self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -853,7 +858,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD, IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT, self.label = "Subtract", self.tooltip = "The specified values will be added to all selected frames.", - self.undoAction = "Subtract Frame Properties", + self.snapshotAction = "Subtract Frame Properties", self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -861,7 +866,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT, IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SET, self.label = "Set", self.tooltip = "The specified values will be set to the specified value in selected frames.", - self.undoAction = "Set Frame Properties", + self.snapshotAction = "Set Frame Properties", self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -899,7 +904,7 @@ IMGUI_ITEM(IMGUI_SCALE_ANM2_VALUE, IMGUI_ITEM(IMGUI_SCALE_ANM2_SCALE, self.label = "Scale", self.tooltip = "Scale the anm2 with the value specified.", - self.undoAction = "Scale Anm2", + self.snapshotAction = "Scale Anm2", self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.isSameLine = true ); @@ -926,7 +931,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE, IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION, self.label = "Location", self.tooltip = "Select the location of the rendered animation.", - self.max = 255 + self.max = 1024 ); IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE, @@ -939,7 +944,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE, IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_PATH, self.label = "FFmpeg Path", self.tooltip = "Sets the path FFmpeg currently resides in.\nFFmpeg is required for rendering animations.\nDownload it from https://ffmpeg.org/, your package manager, or wherever else.", - self.max = 255 + self.max = 1024 ); IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT, @@ -1016,7 +1021,7 @@ IMGUI_ITEM(IMGUI_ANIMATIONS_CHILD, self.label = "## Animations Child", self.flag IMGUI_ITEM(IMGUI_ANIMATION, self.label = "## Animation Item", - self.undoAction = "Select Animation", + self.snapshotAction = "Select Animation", self.dragDrop = "## Animation Drag Drop", self.atlas = ATLAS_ANIMATION, self.idOffset = 2000 @@ -1026,7 +1031,7 @@ IMGUI_ITEM(IMGUI_ANIMATION, IMGUI_ITEM(IMGUI_ANIMATION_ADD, self.label = "Add", self.tooltip = "Adds a new animation.", - self.undoAction = "Add Animation", + self.snapshotAction = "Add Animation", self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1034,7 +1039,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_ADD, IMGUI_ITEM(IMGUI_ANIMATION_DUPLICATE, self.label = "Duplicate", self.tooltip = "Duplicates the selected animation, placing it after.", - self.undoAction = "Duplicate Animation", + self.snapshotAction = "Duplicate Animation", self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1102,7 +1107,7 @@ IMGUI_ITEM(IMGUI_MERGE_DELETE_ANIMATIONS_AFTER, IMGUI_ITEM(IMGUI_MERGE_CONFIRM, self.label = "Merge", self.tooltip = "Merge the selected animations with the options set.", - self.undoAction = "Merge Animations", + self.snapshotAction = "Merge Animations", self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.isSameLine = true ); @@ -1110,7 +1115,7 @@ IMGUI_ITEM(IMGUI_MERGE_CONFIRM, IMGUI_ITEM(IMGUI_ANIMATION_REMOVE, self.label = "Remove", self.tooltip = "Remove the selected animation.", - self.undoAction = "Remove Animation", + self.snapshotAction = "Remove Animation", self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.chord = ImGuiKey_Delete, self.focusWindow = IMGUI_ANIMATIONS.label, @@ -1120,7 +1125,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_REMOVE, IMGUI_ITEM(IMGUI_ANIMATION_DEFAULT, self.label = "Default", self.tooltip = "Set the selected animation as the default one.", - self.undoAction = "Default Animation", + self.snapshotAction = "Default Animation", self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1143,7 +1148,7 @@ IMGUI_ITEM(IMGUI_EVENT, IMGUI_ITEM(IMGUI_EVENTS_ADD, self.label = "Add", self.tooltip = "Adds a new event.", - self.undoAction = "Add Event", + self.snapshotAction = "Add Event", self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1151,7 +1156,7 @@ IMGUI_ITEM(IMGUI_EVENTS_ADD, IMGUI_ITEM(IMGUI_EVENTS_REMOVE_UNUSED, self.label = "Remove Unused", self.tooltip = "Removes all unused events (i.e., not being used in any triggers in any animation).", - self.undoAction = "Remove Unused Events", + self.snapshotAction = "Remove Unused Events", self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT ); @@ -1197,6 +1202,7 @@ IMGUI_ITEM(IMGUI_SPRITESHEET_ADD, IMGUI_ITEM(IMGUI_SPRITESHEETS_RELOAD, self.label = "Reload", self.tooltip = "Reload the selected spritesheet.", + self.snapshotAction = "Reload Spritesheet", self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT, self.isSameLine = true ); @@ -1373,35 +1379,35 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES, self.label = "Frame Properties"); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_POSITION, self.label = "Position", self.tooltip = "Change the position of the selected frame.", - self.undoAction = "Frame Position", + self.snapshotAction = "Frame Position", self.isUseItemActivated = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_CROP, self.label = "Crop", self.tooltip = "Change the crop position of the selected frame.", - self.undoAction = "Frame Crop", + self.snapshotAction = "Frame Crop", self.isUseItemActivated = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SIZE, self.label = "Size", self.tooltip = "Change the size of the crop of the selected frame.", - self.undoAction = "Frame Size", + self.snapshotAction = "Frame Size", self.isUseItemActivated = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_PIVOT, self.label = "Pivot", self.tooltip = "Change the pivot of the selected frame.", - self.undoAction = "Frame Pivot", + self.snapshotAction = "Frame Pivot", self.isUseItemActivated = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE, self.label = "Scale", self.tooltip = "Change the scale of the selected frame.", - self.undoAction = "Frame Scale", + self.snapshotAction = "Frame Scale", self.isUseItemActivated = true, self.value = 100 ); @@ -1409,14 +1415,14 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE, IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_ROTATION, self.label = "Rotation", self.tooltip = "Change the rotation of the selected frame.", - self.undoAction = "Frame Rotation", + self.snapshotAction = "Frame Rotation", self.isUseItemActivated = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_DELAY, self.label = "Duration", self.tooltip = "Change the duration of the selected frame.", - self.undoAction = "Frame Duration", + self.snapshotAction = "Frame Duration", self.isUseItemActivated = true, self.min = ANM2_FRAME_NUM_MIN, self.max = ANM2_FRAME_NUM_MAX, @@ -1426,7 +1432,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_DELAY, IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_TINT, self.label = "Tint", self.tooltip = "Change the tint of the selected frame.", - self.undoAction = "Frame Tint", + self.snapshotAction = "Frame Tint", self.isUseItemActivated = true, self.value = 1 ); @@ -1434,7 +1440,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_TINT, IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_COLOR_OFFSET, self.label = "Color Offset", self.tooltip = "Change the color offset of the selected frame.", - self.undoAction = "Frame Color Offset", + self.snapshotAction = "Frame Color Offset", self.isUseItemActivated = true, self.value = 0 ); @@ -1443,7 +1449,7 @@ const ImVec2 IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE = {75, 0}; IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_X, self.label = "Flip X", self.tooltip = "Change the sign of the X scale, to cheat flipping the layer horizontally.\n(Anm2 doesn't support flipping directly.)", - self.undoAction = "Frame Flip X", + self.snapshotAction = "Frame Flip X", self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE, self.isSameLine = true ); @@ -1451,14 +1457,14 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_X, IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_Y, self.label = "Flip Y", self.tooltip = "Change the sign of the Y scale, to cheat flipping the layer vertically.\n(Anm2 doesn't support flipping directly.)", - self.undoAction = "Frame Flip Y", + self.snapshotAction = "Frame Flip Y", self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_VISIBLE, self.label = "Visible", self.tooltip = "Toggles the visibility of the selected frame.", - self.undoAction = "Frame Visibility", + self.snapshotAction = "Frame Visibility", self.isSameLine = true, self.value = true ); @@ -1466,20 +1472,20 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_VISIBLE, IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_INTERPOLATED, self.label = "Interpolation", self.tooltip = "Toggles the interpolation of the selected frame.", - self.undoAction = "Frame Interpolation", + self.snapshotAction = "Frame Interpolation", self.value = true ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_EVENT, self.label = "Event", self.tooltip = "Change the event the trigger uses.", - self.undoAction = "Trigger Event" + self.snapshotAction = "Trigger Event" ); IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_AT_FRAME, self.label = "At Frame", self.tooltip = "Change the frame where the trigger occurs.", - self.undoAction = "Trigger At Frame" + self.snapshotAction = "Trigger At Frame" ); IMGUI_ITEM(IMGUI_TOOLS, self.label = "Tools"); @@ -1674,7 +1680,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE, self.label = "Root", self.tooltip = "The root item of an animation.\nChanging its properties will transform the rest of the animation.", - self.undoAction = "Root Item Select", + self.snapshotAction = "Root Item Select", self.atlas = ATLAS_ROOT, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE ); @@ -1682,7 +1688,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE, self.label = "## Layer Selectable", self.tooltip = "A layer item.\nA graphical item within the animation.", - self.undoAction = "Layer Item Select", + self.snapshotAction = "Layer Item Select", self.dragDrop = "## Layer Drag Drop", self.atlas = ATLAS_LAYER, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE @@ -1691,7 +1697,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE, self.label = "## Null Selectable", self.tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.", - self.undoAction = "Null Item Select", + self.snapshotAction = "Null Item Select", self.dragDrop = "## Null Drag Drop", self.atlas = ATLAS_NULL, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE @@ -1700,7 +1706,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE, self.label = "Triggers", self.tooltip = "The animation's triggers.\nWill fire based on an event.", - self.undoAction = "Triggers Item Select", + self.snapshotAction = "Triggers Item Select", self.atlas = ATLAS_TRIGGERS, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE ); @@ -1717,28 +1723,28 @@ const inline ImguiItem* IMGUI_TIMELINE_ITEM_SELECTABLES[ANM2_COUNT] IMGUI_ITEM(IMGUI_TIMELINE_ITEM_VISIBLE, self.label = "## Visible", self.tooltip = "The item is visible.\nPress to set to invisible.", - self.undoAction = "Item Invisible", + self.snapshotAction = "Item Invisible", self.atlas = ATLAS_VISIBLE ); IMGUI_ITEM(IMGUI_TIMELINE_ITEM_INVISIBLE, self.label = "## Invisible", self.tooltip = "The item is invisible.\nPress to set to visible.", - self.undoAction = "Item Visible", + self.snapshotAction = "Item Visible", self.atlas = ATLAS_INVISIBLE ); IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SHOW_RECT, self.label = "## Show Rect", self.tooltip = "The rect is shown.\nPress to hide rect.", - self.undoAction = "Hide Rect", + self.snapshotAction = "Hide Rect", self.atlas = ATLAS_SHOW_RECT ); IMGUI_ITEM(IMGUI_TIMELINE_ITEM_HIDE_RECT, self.label = "## Hide Rect", self.tooltip = "The rect is hidden.\nPress to show rect.", - self.undoAction = "Show Rect", + self.snapshotAction = "Show Rect", self.atlas = ATLAS_HIDE_RECT ); @@ -1765,7 +1771,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_FRAME, self.label = "## Frame"); static const vec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.25f}; IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME, self.label = "## Root Frame", - self.undoAction = "Root Frame Select", + self.snapshotAction = "Root Frame Select", self.color = {{0.14f, 0.27f, 0.39f, 1.0f}, {0.28f, 0.54f, 0.78f, 1.0f}, {0.36f, 0.70f, 0.95f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET, @@ -1774,7 +1780,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME, self.label = "## Layer Frame", - self.undoAction = "Layer Frame Select", + self.snapshotAction = "Layer Frame Select", self.dragDrop = "## Layer Frame Drag Drop", self.color = {{0.45f, 0.18f, 0.07f, 1.0f}, {0.78f, 0.32f, 0.12f, 1.0f}, {0.95f, 0.40f, 0.15f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, @@ -1784,7 +1790,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME, self.label = "## Null Frame", - self.undoAction = "Null Frame Select", + self.snapshotAction = "Null Frame Select", self.dragDrop = "## Null Frame Drag Drop", self.color = {{0.17f, 0.33f, 0.17f, 1.0f}, {0.34f, 0.68f, 0.34f, 1.0f}, {0.44f, 0.88f, 0.44f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, @@ -1794,7 +1800,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_TRIGGERS_FRAME, self.label = "## Triggers Frame", - self.undoAction = "Trigger Select", + self.snapshotAction = "Trigger Select", self.color = {{0.36f, 0.14f, 0.24f, 1.0f}, {0.72f, 0.28f, 0.48f, 1.0f}, {0.92f, 0.36f, 0.60f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET, @@ -1834,21 +1840,21 @@ IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM, IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_LAYER, self.label = "Layer", self.tooltip = "Adds a layer item.\nA layer item is a primary graphical item, using a spritesheet.", - self.undoAction = "Add Layer", + self.snapshotAction = "Add Layer", self.isSizeToText = true ); IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_NULL, self.label = "Null", self.tooltip = "Adds a null item.\nA null item is an invisible item, often accessed by the game engine.", - self.undoAction = "Add Null", + self.snapshotAction = "Add Null", self.isSizeToText = true ); IMGUI_ITEM(IMGUI_TIMELINE_REMOVE_ITEM, self.label = "Remove", self.tooltip = "Removes the selected item (layer or null) from the animation.", - self.undoAction = "Remove Item", + self.snapshotAction = "Remove Item", self.focusWindow = IMGUI_TIMELINE.label, self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT ); @@ -1875,7 +1881,7 @@ IMGUI_ITEM(IMGUI_PAUSE, IMGUI_ITEM(IMGUI_ADD_FRAME, self.label = "+ Insert Frame", self.tooltip = "Inserts a frame in the selected animation item, based on the preview time.", - self.undoAction = "Insert Frame", + self.snapshotAction = "Insert Frame", self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1883,7 +1889,7 @@ IMGUI_ITEM(IMGUI_ADD_FRAME, IMGUI_ITEM(IMGUI_REMOVE_FRAME, self.label = "- Delete Frame", self.tooltip = "Removes the selected frame from the selected animation item.", - self.undoAction = "Delete Frame", + self.snapshotAction = "Delete Frame", self.focusWindow = IMGUI_TIMELINE.label, self.chord = ImGuiKey_Delete, self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, @@ -1927,7 +1933,7 @@ IMGUI_ITEM(IMGUI_BAKE_ROUND_ROTATION, IMGUI_ITEM(IMGUI_BAKE_CONFIRM, self.label = "Bake", self.tooltip = "Bake the selected frame with the options selected.", - self.undoAction = "Bake Frames", + self.snapshotAction = "Bake Frames", self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.isSameLine = true ); @@ -1935,7 +1941,7 @@ IMGUI_ITEM(IMGUI_BAKE_CONFIRM, IMGUI_ITEM(IMGUI_FIT_ANIMATION_LENGTH, self.label = "Fit Animation Length", self.tooltip = "Sets the animation's length to the latest frame.", - self.undoAction = "Fit Animation Length", + self.snapshotAction = "Fit Animation Length", self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, self.isSameLine = true ); @@ -1943,7 +1949,7 @@ IMGUI_ITEM(IMGUI_FIT_ANIMATION_LENGTH, IMGUI_ITEM(IMGUI_ANIMATION_LENGTH, self.label = "Length", self.tooltip = "Sets the animation length.\n(Will not change frames.)", - self.undoAction = "Set Animation Length", + self.snapshotAction = "Set Animation Length", self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, self.min = ANM2_FRAME_NUM_MIN, self.max = ANM2_FRAME_NUM_MAX, @@ -1954,7 +1960,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_LENGTH, IMGUI_ITEM(IMGUI_FPS, self.label = "FPS", self.tooltip = "Sets the animation's frames per second (its speed).", - self.undoAction = "Set FPS", + self.snapshotAction = "Set FPS", self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, self.min = ANM2_FPS_MIN, self.max = ANM2_FPS_MAX, @@ -1965,7 +1971,7 @@ IMGUI_ITEM(IMGUI_FPS, IMGUI_ITEM(IMGUI_LOOP, self.label = "Loop", self.tooltip = "Toggles the animation looping.", - self.undoAction = "Set Loop", + self.snapshotAction = "Set Loop", self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT, self.value = true, self.isSameLine = true @@ -1983,7 +1989,7 @@ 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.undoAction = "Cut", + self.snapshotAction = "Cut", self.function = imgui_cut, self.chord = ImGuiMod_Ctrl | ImGuiKey_X, self.isSizeToText = true @@ -1992,7 +1998,7 @@ IMGUI_ITEM(IMGUI_CUT, IMGUI_ITEM(IMGUI_COPY, self.label = "Copy", self.tooltip = "Copies the currently selected contextual element to the clipboard.", - self.undoAction = "Copy", + self.snapshotAction = "Copy", self.function = imgui_copy, self.chord = ImGuiMod_Ctrl | ImGuiKey_C, self.isSizeToText = true @@ -2001,7 +2007,7 @@ IMGUI_ITEM(IMGUI_COPY, IMGUI_ITEM(IMGUI_PASTE, self.label = "Paste", self.tooltip = "Pastes the currently selection contextual element from the clipboard.", - self.undoAction = "Paste", + self.snapshotAction = "Paste", self.function = imgui_paste, self.chord = ImGuiMod_Ctrl | ImGuiKey_V, self.isSizeToText = true @@ -2010,7 +2016,7 @@ IMGUI_ITEM(IMGUI_PASTE, IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT, self.label = "## Input Text", self.tooltip = "Rename the selected item.", - self.undoAction = "Rename Item", + self.snapshotAction = "Rename Item", self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue, self.max = 255 ); @@ -2018,7 +2024,7 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT, IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT, self.label = "## Input Int", self.tooltip = "Change the selected item's value.", - self.undoAction = "Change Value", + self.snapshotAction = "Change Value", self.step = 0 ); diff --git a/src/main.cpp b/src/main.cpp index 124247a..8a531cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,7 @@ static bool _anm2_rescale(const std::string& file, f32 scale) { Anm2 anm2; - if (!anm2_deserialize(&anm2, nullptr, file)) return false; + if (!anm2_deserialize(&anm2, file)) return false; anm2_scale(&anm2, scale); return anm2_serialize(&anm2, file); } diff --git a/src/preview.cpp b/src/preview.cpp index 086177e..873b11c 100644 --- a/src/preview.cpp +++ b/src/preview.cpp @@ -85,7 +85,7 @@ void preview_draw(Preview* self) GLuint& shaderGrid = self->resources->shaders[SHADER_GRID]; mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER); - canvas_texture_set(&self->canvas); + canvas_framebuffer_resize_check(&self->canvas); canvas_bind(&self->canvas); canvas_viewport_set(&self->canvas); @@ -108,12 +108,12 @@ void preview_draw(Preview* self) anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time); if (self->settings->previewIsRootTransform) - rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale)); + rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation); // Root if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible) { - mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale)); + mat4 model = canvas_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation); mat4 rootTransform = transform * model; f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET); canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR); @@ -133,18 +133,18 @@ void preview_draw(Preview* self) if (!frame.isVisible) continue; - mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale)); + mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation); mat4 layerTransform = transform * (rootModel * model); - Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID); + Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture; - if (texture && !texture->isInvalid) + if (!texture.isInvalid) { - vec2 uvMin = frame.crop / vec2(texture->size); - vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size); + vec2 uvMin = frame.crop / vec2(texture.size); + vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size); f32 vertices[] = UV_VERTICES(uvMin, uvMax); - canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB); + canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB); } if (self->settings->previewIsBorder) @@ -153,7 +153,7 @@ void preview_draw(Preview* self) if (self->settings->previewIsPivots) { f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT); - mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale)); + mat4 pivotModel = canvas_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation); mat4 pivotTransform = transform * (rootModel * pivotModel); canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR); } @@ -182,7 +182,7 @@ void preview_draw(Preview* self) vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE; AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : ATLAS_TARGET; - mat4 model = quad_model_get(size, frame.position, size * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale)); + mat4 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); @@ -191,7 +191,7 @@ void preview_draw(Preview* self) if (null.isShowRect) { - mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale)); + mat4 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); } @@ -210,7 +210,7 @@ void preview_draw(Preview* self) anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time); if (self->settings->previewIsRootTransform) - rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale)); + rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale)); for (auto [i, id] : self->anm2->layerMap) { @@ -225,22 +225,21 @@ void preview_draw(Preview* self) if (!frame.isVisible) continue; - Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID); + Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture; - if (!texture || texture->isInvalid) - continue; + if (texture.isInvalid) continue; - vec2 uvMin = frame.crop / vec2(texture->size); - vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size); + vec2 uvMin = frame.crop / vec2(texture.size); + vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size); f32 vertices[] = UV_VERTICES(uvMin, uvMax); - mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale)); + mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation); mat4 layerTransform = transform * (rootModel * model); vec4 tint = frame.tintRGBA; tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency); - canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, tint, frame.offsetRGB); + canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, tint, frame.offsetRGB); } } diff --git a/src/resources.cpp b/src/resources.cpp index 4174111..5e57912 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -1,17 +1,5 @@ #include "resources.h" -void resources_texture_init(Resources* self, const std::string& path, s32 id) -{ - Texture texture; - - if (map_find(self->textures, id)) - texture_free(&self->textures[id]); - - texture_from_path_init(&texture, path); - - self->textures[id] = texture; -} - void resources_init(Resources* self) { texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, TEXTURE_CHANNELS, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH); @@ -22,18 +10,8 @@ void resources_init(Resources* self) void resources_free(Resources* self) { - resources_textures_free(self); - for (auto& shader : self->shaders) shader_free(&shader); texture_free(&self->atlas); -} - -void resources_textures_free(Resources* self) -{ - for (auto& [id, texture] : self->textures) - texture_free(&self->textures[id]); - - log_info(RESOURCES_TEXTURES_FREE_INFO); } \ No newline at end of file diff --git a/src/resources.h b/src/resources.h index 68ce17a..912ceca 100644 --- a/src/resources.h +++ b/src/resources.h @@ -10,10 +10,7 @@ struct Resources { GLuint shaders[SHADER_COUNT]; Texture atlas; - std::map textures; }; void resources_init(Resources* self); -void resources_texture_init(Resources* self, const std::string& path, s32 id); void resources_free(Resources* self); -void resources_textures_free(Resources* self); diff --git a/src/snapshots.cpp b/src/snapshots.cpp index 97335cf..2c8cf46 100644 --- a/src/snapshots.cpp +++ b/src/snapshots.cpp @@ -1,6 +1,6 @@ #include "snapshots.h" -static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot) +static void _snapshot_stack_push(SnapshotStack* stack, Snapshot* snapshot) { if (stack->top >= SNAPSHOT_STACK_MAX) { @@ -11,20 +11,29 @@ static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot) stack->snapshots[stack->top++] = *snapshot; } -static bool _snapshot_stack_pop(SnapshotStack* stack, Snapshot* snapshot) +static Snapshot* _snapshot_stack_pop(SnapshotStack* stack) { - if (stack->top == 0) return false; - - *snapshot = stack->snapshots[--stack->top]; - return true; + if (stack->top == 0) return nullptr; + return &stack->snapshots[--stack->top]; } -static void _snapshot_set(Snapshots* self, const Snapshot& snapshot) +static void _snapshot_set(Snapshots* self, Snapshot* snapshot) { - *self->anm2 = snapshot.anm2; - *self->reference = snapshot.reference; - self->preview->time = snapshot.time; - self->action = snapshot.action; + if (!snapshot) return; + + *self->anm2 = snapshot->anm2; + *self->reference = snapshot->reference; + self->preview->time = snapshot->time; + self->action = snapshot->action; + + anm2_spritesheet_texture_pixels_upload(self->anm2); +} + +Snapshot snapshot_get(Snapshots* self) +{ + Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, self->action}; + anm2_spritesheet_texture_pixels_download(&snapshot.anm2); + return snapshot; } void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview) @@ -41,18 +50,17 @@ void snapshots_reset(Snapshots* self) self->action.clear(); } -void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot) +void snapshots_undo_push(Snapshots* self, Snapshot* snapshot) { - _snapshot_stack_push(&self->undoStack, snapshot); self->redoStack.top = 0; + _snapshot_stack_push(&self->undoStack, snapshot); } void snapshots_undo(Snapshots* self) { - Snapshot snapshot; - if (_snapshot_stack_pop(&self->undoStack, &snapshot)) + if (Snapshot* snapshot = _snapshot_stack_pop(&self->undoStack)) { - Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action}; + Snapshot current = snapshot_get(self); _snapshot_stack_push(&self->redoStack, ¤t); _snapshot_set(self, snapshot); } @@ -60,10 +68,9 @@ void snapshots_undo(Snapshots* self) void snapshots_redo(Snapshots* self) { - Snapshot snapshot; - if (_snapshot_stack_pop(&self->redoStack, &snapshot)) + if (Snapshot* snapshot = _snapshot_stack_pop(&self->redoStack)) { - Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action}; + Snapshot current = snapshot_get(self); _snapshot_stack_push(&self->undoStack, ¤t); _snapshot_set(self, snapshot); } diff --git a/src/snapshots.h b/src/snapshots.h index f1cfd93..3399a9d 100644 --- a/src/snapshots.h +++ b/src/snapshots.h @@ -4,7 +4,7 @@ #include "preview.h" #include "texture.h" -#define SNAPSHOT_STACK_MAX 1000 +#define SNAPSHOT_STACK_MAX 100 #define SNAPSHOT_ACTION "Action" struct Snapshot @@ -33,8 +33,9 @@ struct Snapshots SnapshotStack redoStack; }; -void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot); +void snapshots_undo_push(Snapshots* self, Snapshot* snapshot); void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview); void snapshots_undo(Snapshots* self); void snapshots_redo(Snapshots* self); -void snapshots_reset(Snapshots* self); \ No newline at end of file +void snapshots_reset(Snapshots* self); +Snapshot snapshot_get(Snapshots* self); \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp index e25d158..a354853 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -99,13 +99,21 @@ void init(State* self) glDisable(GL_DEPTH_TEST); glDisable(GL_LINE_SMOOTH); + if (!self->argument.empty()) + { + anm2_deserialize(&self->anm2, self->argument); + window_title_from_path_set(self->window, self->argument); + } + else + anm2_new(&self->anm2); + resources_init(&self->resources); dialog_init(&self->dialog, self->window); clipboard_init(&self->clipboard, &self->anm2); - snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview); preview_init(&self->preview, &self->anm2, &self->reference, &self->resources, &self->settings); generate_preview_init(&self->generatePreview, &self->anm2, &self->reference, &self->resources, &self->settings); editor_init(&self->editor, &self->anm2, &self->reference, &self->resources, &self->settings); + snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview); imgui_init ( @@ -123,14 +131,6 @@ void init(State* self) self->window, &self->glContext ); - - if (!self->argument.empty()) - { - anm2_deserialize(&self->anm2, &self->resources, self->argument); - window_title_from_path_set(self->window, self->argument); - } - else - anm2_new(&self->anm2); } void loop(State* self) diff --git a/src/texture.cpp b/src/texture.cpp index 7ef2a58..c99d6d7 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -17,7 +17,9 @@ static void _texture_gl_set(Texture* self, const u8* data) { - glGenTextures(1, &self->id); + if (self->id == GL_ID_NONE) + glGenTextures(1, &self->id); + glBindTexture(GL_TEXTURE_2D, self->id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -40,7 +42,6 @@ std::vector texture_download(const Texture* self) bool texture_from_path_init(Texture* self, const std::string& path) { - *self = Texture{}; u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS); if (!data) @@ -49,11 +50,12 @@ bool texture_from_path_init(Texture* self, const std::string& path) if (!data) { log_error(std::format(TEXTURE_INIT_ERROR, path)); - self->isInvalid = true; return false; } } + self->isInvalid = false; + log_info(std::format(TEXTURE_INIT_INFO, path)); _texture_gl_set(self, data); @@ -132,34 +134,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; -} - -Texture texture_copy(Texture* self) -{ - Texture copy = *self; - _texture_gl_set(©, nullptr); - - GLuint fboSource; - GLuint fboDestination; - glGenFramebuffers(1, &fboSource); - glGenFramebuffers(1, &fboDestination); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSource); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->id, 0); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDestination); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copy.id, 0); - - glBlitFramebuffer - ( - 0, 0, self->size.x, self->size.y, - 0, 0, self->size.x, self->size.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST - ); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fboSource); - glDeleteFramebuffers(1, &fboDestination); - - return copy; } \ No newline at end of file diff --git a/src/texture.h b/src/texture.h index c222cf9..84d577f 100644 --- a/src/texture.h +++ b/src/texture.h @@ -10,10 +10,10 @@ struct Texture { - GLuint id = 0; - ivec2 size = {0, 0}; - s32 channels = -1; - bool isInvalid = false; + GLuint id = GL_ID_NONE; + ivec2 size{}; + s32 channels = TEXTURE_CHANNELS; + bool isInvalid = true; }; bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, const u8* data, u32 length);