Skip to content

Commit

Permalink
Fixed joint transform interpolation, working on improved quaternion i…
Browse files Browse the repository at this point in the history
…nterpolation.
  • Loading branch information
Trainguy9512 committed Mar 1, 2025
1 parent 737380e commit 34b1a49
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.trainguy9512.animationoverhaul.animation.animator.entity;

import com.mojang.math.Axis;
import com.trainguy9512.animationoverhaul.AnimationOverhaulMain;
import com.trainguy9512.animationoverhaul.animation.data.*;
import com.trainguy9512.animationoverhaul.animation.data.driver.Driver;
import com.trainguy9512.animationoverhaul.animation.data.key.AnimationDriverKey;
import com.trainguy9512.animationoverhaul.animation.pose.AnimationPose;
import com.trainguy9512.animationoverhaul.animation.joint.JointTransform;
import com.trainguy9512.animationoverhaul.animation.pose.ComponentSpacePose;
import com.trainguy9512.animationoverhaul.animation.pose.LocalSpacePose;
import com.trainguy9512.animationoverhaul.animation.pose.function.*;
import com.trainguy9512.animationoverhaul.animation.pose.function.cache.SavedCachedPoseContainer;
Expand Down Expand Up @@ -112,23 +113,30 @@ public PoseFunction<LocalSpacePose> constructPoseFunction(SavedCachedPoseContain
Random random = new Random();
PoseFunction<LocalSpacePose> testSequencePlayer = SequencePlayerFunction.builder(ANIMATION_FP_PLAYER_IDLE)
.setLooping(true)
.setPlayRate((context) -> random.nextFloat(3))
.setPlayRate((context) -> 1f)
.build();
//cachedPoseContainer.register("TEST_SEQ_PLAYER", testSequencePlayer);


PoseFunction<LocalSpacePose> testTransformer = LocalPoseConversionFunction.of(
JointTransformerFunction.componentSpaceBuilder(ComponentPoseConversionFunction.of(testSequencePlayer), LEFT_ARM_JOINT)
.setTranslationConfiguration(JointTransformerFunction.TransformChannelConfiguration.of(
(context) -> new Vector3f((float) Math.sin(context.gameTime() * 8f) * 0.6f, 0, 0),
JointTransform.TransformType.ADD,
JointTransform.TransformSpace.COMPONENT))
.build());
.setTranslation(
(context) -> new Vector3f((float) Math.sin(context.gameTimeSeconds() * 8f) * 0f, 0, 0),
JointTransform.TransformType.ADD,
JointTransform.TransformSpace.COMPONENT
)
.setRotation(
(context) -> Axis.XP.rotation((float) (Math.sin(context.gameTimeSeconds() * 0f) * Mth.PI * 0.3f)),
JointTransform.TransformType.REPLACE,
JointTransform.TransformSpace.COMPONENT
)
.setWeight(context -> Mth.sin(context.gameTimeSeconds() * 12f) * 0.5f + 0.5f)
.build());

//PoseFunction<LocalSpacePose> cached = cachedPoseContainer.getOrThrow("TEST_SEQ_PLAYER");
PoseFunction<LocalSpacePose> blendMultipleFunction = BlendMultipleFunction.builder(testSequencePlayer).addBlendInput(testSequencePlayer, (evaluationState) -> 0.5f).build();

return blendMultipleFunction;
return testTransformer;
}


