some new crap
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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), ®ionNameFormat);
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user