-
Notifications
You must be signed in to change notification settings - Fork 6
/
GeoModel.java
204 lines (168 loc) · 8.28 KB
/
GeoModel.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package mod.azure.azurelib.model;
import java.util.Optional;
import java.util.function.BiConsumer;
import mod.azure.azurelib.AzureLibException;
import mod.azure.azurelib.cache.AzureLibCache;
import mod.azure.azurelib.cache.object.BakedGeoModel;
import mod.azure.azurelib.cache.object.GeoBone;
import mod.azure.azurelib.constant.DataTickets;
import mod.azure.azurelib.core.animatable.GeoAnimatable;
import mod.azure.azurelib.core.animatable.model.CoreGeoModel;
import mod.azure.azurelib.core.animation.AnimatableManager;
import mod.azure.azurelib.core.animation.Animation;
import mod.azure.azurelib.core.animation.AnimationProcessor;
import mod.azure.azurelib.core.animation.AnimationState;
import mod.azure.azurelib.core.molang.MolangParser;
import mod.azure.azurelib.core.molang.MolangQueries;
import mod.azure.azurelib.core.object.DataTicket;
import mod.azure.azurelib.loading.object.BakedAnimations;
import mod.azure.azurelib.renderer.GeoRenderer;
import mod.azure.azurelib.util.RenderUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
/**
* Base class for all code-based model objects.<br>
* All models to registered to a {@link GeoRenderer} should be an instance of this or one of its subclasses.
*/
public abstract class GeoModel<T extends GeoAnimatable> implements CoreGeoModel<T> {
private final AnimationProcessor<T> processor = new AnimationProcessor<>(this);
private BakedGeoModel currentModel = null;
private double animTime;
private double lastGameTickTime;
private long lastRenderedInstance = -1;
/**
* Returns the resource path for the {@link BakedGeoModel} (model json file) to render based on the provided animatable
*/
public abstract ResourceLocation getModelResource(T animatable);
/**
* Returns the resource path for the texture file to render based on the provided animatable
*/
public abstract ResourceLocation getTextureResource(T animatable);
/**
* Returns the resourcepath for the {@link BakedAnimations} (animation json file) to use for animations based on the provided animatable
*/
public abstract ResourceLocation getAnimationResource(T animatable);
/**
* Override this and return true if AzureLib should crash when attempting to animate the model, but fails to find a bone.<br>
* By default, AzureLib will just gracefully ignore a missing bone, which might cause oddities with incorrect models or mismatching variables.<br>
*/
public boolean crashIfBoneMissing() {
return false;
}
/**
* Gets the default render type for this animatable, to be selected by default by the renderer using it
*/
public RenderType getRenderType(T animatable, ResourceLocation texture) {
return RenderType.entityCutoutNoCull(texture);
}
@Override
public final BakedGeoModel getBakedGeoModel(String location) {
return getBakedModel(new ResourceLocation(location));
}
/**
* Get the baked geo model object used for rendering from the given resource path
*/
public BakedGeoModel getBakedModel(ResourceLocation location) {
BakedGeoModel model = AzureLibCache.getBakedModels().get(location);
if (model == null)
throw new AzureLibException(location, "Unable to find model");
if (model != this.currentModel) {
this.processor.setActiveModel(model);
this.currentModel = model;
}
return this.currentModel;
}
/**
* Gets a bone from this model by name
*
* @param name The name of the bone
* @return An {@link Optional} containing the {@link mod.azure.azurelib.cache.object.GeoBone} if one matches, otherwise an empty Optional
*/
public Optional<mod.azure.azurelib.cache.object.GeoBone> getBone(String name) {
return Optional.ofNullable((GeoBone) getAnimationProcessor().getBone(name));
}
/**
* Get the baked animation object used for rendering from the given resource path
*/
@Override
public Animation getAnimation(T animatable, String name) {
ResourceLocation location = getAnimationResource(animatable);
BakedAnimations bakedAnimations = AzureLibCache.getBakedAnimations().get(location);
if (bakedAnimations == null)
throw new AzureLibException(location, "Unable to find animation.");
return bakedAnimations.getAnimation(name);
}
@Override
public AnimationProcessor<T> getAnimationProcessor() {
return this.processor;
}
/**
* Add additional {@link DataTicket DataTickets} to the {@link AnimationState} to be handled by your animation handler at render time
*
* @param animatable The animatable instance currently being animated
* @param instanceId The unique instance id of the animatable being animated
* @param dataConsumer The DataTicket + data consumer to be added to the AnimationEvent
*/
public void addAdditionalStateData(T animatable, long instanceId, BiConsumer<DataTicket<T>, T> dataConsumer) {
}
@Override
public void handleAnimations(T animatable, long instanceId, AnimationState<T> animationState) {
Minecraft mc = Minecraft.getInstance();
AnimatableManager<T> animatableManager = animatable.getAnimatableInstanceCache().getManagerForId(instanceId);
Double currentTick = animationState.getData(DataTickets.TICK);
if (currentTick == null)
currentTick = animatable instanceof LivingEntity livingEntity ? (double) livingEntity.tickCount : RenderUtils.getCurrentTick();
if (animatableManager.getFirstTickTime() == -1)
animatableManager.startedAt(currentTick + mc.getFrameTime());
double currentFrameTime = currentTick - animatableManager.getFirstTickTime();
boolean isReRender = !animatableManager.isFirstTick() && currentFrameTime == animatableManager.getLastUpdateTime();
if (isReRender && instanceId == this.lastRenderedInstance)
return;
if (!isReRender && (!mc.isPaused() || animatable.shouldPlayAnimsWhileGamePaused())) {
if (animatable instanceof LivingEntity) {
animatableManager.updatedAt(currentFrameTime);
} else {
animatableManager.updatedAt(currentFrameTime);
}
double lastUpdateTime = animatableManager.getLastUpdateTime();
this.animTime += lastUpdateTime - this.lastGameTickTime;
this.lastGameTickTime = lastUpdateTime;
}
animationState.animationTick = this.animTime;
AnimationProcessor<T> processor = getAnimationProcessor();
processor.preAnimationSetup(animationState.getAnimatable(), this.animTime);
if (!processor.getRegisteredBones().isEmpty())
processor.tickAnimation(animatable, this, animatableManager, this.animTime, animationState, crashIfBoneMissing());
setCustomAnimations(animatable, instanceId, animationState);
}
@Override
public void applyMolangQueries(T animatable, double animTime) {
MolangParser parser = MolangParser.INSTANCE;
Minecraft mc = Minecraft.getInstance();
parser.setMemoizedValue(MolangQueries.LIFE_TIME, () -> animTime / 20d);
parser.setMemoizedValue(MolangQueries.ACTOR_COUNT, mc.level::getEntityCount);
parser.setMemoizedValue(MolangQueries.TIME_OF_DAY, () -> mc.level.getDayTime() / 24000f);
parser.setMemoizedValue(MolangQueries.MOON_PHASE, mc.level::getMoonPhase);
if (animatable instanceof Entity entity) {
parser.setMemoizedValue(MolangQueries.DISTANCE_FROM_CAMERA, () -> mc.gameRenderer.getMainCamera().getPosition().distanceTo(entity.position()));
parser.setMemoizedValue(MolangQueries.IS_ON_GROUND, () -> RenderUtils.booleanToFloat(entity.onGround()));
parser.setMemoizedValue(MolangQueries.IS_IN_WATER, () -> RenderUtils.booleanToFloat(entity.isInWater()));
parser.setMemoizedValue(MolangQueries.IS_IN_WATER_OR_RAIN, () -> RenderUtils.booleanToFloat(entity.isInWaterRainOrBubble()));
if (entity instanceof LivingEntity livingEntity) {
parser.setMemoizedValue(MolangQueries.HEALTH, livingEntity::getHealth);
parser.setMemoizedValue(MolangQueries.MAX_HEALTH, livingEntity::getMaxHealth);
parser.setMemoizedValue(MolangQueries.IS_ON_FIRE, () -> RenderUtils.booleanToFloat(livingEntity.isOnFire()));
parser.setMemoizedValue(MolangQueries.GROUND_SPEED, () -> {
Vec3 velocity = livingEntity.getDeltaMovement();
return Mth.sqrt((float) ((velocity.x * velocity.x) + (velocity.z * velocity.z)));
});
parser.setMemoizedValue(MolangQueries.YAW_SPEED, () -> livingEntity.getViewYRot((float) animTime - livingEntity.getViewYRot((float) animTime - 0.1f)));
}
}
}
}