Expand Down Expand Up @@ -171,8 +179,10 @@ public void extractAnimationData(LocalPlayer dataReference, OnTickDataContainer
driverContainer.loadValueIntoDriver(CAMERA_ROTATION, targetRotation);



Vector3f dampenedCameraRotation = new Vector3f(driverContainer.getPreviousDriverValue(DAMPENED_CAMERA_ROTATION));


// If the dampened camera rotation is 0 (which is what it is upon initialization), set it to the target
if(dampenedCameraRotation.x() == 0F && dampenedCameraRotation.y() == 0F){
dampenedCameraRotation = targetRotation;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
package com.trainguy9512.animationoverhaul.animation.data;

import com.google.common.collect.Maps;
import com.trainguy9512.animationoverhaul.AnimationOverhaulMain;
import com.trainguy9512.animationoverhaul.animation.animator.JointAnimator;
import com.trainguy9512.animationoverhaul.animation.animator.entity.FirstPersonPlayerJointAnimator;
import com.trainguy9512.animationoverhaul.animation.data.driver.Driver;
import com.trainguy9512.animationoverhaul.animation.data.key.AnimationDataKey;
import com.trainguy9512.animationoverhaul.animation.data.key.AnimationDriverKey;
import com.trainguy9512.animationoverhaul.animation.joint.JointSkeleton;
import com.trainguy9512.animationoverhaul.animation.pose.AnimationPose;
import com.trainguy9512.animationoverhaul.animation.pose.LocalSpacePose;
import com.trainguy9512.animationoverhaul.animation.pose.function.PoseFunction;
import com.trainguy9512.animationoverhaul.animation.pose.function.cache.SavedCachedPoseContainer;
import com.trainguy9512.animationoverhaul.animation.pose.sampler.PoseSampler;
import com.trainguy9512.animationoverhaul.animation.pose.sampler.Sampleable;
import com.trainguy9512.animationoverhaul.animation.pose.sampler.SampleableFromInput;
import com.trainguy9512.animationoverhaul.util.Interpolator;

import java.util.HashMap;
import java.util.Map;

public class AnimationDataContainer implements PoseCalculationDataContainer, OnTickDataContainer {

private final Map<AnimationDataKey<? extends Driver<?>>, Driver<?>> drivers;
private final SavedCachedPoseContainer savedCachedPoseContainer;
private final PoseFunction<LocalSpacePose> poseFunction;
private long currentTick;

private final JointSkeleton jointSkeleton;
private final AnimationDriverKey<LocalSpacePose> perTickCalculatedPoseDriverKey;
private final AnimationDriverKey<Long> gameTimeTicksDriverKey;

private AnimationDataContainer(JointAnimator<?> jointAnimator){
this.drivers = Maps.newHashMap();
this.savedCachedPoseContainer = SavedCachedPoseContainer.of();
this.poseFunction = jointAnimator.constructPoseFunction(savedCachedPoseContainer).wrapUnique();
this.currentTick = 0;

this.jointSkeleton = jointAnimator.buildSkeleton();
this.perTickCalculatedPoseDriverKey = AnimationDriverKey.driverKeyOf("per_tick_calculated_pose", () -> Driver.ofInterpolatable(() -> LocalSpacePose.of(jointSkeleton), Interpolator.LOCAL_SPACE_POSE));
this.gameTimeTicksDriverKey = AnimationDriverKey.driverKeyOf("game_time", () -> Driver.ofConstant(() -> 0L));
}

public static AnimationDataContainer of(JointAnimator<?> jointAnimator){
Expand All @@ -54,13 +47,17 @@ public AnimationDriverKey<LocalSpacePose> getPerTickCalculatedPoseDriverKey(){
}

public void tick(){
this.poseFunction.tick(PoseFunction.FunctionEvaluationState.of(this, false, this.currentTick));
this.currentTick++;
this.loadValueIntoDriver(gameTimeTicksDriverKey, this.getDriverValue(gameTimeTicksDriverKey) + 1);
this.poseFunction.tick(PoseFunction.FunctionEvaluationState.of(this, false, this.getDriverValue(gameTimeTicksDriverKey)));
}

public LocalSpacePose computePose(float partialTicks){
this.savedCachedPoseContainer.clearCaches();
return this.poseFunction.compute(PoseFunction.FunctionInterpolationContext.of(this, partialTicks));
return this.poseFunction.compute(PoseFunction.FunctionInterpolationContext.of(
this,
partialTicks,
(this.getDriverValueInterpolated(gameTimeTicksDriverKey, 1) - (1 - partialTicks)) / 20f
));
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -94,6 +91,6 @@ public <D> D getDriverValueInterpolated(AnimationDriverKey<D> driverKey, float p
}

public void pushDriverValuesToPrevious(){
this.drivers.values().forEach(Driver::pushValueToPrevious);
this.drivers.values().forEach(Driver::pushToPrevious);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ public void loadValue(D newValue){
* If the new value is null, the default value is used instead.
* @param newValue Value to load for the current tick
*/
public void loadValueAndPush(D newValue){
this.pushValueToPrevious();
public void pushToPreviousAndLoadValue(D newValue){
this.pushToPrevious();
this.loadValue(newValue);
}

/**
* Pushes the current value to the previous tick's value.
*/
public void pushValueToPrevious(){
public void pushToPrevious(){
this.valuePrevious = this.valueCurrent;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.resources.ResourceLocation;
import org.joml.*;
import org.joml.Math;

public final class JointTransform {

Expand Down Expand Up @@ -54,21 +55,32 @@ public Quaternionf getRotation(){
return this.transform.getNormalizedRotation(new Quaternionf());
}

public Vector3f getScale(){
return this.transform.getScale(new Vector3f());
}

public Vector3f getEulerRotationZYX(){
return this.transform.getEulerAnglesZYX(new Vector3f());
}

public PartPose asPartPose(){
Vector3f rotation = this.getEulerRotationZYX();
Vector3f translation = this.getTranslation();
return PartPose.offsetAndRotation(
translation.x(),
translation.y(),
translation.z(),
rotation.x(),
rotation.y(),
rotation.z()
);
Vector3f scale = this.getScale();
return PartPose
.offsetAndRotation(
translation.x(),
translation.y(),
translation.z(),
rotation.x(),
rotation.y(),
rotation.z()
)
.scaled(
scale.x(),
scale.y(),
scale.z()
);
}

public void translate(Vector3f translation, TransformSpace transformSpace, TransformType transformType){
Expand All @@ -89,11 +101,16 @@ public void rotate(Quaternionf rotation, TransformSpace transformSpace, Transfor
switch (transformType){
case ADD -> {
switch (transformSpace){
case COMPONENT, PARENT -> this.transform.rotation(this.transform.getNormalizedRotation(new Quaternionf()).premul(rotation));
//case COMPONENT, PARENT -> this.transform.rotation(this.transform.getNormalizedRotation(new Quaternionf()).premul(rotation));
case LOCAL -> this.transform.rotate(rotation);
case COMPONENT, PARENT -> {
Quaternionf currentRotation = this.transform.getUnnormalizedRotation(new Quaternionf());
rotation.mul(currentRotation, currentRotation);
this.transform.translationRotateScale(this.getTranslation(), currentRotation, this.getScale());
}
}
}
case REPLACE -> this.transform.rotation(rotation);
case REPLACE -> this.transform.translationRotateScale(this.getTranslation(), rotation, this.getScale());
}
}

Expand Down Expand Up @@ -126,7 +143,23 @@ public JointTransform mirrored(){
}

public JointTransform interpolated(JointTransform other, float weight){
return JointTransform.of(this.transform.lerp(other.transform, weight, new Matrix4f()));
Vector3f translation = this.transform.getTranslation(new Vector3f());
Quaternionf rotation = this.transform.getNormalizedRotation(new Quaternionf());
Vector3f scale = this.transform.getScale(new Vector3f());

Vector3f otherTranslation = other.transform.getTranslation(new Vector3f());
Quaternionf otherRotation = other.transform.getNormalizedRotation(new Quaternionf());
Vector3f otherScale = other.transform.getScale(new Vector3f());

if(rotation.dot(otherRotation) < 0){
otherRotation.invert(otherRotation);
}

translation.lerp(otherTranslation, weight);
rotation.slerp(otherRotation, weight);
scale.lerp(otherScale, weight);

return JointTransform.of(new Matrix4f().translationRotateScale(translation, rotation, scale));
}

public void translateAndRotatePoseStack(PoseStack poseStack){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public BlendMultipleFunction(PoseFunction<LocalSpacePose> baseFunction, Map<Blen
public void tick(FunctionEvaluationState evaluationState) {
this.baseFunction.tick(evaluationState);
this.inputs.forEach((blendInput, weightDriver) -> {
weightDriver.pushValueToPrevious();
weightDriver.pushToPrevious();
float weight = blendInput.weightFunction.apply(evaluationState);
weightDriver.loadValue(weight);

Expand Down
Loading

0 comments on commit 34b1a49

Please sign in to comment.