Refactor + render animation tweaks + updated frame properties + bug fixes
This commit is contained in:
+5
-4
@@ -4,10 +4,11 @@
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "file_.h"
|
||||
#include "map_.h"
|
||||
#include "time_.h"
|
||||
#include "vector_.h"
|
||||
#include "working_directory.h"
|
||||
#include "xml_.h"
|
||||
|
||||
using namespace tinyxml2;
|
||||
@@ -23,7 +24,7 @@ namespace anm2ed::anm2
|
||||
{
|
||||
XMLDocument document;
|
||||
|
||||
filesystem::File file(path, "rb");
|
||||
File file(path, "rb");
|
||||
if (!file)
|
||||
{
|
||||
if (errorString) *errorString = localize.get(ERROR_FILE_NOT_FOUND);
|
||||
@@ -38,7 +39,7 @@ namespace anm2ed::anm2
|
||||
return;
|
||||
}
|
||||
|
||||
filesystem::WorkingDirectory workingDirectory(path, true);
|
||||
WorkingDirectory workingDirectory(path, WorkingDirectory::FILE);
|
||||
|
||||
const XMLElement* element = document.RootElement();
|
||||
|
||||
@@ -65,7 +66,7 @@ namespace anm2ed::anm2
|
||||
XMLDocument document;
|
||||
document.InsertFirstChild(to_element(document));
|
||||
|
||||
filesystem::File file(path, "wb");
|
||||
File file(path, "wb");
|
||||
if (!file)
|
||||
{
|
||||
if (errorString) *errorString = localize.get(ERROR_FILE_NOT_FOUND);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "anm2.h"
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "map_.h"
|
||||
#include "path_.h"
|
||||
#include "working_directory.h"
|
||||
|
||||
using namespace anm2ed::types;
|
||||
using namespace anm2ed::util;
|
||||
@@ -22,7 +23,7 @@ namespace anm2ed::anm2
|
||||
labels.emplace_back(localize.get(BASIC_NONE));
|
||||
for (auto& [id, sound] : content.sounds)
|
||||
{
|
||||
auto pathString = filesystem::path_to_utf8(sound.path);
|
||||
auto pathString = path::to_utf8(sound.path);
|
||||
labels.emplace_back(std::vformat(localize.get(FORMAT_SOUND), std::make_format_args(id, pathString)));
|
||||
}
|
||||
return labels;
|
||||
@@ -57,7 +58,7 @@ namespace anm2ed::anm2
|
||||
return false;
|
||||
}
|
||||
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
WorkingDirectory workingDirectory(directory);
|
||||
|
||||
for (auto element = document.FirstChildElement("Sound"); element; element = element->NextSiblingElement("Sound"))
|
||||
{
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "map_.h"
|
||||
#include "path_.h"
|
||||
#include "working_directory.h"
|
||||
|
||||
using namespace anm2ed::types;
|
||||
using namespace anm2ed::util;
|
||||
@@ -41,7 +42,7 @@ namespace anm2ed::anm2
|
||||
labels.emplace_back(localize.get(BASIC_NONE));
|
||||
for (auto& [id, spritesheet] : content.spritesheets)
|
||||
{
|
||||
auto pathString = filesystem::path_to_utf8(spritesheet.path);
|
||||
auto pathString = path::to_utf8(spritesheet.path);
|
||||
labels.emplace_back(std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
}
|
||||
return labels;
|
||||
@@ -62,7 +63,7 @@ namespace anm2ed::anm2
|
||||
return false;
|
||||
}
|
||||
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
WorkingDirectory workingDirectory(directory);
|
||||
|
||||
for (auto element = document.FirstChildElement("Spritesheet"); element;
|
||||
element = element->NextSiblingElement("Spritesheet"))
|
||||
|
||||
@@ -117,7 +117,5 @@ namespace anm2ed::anm2
|
||||
}
|
||||
|
||||
void Frame::shorten() { duration = glm::clamp(--duration, FRAME_DURATION_MIN, FRAME_DURATION_MAX); }
|
||||
|
||||
void Frame::extend() { duration = glm::clamp(++duration, FRAME_DURATION_MIN, FRAME_DURATION_MAX); }
|
||||
|
||||
}
|
||||
+25
-4
@@ -35,6 +35,8 @@ namespace anm2ed::anm2
|
||||
MEMBERS
|
||||
#undef X
|
||||
|
||||
#undef MEMBERS
|
||||
|
||||
Frame() = default;
|
||||
Frame(tinyxml2::XMLElement*, Type);
|
||||
tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&, Type);
|
||||
@@ -46,10 +48,29 @@ namespace anm2ed::anm2
|
||||
|
||||
struct FrameChange
|
||||
{
|
||||
#define X(name, type, ...) std::optional<type> name{};
|
||||
MEMBERS
|
||||
#undef X
|
||||
std::optional<bool> isVisible{};
|
||||
std::optional<bool> isInterpolated{};
|
||||
std::optional<float> rotation{};
|
||||
std::optional<int> duration{};
|
||||
std::optional<int> atFrame{};
|
||||
std::optional<int> eventID{};
|
||||
std::optional<float> pivotX{};
|
||||
std::optional<float> pivotY{};
|
||||
std::optional<float> cropX{};
|
||||
std::optional<float> cropY{};
|
||||
std::optional<float> positionX{};
|
||||
std::optional<float> positionY{};
|
||||
std::optional<float> sizeX{};
|
||||
std::optional<float> sizeY{};
|
||||
std::optional<float> scaleX{};
|
||||
std::optional<float> scaleY{};
|
||||
std::optional<float> colorOffsetR{};
|
||||
std::optional<float> colorOffsetG{};
|
||||
std::optional<float> colorOffsetB{};
|
||||
std::optional<float> tintR{};
|
||||
std::optional<float> tintG{};
|
||||
std::optional<float> tintB{};
|
||||
std::optional<float> tintA{};
|
||||
};
|
||||
|
||||
#undef MEMBERS
|
||||
}
|
||||
|
||||
+55
-59
@@ -132,6 +132,39 @@ namespace anm2ed::anm2
|
||||
auto end = numberFrames > -1 ? start + numberFrames : (int)frames.size();
|
||||
end = glm::clamp(end, start, (int)frames.size());
|
||||
|
||||
const auto clamp_identity = [](auto value) { return value; };
|
||||
const auto clamp01 = [](auto value) { return glm::clamp(value, 0.0f, 1.0f); };
|
||||
const auto clamp_duration = [](int value) { return std::max(FRAME_DURATION_MIN, value); };
|
||||
|
||||
auto apply_scalar_with_clamp = [&](auto& target, const auto& optionalValue, auto clampFunc)
|
||||
{
|
||||
if (!optionalValue) return;
|
||||
auto value = *optionalValue;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ADJUST:
|
||||
target = clampFunc(value);
|
||||
break;
|
||||
case ADD:
|
||||
target = clampFunc(target + value);
|
||||
break;
|
||||
case SUBTRACT:
|
||||
target = clampFunc(target - value);
|
||||
break;
|
||||
case MULTIPLY:
|
||||
target = clampFunc(target * value);
|
||||
break;
|
||||
case DIVIDE:
|
||||
if (value == decltype(value){}) return;
|
||||
target = clampFunc(target / value);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto apply_scalar = [&](auto& target, const auto& optionalValue)
|
||||
{ apply_scalar_with_clamp(target, optionalValue, clamp_identity); };
|
||||
|
||||
for (int i = useStart; i < end; i++)
|
||||
{
|
||||
Frame& frame = frames[i];
|
||||
@@ -139,69 +172,32 @@ namespace anm2ed::anm2
|
||||
if (change.isVisible) frame.isVisible = *change.isVisible;
|
||||
if (change.isInterpolated) frame.isInterpolated = *change.isInterpolated;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ADJUST:
|
||||
if (change.rotation) frame.rotation = *change.rotation;
|
||||
if (change.duration) frame.duration = std::max(FRAME_DURATION_MIN, *change.duration);
|
||||
if (change.crop) frame.crop = *change.crop;
|
||||
if (change.pivot) frame.pivot = *change.pivot;
|
||||
if (change.position) frame.position = *change.position;
|
||||
if (change.size) frame.size = *change.size;
|
||||
if (change.scale) frame.scale = *change.scale;
|
||||
if (change.colorOffset) frame.colorOffset = glm::clamp(*change.colorOffset, 0.0f, 1.0f);
|
||||
if (change.tint) frame.tint = glm::clamp(*change.tint, 0.0f, 1.0f);
|
||||
break;
|
||||
apply_scalar(frame.rotation, change.rotation);
|
||||
apply_scalar_with_clamp(frame.duration, change.duration, clamp_duration);
|
||||
|
||||
case ADD:
|
||||
if (change.rotation) frame.rotation += *change.rotation;
|
||||
if (change.duration) frame.duration = std::max(FRAME_DURATION_MIN, frame.duration + *change.duration);
|
||||
if (change.crop) frame.crop += *change.crop;
|
||||
if (change.pivot) frame.pivot += *change.pivot;
|
||||
if (change.position) frame.position += *change.position;
|
||||
if (change.size) frame.size += *change.size;
|
||||
if (change.scale) frame.scale += *change.scale;
|
||||
if (change.colorOffset) frame.colorOffset = glm::clamp(frame.colorOffset + *change.colorOffset, 0.0f, 1.0f);
|
||||
if (change.tint) frame.tint = glm::clamp(frame.tint + *change.tint, 0.0f, 1.0f);
|
||||
break;
|
||||
apply_scalar(frame.crop.x, change.cropX);
|
||||
apply_scalar(frame.crop.y, change.cropY);
|
||||
|
||||
case SUBTRACT:
|
||||
if (change.rotation) frame.rotation -= *change.rotation;
|
||||
if (change.duration) frame.duration = std::max(FRAME_DURATION_MIN, frame.duration - *change.duration);
|
||||
if (change.crop) frame.crop -= *change.crop;
|
||||
if (change.pivot) frame.pivot -= *change.pivot;
|
||||
if (change.position) frame.position -= *change.position;
|
||||
if (change.size) frame.size -= *change.size;
|
||||
if (change.scale) frame.scale -= *change.scale;
|
||||
if (change.colorOffset) frame.colorOffset = glm::clamp(frame.colorOffset - *change.colorOffset, 0.0f, 1.0f);
|
||||
if (change.tint) frame.tint = glm::clamp(frame.tint - *change.tint, 0.0f, 1.0f);
|
||||
break;
|
||||
apply_scalar(frame.pivot.x, change.pivotX);
|
||||
apply_scalar(frame.pivot.y, change.pivotY);
|
||||
|
||||
case MULTIPLY:
|
||||
if (change.rotation) frame.rotation *= *change.rotation;
|
||||
if (change.duration) frame.duration = std::max(FRAME_DURATION_MIN, frame.duration * *change.duration);
|
||||
if (change.crop) frame.crop *= *change.crop;
|
||||
if (change.pivot) frame.pivot *= *change.pivot;
|
||||
if (change.position) frame.position *= *change.position;
|
||||
if (change.size) frame.size *= *change.size;
|
||||
if (change.scale) frame.scale *= *change.scale;
|
||||
if (change.colorOffset) frame.colorOffset = glm::clamp(frame.colorOffset * *change.colorOffset, 0.0f, 1.0f);
|
||||
if (change.tint) frame.tint = glm::clamp(frame.tint * *change.tint, 0.0f, 1.0f);
|
||||
break;
|
||||
apply_scalar(frame.position.x, change.positionX);
|
||||
apply_scalar(frame.position.y, change.positionY);
|
||||
|
||||
case DIVIDE:
|
||||
if (change.rotation && *change.rotation != 0.0f) frame.rotation /= *change.rotation;
|
||||
if (change.duration && *change.duration != 0)
|
||||
frame.duration = std::max(FRAME_DURATION_MIN, frame.duration / *change.duration);
|
||||
if (change.crop) frame.crop /= *change.crop;
|
||||
if (change.pivot) frame.pivot /= *change.pivot;
|
||||
if (change.position) frame.position /= *change.position;
|
||||
if (change.size) frame.size /= *change.size;
|
||||
if (change.scale) frame.scale /= *change.scale;
|
||||
if (change.colorOffset) frame.colorOffset = glm::clamp(frame.colorOffset / *change.colorOffset, 0.0f, 1.0f);
|
||||
if (change.tint) frame.tint = glm::clamp(frame.tint / *change.tint, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
apply_scalar(frame.size.x, change.sizeX);
|
||||
apply_scalar(frame.size.y, change.sizeY);
|
||||
|
||||
apply_scalar(frame.scale.x, change.scaleX);
|
||||
apply_scalar(frame.scale.y, change.scaleY);
|
||||
|
||||
apply_scalar_with_clamp(frame.colorOffset.x, change.colorOffsetR, clamp01);
|
||||
apply_scalar_with_clamp(frame.colorOffset.y, change.colorOffsetG, clamp01);
|
||||
apply_scalar_with_clamp(frame.colorOffset.z, change.colorOffsetB, clamp01);
|
||||
|
||||
apply_scalar_with_clamp(frame.tint.x, change.tintR, clamp01);
|
||||
apply_scalar_with_clamp(frame.tint.y, change.tintG, clamp01);
|
||||
apply_scalar_with_clamp(frame.tint.z, change.tintB, clamp01);
|
||||
apply_scalar_with_clamp(frame.tint.w, change.tintA, clamp01);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-20
@@ -1,6 +1,7 @@
|
||||
#include "sound.h"
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "path_.h"
|
||||
#include "working_directory.h"
|
||||
#include "xml_.h"
|
||||
|
||||
using namespace anm2ed::resource;
|
||||
@@ -21,23 +22,11 @@ namespace anm2ed::anm2
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path make_relative_or_keep(const std::filesystem::path& input)
|
||||
{
|
||||
if (input.empty()) return input;
|
||||
std::error_code ec{};
|
||||
auto relative = std::filesystem::relative(input, ec);
|
||||
if (!ec) return relative;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
Sound::Sound(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
this->path = filesystem::path_lower_case_backslash_handle(this->path);
|
||||
WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? path::make_relative(path) : this->path;
|
||||
this->path = path::lower_case_backslash_handle(this->path);
|
||||
audio = Audio(this->path);
|
||||
}
|
||||
|
||||
@@ -46,7 +35,7 @@ namespace anm2ed::anm2
|
||||
if (!element) return;
|
||||
element->QueryIntAttribute("Id", &id);
|
||||
xml::query_path_attribute(element, "Path", &path);
|
||||
path = filesystem::path_lower_case_backslash_handle(path);
|
||||
path = path::lower_case_backslash_handle(path);
|
||||
audio = Audio(path);
|
||||
}
|
||||
|
||||
@@ -54,7 +43,7 @@ namespace anm2ed::anm2
|
||||
{
|
||||
auto element = document.NewElement("Sound");
|
||||
element->SetAttribute("Id", id);
|
||||
auto pathString = filesystem::path_to_utf8(path);
|
||||
auto pathString = path::to_utf8(path);
|
||||
element->SetAttribute("Path", pathString.c_str());
|
||||
return element;
|
||||
}
|
||||
@@ -72,8 +61,6 @@ namespace anm2ed::anm2
|
||||
}
|
||||
|
||||
void Sound::reload(const std::filesystem::path& directory) { *this = Sound(directory, this->path); }
|
||||
|
||||
bool Sound::is_valid() { return audio.is_valid(); }
|
||||
|
||||
void Sound::play() { audio.play(); }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "spritesheet.h"
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "path_.h"
|
||||
#include "working_directory.h"
|
||||
#include "xml_.h"
|
||||
|
||||
using namespace anm2ed::resource;
|
||||
@@ -17,27 +18,15 @@ namespace anm2ed::anm2
|
||||
// Spritesheet paths from Isaac Rebirth are made with the assumption that paths are case-insensitive
|
||||
// However when using the resource dumper, the spritesheet paths are all lowercase (on Linux anyway)
|
||||
// This will handle this case and make the paths OS-agnostic
|
||||
path = filesystem::path_lower_case_backslash_handle(path);
|
||||
path = path::lower_case_backslash_handle(path);
|
||||
texture = Texture(path);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path make_relative_or_keep(const std::filesystem::path& input)
|
||||
{
|
||||
if (input.empty()) return input;
|
||||
std::error_code ec{};
|
||||
auto relative = std::filesystem::relative(input, ec);
|
||||
if (!ec) return relative;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
Spritesheet::Spritesheet(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
this->path = filesystem::path_lower_case_backslash_handle(this->path);
|
||||
WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? path::make_relative(path) : this->path;
|
||||
this->path = path::lower_case_backslash_handle(this->path);
|
||||
texture = Texture(this->path);
|
||||
}
|
||||
|
||||
@@ -45,7 +34,7 @@ namespace anm2ed::anm2
|
||||
{
|
||||
auto element = document.NewElement("Spritesheet");
|
||||
element->SetAttribute("Id", id);
|
||||
auto pathString = filesystem::path_to_utf8(path);
|
||||
auto pathString = path::to_utf8(path);
|
||||
element->SetAttribute("Path", pathString.c_str());
|
||||
return element;
|
||||
}
|
||||
@@ -64,8 +53,8 @@ namespace anm2ed::anm2
|
||||
|
||||
bool Spritesheet::save(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? path::make_relative(path) : this->path;
|
||||
return texture.write_png(this->path);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user