Snapshots, refactoring
This commit is contained in:
@@ -19,7 +19,6 @@ namespace anm2ed::animation_preview
|
||||
{
|
||||
constexpr auto NULL_COLOR = vec4(0.0f, 0.0f, 1.0f, 0.90f);
|
||||
constexpr auto TARGET_SIZE = vec2(32, 32);
|
||||
constexpr auto PIVOT_SIZE = vec2(8, 8);
|
||||
constexpr auto POINT_SIZE = vec2(4, 4);
|
||||
constexpr auto NULL_RECT_SIZE = vec2(100);
|
||||
constexpr auto TRIGGER_TEXT_COLOR = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
@@ -83,9 +82,17 @@ namespace anm2ed::animation_preview
|
||||
|
||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||
|
||||
imgui::shortcut(settings.shortcutCenterView);
|
||||
if (ImGui::Button("Center View", widgetSize)) pan = vec2();
|
||||
imgui::set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Fit", widgetSize);
|
||||
|
||||
imgui::shortcut(settings.shortcutFit);
|
||||
if (ImGui::Button("Fit", widgetSize))
|
||||
if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform));
|
||||
imgui::set_item_tooltip_shortcut("Set the view to match the extent of the animation.", settings.shortcutFit);
|
||||
|
||||
ImGui::TextUnformatted(std::format(POSITION_FORMAT, (int)mousePos.x, (int)mousePos.y).c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -181,9 +188,8 @@ namespace anm2ed::animation_preview
|
||||
auto layerTransform = transform * math::quad_model_get(frame.size, frame.position, frame.pivot,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
|
||||
auto inset = 0.5f / vec2(texture.size); // needed to avoid bleed
|
||||
auto uvMin = frame.crop / vec2(texture.size) + inset;
|
||||
auto uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
||||
auto uvMin = frame.crop / vec2(texture.size);
|
||||
auto uvMax = (frame.crop + frame.size) / vec2(texture.size);
|
||||
auto vertices = math::uv_vertices_get(uvMin, uvMax);
|
||||
vec3 frameColorOffset = frame.offset + colorOffset;
|
||||
vec4 frameTint = frame.tint;
|
||||
@@ -298,15 +304,24 @@ namespace anm2ed::animation_preview
|
||||
|
||||
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
|
||||
|
||||
auto isRound = settings.propertiesIsRound;
|
||||
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
||||
auto isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
auto isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
|
||||
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
|
||||
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
|
||||
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
|
||||
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
|
||||
auto isLeftReleased = ImGui::IsKeyReleased(ImGuiKey_LeftArrow);
|
||||
auto isRightReleased = ImGui::IsKeyReleased(ImGuiKey_RightArrow);
|
||||
auto isUpReleased = ImGui::IsKeyReleased(ImGuiKey_UpArrow);
|
||||
auto isDownReleased = ImGui::IsKeyReleased(ImGuiKey_DownArrow);
|
||||
auto isLeft = imgui::chord_repeating(ImGuiKey_LeftArrow);
|
||||
auto isRight = imgui::chord_repeating(ImGuiKey_RightArrow);
|
||||
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
|
||||
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
|
||||
auto isMouseRightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
||||
auto mouseDelta = to_vec2(ImGui::GetIO().MouseDelta);
|
||||
auto mouseDelta = to_ivec2(ImGui::GetIO().MouseDelta);
|
||||
auto mouseWheel = ImGui::GetIO().MouseWheel;
|
||||
auto isZoomIn = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomIn));
|
||||
auto isZoomOut = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomOut));
|
||||
@@ -314,7 +329,10 @@ namespace anm2ed::animation_preview
|
||||
auto frame = document.frame_get();
|
||||
auto useTool = tool;
|
||||
auto step = isMod ? step::FAST : step::NORMAL;
|
||||
auto isClick = isMouseDown;
|
||||
auto isKeyPressed = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
|
||||
auto isKeyReleased = isLeftReleased || isRightReleased || isUpReleased || isDownReleased;
|
||||
auto isBegin = isMouseClick || isKeyPressed;
|
||||
auto isEnd = isMouseReleased || isKeyReleased;
|
||||
|
||||
if (isMouseMiddleDown) useTool = tool::PAN;
|
||||
if (tool == tool::MOVE && isMouseRightDown) useTool = tool::SCALE;
|
||||
@@ -323,30 +341,42 @@ namespace anm2ed::animation_preview
|
||||
switch (useTool)
|
||||
{
|
||||
case tool::PAN:
|
||||
if (isClick || isMouseMiddleDown) pan += isRound ? vec2(ivec2(mouseDelta)) : mouseDelta;
|
||||
if (isMouseDown || isMouseMiddleDown) pan += mouseDelta;
|
||||
break;
|
||||
case tool::MOVE:
|
||||
if (!frame) break;
|
||||
if (isClick) frame->position = isRound ? vec2(ivec2(mousePos)) : mousePos;
|
||||
if (isBegin) document.snapshot("Frame Position");
|
||||
if (isMouseDown) frame->position = mousePos;
|
||||
if (isLeft) frame->position.x -= step;
|
||||
if (isRight) frame->position.x += step;
|
||||
if (isUp) frame->position.y -= step;
|
||||
if (isDown) frame->position.y += step;
|
||||
if (isEnd) document.change(change::FRAMES);
|
||||
break;
|
||||
case tool::SCALE:
|
||||
if (!frame) break;
|
||||
if (isClick) frame->scale += isRound ? vec2(ivec2(mouseDelta)) : mouseDelta;
|
||||
if (isBegin) document.snapshot("Frame Scale");
|
||||
if (isMouseDown) frame->scale += mouseDelta;
|
||||
if (isLeft) frame->scale.x -= step;
|
||||
if (isRight) frame->scale.x += step;
|
||||
if (isUp) frame->scale.y -= step;
|
||||
if (isDown) frame->scale.y += step;
|
||||
if (isEnd) document.change(change::FRAMES);
|
||||
break;
|
||||
case tool::ROTATE:
|
||||
if (!frame) break;
|
||||
if (isClick) frame->rotation += isRound ? (int)mouseDelta.y : mouseDelta.y;
|
||||
if (isBegin) document.snapshot("Frame Rotation");
|
||||
if (isMouseDown) frame->rotation += mouseDelta.y;
|
||||
if (isLeft || isDown) frame->rotation -= step;
|
||||
if (isUp || isRight) frame->rotation += step;
|
||||
if (isEnd) document.change(change::FRAMES);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (mouseWheel != 0 || isZoomIn || isZoomOut)
|
||||
zoom_set(zoom, pan, mousePos, (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
|
||||
zoom_set(zoom, pan, vec2(mousePos), (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace anm2ed::animation_preview
|
||||
class AnimationPreview : public canvas::Canvas
|
||||
{
|
||||
bool isPreviewHovered{};
|
||||
glm::vec2 mousePos{};
|
||||
glm::ivec2 mousePos{};
|
||||
|
||||
public:
|
||||
AnimationPreview();
|
||||
|
||||
44
src/anm2.cpp
44
src/anm2.cpp
@@ -797,6 +797,50 @@ namespace anm2ed::anm2
|
||||
return std::string(printer.CStr());
|
||||
}
|
||||
|
||||
vec4 Animation::rect(bool isRootTransform)
|
||||
{
|
||||
f32 minX = std::numeric_limits<f32>::infinity();
|
||||
f32 minY = std::numeric_limits<f32>::infinity();
|
||||
f32 maxX = -std::numeric_limits<f32>::infinity();
|
||||
f32 maxY = -std::numeric_limits<f32>::infinity();
|
||||
bool any = false;
|
||||
|
||||
constexpr ivec2 CORNERS[4] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
|
||||
|
||||
for (float t = 0.0f; t < (float)frameNum; t += 1.0f)
|
||||
{
|
||||
mat4 transform(1.0f);
|
||||
|
||||
if (isRootTransform)
|
||||
{
|
||||
auto root = rootAnimation.frame_generate(t, anm2::ROOT);
|
||||
transform *= math::quad_model_parent_get(root.position, {}, math::percent_to_unit(root.scale), root.rotation);
|
||||
}
|
||||
|
||||
for (auto& [id, layerAnimation] : layerAnimations)
|
||||
{
|
||||
auto frame = layerAnimation.frame_generate(t, anm2::LAYER);
|
||||
|
||||
if (frame.size == vec2() || !frame.isVisible) continue;
|
||||
|
||||
auto layerTransform = transform * math::quad_model_get(frame.size, frame.position, frame.pivot,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
for (auto& corner : CORNERS)
|
||||
{
|
||||
vec4 world = layerTransform * vec4(corner, 0.0f, 1.0f);
|
||||
minX = std::min(minX, world.x);
|
||||
minY = std::min(minY, world.y);
|
||||
maxX = std::max(maxX, world.x);
|
||||
maxY = std::max(maxY, world.y);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any) return vec4(-1.0f);
|
||||
return {minX, minY, maxX - minX, maxY - minY};
|
||||
}
|
||||
|
||||
Animations::Animations() = default;
|
||||
|
||||
Animations::Animations(XMLElement* element)
|
||||
|
||||
@@ -208,6 +208,7 @@ namespace anm2ed::anm2
|
||||
void item_remove(Type, int = -1);
|
||||
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*);
|
||||
int length();
|
||||
glm::vec4 rect(bool);
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "math.h"
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
using namespace glm;
|
||||
@@ -183,13 +184,11 @@ namespace anm2ed::canvas
|
||||
|
||||
void Canvas::grid_render(Shader& shader, float zoom, vec2 pan, ivec2 size, ivec2 offset, vec4 color)
|
||||
{
|
||||
auto zoomFactor = math::percent_to_unit(zoom);
|
||||
auto transform = glm::inverse(transform_get(zoom, pan));
|
||||
|
||||
glUseProgram(shader.id);
|
||||
|
||||
glUniform2f(glGetUniformLocation(shader.id, shader::UNIFORM_VIEW_SIZE), this->size.x, this->size.y);
|
||||
glUniform2f(glGetUniformLocation(shader.id, shader::UNIFORM_PAN), pan.x, pan.y);
|
||||
glUniform1f(glGetUniformLocation(shader.id, shader::UNIFORM_ZOOM), zoomFactor);
|
||||
glUniformMatrix4fv(glGetUniformLocation(shader.id, shader::UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
|
||||
glUniform2f(glGetUniformLocation(shader.id, shader::UNIFORM_SIZE), (float)size.x, (float)size.y);
|
||||
glUniform2f(glGetUniformLocation(shader.id, shader::UNIFORM_OFFSET), (float)offset.x, (float)offset.y);
|
||||
glUniform4f(glGetUniformLocation(shader.id, shader::UNIFORM_COLOR), color.r, color.g, color.b, color.a);
|
||||
@@ -262,7 +261,7 @@ namespace anm2ed::canvas
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Canvas::zoom_set(float& zoom, vec2& pan, vec2& focus, float step)
|
||||
void Canvas::zoom_set(float& zoom, vec2& pan, vec2 focus, float step)
|
||||
{
|
||||
auto zoomFactor = math::percent_to_unit(zoom);
|
||||
float newZoom = glm::clamp(math::round_nearest_multiple(zoom + step, step), canvas::ZOOM_MIN, canvas::ZOOM_MAX);
|
||||
@@ -279,4 +278,19 @@ namespace anm2ed::canvas
|
||||
auto zoomFactor = math::percent_to_unit(zoom);
|
||||
return (position - pan - (size * 0.5f)) / zoomFactor;
|
||||
}
|
||||
|
||||
void Canvas::set_to_rect(float& zoom, vec2& pan, vec4 rect)
|
||||
{
|
||||
if (rect != vec4(-1.0f) && (rect.z > 0 && rect.w > 0))
|
||||
{
|
||||
f32 scaleX = size.x / rect.z;
|
||||
f32 scaleY = size.y / rect.w;
|
||||
f32 fitScale = std::min(scaleX, scaleY);
|
||||
|
||||
zoom = math::unit_to_percent(fitScale);
|
||||
|
||||
vec2 rectCenter = {rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f};
|
||||
pan = -rectCenter * fitScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace anm2ed::canvas
|
||||
{
|
||||
constexpr float 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};
|
||||
|
||||
constexpr auto PIVOT_SIZE = glm::vec2(8, 8);
|
||||
constexpr auto ZOOM_MIN = 1.0f;
|
||||
constexpr auto ZOOM_MAX = 2000.0f;
|
||||
constexpr auto POSITION_FORMAT = "Position: ({:8} {:8})";
|
||||
@@ -49,7 +50,8 @@ namespace anm2ed::canvas
|
||||
void clear(glm::vec4&);
|
||||
void bind();
|
||||
void unbind();
|
||||
void zoom_set(float&, glm::vec2&, glm::vec2&, float);
|
||||
void zoom_set(float&, glm::vec2&, glm::vec2, float);
|
||||
glm::vec2 position_translate(float&, glm::vec2&, glm::vec2);
|
||||
void set_to_rect(float& zoom, glm::vec2& pan, glm::vec4 rect);
|
||||
};
|
||||
}
|
||||
|
||||
128
src/document.cpp
128
src/document.cpp
@@ -13,6 +13,8 @@ using namespace anm2ed::toast;
|
||||
using namespace anm2ed::types;
|
||||
using namespace anm2ed::util;
|
||||
|
||||
using namespace glm;
|
||||
|
||||
namespace anm2ed::document
|
||||
{
|
||||
Document::Document(const std::string& path, bool isNew, std::string* errorString)
|
||||
@@ -158,6 +160,110 @@ namespace anm2ed::document
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_crop_set(anm2::Frame* frame, vec2 crop)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Crop");
|
||||
frame->crop = crop;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_size_set(anm2::Frame* frame, vec2 size)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Size");
|
||||
frame->size = size;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_position_set(anm2::Frame* frame, vec2 position)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Position");
|
||||
frame->position = position;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_pivot_set(anm2::Frame* frame, vec2 pivot)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Pivot");
|
||||
frame->pivot = pivot;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_scale_set(anm2::Frame* frame, vec2 scale)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Scale");
|
||||
frame->scale = scale;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_rotation_set(anm2::Frame* frame, float rotation)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Rotation");
|
||||
frame->rotation = rotation;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_delay_set(anm2::Frame* frame, int delay)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Delay");
|
||||
frame->delay = delay;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_tint_set(anm2::Frame* frame, vec4 tint)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Tint");
|
||||
frame->tint = tint;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_offset_set(anm2::Frame* frame, vec3 offset)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Color Offset");
|
||||
frame->offset = offset;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_is_visible_set(anm2::Frame* frame, bool isVisible)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Visibility");
|
||||
frame->isVisible = isVisible;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_is_interpolated_set(anm2::Frame* frame, bool isInterpolated)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Interpolation");
|
||||
frame->isInterpolated = isInterpolated;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_flip_x(anm2::Frame* frame)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Flip X");
|
||||
frame->scale.x = -frame->scale.x;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
void Document::frame_flip_y(anm2::Frame* frame)
|
||||
{
|
||||
if (!frame) return;
|
||||
snapshot("Frame Flip Y");
|
||||
frame->scale.y = -frame->scale.y;
|
||||
change(change::FRAMES);
|
||||
}
|
||||
|
||||
anm2::Item* Document::item_get()
|
||||
{
|
||||
return anm2.item_get(reference);
|
||||
@@ -301,16 +407,6 @@ namespace anm2ed::document
|
||||
toasts.error(std::format("Failed to deserialize event(s): {}", errorString));
|
||||
}
|
||||
|
||||
void Document::item_visible_toggle(anm2::Item* item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
snapshot("Item Visible");
|
||||
item->isVisible = !item->isVisible;
|
||||
|
||||
change(change::ITEMS);
|
||||
}
|
||||
|
||||
void Document::item_add(anm2::Type type, int id, std::string& name, locale::Type locale, int spritesheetID)
|
||||
{
|
||||
snapshot("Add Item");
|
||||
@@ -330,15 +426,21 @@ namespace anm2ed::document
|
||||
|
||||
void Document::item_remove(anm2::Animation* animation)
|
||||
{
|
||||
snapshot("Remove Item");
|
||||
|
||||
if (!animation) return;
|
||||
|
||||
snapshot("Remove Item");
|
||||
animation->item_remove(reference.itemType, reference.itemID);
|
||||
reference = {reference.animationIndex};
|
||||
change(change::ITEMS);
|
||||
}
|
||||
|
||||
void Document::item_visible_toggle(anm2::Item* item)
|
||||
{
|
||||
if (!item) return;
|
||||
snapshot("Item Visibility");
|
||||
item->isVisible = !item->isVisible;
|
||||
change(change::ITEMS);
|
||||
}
|
||||
|
||||
anm2::Animation* Document::animation_get()
|
||||
{
|
||||
return anm2.animation_get(reference);
|
||||
|
||||
@@ -73,12 +73,27 @@ namespace anm2ed::document
|
||||
|
||||
anm2::Frame* frame_get();
|
||||
void frames_add(anm2::Item* item);
|
||||
void frames_change();
|
||||
void frames_delete(anm2::Item* item);
|
||||
void frames_bake(int, bool, bool);
|
||||
void frame_crop_set(anm2::Frame*, glm::vec2);
|
||||
void frame_size_set(anm2::Frame*, glm::vec2);
|
||||
void frame_position_set(anm2::Frame*, glm::vec2);
|
||||
void frame_pivot_set(anm2::Frame*, glm::vec2);
|
||||
void frame_scale_set(anm2::Frame*, glm::vec2);
|
||||
void frame_rotation_set(anm2::Frame*, float);
|
||||
void frame_delay_set(anm2::Frame*, int);
|
||||
void frame_tint_set(anm2::Frame*, glm::vec4);
|
||||
void frame_offset_set(anm2::Frame*, glm::vec3);
|
||||
void frame_is_visible_set(anm2::Frame*, bool);
|
||||
void frame_is_interpolated_set(anm2::Frame*, bool);
|
||||
void frame_flip_x(anm2::Frame* frame);
|
||||
void frame_flip_y(anm2::Frame* frame);
|
||||
|
||||
anm2::Item* item_get();
|
||||
void item_add(anm2::Type, int, std::string&, types::locale::Type, int);
|
||||
void item_remove(anm2::Animation* animation);
|
||||
void item_visible_toggle(anm2::Item*);
|
||||
|
||||
anm2::Spritesheet* spritesheet_get();
|
||||
void spritesheet_add(const std::string&);
|
||||
@@ -97,8 +112,6 @@ namespace anm2ed::document
|
||||
void events_remove_unused();
|
||||
void events_deserialize(const std::string&, types::merge::Type);
|
||||
|
||||
void item_visible_toggle(anm2::Item*);
|
||||
|
||||
void animation_add();
|
||||
void animation_duplicate();
|
||||
void animation_default();
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace anm2ed::frame_properties
|
||||
auto& anm2 = document.anm2;
|
||||
auto& reference = document.reference;
|
||||
auto& type = reference.itemType;
|
||||
auto& isRound = settings.propertiesIsRound;
|
||||
auto frame = document.frame_get();
|
||||
auto useFrame = frame ? *frame : anm2::Frame();
|
||||
|
||||
ImGui::BeginDisabled(!frame);
|
||||
{
|
||||
@@ -44,77 +44,75 @@ namespace anm2ed::frame_properties
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_);
|
||||
{
|
||||
if (ImGui::InputFloat2("Crop", frame ? value_ptr(frame->crop) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(frame->crop) : ""))
|
||||
if (isRound) frame->crop = ivec2(frame->crop);
|
||||
if (ImGui::InputFloat2("Crop", frame ? value_ptr(useFrame.crop) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(useFrame.crop) : ""))
|
||||
document.frame_crop_set(frame, useFrame.crop);
|
||||
ImGui::SetItemTooltip("%s", "Change the crop position the frame uses.");
|
||||
|
||||
if (ImGui::InputFloat2("Size", frame ? value_ptr(frame->size) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(frame->size) : ""))
|
||||
if (isRound) frame->crop = ivec2(frame->size);
|
||||
if (ImGui::InputFloat2("Size", frame ? value_ptr(useFrame.size) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(useFrame.size) : ""))
|
||||
document.frame_size_set(frame, useFrame.size);
|
||||
ImGui::SetItemTooltip("%s", "Change the size of the crop the frame uses.");
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::InputFloat2("Position", frame ? value_ptr(frame->position) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(frame->position) : ""))
|
||||
if (isRound) frame->position = ivec2(frame->position);
|
||||
if (ImGui::InputFloat2("Position", frame ? value_ptr(useFrame.position) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(useFrame.position) : ""))
|
||||
document.frame_position_set(frame, useFrame.position);
|
||||
ImGui::SetItemTooltip("%s", "Change the position of the frame.");
|
||||
|
||||
ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_);
|
||||
{
|
||||
if (ImGui::InputFloat2("Pivot", frame ? value_ptr(frame->pivot) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(frame->pivot) : ""))
|
||||
if (isRound) frame->position = ivec2(frame->position);
|
||||
if (ImGui::InputFloat2("Pivot", frame ? value_ptr(useFrame.pivot) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(useFrame.pivot) : ""))
|
||||
document.frame_pivot_set(frame, useFrame.pivot);
|
||||
ImGui::SetItemTooltip("%s", "Change the pivot of the frame; i.e., where it is centered.");
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::InputFloat2("Scale", frame ? value_ptr(frame->scale) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(frame->scale) : ""))
|
||||
if (isRound) frame->position = ivec2(frame->position);
|
||||
if (ImGui::InputFloat2("Scale", frame ? value_ptr(useFrame.scale) : &dummy_value<float>(),
|
||||
frame ? vec2_format_get(useFrame.scale) : ""))
|
||||
document.frame_scale_set(frame, useFrame.scale);
|
||||
ImGui::SetItemTooltip("%s", "Change the scale of the frame, in percent.");
|
||||
|
||||
if (ImGui::InputFloat("Rotation", frame ? &frame->rotation : &dummy_value<float>(), step::NORMAL, step::FAST,
|
||||
frame ? float_format_get(frame->rotation) : ""))
|
||||
if (isRound) frame->rotation = (int)frame->rotation;
|
||||
if (ImGui::InputFloat("Rotation", frame ? &useFrame.rotation : &dummy_value<float>(), step::NORMAL,
|
||||
step::FAST, frame ? float_format_get(useFrame.rotation) : ""))
|
||||
document.frame_rotation_set(frame, useFrame.rotation);
|
||||
ImGui::SetItemTooltip("%s", "Change the rotation of the frame.");
|
||||
|
||||
ImGui::InputInt("Duration", frame ? &frame->delay : &dummy_value<int>(), step::NORMAL, step::FAST,
|
||||
!frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0);
|
||||
if (ImGui::InputInt("Duration", frame ? &useFrame.delay : &dummy_value<int>(), step::NORMAL, step::FAST,
|
||||
!frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
|
||||
document.frame_delay_set(frame, useFrame.delay);
|
||||
ImGui::SetItemTooltip("%s", "Change how long the frame lasts.");
|
||||
|
||||
ImGui::ColorEdit4("Tint", frame ? value_ptr(frame->tint) : &dummy_value<float>());
|
||||
if (ImGui::ColorEdit4("Tint", frame ? value_ptr(useFrame.tint) : &dummy_value<float>()))
|
||||
document.frame_tint_set(frame, useFrame.tint);
|
||||
ImGui::SetItemTooltip("%s", "Change the tint of the frame.");
|
||||
ImGui::ColorEdit3("Color Offset", frame ? value_ptr(frame->offset) : &dummy_value<float>());
|
||||
|
||||
if (ImGui::ColorEdit3("Color Offset", frame ? value_ptr(useFrame.offset) : &dummy_value<float>()))
|
||||
document.frame_offset_set(frame, useFrame.offset);
|
||||
ImGui::SetItemTooltip("%s", "Change the color added onto the frame.");
|
||||
|
||||
ImGui::Checkbox("Visible", frame ? &frame->isVisible : &dummy_value<bool>());
|
||||
if (ImGui::Checkbox("Visible", frame ? &useFrame.isVisible : &dummy_value<bool>()))
|
||||
document.frame_is_visible_set(frame, useFrame.isVisible);
|
||||
ImGui::SetItemTooltip("%s", "Toggle the frame's visibility.");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Interpolated", frame ? &frame->isInterpolated : &dummy_value<bool>());
|
||||
|
||||
if (ImGui::Checkbox("Interpolated", frame ? &useFrame.isInterpolated : &dummy_value<bool>()))
|
||||
document.frame_is_interpolated_set(frame, useFrame.isInterpolated);
|
||||
ImGui::SetItemTooltip(
|
||||
"%s", "Toggle the frame interpolating; i.e., blending its values into the next frame based on the time.");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::Checkbox("Round", &settings.propertiesIsRound);
|
||||
ImGui::BeginDisabled(!frame);
|
||||
ImGui::SetItemTooltip(
|
||||
"%s", "When toggled, decimal values will be snapped to their nearest whole value when changed.");
|
||||
|
||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||
|
||||
if (ImGui::Button("Flip X", widgetSize))
|
||||
if (frame) frame->scale.x = -frame->scale.x;
|
||||
if (ImGui::Button("Flip X", widgetSize)) document.frame_flip_x(frame);
|
||||
ImGui::SetItemTooltip("%s", "Flip the horizontal scale of the frame, to cheat mirroring the frame "
|
||||
"horizontally.\n(Note: the format does not support mirroring.)");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Flip Y", widgetSize))
|
||||
if (frame) frame->scale.y = -frame->scale.y;
|
||||
if (ImGui::Button("Flip Y", widgetSize)) document.frame_flip_y(frame);
|
||||
ImGui::SetItemTooltip("%s", "Flip the vertical scale of the frame, to cheat mirroring the frame "
|
||||
"vertically.\n(Note: the format does not support mirroring.)");
|
||||
}
|
||||
|
||||
@@ -236,8 +236,8 @@ namespace anm2ed::imgui
|
||||
bool shortcut(std::string string, shortcut::Type type)
|
||||
{
|
||||
if (ImGui::GetTopMostPopupModal() != nullptr) return false;
|
||||
auto flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
|
||||
: ImGuiInputFlags_RouteFocused;
|
||||
int flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
|
||||
: ImGuiInputFlags_RouteFocused;
|
||||
if (type == shortcut::GLOBAL_SET || type == shortcut::FOCUSED_SET)
|
||||
{
|
||||
ImGui::SetNextItemShortcut(string_to_chord(string), flags);
|
||||
|
||||
37
src/shader.h
37
src/shader.h
@@ -66,25 +66,24 @@ namespace anm2ed::shader
|
||||
)";
|
||||
|
||||
constexpr auto GRID_VERTEX = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position;
|
||||
layout (location = 1) in vec2 i_uv;
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 i_position;
|
||||
layout (location = 1) in vec2 i_uv;
|
||||
|
||||
out vec2 i_uv_out;
|
||||
out vec2 i_uv_out;
|
||||
|
||||
void main() {
|
||||
i_uv_out = i_position;
|
||||
gl_Position = vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
void main()
|
||||
{
|
||||
i_uv_out = i_position;
|
||||
gl_Position = vec4(i_position, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr auto GRID_FRAGMENT = R"(
|
||||
#version 330 core
|
||||
in vec2 i_uv_out;
|
||||
|
||||
uniform vec2 u_view_size;
|
||||
uniform vec2 u_pan;
|
||||
uniform float u_zoom;
|
||||
uniform mat4 u_transform;
|
||||
uniform vec2 u_size;
|
||||
uniform vec2 u_offset;
|
||||
uniform vec4 u_color;
|
||||
@@ -93,18 +92,15 @@ void main() {
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 viewSize = max(u_view_size, vec2(1.0));
|
||||
float zoom = max(u_zoom, 1e-6);
|
||||
vec2 pan = u_pan;
|
||||
|
||||
vec2 world = (i_uv_out - (2.0 * pan / viewSize)) * (viewSize / (2.0 * zoom));
|
||||
world += vec2(0.5); // Half pixel nudge
|
||||
vec4 world4 = u_transform * vec4(i_uv_out, 0.0, 1.0);
|
||||
vec2 world = world4.xy / world4.w;
|
||||
|
||||
vec2 cell = max(u_size, vec2(1.0));
|
||||
vec2 grid = (world - u_offset) / cell;
|
||||
|
||||
vec2 d = abs(fract(grid) - 0.5);
|
||||
float distance = min(d.x, d.y);
|
||||
vec2 frac = fract(grid);
|
||||
vec2 distToLine = min(frac, 1.0 - frac);
|
||||
float distance = min(distToLine.x, distToLine.y);
|
||||
|
||||
float fw = min(fwidth(grid.x), fwidth(grid.y));
|
||||
float alpha = 1.0 - smoothstep(0.0, fw, distance);
|
||||
@@ -127,9 +123,6 @@ void main() {
|
||||
constexpr auto UNIFORM_MODEL = "u_model";
|
||||
constexpr auto UNIFORM_RECT_SIZE = "u_rect_size";
|
||||
constexpr auto UNIFORM_TEXTURE = "u_texture";
|
||||
constexpr auto UNIFORM_VIEW_SIZE = "u_view_size";
|
||||
constexpr auto UNIFORM_PAN = "u_pan";
|
||||
constexpr auto UNIFORM_ZOOM = "u_zoom";
|
||||
|
||||
enum Type
|
||||
{
|
||||
|
||||
@@ -21,6 +21,9 @@ namespace anm2ed::spritesheet_editor
|
||||
void SpritesheetEditor::update(Manager& manager, Settings& settings, Resources& resources)
|
||||
{
|
||||
auto& document = *manager.get();
|
||||
auto& anm2 = document.anm2;
|
||||
auto& reference = document.reference;
|
||||
auto& referenceSpritesheet = document.referenceSpritesheet;
|
||||
auto& pan = document.editorPan;
|
||||
auto& zoom = document.editorZoom;
|
||||
auto& backgroundColor = settings.editorBackgroundColor;
|
||||
@@ -59,9 +62,16 @@ namespace anm2ed::spritesheet_editor
|
||||
|
||||
auto widgetSize = ImVec2(imgui::row_widget_width_get(2), 0);
|
||||
|
||||
if (ImGui::Button("Center View", widgetSize)) pan = vec2();
|
||||
imgui::shortcut(settings.shortcutCenterView);
|
||||
if (ImGui::Button("Center View", widgetSize)) pan = -size * 0.5f;
|
||||
imgui::set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Fit", widgetSize);
|
||||
|
||||
imgui::shortcut(settings.shortcutFit);
|
||||
if (ImGui::Button("Fit", widgetSize))
|
||||
if (spritesheet) set_to_rect(zoom, pan, {0, 0, spritesheet->texture.size.x, spritesheet->texture.size.y});
|
||||
imgui::set_item_tooltip_shortcut("Set the view to match the extent of the spritesheet.", settings.shortcutFit);
|
||||
|
||||
ImGui::TextUnformatted(std::format(POSITION_FORMAT, (int)mousePos.x, (int)mousePos.y).c_str());
|
||||
}
|
||||
@@ -84,12 +94,27 @@ namespace anm2ed::spritesheet_editor
|
||||
viewport_set();
|
||||
clear(backgroundColor);
|
||||
|
||||
auto frame = document.frame_get();
|
||||
|
||||
if (spritesheet)
|
||||
{
|
||||
auto& texture = spritesheet->texture;
|
||||
auto transform = transform_get(zoom, pan) * math::quad_model_get(texture.size);
|
||||
texture_render(shaderTexture, texture.id, transform);
|
||||
if (isBorder) rect_render(lineShader, transform);
|
||||
auto transform = transform_get(zoom, pan);
|
||||
|
||||
auto spritesheetTransform = transform * math::quad_model_get(texture.size);
|
||||
texture_render(shaderTexture, texture.id, spritesheetTransform);
|
||||
if (isBorder) rect_render(lineShader, spritesheetTransform);
|
||||
|
||||
if (frame && reference.itemID > -1 &&
|
||||
anm2.content.layers.at(reference.itemID).spritesheetID == referenceSpritesheet)
|
||||
{
|
||||
auto cropTransform = transform * math::quad_model_get(frame->size, frame->crop);
|
||||
rect_render(lineShader, cropTransform, color::RED);
|
||||
|
||||
auto pivotTransform =
|
||||
transform * math::quad_model_get(canvas::PIVOT_SIZE, frame->crop + frame->pivot, PIVOT_SIZE * 0.5f);
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color::RED);
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrid) grid_render(shaderGrid, zoom, pan, gridSize, gridOffset, gridColor);
|
||||
@@ -102,19 +127,78 @@ namespace anm2ed::spritesheet_editor
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
|
||||
previousMousePos = mousePos;
|
||||
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
|
||||
|
||||
auto isMouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
auto isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
auto isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
|
||||
auto mouseDelta = ImGui::GetIO().MouseDelta;
|
||||
auto mouseDelta = to_ivec2(ImGui::GetIO().MouseDelta);
|
||||
auto mouseWheel = ImGui::GetIO().MouseWheel;
|
||||
auto& toolColor = settings.toolColor;
|
||||
auto isZoomIn = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomIn));
|
||||
auto isZoomOut = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomOut));
|
||||
auto isLeft = imgui::chord_repeating(ImGuiKey_LeftArrow);
|
||||
auto isRight = imgui::chord_repeating(ImGuiKey_RightArrow);
|
||||
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
|
||||
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
|
||||
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
|
||||
auto step = isMod ? step::FAST : step::NORMAL;
|
||||
auto useTool = tool;
|
||||
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
||||
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
|
||||
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
|
||||
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
|
||||
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
|
||||
auto isLeftReleased = ImGui::IsKeyReleased(ImGuiKey_LeftArrow);
|
||||
auto isRightReleased = ImGui::IsKeyReleased(ImGuiKey_RightArrow);
|
||||
auto isUpReleased = ImGui::IsKeyReleased(ImGuiKey_UpArrow);
|
||||
auto isDownReleased = ImGui::IsKeyReleased(ImGuiKey_DownArrow);
|
||||
auto frame = document.frame_get();
|
||||
auto isKeyPressed = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
|
||||
auto isKeyReleased = isLeftReleased || isRightReleased || isUpReleased || isDownReleased;
|
||||
auto isBegin = isMouseClick || isKeyPressed;
|
||||
auto isEnd = isMouseReleased || isKeyReleased;
|
||||
|
||||
if ((tool == tool::PAN && isMouseDown) || isMouseMiddleDown) pan += vec2(mouseDelta.x, mouseDelta.y);
|
||||
if (isMouseMiddleDown) useTool = tool::PAN;
|
||||
|
||||
switch (tool)
|
||||
switch (useTool)
|
||||
{
|
||||
case tool::PAN:
|
||||
if (isMouseDown || isMouseMiddleDown) pan += mouseDelta;
|
||||
break;
|
||||
case tool::MOVE:
|
||||
if (!frame) break;
|
||||
if (isBegin) document.snapshot("Frame Pivot");
|
||||
if (isMouseDown) frame->pivot = ivec2(mousePos - frame->crop);
|
||||
if (isLeft) frame->pivot.x -= step;
|
||||
if (isRight) frame->pivot.x += step;
|
||||
if (isUp) frame->pivot.y -= step;
|
||||
if (isDown) frame->pivot.y += step;
|
||||
if (isEnd) document.change(change::FRAMES);
|
||||
break;
|
||||
case tool::CROP:
|
||||
if (!frame) break;
|
||||
if (isBegin) document.snapshot(isMod ? "Frame Size" : "Frame Crop");
|
||||
if (isMouseClicked) frame->crop = ivec2(mousePos);
|
||||
if (isMouseDown) frame->size = ivec2(mousePos - frame->crop);
|
||||
if (isLeft) isMod ? frame->size.x -= step : frame->crop.x -= step;
|
||||
if (isRight) isMod ? frame->size.x += step : frame->crop.x += step;
|
||||
if (isUp) isMod ? frame->size.y -= step : frame->crop.y -= step;
|
||||
if (isDown) isMod ? frame->size.y += step : frame->crop.y += step;
|
||||
if (isEnd) document.change(change::FRAMES);
|
||||
break;
|
||||
case tool::DRAW:
|
||||
case tool::ERASE:
|
||||
{
|
||||
if (!spritesheet) break;
|
||||
if (isMouseClicked) document.snapshot(tool == tool::DRAW ? "Draw" : "Erase");
|
||||
auto color = tool == tool::DRAW ? toolColor : vec4();
|
||||
if (isMouseDown) spritesheet->texture.pixel_line(ivec2(previousMousePos), ivec2(mousePos), color);
|
||||
if (isMouseReleased) document.change(change::FRAMES);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace anm2ed::spritesheet_editor
|
||||
class SpritesheetEditor : public canvas::Canvas
|
||||
{
|
||||
glm::vec2 mousePos{};
|
||||
glm::vec2 previousMousePos{};
|
||||
|
||||
public:
|
||||
SpritesheetEditor();
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "math.h"
|
||||
|
||||
using namespace anm2ed::math;
|
||||
using namespace glm;
|
||||
|
||||
namespace anm2ed::texture
|
||||
@@ -140,6 +143,55 @@ namespace anm2ed::texture
|
||||
return stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, this->pixels.data(), size.x * CHANNELS);
|
||||
}
|
||||
|
||||
void Texture::pixel_set(ivec2 position, vec4 color)
|
||||
{
|
||||
if (position.x < 0 || position.y < 0 || position.x >= size.x || position.y >= size.y) return;
|
||||
|
||||
uint8 rgba8[4] = {(uint8)float_to_uint8(color.r), (uint8)float_to_uint8(color.g), (uint8)float_to_uint8(color.b),
|
||||
(uint8)float_to_uint8(color.a)};
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba8);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture::pixel_line(ivec2 start, ivec2 end, vec4 color)
|
||||
{
|
||||
auto plot = [&](ivec2 pos)
|
||||
{
|
||||
pixel_set(pos, color);
|
||||
};
|
||||
|
||||
int x0 = start.x;
|
||||
int y0 = start.y;
|
||||
int x1 = end.x;
|
||||
int y1 = end.y;
|
||||
|
||||
int dx = std::abs(x1 - x0);
|
||||
int dy = -std::abs(y1 - y0);
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
|
||||
while (true)
|
||||
{
|
||||
plot({x0, y0});
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 >= dy)
|
||||
{
|
||||
err += dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 <= dx)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::bind(GLuint unit)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace anm2ed::texture
|
||||
Texture(const char*, size_t, glm::ivec2);
|
||||
Texture(const std::string&);
|
||||
bool write_png(const std::string&);
|
||||
void pixel_set(glm::ivec2, glm::vec4);
|
||||
void pixel_line(glm::ivec2, glm::ivec2, glm::vec4);
|
||||
void bind(GLuint = 0);
|
||||
void unbind(GLuint = 0);
|
||||
};
|
||||
|
||||
@@ -46,8 +46,8 @@ namespace anm2ed::timeline
|
||||
|
||||
constexpr auto HELP_FORMAT = R"(- Press {} to decrement time.
|
||||
- Press {} to increment time.
|
||||
- Press {} to extend the selected frame, by one frame.
|
||||
- Press {} to shorten the selected frame, by one frame.
|
||||
- Press {} to extend the selected frame, by one frame.
|
||||
- Hold Alt while clicking a non-trigger frame to toggle interpolation.)";
|
||||
|
||||
void Timeline::item_child(Manager& manager, Document& document, anm2::Animation* animation, Settings& settings,
|
||||
@@ -479,7 +479,8 @@ namespace anm2ed::timeline
|
||||
|
||||
if (ImGui::Button("##Frame Button", size))
|
||||
{
|
||||
if (type != anm2::TRIGGER && ImGui::IsKeyDown(ImGuiMod_Alt)) frame.isInterpolated = !frame.isInterpolated;
|
||||
if (type != anm2::TRIGGER && ImGui::IsKeyDown(ImGuiMod_Alt))
|
||||
document.frame_is_interpolated_set(&frame, !frame.isInterpolated);
|
||||
if (type == anm2::LAYER)
|
||||
{
|
||||
document.referenceSpritesheet = anm2.content.layers[id].spritesheetID;
|
||||
|
||||
Reference in New Issue
Block a user