From ad125c15a29d614c40d970a19d06ad4911f18b74 Mon Sep 17 00:00:00 2001 From: shweet Date: Thu, 19 Mar 2026 03:46:42 -0400 Subject: [PATCH] queue oh el --- .vscode/launch.json | 8 - .vscode/tasks.json | 49 +- compile_commands.json | 2 +- src/anm2/anm2_spritesheets.cpp | 15 + src/anm2_new/anm2.cpp | 603 ------------------------- src/anm2_new/anm2.hpp | 90 ---- src/imgui/window/animation_preview.cpp | 9 + src/imgui/window/animation_preview.hpp | 1 + src/imgui/window/regions.cpp | 10 - src/imgui/window/timeline.cpp | 29 +- src/imgui/wizard/configure.cpp | 5 +- src/imgui_new/anm2_window.cpp | 16 - src/imgui_new/anm2_window.hpp | 12 - src/resource/strings.hpp | 4 +- src/settings.hpp | 1 + 15 files changed, 75 insertions(+), 779 deletions(-) delete mode 100644 src/anm2_new/anm2.cpp delete mode 100644 src/anm2_new/anm2.hpp delete mode 100644 src/imgui_new/anm2_window.cpp delete mode 100644 src/imgui_new/anm2_window.hpp diff --git a/.vscode/launch.json b/.vscode/launch.json index dcdee9f..cdfd141 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,12 +5,8 @@ "name": "Debug", "type": "cppdbg", "request": "launch", -<<<<<<< HEAD "preLaunchTask": "build-debug", "program": "${workspaceFolder}/out/build/linux-debug/bin/anm2ed", -======= - "program": "${workspaceFolder}/out/build/linux-debug/anm2ed", ->>>>>>> f58d894 (Render animation fixes, vs code tasks) "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -33,12 +29,8 @@ "name": "Release", "type": "cppdbg", "request": "launch", -<<<<<<< HEAD "preLaunchTask": "build-release", "program": "${workspaceFolder}/out/build/linux-release/bin/anm2ed", -======= - "program": "${workspaceFolder}/out/build/linux-release/anm2ed", ->>>>>>> f58d894 (Render animation fixes, vs code tasks) "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 546a48d..81fe5dd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,8 +2,7 @@ "version": "2.0.0", "tasks": [ { - "label": "run-debug", -<<<<<<< HEAD + "label": "configure-debug", "type": "shell", "command": "cmake", "args": [ @@ -24,22 +23,12 @@ "args": [ "--build", "out/build/linux-debug", + "--parallel", + "8", "--target", "anm2ed" ], - "dependsOn": "run-debug", -======= - "type": "shell", - "command": "cmake -S . -B out/build/linux-debug -DCMAKE_BUILD_TYPE=Debug && cmake --build out/build/linux-debug --parallel 8 --target anm2ed && ./out/build/linux-debug/anm2ed", - "problemMatcher": [ - "$gcc" - ] - }, - { - "label": "build", - "type": "shell", - "command": "cmake -S . -B out/build/linux-debug -DCMAKE_BUILD_TYPE=Debug && cmake --build out/build/linux-debug --parallel 8 --target anm2ed", ->>>>>>> f58d894 (Render animation fixes, vs code tasks) + "dependsOn": "configure-debug", "group": { "kind": "build", "isDefault": true @@ -49,9 +38,15 @@ ] }, { - "label": "run-release", + "label": "run-debug", + "type": "shell", + "command": "./out/build/linux-debug/bin/anm2ed", + "dependsOn": "build-debug", + "problemMatcher": [] + }, + { + "label": "configure-release", "type": "shell", -<<<<<<< HEAD "command": "cmake", "args": [ "-S", @@ -60,9 +55,6 @@ "out/build/linux-release", "-DCMAKE_BUILD_TYPE=Release" ], -======= - "command": "cmake -S . -B out/build/linux-release -DCMAKE_BUILD_TYPE=Release && cmake --build out/build/linux-release --parallel 8 --target anm2ed && ./out/build/linux-release/anm2ed", ->>>>>>> f58d894 (Render animation fixes, vs code tasks) "problemMatcher": [ "$gcc" ] @@ -70,28 +62,27 @@ { "label": "build-release", "type": "shell", -<<<<<<< HEAD "command": "cmake", "args": [ "--build", "out/build/linux-release", + "--parallel", + "8", "--target", "anm2ed" ], - "dependsOn": "run-release", -======= - "command": "cmake -S . -B out/build/linux-release -DCMAKE_BUILD_TYPE=Release && cmake --build out/build/linux-release --parallel 8 --target anm2ed", ->>>>>>> f58d894 (Render animation fixes, vs code tasks) + "dependsOn": "configure-release", "group": "build", "problemMatcher": [ "$gcc" ] }, { - "label": "build", - "dependsOn": "build-debug", - "group": "build", + "label": "run-release", + "type": "shell", + "command": "./out/build/linux-release/bin/anm2ed", + "dependsOn": "build-release", "problemMatcher": [] } ] -} \ No newline at end of file +} diff --git a/compile_commands.json b/compile_commands.json index 5e95284..bc0dce8 120000 --- a/compile_commands.json +++ b/compile_commands.json @@ -1 +1 @@ -/home/anon/sda/Personal/Repos/anm2ed/build/compile_commands.json \ No newline at end of file +/home/anon/sda/Personal/Repos/anm2ed/out/build/linux-debug/compile_commands.json \ No newline at end of file diff --git a/src/anm2/anm2_spritesheets.cpp b/src/anm2/anm2_spritesheets.cpp index e4b55ae..b99a7b3 100644 --- a/src/anm2/anm2_spritesheets.cpp +++ b/src/anm2/anm2_spritesheets.cpp @@ -419,6 +419,7 @@ namespace anm2ed::anm2 std::unordered_map offsets{}; offsets[baseId] = {}; + auto baseTextureSize = base.texture.size; auto mergedTexture = base.texture; for (auto id : ids) { @@ -446,6 +447,20 @@ namespace anm2ed::anm2 base.regionOrder.push_back(id); } + auto baseLocationRegionID = map::next_id_get(base.regions); + auto baseFilename = path::to_utf8(base.path.stem()); + auto baseLocationRegionName = baseFilename.empty() ? std::format("#{}", baseId) : baseFilename; + auto baseLocationRegionPivot = + regionOrigin == origin::ORIGIN_CENTER ? glm::vec2(baseTextureSize) * 0.5f : glm::vec2(); + base.regions[baseLocationRegionID] = { + .name = baseLocationRegionName, + .crop = {}, + .pivot = glm::ivec2(baseLocationRegionPivot), + .size = baseTextureSize, + .origin = regionOrigin, + }; + base.regionOrder.push_back(baseLocationRegionID); + for (auto id : ids) { if (id == baseId) continue; diff --git a/src/anm2_new/anm2.cpp b/src/anm2_new/anm2.cpp deleted file mode 100644 index ce237dd..0000000 --- a/src/anm2_new/anm2.cpp +++ /dev/null @@ -1,603 +0,0 @@ -#include "anm2.hpp" - -#include -#include -#include -#include - -#include "../log.hpp" -#include "../util/path_.hpp" -#include "../util/working_directory.hpp" -#include "../util/xml_.hpp" - -using namespace tinyxml2; -using namespace anm2ed::util; - -namespace anm2ed::resource::anm2_new -{ - Anm2::Item::Item(XMLElement* element, Type _type) - { - if (!element) return; - - type = _type; - - auto isFrameOwnerType = - _type == ROOT_ANIMATION || _type == LAYER_ANIMATION || _type == NULL_ANIMATION || _type == TRIGGERS; - auto isFrameLikeElement = - element->Name() && (std::strcmp(element->Name(), "Frame") == 0 || std::strcmp(element->Name(), "Trigger") == 0); - - if (isFrameOwnerType && isFrameLikeElement) - { - ownerType = _type; - type = _type == TRIGGERS ? TRIGGER : FRAME; - - switch (_type) - { - case ROOT_ANIMATION: - case NULL_ANIMATION: - element->QueryFloatAttribute("XPosition", &position.x); - element->QueryFloatAttribute("YPosition", &position.y); - element->QueryFloatAttribute("XScale", &scale.x); - element->QueryFloatAttribute("YScale", &scale.y); - element->QueryIntAttribute("Delay", &duration); - element->QueryBoolAttribute("Visible", &isVisible); - xml::query_color_attribute(element, "RedTint", tint.r); - xml::query_color_attribute(element, "GreenTint", tint.g); - xml::query_color_attribute(element, "BlueTint", tint.b); - xml::query_color_attribute(element, "AlphaTint", tint.a); - xml::query_color_attribute(element, "RedOffset", colorOffset.r); - xml::query_color_attribute(element, "GreenOffset", colorOffset.g); - xml::query_color_attribute(element, "BlueOffset", colorOffset.b); - element->QueryFloatAttribute("Rotation", &rotation); - element->QueryBoolAttribute("Interpolated", &isInterpolated); - break; - case LAYER_ANIMATION: - element->QueryIntAttribute("RegionId", ®ionID); - element->QueryFloatAttribute("XPosition", &position.x); - element->QueryFloatAttribute("YPosition", &position.y); - element->QueryFloatAttribute("XPivot", &pivot.x); - element->QueryFloatAttribute("YPivot", &pivot.y); - element->QueryFloatAttribute("XCrop", &crop.x); - element->QueryFloatAttribute("YCrop", &crop.y); - element->QueryFloatAttribute("Width", &size.x); - element->QueryFloatAttribute("Height", &size.y); - element->QueryFloatAttribute("XScale", &scale.x); - element->QueryFloatAttribute("YScale", &scale.y); - element->QueryIntAttribute("Delay", &duration); - element->QueryBoolAttribute("Visible", &isVisible); - xml::query_color_attribute(element, "RedTint", tint.r); - xml::query_color_attribute(element, "GreenTint", tint.g); - xml::query_color_attribute(element, "BlueTint", tint.b); - xml::query_color_attribute(element, "AlphaTint", tint.a); - xml::query_color_attribute(element, "RedOffset", colorOffset.r); - xml::query_color_attribute(element, "GreenOffset", colorOffset.g); - xml::query_color_attribute(element, "BlueOffset", colorOffset.b); - element->QueryFloatAttribute("Rotation", &rotation); - element->QueryBoolAttribute("Interpolated", &isInterpolated); - break; - case TRIGGERS: - { - element->QueryIntAttribute("EventId", &eventID); - element->QueryIntAttribute("AtFrame", &atFrame); - int soundID{}; - if (element->QueryIntAttribute("SoundId", &soundID) == XML_SUCCESS) soundIDs.push_back(soundID); - for (auto* child = element->FirstChildElement("Sound"); child; child = child->NextSiblingElement("Sound")) - if (child->QueryIntAttribute("Id", &soundID) == XML_SUCCESS) soundIDs.push_back(soundID); - break; - } - default: - break; - } - - return; - } - - switch (_type) - { - case INFO: - xml::query_string_attribute(element, "CreatedBy", &createdBy); - xml::query_string_attribute(element, "CreatedOn", &createdOn); - element->QueryIntAttribute("Fps", &fps); - element->QueryIntAttribute("Version", &version); - break; - case SPRITESHEET: - element->QueryIntAttribute("Id", &id); - xml::query_path_attribute(element, "Path", &path); - path = util::path::lower_case_backslash_handle(path); - texture = Texture(path); - break; - case REGION: - { - element->QueryIntAttribute("Id", &id); - xml::query_string_attribute(element, "Name", &name); - element->QueryFloatAttribute("XCrop", &crop.x); - element->QueryFloatAttribute("YCrop", &crop.y); - element->QueryFloatAttribute("Width", &size.x); - element->QueryFloatAttribute("Height", &size.y); - - auto* origin = element->Attribute("Origin"); - if (origin && std::string(origin) == "TopLeft") - pivot = {}; - else if (origin && std::string(origin) == "Center") - pivot = glm::vec2((int)(size.x / 2.0f), (int)(size.y / 2.0f)); - else - { - element->QueryFloatAttribute("XPivot", &pivot.x); - element->QueryFloatAttribute("YPivot", &pivot.y); - } - break; - } - case LAYER: - element->QueryIntAttribute("Id", &id); - xml::query_string_attribute(element, "Name", &name); - element->QueryIntAttribute("SpritesheetId", &spritesheetID); - break; - case NULL_: - element->QueryIntAttribute("Id", &id); - xml::query_string_attribute(element, "Name", &name); - element->QueryBoolAttribute("ShowRect", &isShowRect); - break; - case EVENT: - element->QueryIntAttribute("Id", &id); - xml::query_string_attribute(element, "Name", &name); - break; - case SOUND: - element->QueryIntAttribute("Id", &id); - xml::query_path_attribute(element, "Path", &path); - path = util::path::lower_case_backslash_handle(path); - sound = Audio(path); - break; - case ANIMATION: - xml::query_string_attribute(element, "Name", &name); - element->QueryIntAttribute("FrameNum", &frameNum); - element->QueryBoolAttribute("Loop", &isLoop); - break; - case ROOT_ANIMATION: - case TRIGGERS: - break; - case LAYER_ANIMATION: - element->QueryIntAttribute("LayerId", &itemID); - element->QueryBoolAttribute("Visible", &isVisible); - break; - case NULL_ANIMATION: - element->QueryIntAttribute("NullId", &itemID); - element->QueryBoolAttribute("Visible", &isVisible); - break; - case FRAME: - case TRIGGER: - default: - break; - } - } - - XMLElement* Anm2::Item::to_element(XMLDocument& document) const - { - switch (type) - { - case INFO: - { - auto* element = document.NewElement("Info"); - element->SetAttribute("CreatedBy", createdBy.c_str()); - element->SetAttribute("CreatedOn", createdOn.c_str()); - element->SetAttribute("Fps", fps); - element->SetAttribute("Version", version); - return element; - } - case SPRITESHEET: - { - auto* element = document.NewElement("Spritesheet"); - element->SetAttribute("Id", id); - auto pathString = util::path::to_utf8(path); - element->SetAttribute("Path", pathString.c_str()); - return element; - } - case REGION: - { - auto* element = document.NewElement("Region"); - element->SetAttribute("Id", id); - element->SetAttribute("Name", name.c_str()); - element->SetAttribute("XCrop", crop.x); - element->SetAttribute("YCrop", crop.y); - element->SetAttribute("Width", size.x); - element->SetAttribute("Height", size.y); - element->SetAttribute("XPivot", pivot.x); - element->SetAttribute("YPivot", pivot.y); - return element; - } - case LAYER: - { - auto* element = document.NewElement("Layer"); - element->SetAttribute("Id", id); - element->SetAttribute("Name", name.c_str()); - element->SetAttribute("SpritesheetId", spritesheetID); - return element; - } - case NULL_: - { - auto* element = document.NewElement("Null"); - element->SetAttribute("Id", id); - element->SetAttribute("Name", name.c_str()); - if (isShowRect) element->SetAttribute("ShowRect", isShowRect); - return element; - } - case EVENT: - { - auto* element = document.NewElement("Event"); - element->SetAttribute("Id", id); - element->SetAttribute("Name", name.c_str()); - return element; - } - case SOUND: - { - auto* element = document.NewElement("Sound"); - element->SetAttribute("Id", id); - auto pathString = util::path::to_utf8(path); - element->SetAttribute("Path", pathString.c_str()); - return element; - } - case ANIMATION: - { - auto* element = document.NewElement("Animation"); - element->SetAttribute("Name", name.c_str()); - element->SetAttribute("FrameNum", frameNum); - element->SetAttribute("Loop", isLoop); - return element; - } - case ROOT_ANIMATION: - return document.NewElement("RootAnimation"); - case LAYER_ANIMATION: - { - auto* element = document.NewElement("LayerAnimation"); - element->SetAttribute("LayerId", itemID); - element->SetAttribute("Visible", isVisible); - return element; - } - case NULL_ANIMATION: - { - auto* element = document.NewElement("NullAnimation"); - element->SetAttribute("NullId", itemID); - element->SetAttribute("Visible", isVisible); - return element; - } - case TRIGGERS: - return document.NewElement("Triggers"); - case TRIGGER: - { - auto* element = document.NewElement("Trigger"); - if (eventID != -1) element->SetAttribute("EventId", eventID); - for (auto soundID : soundIDs) - { - if (soundID == -1) continue; - auto* soundElement = element->InsertNewChildElement("Sound"); - soundElement->SetAttribute("Id", soundID); - } - element->SetAttribute("AtFrame", atFrame); - return element; - } - case FRAME: - { - auto* element = document.NewElement("Frame"); - - if (ownerType == LAYER_ANIMATION && regionID != -1) element->SetAttribute("RegionId", regionID); - element->SetAttribute("XPosition", position.x); - element->SetAttribute("YPosition", position.y); - - if (ownerType == LAYER_ANIMATION) - { - element->SetAttribute("XPivot", pivot.x); - element->SetAttribute("YPivot", pivot.y); - element->SetAttribute("XCrop", crop.x); - element->SetAttribute("YCrop", crop.y); - element->SetAttribute("Width", size.x); - element->SetAttribute("Height", size.y); - } - - element->SetAttribute("XScale", scale.x); - element->SetAttribute("YScale", scale.y); - element->SetAttribute("Delay", duration); - element->SetAttribute("Visible", isVisible); - element->SetAttribute("RedTint", (int)glm::clamp(tint.r * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("GreenTint", (int)glm::clamp(tint.g * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("BlueTint", (int)glm::clamp(tint.b * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("AlphaTint", (int)glm::clamp(tint.a * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("RedOffset", (int)glm::clamp(colorOffset.r * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("GreenOffset", (int)glm::clamp(colorOffset.g * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("BlueOffset", (int)glm::clamp(colorOffset.b * 255.0f, 0.0f, 255.0f)); - element->SetAttribute("Rotation", rotation); - element->SetAttribute("Interpolated", isInterpolated); - - return element; - } - default: - break; - } - - return document.NewElement("Item"); - } - - std::string Anm2::Item::to_string() const - { - XMLDocument document; - document.InsertEndChild(to_element(document)); - return xml::document_to_string(document); - } - - Anm2::Anm2(const std::filesystem::path& path) - { - XMLDocument document; - auto pathString = util::path::to_utf8(path); - - if (document.LoadFile(pathString.c_str()) != XML_SUCCESS) - { - logger.error(std::format("Failed to initialize anm2: {} ({})", pathString, document.ErrorStr())); - isValid = false; - return; - } - - WorkingDirectory workingDirectory(path, WorkingDirectory::FILE); - - this->path = path; - isValid = true; - - auto item_add = [&](Item item) { items.emplace_back(std::move(item)); }; - - if (auto* root = document.RootElement()) - { - if (auto* infoElement = root->FirstChildElement("Info")) item_add(Item(infoElement, INFO)); - - if (auto* contentElement = root->FirstChildElement("Content")) - { - if (auto* spritesheetsElement = contentElement->FirstChildElement("Spritesheets")) - { - for (auto* child = spritesheetsElement->FirstChildElement("Spritesheet"); child; - child = child->NextSiblingElement("Spritesheet")) - { - auto spritesheet = Item(child, SPRITESHEET); - auto spritesheetID = spritesheet.id; - item_add(spritesheet); - - for (auto* regionChild = child->FirstChildElement("Region"); regionChild; - regionChild = regionChild->NextSiblingElement("Region")) - { - auto region = Item(regionChild, REGION); - region.itemID = spritesheetID; - item_add(region); - } - } - } - - if (auto* layersElement = contentElement->FirstChildElement("Layers")) - for (auto* child = layersElement->FirstChildElement("Layer"); child; - child = child->NextSiblingElement("Layer")) - item_add(Item(child, LAYER)); - - if (auto* nullsElement = contentElement->FirstChildElement("Nulls")) - for (auto* child = nullsElement->FirstChildElement("Null"); child; child = child->NextSiblingElement("Null")) - item_add(Item(child, NULL_)); - - if (auto* eventsElement = contentElement->FirstChildElement("Events")) - for (auto* child = eventsElement->FirstChildElement("Event"); child; - child = child->NextSiblingElement("Event")) - item_add(Item(child, EVENT)); - - if (auto* soundsElement = contentElement->FirstChildElement("Sounds")) - for (auto* child = soundsElement->FirstChildElement("Sound"); child; - child = child->NextSiblingElement("Sound")) - item_add(Item(child, SOUND)); - } - - if (auto* animationsElement = root->FirstChildElement("Animations")) - { - int animationIndex = 0; - for (auto* animationElement = animationsElement->FirstChildElement("Animation"); animationElement; - animationElement = animationElement->NextSiblingElement("Animation")) - { - auto animation = Item(animationElement, ANIMATION); - animation.id = animationIndex++; - item_add(animation); - - if (auto* rootAnimationElement = animationElement->FirstChildElement("RootAnimation")) - { - Item rootAnimation{}; - rootAnimation.type = ROOT_ANIMATION; - rootAnimation.animationID = animation.id; - item_add(rootAnimation); - - for (auto* frameElement = rootAnimationElement->FirstChildElement("Frame"); frameElement; - frameElement = frameElement->NextSiblingElement("Frame")) - { - auto frame = Item(frameElement, ROOT_ANIMATION); - frame.animationID = animation.id; - frame.ownerID = -1; - item_add(frame); - } - } - - if (auto* layerAnimationsElement = animationElement->FirstChildElement("LayerAnimations")) - { - for (auto* layerAnimationElement = layerAnimationsElement->FirstChildElement("LayerAnimation"); - layerAnimationElement; - layerAnimationElement = layerAnimationElement->NextSiblingElement("LayerAnimation")) - { - auto layerAnimation = Item(layerAnimationElement, LAYER_ANIMATION); - layerAnimation.animationID = animation.id; - item_add(layerAnimation); - - for (auto* frameElement = layerAnimationElement->FirstChildElement("Frame"); frameElement; - frameElement = frameElement->NextSiblingElement("Frame")) - { - auto frame = Item(frameElement, LAYER_ANIMATION); - frame.animationID = animation.id; - frame.ownerID = layerAnimation.itemID; - item_add(frame); - } - } - } - - if (auto* nullAnimationsElement = animationElement->FirstChildElement("NullAnimations")) - { - for (auto* nullAnimationElement = nullAnimationsElement->FirstChildElement("NullAnimation"); - nullAnimationElement; nullAnimationElement = nullAnimationElement->NextSiblingElement("NullAnimation")) - { - auto nullAnimation = Item(nullAnimationElement, NULL_ANIMATION); - nullAnimation.animationID = animation.id; - item_add(nullAnimation); - - for (auto* frameElement = nullAnimationElement->FirstChildElement("Frame"); frameElement; - frameElement = frameElement->NextSiblingElement("Frame")) - { - auto frame = Item(frameElement, NULL_ANIMATION); - frame.animationID = animation.id; - frame.ownerID = nullAnimation.itemID; - item_add(frame); - } - } - } - - if (auto* triggersElement = animationElement->FirstChildElement("Triggers")) - { - Item triggers{}; - triggers.type = TRIGGERS; - triggers.animationID = animation.id; - item_add(triggers); - - for (auto* triggerElement = triggersElement->FirstChildElement("Trigger"); triggerElement; - triggerElement = triggerElement->NextSiblingElement("Trigger")) - { - auto trigger = Item(triggerElement, TRIGGERS); - trigger.animationID = animation.id; - trigger.ownerID = -1; - item_add(trigger); - } - } - } - } - } - - logger.info(std::format("Initialized anm2: {}", pathString)); - } - - XMLElement* Anm2::to_element(XMLDocument& document) const - { - auto* root = document.NewElement("AnimatedActor"); - auto add_frames = [&](XMLElement* parent, Type ownerType, int animationID, int ownerID) - { - for (auto& item : items) - { - if (item.type != FRAME && item.type != TRIGGER) continue; - if (item.ownerType != ownerType) continue; - if (item.animationID != animationID) continue; - if (item.ownerID != ownerID) continue; - parent->InsertEndChild(item.to_element(document)); - } - }; - - const Item* info = nullptr; - for (auto& item : items) - if (item.type == INFO) - { - info = &item; - break; - } - if (info) root->InsertEndChild(info->to_element(document)); - - auto* contentElement = document.NewElement("Content"); - auto* spritesheetsElement = document.NewElement("Spritesheets"); - auto* layersElement = document.NewElement("Layers"); - auto* nullsElement = document.NewElement("Nulls"); - auto* eventsElement = document.NewElement("Events"); - auto* soundsElement = document.NewElement("Sounds"); - - for (auto& item : items) - { - if (item.type != SPRITESHEET) continue; - auto* spritesheetElement = item.to_element(document); - for (auto& region : items) - if (region.type == REGION && region.itemID == item.id) - spritesheetElement->InsertEndChild(region.to_element(document)); - spritesheetsElement->InsertEndChild(spritesheetElement); - } - - for (auto& item : items) - if (item.type == LAYER) layersElement->InsertEndChild(item.to_element(document)); - - for (auto& item : items) - if (item.type == NULL_) nullsElement->InsertEndChild(item.to_element(document)); - - for (auto& item : items) - if (item.type == EVENT) eventsElement->InsertEndChild(item.to_element(document)); - - for (auto& item : items) - if (item.type == SOUND) soundsElement->InsertEndChild(item.to_element(document)); - - contentElement->InsertEndChild(spritesheetsElement); - contentElement->InsertEndChild(layersElement); - contentElement->InsertEndChild(nullsElement); - contentElement->InsertEndChild(eventsElement); - if (soundsElement->FirstChildElement("Sound")) contentElement->InsertEndChild(soundsElement); - root->InsertEndChild(contentElement); - - auto* animationsElement = document.NewElement("Animations"); - - for (auto& animation : items) - { - if (animation.type != ANIMATION) continue; - auto* animationElement = animation.to_element(document); - - auto* rootAnimationElement = document.NewElement("RootAnimation"); - add_frames(rootAnimationElement, ROOT_ANIMATION, animation.id, -1); - animationElement->InsertEndChild(rootAnimationElement); - - auto* layerAnimationsElement = document.NewElement("LayerAnimations"); - for (auto& layerAnimation : items) - { - if (layerAnimation.type != LAYER_ANIMATION || layerAnimation.animationID != animation.id) continue; - auto* layerAnimationElement = layerAnimation.to_element(document); - add_frames(layerAnimationElement, LAYER_ANIMATION, animation.id, layerAnimation.itemID); - layerAnimationsElement->InsertEndChild(layerAnimationElement); - } - animationElement->InsertEndChild(layerAnimationsElement); - - auto* nullAnimationsElement = document.NewElement("NullAnimations"); - for (auto& nullAnimation : items) - { - if (nullAnimation.type != NULL_ANIMATION || nullAnimation.animationID != animation.id) continue; - auto* nullAnimationElement = nullAnimation.to_element(document); - add_frames(nullAnimationElement, NULL_ANIMATION, animation.id, nullAnimation.itemID); - nullAnimationsElement->InsertEndChild(nullAnimationElement); - } - animationElement->InsertEndChild(nullAnimationsElement); - - auto* triggersElement = document.NewElement("Triggers"); - add_frames(triggersElement, TRIGGERS, animation.id, -1); - animationElement->InsertEndChild(triggersElement); - - animationsElement->InsertEndChild(animationElement); - } - - root->InsertEndChild(animationsElement); - return root; - } - - bool Anm2::serialize(const std::filesystem::path& path, std::string* errorString) const - { - XMLDocument document; - document.InsertEndChild(to_element(document)); - - auto pathString = util::path::to_utf8(path); - if (document.SaveFile(pathString.c_str()) != XML_SUCCESS) - { - if (errorString) *errorString = document.ErrorStr(); - return false; - } - - return true; - } - - std::string Anm2::to_string() const - { - XMLDocument document; - document.InsertEndChild(to_element(document)); - return xml::document_to_string(document); - } -} diff --git a/src/anm2_new/anm2.hpp b/src/anm2_new/anm2.hpp deleted file mode 100644 index ace6406..0000000 --- a/src/anm2_new/anm2.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "../resource/audio.hpp" -#include "../resource/texture.hpp" - -#include - -namespace anm2ed::resource::anm2_new -{ - class Anm2 - { - public: - enum Type - { - NONE, - INFO, - SPRITESHEET, - REGION, - LAYER, - NULL_, - EVENT, - SOUND, - ANIMATION, - ROOT_ANIMATION, - LAYER_ANIMATION, - NULL_ANIMATION, - TRIGGERS, - FRAME, - TRIGGER - }; - - class Item - { - public: - Type type{NONE}; - Type ownerType{NONE}; - std::string name{}; - bool isInterpolated{false}; - bool isLoop{false}; - bool isVisible{false}; - bool isShowRect{false}; - glm::vec2 crop{}; - glm::vec2 pivot{}; - glm::vec2 position{}; - glm::vec2 scale{100, 100}; - glm::vec2 size{}; - glm::vec3 colorOffset{}; - glm::vec4 tint{1, 1, 1, 1}; - int atFrame{-1}; - int duration{1}; - int eventID{-1}; - int fps{30}; - int frameNum{1}; - int id{-1}; - int itemID{-1}; - int ownerID{-1}; - int animationID{-1}; - int spritesheetID{-1}; - int regionID{-1}; - int version{0}; - float rotation{}; - resource::Texture texture{}; - resource::Audio sound{}; - std::filesystem::path path{}; - std::string createdBy{}; - std::string createdOn{}; - std::vector soundIDs{}; - - Item() = default; - Item(tinyxml2::XMLElement*, Type); - tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&) const; - std::string to_string() const; - }; - - std::vector items{}; - std::filesystem::path path{}; - bool isValid{false}; - - Anm2() = default; - Anm2(const std::filesystem::path&); - tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&) const; - bool serialize(const std::filesystem::path&, std::string* = nullptr) const; - std::string to_string() const; - }; -} diff --git a/src/imgui/window/animation_preview.cpp b/src/imgui/window/animation_preview.cpp index 78b817c..82cea0d 100644 --- a/src/imgui/window/animation_preview.cpp +++ b/src/imgui/window/animation_preview.cpp @@ -165,6 +165,12 @@ namespace anm2ed::imgui auto& overlayIndex = document.overlayIndex; auto& pan = document.previewPan; + auto stop_all_sounds = [&]() + { + for (auto& sound : anm2.content.sounds | std::views::values) + sound.audio.stop(mixer); + }; + if (manager.isRecording) { auto& ffmpegPath = settings.renderFFmpegPath; @@ -417,6 +423,9 @@ namespace anm2ed::imgui frameTime = playback.time; } + + if (wasPlaybackPlaying && !playback.isPlaying) stop_all_sounds(); + wasPlaybackPlaying = playback.isPlaying; } void AnimationPreview::update(Manager& manager, Settings& settings, Resources& resources) diff --git a/src/imgui/window/animation_preview.hpp b/src/imgui/window/animation_preview.hpp index 152a229..b9dcd7a 100644 --- a/src/imgui/window/animation_preview.hpp +++ b/src/imgui/window/animation_preview.hpp @@ -14,6 +14,7 @@ namespace anm2ed::imgui { MIX_Mixer* mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr); AudioStream audioStream = AudioStream(mixer); + bool wasPlaybackPlaying{}; bool isPreviewHovered{}; bool isSizeTrySet{true}; Settings savedSettings{}; diff --git a/src/imgui/window/regions.cpp b/src/imgui/window/regions.cpp index e125644..261c3fb 100644 --- a/src/imgui/window/regions.cpp +++ b/src/imgui/window/regions.cpp @@ -5,7 +5,6 @@ #include -<<<<<<< HEAD #include "document.hpp" #include "log.hpp" #include "map_.hpp" @@ -13,15 +12,6 @@ #include "strings.hpp" #include "toast.hpp" #include "vector_.hpp" -======= -#include "document.h" -#include "log.h" -#include "map_.h" -#include "math_.h" -#include "strings.h" -#include "toast.h" -#include "vector_.h" ->>>>>>> f58d894 (Render animation fixes, vs code tasks) #include "../../util/map_.hpp" diff --git a/src/imgui/window/timeline.cpp b/src/imgui/window/timeline.cpp index 986aa42..81f1c38 100644 --- a/src/imgui/window/timeline.cpp +++ b/src/imgui/window/timeline.cpp @@ -83,7 +83,6 @@ namespace anm2ed::imgui constexpr auto FRAME_DRAG_PAYLOAD_ID = "Frame Drag Drop"; constexpr auto FRAME_TOOLTIP_HOVER_DELAY = 0.75f; // Extra delay for frame info tooltip. -#define ITEM_FRAME_CHILD_HEIGHT ImGui::GetTextLineHeightWithSpacing() + (ImGui::GetStyle().WindowPadding.y * 1.5) #define ITEM_CHILD_WIDTH ImGui::GetTextLineHeightWithSpacing() * 12.5 void Timeline::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard) @@ -95,6 +94,8 @@ namespace anm2ed::imgui auto& frames = document.frames; auto& region = document.region; auto animation = document.animation_get(); + auto itemFrameChildHeight = (ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().WindowPadding.y * 1.5f) * + settings.timelineItemHeight; style = ImGui::GetStyle(); auto isLightTheme = settings.theme == theme::LIGHT; @@ -172,6 +173,13 @@ namespace anm2ed::imgui frameFocusRequested = reference.frameIndex >= 0; }; + auto playback_stop = [&]() + { + playback.isPlaying = false; + playback.isFinished = false; + playback.timing_reset(); + }; + auto frame_insert = [&](anm2::Item* item) { if (!item) return; @@ -597,7 +605,7 @@ namespace anm2ed::imgui ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding); - auto itemSize = ImVec2(ImGui::GetContentRegionAvail().x, ITEM_FRAME_CHILD_HEIGHT); + auto itemSize = ImVec2(ImGui::GetContentRegionAvail().x, itemFrameChildHeight); if (ImGui::BeginChild(label.c_str(), itemSize, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollWithMouse)) { @@ -961,7 +969,7 @@ namespace anm2ed::imgui ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding); - auto childSize = ImVec2(width, ITEM_FRAME_CHILD_HEIGHT); + auto childSize = ImVec2(width, itemFrameChildHeight); ImGui::PopStyleVar(2); @@ -1046,7 +1054,10 @@ namespace anm2ed::imgui } if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && ImGui::IsMouseDown(0)) + { + if (!isDragging) playback_stop(); isDragging = true; + } auto childPos = ImGui::GetWindowPos(); auto mousePos = ImGui::GetIO().MousePos; @@ -1497,12 +1508,11 @@ namespace anm2ed::imgui ImGui::GetTextLineHeightWithSpacing() + (ImGui::GetStyle().WindowPadding.y * 2)); auto playheadIndex = std::floor(playback.time); auto lineCenterX = cursorScreenPos.x + frameSize.x * (playheadIndex + 0.6f) - scroll.x; - float lineOffsetY = frameSize.y; auto linePos = - ImVec2(lineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), cursorScreenPos.y + frameSize.y + lineOffsetY); + ImVec2(lineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), cursorScreenPos.y + (frameSize.y * 2.0f)); auto lineSize = ImVec2((PLAYHEAD_LINE_THICKNESS / 2.0f), - viewListChildSize.y - frameSize.y - lineOffsetY - - (isHorizontalScroll ? ImGui::GetStyle().ScrollbarSize : 0.0f)); + viewListChildSize.y - frameSize.y - + (isHorizontalScroll ? ImGui::GetStyle().ScrollbarSize * 2.0f : 0.0f)); auto rectMin = windowDrawList->GetClipRectMin(); auto rectMax = windowDrawList->GetClipRectMax(); @@ -1623,7 +1633,8 @@ namespace anm2ed::imgui ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); if (ImGui::Begin(localize.get(LABEL_TIMELINE_WINDOW), &settings.windowIsTimeline)) { - isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); + isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows | + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); frames_child(); items_child(); } @@ -1853,12 +1864,14 @@ namespace anm2ed::imgui if (shortcut(manager.chords[SHORTCUT_MOVE_PLAYHEAD_BACK], shortcut::GLOBAL)) { + playback_stop(); playback.decrement(settings.playbackIsClamp ? animation->frameNum : anm2::FRAME_NUM_MAX); document.frameTime = playback.time; } if (shortcut(manager.chords[SHORTCUT_MOVE_PLAYHEAD_FORWARD], shortcut::GLOBAL)) { + playback_stop(); playback.increment(settings.playbackIsClamp ? animation->frameNum : anm2::FRAME_NUM_MAX); document.frameTime = playback.time; } diff --git a/src/imgui/wizard/configure.cpp b/src/imgui/wizard/configure.cpp index bf91bc4..5e3dc0b 100644 --- a/src/imgui/wizard/configure.cpp +++ b/src/imgui/wizard/configure.cpp @@ -23,6 +23,9 @@ namespace anm2ed::imgui::wizard ImGui::SeparatorText(localize.get(LABEL_WINDOW_MENU)); input_float_range(localize.get(LABEL_UI_SCALE), temporary.uiScale, 0.5f, 2.0f, 0.25f, 0.25f, "%.2f"); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_UI_SCALE)); + input_float_range(localize.get(LABEL_ITEM_HEIGHT), temporary.timelineItemHeight, 0.75f, 1.25f, 0.05f, 0.05f, + "%.2f"); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ITEM_HEIGHT)); ImGui::Checkbox(localize.get(LABEL_VSYNC), &temporary.isVsync); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_VSYNC)); @@ -205,4 +208,4 @@ namespace anm2ed::imgui::wizard if (ImGui::Button(localize.get(LABEL_CLOSE), widgetSize)) isSet = true; ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CLOSE_SETTINGS)); } -} \ No newline at end of file +} diff --git a/src/imgui_new/anm2_window.cpp b/src/imgui_new/anm2_window.cpp deleted file mode 100644 index 1b3bb66..0000000 --- a/src/imgui_new/anm2_window.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "anm2_window.hpp" - -#include "imgui.h" - -using namespace anm2ed::resource::anm2_new; - -namespace anm2ed::imgui_new -{ - void Window::update(Anm2& anm2, Anm2::Type type, Settings& settings) - { - if (ImGui::Begin("TestWindow", &settings.windowIsEvents)) - { - } - ImGui::End(); - } -} \ No newline at end of file diff --git a/src/imgui_new/anm2_window.hpp b/src/imgui_new/anm2_window.hpp deleted file mode 100644 index 196a877..0000000 --- a/src/imgui_new/anm2_window.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "../anm2_new/anm2.hpp" -#include "../settings.hpp" - -namespace anm2ed::imgui_new -{ - class Window - { - void update(anm2ed::resource::anm2_new::Anm2&, anm2ed::resource::anm2_new::Anm2::Type type, Settings&); - }; -} \ No newline at end of file diff --git a/src/resource/strings.hpp b/src/resource/strings.hpp index 646166b..b2ce13e 100644 --- a/src/resource/strings.hpp +++ b/src/resource/strings.hpp @@ -372,6 +372,7 @@ namespace anm2ed X(LABEL_TRANSPARENT, "Transparent", "Transparentes", "Прозрачный", "透明", "투명도") \ X(LABEL_TYPE, "Type", "Tipo", "Тип", "类型", "유형") \ X(LABEL_UI_SCALE, "UI Scale", "Escala de la Interfaz", "Размер UI", "界面缩放", "UI 비율") \ + X(LABEL_ITEM_HEIGHT, "Item Height", "Altura del Item", "Высота элемента", "项目高度", "항목 높이") \ X(LABEL_USE_DEFAULT_SETTINGS, "Use Default Settings", "Usar Opciones Predeterminadas", "Изпользовать настройки по умолчанию", "使用默认设置", "기본값으로 사용") \ X(LABEL_VALUE_COLUMN, "Value", "Valor", "Значение", "数值", "값") \ X(LABEL_VSYNC, "Vsync", "Vsync", "Вертикальная синхронизация (V-sync)", "垂直同步", "수직 동기화") \ @@ -618,7 +619,7 @@ namespace anm2ed X(TOOLTIP_SET_TO_RECOMMENDED, "Use a recommended value for rows/columns.", "Usa un valor recomendado para las filas/columnas.", "Использовать рекомендованное значение для рядов/колонн.", "应用列/行的推荐值.", "행/열에 권장값을 사용합니다.") \ X(TOOLTIP_SPLIT, "Based on the playhead time, split the selected frame into two.", "Basado en tiempo del encabezado de reproduccion, divide el Frame seleccionado en dos.", "С учётом позиции ползунка воспроизведения разделяет выбранный кадр на два.", "根据播放头位置,将所选帧拆分成两个。", "재생 헤드 시간에 따라 선택한 프레임을 두 개로 분할합니다.") \ X(TOOLTIP_SIZE, "Change the crop size the frame uses.", "Cambia el tamaño de recorte que usa el Frame.", "Изменить размер обрезки, который использует этот кадр.", "更改此帧的裁剪大小.", "프레임에 대응되는 스프라이트 시트의 사용 영역의 크기를 변경합니다.") \ - X(TOOLTIP_SOUND, "Toggle sounds playing with triggers.\nBind sounds to events in the Events window.", "Alterna los sonidos reproduciendoce con triggers.\nEnlaza sonidos a eventos en la Ventana de Eventos.", "Переключить воспроизведения звуков с помощью триггеров.\nПривязывайте звуки к событиям в окне событий.", "切换是否在触发器触发时播放声音.\n可以在事件窗口里链接声音与事件.", "트리거와 함께 사운드를 재생할지 정합니다.\n사운드는 이벤트 창에서 이벤트에 연결하세요.") \ + X(TOOLTIP_SOUND, "Toggle sounds playing with triggers.\nSet a trigger's sounds in Frame Properties.", "Alterna los sonidos reproducidos con triggers.\nConfigura los sonidos de un trigger en Propiedades de Frame.", "Переключить воспроизведение звуков у триггеров.\nНастраивайте звуки триггера в свойствах кадра.", "切换是否在触发器触发时播放声音.\n可在帧属性中设置触发器的声音.", "트리거와 함께 사운드를 재생할지 정합니다.\n트리거 사운드는 프레임 속성에서 설정하세요.") \ X(TEXT_SOUND_PLAY, "Click to play.", "Click para reproducir.", "Нажмите, чтобы возпроизвести.", "点击播放.", "클릭하여 재생합니다.") \ X(TOOLTIP_SOUND_INVALID, "This sound could not be loaded. Replace the file.", "Este sonido no se pudo cargar. Reemplaza el archivo.", "Этот звук не удалось загрузить. Замените файл.", "无法加载此声音。请替换文件。", "이 사운드를 불러올 수 없습니다. 파일을 교체하세요.") \ X(TOOLTIP_SOUND_ADD, "Add a sound.", "Añadir un sonido.", "Добавить звук.", "添加一个声音.", "사운드를 추가합니다.") \ @@ -650,6 +651,7 @@ namespace anm2ed X(TOOLTIP_REMOVE_TRIGGER_SOUND, "Remove the last trigger sound.", "Remover el último sonido del trigger.", "Удалить последний звук триггера.", "移除最后一个事件触发器声音.", "마지막 트리거 사운드를 제거합니다.") \ X(TOOLTIP_TRIGGER_VISIBILITY, "Toggle the trigger's visibility.", "Alterna la visibilidad del trigger.", "Переключить видимость триггера.", "切换触发器是否可见.", "트리거를 표시하거나 숨깁니다.") \ X(TOOLTIP_UI_SCALE, "Change the scale of the UI.", "Cambia la escala de la interfaz de usuario.", "Изменить масштаб пользовательского интерфейса.", "更改界面(UI)的缩放.", "UI 비율을 변경합니다.") \ + X(TOOLTIP_ITEM_HEIGHT, "Set the height of items in Timeline.", "Establece la altura de los items en la Línea de tiempo.", "Установить высоту элементов на таймлайне.", "设置时间轴中项目的高度.", "타임라인 항목의 높이를 설정합니다.") \ X(TOOLTIP_UNUSED_ITEMS_HIDDEN, "Unused layers/nulls are hidden. Press to show them.", "Las capas/nulls no utilizados estan ocultos. Presiona para hacerlos visibles", "Неиспользуемые слои/нули скрыты. Нажмите, чтобы их показать.", "正在隐藏未使用的动画层/Null. 点击以显示它们.", "사용되지 않는 레이어/Null이 숨겨져 있습니다. 표시하려면 누르세요.") \ X(TOOLTIP_UNUSED_ITEMS_SHOWN, "Unused layers/nulls are shown. Press to hide them.", "Las capas/nulls no utilizados estan visibles. Presiona para ocultarlos", "Неиспользуемые слои/нули видимы. Нажмите, чтобы их скрыть.", "正在显示未使用的动画层/Null. 点击以隐藏它们.", "사용되지 않는 레이어/Null이 표시되어 있습니다. 숨기려면 누르세요.") \ X(TOOLTIP_USE_DEFAULT_SETTINGS, "Reset the settings to their defaults.", "Reinicia las configuraciones a sus predeterminados.", "Сбросить настройки на настройки по умолчанию.", "重设所有设置为默认.", "설정을 기본값으로 재설정합니다.") \ diff --git a/src/settings.hpp b/src/settings.hpp index fdbc1d3..bc6cf49 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -55,6 +55,7 @@ namespace anm2ed X(WINDOW_POSITION, windowPosition, STRING_UNDEFINED, IVEC2, glm::ivec2()) \ X(IS_VSYNC, isVsync, STRING_UNDEFINED, BOOL, true) \ X(UI_SCALE, uiScale, STRING_UNDEFINED, FLOAT, 1.0f) \ + X(TIMELINE_ITEM_HEIGHT, timelineItemHeight, STRING_UNDEFINED, FLOAT, 1.0f) \ X(THEME, theme, STRING_UNDEFINED, INT, types::theme::DARK) \ X(LANGUAGE, language, STRING_UNDEFINED, INT, ENGLISH) \ \