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
+5 -5
View File
@@ -962,8 +962,8 @@ namespace anm2ed
baked.scale = glm::mix(original.scale, next.scale, amount); baked.scale = glm::mix(original.scale, next.scale, amount);
baked.colorOffset = glm::mix(original.colorOffset, next.colorOffset, amount); baked.colorOffset = glm::mix(original.colorOffset, next.colorOffset, amount);
baked.tint = glm::mix(original.tint, next.tint, amount); baked.tint = glm::mix(original.tint, next.tint, amount);
if (isRoundScale) baked.scale = glm::vec2(glm::ivec2(baked.scale)); if (isRoundScale) baked.scale = glm::round(baked.scale);
if (isRoundRotation) baked.rotation = (int)baked.rotation; if (isRoundRotation) baked.rotation = std::round(baked.rotation);
if (insertIndex == index) if (insertIndex == index)
track.children[insertIndex] = baked; track.children[insertIndex] = baked;
@@ -975,7 +975,7 @@ namespace anm2ed
} }
} }
void frames_generate_from_grid(Element& track, glm::ivec2 startPosition, glm::ivec2 size, glm::ivec2 pivot, void frames_generate_from_grid(Element& track, glm::ivec2 startPosition, glm::ivec2 size, glm::vec2 pivot,
int columns, int count, int duration) int columns, int count, int duration)
{ {
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
@@ -1174,8 +1174,8 @@ namespace anm2ed
baked.scale = glm::mix(original.scale, nextFrame.scale, amount); baked.scale = glm::mix(original.scale, nextFrame.scale, amount);
baked.colorOffset = glm::mix(original.colorOffset, nextFrame.colorOffset, amount); baked.colorOffset = glm::mix(original.colorOffset, nextFrame.colorOffset, amount);
baked.tint = glm::mix(original.tint, nextFrame.tint, amount); baked.tint = glm::mix(original.tint, nextFrame.tint, amount);
if (isRoundScale) baked.scale = glm::vec2(glm::ivec2(baked.scale)); if (isRoundScale) baked.scale = glm::round(baked.scale);
if (isRoundRotation) baked.rotation = (int)baked.rotation; if (isRoundRotation) baked.rotation = std::round(baked.rotation);
if (insertIndex == index) if (insertIndex == index)
track.children[insertIndex] = baked; track.children[insertIndex] = baked;
+1 -1
View File
@@ -300,7 +300,7 @@ namespace anm2ed
int frame_index_from_time_get(const Element&, float); int frame_index_from_time_get(const Element&, float);
float frame_time_from_index_get(const Element&, int); float frame_time_from_index_get(const Element&, int);
void frame_bake(Element&, int, int, bool, bool); void frame_bake(Element&, int, int, bool, bool);
void frames_generate_from_grid(Element&, glm::ivec2, glm::ivec2, glm::ivec2, int, int, int); void frames_generate_from_grid(Element&, glm::ivec2, glm::ivec2, glm::vec2, int, int, int);
bool frames_deserialize(Element&, const std::string&, int, std::set<int>&, std::string* = nullptr); bool frames_deserialize(Element&, const std::string&, int, std::set<int>&, std::string* = nullptr);
void frames_sort_by_at_frame(Element&); void frames_sort_by_at_frame(Element&);
void frames_change(Element&, FrameChange, ItemType, ChangeType, const std::set<int>&); void frames_change(Element&, FrameChange, ItemType, ChangeType, const std::set<int>&);
+29 -8
View File
@@ -34,6 +34,7 @@ using namespace glm;
namespace anm2ed::imgui namespace anm2ed::imgui
{ {
constexpr auto NULL_COLOR = vec4(0.0f, 0.0f, 1.0f, 0.90f); 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 TARGET_SIZE = vec2(32, 32);
constexpr auto POINT_SIZE = vec2(4, 4); constexpr auto POINT_SIZE = vec2(4, 4);
constexpr auto TRIGGER_TEXT_COLOR_DARK = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); 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 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 = {}, auto render = [&](Element* animation, float time, vec3 colorOffset = {}, float alphaOffset = {},
const std::vector<OnionskinSample>* layeredOnions = nullptr, bool isIndexMode = false) 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()); 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); if (isBorder) rect_render(shaderLine, layerTransform, layerModel, color);
@@ -1048,19 +1066,22 @@ namespace anm2ed::imgui
auto useTool = tool; auto useTool = tool;
auto step = (float)(isMod ? STEP_FAST : STEP); auto step = (float)(isMod ? STEP_FAST : STEP);
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos)); 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 = [&]() auto selected_frame_references_get = [&]()
{ {
std::set<Reference> result = document.frames.references; std::set<Reference> result = document.frames.references;
for (auto frameIndex : frames) for (auto frameIndex : frames)
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex}); result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
std::erase_if(result, std::erase_if(result,
[](const Reference& frameReference) [&](const Reference& frameReference) { return !is_frame_reference_valid(frameReference); });
{ if (result.empty() && is_frame_reference_valid(reference))
return frameReference.itemType == NONE || frameReference.itemType == TRIGGER ||
frameReference.frameIndex < 0;
});
if (result.empty() && reference.itemType != NONE && reference.itemType != TRIGGER &&
reference.frameIndex != -1)
result.insert(reference); result.insert(reference);
return result; return result;
}; };
+23 -15
View File
@@ -351,24 +351,32 @@ namespace anm2ed::imgui
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift); auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
auto frame = auto is_frame_reference_valid = [&](const Reference& frameReference)
anm2.element_get(reference.animationIndex, referenceItemType, reference.frameIndex, reference.itemID); {
auto item = anm2.element_get(reference.animationIndex, referenceItemType, reference.itemID); if (frameReference.frameIndex < 0 || frameReference.itemType != LAYER) return false;
auto layer = anm2.element_get(ElementType::LAYER_ELEMENT, reference.itemID); auto frameLayer = anm2.element_get(ElementType::LAYER_ELEMENT, frameReference.itemID);
bool isReferenceLayerOnSpritesheet = if (!frameLayer || frameLayer->spritesheetId != referenceSpritesheet) return false;
frame && reference.itemID > -1 && layer && layer->spritesheetId == referenceSpritesheet; auto frameItem = anm2.element_get(frameReference.animationIndex, ItemType::LAYER, frameReference.itemID);
return frameItem && track_frame_get(*frameItem, frameReference.frameIndex);
};
auto selectedFrameReferences = document.frames.references; auto selectedFrameReferences = document.frames.references;
for (auto frameIndex : document.frames.selection) for (auto frameIndex : document.frames.selection)
selectedFrameReferences.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex}); selectedFrameReferences.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
std::erase_if(selectedFrameReferences, [&](const Reference& frameReference) std::erase_if(selectedFrameReferences, [&](const Reference& frameReference)
{ { return !is_frame_reference_valid(frameReference); });
if (frameReference.frameIndex < 0) return true; if (selectedFrameReferences.empty() && is_frame_reference_valid(reference))
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)
selectedFrameReferences.insert(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 useTool = tool;
auto step = (float)(isMod ? STEP_FAST : STEP); auto step = (float)(isMod ? STEP_FAST : STEP);
auto stepX = isGridSnap ? step * gridSize.x : 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 frame_change_from_current_apply = [&](auto frameChangeGet, ChangeType changeType = ChangeType::ADJUST)
{ {
auto queuedReference = reference; auto queuedReference = editReference;
auto queuedReferenceItemType = referenceItemType; auto queuedReferenceItemType = editReferenceItemType;
auto queuedFrameReferences = selectedFrameReferences; auto queuedFrameReferences = selectedFrameReferences;
manager.command_push({manager.selected, manager.command_push({manager.selected,
[=](Manager&, Document& document) [=](Manager&, Document& document)
+312 -2
View File
@@ -11,6 +11,7 @@
#include "actions.hpp" #include "actions.hpp"
#include "log.hpp" #include "log.hpp"
#include "math.hpp"
#include "toast.hpp" #include "toast.hpp"
#include "util/imgui/draw.hpp" #include "util/imgui/draw.hpp"
#include "util/imgui/input.hpp" #include "util/imgui/input.hpp"
@@ -110,6 +111,12 @@ namespace anm2ed::imgui
bool isGroup{}; bool isGroup{};
}; };
struct RootFrameSpan
{
int start{};
int end{};
};
void Timeline::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard) void Timeline::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
{ {
auto& document = *manager.get(); auto& document = *manager.get();
@@ -227,6 +234,13 @@ namespace anm2ed::imgui
return left.animationIndex == right.animationIndex && left.itemType == right.itemType && return left.animationIndex == right.animationIndex && left.itemType == right.itemType &&
left.itemID == right.itemID; 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) auto group_selection_reset_for = [this](Document& targetDocument)
{ {
targetDocument.groupReferences.clear(); targetDocument.groupReferences.clear();
@@ -235,9 +249,15 @@ namespace anm2ed::imgui
auto frame_references_for_current_get = [&]() auto frame_references_for_current_get = [&]()
{ {
std::set<Reference> result = frames.references; std::set<Reference> result = frames.references;
std::erase_if(result, [&](const Reference& frameReference)
{ return !is_frame_reference_valid_for(document, frameReference); });
if (result.empty()) if (result.empty())
{
for (auto frameIndex : frames.selection) for (auto frameIndex : frames.selection)
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex}); 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; return result;
}; };
auto item_references_for_current_get = [&]() auto item_references_for_current_get = [&]()
@@ -376,6 +396,38 @@ namespace anm2ed::imgui
frameFocusIndex = targetReference.frameIndex; frameFocusIndex = targetReference.frameIndex;
frameFocusRequested = targetReference.frameIndex >= 0; 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) auto reference_clear_for = [=](Document& targetDocument)
{ {
targetDocument.reference = {targetDocument.reference.animationIndex}; 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 frame_split = [&]()
{ {
auto selectedFrames = frame_references_for_current_get(); auto selectedFrames = frame_references_for_current_get();
@@ -1448,6 +1665,7 @@ namespace anm2ed::imgui
auto selectedBakeFrames = selectedFrames; auto selectedBakeFrames = selectedFrames;
std::erase_if(selectedBakeFrames, std::erase_if(selectedBakeFrames,
[](const Reference& frameReference) { return frameReference.itemType == TRIGGER; }); [](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 && bool isMakeRegion = frame && reference.itemType == LAYER && reference.itemID != -1 && frame->regionId == -1 &&
layer_get(reference.itemID) && spritesheet_get(layer_get(reference.itemID)->spritesheetId); layer_get(reference.itemID) && spritesheet_get(layer_get(reference.itemID)->spritesheetId);
Actions actions{}; Actions actions{};
@@ -1469,6 +1687,10 @@ namespace anm2ed::imgui
.shortcut = SHORTCUT_BAKE, .shortcut = SHORTCUT_BAKE,
.isEnabled = [=]() { return !selectedBakeFrames.empty(); }, .isEnabled = [=]() { return !selectedBakeFrames.empty(); },
.run = [&]() { frames_bake(); }}); .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, actions.add({.label = LABEL_FIT_ANIMATION_LENGTH,
.shortcut = SHORTCUT_FIT, .shortcut = SHORTCUT_FIT,
.isEnabled = [&]() { return animation && animation->frameNum != animation_length_get(*animation); }, .isEnabled = [&]() { return animation && animation->frameNum != animation_length_get(*animation); },
@@ -2763,6 +2985,23 @@ namespace anm2ed::imgui
draggedFrameIndex = (int)i; draggedFrameIndex = (int)i;
draggedFrameStart = hoveredTime; draggedFrameStart = hoveredTime;
if (type != TRIGGER) draggedFrameStartDuration = frame.duration; 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; draggedFrameStartMouseX = ImGui::GetIO().MousePos.x;
draggedFrameWidth = frameSize.x; draggedFrameWidth = frameSize.x;
} }
@@ -2907,6 +3146,7 @@ namespace anm2ed::imgui
auto targetType = draggedFrameType; auto targetType = draggedFrameType;
auto targetIndex = draggedFrameIndex; auto targetIndex = draggedFrameIndex;
auto targetStartDuration = draggedFrameStartDuration; auto targetStartDuration = draggedFrameStartDuration;
auto targetStartDurations = draggedFrameStartDurations;
auto targetHoveredTime = hoveredTime; auto targetHoveredTime = hoveredTime;
auto targetDurationDelta = durationDelta; auto targetDurationDelta = durationDelta;
auto isPlaybackClamp = settings.playbackIsClamp; auto isPlaybackClamp = settings.playbackIsClamp;
@@ -2932,8 +3172,20 @@ namespace anm2ed::imgui
} }
else else
{ {
frame->duration = glm::clamp(targetStartDuration + targetDurationDelta, FRAME_DURATION_MIN, if (targetStartDurations.empty())
FRAME_DURATION_MAX); {
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; draggedFrameIndex = -1;
draggedFrameStart = -1; draggedFrameStart = -1;
draggedFrameStartDuration = -1; draggedFrameStartDuration = -1;
draggedFrameStartDurations.clear();
draggedFrameStartMouseX = 0.0f; draggedFrameStartMouseX = 0.0f;
draggedFrameWidth = 0.0f; draggedFrameWidth = 0.0f;
isDraggedFrameSnapshot = false; isDraggedFrameSnapshot = false;
@@ -3354,6 +3607,63 @@ namespace anm2ed::imgui
ImGui::EndPopup(); 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 (animation)
{ {
if (shortcut(manager.chords[SHORTCUT_PLAY_PAUSE], shortcut::GLOBAL)) playback.toggle(); if (shortcut(manager.chords[SHORTCUT_PLAY_PAUSE], shortcut::GLOBAL)) playback.toggle();
+17
View File
@@ -38,6 +38,18 @@ namespace anm2ed::imgui
bool isActive{}; bool isActive{};
}; };
struct FrameDurationDrag
{
Reference reference{};
int duration{1};
};
enum class BakeIntoOtherFramesTarget
{
CURRENT_SELECTION,
ALL
};
class Timeline class Timeline
{ {
bool isDragging{}; bool isDragging{};
@@ -45,6 +57,10 @@ namespace anm2ed::imgui
bool isHorizontalScroll{}; bool isHorizontalScroll{};
popup::ItemProperties itemProperties{}; popup::ItemProperties itemProperties{};
PopupHelper bakePopup{PopupHelper(LABEL_TIMELINE_BAKE_POPUP, POPUP_SMALL_NO_HEIGHT)}; 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{}; int hoveredTime{};
bool isFrameBoxPending{}; bool isFrameBoxPending{};
bool isFrameBoxSelecting{}; bool isFrameBoxSelecting{};
@@ -65,6 +81,7 @@ namespace anm2ed::imgui
int draggedFrameIndex{-1}; int draggedFrameIndex{-1};
int draggedFrameStart{-1}; int draggedFrameStart{-1};
int draggedFrameStartDuration{-1}; int draggedFrameStartDuration{-1};
std::vector<FrameDurationDrag> draggedFrameStartDurations{};
float draggedFrameStartMouseX{}; float draggedFrameStartMouseX{};
float draggedFrameWidth{}; float draggedFrameWidth{};
bool isDraggedFrameSnapshot{}; bool isDraggedFrameSnapshot{};
+1 -1
View File
@@ -1057,7 +1057,7 @@ namespace anm2ed::imgui
auto index = *indices.rbegin(); auto index = *indices.rbegin();
selection = {index}; selection = {index};
reference = {index}; reference = {index};
window.newElementId = index; window.newElementId = indices.size() == 1 ? index : -1;
window.scrollQueued = index; window.scrollQueued = index;
} }
} }
@@ -1,6 +1,7 @@
#include "generate_animation_from_grid.hpp" #include "generate_animation_from_grid.hpp"
#include <algorithm> #include <algorithm>
#include <format>
#include "math.hpp" #include "math.hpp"
#include "types.hpp" #include "types.hpp"
#include "util/imgui/draw.hpp" #include "util/imgui/draw.hpp"
@@ -28,6 +29,8 @@ namespace anm2ed::imgui::wizard
auto& columns = settings.generateColumns; auto& columns = settings.generateColumns;
auto& count = settings.generateCount; auto& count = settings.generateCount;
auto& delay = settings.generateDuration; auto& delay = settings.generateDuration;
auto& isMakeRegions = settings.generateIsMakeRegions;
auto& regionNameFormat = settings.generateRegionNameFormat;
auto& zoom = settings.generateZoom; auto& zoom = settings.generateZoom;
auto& zoomStep = settings.inputZoomStep; auto& zoomStep = settings.inputZoomStep;
auto layerReferences = document.layer_references_get(); 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_START_POSITION), value_ptr(startPosition));
ImGui::InputInt2(localize.get(LABEL_GENERATE_FRAME_SIZE), value_ptr(size)); 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_ROWS), &rows, STEP, STEP_FAST);
ImGui::InputInt(localize.get(LABEL_GENERATE_COLUMNS), &columns, 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); 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::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(); ImGui::EndChild();
@@ -132,17 +142,37 @@ namespace anm2ed::imgui::wizard
auto queuedColumns = columns; auto queuedColumns = columns;
auto queuedCount = count; auto queuedCount = count;
auto queuedDelay = delay; auto queuedDelay = delay;
auto queuedIsMakeRegions = isMakeRegions;
auto queuedRegionNameFormat = regionNameFormat;
manager.command_push({manager.selected, manager.command_push({manager.selected,
[=](Manager&, Document& document) [=](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 isChanged{};
bool isRegionsChanged{};
for (auto queuedReference : queuedLayerReferences) for (auto queuedReference : queuedLayerReferences)
{ {
auto item = document.anm2.element_get(queuedReference.animationIndex, ItemType::LAYER, auto item = document.anm2.element_get(queuedReference.animationIndex, ItemType::LAYER,
queuedReference.itemID); queuedReference.itemID);
auto animation = auto animation =
document.anm2.element_get(ElementType::ANIMATION, queuedReference.animationIndex); 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 (!item || !animation) continue;
if (!isChanged) if (!isChanged)
@@ -150,11 +180,29 @@ namespace anm2ed::imgui::wizard
document.snapshot(localize.get(EDIT_GENERATE_ANIMATION_FROM_GRID)); document.snapshot(localize.get(EDIT_GENERATE_ANIMATION_FROM_GRID));
isChanged = true; isChanged = true;
} }
auto frameIndexStart = (int)item->children.size();
frames_generate_from_grid(*item, queuedStartPosition, queuedSize, queuedPivot, frames_generate_from_grid(*item, queuedStartPosition, queuedSize, queuedPivot,
queuedColumns, queuedCount, queuedDelay); 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); 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; isEnd = true;
} }
+13
View File
@@ -113,6 +113,7 @@ namespace anm2ed
X(EDIT_ANIMATION_LENGTH, "Animation Length", "Duracion De Animacion", "Длина анимации", "动画时长", "애니메이션 길이") \ X(EDIT_ANIMATION_LENGTH, "Animation Length", "Duracion De Animacion", "Длина анимации", "动画时长", "애니메이션 길이") \
X(EDIT_AUTHOR, "Author", "Autor", "Автор", "制作者", "작성자") \ X(EDIT_AUTHOR, "Author", "Autor", "Автор", "制作者", "작성자") \
X(EDIT_BAKE_FRAMES, "Bake Frames", "Hacer Bake de Frames", "Запечь кадры", "烘培/提前渲染", "프레임 베이크") \ X(EDIT_BAKE_FRAMES, "Bake Frames", "Hacer Bake de Frames", "Запечь кадры", "烘培/提前渲染", "프레임 베이크") \
X(EDIT_BAKE_INTO_OTHER_FRAMES, "Bake into Other Frames", "Hacer bake en otros frames", "Запечь в другие кадры", "烘焙到其他帧", "다른 프레임에 베이크") \
X(EDIT_CHANGE_FRAME_PROPERTIES, "Change Frame Properties", "Cambiar Propiedades de Frame", "Изменить свойства кадров", "更改帧属性", "프레임 속성 변경") \ X(EDIT_CHANGE_FRAME_PROPERTIES, "Change Frame Properties", "Cambiar Propiedades de Frame", "Изменить свойства кадров", "更改帧属性", "프레임 속성 변경") \
X(EDIT_CUT_ANIMATIONS, "Cut Animation(s)", "Cortar Animacion(es)", "Вырезать анимации", "剪切动画", "애니메이션 잘라내기") \ X(EDIT_CUT_ANIMATIONS, "Cut Animation(s)", "Cortar Animacion(es)", "Вырезать анимации", "剪切动画", "애니메이션 잘라내기") \
X(EDIT_CUT_FRAMES, "Cut Frame(s)", "Cortar Frame(s)", "Вырезать кадры", "剪切多个/单个帧", "프레임 잘라내기") \ X(EDIT_CUT_FRAMES, "Cut Frame(s)", "Cortar Frame(s)", "Вырезать кадры", "剪切多个/单个帧", "프레임 잘라내기") \
@@ -244,6 +245,9 @@ namespace anm2ed
X(LABEL_AXES, "Axes", "Ejes", "Оси", "坐标轴", "가로/세로 축") \ X(LABEL_AXES, "Axes", "Ejes", "Оси", "坐标轴", "가로/세로 축") \
X(LABEL_BACKGROUND_COLOR, "Background", "Fondo", "Фон", "背景", "배경색") \ X(LABEL_BACKGROUND_COLOR, "Background", "Fondo", "Фон", "背景", "배경색") \
X(LABEL_BAKE, "Bake", "Bake", "Запечь", "提前渲染", "베이크") \ X(LABEL_BAKE, "Bake", "Bake", "Запечь", "提前渲染", "베이크") \
X(LABEL_BAKE_INTO_OTHER_FRAMES, "Bake into Other Frames", "Bake en otros frames", "Запечь в другие кадры", "烘焙到其他帧", "다른 프레임에 베이크") \
X(LABEL_BAKE_MATCH_ROOT_INTERPOLATION, "Bake other frames to match interpolation", "Bake otros frames para coincidir con interpolación", "Запечь другие кадры для соответствия интерполяции", "烘焙其他帧以匹配插值", "보간에 맞게 다른 프레임 베이크") \
X(LABEL_BAKE_USE_ROOT_PIVOT, "Use root pivot", "Usar pivote root", "Использовать корневой пивот", "使用根枢轴", "Root 중심점 사용") \
X(LABEL_SPLIT, "Split", "Dividir", "Разделить", "拆分", "분할") \ X(LABEL_SPLIT, "Split", "Dividir", "Разделить", "拆分", "분할") \
X(LABEL_BORDER, "Border", "Borde", "Границы", "边框", "경계선") \ X(LABEL_BORDER, "Border", "Borde", "Границы", "边框", "경계선") \
X(LABEL_CHANGE_ALL_FRAME_PROPERTIES, "Change All Frame Properties", "Cambiar todas las propiedades de frame", "Изменить все свойства кадра", "更改所有帧属性", "모든 프레임 속성 변경") \ X(LABEL_CHANGE_ALL_FRAME_PROPERTIES, "Change All Frame Properties", "Cambiar todas las propiedades de frame", "Изменить все свойства кадра", "更改所有帧属性", "모든 프레임 속성 변경") \
@@ -251,6 +255,7 @@ namespace anm2ed
X(LABEL_CLAMP, "Clamp", "Clamp", "Ограничить", "限制数值范围", "작업 영역 제한") \ X(LABEL_CLAMP, "Clamp", "Clamp", "Ограничить", "限制数值范围", "작업 영역 제한") \
X(LABEL_CLEAR_LIST, "Clear List", "Limpiar Lista", "Стереть список", "清除列表", "기록 삭제") \ X(LABEL_CLEAR_LIST, "Clear List", "Limpiar Lista", "Стереть список", "清除列表", "기록 삭제") \
X(LABEL_CLOSE, "Close", "Cerrar", "Закрыть", "关闭", "닫기") \ X(LABEL_CLOSE, "Close", "Cerrar", "Закрыть", "关闭", "닫기") \
X(LABEL_CURRENT_SELECTION, "Current Selection", "Selección actual", "Текущее выделение", "当前选择", "현재 선택") \
X(LABEL_SERIALIZATION, "Serialization", "Serialización", "Сериализация", "序列化", "직렬화") \ X(LABEL_SERIALIZATION, "Serialization", "Serialización", "Сериализация", "序列化", "직렬화") \
X(LABEL_GROUPS, "Groups", "Grupos", "Группы", "", "그룹") \ X(LABEL_GROUPS, "Groups", "Grupos", "Группы", "", "그룹") \
X(LABEL_REGIONS, "Regions", "Regiones", "Регионы", "区域", "영역") \ X(LABEL_REGIONS, "Regions", "Regiones", "Регионы", "区域", "영역") \
@@ -287,8 +292,10 @@ namespace anm2ed
X(LABEL_GENERATE_COLUMNS, "Columns", "Columnas", "Колонны", "", "") \ X(LABEL_GENERATE_COLUMNS, "Columns", "Columnas", "Колонны", "", "") \
X(LABEL_GENERATE_COUNT, "Count", "Conteo", "Кол-во", "数量", "프레임 수") \ X(LABEL_GENERATE_COUNT, "Count", "Conteo", "Кол-во", "数量", "프레임 수") \
X(LABEL_GENERATE_FRAME_SIZE, "Frame Size", "Tamaño de Frame", "Размер кадра", "单帧大小", "프레임 크기") \ X(LABEL_GENERATE_FRAME_SIZE, "Frame Size", "Tamaño de Frame", "Размер кадра", "单帧大小", "프레임 크기") \
X(LABEL_GENERATE_MAKE_REGIONS, "Make Regions", "Crear Regiones", "Создать регионы", "创建区域", "영역 만들기") \
X(LABEL_GENERATE_ROWS, "Rows", "Filas", "Ряды", "", "") \ X(LABEL_GENERATE_ROWS, "Rows", "Filas", "Ряды", "", "") \
X(LABEL_GENERATE_START_POSITION, "Start Position", "Poscicion de Inicio", "Начальная позиция", "起始位置", "시작 위치") \ X(LABEL_GENERATE_START_POSITION, "Start Position", "Poscicion de Inicio", "Начальная позиция", "起始位置", "시작 위치") \
X(LABEL_ALL, "All", "Todo", "Все", "全部", "전체") \
X(LABEL_ALL_ANIMATIONS, "All Animations", "Todas las Animaciones", "Все анимации", "所有动画", "모든 애니메이션") \ X(LABEL_ALL_ANIMATIONS, "All Animations", "Todas las Animaciones", "Все анимации", "所有动画", "모든 애니메이션") \
X(LABEL_HELP_MENU, "Help", "Ayuda", "Помощь", "帮助", "도움말") \ X(LABEL_HELP_MENU, "Help", "Ayuda", "Помощь", "帮助", "도움말") \
X(LABEL_IGNORE_FRAMES, "Ignore Frames", "Ignorar Frames", "Игнорировать кадры", "忽略帧", "프레임 무시") \ X(LABEL_IGNORE_FRAMES, "Ignore Frames", "Ignorar Frames", "Игнорировать кадры", "忽略帧", "프레임 무시") \
@@ -534,6 +541,10 @@ namespace anm2ed
X(TOOLTIP_BACKGROUND_COLOR, "Change the background color.", "Cambia el color del fondo.", "Изменить цвет фона.", "更改背景颜色.", "배경색을 변경합니다.") \ X(TOOLTIP_BACKGROUND_COLOR, "Change the background color.", "Cambia el color del fondo.", "Изменить цвет фона.", "更改背景颜色.", "배경색을 변경합니다.") \
X(TOOLTIP_BAKE_FRAMES, "Turn interpolated frames into uninterpolated ones.\nUse the shortcut to bake frames quickly.", "Cambia Frames interpolados a no interpolados.\n usa el atajo para hacer bake de Frames mas rapido.", "Превратить интерполированные кадры в неинтерполированные.\nИспользуйте горячую клавишу, чтобы быстро запечь кадры.", "将线性插值的帧转换为普通帧。\n使用快捷键可快速烘焙帧.", "연결된 프레임을 연결되지 않은 프레임으로 고정화합니다.\n단축키를 사용하면 프레임을 빠르게 베이크할 수 있습니다.") \ X(TOOLTIP_BAKE_FRAMES, "Turn interpolated frames into uninterpolated ones.\nUse the shortcut to bake frames quickly.", "Cambia Frames interpolados a no interpolados.\n usa el atajo para hacer bake de Frames mas rapido.", "Превратить интерполированные кадры в неинтерполированные.\nИспользуйте горячую клавишу, чтобы быстро запечь кадры.", "将线性插值的帧转换为普通帧。\n使用快捷键可快速烘焙帧.", "연결된 프레임을 연결되지 않은 프레임으로 고정화합니다.\n단축키를 사용하면 프레임을 빠르게 베이크할 수 있습니다.") \
X(TOOLTIP_BAKE_FRAMES_OPTIONS, "Bake the selected frame(s) with the options selected.", "Hacer bake el Frame(s) seleccionado con las opciones seleccionadas.", "Запечь выбранные кадры с выбранными настройками.", "替换所选旧图集为新图集.", "선택된 프레임을 선택한 옵션으로 베이킹합니다.") \ X(TOOLTIP_BAKE_FRAMES_OPTIONS, "Bake the selected frame(s) with the options selected.", "Hacer bake el Frame(s) seleccionado con las opciones seleccionadas.", "Запечь выбранные кадры с выбранными настройками.", "替换所选旧图集为新图集.", "선택된 프레임을 선택한 옵션으로 베이킹합니다.") \
X(TOOLTIP_BAKE_INTO_OTHER_FRAMES_CURRENT_SELECTION, "The frames inside the currently selected items will have the selected root frames' transformation applied to them.", "Los frames dentro de los items seleccionados tendrán aplicada la transformación de los root frames seleccionados.", "Кадры внутри выбранных элементов получат трансформацию выбранных корневых кадров.", "当前所选物品中的帧会应用所选根帧的变换.", "현재 선택된 항목 안의 프레임에 선택된 Root 프레임의 변환을 적용합니다.") \
X(TOOLTIP_BAKE_INTO_OTHER_FRAMES_ALL, "All layer/null frames will have the selected root frames' transformations applied to them.", "Todos los frames de capas/nulls tendrán aplicadas las transformaciones de los root frames seleccionados.", "Все кадры слоев/нулей получат трансформации выбранных корневых кадров.", "所有图层/Null帧会应用所选根帧的变换.", "모든 레이어/Null 프레임에 선택된 Root 프레임의 변환을 적용합니다.") \
X(TOOLTIP_BAKE_MATCH_ROOT_INTERPOLATION, "Bake affected layer/null frames to duration 1 before applying root transforms, so they match the selected root frames' interpolation.", "Hornea los frames de capas/nulls afectados a duración 1 antes de aplicar transformaciones root, para coincidir con la interpolación root seleccionada.", "Запечь затронутые кадры слоев/нулей до длительности 1 перед применением корневых трансформаций, чтобы соответствовать интерполяции выбранных корневых кадров.", "应用根变换前,将受影响的图层/Null帧烘焙为时长1,以匹配所选根帧的插值.", "선택된 Root 프레임의 보간에 맞도록 Root 변환 적용 전에 영향받는 레이어/Null 프레임을 지속시간 1로 베이크합니다.") \
X(TOOLTIP_BAKE_USE_ROOT_PIVOT, "Scale and rotate other frames around the root frame position instead of the animation origin.", "Escala y rota otros frames alrededor de la posición del root frame en vez del origen de la animación.", "Масштабировать и вращать другие кадры вокруг позиции корневого кадра, а не начала координат анимации.", "围绕根帧位置而不是动画原点缩放和旋转其他帧.", "애니메이션 원점 대신 Root 프레임 위치를 중심으로 다른 프레임을 확대/회전합니다.") \
X(TOOLTIP_BORDER, "Toggle the visibility of borders around layers.", "Alterna la visibilidad de los bordes alrededor de las capas.", "Переключить видимость границ около слоев.", "切换动画层边框是否可见.", "레이어 주변 경계선을 표시하거나 숨깁니다.") \ X(TOOLTIP_BORDER, "Toggle the visibility of borders around layers.", "Alterna la visibilidad de los bordes alrededor de las capas.", "Переключить видимость границ около слоев.", "切换动画层边框是否可见.", "레이어 주변 경계선을 표시하거나 숨깁니다.") \
X(TOOLTIP_CANCEL_ADD_ITEM, "Cancel adding an item.", "Cancelar añadir un item.", "Отменить добавление предмета.", "取消添加物品.", "항목 추가를 취소합니다.") \ X(TOOLTIP_CANCEL_ADD_ITEM, "Cancel adding an item.", "Cancelar añadir un item.", "Отменить добавление предмета.", "取消添加物品.", "항목 추가를 취소합니다.") \
X(TOOLTIP_CANCEL_BAKE_FRAMES, "Cancel baking frames.", "Cancelar hacer bake de Frames.", "Отменить запечку кадров.", "取消提前渲染.", "프레임 베이킹을 취소합니다.") \ X(TOOLTIP_CANCEL_BAKE_FRAMES, "Cancel baking frames.", "Cancelar hacer bake de Frames.", "Отменить запечку кадров.", "取消提前渲染.", "프레임 베이킹을 취소합니다.") \
@@ -608,6 +619,8 @@ namespace anm2ed
X(TOOLTIP_MERGE_SPRITESHEETS_TOP_RIGHT, "The merged spritesheets will be joined at the top right of the previous spritesheet.", "Los spritesheets fusionados se unirán en la parte superior derecha del spritesheet anterior.", "Объединяемые спрайт-листы будут стыковаться в верхнем правом углу предыдущего спрайт-листа.", "合并的图集将拼接在前一个图集的右上方。", "병합된 스프라이트 시트는 이전 스프라이트 시트의 우상단에 이어 붙습니다.") \ X(TOOLTIP_MERGE_SPRITESHEETS_TOP_RIGHT, "The merged spritesheets will be joined at the top right of the previous spritesheet.", "Los spritesheets fusionados se unirán en la parte superior derecha del spritesheet anterior.", "Объединяемые спрайт-листы будут стыковаться в верхнем правом углу предыдущего спрайт-листа.", "合并的图集将拼接在前一个图集的右上方。", "병합된 스프라이트 시트는 이전 스프라이트 시트의 우상단에 이어 붙습니다.") \
X(TOOLTIP_MERGE_MAKE_SPRITESHEET_REGIONS, "The respective spritesheets will be added as regions.", "Los spritesheets correspondientes se agregarán como regiones.", "Соответствующие спрайт-листы будут добавлены как регионы.", "对应图集将作为区域添加。", "해당 스프라이트 시트가 영역으로 추가됩니다.") \ X(TOOLTIP_MERGE_MAKE_SPRITESHEET_REGIONS, "The respective spritesheets will be added as regions.", "Los spritesheets correspondientes se agregarán como regiones.", "Соответствующие спрайт-листы будут добавлены как регионы.", "对应图集将作为区域添加。", "해당 스프라이트 시트가 영역으로 추가됩니다.") \
X(TOOLTIP_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION, "If disabled, the base spritesheet will not be added as a region in the merged spritesheet.", "Si se desactiva, el spritesheet base no se agregará como región en el spritesheet combinado.", "Если отключено, базовый спрайт-лист не будет добавлен как регион в объединённый спрайт-лист.", "禁用后,基础图集不会作为区域添加到合并后的图集中。", "비활성화하면 기본 스프라이트 시트가 병합된 스프라이트 시트에 영역으로 추가되지 않습니다.") \ X(TOOLTIP_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION, "If disabled, the base spritesheet will not be added as a region in the merged spritesheet.", "Si se desactiva, el spritesheet base no se agregará como región en el spritesheet combinado.", "Если отключено, базовый спрайт-лист не будет добавлен как регион в объединённый спрайт-лист.", "禁用后,基础图集不会作为区域添加到合并后的图集中。", "비활성화하면 기본 스프라이트 시트가 병합된 스프라이트 시트에 영역으로 추가되지 않습니다.") \
X(TOOLTIP_GENERATE_MAKE_REGIONS, "Will create spritesheet regions for each frame generated for the current layer's spritesheet.", "Creará regiones de spritesheet para cada frame generado para el spritesheet de la capa actual.", "Создаст регионы спрайт-листа для каждого кадра, созданного для спрайт-листа текущего слоя.", "会为当前动画层图集生成的每个帧创建图集区域。", "현재 레이어의 스프라이트 시트에 생성된 각 프레임마다 영역을 만듭니다.") \
X(TOOLTIP_GENERATE_REGION_NAME_FORMAT, "Set the names of the generated regions.\n{} is the frame number.", "Define los nombres de las regiones generadas.\n{} es el número de frame.", "Задаёт имена создаваемых регионов.\n{} — номер кадра.", "设置生成区域的名称。\n{} 是帧编号。", "생성된 영역의 이름을 설정합니다.\n{}는 프레임 번호입니다.") \
X(TOOLTIP_PACK_SPRITESHEET, "Pack the spritesheet by its regions and rebuild the texture.", "Empaqueta el spritesheet por sus regiones y reconstruye la textura.", "Упаковать спрайт-лист по его регионам и пересобрать текстуру.", "按区域打包图集并重建纹理。", "영역 기준으로 스프라이트 시트를 패킹하고 텍스처를 다시 만듭니다.") \ X(TOOLTIP_PACK_SPRITESHEET, "Pack the spritesheet by its regions and rebuild the texture.", "Empaqueta el spritesheet por sus regiones y reconstruye la textura.", "Упаковать спрайт-лист по его регионам и пересобрать текстуру.", "按区域打包图集并重建纹理。", "영역 기준으로 스프라이트 시트를 패킹하고 텍스처를 다시 만듭니다.") \
X(TOOLTIP_OUTPUT_PATH, "Set the output path or directory for the animation.", "Ajusta la ruta de salida o el directiorio de la animacion.", "Установить путь или директорию вывода для анимации.", "更改动画的输出路径/目录.", "애니메이션의 출력 경로 또는 디렉터리를 설정합니다.") \ X(TOOLTIP_OUTPUT_PATH, "Set the output path or directory for the animation.", "Ajusta la ruta de salida o el directiorio de la animacion.", "Установить путь или директорию вывода для анимации.", "更改动画的输出路径/目录.", "애니메이션의 출력 경로 또는 디렉터리를 설정합니다.") \
X(TOOLTIP_OVERLAY, "Set an animation to be drawn over the current animation.", "Ajusta una animacion para ser dibujada sobre la animacion actual.", "Установить анимацию, которая будет выведена над текущей анимацией.", "设置一个当前动画的覆盖动画.", "현재 애니메이션 위에 그려질 애니메이션을 설정합니다.") \ X(TOOLTIP_OVERLAY, "Set an animation to be drawn over the current animation.", "Ajusta una animacion para ser dibujada sobre la animacion actual.", "Установить анимацию, которая будет выведена над текущей анимацией.", "设置一个当前动画的覆盖动画.", "현재 애니메이션 위에 그려질 애니메이션을 설정합니다.") \
+5 -1
View File
@@ -146,11 +146,13 @@ namespace anm2ed
\ \
X(GENERATE_START_POSITION, generateStartPosition, STRING_UNDEFINED, IVEC2, {}) \ X(GENERATE_START_POSITION, generateStartPosition, STRING_UNDEFINED, IVEC2, {}) \
X(GENERATE_SIZE, generateSize, STRING_UNDEFINED, IVEC2, {64, 64}) \ X(GENERATE_SIZE, generateSize, STRING_UNDEFINED, IVEC2, {64, 64}) \
X(GENERATE_PIVOT, generatePivot, STRING_UNDEFINED, IVEC2, {32, 32}) \ X(GENERATE_PIVOT, generatePivot, STRING_UNDEFINED, VEC2, {32, 32}) \
X(GENERATE_ROWS, generateRows, STRING_UNDEFINED, INT, 4) \ X(GENERATE_ROWS, generateRows, STRING_UNDEFINED, INT, 4) \
X(GENERATE_COLUMNS, generateColumns, STRING_UNDEFINED, INT, 4) \ X(GENERATE_COLUMNS, generateColumns, STRING_UNDEFINED, INT, 4) \
X(GENERATE_COUNT, generateCount, STRING_UNDEFINED, INT, 16) \ X(GENERATE_COUNT, generateCount, STRING_UNDEFINED, INT, 16) \
X(GENERATE_DURATION, generateDuration, STRING_UNDEFINED, INT, 1) \ X(GENERATE_DURATION, generateDuration, STRING_UNDEFINED, INT, 1) \
X(GENERATE_IS_MAKE_REGIONS, generateIsMakeRegions, STRING_UNDEFINED, BOOL, false) \
X(GENERATE_REGION_NAME_FORMAT, generateRegionNameFormat, STRING_UNDEFINED, STRING, "Frame {}") \
X(GENERATE_ZOOM, generateZoom, STRING_UNDEFINED, FLOAT, 100.0f) \ X(GENERATE_ZOOM, generateZoom, STRING_UNDEFINED, FLOAT, 100.0f) \
\ \
X(EDITOR_IS_GRID, editorIsGrid, STRING_UNDEFINED, BOOL, true) \ X(EDITOR_IS_GRID, editorIsGrid, STRING_UNDEFINED, BOOL, true) \
@@ -175,6 +177,8 @@ namespace anm2ed
X(BAKE_INTERVAL, bakeInterval, STRING_UNDEFINED, INT, 1) \ X(BAKE_INTERVAL, bakeInterval, STRING_UNDEFINED, INT, 1) \
X(BAKE_IS_ROUND_SCALE, bakeIsRoundScale, STRING_UNDEFINED, BOOL, true) \ X(BAKE_IS_ROUND_SCALE, bakeIsRoundScale, STRING_UNDEFINED, BOOL, true) \
X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, STRING_UNDEFINED, BOOL, true) \ X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, STRING_UNDEFINED, BOOL, true) \
X(BAKE_IS_MATCH_ROOT_INTERPOLATION, bakeIsMatchRootInterpolation, STRING_UNDEFINED, BOOL, false) \
X(BAKE_IS_USE_ROOT_PIVOT, bakeIsUseRootPivot, STRING_UNDEFINED, BOOL, true) \
\ \
X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, STRING_UNDEFINED, INT, LAYER) \ X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, STRING_UNDEFINED, INT, LAYER) \
X(TIMELINE_ADD_ITEM_DESTINATION, timelineAddItemDestination, STRING_UNDEFINED, INT, types::destination::ALL) \ X(TIMELINE_ADD_ITEM_DESTINATION, timelineAddItemDestination, STRING_UNDEFINED, INT, types::destination::ALL) \
+1 -1
View File
@@ -53,6 +53,6 @@ Alternatively, if you have subscribed to the mod, you can find the latest releas
[h3]Happy animating![/h3] [h3]Happy animating![/h3]
[img]https://files.catbox.moe/4auc1c.gif[/img] [img]https://files.catbox.moe/4auc1c.gif[/img]
</description> </description>
<version>2.22</version> <version>2.23</version>
<visibility>Public</visibility> <visibility>Public</visibility>
</metadata> </metadata>