Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/base
diff options
context:
space:
mode:
authorWasabiRaptor <ketchupraptor@gmail.com>2025-05-18 20:05:41 -0400
committerWasabiRaptor <ketchupraptor@gmail.com>2025-05-18 20:05:41 -0400
commit0bf17a8dc4a4e7c7c1b763a2e12ac0de27caff1a (patch)
treed29eb44ec5e7887fe1a2ab521254ff0230b5ba2f /source/base
parentda281a0c5286014f952b5a8ce2f346f99c114089 (diff)
big feature expansion
Diffstat (limited to 'source/base')
-rw-r--r--source/base/StarAnimatedPartSet.cpp138
-rw-r--r--source/base/StarAnimatedPartSet.hpp28
2 files changed, 157 insertions, 9 deletions
diff --git a/source/base/StarAnimatedPartSet.cpp b/source/base/StarAnimatedPartSet.cpp
index 5ffc68f..9e5f816 100644
--- a/source/base/StarAnimatedPartSet.cpp
+++ b/source/base/StarAnimatedPartSet.cpp
@@ -1,11 +1,12 @@
#include "StarAnimatedPartSet.hpp"
#include "StarMathCommon.hpp"
+#include "StarJsonExtra.hpp"
namespace Star {
AnimatedPartSet::AnimatedPartSet() {}
-AnimatedPartSet::AnimatedPartSet(Json config) {
+AnimatedPartSet::AnimatedPartSet(Json config, uint8_t animatorVersion) {
for (auto const& stateTypePair : config.get("stateTypes", JsonObject()).iterateObject()) {
auto const& stateTypeName = stateTypePair.first;
auto const& stateTypeConfig = stateTypePair.second;
@@ -33,6 +34,7 @@ AnimatedPartSet::AnimatedPartSet(Json config) {
newStateType.states.sortByKey();
newStateType.activeState.stateTypeName = stateTypeName;
+ newStateType.activeState.reverse = false;
newStateType.activeStateDirty = true;
if (newStateType.defaultState.empty() && !newStateType.states.empty())
@@ -65,6 +67,7 @@ AnimatedPartSet::AnimatedPartSet(Json config) {
}
}
newPart.activePart.partName = partPair.first;
+ newPart.activePart.setAnimationAffineTransform(Mat3F::identity());
newPart.activePartDirty = true;
m_parts[partName] = std::move(newPart);
@@ -110,11 +113,12 @@ StringList AnimatedPartSet::partNames() const {
return m_parts.keys();
}
-bool AnimatedPartSet::setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart) {
+bool AnimatedPartSet::setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart, bool reverse) {
auto& stateType = m_stateTypes.get(stateTypeName);
- if (stateType.activeState.stateName != stateName || alwaysStart) {
+ if (stateType.activeState.stateName != stateName || alwaysStart || stateType.activeState.reverse != reverse) {
stateType.activeState.stateName = stateName;
stateType.activeState.timer = 0.0f;
+ stateType.activeState.frameProgress = 0.0f;
stateType.activeStatePointer = stateType.states.get(stateName).get();
stateType.activeStateDirty = true;
@@ -174,11 +178,15 @@ size_t AnimatedPartSet::activeStateIndex(String const& stateTypeName) const {
auto const& stateType = m_stateTypes.get(stateTypeName);
return *stateType.states.indexOf(stateType.activeState.stateName);
}
+bool AnimatedPartSet::activeStateReverse(String const& stateTypeName) const {
+ auto const& stateType = m_stateTypes.get(stateTypeName);
+ return stateType.activeState.reverse;
+}
-bool AnimatedPartSet::setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart) {
+bool AnimatedPartSet::setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart, bool reverse) {
auto const& stateType = m_stateTypes.get(stateTypeName);
String const& stateName = stateType.states.keyAt(stateIndex);
- return setActiveState(stateTypeName, stateName, alwaysStart);
+ return setActiveState(stateTypeName, stateName, alwaysStart, reverse);
}
void AnimatedPartSet::update(float dt) {
@@ -247,15 +255,40 @@ void AnimatedPartSet::freshenActiveState(StateType& stateType) {
if (stateType.activeStateDirty) {
auto const& state = *stateType.activeStatePointer;
auto& activeState = stateType.activeState;
- activeState.frame = clamp<int>(activeState.timer / state.cycle * state.frames, 0, state.frames - 1);
+
+ double progress = (activeState.timer / state.cycle * state.frames);
+ activeState.frameProgress = std::fmod(progress, 1);
+ activeState.frame = clamp<int>(progress, 0, state.frames - 1);
+ if (activeState.reverse) {
+ activeState.frame = (state.frames - 1) - activeState.frame;
+ if (state.animationMode == Loop)
+ if (activeState.frame <= 0 ) {
+ activeState.nextFrame = state.frames - 1;
+ } else {
+ activeState.nextFrame = clamp<int>(activeState.frame - 1, 0, state.frames - 1);
+ }
+ } else {
+ if (state.animationMode == Loop)
+ if (activeState.frame >= (state.frames-1) ) {
+ activeState.nextFrame = 0;
+ } else {
+ activeState.nextFrame = clamp<int>(activeState.frame + 1, 0, state.frames - 1);
+ }
+ }
activeState.properties = stateType.stateTypeProperties;
activeState.properties.merge(state.stateProperties, true);
+ activeState.nextProperties = activeState.properties;
+
for (auto const& pair : state.stateFrameProperties) {
if (activeState.frame < pair.second.size())
activeState.properties[pair.first] = pair.second.get(activeState.frame);
}
+ for (auto const& pair : state.stateFrameProperties) {
+ if (activeState.nextFrame < pair.second.size())
+ activeState.nextProperties[pair.first] = pair.second.get(activeState.nextFrame);
+ }
stateType.activeStateDirty = false;
}
@@ -292,16 +325,73 @@ void AnimatedPartSet::freshenActivePart(Part& part) {
freshenActiveState(stateType);
activePart.activeState = stateType.activeState;
unsigned frame = stateType.activeState.frame;
+ unsigned nextFrame = stateType.activeState.nextFrame;
// Then set the part state data, as well as any part state frame data if
// the current frame is within the list size.
activePart.properties.merge(partState->partStateProperties, true);
+ activePart.nextProperties = activePart.properties;
for (auto const& pair : partState->partStateFrameProperties) {
if (frame < pair.second.size())
activePart.properties[pair.first] = pair.second.get(frame);
}
+ for (auto const& pair : partState->partStateFrameProperties) {
+ if (nextFrame< pair.second.size())
+ activePart.nextProperties[pair.first] = pair.second.get(nextFrame);
+ }
+ if (version() > 0) {
+ auto processTransforms = [](Mat3F mat, JsonArray transforms, JsonObject properties) -> Mat3F {
+ for (auto const& v : transforms) {
+ auto actionData = v.toArray();
+ auto action = actionData[0].toString();
+ if (action == "reset") {
+ mat = Mat3F::identity();
+ } else if (action == "translate") {
+ mat.translate(jsonToVec2F(actionData[1]));
+ } else if (action == "rotate") {
+ if (auto center = actionData[2]) {
+ mat.rotate(actionData[1].toFloat(), jsonToVec2F(center));
+ } else if (auto center = properties.ptr("rotationCenter")) {
+ mat.rotate(actionData[1].toFloat(), jsonToVec2F(center));
+ } else if (auto center = properties.ptr("center")) {
+ mat.rotate(actionData[1].toFloat(), jsonToVec2F(center));
+ } else {
+ mat.rotate(actionData[1].toFloat());
+ }
+ } else if (action == "scale") {
+ if (auto center = actionData[2]) {
+ mat.scale(actionData[1].toFloat(), jsonToVec2F(center));
+ } else if (auto center = properties.ptr("scaleCenter")) {
+ mat.scale(actionData[1].toFloat(), jsonToVec2F(center));
+ } else if (auto center = properties.ptr("center")) {
+ mat.scale(actionData[1].toFloat(), jsonToVec2F(center));
+ } else {
+ mat.scale(actionData[1].toFloat());
+ }
+ } else if (action == "transform") {
+ mat = Mat3F(actionData[1].toFloat(), actionData[2].toFloat(), actionData[3].toFloat(), actionData[4].toFloat(), actionData[5].toFloat(), actionData[6].toFloat(), 0, 0, 1) * mat;
+ }
+ }
+ return mat;
+ };
+
+
+ if (auto transforms = activePart.properties.ptr("transforms")) {
+ auto mat = processTransforms(activePart.animationAffineTransform(), transforms->toArray(), activePart.properties);
+ if (activePart.properties.value("interpolated").optBool().value(false)) {
+ if (auto nextTransforms = activePart.nextProperties.ptr("transforms")) {
+ auto nextMat = processTransforms(activePart.animationAffineTransform(), nextTransforms->toArray(), activePart.nextProperties);
+ activePart.setAnimationAffineTransform(mat, nextMat, stateType.activeState.frameProgress);
+ } else {
+ activePart.setAnimationAffineTransform(mat);
+ }
+ } else {
+ activePart.setAnimationAffineTransform(mat);
+ }
+ }
+ }
// Each part can only have one state type x state match, so we are done.
break;
}
@@ -310,4 +400,40 @@ void AnimatedPartSet::freshenActivePart(Part& part) {
}
}
+void AnimatedPartSet::ActivePartInformation::setAnimationAffineTransform(Mat3F const& matrix) {
+ xTranslationAnimation = matrix[0][2];
+ yTranslationAnimation = matrix[1][2];
+ xScaleAnimation = sqrt(square(matrix[0][0]) + square(matrix[0][1]));
+ yScaleAnimation = sqrt(square(matrix[1][0]) + square(matrix[1][1]));
+ xShearAnimation = atan2(matrix[0][1], matrix[0][0]);
+ yShearAnimation = atan2(matrix[1][0], matrix[1][1]);
+}
+void AnimatedPartSet::ActivePartInformation::setAnimationAffineTransform(Mat3F const& mat1, Mat3F const& mat2, float progress) {
+ xTranslationAnimation = mat1[0][2];
+ yTranslationAnimation = mat1[1][2];
+ xScaleAnimation = sqrt(square(mat1[0][0]) + square(mat1[0][1]));
+ yScaleAnimation = sqrt(square(mat1[1][0]) + square(mat1[1][1]));
+ xShearAnimation = atan2(mat1[0][1], mat1[0][0]);
+ yShearAnimation = atan2(mat1[1][0], mat1[1][1]);
+
+ xTranslationAnimation += (mat2[0][2] - xTranslationAnimation) * progress;
+ yTranslationAnimation += (mat2[1][2] - yTranslationAnimation) * progress;
+ xScaleAnimation += (sqrt(square(mat2[0][0]) + square(mat2[0][1])) - xScaleAnimation) * progress;
+ yScaleAnimation += (sqrt(square(mat2[1][0]) + square(mat2[1][1])) - yScaleAnimation) * progress;
+ xShearAnimation += (atan2(mat2[0][1], mat2[0][0]) - xShearAnimation) * progress;
+ yShearAnimation += (atan2(mat2[1][0], mat2[1][1]) - yShearAnimation) * progress;
+}
+
+Mat3F AnimatedPartSet::ActivePartInformation::animationAffineTransform() const {
+ return Mat3F(
+ xScaleAnimation * cos(xShearAnimation), xScaleAnimation * sin(xShearAnimation), xTranslationAnimation,
+ yScaleAnimation * sin(yShearAnimation), yScaleAnimation * cos(yShearAnimation), yTranslationAnimation,
+ 0, 0, 1
+ );
+}
+
+uint8_t AnimatedPartSet::version() const {
+ return m_animatorVersion;
+}
+
}
diff --git a/source/base/StarAnimatedPartSet.hpp b/source/base/StarAnimatedPartSet.hpp
index a608924..43755be 100644
--- a/source/base/StarAnimatedPartSet.hpp
+++ b/source/base/StarAnimatedPartSet.hpp
@@ -2,6 +2,7 @@
#include "StarOrderedMap.hpp"
#include "StarJson.hpp"
+#include "StarMatrix3.hpp"
namespace Star {
@@ -46,7 +47,11 @@ public:
String stateName;
float timer;
unsigned frame;
+ float frameProgress;
JsonObject properties;
+ bool reverse;
+ unsigned nextFrame;
+ JsonObject nextProperties;
};
struct ActivePartInformation {
@@ -54,6 +59,18 @@ public:
// If a state match is found, this will be set.
Maybe<ActiveStateInformation> activeState;
JsonObject properties;
+ JsonObject nextProperties;
+
+ Mat3F animationAffineTransform() const;
+ void setAnimationAffineTransform(Mat3F const& matrix);
+ void setAnimationAffineTransform(Mat3F const& mat1, Mat3F const& mat2, float progress);
+
+ float xTranslationAnimation;
+ float yTranslationAnimation;
+ float xScaleAnimation;
+ float yScaleAnimation;
+ float xShearAnimation;
+ float yShearAnimation;
};
enum AnimationMode {
@@ -97,7 +114,7 @@ public:
};
AnimatedPartSet();
- AnimatedPartSet(Json config);
+ AnimatedPartSet(Json config, uint8_t animatiorVersion);
// Returns the available state types.
StringList stateTypes() const;
@@ -118,7 +135,7 @@ public:
// beginning. If alwaysStart is true, then starts the state animation off at
// the beginning even if no state change has occurred. Returns true if a
// state animation reset was done.
- bool setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart = false);
+ bool setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart = false, bool reverse = false);
// Restart this given state type's timer off at the beginning.
void restartState(String const& stateTypeName);
@@ -141,7 +158,8 @@ public:
// state type is ordered, it is possible to simply serialize and deserialize
// the state index for that state type.
size_t activeStateIndex(String const& stateTypeName) const;
- bool setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart = false);
+ bool activeStateReverse(String const& stateTypeName) const;
+ bool setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart = false, bool reverse = false);
// Animate each state type forward 'dt' time, and either change state frames
// or transition to new states, depending on the config.
@@ -150,6 +168,8 @@ public:
// Pushes all the animations into their final state
void finishAnimations();
+ uint8_t version() const;
+
private:
static AnimationMode stringToAnimationMode(String const& string);
@@ -158,6 +178,8 @@ private:
OrderedHashMap<String, StateType> m_stateTypes;
StringMap<Part> m_parts;
+
+ uint8_t m_animatorVersion;
};
}