diff --git a/api/src/main/java/oxy/bascenario/api/animation/Animation.java b/api/src/main/java/oxy/bascenario/api/animation/Animation.java deleted file mode 100644 index 115c4662..00000000 --- a/api/src/main/java/oxy/bascenario/api/animation/Animation.java +++ /dev/null @@ -1,171 +0,0 @@ -package oxy.bascenario.api.animation; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.HashMap; -import java.util.Map; - -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -@Getter -public class Animation { - private final String name; - private AnimationTimeline globalTimeline, defaultTimeline; - - private float maxDuration; - private final Map timelines = new HashMap<>(); - - private String initExpression; - private boolean resetWhenFinish; - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private String name; - - private final AnimationTimeline.Builder global = new AnimationTimeline.Builder(); - private final AnimationTimeline.Builder defaultTimeline = new AnimationTimeline.Builder(); - - private float maxDuration; - private final Map timelines = new HashMap<>(); - - private String initExpression; - private boolean resetWhenFinish; - - private Builder() { - } - - public String name() { - return name; - } - - public Builder name(String name) { - this.name = name; - return this; - } - - public AnimationValue defaultRotation() { - return defaultTimeline.build().getRotation(); - } - - public Builder defaultRotation(AnimationValue rotation) { - defaultTimeline.rotation(rotation); - return this; - } - - public AnimationValue defaultOffset() { - return defaultTimeline.build().getOffset(); - } - - public Builder defaultOffset(AnimationValue offset) { - defaultTimeline.offset(offset); - return this; - } - - public AnimationValue defaultPivot() { - return defaultTimeline.build().getPivot(); - } - - public Builder defaultPivot(AnimationValue pivot) { - defaultTimeline.pivot(pivot); - return this; - } - - public AnimationValue defaultScale() { - return defaultTimeline.build().getOffset(); - } - - public Builder defaultScale(AnimationValue scale) { - defaultTimeline.scale(scale); - return this; - } - - public boolean resetWhenFinish() { - return resetWhenFinish; - } - - public Builder resetWhenFinish(boolean resetWhenFinish) { - this.resetWhenFinish = resetWhenFinish; - return this; - } - - public AnimationValue rotation() { - return global.build().getRotation(); - } - - public Builder rotation(AnimationValue rotation) { - global.rotation(rotation); - return this; - } - - public AnimationValue offset() { - return global.build().getOffset(); - } - - public Builder offset(AnimationValue offset) { - global.offset(offset); - return this; - } - - public AnimationValue pivot() { - return global.build().getPivot(); - } - - public Builder pivot(AnimationValue pivot) { - global.pivot(pivot); - return this; - } - - public AnimationValue scale() { - return global.build().getOffset(); - } - - public Builder scale(AnimationValue scale) { - global.scale(scale); - return this; - } - - public float maxDuration() { - return maxDuration; - } - - public Builder maxDuration(float maxDuration) { - this.maxDuration = maxDuration; - return this; - } - - public String initExpression() { - return initExpression; - } - - public Builder initExpression(String initExpression) { - this.initExpression = initExpression; - return this; - } - - public Map timelines() { - return timelines; - } - - public Builder put(float time, AnimationTimeline timeline) { - this.timelines.put(time, timeline); - return this; - } - - public Animation build() { - final Animation animation = new Animation(this.name); - animation.globalTimeline = global.build(); - animation.defaultTimeline = defaultTimeline.build(); - - animation.resetWhenFinish = resetWhenFinish; - - animation.maxDuration = maxDuration; - animation.timelines.putAll(timelines); - animation.initExpression = initExpression; - return animation; - } - } -} diff --git a/api/src/main/java/oxy/bascenario/api/animation/AnimationTimeline.java b/api/src/main/java/oxy/bascenario/api/animation/AnimationTimeline.java deleted file mode 100644 index ecdeeaec..00000000 --- a/api/src/main/java/oxy/bascenario/api/animation/AnimationTimeline.java +++ /dev/null @@ -1,18 +0,0 @@ -package oxy.bascenario.api.animation; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -@ToString -@Builder(toBuilder = true, builderClassName = "Builder") -@Getter -public class AnimationTimeline { - private AnimationValue rotation, offset, scale, pivot; - private String condition; - private Type type; - - public enum Type { - SET, OFFSET - } -} diff --git a/api/src/main/java/oxy/bascenario/api/animation/AnimationValue.java b/api/src/main/java/oxy/bascenario/api/animation/AnimationValue.java deleted file mode 100644 index 98341e40..00000000 --- a/api/src/main/java/oxy/bascenario/api/animation/AnimationValue.java +++ /dev/null @@ -1,6 +0,0 @@ -package oxy.bascenario.api.animation; - -import oxy.bascenario.api.effects.Easing; - -public record AnimationValue(String[] value, String duration, Easing easing) { -} diff --git a/api/src/main/java/oxy/bascenario/api/animation/api/Animation.java b/api/src/main/java/oxy/bascenario/api/animation/api/Animation.java new file mode 100644 index 00000000..8d517217 --- /dev/null +++ b/api/src/main/java/oxy/bascenario/api/animation/api/Animation.java @@ -0,0 +1,18 @@ +package oxy.bascenario.api.animation.api; + +import java.util.List; + +public interface Animation { + void render(); + long duration(); + String type(); + + default List> options() { + return List.of(); + } + default T get(int index) { + return null; + } + default void set(int index, Object value) { + } +} diff --git a/api/src/main/java/oxy/bascenario/api/animation/api/AnimationMode.java b/api/src/main/java/oxy/bascenario/api/animation/api/AnimationMode.java new file mode 100644 index 00000000..bfc5aff4 --- /dev/null +++ b/api/src/main/java/oxy/bascenario/api/animation/api/AnimationMode.java @@ -0,0 +1,19 @@ +package oxy.bascenario.api.animation.api; + +public enum AnimationMode { + + /** + * The animation will play once and then stop.
+ * The last animation value will be returned. + */ + DEFAULT, + /** + * Loop the animation from the beginning after it has finished. + */ + LOOP, + /** + * Loop the animation in a ping-pong way (forward-backward-forward-backward...). + */ + PING_PONG, + +} \ No newline at end of file diff --git a/api/src/main/java/oxy/bascenario/api/animation/api/AnimationOption.java b/api/src/main/java/oxy/bascenario/api/animation/api/AnimationOption.java new file mode 100644 index 00000000..036f0e61 --- /dev/null +++ b/api/src/main/java/oxy/bascenario/api/animation/api/AnimationOption.java @@ -0,0 +1,4 @@ +package oxy.bascenario.api.animation.api; + +public record AnimationOption(String name, T value) { +} diff --git a/api/src/main/java/oxy/bascenario/api/animation/frame/FrameAnimation.java b/api/src/main/java/oxy/bascenario/api/animation/frame/FrameAnimation.java new file mode 100644 index 00000000..9c65124c --- /dev/null +++ b/api/src/main/java/oxy/bascenario/api/animation/frame/FrameAnimation.java @@ -0,0 +1,89 @@ +package oxy.bascenario.api.animation.frame; + +import oxy.bascenario.api.animation.api.Animation; +import oxy.bascenario.api.animation.api.AnimationMode; +import oxy.bascenario.api.animation.api.AnimationOption; +import oxy.bascenario.api.effects.Easing; + +import java.util.Comparator; +import java.util.List; + +public class FrameAnimation implements Animation { + private final List> frames; + private final long duration; + private final T start; + + private boolean reversed; + + @SuppressWarnings("ALL") + @SafeVarargs + public FrameAnimation(T start, Frame... frames) { + this.start = start; + + this.frames = List.of(frames); + this.frames.sort(Comparator.comparingLong(f -> f.timestamp())); + if (this.frames.isEmpty()) { + this.duration = 0; + return; + } + + this.duration = this.frames.get(this.frames.size() - 1).timestamp(); + } + + private Frame currentFrame(long timestamp) { + Frame frame = null; + for (Frame other : this.reversed ? this.frames.reversed() : this.frames) { + if (this.reversed ? other.timestamp > timestamp : other.timestamp < timestamp) { + continue; + } + + frame = other; + break; + } + + return frame; + } + + @Override + public void render() { + } + + @Override + public long duration() { + return this.duration; + } + + @Override + public String type() { + return "frame"; + } + + public record Frame(long timestamp, long duration, Easing easing, T value) { + } + + private boolean resetOnPlay = true; + private AnimationMode mode = AnimationMode.DEFAULT; + @Override + public List> options() { + // Reset On Play + return List.of(new AnimationOption<>("Reset on Play", true), new AnimationOption<>("Animation Mode", AnimationMode.DEFAULT)); + } + + @Override + public void set(int index, Object value) { + switch (index) { + case 0 -> this.resetOnPlay = (boolean) value; + case 1 -> this.mode = (AnimationMode) value; + } + } + + @SuppressWarnings("ALL") + @Override + public Object get(int index) { + return switch (index) { + case 0 -> this.resetOnPlay; + case 1 -> this.mode; + default -> null; + }; + } +} diff --git a/api/src/main/java/oxy/bascenario/api/managers/AnimationManagerApi.java b/api/src/main/java/oxy/bascenario/api/managers/AnimationManagerApi.java index 44c696bf..021a8f79 100644 --- a/api/src/main/java/oxy/bascenario/api/managers/AnimationManagerApi.java +++ b/api/src/main/java/oxy/bascenario/api/managers/AnimationManagerApi.java @@ -1,6 +1,6 @@ package oxy.bascenario.api.managers; -import oxy.bascenario.api.animation.Animation; +import oxy.bascenario.api.animation.api.Animation; public interface AnimationManagerApi { Animation put(String name, Animation animation); diff --git a/core/src/main/java/oxy/bascenario/managers/AnimationManager.java b/core/src/main/java/oxy/bascenario/managers/AnimationManager.java index b7b6d4f2..163e7f85 100644 --- a/core/src/main/java/oxy/bascenario/managers/AnimationManager.java +++ b/core/src/main/java/oxy/bascenario/managers/AnimationManager.java @@ -1,10 +1,7 @@ package oxy.bascenario.managers; import oxy.bascenario.Base; -import oxy.bascenario.api.animation.Animation; -import oxy.bascenario.api.animation.AnimationTimeline; -import oxy.bascenario.api.animation.AnimationValue; -import oxy.bascenario.api.effects.Easing; +import oxy.bascenario.api.animation.api.Animation; import oxy.bascenario.api.managers.AnimationManagerApi; import oxy.bascenario.utils.math.Pair; @@ -52,59 +49,59 @@ public void shutdown() { } private void initDefaultAnimations() { - this.put( - "bascenarioengine:default-shake", - Animation.builder() - .name("Default Shake") - .offset(new AnimationValue(new String[] {"math.abs(query.offset(0) + 19.2) <= 0.0001 ? 19.2 : -19.2", "0"}, "0.08", Easing.LINEAR)) - .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.08", Easing.LINEAR)) - .maxDuration(0.26f) - .resetWhenFinish(true) - .build() - ); - - this.put( - "bascenarioengine:down-then-up", - Animation.builder() - .name("Down Then Up") - .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "108"}, "0.3", Easing.LINEAR)).build()) - .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.3", Easing.LINEAR)) - .maxDuration(0.3f) - .resetWhenFinish(true) - .build() - ); - - this.put( - "bascenarioengine:hangry", - Animation.builder() - .name("Default Angry") - .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "-108"}, "0.2", Easing.LINEAR)).build()) - .put(0.2f, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "0"}, "0.2", Easing.LINEAR)).build()) - .put(0.4f, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "-108"}, "0.2", Easing.LINEAR)).build()) - .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.3", Easing.LINEAR)) - .maxDuration(0.6f) - .resetWhenFinish(true) - .build() - ); - - this.put( - "bascenarioengine:test", - Animation.builder() - .name("Dev Test") - .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"-960", "0"}, "1", Easing.LINEAR)).build()) - .put(1f, AnimationTimeline.builder().scale(new AnimationValue(new String[] {"1.5", "1.5"}, "0.3", Easing.QUAD)).build()) - .put(1.3f, AnimationTimeline.builder().scale(new AnimationValue(new String[] {"1", "1"}, "0.3", Easing.QUAD)).build()) - .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.1", Easing.LINEAR)) - .maxDuration(1.6f) - .resetWhenFinish(true) - .build() - ); - - this.put( - "bascenarioengine:loop-test", - Animation.builder() - .name("Dev Loop Test") - .rotation(new AnimationValue(new String[] {"0", "0", "57.29577951308232 * (math.mod(q.currentTimeMillis, 500) / 500 * math.pi * 2)"}, "0", Easing.LINEAR)).build() - ); +// this.put( +// "bascenarioengine:default-shake", +// Animation.builder() +// .name("Default Shake") +// .offset(new AnimationValue(new String[] {"math.abs(query.offset(0) + 19.2) <= 0.0001 ? 19.2 : -19.2", "0"}, "0.08", Easing.LINEAR)) +// .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.08", Easing.LINEAR)) +// .maxDuration(0.26f) +// .resetWhenFinish(true) +// .build() +// ); +// +// this.put( +// "bascenarioengine:down-then-up", +// Animation.builder() +// .name("Down Then Up") +// .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "108"}, "0.3", Easing.LINEAR)).build()) +// .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.3", Easing.LINEAR)) +// .maxDuration(0.3f) +// .resetWhenFinish(true) +// .build() +// ); +// +// this.put( +// "bascenarioengine:hangry", +// Animation.builder() +// .name("Default Angry") +// .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "-108"}, "0.2", Easing.LINEAR)).build()) +// .put(0.2f, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "0"}, "0.2", Easing.LINEAR)).build()) +// .put(0.4f, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"0", "-108"}, "0.2", Easing.LINEAR)).build()) +// .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.3", Easing.LINEAR)) +// .maxDuration(0.6f) +// .resetWhenFinish(true) +// .build() +// ); +// +// this.put( +// "bascenarioengine:test", +// Animation.builder() +// .name("Dev Test") +// .put(0, AnimationTimeline.builder().offset(new AnimationValue(new String[] {"-960", "0"}, "1", Easing.LINEAR)).build()) +// .put(1f, AnimationTimeline.builder().scale(new AnimationValue(new String[] {"1.5", "1.5"}, "0.3", Easing.QUAD)).build()) +// .put(1.3f, AnimationTimeline.builder().scale(new AnimationValue(new String[] {"1", "1"}, "0.3", Easing.QUAD)).build()) +// .defaultOffset(new AnimationValue(new String[]{"0", "0"}, "0.1", Easing.LINEAR)) +// .maxDuration(1.6f) +// .resetWhenFinish(true) +// .build() +// ); +// +// this.put( +// "bascenarioengine:loop-test", +// Animation.builder() +// .name("Dev Loop Test") +// .rotation(new AnimationValue(new String[] {"0", "0", "57.29577951308232 * (math.mod(q.currentTimeMillis, 500) / 500 * math.pi * 2)"}, "0", Easing.LINEAR)).build() +// ); } } diff --git a/core/src/main/java/oxy/bascenario/screens/renderer/AnimationTicker.java b/core/src/main/java/oxy/bascenario/screens/renderer/AnimationTicker.java index 94bd4ed5..9fbda443 100644 --- a/core/src/main/java/oxy/bascenario/screens/renderer/AnimationTicker.java +++ b/core/src/main/java/oxy/bascenario/screens/renderer/AnimationTicker.java @@ -1,8 +1,6 @@ package oxy.bascenario.screens.renderer; -import oxy.bascenario.api.animation.Animation; -import oxy.bascenario.api.animation.AnimationTimeline; -import oxy.bascenario.api.animation.AnimationValue; +import oxy.bascenario.api.animation.api.Animation; import oxy.bascenario.api.utils.math.Vec2; import oxy.bascenario.api.utils.math.Vec3; import oxy.bascenario.screens.ScenarioScreen; diff --git a/core/src/main/java/oxy/bascenario/utils/animation/AnimationUtils.java b/core/src/main/java/oxy/bascenario/utils/animation/AnimationUtils.java index b2cfef0a..e7506cb5 100644 --- a/core/src/main/java/oxy/bascenario/utils/animation/AnimationUtils.java +++ b/core/src/main/java/oxy/bascenario/utils/animation/AnimationUtils.java @@ -3,7 +3,6 @@ import lombok.experimental.UtilityClass; import net.lenni0451.commons.animation.easing.EasingFunction; import net.lenni0451.commons.animation.easing.EasingMode; -import oxy.bascenario.api.animation.AnimationValue; import oxy.bascenario.api.effects.Easing; import oxy.bascenario.api.utils.math.Vec2; import oxy.bascenario.api.utils.math.Vec3; diff --git a/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java b/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java index 0cfc9427..0b38dbba 100644 --- a/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java +++ b/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java @@ -52,17 +52,17 @@ public void render() { ImGui.separatorText("Animations"); - add("Play Animation", - "Play an defined animation, default or custom with an element on the selected track.", - new PlayAnimationEvent(0, "bascenarioengine:default-shake", false)); - - add("Play Sprite Animation", - "Play an animation that is defined in the spine (.skel) file you imported to a sprite character on the selected track.", - new SpriteAnimationEvent(0, 0.2f, "Idle_01", 1)); - - add("Stop Animation", - "Stop any defined animation, (note) not sprite animation.", - new StopAnimationEvent(0, "bascenarioengine:default-shake")); +// add("Play Animation", +// "Play an defined animation, default or custom with an element on the selected track.", +// new PlayAnimationEvent(0, "bascenarioengine:default-shake", false)); + +// add("Play Sprite Animation", +// "Play an animation that is defined in the spine (.skel) file you imported to a sprite character on the selected track.", +// new SpriteAnimationEvent(0, 0.2f, "Idle_01", 1)); +// +// add("Stop Animation", +// "Stop any defined animation, (note) not sprite animation.", +// new StopAnimationEvent(0, "bascenarioengine:default-shake")); ImGui.separatorText("Dialogues");