some new crap

This commit is contained in:
2026-06-20 15:06:04 -04:00
parent 1005b5e4ce
commit 4db3e28ced
11 changed files with 457 additions and 36 deletions
+29 -8
View File
@@ -34,6 +34,7 @@ using namespace glm;
namespace anm2ed::imgui
{
constexpr auto NULL_COLOR = vec4(0.0f, 0.0f, 1.0f, 0.90f);
constexpr auto SELECTED_LAYER_BORDER_COLOR = vec4(1.0f, 0.1059f, 0.5882f, 1.0f);
constexpr auto TARGET_SIZE = vec2(32, 32);
constexpr auto POINT_SIZE = vec2(4, 4);
constexpr auto TRIGGER_TEXT_COLOR_DARK = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
@@ -740,6 +741,21 @@ namespace anm2ed::imgui
}
auto referenceItemType = static_cast<ItemType>(reference.itemType);
auto is_layer_animation_selected = [&](int id)
{
if (reference.animationIndex != -1 && referenceItemType == ItemType::LAYER && reference.itemID == id)
return true;
for (auto itemReference : document.items.references)
if (itemReference.animationIndex == reference.animationIndex && itemReference.itemType == LAYER &&
itemReference.itemID == id)
return true;
for (auto frameReference : document.frames.references)
if (frameReference.animationIndex == reference.animationIndex && frameReference.itemType == LAYER &&
frameReference.itemID == id)
return true;
return document.frames.references.empty() && !frames.empty() && referenceItemType == ItemType::LAYER &&
reference.itemID == id;
};
auto render = [&](Element* animation, float time, vec3 colorOffset = {}, float alphaOffset = {},
const std::vector<OnionskinSample>* layeredOnions = nullptr, bool isIndexMode = false)
@@ -873,7 +889,9 @@ namespace anm2ed::imgui
texture_render(shaderTexture, texture.id, layerTransform, frameTint, frameColorOffset, vertices.data());
auto color = isOnion ? vec4(sampleColor, 1.0f - sampleAlpha) : color::RED;
auto color = isOnion ? vec4(sampleColor, 1.0f - sampleAlpha)
: is_layer_animation_selected(id) ? SELECTED_LAYER_BORDER_COLOR
: color::RED;
if (isBorder) rect_render(shaderLine, layerTransform, layerModel, color);
@@ -1048,19 +1066,22 @@ namespace anm2ed::imgui
auto useTool = tool;
auto step = (float)(isMod ? STEP_FAST : STEP);
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
auto is_frame_reference_valid = [&](const Reference& frameReference)
{
if (frameReference.itemType == NONE || frameReference.itemType == TRIGGER || frameReference.frameIndex < 0)
return false;
auto itemType = static_cast<ItemType>(frameReference.itemType);
auto item = anm2.element_get(frameReference.animationIndex, itemType, frameReference.itemID);
return item && track_frame_get(*item, frameReference.frameIndex);
};
auto selected_frame_references_get = [&]()
{
std::set<Reference> result = document.frames.references;
for (auto frameIndex : frames)
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
std::erase_if(result,
[](const Reference& frameReference)
{
return frameReference.itemType == NONE || frameReference.itemType == TRIGGER ||
frameReference.frameIndex < 0;
});
if (result.empty() && reference.itemType != NONE && reference.itemType != TRIGGER &&
reference.frameIndex != -1)
[&](const Reference& frameReference) { return !is_frame_reference_valid(frameReference); });
if (result.empty() && is_frame_reference_valid(reference))
result.insert(reference);
return result;
};
+23 -15
View File
@@ -351,24 +351,32 @@ namespace anm2ed::imgui
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
auto frame =
anm2.element_get(reference.animationIndex, referenceItemType, reference.frameIndex, reference.itemID);
auto item = anm2.element_get(reference.animationIndex, referenceItemType, reference.itemID);
auto layer = anm2.element_get(ElementType::LAYER_ELEMENT, reference.itemID);
bool isReferenceLayerOnSpritesheet =
frame && reference.itemID > -1 && layer && layer->spritesheetId == referenceSpritesheet;
auto is_frame_reference_valid = [&](const Reference& frameReference)
{
if (frameReference.frameIndex < 0 || frameReference.itemType != LAYER) return false;
auto frameLayer = anm2.element_get(ElementType::LAYER_ELEMENT, frameReference.itemID);
if (!frameLayer || frameLayer->spritesheetId != referenceSpritesheet) return false;
auto frameItem = anm2.element_get(frameReference.animationIndex, ItemType::LAYER, frameReference.itemID);
return frameItem && track_frame_get(*frameItem, frameReference.frameIndex);
};
auto selectedFrameReferences = document.frames.references;
for (auto frameIndex : document.frames.selection)
selectedFrameReferences.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
std::erase_if(selectedFrameReferences, [&](const Reference& frameReference)
{
if (frameReference.frameIndex < 0) return true;
if (frameReference.itemType != LAYER) return true;
auto frameLayer = document.anm2.element_get(ElementType::LAYER_ELEMENT, frameReference.itemID);
return !frameLayer || frameLayer->spritesheetId != referenceSpritesheet;
});
if (selectedFrameReferences.empty() && reference.frameIndex != -1 && isReferenceLayerOnSpritesheet)
{ return !is_frame_reference_valid(frameReference); });
if (selectedFrameReferences.empty() && is_frame_reference_valid(reference))
selectedFrameReferences.insert(reference);
auto editReference = reference;
if (!selectedFrameReferences.contains(reference) && !selectedFrameReferences.empty())
editReference = *selectedFrameReferences.begin();
auto editReferenceItemType = static_cast<ItemType>(editReference.itemType);
auto frame =
anm2.element_get(editReference.animationIndex, editReferenceItemType, editReference.frameIndex,
editReference.itemID);
auto item = anm2.element_get(editReference.animationIndex, editReferenceItemType, editReference.itemID);
auto layer = anm2.element_get(ElementType::LAYER_ELEMENT, editReference.itemID);
bool isReferenceLayerOnSpritesheet =
frame && editReference.itemID > -1 && layer && layer->spritesheetId == referenceSpritesheet;
auto useTool = tool;
auto step = (float)(isMod ? STEP_FAST : STEP);
auto stepX = isGridSnap ? step * gridSize.x : step;
@@ -442,8 +450,8 @@ namespace anm2ed::imgui
};
auto frame_change_from_current_apply = [&](auto frameChangeGet, ChangeType changeType = ChangeType::ADJUST)
{
auto queuedReference = reference;
auto queuedReferenceItemType = referenceItemType;
auto queuedReference = editReference;
auto queuedReferenceItemType = editReferenceItemType;
auto queuedFrameReferences = selectedFrameReferences;
manager.command_push({manager.selected,
[=](Manager&, Document& document)
+312 -2
View File
@@ -11,6 +11,7 @@
#include "actions.hpp"
#include "log.hpp"
#include "math.hpp"
#include "toast.hpp"
#include "util/imgui/draw.hpp"
#include "util/imgui/input.hpp"
@@ -110,6 +111,12 @@ namespace anm2ed::imgui
bool isGroup{};
};
struct RootFrameSpan
{
int start{};
int end{};
};
void Timeline::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
{
auto& document = *manager.get();
@@ -227,6 +234,13 @@ namespace anm2ed::imgui
return left.animationIndex == right.animationIndex && left.itemType == right.itemType &&
left.itemID == right.itemID;
};
auto is_frame_reference_valid_for = [&](Document& targetDocument, const Reference& frameReference)
{
if (frameReference.itemType == NONE || frameReference.frameIndex < 0) return false;
auto item = command_item_get(targetDocument, frameReference.animationIndex, frameReference.itemType,
frameReference.itemID);
return item && track_frame_get(*item, frameReference.frameIndex);
};
auto group_selection_reset_for = [this](Document& targetDocument)
{
targetDocument.groupReferences.clear();
@@ -235,9 +249,15 @@ namespace anm2ed::imgui
auto frame_references_for_current_get = [&]()
{
std::set<Reference> result = frames.references;
std::erase_if(result, [&](const Reference& frameReference)
{ return !is_frame_reference_valid_for(document, frameReference); });
if (result.empty())
{
for (auto frameIndex : frames.selection)
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
std::erase_if(result, [&](const Reference& frameReference)
{ return !is_frame_reference_valid_for(document, frameReference); });
}
return result;
};
auto item_references_for_current_get = [&]()
@@ -376,6 +396,38 @@ namespace anm2ed::imgui
frameFocusIndex = targetReference.frameIndex;
frameFocusRequested = targetReference.frameIndex >= 0;
};
auto frames_reference_normalize_for = [&](Document& targetDocument)
{
std::erase_if(targetDocument.frames.references, [&](const Reference& frameReference)
{ return !is_frame_reference_valid_for(targetDocument, frameReference); });
if (targetDocument.frames.references.empty() && !targetDocument.frames.selection.empty())
{
for (auto frameIndex : targetDocument.frames.selection)
targetDocument.frames.references.insert(
{targetDocument.reference.animationIndex, targetDocument.reference.itemType,
targetDocument.reference.itemID, frameIndex});
std::erase_if(targetDocument.frames.references, [&](const Reference& frameReference)
{ return !is_frame_reference_valid_for(targetDocument, frameReference); });
}
if (!targetDocument.frames.references.empty())
{
targetDocument.items.references.clear();
for (auto frameReference : targetDocument.frames.references)
targetDocument.items.references.insert(item_reference_from_frame_get(frameReference));
if (!targetDocument.frames.references.contains(targetDocument.reference) ||
!is_frame_reference_valid_for(targetDocument, targetDocument.reference))
targetDocument.reference = *targetDocument.frames.references.begin();
frames_selection_sync_for(targetDocument);
return;
}
if (targetDocument.reference.frameIndex >= 0 &&
!is_frame_reference_valid_for(targetDocument, targetDocument.reference))
targetDocument.reference.frameIndex = -1;
};
frames_reference_normalize_for(document);
auto reference_clear_for = [=](Document& targetDocument)
{
targetDocument.reference = {targetDocument.reference.animationIndex};
@@ -650,6 +702,171 @@ namespace anm2ed::imgui
});
};
auto selected_root_frame_references_get = [&]()
{
auto selectedFrames = frame_references_for_current_get();
std::erase_if(selectedFrames,
[](const Reference& frameReference) { return frameReference.itemType != ROOT; });
return selectedFrames;
};
auto item_references_for_bake_into_other_frames_get = [](const Document& targetDocument,
const Element& targetAnimation,
BakeIntoOtherFramesTarget target, bool isLayers,
bool isNulls)
{
std::set<Reference> result{};
auto animationIndex = targetDocument.reference.animationIndex;
if (target == BakeIntoOtherFramesTarget::CURRENT_SELECTION)
{
for (auto itemReference : targetDocument.items.references)
{
itemReference.frameIndex = -1;
if (itemReference.itemType == LAYER || itemReference.itemType == NULL_) result.insert(itemReference);
}
return result;
}
auto add_items = [&](const Element& container, int itemType)
{
auto trackType = item_type_to_track_type_get(static_cast<ItemType>(itemType));
auto add_item = [&](auto&& self, const Element& item) -> void
{
if (item.type == ElementType::GROUP)
{
for (const auto& child : item.children)
self(self, child);
return;
}
if (item.type != trackType) return;
auto itemID = itemType == LAYER ? item.layerId : item.nullId;
result.insert({animationIndex, itemType, itemID, -1});
};
for (const auto& item : container.children)
add_item(add_item, item);
};
if (isLayers)
if (auto layerAnimations = element_child_first_get(targetAnimation, ElementType::LAYER_ANIMATIONS))
add_items(*layerAnimations, LAYER);
if (isNulls)
if (auto nullAnimations = element_child_first_get(targetAnimation, ElementType::NULL_ANIMATIONS))
add_items(*nullAnimations, NULL_);
return result;
};
auto is_bake_into_other_frames_ready = [&]()
{
auto selectedRootFrames = selected_root_frame_references_get();
if (selectedRootFrames.empty() || !animation) return false;
auto targetItems = item_references_for_bake_into_other_frames_get(
document, *animation, bakeIntoOtherFramesTarget, isBakeIntoOtherFramesLayers, isBakeIntoOtherFramesNulls);
return !targetItems.empty();
};
auto frame_root_transform_apply = [](Element& frame, const Element& rootFrame, bool isRoundScale,
bool isRoundRotation, bool isUseRootPivot)
{
auto rootScale = math::percent_to_unit(rootFrame.scale);
auto pivot = isUseRootPivot ? rootFrame.position : glm::vec2();
auto offset = (frame.position - pivot) * rootScale;
auto radians = glm::radians(rootFrame.rotation);
auto cos = std::cos(radians);
auto sin = std::sin(radians);
frame.position =
rootFrame.position + glm::vec2(offset.x * cos - offset.y * sin, offset.x * sin + offset.y * cos);
frame.scale *= rootScale;
frame.rotation += rootFrame.rotation;
if (isRoundScale) frame.scale = glm::round(frame.scale);
if (isRoundRotation) frame.rotation = std::round(frame.rotation);
};
auto bake_into_other_frames = [&]()
{
auto selectedRootFrames = selected_root_frame_references_get();
if (selectedRootFrames.empty()) return;
auto target = bakeIntoOtherFramesTarget;
auto isLayers = isBakeIntoOtherFramesLayers;
auto isNulls = isBakeIntoOtherFramesNulls;
auto isRoundScale = settings.bakeIsRoundScale;
auto isRoundRotation = settings.bakeIsRoundRotation;
auto isMatchRootInterpolation = settings.bakeIsMatchRootInterpolation;
auto isUseRootPivot = settings.bakeIsUseRootPivot;
edit_command_push(EDIT_BAKE_INTO_OTHER_FRAMES, Document::FRAMES,
[=](Manager&, Document& document) mutable
{
auto animation = command_animation_get(document, document.reference.animationIndex);
if (!animation) return;
auto root = command_item_get(document, document.reference.animationIndex, ROOT, -1);
if (!root) return;
std::vector<RootFrameSpan> spans{};
for (auto rootReference : selectedRootFrames)
{
auto rootFrame = command_frame_get(document, rootReference);
if (!rootFrame) continue;
auto start = (int)frame_time_from_index_get(*root, rootReference.frameIndex);
spans.push_back({start, start + rootFrame->duration});
}
if (spans.empty()) return;
auto targetItems = item_references_for_bake_into_other_frames_get(
document, *animation, target, isLayers, isNulls);
if (targetItems.empty()) return;
for (auto itemReference : targetItems)
{
auto item = command_item_get(document, itemReference.animationIndex,
itemReference.itemType, itemReference.itemID);
if (!item) continue;
if (isMatchRootInterpolation)
{
std::vector<int> bakeIndices{};
for (auto [i, frame] : std::views::enumerate(item->children))
{
auto frameStart = (int)frame_time_from_index_get(*item, (int)i);
for (auto span : spans)
if (frameStart >= span.start && frameStart < span.end)
{
bakeIndices.push_back((int)i);
break;
}
}
for (auto it = bakeIndices.rbegin(); it != bakeIndices.rend(); ++it)
frame_bake(*item, *it, FRAME_DURATION_MIN, isRoundScale, isRoundRotation);
}
for (auto [i, frame] : std::views::enumerate(item->children))
{
auto frameStart = (int)frame_time_from_index_get(*item, (int)i);
bool isInsideSpan{};
for (auto span : spans)
if (frameStart >= span.start && frameStart < span.end)
{
isInsideSpan = true;
break;
}
if (!isInsideSpan) continue;
auto rootFrame = frame_generate(*root, (float)frameStart);
frame_root_transform_apply(frame, rootFrame, isRoundScale, isRoundRotation,
isUseRootPivot);
}
}
for (auto rootReference : selectedRootFrames)
{
auto rootFrame = command_frame_get(document, rootReference);
if (!rootFrame) continue;
rootFrame->position = {};
rootFrame->scale = {100.0f, 100.0f};
rootFrame->rotation = {};
}
});
};
auto frame_split = [&]()
{
auto selectedFrames = frame_references_for_current_get();
@@ -1448,6 +1665,7 @@ namespace anm2ed::imgui
auto selectedBakeFrames = selectedFrames;
std::erase_if(selectedBakeFrames,
[](const Reference& frameReference) { return frameReference.itemType == TRIGGER; });
auto selectedRootFrames = selected_root_frame_references_get();
bool isMakeRegion = frame && reference.itemType == LAYER && reference.itemID != -1 && frame->regionId == -1 &&
layer_get(reference.itemID) && spritesheet_get(layer_get(reference.itemID)->spritesheetId);
Actions actions{};
@@ -1469,6 +1687,10 @@ namespace anm2ed::imgui
.shortcut = SHORTCUT_BAKE,
.isEnabled = [=]() { return !selectedBakeFrames.empty(); },
.run = [&]() { frames_bake(); }});
actions.add({.label = LABEL_BAKE_INTO_OTHER_FRAMES,
.shortcut = -1,
.isEnabled = [=]() { return !selectedRootFrames.empty(); },
.run = [&]() { bakeIntoOtherFramesPopup.open(); }});
actions.add({.label = LABEL_FIT_ANIMATION_LENGTH,
.shortcut = SHORTCUT_FIT,
.isEnabled = [&]() { return animation && animation->frameNum != animation_length_get(*animation); },
@@ -2763,6 +2985,23 @@ namespace anm2ed::imgui
draggedFrameIndex = (int)i;
draggedFrameStart = hoveredTime;
if (type != TRIGGER) draggedFrameStartDuration = frame.duration;
draggedFrameStartDurations.clear();
if (type != TRIGGER)
{
auto selectedReferences = frame_references_for_current_get();
if (!selectedReferences.contains(frameReference)) selectedReferences = {frameReference};
std::erase_if(selectedReferences, [](const Reference& selectedReference)
{ return selectedReference.itemType == TRIGGER; });
if (selectedReferences.empty()) selectedReferences = {frameReference};
for (auto selectedReference : selectedReferences)
{
auto selectedItem = item_get(selectedReference.itemType, selectedReference.itemID);
auto selectedFrame =
selectedItem ? track_frame_get(*selectedItem, selectedReference.frameIndex) : nullptr;
if (selectedFrame)
draggedFrameStartDurations.push_back({selectedReference, selectedFrame->duration});
}
}
draggedFrameStartMouseX = ImGui::GetIO().MousePos.x;
draggedFrameWidth = frameSize.x;
}
@@ -2907,6 +3146,7 @@ namespace anm2ed::imgui
auto targetType = draggedFrameType;
auto targetIndex = draggedFrameIndex;
auto targetStartDuration = draggedFrameStartDuration;
auto targetStartDurations = draggedFrameStartDurations;
auto targetHoveredTime = hoveredTime;
auto targetDurationDelta = durationDelta;
auto isPlaybackClamp = settings.playbackIsClamp;
@@ -2932,8 +3172,20 @@ namespace anm2ed::imgui
}
else
{
frame->duration = glm::clamp(targetStartDuration + targetDurationDelta, FRAME_DURATION_MIN,
FRAME_DURATION_MAX);
if (targetStartDurations.empty())
{
frame->duration = glm::clamp(targetStartDuration + targetDurationDelta,
FRAME_DURATION_MIN, FRAME_DURATION_MAX);
return;
}
for (auto frameDuration : targetStartDurations)
{
auto targetFrame = command_frame_get(document, frameDuration.reference);
if (!targetFrame) continue;
targetFrame->duration = glm::clamp(frameDuration.duration + targetDurationDelta,
FRAME_DURATION_MIN, FRAME_DURATION_MAX);
}
}
});
}
@@ -2955,6 +3207,7 @@ namespace anm2ed::imgui
draggedFrameIndex = -1;
draggedFrameStart = -1;
draggedFrameStartDuration = -1;
draggedFrameStartDurations.clear();
draggedFrameStartMouseX = 0.0f;
draggedFrameWidth = 0.0f;
isDraggedFrameSnapshot = false;
@@ -3354,6 +3607,63 @@ namespace anm2ed::imgui
ImGui::EndPopup();
}
bakeIntoOtherFramesPopup.trigger();
if (ImGui::BeginPopupModal(bakeIntoOtherFramesPopup.label(), &bakeIntoOtherFramesPopup.isOpen,
ImGuiWindowFlags_NoResize))
{
auto& isRoundRotation = settings.bakeIsRoundRotation;
auto& isRoundScale = settings.bakeIsRoundScale;
auto& isMatchRootInterpolation = settings.bakeIsMatchRootInterpolation;
auto& isUseRootPivot = settings.bakeIsUseRootPivot;
int target = (int)bakeIntoOtherFramesTarget;
ImGui::RadioButton(localize.get(LABEL_CURRENT_SELECTION), &target,
(int)BakeIntoOtherFramesTarget::CURRENT_SELECTION);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_INTO_OTHER_FRAMES_CURRENT_SELECTION));
ImGui::SameLine();
ImGui::RadioButton(localize.get(LABEL_ALL), &target, (int)BakeIntoOtherFramesTarget::ALL);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_INTO_OTHER_FRAMES_ALL));
bakeIntoOtherFramesTarget = (BakeIntoOtherFramesTarget)target;
auto isCurrentSelection = bakeIntoOtherFramesTarget == BakeIntoOtherFramesTarget::CURRENT_SELECTION;
ImGui::BeginDisabled(isCurrentSelection);
ImGui::Checkbox(localize.get(LABEL_LAYERS), &isBakeIntoOtherFramesLayers);
ImGui::SameLine();
ImGui::Checkbox(localize.get(LABEL_NULLS), &isBakeIntoOtherFramesNulls);
ImGui::EndDisabled();
ImGui::Checkbox(localize.get(LABEL_ROUND_ROTATION), &isRoundRotation);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROUND_ROTATION));
ImGui::Checkbox(localize.get(LABEL_ROUND_SCALE), &isRoundScale);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROUND_SCALE));
ImGui::Checkbox(localize.get(LABEL_BAKE_MATCH_ROOT_INTERPOLATION), &isMatchRootInterpolation);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_MATCH_ROOT_INTERPOLATION));
ImGui::Checkbox(localize.get(LABEL_BAKE_USE_ROOT_PIVOT), &isUseRootPivot);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_USE_ROOT_PIVOT));
auto widgetSize = widget_size_with_row_get(2);
shortcut(manager.chords[SHORTCUT_CONFIRM]);
ImGui::BeginDisabled(!is_bake_into_other_frames_ready());
if (ImGui::Button(localize.get(LABEL_BAKE), widgetSize))
{
bake_into_other_frames();
bakeIntoOtherFramesPopup.close();
}
ImGui::EndDisabled();
ImGui::SameLine();
shortcut(manager.chords[SHORTCUT_CANCEL]);
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) bakeIntoOtherFramesPopup.close();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CANCEL_BAKE_FRAMES));
ImGui::EndPopup();
}
if (animation)
{
if (shortcut(manager.chords[SHORTCUT_PLAY_PAUSE], shortcut::GLOBAL)) playback.toggle();
+17
View File
@@ -38,6 +38,18 @@ namespace anm2ed::imgui
bool isActive{};
};
struct FrameDurationDrag
{
Reference reference{};
int duration{1};
};
enum class BakeIntoOtherFramesTarget
{
CURRENT_SELECTION,
ALL
};
class Timeline
{
bool isDragging{};
@@ -45,6 +57,10 @@ namespace anm2ed::imgui
bool isHorizontalScroll{};
popup::ItemProperties itemProperties{};
PopupHelper bakePopup{PopupHelper(LABEL_TIMELINE_BAKE_POPUP, POPUP_SMALL_NO_HEIGHT)};
PopupHelper bakeIntoOtherFramesPopup{PopupHelper(LABEL_BAKE_INTO_OTHER_FRAMES, POPUP_SMALL_NO_HEIGHT)};
BakeIntoOtherFramesTarget bakeIntoOtherFramesTarget{BakeIntoOtherFramesTarget::CURRENT_SELECTION};
bool isBakeIntoOtherFramesLayers{true};
bool isBakeIntoOtherFramesNulls{true};
int hoveredTime{};
bool isFrameBoxPending{};
bool isFrameBoxSelecting{};
@@ -65,6 +81,7 @@ namespace anm2ed::imgui
int draggedFrameIndex{-1};
int draggedFrameStart{-1};
int draggedFrameStartDuration{-1};
std::vector<FrameDurationDrag> draggedFrameStartDurations{};
float draggedFrameStartMouseX{};
float draggedFrameWidth{};
bool isDraggedFrameSnapshot{};
+1 -1
View File
@@ -1057,7 +1057,7 @@ namespace anm2ed::imgui
auto index = *indices.rbegin();
selection = {index};
reference = {index};
window.newElementId = index;
window.newElementId = indices.size() == 1 ? index : -1;
window.scrollQueued = index;
}
}
@@ -1,6 +1,7 @@
#include "generate_animation_from_grid.hpp"
#include <algorithm>
#include <format>
#include "math.hpp"
#include "types.hpp"
#include "util/imgui/draw.hpp"
@@ -28,6 +29,8 @@ namespace anm2ed::imgui::wizard
auto& columns = settings.generateColumns;
auto& count = settings.generateCount;
auto& delay = settings.generateDuration;
auto& isMakeRegions = settings.generateIsMakeRegions;
auto& regionNameFormat = settings.generateRegionNameFormat;
auto& zoom = settings.generateZoom;
auto& zoomStep = settings.inputZoomStep;
auto layerReferences = document.layer_references_get();
@@ -43,13 +46,20 @@ namespace anm2ed::imgui::wizard
{
ImGui::InputInt2(localize.get(LABEL_GENERATE_START_POSITION), value_ptr(startPosition));
ImGui::InputInt2(localize.get(LABEL_GENERATE_FRAME_SIZE), value_ptr(size));
ImGui::InputInt2(localize.get(BASIC_PIVOT), value_ptr(pivot));
ImGui::InputFloat2(localize.get(BASIC_PIVOT), value_ptr(pivot), math::vec2_format_get(pivot));
ImGui::InputInt(localize.get(LABEL_GENERATE_ROWS), &rows, STEP, STEP_FAST);
ImGui::InputInt(localize.get(LABEL_GENERATE_COLUMNS), &columns, STEP, STEP_FAST);
input_int_range(localize.get(LABEL_GENERATE_COUNT), count, FRAME_NUM_MIN, rows * columns);
ImGui::InputInt(localize.get(BASIC_DURATION), &delay, STEP, STEP_FAST);
ImGui::Separator();
ImGui::Checkbox(localize.get(LABEL_GENERATE_MAKE_REGIONS), &isMakeRegions);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GENERATE_MAKE_REGIONS));
ImGui::BeginDisabled(!isMakeRegions);
input_text_string(localize.get(LABEL_FORMAT), &regionNameFormat);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GENERATE_REGION_NAME_FORMAT));
ImGui::EndDisabled();
}
ImGui::EndChild();
@@ -132,17 +142,37 @@ namespace anm2ed::imgui::wizard
auto queuedColumns = columns;
auto queuedCount = count;
auto queuedDelay = delay;
auto queuedIsMakeRegions = isMakeRegions;
auto queuedRegionNameFormat = regionNameFormat;
manager.command_push({manager.selected,
[=](Manager&, Document& document)
{
auto region_name_get = [](const std::string& format, int frameNumber)
{
try
{
return std::vformat(format, std::make_format_args(frameNumber));
}
catch (const std::format_error&)
{
return format;
}
};
bool isChanged{};
bool isRegionsChanged{};
for (auto queuedReference : queuedLayerReferences)
{
auto item = document.anm2.element_get(queuedReference.animationIndex, ItemType::LAYER,
queuedReference.itemID);
auto animation =
document.anm2.element_get(ElementType::ANIMATION, queuedReference.animationIndex);
auto layer = document.anm2.element_get(ElementType::LAYER_ELEMENT,
queuedReference.itemID);
auto spritesheet = layer ? document.anm2.element_get(ElementType::SPRITESHEET,
layer->spritesheetId)
: nullptr;
if (!item || !animation) continue;
if (!isChanged)
@@ -150,11 +180,29 @@ namespace anm2ed::imgui::wizard
document.snapshot(localize.get(EDIT_GENERATE_ANIMATION_FROM_GRID));
isChanged = true;
}
auto frameIndexStart = (int)item->children.size();
frames_generate_from_grid(*item, queuedStartPosition, queuedSize, queuedPivot,
queuedColumns, queuedCount, queuedDelay);
if (queuedIsMakeRegions && spritesheet)
for (int frameIndex = frameIndexStart; frameIndex < (int)item->children.size();
++frameIndex)
{
auto& frame = item->children[frameIndex];
if (frame.type != ElementType::FRAME) continue;
auto region = element_make(ElementType::REGION);
region.id = element_child_next_id_get(*spritesheet, ElementType::REGION);
region.name = region_name_get(queuedRegionNameFormat, frameIndex - frameIndexStart);
region.crop = frame.crop;
region.size = frame.size;
region.pivot = frame.pivot;
frame.regionId = region.id;
spritesheet->children.push_back(region);
isRegionsChanged = true;
}
animation->frameNum = animation_length_get(*animation);
}
if (isChanged) document.anm2_change(Document::FRAMES);
if (isChanged) document.anm2_change(isRegionsChanged ? Document::ALL : Document::FRAMES);
}});
isEnd = true;
}