Skip to content

Commit 2b9d2cf

Browse files
committed
New Dog Walking Dynamics!
- Introduced `DogWalkAnimationState` as a dedicated locomotion data manager for Dog instead of the vanilla default. This smoothes the input movement speed using `SecondOrderDynamics`. This also handles procedural gait blending and banking mechanics. - Implemented running: Dogs now smoothly transition to a running gait (`WALK_GALLOP`) when their speed reaches a threshold for a certain amount of time. - Implemented visual banking: Dogs now centrifugally lean into turns when running. The bank angle is calculated dynamically based on yaw velocity and smoothed using `SecondOrderDynamics`. - Added dedicated keyframe animations for walking and running: `WALK_SLOW_TROT` and `WALK_GALLOP`. The former now replaces the current procedural oscillating walk animation in `DogModel`. - Added `AnimSnapshot` utility class. This allows capturing `DogModel` poses and blending them together (LERP) to create seamless transitions between keyframe animations. - Updated `DogModel` to utilize the snapshot system, blending the two animations based on the dog's current speed and running state. - Updated `DogRenderer` to apply the calculated banking rotation to the model's PoseStack.
1 parent a5be6b2 commit 2b9d2cf

File tree

7 files changed

+336
-3
lines changed

7 files changed

+336
-3
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package doggytalents.client.entity.model.dog;
2+
3+
import net.minecraft.client.model.geom.ModelPart;
4+
import net.minecraft.util.Mth;
5+
6+
public class AnimSnapshot {
7+
8+
9+
public Part root = new Part();
10+
11+
public Part head = new Part();
12+
public Part realHead = new Part();
13+
public Part body = new Part();
14+
public Part mane = new Part();
15+
public Part legBackRight = new Part();
16+
public Part legBackLeft = new Part();
17+
public Part legFrontRight = new Part();
18+
public Part legFrontLeft = new Part();
19+
public Part tail = new Part();
20+
public Part realTail = new Part();
21+
22+
//Optional parts
23+
public Part earLeft = new Part();
24+
public Part earRight = new Part();
25+
26+
27+
public void store(DogModel model) {
28+
storePart(root, model.root);
29+
storePart(head, model.head);
30+
storePart(realHead, model.realHead);
31+
storePart(body, model.body);
32+
storePart(mane, model.mane);
33+
storePart(legBackRight, model.legBackRight);
34+
storePart(legBackLeft, model.legBackLeft);
35+
storePart(legFrontRight, model.legFrontRight);
36+
storePart(legFrontLeft, model.legFrontLeft);
37+
storePart(tail, model.tail);
38+
storePart(realTail, model.realTail);
39+
40+
model.earLeft.ifPresent(x -> storePart(earLeft, x));
41+
model.earRight.ifPresent(x -> storePart(earRight, x));
42+
}
43+
44+
public void load(DogModel model) {
45+
loadPart(model.root, root);
46+
loadPart(model.head, head);
47+
loadPart(model.realHead, realHead);
48+
loadPart(model.body, body);
49+
loadPart(model.mane, mane);
50+
loadPart(model.legBackRight, legBackRight);
51+
loadPart(model.legBackLeft, legBackLeft);
52+
loadPart(model.legFrontRight, legFrontRight);
53+
loadPart(model.legFrontLeft, legFrontLeft);
54+
loadPart(model.tail, tail);
55+
loadPart(model.realTail, realTail);
56+
57+
model.earLeft.ifPresent(x -> loadPart(x, earLeft));
58+
model.earRight.ifPresent(x -> loadPart(x, earRight));
59+
}
60+
61+
private static void storePart(Part part, ModelPart model_part) {
62+
part.x = model_part.x;
63+
part.y = model_part.y;
64+
part.z = model_part.z;
65+
part.xrot = model_part.xRot;
66+
part.yrot = model_part.yRot;
67+
part.zrot = model_part.zRot;
68+
}
69+
70+
private static void loadPart(ModelPart model_part, Part part) {
71+
model_part.x = part.x;
72+
model_part.y = part.y;
73+
model_part.z = part.z;
74+
model_part.xRot = part.xrot;
75+
model_part.yRot = part.yrot;
76+
model_part.zRot = part.zrot;
77+
}
78+
79+
public static void blendAndApply(float progress,
80+
AnimSnapshot result1, AnimSnapshot result2,
81+
DogModel model) {
82+
83+
blendPartAndApply(progress, result1.root, result2.root, model.root);
84+
blendPartAndApply(progress, result1.head, result2.head, model.head);
85+
blendPartAndApply(progress, result1.realHead, result2.realHead, model.realHead);
86+
blendPartAndApply(progress, result1.body, result2.body, model.body);
87+
blendPartAndApply(progress, result1.mane, result2.mane, model.mane);
88+
blendPartAndApply(progress, result1.legBackRight, result2.legBackRight, model.legBackRight);
89+
blendPartAndApply(progress, result1.legBackLeft, result2.legBackLeft, model.legBackLeft);
90+
blendPartAndApply(progress, result1.legFrontRight, result2.legFrontRight, model.legFrontRight);
91+
blendPartAndApply(progress, result1.legFrontLeft, result2.legFrontLeft, model.legFrontLeft);
92+
blendPartAndApply(progress, result1.tail, result2.tail, model.tail);
93+
blendPartAndApply(progress, result1.realTail, result2.realTail, model.realTail);
94+
95+
model.earLeft.ifPresent(x -> blendPartAndApply(progress, result1.earLeft, result2.earLeft, x));
96+
model.earRight.ifPresent(x -> blendPartAndApply(progress, result1.earRight, result2.earRight, x));
97+
}
98+
99+
private static void blendPartAndApply(float progress,
100+
Part part1, Part part2, ModelPart model_part) {
101+
102+
model_part.x = interp(progress, part1.x, part2.x);
103+
model_part.y = interp(progress, part1.y, part2.y);
104+
model_part.z = interp(progress, part1.z, part2.z);
105+
model_part.xRot = interpRot(progress, part1.xrot, part2.xrot);
106+
model_part.yRot = interpRot(progress, part1.yrot, part2.yrot);
107+
model_part.zRot = interpRot(progress, part1.zrot, part2.zrot);
108+
}
109+
110+
private static float interp(float progress, float a, float b) {
111+
return Mth.lerp(progress, a, b);
112+
}
113+
114+
private static float interpRot(float progress, float a, float b) {
115+
return Mth.rotLerp(progress ,a, b);
116+
}
117+
118+
public static class Part {
119+
public float x, y, z, xrot, yrot, zrot;
120+
}
121+
122+
}

