134 lines
4.9 KiB
Java
134 lines
4.9 KiB
Java
package net.minecraft.client.renderer.item;
|
|
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.MapCodec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.multiplayer.ClientLevel;
|
|
import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperties;
|
|
import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperty;
|
|
import net.minecraft.client.resources.model.ResolvableModel;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.item.ItemDisplayContext;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class RangeSelectItemModel implements ItemModel {
|
|
private static final int LINEAR_SEARCH_THRESHOLD = 16;
|
|
private final RangeSelectItemModelProperty property;
|
|
private final float scale;
|
|
private final float[] thresholds;
|
|
private final ItemModel[] models;
|
|
private final ItemModel fallback;
|
|
|
|
RangeSelectItemModel(RangeSelectItemModelProperty property, float scale, float[] thresholds, ItemModel[] models, ItemModel fallback) {
|
|
this.property = property;
|
|
this.thresholds = thresholds;
|
|
this.models = models;
|
|
this.fallback = fallback;
|
|
this.scale = scale;
|
|
}
|
|
|
|
private static int lastIndexLessOrEqual(float[] thresholds, float value) {
|
|
if (thresholds.length < 16) {
|
|
for (int i = 0; i < thresholds.length; i++) {
|
|
if (thresholds[i] > value) {
|
|
return i - 1;
|
|
}
|
|
}
|
|
|
|
return thresholds.length - 1;
|
|
} else {
|
|
int ix = Arrays.binarySearch(thresholds, value);
|
|
if (ix < 0) {
|
|
int j = ~ix;
|
|
return j - 1;
|
|
} else {
|
|
return ix;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void update(
|
|
ItemStackRenderState renderState,
|
|
ItemStack stack,
|
|
ItemModelResolver itemModelResolver,
|
|
ItemDisplayContext displayContext,
|
|
@Nullable ClientLevel level,
|
|
@Nullable LivingEntity entity,
|
|
int seed
|
|
) {
|
|
float f = this.property.get(stack, level, entity, seed) * this.scale;
|
|
ItemModel itemModel;
|
|
if (Float.isNaN(f)) {
|
|
itemModel = this.fallback;
|
|
} else {
|
|
int i = lastIndexLessOrEqual(this.thresholds, f);
|
|
itemModel = i == -1 ? this.fallback : this.models[i];
|
|
}
|
|
|
|
itemModel.update(renderState, stack, itemModelResolver, displayContext, level, entity, seed);
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public record Entry(float threshold, ItemModel.Unbaked model) {
|
|
public static final Codec<RangeSelectItemModel.Entry> CODEC = RecordCodecBuilder.create(
|
|
instance -> instance.group(
|
|
Codec.FLOAT.fieldOf("threshold").forGetter(RangeSelectItemModel.Entry::threshold),
|
|
ItemModels.CODEC.fieldOf("model").forGetter(RangeSelectItemModel.Entry::model)
|
|
)
|
|
.apply(instance, RangeSelectItemModel.Entry::new)
|
|
);
|
|
public static final Comparator<RangeSelectItemModel.Entry> BY_THRESHOLD = Comparator.comparingDouble(RangeSelectItemModel.Entry::threshold);
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public record Unbaked(RangeSelectItemModelProperty property, float scale, List<RangeSelectItemModel.Entry> entries, Optional<ItemModel.Unbaked> fallback)
|
|
implements ItemModel.Unbaked {
|
|
public static final MapCodec<RangeSelectItemModel.Unbaked> MAP_CODEC = RecordCodecBuilder.mapCodec(
|
|
instance -> instance.group(
|
|
RangeSelectItemModelProperties.MAP_CODEC.forGetter(RangeSelectItemModel.Unbaked::property),
|
|
Codec.FLOAT.optionalFieldOf("scale", 1.0F).forGetter(RangeSelectItemModel.Unbaked::scale),
|
|
RangeSelectItemModel.Entry.CODEC.listOf().fieldOf("entries").forGetter(RangeSelectItemModel.Unbaked::entries),
|
|
ItemModels.CODEC.optionalFieldOf("fallback").forGetter(RangeSelectItemModel.Unbaked::fallback)
|
|
)
|
|
.apply(instance, RangeSelectItemModel.Unbaked::new)
|
|
);
|
|
|
|
@Override
|
|
public MapCodec<RangeSelectItemModel.Unbaked> type() {
|
|
return MAP_CODEC;
|
|
}
|
|
|
|
@Override
|
|
public ItemModel bake(ItemModel.BakingContext context) {
|
|
float[] fs = new float[this.entries.size()];
|
|
ItemModel[] itemModels = new ItemModel[this.entries.size()];
|
|
List<RangeSelectItemModel.Entry> list = new ArrayList(this.entries);
|
|
list.sort(RangeSelectItemModel.Entry.BY_THRESHOLD);
|
|
|
|
for (int i = 0; i < list.size(); i++) {
|
|
RangeSelectItemModel.Entry entry = (RangeSelectItemModel.Entry)list.get(i);
|
|
fs[i] = entry.threshold;
|
|
itemModels[i] = entry.model.bake(context);
|
|
}
|
|
|
|
ItemModel itemModel = (ItemModel)this.fallback.map(unbaked -> unbaked.bake(context)).orElse(context.missingItemModel());
|
|
return new RangeSelectItemModel(this.property, this.scale, fs, itemModels, itemModel);
|
|
}
|
|
|
|
@Override
|
|
public void resolveDependencies(ResolvableModel.Resolver resolver) {
|
|
this.fallback.ifPresent(unbaked -> unbaked.resolveDependencies(resolver));
|
|
this.entries.forEach(entry -> entry.model.resolveDependencies(resolver));
|
|
}
|
|
}
|
|
}
|