Skip to content

Commit 1e2b25c

Browse files
committed
DTN dedicated Animation Format: Codec
1 parent fd6852b commit 1e2b25c

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package doggytalents.client.entity.model.animation;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.stream.Collectors;
9+
10+
import org.joml.Vector3f;
11+
12+
import com.google.common.collect.ImmutableBiMap;
13+
import com.mojang.serialization.Codec;
14+
import com.mojang.serialization.codecs.RecordCodecBuilder;
15+
16+
import net.minecraft.client.animation.AnimationChannel;
17+
import net.minecraft.client.animation.AnimationDefinition;
18+
import net.minecraft.client.animation.Keyframe;
19+
import net.minecraft.util.ExtraCodecs;
20+
21+
public class DTNAnimationCodec {
22+
23+
/**
24+
* Format
25+
* {@snippet Lang=JSON :
26+
* {
27+
* "dtn_format_version": "1.0",
28+
* "length": 0.5,
29+
* "channels": [
30+
* {
31+
* "part": "tail",
32+
* "type": "position",
33+
* "keyframes": [
34+
* {
35+
* "at": 0,
36+
* "value": [
37+
* 0,
38+
* 0.5,
39+
* -0.4
40+
* ],
41+
* "interp": "catmullrom"
42+
* }
43+
* ]
44+
* }
45+
* ],
46+
* "loop": true
47+
* }
48+
* }
49+
*/
50+
public static final Codec<AnimationDefinition> CODEC = RecordCodecBuilder.create(
51+
builder -> builder.group(
52+
Codec.FLOAT.fieldOf("length")
53+
.forGetter(AnimationDefinition::lengthInSeconds),
54+
Codec.BOOL.fieldOf("loop")
55+
.forGetter(AnimationDefinition::looping),
56+
channelWithPartCodec().listOf().xmap(
57+
DTNAnimationCodec::channelWithPartListToMap,
58+
DTNAnimationCodec::channelWithPartListFromMap
59+
).fieldOf("channels").forGetter(AnimationDefinition::boneAnimations)
60+
)
61+
.apply(builder, AnimationDefinition::new)
62+
);
63+
64+
private static Map<String, List<AnimationChannel>>
65+
channelWithPartListToMap(List<ChannelWithPart> channelsWithParts) {
66+
67+
var channel_by_part = new HashMap<String, List<AnimationChannel>>();
68+
channelsWithParts.forEach(channelsWithPart -> {
69+
channel_by_part.computeIfAbsent(channelsWithPart.part(), k -> new ArrayList<>())
70+
.add(channelsWithPart.channel());
71+
});
72+
return channel_by_part;
73+
}
74+
private static List<ChannelWithPart> channelWithPartListFromMap(
75+
Map<String, List<AnimationChannel>> map) {
76+
77+
var ret = new ArrayList<ChannelWithPart>();
78+
for (var entry : map.entrySet()) {
79+
var part = entry.getKey();
80+
var channels = entry.getValue();
81+
for (var channel : channels) {
82+
ret.add(new ChannelWithPart(part, channel));
83+
}
84+
}
85+
86+
return ret;
87+
}
88+
89+
private static final ImmutableBiMap<String, AnimationChannel.Target>
90+
CHANNEL_TYPE_BY_ID = ImmutableBiMap.of(
91+
"position", AnimationChannel.Targets.POSITION,
92+
"rotation", AnimationChannel.Targets.ROTATION
93+
);
94+
private static final AnimationChannel.Target getChannelTypeFromId(String id) {
95+
return CHANNEL_TYPE_BY_ID.get(id);
96+
}
97+
private static final String getIdFromChannelType(AnimationChannel.Target interp) {
98+
return CHANNEL_TYPE_BY_ID.inverse().get(interp);
99+
}
100+
private record ChannelWithPart(String part, AnimationChannel channel) {
101+
public AnimationChannel.Target type() { return channel.target(); }
102+
public List<Keyframe> keyframesForEncode() {
103+
return fixChannelWhenEncode(channel);
104+
}
105+
public static ChannelWithPart of(String part, AnimationChannel.Target type,
106+
List<Keyframe> keyframes) {
107+
return new ChannelWithPart(part,
108+
fixChannelWhenDecode(type, keyframes));
109+
}
110+
}
111+
private static AnimationChannel fixChannelWhenDecode(
112+
AnimationChannel.Target type, List<Keyframe> keyframes) {
113+
114+
if (type != AnimationChannel.Targets.ROTATION)
115+
return new AnimationChannel(type, keyframes.toArray(Keyframe[]::new));
116+
117+
var new_keyframes = keyframes.stream()
118+
.map(kf -> {
119+
var old_value = kf.target();
120+
var new_value = new Vector3f(-old_value.x(), -old_value.y(), old_value.z());
121+
return new Keyframe(kf.timestamp(), new_value, kf.interpolation());
122+
})
123+
.toArray(Keyframe[]::new);
124+
return new AnimationChannel(type, new_keyframes);
125+
}
126+
private static List<Keyframe> fixChannelWhenEncode(AnimationChannel channel) {
127+
var type = channel.target();
128+
if (type != AnimationChannel.Targets.ROTATION)
129+
return Arrays.asList(channel.keyframes());
130+
131+
return Arrays.stream(channel.keyframes())
132+
.map(kf -> {
133+
var old_value = kf.target();
134+
var new_value = new Vector3f(-old_value.x(), -old_value.y(), old_value.z());
135+
return new Keyframe(kf.timestamp(), new_value, kf.interpolation());
136+
})
137+
.collect(Collectors.toList());
138+
}
139+
private static Codec<ChannelWithPart> channelWithPartCodec() {
140+
return RecordCodecBuilder.create(
141+
builder -> builder.group(
142+
Codec.STRING.fieldOf("part")
143+
.forGetter(ChannelWithPart::part),
144+
Codec.STRING
145+
.xmap(
146+
DTNAnimationCodec::getChannelTypeFromId,
147+
DTNAnimationCodec::getIdFromChannelType
148+
)
149+
.fieldOf("type")
150+
.forGetter(ChannelWithPart::type),
151+
keyframeCodec().listOf()
152+
.fieldOf("keyframes")
153+
.forGetter(ChannelWithPart::keyframesForEncode)
154+
)
155+
.apply(builder, ChannelWithPart::of)
156+
);
157+
}
158+
159+
private static final ImmutableBiMap<String, AnimationChannel.Interpolation>
160+
INTERP_BY_ID = ImmutableBiMap.of(
161+
"linear", AnimationChannel.Interpolations.LINEAR,
162+
"catmullrom", AnimationChannel.Interpolations.CATMULLROM
163+
);
164+
private static final AnimationChannel.Interpolation getInterpFromId(String id) {
165+
return INTERP_BY_ID.get(id);
166+
}
167+
private static final String getIdFromInterp(AnimationChannel.Interpolation interp) {
168+
return INTERP_BY_ID.inverse().get(interp);
169+
}
170+
private static Codec<Keyframe> keyframeCodec() {
171+
return RecordCodecBuilder.create(
172+
builder -> builder.group(
173+
Codec.FLOAT.fieldOf("at")
174+
.forGetter(Keyframe::timestamp),
175+
ExtraCodecs.VECTOR3F.optionalFieldOf("value", new Vector3f())
176+
.forGetter(Keyframe::target),
177+
Codec.STRING.xmap(
178+
DTNAnimationCodec::getInterpFromId,
179+
DTNAnimationCodec::getIdFromInterp
180+
).fieldOf("interp").forGetter(Keyframe::interpolation)
181+
)
182+
.apply(builder, Keyframe::new)
183+
);
184+
}
185+
}

0 commit comments

Comments
 (0)