Skip to content

Commit

Permalink
feat: case animation
Browse files Browse the repository at this point in the history
todo: finish player animation
  • Loading branch information
Duzos committed Aug 26, 2024
1 parent d64a053 commit 2ba5adf
Show file tree
Hide file tree
Showing 18 changed files with 1,702 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package mc.duzo.timeless.client.animation;

import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.animation.Animation;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.entity.AnimationState;

import mc.duzo.timeless.client.animation.player.PlayerAnimationHelper;

public abstract class AnimationHolder {
protected final AnimationState state;
protected final Animation animation;

protected AnimationHolder(Animation anim) {
this.state = new AnimationState();
this.animation = anim;
}

public void update(EntityModel<?> model, float progress, AbstractClientPlayerEntity player) {
// overwritten update method goes here

if (this.isFinished(player)) {
this.state.stop();
this.onFinished(player);
} else {
if (!this.state.isRunning()) {
this.onStart(player);
}
this.state.startIfNotRunning(player.age);
}
}

public boolean isFinished(AbstractClientPlayerEntity entity) {
if (this.animation.looping()) return false; // Looping animations should extend this class so they properly finish

return this.getRunningSeconds() >= this.animation.lengthInSeconds();
}

protected void onFinished(AbstractClientPlayerEntity player) {

}
protected void onStart(AbstractClientPlayerEntity player) {

}

protected float getRunningSeconds() {
return PlayerAnimationHelper.getRunningSeconds(this.animation, this.state.getTimeRunning());
}
public Animation getAnimation() {
return animation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package mc.duzo.timeless.client.animation.player;

import java.util.Optional;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.joml.Vector3f;

import net.minecraft.client.model.ModelPart;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.entity.animation.Animation;
import net.minecraft.client.render.entity.animation.Keyframe;
import net.minecraft.client.render.entity.animation.Transformation;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.entity.AnimationState;
import net.minecraft.util.math.MathHelper;

import mc.duzo.timeless.client.animation.player.holder.PlayerAnimationHolder;
import mc.duzo.timeless.suit.client.animation.SuitAnimationHolder;
import mc.duzo.timeless.suit.client.animation.SuitAnimationTracker;

/**
* From {@link net.minecraft.client.render.entity.animation.AnimationHelper},
* Edited for animating PlayerModel
*
* @author duzo
*/
@Environment(value= EnvType.CLIENT)
public class PlayerAnimationHelper {
public static void animate(PlayerEntityModel<?> model, Animation animation, long runningTime, float scale, Vector3f tempVec) {
float f = getRunningSeconds(animation, runningTime);
animation.boneAnimations().forEach((key, list) -> {
// TODO - fix "player" / "root" part not animating properly
Optional<ModelPart> optional = getChild(model, key);
optional.ifPresent(part -> list.forEach(transformation -> {
Keyframe[] keyframes = transformation.keyframes();
int i = Math.max(0, MathHelper.binarySearch(0, keyframes.length, index -> f <= keyframes[index].timestamp()) - 1);
int j = Math.min(keyframes.length - 1, i + 1);
Keyframe keyframe = keyframes[i];
Keyframe keyframe2 = keyframes[j];
float h = f - keyframe.timestamp();
float k = j != i ? MathHelper.clamp(h / (keyframe2.timestamp() - keyframe.timestamp()), 0.0f, 1.0f) : 0.0f;
keyframe2.interpolation().apply(tempVec, k, keyframes, i, j, scale);
transformation.target().apply(part, tempVec);
}));
});

correctSleevies(model);
}

private static void correctSleevies(PlayerEntityModel<?> model) {
model.hat.copyTransform(model.head);
model.leftSleeve.copyTransform(model.leftArm);
model.rightSleeve.copyTransform(model.rightArm);
model.leftPants.copyTransform(model.leftLeg);
model.rightPants.copyTransform(model.rightLeg);
model.jacket.copyTransform(model.body);
}

public static void updateAnimation(AnimationState animationState, Animation animation, float animationProgress, float speedMultiplier, PlayerEntityModel<?> model) {
animationState.update(animationProgress, speedMultiplier);
animationState.run(state -> animate(model, animation, state.getTimeRunning(), 1.0f, new Vector3f()));
}
public static void updateAnimation(AnimationState animationState, Animation animation, float animationProgress, PlayerEntityModel<?> model) {
updateAnimation(animationState, animation, animationProgress, 1.0f, model);
}

public static float getRunningSeconds(Animation animation, long runningTime) {
float f = (float)runningTime / 1000.0f;
return animation.looping() ? f % animation.lengthInSeconds() : f;
}

private static Optional<ModelPart> getChild(PlayerEntityModel<?> model, String name) {
return ((PlayerModelHook) model).timeless$getChild(name);
}

public static Vector3f getModelPartPivot(PlayerEntityModel<?> model, String name) {
Optional<ModelPart> optional = getChild(model, name);
if(optional.isEmpty()) return new Vector3f(0.0F, 0.0F, 0.0F);
Vector3f pivot = new Vector3f(optional.get().pivotX, optional.get().pivotY, optional.get().pivotZ);
return pivot;
}

public static Vector3f getModelPartRotation(PlayerEntityModel<?> model, String name) {
Optional<ModelPart> optional = getChild(model, name);
if(optional.isEmpty()) return new Vector3f(0.0F, 0.0F, 0.0F);
Vector3f rotation = new Vector3f(optional.get().pivotX, optional.get().pivotY, optional.get().pivotZ);
return rotation;
}

public static void runAnimations(AbstractClientPlayerEntity livingEntity, PlayerEntityModel<?> model, float progress) {
PlayerAnimationHolder anim = PlayerAnimationTracker.getAnimation(livingEntity);

if (anim != null) {
anim.update(model, progress, livingEntity);
return;
}

SuitAnimationHolder animSuit = SuitAnimationTracker.getAnimation(livingEntity);
if (animSuit.getModel() == null) return;
animSuit.getModel().copyTo(model);
}

public static void startAnimations(AbstractClientPlayerEntity entity) {

}
public static boolean isRunningAnimations(AbstractClientPlayerEntity livingEntity) {
PlayerAnimationHolder anim = PlayerAnimationTracker.getAnimation(livingEntity);
SuitAnimationHolder animSuit = SuitAnimationTracker.getAnimation(livingEntity);
return (anim != null && !anim.isFinished(livingEntity)) || (animSuit != null && animSuit.shouldPlayerMimic() && !animSuit.isFinished(livingEntity));
}

public static void playAnimation(AbstractClientPlayerEntity player, Animation animation) {
playAnimation(player, new PlayerAnimationHolder(animation));
}
public static void playAnimation(AbstractClientPlayerEntity player, PlayerAnimationHolder holder) {
stopAnimation(player);
PlayerAnimationTracker.addAnimation(player.getUuid(), holder);
}
public static void stopAnimation(AbstractClientPlayerEntity player) {
PlayerAnimationTracker.clearAnimation(player.getUuid());
}
public static final Transformation.Interpolation STEP = (dest, delta, keyframes, start, end, scale) -> {
Vector3f vector3f = keyframes[start].target();
Vector3f vector3f2 = keyframes[end].target();
return vector3f.lerp(vector3f2, 0, dest).mul(scale);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package mc.duzo.timeless.client.animation.player;

import java.util.HashMap;
import java.util.UUID;

import net.minecraft.client.network.AbstractClientPlayerEntity;

import mc.duzo.timeless.client.animation.player.holder.PlayerAnimationHolder;

public class PlayerAnimationTracker {
private static final HashMap<UUID, PlayerAnimationHolder> ANIMATIONS = new HashMap<>();

public static PlayerAnimationHolder getAnimation(AbstractClientPlayerEntity entity) {
UUID uuid = entity.getUuid();

PlayerAnimationHolder anim = ANIMATIONS.get(uuid);

if (anim != null && anim.isFinished(entity)) {
ANIMATIONS.remove(uuid);
}

return anim;
}
public static void addAnimation(UUID uuid, PlayerAnimationHolder animation) {
ANIMATIONS.put(uuid, animation);
}
public static void clearAnimation(UUID uuid) {
ANIMATIONS.remove(uuid);
}
}
Loading

0 comments on commit 2ba5adf

Please sign in to comment.