src/main/java/doggytalents/client/entity/model/dog/DogModel.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
import doggytalents.api.inferface.AbstractDog;
2020
import doggytalents.api.registry.Accessory;
2121
import doggytalents.api.registry.AccessoryInstance;
22+
import doggytalents.client.entity.model.animation.DTNAnimationLoader;
2223
import doggytalents.client.entity.model.animation.DogAnimationRegistry;
2324
import doggytalents.client.entity.model.animation.DogKeyframeAnimations;
25+
import doggytalents.client.entity.model.animation.DTNAnimationLoader.DogAnimationHolder;
26+
import doggytalents.client.entity.model.animation.DogKeyframeAnimations.AnimationContext;
2427
import doggytalents.common.entity.Dog;
2528
import doggytalents.common.util.Util;
2629
import net.minecraft.client.animation.AnimationDefinition;
@@ -63,6 +66,12 @@ public class DogModel extends EntityModel<Dog> {
6366
public Optional<ModelPart> earLeft;
6467
public Optional<ModelPart> earRight;
6568

69+
private final AnimSnapshot animSnapshot1 = new AnimSnapshot();
70+
private final AnimSnapshot animSnapshot2 = new AnimSnapshot();
71+
72+
private final DogAnimationHolder WALK_SLOW_TROT = DTNAnimationLoader.INSTANCE.getAnim("slow_trot");
73+
private final DogAnimationHolder WALK_GALLOP = DTNAnimationLoader.INSTANCE.getAnim("gallop");
74+
6675
public DogModel(ModelPart box) {
6776
initDogModel(box);
6877
}
@@ -173,9 +182,40 @@ public void prepareMobModel(Dog dog, float limbSwing, float limbSwingAmount, flo
173182
}
174183

175184
public void setUpStandPose(Dog dog, float limbSwing, float limbSwingAmount, float partialTickTime) {
176-
animateStandWalking(dog, limbSwing, limbSwingAmount, partialTickTime);
185+
animateWalkAndRun(dog, limbSwing, limbSwingAmount, partialTickTime);
186+
}
187+
188+
public void animateWalkAndRun(Dog dog, float limbSwing, float limbSwingAmount, float partialTickTime) {
189+
final var pose_1 = this.animSnapshot1;
190+
final var pose_2 = this.animSnapshot2;
191+
192+
var walk_pos = dog.dogWalkAnimation.position(partialTickTime);
193+
var walk_speed = dog.dogWalkAnimation.speed(partialTickTime);
194+
var anim_context = AnimationContext.of(
195+
this::searchForPartWithName,
196+
x -> x.resetPose());
197+
198+
final long walk_animation_start = 830;
199+
long time = walk_animation_start + Util.tickMayWithPartialToMillis(walk_pos * 2.5);
200+
float anim_swing = walk_speed <= 0.2f ? walk_speed/0.2f : 1;
201+
anim_swing = Mth.clamp(anim_swing, 0, 1);
202+
203+
this.resetAllPose();
204+
if (anim_swing > Mth.EPSILON) {
205+
DogKeyframeAnimations.keyframeAnimate(anim_context, WALK_SLOW_TROT.get(), time, anim_swing, vecObj);
206+
}
207+
208+
var anim_blend = dog.dogWalkAnimation.runningBlend(partialTickTime);
209+
if (anim_blend > Mth.EPSILON) {
210+
pose_1.store(this);
211+
this.resetAllPose();
212+
DogKeyframeAnimations.keyframeAnimate(anim_context, WALK_GALLOP.get(), time / 2, 1, vecObj);
213+
pose_2.store(this);
214+
AnimSnapshot.blendAndApply(anim_blend, pose_1, pose_2, this);
215+
}
177216
}
178217

218+
@Deprecated
179219
public void animateStandWalking(Dog dog, float limbSwing, float limbSwingAmount, float partialTickTime) {
180220
float w = Mth.cos(limbSwing * 0.6662F);
181221
float w1 = Mth.cos(limbSwing * 0.6662F + (float) Math.PI);
@@ -210,6 +250,7 @@ public void animateStandWalking(Dog dog, float limbSwing, float limbSwingAmount,
210250
this.legFrontLeft.xRot += w * 1.4F * limbSwingAmount;
211251
}
212252

253+
@Deprecated
213254
private float getAnimateWalkingValue(float w, float swingAmount, float amplitude) {
214255
int sign = Mth.sign(amplitude);
215256
amplitude = Math.abs(amplitude);

src/main/java/doggytalents/client/entity/render/DogRenderer.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,17 @@ protected void setupRotations(Dog p_115317_, PoseStack p_115318_, float p_115319
483483
p_115318_.mulPose(Axis.ZP.rotationDegrees(f * this.getFlipDegrees(p_115317_)));
484484
} else
485485
p_115318_.mulPose(Axis.YP.rotationDegrees(180.0F - p_115320_));
486-
return;
486+
} else {
487+
super.setupRotations(p_115317_, p_115318_, p_115319_, p_115320_, p_115321_, x);
488+
}
489+
490+
//Bank
491+
if (p_115317_.deathTime <= 0 && p_115317_.dogWalkAnimation.isBanking()) {
492+
var dog = p_115317_;
493+
var bank_value = -dog.dogWalkAnimation.bankValue(p_115321_);
494+
var max_bank = dog.dogWalkAnimation.maxBankZRot();
495+
p_115318_.mulPose(Axis.ZP.rotationDegrees(bank_value * max_bank));
487496
}
488-
super.setupRotations(p_115317_, p_115318_, p_115319_, p_115320_, p_115321_, x);
489497
}
490498

491499

src/main/java/doggytalents/common/entity/Dog.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import doggytalents.common.entity.ai.triggerable.TriggerableAction.ActionState;
4242
import doggytalents.common.entity.anim.DogAnimationManager;
4343
import doggytalents.common.entity.anim.DogPose;
44+
import doggytalents.common.entity.anim.DogWalkAnimationState;
4445
import doggytalents.common.entity.anim.DogAnimationManager.DogAnimDebugState;
4546
import doggytalents.common.entity.datasync.DogDataSyncManager;
4647
import doggytalents.common.entity.DogIncapacitatedMananger.BandaidState;
@@ -274,6 +275,7 @@ public class Dog extends AbstractDog {
274275
public final DogMoodManager dogMood = new DogMoodManager(this);
275276
public final DogSoundManager dogSoundManager
276277
= new DogSoundManager(this);
278+
public final DogWalkAnimationState dogWalkAnimation = new DogWalkAnimationState(this);
277279
private DogAlterationProps alterationProps
278280
= new DogAlterationProps();
279281
private IDogRangedAttackManager dogRangedAttackManager
@@ -1116,6 +1118,22 @@ public InteractionResult mobInteract(Player player, InteractionHand hand) {
11161118
return InteractionResult.PASS;
11171119
}
11181120

1121+
@Override
1122+
public void calculateEntityAnimation(boolean ySpeed) {
1123+
float rawDeltaMove = (float) Mth.length(
1124+
this.getX() - this.xo,
1125+
0,
1126+
this.getZ() - this.zo
1127+
);
1128+
this.updateWalkAnimation(rawDeltaMove);
1129+
}
1130+
1131+
@Override
1132+
protected void updateWalkAnimation(float rawDeltaMove) {
1133+
super.updateWalkAnimation(rawDeltaMove);
1134+
this.dogWalkAnimation.update(rawDeltaMove);
1135+
}
1136+
11191137
private InteractionResult handleDogSitStand(Player player) {
11201138
if (!this.canInteract(player))
11211139
return InteractionResult.FAIL;

0 commit comments

Comments
 (0)