....that!
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
namespace anm2ed::anm2
|
||||
{
|
||||
constexpr auto FRAME_DURATION_MIN = 1;
|
||||
constexpr auto FRAME_DURATION_MAX = 100000;
|
||||
constexpr auto FRAME_DURATION_MAX = 1000000;
|
||||
|
||||
#define MEMBERS \
|
||||
X(isVisible, bool, true) \
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
namespace anm2ed
|
||||
{
|
||||
Clipboard::Clipboard() { set(""); }
|
||||
|
||||
std::string Clipboard::get()
|
||||
{
|
||||
auto text = SDL_GetClipboardText();
|
||||
@@ -13,13 +15,7 @@ namespace anm2ed
|
||||
return string;
|
||||
}
|
||||
|
||||
bool Clipboard::is_empty()
|
||||
{
|
||||
return get().empty();
|
||||
}
|
||||
bool Clipboard::is_empty() { return get().empty(); }
|
||||
|
||||
void Clipboard::set(const std::string& string)
|
||||
{
|
||||
SDL_SetClipboardText(string.data());
|
||||
}
|
||||
void Clipboard::set(const std::string& string) { SDL_SetClipboardText(string.data()); }
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace anm2ed
|
||||
class Clipboard
|
||||
{
|
||||
public:
|
||||
Clipboard();
|
||||
bool is_empty();
|
||||
std::string get();
|
||||
void set(const std::string&);
|
||||
|
||||
@@ -14,10 +14,17 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto viewport = ImGui::GetMainViewport();
|
||||
auto windowHeight = ImGui::GetFrameHeightWithSpacing();
|
||||
bool isLightTheme = settings.theme == theme::LIGHT;
|
||||
bool pushedStyle = false;
|
||||
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + taskbar.height));
|
||||
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, windowHeight));
|
||||
if (isLightTheme)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_TitleBgActive));
|
||||
pushedStyle = true;
|
||||
}
|
||||
|
||||
for (auto& document : manager.documents)
|
||||
{
|
||||
@@ -159,6 +166,7 @@ namespace anm2ed::imgui
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
if (pushedStyle) ImGui::PopStyleColor();
|
||||
|
||||
if (manager.isAnm2DragDrop)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,20 @@ using namespace glm;
|
||||
|
||||
namespace anm2ed::imgui
|
||||
{
|
||||
constexpr ImVec4 COLOR_LIGHT_BUTTON{0.98f, 0.98f, 0.98f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TITLE_BG{0.78f, 0.78f, 0.78f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TITLE_BG_ACTIVE{0.64f, 0.64f, 0.64f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TITLE_BG_COLLAPSED{0.74f, 0.74f, 0.74f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TABLE_HEADER{0.78f, 0.78f, 0.78f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB{0.74f, 0.74f, 0.74f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_HOVERED{0.82f, 0.82f, 0.82f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_SELECTED{0.92f, 0.92f, 0.92f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_DIMMED{0.70f, 0.70f, 0.70f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_DIMMED_SELECTED{0.86f, 0.86f, 0.86f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_OVERLINE{0.55f, 0.55f, 0.55f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_TAB_DIMMED_OVERLINE{0.50f, 0.50f, 0.50f, 1.0f};
|
||||
constexpr ImVec4 COLOR_LIGHT_CHECK_MARK{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
constexpr auto FRAME_BORDER_SIZE = 1.0f;
|
||||
|
||||
void theme_set(theme::Type theme)
|
||||
{
|
||||
@@ -28,6 +42,26 @@ namespace anm2ed::imgui
|
||||
ImGui::StyleColorsClassic();
|
||||
break;
|
||||
}
|
||||
auto& style = ImGui::GetStyle();
|
||||
style.FrameBorderSize = FRAME_BORDER_SIZE;
|
||||
|
||||
if (theme == theme::LIGHT)
|
||||
{
|
||||
auto& colors = style.Colors;
|
||||
colors[ImGuiCol_Button] = COLOR_LIGHT_BUTTON;
|
||||
colors[ImGuiCol_TitleBg] = COLOR_LIGHT_TITLE_BG;
|
||||
colors[ImGuiCol_TitleBgActive] = COLOR_LIGHT_TITLE_BG_ACTIVE;
|
||||
colors[ImGuiCol_TitleBgCollapsed] = COLOR_LIGHT_TITLE_BG_COLLAPSED;
|
||||
colors[ImGuiCol_TableHeaderBg] = COLOR_LIGHT_TABLE_HEADER;
|
||||
colors[ImGuiCol_Tab] = COLOR_LIGHT_TAB;
|
||||
colors[ImGuiCol_TabHovered] = COLOR_LIGHT_TAB_HOVERED;
|
||||
colors[ImGuiCol_TabSelected] = COLOR_LIGHT_TAB_SELECTED;
|
||||
colors[ImGuiCol_TabSelectedOverline] = COLOR_LIGHT_TAB_OVERLINE;
|
||||
colors[ImGuiCol_TabDimmed] = COLOR_LIGHT_TAB_DIMMED;
|
||||
colors[ImGuiCol_TabDimmedSelected] = COLOR_LIGHT_TAB_DIMMED_SELECTED;
|
||||
colors[ImGuiCol_TabDimmedSelectedOverline] = COLOR_LIGHT_TAB_DIMMED_OVERLINE;
|
||||
colors[ImGuiCol_CheckMark] = COLOR_LIGHT_CHECK_MARK;
|
||||
}
|
||||
}
|
||||
|
||||
int input_text_callback(ImGuiInputTextCallbackData* data)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "math_.h"
|
||||
#include "render.h"
|
||||
#include "shader.h"
|
||||
#include "snapshots.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "icon.h"
|
||||
@@ -372,7 +373,6 @@ namespace anm2ed::imgui
|
||||
|
||||
for (int i = 0; i < theme::COUNT; i++)
|
||||
{
|
||||
if (i == theme::LIGHT) continue; // TODO; light mode is jank rn so i am soft disabling it
|
||||
ImGui::RadioButton(theme::STRINGS[i], &editSettings.theme, i);
|
||||
ImGui::SameLine();
|
||||
}
|
||||
@@ -400,6 +400,11 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::Checkbox("Overwrite Warning", &editSettings.fileIsWarnOverwrite);
|
||||
ImGui::SetItemTooltip("A warning will be shown when saving a file.");
|
||||
|
||||
ImGui::SeparatorText("Snapshots");
|
||||
input_int_range("Stack Size", editSettings.fileSnapshotStackSize, 0, 1000);
|
||||
ImGui::SetItemTooltip("Set the maximum snapshot stack size of a document\n(i.e., how many undo/redos are "
|
||||
"preserved at a time).");
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
@@ -503,6 +508,9 @@ namespace anm2ed::imgui
|
||||
settings = editSettings;
|
||||
imgui::theme_set((theme::Type)editSettings.theme);
|
||||
manager.chords_set(settings);
|
||||
SnapshotStack::max_size_set(settings.fileSnapshotStackSize);
|
||||
for (auto& document : manager.documents)
|
||||
document.snapshots.apply_limit();
|
||||
configurePopup.close();
|
||||
}
|
||||
ImGui::SetItemTooltip("Use the configured settings.");
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace anm2ed::imgui
|
||||
constexpr auto TARGET_SIZE = vec2(32, 32);
|
||||
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);
|
||||
constexpr auto TRIGGER_TEXT_COLOR_DARK = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
constexpr auto TRIGGER_TEXT_COLOR_LIGHT = ImVec4(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
AnimationPreview::AnimationPreview() : Canvas(vec2()) {}
|
||||
|
||||
@@ -136,6 +137,8 @@ namespace anm2ed::imgui
|
||||
zoom = savedZoom;
|
||||
overlayIndex = savedOverlayIndex;
|
||||
isSizeTrySet = true;
|
||||
hasPendingZoomPanAdjust = false;
|
||||
isCheckerPanInitialized = false;
|
||||
}
|
||||
|
||||
if (settings.timelineIsSound) audioStream.capture_end(mixer);
|
||||
@@ -204,10 +207,38 @@ namespace anm2ed::imgui
|
||||
auto& tool = settings.tool;
|
||||
auto& isOnlyShowLayers = settings.timelineIsOnlyShowLayers;
|
||||
auto& shaderLine = resources.shaders[shader::LINE];
|
||||
bool isLightTheme = settings.theme == theme::LIGHT;
|
||||
auto& shaderAxes = resources.shaders[shader::AXIS];
|
||||
auto& shaderGrid = resources.shaders[shader::GRID];
|
||||
auto& shaderTexture = resources.shaders[shader::TEXTURE];
|
||||
|
||||
auto reset_checker_pan = [&]()
|
||||
{
|
||||
checkerPan = pan;
|
||||
checkerSyncPan = pan;
|
||||
checkerSyncZoom = zoom;
|
||||
isCheckerPanInitialized = true;
|
||||
hasPendingZoomPanAdjust = false;
|
||||
};
|
||||
|
||||
auto sync_checker_pan = [&]()
|
||||
{
|
||||
if (!isCheckerPanInitialized)
|
||||
{
|
||||
reset_checker_pan();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pan != checkerSyncPan || zoom != checkerSyncZoom)
|
||||
{
|
||||
bool ignorePanDelta = hasPendingZoomPanAdjust && zoom != checkerSyncZoom;
|
||||
if (!ignorePanDelta) checkerPan += pan - checkerSyncPan;
|
||||
checkerSyncPan = pan;
|
||||
checkerSyncZoom = zoom;
|
||||
if (ignorePanDelta) hasPendingZoomPanAdjust = false;
|
||||
}
|
||||
};
|
||||
|
||||
auto center_view = [&]() { pan = vec2(); };
|
||||
|
||||
if (ImGui::Begin("Animation Preview", &settings.windowIsAnimationPreview))
|
||||
@@ -351,28 +382,77 @@ namespace anm2ed::imgui
|
||||
if (isAxes) axes_render(shaderAxes, zoom, pan, axesColor);
|
||||
if (isGrid) grid_render(shaderGrid, zoom, pan, gridSize, gridOffset, gridColor);
|
||||
|
||||
auto render = [&](anm2::Animation* animation, float time, vec3 colorOffset = {}, float alphaOffset = {},
|
||||
bool isOnionskin = false)
|
||||
auto baseTransform = transform_get(zoom, pan);
|
||||
auto frameTime = document.frameTime > -1 && !playback.isPlaying ? document.frameTime : playback.time;
|
||||
|
||||
struct OnionskinSample
|
||||
{
|
||||
float time{};
|
||||
vec3 colorOffset{};
|
||||
float alphaOffset{};
|
||||
glm::mat4 transform{1.0f};
|
||||
anm2::Frame root{};
|
||||
};
|
||||
|
||||
std::vector<OnionskinSample> onionskinSamples;
|
||||
|
||||
if (animation && settings.onionskinIsEnabled)
|
||||
{
|
||||
auto add_samples = [&](int count, int direction, vec3 color)
|
||||
{
|
||||
for (int i = 1; i <= count; ++i)
|
||||
{
|
||||
float useTime = frameTime + (float)(direction * i);
|
||||
if (useTime < 0.0f || useTime > animation->frameNum) continue;
|
||||
|
||||
float alphaOffset = (1.0f / (count + 1)) * i;
|
||||
OnionskinSample sample{};
|
||||
sample.time = useTime;
|
||||
sample.colorOffset = color;
|
||||
sample.alphaOffset = alphaOffset;
|
||||
sample.root = animation->rootAnimation.frame_generate(sample.time, anm2::ROOT);
|
||||
sample.transform = baseTransform;
|
||||
if (isRootTransform)
|
||||
sample.transform *= math::quad_model_parent_get(
|
||||
sample.root.position, {}, math::percent_to_unit(sample.root.scale), sample.root.rotation);
|
||||
onionskinSamples.push_back(sample);
|
||||
}
|
||||
};
|
||||
|
||||
add_samples(settings.onionskinBeforeCount, -1, settings.onionskinBeforeColor);
|
||||
add_samples(settings.onionskinAfterCount, 1, settings.onionskinAfterColor);
|
||||
}
|
||||
|
||||
auto render = [&](anm2::Animation* animation, float time, vec3 colorOffset = {}, float alphaOffset = {},
|
||||
const std::vector<OnionskinSample>* layeredOnions = nullptr)
|
||||
{
|
||||
auto baseTransform = transform_get(zoom, pan);
|
||||
auto transform = baseTransform;
|
||||
auto root = animation->rootAnimation.frame_generate(time, anm2::ROOT);
|
||||
|
||||
if (isRootTransform)
|
||||
transform *= math::quad_model_parent_get(root.position, {}, math::percent_to_unit(root.scale), root.rotation);
|
||||
|
||||
if (!isOnlyShowLayers && root.isVisible && animation->rootAnimation.isVisible)
|
||||
auto draw_root = [&](const anm2::Frame& rootFrame, vec3 sampleColor, float sampleAlpha, bool isOnion)
|
||||
{
|
||||
auto rootTransform =
|
||||
isRootTransform ? baseTransform * math::quad_model_get(TARGET_SIZE, root.position, TARGET_SIZE * 0.5f,
|
||||
math::percent_to_unit(root.scale), root.rotation)
|
||||
: baseTransform * math::quad_model_get(TARGET_SIZE, {}, TARGET_SIZE * 0.5f);
|
||||
if (isOnlyShowLayers || !rootFrame.isVisible || !animation->rootAnimation.isVisible) return;
|
||||
|
||||
vec4 color = isOnionskin ? vec4(colorOffset, alphaOffset) : color::GREEN;
|
||||
auto rootTransform =
|
||||
isRootTransform
|
||||
? baseTransform * math::quad_model_get(TARGET_SIZE, rootFrame.position, TARGET_SIZE * 0.5f,
|
||||
math::percent_to_unit(rootFrame.scale), rootFrame.rotation)
|
||||
: baseTransform * math::quad_model_get(TARGET_SIZE, {}, TARGET_SIZE * 0.5f);
|
||||
|
||||
vec4 color = isOnion ? vec4(sampleColor, sampleAlpha) : color::GREEN;
|
||||
|
||||
auto icon = isAltIcons ? icon::TARGET_ALT : icon::TARGET;
|
||||
texture_render(shaderTexture, resources.icons[icon].id, rootTransform, color);
|
||||
}
|
||||
};
|
||||
|
||||
if (layeredOnions)
|
||||
for (auto& sample : *layeredOnions)
|
||||
draw_root(sample.root, sample.colorOffset, sample.alphaOffset, true);
|
||||
|
||||
draw_root(root, {}, 0.0f, false);
|
||||
|
||||
for (auto& id : animation->layerOrder)
|
||||
{
|
||||
@@ -381,46 +461,56 @@ namespace anm2ed::imgui
|
||||
|
||||
auto& layer = anm2.content.layers.at(id);
|
||||
|
||||
if (auto frame = layerAnimation.frame_generate(time, anm2::LAYER); frame.isVisible)
|
||||
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
||||
if (!spritesheet || !spritesheet->is_valid()) continue;
|
||||
|
||||
auto draw_layer =
|
||||
[&](float sampleTime, const glm::mat4& sampleTransform, vec3 sampleColor, float sampleAlpha, bool isOnion)
|
||||
{
|
||||
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
||||
if (!spritesheet || !spritesheet->is_valid()) continue;
|
||||
|
||||
auto& texture = spritesheet->texture;
|
||||
|
||||
auto layerModel = math::quad_model_get(frame.size, frame.position, frame.pivot,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto layerTransform = transform * layerModel;
|
||||
|
||||
auto texSize = vec2(texture.size);
|
||||
if (texSize.x <= 0.0f || texSize.y <= 0.0f) continue;
|
||||
|
||||
auto uvMin = frame.crop / texSize;
|
||||
auto uvMax = (frame.crop + frame.size) / texSize;
|
||||
vec3 frameColorOffset = frame.colorOffset + colorOffset;
|
||||
vec4 frameTint = frame.tint;
|
||||
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
|
||||
|
||||
auto inset = vec2(0.5f) / texSize;
|
||||
uvMin += inset;
|
||||
uvMax -= inset;
|
||||
auto vertices = math::uv_vertices_get(uvMin, uvMax);
|
||||
|
||||
texture_render(shaderTexture, texture.id, layerTransform, frameTint, frameColorOffset, vertices.data());
|
||||
|
||||
auto color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : color::RED;
|
||||
|
||||
if (isBorder) rect_render(shaderLine, layerTransform, layerModel, color);
|
||||
|
||||
if (isPivots)
|
||||
if (auto frame = layerAnimation.frame_generate(sampleTime, anm2::LAYER); frame.isVisible)
|
||||
{
|
||||
auto pivotModel = math::quad_model_get(PIVOT_SIZE, frame.position, PIVOT_SIZE * 0.5f,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto pivotTransform = transform * pivotModel;
|
||||
auto& texture = spritesheet->texture;
|
||||
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color);
|
||||
auto layerModel = math::quad_model_get(frame.size, frame.position, frame.pivot,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto layerTransform = sampleTransform * layerModel;
|
||||
|
||||
auto texSize = vec2(texture.size);
|
||||
if (texSize.x <= 0.0f || texSize.y <= 0.0f) return;
|
||||
|
||||
auto uvMin = frame.crop / texSize;
|
||||
auto uvMax = (frame.crop + frame.size) / texSize;
|
||||
vec3 frameColorOffset = frame.colorOffset + colorOffset + sampleColor;
|
||||
vec4 frameTint = frame.tint;
|
||||
frameTint.a = std::max(0.0f, frameTint.a - (alphaOffset + sampleAlpha));
|
||||
|
||||
auto inset = vec2(0.5f) / texSize;
|
||||
uvMin += inset;
|
||||
uvMax -= inset;
|
||||
auto vertices = math::uv_vertices_get(uvMin, uvMax);
|
||||
|
||||
texture_render(shaderTexture, texture.id, layerTransform, frameTint, frameColorOffset, vertices.data());
|
||||
|
||||
auto color = isOnion ? vec4(sampleColor, 1.0f - sampleAlpha) : color::RED;
|
||||
|
||||
if (isBorder) rect_render(shaderLine, layerTransform, layerModel, color);
|
||||
|
||||
if (isPivots)
|
||||
{
|
||||
auto pivotModel = math::quad_model_get(PIVOT_SIZE, frame.position, PIVOT_SIZE * 0.5f,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto pivotTransform = sampleTransform * pivotModel;
|
||||
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (layeredOnions)
|
||||
for (auto& sample : *layeredOnions)
|
||||
draw_layer(sample.time, sample.transform, sample.colorOffset, sample.alphaOffset, true);
|
||||
|
||||
draw_layer(time, transform, {}, 0.0f, false);
|
||||
}
|
||||
|
||||
for (auto& [id, nullAnimation] : animation->nullAnimations)
|
||||
@@ -429,71 +519,57 @@ namespace anm2ed::imgui
|
||||
|
||||
auto& isShowRect = anm2.content.nulls[id].isShowRect;
|
||||
|
||||
if (auto frame = nullAnimation.frame_generate(time, anm2::NULL_); frame.isVisible)
|
||||
auto draw_null =
|
||||
[&](float sampleTime, const glm::mat4& sampleTransform, vec3 sampleColor, float sampleAlpha, bool isOnion)
|
||||
{
|
||||
auto icon = isShowRect ? icon::POINT : isAltIcons ? icon::TARGET_ALT : icon::TARGET;
|
||||
|
||||
auto& size = isShowRect ? POINT_SIZE : TARGET_SIZE;
|
||||
auto color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset)
|
||||
: id == reference.itemID && reference.itemType == anm2::NULL_ ? color::RED
|
||||
: NULL_COLOR;
|
||||
|
||||
auto nullModel = math::quad_model_get(size, frame.position, size * 0.5f, math::percent_to_unit(frame.scale),
|
||||
frame.rotation);
|
||||
auto nullTransform = transform * nullModel;
|
||||
|
||||
texture_render(shaderTexture, resources.icons[icon].id, nullTransform, color);
|
||||
|
||||
if (isShowRect)
|
||||
if (auto frame = nullAnimation.frame_generate(sampleTime, anm2::NULL_); frame.isVisible)
|
||||
{
|
||||
auto rectModel = math::quad_model_get(NULL_RECT_SIZE, frame.position, NULL_RECT_SIZE * 0.5f,
|
||||
auto icon = isShowRect ? icon::POINT : isAltIcons ? icon::TARGET_ALT : icon::TARGET;
|
||||
|
||||
auto& size = isShowRect ? POINT_SIZE : TARGET_SIZE;
|
||||
auto color = isOnion ? vec4(sampleColor, 1.0f - sampleAlpha)
|
||||
: id == reference.itemID && reference.itemType == anm2::NULL_ ? color::RED
|
||||
: NULL_COLOR;
|
||||
|
||||
auto nullModel = math::quad_model_get(size, frame.position, size * 0.5f,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto rectTransform = transform * rectModel;
|
||||
auto nullTransform = sampleTransform * nullModel;
|
||||
|
||||
rect_render(shaderLine, rectTransform, rectModel, color);
|
||||
texture_render(shaderTexture, resources.icons[icon].id, nullTransform, color);
|
||||
|
||||
if (isShowRect)
|
||||
{
|
||||
auto rectModel = math::quad_model_get(NULL_RECT_SIZE, frame.position, NULL_RECT_SIZE * 0.5f,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto rectTransform = sampleTransform * rectModel;
|
||||
|
||||
rect_render(shaderLine, rectTransform, rectModel, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (layeredOnions)
|
||||
for (auto& sample : *layeredOnions)
|
||||
draw_null(sample.time, sample.transform, sample.colorOffset, sample.alphaOffset, true);
|
||||
|
||||
draw_null(time, transform, {}, 0.0f, false);
|
||||
}
|
||||
};
|
||||
|
||||
auto onionskin_render = [&](float time, int count, int direction, vec3 color)
|
||||
{
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
float useTime = time + (float)(direction * i);
|
||||
if (useTime < 0.0f || useTime > animation->frameNum) continue;
|
||||
|
||||
float alphaOffset = (1.0f / (count + 1)) * i;
|
||||
render(animation, useTime, color, alphaOffset, true);
|
||||
}
|
||||
};
|
||||
|
||||
auto onionskins_render = [&](float time)
|
||||
{
|
||||
onionskin_render(time, settings.onionskinBeforeCount, -1, settings.onionskinBeforeColor);
|
||||
onionskin_render(time, settings.onionskinAfterCount, 1, settings.onionskinAfterColor);
|
||||
};
|
||||
|
||||
auto frameTime = document.frameTime > -1 && !playback.isPlaying ? document.frameTime : playback.time;
|
||||
|
||||
if (animation)
|
||||
{
|
||||
auto& drawOrder = settings.onionskinDrawOrder;
|
||||
auto& isEnabled = settings.onionskinIsEnabled;
|
||||
auto layeredOnions = settings.onionskinIsEnabled ? &onionskinSamples : nullptr;
|
||||
|
||||
if (drawOrder == draw_order::BELOW && isEnabled) onionskins_render(frameTime);
|
||||
|
||||
render(animation, frameTime);
|
||||
render(animation, frameTime, {}, 0.0f, layeredOnions);
|
||||
|
||||
if (auto overlayAnimation = anm2.animation_get(overlayIndex))
|
||||
render(overlayAnimation, frameTime, {}, 1.0f - math::uint8_to_float(overlayTransparency));
|
||||
|
||||
if (drawOrder == draw_order::ABOVE && isEnabled) onionskins_render(frameTime);
|
||||
}
|
||||
|
||||
unbind();
|
||||
|
||||
render_checker_background(ImGui::GetWindowDrawList(), min, max, -size - pan, CHECKER_SIZE);
|
||||
sync_checker_pan();
|
||||
render_checker_background(ImGui::GetWindowDrawList(), min, max, -size - checkerPan, CHECKER_SIZE);
|
||||
ImGui::Image(texture, to_imvec2(size));
|
||||
|
||||
isPreviewHovered = ImGui::IsItemHovered();
|
||||
@@ -510,7 +586,8 @@ namespace anm2ed::imgui
|
||||
|
||||
drawList->PushClipRect(clipMin, clipMax);
|
||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
|
||||
drawList->AddText(textPos, ImGui::GetColorU32(TRIGGER_TEXT_COLOR),
|
||||
auto triggerTextColor = isLightTheme ? TRIGGER_TEXT_COLOR_LIGHT : TRIGGER_TEXT_COLOR_DARK;
|
||||
drawList->AddText(textPos, ImGui::GetColorU32(triggerTextColor),
|
||||
anm2.content.events.at(trigger.eventID).name.c_str());
|
||||
ImGui::PopFont();
|
||||
drawList->PopClipRect();
|
||||
@@ -648,8 +725,12 @@ namespace anm2ed::imgui
|
||||
}
|
||||
|
||||
if (mouseWheel != 0 || isZoomIn || isZoomOut)
|
||||
{
|
||||
auto previousZoom = zoom;
|
||||
zoom_set(zoom, pan, mouseWheel != 0 ? vec2(mousePos) : vec2(),
|
||||
(mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
|
||||
if (zoom != previousZoom) hasPendingZoomPanAdjust = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
@@ -677,6 +758,8 @@ namespace anm2ed::imgui
|
||||
settings = savedSettings;
|
||||
overlayIndex = savedOverlayIndex;
|
||||
isSizeTrySet = true;
|
||||
hasPendingZoomPanAdjust = false;
|
||||
isCheckerPanInitialized = false;
|
||||
|
||||
if (settings.timelineIsSound) audioStream.capture_end(mixer);
|
||||
|
||||
@@ -693,6 +776,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
center_view();
|
||||
zoom = settings.previewStartZoom;
|
||||
reset_checker_pan();
|
||||
document.isAnimationPreviewSet = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace anm2ed::imgui
|
||||
glm::vec2 savedPan{};
|
||||
int savedOverlayIndex{};
|
||||
glm::ivec2 mousePos{};
|
||||
glm::vec2 checkerPan{};
|
||||
glm::vec2 checkerSyncPan{};
|
||||
float checkerSyncZoom{};
|
||||
bool isCheckerPanInitialized{};
|
||||
bool hasPendingZoomPanAdjust{};
|
||||
std::vector<resource::Texture> renderFrames{};
|
||||
|
||||
public:
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace anm2ed::imgui
|
||||
auto& beforeColor = settings.onionskinBeforeColor;
|
||||
auto& afterCount = settings.onionskinAfterCount;
|
||||
auto& afterColor = settings.onionskinAfterColor;
|
||||
auto& drawOrder = settings.onionskinDrawOrder;
|
||||
|
||||
if (ImGui::Begin("Onionskin", &settings.windowIsOnionskin))
|
||||
{
|
||||
@@ -38,14 +37,6 @@ namespace anm2ed::imgui
|
||||
|
||||
configure_widgets("Before", beforeCount, beforeColor);
|
||||
configure_widgets("After", afterCount, afterColor);
|
||||
|
||||
ImGui::Text("Draw Order");
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Below", &drawOrder, draw_order::BELOW);
|
||||
ImGui::SetItemTooltip("The onionskin frames will draw below the original frames.");
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Above", &drawOrder, draw_order::ABOVE);
|
||||
ImGui::SetItemTooltip("The onionskin frames will draw above the original frames.");
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
|
||||
@@ -50,6 +50,33 @@ namespace anm2ed::imgui
|
||||
auto& shaderTexture = resources.shaders[shader::TEXTURE];
|
||||
auto& dashedShader = resources.shaders[shader::DASHED];
|
||||
|
||||
auto reset_checker_pan = [&]()
|
||||
{
|
||||
checkerPan = pan;
|
||||
checkerSyncPan = pan;
|
||||
checkerSyncZoom = zoom;
|
||||
isCheckerPanInitialized = true;
|
||||
hasPendingZoomPanAdjust = false;
|
||||
};
|
||||
|
||||
auto sync_checker_pan = [&]()
|
||||
{
|
||||
if (!isCheckerPanInitialized)
|
||||
{
|
||||
reset_checker_pan();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pan != checkerSyncPan || zoom != checkerSyncZoom)
|
||||
{
|
||||
bool ignorePanDelta = hasPendingZoomPanAdjust && zoom != checkerSyncZoom;
|
||||
if (!ignorePanDelta) checkerPan += pan - checkerSyncPan;
|
||||
checkerSyncPan = pan;
|
||||
checkerSyncZoom = zoom;
|
||||
if (ignorePanDelta) hasPendingZoomPanAdjust = false;
|
||||
}
|
||||
};
|
||||
|
||||
auto center_view = [&]() { pan = -size * 0.5f; };
|
||||
|
||||
if (ImGui::Begin("Spritesheet Editor", &settings.windowIsSpritesheetEditor))
|
||||
@@ -173,7 +200,8 @@ namespace anm2ed::imgui
|
||||
|
||||
unbind();
|
||||
|
||||
render_checker_background(drawList, min, max, -size * 0.5f - pan, CHECKER_SIZE);
|
||||
sync_checker_pan();
|
||||
render_checker_background(drawList, min, max, -size * 0.5f - checkerPan, CHECKER_SIZE);
|
||||
if (!isTransparent) drawList->AddRectFilled(min, max, ImGui::GetColorU32(to_imvec4(vec4(backgroundColor, 1.0f))));
|
||||
drawList->AddImage(texture, min, max);
|
||||
ImGui::InvisibleButton("##Spritesheet Editor", to_imvec2(size));
|
||||
@@ -236,15 +264,15 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (gridSize.x != 0)
|
||||
{
|
||||
auto offsetX = static_cast<float>(gridOffset.x);
|
||||
auto sizeX = static_cast<float>(gridSize.x);
|
||||
auto offsetX = (float)(gridOffset.x);
|
||||
auto sizeX = (float)(gridSize.x);
|
||||
minPoint.x = std::floor((minPoint.x - offsetX) / sizeX) * sizeX + offsetX;
|
||||
maxPoint.x = std::ceil((maxPoint.x - offsetX) / sizeX) * sizeX + offsetX;
|
||||
}
|
||||
if (gridSize.y != 0)
|
||||
{
|
||||
auto offsetY = static_cast<float>(gridOffset.y);
|
||||
auto sizeY = static_cast<float>(gridSize.y);
|
||||
auto offsetY = (float)(gridOffset.y);
|
||||
auto sizeY = (float)(gridSize.y);
|
||||
minPoint.y = std::floor((minPoint.y - offsetY) / sizeY) * sizeY + offsetY;
|
||||
maxPoint.y = std::ceil((maxPoint.y - offsetY) / sizeY) * sizeY + offsetY;
|
||||
}
|
||||
@@ -380,7 +408,9 @@ namespace anm2ed::imgui
|
||||
if (auto spritesheet = document.spritesheet_get(); spritesheet && mouseWheel == 0)
|
||||
focus = spritesheet->texture.size / 2;
|
||||
|
||||
auto previousZoom = zoom;
|
||||
zoom_set(zoom, pan, focus, (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
|
||||
if (zoom != previousZoom) hasPendingZoomPanAdjust = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,6 +422,7 @@ namespace anm2ed::imgui
|
||||
zoom = settings.editorStartZoom;
|
||||
set();
|
||||
center_view();
|
||||
reset_checker_pan();
|
||||
document.isSpritesheetEditorSet = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace anm2ed::imgui
|
||||
glm::vec2 mousePos{};
|
||||
glm::vec2 previousMousePos{};
|
||||
glm::vec2 cropAnchor{};
|
||||
glm::vec2 checkerPan{};
|
||||
glm::vec2 checkerSyncPan{};
|
||||
float checkerSyncZoom{};
|
||||
bool isCheckerPanInitialized{};
|
||||
bool hasPendingZoomPanAdjust{};
|
||||
|
||||
public:
|
||||
SpritesheetEditor();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "timeline.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
@@ -40,6 +41,7 @@ namespace anm2ed::imgui
|
||||
constexpr auto TIMELINE_PLAYHEAD_RECT_COLOR_DARK = ImVec4(0.60f, 0.45f, 0.30f, 1.0f);
|
||||
constexpr auto TIMELINE_PLAYHEAD_RECT_COLOR_LIGHT = ImVec4(0.8353f, 0.8353f, 0.7294f, 1.0f);
|
||||
constexpr auto TIMELINE_TICK_COLOR_LIGHT = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
constexpr auto TIMELINE_CHILD_BG_COLOR_LIGHT = ImVec4(0.5490f, 0.5490f, 0.5882f, 1.0f);
|
||||
constexpr auto TIMELINE_TEXT_COLOR_LIGHT = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
constexpr glm::vec4 FRAME_COLOR_LIGHT_BASE[] = {{0.80f, 0.80f, 0.80f, 1.0f},
|
||||
@@ -48,18 +50,18 @@ namespace anm2ed::imgui
|
||||
{0.6157f, 1.0f, 0.5882f, 1.0f},
|
||||
{1.0f, 0.5882f, 0.8314f, 1.0f}};
|
||||
constexpr glm::vec4 FRAME_COLOR_LIGHT_ACTIVE[] = {{0.74f, 0.74f, 0.74f, 1.0f},
|
||||
{0.4700f, 0.6830f, 0.95f, 1.0f},
|
||||
{0.96f, 0.956f, 0.548f, 1.0f},
|
||||
{0.5757f, 0.95f, 0.5482f, 1.0f},
|
||||
{0.94f, 0.5482f, 0.7814f, 1.0f}};
|
||||
{0.0980f, 0.3765f, 0.6431f, 1.0f},
|
||||
{1.0f, 0.5255f, 0.3333f, 1.0f},
|
||||
{0.3686f, 0.5765f, 0.2353f, 1.0f},
|
||||
{0.6118f, 0.2039f, 0.2745f, 1.0f}};
|
||||
constexpr glm::vec4 FRAME_COLOR_LIGHT_HOVERED[] = {{0.84f, 0.84f, 0.84f, 1.0f},
|
||||
{0.5616f, 0.7733f, 1.0f, 1.0f},
|
||||
{1.0f, 0.9361f, 0.5482f, 1.0f},
|
||||
{0.6557f, 1.0f, 0.6282f, 1.0f},
|
||||
{1.0f, 0.6282f, 0.8714f, 1.0f}};
|
||||
{0.0752f, 0.2887f, 0.4931f, 1.0f},
|
||||
{0.85f, 0.4467f, 0.2833f, 1.0f},
|
||||
{0.2727f, 0.4265f, 0.1741f, 1.0f},
|
||||
{0.4618f, 0.1539f, 0.2072f, 1.0f}};
|
||||
constexpr glm::vec4 ITEM_COLOR_LIGHT_BASE[] = {{0.3059f, 0.3255f, 0.5412f, 1.0f},
|
||||
{0.3333f, 0.5725f, 0.8392f, 1.0f},
|
||||
{0.8706f, 0.4549f, 0.2353f, 1.0f},
|
||||
{1.0f, 0.5412f, 0.3412f, 1.0f},
|
||||
{0.5255f, 0.8471f, 0.4588f, 1.0f},
|
||||
{0.7961f, 0.3882f, 0.5412f, 1.0f}};
|
||||
constexpr glm::vec4 ITEM_COLOR_LIGHT_ACTIVE[] = {{0.3459f, 0.3655f, 0.5812f, 1.0f},
|
||||
@@ -67,6 +69,11 @@ namespace anm2ed::imgui
|
||||
{0.9106f, 0.4949f, 0.2753f, 1.0f},
|
||||
{0.5655f, 0.8871f, 0.4988f, 1.0f},
|
||||
{0.8361f, 0.4282f, 0.5812f, 1.0f}};
|
||||
constexpr glm::vec4 ITEM_COLOR_LIGHT_SELECTED[] = {{0.74f, 0.74f, 0.74f, 1.0f},
|
||||
{0.2039f, 0.4549f, 0.7176f, 1.0f},
|
||||
{0.8745f, 0.4392f, 0.2275f, 1.0f},
|
||||
{0.3765f, 0.6784f, 0.2980f, 1.0f},
|
||||
{0.6353f, 0.2235f, 0.3647f, 1.0f}};
|
||||
|
||||
constexpr auto FRAME_MULTIPLE = 5;
|
||||
constexpr auto FRAME_DRAG_PAYLOAD_ID = "Frame Drag Drop";
|
||||
@@ -302,7 +309,15 @@ namespace anm2ed::imgui
|
||||
auto iconTintCurrent = isLightTheme && type == anm2::NONE ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : itemIconTint;
|
||||
auto baseColorVec = item_color_vec(type);
|
||||
auto activeColorVec = item_color_active_vec(type);
|
||||
auto colorVec = isActive ? activeColorVec : baseColorVec;
|
||||
bool isTypeNone = type == anm2::NONE;
|
||||
auto colorVec = baseColorVec;
|
||||
if (isActive && !isTypeNone)
|
||||
{
|
||||
if (isLightTheme)
|
||||
colorVec = ITEM_COLOR_LIGHT_SELECTED[type_index(type)];
|
||||
else
|
||||
colorVec = activeColorVec;
|
||||
}
|
||||
auto color = to_imvec4(colorVec);
|
||||
color = !isVisible ? to_imvec4(colorVec * COLOR_HIDDEN_MULTIPLIER) : color;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, color);
|
||||
@@ -319,7 +334,7 @@ namespace anm2ed::imgui
|
||||
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
|
||||
if (type != anm2::NONE)
|
||||
if (!isTypeNone)
|
||||
{
|
||||
ImGui::SetCursorPos(to_imvec2(to_vec2(cursorPos) - to_vec2(style.ItemSpacing)));
|
||||
|
||||
@@ -618,6 +633,8 @@ namespace anm2ed::imgui
|
||||
ImGui::PushID(index);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
bool isDefaultChild = type == anm2::NONE;
|
||||
if (isLightTheme && isDefaultChild) ImGui::PushStyleColor(ImGuiCol_ChildBg, TIMELINE_CHILD_BG_COLOR_LIGHT);
|
||||
|
||||
if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
@@ -628,7 +645,7 @@ namespace anm2ed::imgui
|
||||
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
auto border = ImGui::GetStyle().FrameBorderSize;
|
||||
auto border = glm::max(0.5f, ImGui::GetStyle().FrameBorderSize * 0.5f);
|
||||
auto borderLineLength = frameSize.y / 5;
|
||||
auto frameMin = std::max(0, (int)std::floor(scroll.x / frameSize.x) - 1);
|
||||
auto frameMax = std::min(anm2::FRAME_NUM_MAX, (int)std::ceil((scroll.x + clipMax.x) / frameSize.x) + 1);
|
||||
@@ -657,11 +674,11 @@ namespace anm2ed::imgui
|
||||
auto frameScreenPos = ImVec2(cursorScreenPos.x + frameSize.x * (float)i, cursorScreenPos.y);
|
||||
|
||||
drawList->AddRect(frameScreenPos, ImVec2(frameScreenPos.x + border, frameScreenPos.y + borderLineLength),
|
||||
ImGui::GetColorU32(timelineTickColor));
|
||||
ImGui::GetColorU32(timelineTickColor), 0, 0, 0.5f);
|
||||
|
||||
drawList->AddRect(ImVec2(frameScreenPos.x, frameScreenPos.y + frameSize.y - borderLineLength),
|
||||
ImVec2(frameScreenPos.x + border, frameScreenPos.y + frameSize.y),
|
||||
ImGui::GetColorU32(timelineTickColor));
|
||||
ImGui::GetColorU32(timelineTickColor), 0, 0, 0.5);
|
||||
|
||||
if (i % FRAME_MULTIPLE == 0)
|
||||
{
|
||||
@@ -1021,6 +1038,7 @@ namespace anm2ed::imgui
|
||||
context_menu();
|
||||
|
||||
ImGui::EndChild();
|
||||
if (isLightTheme && isDefaultChild) ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
index++;
|
||||
|
||||
@@ -43,6 +43,8 @@ namespace anm2ed::imgui
|
||||
}
|
||||
};
|
||||
|
||||
auto iconTint = settings.theme == theme::LIGHT ? ImVec4(0.0f, 0.0f, 0.0f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
for (int i = 0; i < tool::COUNT; i++)
|
||||
{
|
||||
auto& info = tool::INFO[i];
|
||||
@@ -65,7 +67,9 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (i == tool::UNDO) ImGui::BeginDisabled(!document.is_able_to_undo());
|
||||
if (i == tool::REDO) ImGui::BeginDisabled(!document.is_able_to_redo());
|
||||
if (ImGui::ImageButton(info.label, resources.icons[info.icon].id, to_imvec2(size))) tool_use((tool::Type)i);
|
||||
if (ImGui::ImageButton(info.label, resources.icons[info.icon].id, to_imvec2(size), ImVec2(0, 0), ImVec2(1, 1),
|
||||
ImVec4(0, 0, 0, 0), iconTint))
|
||||
tool_use((tool::Type)i);
|
||||
if (i == tool::UNDO) ImGui::EndDisabled();
|
||||
if (i == tool::REDO) ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "imgui_.h"
|
||||
|
||||
#include "snapshots.h"
|
||||
#include "socket.h"
|
||||
|
||||
using namespace anm2ed::types;
|
||||
@@ -110,6 +111,7 @@ namespace anm2ed
|
||||
}
|
||||
|
||||
settings = Settings(settings_path());
|
||||
SnapshotStack::max_size_set(settings.fileSnapshotStackSize);
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
@@ -184,11 +186,6 @@ namespace anm2ed
|
||||
|
||||
imgui::theme_set((theme::Type)settings.theme);
|
||||
|
||||
if (settings.theme == theme::DARK)
|
||||
ImGui::StyleColorsDark();
|
||||
else if (settings.theme == theme::LIGHT)
|
||||
ImGui::StyleColorsClassic();
|
||||
|
||||
ImGui_ImplSDL3_InitForOpenGL(window, glContext);
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace anm2ed
|
||||
X(FILE_IS_AUTOSAVE, fileIsAutosave, "Autosave", BOOL, true) \
|
||||
X(FILE_AUTOSAVE_TIME, fileAutosaveTime, "Autosave Time", INT, 1) \
|
||||
X(FILE_IS_WARN_OVERWRITE, fileIsWarnOverwrite, "Warn on Overwrite", BOOL, true) \
|
||||
X(FILE_SNAPSHOT_STACK_SIZE, fileSnapshotStackSize, "Snapshot Stack Size", INT, 50) \
|
||||
\
|
||||
X(KEYBOARD_REPEAT_DELAY, keyboardRepeatDelay, "Repeat Delay", FLOAT, 0.300f) \
|
||||
X(KEYBOARD_REPEAT_RATE, keyboardRepeatRate, "Repeat Rate", FLOAT, 0.050f) \
|
||||
@@ -136,7 +137,6 @@ namespace anm2ed
|
||||
X(TIMELINE_IS_SOUND, timelineIsSound, "Sound", BOOL, true) \
|
||||
\
|
||||
X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, "Enabled", BOOL, false) \
|
||||
X(ONIONSKIN_DRAW_ORDER, onionskinDrawOrder, "Draw Order", INT, 0) \
|
||||
X(ONIONSKIN_BEFORE_COUNT, onionskinBeforeCount, "Frames", INT, 0) \
|
||||
X(ONIONSKIN_AFTER_COUNT, onionskinAfterCount, "Frames", INT, 0) \
|
||||
X(ONIONSKIN_BEFORE_COLOR, onionskinBeforeColor, "Color", VEC3, types::color::RED) \
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
#include "snapshots.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace anm2ed::snapshots;
|
||||
|
||||
namespace anm2ed
|
||||
{
|
||||
bool SnapshotStack::is_empty() { return top == 0; }
|
||||
int SnapshotStack::maxSize = snapshots::MAX;
|
||||
|
||||
bool SnapshotStack::is_empty() { return stack.empty(); }
|
||||
|
||||
void SnapshotStack::push(const Snapshot& snapshot)
|
||||
{
|
||||
if (top >= MAX)
|
||||
if (maxSize <= 0)
|
||||
{
|
||||
for (int i = 0; i < MAX - 1; i++)
|
||||
snapshots[i] = snapshots[i + 1];
|
||||
top = MAX - 1;
|
||||
stack.clear();
|
||||
return;
|
||||
}
|
||||
snapshots[top++] = snapshot;
|
||||
if ((int)stack.size() >= maxSize) stack.pop_front();
|
||||
stack.push_back(snapshot);
|
||||
}
|
||||
|
||||
Snapshot* SnapshotStack::pop()
|
||||
std::optional<Snapshot> SnapshotStack::pop()
|
||||
{
|
||||
if (is_empty()) return nullptr;
|
||||
return &snapshots[--top];
|
||||
if (is_empty()) return std::nullopt;
|
||||
auto snapshot = stack.back();
|
||||
stack.pop_back();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void SnapshotStack::clear() { top = 0; }
|
||||
void SnapshotStack::clear() { stack.clear(); }
|
||||
|
||||
void SnapshotStack::trim_to_limit()
|
||||
{
|
||||
if (maxSize <= 0)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
while ((int)stack.size() > maxSize)
|
||||
stack.pop_front();
|
||||
}
|
||||
|
||||
void SnapshotStack::max_size_set(int value) { maxSize = std::max(0, value); }
|
||||
|
||||
int SnapshotStack::max_size_get() { return maxSize; }
|
||||
|
||||
void Snapshots::push(const Snapshot& snapshot)
|
||||
{
|
||||
@@ -54,4 +76,10 @@ namespace anm2ed
|
||||
undoStack.clear();
|
||||
redoStack.clear();
|
||||
}
|
||||
|
||||
void Snapshots::apply_limit()
|
||||
{
|
||||
undoStack.trim_to_limit();
|
||||
redoStack.trim_to_limit();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
|
||||
#include "anm2/anm2.h"
|
||||
#include "playback.h"
|
||||
#include "storage.h"
|
||||
@@ -34,13 +37,20 @@ namespace anm2ed
|
||||
class SnapshotStack
|
||||
{
|
||||
public:
|
||||
Snapshot snapshots[snapshots::MAX];
|
||||
int top{};
|
||||
SnapshotStack() = default;
|
||||
|
||||
bool is_empty();
|
||||
void push(const Snapshot&);
|
||||
Snapshot* pop();
|
||||
std::optional<Snapshot> pop();
|
||||
void clear();
|
||||
void trim_to_limit();
|
||||
|
||||
static void max_size_set(int);
|
||||
static int max_size_get();
|
||||
|
||||
private:
|
||||
static int maxSize;
|
||||
std::deque<Snapshot> stack;
|
||||
};
|
||||
|
||||
class Snapshots
|
||||
@@ -55,5 +65,6 @@ namespace anm2ed
|
||||
void undo();
|
||||
void redo();
|
||||
void reset();
|
||||
void apply_limit();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,15 +4,6 @@
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
namespace anm2ed::types::draw_order
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
BELOW,
|
||||
ABOVE
|
||||
};
|
||||
}
|
||||
|
||||
namespace anm2ed::types::theme
|
||||
{
|
||||
#define THEMES \
|
||||
|
||||
Reference in New Issue
Block a user