This gist represents a skeleton structure for overriding Items depending on their ModelTransformationMode. All code is free to use, with no attribution needed.
Last active
March 22, 2024 23:10
-
-
Save avetharun/2567f8612e2891c05825c73d1c3d23a4 to your computer and use it in GitHub Desktop.
[FabricMC] Custom model overrides for Items depending on their ModelTransformationMode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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