Skip to content

Commit

Permalink
Updated readme.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Trainguy9512 committed Feb 17, 2025
1 parent 77845cc commit b5bf57d
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 63 deletions.
52 changes: 40 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,50 @@
# 🏃 Trainguy's Animation Overhaul

Repository for a 1.19.3+ Minecraft mod centered around improving the game's character animations via a realtime animation system, included in the Moonflower suite of mods. The primary focus is third and first person character animation improvements, and once those are done the mod will be released and I will move on to entity and block animations.
Repository for a 1.21.4+ Minecraft mod centered around complex gameplay-driven character animations through a real-time animation system I'm building inspired by Unreal Engine's Animation Blueprints. Is currently included in the Moonflower suite of mods.

> **Warning**
> This project is still in heavy development! You are free to compile yourself and try it out, but keep in mind that there will be missing animations, placeholders, and debugging visuals that will not look correct. **This mod is not in a playable state!**
> **Warning!**
> This project is still in heavy development! You are free to compile yourself and try it out, but keep in mind that there will be missing animations, placeholders, and debugging visuals that will not look correct in a normal gameplay context.
## 📜 Planned Features

- 🟩 Complete
- 🟨 High Priority
- 🟥 Low Priority
- ❌ Currently out-of-scope (not permanently though!)

| Feature | Status | Notes |
|:--------------------------------|:-------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Pose Sampler System | 🟩 | Implementation of state machines, blend spaces, and montage tracks. |
| PyQT Maya Exporter | 🟨 | Tool for exporting animations out of Maya with new format with scale support. |
| First Person Player Animations | 🟨 | The first proper stress-test of the system. |
| In-game configuration | 🟨 | Settings for tweaking individual aspects of different joint animators. |
| Block / Block Entity Animations | 🟥 | Would like to re-add support for block animations, similar to the earlier implementation with Pollen, after first person animations are far enough along. |
| Third Person Player Animations | 🟥 | Whether or not this will be included with the release version or not is TBD. |
| Back-porting | 🟥 | Depends on the demand, given that this mod is intended to be used on vanilla-ish versions of the game and usually people playing vanilla don't often play older versions. |
| Synchronised Sound || I don't know how the sound system works currently, or what it would take to make sounds trigger with animations without breaking other sound mods, but it's something I'm keeping in mind. |
| Open API for Modding || I would like to lock down the design of the animation systems further before considering making this an open API |
| Entity Animations || Too high-scope to do on my own at this juncture, requires a large amount of animations/character rigs. Functionality will support it if I were to find somebody to help out on this. |
| Data-Driven Joint Animators || Design would need to be locked down enough prior to considering this. |

## 🔗 Socials
- My Discord server: https://discord.gg/HRg4nxvWWW
- My Discord server: _Work-in-progress_
- My Twitter: https://twitter.com/Trainguy9512
- Moonflower Website: https://moonflower.gg/
- Moonflower Twitter: https://www.moonflower.gg/twitter
- Moonflower Discord: https://www.moonflower.gg/discord

## 📘 Additional Credits
- Timeline and lerp system
## 📘 Credits
- Lead Development, Rigging, Animation
- [James Pelter (Trainguy9512)](https://x.com/Trainguy9512)
- Timeline and easing system
- [Marvin Schürz](https://twitter.com/minetoblend)
- Contributors
- [TomB-134](https://github.com/TomB-134)
- [AlphaKR93](https://github.com/AlphaKR93)
- [LizIsTired](https://github.com/LizIsTired)
- [CaioMGT](https://github.com/CaioMGT)
- [Superpowers04](https://github.com/superpowers04)
- Special thanks to members of the Moonflower team for supporting my development on this and helping answer my questions!

## 🧵 Usage and Contribution
- Pull requests are welcome!
Expand All @@ -31,12 +55,16 @@ Repository for a 1.19.3+ Minecraft mod centered around improving the game's char
## 🔍 FAQ

- What versions of the game will this mod support?
> For right now, the mod is being worked on in 1.19.3. Once I figure out better methods of version control for handling multiple versions, I would like to make the mod available on 1.18.2+, but this depends on whether 1.19's keyframed animation changes make me want to make 1.19.2 as the floor version. It will always support the latest release version of the game, barring huge rendering or animation changes.
- Will the mod come to forge?
> Yes, for the time being the mod is being developed on Fabric but the mod will eventually be moved back to an architectury project once the mod is finished enough to move back over to a pollen-based architectury project.
> For right now, the mod is being worked on in the latest version of Java Minecraft. Upon release, the plan is to gear the project towards supporting multiple versions at once onwards. Whether or not there will be backports is TBD
- What mod loaders will this mod be compatible with?
> Initially, likely only Fabric, as this mod being built to be used in a vanilla-ish setting and to my knowledge, Fabric is generally more geared towards vanilla gameplay than Forge is.
- Will the mod come to Forge/NeoForge?
> Likely yes, as part of multi-version development I plan on gearing the project for closer to release.
- What will the mod require as a dependency?
> Right now, just Fabric API. Once Pollen is updated to 1.19, the project will be reformatted to an architectury project which will add Pollen as a dependency, for things like registries and custom block renderers.
> Right now, just the Fabric API.
- What is this mod compatible with?
> Currently there is no official list of what will or will not work, but generally most cosmetic vanilla-friendly mods like Essential, 3D Skin Layers, and armor mods should work perfectly fine. Mods that change the player model to add additional animations like Better Combat or Emotecraft will not work, as they have different implementations for their animations that either overwrite or get overwritten by this mod. Content heavy mods that add additional animations for items and abilities are very likely to be visually absent, but this shouldn't inherently break anything.
> Currently there is no official list of what will or will not work, but generally most cosmetic vanilla-friendly mods like Essential, 3D Skin Layers, and other cosmetic mods should work perfectly fine. Mods that change the player model to add additional animations like Better Combat or Emotecraft will probably not work, as they have different implementations for their animations that either overwrite or get overwritten by this mod.
>
> Additionally, right now there are no plans to implement compatibility with heavier content mods which would have their own interaction animations, though one day this may come in the form of separate mods with an API.
If you feel this FAQ is missing anything or you have any additional questions, please let me know! You can reach me at Trainguy#9512 on discord
If you feel this FAQ is missing anything or you have any additional questions, please let me know by sending a message request to my Discord account, `Trainguy9512`
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public boolean containsTimelineForJoint(String joint){
}

public static class Builder{
private HashMap<String, Timeline<JointTransform>> jointTimelines;
private final HashMap<String, Timeline<JointTransform>> jointTimelines;
private final float frameLength;

protected Builder(float frameLength){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,15 @@ public AnimationPose interpolatedFilteredByJoints(AnimationPose other, float wei
if(weight == 0){
return new AnimationPose(this);
}
// TODO: Ensure pose samplers interpolate poses correctly to preserve pose spaces.
// Convert the other pose's pose space to match this pose's pose space.
AnimationPose otherConverted = this.poseSpace == other.poseSpace ? other :
this.poseSpace == PoseSpace.ENTITY ? other.convertedToEntitySpace() :
other.convertedToLocalSpace();
AnimationPose otherConverted = other.convertedToLocalSpace();

AnimationPose interpolatedAnimationPose = new AnimationPose(this);
AnimationPose interpolatedAnimationPose = this.convertedToLocalSpace();
joints.stream()
.filter(joint -> this.getJointSkeleton().containsJoint(joint))
.forEach(joint -> interpolatedAnimationPose.setJointTransform(joint,
weight == 1 ? otherConverted.getJointTransform(joint) :
this.getJointTransform(joint).interpolated(other.getJointTransform(joint), weight))
interpolatedAnimationPose.getJointTransform(joint).interpolated(other.getJointTransform(joint), weight))
);
return interpolatedAnimationPose;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,6 @@ public void pushPose(AnimationPose animationPose){
public AnimationPose getBlendedPose(float partialTicks){
// uncomment this for debugging
//partialTicks = 1;
return this.poseOld.getBlendedLinear(this.pose, partialTicks).convertedToEntitySpace();
return this.poseOld.interpolated(this.pose, partialTicks).convertedToEntitySpace();
}

/*
private static Locator lerpLocator(float value, Locator locatorOld, Locator locator){
Locator locatorNew = new Locator(locator.getIdentifier());
locatorNew.translateX = Mth.lerp(value, locatorOld.translateX, locator.translateX);
locatorNew.translateY = Mth.lerp(value, locatorOld.translateY, locator.translateY);
locatorNew.translateZ = Mth.lerp(value, locatorOld.translateZ, locator.translateZ);
locatorNew.rotateX = Mth.rotLerp(value, locatorOld.rotateX, locator.rotateX);
locatorNew.rotateY = Mth.rotLerp(value, locatorOld.rotateY, locator.rotateY);
locatorNew.rotateZ = Mth.rotLerp(value, locatorOld.rotateZ, locator.rotateZ);
return locatorNew;
}
public String getLocator(String identifier){
return this.pose.getSkeleton().containsLocator(identifier) ? identifier : "null";
}
*/

/*
public boolean containsLocator(String identifier){
return this.pose.getSkeleton().containsLocator(identifier);
}
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public AnimationPose sample(AnimationDriverContainer animationDriverContainer, P
return firstEntry.getValue().sampleEntry(jointSkeleton, this.getTimeElapsed());

float relativeTime = (this.currentValue - firstEntry.getKey()) / (secondEntry.getKey() - firstEntry.getKey());
return firstEntry.getValue().sampleEntry(jointSkeleton, this.getTimeElapsed()).getBlendedLinear(
return firstEntry.getValue().sampleEntry(jointSkeleton, this.getTimeElapsed()).interpolated(
secondEntry.getValue().sampleEntry(jointSkeleton, this.getTimeElapsed()),
relativeTime
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import net.minecraft.resources.ResourceLocation;

import java.util.LinkedHashMap;
import java.util.Map;

public class AnimationMontageTrack extends PoseSampler implements SampleableFromInput {

Expand Down Expand Up @@ -49,7 +50,14 @@ public void tick(AnimationDriverContainer animationDriverContainer, PoseSamplerS
public AnimationPose sample(AnimationDriverContainer animationDriverContainer, PoseSamplerStateContainer poseSamplerStateContainer, JointSkeleton jointSkeleton, AnimationPose inputPose) {
if(!this.montages.isEmpty()){
AnimationPose pose = new AnimationPose(inputPose);
this.montages.forEach((configuration, timeElapsed) -> pose.blend(AnimationPose.fromAnimationSequence(jointSkeleton, configuration.animationSequence, (timeElapsed + configuration.startTime) / AnimationSequenceData.INSTANCE.get(configuration.animationSequence).frameLength()), configuration.getWeight(timeElapsed), Easing.LINEAR));
for (Map.Entry<MontageConfiguration, Float> entry : this.montages.entrySet()){
MontageConfiguration configuration = entry.getKey();
float timeElapsed = entry.getValue();
float weight = configuration.getWeight(timeElapsed);
if(weight > 0){
pose = pose.interpolated(AnimationPose.fromAnimationSequence(jointSkeleton, configuration.animationSequence, (timeElapsed + configuration.startTime) / AnimationSequenceData.INSTANCE.get(configuration.animationSequence).frameLength()), weight);
}
}
return pose;
}
return inputPose;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,18 @@ public AnimationPose sample(AnimationDriverContainer animationDriverContainer, P
AnimationPose animationPose = this.getPoseFromState(this.activeStates.getFirst(), animationDriverContainer, poseSamplerStateContainer, jointSkeleton);
if(this.activeStates.size() > 1){
for(S stateIdentifier : this.activeStates){
animationPose.blend(
animationPose = animationPose.interpolated(
this.getPoseFromState(stateIdentifier, animationDriverContainer, poseSamplerStateContainer, jointSkeleton),
this.statesHashMap.get(stateIdentifier).getWeight(),
this.statesHashMap.get(stateIdentifier).getCurrentTransition().easing());
this.statesHashMap.get(stateIdentifier).getCurrentTransition().easing().ease(
this.statesHashMap.get(stateIdentifier).getWeight()
)
);
}
}
//AnimationOverhaulMain.LOGGER.info(this.activeStates.toString());
return animationPose;
}
AnimationOverhaulMain.LOGGER.warn("No active states in state machine {}", this.getIdentifier());
//AnimationOverhaulMain.LOGGER.warn("No active states in state machine {}", this.getIdentifier());
return AnimationPose.of(jointSkeleton);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import net.minecraft.util.Mth;

/**
* @author Marvin Schürz
*/
@FunctionalInterface
public interface Easing {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.trainguy9512.animationoverhaul.util.time;

/**
* @author Marvin Schürz
*/
@FunctionalInterface
public interface Interpolator<T> {

T interpolate(T a, T b, float t);

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

import java.util.TreeMap;

/**
* @author Marvin Schürz
*/
public class Timeline<T> {

private TreeMap<Float, Keyframe<T>> keyframes = new TreeMap();
private final Lerper<T> lerper;
private final Interpolator<T> lerper;

public Timeline(Lerper<T> lerper) {
public Timeline(Interpolator<T> lerper) {
this.lerper = lerper;
}

Expand All @@ -36,7 +39,7 @@ public T getValueAtFrame(float time) {
float relativeTime = (time - firstKeyframe.getKey()) / (secondKeyframe.getKey() - firstKeyframe.getKey());


return lerper.lerp(
return lerper.interpolate(
firstKeyframe.getValue().getValue(),
secondKeyframe.getValue().getValue(),
secondKeyframe.getValue().getEasing().ease(relativeTime)
Expand All @@ -53,7 +56,7 @@ public T getValueAtPercentage(float time) {
}

public Timeline<T> addKeyframe(float time, T value) {
return addKeyframe(time, value, new Easing.Linear());
return addKeyframe(time, value, Easing.LINEAR);
}

public Timeline<T> addKeyframe(float time, T value, Easing easing) {
Expand Down Expand Up @@ -85,6 +88,6 @@ public static Timeline<Float> floatTimeline() {
}

public static Timeline<JointTransform> jointTransformTimeline() {
return new Timeline<>(JointTransform::blendLinear);
return new Timeline<>(JointTransform::interpolated);
}
}

0 comments on commit b5bf57d

Please sign in to comment.