forked from FabricMC/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fabric-rendering-v1: Custom Armor Model & Texture (FabricMC#963)
* Moving testmod id to rendering-v1 Signed-off-by: shedaniel <[email protected]> Moving testmod to rendering-v1 Signed-off-by: shedaniel <[email protected]> Reviews Signed-off-by: shedaniel <[email protected]> prefix the extensions with armor Signed-off-by: shedaniel <[email protected]> change name Signed-off-by: shedaniel <[email protected]> drop custom Signed-off-by: shedaniel <[email protected]> thing Signed-off-by: shedaniel <[email protected]> javadocs Signed-off-by: shedaniel <[email protected]> fix imports Signed-off-by: shedaniel <[email protected]> forgot to do asItem Signed-off-by: shedaniel <[email protected]> add null checks and convert to ItemConvertible Signed-off-by: shedaniel <[email protected]> fix license Signed-off-by: shedaniel <[email protected]> did thing Signed-off-by: shedaniel <[email protected]> it now compiles Signed-off-by: shedaniel <[email protected]> change to a registry Signed-off-by: shedaniel <[email protected]> add @unique Signed-off-by: shedaniel <[email protected]> migrate to fabric-item-api-v1 Signed-off-by: shedaniel <[email protected]> did some renaming and improvements Signed-off-by: shedaniel <[email protected]> don't need that Signed-off-by: shedaniel <[email protected]> armor Signed-off-by: shedaniel <[email protected]> * add license to CustomArmorTests Signed-off-by: shedaniel <[email protected]> * Add @nullable annotations and fix compile Signed-off-by: shedaniel <[email protected]> * javadoc Signed-off-by: shedaniel <[email protected]> * Fix reviews Signed-off-by: shedaniel <[email protected]> * Update fabric-rendering-v1/src/main/java/net/fabricmc/fabric/mixin/client/rendering/MixinArmorFeatureRenderer.java Co-authored-by: Erlend Åmdal <[email protected]> * Add registerSimpleTexture Pass through secondLayer and suffix Use Identifier's over strings Fix the test mod * license fix Co-authored-by: Erlend Åmdal <[email protected]> Co-authored-by: modmuss50 <[email protected]>
- Loading branch information
1 parent
ba858fb
commit f21864f
Showing
13 changed files
with
568 additions
and
1 deletion.
There are no files selected for viewing
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
165 changes: 165 additions & 0 deletions
165
...-v1/src/main/java/net/fabricmc/fabric/api/client/rendering/v1/ArmorRenderingRegistry.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.fabricmc.fabric.api.client.rendering.v1; | ||
|
||
import java.util.Arrays; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import net.minecraft.client.render.entity.model.BipedEntityModel; | ||
import net.minecraft.entity.EquipmentSlot; | ||
import net.minecraft.entity.LivingEntity; | ||
import net.minecraft.util.Identifier; | ||
import net.minecraft.item.Item; | ||
import net.minecraft.item.ItemStack; | ||
|
||
import net.fabricmc.api.EnvType; | ||
import net.fabricmc.api.Environment; | ||
import net.fabricmc.fabric.impl.client.rendering.ArmorRenderingRegistryImpl; | ||
|
||
/** | ||
* A class for registering custom armor models and textures for {@link Item}, to be provided by a {@link ModelProvider} or {@link TextureProvider}. | ||
* | ||
* <p>This can be used to replace existing vanilla armor models and textures conditionally, however each {@link Item} | ||
* instance can only allow one {@link ModelProvider} or {@link TextureProvider} respectively, causing potential conflicts | ||
* with other mods if you replace the model or texture for vanilla items. Consider using a separate item instead.</p> | ||
* | ||
* <p>A custom model would probably also require a custom texture to go along it, the model will use the vanilla texture if it is undefined.</p> | ||
* | ||
* <p>Since armor textures identifier in vanilla is hardcoded to be in the {@code minecraft} namespace, this registry can also be | ||
* used to use a custom namespace if desired.</p> | ||
*/ | ||
@Environment(EnvType.CLIENT) | ||
public final class ArmorRenderingRegistry { | ||
private ArmorRenderingRegistry() { | ||
} | ||
|
||
/** | ||
* Registers a provider for custom armor models for an item. | ||
* | ||
* @param provider the provider for the model | ||
* @param items the items to be registered for | ||
*/ | ||
public static void registerModel(@Nullable ModelProvider provider, Item... items) { | ||
registerModel(provider, Arrays.asList(items)); | ||
} | ||
|
||
/** | ||
* Registers a provider for custom armor models for an item. | ||
* | ||
* @param provider the provider for the model | ||
* @param items the items to be registered for | ||
*/ | ||
public static void registerModel(@Nullable ModelProvider provider, Iterable<Item> items) { | ||
ArmorRenderingRegistryImpl.registerModel(provider, items); | ||
} | ||
|
||
/** | ||
* Registers a provider for custom texture models for an item. | ||
* | ||
* @param provider the provider for the texture | ||
* @param items the items to be registered for | ||
*/ | ||
public static void registerTexture(@Nullable TextureProvider provider, Item... items) { | ||
registerTexture(provider, Arrays.asList(items)); | ||
} | ||
|
||
/** | ||
* Registers a provider for custom texture models for an item. | ||
* | ||
* @param provider the provider for the texture | ||
* @param items the items to be registered for | ||
*/ | ||
public static void registerTexture(@Nullable TextureProvider provider, Iterable<Item> items) { | ||
ArmorRenderingRegistryImpl.registerTexture(provider, items); | ||
} | ||
|
||
/** | ||
* Register simple armor items to use the vanilla armor file name under the mods namespace. | ||
* | ||
* @param identifier The namespace + path to use for the armor texture location. | ||
* @param items the items to be registered | ||
*/ | ||
public static void registerSimpleTexture(Identifier identifier, Item... items) { | ||
registerTexture((entity, stack, slot, secondLayer, suffix, defaultTexture) -> { | ||
return new Identifier(identifier.getNamespace(), "textures/models/armor/" + identifier.getPath() + "_layer_" + (secondLayer ? 2 : 1) + (suffix == null ? "" : "_" + suffix) + ".png"); | ||
}, items); | ||
} | ||
|
||
/** | ||
* Gets the model of the armor piece. | ||
* | ||
* @param entity The entity equipping the armor | ||
* @param stack The item stack of the armor | ||
* @param slot The slot which the armor is in | ||
* @param defaultModel The default model that vanilla provides | ||
* @return The model of the armor piece. | ||
*/ | ||
@NotNull | ||
public static BipedEntityModel<LivingEntity> getArmorModel(LivingEntity entity, ItemStack stack, EquipmentSlot slot, BipedEntityModel<LivingEntity> defaultModel) { | ||
return ArmorRenderingRegistryImpl.getArmorModel(entity, stack, slot, defaultModel); | ||
} | ||
|
||
/** | ||
* Gets the armor texture {@link net.minecraft.util.Identifier}. | ||
* | ||
* @param entity The entity equipping the armor | ||
* @param stack The item stack of the armor | ||
* @param slot The slot which the armor is in | ||
* @param secondLayer True if using the second texture layer | ||
* @param suffix The texture suffix, used in vanilla by {@link net.minecraft.item.DyeableArmorItem} | ||
* @param defaultTexture The default vanilla texture identifier | ||
* @return the custom armor texture identifier, return null to use the vanilla ones. Defaulted to the item's registry id. | ||
*/ | ||
@NotNull | ||
public static Identifier getArmorTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, boolean secondLayer, @Nullable String suffix, Identifier defaultTexture) { | ||
return ArmorRenderingRegistryImpl.getArmorTexture(entity, stack, slot, secondLayer, suffix, defaultTexture); | ||
} | ||
|
||
@FunctionalInterface | ||
@Environment(EnvType.CLIENT) | ||
public interface ModelProvider { | ||
/** | ||
* Gets the model of the armor piece. | ||
* | ||
* @param entity The entity equipping the armor | ||
* @param stack The item stack of the armor | ||
* @param slot The slot which the armor is in | ||
* @param defaultModel The default vanilla armor model | ||
* @return The model of the armor piece. Should never be null. | ||
*/ | ||
@NotNull | ||
BipedEntityModel<LivingEntity> getArmorModel(LivingEntity entity, ItemStack stack, EquipmentSlot slot, BipedEntityModel<LivingEntity> defaultModel); | ||
} | ||
|
||
@FunctionalInterface | ||
@Environment(EnvType.CLIENT) | ||
public interface TextureProvider { | ||
/** | ||
* Gets the armor texture {@link net.minecraft.util.Identifier}. | ||
* | ||
* @param entity The entity equipping the armor | ||
* @param stack The item stack of the armor | ||
* @param slot The slot which the armor is in | ||
* @param defaultTexture The default vanilla texture identifier | ||
* @return the custom armor texture identifier. Should never be null. | ||
*/ | ||
@NotNull | ||
Identifier getArmorTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, boolean secondLayer, @Nullable String suffix, Identifier defaultTexture); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
...g-v1/src/main/java/net/fabricmc/fabric/impl/client/rendering/ArmorProviderExtensions.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.fabricmc.fabric.impl.client.rendering; | ||
|
||
import org.jetbrains.annotations.Nullable; | ||
|
||
import net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderingRegistry; | ||
|
||
public interface ArmorProviderExtensions { | ||
@Nullable | ||
ArmorRenderingRegistry.ModelProvider fabric_getArmorModelProvider(); | ||
|
||
@Nullable | ||
ArmorRenderingRegistry.TextureProvider fabric_getArmorTextureProvider(); | ||
|
||
void fabric_setArmorModelProvider(@Nullable ArmorRenderingRegistry.ModelProvider provider); | ||
|
||
void fabric_setArmorTextureProvider(@Nullable ArmorRenderingRegistry.TextureProvider provider); | ||
} |
79 changes: 79 additions & 0 deletions
79
...1/src/main/java/net/fabricmc/fabric/impl/client/rendering/ArmorRenderingRegistryImpl.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.fabricmc.fabric.impl.client.rendering; | ||
|
||
import java.util.Objects; | ||
|
||
import org.jetbrains.annotations.Nullable; | ||
|
||
import net.minecraft.client.render.entity.model.BipedEntityModel; | ||
import net.minecraft.entity.EquipmentSlot; | ||
import net.minecraft.entity.LivingEntity; | ||
import net.minecraft.item.Item; | ||
import net.minecraft.item.ItemStack; | ||
import net.minecraft.util.Identifier; | ||
|
||
import net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderingRegistry; | ||
|
||
public final class ArmorRenderingRegistryImpl { | ||
private ArmorRenderingRegistryImpl() { | ||
} | ||
|
||
public static void registerModel(ArmorRenderingRegistry.ModelProvider provider, Iterable<Item> items) { | ||
Objects.requireNonNull(items); | ||
|
||
for (Item item : items) { | ||
Objects.requireNonNull(item); | ||
|
||
((ArmorProviderExtensions) item).fabric_setArmorModelProvider(provider); | ||
} | ||
} | ||
|
||
public static void registerTexture(ArmorRenderingRegistry.TextureProvider provider, Iterable<Item> items) { | ||
Objects.requireNonNull(items); | ||
|
||
for (Item item : items) { | ||
Objects.requireNonNull(item); | ||
|
||
((ArmorProviderExtensions) item).fabric_setArmorTextureProvider(provider); | ||
} | ||
} | ||
|
||
public static BipedEntityModel<LivingEntity> getArmorModel(LivingEntity entity, ItemStack stack, EquipmentSlot slot, BipedEntityModel<LivingEntity> defaultModel) { | ||
if (!stack.isEmpty()) { | ||
ArmorRenderingRegistry.ModelProvider provider = ((ArmorProviderExtensions) stack.getItem()).fabric_getArmorModelProvider(); | ||
|
||
if (provider != null) { | ||
return provider.getArmorModel(entity, stack, slot, defaultModel); | ||
} | ||
} | ||
|
||
return defaultModel; | ||
} | ||
|
||
public static Identifier getArmorTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, boolean secondLayer, @Nullable String suffix, Identifier defaultTexture) { | ||
if (!stack.isEmpty()) { | ||
ArmorRenderingRegistry.TextureProvider provider = ((ArmorProviderExtensions) stack.getItem()).fabric_getArmorTextureProvider(); | ||
|
||
if (provider != null) { | ||
return provider.getArmorTexture(entity, stack, slot, secondLayer, suffix, defaultTexture); | ||
} | ||
} | ||
|
||
return defaultTexture; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
...1/src/main/java/net/fabricmc/fabric/mixin/client/rendering/MixinArmorFeatureRenderer.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.fabricmc.fabric.mixin.client.rendering; | ||
|
||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
import org.spongepowered.asm.mixin.Final; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.Shadow; | ||
import org.spongepowered.asm.mixin.Unique; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; | ||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture; | ||
|
||
import net.minecraft.client.render.VertexConsumerProvider; | ||
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; | ||
import net.minecraft.client.render.entity.feature.FeatureRenderer; | ||
import net.minecraft.client.render.entity.feature.FeatureRendererContext; | ||
import net.minecraft.client.render.entity.model.BipedEntityModel; | ||
import net.minecraft.client.util.math.MatrixStack; | ||
import net.minecraft.entity.EquipmentSlot; | ||
import net.minecraft.entity.LivingEntity; | ||
import net.minecraft.item.ArmorItem; | ||
import net.minecraft.item.ItemStack; | ||
import net.minecraft.util.Identifier; | ||
|
||
import net.fabricmc.api.EnvType; | ||
import net.fabricmc.api.Environment; | ||
import net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderingRegistry; | ||
|
||
@Mixin(ArmorFeatureRenderer.class) | ||
@Environment(EnvType.CLIENT) | ||
public abstract class MixinArmorFeatureRenderer extends FeatureRenderer { | ||
@Shadow | ||
@Final | ||
private static Map<String, Identifier> ARMOR_TEXTURE_CACHE; | ||
|
||
public MixinArmorFeatureRenderer(FeatureRendererContext context) { | ||
super(context); | ||
} | ||
|
||
@Unique | ||
private LivingEntity storedEntity; | ||
@Unique | ||
private EquipmentSlot storedSlot; | ||
|
||
@Inject(method = "render", at = @At("HEAD")) | ||
private void storeEntity(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { | ||
// We store the living entity wearing the armor before we render | ||
this.storedEntity = livingEntity; | ||
} | ||
|
||
@Inject(method = "renderArmor", at = @At("HEAD")) | ||
private void storeSlot(MatrixStack matrices, VertexConsumerProvider vertexConsumers, LivingEntity livingEntity, EquipmentSlot slot, int i, BipedEntityModel bipedEntityModel, CallbackInfo ci) { | ||
// We store the current armor slot that is rendering before we render each armor piece | ||
this.storedSlot = slot; | ||
} | ||
|
||
@Inject(method = "render", at = @At("RETURN")) | ||
private void removeStored(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntity livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { | ||
// We remove the stored data after we render | ||
this.storedEntity = null; | ||
this.storedSlot = null; | ||
} | ||
|
||
@Inject(method = "getArmor", at = @At("RETURN"), cancellable = true) | ||
private void selectArmorModel(EquipmentSlot slot, CallbackInfoReturnable<BipedEntityModel<LivingEntity>> cir) { | ||
ItemStack stack = storedEntity.getEquippedStack(slot); | ||
|
||
BipedEntityModel<LivingEntity> defaultModel = cir.getReturnValue(); | ||
BipedEntityModel<LivingEntity> model = ArmorRenderingRegistry.getArmorModel(storedEntity, stack, slot, defaultModel); | ||
|
||
if (model != defaultModel) { | ||
cir.setReturnValue(model); | ||
} | ||
} | ||
|
||
@Inject(method = "getArmorTexture", at = @At(value = "INVOKE", target = "Ljava/util/Map;computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) | ||
private void getArmorTexture(ArmorItem armorItem, boolean secondLayer, /* @Nullable */ String suffix, CallbackInfoReturnable<Identifier> cir, String vanillaIdentifier) { | ||
String texture = ArmorRenderingRegistry.getArmorTexture(storedEntity, storedEntity.getEquippedStack(storedSlot), storedSlot, secondLayer, suffix, new Identifier(vanillaIdentifier)).toString(); | ||
|
||
if (!Objects.equals(texture, vanillaIdentifier)) { | ||
cir.setReturnValue(ARMOR_TEXTURE_CACHE.computeIfAbsent(texture, Identifier::new)); | ||
} | ||
} | ||
} |
Oops, something went wrong.