Skip to content

Instantly share code, notes, and snippets.

@avetharun
Last active March 22, 2024 23:10
Show Gist options
  • Save avetharun/2567f8612e2891c05825c73d1c3d23a4 to your computer and use it in GitHub Desktop.
Save avetharun/2567f8612e2891c05825c73d1c3d23a4 to your computer and use it in GitHub Desktop.
[FabricMC] Custom model overrides for Items depending on their ModelTransformationMode

This gist represents a skeleton structure for overriding Items depending on their ModelTransformationMode. All code is free to use, with no attribution needed.

// Not required, but is very helpful.
@Environment(EnvType.CLIENT)
public class BooleanModelOverride implements ClampedModelPredicateProvider {
public static ModelTransformationMode currentModelTransform = ModelTransformationMode.NONE;
public static int offset = 1;
int thisOffset = 0;
public BooleanModelOverride(Function4<ItemStack, ClientWorld, LivingEntity, Integer, Boolean> p) {
this.predicate = p;
this.thisOffset = offset++;
}
public float execute(ItemStack s, ClientWorld w, LivingEntity e, int seed) {
if (predicate == null) {return 0;}
return predicate.apply(s, w, e, seed) ? 0.07991f * thisOffset : 0f;
}
public static boolean isRenderingInGUI(ItemStack stack) {
return currentModelTransform == ModelTransformationMode.GUI && !isRenderingInHandAny(stack);
}
public static boolean isRenderingInHandFirst(ItemStack stack) {
return currentModelTransform.isFirstPerson();
}
public static boolean isRenderingInHandThird(ItemStack stack) {
return currentModelTransform == ModelTransformationMode.THIRD_PERSON_LEFT_HAND || currentModelTransform == ModelTransformationMode.THIRD_PERSON_RIGHT_HAND;
}
public static boolean isRenderingInHandAny(ItemStack stack) {
return isRenderingInHandFirst(stack) || isRenderingInHandThird(stack);
}
public static boolean isRenderingInFixedPos(ItemStack stack) {
return currentModelTransform == ModelTransformationMode.FIXED;
}
public static boolean isRenderingAsDropped(ItemStack stack) {
return currentModelTransform == ModelTransformationMode.GROUND;
}
public final Function4<ItemStack, ClientWorld, LivingEntity, Integer, Boolean> predicate;
@Override
public float unclampedCall(ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity, int seed) {
return execute(stack, world, entity, seed);
}
}
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@ModifyArg(method="renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V",
at=@At(value="INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;renderBakedItemModel(Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/item/ItemStack;IILnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;)V"))
private BakedModel injectedModel(BakedModel model, @Local ItemStack stack, @Local MatrixStack matrixStack, @Local ModelTransformationMode mode, @Local VertexConsumerProvider consumerProvider) {
var m = model.getOverrides().apply(model,stack,MinecraftClient.getInstance().world, null,42);
return m;
}
@Inject(at=@At("HEAD"), method= "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V")
public void renderItem(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci, @Share("model") LocalRef<BakedModel> modelRef) {
BooleanModelOverride.currentModelTransform = renderMode;
}
}
@Environment(EnvType.CLIENT)
@Mixin(targets = "net.minecraft.client.render.model.json.ModelOverride$Deserializer")
public class ModelOverrideDeserializerMixin {
/**
* Calculate a 64 bits hash by combining CRC32 with Adler32.
*
* @param bytes a byte array
* @return a hash number
*/
public static long getHash64(byte[] bytes) {
CRC32 crc32 = new CRC32();
Adler32 adl32 = new Adler32();
crc32.update(bytes);
adl32.update(bytes);
long crc = crc32.getValue();
long adl = adl32.getValue();
return ((crc << 32) | adl) + (crc << 8);
}
public static long getHash64(String s) {
return getHash64(s.getBytes(StandardCharsets.UTF_8));
}
// Unfortunately, I need to do this to allow different types. Sorry!
@Inject(method="deserializeMinPropertyValues", at=@At("HEAD"), cancellable = true)
private void onDeserializeMinPropertyValues(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir) { List<ModelOverride.Condition> conds = new ArrayList<>();
JsonObject jsonObject = JsonHelper.getObject(object, "predicate");
for (Map.Entry<String, JsonElement> stringJsonElementEntry : jsonObject.entrySet()) {
if (JsonHelper.isBoolean(stringJsonElementEntry.getValue())) {
boolean s = JsonHelper.asBoolean(stringJsonElementEntry.getValue(), stringJsonElementEntry.getKey());
float v = s ? 0.07991f : 0;
conds.add(new ModelOverride.Condition(new Identifier(stringJsonElementEntry.getKey()), v));
continue;
} else if (JsonHelper.isString(stringJsonElementEntry.getValue())) {
float v = getHash64(JsonHelper.asString(stringJsonElementEntry.getValue(), stringJsonElementEntry.getKey()));
conds.add(new ModelOverride.Condition(new Identifier(stringJsonElementEntry.getKey()), v));
continue;
}
conds.add(new ModelOverride.Condition(new Identifier(stringJsonElementEntry.getKey()), JsonHelper.asFloat(stringJsonElementEntry.getValue(), stringJsonElementEntry.getKey())));
i++;
}
cir.setReturnValue (conds);
}
}
public void onInitializeClient() {
...
ModelPredicateProviderRegistry.register(new Identifier("is_hand_first"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandFirst(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_hand_third"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandThird(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_hand_any"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandAny(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_hand_first"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandFirst(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_hand_third"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandThird(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_hand_any"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInHandAny(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_gui"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInGUI(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_inventory"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInGUI(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_fixed"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInFixedPos(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_gui"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInGUI(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_inventory"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInGUI(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_fixed"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInFixedPos(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_in_item_frame"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingInFixedPos(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_dropped"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingAsDropped(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_ground"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingAsDropped(itemStack)));
ModelPredicateProviderRegistry.register(new Identifier("is_on_ground"), new BooleanModelOverride((itemStack, clientWorld, livingEntity, integer) -> BooleanModelOverride.isRenderingAsDropped(itemStack)));
}
{
"credit": "Made with Blockbench",
"parent": "namespace:item/my_item",
"gui_light": "front",
"overrides": [
{"predicate": {"is_gui": false, "is_hand_any": true, "swing_time": 0, "use_time": 0}, "model": "namespace:item/in_hand"},
{"predicate": {"is_gui": false, "is_hand_any": true, "use_time": 0.001}, "model": "namespace:item/in_hand_using"},
{"predicate": {"is_gui": false, "is_hand_any": true, "swing_time": 0.001, "use_time": 0}, "model": "namespace:item/in_hand_swinging"}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment