Friends :)

This commit is contained in:
2026-05-22 08:15:16 -04:00
parent 1bc5199e3b
commit b1c9d38ef5
21 changed files with 87950 additions and 30586 deletions
+16 -5
View File
@@ -30,9 +30,9 @@ namespace anm2ed::imgui::popup
popup.open();
}
void ItemProperties::update(Manager& manager, Settings& settings, Document& document, Reference& reference,
const std::function<void(int, int)>& referenceSetItem)
bool ItemProperties::update(Manager& manager, Settings& settings, Document& document, Reference& reference)
{
bool isAdded{};
auto& anm2 = document.anm2;
auto animation = anm2.element_get(ElementType::ANIMATION, reference.animationIndex);
@@ -55,7 +55,7 @@ namespace anm2ed::imgui::popup
if (ImGui::BeginChild("##Content", ImVec2(0, contentHeight), ImGuiChildFlags_Borders))
{
auto spaced_pair = [](const std::function<void()>& left, const std::function<void()>& right)
auto spaced_pair = [](auto left, auto right)
{
auto startX = ImGui::GetCursorPosX();
auto secondX = startX + ImGui::GetContentRegionAvail().x * 0.5f;
@@ -194,7 +194,6 @@ namespace anm2ed::imgui::popup
auto queuedAddItemName = addItemName;
auto queuedAddItemSpritesheetID = addItemSpritesheetID;
auto queuedAddItemIsShowRect = addItemIsShowRect;
auto queuedReferenceSetItem = referenceSetItem;
manager.command_push({manager.selected,
[=](Manager&, Document& document)
@@ -213,9 +212,19 @@ namespace anm2ed::imgui::popup
document.anm2_change(Document::ITEMS);
if (addId != -1) queuedReferenceSetItem((int)queuedType, addId);
if (addId != -1)
{
document.reference = {queuedAnimationIndex, (int)queuedType, addId};
document.items.references = {document.reference};
document.frames.selection.clear();
document.frames.references.clear();
if (queuedType == LAYER)
if (auto layer = document.anm2.element_get(ElementType::LAYER_ELEMENT, addId))
document.spritesheet.reference = layer->spritesheetId;
}
}});
isAdded = true;
close();
}
ImGui::EndDisabled();
@@ -229,5 +238,7 @@ namespace anm2ed::imgui::popup
ImGui::EndPopup();
}
return isAdded;
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
#pragma once
#include <functional>
#include <set>
#include <string>
@@ -24,6 +23,6 @@ namespace anm2ed::imgui::popup
public:
void open();
void update(Manager&, Settings&, Document&, Reference&, const std::function<void(int, int)>&);
bool update(Manager&, Settings&, Document&, Reference&);
};
}
+67 -27
View File
@@ -1041,16 +1041,37 @@ namespace anm2ed::imgui
auto isZoomOut = isFocused && shortcut(manager.chords[SHORTCUT_ZOOM_OUT], shortcut::GLOBAL);
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
auto useTool = tool;
auto step = (float)(isMod ? STEP_FAST : STEP);
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
auto selected_frame_references_get = [&]()
{
std::set<Reference> result = document.frames.references;
if (result.empty())
for (auto frameIndex : frames)
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
if (result.empty() && reference.itemType != NONE && reference.frameIndex != -1) result.insert(reference);
std::erase_if(result,
[](const Reference& frameReference)
{ return frameReference.itemType == NONE || frameReference.itemType == TRIGGER; });
return result;
};
auto selectedFrameReferences = selected_frame_references_get();
auto editReference = reference;
auto editItemType = referenceItemType;
if ((referenceItemType == ItemType::TRIGGER || !selectedFrameReferences.contains(reference)) &&
!selectedFrameReferences.empty())
{
editReference = *selectedFrameReferences.begin();
editItemType = static_cast<ItemType>(editReference.itemType);
}
auto frame =
anm2.element_get(reference.animationIndex, referenceItemType, reference.frameIndex, reference.itemID);
auto item = anm2.element_get(reference.animationIndex, referenceItemType, reference.itemID);
auto useTool = tool;
auto step = isMod ? STEP_FAST : STEP;
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
anm2.element_get(editReference.animationIndex, editItemType, editReference.frameIndex, editReference.itemID);
auto item = anm2.element_get(editReference.animationIndex, editItemType, editReference.itemID);
auto nulls = anm2.element_get(ElementType::NULLS);
auto selectedNull = referenceItemType == ItemType::NULL_ && nulls
? element_child_id_get(*nulls, ElementType::NULL_ELEMENT, reference.itemID)
auto selectedNull = editItemType == ItemType::NULL_ && nulls
? element_child_id_get(*nulls, ElementType::NULL_ELEMENT, editReference.itemID)
: nullptr;
bool isSelectedNullRect = selectedNull && selectedNull->isShowRect;
auto null_rect_top_left = [](const Element& frame) { return frame.position - (frame.scale * 0.5f); };
@@ -1081,19 +1102,37 @@ namespace anm2ed::imgui
};
auto frame_change_apply = [&](FrameChange frameChange, ChangeType changeType = ChangeType::ADJUST)
{
auto queuedReference = reference;
auto queuedReferenceItemType = referenceItemType;
auto queuedFrames = frames;
auto queuedFrameReferences = selectedFrameReferences;
manager.command_push({manager.selected,
[=](Manager&, Document& document)
{
auto item = document.anm2.element_get(queuedReference.animationIndex,
queuedReferenceItemType,
queuedReference.itemID);
if (!item) return;
frames_change(*item, frameChange, queuedReferenceItemType, changeType, queuedFrames);
std::map<Reference, std::set<int>> groupedFrames{};
for (auto frameReference : queuedFrameReferences)
groupedFrames[{frameReference.animationIndex, frameReference.itemType,
frameReference.itemID, -1}]
.insert(frameReference.frameIndex);
for (auto& [itemReference, itemFrames] : groupedFrames)
{
auto itemType = static_cast<ItemType>(itemReference.itemType);
auto item = document.anm2.element_get(itemReference.animationIndex, itemType,
itemReference.itemID);
if (!item) continue;
frames_change(*item, frameChange, itemType, changeType, itemFrames);
}
}});
};
auto frame_position_apply = [&](vec2 position)
{
if (!frame) return;
frame_change_apply({.positionX = position.x - frame->position.x, .positionY = position.y - frame->position.y},
ChangeType::ADD);
};
auto frame_scale_apply = [&](vec2 scale)
{
if (!frame) return;
frame_change_apply({.scaleX = scale.x - frame->scale.x, .scaleY = scale.y - frame->scale.y}, ChangeType::ADD);
};
auto frames_changed = [&]()
{
manager.command_push(
@@ -1103,10 +1142,11 @@ namespace anm2ed::imgui
{
topLeft = vec2(ivec2(topLeft));
rectSize = vec2(ivec2(rectSize));
frame_change_apply({.positionX = topLeft.x + rectSize.x * 0.5f,
.positionY = topLeft.y + rectSize.y * 0.5f,
.scaleX = rectSize.x,
.scaleY = rectSize.y});
frame_change_apply({.positionX = topLeft.x + rectSize.x * 0.5f - frame->position.x,
.positionY = topLeft.y + rectSize.y * 0.5f - frame->position.y,
.scaleX = rectSize.x - frame->scale.x,
.scaleY = rectSize.y - frame->scale.y},
ChangeType::ADD);
};
auto& toolInfo = tool::INFO[useTool];
@@ -1125,7 +1165,7 @@ namespace anm2ed::imgui
if (isMouseDown || isMouseMiddleDown) pan += vec2(mouseDelta.x, mouseDelta.y);
break;
case tool::MOVE:
if (!item || !frame || frames.empty()) break;
if (!item || !frame || selectedFrameReferences.empty()) break;
if (isToolBegin)
{
frame_snapshot(EDIT_FRAME_POSITION);
@@ -1140,10 +1180,10 @@ namespace anm2ed::imgui
{
auto position = mousePos - moveOffset;
if (isSelectedNullRect)
frame_change_apply({.positionX = (int)(position.x + frame->scale.x * 0.5f),
.positionY = (int)(position.y + frame->scale.y * 0.5f)});
frame_position_apply(vec2((float)(int)(position.x + frame->scale.x * 0.5f),
(float)(int)(position.y + frame->scale.y * 0.5f)));
else
frame_change_apply({.positionX = (int)position.x, .positionY = (int)position.y});
frame_position_apply(vec2((float)(int)position.x, (float)(int)position.y));
}
if (isLeftPressed) frame_change_apply({.positionX = step}, ChangeType::SUBTRACT);
@@ -1165,7 +1205,7 @@ namespace anm2ed::imgui
}
break;
case tool::SCALE:
if (!item || !frame || frames.empty()) break;
if (!item || !frame || selectedFrameReferences.empty()) break;
if (isToolBegin)
{
frame_snapshot(EDIT_FRAME_SCALE);
@@ -1192,7 +1232,7 @@ namespace anm2ed::imgui
{
auto scale = frame->scale + vec2(mouseDelta.x, mouseDelta.y);
if (isMod) scale = {scale.x, scale.x};
frame_change_apply({.scaleX = scale.x, .scaleY = scale.y});
frame_scale_apply(scale);
}
}
@@ -1237,9 +1277,9 @@ namespace anm2ed::imgui
}
break;
case tool::ROTATE:
if (!item || !frame || frames.empty()) break;
if (!item || !frame || selectedFrameReferences.empty()) break;
if (isToolBegin) frame_snapshot(EDIT_FRAME_ROTATION);
if (isToolMouseDown) frame_change_apply({.rotation = (int)mouseDelta.x}, ChangeType::ADD);
if (isToolMouseDown) frame_change_apply({.rotation = (float)(int)mouseDelta.x}, ChangeType::ADD);
if (isLeftPressed || isDownPressed) frame_change_apply({.rotation = step}, ChangeType::SUBTRACT);
if (isUpPressed || isRightPressed) frame_change_apply({.rotation = step}, ChangeType::ADD);
+9 -7
View File
@@ -355,7 +355,7 @@ namespace anm2ed::imgui
bool isReferenceLayerOnSpritesheet =
frame && reference.itemID > -1 && layer && layer->spritesheetId == referenceSpritesheet;
auto useTool = tool;
auto step = isMod ? STEP_FAST : STEP;
auto step = (float)(isMod ? STEP_FAST : STEP);
auto stepX = isGridSnap ? step * gridSize.x : step;
auto stepY = isGridSnap ? step * gridSize.y : step;
previousMousePos = mousePos;
@@ -692,10 +692,10 @@ namespace anm2ed::imgui
if (isMouseDown)
{
auto frameCrop = ivec2(frame->crop);
frame_change_apply({.pivotX = (int)(mousePos.x - frameCrop.x),
.pivotY = (int)(mousePos.y - frameCrop.y),
.cropX = frameCrop.x,
.cropY = frameCrop.y});
frame_change_apply({.pivotX = (float)(int)(mousePos.x - frameCrop.x),
.pivotY = (float)(int)(mousePos.y - frameCrop.y),
.cropX = (float)frameCrop.x,
.cropY = (float)frameCrop.y});
}
if (isLeftPressed) frame_change_apply({.pivotX = step}, ChangeType::SUBTRACT);
if (isRightPressed) frame_change_apply({.pivotX = step}, ChangeType::ADD);
@@ -703,7 +703,8 @@ namespace anm2ed::imgui
if (isDownPressed) frame_change_apply({.pivotY = step}, ChangeType::ADD);
frame_change_from_current_apply(
[](const Element& frame) { return FrameChange{.pivotX = (int)frame.pivot.x, .pivotY = (int)frame.pivot.y}; });
[](const Element& frame)
{ return FrameChange{.pivotX = (float)(int)frame.pivot.x, .pivotY = (float)(int)frame.pivot.y}; });
if (isDuring)
{
@@ -770,7 +771,8 @@ namespace anm2ed::imgui
if (isMouseClicked)
{
cropAnchor = mousePos;
frame_change_apply({.cropX = (int)cropAnchor.x, .cropY = (int)cropAnchor.y, .sizeX = {}, .sizeY = {}});
frame_change_apply(
{.cropX = (float)(int)cropAnchor.x, .cropY = (float)(int)cropAnchor.y, .sizeX = {}, .sizeY = {}});
}
if (isMouseDown)
{
+142 -92
View File
@@ -973,15 +973,14 @@ namespace anm2ed::imgui
auto row_drag_references_get = [&](const TimelineItemRow& row)
{
auto clicked = row_reference_get(row);
if (clicked.type != LAYER && clicked.type != NULL_) return std::vector<TimelineRowReference>{clicked};
auto rows = selected_row_references_get();
if (!is_row_reference_selected(clicked)) rows = {clicked};
if (rows.empty()) rows = {clicked};
auto type = rows.front().type;
if (type != LAYER && type != NULL_) return std::vector<TimelineRowReference>{clicked};
for (auto rowReference : rows)
if (rowReference.type != type || (rowReference.type != LAYER && rowReference.type != NULL_))
return std::vector<TimelineRowReference>{clicked};
std::erase_if(rows, [&](const TimelineRowReference& rowReference) { return rowReference.type != clicked.type; });
if (rows.empty()) rows = {clicked};
return rows;
};
@@ -1289,7 +1288,8 @@ namespace anm2ed::imgui
auto item = item_get(frameReference.itemType, frameReference.itemID);
auto frame = item ? track_frame_get(*item, frameReference.frameIndex) : nullptr;
if (!frame) continue;
clipboardString += element_to_string(*frame);
auto parentType = item_type_to_track_type_get(item_type_get(frameReference.itemType));
clipboardString += element_to_string(*frame, parentType);
}
if (!clipboardString.empty()) clipboard.set(clipboardString);
};
@@ -2334,6 +2334,38 @@ namespace anm2ed::imgui
return leftMin.x <= rightMax.x && leftMax.x >= rightMin.x && leftMin.y <= rightMax.y &&
leftMax.y >= rightMin.y;
};
auto frame_overlay_draw = [&](ImDrawList* drawList, ImVec2 clipMin, ImVec2 clipMax)
{
if (isFrameBoxSelecting && isFrameBoxClipSet)
{
auto boxContentMin =
ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
auto boxContentMax =
ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
auto boxMin = frame_box_screen_point_get(boxContentMin);
auto boxMax = frame_box_screen_point_get(boxContentMax);
auto boxClipMin = clipMin;
if (isPlayheadLineSet) boxClipMin.y = std::max(boxClipMin.y, playheadLineTopY);
drawList->PushClipRect(boxClipMin, clipMax, true);
drawList->AddRectFilled(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTargetBg));
drawList->AddRect(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTarget));
drawList->PopClipRect();
}
if (isPlayheadLineSet)
{
auto lineTopY = std::max(clipMin.y, playheadLineTopY);
if (clipMax.y > lineTopY)
{
auto linePos = ImVec2(playheadLineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), lineTopY);
auto lineSize = ImVec2(PLAYHEAD_LINE_THICKNESS * 0.5f, clipMax.y - lineTopY);
drawList->PushClipRect(clipMin, clipMax, true);
drawList->AddRectFilled(linePos, ImVec2(linePos.x + lineSize.x, linePos.y + lineSize.y),
ImGui::GetColorU32(playheadLineColor));
drawList->PopClipRect();
}
}
};
auto frame_child = [&](const TimelineItemRow& row, int& index, float width)
{
@@ -2382,7 +2414,40 @@ namespace anm2ed::imgui
bool isDefaultChild = type == NONE;
if (isLightTheme && isDefaultChild) ImGui::PushStyleColor(ImGuiCol_ChildBg, TIMELINE_CHILD_BG_COLOR_LIGHT);
if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders))
bool isFramesChildVisible = ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders);
auto drawList = ImGui::GetWindowDrawList();
auto clipMax = drawList->GetClipRectMax();
auto length = animation ? animation->frameNum : 0;
auto frameSize = ImVec2(ImGui::GetTextLineHeight(), ImGui::GetContentRegionAvail().y);
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
auto cursorPos = ImGui::GetCursorPos();
auto cursorScreenPos = ImGui::GetCursorScreenPos();
auto border = glm::max(0.5f, ImGui::GetStyle().FrameBorderSize * 0.5f);
auto borderLineLength = frameSize.y / 5;
auto frameMin = std::max(0, (int)std::floor(scroll.x / frameSize.x) - 1);
auto frameMax = std::min(FRAME_NUM_MAX, (int)std::ceil((scroll.x + clipMax.x) / frameSize.x) + 1);
if (isFrameBoxSelecting && isFrameBoxClipSet && type != NONE && animation && item)
{
auto boxMin = ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
auto boxMax = ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
auto rowMinY = cursorScreenPos.y + scroll.y - frameBoxClipMin.y;
auto rowMaxY = rowMinY + childSize.y;
float selectionFrameTime{};
for (auto [i, frame] : std::views::enumerate(item->children))
{
auto frameReference = Reference{reference.animationIndex, type, id, (int)i};
auto frameStart = type == TRIGGER ? frame.atFrame : selectionFrameTime;
auto frameEnd = type == TRIGGER ? frameStart + 1.0f : frameStart + frame.duration;
auto frameContentMin = ImVec2(frameStart * frameSize.x, rowMinY);
auto frameContentMax = ImVec2(frameEnd * frameSize.x, rowMaxY);
if (is_frame_box_overlapping(frameContentMin, frameContentMax, boxMin, boxMax))
frameBoxSelection.insert(frameReference);
if (type != TRIGGER) selectionFrameTime += frame.duration;
}
}
if (isFramesChildVisible)
{
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteFocused))
@@ -2396,19 +2461,6 @@ namespace anm2ed::imgui
reference_clear();
}
auto drawList = ImGui::GetWindowDrawList();
auto clipMax = drawList->GetClipRectMax();
auto length = animation ? animation->frameNum : 0;
auto frameSize = ImVec2(ImGui::GetTextLineHeight(), ImGui::GetContentRegionAvail().y);
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
auto cursorPos = ImGui::GetCursorPos();
auto cursorScreenPos = ImGui::GetCursorScreenPos();
auto border = glm::max(0.5f, ImGui::GetStyle().FrameBorderSize * 0.5f);
auto borderLineLength = frameSize.y / 5;
auto frameMin = std::max(0, (int)std::floor(scroll.x / frameSize.x) - 1);
auto frameMax = std::min(FRAME_NUM_MAX, (int)std::ceil((scroll.x + clipMax.x) / frameSize.x) + 1);
pickerLineDrawList = drawList;
if (type == NONE)
{
if (length > 0)
@@ -2495,7 +2547,7 @@ namespace anm2ed::imgui
{
float frameTime{};
if (!isFrameBoxSelecting && !frameMoveDrag.isActive && ImGui::IsWindowHovered() &&
if (!isFrameBoxPending && !isFrameBoxSelecting && !frameMoveDrag.isActive && ImGui::IsWindowHovered() &&
(ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsMouseReleased(ImGuiMouseButton_Right)) &&
!ImGui::IsAnyItemHovered())
reference_set_item(type, id);
@@ -2515,7 +2567,7 @@ namespace anm2ed::imgui
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsAnyItemHovered())
{
isFrameBoxSelecting = true;
isFrameBoxPending = true;
isFrameBoxAdditive = ImGui::IsKeyDown(ImGuiMod_Ctrl);
frameBoxStart = frame_box_content_point_get();
frameBoxEnd = frameBoxStart;
@@ -2595,16 +2647,6 @@ namespace anm2ed::imgui
auto buttonSize = type == TRIGGER ? frameSize : to_imvec2(vec2(frameSize.x * frame.duration, frameSize.y));
auto frameStart = type == TRIGGER ? frame.atFrame : frameTime;
auto frameEnd = type == TRIGGER ? frameStart + 1.0f : frameStart + frame.duration;
if (isFrameBoxSelecting && isFrameBoxClipSet)
{
auto boxMin = ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
auto boxMax = ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
auto rowMinY = cursorScreenPos.y + scroll.y - frameBoxClipMin.y;
auto frameContentMin = ImVec2(frameStart * frameSize.x, rowMinY);
auto frameContentMax = ImVec2(frameEnd * frameSize.x, rowMinY + frameSize.y);
if (is_frame_box_overlapping(frameContentMin, frameContentMax, boxMin, boxMax))
frameBoxSelection.insert(frameReference);
}
if (frameEnd <= (float)frameMin || frameStart >= (float)frameMax)
{
if (type != TRIGGER) frameTime += frame.duration;
@@ -2692,7 +2734,7 @@ namespace anm2ed::imgui
ImGui::PopStyleColor(4);
if (ImGui::IsItemHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Left))
if (!isDraggedFrameActive && ImGui::IsItemHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Left))
{
if (ImGui::IsMouseDown(ImGuiMouseButton_Left))
{
@@ -2704,6 +2746,8 @@ namespace anm2ed::imgui
draggedFrameIndex = (int)i;
draggedFrameStart = hoveredTime;
if (type != TRIGGER) draggedFrameStartDuration = frame.duration;
draggedFrameStartMouseX = ImGui::GetIO().MousePos.x;
draggedFrameWidth = frameSize.x;
}
}
}
@@ -2816,13 +2860,20 @@ namespace anm2ed::imgui
frameSelectionSnapshotReference = reference;
}
}
}
if (isDraggedFrameActive)
{
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
auto durationDelta = draggedFrameWidth > 0.0f
? static_cast<int>((ImGui::GetIO().MousePos.x - draggedFrameStartMouseX) /
draggedFrameWidth)
: hoveredTime - draggedFrameStart;
auto isDraggedFrameChanged =
draggedFrameType == TRIGGER ? hoveredTime != draggedFrameStart : durationDelta != 0;
if (!isDraggedFrameSnapshot && hoveredTime != draggedFrameStart)
if (!isDraggedFrameSnapshot && isDraggedFrameChanged)
{
isDraggedFrameSnapshot = true;
snapshot_command_push(draggedFrameType == TRIGGER ? EDIT_TRIGGER_AT_FRAME : EDIT_FRAME_DURATION);
@@ -2833,9 +2884,9 @@ namespace anm2ed::imgui
auto targetReference = draggedFrameReference;
auto targetType = draggedFrameType;
auto targetIndex = draggedFrameIndex;
auto targetStart = draggedFrameStart;
auto targetStartDuration = draggedFrameStartDuration;
auto targetHoveredTime = hoveredTime;
auto targetDurationDelta = durationDelta;
auto isPlaybackClamp = settings.playbackIsClamp;
auto animationLength = animation ? animation->frameNum : FRAME_NUM_MAX;
command_push([=](Manager&, Document& document)
@@ -2859,8 +2910,8 @@ namespace anm2ed::imgui
}
else
{
frame->duration = glm::clamp(targetStartDuration + (targetHoveredTime - targetStart),
FRAME_DURATION_MIN, FRAME_DURATION_MAX);
frame->duration = glm::clamp(targetStartDuration + targetDurationDelta, FRAME_DURATION_MIN,
FRAME_DURATION_MAX);
}
});
}
@@ -2882,6 +2933,8 @@ namespace anm2ed::imgui
draggedFrameIndex = -1;
draggedFrameStart = -1;
draggedFrameStartDuration = -1;
draggedFrameStartMouseX = 0.0f;
draggedFrameWidth = 0.0f;
isDraggedFrameSnapshot = false;
frameSelectionLocked.clear();
}
@@ -2971,23 +3024,34 @@ namespace anm2ed::imgui
frameBoxClipMin = frameBoxDrawList->GetClipRectMin();
frameBoxClipMax = frameBoxDrawList->GetClipRectMax();
isFrameBoxClipSet = true;
if (isFrameBoxSelecting)
if (isFrameBoxPending || isFrameBoxSelecting)
{
auto& io = ImGui::GetIO();
auto threshold = ImGui::GetTextLineHeightWithSpacing() * 0.125f;
frameBoxEnd = frame_box_content_point_get();
auto distance = ImVec2(frameBoxEnd.x - frameBoxStart.x, frameBoxEnd.y - frameBoxStart.y);
if (isFrameBoxPending && distance.x * distance.x + distance.y * distance.y >= threshold * threshold)
{
isFrameBoxPending = false;
isFrameBoxSelecting = true;
}
auto edgeSize = ImGui::GetTextLineHeightWithSpacing();
auto scrollStep = edgeSize * 0.5f;
if (io.MousePos.x < frameBoxClipMin.x + edgeSize)
scroll.x -= scrollStep;
else if (io.MousePos.x > frameBoxClipMax.x - edgeSize)
scroll.x += scrollStep;
if (io.MousePos.y < frameBoxClipMin.y + edgeSize)
scroll.y -= scrollStep;
else if (io.MousePos.y > frameBoxClipMax.y - edgeSize)
scroll.y += scrollStep;
ImGui::SetScrollX(scroll.x);
ImGui::SetScrollY(scroll.y);
frameBoxEnd = frame_box_content_point_get();
frameBoxSelection.clear();
if (isFrameBoxSelecting)
{
if (io.MousePos.x < frameBoxClipMin.x + edgeSize)
scroll.x -= scrollStep;
else if (io.MousePos.x > frameBoxClipMax.x - edgeSize)
scroll.x += scrollStep;
if (io.MousePos.y < frameBoxClipMin.y + edgeSize)
scroll.y -= scrollStep;
else if (io.MousePos.y > frameBoxClipMax.y - edgeSize)
scroll.y += scrollStep;
ImGui::SetScrollX(scroll.x);
ImGui::SetScrollY(scroll.y);
frameBoxEnd = frame_box_content_point_get();
frameBoxSelection.clear();
}
}
int index{};
@@ -3016,53 +3080,39 @@ namespace anm2ed::imgui
ImGui::EndTable();
}
ImDrawList* windowDrawList = ImGui::GetWindowDrawList();
ImDrawList* foregroundDrawList = ImGui::GetForegroundDrawList();
auto lineBottomY = ImGui::GetWindowPos().y + ImGui::GetWindowSize().y;
if (isHorizontalScroll) lineBottomY -= ImGui::GetStyle().ScrollbarSize;
auto rectMin = windowDrawList->GetClipRectMin();
auto rectMax = windowDrawList->GetClipRectMax();
if (isFrameBoxSelecting && isFrameBoxClipSet)
if (isFrameBoxClipSet)
{
auto boxContentMin =
ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
auto boxContentMax =
ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
auto boxMin = frame_box_screen_point_get(boxContentMin);
auto boxMax = frame_box_screen_point_get(boxContentMax);
auto boxClipMin = rectMin;
if (isPlayheadLineSet) boxClipMin.y = std::max(boxClipMin.y, playheadLineTopY);
foregroundDrawList->PushClipRect(boxClipMin, rectMax, true);
foregroundDrawList->AddRectFilled(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTargetBg));
foregroundDrawList->AddRect(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTarget));
foregroundDrawList->PopClipRect();
auto overlayCursor = ImGui::GetCursorScreenPos();
auto overlayPos = ImGui::GetWindowPos();
auto overlaySize = ImGui::GetWindowSize();
ImGui::SetCursorScreenPos(overlayPos);
if (ImGui::BeginChild("##Frames Overlay Child", overlaySize, ImGuiChildFlags_None,
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoNavInputs))
frame_overlay_draw(ImGui::GetWindowDrawList(), frameBoxClipMin, frameBoxClipMax);
ImGui::EndChild();
ImGui::SetCursorScreenPos(overlayCursor);
}
if (pickerLineDrawList && isPlayheadLineSet && lineBottomY > playheadLineTopY)
{
auto linePos = ImVec2(playheadLineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), playheadLineTopY);
auto lineSize = ImVec2((PLAYHEAD_LINE_THICKNESS / 2.0f), std::max(0.0f, lineBottomY - playheadLineTopY));
foregroundDrawList->PushClipRect(rectMin, rectMax, true);
foregroundDrawList->AddRectFilled(linePos, ImVec2(linePos.x + lineSize.x, linePos.y + lineSize.y),
ImGui::GetColorU32(playheadLineColor));
foregroundDrawList->PopClipRect();
}
if (isFrameBoxSelecting)
if (isFrameBoxPending || isFrameBoxSelecting)
{
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left))
{
group_selection_reset();
if (isFrameBoxAdditive)
document.frames.references.insert(frameBoxSelection.begin(), frameBoxSelection.end());
else
document.frames.references = frameBoxSelection;
document.items.references.clear();
for (auto frameReference : document.frames.references)
document.items.references.insert(item_reference_from_frame_get(frameReference));
if (!document.frames.references.empty()) reference = *document.frames.references.begin();
frames_selection_sync_for(document);
if (isFrameBoxSelecting)
{
group_selection_reset();
if (isFrameBoxAdditive)
document.frames.references.insert(frameBoxSelection.begin(), frameBoxSelection.end());
else
document.frames.references = frameBoxSelection;
document.items.references.clear();
for (auto frameReference : document.frames.references)
document.items.references.insert(item_reference_from_frame_get(frameReference));
if (!document.frames.references.empty()) reference = *document.frames.references.begin();
frames_selection_sync_for(document);
}
isFrameBoxPending = false;
isFrameBoxSelecting = false;
frameBoxSelection.clear();
}
@@ -3240,7 +3290,7 @@ namespace anm2ed::imgui
ImGui::PopStyleVar();
ImGui::End();
itemProperties.update(manager, settings, document, reference, reference_set_item);
if (itemProperties.update(manager, settings, document, reference)) group_selection_reset();
group_properties_update();
bakePopup.trigger();
+3 -1
View File
@@ -56,6 +56,7 @@ namespace anm2ed::imgui
popup::ItemProperties itemProperties{};
PopupHelper bakePopup{PopupHelper(LABEL_TIMELINE_BAKE_POPUP, POPUP_SMALL_NO_HEIGHT)};
int hoveredTime{};
bool isFrameBoxPending{};
bool isFrameBoxSelecting{};
bool isFrameBoxAdditive{};
ImVec2 frameBoxStart{};
@@ -75,6 +76,8 @@ namespace anm2ed::imgui
int draggedFrameIndex{-1};
int draggedFrameStart{-1};
int draggedFrameStartDuration{-1};
float draggedFrameStartMouseX{};
float draggedFrameWidth{};
bool isDraggedFrameSnapshot{};
bool frameFocusRequested{};
int frameFocusIndex{-1};
@@ -87,7 +90,6 @@ namespace anm2ed::imgui
bool isFrameSelectionAnchorSet{};
std::vector<TimelineRowReference> rowDragReferences{};
glm::vec2 scroll{};
ImDrawList* pickerLineDrawList{};
ImGuiStyle style{};
public:
-1
View File
@@ -26,7 +26,6 @@ namespace anm2ed::imgui
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse))
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
ImGui::TextUnformatted(localize.get(LABEL_APPLICATION_NAME));
ImGui::PopFont();
+11 -3
View File
@@ -292,8 +292,8 @@ namespace anm2ed::imgui
std::filesystem::path window_asset_path_get(Document& document, const std::filesystem::path& path)
{
WorkingDirectory workingDirectory(document.directory_get());
return path::lower_case_backslash_handle(path::make_relative(path));
auto loadPath = path::lower_case_backslash_handle(path);
return path::backslash_replace(path::make_relative(loadPath, document.directory_get()));
}
void window_directory_open(Dialog& dialog, Document& document, const std::filesystem::path& path)
@@ -1900,7 +1900,12 @@ namespace anm2ed::imgui
window.reload = [](Window& window, Manager&, Settings&, Document& document, Clipboard&)
{
auto selected = document.spritesheet.selection;
window_edit(window, document, localize.get(EDIT_RELOAD_SPRITESHEETS), []() {});
window_edit(window, document, localize.get(EDIT_RELOAD_SPRITESHEETS),
[&]()
{
for (auto& id : selected)
document.texture_reload(id);
});
for (auto& id : selected)
{
@@ -2286,6 +2291,7 @@ namespace anm2ed::imgui
auto spritesheet = document.anm2.element_get(ElementType::SPRITESHEET, id);
if (!spritesheet) return;
spritesheet->path = window_asset_path_get(document, dialogPath);
document.texture_reload(id);
};
window_edit(document, Document::SPRITESHEETS, localize.get(EDIT_REPLACE_SPRITESHEET),
@@ -2544,6 +2550,7 @@ namespace anm2ed::imgui
{
auto sound = window_element_get(window, document.anm2, id);
if (!sound) continue;
document.sound_reload(id);
auto pathString = path::to_utf8(sound->path);
toasts.push(std::vformat(localize.get(TOAST_RELOAD_SOUND), std::make_format_args(id, pathString)));
logger.info(
@@ -2850,6 +2857,7 @@ namespace anm2ed::imgui
auto sound = document.anm2.element_get(ElementType::SOUND_ELEMENT, id);
if (!sound) return;
sound->path = window_asset_path_get(document, dialogPath);
document.sound_reload(id);
auto pathString = path::to_utf8(sound->path);
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SOUND), std::make_format_args(id, pathString)));
logger.info(std::vformat(localize.get(TOAST_REPLACE_SOUND, anm2ed::ENGLISH),
+245 -27
View File
@@ -1,23 +1,35 @@
#include "about.hpp"
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <imgui.h>
#include <vector>
#include <format>
#include <imgui.h>
#include "log.hpp"
#include "math.hpp"
#include "strings.hpp"
using namespace anm2ed::resource;
using namespace anm2ed::util;
using namespace glm;
namespace anm2ed::imgui::wizard
{
static constexpr auto CREDIT_DELAY = 1.0f;
static constexpr auto CREDIT_SCROLL_SPEED = 25.0f;
static constexpr float CREDIT_DELAY = 0.66f;
static constexpr float CREDIT_SCROLL_SPEED = 33.0f;
static constexpr float FRIEND_HEIGHT_MULTIPLIER = 2.0f;
static constexpr float FRIEND_PADDING_RATIO = 0.15f;
static constexpr int FRIEND_ROW_SLOT_COUNT = 5;
static constexpr int FRIEND_ORDER_LEFT[] = {resource::friends::MEAT_BOY, resource::friends::ISAAC};
static constexpr int FRIEND_ORDER_RIGHT[] = {resource::friends::STACY, resource::friends::ASH};
static constexpr About::Credit CREDITS[] = {
{"Anm2Ed", font::BOLD},
{"License: GPLv3"},
{""},
{"Designer", font::BOLD},
{"Design/Programming", font::BOLD},
{"Shweet"},
{"OpenAI Codex"},
{""},
@@ -30,7 +42,7 @@ namespace anm2ed::imgui::wizard
{"CxRedix (Chinese)"},
{"sawalk/사왈이 (Korean)"},
{""},
{"Based on the work of:", font::BOLD},
{"Based on the work of", font::BOLD},
{"Adrian Gavrilita"},
{"Simon Parzer"},
{"Matt Kapuszczak"},
@@ -41,6 +53,9 @@ namespace anm2ed::imgui::wizard
{"https://www.newgrounds.com/audio/listen/1565433"},
{"License: CC0"},
{""},
{"Dancing Characters", font::BOLD},
{"Shweet"},
{""},
{"Libraries", font::BOLD},
{"Dear ImGui"},
{"https://github.com/ocornut/imgui"},
@@ -89,46 +104,250 @@ namespace anm2ed::imgui::wizard
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{"enjoy the jams :)"},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
};
static constexpr auto CREDIT_COUNT = (int)(sizeof(CREDITS) / sizeof(About::Credit));
const Element* friend_animation_get(const About::FriendState& state)
{
auto animations = state.anm2.element_get(ElementType::ANIMATIONS);
if (!animations) return nullptr;
if (!animations->defaultAnimation.empty())
for (const auto& child : animations->children)
if (child.type == ElementType::ANIMATION && child.name == animations->defaultAnimation) return &child;
for (const auto& child : animations->children)
if (child.type == ElementType::ANIMATION) return &child;
return nullptr;
}
void friend_state_load(About::FriendState& state, const resource::friends::Info& info)
{
if (!state.canvas) state.canvas = std::make_unique<Canvas>(vec2(1.0f, 1.0f));
state.anm2 = Anm2{};
state.textures.clear();
state.rect = vec4(-1.0f);
state.time = 0.0f;
state.fps = 30.0f;
state.isLoaded = false;
std::string errorString{};
if (!state.anm2.load_string(info.anm2, &errorString))
{
logger.error(std::format("Unable to load friend animation {}: {}", info.name, errorString));
return;
}
if (auto anm2Info = state.anm2.element_get(ElementType::INFO))
if (anm2Info->fps > 0) state.fps = (float)anm2Info->fps;
if (auto spritesheets = state.anm2.element_get(ElementType::SPRITESHEETS))
for (auto& spritesheet : spritesheets->children)
if (spritesheet.type == ElementType::SPRITESHEET)
state.textures[spritesheet.id] = Texture(info.png, info.pngSize);
auto animation = friend_animation_get(state);
if (!animation)
{
logger.error(std::format("Friend animation {} has no animation.", info.name));
return;
}
state.rect = state.anm2.animation_rect(*animation, true);
state.isLoaded = state.rect != vec4(-1.0f) && !state.textures.empty();
}
void friend_state_tick(About::FriendState& state, float delta)
{
if (!state.isLoaded) return;
auto animation = friend_animation_get(state);
if (!animation || animation->frameNum <= 0) return;
state.time = std::fmod(state.time + delta * state.fps, (float)animation->frameNum);
if (state.time < 0.0f) state.time += (float)animation->frameNum;
}
ImVec2 friend_size_get(const About::FriendState& state, float height)
{
auto width = height;
if (state.rect.w > 0.0f && state.rect.z > 0.0f) width = height * (state.rect.z / state.rect.w);
return ImVec2(std::max(width, 1.0f), std::max(height, 1.0f));
}
void friend_layer_draw(About::FriendState& state, Resources& resources, const mat4& baseTransform,
const Element& layerAnimation, const Element& rootFrame)
{
if (layerAnimation.type == ElementType::GROUP)
{
for (auto& child : layerAnimation.children)
friend_layer_draw(state, resources, baseTransform, child, rootFrame);
return;
}
if (layerAnimation.type != ElementType::LAYER_ANIMATION || !layerAnimation.isVisible) return;
auto layer = state.anm2.element_get(ElementType::LAYER_ELEMENT, layerAnimation.layerId);
if (!layer) return;
auto textureIt = state.textures.find(layer->spritesheetId);
if (textureIt == state.textures.end() || !textureIt->second.is_valid()) return;
auto frame = state.anm2.frame_effective(layerAnimation.layerId, frame_generate(layerAnimation, state.time));
if (!frame.isVisible || frame.size == vec2()) return;
auto& texture = textureIt->second;
auto textureSize = vec2(texture.size);
if (textureSize.x <= 0.0f || textureSize.y <= 0.0f) return;
auto transform = baseTransform * math::quad_model_get(frame.size, frame.position, frame.pivot,
math::percent_to_unit(frame.scale), frame.rotation);
auto uvVertices = math::uv_vertices_get(frame.crop / textureSize, (frame.crop + frame.size) / textureSize);
auto tint = frame.tint * rootFrame.tint;
auto colorOffset = frame.colorOffset + rootFrame.colorOffset;
state.canvas->texture_render(resources.shaders[shader::TEXTURE], texture.id, transform, tint, colorOffset,
uvVertices.data());
}
void friend_canvas_draw(About::FriendState& state, Resources& resources, ImVec2 displaySize)
{
if (!state.isLoaded || !state.canvas) return;
auto canvasSize = vec2(std::max(displaySize.x, 1.0f), std::max(displaySize.y, 1.0f));
state.canvas->size_set(canvasSize);
state.canvas->bind();
state.canvas->viewport_set();
state.canvas->clear(vec4(0.0f));
auto animation = friend_animation_get(state);
if (!animation)
{
state.canvas->unbind();
return;
}
float zoom = 100.0f;
vec2 pan{};
auto rect = state.rect;
auto padding = std::max(2.0f, std::max(rect.z, rect.w) * FRIEND_PADDING_RATIO);
rect.x -= padding;
rect.y -= padding;
rect.z += padding * 2.0f;
rect.w += padding * 2.0f;
state.canvas->set_to_rect(zoom, pan, rect);
auto transform = state.canvas->transform_get(zoom, pan);
Element rootFrame{};
if (auto root = animation_item_get(*animation, ItemType::ROOT))
{
rootFrame = frame_generate(*root, state.time);
transform *= math::quad_model_parent_get(rootFrame.position, {}, math::percent_to_unit(rootFrame.scale),
rootFrame.rotation);
}
if (auto layerAnimations = element_child_first_get(*animation, ElementType::LAYER_ANIMATIONS))
for (auto& layerAnimation : layerAnimations->children)
friend_layer_draw(state, resources, transform, layerAnimation, rootFrame);
state.canvas->unbind();
}
void friend_row_draw(About& about, Resources& resources, const char* titleLabel, ImVec2 size)
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
auto titleSize = ImGui::CalcTextSize(titleLabel);
ImGui::PopFont();
auto slotWidth = std::max(size.x / (float)FRIEND_ROW_SLOT_COUNT, 1.0f);
float friendHeight = titleSize.y * FRIEND_HEIGHT_MULTIPLIER;
float maxAspect{};
for (auto index : FRIEND_ORDER_LEFT)
maxAspect = std::max(maxAspect, about.friendStates[index].rect.w > 0.0f
? about.friendStates[index].rect.z / about.friendStates[index].rect.w
: 1.0f);
for (auto index : FRIEND_ORDER_RIGHT)
maxAspect = std::max(maxAspect, about.friendStates[index].rect.w > 0.0f
? about.friendStates[index].rect.z / about.friendStates[index].rect.w
: 1.0f);
if (maxAspect > 0.0f) friendHeight = std::min(friendHeight, slotWidth / maxAspect);
friendHeight = std::max(friendHeight, 1.0f);
std::array<ImVec2, resource::friends::COUNT> friendSizes{};
for (auto index : FRIEND_ORDER_LEFT)
friendSizes[index] = friend_size_get(about.friendStates[index], friendHeight);
for (auto index : FRIEND_ORDER_RIGHT)
friendSizes[index] = friend_size_get(about.friendStates[index], friendHeight);
for (auto index : FRIEND_ORDER_LEFT)
friend_canvas_draw(about.friendStates[index], resources, friendSizes[index]);
for (auto index : FRIEND_ORDER_RIGHT)
friend_canvas_draw(about.friendStates[index], resources, friendSizes[index]);
auto rowHeight = std::max(titleSize.y, friendHeight);
auto rowMin = ImGui::GetCursorScreenPos();
auto rowMax = ImVec2(rowMin.x + size.x, rowMin.y + rowHeight);
auto drawList = ImGui::GetWindowDrawList();
auto textColor = ImGui::GetColorU32(ImGuiCol_Text);
auto slot_center_x_get = [&](int slot)
{
auto offset = slotWidth * ((float)slot + 0.5f);
return std::min(rowMin.x + offset, rowMax.x - slotWidth * 0.5f);
};
auto image_draw = [&](int slot, int index)
{
auto centerX = slot_center_x_get(slot);
auto size = friendSizes[index];
auto min = ImVec2(centerX - size.x * 0.5f, rowMin.y + (rowHeight - size.y) * 0.5f);
auto max = ImVec2(min.x + size.x, min.y + size.y);
drawList->AddImage((ImTextureID)(intptr_t)about.friendStates[index].canvas->texture, min, max);
};
drawList->PushClipRect(rowMin, rowMax, true);
image_draw(0, resource::friends::MEAT_BOY);
image_draw(1, resource::friends::ISAAC);
auto titleCenterX = slot_center_x_get(2);
auto titlePos = ImVec2(titleCenterX - titleSize.x * 0.5f, rowMin.y + (rowHeight - titleSize.y) * 0.5f);
drawList->AddText(resources.fonts[font::BOLD].get(), (float)font::SIZE_LARGE, titlePos, textColor, titleLabel);
image_draw(3, resource::friends::STACY);
image_draw(4, resource::friends::ASH);
drawList->PopClipRect();
ImGui::Dummy(ImVec2(size.x, rowHeight));
}
void About::reset(Resources& resources)
{
resources.music_track().play(true);
creditsState = {};
creditsState.spawnTimer = CREDIT_DELAY;
for (int i = 0; i < resource::friends::COUNT; ++i)
friend_state_load(friendStates[i], resource::friends::FRIENDS[i]);
}
void About::update(Resources& resources)
{
auto size = ImGui::GetContentRegionAvail();
auto applicationLabel = localize.get(LABEL_APPLICATION_NAME);
auto versionLabel = localize.get(LABEL_APPLICATION_VERSION);
auto titleLabel = localize.get(LABEL_APPLICATION_NAME);
auto delta = ImGui::GetIO().DeltaTime;
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
for (auto& friendState : friendStates)
friend_state_tick(friendState, delta);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(applicationLabel).x) / 2);
ImGui::TextUnformatted(applicationLabel);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(versionLabel).x) / 2);
ImGui::TextUnformatted(versionLabel);
ImGui::PopFont();
friend_row_draw(*this, resources, titleLabel, size);
auto creditRegionPos = ImGui::GetCursorScreenPos();
auto creditRegionSize = ImGui::GetContentRegionAvail();
@@ -140,7 +359,6 @@ namespace anm2ed::imgui::wizard
auto clipMax = ImVec2(creditRegionPos.x + creditRegionSize.x, creditRegionPos.y + creditRegionSize.y);
drawList->PushClipRect(creditRegionPos, clipMax, true);
auto delta = ImGui::GetIO().DeltaTime;
creditsState.spawnTimer -= delta;
auto maxVisible = std::max(1, (int)std::floor(creditRegionSize.y / (float)fontSize));
@@ -191,4 +409,4 @@ namespace anm2ed::imgui::wizard
drawList->PopClipRect();
}
}
}
}
+21 -1
View File
@@ -1,5 +1,13 @@
#pragma once
#include <array>
#include <memory>
#include <unordered_map>
#include <vector>
#include "../../anm2/anm2.hpp"
#include "../../canvas.hpp"
#include "../../resource/friends.hpp"
#include "../../resources.hpp"
namespace anm2ed::imgui::wizard
@@ -26,11 +34,23 @@ namespace anm2ed::imgui::wizard
int nextIndex{};
};
struct FriendState
{
Anm2 anm2{};
std::unique_ptr<Canvas> canvas{};
std::unordered_map<int, resource::Texture> textures{};
glm::vec4 rect{-1.0f};
float time{};
float fps{30.0f};
bool isLoaded{};
};
int creditsIndex{};
CreditsState creditsState{};
std::array<FriendState, resource::friends::COUNT> friendStates{};
void reset(Resources& resources);
void update(Resources& resources);
};
}
}